Repository: bytecodealliance/wasmtime
Pull request #1732 (2020-05-20 14:49:13)

arm64: Use FPU instrctions for Fcopysign

github-actions[bot]: #### Subscribe to Label Action

cc @bnjbvr

<details>
This issue or pull request has been labeled: "cranelift", "cranelift:area:aarch64"

Thus the following users have been cc'd because of the following labels:

* bnjbvr: cranelift

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 0859dec9b..4be5e4c19 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1640,9 +1640,9 @@ dependencies = [
 
 [[package]]
 name = "regalloc"
-version = "0.0.24"
+version = "0.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5842bece8a4b1690ffa6d9d959081c1d5d851ee4337a36c0a121fafe8c16add2"
+checksum = "b36727ea09f6e363ddebb29b2d2620052267cc1fc6e0da7da63317938eb4104c"
 dependencies = [
  "log",
  "rustc-hash",
@@ -2188,7 +2188,6 @@ name = "wasmtime-c-api"
 version = "0.16.0"
 dependencies = [
  "anyhow",
- "env_logger 0.7.1",
  "once_cell",
  "wasi-common",
  "wasmtime",
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index b86453c50..0bc1c3200 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -25,7 +25,7 @@ smallvec = { version = "1.0.0" }
 thiserror = "1.0.4"
 byteorder = { version = "1.3.2", default-features = false }
 peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true }
-regalloc = "0.0.24"
+regalloc = "0.0.23"
 # It is a goal of the cranelift-codegen crate to have minimal external dependencies.
 # Please don't add any unless they are essential to the task of creating binary
 # machine code. Integration tests that need external dependencies can be
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 0e6dcdb1f..462628143 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -85,12 +85,12 @@ pub fn u64_constant(bits: u64) -> ConstantData {
 // Instructions and subcomponents: emission
 
 fn machreg_to_gpr(m: Reg) -> u32 {
-    assert!(m.get_class() == RegClass::I64);
+    assert_eq!(m.get_class(), RegClass::I64);
     u32::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
 }
 
 fn machreg_to_vec(m: Reg) -> u32 {
-    assert!(m.get_class() == RegClass::V128);
+    assert_eq!(m.get_class(), RegClass::V128);
     u32::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
 }
 
@@ -948,6 +948,44 @@ impl MachInstEmit for Inst {
                 };
                 sink.put4(enc_fpurrr(top22, rd, rn, rm));
             }
+            &Inst::FpuRRI { fpu_op, rd, rn } => match fpu_op {
+                FPUOpRI::UShr32(imm) => {
+                    debug_assert_eq!(32, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
+                }
+                FPUOpRI::UShr64(imm) => {
+                    debug_assert_eq!(64, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b01_1_111110_0000000_00_0_0_0_1_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
+                }
+                FPUOpRI::Sli64(imm) => {
+                    debug_assert_eq!(64, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b01_1_111110_0000000_010101_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
+                }
+                FPUOpRI::Sli32(imm) => {
+                    debug_assert_eq!(32, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b0_0_1_011110_0000000_010101_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
+                }
+            },
             &Inst::FpuRRRR {
                 fpu_op,
                 rd,
@@ -1163,13 +1201,17 @@ impl MachInstEmit for Inst {
             }
             &Inst::Jump { ref dest } => {
                 let off = sink.cur_offset();
-                // Indicate that the jump uses a label, if so, so that a fixup can occur later.
+                // Emit the jump itself.
+                sink.put4(enc_jump26(0b000101, dest.as_offset26_or_zero()));
+                // After the jump has been emitted, indicate that it uses a
+                // label, if so, so that a fixup can occur later. This happens
+                // after we emit the bytes because the fixup might occur right
+                // away (so the bytes must actually exist now).
                 if let Some(l) = dest.as_label() {
                     sink.use_label_at_offset(off, l, LabelUse::Branch26);
-                    sink.add_uncond_branch(off, off + 4, l);
+                    let cur_off = sink.cur_offset();
+                    sink.add_uncond_branch(off, cur_off, l);
                 }
-                // Emit the jump itself.
-                sink.put4(enc_jump26(0b000101, dest.as_offset26_or_zero()));
             }
             &Inst::Ret => {
                 sink.put4(0xd65f03c0);
@@ -1204,27 +1246,28 @@ impl MachInstEmit for Inst {
             } => {
                 // Conditional part first.
                 let cond_off = sink.cur_offset();
+                sink.put4(enc_conditional_br(taken, kind));
                 if let Some(l) = taken.as_label() {
                     sink.use_label_at_offset(cond_off, l, LabelUse::Branch19);
+                    let cur_off = sink.cur_offset();
                     let inverted = enc_conditional_br(taken, kind.invert()).to_le_bytes();
-                    sink.add_cond_branch(cond_off, cond_off + 4, l, &inverted[..]);
+                    sink.add_cond_branch(cond_off, cur_off, l, &inverted[..]);
                 }
-                sink.put4(enc_conditional_br(taken, kind));
-
-                // Unconditional part next.
+                // Unconditional part.
                 let uncond_off = sink.cur_offset();
+                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
                 if let Some(l) = not_taken.as_label() {
                     sink.use_label_at_offset(uncond_off, l, LabelUse::Branch26);
-                    sink.add_uncond_branch(uncond_off, uncond_off + 4, l);
+                    let cur_off = sink.cur_offset();
+                    sink.add_uncond_branch(uncond_off, cur_off, l);
                 }
-                sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
             }
             &Inst::OneWayCondBr { target, kind } => {
                 let off = sink.cur_offset();
+                sink.put4(enc_conditional_br(target, kind));
                 if let Some(l) = target.as_label() {
                     sink.use_label_at_offset(off, l, LabelUse::Branch19);
                 }
-                sink.put4(enc_conditional_br(target, kind));
             }
             &Inst::IndirectBr { rn, .. } => {
                 sink.put4(enc_br(rn));
@@ -1302,12 +1345,12 @@ impl MachInstEmit for Inst {
                 for &target in targets.iter() {
                     let word_off = sink.cur_offset();
                     let off_into_table = word_off - jt_off;
+                    sink.put4(off_into_table);
                     sink.use_label_at_offset(
                         word_off,
                         target.as_label().unwrap(),
                         LabelUse::PCRel32,
                     );
-                    sink.put4(off_into_table);
                 }
 
                 // Lowering produces an EmitIsland before using a JTSequence, so we can safely
diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
index b8e6bf65b..7c473a83d 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
@@ -106,6 +106,85 @@ impl SImm7Scaled {
     }
 }
 
+#[derive(Clone, Copy, Debug)]
+pub struct FPULeftShiftImm {
+    pub amount: u8,
+    pub lane_size_in_bits: u8,
+}
+
+impl FPULeftShiftImm {
+    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
+        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
+        if amount < lane_size_in_bits {
+            Some(Self {
+                amount,
+                lane_size_in_bits,
+            })
+        } else {
+            None
+        }
+    }
+
+    pub fn enc(&self) -> u32 {
+        debug_assert!(self.lane_size_in_bits.is_power_of_two());
+        debug_assert!(self.lane_size_in_bits > self.amount);
+        // The encoding of the immediate follows the table below,
+        // where xs encode the shift amount.
+        //
+        // | lane_size_in_bits | encoding |
+        // +------------------------------+
+        // | 8                 | 0001xxx  |
+        // | 16                | 001xxxx  |
+        // | 32                | 01xxxxx  |
+        // | 64                | 1xxxxxx  |
+        //
+        // The highest one bit is represented by `lane_size_in_bits`. Since
+        // `lane_size_in_bits` is a power of 2 and `amount` is less
+        // than `lane_size_in_bits`, they can be ORed
+        // together to produced the encoded value.
+        u32::from(self.lane_size_in_bits | self.amount)
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct FPURightShiftImm {
+    pub amount: u8,
+    pub lane_size_in_bits: u8,
+}
+
+impl FPURightShiftImm {
+    pub fn maybe_from_u8(amount: u8, lane_size_in_bits: u8) -> Option<Self> {
+        debug_assert!(lane_size_in_bits == 32 || lane_size_in_bits == 64);
+        if amount > 0 && amount <= lane_size_in_bits {
+            Some(Self {
+                amount,
+                lane_size_in_bits,
+            })
+        } else {
+            None
+        }
+    }
+
+    pub fn enc(&self) -> u32 {
+        debug_assert_ne!(0, self.amount);
+        // The encoding of the immediate follows the table below,
+        // where xs encodes the negated shift amount.
+        //
+        // | lane_size_in_bits | encoding |
+        // +------------------------------+
+        // | 8                 | 0001xxx  |
+        // | 16                | 001xxxx  |
+        // | 32                | 01xxxxx  |
+        // | 64                | 1xxxxxx  |
+        //
+        // The shift amount is negated such that a shift ammount
+        // of 1 (in 64-bit) is encoded as 0b111111 and a shift
+        // amount of 64 is encoded as 0b000000,
+        // in the bottom 6 bits.
+        u32::from((self.lane_size_in_bits * 2) - self.amount)
+    }
+}
+
 /// a 9-bit signed offset.
 #[derive(Clone, Copy, Debug)]
 pub struct SImm9 {
@@ -576,6 +655,18 @@ impl ShowWithRRU for SImm7Scaled {
     }
 }
 
+impl ShowWithRRU for FPULeftShiftImm {
+    fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
+        format!("#{}", self.amount)
+    }
+}
+
+impl ShowWithRRU for FPURightShiftImm {
+    fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
+        format!("#{}", self.amount)
+    }
+}
+
 impl ShowWithRRU for SImm9 {
     fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
         format!("#{}", self.value)
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 12291c8c0..53953efc5 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -4,7 +4,7 @@
 #![allow(dead_code)]
 
 use crate::binemit::CodeOffset;
-use crate::ir::types::{B1, B16, B32, B64, B8, F32, F64, FFLAGS, I16, I32, I64, I8, IFLAGS};
+use crate::ir::types::{B1, B16, B32, B64, B8, F32, F32X2, F64, FFLAGS, I16, I32, I64, I8, IFLAGS};
 use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
 use crate::machinst::*;
 use crate::{settings, CodegenError, CodegenResult};
@@ -124,6 +124,19 @@ pub enum FPUOp2 {
     Min64,
 }
 
+/// A floating-point unit (FPU) operation with two args, a register and an immediate.
+#[derive(Copy, Clone, Debug)]
+pub enum FPUOpRI {
+    /// Unsigned right shift. Rd = Rn << #imm
+    UShr32(FPURightShiftImm),
+    /// Unsigned right shift. Rd = Rn << #imm
+    UShr64(FPURightShiftImm),
+    /// Shift left and insert. Rd |= Rn << #imm
+    Sli32(FPULeftShiftImm),
+    /// Shift left and insert. Rd |= Rn << #imm
+    Sli64(FPULeftShiftImm),
+}
+
 /// A floating-point unit (FPU) operation with three args.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum FPUOp3 {
@@ -472,6 +485,12 @@ pub enum Inst {
         rm: Reg,
     },
 
+    FpuRRI {
+        fpu_op: FPUOpRI,
+        rd: Writable<Reg>,
+        rn: Reg,
+    },
+
     /// 3-op FPU instruction.
     FpuRRRR {
         fpu_op: FPUOp3,
@@ -1034,6 +1053,13 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
             collector.add_use(rn);
             collector.add_use(rm);
         }
+        &Inst::FpuRRI { fpu_op, rd, rn, .. } => {
+            match fpu_op {
+                FPUOpRI::UShr32(..) | FPUOpRI::UShr64(..) => collector.add_def(rd),
+                FPUOpRI::Sli32(..) | FPUOpRI::Sli64(..) => collector.add_mod(rd),
+            }
+            collector.add_use(rn);
+        }
         &Inst::FpuRRRR { rd, rn, rm, ra, .. } => {
             collector.add_def(rd);
             collector.add_use(rn);
@@ -1168,29 +1194,29 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
 //=============================================================================
 // Instructions: map_regs
 
-fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
-    fn map_use<RUM: RegUsageMapper>(m: &RUM, r: &mut Reg) {
+fn aarch64_map_regs(inst: &mut Inst, mapper: &RegUsageMapper) {
+    fn map_use(m: &RegUsageMapper, r: &mut Reg) {
         if r.is_virtual() {
             let new = m.get_use(r.to_virtual_reg()).unwrap().to_reg();
             *r = new;
         }
     }
 
-    fn map_def<RUM: RegUsageMapper>(m: &RUM, r: &mut Writable<Reg>) {
+    fn map_def(m: &RegUsageMapper, r: &mut Writable<Reg>) {
         if r.to_reg().is_virtual() {
             let new = m.get_def(r.to_reg().to_virtual_reg()).unwrap().to_reg();
             *r = Writable::from_reg(new);
         }
     }
 
-    fn map_mod<RUM: RegUsageMapper>(m: &RUM, r: &mut Writable<Reg>) {
+    fn map_mod(m: &RegUsageMapper, r: &mut Writable<Reg>) {
         if r.to_reg().is_virtual() {
             let new = m.get_mod(r.to_reg().to_virtual_reg()).unwrap().to_reg();
             *r = Writable::from_reg(new);
         }
     }
 
-    fn map_mem<RUM: RegUsageMapper>(m: &RUM, mem: &mut MemArg) {
+    fn map_mem(m: &RegUsageMapper, mem: &mut MemArg) {
         // N.B.: we take only the pre-map here, but this is OK because the
         // only addressing modes that update registers (pre/post-increment on
         // AArch64) both read and write registers, so they are "mods" rather
@@ -1219,7 +1245,7 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
         };
     }
 
-    fn map_pairmem<RUM: RegUsageMapper>(m: &RUM, mem: &mut PairMemArg) {
+    fn map_pairmem(m: &RegUsageMapper, mem: &mut PairMemArg) {
         match mem {
             &mut PairMemArg::SignedOffset(ref mut reg, ..) => map_use(m, reg),
             &mut PairMemArg::PreIndexed(ref mut reg, ..) => map_def(m, reg),
@@ -1227,7 +1253,7 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
         }
     }
 
-    fn map_br<RUM: RegUsageMapper>(m: &RUM, br: &mut CondBrKind) {
+    fn map_br(m: &RegUsageMapper, br: &mut CondBrKind) {
         match br {
             &mut CondBrKind::Zero(ref mut reg) => map_use(m, reg),
             &mut CondBrKind::NotZero(ref mut reg) => map_use(m, reg),
@@ -1482,6 +1508,14 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
             map_use(mapper, rn);
             map_use(mapper, rm);
         }
+        &mut Inst::FpuRRI {
+            ref mut rd,
+            ref mut rn,
+            ..
+        } => {
+            map_def(mapper, rd);
+            map_use(mapper, rn);
+        }
         &mut Inst::FpuRRRR {
             ref mut rd,
             ref mut rn,
@@ -1738,7 +1772,7 @@ impl MachInst for Inst {
         aarch64_get_regs(self, collector)
     }
 
-    fn map_regs<RUM: RegUsageMapper>(&mut self, mapper: &RUM) {
+    fn map_regs(&mut self, mapper: &RegUsageMapper) {
         aarch64_map_regs(self, mapper);
     }
 
@@ -2236,6 +2270,23 @@ impl ShowWithRRU for Inst {
                 let rm = show_freg_sized(rm, mb_rru, size);
                 format!("{} {}, {}, {}", op, rd, rn, rm)
             }
+            &Inst::FpuRRI { fpu_op, rd, rn } => {
+                let (op, imm, vector) = match fpu_op {
+                    FPUOpRI::UShr32(imm) => ("ushr", imm.show_rru(mb_rru), true),
+                    FPUOpRI::UShr64(imm) => ("ushr", imm.show_rru(mb_rru), false),
+                    FPUOpRI::Sli32(imm) => ("sli", imm.show_rru(mb_rru), true),
+                    FPUOpRI::Sli64(imm) => ("sli", imm.show_rru(mb_rru), false),
+                };
+
+                let show_vreg_fn: fn(Reg, Option<&RealRegUniverse>) -> String = if vector {
+                    |reg, mb_rru| show_vreg_vector(reg, mb_rru, F32X2)
+                } else {
+                    show_vreg_scalar
+                };
+                let rd = show_vreg_fn(rd.to_reg(), mb_rru);
+                let rn = show_vreg_fn(rn, mb_rru);
+                format!("{} {}, {}, {}", op, rd, rn, imm)
+            }
             &Inst::FpuRRRR {
                 fpu_op,
                 rd,
diff --git a/cranelift/codegen/src/isa/aarch64/inst/regs.rs b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
index 099d81626..242fb66fc 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
@@ -1,5 +1,6 @@
 //! AArch64 ISA definitions: registers.
 
+use crate::ir::types::*;
 use crate::isa::aarch64::inst::InstSize;
 use crate::machinst::*;
 use crate::settings;
@@ -307,3 +308,16 @@ pub fn show_vreg_scalar(reg: Reg, mb_rru: Option<&RealRegUniverse>) -> String {
     }
     s
 }
+
+/// Show a vector register.
+pub fn show_vreg_vector(reg: Reg, mb_rru: Option<&RealRegUniverse>, ty: Type) -> String {
+    assert_eq!(RegClass::V128, reg.get_class());
+    let mut s = reg.show_rru(mb_rru);
+
+    match ty {
+        F32X2 => s.push_str(".2s"),
+        _ => unimplemented!(),
+    }
+
+    s
+}
diff --git a/cranelift/codegen/src/isa/aarch64/lower_inst.rs b/cranelift/codegen/src/isa/aarch64/lower_inst.rs
index a92dea7a7..2946e1647 100644
--- a/cranelift/codegen/src/isa/aarch64/lower_inst.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower_inst.rs
@@ -1460,54 +1460,38 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
         Opcode::Fcopysign => {
             // Copy the sign bit from inputs[1] to inputs[0]. We use the following sequence:
             //
-            // (64 bits for example, 32-bit sequence is analogous):
+            // This is a scalar Fcopysign.
+            // This uses scalar NEON operations for 64-bit and vector operations (2S) for 32-bit.
             //
-            // MOV Xtmp1, Dinput0
-            // MOV Xtmp2, Dinput1
-            // AND Xtmp2, 0x8000_0000_0000_0000
-            // BIC Xtmp1, 0x8000_0000_0000_0000
-            // ORR Xtmp1, Xtmp1, Xtmp2
-            // MOV Doutput, Xtmp1
+            //  mov vd, vn
+            //  ushr vtmp, vm, #63 / #31
+            //  sli vd, vtmp, #63 / #31
 
             let ty = ctx.output_ty(insn, 0);
-            let bits = ty_bits(ty);
+            let bits = ty_bits(ty) as u8;
             assert!(bits == 32 || bits == 64);
             let rn = input_to_reg(ctx, inputs[0], NarrowValueMode::None);
             let rm = input_to_reg(ctx, inputs[1], NarrowValueMode::None);
             let rd = output_to_reg(ctx, outputs[0]);
-            let tmp1 = ctx.alloc_tmp(RegClass::I64, I64);
-            let tmp2 = ctx.alloc_tmp(RegClass::I64, I64);
-            ctx.emit(Inst::MovFromVec64 { rd: tmp1, rn: rn });
-            ctx.emit(Inst::MovFromVec64 { rd: tmp2, rn: rm });
-            let imml = if bits == 32 {
-                ImmLogic::maybe_from_u64(0x8000_0000, I32).unwrap()
-            } else {
-                ImmLogic::maybe_from_u64(0x8000_0000_0000_0000, I64).unwrap()
-            };
-            let alu_op = choose_32_64(ty, ALUOp::And32, ALUOp::And64);
-            ctx.emit(Inst::AluRRImmLogic {
-                alu_op,
-                rd: tmp2,
-                rn: tmp2.to_reg(),
-                imml: imml.clone(),
-            });
-            let alu_op = choose_32_64(ty, ALUOp::AndNot32, ALUOp::AndNot64);
-            ctx.emit(Inst::AluRRImmLogic {
-                alu_op,
-                rd: tmp1,
-                rn: tmp1.to_reg(),
-                imml,
-            });
-            let alu_op = choose_32_64(ty, ALUOp::Orr32, ALUOp::Orr64);
-            ctx.emit(Inst::AluRRR {
-                alu_op,
-                rd: tmp1,
-                rn: tmp1.to_reg(),
-                rm: tmp2.to_reg(),
+            let tmp = ctx.alloc_tmp(RegClass::V128, F64);
+
+            // Copy LHS to rd.
+            ctx.emit(Inst::FpuMove64 { rd, rn });
+
+            // Copy the sign bit to the lowest bit in tmp.
+            let imm = FPURightShiftImm::maybe_from_u8(bits - 1, bits).unwrap();
+            ctx.emit(Inst::FpuRRI {
+                fpu_op: choose_32_64(ty, FPUOpRI::UShr32(imm), FPUOpRI::UShr64(imm)),
+                rd: tmp,
+                rn: rm,
             });
-            ctx.emit(Inst::MovToVec64 {
+
+            // Insert the bit from tmp into the sign bit of rd.
+            let imm = FPULeftShiftImm::maybe_from_u8(bits - 1, bits).unwrap();
+            ctx.emit(Inst::FpuRRI {
+                fpu_op: choose_32_64(ty, FPUOpRI::Sli32(imm), FPUOpRI::Sli64(imm)),
                 rd,
-                rn: tmp1.to_reg(),
+                rn: tmp.to_reg(),
             });
         }
 
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index b19013dc5..cd0bdbb5b 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -812,14 +812,14 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
             let disp = dest.as_offset32_or_zero() - 5;
             let disp = disp as u32;
             let br_start = sink.cur_offset();
-            let br_disp_off = br_start + 1;
-            let br_end = br_start + 5;
+            sink.put1(0xE9);
+            let br_disp_off = sink.cur_offset();
+            sink.put4(disp);
+            let br_end = sink.cur_offset();
             if let Some(l) = dest.as_label() {
                 sink.use_label_at_offset(br_disp_off, l, LabelUse::Rel32);
                 sink.add_uncond_branch(br_start, br_end, l);
             }
-            sink.put1(0xE9);
-            sink.put4(disp);
         }
         Inst::JmpCondSymm {
             cc,
@@ -835,31 +835,31 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
             let taken_disp = taken.as_offset32_or_zero() - 6;
             let taken_disp = taken_disp as u32;
             let cond_start = sink.cur_offset();
-            let cond_disp_off = cond_start + 2;
-            let cond_end = cond_start + 6;
+            sink.put1(0x0F);
+            sink.put1(0x80 + cc.get_enc());
+            let cond_disp_off = sink.cur_offset();
+            sink.put4(taken_disp);
+            let cond_end = sink.cur_offset();
             if let Some(l) = taken.as_label() {
                 sink.use_label_at_offset(cond_disp_off, l, LabelUse::Rel32);
                 let inverted: [u8; 6] =
                     [0x0F, 0x80 + (cc.invert().get_enc()), 0xFA, 0xFF, 0xFF, 0xFF];
                 sink.add_cond_branch(cond_start, cond_end, l, &inverted[..]);
             }
-            sink.put1(0x0F);
-            sink.put1(0x80 + cc.get_enc());
-            sink.put4(taken_disp);
 
             // Unconditional part.
 
             let nt_disp = not_taken.as_offset32_or_zero() - 5;
             let nt_disp = nt_disp as u32;
             let uncond_start = sink.cur_offset();
-            let uncond_disp_off = uncond_start + 1;
-            let uncond_end = uncond_start + 5;
+            sink.put1(0xE9);
+            let uncond_disp_off = sink.cur_offset();
+            sink.put4(nt_disp);
+            let uncond_end = sink.cur_offset();
             if let Some(l) = not_taken.as_label() {
                 sink.use_label_at_offset(uncond_disp_off, l, LabelUse::Rel32);
                 sink.add_uncond_branch(uncond_start, uncond_end, l);
             }
-            sink.put1(0xE9);
-            sink.put4(nt_disp);
         }
         Inst::JmpUnknown { target } => {
             match target {
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 1f3eb0a1e..a42653915 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -576,21 +576,21 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
 //=============================================================================
 // Instructions and subcomponents: map_regs
 
-fn map_use<RUM: RegUsageMapper>(m: &RUM, r: &mut Reg) {
+fn map_use(m: &RegUsageMapper, r: &mut Reg) {
     if r.is_virtual() {
         let new = m.get_use(r.to_virtual_reg()).unwrap().to_reg();
         *r = new;
     }
 }
 
-fn map_def<RUM: RegUsageMapper>(m: &RUM, r: &mut Writable<Reg>) {
+fn map_def(m: &RegUsageMapper, r: &mut Writable<Reg>) {
     if r.to_reg().is_virtual() {
         let new = m.get_def(r.to_reg().to_virtual_reg()).unwrap().to_reg();
         *r = Writable::from_reg(new);
     }
 }
 
-fn map_mod<RUM: RegUsageMapper>(m: &RUM, r: &mut Writable<Reg>) {
+fn map_mod(m: &RegUsageMapper, r: &mut Writable<Reg>) {
     if r.to_reg().is_virtual() {
         let new = m.get_mod(r.to_reg().to_virtual_reg()).unwrap().to_reg();
         *r = Writable::from_reg(new);
@@ -598,7 +598,7 @@ fn map_mod<RUM: RegUsageMapper>(m: &RUM, r: &mut Writable<Reg>) {
 }
 
 impl Addr {
-    fn map_uses<RUM: RegUsageMapper>(&mut self, map: &RUM) {
+    fn map_uses(&mut self, map: &RegUsageMapper) {
         match self {
             Addr::IR {
                 simm32: _,
@@ -618,7 +618,7 @@ impl Addr {
 }
 
 impl RMI {
-    fn map_uses<RUM: RegUsageMapper>(&mut self, map: &RUM) {
+    fn map_uses(&mut self, map: &RegUsageMapper) {
         match self {
             RMI::R { ref mut reg } => map_use(map, reg),
             RMI::M { ref mut addr } => addr.map_uses(map),
@@ -628,7 +628,7 @@ impl RMI {
 }
 
 impl RM {
-    fn map_uses<RUM: RegUsageMapper>(&mut self, map: &RUM) {
+    fn map_uses(&mut self, map: &RegUsageMapper) {
         match self {
             RM::R { ref mut reg } => map_use(map, reg),
             RM::M { ref mut addr } => addr.map_uses(map),
@@ -636,7 +636,7 @@ impl RM {
     }
 }
 
-fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
+fn x64_map_regs(inst: &mut Inst, mapper: &RegUsageMapper) {
     // Note this must be carefully synchronized with x64_get_regs.
     match inst {
         // ** Nop
@@ -739,7 +739,7 @@ impl MachInst for Inst {
         x64_get_regs(&self, collector)
     }
 
-    fn map_regs<RUM: RegUsageMapper>(&mut self, mapper: &RUM) {
+    fn map_regs(&mut self, mapper: &RegUsageMapper) {
         x64_map_regs(self, mapper);
     }
 
diff --git a/cranelift/codegen/src/machinst/buffer.rs b/cranelift/codegen/src/machinst/buffer.rs
index 03b4ab075..cb7564f25 100644
--- a/cranelift/codegen/src/machinst/buffer.rs
+++ b/cranelift/codegen/src/machinst/buffer.rs
@@ -105,40 +105,6 @@
 //!   (fixups happen at island emission time, at which point latest-branches are
 //!   cleared, or at the end of emission), so we are sure to catch and redirect
 //!   all possible paths to this instruction.
-//!
-//! # Branch-optimization Correctness
-//!
-//! The branch-optimization mechanism depends on a few data structures with
-//! invariants, which are always held outside the scope of top-level public
-//! methods:
-//!
-//! - The latest-branches list. Each entry describes a span of the buffer
-//!   (start/end offsets), the label target, the corresponding fixup-list entry
-//!   index, and the bytes (must be the same length) for the inverted form, if
-//!   conditional. The list of labels that are bound to the start-offset of this
-//!   branch is *complete* (if any label has a resolved offset equal to `start`
-//!   and is not an alias, it must appear in this list) and *precise* (no label
-//!   in this list can be bound to another offset). No label in this list should
-//!   be an alias.  No two branch ranges can overlap, and branches are in
-//!   ascending-offset order.
-//!
-//! - The labels-at-tail list. This contains all MachLabels that have been bound
-//!   to (whose resolved offsets are equal to) the tail offset of the buffer.
-//!   No label in this list should be an alias.
-//!
-//! - The label_offsets array, containing the bound offset of a label or
-//!   UNKNOWN. No label can be bound at an offset greater than the current
-//!   buffer tail.
-//!
-//! - The label_aliases array, containing another label to which a label is
-//!   bound or UNKNOWN. A label's resolved offset is the resolved offset
-//!   of the label it is aliased to, if this is set.
-//!
-//! We argue below, at each method, how the invariants in these data structures
-//! are maintained (grep for "Post-invariant").
-//!
-//! Given these invariants, we argue why each optimization preserves execution
-//! semantics below (grep for "Preserves execution semantics").
 
 use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
 use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
@@ -196,21 +162,8 @@ pub struct MachBuffer<I: VCodeInst> {
     /// Latest branches, to facilitate in-place editing for better fallthrough
     /// behavior and empty-block removal.
     latest_branches: SmallVec<[MachBranch; 4]>,
-    /// All labels at the current offset (emission tail). This is lazily
-    /// cleared: it is actually accurate as long as the current offset is
-    /// `labels_at_tail_off`, but if `cur_offset()` has grown larger, it should
-    /// be considered as empty.
-    ///
-    /// For correctness, this *must* be complete (i.e., the vector must contain
-    /// all labels whose offsets are resolved to the current tail), because we
-    /// rely on it to update labels when we truncate branches.
-    labels_at_tail: SmallVec<[MachLabel; 4]>,
-    /// The last offset at which `labels_at_tail` is valid. It is conceptually
-    /// always describing the tail of the buffer, but we do not clear
-    /// `labels_at_tail` eagerly when the tail grows, rather we lazily clear it
-    /// when the offset has grown past this (`labels_at_tail_off`) point.
-    /// Always <= `cur_offset()`.
-    labels_at_tail_off: CodeOffset,
+    /// All labels, in offset order.
+    labels_by_offset: SmallVec<[(MachLabel, CodeOffset); 16]>,
 }
 
 /// A `MachBuffer` once emission is completed: holds generated code and records,
@@ -270,60 +223,10 @@ impl<I: VCodeInst> MachBuffer<I> {
             island_deadline: UNKNOWN_LABEL_OFFSET,
             island_worst_case_size: 0,
             latest_branches: SmallVec::new(),
-            labels_at_tail: SmallVec::new(),
-            labels_at_tail_off: 0,
+            labels_by_offset: SmallVec::new(),
         }
     }
 
-    /// Debug-only: check invariants of labels and branch-records described
-    /// under "Branch-optimization Correctness" above.
-    #[cfg(debug)]
-    fn check_label_branch_invariants(&self) {
-        let cur_off = self.cur_offset();
-        // Check that every entry in latest_branches has *correct*
-        // labels_at_this_branch lists. We do not check completeness because
-        // that would require building a reverse index, which is too slow even
-        // for a debug invariant check.
-        let mut last_end = 0;
-        for b in &self.latest_branches {
-            debug_assert!(b.start < b.end);
-            debug_assert!(b.end <= cur_off);
-            debug_assert!(b.start >= last_end);
-            last_end = b.end;
-            for &l in &b.labels_at_this_branch {
-                debug_assert_eq!(self.resolve_label_offset(l), b.start);
-                debug_assert_eq!(self.label_aliases[l.0 as usize], UNKNOWN_LABEL);
-            }
-        }
-
-        // Check that every label is unresolved, or resolved at or before
-        // cur_offset. If at cur_offset, must be in `labels_at_tail`.
-        for (i, &off) in self.label_offsets.iter().enumerate() {
-            let label = MachLabel(i as u32);
-            debug_assert!(off == UNKNOWN_LABEL_OFFSET || off <= cur_off);
-            if off == cur_off {
-                debug_assert!(
-                    self.labels_at_tail_off == cur_off && self.labels_at_tail.contains(&label)
-                );
-            }
-        }
-
-        // Check that every label in `labels_at_tail_off` is precise, i.e.,
-        // resolves to the cur offset.
-        debug_assert!(self.labels_at_tail_off <= cur_off);
-        if self.labels_at_tail_off == cur_off {
-            for &l in &self.labels_at_tail {
-                debug_assert_eq!(self.resolve_label_offset(l), cur_off);
-                debug_assert_eq!(self.label_aliases[l.0 as usize], UNKNOWN_LABEL);
-            }
-        }
-    }
-
-    #[cfg(not(debug))]
-    fn check_label_branch_invariants(&self) {
-        // Nothing.
-    }
-
     /// Current offset from start of buffer.
     pub fn cur_offset(&self) -> CodeOffset {
         self.data.len() as CodeOffset
@@ -333,15 +236,6 @@ impl<I: VCodeInst> MachBuffer<I> {
     pub fn put1(&mut self, value: u8) {
         trace!("MachBuffer: put byte @ {}: {:x}", self.cur_offset(), value);
         self.data.push(value);
-
-        // Post-invariant: conceptual-labels_at_tail contains a complete and
-        // precise list of labels bound at `cur_offset()`. We have advanced
-        // `cur_offset()`, hence if it had been equal to `labels_at_tail_off`
-        // before, it is not anymore (and it cannot become equal, because
-        // `labels_at_tail_off` is always <= `cur_offset()`). Thus the list is
-        // conceptually empty (even though it is only lazily cleared). No labels
-        // can be bound at this new offset (by invariant on `label_offsets`).
-        // Hence the invariant holds.
     }
 
     /// Add 2 bytes.
@@ -353,8 +247,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         );
         let bytes = value.to_le_bytes();
         self.data.extend_from_slice(&bytes[..]);
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Add 4 bytes.
@@ -366,8 +258,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         );
         let bytes = value.to_le_bytes();
         self.data.extend_from_slice(&bytes[..]);
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Add 8 bytes.
@@ -379,8 +269,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         );
         let bytes = value.to_le_bytes();
         self.data.extend_from_slice(&bytes[..]);
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Add a slice of bytes.
@@ -391,8 +279,6 @@ impl<I: VCodeInst> MachBuffer<I> {
             data.len()
         );
         self.data.extend_from_slice(data);
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Reserve appended space and return a mutable slice referring to it.
@@ -402,8 +288,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         let new_len = self.data.len() + len;
         self.data.resize(new_len, 0);
         &mut self.data[off..]
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Align up to the given alignment.
@@ -413,8 +297,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         while self.cur_offset() & (align_to - 1) != 0 {
             self.put1(0);
         }
-
-        // Post-invariant: as for `put1()`.
     }
 
     /// Allocate a `Label` to refer to some offset. May not be bound to a fixed
@@ -425,9 +307,6 @@ impl<I: VCodeInst> MachBuffer<I> {
         self.label_aliases.push(UNKNOWN_LABEL);
         trace!("MachBuffer: new label -> {:?}", MachLabel(l));
         MachLabel(l)
-
-        // Post-invariant: the only mutation is to add a new label; it has no
-        // bound offset yet, so it trivially satisfies all invariants.
     }
 
     /// Reserve the first N MachLabels for blocks.
@@ -437,50 +316,19 @@ impl<I: VCodeInst> MachBuffer<I> {
         self.label_offsets
             .resize(blocks as usize, UNKNOWN_LABEL_OFFSET);
         self.label_aliases.resize(blocks as usize, UNKNOWN_LABEL);
-
-        // Post-invariant: as for `get_label()`.
     }
 
-    /// Bind a label to the current offset. A label can only be bound once.
+    /// Bind a label to the current offset.
     pub fn bind_label(&mut self, label: MachLabel) {
         trace!(
             "MachBuffer: bind label {:?} at offset {}",
             label,
             self.cur_offset()
         );
-        debug_assert_eq!(self.label_offsets[label.0 as usize], UNKNOWN_LABEL_OFFSET);
-        debug_assert_eq!(self.label_aliases[label.0 as usize], UNKNOWN_LABEL);
         let offset = self.cur_offset();
         self.label_offsets[label.0 as usize] = offset;
-        self.lazily_clear_labels_at_tail();
-        self.labels_at_tail.push(label);
-
-        // Invariants hold: bound offset of label is <= cur_offset (in fact it
-        // is equal). If the `labels_at_tail` list was complete and precise
-        // before, it is still, because we have bound this label to the current
-        // offset and added it to the list (which contains all labels at the
-        // current offset).
-
-        self.check_label_branch_invariants();
+        self.labels_by_offset.push((label, offset));
         self.optimize_branches();
-
-        // Post-invariant: by `optimize_branches()` (see argument there).
-        self.check_label_branch_invariants();
-    }
-
-    /// Lazily clear `labels_at_tail` if the tail offset has moved beyond the
-    /// offset that it applies to.
-    fn lazily_clear_labels_at_tail(&mut self) {
-        let offset = self.cur_offset();
-        if offset > self.labels_at_tail_off {
-            self.labels_at_tail_off = offset;
-            self.labels_at_tail.clear();
-        }
-
-        // Post-invariant: either labels_at_tail_off was at cur_offset, and
-        // state is untouched, or was less than cur_offset, in which case the
-        // labels_at_tail list was conceptually empty, and is now actually
-        // empty.
     }
 
     /// Resolve a label to an offset, if known. May return `UNKNOWN_LABEL_OFFSET`.
@@ -491,16 +339,15 @@ impl<I: VCodeInst> MachBuffer<I> {
         } else {
             self.label_offsets[label.0 as usize]
         }
-
-        // Post-invariant: no mutations.
     }
 
     /// Emit a reference to the given label with the given reference type (i.e.,
     /// branch-instruction format) at the current offset.  This is like a
     /// relocation, but handled internally.
     ///
-    /// This can be called before the branch is actually emitted; fixups will
-    /// not happen until an island is emitted or the buffer is finished.
+    /// Because the offset of the label may already be known and the patch may
+    /// happen immediately, the buffer must already contain bytes at `offset` up
+    /// to `offset + kind.patch_size()`.
     pub fn use_label_at_offset(&mut self, offset: CodeOffset, label: MachLabel, kind: I::LabelUse) {
         trace!(
             "MachBuffer: use_label_at_offset: offset {} label {:?} kind {:?}",
@@ -508,6 +355,7 @@ impl<I: VCodeInst> MachBuffer<I> {
             label,
             kind
         );
+        debug_assert!(offset + kind.patch_size() <= self.cur_offset());
 
         // Add the fixup, and update the worst-case island size based on a
         // veneer for this label use.
@@ -524,51 +372,26 @@ impl<I: VCodeInst> MachBuffer<I> {
         if deadline < self.island_deadline {
             self.island_deadline = deadline;
         }
-
-        // Post-invariant: no mutations to branches/labels data structures.
-        self.check_label_branch_invariants();
     }
 
     /// Inform the buffer of an unconditional branch at the given offset,
     /// targetting the given label. May be used to optimize branches.
     /// The last added label-use must correspond to this branch.
-    /// This must be called when the current offset is equal to `start`; i.e.,
-    /// before actually emitting the branch. This implies that for a branch that
-    /// uses a label and is eligible for optimizations by the MachBuffer, the
-    /// proper sequence is:
-    ///
-    /// - Call `use_label_at_offset()` to emit the fixup record.
-    /// - Call `add_uncond_branch()` to make note of the branch.
-    /// - Emit the bytes for the branch's machine code.
-    ///
-    /// Additional requirement: no labels may be bound between `start` and `end`
-    /// (exclusive on both ends).
     pub fn add_uncond_branch(&mut self, start: CodeOffset, end: CodeOffset, target: MachLabel) {
-        assert!(self.cur_offset() == start);
-        debug_assert!(end > start);
         assert!(!self.fixup_records.is_empty());
         let fixup = self.fixup_records.len() - 1;
-        self.lazily_clear_labels_at_tail();
         self.latest_branches.push(MachBranch {
             start,
             end,
             target,
             fixup,
             inverted: None,
-            labels_at_this_branch: self.labels_at_tail.clone(),
         });
-
-        // Post-invariant: we asserted branch start is current tail; the list of
-        // labels at branch is cloned from list of labels at current tail.
-        self.check_label_branch_invariants();
     }
 
     /// Inform the buffer of a conditional branch at the given offset,
     /// targetting the given label. May be used to optimize branches.
     /// The last added label-use must correspond to this branch.
-    ///
-    /// Additional requirement: no labels may be bound between `start` and `end`
-    /// (exclusive on both ends).
     pub fn add_cond_branch(
         &mut self,
         start: CodeOffset,
@@ -576,127 +399,47 @@ impl<I: VCodeInst> MachBuffer<I> {
         target: MachLabel,
         inverted: &[u8],
     ) {
-        assert!(self.cur_offset() == start);
-        debug_assert!(end > start);
         assert!(!self.fixup_records.is_empty());
-        debug_assert!(inverted.len() == (end - start) as usize);
         let fixup = self.fixup_records.len() - 1;
         let inverted = Some(SmallVec::from(inverted));
-        self.lazily_clear_labels_at_tail();
         self.latest_branches.push(MachBranch {
             start,
             end,
             target,
             fixup,
             inverted,
-            labels_at_this_branch: self.labels_at_tail.clone(),
         });
-
-        // Post-invariant: we asserted branch start is current tail; labels at
-        // branch list is cloned from list of labels at current tail.
-        self.check_label_branch_invariants();
     }
 
     fn truncate_last_branch(&mut self) {
-        self.lazily_clear_labels_at_tail();
-        // Invariants hold at this point.
-
         let b = self.latest_branches.pop().unwrap();
         assert!(b.end == self.cur_offset());
-
-        // State:
-        //    [PRE CODE]
-        //  Offset b.start, b.labels_at_this_branch:
-        //    [BRANCH CODE]
-        //  cur_off, self.labels_at_tail -->
-        //    (end of buffer)
         self.data.truncate(b.start as usize);
         self.fixup_records.truncate(b.fixup);
-        // State:
-        //    [PRE CODE]
-        //  cur_off, Offset b.start, b.labels_at_this_branch:
-        //    (end of buffer)
-        //
-        //  self.labels_at_tail -->  (past end of buffer)
         let cur_off = self.cur_offset();
-        self.labels_at_tail_off = cur_off;
-        // State:
-        //    [PRE CODE]
-        //  cur_off, Offset b.start, b.labels_at_this_branch,
-        //  self.labels_at_tail:
-        //    (end of buffer)
-        //
-        // resolve_label_offset(l) for l in labels_at_tail:
-        //    (past end of buffer)
-
         trace!(
             "truncate_last_branch: truncated {:?}; off now {}",
             b,
             cur_off
         );
-
-        // Fix up resolved label offsets for labels at tail.
-        for &l in &self.labels_at_tail {
-            self.label_offsets[l.0 as usize] = cur_off;
+        for &mut (l, ref mut off) in self.labels_by_offset.iter_mut().rev() {
+            if *off > cur_off {
+                *off = cur_off;
+                trace!(" -> label {:?} reassigned to {}", l, cur_off);
+                self.label_offsets[l.0 as usize] = cur_off;
+            } else {
+                break;
+            }
         }
-        // Old labels_at_this_branch are now at cur_off.
-        self.labels_at_tail
-            .extend(b.labels_at_this_branch.into_iter());
-
-        // Post-invariant: this operation is defined to truncate the buffer,
-        // which moves cur_off backward, and to move labels at the end of the
-        // buffer back to the start-of-branch offset.
-        //
-        // latest_branches satisfies all invariants:
-        // - it has no branches past the end of the buffer (branches are in
-        //   order, we removed the last one, and we truncated the buffer to just
-        //   before the start of that branch)
-        // - no labels were moved to lower offsets than the (new) cur_off, so
-        //   the labels_at_this_branch list for any other branch need not change.
-        //
-        // labels_at_tail satisfies all invariants:
-        // - all labels that were at the tail after the truncated branch are
-        //   moved backward to just before the branch, which becomes the new tail;
-        //   thus every element in the list should remain (ensured by `.extend()`
-        //   above).
-        // - all labels that refer to the new tail, which is the start-offset of
-        //   the truncated branch, must be present. The `labels_at_this_branch`
-        //   list in the truncated branch's record is a complete and precise list
-        //   of exactly these labels; we append these to labels_at_tail.
-        // - labels_at_tail_off is at cur_off after truncation occurs, so the
-        //   list is valid (not to be lazily cleared).
-        //
-        // The stated operation was performed:
-        // - For each label at the end of the buffer prior to this method, it
-        //   now resolves to the new (truncated) end of the buffer: it must have
-        //   been in `labels_at_tail` (this list is precise and complete, and
-        //   the tail was at the end of the truncated branch on entry), and we
-        //   iterate over this list and set `label_offsets` to the new tail.
-        //   None of these labels could have been an alias (by invariant), so
-        //   `label_offsets` is authoritative for each.
-        // - No other labels will be past the end of the buffer, because of the
-        //   requirement that no labels be bound to the middle of branch ranges
-        //   (see comments to `add_{cond,uncond}_branch()`).
-        // - The buffer is truncated to just before the last branch, and the
-        //   fixup record referring to that last branch is removed.
-        self.check_label_branch_invariants();
     }
 
     fn optimize_branches(&mut self) {
-        self.lazily_clear_labels_at_tail();
-        // Invariants valid at this point.
-
         trace!(
             "enter optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
             self.latest_branches,
-            self.labels_at_tail,
+            self.labels_by_offset,
             self.fixup_records
         );
-
-        // We continue to munch on branches at the tail of the buffer until no
-        // more rules apply. Note that the loop only continues if a branch is
-        // actually truncated (or if labels are redirected away from a branch),
-        // so this always makes progress.
         while let Some(b) = self.latest_branches.last() {
             let cur_off = self.cur_offset();
             trace!("optimize_branches: last branch {:?} at off {}", b, cur_off);
@@ -708,141 +451,45 @@ impl<I: VCodeInst> MachBuffer<I> {
                 break;
             }
 
-            // Invariant: we are looking at a branch that ends at the tail of
-            // the buffer.
-
-            // For any branch, conditional or unconditional:
-            // - If the target is a label at the current offset, then remove
-            //   the conditional branch, and reset all labels that targetted
-            //   the current offset (end of branch) to the truncated
-            //   end-of-code.
-            //
-            // Preserves execution semantics: a branch to its own fallthrough
-            // address is equivalent to a no-op; in both cases, nextPC is the
-            // fallthrough.
-            if self.resolve_label_offset(b.target) == cur_off {
-                trace!("branch with target == cur off; truncating");
-                self.truncate_last_branch();
-                continue;
-            }
-
             // If latest is an unconditional branch:
-            //
-            // - If the branch's target is not its own start address, then for
-            //   each label at the start of branch, make the label an alias of the
-            //   branch target, and remove the label from the "labels at this
-            //   branch" list.
-            //
-            //   - Preserves execution semantics: an unconditional branch's
-            //     only effect is to set PC to a new PC; this change simply
-            //     collapses one step in the step-semantics.
-            //
-            //   - Post-invariant: the labels that were bound to the start of
-            //     this branch become aliases, so they must not be present in any
-            //     labels-at-this-branch list or the labels-at-tail list. The
-            //     labels are removed form the latest-branch record's
-            //     labels-at-this-branch list, and are never placed in the
-            //     labels-at-tail list. Furthermore, it is correct that they are
-            //     not in either list, because they are now aliases, and labels
-            //     that are aliases remain aliases forever.
-            //
+            // - For each label at this point, make the label an alias of
+            //   the branch target. We can now assume below that the
+            //   unconditional branch is reachable only via fallthrough, and we
+            //   are free to remove it in an optimization.
             // - If there is a prior unconditional branch that ends just before
-            //   this one begins, and this branch has no labels bound to its
-            //   start, then we can truncate this branch, because it is entirely
-            //   unreachable (we have redirected all labels that make it
-            //   reachable otherwise). Do so and continue around the loop.
-            //
-            //   - Preserves execution semantics: the branch is unreachable,
-            //     because execution can only flow into an instruction from the
-            //     prior instruction's fallthrough or from a branch bound to that
-            //     instruction's start offset. Unconditional branches have no
-            //     fallthrough, so if the prior instruction is an unconditional
-            //     branch, no fallthrough entry can happen. The
-            //     labels-at-this-branch list is complete (by invariant), so if it
-            //     is empty, then the instruction is entirely unreachable. Thus,
-            //     it can be removed.
-            //
-            //   - Post-invariant: ensured by truncate_last_branch().
-            //
+            //   this one begins, then we can truncate this branch, because it is
+            //   entirely unreachable (due to above). Trim the end of the
+            //   `labels_by_offset` array and continue around the loop.
             // - If there is a prior conditional branch whose target label
             //   resolves to the current offset (branches around the
             //   unconditional branch), then remove the unconditional branch,
             //   and make the target of the unconditional the target of the
             //   conditional instead.
-            //
-            //   - Preserves execution semantics: previously we had:
-            //
-            //         L1:
-            //            cond_br L2
-            //            br L3
-            //         L2:
-            //            (end of buffer)
-            //
-            //     by removing the last branch, we have:
-            //
-            //         L1:
-            //            cond_br L2
-            //         L2:
-            //            (end of buffer)
-            //
-            //     we then fix up the records for the conditional branch to
-            //     have:
-            //
-            //         L1:
-            //           cond_br.inverted L3
-            //         L2:
-            //
-            //     In the original code, control flow reaches L2 when the
-            //     conditional branch's predicate is true, and L3 otherwise. In
-            //     the optimized code, the same is true.
-            //
-            //   - Post-invariant: all edits to latest_branches and
-            //     labels_at_tail are performed by `truncate_last_branch()`,
-            //     which maintains the invariants at each step.
-
             if b.is_uncond() {
                 // Set any label equal to current branch's start as an alias of
-                // the branch's target, if the target is not the branch itself
-                // (i.e., an infinite loop).
-                if self.resolve_label_offset(b.target) != b.start {
-                    let redirected = b.labels_at_this_branch.len();
-                    for &l in &b.labels_at_this_branch {
-                        trace!(
-                            " -> label at start of branch {:?} redirected to target {:?}",
-                            l,
-                            b.target
-                        );
-                        self.label_aliases[l.0 as usize] = b.target;
-                        // NOTE: we continue to ensure the invariant that labels
-                        // pointing to tail of buffer are in `labels_at_tail`
-                        // because we already ensured above that the last branch
-                        // cannot have a target of `cur_off`; so we never have
-                        // to put the label into `labels_at_tail` when moving it
-                        // here.
-                    }
-                    // Maintain invariant: all branches have been redirected
-                    // and are no longer pointing at the start of this branch.
-                    let mut_b = self.latest_branches.last_mut().unwrap();
-                    mut_b.labels_at_this_branch.clear();
-
-                    if redirected > 0 {
-                        trace!(" -> after label redirects, restarting loop");
+                // the branch's target.
+                for &(l, off) in self.labels_by_offset.iter().rev() {
+                    trace!(" -> uncond: latest label {:?} at off {}", l, off);
+                    if off > b.start {
                         continue;
+                    } else if off == b.start {
+                        trace!(" -> setting alias to {:?}", b.target);
+                        self.label_aliases[l.0 as usize] = b.target;
+                    } else {
+                        break;
                     }
                 }
-                let b = self.latest_branches.last().unwrap();
+
+                // If the branch target is the next offset,
 
                 // Examine any immediately preceding branch.
                 if self.latest_branches.len() > 1 {
                     let prev_b = &self.latest_branches[self.latest_branches.len() - 2];
                     trace!(" -> more than one branch; prev_b = {:?}", prev_b);
-                    // This uncond is immediately after another uncond; we
-                    // should have already redirected labels to this uncond away
-                    // (but check to be sure); so we can truncate this uncond.
-                    if prev_b.is_uncond()
-                        && prev_b.end == b.start
-                        && b.labels_at_this_branch.is_empty()
-                    {
+                    // This uncond is immediately after another uncond; we've
+                    // already redirected labels to this uncond away; so we can
+                    // truncate this uncond.
+                    if prev_b.is_uncond() && prev_b.end == b.start {
                         trace!(" -> uncond follows another uncond; truncating");
                         self.truncate_last_branch();
                         continue;
@@ -859,38 +506,34 @@ impl<I: VCodeInst> MachBuffer<I> {
                         && self.resolve_label_offset(prev_b.target) == cur_off
                     {
                         trace!(" -> uncond follows a conditional, and conditional's target resolves to current offset");
-                        // Save the target of the uncond (this becomes the
-                        // target of the cond), and truncate the uncond.
                         let target = b.target;
                         let data = prev_b.inverted.clone().unwrap();
                         self.truncate_last_branch();
-
-                        // Mutate the code and cond branch.
-                        let off_before_edit = self.cur_offset();
                         let prev_b = self.latest_branches.last_mut().unwrap();
                         let not_inverted = SmallVec::from(
                             &self.data[(prev_b.start as usize)..(prev_b.end as usize)],
                         );
-
-                        // Low-level edit: replaces bytes of branch with
-                        // inverted form. cur_off remains the same afterward, so
-                        // we do not need to modify label data structures.
                         self.data.truncate(prev_b.start as usize);
                         self.data.extend_from_slice(&data[..]);
-
-                        // Save the original code as the inversion of the
-                        // inverted branch, in case we later edit this branch
-                        // again.
                         prev_b.inverted = Some(not_inverted);
                         self.fixup_records[prev_b.fixup].label = target;
                         trace!(" -> reassigning target of condbr to {:?}", target);
                         prev_b.target = target;
-                        debug_assert_eq!(off_before_edit, self.cur_offset());
                         continue;
                     }
                 }
             }
 
+            // For any branch, conditional or unconditional:
+            // - If the target is a label at the current offset, then remove
+            //   the conditional branch, and reset all labels that targetted
+            //   the current offset (end of branch) to the truncated
+            //   end-of-code.
+            if self.resolve_label_offset(b.target) == cur_off {
+                trace!("branch with target == cur off; truncating");
+                self.truncate_last_branch();
+            }
+
             // If we couldn't do anything with the last branch, then break.
             break;
         }
@@ -900,16 +543,12 @@ impl<I: VCodeInst> MachBuffer<I> {
         trace!(
             "leave optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
             self.latest_branches,
-            self.labels_at_tail,
+            self.labels_by_offset,
             self.fixup_records
         );
     }
 
     fn purge_latest_branches(&mut self) {
-        // All of our branch simplification rules work only if a branch ends at
-        // the tail of the buffer, with no following code; and branches are in
-        // order in latest_branches; so if the last entry ends prior to
-        // cur_offset, then clear all entries.
         let cur_off = self.cur_offset();
         if let Some(l) = self.latest_branches.last() {
             if l.end < cur_off {
@@ -917,10 +556,6 @@ impl<I: VCodeInst> MachBuffer<I> {
                 self.latest_branches.clear();
             }
         }
-
-        // Post-invariant: no invariant requires any branch to appear in
-        // `latest_branches`; it is always optional. The list-clear above thus
-        // preserves all semantics.
     }
 
     /// Emit a constant at some point in the future, binding the given label to
@@ -1294,12 +929,6 @@ struct MachBranch {
     target: MachLabel,
     fixup: usize,
     inverted: Option<SmallVec<[u8; 8]>>,
-    /// All labels pointing to the start of this branch. For correctness, this
-    /// *must* be complete (i.e., must contain all labels whose resolved offsets
-    /// are at the start of this branch): we rely on being able to redirect all
-    /// labels that could jump to this branch before removing it, if it is
-    /// otherwise unreachable.
-    labels_at_this_branch: SmallVec<[MachLabel; 4]>,
 }
 
 impl MachBranch {
diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs
index 8a29b06c0..517c3ac81 100644
--- a/cranelift/codegen/src/machinst/mod.rs
+++ b/cranelift/codegen/src/machinst/mod.rs
@@ -138,7 +138,7 @@ pub trait MachInst: Clone + Debug {
 
     /// Map virtual registers to physical registers using the given virt->phys
     /// maps corresponding to the program points prior to, and after, this instruction.
-    fn map_regs<RUM: RegUsageMapper>(&mut self, maps: &RUM);
+    fn map_regs(&mut self, maps: &RegUsageMapper);
 
     /// If this is a simple move, return the (source, destination) tuple of registers.
     fn is_move(&self) -> Option<(Writable<Reg>, Reg)>;
diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs
index ec28d3d2e..d4c13bff0 100644
--- a/cranelift/codegen/src/machinst/vcode.rs
+++ b/cranelift/codegen/src/machinst/vcode.rs
@@ -457,7 +457,7 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
         insn.get_regs(collector)
     }
 
-    fn map_regs<RUM: RegUsageMapper>(insn: &mut I, mapper: &RUM) {
+    fn map_regs(insn: &mut I, mapper: &RegUsageMapper) {
         insn.map_regs(mapper);
     }
 
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index 861863403..b4ebb8c28 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -17,7 +17,6 @@ test = false
 doctest = false
 
 [dependencies]
-env_logger = "0.7"
 anyhow = "1.0"
 once_cell = "1.3"
 wasmtime = { path = "../wasmtime", default-features = false }
diff --git a/crates/c-api/macros/src/lib.rs b/crates/c-api/macros/src/lib.rs
index 304b95c4e..91ec70253 100644
--- a/crates/c-api/macros/src/lib.rs
+++ b/crates/c-api/macros/src/lib.rs
@@ -72,17 +72,17 @@ pub fn declare_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
 
         #[no_mangle]
         pub extern fn #same(a: &#ty, b: &#ty) -> bool {
-            a.externref().ptr_eq(&b.externref())
+            a.anyref().ptr_eq(&b.anyref())
         }
 
         #[no_mangle]
         pub extern fn #get_host_info(a: &#ty) -> *mut std::os::raw::c_void {
-            crate::r#ref::get_host_info(&a.externref())
+            crate::r#ref::get_host_info(&a.anyref())
         }
 
         #[no_mangle]
         pub extern fn #set_host_info(a: &#ty, info: *mut std::os::raw::c_void) {
-            crate::r#ref::set_host_info(&a.externref(), info, None)
+            crate::r#ref::set_host_info(&a.anyref(), info, None)
         }
 
         #[no_mangle]
@@ -91,12 +91,12 @@ pub fn declare_ref(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
             info: *mut std::os::raw::c_void,
             finalizer: Option<extern "C" fn(*mut std::os::raw::c_void)>,
         ) {
-            crate::r#ref::set_host_info(&a.externref(), info, finalizer)
+            crate::r#ref::set_host_info(&a.anyref(), info, finalizer)
         }
 
         #[no_mangle]
         pub extern fn #as_ref(a: &#ty) -> Box<crate::wasm_ref_t> {
-            let r = a.externref();
+            let r = a.anyref();
             Box::new(crate::wasm_ref_t { r })
         }
 
diff --git a/crates/c-api/src/engine.rs b/crates/c-api/src/engine.rs
index 843c261ea..38f0e985d 100644
--- a/crates/c-api/src/engine.rs
+++ b/crates/c-api/src/engine.rs
@@ -11,16 +11,6 @@ wasmtime_c_api_macros::declare_own!(wasm_engine_t);
 
 #[no_mangle]
 pub extern "C" fn wasm_engine_new() -> Box<wasm_engine_t> {
-    // Enable the `env_logger` crate since this is as good a place as any to
-    // support some "top level initialization" for the C API. Almost all support
-    // should go through this one way or another, so this ensures that
-    // `RUST_LOG` should work reasonably well.
-    //
-    // Note that we `drop` the result here since this fails after the first
-    // initialization attempt. We don't mind that though because this function
-    // can be called multiple times, so we just ignore the result.
-    drop(env_logger::try_init());
-
     Box::new(wasm_engine_t {
         engine: HostRef::new(Engine::default()),
     })
diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs
index 025f23349..b882082fa 100644
--- a/crates/c-api/src/extern.rs
+++ b/crates/c-api/src/extern.rs
@@ -18,12 +18,12 @@ pub(crate) enum ExternHost {
 }
 
 impl wasm_extern_t {
-    fn externref(&self) -> wasmtime::ExternRef {
+    fn anyref(&self) -> wasmtime::AnyRef {
         match &self.which {
-            ExternHost::Func(f) => f.externref(),
-            ExternHost::Global(f) => f.externref(),
-            ExternHost::Memory(f) => f.externref(),
-            ExternHost::Table(f) => f.externref(),
+            ExternHost::Func(f) => f.anyref(),
+            ExternHost::Global(f) => f.anyref(),
+            ExternHost::Memory(f) => f.anyref(),
+            ExternHost::Table(f) => f.anyref(),
         }
     }
 }
diff --git a/crates/c-api/src/func.rs b/crates/c-api/src/func.rs
index 0d3e3d10c..879248b23 100644
--- a/crates/c-api/src/func.rs
+++ b/crates/c-api/src/func.rs
@@ -70,8 +70,8 @@ impl wasm_func_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.func().externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.func().anyref()
     }
 }
 
diff --git a/crates/c-api/src/global.rs b/crates/c-api/src/global.rs
index b7ef6ebd7..fb3f163ba 100644
--- a/crates/c-api/src/global.rs
+++ b/crates/c-api/src/global.rs
@@ -26,8 +26,8 @@ impl wasm_global_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.global().externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.global().anyref()
     }
 }
 
diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs
index 6d528530d..0ac864f5c 100644
--- a/crates/c-api/src/instance.rs
+++ b/crates/c-api/src/instance.rs
@@ -22,8 +22,8 @@ impl wasm_instance_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.instance.externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.instance.anyref()
     }
 }
 
diff --git a/crates/c-api/src/memory.rs b/crates/c-api/src/memory.rs
index 48159b94e..d77f01c4e 100644
--- a/crates/c-api/src/memory.rs
+++ b/crates/c-api/src/memory.rs
@@ -26,8 +26,8 @@ impl wasm_memory_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.memory().externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.memory().anyref()
     }
 }
 
diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs
index 23c5eae36..036363260 100644
--- a/crates/c-api/src/module.rs
+++ b/crates/c-api/src/module.rs
@@ -15,8 +15,8 @@ pub struct wasm_module_t {
 wasmtime_c_api_macros::declare_ref!(wasm_module_t);
 
 impl wasm_module_t {
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.module.externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.module.anyref()
     }
 }
 
diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs
index 00f93865c..4606cc41e 100644
--- a/crates/c-api/src/ref.rs
+++ b/crates/c-api/src/ref.rs
@@ -1,11 +1,11 @@
 use crate::HostInfoState;
 use std::os::raw::c_void;
-use wasmtime::ExternRef;
+use wasmtime::AnyRef;
 
 #[repr(C)]
 #[derive(Clone)]
 pub struct wasm_ref_t {
-    pub(crate) r: ExternRef,
+    pub(crate) r: AnyRef,
 }
 
 wasmtime_c_api_macros::declare_own!(wasm_ref_t);
@@ -20,7 +20,7 @@ pub extern "C" fn wasm_ref_same(a: &wasm_ref_t, b: &wasm_ref_t) -> bool {
     a.r.ptr_eq(&b.r)
 }
 
-pub(crate) fn get_host_info(r: &ExternRef) -> *mut c_void {
+pub(crate) fn get_host_info(r: &AnyRef) -> *mut c_void {
     let host_info = match r.host_info() {
         Some(info) => info,
         None => return std::ptr::null_mut(),
@@ -37,7 +37,7 @@ pub extern "C" fn wasm_ref_get_host_info(a: &wasm_ref_t) -> *mut c_void {
 }
 
 pub(crate) fn set_host_info(
-    r: &ExternRef,
+    r: &AnyRef,
     info: *mut c_void,
     finalizer: Option<extern "C" fn(*mut c_void)>,
 ) {
diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs
index 16626acf7..ab4ae3ff2 100644
--- a/crates/c-api/src/table.rs
+++ b/crates/c-api/src/table.rs
@@ -1,7 +1,7 @@
 use crate::{handle_result, wasm_func_t, wasm_ref_t, wasmtime_error_t};
 use crate::{wasm_extern_t, wasm_store_t, wasm_tabletype_t, ExternHost};
 use std::ptr;
-use wasmtime::{ExternRef, HostRef, Table, Val};
+use wasmtime::{AnyRef, HostRef, Table, Val};
 
 #[derive(Clone)]
 #[repr(transparent)]
@@ -28,8 +28,8 @@ impl wasm_table_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.table().externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.table().anyref()
     }
 }
 
@@ -41,7 +41,7 @@ pub extern "C" fn wasm_table_new(
 ) -> Option<Box<wasm_table_t>> {
     let init: Val = match init {
         Some(init) => init.r.into(),
-        None => Val::ExternRef(ExternRef::Null),
+        None => Val::AnyRef(AnyRef::Null),
     };
     let table = Table::new(&store.store.borrow(), tt.ty().ty.clone(), init).ok()?;
     Some(Box::new(wasm_table_t {
@@ -60,7 +60,7 @@ pub extern "C" fn wasmtime_funcref_table_new(
 ) -> Option<Box<wasmtime_error_t>> {
     let init: Val = match init {
         Some(val) => Val::FuncRef(val.func().borrow().clone()),
-        None => Val::ExternRef(ExternRef::Null),
+        None => Val::AnyRef(AnyRef::Null),
     };
     handle_result(
         Table::new(&store.store.borrow(), tt.ty().ty.clone(), init),
@@ -84,7 +84,7 @@ pub extern "C" fn wasm_table_type(t: &wasm_table_t) -> Box<wasm_tabletype_t> {
 pub extern "C" fn wasm_table_get(t: &wasm_table_t, index: wasm_table_size_t) -> *mut wasm_ref_t {
     match t.table().borrow().get(index) {
         Some(val) => into_funcref(val),
-        None => into_funcref(Val::ExternRef(ExternRef::Null)),
+        None => into_funcref(Val::AnyRef(AnyRef::Null)),
     }
 }
 
@@ -99,7 +99,7 @@ pub extern "C" fn wasmtime_funcref_table_get(
             *ptr = match val {
                 // TODO: what do do about creating new `HostRef` handles here?
                 Val::FuncRef(f) => Box::into_raw(Box::new(HostRef::new(f).into())),
-                Val::ExternRef(ExternRef::Null) => ptr::null_mut(),
+                Val::AnyRef(AnyRef::Null) => ptr::null_mut(),
                 _ => return false,
             };
         }
@@ -127,20 +127,20 @@ pub extern "C" fn wasmtime_funcref_table_set(
 ) -> Option<Box<wasmtime_error_t>> {
     let val = match val {
         Some(val) => Val::FuncRef(val.func().borrow().clone()),
-        None => Val::ExternRef(ExternRef::Null),
+        None => Val::AnyRef(AnyRef::Null),
     };
     handle_result(t.table().borrow().set(index, val), |()| {})
 }
 
 fn into_funcref(val: Val) -> *mut wasm_ref_t {
-    if let Val::ExternRef(ExternRef::Null) = val {
+    if let Val::AnyRef(AnyRef::Null) = val {
         return ptr::null_mut();
     }
-    let externref = match val.externref() {
-        Some(externref) => externref,
+    let anyref = match val.anyref() {
+        Some(anyref) => anyref,
         None => return ptr::null_mut(),
     };
-    let r = Box::new(wasm_ref_t { r: externref });
+    let r = Box::new(wasm_ref_t { r: anyref });
     Box::into_raw(r)
 }
 
@@ -148,7 +148,7 @@ unsafe fn from_funcref(r: *mut wasm_ref_t) -> Val {
     if !r.is_null() {
         Box::from_raw(r).r.into()
     } else {
-        Val::ExternRef(ExternRef::Null)
+        Val::AnyRef(AnyRef::Null)
     }
 }
 
@@ -176,7 +176,7 @@ pub extern "C" fn wasmtime_funcref_table_grow(
 ) -> Option<Box<wasmtime_error_t>> {
     let val = match init {
         Some(val) => Val::FuncRef(val.func().borrow().clone()),
-        None => Val::ExternRef(ExternRef::Null),
+        None => Val::AnyRef(AnyRef::Null),
     };
     handle_result(t.table().borrow().grow(delta, val), |prev| {
         if let Some(ptr) = prev_size {
diff --git a/crates/c-api/src/trap.rs b/crates/c-api/src/trap.rs
index 8a93cba14..67ca334d7 100644
--- a/crates/c-api/src/trap.rs
+++ b/crates/c-api/src/trap.rs
@@ -17,8 +17,8 @@ impl wasm_trap_t {
         }
     }
 
-    fn externref(&self) -> wasmtime::ExternRef {
-        self.trap.externref()
+    fn anyref(&self) -> wasmtime::AnyRef {
+        self.trap.anyref()
     }
 }
 
diff --git a/crates/c-api/src/types/val.rs b/crates/c-api/src/types/val.rs
index a421516b8..5333ec608 100644
--- a/crates/c-api/src/types/val.rs
+++ b/crates/c-api/src/types/val.rs
@@ -13,7 +13,7 @@ pub const WASM_I32: wasm_valkind_t = 0;
 pub const WASM_I64: wasm_valkind_t = 1;
 pub const WASM_F32: wasm_valkind_t = 2;
 pub const WASM_F64: wasm_valkind_t = 3;
-pub const WASM_EXTERNREF: wasm_valkind_t = 128;
+pub const WASM_ANYREF: wasm_valkind_t = 128;
 pub const WASM_FUNCREF: wasm_valkind_t = 129;
 
 #[no_mangle]
@@ -34,7 +34,7 @@ pub(crate) fn into_valtype(kind: wasm_valkind_t) -> ValType {
         WASM_I64 => ValType::I64,
         WASM_F32 => ValType::F32,
         WASM_F64 => ValType::F64,
-        WASM_EXTERNREF => ValType::ExternRef,
+        WASM_ANYREF => ValType::AnyRef,
         WASM_FUNCREF => ValType::FuncRef,
         _ => panic!("unexpected kind: {}", kind),
     }
@@ -46,7 +46,7 @@ pub(crate) fn from_valtype(ty: &ValType) -> wasm_valkind_t {
         ValType::I64 => WASM_I64,
         ValType::F32 => WASM_F32,
         ValType::F64 => WASM_F64,
-        ValType::ExternRef => WASM_EXTERNREF,
+        ValType::AnyRef => WASM_ANYREF,
         ValType::FuncRef => WASM_FUNCREF,
         _ => panic!("wasm_valkind_t has no known conversion for {:?}", ty),
     }
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index a3d960843..a6df5a56c 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -65,7 +65,7 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) {
         Ok(imps) => imps,
         Err(_) => {
             // There are some value types that we can't synthesize a
-            // dummy value for (e.g. externrefs) and for modules that
+            // dummy value for (e.g. anyrefs) and for modules that
             // import things of these types we skip instantiation.
             return;
         }
@@ -147,7 +147,7 @@ pub fn differential_execution(
             Ok(imps) => imps,
             Err(e) => {
                 // There are some value types that we can't synthesize a
-                // dummy value for (e.g. externrefs) and for modules that
+                // dummy value for (e.g. anyrefs) and for modules that
                 // import things of these types we skip instantiation.
                 eprintln!("Warning: failed to synthesize dummy imports: {}", e);
                 continue;
@@ -244,8 +244,9 @@ pub fn differential_execution(
                                 fail()
                             }
                         }
-                        (Val::ExternRef(_), Val::ExternRef(_))
-                        | (Val::FuncRef(_), Val::FuncRef(_)) => continue,
+                        (Val::AnyRef(_), Val::AnyRef(_)) | (Val::FuncRef(_), Val::FuncRef(_)) => {
+                            continue
+                        }
                         _ => fail(),
                     }
                 }
@@ -328,7 +329,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
                     Ok(imps) => imps,
                     Err(_) => {
                         // There are some value types that we can't synthesize a
-                        // dummy value for (e.g. externrefs) and for modules that
+                        // dummy value for (e.g. anyrefs) and for modules that
                         // import things of these types we skip instantiation.
                         continue;
                     }
diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs
index 1dc35ed3a..2e904ec1a 100644
--- a/crates/fuzzing/src/oracles/dummy.rs
+++ b/crates/fuzzing/src/oracles/dummy.rs
@@ -44,9 +44,9 @@ pub fn dummy_value(val_ty: &ValType) -> Result<Val, Trap> {
                 "dummy_value: unsupported function return type: v128".to_string(),
             ))
         }
-        ValType::ExternRef => {
+        ValType::AnyRef => {
             return Err(Trap::new(
-                "dummy_value: unsupported function return type: externref".to_string(),
+                "dummy_value: unsupported function return type: anyref".to_string(),
             ))
         }
         ValType::FuncRef => {
diff --git a/crates/wasi-common/src/handle.rs b/crates/wasi-common/src/handle.rs
index 574db3de1..21a0ab4f2 100644
--- a/crates/wasi-common/src/handle.rs
+++ b/crates/wasi-common/src/handle.rs
@@ -135,19 +135,6 @@ pub(crate) trait Handle {
     fn create_directory(&self, _path: &str) -> Result<()> {
         Err(Errno::Acces)
     }
-    fn filestat_get_at(&self, _path: &str, _follow: bool) -> Result<types::Filestat> {
-        Err(Errno::Acces)
-    }
-    fn filestat_set_times_at(
-        &self,
-        _path: &str,
-        _atim: types::Timestamp,
-        _mtim: types::Timestamp,
-        _fst_flags: types::Fstflags,
-        _follow: bool,
-    ) -> Result<()> {
-        Err(Errno::Acces)
-    }
     fn openat(
         &self,
         _path: &str,
diff --git a/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs b/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs
index 31528beb3..31117243a 100644
--- a/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs
+++ b/crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs
@@ -469,8 +469,15 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
         let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_GET);
         let entry = self.get_entry(dirfd)?;
         let (dirfd, path) = path::get(&entry, &required_rights, flags, path, false)?;
-        let host_filestat =
-            dirfd.filestat_get_at(&path, flags.contains(&types::Lookupflags::SYMLINK_FOLLOW))?;
+        let host_filestat = dirfd
+            .openat(
+                &path,
+                false,
+                false,
+                types::Oflags::empty(),
+                types::Fdflags::empty(),
+            )?
+            .filestat_get()?;
         Ok(host_filestat)
     }
 
@@ -486,13 +493,15 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
         let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_SET_TIMES);
         let entry = self.get_entry(dirfd)?;
         let (dirfd, path) = path::get(&entry, &required_rights, flags, path, false)?;
-        dirfd.filestat_set_times_at(
-            &path,
-            atim,
-            mtim,
-            fst_flags,
-            flags.contains(&types::Lookupflags::SYMLINK_FOLLOW),
-        )?;
+        dirfd
+            .openat(
+                &path,
+                false,
+                false,
+                types::Oflags::empty(),
+                types::Fdflags::empty(),
+            )?
+            .filestat_set_times(atim, mtim, fst_flags)?;
         Ok(())
     }
 
diff --git a/crates/wasi-common/src/sys/osdir.rs b/crates/wasi-common/src/sys/osdir.rs
index b1051b886..29a69e0c1 100644
--- a/crates/wasi-common/src/sys/osdir.rs
+++ b/crates/wasi-common/src/sys/osdir.rs
@@ -69,19 +69,6 @@ impl Handle for OsDir {
     fn create_directory(&self, path: &str) -> Result<()> {
         path::create_directory(self, path)
     }
-    fn filestat_get_at(&self, path: &str, follow: bool) -> Result<types::Filestat> {
-        path::filestat_get_at(self, path, follow)
-    }
-    fn filestat_set_times_at(
-        &self,
-        path: &str,
-        atim: types::Timestamp,
-        mtim: types::Timestamp,
-        fst_flags: types::Fstflags,
-        follow: bool,
-    ) -> Result<()> {
-        path::filestat_set_times_at(self, path, atim, mtim, fst_flags, follow)
-    }
     fn openat(
         &self,
         path: &str,
diff --git a/crates/wasi-common/src/sys/unix/path.rs b/crates/wasi-common/src/sys/unix/path.rs
index e416cdd06..9ffa5f7b6 100644
--- a/crates/wasi-common/src/sys/unix/path.rs
+++ b/crates/wasi-common/src/sys/unix/path.rs
@@ -1,8 +1,7 @@
 use crate::handle::{Handle, HandleRights};
 use crate::sys::osdir::OsDir;
-use crate::sys::AsFile;
 use crate::wasi::{types, Errno, Result};
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryFrom;
 use std::ffi::OsStr;
 use std::fs::File;
 use std::os::unix::prelude::{AsRawFd, FromRawFd, OsStrExt};
@@ -205,57 +204,3 @@ pub(crate) fn remove_directory(dirfd: &OsDir, path: &str) -> Result<()> {
     unsafe { unlinkat(dirfd.as_raw_fd(), path, AtFlag::REMOVEDIR)? };
     Ok(())
 }
-
-pub(crate) fn filestat_get_at(dirfd: &OsDir, path: &str, follow: bool) -> Result<types::Filestat> {
-    use yanix::file::{fstatat, AtFlag};
-    let flags = if follow {
-        AtFlag::empty()
-    } else {
-        AtFlag::SYMLINK_NOFOLLOW
-    };
-    let stat = unsafe { fstatat(dirfd.as_raw_fd(), path, flags)? };
-    let stat = stat.try_into()?;
-    Ok(stat)
-}
-
-pub(crate) fn filestat_set_times_at(
-    dirfd: &OsDir,
-    path: &str,
-    atim: types::Timestamp,
-    mtim: types::Timestamp,
-    fst_flags: types::Fstflags,
-    follow: bool,
-) -> Result<()> {
-    use std::time::{Duration, UNIX_EPOCH};
-    use yanix::filetime::*;
-
-    let set_atim = fst_flags.contains(&types::Fstflags::ATIM);
-    let set_atim_now = fst_flags.contains(&types::Fstflags::ATIM_NOW);
-    let set_mtim = fst_flags.contains(&types::Fstflags::MTIM);
-    let set_mtim_now = fst_flags.contains(&types::Fstflags::MTIM_NOW);
-
-    if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
-        return Err(Errno::Inval);
-    }
-
-    let atim = if set_atim {
-        let time = UNIX_EPOCH + Duration::from_nanos(atim);
-        FileTime::FileTime(filetime::FileTime::from_system_time(time))
-    } else if set_atim_now {
-        FileTime::Now
-    } else {
-        FileTime::Omit
-    };
-    let mtim = if set_mtim {
-        let time = UNIX_EPOCH + Duration::from_nanos(mtim);
-        FileTime::FileTime(filetime::FileTime::from_system_time(time))
-    } else if set_mtim_now {
-        FileTime::Now
-    } else {
-        FileTime::Omit
-    };
-
-    utimensat(&*dirfd.as_file()?, path, atim, mtim, !follow)?;
-
-    Ok(())
-}
diff --git a/crates/wasi-common/src/sys/windows/path.rs b/crates/wasi-common/src/sys/windows/path.rs
index 50a234474..d41086671 100644
--- a/crates/wasi-common/src/sys/windows/path.rs
+++ b/crates/wasi-common/src/sys/windows/path.rs
@@ -1,6 +1,6 @@
 use crate::handle::{Handle, HandleRights};
 use crate::sys::osdir::OsDir;
-use crate::sys::{fd, AsFile};
+use crate::sys::AsFile;
 use crate::wasi::{types, Errno, Result};
 use std::convert::TryFrom;
 use std::ffi::{OsStr, OsString};
@@ -494,42 +494,3 @@ pub(crate) fn remove_directory(dirfd: &OsDir, path: &str) -> Result<()> {
     let path = concatenate(dirfd, path)?;
     std::fs::remove_dir(&path).map_err(Into::into)
 }
-
-pub(crate) fn filestat_get_at(dirfd: &OsDir, path: &str, follow: bool) -> Result<types::Filestat> {
-    use winx::file::Flags;
-    let path = concatenate(dirfd, path)?;
-    let mut opts = OpenOptions::new();
-
-    if !follow {
-        // By specifying FILE_FLAG_OPEN_REPARSE_POINT, we force Windows to *not* dereference symlinks.
-        opts.custom_flags(Flags::FILE_FLAG_OPEN_REPARSE_POINT.bits());
-    }
-
-    let file = opts.read(true).open(path)?;
-    let stat = fd::filestat_get(&file)?;
-    Ok(stat)
-}
-
-pub(crate) fn filestat_set_times_at(
-    dirfd: &OsDir,
-    path: &str,
-    atim: types::Timestamp,
-    mtim: types::Timestamp,
-    fst_flags: types::Fstflags,
-    follow: bool,
-) -> Result<()> {
-    use winx::file::{AccessMode, Flags};
-    let path = concatenate(dirfd, path)?;
-    let mut opts = OpenOptions::new();
-
-    if !follow {
-        // By specifying FILE_FLAG_OPEN_REPARSE_POINT, we force Windows to *not* dereference symlinks.
-        opts.custom_flags(Flags::FILE_FLAG_OPEN_REPARSE_POINT.bits());
-    }
-
-    let file = opts
-        .access_mode(AccessMode::FILE_WRITE_ATTRIBUTES.bits())
-        .open(path)?;
-    fd::filestat_set_times(&file, atim, mtim, fst_flags)?;
-    Ok(())
-}
diff --git a/crates/wasi-common/src/virtfs.rs b/crates/wasi-common/src/virtfs.rs
index 3d9b61b19..c868a2795 100644
--- a/crates/wasi-common/src/virtfs.rs
+++ b/crates/wasi-common/src/virtfs.rs
@@ -600,36 +600,6 @@ impl Handle for VirtualDir {
             }
         }
     }
-    fn filestat_get_at(&self, path: &str, _follow: bool) -> Result<types::Filestat> {
-        let stat = self
-            .openat(
-                path,
-                false,
-                false,
-                types::Oflags::empty(),
-                types::Fdflags::empty(),
-            )?
-            .filestat_get()?;
-        Ok(stat)
-    }
-    fn filestat_set_times_at(
-        &self,
-        path: &str,
-        atim: types::Timestamp,
-        mtim: types::Timestamp,
-        fst_flags: types::Fstflags,
-        _follow: bool,
-    ) -> Result<()> {
-        self.openat(
-            path,
-            false,
-            false,
-            types::Oflags::empty(),
-            types::Fdflags::empty(),
-        )?
-        .filestat_set_times(atim, mtim, fst_flags)?;
-        Ok(())
-    }
     fn openat(
         &self,
         path: &str,
diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index 70bf2843b..dda4fcee7 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -298,7 +298,7 @@ impl Func {
     /// | `f32`              | `f32`            |
     /// | `f64`              | `f64`            |
     /// | (not supported)    | `v128`           |
-    /// | (not supported)    | `externref`         |
+    /// | (not supported)    | `anyref`         |
     ///
     /// Any of the Rust types can be returned from the closure as well, in
     /// addition to some extra types
diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs
index e08fbb15f..034283326 100644
--- a/crates/wasmtime/src/lib.rs
+++ b/crates/wasmtime/src/lib.rs
@@ -29,7 +29,7 @@ pub use crate::func::*;
 pub use crate::instance::Instance;
 pub use crate::linker::*;
 pub use crate::module::Module;
-pub use crate::r#ref::{ExternRef, HostRef};
+pub use crate::r#ref::{AnyRef, HostRef};
 pub use crate::runtime::*;
 pub use crate::trap::Trap;
 pub use crate::types::*;
diff --git a/crates/wasmtime/src/ref.rs b/crates/wasmtime/src/ref.rs
index f78aaf0e9..2083c183d 100644
--- a/crates/wasmtime/src/ref.rs
+++ b/crates/wasmtime/src/ref.rs
@@ -38,7 +38,7 @@ pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);
 
 /// Represents an opaque reference to any data within WebAssembly.
 #[derive(Clone)]
-pub enum ExternRef {
+pub enum AnyRef {
     /// A reference to no data.
     Null,
     /// A reference to data stored internally in `wasmtime`.
@@ -47,54 +47,52 @@ pub enum ExternRef {
     Other(OtherRef),
 }
 
-impl ExternRef {
-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.
+impl AnyRef {
+    /// Creates a new instance of `AnyRef` from `Box<dyn Any>`.
     pub fn new(data: Box<dyn Any>) -> Self {
         let info = AnyAndHostInfo {
             any: data,
             host_info: None,
         };
-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))
+        AnyRef::Other(OtherRef(Rc::new(RefCell::new(info))))
     }
 
     /// Creates a `Null` reference.
     pub fn null() -> Self {
-        ExternRef::Null
+        AnyRef::Null
     }
 
     /// Returns the data stored in the reference if available.
     /// # Panics
-    /// Panics if the variant isn't `ExternRef::Other`.
+    /// Panics if the variant isn't `AnyRef::Other`.
     pub fn data(&self) -> cell::Ref<Box<dyn Any>> {
         match self {
-            ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
-            _ => panic!("expected ExternRef::Other"),
+            AnyRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),
+            _ => panic!("expected AnyRef::Other"),
         }
     }
 
-    /// Returns true if the two `ExternRef<T>`'s point to the same value (not just
+    /// Returns true if the two `AnyRef<T>`'s point to the same value (not just
     /// values that compare as equal).
-    pub fn ptr_eq(&self, other: &ExternRef) -> bool {
+    pub fn ptr_eq(&self, other: &AnyRef) -> bool {
         match (self, other) {
-            (ExternRef::Null, ExternRef::Null) => true,
-            (ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {
+            (AnyRef::Null, AnyRef::Null) => true,
+            (AnyRef::Ref(InternalRef(ref a)), AnyRef::Ref(InternalRef(ref b))) => {
                 a.ptr_eq(b.as_ref())
             }
-            (ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {
-                Rc::ptr_eq(a, b)
-            }
+            (AnyRef::Other(OtherRef(ref a)), AnyRef::Other(OtherRef(ref b))) => Rc::ptr_eq(a, b),
             _ => false,
         }
     }
 
     /// Returns a mutable reference to the host information if available.
     /// # Panics
-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.
+    /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
     pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {
         match self {
-            ExternRef::Null => panic!("null"),
-            ExternRef::Ref(r) => r.0.host_info(),
-            ExternRef::Other(r) => {
+            AnyRef::Null => panic!("null"),
+            AnyRef::Ref(r) => r.0.host_info(),
+            AnyRef::Other(r) => {
                 let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);
                 if info.is_none() {
                     return None;
@@ -104,26 +102,26 @@ impl ExternRef {
         }
     }
 
-    /// Sets the host information for an `ExternRef`.
+    /// Sets the host information for an `AnyRef`.
     /// # Panics
-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.
+    /// Panics if `AnyRef` is already borrowed or `AnyRef` is `Null`.
     pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {
         match self {
-            ExternRef::Null => panic!("null"),
-            ExternRef::Ref(r) => r.0.set_host_info(info),
-            ExternRef::Other(r) => {
+            AnyRef::Null => panic!("null"),
+            AnyRef::Ref(r) => r.0.set_host_info(info),
+            AnyRef::Other(r) => {
                 r.0.borrow_mut().host_info = info;
             }
         }
     }
 }
 
-impl fmt::Debug for ExternRef {
+impl fmt::Debug for AnyRef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            ExternRef::Null => write!(f, "null"),
-            ExternRef::Ref(_) => write!(f, "externref"),
-            ExternRef::Other(_) => write!(f, "other ref"),
+            AnyRef::Null => write!(f, "null"),
+            AnyRef::Ref(_) => write!(f, "anyref"),
+            AnyRef::Other(_) => write!(f, "other ref"),
         }
     }
 }
@@ -131,7 +129,7 @@ impl fmt::Debug for ExternRef {
 struct ContentBox<T> {
     content: T,
     host_info: Option<Box<dyn Any>>,
-    externref_data: Weak<dyn InternalRefBase>,
+    anyref_data: Weak<dyn InternalRefBase>,
 }
 
 /// Represents a piece of data located in the host environment.
@@ -140,11 +138,11 @@ pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);
 impl<T: 'static> HostRef<T> {
     /// Creates a new `HostRef<T>` from `T`.
     pub fn new(item: T) -> HostRef<T> {
-        let externref_data: Weak<HostRef<T>> = Weak::new();
+        let anyref_data: Weak<HostRef<T>> = Weak::new();
         let content = ContentBox {
             content: item,
             host_info: None,
-            externref_data,
+            anyref_data,
         };
         HostRef(Rc::new(RefCell::new(content)))
     }
@@ -170,17 +168,17 @@ impl<T: 'static> HostRef<T> {
     }
 
     /// Returns an opaque reference to the wrapped data in the form of
-    /// an `ExternRef`.
+    /// an `AnyRef`.
     /// # Panics
     /// Panics if `HostRef<T>` is already mutably borrowed.
-    pub fn externref(&self) -> ExternRef {
-        let r = self.0.borrow_mut().externref_data.upgrade();
+    pub fn anyref(&self) -> AnyRef {
+        let r = self.0.borrow_mut().anyref_data.upgrade();
         if let Some(r) = r {
-            return ExternRef::Ref(InternalRef(r));
+            return AnyRef::Ref(InternalRef(r));
         }
-        let externref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
-        self.0.borrow_mut().externref_data = Rc::downgrade(&externref_data);
-        ExternRef::Ref(InternalRef(externref_data))
+        let anyref_data: Rc<dyn InternalRefBase> = Rc::new(self.clone());
+        self.0.borrow_mut().anyref_data = Rc::downgrade(&anyref_data);
+        AnyRef::Ref(InternalRef(anyref_data))
     }
 }
 
diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs
index 2ba9f2fc4..f1a0b7892 100644
--- a/crates/wasmtime/src/runtime.rs
+++ b/crates/wasmtime/src/runtime.rs
@@ -172,7 +172,7 @@ impl Config {
     /// feature can be enabled through this method for appropriate wasm
     /// modules.
     ///
-    /// This feature gates items such as the `externref` type and multiple tables
+    /// This feature gates items such as the `anyref` type and multiple tables
     /// being in a module. Note that enabling the reference types feature will
     /// also enable the bulk memory feature.
     ///
diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs
index 5fe37eeea..8bf966a04 100644
--- a/crates/wasmtime/src/types.rs
+++ b/crates/wasmtime/src/types.rs
@@ -62,7 +62,7 @@ pub enum ValType {
     /// A 128 bit number.
     V128,
     /// A reference to opaque data in the Wasm instance.
-    ExternRef, /* = 128 */
+    AnyRef, /* = 128 */
     /// A reference to a Wasm function.
     FuncRef,
 }
@@ -80,7 +80,7 @@ impl ValType {
     /// Returns true if `ValType` matches either of the reference types.
     pub fn is_ref(&self) -> bool {
         match self {
-            ValType::ExternRef | ValType::FuncRef => true,
+            ValType::AnyRef | ValType::FuncRef => true,
             _ => false,
         }
     }
@@ -321,7 +321,7 @@ impl GlobalType {
 /// A descriptor for a table in a WebAssembly module.
 ///
 /// Tables are contiguous chunks of a specific element, typically a `funcref` or
-/// an `externref`. The most common use for tables is a function table through
+/// an `anyref`. The most common use for tables is a function table through
 /// which `call_indirect` can invoke other functions.
 #[derive(Debug, Clone, Hash, Eq, PartialEq)]
 pub struct TableType {
diff --git a/crates/wasmtime/src/values.rs b/crates/wasmtime/src/values.rs
index c0b8dd810..6eb8004d9 100644
--- a/crates/wasmtime/src/values.rs
+++ b/crates/wasmtime/src/values.rs
@@ -1,4 +1,4 @@
-use crate::r#ref::ExternRef;
+use crate::r#ref::AnyRef;
 use crate::{Func, Store, ValType};
 use anyhow::{bail, Result};
 use std::ptr;
@@ -25,10 +25,10 @@ pub enum Val {
     /// `f64::from_bits` to create an `f64` value.
     F64(u64),
 
-    /// An `externref` value which can hold opaque data to the wasm instance itself.
+    /// An `anyref` value which can hold opaque data to the wasm instance itself.
     ///
     /// Note that this is a nullable value as well.
-    ExternRef(ExternRef),
+    AnyRef(AnyRef),
 
     /// A first-class reference to a WebAssembly function.
     FuncRef(Func),
@@ -62,9 +62,9 @@ macro_rules! accessors {
 }
 
 impl Val {
-    /// Returns a null `externref` value.
+    /// Returns a null `anyref` value.
     pub fn null() -> Val {
-        Val::ExternRef(ExternRef::null())
+        Val::AnyRef(AnyRef::null())
     }
 
     /// Returns the corresponding [`ValType`] for this `Val`.
@@ -74,7 +74,7 @@ impl Val {
             Val::I64(_) => ValType::I64,
             Val::F32(_) => ValType::F32,
             Val::F64(_) => ValType::F64,
-            Val::ExternRef(_) => ValType::ExternRef,
+            Val::AnyRef(_) => ValType::AnyRef,
             Val::FuncRef(_) => ValType::FuncRef,
             Val::V128(_) => ValType::V128,
         }
@@ -115,10 +115,10 @@ impl Val {
     /// Attempt to access the underlying value of this `Val`, returning
     /// `None` if it is not the correct type.
     ///
-    /// This will return `Some` for both the `ExternRef` and `FuncRef` types.
-    pub fn externref(&self) -> Option<ExternRef> {
+    /// This will return `Some` for both the `AnyRef` and `FuncRef` types.
+    pub fn anyref(&self) -> Option<AnyRef> {
         match self {
-            Val::ExternRef(e) => Some(e.clone()),
+            Val::AnyRef(e) => Some(e.clone()),
             _ => None,
         }
     }
@@ -129,8 +129,8 @@ impl Val {
     /// # Panics
     ///
     /// Panics if `self` is not of the right type.
-    pub fn unwrap_externref(&self) -> ExternRef {
-        self.externref().expect("expected externref")
+    pub fn unwrap_anyref(&self) -> AnyRef {
+        self.anyref().expect("expected anyref")
     }
 
     pub(crate) fn comes_from_same_store(&self, store: &Store) -> bool {
@@ -138,10 +138,10 @@ impl Val {
             Val::FuncRef(f) => Store::same(store, f.store()),
 
             // TODO: need to implement this once we actually finalize what
-            // `externref` will look like and it's actually implemented to pass it
+            // `anyref` will look like and it's actually implemented to pass it
             // to compiled wasm as well.
-            Val::ExternRef(ExternRef::Ref(_)) | Val::ExternRef(ExternRef::Other(_)) => false,
-            Val::ExternRef(ExternRef::Null) => true,
+            Val::AnyRef(AnyRef::Ref(_)) | Val::AnyRef(AnyRef::Other(_)) => false,
+            Val::AnyRef(AnyRef::Null) => true,
 
             // Integers have no association with any particular store, so
             // they're always considered as "yes I came from that store",
@@ -174,9 +174,9 @@ impl From<f64> for Val {
     }
 }
 
-impl From<ExternRef> for Val {
-    fn from(val: ExternRef) -> Val {
-        Val::ExternRef(val)
+impl From<AnyRef> for Val {
+    fn from(val: AnyRef) -> Val {
+        Val::AnyRef(val)
     }
 }
 
@@ -194,7 +194,7 @@ pub(crate) fn into_checked_anyfunc(
         bail!("cross-`Store` values are not supported");
     }
     Ok(match val {
-        Val::ExternRef(ExternRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
+        Val::AnyRef(AnyRef::Null) => wasmtime_runtime::VMCallerCheckedAnyfunc {
             func_ptr: ptr::null(),
             type_index: wasmtime_runtime::VMSharedSignatureIndex::default(),
             vmctx: ptr::null_mut(),
@@ -216,7 +216,7 @@ pub(crate) fn from_checked_anyfunc(
     store: &Store,
 ) -> Val {
     if item.type_index == wasmtime_runtime::VMSharedSignatureIndex::default() {
-        return Val::ExternRef(ExternRef::Null);
+        return Val::AnyRef(AnyRef::Null);
     }
     let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
     let export = wasmtime_runtime::ExportFunction {
diff --git a/src/commands/run.rs b/src/commands/run.rs
index e638606b8..d25340209 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -337,8 +337,8 @@ impl RunCommand {
                 Val::I64(i) => println!("{}", i),
                 Val::F32(f) => println!("{}", f),
                 Val::F64(f) => println!("{}", f),
-                Val::ExternRef(_) => println!("<externref>"),
-                Val::FuncRef(_) => println!("<externref>"),
+                Val::AnyRef(_) => println!("<anyref>"),
+                Val::FuncRef(_) => println!("<anyref>"),
                 Val::V128(i) => println!("{}", i),
             }
         }


Test patch:
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
index 55977796c..b948f4fd8 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -2400,6 +2400,46 @@ fn test_aarch64_binemit() {
         "fmadd d15, d30, d31, d1",
     ));
 
+    insns.push((
+        Inst::FpuRRI {
+            fpu_op: FPUOpRI::UShr32(FPURightShiftImm::maybe_from_u8(32, 32).unwrap()),
+            rd: writable_vreg(2),
+            rn: vreg(5),
+        },
+        "A204202F",
+        "ushr v2.2s, v5.2s, #32",
+    ));
+
+    insns.push((
+        Inst::FpuRRI {
+            fpu_op: FPUOpRI::UShr64(FPURightShiftImm::maybe_from_u8(63, 64).unwrap()),
+            rd: writable_vreg(2),
+            rn: vreg(5),
+        },
+        "A204417F",
+        "ushr d2, d5, #63",
+    ));
+
+    insns.push((
+        Inst::FpuRRI {
+            fpu_op: FPUOpRI::Sli32(FPULeftShiftImm::maybe_from_u8(31, 32).unwrap()),
+            rd: writable_vreg(4),
+            rn: vreg(10),
+        },
+        "44553F2F",
+        "sli v4.2s, v10.2s, #31",
+    ));
+
+    insns.push((
+        Inst::FpuRRI {
+            fpu_op: FPUOpRI::Sli64(FPULeftShiftImm::maybe_from_u8(63, 64).unwrap()),
+            rd: writable_vreg(4),
+            rn: vreg(10),
+        },
+        "44557F7F",
+        "sli d4, d10, #63",
+    ));
+
     insns.push((
         Inst::FpuToInt {
             op: FpuToIntOp::F32ToU32,
diff --git a/cranelift/filetests/filetests/vcode/aarch64/floating-point.clif b/cranelift/filetests/filetests/vcode/aarch64/floating-point.clif
index b5d0c3768..6b991026a 100644
--- a/cranelift/filetests/filetests/vcode/aarch64/floating-point.clif
+++ b/cranelift/filetests/filetests/vcode/aarch64/floating-point.clif
@@ -397,12 +397,8 @@ block0(v0: f32, v1: f32):
 
 ; check:  stp fp, lr, [sp, #-16]!
 ; nextln:  mov fp, sp
-; nextln:  mov x0, v0.d[0]
-; nextln:  mov x1, v1.d[0]
-; nextln:  and w1, w1, #2147483648
-; nextln:  bic w0, w0, #2147483648
-; nextln:  orr w0, w0, w1
-; nextln:  mov v0.d[0], x0
+; nextln:  ushr v1.2s, v1.2s, #31
+; nextln:  sli v0.2s, v1.2s, #31
 ; nextln:  mov sp, fp
 ; nextln:  ldp fp, lr, [sp], #16
 ; nextln:  ret
@@ -415,12 +411,8 @@ block0(v0: f64, v1: f64):
 
 ; check:  stp fp, lr, [sp, #-16]!
 ; nextln:  mov fp, sp
-; nextln:  mov x0, v0.d[0]
-; nextln:  mov x1, v1.d[0]
-; nextln:  and x1, x1, #9223372036854775808
-; nextln:  bic x0, x0, #9223372036854775808
-; nextln:  orr x0, x0, x1
-; nextln:  mov v0.d[0], x0
+; nextln:  ushr d1, d1, #63
+; nextln:  sli d0, d1, #63
 ; nextln:  mov sp, fp
 ; nextln:  ldp fp, lr, [sp], #16
 ; nextln:  ret
diff --git a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs
index bef9ff029..af9d19621 100644
--- a/crates/test-programs/wasi-tests/src/bin/path_filestat.rs
+++ b/crates/test-programs/wasi-tests/src/bin/path_filestat.rs
@@ -55,20 +55,31 @@ unsafe fn test_path_filestat(dir_fd: wasi::Fd) {
     assert_eq!(stat.size, 0, "file size should be 0");
 
     // Check path_filestat_set_times
+    let old_atim = stat.atim;
     let new_mtim = stat.mtim - 100;
-    wasi::path_filestat_set_times(dir_fd, 0, "file", 0, new_mtim, wasi::FSTFLAGS_MTIM)
-        .expect("path_filestat_set_times should succeed");
+    wasi::path_filestat_set_times(
+        dir_fd,
+        0,
+        "file",
+        // on purpose: the syscall should not touch atim, because
+        // neither of the ATIM flags is set
+        new_mtim,
+        new_mtim,
+        wasi::FSTFLAGS_MTIM,
+    )
+    .expect("path_filestat_set_times should succeed");
 
     stat = wasi::path_filestat_get(dir_fd, 0, "file")
         .expect("reading file stats after path_filestat_set_times");
     assert_eq!(stat.mtim, new_mtim, "mtim should change");
+    assert_eq!(stat.atim, old_atim, "atim should not change");
 
     assert_eq!(
         wasi::path_filestat_set_times(
             dir_fd,
             0,
             "file",
-            0,
+            new_mtim,
             new_mtim,
             wasi::FSTFLAGS_MTIM | wasi::FSTFLAGS_MTIM_NOW,
         )
@@ -82,14 +93,16 @@ unsafe fn test_path_filestat(dir_fd: wasi::Fd) {
     stat = wasi::path_filestat_get(dir_fd, 0, "file")
         .expect("reading file stats after ERRNO_INVAL fd_filestat_set_times");
     assert_eq!(stat.mtim, new_mtim, "mtim should not change");
+    assert_eq!(stat.atim, old_atim, "atim should not change");
 
+    let new_atim = old_atim - 100;
     assert_eq!(
         wasi::path_filestat_set_times(
             dir_fd,
             0,
             "file",
-            0,
-            0,
+            new_atim,
+            new_atim,
             wasi::FSTFLAGS_ATIM | wasi::FSTFLAGS_ATIM_NOW,
         )
         .expect_err("ATIM & ATIM_NOW can't both be set")
@@ -98,46 +111,14 @@ unsafe fn test_path_filestat(dir_fd: wasi::Fd) {
         "errno should be ERRNO_INVAL"
     );
 
-    // Create a symlink
-    wasi::path_symlink("file", dir_fd, "symlink").expect("creating symlink to a file");
-
-    // Check path_filestat_set_times on the symlink itself
-    let mut sym_stat = wasi::path_filestat_get(dir_fd, 0, "file").expect("reading file stats");
-
-    let sym_new_mtim = sym_stat.mtim - 200;
-    wasi::path_filestat_set_times(dir_fd, 0, "symlink", 0, sym_new_mtim, wasi::FSTFLAGS_MTIM)
-        .expect("path_filestat_set_times should succeed on symlink");
-
-    sym_stat = wasi::path_filestat_get(dir_fd, 0, "symlink")
-        .expect("reading file stats after path_filestat_set_times");
-    assert_eq!(sym_stat.mtim, sym_new_mtim, "mtim should change");
-
-    // Now, dereference the symlink
-    sym_stat = wasi::path_filestat_get(dir_fd, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, "symlink")
-        .expect("reading file stats on the dereferenced symlink");
-    assert_eq!(
-        sym_stat.mtim, stat.mtim,
-        "symlink mtim should be equal to pointee's when dereferenced"
-    );
-
-    // Finally, change stat of the original file by dereferencing the symlink
-    wasi::path_filestat_set_times(
-        dir_fd,
-        wasi::LOOKUPFLAGS_SYMLINK_FOLLOW,
-        "symlink",
-        0,
-        sym_stat.mtim,
-        wasi::FSTFLAGS_MTIM,
-    )
-    .expect("path_filestat_set_times should succeed on setting stat on original file");
-
+    // check if the times were untouched
     stat = wasi::path_filestat_get(dir_fd, 0, "file")
-        .expect("reading file stats after path_filestat_set_times");
-    assert_eq!(stat.mtim, sym_stat.mtim, "mtim should change");
+        .expect("reading file stats after ERRNO_INVAL path_filestat_set_times");
+    assert_eq!(stat.mtim, new_mtim, "mtim should not change");
+    assert_eq!(stat.atim, old_atim, "atim should not change");
 
     wasi::fd_close(file_fd).expect("closing a file");
     wasi::path_unlink_file(dir_fd, "file").expect("removing a file");
-    wasi::path_unlink_file(dir_fd, "symlink").expect("removing a symlink");
 }
 fn main() {
     let mut args = env::args();
diff --git a/crates/wast/src/spectest.rs b/crates/wast/src/spectest.rs
index ed10b7786..60f6a6d09 100644
--- a/crates/wast/src/spectest.rs
+++ b/crates/wast/src/spectest.rs
@@ -35,7 +35,7 @@ pub fn link_spectest(linker: &mut Linker) -> Result<()> {
     linker.define("spectest", "global_f64", g)?;
 
     let ty = TableType::new(ValType::FuncRef, Limits::new(10, Some(20)));
-    let table = Table::new(linker.store(), ty, Val::ExternRef(ExternRef::Null))?;
+    let table = Table::new(linker.store(), ty, Val::AnyRef(AnyRef::Null))?;
     linker.define("spectest", "table", table)?;
 
     let ty = MemoryType::new(Limits::new(1, Some(2)));
diff --git a/tests/all/externals.rs b/tests/all/externals.rs
index 0919f1368..ba9978cbb 100644
--- a/tests/all/externals.rs
+++ b/tests/all/externals.rs
@@ -28,47 +28,27 @@ fn bad_tables() {
 
     // get out of bounds
     let ty = TableType::new(ValType::FuncRef, Limits::new(0, Some(1)));
-    let t = Table::new(
-        &Store::default(),
-        ty.clone(),
-        Val::ExternRef(ExternRef::Null),
-    )
-    .unwrap();
+    let t = Table::new(&Store::default(), ty.clone(), Val::AnyRef(AnyRef::Null)).unwrap();
     assert!(t.get(0).is_none());
     assert!(t.get(u32::max_value()).is_none());
 
     // set out of bounds or wrong type
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
-    let t = Table::new(
-        &Store::default(),
-        ty.clone(),
-        Val::ExternRef(ExternRef::Null),
-    )
-    .unwrap();
+    let t = Table::new(&Store::default(), ty.clone(), Val::AnyRef(AnyRef::Null)).unwrap();
     assert!(t.set(0, Val::I32(0)).is_err());
-    assert!(t.set(0, Val::ExternRef(ExternRef::Null)).is_ok());
-    assert!(t.set(1, Val::ExternRef(ExternRef::Null)).is_err());
+    assert!(t.set(0, Val::AnyRef(AnyRef::Null)).is_ok());
+    assert!(t.set(1, Val::AnyRef(AnyRef::Null)).is_err());
 
     // grow beyond max
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(1)));
-    let t = Table::new(
-        &Store::default(),
-        ty.clone(),
-        Val::ExternRef(ExternRef::Null),
-    )
-    .unwrap();
-    assert!(t.grow(0, Val::ExternRef(ExternRef::Null)).is_ok());
-    assert!(t.grow(1, Val::ExternRef(ExternRef::Null)).is_err());
+    let t = Table::new(&Store::default(), ty.clone(), Val::AnyRef(AnyRef::Null)).unwrap();
+    assert!(t.grow(0, Val::AnyRef(AnyRef::Null)).is_ok());
+    assert!(t.grow(1, Val::AnyRef(AnyRef::Null)).is_err());
     assert_eq!(t.size(), 1);
 
     // grow wrong type
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, Some(2)));
-    let t = Table::new(
-        &Store::default(),
-        ty.clone(),
-        Val::ExternRef(ExternRef::Null),
-    )
-    .unwrap();
+    let t = Table::new(&Store::default(), ty.clone(), Val::AnyRef(AnyRef::Null)).unwrap();
     assert!(t.grow(1, Val::I32(0)).is_err());
     assert_eq!(t.size(), 1);
 }
@@ -88,7 +68,7 @@ fn cross_store() -> anyhow::Result<()> {
     let ty = MemoryType::new(Limits::new(1, None));
     let memory = Memory::new(&store2, ty);
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
-    let table = Table::new(&store2, ty, Val::ExternRef(ExternRef::Null))?;
+    let table = Table::new(&store2, ty, Val::AnyRef(AnyRef::Null))?;
 
     let need_func = Module::new(&store1, r#"(module (import "" "" (func)))"#)?;
     assert!(Instance::new(&need_func, &[func.into()]).is_err());
@@ -125,7 +105,7 @@ fn cross_store() -> anyhow::Result<()> {
 
     // ============ Cross-store funcs ==============
 
-    // TODO: need to actually fill this out once we support externref params/locals
+    // TODO: need to actually fill this out once we support anyref params/locals
     // let module = Module::new(&store1, r#"(module (func (export "a") (param funcref)))"#)?;
 
     Ok(())
diff --git a/tests/all/linker.rs b/tests/all/linker.rs
index b84d7e9c1..efca28526 100644
--- a/tests/all/linker.rs
+++ b/tests/all/linker.rs
@@ -56,11 +56,11 @@ fn link_twice_bad() -> Result<()> {
 
     // tables
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
-    let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?;
+    let table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null))?;
     linker.define("", "", table.clone())?;
     assert!(linker.define("", "", table.clone()).is_err());
     let ty = TableType::new(ValType::FuncRef, Limits::new(2, None));
-    let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?;
+    let table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null))?;
     assert!(linker.define("", "", table.clone()).is_err());
     Ok(())
 }
diff --git a/tests/all/table.rs b/tests/all/table.rs
index f7599e87c..8408cd905 100644
--- a/tests/all/table.rs
+++ b/tests/all/table.rs
@@ -4,9 +4,9 @@ use wasmtime::*;
 fn get_none() {
     let store = Store::default();
     let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
-    let table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null)).unwrap();
+    let table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null)).unwrap();
     match table.get(0) {
-        Some(Val::ExternRef(ExternRef::Null)) => {}
+        Some(Val::AnyRef(AnyRef::Null)) => {}
         _ => panic!(),
     }
     assert!(table.get(1).is_none());
diff --git a/tests/all/use_after_drop.rs b/tests/all/use_after_drop.rs
index 67edfc158..b1f45041c 100644
--- a/tests/all/use_after_drop.rs
+++ b/tests/all/use_after_drop.rs
@@ -11,7 +11,7 @@ fn use_func_after_drop() -> Result<()> {
             assert_eq!(closed_over_data, "abcd");
         });
         let ty = TableType::new(ValType::FuncRef, Limits::new(1, None));
-        table = Table::new(&store, ty, Val::ExternRef(ExternRef::Null))?;
+        table = Table::new(&store, ty, Val::AnyRef(AnyRef::Null))?;
         table.set(0, func.into())?;
     }
     let func = table.get(0).unwrap().funcref().unwrap().clone();