Repository: bytecodealliance/wasmtime
Pull request #5930 (2023-03-04 22:18:45)

x64: Add a smattering of lowerings for `shuffle` specializations
I took the [meshoptimizer benchmark](https://github.com/zeux/meshoptimizer), compiled it to wasm, and ran it through both Wasmtime and v8. One major improvement for Wasmtime was to use specialized shuffle instructions instead of the generic fallback which requires two `pshufd` with rip-relative immediates plus a `por` vs a single instruction in many cases. I trawled through the v8 disassembly and found a number of shuffle-related instructions and translated all of their lowerings to Wasmtime. On one specific benchmark in that program this PR improves the throughput by 30%, and another by 5%.



Patch:
diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs
index 1f8e36c4f..34961f503 100644
--- a/cranelift/codegen/meta/src/cdsl/instructions.rs
+++ b/cranelift/codegen/meta/src/cdsl/instructions.rs
@@ -159,15 +159,15 @@ impl InstructionBuilder {
         }
     }
 
-    pub fn operands_in(mut self, operands: Vec<&Operand>) -> Self {
+    pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
         assert!(self.operands_in.is_none());
-        self.operands_in = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self.operands_in = Some(operands);
         self
     }
 
-    pub fn operands_out(mut self, operands: Vec<&Operand>) -> Self {
+    pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
         assert!(self.operands_out.is_none());
-        self.operands_out = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self.operands_out = Some(operands);
         self
     }
 
diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs
index 372407965..f36790f26 100755
--- a/cranelift/codegen/meta/src/shared/instructions.rs
+++ b/cranelift/codegen/meta/src/shared/instructions.rs
@@ -17,9 +17,6 @@ fn define_control_flow(
     imm: &Immediates,
     entities: &EntityRefs,
 ) {
-    let block_call = &Operand::new("block_call", &entities.block_call)
-        .with_doc("Destination basic block, with its arguments provided");
-
     ig.push(
         Inst::new(
             "jump",
@@ -32,7 +29,8 @@ fn define_control_flow(
         "#,
             &formats.jump,
         )
-        .operands_in(vec![block_call])
+        .operands_in(vec![Operand::new("block_call", &entities.block_call)
+            .with_doc("Destination basic block, with its arguments provided")])
         .branches(),
     );
 
@@ -42,25 +40,23 @@ fn define_control_flow(
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
 
-    {
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        let block_then = &Operand::new("block_then", &entities.block_then).with_doc("Then block");
-        let block_else = &Operand::new("block_else", &entities.block_else).with_doc("Else block");
-
-        ig.push(
-            Inst::new(
-                "brif",
-                r#"
+    ig.push(
+        Inst::new(
+            "brif",
+            r#"
         Conditional branch when cond is non-zero.
 
         Take the ``then`` branch when ``c != 0``, and the ``else`` branch otherwise.
         "#,
-                &formats.brif,
-            )
-            .operands_in(vec![c, block_then, block_else])
-            .branches(),
-        );
-    }
+            &formats.brif,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("block_then", &entities.block_then).with_doc("Then block"),
+            Operand::new("block_else", &entities.block_else).with_doc("Else block"),
+        ])
+        .branches(),
+    );
 
     {
         let _i32 = &TypeVar::new(
@@ -68,8 +64,6 @@ fn define_control_flow(
             "A 32 bit scalar integer type",
             TypeSetBuilder::new().ints(32..32).build(),
         );
-        let x = &Operand::new("x", _i32).with_doc("i32 index into jump table");
-        let JT = &Operand::new("JT", &entities.jump_table);
 
         ig.push(
             Inst::new(
@@ -93,7 +87,10 @@ fn define_control_flow(
         "#,
                 &formats.branch_table,
             )
-            .operands_in(vec![x, JT])
+            .operands_in(vec![
+                Operand::new("x", _i32).with_doc("i32 index into jump table"),
+                Operand::new("JT", &entities.jump_table),
+            ])
             .branches(),
         );
     }
@@ -108,8 +105,8 @@ fn define_control_flow(
         Inst::new(
             "debugtrap",
             r#"
-    Encodes an assembly debug trap.
-    "#,
+        Encodes an assembly debug trap.
+        "#,
             &formats.nullary,
         )
         .other_side_effects()
@@ -117,81 +114,84 @@ fn define_control_flow(
         .can_store(),
     );
 
-    {
-        let code = &Operand::new("code", &imm.trapcode);
-        ig.push(
-            Inst::new(
-                "trap",
-                r#"
+    ig.push(
+        Inst::new(
+            "trap",
+            r#"
         Terminate execution unconditionally.
         "#,
-                &formats.trap,
-            )
-            .operands_in(vec![code])
-            .can_trap()
-            .terminates_block(),
-        );
+            &formats.trap,
+        )
+        .operands_in(vec![Operand::new("code", &imm.trapcode)])
+        .can_trap()
+        .terminates_block(),
+    );
 
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        ig.push(
-            Inst::new(
-                "trapz",
-                r#"
+    ig.push(
+        Inst::new(
+            "trapz",
+            r#"
         Trap when zero.
 
         if ``c`` is non-zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-        ig.push(
-            Inst::new(
-                "resumable_trap",
-                r#"
+    ig.push(
+        Inst::new(
+            "resumable_trap",
+            r#"
         A resumable trap.
 
         This instruction allows non-conditional traps to be used as non-terminal instructions.
         "#,
-                &formats.trap,
-            )
-            .operands_in(vec![code])
-            .can_trap(),
-        );
+            &formats.trap,
+        )
+        .operands_in(vec![Operand::new("code", &imm.trapcode)])
+        .can_trap(),
+    );
 
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        ig.push(
-            Inst::new(
-                "trapnz",
-                r#"
+    ig.push(
+        Inst::new(
+            "trapnz",
+            r#"
         Trap when non-zero.
 
         If ``c`` is zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-        ig.push(
-            Inst::new(
-                "resumable_trapnz",
-                r#"
+    ig.push(
+        Inst::new(
+            "resumable_trapnz",
+            r#"
         A resumable trap to be called when the passed condition is non-zero.
 
         If ``c`` is zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
-    }
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "return",
@@ -204,14 +204,12 @@ fn define_control_flow(
         "#,
             &formats.multiary,
         )
-        .operands_in(vec![rvals])
+        .operands_in(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .returns(),
     );
 
-    let FN = &Operand::new("FN", &entities.func_ref)
-        .with_doc("function to call, declared by `function`");
-    let args = &Operand::new("args", &entities.varargs).with_doc("call arguments");
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "call",
@@ -223,15 +221,17 @@ fn define_control_flow(
         "#,
             &formats.call,
         )
-        .operands_in(vec![FN, args])
-        .operands_out(vec![rvals])
+        .operands_in(vec![
+            Operand::new("FN", &entities.func_ref)
+                .with_doc("function to call, declared by `function`"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
+        .operands_out(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .call(),
     );
 
-    let SIG = &Operand::new("SIG", &entities.sig_ref).with_doc("function signature");
-    let callee = &Operand::new("callee", iAddr).with_doc("address of function to call");
-    let args = &Operand::new("args", &entities.varargs).with_doc("call arguments");
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "call_indirect",
@@ -248,8 +248,14 @@ fn define_control_flow(
         "#,
             &formats.call_indirect,
         )
-        .operands_in(vec![SIG, callee, args])
-        .operands_out(vec![rvals])
+        .operands_in(vec![
+            Operand::new("SIG", &entities.sig_ref).with_doc("function signature"),
+            Operand::new("callee", iAddr).with_doc("address of function to call"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
+        .operands_out(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .call(),
     );
 
@@ -268,7 +274,11 @@ fn define_control_flow(
         "#,
             &formats.call,
         )
-        .operands_in(vec![FN, args])
+        .operands_in(vec![
+            Operand::new("FN", &entities.func_ref)
+                .with_doc("function to call, declared by `function`"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
         .returns()
         .call(),
     );
@@ -293,14 +303,15 @@ fn define_control_flow(
         "#,
             &formats.call_indirect,
         )
-        .operands_in(vec![SIG, callee, args])
+        .operands_in(vec![
+            Operand::new("SIG", &entities.sig_ref).with_doc("function signature"),
+            Operand::new("callee", iAddr).with_doc("address of function to call"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
         .returns()
         .call(),
     );
 
-    let FN = &Operand::new("FN", &entities.func_ref)
-        .with_doc("function to call, declared by `function`");
-    let addr = &Operand::new("addr", iAddr);
     ig.push(
         Inst::new(
             "func_addr",
@@ -315,8 +326,9 @@ fn define_control_flow(
         "#,
             &formats.func_addr,
         )
-        .operands_in(vec![FN])
-        .operands_out(vec![addr]),
+        .operands_in(vec![Operand::new("FN", &entities.func_ref)
+            .with_doc("function to call, declared by `function`")])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 }
 
@@ -339,9 +351,6 @@ fn define_simd_lane_access(
             .build(),
     );
 
-    let x = &Operand::new("x", &TxN.lane_of()).with_doc("Value to splat to all lanes");
-    let a = &Operand::new("a", TxN);
-
     ig.push(
         Inst::new(
             "splat",
@@ -352,8 +361,10 @@ fn define_simd_lane_access(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", &TxN.lane_of()).with_doc("Value to splat to all lanes")
+        ])
+        .operands_out(vec![Operand::new("a", TxN)]),
     );
 
     let I8x16 = &TypeVar::new(
@@ -365,9 +376,6 @@ fn define_simd_lane_access(
             .includes_scalars(false)
             .build(),
     );
-    let x = &Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes");
-    let y = &Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes");
-    let a = &Operand::new("a", I8x16);
 
     ig.push(
         Inst::new(
@@ -382,8 +390,11 @@ fn define_simd_lane_access(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes"),
+            Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes"),
+        ])
+        .operands_out(vec![Operand::new("a", I8x16)]),
     );
 
     ig.push(
@@ -403,15 +414,13 @@ fn define_simd_lane_access(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes"),
+            Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes"),
+        ])
+        .operands_out(vec![Operand::new("a", I8x16)]),
     );
 
-    let x = &Operand::new("x", TxN).with_doc("The vector to modify");
-    let y = &Operand::new("y", &TxN.lane_of()).with_doc("New lane value");
-    let Idx = &Operand::new("Idx", &imm.uimm8).with_doc("Lane index");
-    let a = &Operand::new("a", TxN);
-
     ig.push(
         Inst::new(
             "insertlane",
@@ -423,13 +432,14 @@ fn define_simd_lane_access(
         "#,
             &formats.ternary_imm8,
         )
-        .operands_in(vec![x, y, Idx])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN).with_doc("The vector to modify"),
+            Operand::new("y", &TxN.lane_of()).with_doc("New lane value"),
+            Operand::new("Idx", &imm.uimm8).with_doc("Lane index"),
+        ])
+        .operands_out(vec![Operand::new("a", TxN)]),
     );
 
-    let x = &Operand::new("x", TxN);
-    let a = &Operand::new("a", &TxN.lane_of());
-
     ig.push(
         Inst::new(
             "extractlane",
@@ -443,8 +453,11 @@ fn define_simd_lane_access(
         "#,
             &formats.binary_imm8,
         )
-        .operands_in(vec![x, Idx])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN),
+            Operand::new("Idx", &imm.uimm8).with_doc("Lane index"),
+        ])
+        .operands_out(vec![Operand::new("a", &TxN.lane_of())]),
     );
 }
 
@@ -464,10 +477,6 @@ fn define_simd_arithmetic(
             .build(),
     );
 
-    let a = &Operand::new("a", Int);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "smin",
@@ -476,8 +485,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -488,8 +497,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -500,8 +509,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -512,8 +521,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let IxN = &TypeVar::new(
@@ -526,10 +535,6 @@ fn define_simd_arithmetic(
             .build(),
     );
 
-    let a = &Operand::new("a", IxN);
-    let x = &Operand::new("x", IxN);
-    let y = &Operand::new("y", IxN);
-
     ig.push(
         Inst::new(
             "avg_round",
@@ -540,8 +545,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -556,8 +561,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -574,8 +579,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -590,8 +595,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -606,8 +611,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 }
 
@@ -716,16 +721,6 @@ pub(crate) fn define(
 
     let MemTo = &TypeVar::copy_from(Mem, "MemTo".to_string());
 
-    let addr = &Operand::new("addr", iAddr);
-
-    let SS = &Operand::new("SS", &entities.stack_slot);
-    let DSS = &Operand::new("DSS", &entities.dynamic_stack_slot);
-    let Offset = &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address");
-    let x = &Operand::new("x", Mem).with_doc("Value to be stored");
-    let a = &Operand::new("a", Mem).with_doc("Value loaded");
-    let p = &Operand::new("p", iAddr);
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-
     ig.push(
         Inst::new(
             "load",
@@ -737,8 +732,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -753,7 +752,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -762,8 +766,6 @@ pub(crate) fn define(
         "An integer type with more than 8 bits",
         TypeSetBuilder::new().ints(16..64).build(),
     );
-    let x = &Operand::new("x", iExt8);
-    let a = &Operand::new("a", iExt8);
 
     ig.push(
         Inst::new(
@@ -775,8 +777,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt8)])
         .can_load(),
     );
 
@@ -790,8 +796,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt8)])
         .can_load(),
     );
 
@@ -805,7 +815,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt8),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -814,8 +829,6 @@ pub(crate) fn define(
         "An integer type with more than 16 bits",
         TypeSetBuilder::new().ints(32..64).build(),
     );
-    let x = &Operand::new("x", iExt16);
-    let a = &Operand::new("a", iExt16);
 
     ig.push(
         Inst::new(
@@ -827,8 +840,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt16)])
         .can_load(),
     );
 
@@ -842,8 +859,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt16)])
         .can_load(),
     );
 
@@ -857,7 +878,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt16),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -866,8 +892,6 @@ pub(crate) fn define(
         "An integer type with more than 32 bits",
         TypeSetBuilder::new().ints(64..64).build(),
     );
-    let x = &Operand::new("x", iExt32);
-    let a = &Operand::new("a", iExt32);
 
     ig.push(
         Inst::new(
@@ -879,8 +903,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt32)])
         .can_load(),
     );
 
@@ -894,8 +922,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt32)])
         .can_load(),
     );
 
@@ -909,7 +941,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt32),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -922,7 +959,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I16x8).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -933,8 +969,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I16x8).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -947,8 +987,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I16x8).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -961,7 +1005,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I32x4).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -972,8 +1015,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I32x4).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -986,8 +1033,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I32x4).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1000,7 +1051,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I64x2).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -1011,8 +1061,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I64x2).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1025,16 +1079,15 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I64x2).with_doc("Value loaded")])
         .can_load(),
     );
 
-    let x = &Operand::new("x", Mem).with_doc("Value to be stored");
-    let a = &Operand::new("a", Mem).with_doc("Value loaded");
-    let Offset =
-        &Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot");
-
     ig.push(
         Inst::new(
             "stack_load",
@@ -1050,8 +1103,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_load,
         )
-        .operands_in(vec![SS, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1070,7 +1126,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_store,
         )
-        .operands_in(vec![x, SS, Offset])
+        .operands_in(vec![
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
         .can_store(),
     );
 
@@ -1086,8 +1146,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_load,
         )
-        .operands_in(vec![SS, Offset])
-        .operands_out(vec![addr]),
+        .operands_in(vec![
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1101,8 +1164,8 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_load,
         )
-        .operands_in(vec![DSS])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("DSS", &entities.dynamic_stack_slot)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1117,11 +1180,13 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_store,
         )
-        .operands_in(vec![x, DSS])
+        .operands_in(vec![
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("DSS", &entities.dynamic_stack_slot),
+        ])
         .can_store(),
     );
 
-    let GV = &Operand::new("GV", &entities.global_value);
     ig.push(
         Inst::new(
             "dynamic_stack_addr",
@@ -1132,8 +1197,8 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_load,
         )
-        .operands_in(vec![DSS])
-        .operands_out(vec![addr]),
+        .operands_in(vec![Operand::new("DSS", &entities.dynamic_stack_slot)])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1144,8 +1209,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     ig.push(
@@ -1156,8 +1221,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     ig.push(
@@ -1168,8 +1233,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     // Note this instruction is marked as having other side-effects, so GVN won't try to hoist it,
@@ -1186,7 +1251,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr])
+        .operands_out(vec![Operand::new("addr", iAddr)])
         .other_side_effects(),
     );
 
@@ -1198,7 +1263,7 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![addr])
+        .operands_in(vec![Operand::new("addr", iAddr)])
         .other_side_effects(),
     );
 
@@ -1212,7 +1277,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1223,7 +1288,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1236,7 +1301,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     let TableOffset = &TypeVar::new(
@@ -1244,10 +1309,6 @@ pub(crate) fn define(
         "An unsigned table offset",
         TypeSetBuilder::new().ints(32..64).build(),
     );
-    let T = &Operand::new("T", &entities.table);
-    let p = &Operand::new("p", TableOffset);
-    let Offset =
-        &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address");
 
     ig.push(
         Inst::new(
@@ -1267,13 +1328,14 @@ pub(crate) fn define(
         "#,
             &formats.table_addr,
         )
-        .operands_in(vec![T, p, Offset])
-        .operands_out(vec![addr]),
+        .operands_in(vec![
+            Operand::new("T", &entities.table),
+            Operand::new("p", TableOffset),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address"),
+        ])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
-    let N = &Operand::new("N", &imm.imm64);
-    let a = &Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value");
-
     ig.push(
         Inst::new(
             "iconst",
@@ -1285,13 +1347,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_imm,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.imm64)])
+        .operands_out(vec![
+            Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.ieee32);
-    let a = &Operand::new("a", f32_).with_doc("A constant f32 scalar value");
-
     ig.push(
         Inst::new(
             "f32const",
@@ -1302,13 +1363,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_ieee32,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.ieee32)])
+        .operands_out(vec![
+            Operand::new("a", f32_).with_doc("A constant f32 scalar value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.ieee64);
-    let a = &Operand::new("a", f64_).with_doc("A constant f64 scalar value");
-
     ig.push(
         Inst::new(
             "f64const",
@@ -1319,14 +1379,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_ieee64,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.ieee64)])
+        .operands_out(vec![
+            Operand::new("a", f64_).with_doc("A constant f64 scalar value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.pool_constant)
-        .with_doc("The 16 immediate bytes of a 128-bit vector");
-    let a = &Operand::new("a", TxN).with_doc("A constant vector value");
-
     ig.push(
         Inst::new(
             "vconst",
@@ -1337,12 +1395,13 @@ pub(crate) fn define(
         "#,
             &formats.unary_const,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.pool_constant)
+            .with_doc("The 16 immediate bytes of a 128-bit vector")])
+        .operands_out(vec![
+            Operand::new("a", TxN).with_doc("A constant vector value")
+        ]),
     );
 
-    let mask = &Operand::new("mask", &imm.uimm128)
-        .with_doc("The 16 immediate bytes used for selecting the elements to shuffle");
     let Tx16 = &TypeVar::new(
         "Tx16",
         "A SIMD vector with exactly 16 lanes of 8-bit values; eventually this may support other \
@@ -1353,8 +1412,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", Tx16).with_doc("A vector value");
-    let b = &Operand::new("b", Tx16).with_doc("A vector value");
 
     ig.push(
         Inst::new(
@@ -1369,12 +1426,15 @@ pub(crate) fn define(
         "#,
             &formats.shuffle,
         )
-        .operands_in(vec![a, b, mask])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("a", Tx16).with_doc("A vector value"),
+            Operand::new("b", Tx16).with_doc("A vector value"),
+            Operand::new("mask", &imm.uimm128)
+                .with_doc("The 16 immediate bytes used for selecting the elements to shuffle"),
+        ])
+        .operands_out(vec![Operand::new("a", Tx16).with_doc("A vector value")]),
     );
 
-    let a = &Operand::new("a", Ref).with_doc("A constant reference null value");
-
     ig.push(
         Inst::new(
             "null",
@@ -1385,7 +1445,9 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![a]),
+        .operands_out(vec![
+            Operand::new("a", Ref).with_doc("A constant reference null value")
+        ]),
     );
 
     ig.push(Inst::new(
@@ -1398,11 +1460,6 @@ pub(crate) fn define(
         &formats.nullary,
     ));
 
-    let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-    let x = &Operand::new("x", Any).with_doc("Value to use when `c` is true");
-    let y = &Operand::new("y", Any).with_doc("Value to use when `c` is false");
-    let a = &Operand::new("a", Any);
-
     ig.push(
         Inst::new(
             "select",
@@ -1414,8 +1471,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
     ig.push(
@@ -1441,15 +1502,18 @@ pub(crate) fn define(
             "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)])
         .other_side_effects()
         // We can de-duplicate spectre selects since the side effect is
         // idempotent.
         .side_effects_idempotent(),
     );
 
-    let c = &Operand::new("c", Any).with_doc("Controlling value to test");
     ig.push(
         Inst::new(
             "bitselect",
@@ -1462,8 +1526,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", Any).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
     ig.push(
@@ -1480,13 +1548,14 @@ pub(crate) fn define(
             "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", Any).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
-    let a = &Operand::new("a", TxN);
-    let s = &Operand::new("s", i8);
-
     ig.push(
         Inst::new(
             "vany_true",
@@ -1497,8 +1566,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![s]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("s", i8)]),
     );
 
     ig.push(
@@ -1511,13 +1580,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![s]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("s", i8)]),
     );
 
-    let a = &Operand::new("a", TxN);
-    let x = &Operand::new("x", Int);
-
     ig.push(
         Inst::new(
             "vhigh_bits",
@@ -1529,15 +1595,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![x]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("x", Int)]),
     );
 
-    let a = &Operand::new("a", &Int.as_bool());
-    let Cond = &Operand::new("Cond", &imm.intcc);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "icmp",
@@ -1561,14 +1622,14 @@ pub(crate) fn define(
         "#,
             &formats.int_compare,
         )
-        .operands_in(vec![Cond, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.intcc),
+            Operand::new("x", Int),
+            Operand::new("y", Int),
+        ])
+        .operands_out(vec![Operand::new("a", &Int.as_bool())]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
-
     ig.push(
         Inst::new(
             "icmp_imm",
@@ -1583,14 +1644,14 @@ pub(crate) fn define(
         "#,
             &formats.int_compare_imm,
         )
-        .operands_in(vec![Cond, x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.intcc),
+            Operand::new("x", iB),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let a = &Operand::new("a", Int);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "iadd",
@@ -1602,8 +1663,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1617,8 +1678,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1629,8 +1690,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1641,8 +1702,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1658,8 +1719,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1673,8 +1734,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1688,8 +1749,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let I16or32 = &TypeVar::new(
@@ -1698,10 +1759,6 @@ pub(crate) fn define(
         TypeSetBuilder::new().ints(16..32).simd_lanes(4..8).build(),
     );
 
-    let qx = &Operand::new("x", I16or32);
-    let qy = &Operand::new("y", I16or32);
-    let qa = &Operand::new("a", I16or32);
-
     ig.push(
         Inst::new(
             "sqmul_round_sat",
@@ -1714,8 +1771,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![qx, qy])
-        .operands_out(vec![qa]),
+        .operands_in(vec![Operand::new("x", I16or32), Operand::new("y", I16or32)])
+        .operands_out(vec![Operand::new("a", I16or32)]),
     );
 
     ig.push(
@@ -1730,89 +1787,79 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![qx, qy])
-        .operands_out(vec![qa]),
+        .operands_in(vec![Operand::new("x", I16or32), Operand::new("y", I16or32)])
+        .operands_out(vec![Operand::new("a", I16or32)]),
     );
 
-    {
-        // Integer division and remainder are scalar-only; most
-        // hardware does not directly support vector integer division.
+    // Integer division and remainder are scalar-only; most
+    // hardware does not directly support vector integer division.
 
-        let x = &Operand::new("x", iB);
-        let y = &Operand::new("y", iB);
-        let a = &Operand::new("a", iB);
-
-        ig.push(
-            Inst::new(
-                "udiv",
-                r#"
-            Unsigned integer division: `a := \lfloor {x \over y} \rfloor`.
-
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "udiv",
+            r#"
+        Unsigned integer division: `a := \lfloor {x \over y} \rfloor`.
 
-        ig.push(
-            Inst::new(
-                "sdiv",
-                r#"
-            Signed integer division rounded toward zero: `a := sign(xy)
-            \lfloor {|x| \over |y|}\rfloor`.
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero, or if the result is not
-            representable in `B` bits two's complement. This only happens
-            when `x = -2^{B-1}, y = -1`.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "sdiv",
+            r#"
+        Signed integer division rounded toward zero: `a := sign(xy)
+        \lfloor {|x| \over |y|}\rfloor`.
 
-        ig.push(
-            Inst::new(
-                "urem",
-                r#"
-            Unsigned integer remainder.
+        This operation traps if the divisor is zero, or if the result is not
+        representable in `B` bits two's complement. This only happens
+        when `x = -2^{B-1}, y = -1`.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "urem",
+            r#"
+        Unsigned integer remainder.
 
-        ig.push(
-            Inst::new(
-                "srem",
-                r#"
-            Signed integer remainder. The result has the sign of the dividend.
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
-    }
+    ig.push(
+        Inst::new(
+            "srem",
+            r#"
+        Signed integer remainder. The result has the sign of the dividend.
 
-    let a = &Operand::new("a", iB);
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
     ig.push(
         Inst::new(
@@ -1827,8 +1874,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1844,8 +1891,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1860,8 +1907,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1878,8 +1925,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1894,8 +1941,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1910,8 +1957,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1931,19 +1978,10 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let a = &Operand::new("a", iB);
-    let x = &Operand::new("x", iB);
-    let y = &Operand::new("y", iB);
-
-    let c_in = &Operand::new("c_in", i8).with_doc("Input carry flag");
-    let c_out = &Operand::new("c_out", i8).with_doc("Output carry flag");
-    let b_in = &Operand::new("b_in", i8).with_doc("Input borrow flag");
-    let b_out = &Operand::new("b_out", i8).with_doc("Output borrow flag");
-
     ig.push(
         Inst::new(
             "iadd_cin",
@@ -1961,8 +1999,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, c_in])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("c_in", i8).with_doc("Input carry flag"),
+        ])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1983,8 +2025,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a, c_out]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("c_out", i8).with_doc("Output carry flag"),
+        ]),
     );
 
     ig.push(
@@ -2005,38 +2050,42 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, c_in])
-        .operands_out(vec![a, c_out]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("c_in", i8).with_doc("Input carry flag"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("c_out", i8).with_doc("Output carry flag"),
+        ]),
     );
 
-    {
-        let code = &Operand::new("code", &imm.trapcode);
-
-        let i32_64 = &TypeVar::new(
-            "i32_64",
-            "A 32 or 64-bit scalar integer type",
-            TypeSetBuilder::new().ints(32..64).build(),
-        );
+    let i32_64 = &TypeVar::new(
+        "i32_64",
+        "A 32 or 64-bit scalar integer type",
+        TypeSetBuilder::new().ints(32..64).build(),
+    );
 
-        let a = &Operand::new("a", i32_64);
-        let x = &Operand::new("x", i32_64);
-        let y = &Operand::new("y", i32_64);
-        ig.push(
-            Inst::new(
-                "uadd_overflow_trap",
-                r#"
-            Unsigned addition of x and y, trapping if the result overflows.
+    ig.push(
+        Inst::new(
+            "uadd_overflow_trap",
+            r#"
+        Unsigned addition of x and y, trapping if the result overflows.
 
-            Accepts 32 or 64-bit integers, and does not support vector types.
-            "#,
-                &formats.int_add_trap,
-            )
-            .operands_in(vec![x, y, code])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
-    }
+        Accepts 32 or 64-bit integers, and does not support vector types.
+        "#,
+            &formats.int_add_trap,
+        )
+        .operands_in(vec![
+            Operand::new("x", i32_64),
+            Operand::new("y", i32_64),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .operands_out(vec![Operand::new("a", i32_64)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
     ig.push(
         Inst::new(
@@ -2055,8 +2104,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, b_in])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("b_in", i8).with_doc("Input borrow flag"),
+        ])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2077,8 +2130,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a, b_out]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("b_out", i8).with_doc("Output borrow flag"),
+        ]),
     );
 
     ig.push(
@@ -2099,8 +2155,15 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, b_in])
-        .operands_out(vec![a, b_out]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("b_in", i8).with_doc("Input borrow flag"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("b_out", i8).with_doc("Output borrow flag"),
+        ]),
     );
 
     let bits = &TypeVar::new(
@@ -2113,9 +2176,6 @@ pub(crate) fn define(
             .includes_scalars(true)
             .build(),
     );
-    let x = &Operand::new("x", bits);
-    let y = &Operand::new("y", bits);
-    let a = &Operand::new("a", bits);
 
     ig.push(
         Inst::new(
@@ -2125,8 +2185,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2137,8 +2197,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2149,8 +2209,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2161,8 +2221,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2175,8 +2235,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2189,8 +2249,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2203,14 +2263,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
-    let a = &Operand::new("a", iB);
-
     ig.push(
         Inst::new(
             "band_imm",
@@ -2224,8 +2280,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2241,8 +2297,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2258,15 +2314,10 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let x = &Operand::new("x", Int).with_doc("Scalar or vector value to shift");
-    let y = &Operand::new("y", iB).with_doc("Number of bits to shift");
-    let Y = &Operand::new("Y", &imm.imm64);
-    let a = &Operand::new("a", Int);
-
     ig.push(
         Inst::new(
             "rotl",
@@ -2277,8 +2328,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2291,8 +2345,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2305,8 +2362,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2319,8 +2379,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2341,8 +2404,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2364,8 +2430,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2380,8 +2449,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2394,8 +2466,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2408,8 +2483,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2422,13 +2500,13 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
-    let x = &Operand::new("x", iB);
-    let a = &Operand::new("a", iB);
-
     ig.push(
         Inst::new(
             "bitrev",
@@ -2439,8 +2517,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2455,8 +2533,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2471,8 +2549,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2487,13 +2565,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let x = &Operand::new("x", iSwappable);
-    let a = &Operand::new("a", iSwappable);
-
     ig.push(
         Inst::new(
             "bswap",
@@ -2504,13 +2579,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iSwappable)])
+        .operands_out(vec![Operand::new("a", iSwappable)]),
     );
 
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", Int);
-
     ig.push(
         Inst::new(
             "popcnt",
@@ -2521,8 +2593,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let Float = &TypeVar::new(
@@ -2534,10 +2606,6 @@ pub(crate) fn define(
             .dynamic_simd_lanes(Interval::All)
             .build(),
     );
-    let Cond = &Operand::new("Cond", &imm.floatcc);
-    let x = &Operand::new("x", Float);
-    let y = &Operand::new("y", Float);
-    let a = &Operand::new("a", &Float.as_bool());
 
     ig.push(
         Inst::new(
@@ -2611,15 +2679,14 @@ pub(crate) fn define(
         "#,
             &formats.float_compare,
         )
-        .operands_in(vec![Cond, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.floatcc),
+            Operand::new("x", Float),
+            Operand::new("y", Float),
+        ])
+        .operands_out(vec![Operand::new("a", &Float.as_bool())]),
     );
 
-    let x = &Operand::new("x", Float);
-    let y = &Operand::new("y", Float);
-    let z = &Operand::new("z", Float);
-    let a = &Operand::new("a", Float).with_doc("Result of applying operator to each lane");
-
     ig.push(
         Inst::new(
             "fadd",
@@ -2628,8 +2695,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2640,8 +2709,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2652,8 +2723,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2668,8 +2741,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2680,8 +2755,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2695,12 +2772,16 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, z])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Float),
+            Operand::new("y", Float),
+            Operand::new("z", Float),
+        ])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit inverted");
-
     ig.push(
         Inst::new(
             "fneg",
@@ -2711,12 +2792,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit inverted")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit cleared");
-
     ig.push(
         Inst::new(
             "fabs",
@@ -2727,12 +2808,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit cleared")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit changed to that of ``y``");
-
     ig.push(
         Inst::new(
             "fcopysign",
@@ -2744,12 +2825,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit changed to that of ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``");
-
     ig.push(
         Inst::new(
             "fmin",
@@ -2763,8 +2844,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``")
+        ]),
     );
 
     ig.push(
@@ -2779,12 +2862,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``");
-
     ig.push(
         Inst::new(
             "fmax",
@@ -2798,8 +2881,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``")
+        ]),
     );
 
     ig.push(
@@ -2814,12 +2899,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` rounded to integral value");
-
     ig.push(
         Inst::new(
             "ceil",
@@ -2828,8 +2913,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2840,8 +2927,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2852,8 +2941,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2865,13 +2956,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", Ref);
-
     ig.push(
         Inst::new(
             "is_null",
@@ -2883,13 +2973,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Ref)])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", Ref);
-
     ig.push(
         Inst::new(
             "is_invalid",
@@ -2901,14 +2988,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Ref)])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let x = &Operand::new("x", Mem);
-    let a = &Operand::new("a", MemTo).with_doc("Bits of `x` reinterpreted");
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-
     ig.push(
         Inst::new(
             "bitcast",
@@ -2926,13 +3009,15 @@ pub(crate) fn define(
         "#,
             &formats.load_no_offset,
         )
-        .operands_in(vec![MemFlags, x])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", Mem),
+        ])
+        .operands_out(vec![
+            Operand::new("a", MemTo).with_doc("Bits of `x` reinterpreted")
+        ]),
     );
 
-    let a = &Operand::new("a", TxN).with_doc("A vector value");
-    let s = &Operand::new("s", &TxN.lane_of()).with_doc("A scalar value");
-
     ig.push(
         Inst::new(
             "scalar_to_vector",
@@ -2942,8 +3027,10 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![s])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("s", &TxN.lane_of()).with_doc("A scalar value")
+        ])
+        .operands_out(vec![Operand::new("a", TxN).with_doc("A vector value")]),
     );
 
     let Truthy = &TypeVar::new(
@@ -2956,8 +3043,6 @@ pub(crate) fn define(
         "An integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", Truthy);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -2969,8 +3054,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Truthy)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let Int = &TypeVar::new(
@@ -2984,8 +3069,6 @@ pub(crate) fn define(
         "A smaller integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -2998,8 +3081,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let I16or32or64xN = &TypeVar::new(
@@ -3013,10 +3096,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", I16or32or64xN);
-    let y = &Operand::new("y", I16or32or64xN);
-    let a = &Operand::new("a", &I16or32or64xN.split_lanes());
-
     ig.push(
         Inst::new(
             "snarrow",
@@ -3030,8 +3109,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I16or32or64xN),
+            Operand::new("y", I16or32or64xN),
+        ])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     ig.push(
@@ -3050,8 +3132,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I16or32or64xN),
+            Operand::new("y", I16or32or64xN),
+        ])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     ig.push(
@@ -3069,8 +3154,8 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I16or32or64xN), Operand::new("y", I16or32or64xN)])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     let I8or16or32xN = &TypeVar::new(
@@ -3084,9 +3169,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", I8or16or32xN);
-    let a = &Operand::new("a", &I8or16or32xN.merge_lanes());
-
     ig.push(
         Inst::new(
             "swiden_low",
@@ -3097,8 +3179,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3111,8 +3193,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3125,8 +3207,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3139,14 +3221,10 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
-    let x = &Operand::new("x", I8or16or32xN);
-    let y = &Operand::new("y", I8or16or32xN);
-    let a = &Operand::new("a", I8or16or32xN);
-
     ig.push(
         Inst::new(
             "iadd_pairwise",
@@ -3160,8 +3238,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8or16or32xN),
+            Operand::new("y", I8or16or32xN),
+        ])
+        .operands_out(vec![Operand::new("a", I8or16or32xN)]),
     );
 
     let I8x16 = &TypeVar::new(
@@ -3173,9 +3254,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let x = &Operand::new("x", I8x16);
-    let y = &Operand::new("y", I8x16);
-    let a = &Operand::new("a", I16x8);
 
     ig.push(
         Inst::new(
@@ -3190,8 +3268,8 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8x16), Operand::new("y", I8x16)])
+        .operands_out(vec![Operand::new("a", I16x8)]),
     );
 
     {
@@ -3201,9 +3279,6 @@ pub(crate) fn define(
             TypeSetBuilder::new().ints(Interval::All).build(),
         );
 
-        let x = &Operand::new("x", Int);
-        let a = &Operand::new("a", IntTo);
-
         ig.push(
             Inst::new(
                 "uextend",
@@ -3220,8 +3295,8 @@ pub(crate) fn define(
         "#,
                 &formats.unary,
             )
-            .operands_in(vec![x])
-            .operands_out(vec![a]),
+            .operands_in(vec![Operand::new("x", Int)])
+            .operands_out(vec![Operand::new("a", IntTo)]),
         );
 
         ig.push(
@@ -3240,8 +3315,8 @@ pub(crate) fn define(
         "#,
                 &formats.unary,
             )
-            .operands_in(vec![x])
-            .operands_out(vec![a]),
+            .operands_in(vec![Operand::new("x", Int)])
+            .operands_out(vec![Operand::new("a", IntTo)]),
         );
     }
 
@@ -3254,9 +3329,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", Float);
-    let a = &Operand::new("a", FloatTo);
-
     ig.push(
         Inst::new(
             "fpromote",
@@ -3274,8 +3346,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3295,8 +3367,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     let F64x2 = &TypeVar::new(
@@ -3318,9 +3390,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", F64x2);
-    let a = &Operand::new("a", F32x4);
-
     ig.push(
         Inst::new(
             "fvdemote",
@@ -3340,8 +3409,8 @@ pub(crate) fn define(
                 "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", F64x2)])
+        .operands_out(vec![Operand::new("a", F32x4)]),
     );
 
     ig.push(
@@ -3358,8 +3427,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![x]),
+        .operands_in(vec![Operand::new("a", F32x4)])
+        .operands_out(vec![Operand::new("x", F64x2)]),
     );
 
     let FloatScalar = &TypeVar::new(
@@ -3372,8 +3441,6 @@ pub(crate) fn define(
         "An scalar only integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", FloatScalar);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -3388,8 +3455,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("x", FloatScalar)])
+        .operands_out(vec![Operand::new("a", IntTo)])
         .can_trap()
         .side_effects_idempotent(),
     );
@@ -3407,8 +3474,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("x", FloatScalar)])
+        .operands_out(vec![Operand::new("a", IntTo)])
         .can_trap()
         .side_effects_idempotent(),
     );
@@ -3422,9 +3489,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", Float);
-    let a = &Operand::new("a", IntTo);
-
     ig.push(
         Inst::new(
             "fcvt_to_uint_sat",
@@ -3435,8 +3499,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     ig.push(
@@ -3448,8 +3512,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     ig.push(
@@ -3462,8 +3526,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let Int = &TypeVar::new(
@@ -3474,8 +3538,6 @@ pub(crate) fn define(
             .simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", FloatTo);
 
     ig.push(
         Inst::new(
@@ -3490,8 +3552,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3507,8 +3569,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3527,8 +3589,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     let WideInt = &TypeVar::new(
@@ -3539,9 +3601,6 @@ pub(crate) fn define(
             .simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", WideInt);
-    let lo = &Operand::new("lo", &WideInt.half_width()).with_doc("The low bits of `x`");
-    let hi = &Operand::new("hi", &WideInt.half_width()).with_doc("The high bits of `x`");
 
     ig.push(
         Inst::new(
@@ -3557,15 +3616,13 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![lo, hi]),
+        .operands_in(vec![Operand::new("x", WideInt)])
+        .operands_out(vec![
+            Operand::new("lo", &WideInt.half_width()).with_doc("The low bits of `x`"),
+            Operand::new("hi", &WideInt.half_width()).with_doc("The high bits of `x`"),
+        ]),
     );
 
-    let lo = &Operand::new("lo", NarrowInt);
-    let hi = &Operand::new("hi", NarrowInt);
-    let a = &Operand::new("a", &NarrowInt.double_width())
-        .with_doc("The concatenation of `lo` and `hi`");
-
     ig.push(
         Inst::new(
             "iconcat",
@@ -3578,8 +3635,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![lo, hi])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("lo", NarrowInt),
+            Operand::new("hi", NarrowInt),
+        ])
+        .operands_out(vec![Operand::new("a", &NarrowInt.double_width())
+            .with_doc("The concatenation of `lo` and `hi`")]),
     );
 
     // Instructions relating to atomic memory accesses and fences
@@ -3588,12 +3649,6 @@ pub(crate) fn define(
         "Any type that can be stored in memory, which can be used in an atomic operation",
         TypeSetBuilder::new().ints(8..64).build(),
     );
-    let x = &Operand::new("x", AtomicMem).with_doc("Value to be atomically stored");
-    let a = &Operand::new("a", AtomicMem).with_doc("Value atomically loaded");
-    let e = &Operand::new("e", AtomicMem).with_doc("Expected value in CAS");
-    let p = &Operand::new("p", iAddr);
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-    let AtomicRmwOp = &Operand::new("AtomicRmwOp", &imm.atomic_rmw_op);
 
     ig.push(
         Inst::new(
@@ -3607,8 +3662,15 @@ pub(crate) fn define(
         "#,
             &formats.atomic_rmw,
         )
-        .operands_in(vec![MemFlags, AtomicRmwOp, p, x])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("AtomicRmwOp", &imm.atomic_rmw_op),
+            Operand::new("p", iAddr),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .can_store()
         .other_side_effects(),
@@ -3629,8 +3691,15 @@ pub(crate) fn define(
         "#,
             &formats.atomic_cas,
         )
-        .operands_in(vec![MemFlags, p, e, x])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("e", AtomicMem).with_doc("Expected value in CAS"),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .can_store()
         .other_side_effects(),
@@ -3649,8 +3718,13 @@ pub(crate) fn define(
         "#,
             &formats.load_no_offset,
         )
-        .operands_in(vec![MemFlags, p])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .other_side_effects(),
     );
@@ -3668,7 +3742,11 @@ pub(crate) fn define(
         "#,
             &formats.store_no_offset,
         )
-        .operands_in(vec![MemFlags, x, p])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+            Operand::new("p", iAddr),
+        ])
         .can_store()
         .other_side_effects(),
     );
@@ -3695,9 +3773,6 @@ pub(crate) fn define(
             .dynamic_simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", TxN).with_doc("The dynamic vector to extract from");
-    let y = &Operand::new("y", &imm.uimm8).with_doc("128-bit vector index");
-    let a = &Operand::new("a", &TxN.dynamic_to_vector()).with_doc("New fixed vector");
 
     ig.push(
         Inst::new(
@@ -3707,7 +3782,12 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm8,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN).with_doc("The dynamic vector to extract from"),
+            Operand::new("y", &imm.uimm8).with_doc("128-bit vector index"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", &TxN.dynamic_to_vector()).with_doc("New fixed vector")
+        ]),
     );
 }
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 69796c4bc..57e187142 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -513,10 +513,14 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
     }
 
     fn amode(&mut self, ty: Type, addr: Value, offset: u32) -> AMode {
+        let addr_ty = self.value_type(addr);
+        assert!(addr_ty == I64 || addr_ty == R64);
         lower_address(self.lower_ctx, ty, addr, offset as i32)
     }
 
     fn pair_amode(&mut self, addr: Value, offset: u32) -> PairAMode {
+        let addr_ty = self.value_type(addr);
+        assert!(addr_ty == I64 || addr_ty == R64);
         lower_pair_address(self.lower_ctx, addr, offset as i32)
     }
 
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index b08d6558f..0a15865e7 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -697,63 +697,63 @@
 
 ;;;;;  Rules for `uload8`;;;;;;;;;
 (rule
-  (lower (uload8 flags p offset))
+  (lower (uload8 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 8) flags $I64))
 ;;;;;  Rules for `sload8`;;;;;;;;;
 (rule
-  (lower (sload8 flags p offset))
+  (lower (sload8 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 8) flags $I64))
 ;;;;;  Rules for `uload16`;;;;;;;;;
 (rule
-  (lower (uload16 flags p offset))
+  (lower (uload16 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 16) flags $I64))
 
 ;;;;;  Rules for `iload16`;;;;;;;;;
 (rule
-  (lower (sload16 flags p offset))
+  (lower (sload16 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 16) flags $I64))
 
 ;;;;;  Rules for `uload32`;;;;;;;;;
 (rule
-  (lower (uload32 flags p offset))
+  (lower (uload32 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 32) flags $I64))
 
 ;;;;;  Rules for `iload16`;;;;;;;;;
 (rule
-  (lower (sload32 flags p offset))
+  (lower (sload32 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 32) flags $I64))
 
 (rule
-  (lower (has_type ty (load flags p offset)))
+  (lower (has_type ty (load flags p @ (value_type (ty_addr64 _)) offset)))
   (gen_load p offset (load_op ty) flags ty)
 )
 ;;;; for I128
 (rule 1
-  (lower (has_type $I128 (load flags p offset)))
+  (lower (has_type $I128 (load flags p @ (value_type (ty_addr64 _)) offset)))
   (gen_load_128 p offset flags))
 
 ;;;;;  Rules for `istore8`;;;;;;;;;
 (rule
-  (lower (istore8 flags x p offset))
+  (lower (istore8 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sb) flags x))
 ;;;;;  Rules for `istore16`;;;;;;;;;
 (rule
-  (lower (istore16 flags x p offset))
+  (lower (istore16 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sh) flags x))
 
 ;;;;;  Rules for `istore32`;;;;;;;;;
 (rule
-  (lower (istore32 flags x p offset))
+  (lower (istore32 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sw) flags x))
 
 ;;;;;  Rules for `store`;;;;;;;;;
 (rule
-  (lower (store flags x @ (value_type ty) p offset))
+  (lower (store flags x @ (value_type ty) p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (store_op ty) flags x))
 
 ;;; special for I128
 (rule 1
-  (lower (store flags x @ (value_type $I128 ) p offset))
+  (lower (store flags x @ (value_type $I128 ) p @ (value_type (ty_addr64 _)) offset))
   (gen_store_128 p offset flags x))
 
 (decl gen_icmp (IntCC ValueRegs ValueRegs Type) Reg)
diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle
index 815513f04..ffc5f7350 100644
--- a/cranelift/codegen/src/isa/s390x/inst.isle
+++ b/cranelift/codegen/src/isa/s390x/inst.isle
@@ -1811,10 +1811,10 @@
 
 (decl lower_address (MemFlags Value Offset32) MemArg)
 
-(rule (lower_address flags addr (i64_from_offset offset))
+(rule (lower_address flags addr @ (value_type (ty_addr64 _)) (i64_from_offset offset))
       (memarg_reg_plus_off addr offset 0 flags))
 
-(rule 1 (lower_address flags (iadd x y) (i64_from_offset 0))
+(rule 1 (lower_address flags (has_type (ty_addr64 _) (iadd x y)) (i64_from_offset 0))
       (memarg_reg_plus_reg x y 0 flags))
 
 (rule 1 (lower_address flags
@@ -1828,10 +1828,10 @@
 
 (decl lower_address_bias (MemFlags Value Offset32 u8) MemArg)
 
-(rule (lower_address_bias flags addr (i64_from_offset offset) bias)
+(rule (lower_address_bias flags addr @ (value_type $I64) (i64_from_offset offset) bias)
       (memarg_reg_plus_off addr offset bias flags))
 
-(rule 1 (lower_address_bias flags (iadd x y) (i64_from_offset 0) bias)
+(rule 1 (lower_address_bias flags (has_type $I64 (iadd x y)) (i64_from_offset 0) bias)
       (memarg_reg_plus_reg x y bias flags))
 
 
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index 5293d7071..a03e38083 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -863,6 +863,12 @@
             Xorpd
             Phaddw
             Phaddd
+            Punpckhdq
+            Punpckldq
+            Punpckhqdq
+            Punpcklqdq
+            Pshuflw
+            Pshufhw
           ))
 
 (type CmpOpcode extern
@@ -1114,7 +1120,7 @@
         (Amode.ImmRegRegShift off sum index shift flags)))
 
 ;; Finally, define the toplevel `to_amode`.
-(rule (to_amode flags base offset)
+(rule (to_amode flags base @ (value_type (ty_addr64 _)) offset)
       (amode_finalize (amode_add (amode_initial flags offset) base)))
 
 ;; If an amode has no registers at all and only offsets (a constant
@@ -1347,6 +1353,12 @@
             Vcvttps2dq
             Vphaddw
             Vphaddd
+            Vpunpckhdq
+            Vpunpckldq
+            Vpunpckhqdq
+            Vpunpcklqdq
+            Vpshuflw
+            Vpshufhw
           ))
 
 (type Avx512Opcode extern
@@ -2729,6 +2741,38 @@
       (if-let $true (has_avx))
       (xmm_rmir_vex (AvxOpcode.Vpunpcklwd) src1 src2))
 
+;; Helper for creating `punpckldq` instructions.
+(decl x64_punpckldq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckldq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckldq) src1 src2))
+(rule 1 (x64_punpckldq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckldq) src1 src2))
+
+;; Helper for creating `punpckhdq` instructions.
+(decl x64_punpckhdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckhdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckhdq) src1 src2))
+(rule 1 (x64_punpckhdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckhdq) src1 src2))
+
+;; Helper for creating `punpcklqdq` instructions.
+(decl x64_punpcklqdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpcklqdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpcklqdq) src1 src2))
+(rule 1 (x64_punpcklqdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpcklqdq) src1 src2))
+
+;; Helper for creating `punpckhqdq` instructions.
+(decl x64_punpckhqdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckhqdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckhqdq) src1 src2))
+(rule 1 (x64_punpckhqdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckhqdq) src1 src2))
+
 ;; Helper for creating `unpcklps` instructions.
 (decl x64_unpcklps (Xmm XmmMem) Xmm)
 (rule 0 (x64_unpcklps src1 src2)
@@ -3284,6 +3328,22 @@
       (if-let $true (has_avx))
       (xmm_rmir_vex (AvxOpcode.Vpshufb) src1 src2))
 
+;; Helper for creating `pshuflw` instructions.
+(decl x64_pshuflw (XmmMem u8) Xmm)
+(rule (x64_pshuflw src imm)
+      (xmm_unary_rm_r_imm (SseOpcode.Pshuflw) src imm))
+(rule 1 (x64_pshuflw src imm)
+      (if-let $true (has_avx))
+      (xmm_unary_rm_r_imm_vex (AvxOpcode.Vpshuflw) src imm))
+
+;; Helper for creating `pshufhw` instructions.
+(decl x64_pshufhw (XmmMem u8) Xmm)
+(rule (x64_pshufhw src imm)
+      (xmm_unary_rm_r_imm (SseOpcode.Pshufhw) src imm))
+(rule 1 (x64_pshufhw src imm)
+      (if-let $true (has_avx))
+      (xmm_unary_rm_r_imm_vex (AvxOpcode.Vpshufhw) src imm))
+
 ;; Helper for creating `shufps` instructions.
 (decl x64_shufps (Xmm XmmMem u8) Xmm)
 (rule 0 (x64_shufps src1 src2 byte)
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index ca6e40ce5..01ee044ab 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -1117,6 +1117,12 @@ pub enum SseOpcode {
     Xorpd,
     Phaddw,
     Phaddd,
+    Punpckhdq,
+    Punpckldq,
+    Punpckhqdq,
+    Punpcklqdq,
+    Pshuflw,
+    Pshufhw,
 }
 
 impl SseOpcode {
@@ -1256,7 +1262,13 @@ impl SseOpcode {
             | SseOpcode::Subpd
             | SseOpcode::Subsd
             | SseOpcode::Ucomisd
-            | SseOpcode::Xorpd => SSE2,
+            | SseOpcode::Xorpd
+            | SseOpcode::Punpckldq
+            | SseOpcode::Punpckhdq
+            | SseOpcode::Punpcklqdq
+            | SseOpcode::Punpckhqdq
+            | SseOpcode::Pshuflw
+            | SseOpcode::Pshufhw => SSE2,
 
             SseOpcode::Pabsb
             | SseOpcode::Pabsw
@@ -1501,6 +1513,12 @@ impl fmt::Debug for SseOpcode {
             SseOpcode::Xorpd => "xorpd",
             SseOpcode::Phaddw => "phaddw",
             SseOpcode::Phaddd => "phaddd",
+            SseOpcode::Punpckldq => "punpckldq",
+            SseOpcode::Punpckhdq => "punpckhdq",
+            SseOpcode::Punpcklqdq => "punpcklqdq",
+            SseOpcode::Punpckhqdq => "punpckhqdq",
+            SseOpcode::Pshuflw => "pshuflw",
+            SseOpcode::Pshufhw => "pshufhw",
         };
         write!(fmt, "{}", name)
     }
@@ -1669,7 +1687,13 @@ impl AvxOpcode {
             | AvxOpcode::Vcvttpd2dq
             | AvxOpcode::Vcvttps2dq
             | AvxOpcode::Vphaddw
-            | AvxOpcode::Vphaddd => {
+            | AvxOpcode::Vphaddd
+            | AvxOpcode::Vpunpckldq
+            | AvxOpcode::Vpunpckhdq
+            | AvxOpcode::Vpunpcklqdq
+            | AvxOpcode::Vpunpckhqdq
+            | AvxOpcode::Vpshuflw
+            | AvxOpcode::Vpshufhw => {
                 smallvec![InstructionSet::AVX]
             }
         }
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index b92e1ecd9..6d86bffd0 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1789,6 +1789,8 @@ pub(crate) fn emit(
                 SseOpcode::Roundpd => (LegacyPrefixes::_66, 0x0F3A09, 3),
                 SseOpcode::Roundsd => (LegacyPrefixes::_66, 0x0F3A0B, 3),
                 SseOpcode::Pshufd => (LegacyPrefixes::_66, 0x0F70, 2),
+                SseOpcode::Pshuflw => (LegacyPrefixes::_F2, 0x0F70, 2),
+                SseOpcode::Pshufhw => (LegacyPrefixes::_F3, 0x0F70, 2),
                 _ => unimplemented!("Opcode {:?} not implemented", op),
             };
             match src {
@@ -1946,6 +1948,10 @@ pub(crate) fn emit(
                 SseOpcode::Punpckhwd => (LegacyPrefixes::_66, 0x0F69, 2),
                 SseOpcode::Punpcklbw => (LegacyPrefixes::_66, 0x0F60, 2),
                 SseOpcode::Punpcklwd => (LegacyPrefixes::_66, 0x0F61, 2),
+                SseOpcode::Punpckldq => (LegacyPrefixes::_66, 0x0F62, 2),
+                SseOpcode::Punpcklqdq => (LegacyPrefixes::_66, 0x0F6C, 2),
+                SseOpcode::Punpckhdq => (LegacyPrefixes::_66, 0x0F6A, 2),
+                SseOpcode::Punpckhqdq => (LegacyPrefixes::_66, 0x0F6D, 2),
                 SseOpcode::Pxor => (LegacyPrefixes::_66, 0x0FEF, 2),
                 SseOpcode::Subps => (LegacyPrefixes::None, 0x0F5C, 2),
                 SseOpcode::Subpd => (LegacyPrefixes::_66, 0x0F5C, 2),
@@ -2171,6 +2177,10 @@ pub(crate) fn emit(
                 AvxOpcode::Vmaxsd => (LP::_F2, OM::_0F, 0x5F),
                 AvxOpcode::Vphaddw => (LP::_66, OM::_0F38, 0x01),
                 AvxOpcode::Vphaddd => (LP::_66, OM::_0F38, 0x02),
+                AvxOpcode::Vpunpckldq => (LP::_66, OM::_0F, 0x62),
+                AvxOpcode::Vpunpckhdq => (LP::_66, OM::_0F, 0x6A),
+                AvxOpcode::Vpunpcklqdq => (LP::_66, OM::_0F, 0x6C),
+                AvxOpcode::Vpunpckhqdq => (LP::_66, OM::_0F, 0x6D),
                 _ => panic!("unexpected rmir vex opcode {op:?}"),
             };
             VexInstruction::new()
@@ -2400,6 +2410,8 @@ pub(crate) fn emit(
             let (prefix, map, opcode) = match op {
                 AvxOpcode::Vroundps => (LegacyPrefixes::_66, OpcodeMap::_0F3A, 0x08),
                 AvxOpcode::Vroundpd => (LegacyPrefixes::_66, OpcodeMap::_0F3A, 0x09),
+                AvxOpcode::Vpshuflw => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x70),
+                AvxOpcode::Vpshufhw => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x70),
                 _ => panic!("unexpected rmr_imm_vex opcode {op:?}"),
             };
 
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index ac27aae4d..e33c5ee78 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -3529,16 +3529,98 @@
 
 ;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; Special case for the `punpckhbw` instruction which interleaves the upper
-;; lanes of the two input registers.
-(rule 4 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
+;; Special case the `pshuf{l,h}w` instruction which shuffles four 16-bit
+;; integers within one value, preserving the other four 16-bit integers in that
+;; value (either the high or low half). The complicated logic is in the
+;; extractors here implemented in Rust and note that there's two cases for each
+;; instruction here to match when either the first or second shuffle operand is
+;; used.
+(rule 12 (lower (shuffle x y (pshuflw_lhs_imm imm)))
+      (x64_pshuflw x imm))
+(rule 11 (lower (shuffle x y (pshuflw_rhs_imm imm)))
+      (x64_pshuflw y imm))
+(rule 10 (lower (shuffle x y (pshufhw_lhs_imm imm)))
+      (x64_pshufhw x imm))
+(rule 9 (lower (shuffle x y (pshufhw_rhs_imm imm)))
+      (x64_pshufhw y imm))
+
+(decl pshuflw_lhs_imm (u8) Immediate)
+(extern extractor pshuflw_lhs_imm pshuflw_lhs_imm)
+(decl pshuflw_rhs_imm (u8) Immediate)
+(extern extractor pshuflw_rhs_imm pshuflw_rhs_imm)
+(decl pshufhw_lhs_imm (u8) Immediate)
+(extern extractor pshufhw_lhs_imm pshufhw_lhs_imm)
+(decl pshufhw_rhs_imm (u8) Immediate)
+(extern extractor pshufhw_rhs_imm pshufhw_rhs_imm)
+
+;; Special case for the `pshufd` instruction which will permute 32-bit values
+;; within a single register. This is only applicable if the `imm` specified
+;; selects 32-bit values from either `x` or `y`, but not both. This means
+;; there's one rule for selecting from `x` and another rule for selecting from
+;; `y`.
+(rule 8 (lower (shuffle x y (pshufd_lhs_imm imm)))
+      (x64_pshufd x imm))
+(rule 7 (lower (shuffle x y (pshufd_rhs_imm imm)))
+      (x64_pshufd y imm))
+
+(decl pshufd_lhs_imm (u8) Immediate)
+(extern extractor pshufd_lhs_imm pshufd_lhs_imm)
+(decl pshufd_rhs_imm (u8) Immediate)
+(extern extractor pshufd_rhs_imm pshufd_rhs_imm)
+
+;; Special case for i8-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
       (x64_punpckhbw a b))
-
-;; Special case for the `punpcklbw` instruction which interleaves the lower
-;; lanes of the two input registers.
-(rule 4 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
       (x64_punpcklbw a b))
 
+;; Special case for i16-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908)))
+      (x64_punpckhwd a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100)))
+      (x64_punpcklwd a b))
+
+;; Special case for i32-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908)))
+      (x64_punpckhdq a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100)))
+      (x64_punpckldq a b))
+
+;; Special case for i64-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908)))
+      (x64_punpckhqdq a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100)))
+      (x64_punpcklqdq a b))
+
+;; If the vector shift mask is all 0s then that means the first byte of the
+;; first operand is broadcast to all bytes. Falling through would load an
+;; all-zeros constant from a rip-relative location but it should be slightly
+;; more efficient to execute the `pshufb` here-and-now with an xor'd-to-be-zero
+;; register.
+(rule 6 (lower (shuffle a _ (u128_from_immediate 0)))
+      (x64_pshufb a (xmm_zero $I8X16)))
+
+;; Special case for the `shufps` instruction which will select two 32-bit values
+;; from the first operand and two 32-bit values from the second operand. Note
+;; that there is a second case here as well for when the operands can be
+;; swapped.
+;;
+;; Note that the priority of this instruction is currently lower than the above
+;; special cases since `shufps` handles many of them and for now it's
+;; hypothesized that the dedicated instructions are better than `shufps`.
+;; Someone with more knowledge about x86 timings should perhaps reorder the
+;; rules here eventually though.
+(rule 5 (lower (shuffle x y (shufps_imm imm)))
+      (x64_shufps x y imm))
+(rule 4 (lower (shuffle x y (shufps_rev_imm imm)))
+      (x64_shufps y x imm))
+
+(decl shufps_imm(u8) Immediate)
+(extern extractor shufps_imm shufps_imm)
+(decl shufps_rev_imm(u8) Immediate)
+(extern extractor shufps_rev_imm shufps_rev_imm)
+
+
 ;; If `lhs` and `rhs` are the same we can use a single PSHUFB to shuffle the XMM
 ;; register. We statically build `constructed_mask` to zero out any unknown lane
 ;; indices (may not be completely necessary: verification could fail incorrect
diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs
index 61be54a00..a5549e0fd 100644
--- a/cranelift/codegen/src/isa/x64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/x64/lower/isle.rs
@@ -999,6 +999,124 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
             },
         }
     }
+
+    fn pshufd_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufd_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        // When selecting from the right-hand-side, subtract these all by 4
+        // which will bail out if anything is less than 4. Afterwards the check
+        // is the same as `pshufd_lhs_imm` above.
+        let a = a.checked_sub(4)?;
+        let b = b.checked_sub(4)?;
+        let c = c.checked_sub(4)?;
+        let d = d.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn shufps_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // The `shufps` instruction selects the first two elements from the
+        // first vector and the second two elements from the second vector, so
+        // offset the third/fourth selectors by 4 and then make sure everything
+        // fits in 32-bits.
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        let c = c.checked_sub(4)?;
+        let d = d.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn shufps_rev_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // This is almost the same as `shufps_imm` except the elements that are
+        // subtracted are reversed. This handles the case that `shufps`
+        // instruction can be emitted if the order of the operands are swapped.
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        let a = a.checked_sub(4)?;
+        let b = b.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshuflw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Similar to `shufps` except this operates over 16-bit values so four
+        // of them must be fixed and the other four must be in-range to encode
+        // in the immediate.
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshuflw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let a = a.checked_sub(8)?;
+        let b = b.checked_sub(8)?;
+        let c = c.checked_sub(8)?;
+        let d = d.checked_sub(8)?;
+        let e = e.checked_sub(8)?;
+        let f = f.checked_sub(8)?;
+        let g = g.checked_sub(8)?;
+        let h = h.checked_sub(8)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufhw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Similar to `pshuflw` except that the first four operands must be
+        // fixed and the second four are offset by an extra 4 and tested to
+        // make sure they're all in the range [4, 8).
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let e = e.checked_sub(4)?;
+        let f = f.checked_sub(4)?;
+        let g = g.checked_sub(4)?;
+        let h = h.checked_sub(4)?;
+        if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
+            Some(e | (f << 2) | (g << 4) | (h << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufhw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Note that everything here is offset by at least 8 and the upper
+        // bits are offset by 12 to test they're in the range of [12, 16).
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let a = a.checked_sub(8)?;
+        let b = b.checked_sub(8)?;
+        let c = c.checked_sub(8)?;
+        let d = d.checked_sub(8)?;
+        let e = e.checked_sub(12)?;
+        let f = f.checked_sub(12)?;
+        let g = g.checked_sub(12)?;
+        let h = h.checked_sub(12)?;
+        if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
+            Some(e | (f << 2) | (g << 4) | (h << 6))
+        } else {
+            None
+        }
+    }
 }
 
 impl IsleContext<'_, '_, MInst, X64Backend> {
diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs
index 435955fa2..aa3714f14 100644
--- a/cranelift/codegen/src/isle_prelude.rs
+++ b/cranelift/codegen/src/isle_prelude.rs
@@ -412,6 +412,14 @@ macro_rules! isle_common_prelude_methods {
             }
         }
 
+        #[inline]
+        fn ty_addr64(&mut self, ty: Type) -> Option<Type> {
+            match ty {
+                I64 | R64 => Some(ty),
+                _ => None,
+            }
+        }
+
         #[inline]
         fn u64_from_imm64(&mut self, imm: Imm64) -> u64 {
             imm.bits() as u64
diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs
index 70bcb7d12..3eb0db7ea 100644
--- a/cranelift/codegen/src/machinst/isle.rs
+++ b/cranelift/codegen/src/machinst/isle.rs
@@ -585,9 +585,86 @@ macro_rules! isle_lower_prelude_methods {
                 .collect();
             self.lower_ctx.gen_return(rets);
         }
+
+        /// Attempts to interpret the shuffle immediate `imm` as a shuffle of
+        /// 32-bit lanes, returning four integers, each of which is less than 8,
+        /// which represents a permutation of 32-bit lanes as specified by
+        /// `imm`.
+        ///
+        /// For example the shuffle immediate
+        ///
+        /// `0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27`
+        ///
+        /// would return `Some((0, 2, 4, 6))`.
+        fn shuffle32_from_imm(&mut self, imm: Immediate) -> Option<(u8, u8, u8, u8)> {
+            use crate::machinst::isle::shuffle_imm_as_le_lane_idx;
+
+            let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
+            Some((
+                shuffle_imm_as_le_lane_idx(4, &bytes[0..4])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[4..8])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[8..12])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[12..16])?,
+            ))
+        }
+
+        /// Same as `shuffle32_from_imm`, but for 16-bit lane shuffles.
+        fn shuffle16_from_imm(
+            &mut self,
+            imm: Immediate,
+        ) -> Option<(u8, u8, u8, u8, u8, u8, u8, u8)> {
+            use crate::machinst::isle::shuffle_imm_as_le_lane_idx;
+            let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
+            Some((
+                shuffle_imm_as_le_lane_idx(2, &bytes[0..2])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[2..4])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[4..6])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[6..8])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[8..10])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[10..12])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[12..14])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[14..16])?,
+            ))
+        }
     };
 }
 
+/// Returns the `size`-byte lane referred to by the shuffle immediate specified
+/// in `bytes`.
+///
+/// This helper is used by `shuffleNN_from_imm` above and is used to interpret a
+/// byte-based shuffle as a higher-level shuffle of bigger lanes. This will see
+/// if the `bytes` specified, which must have `size` length, specifies a lane in
+/// vectors aligned to a `size`-byte boundary.
+///
+/// Returns `None` if `bytes` doesn't specify a `size`-byte lane aligned
+/// appropriately, or returns `Some(n)` where `n` is the index of the lane being
+/// shuffled.
+pub fn shuffle_imm_as_le_lane_idx(size: u8, bytes: &[u8]) -> Option<u8> {
+    assert_eq!(bytes.len(), usize::from(size));
+
+    // The first index in `bytes` must be aligned to a `size` boundary for the
+    // bytes to be a valid specifier for a lane of `size` bytes.
+    if bytes[0] % size != 0 {
+        return None;
+    }
+
+    // Afterwards the bytes must all be one larger than the prior to specify a
+    // contiguous sequence of bytes that's being shuffled. Basically `bytes`
+    // must refer to the entire `size`-byte lane, in little-endian order.
+    for i in 0..size - 1 {
+        let idx = usize::from(i);
+        if bytes[idx] + 1 != bytes[idx + 1] {
+            return None;
+        }
+    }
+
+    // All of the `bytes` are in-order, meaning that this is a valid shuffle
+    // immediate to specify a lane of `size` bytes. The index, when viewed as
+    // `size`-byte immediates, will be the first byte divided by the byte size.
+    Some(bytes[0] / size)
+}
+
 /// Helpers specifically for machines that use ABICaller.
 #[macro_export]
 #[doc(hidden)]
diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle
index 89fc8e981..fb22e29c0 100644
--- a/cranelift/codegen/src/prelude.isle
+++ b/cranelift/codegen/src/prelude.isle
@@ -347,6 +347,10 @@
 (decl ty_vec128_int (Type) Type)
 (extern extractor ty_vec128_int ty_vec128_int)
 
+;; An extractor that only matches types that can be a 64-bit address.
+(decl ty_addr64 (Type) Type)
+(extern extractor ty_addr64 ty_addr64)
+
 ;; A pure constructor that matches everything except vectors with size 32X2.
 (decl pure partial not_vec32x2 (Type) Type)
 (extern constructor not_vec32x2 not_vec32x2)
diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle
index 51e15cb2a..a7e59d590 100644
--- a/cranelift/codegen/src/prelude_lower.isle
+++ b/cranelift/codegen/src/prelude_lower.isle
@@ -592,6 +592,16 @@
 (decl u64_from_constant (u64) Constant)
 (extern extractor u64_from_constant u64_from_constant)
 
+;; Extracts lane indices, represented as u8's, if the immediate for a
+;; `shuffle` instruction represents shuffling N-bit values. The u8 values
+;; returned will be in the range of 0 to (256/N)-1, inclusive, and index the
+;; N-bit chunks of two concatenated 128-bit vectors starting from the
+;; least-significant bits.
+(decl shuffle32_from_imm (u8 u8 u8 u8) Immediate)
+(extern extractor shuffle32_from_imm shuffle32_from_imm)
+(decl shuffle16_from_imm (u8 u8 u8 u8 u8 u8 u8 u8) Immediate)
+(extern extractor shuffle16_from_imm shuffle16_from_imm)
+
 ;;;; Helpers for generating returns ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Extractor to check for the special case that a `WritableValueRegs`
diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs
index 7a219543f..c55250113 100644
--- a/crates/environ/src/address_map.rs
+++ b/crates/environ/src/address_map.rs
@@ -116,6 +116,23 @@ impl AddressMapSection {
     }
 }
 
+/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets
+/// and the slice of associated file positions for each offset.
+fn parse_address_map(
+    section: &[u8],
+) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
+    let mut section = Bytes(section);
+    // NB: this matches the encoding written by `append_to` above.
+    let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
+    let count = usize::try_from(count.get(LittleEndian)).ok()?;
+    let (offsets, section) =
+        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
+    let (positions, section) =
+        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
+    debug_assert!(section.is_empty());
+    Some((offsets, positions))
+}
+
 /// Lookup an `offset` within an encoded address map section, returning the
 /// original `FilePos` that corresponds to the offset, if found.
 ///
@@ -127,15 +144,7 @@ impl AddressMapSection {
 /// section of the pc that is being looked up. If `offset` is out of range or
 /// doesn't correspond to anything in this file then `None` is returned.
 pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
-    let mut section = Bytes(section);
-    // NB: this matches the encoding written by `append_to` above.
-    let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
-    let count = usize::try_from(count.get(LittleEndian)).ok()?;
-    let (offsets, section) =
-        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
-    let (positions, section) =
-        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
-    debug_assert!(section.is_empty());
+    let (offsets, positions) = parse_address_map(section)?;
 
     // First perform a binary search on the `offsets` array. This is a sorted
     // array of offsets within the text section, which is conveniently what our
@@ -160,3 +169,24 @@ pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
     let pos = positions.get(index)?;
     Some(FilePos(pos.get(LittleEndian)))
 }
+
+/// Iterate over the address map contained in the given address map section.
+///
+/// This function takes a `section` as its first argument which must have been
+/// created with `AddressMapSection` above. This is intended to be the raw
+/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
+///
+/// The yielded offsets are relative to the start of the text section for this
+/// map's code object.
+pub fn iterate_address_map<'a>(
+    section: &'a [u8],
+) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
+    let (offsets, positions) = parse_address_map(section)?;
+
+    Some(
+        offsets
+            .iter()
+            .map(|o| o.get(LittleEndian))
+            .zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
+    )
+}
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index d52832195..1da66f547 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -1042,6 +1042,73 @@ impl Module {
         self.inner.memory_images()?;
         Ok(())
     }
+
+    /// Get the map from `.text` section offsets to Wasm binary offsets for this
+    /// module.
+    ///
+    /// Each entry is a (`.text` section offset, Wasm binary offset) pair.
+    ///
+    /// Entries are yielded in order of `.text` section offset.
+    ///
+    /// Some entries are missing a Wasm binary offset. This is for code that is
+    /// not associated with any single location in the Wasm binary, or for when
+    /// source information was optimized away.
+    ///
+    /// Not every module has an address map, since address map generation can be
+    /// turned off on `Config`.
+    ///
+    /// There is not an entry for every `.text` section offset. Every offset
+    /// after an entry's offset, but before the next entry's offset, is
+    /// considered to map to the same Wasm binary offset as the original
+    /// entry. For example, the address map will not contain the following
+    /// sequnce of entries:
+    ///
+    /// ```ignore
+    /// [
+    ///     // ...
+    ///     (10, Some(42)),
+    ///     (11, Some(42)),
+    ///     (12, Some(42)),
+    ///     (13, Some(43)),
+    ///     // ...
+    /// ]
+    /// ```
+    ///
+    /// Instead, it will drop the entries for offsets `11` and `12` since they
+    /// are the same as the entry for offset `10`:
+    ///
+    /// ```ignore
+    /// [
+    ///     // ...
+    ///     (10, Some(42)),
+    ///     (13, Some(43)),
+    ///     // ...
+    /// ]
+    /// ```
+    pub fn address_map<'a>(&'a self) -> Option<impl Iterator<Item = (usize, Option<u32>)> + 'a> {
+        Some(
+            wasmtime_environ::iterate_address_map(
+                self.code_object().code_memory().address_map_data(),
+            )?
+            .map(|(offset, file_pos)| (offset as usize, file_pos.file_offset())),
+        )
+    }
+
+    /// Get this module's code object's `.text` section, containing its compiled
+    /// executable code.
+    pub fn text(&self) -> &[u8] {
+        self.code_object().code_memory().text()
+    }
+
+    /// Get the locations of functions in this module's `.text` section.
+    ///
+    /// Each function's locartion is a (`.text` section offset, length) pair.
+    pub fn function_locations<'a>(&'a self) -> impl ExactSizeIterator<Item = (usize, usize)> + 'a {
+        self.compiled_module().finished_functions().map(|(f, _)| {
+            let loc = self.compiled_module().func_loc(f);
+            (loc.start as usize, loc.length as usize)
+        })
+    }
 }
 
 impl ModuleInner {
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 1fc04d68e..a741e3e22 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -1,7 +1,7 @@
 use crate::rust::{to_rust_ident, to_rust_upper_camel_case, RustGenerator, TypeMode};
 use crate::types::{TypeInfo, Types};
 use heck::*;
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, HashMap};
 use std::fmt::Write as _;
 use std::io::{Read, Write};
 use std::mem;
@@ -33,6 +33,7 @@ struct Wasmtime {
     exports: Exports,
     types: Types,
     sizes: SizeAlign,
+    interface_names: HashMap<InterfaceId, String>,
 }
 
 enum Import {
@@ -113,6 +114,7 @@ impl Wasmtime {
                 Import::Function { sig, add_to_linker }
             }
             WorldItem::Interface(id) => {
+                gen.gen.interface_names.insert(*id, snake.clone());
                 gen.current_interface = Some(*id);
                 gen.types(*id);
                 gen.generate_trappable_error_types(TypeOwner::Interface(*id));
@@ -159,6 +161,7 @@ impl Wasmtime {
             }
             WorldItem::Type(_) => unreachable!(),
             WorldItem::Interface(id) => {
+                gen.gen.interface_names.insert(*id, snake.clone());
                 gen.current_interface = Some(*id);
                 gen.types(*id);
                 gen.generate_trappable_error_types(TypeOwner::Interface(*id));
@@ -1382,8 +1385,18 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
         self.resolve
     }
 
-    fn current_interface(&self) -> Option<InterfaceId> {
-        self.current_interface
+    fn path_to_interface(&self, interface: InterfaceId) -> Option<String> {
+        match self.current_interface {
+            Some(id) if id == interface => None,
+            _ => {
+                let name = &self.gen.interface_names[&interface];
+                Some(if self.current_interface.is_some() {
+                    format!("super::{name}")
+                } else {
+                    name.clone()
+                })
+            }
+        }
     }
 
     fn push_str(&mut self, s: &str) {
diff --git a/crates/wit-bindgen/src/rust.rs b/crates/wit-bindgen/src/rust.rs
index 6a8c4a9b1..bba7df58b 100644
--- a/crates/wit-bindgen/src/rust.rs
+++ b/crates/wit-bindgen/src/rust.rs
@@ -15,7 +15,7 @@ pub trait RustGenerator<'a> {
 
     fn push_str(&mut self, s: &str);
     fn info(&self, ty: TypeId) -> TypeInfo;
-    fn current_interface(&self) -> Option<InterfaceId>;
+    fn path_to_interface(&self, interface: InterfaceId) -> Option<String>;
 
     fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
         match ty {
@@ -64,19 +64,9 @@ pub trait RustGenerator<'a> {
                 self.result_name(id)
             };
             if let TypeOwner::Interface(id) = ty.owner {
-                if let Some(name) = &self.resolve().interfaces[id].name {
-                    match self.current_interface() {
-                        Some(cur) if cur == id => {}
-                        Some(_other) => {
-                            self.push_str("super::");
-                            self.push_str(&name.to_snake_case());
-                            self.push_str("::");
-                        }
-                        None => {
-                            self.push_str(&name.to_snake_case());
-                            self.push_str("::");
-                        }
-                    }
+                if let Some(path) = self.path_to_interface(id) {
+                    self.push_str(&path);
+                    self.push_str("::");
                 }
             }
             self.push_str(&name);


Test patch:
diff --git a/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif b/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif
new file mode 100644
index 000000000..30cf9721e
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif
@@ -0,0 +1,116 @@
+test compile precise-output
+set enable_simd
+target x86_64 has_avx
+
+function %punpckldq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckldq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckldq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckhdq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckhdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckhdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpcklqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpcklqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpcklqdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckhqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckhqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckhqdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/shuffle.clif b/cranelift/filetests/filetests/isa/x64/shuffle.clif
index 529b95cc5..b056d9f16 100644
--- a/cranelift/filetests/filetests/isa/x64/shuffle.clif
+++ b/cranelift/filetests/filetests/isa/x64/shuffle.clif
@@ -52,3 +52,594 @@ block0(v0: i8x16, v1: i8x16):
 ;   popq %rbp
 ;   retq
 
+function %punpcklwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpcklwd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpcklwd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckhwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhwd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhwd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_0022(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 0 1 2 3 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $160, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0xa0, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_3120(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 4 5 6 7 8 9 10 11 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $39, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0x27, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_7546(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [28 29 30 31 20 21 22 23 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $135, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0x87, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %not_single_pshufd(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   shufps  $78, %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   shufps $0x4e, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckldq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckldq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckldq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckhdq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpcklqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpcklqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpcklqdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %punpckhqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhqdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shufps_3277(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 8 9 10 11 28 29 30 31 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   shufps  $251, %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   shufps $0xfb, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shufps_6500(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [24 25 26 27 20 21 22 23 0 1 2 3 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movdqa  %xmm0, %xmm4
+;   movdqa  %xmm1, %xmm0
+;   shufps  $6, %xmm0, %xmm4, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movdqa %xmm0, %xmm4
+;   movdqa %xmm1, %xmm0
+;   shufps $6, %xmm4, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $27, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x1b, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 6 7 4 5 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $187, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0xbb, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 20 21 18 19 16 17 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $27, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x1b, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 18 19 22 23 18 19 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $119, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x77, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $27, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x1b, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 10 11 14 15 10 11]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $119, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x77, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 28 29 26 27 24 25]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $27, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x1b, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 26 27 30 31 26 27]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $119, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x77, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shuffle_all_zeros(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
+    return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pxor    %xmm3, %xmm3, %xmm3
+;   pshufb  %xmm0, %xmm3, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pxor %xmm3, %xmm3
+;   pshufb %xmm3, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/runtests/simd-shuffle.clif b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
index 621eebda6..60b515628 100644
--- a/cranelift/filetests/filetests/runtests/simd-shuffle.clif
+++ b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
@@ -1,9 +1,10 @@
-test interpret
+;; test interpret ;; FIXME(#5915)
 test run
 target aarch64
 target s390x
 set enable_simd
 target x86_64 has_sse3 has_ssse3 has_sse41
+target x86_64 has_sse3 has_ssse3 has_sse41 has_avx
 target x86_64 has_sse3 has_ssse3 has_sse41 has_avx512vl has_avx512vbmi
 
 function %shuffle_i8x16(i8x16, i8x16) -> i8x16 {
@@ -26,3 +27,234 @@ block0(v0: i8x16):
     return v1
 }
 ; run: %shuffle1([0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]) == [8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7]
+
+function %punpcklbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23]
+    return v2
+}
+; run: %punpcklbw([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 17 2 18 3 19 4 20 5 21 6 22 7 23 8 24]
+
+function %punpckhbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [8 24 9 25 10 26 11 27 12 28 13 29 14 30 15 31]
+    return v2
+}
+; run: %punpckhbw([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [9 25 10 26 11 27 12 28 13 29 14 30 15 31 16 32]
+
+function %punpcklwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %punpcklwd([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 9 2 10 3 11 4 12]
+
+function %punpckhwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %punpckhwd([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [5 13 6 14 7 15 8 16]
+
+function %pshufd_0022(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 0 1 2 3 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [1 1 3 3]
+
+function %pshufd_3120(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 4 5 6 7 8 9 10 11 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [4 2 3 1]
+
+function %pshufd_7546(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [28 29 30 31 20 21 22 23 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [8 6 5 7]
+
+function %not_pshufd(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [3 4 5 6]
+
+function %punpckldq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %punpckldq([1 2 3 4], [5 6 7 8]) == [1 5 2 6]
+
+function %punpckhdq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %punpckldq([1 2 3 4], [5 6 7 8]) == [3 7 4 8]
+
+function %punpcklqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %punpcklqdq([1 2], [5 6]) == [1 5]
+
+function %punpckhqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %punpckhqdq([1 2], [5 6]) == [2 6]
+
+function %shufps_0145(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [1 2 5 6]
+
+function %shufps_3277(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 8 9 10 11 28 29 30 31 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [4 3 8 8]
+
+function %shufps_6500(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [24 25 26 27 20 21 22 23 0 1 2 3 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [7 6 1 1]
+
+function %pshuflw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [4 3 2 1 5 6 7 8]
+
+function %pshuflw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 6 7 4 5 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [4 3 4 3 5 6 7 8]
+
+function %pshuflw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 20 21 18 19 16 17 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_rhs_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [12 11 10 9 13 14 15 16]
+
+function %pshuflw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 18 19 22 23 18 19 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_rhs_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [12 10 12 10 13 14 15 16]
+
+function %pshufhw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 2 3 4 8 7 6 5]
+
+function %pshufhw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 10 11 14 15 10 11]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 2 3 4 8 6 8 6]
+
+function %pshufhw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 28 29 26 27 24 25]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_rhs_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [9 10 11 12 16 15 14 13]
+
+function %pshufhw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 26 27 30 31 26 27]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_rhs_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [9 10 11 12 16 14 16 14]
+
+function %shuffle_all_zeros(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
+    return v2
+}
+; run: %shuffle_all_zeros([5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1], [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]) == [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
diff --git a/crates/component-macro/tests/codegen/rename.wit b/crates/component-macro/tests/codegen/rename.wit
new file mode 100644
index 000000000..d60d4f6f9
--- /dev/null
+++ b/crates/component-macro/tests/codegen/rename.wit
@@ -0,0 +1,14 @@
+interface red {
+  use self.green.{thing}
+
+  foo: func() -> thing
+}
+
+interface green {
+  type thing = s32
+}
+
+default world neptune {
+  import blue: self.red
+  import orange: self.green
+}