Repository: bytecodealliance/wasmtime
Pull request #9088 (2024-08-07 21:03:43)

pulley: pack `dst`, `src1` and `src2` registers into 2 bytes
Pack the `dst`, `src1` and `src2` register operands of binary operators into 2 bytes. Solves the `TODO` at the top of `lib.rs`.

~This required moving the special registers into their own register class, and adding `smov` and `spush`/`spop` to interact with SP/FP.~
I held off on splitting special registers into their own `SReg` class because I'm not sure how to integrate it with ISLE/Regalloc

cfallin: Reassigned @fitzgen to review, I'm not sure how this intersects with your other plans to add instructions with large offsets etc; happy to review this myself if you'd like otherwise.
github-actions[bot]: #### Subscribe to Label Action

cc @fitzgen

<details>
This issue or pull request has been labeled: "pulley"

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

* fitzgen: pulley

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

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
fitzgen: Heads up: the Pulley backend for Cranelift landed in #9089 and I think this can be rebased and landed now

Patch:
diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle
index 672ebf4d8..d7aaaed0d 100644
--- a/cranelift/codegen/src/isa/aarch64/inst.isle
+++ b/cranelift/codegen/src/isa/aarch64/inst.isle
@@ -1355,6 +1355,10 @@
     (MAdd)
     ;; Multiply-sub
     (MSub)
+    ;; Negated fused Multiply-add
+    (NMAdd)
+    ;; Negated fused Multiply-sub
+    (NMSub)
 ))
 
 ;; A conversion from an FP to an integer value.
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 9da0b932c..d458f0d18 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -1878,6 +1878,8 @@ impl MachInstEmit for Inst {
                 let top17 = match fpu_op {
                     FPUOp3::MAdd => 0b000_11111_00_0_00000_0,
                     FPUOp3::MSub => 0b000_11111_00_0_00000_1,
+                    FPUOp3::NMAdd => 0b000_11111_00_1_00000_0,
+                    FPUOp3::NMSub => 0b000_11111_00_1_00000_1,
                 };
                 let top17 = top17 | size.ftype() << 7;
                 sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index cd51aa77e..a7f3ba714 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -1823,6 +1823,8 @@ impl Inst {
                 let op = match fpu_op {
                     FPUOp3::MAdd => "fmadd",
                     FPUOp3::MSub => "fmsub",
+                    FPUOp3::NMAdd => "fnmadd",
+                    FPUOp3::NMSub => "fnmsub",
                 };
                 let rd = pretty_print_vreg_scalar(rd.to_reg(), size);
                 let rn = pretty_print_vreg_scalar(rn, size);
diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle
index 6267de3b5..a36d0d6fc 100644
--- a/cranelift/codegen/src/isa/aarch64/lower.isle
+++ b/cranelift/codegen/src/isa/aarch64/lower.isle
@@ -535,15 +535,22 @@
 (decl pure get_fneg_value (IsFneg) Value)
 (rule (get_fneg_value (IsFneg.Result _ v)) v)
 
-(decl fmadd_series (Type u64 Value Value Value) InstOutput)
-(rule 0 (fmadd_series (ty_scalar_float ty) 0 x y z) (fpu_rrrr (FPUOp3.MAdd) (scalar_size ty) x y z))
-(rule 0 (fmadd_series (ty_scalar_float ty) 1 x y z) (fpu_rrrr (FPUOp3.MSub) (scalar_size ty) x y z))
-
-(rule (lower (has_type (ty_scalar_float ty) (fma x_src y_src z)))
+(decl fmadd_series (Type u64 u64 Value Value Value) InstOutput)
+;; MAdd: + x * y + z
+(rule 0 (fmadd_series (ty_scalar_float ty) 0 0 x y z) (fpu_rrrr (FPUOp3.MAdd) (scalar_size ty) x y z))
+;; MSub: - x * y + z
+(rule 0 (fmadd_series (ty_scalar_float ty) 1 0 x y z) (fpu_rrrr (FPUOp3.MSub) (scalar_size ty) x y z))
+;; NMAdd: - x * y - z
+(rule 0 (fmadd_series (ty_scalar_float ty) 1 1 x y z) (fpu_rrrr (FPUOp3.NMAdd) (scalar_size ty) x y z))
+;; NMSub: + x * y - z
+(rule 0 (fmadd_series (ty_scalar_float ty) 0 1 x y z) (fpu_rrrr (FPUOp3.NMSub) (scalar_size ty) x y z))
+
+(rule (lower (has_type (ty_scalar_float ty) (fma x_src y_src z_src)))
   (let
       ((x_res IsFneg (is_fneg x_src))
-       (y_res IsFneg (is_fneg y_src)))
-      (fmadd_series ty (u64_xor (is_fneg_neg x_res) (is_fneg_neg y_res)) (get_fneg_value x_res) (get_fneg_value y_res) z)))
+       (y_res IsFneg (is_fneg y_src))
+       (z_res IsFneg (is_fneg z_src)))
+      (fmadd_series ty (u64_xor (is_fneg_neg x_res) (is_fneg_neg y_res)) (is_fneg_neg z_res) (get_fneg_value x_res) (get_fneg_value y_res) (get_fneg_value z_res))))
 
 ;; Delegate vector-based lowerings to helpers below
 (rule 1 (lower (has_type ty @ (multi_lane _ _) (fma x y z)))
diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/args.rs b/cranelift/codegen/src/isa/pulley_shared/inst/args.rs
index b0d73bc39..d74c2a7e0 100644
--- a/cranelift/codegen/src/isa/pulley_shared/inst/args.rs
+++ b/cranelift/codegen/src/isa/pulley_shared/inst/args.rs
@@ -2,6 +2,7 @@
 
 use super::*;
 use crate::machinst::abi::StackAMode;
+use pulley_interpreter::regs::Reg as _;
 
 /// A macro for defining a newtype of `Reg` that enforces some invariant about
 /// the wrapped `Reg` (such as that it is of a particular register class).
diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs
index d04ed2bfe..c94ee8226 100644
--- a/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs
+++ b/cranelift/codegen/src/isa/pulley_shared/inst/emit.rs
@@ -9,6 +9,8 @@ use crate::trace;
 use core::marker::PhantomData;
 use cranelift_control::ControlPlane;
 use pulley_interpreter::encode as enc;
+use pulley_interpreter::regs::BinaryOperands;
+use pulley_interpreter::regs::Reg as _;
 
 pub struct EmitInfo {
     #[allow(dead_code)] // Will get used as we fill out this backend.
@@ -333,48 +335,28 @@ fn pulley_emit<P>(
         Inst::Xconst32 { dst, imm } => enc::xconst32(sink, dst, *imm),
         Inst::Xconst64 { dst, imm } => enc::xconst64(sink, dst, *imm),
 
-        Inst::Xadd32 { dst, src1, src2 } => {
-            enc::xadd32(sink, dst, src1, src2);
-        }
-        Inst::Xadd64 { dst, src1, src2 } => {
-            enc::xadd64(sink, dst, src1, src2);
-        }
-
-        Inst::Xeq64 { dst, src1, src2 } => {
-            enc::xeq64(sink, dst, src1, src2);
-        }
-        Inst::Xneq64 { dst, src1, src2 } => {
-            enc::xneq64(sink, dst, src1, src2);
-        }
-        Inst::Xslt64 { dst, src1, src2 } => {
-            enc::xslt64(sink, dst, src1, src2);
-        }
+        Inst::Xadd32 { dst, src1, src2 } => enc::xadd32(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xadd64 { dst, src1, src2 } => enc::xadd64(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xeq64 { dst, src1, src2 } => enc::xeq64(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xneq64 { dst, src1, src2 } => enc::xneq64(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xslt64 { dst, src1, src2 } => enc::xslt64(sink, BinaryOperands::new(dst, src1, src2)),
         Inst::Xslteq64 { dst, src1, src2 } => {
-            enc::xslteq64(sink, dst, src1, src2);
-        }
-        Inst::Xult64 { dst, src1, src2 } => {
-            enc::xult64(sink, dst, src1, src2);
+            enc::xslteq64(sink, BinaryOperands::new(dst, src1, src2))
         }
+        Inst::Xult64 { dst, src1, src2 } => enc::xult64(sink, BinaryOperands::new(dst, src1, src2)),
         Inst::Xulteq64 { dst, src1, src2 } => {
-            enc::xulteq64(sink, dst, src1, src2);
-        }
-        Inst::Xeq32 { dst, src1, src2 } => {
-            enc::xeq32(sink, dst, src1, src2);
-        }
-        Inst::Xneq32 { dst, src1, src2 } => {
-            enc::xneq32(sink, dst, src1, src2);
-        }
-        Inst::Xslt32 { dst, src1, src2 } => {
-            enc::xslt32(sink, dst, src1, src2);
+            enc::xulteq64(sink, BinaryOperands::new(dst, src1, src2))
         }
+
+        Inst::Xeq32 { dst, src1, src2 } => enc::xeq32(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xneq32 { dst, src1, src2 } => enc::xneq32(sink, BinaryOperands::new(dst, src1, src2)),
+        Inst::Xslt32 { dst, src1, src2 } => enc::xslt32(sink, BinaryOperands::new(dst, src1, src2)),
         Inst::Xslteq32 { dst, src1, src2 } => {
-            enc::xslteq32(sink, dst, src1, src2);
-        }
-        Inst::Xult32 { dst, src1, src2 } => {
-            enc::xult32(sink, dst, src1, src2);
+            enc::xslteq32(sink, BinaryOperands::new(dst, src1, src2))
         }
+        Inst::Xult32 { dst, src1, src2 } => enc::xult32(sink, BinaryOperands::new(dst, src1, src2)),
         Inst::Xulteq32 { dst, src1, src2 } => {
-            enc::xulteq32(sink, dst, src1, src2);
+            enc::xulteq32(sink, BinaryOperands::new(dst, src1, src2))
         }
 
         Inst::LoadAddr { dst, mem } => {
@@ -398,8 +380,12 @@ fn pulley_emit<P>(
                     }
 
                     match P::pointer_width() {
-                        PointerWidth::PointerWidth32 => enc::xadd32(sink, dst, base, dst),
-                        PointerWidth::PointerWidth64 => enc::xadd64(sink, dst, base, dst),
+                        PointerWidth::PointerWidth32 => {
+                            enc::xadd32(sink, BinaryOperands::new(dst, base, dst))
+                        }
+                        PointerWidth::PointerWidth64 => {
+                            enc::xadd64(sink, BinaryOperands::new(dst, base, dst))
+                        }
                     }
                 }
             } else {
diff --git a/cranelift/codegen/src/isa/pulley_shared/inst/regs.rs b/cranelift/codegen/src/isa/pulley_shared/inst/regs.rs
index 8a6aa5eb5..16da50b58 100644
--- a/cranelift/codegen/src/isa/pulley_shared/inst/regs.rs
+++ b/cranelift/codegen/src/isa/pulley_shared/inst/regs.rs
@@ -89,17 +89,12 @@ define_registers! {
     x_reg(24) => x24, writable_x24;
     x_reg(25) => x25, writable_x25;
     x_reg(26) => x26, writable_x26;
-    x_reg(27) => x27, writable_x27;
-    x_reg(28) => x28, writable_x28;
-    x_reg(29) => x29, writable_x29;
-    x_reg(30) => x30, writable_x30;
-    x_reg(31) => x31, writable_x31;
 
-    x_reg(32) => stack_reg, writable_stack_reg;
-    x_reg(33) => link_reg, writable_link_reg;
-    x_reg(34) => fp_reg, writable_fp_reg;
-    x_reg(35) => spilltmp_reg, writable_spilltmp_reg;
-    x_reg(36) => spilltmp2_reg, writable_spilltmp2_reg;
+    x_reg(27) => stack_reg, writable_stack_reg;
+    x_reg(28) => link_reg, writable_link_reg;
+    x_reg(29) => fp_reg, writable_fp_reg;
+    x_reg(30) => spilltmp_reg, writable_spilltmp_reg;
+    x_reg(31) => spilltmp2_reg, writable_spilltmp2_reg;
 
     f_reg(0) => f0, writable_f0;
     f_reg(1) => f1, writable_f1;
diff --git a/cranelift/codegen/src/isa/s390x/lower/isle.rs b/cranelift/codegen/src/isa/s390x/lower/isle.rs
index f205fe74f..a3812a6d9 100644
--- a/cranelift/codegen/src/isa/s390x/lower/isle.rs
+++ b/cranelift/codegen/src/isa/s390x/lower/isle.rs
@@ -1049,11 +1049,7 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
 
     #[inline]
     fn sinkable_inst(&mut self, val: Value) -> Option<Inst> {
-        let input = self.lower_ctx.get_value_as_source_or_const(val);
-        if let Some((inst, 0)) = input.inst.as_inst() {
-            return Some(inst);
-        }
-        None
+        self.is_sinkable_inst(val)
     }
 
     #[inline]
diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs
index 34bfb93f9..d05950a3c 100644
--- a/cranelift/codegen/src/isa/x64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/x64/lower/isle.rs
@@ -331,8 +331,7 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
     }
 
     fn sinkable_load(&mut self, val: Value) -> Option<SinkableLoad> {
-        let input = self.lower_ctx.get_value_as_source_or_const(val);
-        if let InputSourceInst::UniqueUse(inst, 0) = input.inst {
+        if let Some(inst) = self.is_sinkable_inst(val) {
             if let Some((addr_input, offset)) =
                 is_mergeable_load(self.lower_ctx, inst, MergeableLoadSize::Min32)
             {
@@ -347,8 +346,7 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
     }
 
     fn sinkable_load_exact(&mut self, val: Value) -> Option<SinkableLoad> {
-        let input = self.lower_ctx.get_value_as_source_or_const(val);
-        if let InputSourceInst::UniqueUse(inst, 0) = input.inst {
+        if let Some(inst) = self.is_sinkable_inst(val) {
             if let Some((addr_input, offset)) =
                 is_mergeable_load(self.lower_ctx, inst, MergeableLoadSize::Exact)
             {
diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs
index fa6e8265d..86cda5d3e 100644
--- a/cranelift/codegen/src/machinst/lower.rs
+++ b/cranelift/codegen/src/machinst/lower.rs
@@ -295,15 +295,36 @@ pub struct Lower<'func, I: VCodeInst> {
 /// actually merged" point. Instead, we compute a
 /// transitive-uniqueness. That is what this enum represents.
 ///
-/// To define it plainly: a value is `Unused` if no references exist
-/// to it; `Once` if only one other op refers to it, *and* that other
-/// op is `Unused` or `Once`; and `Multiple` otherwise. In other
-/// words, `Multiple` is contagious: even if an op's result value is
-/// directly used only once in the CLIF, that value is `Multiple` if
-/// the op that uses it is itself used multiple times (hence could be
-/// codegen'd multiple times). In brief, this analysis tells us
-/// whether, if every op merged all of its operand tree, a given op
-/// could be codegen'd in more than one place.
+/// There is one final caveat as well to the result of this analysis.  Notably,
+/// we define some instructions to be "root" instructions, which means that we
+/// assume they will always be codegen'd at the root of a matching tree, and not
+/// matched. (This comes with the caveat that we actually enforce this property
+/// by making them "opaque" to subtree matching in
+/// `get_value_as_source_or_const`). Because they will always be codegen'd once,
+/// they in some sense "reset" multiplicity: these root instructions can be used
+/// many times, but because their result(s) are only computed once, they only
+/// use their inputs once.
+///
+/// We currently define all multi-result instructions to be "root" instructions,
+/// because it is too complex to reason about matching through them, and they
+/// cause too-coarse-grained approximation of multiplicity otherwise: the
+/// analysis would have to assume (as it used to!) that they are always
+/// multiply-used, simply because they have multiple outputs even if those
+/// outputs are used only once.
+///
+/// In the future we could define other instructions to be "root" instructions
+/// as well, if we make the corresponding change to get_value_as_source_or_const
+/// as well.
+///
+/// To define `ValueUseState` more plainly: a value is `Unused` if no references
+/// exist to it; `Once` if only one other op refers to it, *and* that other op
+/// is `Unused` or `Once`; and `Multiple` otherwise. In other words, `Multiple`
+/// is contagious (except through root instructions): even if an op's result
+/// value is directly used only once in the CLIF, that value is `Multiple` if
+/// the op that uses it is itself used multiple times (hence could be codegen'd
+/// multiple times). In brief, this analysis tells us whether, if every op
+/// merged all of its operand tree, a given op could be codegen'd in more than
+/// one place.
 ///
 /// To compute this, we first consider direct uses. At this point
 /// `Unused` answers are correct, `Multiple` answers are correct, but
@@ -452,7 +473,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
             block_end_colors[bb] = InstColor::new(cur_color);
         }
 
-        let value_ir_uses = Self::compute_use_states(f, sret_param);
+        let value_ir_uses = compute_use_states(f, sret_param);
 
         Ok(Lower {
             f,
@@ -482,120 +503,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
         self.vcode.sigs_mut()
     }
 
-    /// Pre-analysis: compute `value_ir_uses`. See comment on
-    /// `ValueUseState` for a description of what this analysis
-    /// computes.
-    fn compute_use_states(
-        f: &Function,
-        sret_param: Option<Value>,
-    ) -> SecondaryMap<Value, ValueUseState> {
-        // We perform the analysis without recursion, so we don't
-        // overflow the stack on long chains of ops in the input.
-        //
-        // This is sort of a hybrid of a "shallow use-count" pass and
-        // a DFS. We iterate over all instructions and mark their args
-        // as used. However when we increment a use-count to
-        // "Multiple" we push its args onto the stack and do a DFS,
-        // immediately marking the whole dependency tree as
-        // Multiple. Doing both (shallow use-counting over all insts,
-        // and deep Multiple propagation) lets us trim both
-        // traversals, stopping recursion when a node is already at
-        // the appropriate state.
-        //
-        // In particular, note that the *coarsening* into {Unused,
-        // Once, Multiple} is part of what makes this pass more
-        // efficient than a full indirect-use-counting pass.
-
-        let mut value_ir_uses = SecondaryMap::with_default(ValueUseState::Unused);
-
-        if let Some(sret_param) = sret_param {
-            // There's an implicit use of the struct-return parameter in each
-            // copy of the function epilogue, which we count here.
-            value_ir_uses[sret_param] = ValueUseState::Multiple;
-        }
-
-        // Stack of iterators over Values as we do DFS to mark
-        // Multiple-state subtrees. The iterator type is whatever is
-        // returned by `uses` below.
-        let mut stack: SmallVec<[_; 16]> = smallvec![];
-
-        // Find the args for the inst corresponding to the given value.
-        let uses = |value| {
-            trace!(" -> pushing args for {} onto stack", value);
-            if let ValueDef::Result(src_inst, _) = f.dfg.value_def(value) {
-                Some(f.dfg.inst_values(src_inst))
-            } else {
-                None
-            }
-        };
-
-        // Do a DFS through `value_ir_uses` to mark a subtree as
-        // Multiple.
-        for inst in f
-            .layout
-            .blocks()
-            .flat_map(|block| f.layout.block_insts(block))
-        {
-            // If this inst produces multiple values, we must mark all
-            // of its args as Multiple, because otherwise two uses
-            // could come in as Once on our two different results.
-            let force_multiple = f.dfg.inst_results(inst).len() > 1;
-
-            // Iterate over all values used by all instructions, noting an
-            // additional use on each operand.
-            for arg in f.dfg.inst_values(inst) {
-                debug_assert!(f.dfg.value_is_real(arg));
-                let old = value_ir_uses[arg];
-                if force_multiple {
-                    trace!(
-                        "forcing arg {} to Multiple because of multiple results of user inst",
-                        arg
-                    );
-                    value_ir_uses[arg] = ValueUseState::Multiple;
-                } else {
-                    value_ir_uses[arg].inc();
-                }
-                let new = value_ir_uses[arg];
-                trace!("arg {} used, old state {:?}, new {:?}", arg, old, new);
-
-                // On transition to Multiple, do DFS.
-                if old == ValueUseState::Multiple || new != ValueUseState::Multiple {
-                    continue;
-                }
-                if let Some(iter) = uses(arg) {
-                    stack.push(iter);
-                }
-                while let Some(iter) = stack.last_mut() {
-                    if let Some(value) = iter.next() {
-                        debug_assert!(f.dfg.value_is_real(value));
-                        trace!(" -> DFS reaches {}", value);
-                        if value_ir_uses[value] == ValueUseState::Multiple {
-                            // Truncate DFS here: no need to go further,
-                            // as whole subtree must already be Multiple.
-                            // With debug asserts, check one level of
-                            // that invariant at least.
-                            debug_assert!(uses(value).into_iter().flatten().all(|arg| {
-                                debug_assert!(f.dfg.value_is_real(arg));
-                                value_ir_uses[arg] == ValueUseState::Multiple
-                            }));
-                            continue;
-                        }
-                        value_ir_uses[value] = ValueUseState::Multiple;
-                        trace!(" -> became Multiple");
-                        if let Some(iter) = uses(value) {
-                            stack.push(iter);
-                        }
-                    } else {
-                        // Empty iterator, discard.
-                        stack.pop();
-                    }
-                }
-            }
-        }
-
-        value_ir_uses
-    }
-
     fn gen_arg_setup(&mut self) {
         if let Some(entry_bb) = self.f.layout.entry_block() {
             trace!(
@@ -1137,6 +1044,137 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
     }
 }
 
+/// Pre-analysis: compute `value_ir_uses`. See comment on
+/// `ValueUseState` for a description of what this analysis
+/// computes.
+fn compute_use_states(
+    f: &Function,
+    sret_param: Option<Value>,
+) -> SecondaryMap<Value, ValueUseState> {
+    // We perform the analysis without recursion, so we don't
+    // overflow the stack on long chains of ops in the input.
+    //
+    // This is sort of a hybrid of a "shallow use-count" pass and
+    // a DFS. We iterate over all instructions and mark their args
+    // as used. However when we increment a use-count to
+    // "Multiple" we push its args onto the stack and do a DFS,
+    // immediately marking the whole dependency tree as
+    // Multiple. Doing both (shallow use-counting over all insts,
+    // and deep Multiple propagation) lets us trim both
+    // traversals, stopping recursion when a node is already at
+    // the appropriate state.
+    //
+    // In particular, note that the *coarsening* into {Unused,
+    // Once, Multiple} is part of what makes this pass more
+    // efficient than a full indirect-use-counting pass.
+
+    let mut value_ir_uses = SecondaryMap::with_default(ValueUseState::Unused);
+
+    if let Some(sret_param) = sret_param {
+        // There's an implicit use of the struct-return parameter in each
+        // copy of the function epilogue, which we count here.
+        value_ir_uses[sret_param] = ValueUseState::Multiple;
+    }
+
+    // Stack of iterators over Values as we do DFS to mark
+    // Multiple-state subtrees. The iterator type is whatever is
+    // returned by `uses` below.
+    let mut stack: SmallVec<[_; 16]> = smallvec![];
+
+    // Find the args for the inst corresponding to the given value.
+    //
+    // Note that "root" instructions are skipped here. This means that multiple
+    // uses of any result of a multi-result instruction are not considered
+    // multiple uses of the operands of a multi-result instruction. This
+    // requires tight coupling with `get_value_as_source_or_const` above which
+    // is the consumer of the map that this function is producing.
+    let uses = |value| {
+        trace!(" -> pushing args for {} onto stack", value);
+        if let ValueDef::Result(src_inst, _) = f.dfg.value_def(value) {
+            if is_value_use_root(f, src_inst) {
+                None
+            } else {
+                Some(f.dfg.inst_values(src_inst))
+            }
+        } else {
+            None
+        }
+    };
+
+    // Do a DFS through `value_ir_uses` to mark a subtree as
+    // Multiple.
+    for inst in f
+        .layout
+        .blocks()
+        .flat_map(|block| f.layout.block_insts(block))
+    {
+        // Iterate over all values used by all instructions, noting an
+        // additional use on each operand.
+        for arg in f.dfg.inst_values(inst) {
+            debug_assert!(f.dfg.value_is_real(arg));
+            let old = value_ir_uses[arg];
+            value_ir_uses[arg].inc();
+            let new = value_ir_uses[arg];
+            trace!("arg {} used, old state {:?}, new {:?}", arg, old, new);
+
+            // On transition to Multiple, do DFS.
+            if old == ValueUseState::Multiple || new != ValueUseState::Multiple {
+                continue;
+            }
+            if let Some(iter) = uses(arg) {
+                stack.push(iter);
+            }
+            while let Some(iter) = stack.last_mut() {
+                if let Some(value) = iter.next() {
+                    debug_assert!(f.dfg.value_is_real(value));
+                    trace!(" -> DFS reaches {}", value);
+                    if value_ir_uses[value] == ValueUseState::Multiple {
+                        // Truncate DFS here: no need to go further,
+                        // as whole subtree must already be Multiple.
+                        // With debug asserts, check one level of
+                        // that invariant at least.
+                        debug_assert!(uses(value).into_iter().flatten().all(|arg| {
+                            debug_assert!(f.dfg.value_is_real(arg));
+                            value_ir_uses[arg] == ValueUseState::Multiple
+                        }));
+                        continue;
+                    }
+                    value_ir_uses[value] = ValueUseState::Multiple;
+                    trace!(" -> became Multiple");
+                    if let Some(iter) = uses(value) {
+                        stack.push(iter);
+                    }
+                } else {
+                    // Empty iterator, discard.
+                    stack.pop();
+                }
+            }
+        }
+    }
+
+    value_ir_uses
+}
+
+/// Definition of a "root" instruction for the calculation of `ValueUseState`.
+///
+/// This function calculates whether `inst` is considered a "root" for value-use
+/// information. This concept is used to forcibly prevent looking-through the
+/// instruction during `get_value_as_source_or_const` as it additionally
+/// prevents propagating `Multiple`-used results of the `inst` here to the
+/// operands of the instruction.
+///
+/// Currently this is defined as multi-result instructions. That means that
+/// lowerings are never allowed to look through a multi-result instruction to
+/// generate patterns. Note that this isn't possible in ISLE today anyway so
+/// this isn't currently much of a loss.
+///
+/// The main purpose of this function is to prevent the operands of a
+/// multi-result instruction from being forcibly considered `Multiple`-used
+/// regardless of circumstances.
+fn is_value_use_root(f: &Function, inst: Inst) -> bool {
+    f.dfg.inst_results(inst).len() > 1
+}
+
 /// Function-level queries.
 impl<'func, I: VCodeInst> Lower<'func, I> {
     pub fn dfg(&self) -> &DataFlowGraph {
@@ -1267,14 +1305,31 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
         val
     }
 
-    /// Like `get_input_as_source_or_const` but with a `Value`.
+    /// Resolves a particular input of an instruction to the `Value` that it is
+    /// represented with.
+    ///
+    /// For more information see [`Lower::get_value_as_source_or_const`].
     pub fn get_input_as_source_or_const(&self, ir_inst: Inst, idx: usize) -> NonRegInput {
         let val = self.input_as_value(ir_inst, idx);
         self.get_value_as_source_or_const(val)
     }
 
-    /// Resolves a particular input of an instruction to the `Value` that it is
-    /// represented with.
+    /// Resolves a `Value` definition to the source instruction it came from
+    /// plus whether it's a unique-use of that instruction.
+    ///
+    /// This function is the workhorse of pattern-matching in ISLE which enables
+    /// combining multiple instructions together. This is used implicitly in
+    /// patterns such as `(iadd x (iconst y))` where this function is used to
+    /// extract the `(iconst y)` operand.
+    ///
+    /// At its core this function is a wrapper around
+    /// [`DataFlowGraph::value_def`]. This function applies a filter on top of
+    /// that, however, to determine when it is actually safe to "look through"
+    /// the `val` definition here and view the underlying instruction. This
+    /// protects against duplicating side effects, such as loads, for example.
+    ///
+    /// Internally this uses the data computed from `compute_use_states` along
+    /// with other instruction properties to know what to return.
     pub fn get_value_as_source_or_const(&self, val: Value) -> NonRegInput {
         trace!(
             "get_input_for_val: val {} at cur_inst {:?} cur_scan_entry_color {:?}",
@@ -1283,9 +1338,21 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
             self.cur_scan_entry_color,
         );
         let inst = match self.f.dfg.value_def(val) {
-            // OK to merge source instruction if (i) we have a source
-            // instruction, and:
-            // - It has no side-effects, OR
+            // OK to merge source instruction if we have a source
+            // instruction, and one of these two conditions hold:
+            //
+            // - It has no side-effects and this instruction is not a "value-use
+            //   root" instruction. Instructions which are considered "roots"
+            //   for value-use calculations do not have accurate information
+            //   known about the `ValueUseState` of their operands. This is
+            //   currently done for multi-result instructions to prevent a use
+            //   of each result from forcing all operands of the multi-result
+            //   instruction to also be `Multiple`. This in turn means that the
+            //   `ValueUseState` for operands of a "root" instruction to be a
+            //   lie if pattern matching were to look through the multi-result
+            //   instruction. As a result the "look through this instruction"
+            //   logic only succeeds if it's not a root instruction.
+            //
             // - It has a side-effect, has one output value, that one
             //   output has only one use, directly or indirectly (so
             //   cannot be duplicated -- see comment on
@@ -1308,10 +1375,21 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
                 let src_side_effect = has_lowering_side_effect(self.f, src_inst);
                 trace!(" -> src inst {}", src_inst);
                 trace!(" -> has lowering side effect: {}", src_side_effect);
-                if !src_side_effect {
-                    // Pure instruction: always possible to
-                    // sink. Let's determine whether we are the only
-                    // user or not.
+                if is_value_use_root(self.f, src_inst) {
+                    // If this instruction is a "root instruction" then it's
+                    // required that we can't look through it to see the
+                    // definition. This means that the `ValueUseState` for the
+                    // operands of this result assume that this instruction is
+                    // generated exactly once which might get violated were we
+                    // to allow looking through it.
+                    trace!(" -> is a root instruction");
+                    InputSourceInst::None
+                } else if !src_side_effect {
+                    // Otherwise if this instruction has no side effects and the
+                    // value is used only once then we can look through it with
+                    // a "unique" tag. A non-unique `Use` can be shown for other
+                    // values ensuring consumers know how it's computed but that
+                    // it's not available to omit.
                     if self.value_ir_uses[val] == ValueUseState::Once {
                         InputSourceInst::UniqueUse(src_inst, result_idx)
                     } else {
@@ -1460,3 +1538,53 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::ValueUseState;
+    use crate::cursor::{Cursor, FuncCursor};
+    use crate::ir::types;
+    use crate::ir::{Function, InstBuilder};
+
+    #[test]
+    fn multi_result_use_once() {
+        let mut func = Function::new();
+        let block0 = func.dfg.make_block();
+        let mut pos = FuncCursor::new(&mut func);
+        pos.insert_block(block0);
+        let v1 = pos.ins().iconst(types::I64, 0);
+        let v2 = pos.ins().iconst(types::I64, 1);
+        let v3 = pos.ins().iconcat(v1, v2);
+        let (v4, v5) = pos.ins().isplit(v3);
+        pos.ins().return_(&[v4, v5]);
+        let func = pos.func;
+
+        let uses = super::compute_use_states(&func, None);
+        assert_eq!(uses[v1], ValueUseState::Once);
+        assert_eq!(uses[v2], ValueUseState::Once);
+        assert_eq!(uses[v3], ValueUseState::Once);
+        assert_eq!(uses[v4], ValueUseState::Once);
+        assert_eq!(uses[v5], ValueUseState::Once);
+    }
+
+    #[test]
+    fn results_used_twice_but_not_operands() {
+        let mut func = Function::new();
+        let block0 = func.dfg.make_block();
+        let mut pos = FuncCursor::new(&mut func);
+        pos.insert_block(block0);
+        let v1 = pos.ins().iconst(types::I64, 0);
+        let v2 = pos.ins().iconst(types::I64, 1);
+        let v3 = pos.ins().iconcat(v1, v2);
+        let (v4, v5) = pos.ins().isplit(v3);
+        pos.ins().return_(&[v4, v4]);
+        let func = pos.func;
+
+        let uses = super::compute_use_states(&func, None);
+        assert_eq!(uses[v1], ValueUseState::Once);
+        assert_eq!(uses[v2], ValueUseState::Once);
+        assert_eq!(uses[v3], ValueUseState::Once);
+        assert_eq!(uses[v4], ValueUseState::Multiple);
+        assert_eq!(uses[v5], ValueUseState::Unused);
+    }
+}
diff --git a/crates/cranelift/src/debug/transform/attr.rs b/crates/cranelift/src/debug/transform/attr.rs
index 8b54c7200..5982100b8 100644
--- a/crates/cranelift/src/debug/transform/attr.rs
+++ b/crates/cranelift/src/debug/transform/attr.rs
@@ -42,7 +42,6 @@ pub(crate) fn clone_die_attributes<'a, R>(
     current_scope_id: write::UnitEntryId,
     subprogram_range_builder: Option<RangeInfoBuilder>,
     scope_ranges: Option<&Vec<(u64, u64)>>,
-    cu_low_pc: u64,
     out_strings: &mut write::StringTable,
     pending_die_refs: &mut PendingUnitRefs,
     pending_di_refs: &mut PendingDebugInfoRefs,
@@ -60,7 +59,7 @@ where
         // FIXME for CU: currently address_transform operate on a single
         // function range, and when CU spans multiple ranges the
         // transformation may be incomplete.
-        RangeInfoBuilder::from(dwarf, unit, entry, cu_low_pc)?
+        RangeInfoBuilder::from(dwarf, unit, entry)?
     };
     range_info.build(addr_tr, out_unit, current_scope_id);
 
@@ -92,7 +91,7 @@ where
                 write::AttributeValue::Address(addr)
             }
             AttributeValue::DebugAddrIndex(i) => {
-                let u = dwarf.debug_addr.get_address(4, unit.addr_base, i)?;
+                let u = dwarf.address(unit, i)?;
                 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
                 write::AttributeValue::Address(addr)
             }
@@ -142,7 +141,7 @@ where
             }
             AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => {
                 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap();
-                let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r, cu_low_pc)?;
+                let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?;
                 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges);
                 write::AttributeValue::RangeListRef(range_list_id)
             }
@@ -307,27 +306,3 @@ where
     }
     Ok(())
 }
-
-pub(crate) fn clone_attr_string<R>(
-    attr_value: &AttributeValue<R>,
-    form: gimli::DwForm,
-    unit: &Unit<R, R::Offset>,
-    dwarf: &gimli::Dwarf<R>,
-    out_strings: &mut write::StringTable,
-) -> Result<write::LineString, Error>
-where
-    R: Reader,
-{
-    let content = dwarf
-        .attr_string(unit, attr_value.clone())?
-        .to_string_lossy()?
-        .into_owned();
-    Ok(match form {
-        gimli::DW_FORM_strp => {
-            let id = out_strings.add(content);
-            write::LineString::StringRef(id)
-        }
-        gimli::DW_FORM_string => write::LineString::String(content.into()),
-        _ => bail!("DW_FORM_line_strp or other not supported"),
-    })
-}
diff --git a/crates/cranelift/src/debug/transform/line_program.rs b/crates/cranelift/src/debug/transform/line_program.rs
index 715428aee..e9bea8be4 100644
--- a/crates/cranelift/src/debug/transform/line_program.rs
+++ b/crates/cranelift/src/debug/transform/line_program.rs
@@ -1,11 +1,7 @@
 use super::address_transform::AddressTransform;
-use super::attr::clone_attr_string;
 use super::{Reader, TransformError};
-use anyhow::{Context, Error};
-use gimli::{
-    write, AttributeValue::DebugLineRef, DebugLine, DebugLineOffset, DebugStr,
-    DebuggingInformationEntry, LineEncoding, Unit,
-};
+use anyhow::{bail, Error};
+use gimli::{write, DebugLineOffset, LineEncoding, Unit};
 use wasmtime_environ::DefinedFuncIndex;
 
 #[derive(Debug)]
@@ -41,65 +37,18 @@ enum ReadLineProgramState {
 
 pub(crate) fn clone_line_program<R>(
     dwarf: &gimli::Dwarf<R>,
-    skeleton_dwarf: &gimli::Dwarf<R>,
     unit: &Unit<R, R::Offset>,
-    root: &DebuggingInformationEntry<R>,
-    skeleton_die: Option<&DebuggingInformationEntry<R>>,
+    comp_name: Option<R>,
     addr_tr: &AddressTransform,
     out_encoding: gimli::Encoding,
-    debug_str: &DebugStr<R>,
-    debug_line: &DebugLine<R>,
     out_strings: &mut write::StringTable,
 ) -> Result<(write::LineProgram, DebugLineOffset, Vec<write::FileId>, u64), Error>
 where
     R: Reader,
 {
-    // Where are the "location" attributes
-    let (location_die, location_dwarf) = match skeleton_die {
-        Some(die) => (die, skeleton_dwarf),
-        _ => (root, dwarf),
-    };
-
-    let offset = match location_die.attr_value(gimli::DW_AT_stmt_list)? {
-        Some(DebugLineRef(offset)) => offset,
-        Some(gimli::AttributeValue::SecOffset(offset)) => DebugLineOffset(offset),
-        _ => {
-            return Err(TransformError("Debug line offset is not found").into());
-        }
-    };
-    let comp_dir = location_die.attr_value(gimli::DW_AT_comp_dir)?;
-    let comp_name = root.attr_value(gimli::DW_AT_name)?;
-    let out_comp_dir = match &comp_dir {
-        Some(comp_dir) => Some(clone_attr_string(
-            comp_dir,
-            gimli::DW_FORM_strp,
-            unit,
-            location_dwarf,
-            out_strings,
-        )?),
-        None => None,
-    };
-    let out_comp_name = match comp_name {
-        Some(_) => clone_attr_string(
-            comp_name
-                .as_ref()
-                .context("failed to read DW_AT_name attribute")?,
-            gimli::DW_FORM_strp,
-            unit,
-            dwarf,
-            out_strings,
-        )?,
-        _ => gimli::write::LineString::String("missing DW_AT_name attribute".into()),
-    };
-
-    let program = debug_line.program(
-        offset,
-        unit.header.address_size(),
-        comp_dir.and_then(|val| val.string_value(&debug_str)),
-        comp_name.and_then(|val| val.string_value(&debug_str)),
-    );
-    if let Ok(program) = program {
+    if let Some(program) = unit.line_program.clone() {
         let header = program.header();
+        let offset = header.offset();
         let file_index_base = if header.version() < 5 { 1 } else { 0 };
         assert!(header.version() <= 5, "not supported 6");
         let line_encoding = LineEncoding {
@@ -109,21 +58,32 @@ where
             line_base: header.line_base(),
             line_range: header.line_range(),
         };
+        let out_comp_dir = match header.directory(0) {
+            Some(comp_dir) => clone_line_string(
+                dwarf.attr_string(unit, comp_dir)?,
+                gimli::DW_FORM_strp,
+                out_strings,
+            )?,
+            None => write::LineString::String(Vec::new()),
+        };
+        let out_comp_name = match comp_name {
+            Some(comp_name) => clone_line_string(comp_name, gimli::DW_FORM_strp, out_strings)?,
+            None => write::LineString::String(Vec::new()),
+        };
+
         let mut out_program = write::LineProgram::new(
             out_encoding,
             line_encoding,
-            out_comp_dir.unwrap_or_else(|| write::LineString::String(Vec::new())),
+            out_comp_dir,
             out_comp_name,
             None,
         );
         let mut dirs = Vec::new();
         dirs.push(out_program.default_directory());
         for dir_attr in header.include_directories() {
-            let dir_id = out_program.add_directory(clone_attr_string(
-                dir_attr,
+            let dir_id = out_program.add_directory(clone_line_string(
+                dwarf.attr_string(unit, dir_attr.clone())?,
                 gimli::DW_FORM_string,
-                unit,
-                location_dwarf,
                 out_strings,
             )?);
             dirs.push(dir_id);
@@ -135,11 +95,9 @@ where
             let dir_index = file_entry.directory_index() + directory_index_correction;
             let dir_id = dirs[dir_index as usize];
             let file_id = out_program.add_file(
-                clone_attr_string(
-                    &file_entry.path_name(),
+                clone_line_string(
+                    dwarf.attr_string(unit, file_entry.path_name())?,
                     gimli::DW_FORM_string,
-                    unit,
-                    location_dwarf,
                     out_strings,
                 )?,
                 dir_id,
@@ -285,3 +243,22 @@ where
         Err(TransformError("Valid line program not found").into())
     }
 }
+
+fn clone_line_string<R>(
+    value: R,
+    form: gimli::DwForm,
+    out_strings: &mut write::StringTable,
+) -> Result<write::LineString, Error>
+where
+    R: Reader,
+{
+    let content = value.to_string_lossy()?.into_owned();
+    Ok(match form {
+        gimli::DW_FORM_strp => {
+            let id = out_strings.add(content);
+            write::LineString::StringRef(id)
+        }
+        gimli::DW_FORM_string => write::LineString::String(content.into()),
+        _ => bail!("DW_FORM_line_strp or other not supported"),
+    })
+}
diff --git a/crates/cranelift/src/debug/transform/range_info_builder.rs b/crates/cranelift/src/debug/transform/range_info_builder.rs
index 3f4138757..526c83e61 100644
--- a/crates/cranelift/src/debug/transform/range_info_builder.rs
+++ b/crates/cranelift/src/debug/transform/range_info_builder.rs
@@ -16,14 +16,13 @@ impl RangeInfoBuilder {
         dwarf: &gimli::Dwarf<R>,
         unit: &Unit<R, R::Offset>,
         entry: &DebuggingInformationEntry<R>,
-        cu_low_pc: u64,
     ) -> Result<Self, Error>
     where
         R: Reader,
     {
         if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
             let r = dwarf.ranges_offset_from_raw(unit, r);
-            return RangeInfoBuilder::from_ranges_ref(dwarf, unit, r, cu_low_pc);
+            return RangeInfoBuilder::from_ranges_ref(dwarf, unit, r);
         };
 
         let low_pc =
@@ -32,7 +31,7 @@ impl RangeInfoBuilder {
             } else if let Some(AttributeValue::DebugAddrIndex(i)) =
                 entry.attr_value(gimli::DW_AT_low_pc)?
             {
-                dwarf.debug_addr.get_address(4, unit.addr_base, i)?
+                dwarf.address(unit, i)?
             } else {
                 return Ok(RangeInfoBuilder::Undefined);
             };
@@ -50,19 +49,11 @@ impl RangeInfoBuilder {
         dwarf: &gimli::Dwarf<R>,
         unit: &Unit<R, R::Offset>,
         ranges: RangeListsOffset,
-        cu_low_pc: u64,
     ) -> Result<Self, Error>
     where
         R: Reader,
     {
-        let unit_encoding = unit.encoding();
-        let mut ranges = dwarf.ranges.ranges(
-            ranges,
-            unit_encoding,
-            cu_low_pc,
-            &dwarf.debug_addr,
-            unit.addr_base,
-        )?;
+        let mut ranges = dwarf.ranges(unit, ranges)?;
         let mut result = Vec::new();
         while let Some(range) = ranges.next()? {
             if range.begin >= range.end {
@@ -83,30 +74,22 @@ impl RangeInfoBuilder {
         unit: &Unit<R, R::Offset>,
         entry: &DebuggingInformationEntry<R>,
         addr_tr: &AddressTransform,
-        cu_low_pc: u64,
     ) -> Result<Self, Error>
     where
         R: Reader,
     {
-        let unit_encoding = unit.encoding();
         let addr =
             if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
                 addr
             } else if let Some(AttributeValue::DebugAddrIndex(i)) =
                 entry.attr_value(gimli::DW_AT_low_pc)?
             {
-                dwarf.debug_addr.get_address(4, unit.addr_base, i)?
+                dwarf.address(unit, i)?
             } else if let Some(AttributeValue::RangeListsRef(r)) =
                 entry.attr_value(gimli::DW_AT_ranges)?
             {
                 let r = dwarf.ranges_offset_from_raw(unit, r);
-                let mut ranges = dwarf.ranges.ranges(
-                    r,
-                    unit_encoding,
-                    cu_low_pc,
-                    &dwarf.debug_addr,
-                    unit.addr_base,
-                )?;
+                let mut ranges = dwarf.ranges(unit, r)?;
                 if let Some(range) = ranges.next()? {
                     range.begin
                 } else {
diff --git a/crates/cranelift/src/debug/transform/unit.rs b/crates/cranelift/src/debug/transform/unit.rs
index 9610f286d..8689566db 100644
--- a/crates/cranelift/src/debug/transform/unit.rs
+++ b/crates/cranelift/src/debug/transform/unit.rs
@@ -262,17 +262,6 @@ pub(crate) fn clone_unit(
     let mut pending_di_refs = PendingDebugInfoRefs::new();
     let mut stack = Vec::new();
 
-    let mut skeleton_die = None;
-
-    // Get entries in outer scope to avoid borrowing on short lived temporary.
-    let mut skeleton_entries = skeleton_unit.entries();
-    if split_unit.is_some() {
-        // From the spec, a skeleton unit has no children so we can assume the first, and only, entry is the DW_TAG_skeleton_unit (https://dwarfstd.org/doc/DWARF5.pdf).
-        if let Some(die_tuple) = skeleton_entries.next_dfs()? {
-            skeleton_die = Some(die_tuple.1);
-        }
-    }
-
     let skeleton_dwarf = &compilation.translations[module].debuginfo.dwarf;
     let memory_offset = &compilation.module_memory_offsets[module];
 
@@ -280,20 +269,16 @@ pub(crate) fn clone_unit(
     let dwarf = split_dwarf.unwrap_or(skeleton_dwarf);
     let unit = split_unit.unwrap_or(skeleton_unit);
     let mut entries = unit.entries();
-    let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) =
+    let (mut comp_unit, unit_id, file_map, file_index_base, wp_die_id, vmctx_die_id) =
         if let Some((depth_delta, entry)) = entries.next_dfs()? {
             assert_eq!(depth_delta, 0);
             let (out_line_program, debug_line_offset, file_map, file_index_base) =
                 clone_line_program(
-                    dwarf,
                     skeleton_dwarf,
-                    unit,
-                    entry,
-                    skeleton_die,
+                    skeleton_unit,
+                    unit.name,
                     addr_tr,
                     out_encoding,
-                    &skeleton_dwarf.debug_str,
-                    &skeleton_dwarf.debug_line,
                     out_strings,
                 )?;
 
@@ -303,7 +288,6 @@ pub(crate) fn clone_unit(
 
                 let root_id = comp_unit.root();
                 die_ref_map.insert(entry.offset(), root_id);
-                let cu_low_pc = unit.low_pc;
 
                 clone_die_attributes(
                     dwarf,
@@ -315,7 +299,6 @@ pub(crate) fn clone_unit(
                     root_id,
                     None,
                     None,
-                    cu_low_pc,
                     out_strings,
                     &mut pending_die_refs,
                     &mut pending_di_refs,
@@ -332,7 +315,6 @@ pub(crate) fn clone_unit(
                     unit_id,
                     file_map,
                     file_index_base,
-                    cu_low_pc,
                     wp_die_id,
                     vmctx_die_id,
                 )
@@ -387,7 +369,7 @@ pub(crate) fn clone_unit(
         current_value_range.update(new_stack_len);
         let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
             let range_builder =
-                RangeInfoBuilder::from_subprogram_die(dwarf, &unit, entry, addr_tr, cu_low_pc)?;
+                RangeInfoBuilder::from_subprogram_die(dwarf, &unit, entry, addr_tr)?;
             if let RangeInfoBuilder::Function(func) = range_builder {
                 let frame_info = compilation.function_frame_info(module, func);
                 current_value_range.push(new_stack_len, frame_info);
@@ -403,7 +385,7 @@ pub(crate) fn clone_unit(
             let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
             let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
             if high_pc.is_some() || ranges.is_some() {
-                let range_builder = RangeInfoBuilder::from(dwarf, &unit, entry, cu_low_pc)?;
+                let range_builder = RangeInfoBuilder::from(dwarf, &unit, entry)?;
                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
                 Some(range_builder)
             } else {
@@ -468,7 +450,6 @@ pub(crate) fn clone_unit(
             die_id,
             range_builder,
             current_scope_ranges.top(),
-            cu_low_pc,
             out_strings,
             &mut pending_die_refs,
             &mut pending_di_refs,
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 0ba5cd953..c5940b3e5 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -1484,6 +1484,12 @@ pub enum ConstOp {
     RefI31,
     RefNull,
     RefFunc(FuncIndex),
+    I32Add,
+    I32Sub,
+    I32Mul,
+    I64Add,
+    I64Sub,
+    I64Mul,
 }
 
 impl ConstOp {
@@ -1500,6 +1506,12 @@ impl ConstOp {
             O::RefFunc { function_index } => Self::RefFunc(FuncIndex::from_u32(function_index)),
             O::GlobalGet { global_index } => Self::GlobalGet(GlobalIndex::from_u32(global_index)),
             O::RefI31 => Self::RefI31,
+            O::I32Add => Self::I32Add,
+            O::I32Sub => Self::I32Sub,
+            O::I32Mul => Self::I32Mul,
+            O::I64Add => Self::I64Add,
+            O::I64Sub => Self::I64Sub,
+            O::I64Mul => Self::I64Mul,
             op => {
                 return Err(wasm_unsupported!(
                     "unsupported opcode in const expression at offset {offset:#x}: {op:?}",
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 555c3d714..a25cfd35c 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -259,9 +259,6 @@ impl Config {
             ret.cranelift_opt_level(OptLevel::Speed);
         }
 
-        // Not yet implemented in Wasmtime
-        ret.features.set(WasmFeatures::EXTENDED_CONST, false);
-
         // Conditionally enabled features depending on compile-time crate
         // features. Note that if these features are disabled then `Config` has
         // no way of re-enabling them.
@@ -285,6 +282,7 @@ impl Config {
         ret.wasm_multi_value(true);
         ret.wasm_bulk_memory(true);
         ret.wasm_simd(true);
+        ret.wasm_extended_const(true);
         ret.wasm_backtrace_details(WasmBacktraceDetails::Environment);
 
         ret
@@ -963,6 +961,17 @@ impl Config {
         self
     }
 
+    /// Configures whether the WebAssembly extended-const [proposal] will
+    /// be enabled for compilation.
+    ///
+    /// This is `true` by default.
+    ///
+    /// [proposal]: https://github.com/webassembly/extended-const
+    pub fn wasm_extended_const(&mut self, enable: bool) -> &mut Self {
+        self.features.set(WasmFeatures::EXTENDED_CONST, enable);
+        self
+    }
+
     /// Configures whether the WebAssembly component-model [proposal] will
     /// be enabled for compilation.
     ///
diff --git a/crates/wasmtime/src/runtime/vm/const_expr.rs b/crates/wasmtime/src/runtime/vm/const_expr.rs
index 55a6d0a8f..198673b93 100644
--- a/crates/wasmtime/src/runtime/vm/const_expr.rs
+++ b/crates/wasmtime/src/runtime/vm/const_expr.rs
@@ -78,6 +78,36 @@ impl ConstExprEvaluator {
                     let raw = VMGcRef::from_i31(i31).as_raw_u32();
                     self.stack.push(ValRaw::anyref(raw));
                 }
+                wasmtime_environ::ConstOp::I32Add => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_add(b)));
+                }
+                wasmtime_environ::ConstOp::I32Sub => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_sub(b)));
+                }
+                wasmtime_environ::ConstOp::I32Mul => {
+                    let b = self.pop()?.get_i32();
+                    let a = self.pop()?.get_i32();
+                    self.stack.push(ValRaw::i32(a.wrapping_mul(b)));
+                }
+                wasmtime_environ::ConstOp::I64Add => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_add(b)));
+                }
+                wasmtime_environ::ConstOp::I64Sub => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_sub(b)));
+                }
+                wasmtime_environ::ConstOp::I64Mul => {
+                    let b = self.pop()?.get_i64();
+                    let a = self.pop()?.get_i64();
+                    self.stack.push(ValRaw::i64(a.wrapping_mul(b)));
+                }
             }
         }
 
diff --git a/pulley/README.md b/pulley/README.md
index df31228f6..49aba7b82 100644
--- a/pulley/README.md
+++ b/pulley/README.md
@@ -47,18 +47,18 @@ to change, instructions to appear and disappear, and APIs to be overhauled.
 Here is the disassembly of `f(a, b) = a + b` in Pulley today:
 
 ```
-       0: 0e 1f f0                        xconst8 x31, -16
-       3: 12 20 20 1f                     xadd32 sp, sp, x31
-       7: 29 20 08 21                     store64_offset8 sp, 8, lr
-       b: 27 20 22                        store64 sp, fp
-       e: 0b 22 20                        xmov fp, sp
-      11: 12 00 00 01                     xadd32 x0, x0, x1
-      15: 0b 20 22                        xmov sp, fp
-      18: 25 21 20 08                     load64_offset8 lr, sp, 8
-      1c: 22 22 20                        load64 fp, sp
-      1f: 0e 1f 10                        xconst8 x31, 16
-      22: 12 20 20 1f                     xadd32 sp, sp, x31
-      26: 00                              ret
+       0: 0e 1a f0                        xconst8 x26, -16
+       3: 12 7b 6b                        xadd32 sp, sp, x26
+       6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+       a: 2a 1b 1d                        store64 sp, fp
+       d: 0b 1d 1b                        xmov fp, sp
+      10: 12 00 04                        xadd32 x0, x0, x1
+      13: 0b 1b 1d                        xmov sp, fp
+      16: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+      1a: 22 1d 1b                        load64 fp, sp
+      1d: 0e 1a 10                        xconst8 x26, 16
+      20: 12 7b 6b                        xadd32 sp, sp, x26
+      23: 00                              ret
 ```
 
 Note that there are a number of things that could be improved here:
diff --git a/pulley/fuzz/src/interp.rs b/pulley/fuzz/src/interp.rs
index 079a4c3a1..9acdb7625 100644
--- a/pulley/fuzz/src/interp.rs
+++ b/pulley/fuzz/src/interp.rs
@@ -70,8 +70,6 @@ fn op_is_safe_for_fuzzing(op: &Op) -> bool {
         Op::Xconst16(op::Xconst16 { dst, .. }) => !dst.is_special(),
         Op::Xconst32(op::Xconst32 { dst, .. }) => !dst.is_special(),
         Op::Xconst64(op::Xconst64 { dst, .. }) => !dst.is_special(),
-        Op::Xadd32(op::Xadd32 { dst, .. }) => !dst.is_special(),
-        Op::Xadd64(op::Xadd64 { dst, .. }) => !dst.is_special(),
         Op::Load32U(_) => false,
         Op::Load32S(_) => false,
         Op::Load64(_) => false,
@@ -93,18 +91,20 @@ fn op_is_safe_for_fuzzing(op: &Op) -> bool {
         Op::BitcastFloatFromInt64(_) => true,
         Op::ExtendedOp(op) => extended_op_is_safe_for_fuzzing(op),
         Op::Call(_) => false,
-        Op::Xeq64(Xeq64 { dst, .. }) => !dst.is_special(),
-        Op::Xneq64(Xneq64 { dst, .. }) => !dst.is_special(),
-        Op::Xslt64(Xslt64 { dst, .. }) => !dst.is_special(),
-        Op::Xslteq64(Xslteq64 { dst, .. }) => !dst.is_special(),
-        Op::Xult64(Xult64 { dst, .. }) => !dst.is_special(),
-        Op::Xulteq64(Xulteq64 { dst, .. }) => !dst.is_special(),
-        Op::Xeq32(Xeq32 { dst, .. }) => !dst.is_special(),
-        Op::Xneq32(Xneq32 { dst, .. }) => !dst.is_special(),
-        Op::Xslt32(Xslt32 { dst, .. }) => !dst.is_special(),
-        Op::Xslteq32(Xslteq32 { dst, .. }) => !dst.is_special(),
-        Op::Xult32(Xult32 { dst, .. }) => !dst.is_special(),
-        Op::Xulteq32(Xulteq32 { dst, .. }) => !dst.is_special(),
+        Op::Xadd32(Xadd32 { operands, .. })
+        | Op::Xadd64(Xadd64 { operands, .. })
+        | Op::Xeq64(Xeq64 { operands, .. })
+        | Op::Xneq64(Xneq64 { operands, .. })
+        | Op::Xslt64(Xslt64 { operands, .. })
+        | Op::Xslteq64(Xslteq64 { operands, .. })
+        | Op::Xult64(Xult64 { operands, .. })
+        | Op::Xulteq64(Xulteq64 { operands, .. })
+        | Op::Xeq32(Xeq32 { operands, .. })
+        | Op::Xneq32(Xneq32 { operands, .. })
+        | Op::Xslt32(Xslt32 { operands, .. })
+        | Op::Xslteq32(Xslteq32 { operands, .. })
+        | Op::Xult32(Xult32 { operands, .. })
+        | Op::Xulteq32(Xulteq32 { operands, .. }) => !operands.dst.is_special(),
     }
 }
 
diff --git a/pulley/src/decode.rs b/pulley/src/decode.rs
index dcc219836..74be7e153 100644
--- a/pulley/src/decode.rs
+++ b/pulley/src/decode.rs
@@ -375,6 +375,15 @@ impl Decode for PcRelOffset {
     }
 }
 
+impl<R: Reg> Decode for BinaryOperands<R> {
+    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
+    where
+        T: BytecodeStream,
+    {
+        u16::decode(bytecode).map(|bits| Self::from_bits(bits))
+    }
+}
+
 /// A Pulley bytecode decoder.
 ///
 /// Does not materialize bytecode instructions, instead all decoding methods are
diff --git a/pulley/src/disas.rs b/pulley/src/disas.rs
index f7f2b9241..6900cfa39 100644
--- a/pulley/src/disas.rs
+++ b/pulley/src/disas.rs
@@ -127,6 +127,16 @@ impl Disas for PcRelOffset {
     }
 }
 
+impl<R: Disas> Disas for BinaryOperands<R> {
+    fn disas(&self, position: usize, disas: &mut String) {
+        self.dst.disas(position, disas);
+        write!(disas, ", ").unwrap();
+        self.src1.disas(position, disas);
+        write!(disas, ", ").unwrap();
+        self.src2.disas(position, disas);
+    }
+}
+
 macro_rules! impl_disas {
     (
         $(
diff --git a/pulley/src/encode.rs b/pulley/src/encode.rs
index fa2a3597a..686ca9946 100644
--- a/pulley/src/encode.rs
+++ b/pulley/src/encode.rs
@@ -118,6 +118,15 @@ impl Encode for PcRelOffset {
     }
 }
 
+impl<R: Reg> Encode for BinaryOperands<R> {
+    fn encode<E>(&self, sink: &mut E)
+    where
+        E: Extend<u8>,
+    {
+        self.to_bits().encode(sink);
+    }
+}
+
 macro_rules! impl_encoders {
     (
         $(
diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs
index f861db27a..d03d6ab99 100644
--- a/pulley/src/interp.rs
+++ b/pulley/src/interp.rs
@@ -76,9 +76,9 @@ impl Vm {
         // NB: make sure this method stays in sync with
         // `PulleyMachineDeps::compute_arg_locs`!
 
-        let mut x_args = (0..16).map(|x| XReg::unchecked_new(x));
-        let mut f_args = (0..16).map(|f| FReg::unchecked_new(f));
-        let mut v_args = (0..16).map(|v| VReg::unchecked_new(v));
+        let mut x_args = (0..16).map(|x| XReg::new_unchecked(x));
+        let mut f_args = (0..16).map(|f| FReg::new_unchecked(f));
+        let mut v_args = (0..16).map(|v| VReg::new_unchecked(v));
 
         for arg in args {
             match arg {
@@ -99,9 +99,9 @@ impl Vm {
 
         self.run(func.as_ptr())?;
 
-        let mut x_rets = (0..16).map(|x| XReg::unchecked_new(x));
-        let mut f_rets = (0..16).map(|f| FReg::unchecked_new(f));
-        let mut v_rets = (0..16).map(|v| VReg::unchecked_new(v));
+        let mut x_rets = (0..16).map(|x| XReg::new_unchecked(x));
+        let mut f_rets = (0..16).map(|f| FReg::new_unchecked(f));
+        let mut v_rets = (0..16).map(|v| VReg::new_unchecked(v));
 
         Ok(rets.into_iter().map(move |ty| match ty {
             RegType::XReg => match x_rets.next() {
@@ -563,10 +563,9 @@ impl MachineState {
         let sp = &mut state.stack[..];
         let sp = sp.as_mut_ptr();
         let sp = unsafe { sp.add(len) };
-        state[XReg::SP] = XRegVal::new_ptr(sp);
-
-        state[XReg::FP] = XRegVal::new_i64(-1);
-        state[XReg::LR] = XRegVal::new_i64(-1);
+        state[XReg::sp] = XRegVal::new_ptr(sp);
+        state[XReg::fp] = XRegVal::new_i64(-1);
+        state[XReg::lr] = XRegVal::new_i64(-1);
 
         state
     }
@@ -606,10 +605,10 @@ impl OpVisitor for InterpreterVisitor<'_> {
     type Return = Continuation;
 
     fn ret(&mut self) -> Self::Return {
-        if self.state[XReg::LR].get_u64() == u64::MAX {
+        if self.state[XReg::lr].get_u64() == u64::MAX {
             Continuation::ReturnToHost
         } else {
-            let return_addr = self.state[XReg::LR].get_ptr();
+            let return_addr = self.state[XReg::lr].get_ptr();
             self.pc = unsafe { UnsafeBytecodeStream::new(return_addr) };
             // log::trace!("returning to {return_addr:#p}");
             Continuation::Continue
@@ -618,7 +617,7 @@ impl OpVisitor for InterpreterVisitor<'_> {
 
     fn call(&mut self, offset: PcRelOffset) -> Self::Return {
         let return_addr = u64::try_from(self.pc.as_ptr() as usize).unwrap();
-        self.state[XReg::LR].set_u64(return_addr);
+        self.state[XReg::lr].set_u64(return_addr);
         self.pc_rel_jump(offset, 5)
     }
 
@@ -742,101 +741,101 @@ impl OpVisitor for InterpreterVisitor<'_> {
         Continuation::Continue
     }
 
-    fn xadd32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u32();
-        let b = self.state[src2].get_u32();
-        self.state[dst].set_u32(a.wrapping_add(b));
+    fn xadd32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u32();
+        let b = self.state[operands.src2].get_u32();
+        self.state[operands.dst].set_u32(a.wrapping_add(b));
         Continuation::Continue
     }
 
-    fn xadd64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u64();
-        let b = self.state[src2].get_u64();
-        self.state[dst].set_u64(a.wrapping_add(b));
+    fn xadd64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u64();
+        let b = self.state[operands.src2].get_u64();
+        self.state[operands.dst].set_u64(a.wrapping_add(b));
         Continuation::Continue
     }
 
-    fn xeq64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u64();
-        let b = self.state[src2].get_u64();
-        self.state[dst].set_u64(u64::from(a == b));
+    fn xeq64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u64();
+        let b = self.state[operands.src2].get_u64();
+        self.state[operands.dst].set_u64(u64::from(a == b));
         Continuation::Continue
     }
 
-    fn xneq64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u64();
-        let b = self.state[src2].get_u64();
-        self.state[dst].set_u64(u64::from(a != b));
+    fn xneq64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u64();
+        let b = self.state[operands.src2].get_u64();
+        self.state[operands.dst].set_u64(u64::from(a != b));
         Continuation::Continue
     }
 
-    fn xslt64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_i64();
-        let b = self.state[src2].get_i64();
-        self.state[dst].set_u64(u64::from(a < b));
+    fn xslt64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_i64();
+        let b = self.state[operands.src2].get_i64();
+        self.state[operands.dst].set_u64(u64::from(a < b));
         Continuation::Continue
     }
 
-    fn xslteq64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_i64();
-        let b = self.state[src2].get_i64();
-        self.state[dst].set_u64(u64::from(a <= b));
+    fn xslteq64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_i64();
+        let b = self.state[operands.src2].get_i64();
+        self.state[operands.dst].set_u64(u64::from(a <= b));
         Continuation::Continue
     }
 
-    fn xult64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u64();
-        let b = self.state[src2].get_u64();
-        self.state[dst].set_u64(u64::from(a < b));
+    fn xult64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u64();
+        let b = self.state[operands.src2].get_u64();
+        self.state[operands.dst].set_u64(u64::from(a < b));
         Continuation::Continue
     }
 
-    fn xulteq64(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u64();
-        let b = self.state[src2].get_u64();
-        self.state[dst].set_u64(u64::from(a <= b));
+    fn xulteq64(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u64();
+        let b = self.state[operands.src2].get_u64();
+        self.state[operands.dst].set_u64(u64::from(a <= b));
         Continuation::Continue
     }
 
-    fn xeq32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u32();
-        let b = self.state[src2].get_u32();
-        self.state[dst].set_u64(u64::from(a == b));
+    fn xeq32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u32();
+        let b = self.state[operands.src2].get_u32();
+        self.state[operands.dst].set_u64(u64::from(a == b));
         Continuation::Continue
     }
 
-    fn xneq32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u32();
-        let b = self.state[src2].get_u32();
-        self.state[dst].set_u64(u64::from(a != b));
+    fn xneq32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u32();
+        let b = self.state[operands.src2].get_u32();
+        self.state[operands.dst].set_u64(u64::from(a != b));
         Continuation::Continue
     }
 
-    fn xslt32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_i32();
-        let b = self.state[src2].get_i32();
-        self.state[dst].set_u64(u64::from(a < b));
+    fn xslt32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_i32();
+        let b = self.state[operands.src2].get_i32();
+        self.state[operands.dst].set_u64(u64::from(a < b));
         Continuation::Continue
     }
 
-    fn xslteq32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_i32();
-        let b = self.state[src2].get_i32();
-        self.state[dst].set_u64(u64::from(a <= b));
+    fn xslteq32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_i32();
+        let b = self.state[operands.src2].get_i32();
+        self.state[operands.dst].set_u64(u64::from(a <= b));
         Continuation::Continue
     }
 
-    fn xult32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u32();
-        let b = self.state[src2].get_u32();
-        self.state[dst].set_u64(u64::from(a < b));
+    fn xult32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u32();
+        let b = self.state[operands.src2].get_u32();
+        self.state[operands.dst].set_u64(u64::from(a < b));
         Continuation::Continue
     }
 
-    fn xulteq32(&mut self, dst: XReg, src1: XReg, src2: XReg) -> Self::Return {
-        let a = self.state[src1].get_u32();
-        let b = self.state[src2].get_u32();
-        self.state[dst].set_u64(u64::from(a <= b));
+    fn xulteq32(&mut self, operands: BinaryOperands<XReg>) -> Self::Return {
+        let a = self.state[operands.src1].get_u32();
+        let b = self.state[operands.src2].get_u32();
+        self.state[operands.dst].set_u64(u64::from(a <= b));
         Continuation::Continue
     }
 
@@ -1024,7 +1023,7 @@ impl ExtendedOpVisitor for InterpreterVisitor<'_> {
     }
 
     fn get_sp(&mut self, dst: XReg) -> Self::Return {
-        let sp = self.state[XReg::SP].get_u64();
+        let sp = self.state[XReg::sp].get_u64();
         self.state[dst].set_u64(sp);
         Continuation::Continue
     }
diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs
index 644c295ef..819f69fc2 100644
--- a/pulley/src/lib.rs
+++ b/pulley/src/lib.rs
@@ -11,10 +11,6 @@ extern crate std;
 #[allow(unused_extern_crates)] // Some cfg's don't use this.
 extern crate alloc;
 
-// TODO: make `struct BinaryOpRegs<T> { dst: T, src1: T, src2: T }` or something
-// and pack it into 2 bytes (5 bits for each operand; 2**5 = 32 possible
-// registers, and then only special isntructions to access special x registers)
-
 /// Calls the given macro with each opcode.
 macro_rules! for_each_op {
     ( $macro:ident ) => {
@@ -69,35 +65,35 @@ macro_rules! for_each_op {
             /// 32-bit wrapping addition: `low32(dst) = low32(src1) + low32(src2)`.
             ///
             /// The upper 32-bits of `dst` are unmodified.
-            xadd32 = Xadd32 { dst: XReg, src1: XReg, src2: XReg };
+            xadd32 = Xadd32 { operands: BinaryOperands<XReg> };
 
             /// 64-bit wrapping addition: `dst = src1 + src2`.
-            xadd64 = Xadd64 { dst: XReg, src1: XReg, src2: XReg };
+            xadd64 = Xadd64 { operands: BinaryOperands<XReg> };
 
             /// 64-bit equality.
-            xeq64 = Xeq64 { dst: XReg, src1: XReg, src2: XReg };
+            xeq64 = Xeq64 { operands: BinaryOperands<XReg> };
             /// 64-bit inequality.
-            xneq64 = Xneq64 { dst: XReg, src1: XReg, src2: XReg };
+            xneq64 = Xneq64 { operands: BinaryOperands<XReg> };
             /// 64-bit signed less-than.
-            xslt64 = Xslt64 { dst: XReg, src1: XReg, src2: XReg };
+            xslt64 = Xslt64 { operands: BinaryOperands<XReg> };
             /// 64-bit signed less-than-equal.
-            xslteq64 = Xslteq64 { dst: XReg, src1: XReg, src2: XReg };
+            xslteq64 = Xslteq64 { operands: BinaryOperands<XReg> };
             /// 64-bit unsigned less-than.
-            xult64 = Xult64 { dst: XReg, src1: XReg, src2: XReg };
+            xult64 = Xult64 { operands: BinaryOperands<XReg> };
             /// 64-bit unsigned less-than-equal.
-            xulteq64 = Xulteq64 { dst: XReg, src1: XReg, src2: XReg };
+            xulteq64 = Xulteq64 { operands: BinaryOperands<XReg> };
             /// 32-bit equality.
-            xeq32 = Xeq32 { dst: XReg, src1: XReg, src2: XReg };
+            xeq32 = Xeq32 { operands: BinaryOperands<XReg> };
             /// 32-bit inequality.
-            xneq32 = Xneq32 { dst: XReg, src1: XReg, src2: XReg };
+            xneq32 = Xneq32 { operands: BinaryOperands<XReg> };
             /// 32-bit signed less-than.
-            xslt32 = Xslt32 { dst: XReg, src1: XReg, src2: XReg };
+            xslt32 = Xslt32 { operands: BinaryOperands<XReg> };
             /// 32-bit signed less-than-equal.
-            xslteq32 = Xslteq32 { dst: XReg, src1: XReg, src2: XReg };
+            xslteq32 = Xslteq32 { operands: BinaryOperands<XReg> };
             /// 32-bit unsigned less-than.
-            xult32 = Xult32 { dst: XReg, src1: XReg, src2: XReg };
+            xult32 = Xult32 { operands: BinaryOperands<XReg> };
             /// 32-bit unsigned less-than-equal.
-            xulteq32 = Xulteq32 { dst: XReg, src1: XReg, src2: XReg };
+            xulteq32 = Xulteq32 { operands: BinaryOperands<XReg> };
 
             /// `dst = zero_extend(load32(ptr))`
             load32_u = Load32U { dst: XReg, ptr: XReg };
diff --git a/pulley/src/regs.rs b/pulley/src/regs.rs
index c8260f7ff..062e5eb1f 100644
--- a/pulley/src/regs.rs
+++ b/pulley/src/regs.rs
@@ -1,181 +1,195 @@
 //! Pulley registers.
 
+use core::hash::Hash;
 use core::{fmt, ops::Range};
 
-macro_rules! define_registers {
-    (
-        $(
-            $( #[$attr:meta] )*
-            pub struct $name:ident = $range:expr;
-        )*
-) => {
-        $(
-            $( #[ $attr ] )*
-            #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
-            pub struct $name(u8);
-
-            impl fmt::Debug for $name {
-                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
-                    fmt::Display::fmt(self, f)
-                }
+/// Trait for common register operations.
+pub trait Reg: Sized + Copy + Eq + Ord + Hash + Into<AnyReg> + fmt::Debug + fmt::Display {
+    /// Range of valid register indices.
+    const RANGE: Range<u8>;
+
+    /// Convert a register index to a register, without bounds checking.
+    unsafe fn new_unchecked(index: u8) -> Self;
+
+    /// Convert a register index to a register, with bounds checking.
+    fn new(index: u8) -> Option<Self> {
+        if Self::RANGE.contains(&index) {
+            Some(unsafe { Self::new_unchecked(index) })
+        } else {
+            None
+        }
+    }
+
+    /// Convert a register to its index.
+    fn to_u8(self) -> u8;
+
+    /// Convert a register to its index.
+    fn index(self) -> usize {
+        self.to_u8().into()
+    }
+}
+
+macro_rules! impl_reg {
+    ($reg_ty:ty, $any:ident, $range:expr) => {
+        impl From<$reg_ty> for AnyReg {
+            fn from(r: $reg_ty) -> Self {
+                AnyReg::$any(r)
             }
+        }
 
-            impl $name {
-                /// The valid register range for this register class.
-                pub const RANGE: Range<u8> = $range;
-
-                /// Construct a new register of this class.
-                #[inline]
-                pub fn new(index: u8) -> Option<Self> {
-                    if Self::RANGE.start <= index && index < Self::RANGE.end {
-                        Some(unsafe { Self::unchecked_new(index) })
-                    } else {
-                        None
-                    }
-                }
+        impl fmt::Display for $reg_ty {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                fmt::Debug::fmt(&self, f)
+            }
+        }
 
-                /// Construct a new register of this class without checking that
-                /// `index` is a valid register index.
-                #[inline]
-                pub unsafe fn unchecked_new(index: u8) -> Self {
-                    debug_assert!(Self::RANGE.start <= index && index < Self::RANGE.end);
-                    Self(index)
-                }
+        impl Reg for $reg_ty {
+            const RANGE: Range<u8> = $range;
 
-                /// Get this register's index.
-                #[inline]
-                pub fn index(&self) -> usize {
-                    usize::from(self.0)
-                }
+            unsafe fn new_unchecked(index: u8) -> Self {
+                core::mem::transmute(index)
             }
 
-            #[cfg(feature = "arbitrary")]
-            impl<'a> arbitrary::Arbitrary<'a> for $name {
-                fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
-                    let index = u.int_in_range(Self::RANGE.start..=Self::RANGE.end - 1)?;
-                    Ok(Self(index))
-                }
+            fn to_u8(self) -> u8 {
+                self as u8
             }
-        )*
-    }
+        }
+    };
 }
 
-define_registers! {
-    /// An `x` register: integers.
-    pub struct XReg = 0..37;
+/// An `x` register: integers.
+#[repr(u8)]
+#[derive(Debug,Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+#[allow(non_camel_case_types, missing_docs)]
+#[rustfmt::skip]
+pub enum XReg {
+    x0,  x1,  x2,  x3,  x4,  x5,  x6,  x7,  x8,  x9,
+    x10, x11, x12, x13, x14, x15, x16, x17, x18, x19,
+    x20, x21, x22, x23, x24, x25, x26,
 
-    /// An `f` register: floats.
-    pub struct FReg = 0..32;
+    /// The special `sp` stack pointer register.
+    sp,
 
-    /// A `v` register: vectors.
-    pub struct VReg = 0..32;
+    /// The special `lr` link register.
+    lr,
+
+    /// The special `fp` frame pointer register.
+    fp,
+
+    /// The special `spilltmp0` scratch register.
+    spilltmp0,
+
+    /// The special `spilltmp1` scratch register.
+    spilltmp1,
 }
 
+impl XReg {
+    /// Is this `x` register a special register?
+    pub fn is_special(self) -> bool {
+        matches!(
+            self,
+            Self::sp | Self::lr | Self::fp | Self::spilltmp0 | Self::spilltmp1
+        )
+    }
+}
+
+/// An `f` register: floats.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+#[allow(non_camel_case_types, missing_docs)]
+#[rustfmt::skip]
+pub enum FReg {
+    f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7,  f8,  f9,
+    f10, f11, f12, f13, f14, f15, f16, f17, f18, f19,
+    f20, f21, f22, f23, f24, f25, f26, f27, f28, f29,
+    f30, f31,
+}
+
+/// A `v` register: vectors.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+#[allow(non_camel_case_types, missing_docs)]
+#[rustfmt::skip]
+pub enum VReg {
+    v0,  v1,  v2,  v3,  v4,  v5,  v6,  v7,  v8,  v9,
+    v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
+    v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
+    v30, v31,
+}
+
+impl_reg!(XReg, X, 0..32);
+impl_reg!(FReg, F, 0..32);
+impl_reg!(VReg, V, 0..32);
+
 /// Any register, regardless of class.
 ///
 /// Never appears inside an instruction -- instructions always name a particular
 /// class of register -- but this is useful for testing and things like that.
 #[allow(missing_docs)]
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
 pub enum AnyReg {
     X(XReg),
     F(FReg),
     V(VReg),
 }
 
-impl From<XReg> for AnyReg {
-    fn from(x: XReg) -> Self {
-        Self::X(x)
-    }
-}
-
-impl From<FReg> for AnyReg {
-    fn from(f: FReg) -> Self {
-        Self::F(f)
-    }
-}
-
-impl From<VReg> for AnyReg {
-    fn from(v: VReg) -> Self {
-        Self::V(v)
-    }
-}
-
 impl fmt::Display for AnyReg {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            AnyReg::X(r) => fmt::Display::fmt(r, f),
-            AnyReg::F(r) => fmt::Display::fmt(r, f),
-            AnyReg::V(r) => fmt::Display::fmt(r, f),
-        }
+        fmt::Debug::fmt(self, f)
     }
 }
 
 impl fmt::Debug for AnyReg {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
-        fmt::Display::fmt(self, f)
-    }
-}
-
-#[cfg(feature = "arbitrary")]
-impl<'a> arbitrary::Arbitrary<'a> for AnyReg {
-    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
-        match u.int_in_range(0..=2)? {
-            0 => Ok(AnyReg::X(u.arbitrary()?)),
-            1 => Ok(AnyReg::F(u.arbitrary()?)),
-            2 => Ok(AnyReg::V(u.arbitrary()?)),
-            _ => unreachable!(),
+        match self {
+            AnyReg::X(r) => fmt::Debug::fmt(r, f),
+            AnyReg::F(r) => fmt::Debug::fmt(r, f),
+            AnyReg::V(r) => fmt::Debug::fmt(r, f),
         }
     }
 }
 
-impl XReg {
-    /// The valid special register range.
-    pub const SPECIAL_RANGE: Range<u8> = 32..37;
-
-    /// The special `sp` stack pointer register.
-    pub const SP: Self = Self(32);
-
-    /// The special `lr` link register.
-    pub const LR: Self = Self(33);
-
-    /// The special `fp` frame pointer register.
-    pub const FP: Self = Self(34);
-
-    /// The special `spilltmp0` scratch register.
-    pub const SPILL_TMP_0: Self = Self(35);
-
-    /// The special `spilltmp1` scratch register.
-    pub const SPILL_TMP_1: Self = Self(36);
-
-    /// Is this `x` register a special register?
-    pub fn is_special(&self) -> bool {
-        self.0 >= Self::SPECIAL_RANGE.start
-    }
+/// Operands to a binary operation, packed into a 16-bit word (5 bits per register).
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+pub struct BinaryOperands<R> {
+    /// The destination register, packed in bits 0..5.
+    pub dst: R,
+    /// The frist source register, packed in bits 5..10.
+    pub src1: R,
+    /// The frist source register, packed in bits 10..15.
+    pub src2: R,
 }
 
-impl fmt::Display for XReg {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            Self::SP => write!(f, "sp"),
-            Self::LR => write!(f, "lr"),
-            Self::FP => write!(f, "fp"),
-            Self::SPILL_TMP_0 => write!(f, "spilltmp0"),
-            Self::SPILL_TMP_1 => write!(f, "spilltmp1"),
-            Self(x) => write!(f, "x{x}"),
+impl<R: Reg> BinaryOperands<R> {
+    /// Convenience constructor for applying `Into`
+    pub fn new(dst: impl Into<R>, src1: impl Into<R>, src2: impl Into<R>) -> Self {
+        Self {
+            dst: dst.into(),
+            src1: src1.into(),
+            src2: src2.into(),
         }
     }
-}
 
-impl fmt::Display for FReg {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "f{}", self.0)
+    /// Convert to dense 16 bit encoding.
+    pub fn to_bits(self) -> u16 {
+        let dst = self.dst.to_u8();
+        let src1 = self.src1.to_u8();
+        let src2 = self.src2.to_u8();
+        (dst as u16) | ((src1 as u16) << 5) | ((src2 as u16) << 10)
     }
-}
 
-impl fmt::Display for VReg {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "v{}", self.0)
+    /// Convert from dense 16 bit encoding. The topmost bit is ignored.
+    pub fn from_bits(bits: u16) -> Self {
+        Self {
+            dst: R::new((bits & 0b11111) as u8).unwrap(),
+            src1: R::new(((bits >> 5) & 0b11111) as u8).unwrap(),
+            src2: R::new(((bits >> 10) & 0b11111) as u8).unwrap(),
+        }
     }
 }
 
@@ -185,17 +199,37 @@ mod tests {
 
     #[test]
     fn special_x_regs() {
-        assert!(XReg::SP.is_special());
-        assert!(XReg::LR.is_special());
-        assert!(XReg::FP.is_special());
-        assert!(XReg::SPILL_TMP_0.is_special());
-        assert!(XReg::SPILL_TMP_1.is_special());
+        assert!(XReg::sp.is_special());
+        assert!(XReg::lr.is_special());
+        assert!(XReg::fp.is_special());
+        assert!(XReg::spilltmp0.is_special());
+        assert!(XReg::spilltmp1.is_special());
     }
 
     #[test]
     fn not_special_x_regs() {
-        for i in 0..32 {
+        for i in 0..27 {
             assert!(!XReg::new(i).unwrap().is_special());
         }
     }
+
+    #[test]
+    fn binary_operands() {
+        let mut i = 0;
+        for src2 in XReg::RANGE {
+            for src1 in XReg::RANGE {
+                for dst in XReg::RANGE {
+                    let operands = BinaryOperands {
+                        dst: XReg::new(dst).unwrap(),
+                        src1: XReg::new(src1).unwrap(),
+                        src2: XReg::new(src2).unwrap(),
+                    };
+                    assert_eq!(operands.to_bits(), i);
+                    assert_eq!(BinaryOperands::from_bits(i), operands);
+                    assert_eq!(BinaryOperands::from_bits(0x8000 | i), operands);
+                    i += 1;
+                }
+            }
+        }
+    }
 }


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 e6f2b4c22..0b50795ec 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -6477,6 +6477,32 @@ fn test_aarch64_binemit() {
         "fmsub d15, d30, d31, d1",
     ));
 
+    insns.push((
+        Inst::FpuRRRR {
+            fpu_op: FPUOp3::NMAdd,
+            size: ScalarSize::Size64,
+            rd: writable_vreg(15),
+            rn: vreg(30),
+            rm: vreg(31),
+            ra: vreg(1),
+        },
+        "CF077F1F",
+        "fnmadd d15, d30, d31, d1",
+    ));
+
+    insns.push((
+        Inst::FpuRRRR {
+            fpu_op: FPUOp3::NMSub,
+            size: ScalarSize::Size64,
+            rd: writable_vreg(15),
+            rn: vreg(30),
+            rm: vreg(31),
+            ra: vreg(1),
+        },
+        "CF877F1F",
+        "fnmsub d15, d30, d31, d1",
+    ));
+
     insns.push((
         Inst::FpuRRI {
             fpu_op: FPUOpRI::UShr32(FPURightShiftImm::maybe_from_u8(32, 32).unwrap()),
diff --git a/cranelift/filetests/filetests/isa/aarch64/fma.clif b/cranelift/filetests/filetests/isa/aarch64/fma.clif
index 12e0f081a..d44099cec 100644
--- a/cranelift/filetests/filetests/isa/aarch64/fma.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/fma.clif
@@ -67,6 +67,72 @@ block0(v0: f64, v1: f64, v2: f64):
 ;   fmsub d0, d0, d1, d2
 ;   ret
 
+function %fnmadd_f32(f32, f32, f32) -> f32 {
+block0(v0: f32, v1: f32, v2: f32):
+    v3 = fneg v1
+    v4 = fneg v2
+    v5 = fma v0, v3, v4
+    return v5
+}
+; VCode:
+; block0:
+;   fnmadd s0, s0, s1, s2
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   fnmadd s0, s0, s1, s2
+;   ret
+
+function %fnmadd_f64(f64, f64, f64) -> f64 {
+block0(v0: f64, v1: f64, v2: f64):
+    v3 = fneg v1
+    v4 = fneg v2
+    v5 = fma v0, v3, v4
+    return v5
+}
+; VCode:
+; block0:
+;   fnmadd d0, d0, d1, d2
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   fnmadd d0, d0, d1, d2
+;   ret
+
+function %fnmadd_f32(f32, f32, f32) -> f32 {
+block0(v0: f32, v1: f32, v2: f32):
+    v3 = fneg v2
+    v4 = fma v0, v1, v3
+    return v4
+}
+; VCode:
+; block0:
+;   fnmsub s0, s0, s1, s2
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   fnmsub s0, s0, s1, s2
+;   ret
+
+function %fnmadd_f64(f64, f64, f64) -> f64 {
+block0(v0: f64, v1: f64, v2: f64):
+    v3 = fneg v2
+    v4 = fma v0, v1, v3
+    return v4
+}
+; VCode:
+; block0:
+;   fnmsub d0, d0, d1, d2
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   fnmsub d0, d0, d1, d2
+;   ret
+
 function %fma_f32x4(f32x4, f32x4, f32x4) -> f32x4 {
 block0(v0: f32x4, v1: f32x4, v2: f32x4):
     v3 = fma v0, v1, v2
diff --git a/cranelift/filetests/filetests/isa/pulley32/brif.clif b/cranelift/filetests/filetests/isa/pulley32/brif.clif
index 1cbff38e4..b2f943d7c 100644
--- a/cranelift/filetests/filetests/isa/pulley32/brif.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/brif.clif
@@ -147,12 +147,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 05 00 01                     xeq32 x5, x0, x1
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 1a 05 04                        xeq32 x5, x0, x1
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
 function %brif_icmp_i16(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -180,12 +180,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 05 00 01                     xneq32 x5, x0, x1
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 1b 05 04                        xneq32 x5, x0, x1
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
 function %brif_icmp_i32(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -244,10 +244,10 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 05 01 00                     xulteq64 x5, x1, x0
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 19 25 00                        xulteq64 x5, x1, x0
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/call.clif b/cranelift/filetests/filetests/isa/pulley32/call.clif
index 15e22724c..d1f2dfe9a 100644
--- a/cranelift/filetests/filetests/isa/pulley32/call.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/call.clif
@@ -12,35 +12,35 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [1204185006387685820006398, 4294967295] }, callee_pop_size: 0 }
 ;   x0 = xconst8 1
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 01 00 00 00 00                  call 0x0    // target = 0x14
-;       19: 0e 00 01                        xconst8 x0, 1
-;       1c: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       20: 22 22 20                        load64 fp, sp
-;       23: 0e 23 10                        xconst8 spilltmp0, 16
-;       26: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       2a: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 01 00 00 00 00                  call 0x0    // target = 0x13
+;       18: 0e 00 01                        xconst8 x0, 1
+;       1b: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       1f: 22 1d 1b                        load64 fp, sp
+;       22: 0e 1e 10                        xconst8 spilltmp0, 16
+;       25: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       28: 00                              ret
 
 function %colocated_args_i32_rets_i32() -> i32 {
     fn0 = colocated %g(i32) -> i32
@@ -53,35 +53,35 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [1204185006387685820006398, 4294967295] }, callee_pop_size: 0 }
 ;   x0 = xconst8 1
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 01 00 00 00 00                  call 0x0    // target = 0x14
-;       19: 0e 00 01                        xconst8 x0, 1
-;       1c: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       20: 22 22 20                        load64 fp, sp
-;       23: 0e 23 10                        xconst8 spilltmp0, 16
-;       26: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       2a: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 01 00 00 00 00                  call 0x0    // target = 0x13
+;       18: 0e 00 01                        xconst8 x0, 1
+;       1b: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       1f: 22 1d 1b                        load64 fp, sp
+;       22: 0e 1e 10                        xconst8 spilltmp0, 16
+;       25: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       28: 00                              ret
 
 function %colocated_args_i64_i32_i64_i32() {
     fn0 = colocated %g(i64, i32, i64, i32)
@@ -96,39 +96,39 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   x1 = xconst8 1
 ;   x2 = xconst8 2
 ;   x3 = xconst8 3
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }, CallArgPair { vreg: p1i, preg: p1i }, CallArgPair { vreg: p2i, preg: p2i }, CallArgPair { vreg: p3i, preg: p3i }], defs: [], clobbers: PRegSet { bits: [1204185006387685820006399, 4294967295] }, callee_pop_size: 0 }
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 0e 01 01                        xconst8 x1, 1
-;       17: 0e 02 02                        xconst8 x2, 2
-;       1a: 0e 03 03                        xconst8 x3, 3
-;       1d: 01 00 00 00 00                  call 0x0    // target = 0x1d
-;       22: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       26: 22 22 20                        load64 fp, sp
-;       29: 0e 23 10                        xconst8 spilltmp0, 16
-;       2c: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       30: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 0e 01 01                        xconst8 x1, 1
+;       16: 0e 02 02                        xconst8 x2, 2
+;       19: 0e 03 03                        xconst8 x3, 3
+;       1c: 01 00 00 00 00                  call 0x0    // target = 0x1c
+;       21: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       25: 22 1d 1b                        load64 fp, sp
+;       28: 0e 1e 10                        xconst8 spilltmp0, 16
+;       2b: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       2e: 00                              ret
 
 function %colocated_rets_i64_i64_i64_i64() -> i64 {
     fn0 = colocated %g() -> i64, i64, i64, i64
@@ -142,37 +142,37 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   call TestCase(%g), CallInfo { uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }, CallRetPair { vreg: Writable { reg: p1i }, preg: p1i }, CallRetPair { vreg: Writable { reg: p2i }, preg: p2i }, CallRetPair { vreg: Writable { reg: p3i }, preg: p3i }], clobbers: PRegSet { bits: [1204185006387685820006384, 4294967295] }, callee_pop_size: 0 }
 ;   x4 = xadd64 x0, x2
 ;   x3 = xadd64 x1, x3
 ;   x0 = xadd64 x4, x3
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 01 00 00 00 00                  call 0x0    // target = 0x11
-;       16: 13 04 00 02                     xadd64 x4, x0, x2
-;       1a: 13 03 01 03                     xadd64 x3, x1, x3
-;       1e: 13 00 04 03                     xadd64 x0, x4, x3
-;       22: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       26: 22 22 20                        load64 fp, sp
-;       29: 0e 23 10                        xconst8 spilltmp0, 16
-;       2c: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       30: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 01 00 00 00 00                  call 0x0    // target = 0x10
+;       15: 13 04 08                        xadd64 x4, x0, x2
+;       18: 13 23 0c                        xadd64 x3, x1, x3
+;       1b: 13 80 0c                        xadd64 x0, x4, x3
+;       1e: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       22: 22 1d 1b                        load64 fp, sp
+;       25: 0e 1e 10                        xconst8 spilltmp0, 16
+;       28: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       2b: 00                              ret
 
 function %colocated_stack_args() {
     fn0 = colocated %g(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
@@ -184,13 +184,13 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
-;   x35 = xconst8 -48
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
+;   x30 = xconst8 -48
+;   x27 = xadd32 x27, x30
 ; block0:
 ;   x15 = xconst8 0
 ;   store64 OutgoingArg(0), x15 // flags =  notrap aligned
@@ -215,52 +215,52 @@ block0:
 ;   x13 = xmov x15
 ;   x14 = xmov x15
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }, CallArgPair { vreg: p1i, preg: p1i }, CallArgPair { vreg: p2i, preg: p2i }, CallArgPair { vreg: p3i, preg: p3i }, CallArgPair { vreg: p4i, preg: p4i }, CallArgPair { vreg: p5i, preg: p5i }, CallArgPair { vreg: p6i, preg: p6i }, CallArgPair { vreg: p7i, preg: p7i }, CallArgPair { vreg: p8i, preg: p8i }, CallArgPair { vreg: p9i, preg: p9i }, CallArgPair { vreg: p10i, preg: p10i }, CallArgPair { vreg: p11i, preg: p11i }, CallArgPair { vreg: p12i, preg: p12i }, CallArgPair { vreg: p13i, preg: p13i }, CallArgPair { vreg: p14i, preg: p14i }, CallArgPair { vreg: p15i, preg: p15i }], defs: [], clobbers: PRegSet { bits: [1204185006387685820006399, 4294967295] }, callee_pop_size: 0 }
-;   x35 = xconst8 48
-;   x32 = xadd32 x32, x35
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 48
+;   x27 = xadd32 x27, x30
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 23 d0                        xconst8 spilltmp0, -48
-;       14: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       18: 0e 0f 00                        xconst8 x15, 0
-;       1b: 2a 20 0f                        store64 sp, x15
-;       1e: 2c 20 08 0f                     store64_offset8 sp, 8, x15
-;       22: 2c 20 10 0f                     store64_offset8 sp, 16, x15
-;       26: 2c 20 18 0f                     store64_offset8 sp, 24, x15
-;       2a: 2c 20 20 0f                     store64_offset8 sp, 32, x15
-;       2e: 2c 20 28 0f                     store64_offset8 sp, 40, x15
-;       32: 0b 00 0f                        xmov x0, x15
-;       35: 0b 01 0f                        xmov x1, x15
-;       38: 0b 02 0f                        xmov x2, x15
-;       3b: 0b 03 0f                        xmov x3, x15
-;       3e: 0b 04 0f                        xmov x4, x15
-;       41: 0b 05 0f                        xmov x5, x15
-;       44: 0b 06 0f                        xmov x6, x15
-;       47: 0b 07 0f                        xmov x7, x15
-;       4a: 0b 08 0f                        xmov x8, x15
-;       4d: 0b 09 0f                        xmov x9, x15
-;       50: 0b 0a 0f                        xmov x10, x15
-;       53: 0b 0b 0f                        xmov x11, x15
-;       56: 0b 0c 0f                        xmov x12, x15
-;       59: 0b 0d 0f                        xmov x13, x15
-;       5c: 0b 0e 0f                        xmov x14, x15
-;       5f: 01 00 00 00 00                  call 0x0    // target = 0x5f
-;       64: 0e 23 30                        xconst8 spilltmp0, 48
-;       67: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       6b: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       6f: 22 22 20                        load64 fp, sp
-;       72: 0e 23 10                        xconst8 spilltmp0, 16
-;       75: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       79: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 1e d0                        xconst8 spilltmp0, -48
+;       13: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       16: 0e 0f 00                        xconst8 x15, 0
+;       19: 2a 1b 0f                        store64 sp, x15
+;       1c: 2c 1b 08 0f                     store64_offset8 sp, 8, x15
+;       20: 2c 1b 10 0f                     store64_offset8 sp, 16, x15
+;       24: 2c 1b 18 0f                     store64_offset8 sp, 24, x15
+;       28: 2c 1b 20 0f                     store64_offset8 sp, 32, x15
+;       2c: 2c 1b 28 0f                     store64_offset8 sp, 40, x15
+;       30: 0b 00 0f                        xmov x0, x15
+;       33: 0b 01 0f                        xmov x1, x15
+;       36: 0b 02 0f                        xmov x2, x15
+;       39: 0b 03 0f                        xmov x3, x15
+;       3c: 0b 04 0f                        xmov x4, x15
+;       3f: 0b 05 0f                        xmov x5, x15
+;       42: 0b 06 0f                        xmov x6, x15
+;       45: 0b 07 0f                        xmov x7, x15
+;       48: 0b 08 0f                        xmov x8, x15
+;       4b: 0b 09 0f                        xmov x9, x15
+;       4e: 0b 0a 0f                        xmov x10, x15
+;       51: 0b 0b 0f                        xmov x11, x15
+;       54: 0b 0c 0f                        xmov x12, x15
+;       57: 0b 0d 0f                        xmov x13, x15
+;       5a: 0b 0e 0f                        xmov x14, x15
+;       5d: 01 00 00 00 00                  call 0x0    // target = 0x5d
+;       62: 0e 1e 30                        xconst8 spilltmp0, 48
+;       65: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       68: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       6c: 22 1d 1b                        load64 fp, sp
+;       6f: 0e 1e 10                        xconst8 spilltmp0, 16
+;       72: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       75: 00                              ret
 
 function %colocated_stack_rets() -> i64 {
     fn0 = colocated %g() -> i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64
@@ -299,13 +299,13 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
-;   x35 = xconst8 -64
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
+;   x30 = xconst8 -64
+;   x27 = xadd32 x27, x30
 ;   store64 sp+56, x16 // flags =  notrap aligned
 ;   store64 sp+48, x18 // flags =  notrap aligned
 ; block0:
@@ -345,65 +345,65 @@ block0:
 ;   x0 = xadd64 x14, x13
 ;   x16 = load64_u sp+56 // flags = notrap aligned
 ;   x18 = load64_u sp+48 // flags = notrap aligned
-;   x35 = xconst8 64
-;   x32 = xadd32 x32, x35
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 64
+;   x27 = xadd32 x27, x30
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 23 c0                        xconst8 spilltmp0, -64
-;       14: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       18: 2c 20 38 10                     store64_offset8 sp, 56, x16
-;       1c: 2c 20 30 12                     store64_offset8 sp, 48, x18
-;       20: 0b 00 20                        xmov x0, sp
-;       23: 01 00 00 00 00                  call 0x0    // target = 0x23
-;       28: 0b 10 0d                        xmov x16, x13
-;       2b: 0b 12 0b                        xmov x18, x11
-;       2e: 22 19 20                        load64 x25, sp
-;       31: 25 0b 20 08                     load64_offset8 x11, sp, 8
-;       35: 25 0d 20 10                     load64_offset8 x13, sp, 16
-;       39: 25 1f 20 18                     load64_offset8 x31, sp, 24
-;       3d: 25 11 20 20                     load64_offset8 x17, sp, 32
-;       41: 13 1e 00 01                     xadd64 x30, x0, x1
-;       45: 13 1d 02 03                     xadd64 x29, x2, x3
-;       49: 13 05 04 05                     xadd64 x5, x4, x5
-;       4d: 13 06 06 07                     xadd64 x6, x6, x7
-;       51: 13 07 08 09                     xadd64 x7, x8, x9
-;       55: 0b 00 12                        xmov x0, x18
-;       58: 13 04 0a 00                     xadd64 x4, x10, x0
-;       5c: 0b 0a 10                        xmov x10, x16
-;       5f: 13 08 0c 0a                     xadd64 x8, x12, x10
-;       63: 13 0e 0e 0f                     xadd64 x14, x14, x15
-;       67: 13 0f 19 0b                     xadd64 x15, x25, x11
-;       6b: 13 0d 0b 0d                     xadd64 x13, x11, x13
-;       6f: 13 00 1f 11                     xadd64 x0, x31, x17
-;       73: 13 01 1e 1d                     xadd64 x1, x30, x29
-;       77: 13 02 05 06                     xadd64 x2, x5, x6
-;       7b: 13 03 07 04                     xadd64 x3, x7, x4
-;       7f: 13 0e 08 0e                     xadd64 x14, x8, x14
-;       83: 13 0d 0f 0d                     xadd64 x13, x15, x13
-;       87: 13 0f 00 00                     xadd64 x15, x0, x0
-;       8b: 13 00 01 02                     xadd64 x0, x1, x2
-;       8f: 13 0e 03 0e                     xadd64 x14, x3, x14
-;       93: 13 0d 0d 0f                     xadd64 x13, x13, x15
-;       97: 13 0e 00 0e                     xadd64 x14, x0, x14
-;       9b: 13 0d 0d 0d                     xadd64 x13, x13, x13
-;       9f: 13 00 0e 0d                     xadd64 x0, x14, x13
-;       a3: 25 10 20 38                     load64_offset8 x16, sp, 56
-;       a7: 25 12 20 30                     load64_offset8 x18, sp, 48
-;       ab: 0e 23 40                        xconst8 spilltmp0, 64
-;       ae: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       b2: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       b6: 22 22 20                        load64 fp, sp
-;       b9: 0e 23 10                        xconst8 spilltmp0, 16
-;       bc: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       c0: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 1e c0                        xconst8 spilltmp0, -64
+;       13: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       16: 2c 1b 38 10                     store64_offset8 sp, 56, x16
+;       1a: 2c 1b 30 12                     store64_offset8 sp, 48, x18
+;       1e: 0b 00 1b                        xmov x0, sp
+;       21: 01 00 00 00 00                  call 0x0    // target = 0x21
+;       26: 0b 10 0d                        xmov x16, x13
+;       29: 0b 12 0b                        xmov x18, x11
+;       2c: 22 19 1b                        load64 x25, sp
+;       2f: 25 0b 1b 08                     load64_offset8 x11, sp, 8
+;       33: 25 0d 1b 10                     load64_offset8 x13, sp, 16
+;       37: 25 1f 1b 18                     load64_offset8 spilltmp1, sp, 24
+;       3b: 25 11 1b 20                     load64_offset8 x17, sp, 32
+;       3f: 13 1e 04                        xadd64 spilltmp0, x0, x1
+;       42: 13 5d 0c                        xadd64 fp, x2, x3
+;       45: 13 85 14                        xadd64 x5, x4, x5
+;       48: 13 c6 1c                        xadd64 x6, x6, x7
+;       4b: 13 07 25                        xadd64 x7, x8, x9
+;       4e: 0b 00 12                        xmov x0, x18
+;       51: 13 44 01                        xadd64 x4, x10, x0
+;       54: 0b 0a 10                        xmov x10, x16
+;       57: 13 88 29                        xadd64 x8, x12, x10
+;       5a: 13 ce 3d                        xadd64 x14, x14, x15
+;       5d: 13 2f 2f                        xadd64 x15, x25, x11
+;       60: 13 6d 35                        xadd64 x13, x11, x13
+;       63: 13 e0 47                        xadd64 x0, spilltmp1, x17
+;       66: 13 c1 77                        xadd64 x1, spilltmp0, fp
+;       69: 13 a2 18                        xadd64 x2, x5, x6
+;       6c: 13 e3 10                        xadd64 x3, x7, x4
+;       6f: 13 0e 39                        xadd64 x14, x8, x14
+;       72: 13 ed 35                        xadd64 x13, x15, x13
+;       75: 13 0f 00                        xadd64 x15, x0, x0
+;       78: 13 20 08                        xadd64 x0, x1, x2
+;       7b: 13 6e 38                        xadd64 x14, x3, x14
+;       7e: 13 ad 3d                        xadd64 x13, x13, x15
+;       81: 13 0e 38                        xadd64 x14, x0, x14
+;       84: 13 ad 35                        xadd64 x13, x13, x13
+;       87: 13 c0 35                        xadd64 x0, x14, x13
+;       8a: 25 10 1b 38                     load64_offset8 x16, sp, 56
+;       8e: 25 12 1b 30                     load64_offset8 x18, sp, 48
+;       92: 0e 1e 40                        xconst8 spilltmp0, 64
+;       95: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       98: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       9c: 22 1d 1b                        load64 fp, sp
+;       9f: 0e 1e 10                        xconst8 spilltmp0, 16
+;       a2: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       a5: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/iadd.clif b/cranelift/filetests/filetests/isa/pulley32/iadd.clif
index 2398ea001..68bfe902d 100644
--- a/cranelift/filetests/filetests/isa/pulley32/iadd.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/iadd.clif
@@ -13,8 +13,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16(i16, i16) -> i16 {
 block0(v0: i16, v1: i16):
@@ -28,8 +28,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32(i32, i32) -> i32 {
 block0(v0: i32, v1: i32):
@@ -43,8 +43,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64(i64, i64) -> i64 {
 block0(v0: i64, v1: i64):
@@ -58,6 +58,6 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 13 00 00 01                     xadd64 x0, x0, x1
-;        4: 00                              ret
+;        0: 13 00 04                        xadd64 x0, x0, x1
+;        3: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/icmp.clif b/cranelift/filetests/filetests/isa/pulley32/icmp.clif
index 5000bcc22..d6f489e68 100644
--- a/cranelift/filetests/filetests/isa/pulley32/icmp.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/icmp.clif
@@ -13,8 +13,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_eq(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -28,8 +28,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_eq(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -43,8 +43,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_eq(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -58,8 +58,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 14 00 00 01                     xeq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 14 00 04                        xeq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ne(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -73,8 +73,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ne(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -88,8 +88,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ne(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -103,8 +103,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ne(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -118,8 +118,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 15 00 00 01                     xneq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 15 00 04                        xneq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ult(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -133,8 +133,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ult(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -148,8 +148,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ult(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -163,8 +163,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ult(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -178,8 +178,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 18 00 00 01                     xult64 x0, x0, x1
-;        4: 00                              ret
+;        0: 18 00 04                        xult64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ule(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -193,8 +193,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ule(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -208,8 +208,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ule(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -223,8 +223,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ule(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -238,8 +238,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 00 00 01                     xulteq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 19 00 04                        xulteq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_slt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -253,8 +253,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_slt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -268,8 +268,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_slt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -283,8 +283,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_slt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -298,8 +298,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 16 00 00 01                     xslt64 x0, x0, x1
-;        4: 00                              ret
+;        0: 16 00 04                        xslt64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_sle(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -313,8 +313,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_sle(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -328,8 +328,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_sle(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -343,8 +343,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_sle(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -358,8 +358,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 17 00 00 01                     xslteq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 17 00 04                        xslteq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ugt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -373,8 +373,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_ugt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -388,8 +388,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_ugt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -403,8 +403,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_ugt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -418,8 +418,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 18 00 01 00                     xult64 x0, x1, x0
-;        4: 00                              ret
+;        0: 18 20 00                        xult64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_sgt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -433,8 +433,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_sgt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -448,8 +448,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_sgt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -463,8 +463,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_sgt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -478,8 +478,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 16 00 01 00                     xslt64 x0, x1, x0
-;        4: 00                              ret
+;        0: 16 20 00                        xslt64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_uge(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -493,8 +493,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_uge(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -508,8 +508,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_uge(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -523,8 +523,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_uge(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -538,8 +538,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 00 01 00                     xulteq64 x0, x1, x0
-;        4: 00                              ret
+;        0: 19 20 00                        xulteq64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_sge(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -553,8 +553,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_sge(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -568,8 +568,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_sge(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -583,8 +583,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_sge(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -598,6 +598,6 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 17 00 01 00                     xslteq64 x0, x1, x0
-;        4: 00                              ret
+;        0: 17 20 00                        xslteq64 x0, x1, x0
+;        3: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/trap.clif b/cranelift/filetests/filetests/isa/pulley32/trap.clif
index 12281fa13..84a07dab6 100644
--- a/cranelift/filetests/filetests/isa/pulley32/trap.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/trap.clif
@@ -33,10 +33,10 @@ block0(v0: i64):
 ;
 ; Disassembled:
 ;        0: 0e 03 2a                        xconst8 x3, 42
-;        3: 14 03 00 03                     xeq64 x3, x0, x3
-;        7: 03 03 07 00 00 00               br_if x3, 0x7    // target = 0xe
-;        d: 00                              ret
-;        e: 33 00 00                        trap
+;        3: 14 03 0c                        xeq64 x3, x0, x3
+;        6: 03 03 07 00 00 00               br_if x3, 0x7    // target = 0xd
+;        c: 00                              ret
+;        d: 33 00 00                        trap
 
 function %trapz(i64) {
 block0(v0: i64):
@@ -58,8 +58,8 @@ block0(v0: i64):
 ;
 ; Disassembled:
 ;        0: 0e 03 2a                        xconst8 x3, 42
-;        3: 14 03 00 03                     xeq64 x3, x0, x3
-;        7: 04 03 07 00 00 00               br_if_not x3, 0x7    // target = 0xe
-;        d: 00                              ret
-;        e: 33 00 00                        trap
+;        3: 14 03 0c                        xeq64 x3, x0, x3
+;        6: 04 03 07 00 00 00               br_if_not x3, 0x7    // target = 0xd
+;        c: 00                              ret
+;        d: 33 00 00                        trap
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/brif.clif b/cranelift/filetests/filetests/isa/pulley64/brif.clif
index 2a33dd7bb..597257d0d 100644
--- a/cranelift/filetests/filetests/isa/pulley64/brif.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/brif.clif
@@ -147,12 +147,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 05 00 01                     xeq32 x5, x0, x1
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 1a 05 04                        xeq32 x5, x0, x1
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
 function %brif_icmp_i16(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -180,12 +180,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 05 00 01                     xneq32 x5, x0, x1
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 1b 05 04                        xneq32 x5, x0, x1
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
 function %brif_icmp_i32(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -244,10 +244,10 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 05 01 00                     xulteq64 x5, x1, x0
-;        4: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xe
-;        a: 0e 00 00                        xconst8 x0, 0
-;        d: 00                              ret
-;        e: 0e 00 01                        xconst8 x0, 1
-;       11: 00                              ret
+;        0: 19 25 00                        xulteq64 x5, x1, x0
+;        3: 03 05 0a 00 00 00               br_if x5, 0xa    // target = 0xd
+;        9: 0e 00 00                        xconst8 x0, 0
+;        c: 00                              ret
+;        d: 0e 00 01                        xconst8 x0, 1
+;       10: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/call.clif b/cranelift/filetests/filetests/isa/pulley64/call.clif
index 5b633b7b6..b787b0a58 100644
--- a/cranelift/filetests/filetests/isa/pulley64/call.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/call.clif
@@ -12,35 +12,35 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [1204185006387685820006398, 4294967295] }, callee_pop_size: 0 }
 ;   x0 = xconst8 1
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 01 00 00 00 00                  call 0x0    // target = 0x14
-;       19: 0e 00 01                        xconst8 x0, 1
-;       1c: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       20: 22 22 20                        load64 fp, sp
-;       23: 0e 23 10                        xconst8 spilltmp0, 16
-;       26: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       2a: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 01 00 00 00 00                  call 0x0    // target = 0x13
+;       18: 0e 00 01                        xconst8 x0, 1
+;       1b: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       1f: 22 1d 1b                        load64 fp, sp
+;       22: 0e 1e 10                        xconst8 spilltmp0, 16
+;       25: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       28: 00                              ret
 
 function %colocated_args_i32_rets_i32() -> i32 {
     fn0 = colocated %g(i32) -> i32
@@ -53,35 +53,35 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [1204185006387685820006398, 4294967295] }, callee_pop_size: 0 }
 ;   x0 = xconst8 1
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 01 00 00 00 00                  call 0x0    // target = 0x14
-;       19: 0e 00 01                        xconst8 x0, 1
-;       1c: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       20: 22 22 20                        load64 fp, sp
-;       23: 0e 23 10                        xconst8 spilltmp0, 16
-;       26: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       2a: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 01 00 00 00 00                  call 0x0    // target = 0x13
+;       18: 0e 00 01                        xconst8 x0, 1
+;       1b: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       1f: 22 1d 1b                        load64 fp, sp
+;       22: 0e 1e 10                        xconst8 spilltmp0, 16
+;       25: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       28: 00                              ret
 
 function %colocated_args_i64_i32_i64_i32() {
     fn0 = colocated %g(i64, i32, i64, i32)
@@ -96,39 +96,39 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   x0 = xconst8 0
 ;   x1 = xconst8 1
 ;   x2 = xconst8 2
 ;   x3 = xconst8 3
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }, CallArgPair { vreg: p1i, preg: p1i }, CallArgPair { vreg: p2i, preg: p2i }, CallArgPair { vreg: p3i, preg: p3i }], defs: [], clobbers: PRegSet { bits: [1204185006387685820006399, 4294967295] }, callee_pop_size: 0 }
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 00 00                        xconst8 x0, 0
-;       14: 0e 01 01                        xconst8 x1, 1
-;       17: 0e 02 02                        xconst8 x2, 2
-;       1a: 0e 03 03                        xconst8 x3, 3
-;       1d: 01 00 00 00 00                  call 0x0    // target = 0x1d
-;       22: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       26: 22 22 20                        load64 fp, sp
-;       29: 0e 23 10                        xconst8 spilltmp0, 16
-;       2c: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       30: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 00 00                        xconst8 x0, 0
+;       13: 0e 01 01                        xconst8 x1, 1
+;       16: 0e 02 02                        xconst8 x2, 2
+;       19: 0e 03 03                        xconst8 x3, 3
+;       1c: 01 00 00 00 00                  call 0x0    // target = 0x1c
+;       21: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       25: 22 1d 1b                        load64 fp, sp
+;       28: 0e 1e 10                        xconst8 spilltmp0, 16
+;       2b: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       2e: 00                              ret
 
 function %colocated_rets_i64_i64_i64_i64() -> i64 {
     fn0 = colocated %g() -> i64, i64, i64, i64
@@ -142,37 +142,37 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
 ; block0:
 ;   call TestCase(%g), CallInfo { uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }, CallRetPair { vreg: Writable { reg: p1i }, preg: p1i }, CallRetPair { vreg: Writable { reg: p2i }, preg: p2i }, CallRetPair { vreg: Writable { reg: p3i }, preg: p3i }], clobbers: PRegSet { bits: [1204185006387685820006384, 4294967295] }, callee_pop_size: 0 }
 ;   x4 = xadd64 x0, x2
 ;   x3 = xadd64 x1, x3
 ;   x0 = xadd64 x4, x3
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 01 00 00 00 00                  call 0x0    // target = 0x11
-;       16: 13 04 00 02                     xadd64 x4, x0, x2
-;       1a: 13 03 01 03                     xadd64 x3, x1, x3
-;       1e: 13 00 04 03                     xadd64 x0, x4, x3
-;       22: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       26: 22 22 20                        load64 fp, sp
-;       29: 0e 23 10                        xconst8 spilltmp0, 16
-;       2c: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       30: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 01 00 00 00 00                  call 0x0    // target = 0x10
+;       15: 13 04 08                        xadd64 x4, x0, x2
+;       18: 13 23 0c                        xadd64 x3, x1, x3
+;       1b: 13 80 0c                        xadd64 x0, x4, x3
+;       1e: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       22: 22 1d 1b                        load64 fp, sp
+;       25: 0e 1e 10                        xconst8 spilltmp0, 16
+;       28: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       2b: 00                              ret
 
 function %colocated_stack_args() {
     fn0 = colocated %g(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
@@ -184,13 +184,13 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
-;   x35 = xconst8 -48
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
+;   x30 = xconst8 -48
+;   x27 = xadd32 x27, x30
 ; block0:
 ;   x15 = xconst8 0
 ;   store64 OutgoingArg(0), x15 // flags =  notrap aligned
@@ -215,52 +215,52 @@ block0:
 ;   x13 = xmov x15
 ;   x14 = xmov x15
 ;   call TestCase(%g), CallInfo { uses: [CallArgPair { vreg: p0i, preg: p0i }, CallArgPair { vreg: p1i, preg: p1i }, CallArgPair { vreg: p2i, preg: p2i }, CallArgPair { vreg: p3i, preg: p3i }, CallArgPair { vreg: p4i, preg: p4i }, CallArgPair { vreg: p5i, preg: p5i }, CallArgPair { vreg: p6i, preg: p6i }, CallArgPair { vreg: p7i, preg: p7i }, CallArgPair { vreg: p8i, preg: p8i }, CallArgPair { vreg: p9i, preg: p9i }, CallArgPair { vreg: p10i, preg: p10i }, CallArgPair { vreg: p11i, preg: p11i }, CallArgPair { vreg: p12i, preg: p12i }, CallArgPair { vreg: p13i, preg: p13i }, CallArgPair { vreg: p14i, preg: p14i }, CallArgPair { vreg: p15i, preg: p15i }], defs: [], clobbers: PRegSet { bits: [1204185006387685820006399, 4294967295] }, callee_pop_size: 0 }
-;   x35 = xconst8 48
-;   x32 = xadd32 x32, x35
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 48
+;   x27 = xadd32 x27, x30
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 23 d0                        xconst8 spilltmp0, -48
-;       14: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       18: 0e 0f 00                        xconst8 x15, 0
-;       1b: 2a 20 0f                        store64 sp, x15
-;       1e: 2c 20 08 0f                     store64_offset8 sp, 8, x15
-;       22: 2c 20 10 0f                     store64_offset8 sp, 16, x15
-;       26: 2c 20 18 0f                     store64_offset8 sp, 24, x15
-;       2a: 2c 20 20 0f                     store64_offset8 sp, 32, x15
-;       2e: 2c 20 28 0f                     store64_offset8 sp, 40, x15
-;       32: 0b 00 0f                        xmov x0, x15
-;       35: 0b 01 0f                        xmov x1, x15
-;       38: 0b 02 0f                        xmov x2, x15
-;       3b: 0b 03 0f                        xmov x3, x15
-;       3e: 0b 04 0f                        xmov x4, x15
-;       41: 0b 05 0f                        xmov x5, x15
-;       44: 0b 06 0f                        xmov x6, x15
-;       47: 0b 07 0f                        xmov x7, x15
-;       4a: 0b 08 0f                        xmov x8, x15
-;       4d: 0b 09 0f                        xmov x9, x15
-;       50: 0b 0a 0f                        xmov x10, x15
-;       53: 0b 0b 0f                        xmov x11, x15
-;       56: 0b 0c 0f                        xmov x12, x15
-;       59: 0b 0d 0f                        xmov x13, x15
-;       5c: 0b 0e 0f                        xmov x14, x15
-;       5f: 01 00 00 00 00                  call 0x0    // target = 0x5f
-;       64: 0e 23 30                        xconst8 spilltmp0, 48
-;       67: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       6b: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       6f: 22 22 20                        load64 fp, sp
-;       72: 0e 23 10                        xconst8 spilltmp0, 16
-;       75: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       79: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 1e d0                        xconst8 spilltmp0, -48
+;       13: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       16: 0e 0f 00                        xconst8 x15, 0
+;       19: 2a 1b 0f                        store64 sp, x15
+;       1c: 2c 1b 08 0f                     store64_offset8 sp, 8, x15
+;       20: 2c 1b 10 0f                     store64_offset8 sp, 16, x15
+;       24: 2c 1b 18 0f                     store64_offset8 sp, 24, x15
+;       28: 2c 1b 20 0f                     store64_offset8 sp, 32, x15
+;       2c: 2c 1b 28 0f                     store64_offset8 sp, 40, x15
+;       30: 0b 00 0f                        xmov x0, x15
+;       33: 0b 01 0f                        xmov x1, x15
+;       36: 0b 02 0f                        xmov x2, x15
+;       39: 0b 03 0f                        xmov x3, x15
+;       3c: 0b 04 0f                        xmov x4, x15
+;       3f: 0b 05 0f                        xmov x5, x15
+;       42: 0b 06 0f                        xmov x6, x15
+;       45: 0b 07 0f                        xmov x7, x15
+;       48: 0b 08 0f                        xmov x8, x15
+;       4b: 0b 09 0f                        xmov x9, x15
+;       4e: 0b 0a 0f                        xmov x10, x15
+;       51: 0b 0b 0f                        xmov x11, x15
+;       54: 0b 0c 0f                        xmov x12, x15
+;       57: 0b 0d 0f                        xmov x13, x15
+;       5a: 0b 0e 0f                        xmov x14, x15
+;       5d: 01 00 00 00 00                  call 0x0    // target = 0x5d
+;       62: 0e 1e 30                        xconst8 spilltmp0, 48
+;       65: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       68: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       6c: 22 1d 1b                        load64 fp, sp
+;       6f: 0e 1e 10                        xconst8 spilltmp0, 16
+;       72: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       75: 00                              ret
 
 function %colocated_stack_rets() -> i64 {
     fn0 = colocated %g() -> i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64
@@ -299,13 +299,13 @@ block0:
 }
 
 ; VCode:
-;   x35 = xconst8 -16
-;   x32 = xadd32 x32, x35
-;   store64 sp+8, x33 // flags =  notrap aligned
-;   store64 sp+0, x34 // flags =  notrap aligned
-;   x34 = xmov x32
-;   x35 = xconst8 -64
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 -16
+;   x27 = xadd32 x27, x30
+;   store64 sp+8, x28 // flags =  notrap aligned
+;   store64 sp+0, x29 // flags =  notrap aligned
+;   x29 = xmov x27
+;   x30 = xconst8 -64
+;   x27 = xadd32 x27, x30
 ;   store64 sp+56, x16 // flags =  notrap aligned
 ;   store64 sp+48, x18 // flags =  notrap aligned
 ; block0:
@@ -345,65 +345,65 @@ block0:
 ;   x0 = xadd64 x14, x13
 ;   x16 = load64_u sp+56 // flags = notrap aligned
 ;   x18 = load64_u sp+48 // flags = notrap aligned
-;   x35 = xconst8 64
-;   x32 = xadd32 x32, x35
-;   x33 = load64_u sp+8 // flags = notrap aligned
-;   x34 = load64_u sp+0 // flags = notrap aligned
-;   x35 = xconst8 16
-;   x32 = xadd32 x32, x35
+;   x30 = xconst8 64
+;   x27 = xadd32 x27, x30
+;   x28 = load64_u sp+8 // flags = notrap aligned
+;   x29 = load64_u sp+0 // flags = notrap aligned
+;   x30 = xconst8 16
+;   x27 = xadd32 x27, x30
 ;   ret
 ;
 ; Disassembled:
-;        0: 0e 23 f0                        xconst8 spilltmp0, -16
-;        3: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;        7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-;        b: 2a 20 22                        store64 sp, fp
-;        e: 0b 22 20                        xmov fp, sp
-;       11: 0e 23 c0                        xconst8 spilltmp0, -64
-;       14: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       18: 2c 20 38 10                     store64_offset8 sp, 56, x16
-;       1c: 2c 20 30 12                     store64_offset8 sp, 48, x18
-;       20: 0b 00 20                        xmov x0, sp
-;       23: 01 00 00 00 00                  call 0x0    // target = 0x23
-;       28: 0b 10 0d                        xmov x16, x13
-;       2b: 0b 12 0b                        xmov x18, x11
-;       2e: 22 19 20                        load64 x25, sp
-;       31: 25 0b 20 08                     load64_offset8 x11, sp, 8
-;       35: 25 0d 20 10                     load64_offset8 x13, sp, 16
-;       39: 25 1f 20 18                     load64_offset8 x31, sp, 24
-;       3d: 25 11 20 20                     load64_offset8 x17, sp, 32
-;       41: 13 1e 00 01                     xadd64 x30, x0, x1
-;       45: 13 1d 02 03                     xadd64 x29, x2, x3
-;       49: 13 05 04 05                     xadd64 x5, x4, x5
-;       4d: 13 06 06 07                     xadd64 x6, x6, x7
-;       51: 13 07 08 09                     xadd64 x7, x8, x9
-;       55: 0b 00 12                        xmov x0, x18
-;       58: 13 04 0a 00                     xadd64 x4, x10, x0
-;       5c: 0b 0a 10                        xmov x10, x16
-;       5f: 13 08 0c 0a                     xadd64 x8, x12, x10
-;       63: 13 0e 0e 0f                     xadd64 x14, x14, x15
-;       67: 13 0f 19 0b                     xadd64 x15, x25, x11
-;       6b: 13 0d 0b 0d                     xadd64 x13, x11, x13
-;       6f: 13 00 1f 11                     xadd64 x0, x31, x17
-;       73: 13 01 1e 1d                     xadd64 x1, x30, x29
-;       77: 13 02 05 06                     xadd64 x2, x5, x6
-;       7b: 13 03 07 04                     xadd64 x3, x7, x4
-;       7f: 13 0e 08 0e                     xadd64 x14, x8, x14
-;       83: 13 0d 0f 0d                     xadd64 x13, x15, x13
-;       87: 13 0f 00 00                     xadd64 x15, x0, x0
-;       8b: 13 00 01 02                     xadd64 x0, x1, x2
-;       8f: 13 0e 03 0e                     xadd64 x14, x3, x14
-;       93: 13 0d 0d 0f                     xadd64 x13, x13, x15
-;       97: 13 0e 00 0e                     xadd64 x14, x0, x14
-;       9b: 13 0d 0d 0d                     xadd64 x13, x13, x13
-;       9f: 13 00 0e 0d                     xadd64 x0, x14, x13
-;       a3: 25 10 20 38                     load64_offset8 x16, sp, 56
-;       a7: 25 12 20 30                     load64_offset8 x18, sp, 48
-;       ab: 0e 23 40                        xconst8 spilltmp0, 64
-;       ae: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       b2: 25 21 20 08                     load64_offset8 lr, sp, 8
-;       b6: 22 22 20                        load64 fp, sp
-;       b9: 0e 23 10                        xconst8 spilltmp0, 16
-;       bc: 12 20 20 23                     xadd32 sp, sp, spilltmp0
-;       c0: 00                              ret
+;        0: 0e 1e f0                        xconst8 spilltmp0, -16
+;        3: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;        6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+;        a: 2a 1b 1d                        store64 sp, fp
+;        d: 0b 1d 1b                        xmov fp, sp
+;       10: 0e 1e c0                        xconst8 spilltmp0, -64
+;       13: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       16: 2c 1b 38 10                     store64_offset8 sp, 56, x16
+;       1a: 2c 1b 30 12                     store64_offset8 sp, 48, x18
+;       1e: 0b 00 1b                        xmov x0, sp
+;       21: 01 00 00 00 00                  call 0x0    // target = 0x21
+;       26: 0b 10 0d                        xmov x16, x13
+;       29: 0b 12 0b                        xmov x18, x11
+;       2c: 22 19 1b                        load64 x25, sp
+;       2f: 25 0b 1b 08                     load64_offset8 x11, sp, 8
+;       33: 25 0d 1b 10                     load64_offset8 x13, sp, 16
+;       37: 25 1f 1b 18                     load64_offset8 spilltmp1, sp, 24
+;       3b: 25 11 1b 20                     load64_offset8 x17, sp, 32
+;       3f: 13 1e 04                        xadd64 spilltmp0, x0, x1
+;       42: 13 5d 0c                        xadd64 fp, x2, x3
+;       45: 13 85 14                        xadd64 x5, x4, x5
+;       48: 13 c6 1c                        xadd64 x6, x6, x7
+;       4b: 13 07 25                        xadd64 x7, x8, x9
+;       4e: 0b 00 12                        xmov x0, x18
+;       51: 13 44 01                        xadd64 x4, x10, x0
+;       54: 0b 0a 10                        xmov x10, x16
+;       57: 13 88 29                        xadd64 x8, x12, x10
+;       5a: 13 ce 3d                        xadd64 x14, x14, x15
+;       5d: 13 2f 2f                        xadd64 x15, x25, x11
+;       60: 13 6d 35                        xadd64 x13, x11, x13
+;       63: 13 e0 47                        xadd64 x0, spilltmp1, x17
+;       66: 13 c1 77                        xadd64 x1, spilltmp0, fp
+;       69: 13 a2 18                        xadd64 x2, x5, x6
+;       6c: 13 e3 10                        xadd64 x3, x7, x4
+;       6f: 13 0e 39                        xadd64 x14, x8, x14
+;       72: 13 ed 35                        xadd64 x13, x15, x13
+;       75: 13 0f 00                        xadd64 x15, x0, x0
+;       78: 13 20 08                        xadd64 x0, x1, x2
+;       7b: 13 6e 38                        xadd64 x14, x3, x14
+;       7e: 13 ad 3d                        xadd64 x13, x13, x15
+;       81: 13 0e 38                        xadd64 x14, x0, x14
+;       84: 13 ad 35                        xadd64 x13, x13, x13
+;       87: 13 c0 35                        xadd64 x0, x14, x13
+;       8a: 25 10 1b 38                     load64_offset8 x16, sp, 56
+;       8e: 25 12 1b 30                     load64_offset8 x18, sp, 48
+;       92: 0e 1e 40                        xconst8 spilltmp0, 64
+;       95: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       98: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+;       9c: 22 1d 1b                        load64 fp, sp
+;       9f: 0e 1e 10                        xconst8 spilltmp0, 16
+;       a2: 12 7b 7b                        xadd32 sp, sp, spilltmp0
+;       a5: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/iadd.clif b/cranelift/filetests/filetests/isa/pulley64/iadd.clif
index 5a7fa7fe5..7a6cffde6 100644
--- a/cranelift/filetests/filetests/isa/pulley64/iadd.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/iadd.clif
@@ -13,8 +13,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16(i16, i16) -> i16 {
 block0(v0: i16, v1: i16):
@@ -28,8 +28,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32(i32, i32) -> i32 {
 block0(v0: i32, v1: i32):
@@ -43,8 +43,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 12 00 00 01                     xadd32 x0, x0, x1
-;        4: 00                              ret
+;        0: 12 00 04                        xadd32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64(i64, i64) -> i64 {
 block0(v0: i64, v1: i64):
@@ -58,6 +58,6 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 13 00 00 01                     xadd64 x0, x0, x1
-;        4: 00                              ret
+;        0: 13 00 04                        xadd64 x0, x0, x1
+;        3: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/icmp.clif b/cranelift/filetests/filetests/isa/pulley64/icmp.clif
index 84372023c..7aaf069f5 100644
--- a/cranelift/filetests/filetests/isa/pulley64/icmp.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/icmp.clif
@@ -13,8 +13,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_eq(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -28,8 +28,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_eq(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -43,8 +43,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1a 00 00 01                     xeq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1a 00 04                        xeq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_eq(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -58,8 +58,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 14 00 00 01                     xeq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 14 00 04                        xeq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ne(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -73,8 +73,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ne(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -88,8 +88,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ne(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -103,8 +103,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1b 00 00 01                     xneq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1b 00 04                        xneq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ne(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -118,8 +118,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 15 00 00 01                     xneq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 15 00 04                        xneq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ult(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -133,8 +133,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ult(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -148,8 +148,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ult(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -163,8 +163,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 00 01                     xult32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1e 00 04                        xult32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ult(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -178,8 +178,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 18 00 00 01                     xult64 x0, x0, x1
-;        4: 00                              ret
+;        0: 18 00 04                        xult64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ule(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -193,8 +193,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_ule(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -208,8 +208,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_ule(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -223,8 +223,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 00 01                     xulteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1f 00 04                        xulteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_ule(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -238,8 +238,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 00 00 01                     xulteq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 19 00 04                        xulteq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_slt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -253,8 +253,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_slt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -268,8 +268,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_slt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -283,8 +283,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 00 01                     xslt32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1c 00 04                        xslt32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_slt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -298,8 +298,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 16 00 00 01                     xslt64 x0, x0, x1
-;        4: 00                              ret
+;        0: 16 00 04                        xslt64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_sle(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -313,8 +313,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i16_sle(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -328,8 +328,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i32_sle(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -343,8 +343,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 00 01                     xslteq32 x0, x0, x1
-;        4: 00                              ret
+;        0: 1d 00 04                        xslteq32 x0, x0, x1
+;        3: 00                              ret
 
 function %i64_sle(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -358,8 +358,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 17 00 00 01                     xslteq64 x0, x0, x1
-;        4: 00                              ret
+;        0: 17 00 04                        xslteq64 x0, x0, x1
+;        3: 00                              ret
 
 function %i8_ugt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -373,8 +373,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_ugt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -388,8 +388,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_ugt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -403,8 +403,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1e 00 01 00                     xult32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1e 20 00                        xult32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_ugt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -418,8 +418,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 18 00 01 00                     xult64 x0, x1, x0
-;        4: 00                              ret
+;        0: 18 20 00                        xult64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_sgt(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -433,8 +433,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_sgt(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -448,8 +448,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_sgt(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -463,8 +463,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1c 00 01 00                     xslt32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1c 20 00                        xslt32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_sgt(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -478,8 +478,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 16 00 01 00                     xslt64 x0, x1, x0
-;        4: 00                              ret
+;        0: 16 20 00                        xslt64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_uge(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -493,8 +493,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_uge(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -508,8 +508,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_uge(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -523,8 +523,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1f 00 01 00                     xulteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1f 20 00                        xulteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_uge(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -538,8 +538,8 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 19 00 01 00                     xulteq64 x0, x1, x0
-;        4: 00                              ret
+;        0: 19 20 00                        xulteq64 x0, x1, x0
+;        3: 00                              ret
 
 function %i8_sge(i8, i8) -> i8 {
 block0(v0: i8, v1: i8):
@@ -553,8 +553,8 @@ block0(v0: i8, v1: i8):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i16_sge(i16, i16) -> i8 {
 block0(v0: i16, v1: i16):
@@ -568,8 +568,8 @@ block0(v0: i16, v1: i16):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i32_sge(i32, i32) -> i8 {
 block0(v0: i32, v1: i32):
@@ -583,8 +583,8 @@ block0(v0: i32, v1: i32):
 ;   ret
 ;
 ; Disassembled:
-;        0: 1d 00 01 00                     xslteq32 x0, x1, x0
-;        4: 00                              ret
+;        0: 1d 20 00                        xslteq32 x0, x1, x0
+;        3: 00                              ret
 
 function %i64_sge(i64, i64) -> i8 {
 block0(v0: i64, v1: i64):
@@ -598,6 +598,6 @@ block0(v0: i64, v1: i64):
 ;   ret
 ;
 ; Disassembled:
-;        0: 17 00 01 00                     xslteq64 x0, x1, x0
-;        4: 00                              ret
+;        0: 17 20 00                        xslteq64 x0, x1, x0
+;        3: 00                              ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/trap.clif b/cranelift/filetests/filetests/isa/pulley64/trap.clif
index 9216fb9a5..ee639b348 100644
--- a/cranelift/filetests/filetests/isa/pulley64/trap.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/trap.clif
@@ -33,10 +33,10 @@ block0(v0: i64):
 ;
 ; Disassembled:
 ;        0: 0e 03 2a                        xconst8 x3, 42
-;        3: 14 03 00 03                     xeq64 x3, x0, x3
-;        7: 03 03 07 00 00 00               br_if x3, 0x7    // target = 0xe
-;        d: 00                              ret
-;        e: 33 00 00                        trap
+;        3: 14 03 0c                        xeq64 x3, x0, x3
+;        6: 03 03 07 00 00 00               br_if x3, 0x7    // target = 0xd
+;        c: 00                              ret
+;        d: 33 00 00                        trap
 
 function %trapz(i64) {
 block0(v0: i64):
@@ -58,8 +58,8 @@ block0(v0: i64):
 ;
 ; Disassembled:
 ;        0: 0e 03 2a                        xconst8 x3, 42
-;        3: 14 03 00 03                     xeq64 x3, x0, x3
-;        7: 04 03 07 00 00 00               br_if_not x3, 0x7    // target = 0xe
-;        d: 00                              ret
-;        e: 33 00 00                        trap
+;        3: 14 03 0c                        xeq64 x3, x0, x3
+;        6: 04 03 07 00 00 00               br_if_not x3, 0x7    // target = 0xd
+;        c: 00                              ret
+;        d: 33 00 00                        trap
 
diff --git a/cranelift/filetests/filetests/isa/x64/i128.clif b/cranelift/filetests/filetests/isa/x64/i128.clif
index 0571e6880..ec3c9b7ed 100644
--- a/cranelift/filetests/filetests/isa/x64/i128.clif
+++ b/cranelift/filetests/filetests/isa/x64/i128.clif
@@ -2034,3 +2034,38 @@ block0(v0: i64, v1: i64):
 ;   popq %rbp
 ;   retq
 
+function %sink_load_of_i128_half(i64, i64) -> i64, i64 {
+block0(v0: i64, v1: i64):
+    v10 = load.i64 v0
+    v18 = uextend.i128 v1
+    v20 = uextend.i128 v10
+    v14 = iadd v18, v20
+    v15, v16 = isplit v14
+    return v15, v16
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   xorq    %rdx, %rdx, %rdx
+;   movq    %rsi, %rax
+;   addq    %rax, 0(%rdi), %rax
+;   adcq    %rdx, $0, %rdx
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   xorq %rdx, %rdx
+;   movq %rsi, %rax
+;   addq (%rdi), %rax ; trap: heap_oob
+;   adcq $0, %rdx
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/pulley/tests/all/disas.rs b/pulley/tests/all/disas.rs
index e643f273d..f5751b574 100644
--- a/pulley/tests/all/disas.rs
+++ b/pulley/tests/all/disas.rs
@@ -26,76 +26,81 @@ fn assert_disas(ops: &[Op], expected: &str) {
 
 #[test]
 fn simple() {
-    let x0 = XReg::new(0).unwrap();
-    let x1 = XReg::new(1).unwrap();
-    let x31 = XReg::new(31).unwrap();
-
     assert_disas(
         &[
             // Prologue.
             Op::Xconst8(Xconst8 {
-                dst: x31,
+                dst: XReg::x26,
                 imm: -16i8,
             }),
             Op::Xadd32(Xadd32 {
-                dst: XReg::SP,
-                src1: XReg::SP,
-                src2: x31,
+                operands: BinaryOperands {
+                    dst: XReg::sp,
+                    src1: XReg::sp,
+                    src2: XReg::x26,
+                },
             }),
             Op::Store64Offset8(Store64Offset8 {
-                ptr: XReg::SP,
+                ptr: XReg::sp,
                 offset: 8,
-                src: XReg::LR,
+                src: XReg::lr,
             }),
             Op::Store64(Store64 {
-                ptr: XReg::SP,
-                src: XReg::FP,
+                ptr: XReg::sp,
+                src: XReg::fp,
             }),
             Op::Xmov(Xmov {
-                dst: XReg::FP,
-                src: XReg::SP,
+                dst: XReg::fp,
+                src: XReg::sp,
             }),
             // Function body.
             Op::Xadd32(Xadd32 {
-                dst: x0,
-                src1: x0,
-                src2: x1,
+                operands: BinaryOperands {
+                    dst: XReg::x0,
+                    src1: XReg::x0,
+                    src2: XReg::x1,
+                },
             }),
             // Epilogue.
             Op::Xmov(Xmov {
-                dst: XReg::SP,
-                src: XReg::FP,
+                dst: XReg::sp,
+                src: XReg::fp,
             }),
             Op::Load64Offset8(Load64Offset8 {
-                dst: XReg::LR,
-                ptr: XReg::SP,
+                dst: XReg::lr,
+                ptr: XReg::sp,
                 offset: 8,
             }),
             Op::Load64(Load64 {
-                dst: XReg::FP,
-                ptr: XReg::SP,
+                dst: XReg::fp,
+                ptr: XReg::sp,
+            }),
+            Op::Xconst8(Xconst8 {
+                dst: XReg::x26,
+                imm: 16,
             }),
-            Op::Xconst8(Xconst8 { dst: x31, imm: 16 }),
             Op::Xadd32(Xadd32 {
-                dst: XReg::SP,
-                src1: XReg::SP,
-                src2: x31,
+                operands: BinaryOperands {
+                    dst: XReg::sp,
+                    src1: XReg::sp,
+                    src2: XReg::x26,
+                },
             }),
             Op::Ret(Ret {}),
         ],
         r#"
-       0: 0e 1f f0                        xconst8 x31, -16
-       3: 12 20 20 1f                     xadd32 sp, sp, x31
-       7: 2c 20 08 21                     store64_offset8 sp, 8, lr
-       b: 2a 20 22                        store64 sp, fp
-       e: 0b 22 20                        xmov fp, sp
-      11: 12 00 00 01                     xadd32 x0, x0, x1
-      15: 0b 20 22                        xmov sp, fp
-      18: 25 21 20 08                     load64_offset8 lr, sp, 8
-      1c: 22 22 20                        load64 fp, sp
-      1f: 0e 1f 10                        xconst8 x31, 16
-      22: 12 20 20 1f                     xadd32 sp, sp, x31
-      26: 00                              ret
+       0: 0e 1a f0                        xconst8 x26, -16
+       3: 12 7b 6b                        xadd32 sp, sp, x26
+       6: 2c 1b 08 1c                     store64_offset8 sp, 8, lr
+       a: 2a 1b 1d                        store64 sp, fp
+       d: 0b 1d 1b                        xmov fp, sp
+      10: 12 00 04                        xadd32 x0, x0, x1
+      13: 0b 1b 1d                        xmov sp, fp
+      16: 25 1c 1b 08                     load64_offset8 lr, sp, 8
+      1a: 22 1d 1b                        load64 fp, sp
+      1d: 0e 1a 10                        xconst8 x26, 16
+      20: 12 7b 6b                        xadd32 sp, sp, x26
+      23: 00                              ret
         "#,
     );
 }
diff --git a/pulley/tests/all/interp.rs b/pulley/tests/all/interp.rs
index a5342619c..085c09cf3 100644
--- a/pulley/tests/all/interp.rs
+++ b/pulley/tests/all/interp.rs
@@ -136,9 +136,11 @@ fn xadd32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xadd32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -154,9 +156,11 @@ fn xadd64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xadd64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -177,9 +181,11 @@ fn xeq64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xeq64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -200,9 +206,11 @@ fn xneq64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xneq64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -230,9 +238,11 @@ fn xslt64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xslt64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -260,9 +270,11 @@ fn xslteq64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xslteq64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -287,9 +299,11 @@ fn xult64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xult64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -314,9 +328,11 @@ fn xulteq64() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xulteq64 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -341,9 +357,11 @@ fn xeq32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xeq32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -365,9 +383,11 @@ fn xneq32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xneq32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -397,9 +417,11 @@ fn xslt32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xslt32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -427,9 +449,11 @@ fn xslteq32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xslteq32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -453,9 +477,11 @@ fn xult32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xult32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
@@ -479,9 +505,11 @@ fn xulteq32() {
             assert_one(
                 [(x(0), 0x1234567812345678), (x(1), a), (x(2), b)],
                 Xulteq32 {
-                    dst: x(0),
-                    src1: x(1),
-                    src2: x(2),
+                    operands: BinaryOperands {
+                        dst: x(0),
+                        src1: x(1),
+                        src2: x(2),
+                    },
                 },
                 x(0),
                 expected,
diff --git a/tests/wast.rs b/tests/wast.rs
index 68ead3224..e4983d7d3 100644
--- a/tests/wast.rs
+++ b/tests/wast.rs
@@ -74,10 +74,6 @@ fn ignore(test: &Path, strategy: Strategy) -> bool {
         if part == "exception-handling" {
             return true;
         }
-        // Not implemented in Wasmtime yet
-        if part == "extended-const" {
-            return true;
-        }
         // Wasmtime doesn't implement the table64 extension yet.
         if part == "memory64" {
             if [
@@ -219,6 +215,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
     let tail_call = feature_found(wast, "tail-call") || feature_found(wast, "function-references");
     let use_shared_memory = feature_found_src(&wast_bytes, "shared_memory")
         || feature_found_src(&wast_bytes, "shared)");
+    let extended_const = feature_found(wast, "extended-const");
 
     if pooling && use_shared_memory {
         log::warn!("skipping pooling test with shared memory");
@@ -240,6 +237,7 @@ fn run_wast(wast: &Path, strategy: Strategy, pooling: bool) -> anyhow::Result<()
         .wasm_relaxed_simd(relaxed_simd)
         .wasm_tail_call(tail_call)
         .wasm_custom_page_sizes(custom_page_sizes)
+        .wasm_extended_const(extended_const)
         .strategy(strategy);
 
     if is_cranelift {