Repository: bytecodealliance/wasmtime
Pull request #1858 (2020-06-10 19:48:30)

Bugfix: scaled addressing mode: round B1 up to one byte.
Issue uncovered by Ben Bouvier during regalloc work.

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->


cfallin: Sure thing, sorry about that -- verified that this case hits the assert without the fix.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index a41b6bf60..8d73830f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -368,7 +368,6 @@ dependencies = [
 name = "cranelift-codegen"
 version = "0.64.0"
 dependencies = [
- "bincode",
  "byteorder",
  "cranelift-bforest",
  "cranelift-codegen-meta",
@@ -1664,13 +1663,12 @@ dependencies = [
 
 [[package]]
 name = "regalloc"
-version = "0.0.26"
+version = "0.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c03092d79e0fd610932d89ed53895a38c0dd3bcd317a0046e69940de32f1d95"
+checksum = "cca5b48c9db66c5ba084e4660b4c0cfe8b551a96074bc04b7c11de86ad0bf1f9"
 dependencies = [
  "log",
  "rustc-hash",
- "serde",
  "smallvec",
 ]
 
@@ -2345,12 +2343,12 @@ version = "0.17.0"
 dependencies = [
  "anyhow",
  "env_logger",
+ "faerie",
  "file-per-thread-logger",
  "filecheck",
  "humantime",
  "libc",
  "more-asserts",
- "object",
  "pretty_env_logger",
  "rayon",
  "structopt",
@@ -2375,9 +2373,9 @@ name = "wasmtime-debug"
 version = "0.17.0"
 dependencies = [
  "anyhow",
+ "faerie",
  "gimli",
  "more-asserts",
- "object",
  "target-lexicon",
  "thiserror",
  "wasmparser 0.57.0",
@@ -2477,8 +2475,8 @@ name = "wasmtime-obj"
 version = "0.17.0"
 dependencies = [
  "anyhow",
+ "faerie",
  "more-asserts",
- "object",
  "wasmtime-environ",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index e419dd425..29aa74217 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,7 +31,7 @@ wasmtime-wast = { path = "crates/wast", version = "0.17.0" }
 wasmtime-wasi = { path = "crates/wasi", version = "0.17.0" }
 wasi-common = { path = "crates/wasi-common", version = "0.17.0" }
 structopt = { version = "0.3.5", features = ["color", "suggestions"] }
-object = { version = "0.18", default-features = false, features = ["write"] }
+faerie = "0.15.0"
 anyhow = "1.0.19"
 target-lexicon = { version = "0.10.0", default-features = false }
 pretty_env_logger = "0.4.0"
diff --git a/build.rs b/build.rs
index 8792f6e4d..59db9b276 100644
--- a/build.rs
+++ b/build.rs
@@ -180,13 +180,11 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
             _ => (),
         },
         "Cranelift" => match (testsuite, testname) {
-            ("simd", "simd_address") => return false,
             ("simd", "simd_i8x16_cmp") => return false,
-            ("simd", "simd_i16x8_cmp") => return false,
-            ("simd", "simd_i32x4_cmp") => return false,
             ("simd", "simd_load_extend") => return false,
-            ("simd", "simd_load_splat") => return false,
             ("simd", "simd_store") => return false,
+            ("simd", "simd_i16x8_cmp") => return false,
+            ("simd", "simd_i32x4_cmp") => return false,
             // Most simd tests are known to fail on aarch64 for now, it's going
             // to be a big chunk of work to implement them all there!
             ("simd", _) if target.contains("aarch64") => return true,
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index e2cce0f85..0e2f5a20f 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -20,13 +20,12 @@ hashbrown = { version = "0.7", optional = true }
 target-lexicon = "0.10"
 log = { version = "0.4.6", default-features = false }
 serde = { version = "1.0.94", features = ["derive"], optional = true }
-bincode = { version = "1.2.1", optional = true }
 gimli = { version = "0.21.0", default-features = false, features = ["write"], optional = true }
 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, version = "0.1.0" }
-regalloc = "0.0.26"
+regalloc = "0.0.25"
 # 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
@@ -75,10 +74,6 @@ all-arch = [
 # For dependent crates that want to serialize some parts of cranelift
 enable-serde = ["serde"]
 
-# Allow snapshotting regalloc test cases. Useful only to report bad register
-# allocation failures, or for regalloc.rs developers.
-regalloc-snapshot = ["bincode", "regalloc/enable-serde"]
-
 # Recompile our optimizations that are written in the `peepmatic` DSL into a
 # compact finite-state transducer automaton.
 rebuild-peephole-optimizers = ["peepmatic"]
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index e59eab630..7d59dcfdc 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -1175,34 +1175,6 @@ impl MachInstEmit for Inst {
                         | machreg_to_gpr(rd.to_reg()),
                 );
             }
-            &Inst::VecDup { rd, rn, ty } => {
-                let imm5 = match ty {
-                    I8 => 0b00001,
-                    I16 => 0b00010,
-                    I32 => 0b00100,
-                    I64 => 0b01000,
-                    _ => unimplemented!(),
-                };
-                sink.put4(
-                    0b010_01110000_00000_000011_00000_00000
-                        | (imm5 << 16)
-                        | (machreg_to_gpr(rn) << 5)
-                        | machreg_to_vec(rd.to_reg()),
-                );
-            }
-            &Inst::VecDupFromFpu { rd, rn, ty } => {
-                let imm5 = match ty {
-                    F32 => 0b00100,
-                    F64 => 0b01000,
-                    _ => unimplemented!(),
-                };
-                sink.put4(
-                    0b010_01110000_00000_000001_00000_00000
-                        | (imm5 << 16)
-                        | (machreg_to_vec(rn) << 5)
-                        | machreg_to_vec(rd.to_reg()),
-                );
-            }
             &Inst::VecExtend { t, rd, rn } => {
                 let (u, immh) = match t {
                     VecExtendOp::Sxtl8 => (0b0, 0b001),
diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
index 20a8225e7..6fea5efb5 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs
@@ -233,6 +233,9 @@ impl UImm12Scaled {
     /// Create a UImm12Scaled from a raw offset and the known scale type, if
     /// possible.
     pub fn maybe_from_i64(value: i64, scale_ty: Type) -> Option<UImm12Scaled> {
+        // Ensure the type is at least one byte.
+        let scale_ty = if scale_ty == B1 { B8 } else { scale_ty };
+
         let scale = scale_ty.bytes();
         assert!(scale.is_power_of_two());
         let scale = scale as i64;
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 1486490c5..efe3fef9e 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -5,8 +5,8 @@
 
 use crate::binemit::CodeOffset;
 use crate::ir::types::{
-    B1, B16, B16X8, B32, B32X4, B64, B64X2, B8, B8X16, F32, F32X2, F32X4, F64, F64X2, FFLAGS, I128,
-    I16, I16X4, I16X8, I32, I32X2, I32X4, I64, I64X2, I8, I8X16, I8X8, IFLAGS,
+    B1, B16, B16X8, B32, B32X4, B64, B64X2, B8, B8X16, F32, F32X2, F64, FFLAGS, I128, I16, I16X4,
+    I16X8, I32, I32X2, I32X4, I64, I64X2, I8, I8X16, I8X8, IFLAGS,
 };
 use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode, Type};
 use crate::machinst::*;
@@ -695,20 +695,6 @@ pub enum Inst {
         ty: Type,
     },
 
-    /// Duplicate general-purpose register to vector.
-    VecDup {
-        rd: Writable<Reg>,
-        rn: Reg,
-        ty: Type,
-    },
-
-    /// Duplicate scalar to vector.
-    VecDupFromFpu {
-        rd: Writable<Reg>,
-        rn: Reg,
-        ty: Type,
-    },
-
     /// Vector extend.
     VecExtend {
         t: VecExtendOp,
@@ -1261,14 +1247,6 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
             collector.add_def(rd);
             collector.add_use(rn);
         }
-        &Inst::VecDup { rd, rn, .. } => {
-            collector.add_def(rd);
-            collector.add_use(rn);
-        }
-        &Inst::VecDupFromFpu { rd, rn, .. } => {
-            collector.add_def(rd);
-            collector.add_use(rn);
-        }
         &Inst::VecExtend { rd, rn, .. } => {
             collector.add_def(rd);
             collector.add_use(rn);
@@ -1826,22 +1804,6 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
             map_def(mapper, rd);
             map_use(mapper, rn);
         }
-        &mut Inst::VecDup {
-            ref mut rd,
-            ref mut rn,
-            ..
-        } => {
-            map_def(mapper, rd);
-            map_use(mapper, rn);
-        }
-        &mut Inst::VecDupFromFpu {
-            ref mut rd,
-            ref mut rn,
-            ..
-        } => {
-            map_def(mapper, rd);
-            map_use(mapper, rn);
-        }
         &mut Inst::VecExtend {
             ref mut rd,
             ref mut rn,
@@ -2621,28 +2583,6 @@ impl ShowWithRRU for Inst {
                 let rn = show_vreg_element(rn, mb_rru, idx, ty);
                 format!("{} {}, {}", op, rd, rn)
             }
-            &Inst::VecDup { rd, rn, ty } => {
-                let vector_type = match ty {
-                    I8 => I8X16,
-                    I16 => I16X8,
-                    I32 => I32X4,
-                    I64 => I64X2,
-                    _ => unimplemented!(),
-                };
-                let rd = show_vreg_vector(rd.to_reg(), mb_rru, vector_type);
-                let rn = show_ireg_sized(rn, mb_rru, InstSize::from_ty(ty));
-                format!("dup {}, {}", rd, rn)
-            }
-            &Inst::VecDupFromFpu { rd, rn, ty } => {
-                let vector_type = match ty {
-                    F32 => F32X4,
-                    F64 => F64X2,
-                    _ => unimplemented!(),
-                };
-                let rd = show_vreg_vector(rd.to_reg(), mb_rru, vector_type);
-                let rn = show_vreg_element(rn, mb_rru, 0, ty);
-                format!("dup {}, {}", rd, rn)
-            }
             &Inst::VecExtend { t, rd, rn } => {
                 let (op, dest, src) = match t {
                     VecExtendOp::Sxtl8 => ("sxtl", I16X8, I8X8),
diff --git a/cranelift/codegen/src/isa/aarch64/inst/regs.rs b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
index 9d7466125..6cd47e588 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
@@ -319,15 +319,13 @@ pub fn show_vreg_vector(reg: Reg, mb_rru: Option<&RealRegUniverse>, ty: Type) ->
     let mut s = reg.show_rru(mb_rru);
 
     match ty {
+        I8X16 => s.push_str(".16b"),
+        I16X8 => s.push_str(".8h"),
+        I32X4 => s.push_str(".4s"),
         F32X2 => s.push_str(".2s"),
-        F32X4 => s.push_str(".4s"),
-        F64X2 => s.push_str(".2d"),
         I8X8 => s.push_str(".8b"),
-        I8X16 => s.push_str(".16b"),
         I16X4 => s.push_str(".4h"),
-        I16X8 => s.push_str(".8h"),
         I32X2 => s.push_str(".2s"),
-        I32X4 => s.push_str(".4s"),
         I64X2 => s.push_str(".2d"),
         _ => unimplemented!(),
     }
diff --git a/cranelift/codegen/src/isa/aarch64/lower_inst.rs b/cranelift/codegen/src/isa/aarch64/lower_inst.rs
index 391773c47..ee5c0a999 100644
--- a/cranelift/codegen/src/isa/aarch64/lower_inst.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower_inst.rs
@@ -1483,24 +1483,13 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
             }
         }
 
-        Opcode::Splat => {
-            let rn = input_to_reg(ctx, inputs[0], NarrowValueMode::None);
-            let rd = output_to_reg(ctx, outputs[0]);
-            let ty = ctx.input_ty(insn, 0);
-            let inst = if ty_is_int(ty) {
-                Inst::VecDup { rd, rn, ty }
-            } else {
-                Inst::VecDupFromFpu { rd, rn, ty }
-            };
-            ctx.emit(inst);
-        }
-
         Opcode::Shuffle
         | Opcode::Vsplit
         | Opcode::Vconcat
         | Opcode::Vselect
         | Opcode::VanyTrue
         | Opcode::VallTrue
+        | Opcode::Splat
         | Opcode::Insertlane
         | Opcode::ScalarToVector
         | Opcode::Swizzle
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 2505286e0..4ba75e394 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -48,7 +48,7 @@ pub(crate) struct X64ABIBody {
     flags: settings::Flags,
 }
 
-fn use_int_reg(ty: types::Type) -> bool {
+fn in_int_reg(ty: types::Type) -> bool {
     match ty {
         types::I8
         | types::I16
@@ -63,13 +63,6 @@ fn use_int_reg(ty: types::Type) -> bool {
     }
 }
 
-fn use_flt_reg(ty: types::Type) -> bool {
-    match ty {
-        types::F32 | types::F64 => true,
-        _ => false,
-    }
-}
-
 fn get_intreg_for_arg_systemv(idx: usize) -> Option<Reg> {
     match idx {
         0 => Some(regs::rdi()),
@@ -82,20 +75,6 @@ fn get_intreg_for_arg_systemv(idx: usize) -> Option<Reg> {
     }
 }
 
-fn get_fltreg_for_arg_systemv(idx: usize) -> Option<Reg> {
-    match idx {
-        0 => Some(regs::xmm0()),
-        1 => Some(regs::xmm1()),
-        2 => Some(regs::xmm2()),
-        3 => Some(regs::xmm3()),
-        4 => Some(regs::xmm4()),
-        5 => Some(regs::xmm5()),
-        6 => Some(regs::xmm6()),
-        7 => Some(regs::xmm7()),
-        _ => None,
-    }
-}
-
 fn get_intreg_for_retval_systemv(idx: usize) -> Option<Reg> {
     match idx {
         0 => Some(regs::rax()),
@@ -104,14 +83,6 @@ fn get_intreg_for_retval_systemv(idx: usize) -> Option<Reg> {
     }
 }
 
-fn get_fltreg_for_retval_systemv(idx: usize) -> Option<Reg> {
-    match idx {
-        0 => Some(regs::xmm0()),
-        1 => Some(regs::xmm1()),
-        _ => None,
-    }
-}
-
 fn is_callee_save_systemv(r: RealReg) -> bool {
     use regs::*;
     match r.get_class() {
@@ -119,7 +90,6 @@ fn is_callee_save_systemv(r: RealReg) -> bool {
             ENC_RBX | ENC_RBP | ENC_R12 | ENC_R13 | ENC_R14 | ENC_R15 => true,
             _ => false,
         },
-        RegClass::V128 => false,
         _ => unimplemented!(),
     }
 }
@@ -136,7 +106,6 @@ impl X64ABIBody {
         // Compute args and retvals from signature.
         let mut args = vec![];
         let mut next_int_arg = 0;
-        let mut next_flt_arg = 0;
         for param in &f.signature.params {
             match param.purpose {
                 ir::ArgumentPurpose::VMContext if f.signature.call_conv.extends_baldrdash() => {
@@ -145,22 +114,15 @@ impl X64ABIBody {
                 }
 
                 ir::ArgumentPurpose::Normal | ir::ArgumentPurpose::VMContext => {
-                    if use_int_reg(param.value_type) {
+                    if in_int_reg(param.value_type) {
                         if let Some(reg) = get_intreg_for_arg_systemv(next_int_arg) {
                             args.push(ABIArg::Reg(reg.to_real_reg()));
                         } else {
                             unimplemented!("passing arg on the stack");
                         }
                         next_int_arg += 1;
-                    } else if use_flt_reg(param.value_type) {
-                        if let Some(reg) = get_fltreg_for_arg_systemv(next_flt_arg) {
-                            args.push(ABIArg::Reg(reg.to_real_reg()));
-                        } else {
-                            unimplemented!("passing arg on the stack");
-                        }
-                        next_flt_arg += 1;
                     } else {
-                        unimplemented!("non int normal register {:?}", param.value_type)
+                        unimplemented!("non int normal register")
                     }
                 }
 
@@ -170,24 +132,16 @@ impl X64ABIBody {
 
         let mut rets = vec![];
         let mut next_int_retval = 0;
-        let mut next_flt_retval = 0;
         for ret in &f.signature.returns {
             match ret.purpose {
                 ir::ArgumentPurpose::Normal => {
-                    if use_int_reg(ret.value_type) {
+                    if in_int_reg(ret.value_type) {
                         if let Some(reg) = get_intreg_for_retval_systemv(next_int_retval) {
                             rets.push(ABIRet::Reg(reg.to_real_reg()));
                         } else {
                             unimplemented!("passing return on the stack");
                         }
                         next_int_retval += 1;
-                    } else if use_flt_reg(ret.value_type) {
-                        if let Some(reg) = get_fltreg_for_retval_systemv(next_flt_retval) {
-                            rets.push(ABIRet::Reg(reg.to_real_reg()));
-                        } else {
-                            unimplemented!("passing return on the stack");
-                        }
-                        next_flt_retval += 1;
                     } else {
                         unimplemented!("returning non integer normal value");
                     }
@@ -278,11 +232,8 @@ impl ABIBody for X64ABIBody {
                 if from_reg.get_class() == RegClass::I32 || from_reg.get_class() == RegClass::I64 {
                     // TODO do we need a sign extension if it's I32?
                     return Inst::mov_r_r(/*is64=*/ true, from_reg.to_reg(), to_reg);
-                } else if from_reg.get_class() == RegClass::V128 {
-                    // TODO: How to support Movss. Should is64 always be true?
-                    return Inst::xmm_r_r(SSE_Op::SSE2_Movsd, from_reg.to_reg(), to_reg);
                 }
-                unimplemented!("moving from non-int arg to vreg {:?}", from_reg.get_class());
+                unimplemented!("moving from non-int arg to vreg");
             }
             ABIArg::_Stack => unimplemented!("moving from stack arg to vreg"),
         }
@@ -315,16 +266,8 @@ impl ABIBody for X64ABIBody {
                         from_reg.to_reg(),
                         Writable::<Reg>::from_reg(to_reg.to_reg()),
                     ))
-                } else if to_reg.get_class() == RegClass::V128
-                    || to_reg.get_class() == RegClass::V128
-                {
-                    ret.push(Inst::xmm_r_r(
-                        SSE_Op::SSE2_Movsd,
-                        from_reg.to_reg(),
-                        Writable::<Reg>::from_reg(to_reg.to_reg()),
-                    ))
                 } else {
-                    unimplemented!("moving from vreg to unsupported return value");
+                    unimplemented!("moving from vreg to non-int return value");
                 }
             }
 
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index 6f4b52156..1e77dd91f 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -144,7 +144,7 @@ impl RM {
     // Constructors.
 
     pub(crate) fn reg(reg: Reg) -> Self {
-        debug_assert!(reg.get_class() == RegClass::I64 || reg.get_class() == RegClass::V128);
+        debug_assert!(reg.get_class() == RegClass::I64);
         RM::R { reg }
     }
 
@@ -205,75 +205,8 @@ impl fmt::Debug for RMI_R_Op {
     }
 }
 
-/// Some scalar SSE operations requiring 2 operands r/m and r
-/// Each instruction is prefixed with the SSE version that introduced
-/// the particular instructions.
-/// TODO: Below only includes scalar operations. To be seen if packed will
-/// be added here.
-#[derive(Clone, PartialEq)]
-pub enum SSE_Op {
-    SSE_Addss,
-    SSE2_Addsd,
-    SSE_Comiss,
-    SSE2_Comisd,
-    SSE2_Cvtsd2ss,
-    SSE2_Cvtsd2si,
-    SSE_Cvtsi2ss,
-    SSE2_Cvtsi2sd,
-    SSE_Cvtss2si,
-    SSE2_Cvtss2sd,
-    SSE_Cvttss2si,
-    SSE2_Cvttsd2si,
-    SSE_Divss,
-    SSE2_Divsd,
-    SSE_Maxss,
-    SSE2_Maxsd,
-    SSE_Minss,
-    SSE2_Minsd,
-    SSE_Movss,
-    SSE2_Movsd,
-    SSE_Mulss,
-    SSE2_Mulsd,
-    SSE_Rcpss,
-    SSE41_Roundss,
-    SSE41_Roundsd,
-    SSE_Rsqrtss,
-    SSE_Sqrtss,
-    SSE2_Sqrtsd,
-    SSE_Subss,
-    SSE2_Subsd,
-    SSE_Ucomiss,
-    SSE2_Ucomisd,
-}
-
-/// Some SSE operations requiring 3 operands i, r/m, and r
-#[derive(Clone, PartialEq)]
-pub enum SSE_RMI_Op {
-    SSE_Cmpss,
-    SSE2_Cmpsd,
-    SSE41_Insertps,
-}
-
-impl SSE_Op {
-    pub(crate) fn to_string(&self) -> String {
-        match self {
-            SSE_Op::SSE_Addss => "addss".to_string(),
-            SSE_Op::SSE_Subss => "subss".to_string(),
-            SSE_Op::SSE_Movss => "movss".to_string(),
-            SSE_Op::SSE2_Movsd => "movsd".to_string(),
-            _ => "unimplemented sse_op".to_string(),
-        }
-    }
-}
-
-impl fmt::Debug for SSE_Op {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "{}", self.to_string())
-    }
-}
-
-/// These indicate ways of extending (widening) a value, using the Intel
-/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
+/// These indicate ways of extending (widening) a value, using the Intel naming:
+/// B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
 #[derive(Clone, PartialEq)]
 pub enum ExtMode {
     /// Byte -> Longword.
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index 77ed7fcb4..b19013dc5 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1,5 +1,6 @@
+use regalloc::{Reg, RegClass};
+
 use crate::isa::x64::inst::*;
-use regalloc::Reg;
 
 fn low8willSXto64(x: u32) -> bool {
     let xs = (x as i32) as i64;
@@ -41,6 +42,7 @@ fn mkSIB(shift: u8, encIndex: u8, encBase: u8) -> u8 {
 #[inline(always)]
 fn iregEnc(reg: Reg) -> u8 {
     debug_assert!(reg.is_real());
+    debug_assert!(reg.get_class() == RegClass::I64);
     reg.get_hw_encoding()
 }
 
@@ -55,16 +57,10 @@ const F_RETAIN_REDUNDANT_REX: u32 = 1;
 /// indicating a 64-bit operation.
 const F_CLEAR_REX_W: u32 = 2;
 
-/// For specifying the legacy prefixes (or `PfxNone` if no prefix required) to
-/// be used at the start an instruction. A select prefix may be required for
-/// various operations, including instructions that operate on GPR, SSE, and Vex
-/// registers.
-enum LegacyPrefix {
-    PfxNone,
-    Pfx66,
-    PfxF2,
-    PfxF3,
-}
+/// Add an 0x66 (operand-size override) prefix.  This is necessary to indicate
+/// a 16-bit operation.  Normally this will be used together with F_CLEAR_REX_W.
+const F_PREFIX_66: u32 = 4;
+
 /// This is the core 'emit' function for instructions that reference memory.
 ///
 /// For an instruction that has as operands a register `encG` and a memory
@@ -86,7 +82,6 @@ enum LegacyPrefix {
 /// indicate a 64-bit operation.
 fn emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
     sink: &mut MachBuffer<Inst>,
-    prefix: LegacyPrefix,
     opcodes: u32,
     mut numOpcodes: usize,
     encG: u8,
@@ -96,15 +91,13 @@ fn emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
     // General comment for this function: the registers in `memE` must be
     // 64-bit integer registers, because they are part of an address
     // expression.  But `encG` can be derived from a register of any class.
+    let prefix66 = (flags & F_PREFIX_66) != 0;
     let clearRexW = (flags & F_CLEAR_REX_W) != 0;
     let retainRedundant = (flags & F_RETAIN_REDUNDANT_REX) != 0;
-
-    // Lower the prefix if applicable.
-    match prefix {
-        LegacyPrefix::Pfx66 => sink.put1(0x66),
-        LegacyPrefix::PfxF2 => sink.put1(0xF2),
-        LegacyPrefix::PfxF3 => sink.put1(0xF3),
-        LegacyPrefix::PfxNone => (),
+    // The operand-size override, if requested.  This indicates a 16-bit
+    // operation.
+    if prefix66 {
+        sink.put1(0x66);
     }
     match memE {
         Addr::IR { simm32, base: regE } => {
@@ -208,7 +201,6 @@ fn emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
 /// simpler.
 fn emit_REX_OPCODES_MODRM_encG_encE(
     sink: &mut MachBuffer<Inst>,
-    prefix: LegacyPrefix,
     opcodes: u32,
     mut numOpcodes: usize,
     encG: u8,
@@ -219,28 +211,22 @@ fn emit_REX_OPCODES_MODRM_encG_encE(
     // don't even have to be from the same class.  For example, for an
     // integer-to-FP conversion insn, one might be RegClass::I64 and the other
     // RegClass::V128.
+    let prefix66 = (flags & F_PREFIX_66) != 0;
     let clearRexW = (flags & F_CLEAR_REX_W) != 0;
     let retainRedundant = (flags & F_RETAIN_REDUNDANT_REX) != 0;
-
     // The operand-size override
-    match prefix {
-        LegacyPrefix::Pfx66 => sink.put1(0x66),
-        LegacyPrefix::PfxF2 => sink.put1(0xF2),
-        LegacyPrefix::PfxF3 => sink.put1(0xF3),
-        LegacyPrefix::PfxNone => (),
+    if prefix66 {
+        sink.put1(0x66);
     }
-
     // The rex byte
     let w = if clearRexW { 0 } else { 1 };
     let r = (encG >> 3) & 1;
     let x = 0;
     let b = (encE >> 3) & 1;
     let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
-
     if rex != 0x40 || retainRedundant {
         sink.put1(rex);
     }
-
     // All other prefixes and opcodes
     while numOpcodes > 0 {
         numOpcodes -= 1;
@@ -256,7 +242,6 @@ fn emit_REX_OPCODES_MODRM_encG_encE(
 
 fn emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
     sink: &mut MachBuffer<Inst>,
-    prefix: LegacyPrefix,
     opcodes: u32,
     numOpcodes: usize,
     regG: Reg,
@@ -265,12 +250,11 @@ fn emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
 ) {
     // JRS FIXME 2020Feb07: this should really just be `regEnc` not `iregEnc`
     let encG = iregEnc(regG);
-    emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(sink, prefix, opcodes, numOpcodes, encG, memE, flags);
+    emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(sink, opcodes, numOpcodes, encG, memE, flags);
 }
 
 fn emit_REX_OPCODES_MODRM_regG_regE(
     sink: &mut MachBuffer<Inst>,
-    prefix: LegacyPrefix,
     opcodes: u32,
     numOpcodes: usize,
     regG: Reg,
@@ -280,7 +264,7 @@ fn emit_REX_OPCODES_MODRM_regG_regE(
     // JRS FIXME 2020Feb07: these should really just be `regEnc` not `iregEnc`
     let encG = iregEnc(regG);
     let encE = iregEnc(regE);
-    emit_REX_OPCODES_MODRM_encG_encE(sink, prefix, opcodes, numOpcodes, encG, encE, flags);
+    emit_REX_OPCODES_MODRM_encG_encE(sink, opcodes, numOpcodes, encG, encE, flags);
 }
 
 /// Write a suitable number of bits from an imm64 to the sink.
@@ -362,7 +346,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     RMI::R { reg: regE } => {
                         emit_REX_OPCODES_MODRM_regG_regE(
                             sink,
-                            LegacyPrefix::PfxNone,
                             0x0FAF,
                             2,
                             regG.to_reg(),
@@ -373,7 +356,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     RMI::M { addr } => {
                         emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                             sink,
-                            LegacyPrefix::PfxNone,
                             0x0FAF,
                             2,
                             regG.to_reg(),
@@ -387,7 +369,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                         // Yes, really, regG twice.
                         emit_REX_OPCODES_MODRM_regG_regE(
                             sink,
-                            LegacyPrefix::PfxNone,
                             opcode,
                             1,
                             regG.to_reg(),
@@ -421,7 +402,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                         // gold standard.
                         emit_REX_OPCODES_MODRM_regG_regE(
                             sink,
-                            LegacyPrefix::PfxNone,
                             opcode_R,
                             1,
                             *regE,
@@ -435,7 +415,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                         // Whereas here we revert to the "normal" G-E ordering.
                         emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                             sink,
-                            LegacyPrefix::PfxNone,
                             opcode_M,
                             1,
                             regG.to_reg(),
@@ -448,15 +427,7 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                         let opcode = if useImm8 { 0x83 } else { 0x81 };
                         // And also here we use the "normal" G-E ordering.
                         let encG = iregEnc(regG.to_reg());
-                        emit_REX_OPCODES_MODRM_encG_encE(
-                            sink,
-                            LegacyPrefix::PfxNone,
-                            opcode,
-                            1,
-                            subopcode_I,
-                            encG,
-                            flags,
-                        );
+                        emit_REX_OPCODES_MODRM_encG_encE(sink, opcode, 1, subopcode_I, encG, flags);
                         emit_simm(sink, if useImm8 { 1 } else { 4 }, *simm32);
                     }
                 }
@@ -484,15 +455,7 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
         }
         Inst::Mov_R_R { is_64, src, dst } => {
             let flags = if *is_64 { F_NONE } else { F_CLEAR_REX_W };
-            emit_REX_OPCODES_MODRM_regG_regE(
-                sink,
-                LegacyPrefix::PfxNone,
-                0x89,
-                1,
-                *src,
-                dst.to_reg(),
-                flags,
-            );
+            emit_REX_OPCODES_MODRM_regG_regE(sink, 0x89, 1, *src, dst.to_reg(), flags);
         }
         Inst::MovZX_M_R { extMode, addr, dst } => {
             match extMode {
@@ -500,7 +463,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVZBL is (REX.W==0) 0F B6 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FB6,
                         2,
                         dst.to_reg(),
@@ -516,7 +478,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // the upper half of the destination anyway.
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FB6,
                         2,
                         dst.to_reg(),
@@ -528,7 +489,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVZWL is (REX.W==0) 0F B7 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FB7,
                         2,
                         dst.to_reg(),
@@ -540,7 +500,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVZWQ is (REX.W==1) 0F B7 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FB7,
                         2,
                         dst.to_reg(),
@@ -554,7 +513,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOV r/m32, r32 is (REX.W==0) 8B /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x8B,
                         1,
                         dst.to_reg(),
@@ -564,22 +522,15 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 }
             }
         }
-        Inst::Mov64_M_R { addr, dst } => emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
-            sink,
-            LegacyPrefix::PfxNone,
-            0x8B,
-            1,
-            dst.to_reg(),
-            addr,
-            F_NONE,
-        ),
+        Inst::Mov64_M_R { addr, dst } => {
+            emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(sink, 0x8B, 1, dst.to_reg(), addr, F_NONE)
+        }
         Inst::MovSX_M_R { extMode, addr, dst } => {
             match extMode {
                 ExtMode::BL => {
                     // MOVSBL is (REX.W==0) 0F BE /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FBE,
                         2,
                         dst.to_reg(),
@@ -591,7 +542,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVSBQ is (REX.W==1) 0F BE /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FBE,
                         2,
                         dst.to_reg(),
@@ -603,7 +553,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVSWL is (REX.W==0) 0F BF /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FBF,
                         2,
                         dst.to_reg(),
@@ -615,7 +564,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVSWQ is (REX.W==1) 0F BF /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x0FBF,
                         2,
                         dst.to_reg(),
@@ -627,7 +575,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOVSLQ is (REX.W==1) 63 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x63,
                         1,
                         dst.to_reg(),
@@ -652,7 +599,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOV r8, r/m8 is (REX.W==0) 88 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x88,
                         1,
                         *src,
@@ -664,19 +610,17 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // MOV r16, r/m16 is 66 (REX.W==0) 89 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::Pfx66,
                         0x89,
                         1,
                         *src,
                         addr,
-                        F_CLEAR_REX_W,
+                        F_CLEAR_REX_W | F_PREFIX_66,
                     )
                 }
                 4 => {
                     // MOV r32, r/m32 is (REX.W==0) 89 /r
                     emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0x89,
                         1,
                         *src,
@@ -686,15 +630,7 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 }
                 8 => {
                     // MOV r64, r/m64 is (REX.W==1) 89 /r
-                    emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
-                        sink,
-                        LegacyPrefix::PfxNone,
-                        0x89,
-                        1,
-                        *src,
-                        addr,
-                        F_NONE,
-                    )
+                    emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(sink, 0x89, 1, *src, addr, F_NONE)
                 }
                 _ => panic!("x64::Inst::Mov_R_M::emit: unreachable"),
             }
@@ -717,7 +653,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // SHL/SHR/SAR %cl, reg64 is (REX.W==1) D3 /subopcode
                     emit_REX_OPCODES_MODRM_encG_encE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xD3,
                         1,
                         subopcode,
@@ -732,7 +667,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // bother with that nicety here.
                     emit_REX_OPCODES_MODRM_encG_encE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xC1,
                         1,
                         subopcode,
@@ -749,7 +683,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
             dst: regG,
         } => {
             let mut retainRedundantRex = 0;
-            let mut prefix = LegacyPrefix::PfxNone;
             if *size == 1 {
                 // Here, a redundant REX prefix changes the meaning of the
                 // instruction.
@@ -758,12 +691,10 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     retainRedundantRex = F_RETAIN_REDUNDANT_REX;
                 }
             }
-            if *size == 2 {
-                prefix = LegacyPrefix::Pfx66;
-            }
             let mut flags = match size {
                 8 => F_NONE,
-                4 | 2 => F_CLEAR_REX_W,
+                4 => F_CLEAR_REX_W,
+                2 => F_CLEAR_REX_W | F_PREFIX_66,
                 1 => F_CLEAR_REX_W | retainRedundantRex,
                 _ => panic!("x64::Inst::Cmp_RMI_R::emit: unreachable"),
             };
@@ -779,14 +710,12 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                         }
                     }
                     // Same comment re swapped args as for Alu_RMI_R.
-                    emit_REX_OPCODES_MODRM_regG_regE(sink, prefix, opcode, 1, *regE, *regG, flags);
+                    emit_REX_OPCODES_MODRM_regG_regE(sink, opcode, 1, *regE, *regG, flags);
                 }
                 RMI::M { addr } => {
                     let opcode = if *size == 1 { 0x3A } else { 0x3B };
                     // Whereas here we revert to the "normal" G-E ordering.
-                    emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
-                        sink, prefix, opcode, 1, *regG, addr, flags,
-                    );
+                    emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(sink, opcode, 1, *regG, addr, flags);
                 }
                 RMI::I { simm32 } => {
                     // FIXME JRS 2020Feb11: there are shorter encodings for
@@ -802,7 +731,7 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     // And also here we use the "normal" G-E ordering.
                     let encG = iregEnc(*regG);
                     emit_REX_OPCODES_MODRM_encG_encE(
-                        sink, prefix, opcode, 1, 7, /*subopcode*/
+                        sink, opcode, 1, 7, /*subopcode*/
                         encG, flags,
                     );
                     emit_simm(sink, if useImm8 { 1 } else { *size }, *simm32);
@@ -822,7 +751,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 RMI::M { addr } => {
                     emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xFF,
                         1,
                         6, /*subopcode*/
@@ -859,7 +787,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     let regEnc = iregEnc(*reg);
                     emit_REX_OPCODES_MODRM_encG_encE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xFF,
                         1,
                         2, /*subopcode*/
@@ -870,7 +797,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 RM::M { addr } => {
                     emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xFF,
                         1,
                         2, /*subopcode*/
@@ -941,7 +867,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                     let regEnc = iregEnc(*reg);
                     emit_REX_OPCODES_MODRM_encG_encE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xFF,
                         1,
                         4, /*subopcode*/
@@ -952,7 +877,6 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 RM::M { addr } => {
                     emit_REX_OPCODES_MODRM_SIB_IMM_encG_memE(
                         sink,
-                        LegacyPrefix::PfxNone,
                         0xFF,
                         1,
                         4, /*subopcode*/
@@ -962,56 +886,7 @@ pub(crate) fn emit(inst: &Inst, sink: &mut MachBuffer<Inst>) {
                 }
             }
         }
-        Inst::XMM_R_R { op, src, dst } => {
-            let flags = F_CLEAR_REX_W;
-            let opcode = match op {
-                SSE_Op::SSE_Movss => 0x0F10,
-                SSE_Op::SSE2_Movsd => 0x0F10,
-                _ => unimplemented!("XMM_R_R opcode"),
-            };
-            let prefix = match op {
-                SSE_Op::SSE_Movss => LegacyPrefix::PfxF3,
-                SSE_Op::SSE2_Movsd => LegacyPrefix::PfxF2,
-                _ => unimplemented!("XMM_R_R opcode"),
-            };
-            emit_REX_OPCODES_MODRM_regG_regE(sink, prefix, opcode, 2, dst.to_reg(), *src, flags);
-        }
-        Inst::XMM_RM_R {
-            op,
-            src: srcE,
-            dst: regG,
-        } => {
-            let flags = F_CLEAR_REX_W;
-            let opcode = match op {
-                SSE_Op::SSE_Addss => 0x0F58,
-                SSE_Op::SSE_Subss => 0x0F5C,
-                _ => unimplemented!("XMM_RM_R opcode"),
-            };
-            match srcE {
-                RM::R { reg: regE } => {
-                    emit_REX_OPCODES_MODRM_regG_regE(
-                        sink,
-                        LegacyPrefix::PfxF3,
-                        opcode,
-                        2,
-                        regG.to_reg(),
-                        *regE,
-                        flags,
-                    );
-                }
-                RM::M { addr } => {
-                    emit_REX_OPCODES_MODRM_SIB_IMM_regG_memE(
-                        sink,
-                        LegacyPrefix::PfxF3,
-                        opcode,
-                        2,
-                        regG.to_reg(),
-                        addr,
-                        flags,
-                    );
-                }
-            }
-        }
+
         _ => panic!("x64_emit: unhandled: {} ", inst.show_rru(None)),
     }
 }
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 132d93e16..1f3eb0a1e 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -150,20 +150,6 @@ pub(crate) enum Inst {
 
     /// jmpq (reg mem)
     JmpUnknown { target: RM },
-
-    /// (add sub and or xor mul adc? sbb?) (32 64) (reg addr imm) reg
-    XMM_RM_R {
-        op: SSE_Op,
-        src: RM,
-        dst: Writable<Reg>,
-    },
-
-    /// mov (64 32) reg reg
-    XMM_R_R {
-        op: SSE_Op,
-        src: Reg,
-        dst: Writable<Reg>,
-    },
 }
 
 // Handy constructors for Insts.
@@ -209,17 +195,6 @@ impl Inst {
         Inst::Mov_R_R { is_64, src, dst }
     }
 
-    pub(crate) fn xmm_r_r(op: SSE_Op, src: Reg, dst: Writable<Reg>) -> Inst {
-        debug_assert!(src.get_class() == RegClass::V128);
-        debug_assert!(dst.to_reg().get_class() == RegClass::V128);
-        Inst::XMM_R_R { op, src, dst }
-    }
-
-    pub(crate) fn xmm_rm_r(op: SSE_Op, src: RM, dst: Writable<Reg>) -> Self {
-        debug_assert!(dst.to_reg().get_class() == RegClass::V128);
-        Self::XMM_RM_R { op, src, dst }
-    }
-
     pub(crate) fn movzx_m_r(extMode: ExtMode, addr: Addr, dst: Writable<Reg>) -> Inst {
         debug_assert!(dst.to_reg().get_class() == RegClass::I64);
         Inst::MovZX_M_R { extMode, addr, dst }
@@ -366,12 +341,6 @@ impl ShowWithRRU for Inst {
                 src.show_rru_sized(mb_rru, sizeLQ(*is_64)),
                 show_ireg_sized(dst.to_reg(), mb_rru, sizeLQ(*is_64)),
             ),
-            Inst::XMM_RM_R { op, src, dst } => format!(
-                "{} {}, {}",
-                ljustify(op.to_string()),
-                src.show_rru_sized(mb_rru, 8),
-                show_ireg_sized(dst.to_reg(), mb_rru, 8),
-            ),
             Inst::Imm_R {
                 dst_is_64,
                 simm64,
@@ -399,12 +368,6 @@ impl ShowWithRRU for Inst {
                 show_ireg_sized(*src, mb_rru, sizeLQ(*is_64)),
                 show_ireg_sized(dst.to_reg(), mb_rru, sizeLQ(*is_64))
             ),
-            Inst::XMM_R_R { op, src, dst } => format!(
-                "{} {}, {}",
-                ljustify(op.to_string()),
-                show_ireg_sized(*src, mb_rru, 8),
-                show_ireg_sized(dst.to_reg(), mb_rru, 8)
-            ),
             Inst::MovZX_M_R { extMode, addr, dst } => {
                 if *extMode == ExtMode::LQ {
                     format!(
@@ -527,10 +490,6 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
             src.get_regs_as_uses(collector);
             collector.add_mod(*dst);
         }
-        Inst::XMM_RM_R { op: _, src, dst } => {
-            src.get_regs_as_uses(collector);
-            collector.add_mod(*dst);
-        }
         Inst::Imm_R {
             dst_is_64: _,
             simm64: _,
@@ -542,10 +501,6 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
             collector.add_use(*src);
             collector.add_def(*dst);
         }
-        Inst::XMM_R_R { op: _, src, dst } => {
-            collector.add_use(*src);
-            collector.add_def(*dst);
-        }
         Inst::MovZX_M_R {
             extMode: _,
             addr,
@@ -694,14 +649,6 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
             src.map_uses(mapper);
             map_mod(mapper, dst);
         }
-        Inst::XMM_RM_R {
-            op: _,
-            ref mut src,
-            ref mut dst,
-        } => {
-            src.map_uses(mapper);
-            map_mod(mapper, dst);
-        }
         Inst::Imm_R {
             dst_is_64: _,
             simm64: _,
@@ -715,14 +662,6 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
             map_use(mapper, src);
             map_def(mapper, dst);
         }
-        Inst::XMM_R_R {
-            op: _,
-            ref mut src,
-            ref mut dst,
-        } => {
-            map_use(mapper, src);
-            map_def(mapper, dst);
-        }
         Inst::MovZX_M_R {
             extMode: _,
             ref mut addr,
@@ -845,8 +784,6 @@ impl MachInst for Inst {
         debug_assert!(rc_dst == rc_src);
         match rc_dst {
             RegClass::I64 => Inst::mov_r_r(true, src_reg, dst_reg),
-            // TODO: How do you just move 32 bits?
-            RegClass::V128 => Inst::xmm_r_r(SSE_Op::SSE2_Movsd, src_reg, dst_reg),
             _ => panic!("gen_move(x64): unhandled regclass"),
         }
     }
diff --git a/cranelift/codegen/src/isa/x64/inst/regs.rs b/cranelift/codegen/src/isa/x64/inst/regs.rs
index 087103b4a..bb8f05fb5 100644
--- a/cranelift/codegen/src/isa/x64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/x64/inst/regs.rs
@@ -78,53 +78,52 @@ pub(crate) fn r11() -> Reg {
 fn fpr(enc: u8, index: u8) -> Reg {
     Reg::new_real(RegClass::V128, enc, index)
 }
-
-pub(crate) fn xmm0() -> Reg {
+fn xmm0() -> Reg {
     fpr(0, 14)
 }
-pub(crate) fn xmm1() -> Reg {
+fn xmm1() -> Reg {
     fpr(1, 15)
 }
-pub(crate) fn xmm2() -> Reg {
+fn xmm2() -> Reg {
     fpr(2, 16)
 }
-pub(crate) fn xmm3() -> Reg {
+fn xmm3() -> Reg {
     fpr(3, 17)
 }
-pub(crate) fn xmm4() -> Reg {
+fn xmm4() -> Reg {
     fpr(4, 18)
 }
-pub(crate) fn xmm5() -> Reg {
+fn xmm5() -> Reg {
     fpr(5, 19)
 }
-pub(crate) fn xmm6() -> Reg {
+fn xmm6() -> Reg {
     fpr(6, 20)
 }
-pub(crate) fn xmm7() -> Reg {
+fn xmm7() -> Reg {
     fpr(7, 21)
 }
-pub(crate) fn xmm8() -> Reg {
+fn xmm8() -> Reg {
     fpr(8, 22)
 }
-pub(crate) fn xmm9() -> Reg {
+fn xmm9() -> Reg {
     fpr(9, 23)
 }
-pub(crate) fn xmm10() -> Reg {
+fn xmm10() -> Reg {
     fpr(10, 24)
 }
-pub(crate) fn xmm11() -> Reg {
+fn xmm11() -> Reg {
     fpr(11, 25)
 }
-pub(crate) fn xmm12() -> Reg {
+fn xmm12() -> Reg {
     fpr(12, 26)
 }
-pub(crate) fn xmm13() -> Reg {
+fn xmm13() -> Reg {
     fpr(13, 27)
 }
-pub(crate) fn xmm14() -> Reg {
+fn xmm14() -> Reg {
     fpr(14, 28)
 }
-pub(crate) fn xmm15() -> Reg {
+fn xmm15() -> Reg {
     fpr(15, 29)
 }
 
diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs
index f306d867b..f85d6166a 100644
--- a/cranelift/codegen/src/isa/x64/lower.rs
+++ b/cranelift/codegen/src/isa/x64/lower.rs
@@ -3,7 +3,7 @@
 #![allow(dead_code)]
 #![allow(non_snake_case)]
 
-use regalloc::{Reg, RegClass, Writable};
+use regalloc::{Reg, Writable};
 
 use crate::ir::condcodes::IntCC;
 use crate::ir::types;
@@ -31,7 +31,7 @@ fn is_int_ty(ty: Type) -> bool {
     }
 }
 
-fn int_ty_is_64(ty: Type) -> bool {
+fn int_ty_to_is64(ty: Type) -> bool {
     match ty {
         types::I8 | types::I16 | types::I32 => false,
         types::I64 => true,
@@ -39,14 +39,6 @@ fn int_ty_is_64(ty: Type) -> bool {
     }
 }
 
-fn flt_ty_is_64(ty: Type) -> bool {
-    match ty {
-        types::F32 => false,
-        types::F64 => true,
-        _ => panic!("type {} is none of F32, F64", ty),
-    }
-}
-
 fn int_ty_to_sizeB(ty: Type) -> u8 {
     match ty {
         types::I8 => 1,
@@ -126,6 +118,7 @@ fn lower_insn_to_regs<'a>(ctx: Ctx<'a>, iri: IRInst) {
     };
 
     // This is all outstandingly feeble.  TODO: much better!
+
     match op {
         Opcode::Iconst => {
             if let Some(w64) = iri_to_u64_immediate(ctx, iri) {
@@ -143,7 +136,7 @@ fn lower_insn_to_regs<'a>(ctx: Ctx<'a>, iri: IRInst) {
             let regD = output_to_reg(ctx, iri, 0);
             let regL = input_to_reg(ctx, iri, 0);
             let regR = input_to_reg(ctx, iri, 1);
-            let is64 = int_ty_is_64(ty.unwrap());
+            let is64 = int_ty_to_is64(ty.unwrap());
             let how = if op == Opcode::Iadd {
                 RMI_R_Op::Add
             } else {
@@ -202,11 +195,7 @@ fn lower_insn_to_regs<'a>(ctx: Ctx<'a>, iri: IRInst) {
             for i in 0..ctx.num_inputs(iri) {
                 let src_reg = input_to_reg(ctx, iri, i);
                 let retval_reg = ctx.retval(i);
-                if src_reg.get_class() == RegClass::I64 {
-                    ctx.emit(Inst::mov_r_r(true, src_reg, retval_reg));
-                } else if src_reg.get_class() == RegClass::V128 {
-                    ctx.emit(Inst::xmm_r_r(SSE_Op::SSE2_Movsd, src_reg, retval_reg));
-                }
+                ctx.emit(Inst::mov_r_r(true, src_reg, retval_reg));
             }
             // N.B.: the Ret itself is generated by the ABI.
         }
@@ -240,23 +229,7 @@ fn lower_insn_to_regs<'a>(ctx: Ctx<'a>, iri: IRInst) {
         | Opcode::SshrImm => {
             panic!("ALU+imm and ALU+carry ops should not appear here!");
         }
-        Opcode::Fadd | Opcode::Fsub => {
-            let regD = output_to_reg(ctx, iri, 0);
-            let regL = input_to_reg(ctx, iri, 0);
-            let regR = input_to_reg(ctx, iri, 1);
-            let is64 = flt_ty_is_64(ty.unwrap());
-            if !is64 {
-                let inst = if op == Opcode::Fadd {
-                    SSE_Op::SSE_Addss
-                } else {
-                    SSE_Op::SSE_Subss
-                };
-                ctx.emit(Inst::xmm_r_r(SSE_Op::SSE_Movss, regL, regD));
-                ctx.emit(Inst::xmm_rm_r(inst, RM::reg(regR), regD));
-            } else {
-                unimplemented!("unimplemented lowering for opcode {:?}", op);
-            }
-        }
+
         _ => unimplemented!("unimplemented lowering for opcode {:?}", op),
     }
 }
diff --git a/cranelift/codegen/src/lib.rs b/cranelift/codegen/src/lib.rs
index 38b173de1..dd871924a 100644
--- a/cranelift/codegen/src/lib.rs
+++ b/cranelift/codegen/src/lib.rs
@@ -1,4 +1,5 @@
 //! Cranelift code generation library.
+
 #![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
 #![warn(unused_import_braces)]
 #![cfg_attr(feature = "std", deny(unstable_features))]
diff --git a/cranelift/codegen/src/machinst/compile.rs b/cranelift/codegen/src/machinst/compile.rs
index 8254fff77..508e242cd 100644
--- a/cranelift/codegen/src/machinst/compile.rs
+++ b/cranelift/codegen/src/machinst/compile.rs
@@ -44,19 +44,6 @@ where
         }
     };
 
-    #[cfg(feature = "regalloc-snapshot")]
-    {
-        use std::fs;
-        use std::path::Path;
-        if let Some(path) = std::env::var("SERIALIZE_REGALLOC").ok() {
-            let snapshot = regalloc::IRSnapshot::from_function(&vcode, b.reg_universe());
-            let serialized = bincode::serialize(&snapshot).expect("couldn't serialize snapshot");
-
-            let file_path = Path::new(&path).join(Path::new(&format!("ir{}.bin", f.name)));
-            fs::write(file_path, &serialized).expect("couldn't write IR snapshot file");
-        }
-    }
-
     let result = {
         let _tt = timing::regalloc();
         allocate_registers_with_opts(
diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs
index 81c59f5f2..ec28d3d2e 100644
--- a/cranelift/codegen/src/machinst/vcode.rs
+++ b/cranelift/codegen/src/machinst/vcode.rs
@@ -136,14 +136,9 @@ impl<I: VCodeInst> VCodeBuilder<I> {
 
     /// Set the type of a VReg.
     pub fn set_vreg_type(&mut self, vreg: VirtualReg, ty: Type) {
-        if self.vcode.vreg_types.len() <= vreg.get_index() {
-            self.vcode.vreg_types.resize(
-                self.vcode.vreg_types.len()
-                    + ((vreg.get_index() + 1) - self.vcode.vreg_types.len()),
-                ir::types::I8,
-            )
+        while self.vcode.vreg_types.len() <= vreg.get_index() {
+            self.vcode.vreg_types.push(ir::types::I8); // Default type.
         }
-
         self.vcode.vreg_types[vreg.get_index()] = ty;
     }
 
@@ -470,8 +465,8 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
         insn.is_move()
     }
 
-    fn get_num_vregs(&self) -> usize {
-        self.vreg_types.len()
+    fn get_vreg_count_estimate(&self) -> Option<usize> {
+        Some(self.vreg_types.len())
     }
 
     fn get_spillslot_size(&self, regclass: RegClass, vreg: VirtualReg) -> u32 {
diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs
index fc282b31b..f93471393 100644
--- a/cranelift/src/wasm.rs
+++ b/cranelift/src/wasm.rs
@@ -51,6 +51,7 @@ pub fn run(
     flag_calc_value_ranges: bool,
 ) -> Result<(), String> {
     let parsed = parse_sets_and_triple(flag_set, flag_triple)?;
+
     for filename in files {
         let path = Path::new(&filename);
         let name = String::from(path.as_os_str().to_string_lossy());
diff --git a/crates/debug/Cargo.toml b/crates/debug/Cargo.toml
index 601d714ee..acd417add 100644
--- a/crates/debug/Cargo.toml
+++ b/crates/debug/Cargo.toml
@@ -14,7 +14,7 @@ edition = "2018"
 [dependencies]
 gimli = "0.21.0"
 wasmparser = "0.57.0"
-object = { version = "0.18", default-features = false, features = ["write"] }
+faerie = "0.15.0"
 wasmtime-environ = { path = "../environ", version = "0.17.0" }
 target-lexicon = { version = "0.10.0", default-features = false }
 anyhow = "1.0"
diff --git a/crates/debug/src/lib.rs b/crates/debug/src/lib.rs
index 45edf5890..bf1dd7c92 100644
--- a/crates/debug/src/lib.rs
+++ b/crates/debug/src/lib.rs
@@ -3,10 +3,8 @@
 #![allow(clippy::cast_ptr_alignment)]
 
 use anyhow::Error;
+use faerie::{Artifact, Decl, SectionKind};
 use more_asserts::assert_gt;
-use object::write::{Object, Relocation, StandardSegment};
-use object::{RelocationEncoding, RelocationKind, SectionKind};
-use std::collections::HashMap;
 use target_lexicon::BinaryFormat;
 use wasmtime_environ::isa::TargetIsa;
 
@@ -18,35 +16,25 @@ mod read_debuginfo;
 mod transform;
 mod write_debuginfo;
 
-pub fn write_debugsections(obj: &mut Object, sections: Vec<DwarfSection>) -> Result<(), Error> {
+pub fn write_debugsections(obj: &mut Artifact, sections: Vec<DwarfSection>) -> Result<(), Error> {
     let (bodies, relocs) = sections
         .into_iter()
         .map(|s| ((s.name.clone(), s.body), (s.name, s.relocs)))
         .unzip::<_, _, Vec<_>, Vec<_>>();
-    let mut ids = HashMap::new();
     for (name, body) in bodies {
-        let segment = obj.segment_name(StandardSegment::Debug).to_vec();
-        let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);
-        ids.insert(name, section_id);
-        obj.append_section_data(section_id, &body, 1);
+        obj.declare_with(name, Decl::section(SectionKind::Debug), body)?;
     }
     for (name, relocs) in relocs {
-        let section_id = *ids.get(&name).unwrap();
         for reloc in relocs {
-            let target_symbol = if reloc.target.starts_with("_wasm_function") {
-                obj.symbol_id(reloc.target.as_bytes()).unwrap()
-            } else {
-                obj.section_symbol(*ids.get(&reloc.target).unwrap())
-            };
-            obj.add_relocation(
-                section_id,
-                Relocation {
-                    offset: u64::from(reloc.offset),
-                    size: reloc.size << 3,
-                    kind: RelocationKind::Absolute,
-                    encoding: RelocationEncoding::Generic,
-                    symbol: target_symbol,
-                    addend: i64::from(reloc.addend),
+            obj.link_with(
+                faerie::Link {
+                    from: &name,
+                    to: &reloc.target,
+                    at: reloc.offset as u64,
+                },
+                faerie::Reloc::Debug {
+                    size: reloc.size,
+                    addend: reloc.addend,
                 },
             )?;
         }
@@ -91,14 +79,13 @@ pub fn write_debugsections_image(
     code_region: (*const u8, usize),
     funcs: &[*const u8],
 ) -> Result<Vec<u8>, Error> {
-    let mut obj = Object::new(BinaryFormat::Elf, isa.triple().architecture);
+    let mut obj = Artifact::new(isa.triple().clone(), String::from("module"));
 
     assert!(!code_region.0.is_null() && code_region.1 > 0);
     assert_gt!(funcs.len(), 0);
 
     let body = unsafe { std::slice::from_raw_parts(code_region.0, code_region.1) };
-    let section_id = obj.add_section(vec![], ".text.all".as_bytes().to_vec(), SectionKind::Text);
-    obj.append_section_data(section_id, body, 1);
+    obj.declare_with("all", Decl::function(), body.to_vec())?;
 
     // Get DWARF sections and patch relocs
     patch_dwarf_sections(&mut sections, funcs);
@@ -106,17 +93,17 @@ pub fn write_debugsections_image(
     write_debugsections(&mut obj, sections)?;
 
     // LLDB is too "magical" about mach-o, generating elf
-    let mut bytes = obj.write()?;
+    let mut bytes = obj.emit_as(BinaryFormat::Elf)?;
     // elf is still missing details...
-    convert_object_elf_to_loadable_file(&mut bytes, code_region.0);
+    convert_faerie_elf_to_loadable_file(&mut bytes, code_region.0);
 
     // let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
-    // ::std::io::Write::write_all(&mut file, &bytes).expect("write");
+    // ::std::io::Write::write(&mut file, &bytes).expect("write");
 
     Ok(bytes)
 }
 
-fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_ptr: *const u8) {
+fn convert_faerie_elf_to_loadable_file(bytes: &mut Vec<u8>, code_ptr: *const u8) {
     use std::ffi::CStr;
     use std::os::raw::c_char;
 
@@ -130,24 +117,21 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_ptr: *const u8)
         e_phoff == 0 && e_phnum == 0,
         "program header table is empty"
     );
+    let e_phentsize = unsafe { *(bytes.as_ptr().offset(0x36) as *const u16) };
+    assert_eq!(e_phentsize, 0x38, "size of ph");
     let e_shentsize = unsafe { *(bytes.as_ptr().offset(0x3A) as *const u16) };
     assert_eq!(e_shentsize, 0x40, "size of sh");
 
     let e_shoff = unsafe { *(bytes.as_ptr().offset(0x28) as *const u64) };
     let e_shnum = unsafe { *(bytes.as_ptr().offset(0x3C) as *const u16) };
     let mut shstrtab_off = 0;
-    for i in 0..e_shnum {
-        let off = e_shoff as isize + i as isize * e_shentsize as isize;
-        let sh_type = unsafe { *(bytes.as_ptr().offset(off + 0x4) as *const u32) };
-        if sh_type != /* SHT_SYMTAB */ 3 {
-            continue;
-        }
-        shstrtab_off = unsafe { *(bytes.as_ptr().offset(off + 0x18) as *const u64) };
-    }
     let mut segment = None;
     for i in 0..e_shnum {
         let off = e_shoff as isize + i as isize * e_shentsize as isize;
         let sh_type = unsafe { *(bytes.as_ptr().offset(off + 0x4) as *const u32) };
+        if sh_type == /* SHT_SYMTAB */ 3 {
+            shstrtab_off = unsafe { *(bytes.as_ptr().offset(off + 0x18) as *const u64) };
+        }
         if sh_type != /* SHT_PROGBITS */ 1 {
             continue;
         }
@@ -201,11 +185,10 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_ptr: *const u8)
     }
 
     // It is somewhat loadable ELF file at this moment.
-    // Update e_flags, e_phoff, e_phentsize and e_phnum.
+    // Update e_flags, e_phoff and e_phnum.
     unsafe {
         *(bytes.as_ptr().offset(0x10) as *mut u16) = /* ET_DYN */ 3;
         *(bytes.as_ptr().offset(0x20) as *mut u64) = ph_off as u64;
-        *(bytes.as_ptr().offset(0x36) as *mut u16) = 0x38 as u16;
         *(bytes.as_ptr().offset(0x38) as *mut u16) = 1 as u16;
     }
 }
diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs
index e6e4cc76b..221c54e0e 100644
--- a/crates/fuzzing/src/generators.rs
+++ b/crates/fuzzing/src/generators.rs
@@ -102,6 +102,7 @@ impl OptLevel {
 #[derive(Arbitrary, Debug)]
 pub struct Config {
     opt_level: OptLevel,
+    debug_verifier: bool,
     debug_info: bool,
     canonicalize_nans: bool,
     interruptable: bool,
@@ -122,6 +123,7 @@ impl Config {
             .static_memory_guard_size(self.static_memory_guard_size.unwrap_or(0).into())
             .dynamic_memory_guard_size(self.dynamic_memory_guard_size.unwrap_or(0).into())
             .cranelift_nan_canonicalization(self.canonicalize_nans)
+            .cranelift_debug_verifier(self.debug_verifier)
             .cranelift_opt_level(self.opt_level.to_wasmtime())
             .interruptable(self.interruptable);
         return cfg;
diff --git a/crates/fuzzing/src/lib.rs b/crates/fuzzing/src/lib.rs
index 534c42542..f344e2bf0 100644
--- a/crates/fuzzing/src/lib.rs
+++ b/crates/fuzzing/src/lib.rs
@@ -37,6 +37,7 @@ pub(crate) fn fuzz_default_config(
     init_fuzzing();
     let mut config = wasmtime::Config::new();
     config
+        .cranelift_debug_verifier(true)
         .cranelift_nan_canonicalization(true)
         .wasm_bulk_memory(true)
         .strategy(strategy)?;
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index 19fcc6b1f..7242da627 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -273,7 +273,9 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) {
             ApiCall::ConfigNew => {
                 log::trace!("creating config");
                 assert!(config.is_none());
-                config = Some(crate::fuzz_default_config(wasmtime::Strategy::Cranelift).unwrap());
+                let mut cfg = Config::new();
+                cfg.cranelift_debug_verifier(true);
+                config = Some(cfg);
             }
 
             ApiCall::ConfigDebugInfo(b) => {
diff --git a/crates/obj/Cargo.toml b/crates/obj/Cargo.toml
index b64ad4607..cc48e4196 100644
--- a/crates/obj/Cargo.toml
+++ b/crates/obj/Cargo.toml
@@ -13,7 +13,7 @@ edition = "2018"
 [dependencies]
 anyhow = "1.0"
 wasmtime-environ = { path = "../environ", version = "0.17.0" }
-object = { version = "0.18", default-features = false, features = ["write"] }
+faerie = "0.15.0"
 more-asserts = "0.2.1"
 
 [badges]
diff --git a/crates/obj/src/data_segment.rs b/crates/obj/src/data_segment.rs
index 159922f25..466254475 100644
--- a/crates/obj/src/data_segment.rs
+++ b/crates/obj/src/data_segment.rs
@@ -1,37 +1,25 @@
 use anyhow::Result;
-use object::write::{Object, StandardSection, Symbol, SymbolSection};
-use object::{SymbolFlags, SymbolKind, SymbolScope};
+use faerie::{Artifact, Decl};
 use wasmtime_environ::DataInitializer;
 
 /// Declares data segment symbol
 pub fn declare_data_segment(
-    obj: &mut Object,
+    obj: &mut Artifact,
     _data_initaliazer: &DataInitializer,
     index: usize,
 ) -> Result<()> {
     let name = format!("_memory_{}", index);
-    let _symbol_id = obj.add_symbol(Symbol {
-        name: name.as_bytes().to_vec(),
-        value: 0,
-        size: 0,
-        kind: SymbolKind::Data,
-        scope: SymbolScope::Linkage,
-        weak: false,
-        section: SymbolSection::Undefined,
-        flags: SymbolFlags::None,
-    });
+    obj.declare(name, Decl::data())?;
     Ok(())
 }
 
 /// Emit segment data and initialization location
 pub fn emit_data_segment(
-    obj: &mut Object,
+    obj: &mut Artifact,
     data_initaliazer: &DataInitializer,
     index: usize,
 ) -> Result<()> {
     let name = format!("_memory_{}", index);
-    let symbol_id = obj.symbol_id(name.as_bytes()).unwrap();
-    let section_id = obj.section_id(StandardSection::Data);
-    obj.add_symbol_data(symbol_id, section_id, data_initaliazer.data, 1);
+    obj.define(name, Vec::from(data_initaliazer.data))?;
     Ok(())
 }
diff --git a/crates/obj/src/function.rs b/crates/obj/src/function.rs
index 6c3a09e6a..b3b3dfaa2 100644
--- a/crates/obj/src/function.rs
+++ b/crates/obj/src/function.rs
@@ -1,6 +1,5 @@
 use anyhow::Result;
-use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
-use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
+use faerie::{Artifact, Decl, Link};
 use wasmtime_environ::entity::EntityRef;
 use wasmtime_environ::settings;
 use wasmtime_environ::settings::Configurable;
@@ -8,43 +7,25 @@ use wasmtime_environ::{Compilation, Module, RelocationTarget, Relocations};
 
 /// Defines module functions
 pub fn declare_functions(
-    obj: &mut Object,
+    obj: &mut Artifact,
     module: &Module,
     relocations: &Relocations,
 ) -> Result<()> {
     for i in 0..module.local.num_imported_funcs {
         let string_name = format!("_wasm_function_{}", i);
-        let _symbol_id = obj.add_symbol(Symbol {
-            name: string_name.as_bytes().to_vec(),
-            value: 0,
-            size: 0,
-            kind: SymbolKind::Text,
-            scope: SymbolScope::Unknown,
-            weak: false,
-            section: SymbolSection::Undefined,
-            flags: SymbolFlags::None,
-        });
+        obj.declare(string_name, Decl::function_import())?;
     }
     for (i, _function_relocs) in relocations.iter().rev() {
         let func_index = module.local.func_index(i);
         let string_name = format!("_wasm_function_{}", func_index.index());
-        let _symbol_id = obj.add_symbol(Symbol {
-            name: string_name.as_bytes().to_vec(),
-            value: 0,
-            size: 0,
-            kind: SymbolKind::Text,
-            scope: SymbolScope::Linkage,
-            weak: false,
-            section: SymbolSection::Undefined,
-            flags: SymbolFlags::None,
-        });
+        obj.declare(string_name, Decl::function().global())?;
     }
     Ok(())
 }
 
 /// Emits module functions
 pub fn emit_functions(
-    obj: &mut Object,
+    obj: &mut Artifact,
     module: &Module,
     compilation: &Compilation,
     relocations: &Relocations,
@@ -65,35 +46,22 @@ pub fn emit_functions(
         let func_index = module.local.func_index(i);
         let string_name = format!("_wasm_function_{}", func_index.index());
 
-        let symbol_id = obj.symbol_id(string_name.as_bytes()).unwrap();
-        let section_id = obj.section_id(StandardSection::Text);
-
-        obj.add_symbol_data(symbol_id, section_id, body, 1);
+        obj.define(string_name, body.clone())?;
     }
 
     for (i, function_relocs) in relocations.iter() {
         let func_index = module.local.func_index(i);
         let string_name = format!("_wasm_function_{}", func_index.index());
-        let symbol_id = obj.symbol_id(string_name.as_bytes()).unwrap();
-        let (_, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap();
-        let section_id = obj.section_id(StandardSection::Text);
         for r in function_relocs {
             debug_assert_eq!(r.addend, 0);
             match r.reloc_target {
                 RelocationTarget::UserFunc(target_index) => {
                     let target_name = format!("_wasm_function_{}", target_index.index());
-                    let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
-                    obj.add_relocation(
-                        section_id,
-                        Relocation {
-                            offset: section_offset + r.offset as u64,
-                            size: 64, // FIXME for all targets
-                            kind: RelocationKind::Absolute,
-                            encoding: RelocationEncoding::Generic,
-                            symbol: target_symbol,
-                            addend: 0,
-                        },
-                    )?;
+                    obj.link(Link {
+                        from: &string_name,
+                        to: &target_name,
+                        at: r.offset as u64,
+                    })?;
                 }
                 RelocationTarget::JumpTable(_, _) => {
                     // ignore relocations for jump tables
diff --git a/crates/obj/src/module.rs b/crates/obj/src/module.rs
index fdd5bf6e2..d5a1b1ed7 100644
--- a/crates/obj/src/module.rs
+++ b/crates/obj/src/module.rs
@@ -3,44 +3,24 @@ use crate::data_segment::{declare_data_segment, emit_data_segment};
 use crate::function::{declare_functions, emit_functions};
 use crate::table::{declare_table, emit_table};
 use anyhow::Result;
-use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
-use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
+use faerie::{Artifact, Decl, Link};
 use wasmtime_environ::isa::TargetFrontendConfig;
 use wasmtime_environ::{Compilation, DataInitializer, Module, Relocations};
 
 fn emit_vmcontext_init(
-    obj: &mut Object,
+    obj: &mut Artifact,
     module: &Module,
     target_config: &TargetFrontendConfig,
 ) -> Result<()> {
     let (data, table_relocs) = layout_vmcontext(module, target_config);
-    let symbol_id = obj.add_symbol(Symbol {
-        name: "_vmcontext_init".as_bytes().to_vec(),
-        value: 0,
-        size: 0,
-        kind: SymbolKind::Data,
-        scope: SymbolScope::Linkage,
-        weak: false,
-        section: SymbolSection::Undefined,
-        flags: SymbolFlags::None,
-    });
-    let section_id = obj.section_id(StandardSection::Data);
-    let section_offset = obj.add_symbol_data(symbol_id, section_id, &data, 1);
-
+    obj.declare_with("_vmcontext_init", Decl::data().global(), data.to_vec())?;
     for reloc in table_relocs.iter() {
         let target_name = format!("_table_{}", reloc.index);
-        let target_symbol = obj.symbol_id(target_name.as_bytes()).unwrap();
-        obj.add_relocation(
-            section_id,
-            Relocation {
-                offset: section_offset + reloc.offset as u64,
-                size: 64, // FIXME for all targets
-                kind: RelocationKind::Absolute,
-                encoding: RelocationEncoding::Generic,
-                symbol: target_symbol,
-                addend: 0,
-            },
-        )?;
+        obj.link(Link {
+            from: "_vmcontext_init",
+            to: &target_name,
+            at: reloc.offset as u64,
+        })?;
     }
     Ok(())
 }
@@ -48,7 +28,7 @@ fn emit_vmcontext_init(
 /// Emits a module that has been emitted with the `wasmtime-environ` environment
 /// implementation to a native object file.
 pub fn emit_module(
-    obj: &mut Object,
+    obj: &mut Artifact,
     module: &Module,
     compilation: &Compilation,
     relocations: &Relocations,
diff --git a/crates/obj/src/table.rs b/crates/obj/src/table.rs
index dd88ce53d..fe4b52c6a 100644
--- a/crates/obj/src/table.rs
+++ b/crates/obj/src/table.rs
@@ -1,29 +1,17 @@
 use anyhow::Result;
-use object::write::{Object, StandardSection, Symbol, SymbolSection};
-use object::{SymbolFlags, SymbolKind, SymbolScope};
+use faerie::{Artifact, Decl};
 
 /// Declares data segment symbol
-pub fn declare_table(obj: &mut Object, index: usize) -> Result<()> {
+pub fn declare_table(obj: &mut Artifact, index: usize) -> Result<()> {
     let name = format!("_table_{}", index);
-    let _symbol_id = obj.add_symbol(Symbol {
-        name: name.as_bytes().to_vec(),
-        value: 0,
-        size: 0,
-        kind: SymbolKind::Data,
-        scope: SymbolScope::Linkage,
-        weak: false,
-        section: SymbolSection::Undefined,
-        flags: SymbolFlags::None,
-    });
+    obj.declare(name, Decl::data())?;
     Ok(())
 }
 
 /// Emit segment data and initialization location
-pub fn emit_table(obj: &mut Object, index: usize) -> Result<()> {
+pub fn emit_table(obj: &mut Artifact, index: usize) -> Result<()> {
     let name = format!("_table_{}", index);
-    let symbol_id = obj.symbol_id(name.as_bytes()).unwrap();
-    let section_id = obj.section_id(StandardSection::Data);
     // FIXME: We need to initialize table using function symbols
-    obj.add_symbol_data(symbol_id, section_id, &[], 1);
+    obj.define(name, Vec::new())?;
     Ok(())
 }
diff --git a/crates/profiling/Cargo.toml b/crates/profiling/Cargo.toml
index d476eb12e..c18935ecd 100644
--- a/crates/profiling/Cargo.toml
+++ b/crates/profiling/Cargo.toml
@@ -16,7 +16,7 @@ cfg-if = "0.1"
 gimli = { version = "0.21.0", optional = true }
 lazy_static = "1.4"
 libc = { version = "0.2.60", default-features = false }
-scroll = { version = "0.10.1", features = ["derive"], optional = true }
+scroll = { version = "0.10.1", optional = true }
 serde = { version = "1.0.99", features = ["derive"] }
 target-lexicon = "0.10.0"
 wasmtime-environ = { path = "../environ", version = "0.17.0" }
diff --git a/crates/wasi-common/src/ctx.rs b/crates/wasi-common/src/ctx.rs
index 05bb0fce9..3121cb146 100644
--- a/crates/wasi-common/src/ctx.rs
+++ b/crates/wasi-common/src/ctx.rs
@@ -2,7 +2,7 @@ use crate::entry::{Entry, EntryHandle};
 use crate::fdpool::FdPool;
 use crate::handle::Handle;
 use crate::sys::osdir::OsDir;
-use crate::sys::stdio::NullDevice;
+use crate::sys::osother::{OsOther, OsOtherExt};
 use crate::sys::stdio::{Stderr, StderrExt, Stdin, StdinExt, Stdout, StdoutExt};
 use crate::virtfs::{VirtualDir, VirtualDirEntry};
 use crate::wasi::types;
@@ -136,9 +136,9 @@ pub struct WasiCtxBuilder {
 impl WasiCtxBuilder {
     /// Builder for a new `WasiCtx`.
     pub fn new() -> Self {
-        let stdin = Some(PendingEntry::Handle(Box::new(NullDevice::new())));
-        let stdout = Some(PendingEntry::Handle(Box::new(NullDevice::new())));
-        let stderr = Some(PendingEntry::Handle(Box::new(NullDevice::new())));
+        let stdin = Some(PendingEntry::Thunk(OsOther::from_null));
+        let stdout = Some(PendingEntry::Thunk(OsOther::from_null));
+        let stderr = Some(PendingEntry::Thunk(OsOther::from_null));
 
         Self {
             stdin,
diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs
index e6f8cef6a..f9c17841a 100644
--- a/crates/wasi-common/src/lib.rs
+++ b/crates/wasi-common/src/lib.rs
@@ -39,6 +39,6 @@ pub use ctx::{WasiCtx, WasiCtxBuilder, WasiCtxBuilderError};
 pub use handle::{Handle, HandleRights};
 pub use sys::osdir::OsDir;
 pub use sys::osfile::OsFile;
-pub use sys::osother::OsOther;
+pub use sys::osother::{OsOther, OsOtherExt};
 pub use sys::preopen_dir;
 pub use virtfs::{FileContents, VirtualDirEntry};
diff --git a/crates/wasi-common/src/sys/osother.rs b/crates/wasi-common/src/sys/osother.rs
index 4e5c90192..314d91dea 100644
--- a/crates/wasi-common/src/sys/osother.rs
+++ b/crates/wasi-common/src/sys/osother.rs
@@ -10,6 +10,13 @@ use std::fs::File;
 use std::io::{self, Read, Write};
 use std::ops::Deref;
 
+/// Extra methods for `OsOther` that are only available when configured for
+/// some operating systems.
+pub trait OsOtherExt {
+    /// Create `OsOther` as `dyn Handle` from null device.
+    fn from_null() -> io::Result<Box<dyn Handle>>;
+}
+
 /// `OsOther` is something of a catch-all for everything not covered with the specific handle
 /// types (`OsFile`, `OsDir`, `Stdio`). It currently encapsulates handles such as OS pipes,
 /// sockets, streams, etc. As such, when redirecting stdio within `WasiCtxBuilder`, the redirected
diff --git a/crates/wasi-common/src/sys/stdio.rs b/crates/wasi-common/src/sys/stdio.rs
index 786643b55..bbd2339ed 100644
--- a/crates/wasi-common/src/sys/stdio.rs
+++ b/crates/wasi-common/src/sys/stdio.rs
@@ -20,10 +20,9 @@ use super::{fd, AsFile};
 use crate::handle::{Handle, HandleRights};
 use crate::sandboxed_tty_writer::SandboxedTTYWriter;
 use crate::wasi::types::{self, Filetype};
-use crate::wasi::{Errno, Result, RightsExt};
+use crate::wasi::Result;
 use std::any::Any;
 use std::cell::Cell;
-use std::convert::TryInto;
 use std::io::{self, Read, Write};
 
 pub(crate) trait StdinExt: Sized {
@@ -175,59 +174,3 @@ impl Handle for Stderr {
         Ok(nwritten)
     }
 }
-
-#[derive(Debug, Clone)]
-pub(crate) struct NullDevice {
-    pub(crate) rights: Cell<HandleRights>,
-    pub(crate) fd_flags: Cell<types::Fdflags>,
-}
-
-impl NullDevice {
-    pub(crate) fn new() -> Self {
-        let rights = HandleRights::new(
-            types::Rights::character_device_base(),
-            types::Rights::character_device_inheriting(),
-        );
-        let rights = Cell::new(rights);
-        let fd_flags = types::Fdflags::empty();
-        let fd_flags = Cell::new(fd_flags);
-        Self { rights, fd_flags }
-    }
-}
-
-impl Handle for NullDevice {
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-    fn try_clone(&self) -> io::Result<Box<dyn Handle>> {
-        Ok(Box::new(self.clone()))
-    }
-    fn get_file_type(&self) -> types::Filetype {
-        types::Filetype::CharacterDevice
-    }
-    fn get_rights(&self) -> HandleRights {
-        self.rights.get()
-    }
-    fn set_rights(&self, rights: HandleRights) {
-        self.rights.set(rights)
-    }
-    // FdOps
-    fn fdstat_get(&self) -> Result<types::Fdflags> {
-        Ok(self.fd_flags.get())
-    }
-    fn fdstat_set_flags(&self, fdflags: types::Fdflags) -> Result<()> {
-        self.fd_flags.set(fdflags);
-        Ok(())
-    }
-    fn read_vectored(&self, _iovs: &mut [io::IoSliceMut]) -> Result<usize> {
-        Ok(0)
-    }
-    fn write_vectored(&self, iovs: &[io::IoSlice]) -> Result<usize> {
-        let mut total_len = 0u32;
-        for iov in iovs {
-            let len: types::Size = iov.len().try_into()?;
-            total_len = total_len.checked_add(len).ok_or(Errno::Overflow)?;
-        }
-        Ok(total_len as usize)
-    }
-}
diff --git a/crates/wasi-common/src/sys/unix/osother.rs b/crates/wasi-common/src/sys/unix/osother.rs
index 19688e269..260c47db8 100644
--- a/crates/wasi-common/src/sys/unix/osother.rs
+++ b/crates/wasi-common/src/sys/unix/osother.rs
@@ -1,9 +1,10 @@
 use super::oshandle::RawOsHandle;
 use super::{get_file_type, get_rights};
-use crate::sys::osother::OsOther;
+use crate::handle::Handle;
+use crate::sys::osother::{OsOther, OsOtherExt};
 use crate::wasi::types;
 use std::convert::TryFrom;
-use std::fs::File;
+use std::fs::{File, OpenOptions};
 use std::io;
 use std::os::unix::prelude::{FromRawFd, IntoRawFd};
 
@@ -20,3 +21,14 @@ impl TryFrom<File> for OsOther {
         Ok(Self::new(file_type, rights, handle))
     }
 }
+
+impl OsOtherExt for OsOther {
+    fn from_null() -> io::Result<Box<dyn Handle>> {
+        let file = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .open("/dev/null")?;
+        let file = Self::try_from(file)?;
+        Ok(Box::new(file))
+    }
+}
diff --git a/crates/wasi-common/src/sys/windows/osother.rs b/crates/wasi-common/src/sys/windows/osother.rs
index 13fb0c0a9..47db73217 100644
--- a/crates/wasi-common/src/sys/windows/osother.rs
+++ b/crates/wasi-common/src/sys/windows/osother.rs
@@ -1,9 +1,10 @@
 use super::oshandle::RawOsHandle;
 use super::{get_file_type, get_rights};
-use crate::sys::osother::OsOther;
+use crate::handle::Handle;
+use crate::sys::osother::{OsOther, OsOtherExt};
 use crate::wasi::types;
 use std::convert::TryFrom;
-use std::fs::File;
+use std::fs::{File, OpenOptions};
 use std::io;
 use std::os::windows::prelude::{FromRawHandle, IntoRawHandle};
 
@@ -20,3 +21,11 @@ impl TryFrom<File> for OsOther {
         Ok(Self::new(file_type, rights, handle))
     }
 }
+
+impl OsOtherExt for OsOther {
+    fn from_null() -> io::Result<Box<dyn Handle>> {
+        let file = OpenOptions::new().read(true).write(true).open("NUL")?;
+        let file = Self::try_from(file)?;
+        Ok(Box::new(file))
+    }
+}
diff --git a/examples/multi.rs b/examples/multi.rs
index f39fd2798..ebdbb9101 100644
--- a/examples/multi.rs
+++ b/examples/multi.rs
@@ -1,4 +1,4 @@
-//! This is an example of working with multi-value modules and dealing with
+//! This is an example of working with mulit-value modules and dealing with
 //! multi-value functions.
 //!
 //! Note that the `Func::wrap*` interfaces cannot be used to return multiple
diff --git a/src/commands/wasm2obj.rs b/src/commands/wasm2obj.rs
index 028ffd595..0ab1a6711 100644
--- a/src/commands/wasm2obj.rs
+++ b/src/commands/wasm2obj.rs
@@ -5,7 +5,6 @@ use crate::{init_file_per_thread_logger, pick_compilation_strategy, CommonOption
 use anyhow::{anyhow, Context as _, Result};
 use std::{
     fs::File,
-    io::Write,
     path::{Path, PathBuf},
     str::FromStr,
 };
@@ -76,13 +75,13 @@ impl WasmToObjCommand {
             self.common.enable_simd,
             self.common.opt_level(),
             self.common.debug_info,
+            self.output.clone(),
             &cache_config,
         )?;
 
-        let mut file =
-            File::create(Path::new(&self.output)).context("failed to create object file")?;
-        file.write_all(&obj.write()?)
-            .context("failed to write object file")?;
+        // FIXME: Make the format a parameter.
+        let file = File::create(Path::new(&self.output)).context("failed to create object file")?;
+        obj.write(file).context("failed to write object file")?;
 
         Ok(())
     }
diff --git a/src/lib.rs b/src/lib.rs
index d254a95e5..d0f647626 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -169,21 +169,13 @@ struct CommonOptions {
     /// Byte size of the guard region after dynamic memories are allocated.
     #[structopt(long)]
     dynamic_memory_guard_size: Option<u64>,
-
-    /// Enable Cranelift's internal debug verifier (expensive)
-    #[structopt(long)]
-    enable_cranelift_debug_verifier: bool,
-
-    /// Enable Cranelift's internal NaN canonicalization
-    #[structopt(long)]
-    enable_cranelift_nan_canonicalization: bool,
 }
 
 impl CommonOptions {
     fn config(&self) -> Result<Config> {
         let mut config = Config::new();
         config
-            .cranelift_debug_verifier(self.enable_cranelift_debug_verifier)
+            .cranelift_debug_verifier(cfg!(debug_assertions))
             .debug_info(self.debug_info)
             .wasm_bulk_memory(self.enable_bulk_memory || self.enable_all)
             .wasm_simd(self.enable_simd || self.enable_all)
@@ -192,8 +184,7 @@ impl CommonOptions {
             .wasm_threads(self.enable_threads || self.enable_all)
             .cranelift_opt_level(self.opt_level())
             .strategy(pick_compilation_strategy(self.cranelift, self.lightbeam)?)?
-            .profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)?
-            .cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization);
+            .profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)?;
         for CraneliftFlag { name, value } in &self.cranelift_flags {
             unsafe {
                 config.cranelift_other_flag(name, value)?;
diff --git a/src/obj.rs b/src/obj.rs
index 844193a83..cfa186aae 100644
--- a/src/obj.rs
+++ b/src/obj.rs
@@ -1,5 +1,5 @@
 use anyhow::{anyhow, bail, Context as _, Result};
-use object::write::Object;
+use faerie::Artifact;
 use target_lexicon::Triple;
 use wasmtime::Strategy;
 use wasmtime_debug::{emit_dwarf, read_debuginfo, write_debugsections};
@@ -21,8 +21,9 @@ pub fn compile_to_obj(
     enable_simd: bool,
     opt_level: wasmtime::OptLevel,
     debug_info: bool,
+    artifact_name: String,
     cache_config: &CacheConfig,
-) -> Result<Object> {
+) -> Result<Artifact> {
     let isa_builder = match target {
         Some(target) => native::lookup(target.clone())?,
         None => native::builder(),
@@ -50,7 +51,7 @@ pub fn compile_to_obj(
 
     let isa = isa_builder.finish(settings::Flags::new(flag_builder));
 
-    let mut obj = Object::new(isa.triple().binary_format, isa.triple().architecture);
+    let mut obj = Artifact::new(isa.triple().clone(), artifact_name);
 
     // TODO: Expose the tunables as command-line flags.
     let mut tunables = Tunables::default();


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 01bb2c38e..d77d6c414 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -1858,60 +1858,6 @@ fn test_aarch64_binemit() {
         "E5979F9A",
         "cset x5, hi",
     ));
-    insns.push((
-        Inst::VecDup {
-            rd: writable_vreg(25),
-            rn: xreg(7),
-            ty: I8,
-        },
-        "F90C014E",
-        "dup v25.16b, w7",
-    ));
-    insns.push((
-        Inst::VecDup {
-            rd: writable_vreg(2),
-            rn: xreg(23),
-            ty: I16,
-        },
-        "E20E024E",
-        "dup v2.8h, w23",
-    ));
-    insns.push((
-        Inst::VecDup {
-            rd: writable_vreg(0),
-            rn: xreg(28),
-            ty: I32,
-        },
-        "800F044E",
-        "dup v0.4s, w28",
-    ));
-    insns.push((
-        Inst::VecDup {
-            rd: writable_vreg(31),
-            rn: xreg(5),
-            ty: I64,
-        },
-        "BF0C084E",
-        "dup v31.2d, x5",
-    ));
-    insns.push((
-        Inst::VecDupFromFpu {
-            rd: writable_vreg(14),
-            rn: vreg(19),
-            ty: F32,
-        },
-        "6E06044E",
-        "dup v14.4s, v19.s[0]",
-    ));
-    insns.push((
-        Inst::VecDupFromFpu {
-            rd: writable_vreg(18),
-            rn: vreg(10),
-            ty: F64,
-        },
-        "5205084E",
-        "dup v18.2d, v10.d[0]",
-    ));
     insns.push((
         Inst::VecExtend {
             t: VecExtendOp::Sxtl8,
diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
index 6b54c7a1f..654dcc6b3 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
@@ -33,23 +33,6 @@ fn test_x64_emit() {
     let r14 = regs::r14();
     let r15 = regs::r15();
 
-    let xmm0 = regs::xmm0();
-    let xmm1 = regs::xmm1();
-    let xmm2 = regs::xmm2();
-    let xmm3 = regs::xmm3();
-    let xmm4 = regs::xmm4();
-    let xmm5 = regs::xmm5();
-    let xmm6 = regs::xmm6();
-    let xmm7 = regs::xmm7();
-    let xmm8 = regs::xmm8();
-    let xmm9 = regs::xmm9();
-    let xmm10 = regs::xmm10();
-    let xmm11 = regs::xmm11();
-    let xmm12 = regs::xmm12();
-    let xmm13 = regs::xmm13();
-    let xmm14 = regs::xmm14();
-    let xmm15 = regs::xmm15();
-
     // And Writable<> versions of the same:
     let w_rax = Writable::<Reg>::from_reg(rax);
     let w_rbx = Writable::<Reg>::from_reg(rbx);
@@ -68,23 +51,6 @@ fn test_x64_emit() {
     let w_r14 = Writable::<Reg>::from_reg(r14);
     let w_r15 = Writable::<Reg>::from_reg(r15);
 
-    let w_xmm0 = Writable::<Reg>::from_reg(xmm0);
-    let w_xmm1 = Writable::<Reg>::from_reg(xmm1);
-    let w_xmm2 = Writable::<Reg>::from_reg(xmm2);
-    let w_xmm3 = Writable::<Reg>::from_reg(xmm3);
-    let _w_xmm4 = Writable::<Reg>::from_reg(xmm4);
-    let _w_xmm5 = Writable::<Reg>::from_reg(xmm5);
-    let _w_xmm6 = Writable::<Reg>::from_reg(xmm6);
-    let _w_xmm7 = Writable::<Reg>::from_reg(xmm7);
-    let _w_xmm8 = Writable::<Reg>::from_reg(xmm8);
-    let _w_xmm9 = Writable::<Reg>::from_reg(xmm9);
-    let w_xmm10 = Writable::<Reg>::from_reg(xmm10);
-    let _w_xmm11 = Writable::<Reg>::from_reg(xmm11);
-    let _w_xmm12 = Writable::<Reg>::from_reg(xmm12);
-    let w_xmm13 = Writable::<Reg>::from_reg(xmm13);
-    let _w_xmm14 = Writable::<Reg>::from_reg(xmm14);
-    let _w_xmm15 = Writable::<Reg>::from_reg(xmm15);
-
     let mut insns = Vec::<(Inst, &str, &str)>::new();
 
     // ========================================================
@@ -2205,67 +2171,6 @@ fn test_x64_emit() {
         "jmp     *321(%r10,%rdx,4)",
     ));
 
-    // ========================================================
-    // XMM_RM_R
-
-    insns.push((
-        Inst::xmm_rm_r(SSE_Op::SSE_Addss, RM::reg(xmm1), w_xmm0),
-        "F30F58C1",
-        "addss   %xmm1, %xmm0",
-    ));
-    insns.push((
-        Inst::xmm_rm_r(SSE_Op::SSE_Subss, RM::reg(xmm0), w_xmm1),
-        "F30F5CC8",
-        "subss   %xmm0, %xmm1",
-    ));
-
-    insns.push((
-        Inst::xmm_rm_r(SSE_Op::SSE_Addss, RM::reg(xmm11), w_xmm13),
-        "F3450F58EB",
-        "addss   %xmm11, %xmm13",
-    ));
-
-    insns.push((
-        Inst::xmm_rm_r(SSE_Op::SSE_Subss, RM::reg(xmm12), w_xmm1),
-        "F3410F5CCC",
-        "subss   %xmm12, %xmm1",
-    ));
-
-    insns.push((
-        Inst::xmm_rm_r(
-            SSE_Op::SSE_Addss,
-            RM::mem(Addr::imm_reg_reg_shift(123, r10, rdx, 2)),
-            w_xmm0,
-        ),
-        "F3410F5844927B",
-        "addss   123(%r10,%rdx,4), %xmm0",
-    ));
-
-    insns.push((
-        Inst::xmm_rm_r(
-            SSE_Op::SSE_Subss,
-            RM::mem(Addr::imm_reg_reg_shift(321, r10, rax, 3)),
-            w_xmm10,
-        ),
-        "F3450F5C94C241010000",
-        "subss   321(%r10,%rax,8), %xmm10",
-    ));
-
-    // ========================================================
-    // XMM_R_R
-
-    insns.push((
-        Inst::xmm_r_r(SSE_Op::SSE_Movss, xmm3, w_xmm2),
-        "F30F10D3",
-        "movss   %xmm3, %xmm2",
-    ));
-
-    insns.push((
-        Inst::xmm_r_r(SSE_Op::SSE2_Movsd, xmm4, w_xmm3),
-        "F20F10DC",
-        "movsd   %xmm4, %xmm3",
-    ));
-
     // ========================================================
     // Actually run the tests!
     let flags = settings::Flags::new(settings::builder());
@@ -2274,6 +2179,7 @@ fn test_x64_emit() {
         // Check the printed text is as expected.
         let actual_printing = insn.show_rru(Some(&rru));
         assert_eq!(expected_printing, actual_printing);
+
         let mut sink = test_utils::TestCodeSink::new();
         let mut buffer = MachBuffer::new();
         insn.emit(&mut buffer, &flags, &mut Default::default());
diff --git a/cranelift/filetests/filetests/vcode/aarch64/saturating-ops.clif b/cranelift/filetests/filetests/vcode/aarch64/saturating-ops.clif
index 0c1b97809..d184eeef9 100644
--- a/cranelift/filetests/filetests/vcode/aarch64/saturating-ops.clif
+++ b/cranelift/filetests/filetests/vcode/aarch64/saturating-ops.clif
@@ -25,10 +25,10 @@ block0(v0: i8, v1: i8):
 
 ; check: stp fp, lr, [sp, #-16]!
 ; nextln: mov fp, sp
-; nextln: uxtb x0, w0
-; nextln: uxtb x1, w1
-; nextln: mov v0.d[0], x0
-; nextln: mov v1.d[0], x1
+; nextln: uxtb x2, w0
+; nextln: uxtb x0, w1
+; nextln: mov v0.d[0], x2
+; nextln: mov v1.d[0], x0
 ; nextln: uqadd d0, d0, d1
 ; nextln: mov x0, v0.d[0]
 ; nextln: mov sp, fp
diff --git a/cranelift/filetests/filetests/vcode/aarch64/shift-rotate.clif b/cranelift/filetests/filetests/vcode/aarch64/shift-rotate.clif
index 7d698a00b..2f2439ff5 100644
--- a/cranelift/filetests/filetests/vcode/aarch64/shift-rotate.clif
+++ b/cranelift/filetests/filetests/vcode/aarch64/shift-rotate.clif
@@ -368,10 +368,10 @@ block0(v0: i16):
 
 ; check: stp fp, lr, [sp, #-16]!
 ; nextln: mov fp, sp
-; nextln: uxth w0, w0
-; nextln: lsr w1, w0, #6
-; nextln: lsl w0, w0, #10
-; nextln: orr w0, w0, w1
+; nextln: uxth w1, w0
+; nextln: lsr w0, w1, #6
+; nextln: lsl w1, w1, #10
+; nextln: orr w0, w1, w0
 ; nextln: mov sp, fp
 ; nextln: ldp fp, lr, [sp], #16
 ; nextln: ret
@@ -385,10 +385,10 @@ block0(v0: i8):
 
 ; check:  stp fp, lr, [sp, #-16]!
 ; nextln:  mov fp, sp
-; nextln:  uxtb w0, w0
-; nextln:  lsr w1, w0, #5
-; nextln:  lsl w0, w0, #3
-; nextln:  orr w0, w0, w1
+; nextln:  uxtb w1, w0
+; nextln:  lsr w0, w1, #5
+; nextln:  lsl w1, w1, #3
+; nextln:  orr w0, w1, w0
 ; nextln:  mov sp, fp
 ; nextln:  ldp fp, lr, [sp], #16
 ; nextln:  ret
diff --git a/cranelift/filetests/filetests/vcode/aarch64/stack.clif b/cranelift/filetests/filetests/vcode/aarch64/stack.clif
index 85f19bdb2..4ebdc672a 100644
--- a/cranelift/filetests/filetests/vcode/aarch64/stack.clif
+++ b/cranelift/filetests/filetests/vcode/aarch64/stack.clif
@@ -113,3 +113,156 @@ block0(v0: i64):
 ; nextln: mov sp, fp
 ; nextln: ldp fp, lr, [sp], #16
 ; nextln: ret
+
+; Force a b1 to be spilled into a slot at an SP offset between 0x100 and
+; 0x1fff, to exercise the scaled addressing mode.
+function %b1_spill_slot(b1) -> b1, i64 {
+    ss0 = explicit_slot 1000
+
+block0(v0: b1):
+  v1 = iconst.i64 1
+  v2 = iconst.i64 2
+  v3 = iconst.i64 3
+  v4 = iconst.i64 4
+  v5 = iconst.i64 5
+  v6 = iconst.i64 6
+  v7 = iconst.i64 7
+  v8 = iconst.i64 8
+  v9 = iconst.i64 9
+  v10 = iconst.i64 10
+  v11 = iconst.i64 11
+  v12 = iconst.i64 12
+  v13 = iconst.i64 13
+  v14 = iconst.i64 14
+  v15 = iconst.i64 15
+  v16 = iconst.i64 16
+  v17 = iconst.i64 17
+  v18 = iconst.i64 18
+  v19 = iconst.i64 19
+  v20 = iconst.i64 20
+  v21 = iconst.i64 21
+  v22 = iconst.i64 22
+  v23 = iconst.i64 23
+  v24 = iconst.i64 24
+  v25 = iconst.i64 25
+  v26 = iconst.i64 26
+  v27 = iconst.i64 27
+  v28 = iconst.i64 28
+  v29 = iconst.i64 29
+  v30 = iconst.i64 30
+  v31 = iconst.i64 31
+  v32 = iconst.i64 32
+  v33 = iconst.i64 33
+  v34 = iconst.i64 34
+  v35 = iconst.i64 35
+  v36 = iconst.i64 36
+  v37 = iconst.i64 37
+  v38 = iconst.i64 38
+  v39 = iconst.i64 39
+  v40 = iconst.i64 30
+  v41 = iconst.i64 31
+  v42 = iconst.i64 32
+  v43 = iconst.i64 33
+  v44 = iconst.i64 34
+  v45 = iconst.i64 35
+  v46 = iconst.i64 36
+  v47 = iconst.i64 37
+  v48 = iconst.i64 38
+  v49 = iconst.i64 39
+  v50 = iconst.i64 30
+  v51 = iconst.i64 31
+  v52 = iconst.i64 32
+  v53 = iconst.i64 33
+  v54 = iconst.i64 34
+  v55 = iconst.i64 35
+  v56 = iconst.i64 36
+  v57 = iconst.i64 37
+  v58 = iconst.i64 38
+  v59 = iconst.i64 39
+  v60 = iconst.i64 30
+  v61 = iconst.i64 31
+  v62 = iconst.i64 32
+  v63 = iconst.i64 33
+  v64 = iconst.i64 34
+  v65 = iconst.i64 35
+  v66 = iconst.i64 36
+  v67 = iconst.i64 37
+  v68 = iconst.i64 38
+  v69 = iconst.i64 39
+
+  v70 = iadd.i64 v1, v2
+  v71 = iadd.i64 v3, v4
+  v72 = iadd.i64 v5, v6
+  v73 = iadd.i64 v7, v8
+  v74 = iadd.i64 v9, v10
+  v75 = iadd.i64 v11, v12
+  v76 = iadd.i64 v13, v14
+  v77 = iadd.i64 v15, v16
+  v78 = iadd.i64 v17, v18
+  v79 = iadd.i64 v19, v20
+  v80 = iadd.i64 v21, v22
+  v81 = iadd.i64 v23, v24
+  v82 = iadd.i64 v25, v26
+  v83 = iadd.i64 v27, v28
+  v84 = iadd.i64 v29, v30
+  v85 = iadd.i64 v31, v32
+  v86 = iadd.i64 v33, v34
+  v87 = iadd.i64 v35, v36
+  v88 = iadd.i64 v37, v38
+  v89 = iadd.i64 v39, v40
+  v90 = iadd.i64 v41, v42
+  v91 = iadd.i64 v43, v44
+  v92 = iadd.i64 v45, v46
+  v93 = iadd.i64 v47, v48
+  v94 = iadd.i64 v49, v50
+  v95 = iadd.i64 v51, v52
+  v96 = iadd.i64 v53, v54
+  v97 = iadd.i64 v55, v56
+  v98 = iadd.i64 v57, v58
+  v99 = iadd.i64 v59, v60
+  v100 = iadd.i64 v61, v62
+  v101 = iadd.i64 v63, v64
+  v102 = iadd.i64 v65, v66
+  v103 = iadd.i64 v67, v68
+
+  v104 = iadd.i64 v69, v70
+  v105 = iadd.i64 v71, v72
+  v106 = iadd.i64 v73, v74
+  v107 = iadd.i64 v75, v76
+  v108 = iadd.i64 v77, v78
+  v109 = iadd.i64 v79, v80
+  v110 = iadd.i64 v81, v82
+  v111 = iadd.i64 v83, v84
+  v112 = iadd.i64 v85, v86
+  v113 = iadd.i64 v87, v88
+  v114 = iadd.i64 v89, v90
+  v115 = iadd.i64 v91, v92
+  v116 = iadd.i64 v93, v94
+  v117 = iadd.i64 v95, v96
+  v118 = iadd.i64 v97, v98
+  v119 = iadd.i64 v99, v100
+  v120 = iadd.i64 v101, v102
+
+  v121 = iadd.i64 v103, v104
+  v122 = iadd.i64 v105, v106
+  v123 = iadd.i64 v107, v108
+  v124 = iadd.i64 v109, v110
+  v125 = iadd.i64 v111, v112
+  v126 = iadd.i64 v113, v114
+  v127 = iadd.i64 v115, v116
+  v128 = iadd.i64 v117, v118
+  v129 = iadd.i64 v119, v120
+
+  v130 = iadd.i64 v121, v122
+  v131 = iadd.i64 v123, v124
+  v132 = iadd.i64 v125, v126
+  v133 = iadd.i64 v127, v128
+
+  v134 = iadd.i64 v129, v130
+  v135 = iadd.i64 v131, v132
+
+  v136 = iadd.i64 v133, v134
+  v137 = iadd.i64 v135, v136
+
+  return v0, v137
+}
diff --git a/tests/all/debug/obj.rs b/tests/all/debug/obj.rs
index 5b9b5700d..a80037351 100644
--- a/tests/all/debug/obj.rs
+++ b/tests/all/debug/obj.rs
@@ -1,6 +1,5 @@
 use anyhow::{Context as _, Result};
 use std::fs::File;
-use std::io::Write;
 use std::path::Path;
 use target_lexicon::Triple;
 use wasmtime::Strategy;
@@ -19,12 +18,17 @@ pub fn compile_cranelift(
         false,
         wasmtime::OptLevel::None,
         true,
+        output
+            .as_ref()
+            .file_name()
+            .unwrap()
+            .to_string_lossy()
+            .to_string(),
         &CacheConfig::new_cache_disabled(),
     )?;
 
-    let mut file = File::create(output).context("failed to create object file")?;
-    file.write_all(&obj.write()?)
-        .context("failed to write object file")?;
+    let file = File::create(output).context("failed to create object file")?;
+    obj.write(file).context("failed to write object file")?;
 
     Ok(())
 }