Repository: bytecodealliance/wasmtime
Pull request #5854 (2023-02-22 20:14:43)

riscv64: Improve `ctz`/`clz`/`cls` codegen
👋 Hey,

This PR started by fixing the lowering rules for `ctz`. The base rule had `(if-let $false (has_zbb))`, but the rule for `i8`/`i16` depended on both `Zbb` and `Zbs`. Which meant that if we only had `Zbb` we had no valid rule for lowering `i8`/`i16`. The fuzzer found this on https://github.com/bytecodealliance/wasmtime/pull/5844#issuecomment-1439854777.

I also ended up improving codegen for these instructions in a number of cases. We try to avoid the base case as much as possible since it involves a loop.

We can now avoid requiring `Zbs` in all cases by falling back to an `ori` or `or` instruction instead to set a single bit.

It also moves some constructors around and other misc stuff since we have a bunch of rules all over the files. I'm trying to collect the ones I find at the top of the file.

It's been fuzzing for about an hour, I'll update this if it finds anything.

jameysharp: I've tried to review this a couple of times and have not succeeded yet. I think it would help me if you can split this into smaller PRs. It looks like you already structured individual commits carefully, but I think the main thing I want in this case is to keep separate the movement of definitions from the actual changes in lowering.

I'm open to alternative suggestions, but would you mind opening a new PR that moves existing definitions without changing them first? I'm hoping that makes the overall diff for _this_ PR easier to review as it should only have the actual logic changes.

If that's too much trouble, I'm happy to discuss other ways to split this, or I can try to review it as-is, or we can see if @elliottt or @cfallin is up for reviewing it. My trouble is that since I'm not familiar with risc-v I have to figure that out at the same time as following the motion of existing code and finding the real changes, and together that's apparently a bit too much for my brain.
afonso360: No worries, and thanks for looking into it (and all the other PR's)!  ❤️

I'll split this into general RISC-V rules cleanups and move the codegen changes into separate PR's.

---

On that note, I've been thinking about doing a pass on the entire RISCV ruleset to add helpers for generating instructions. 

We have a lot of:
```
(alu_rr_imm12 (AluOPRRI.Andi) val (imm12_const 255))
```

Which I would really like for it to become something along these lines:
```
(rv_andi val (imm12 255))
```

I think this would improve readability somwhat, the rules on RISCV are really verbose and I think this is part of it. What do you guys think about that?


jameysharp: I've heard other people say they consider that kind of helper really important in the x86 rules, so I imagine it's a good idea for all backends. I haven't authored enough backend rules to have an opinion myself though. My first reaction had originally been that it seemed like a lot of work with an unclear benefit, but I'm coming around to the conclusion that it's worth doing after all.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 015c7f822..36182b454 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -651,6 +651,7 @@ dependencies = [
  "arbitrary",
  "cranelift",
  "cranelift-native",
+ "once_cell",
  "target-lexicon",
 ]
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 5eae84301..d4eed1f98 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -808,6 +808,28 @@
 (decl imm12_from_u64 (Imm12) u64)
 (extern extractor imm12_from_u64 imm12_from_u64)
 
+
+;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+;; bseti: Set a single bit in a register, indexed by a constant.
+(decl bseti (Reg u64) Reg)
+(rule (bseti val bit)
+  (if-let $false (has_zbs))
+  (if-let $false (u64_le bit 12))
+  (let ((const Reg (load_u64_constant (u64_shl 1 bit))))
+    (alu_rrr (AluOPRRR.Or) val const)))
+
+(rule (bseti val bit)
+  (if-let $false (has_zbs))
+  (if-let $true (u64_le bit 12))
+  (alu_rr_imm12 (AluOPRRI.Ori) val (imm12_const (u64_as_i32 (u64_shl 1 bit)))))
+
+(rule (bseti val bit)
+  (if-let $true (has_zbs))
+  (alu_rr_imm12 (AluOPRRI.Bseti) val (imm12_const (u64_as_i32 bit))))
+
+
 ;; Float Helpers
 
 (decl gen_default_frm () OptionFloatRoundingMode)
@@ -948,89 +970,103 @@
 
 
 (decl lower_ctz (Type Reg) Reg)
-(rule
-  (lower_ctz ty x)
-  (if-let $false (has_zbb))
+(rule (lower_ctz ty x)
   (gen_cltz $false x ty))
 
-(rule 2
-  (lower_ctz $I64 x)
+(rule 1 (lower_ctz (fits_in_16 ty) x)
   (if-let $true (has_zbb))
-  (alu_rr_funct12 (AluOPRRI.Ctz) x))
+  (let ((tmp Reg (bseti x (ty_bits ty))))
+    (alu_rr_funct12 (AluOPRRI.Ctzw) tmp)))
 
-(rule 2
-  (lower_ctz $I32 x)
+(rule 2 (lower_ctz $I32 x)
   (if-let $true (has_zbb))
   (alu_rr_funct12 (AluOPRRI.Ctzw) x))
 
-;;;; for I8 and I16
-(rule 1
-  (lower_ctz ty x)
+(rule 2 (lower_ctz $I64 x)
   (if-let $true (has_zbb))
-  (if-let $true (has_zbs))
-  (let
-    ((tmp Reg (alu_rr_imm12 (AluOPRRI.Bseti) x (imm12_const (ty_bits ty)))))
-    (alu_rr_funct12 (AluOPRRI.Ctzw) x)))
+  (alu_rr_funct12 (AluOPRRI.Ctz) x))
 
-;;;;
+;; Count trailing zeros from a i128 bit value.
+;; We count both halves separately and conditionally add them if it makes sense.
 (decl lower_ctz_128 (ValueRegs) ValueRegs)
-(rule
-  (lower_ctz_128 x)
-  (let
-    (;; count the low part.
-      (low Reg (lower_ctz $I64 (value_regs_get x 0)))
-      ;; count the high part.
-      (high_part Reg (lower_ctz $I64 (value_regs_get x 1)))
-      ;;;
-      (constant_64 Reg (load_u64_constant 64))
-      ;;;
-      (high Reg (gen_select_reg (IntCC.Equal) constant_64 low high_part (zero_reg)))
+(rule (lower_ctz_128 x)
+  (let ((x_lo Reg (value_regs_get x 0))
+        (x_hi Reg (value_regs_get x 1))
+        ;; Count both halves
+        (high Reg (lower_ctz $I64 x_hi))
+        (low Reg (lower_ctz $I64 x_lo))
+        ;; Only add the top half if the bottom is zero
+        (high Reg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg)))
+        (result Reg (alu_add low high)))
+    (zext result $I64 $I128)))
+
 
-      ;; add low and high together.
-      (result Reg (alu_add low high)))
-    (value_regs result (load_u64_constant 0))))
 
 (decl lower_clz (Type Reg) Reg)
-(rule
-  (lower_clz ty rs)
-  (if-let $false (has_zbb))
+(rule (lower_clz ty rs)
   (gen_cltz $true rs ty))
-(rule 2
-  (lower_clz $I64 r)
+
+(rule 1 (lower_clz (fits_in_16 ty) r)
   (if-let $true (has_zbb))
-  (alu_rr_funct12 (AluOPRRI.Clz) r))
-(rule 2
-  (lower_clz $I32 r)
+  (let ((tmp Reg (zext r ty $I64))
+        (count Reg (alu_rr_funct12 (AluOPRRI.Clz) tmp))
+        ;; We always do the operation on the full 64-bit register, so subtract 64 from the result.
+        (result Reg (alu_rr_imm12 (AluOPRRI.Addi) count (imm12_const_add (ty_bits ty) -64))))
+    result))
+
+(rule 2 (lower_clz $I32 r)
   (if-let $true (has_zbb))
   (alu_rr_funct12 (AluOPRRI.Clzw) r))
 
-;;; for I8 and I16
-(rule 1
-  (lower_clz ty r)
+(rule 2 (lower_clz $I64 r)
   (if-let $true (has_zbb))
-  (let
-    ( ;; narrow int make all upper bits are zeros.
-      (tmp Reg (ext_int_if_need $false r ty ))
-      ;;
-      (count Reg (alu_rr_funct12 (AluOPRRI.Clz) tmp))
-      ;;make result
-      (result Reg (alu_rr_imm12 (AluOPRRI.Addi) count (imm12_const_add (ty_bits ty) -64))))
-    result))
+  (alu_rr_funct12 (AluOPRRI.Clz) r))
 
+;; Count leading zeros from a i128 bit value.
+;; We count both halves separately and conditionally add them if it makes sense.
 (decl lower_clz_i128 (ValueRegs) ValueRegs)
-(rule
-  (lower_clz_i128 x)
-  (let
-    ( ;; count high part.
-      (high Reg (lower_clz $I64 (value_regs_get x 1)))
-      ;; coumt low part.
-      (low_part Reg (lower_clz $I64 (value_regs_get x 0)))
-      ;;; load constant 64.
-      (constant_64 Reg (load_u64_constant 64))
-      (low Reg (gen_select_reg (IntCC.Equal) constant_64 high low_part (zero_reg)))
-      ;; add low and high together.
-      (result Reg (alu_add high low)))
-    (value_regs result (load_u64_constant 0))))
+(rule (lower_clz_i128 x)
+  (let ((x_lo Reg (value_regs_get x 0))
+        (x_hi Reg (value_regs_get x 1))
+        ;; Count both halves
+        (high Reg (lower_clz $I64 x_hi))
+        (low Reg (lower_clz $I64 x_lo))
+        ;; Only add the bottom zeros if the top half is zero
+        (low Reg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg)))
+        (result Reg (alu_add high low)))
+    (zext result $I64 $I128)))
+
+
+(decl lower_cls (Type Reg) Reg)
+(rule (lower_cls ty r)
+  (let ((tmp Reg (ext_int_if_need $true r ty))
+        (tmp2 Reg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (gen_bit_not tmp) tmp))
+        (tmp3 Reg (lower_clz ty tmp2)))
+    (alu_rr_imm12 (AluOPRRI.Addi) tmp3 (imm12_const -1))))
+
+;; If the sign bit is set, we count the leading zeros of the inverted value.
+;; Otherwise we can just count the leading zeros of the original value.
+;; Subtract 1 since the sign bit does not count.
+(decl lower_cls_i128 (ValueRegs) ValueRegs)
+(rule (lower_cls_i128 x)
+  (let ((low Reg (value_regs_get x 0))
+        (high Reg (value_regs_get x 1))
+        (low Reg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (gen_bit_not low) low))
+        (high Reg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (gen_bit_not high) high))
+        (tmp ValueRegs (lower_clz_i128 (value_regs low high)))
+        (count Reg (value_regs_get tmp 0))
+        (result Reg (alu_rr_imm12 (AluOPRRI.Addi) count (imm12_const -1))))
+    (zext result $I64 $I128)))
+
+
+(decl gen_cltz (bool Reg Type) Reg)
+(rule (gen_cltz leading rs ty)
+  (let ((tmp WritableReg (temp_writable_reg $I64))
+        (step WritableReg (temp_writable_reg $I64))
+        (sum WritableReg (temp_writable_reg $I64))
+        (_ Unit (emit (MInst.Cltz leading sum step tmp rs ty))))
+    sum))
+
   
 ;; Extends an integer if it is smaller than 64 bits.
 (decl ext_int_if_need (bool ValueRegs Type) ValueRegs)
@@ -1267,27 +1303,6 @@
       (part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) part2)))
     (alu_rrr (AluOPRRR.Or) part1 part3)))
 
-(decl lower_cls (Reg Type) Reg)
-(rule
-  (lower_cls r ty)
-  (let
-    ( ;; extract sign bit.
-      (tmp Reg (ext_int_if_need $true r ty))
-      ;;
-      (tmp2 Reg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (gen_bit_not r) r))
-      ;;
-      (tmp3 Reg (lower_clz ty tmp2)))
-    (alu_rr_imm12 (AluOPRRI.Addi) tmp3 (imm12_const -1))))
-
-(decl gen_cltz (bool Reg Type) Reg)
-(rule
-  (gen_cltz leading rs ty)
-  (let
-    ((tmp WritableReg (temp_writable_reg $I64))
-      (step WritableReg (temp_writable_reg $I64))
-      (sum WritableReg (temp_writable_reg $I64))
-      (_ Unit (emit (MInst.Cltz leading sum step tmp rs ty))))
-    (writable_reg_to_reg sum)))
 
 (decl gen_popcnt (Reg Type) Reg)
 (rule
@@ -1454,24 +1469,6 @@
       (gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high_replacement high))))
 
 
-(decl lower_cls_i128 (ValueRegs) ValueRegs)
-(rule
-  (lower_cls_i128 x)
-  (let
-    ( ;;; we use clz to implement cls
-      ;;; if value is negtive we need inverse all bits.
-      (low Reg
-        (gen_select_reg (IntCC.SignedLessThan) (value_regs_get x 1) (zero_reg) (gen_bit_not (value_regs_get x 0)) (value_regs_get x 0)))
-      ;;;
-       (high Reg
-        (gen_select_reg (IntCC.SignedLessThan) (value_regs_get x 1) (zero_reg) (gen_bit_not (value_regs_get x 1)) (value_regs_get x 1)))
-      ;; count leading zeros.
-      (tmp  ValueRegs (lower_clz_i128 (value_regs low high)))
-      (count Reg (value_regs_get tmp 0))
-      (result Reg (alu_rr_imm12 (AluOPRRI.Addi) count (imm12_const -1))))
-    (value_regs result (load_u64_constant 0))))
-
-
 (decl gen_amode (Reg Offset32 Type) AMode)
 (extern constructor gen_amode gen_amode)
 
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index ec74f4555..458b59847 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -327,14 +327,14 @@
 
 
 ;;;; Rules for `ctz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule (lower (has_type ty (ctz x)))
+(rule (lower (has_type (fits_in_64 ty) (ctz x)))
   (lower_ctz ty x))
 
 (rule 1 (lower (has_type $I128 (ctz x)))
   (lower_ctz_128 x))
 
 ;;;; Rules for `clz` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule (lower (has_type ty (clz x)))
+(rule (lower (has_type (fits_in_64 ty) (clz x)))
   (lower_clz ty x))
 
 (rule 1 (lower (has_type $I128 (clz x)))
@@ -342,7 +342,7 @@
   
 ;;;; Rules for `cls` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule (lower (has_type (fits_in_64 ty) (cls x)))
-  (lower_cls x ty))
+  (lower_cls ty x))
 
 (rule 1 (lower (has_type $I128 (cls x)))
   (lower_cls_i128 x))
diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs
index 749e8bb4e..3de34e436 100644
--- a/cranelift/codegen/src/isle_prelude.rs
+++ b/cranelift/codegen/src/isle_prelude.rs
@@ -38,6 +38,11 @@ macro_rules! isle_common_prelude_methods {
             x as u64
         }
 
+        #[inline]
+        fn u64_as_i32(&mut self, x: u64) -> i32 {
+            x as i32
+        }
+
         #[inline]
         fn i64_neg(&mut self, x: i64) -> i64 {
             x.wrapping_neg()
diff --git a/cranelift/codegen/src/opts/algebraic.isle b/cranelift/codegen/src/opts/algebraic.isle
index 6bc049249..8cab77bd4 100644
--- a/cranelift/codegen/src/opts/algebraic.isle
+++ b/cranelift/codegen/src/opts/algebraic.isle
@@ -323,14 +323,12 @@
       (remat x))
 
 ;; Optimize icmp-of-icmp.
-(rule (simplify (icmp ty
-                      (IntCC.NotEqual)
+(rule (simplify (ne ty
                       (uextend _ inner @ (icmp ty _ _ _))
                       (iconst _ (u64_from_imm64 0))))
       (subsume inner))
 
-(rule (simplify (icmp ty
-                      (IntCC.Equal)
+(rule (simplify (eq ty
                       (uextend _ (icmp ty cc x y))
                       (iconst _ (u64_from_imm64 0))))
       (subsume (icmp ty (intcc_inverse cc) x y)))
@@ -346,36 +344,16 @@
 
 ;; `x == x` is always true for integers; `x != x` is false. Strict
 ;; inequalities are false, and loose inequalities are true.
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.Equal) x x))
-      (iconst ty (imm64 1)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.NotEqual) x x))
-      (iconst ty (imm64 0)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.UnsignedGreaterThan) x x))
-      (iconst ty (imm64 0)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.UnsignedGreaterThanOrEqual) x x))
-      (iconst ty (imm64 1)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.SignedGreaterThan) x x))
-      (iconst ty (imm64 0)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.SignedGreaterThanOrEqual) x x))
-      (iconst ty (imm64 1)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.UnsignedLessThan) x x))
-      (iconst ty (imm64 0)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.UnsignedLessThanOrEqual) x x))
-      (iconst ty (imm64 1)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.SignedLessThan) x x))
-      (iconst ty (imm64 0)))
-(rule (simplify
-       (icmp (fits_in_64 (ty_int ty)) (IntCC.SignedLessThanOrEqual) x x))
-      (iconst ty (imm64 1)))
+(rule (simplify (eq  (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 1)))
+(rule (simplify (ne  (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 0)))
+(rule (simplify (ugt (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 0)))
+(rule (simplify (uge (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 1)))
+(rule (simplify (sgt (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 0)))
+(rule (simplify (sge (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 1)))
+(rule (simplify (ult (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 0)))
+(rule (simplify (ule (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 1)))
+(rule (simplify (slt (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 0)))
+(rule (simplify (sle (fits_in_64 (ty_int ty)) x x)) (iconst ty (imm64 1)))
 
 ;; (x ^ -1) can be replaced with the `bnot` instruction
 (rule (simplify (bxor ty x (iconst ty k)))
@@ -399,215 +377,148 @@
 
 ;; ult(x, 0) == false.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThan) x zero @ (iconst _ (u64_from_imm64 0))))
+       (ult (fits_in_64 (ty_int bty)) x zero @ (iconst _ (u64_from_imm64 0))))
       (subsume (iconst bty (imm64 0))))
 
 ;; ule(x, 0) == eq(x, 0)
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThanOrEqual) x zero @ (iconst _ (u64_from_imm64 0))))
-      (icmp bty (IntCC.Equal) x zero))
+       (ule (fits_in_64 (ty_int bty)) x zero @ (iconst _ (u64_from_imm64 0))))
+      (eq bty x zero))
 
 ;; ugt(x, 0) == ne(x, 0).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThan) x zero @ (iconst _ (u64_from_imm64 0))))
-      (icmp bty (IntCC.NotEqual) x zero))
+       (ugt (fits_in_64 (ty_int bty)) x zero @ (iconst _ (u64_from_imm64 0))))
+      (ne bty x zero))
 
 ;; uge(x, 0) == true.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThanOrEqual) x zero @ (iconst _ (u64_from_imm64 0))))
+       (uge (fits_in_64 (ty_int bty)) x zero @ (iconst _ (u64_from_imm64 0))))
       (subsume (iconst bty (imm64 1))))
 
 ;; ult(x, UMAX) == ne(x, UMAX).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThan) x umax @ (iconst cty (u64_from_imm64 y))))
+       (ult (fits_in_64 (ty_int bty)) x umax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_umax cty)))
-       (icmp bty (IntCC.NotEqual) x umax))
+       (ne bty x umax))
 
 ;; ule(x, UMAX) == true.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedLessThanOrEqual) x umax @ (iconst cty (u64_from_imm64 y))))
+       (ule (fits_in_64 (ty_int bty)) x umax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_umax cty)))
        (subsume (iconst bty (imm64 1))))
 
 ;; ugt(x, UMAX) == false.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThan) x umax @ (iconst cty (u64_from_imm64 y))))
+       (ugt (fits_in_64 (ty_int bty)) x umax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_umax cty)))
        (subsume (iconst bty (imm64 0))))
 
 ;; uge(x, UMAX) == eq(x, UMAX).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.UnsignedGreaterThanOrEqual) x umax @ (iconst cty (u64_from_imm64 y))))
+       (uge (fits_in_64 (ty_int bty)) x umax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_umax cty)))
-       (icmp bty (IntCC.Equal) x umax))
+       (eq bty x umax))
 
 ;; slt(x, SMIN) == false.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThan) x smin @ (iconst cty (u64_from_imm64 y))))
+       (slt (fits_in_64 (ty_int bty)) x smin @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smin cty)))
        (subsume (iconst bty (imm64 0))))
 
 ;; sle(x, SMIN) == eq(x, SMIN).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThanOrEqual) x smin @ (iconst cty (u64_from_imm64 y))))
+       (sle (fits_in_64 (ty_int bty)) x smin @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smin cty)))
-       (icmp bty (IntCC.Equal) x smin))
+       (eq bty x smin))
 
 ;; sgt(x, SMIN) == ne(x, SMIN).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThan) x smin @ (iconst cty (u64_from_imm64 y))))
+       (sgt (fits_in_64 (ty_int bty)) x smin @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smin cty)))
-       (icmp bty (IntCC.NotEqual) x smin))
+       (ne bty x smin))
 
 ;; sge(x, SMIN) == true.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThanOrEqual) x smin @ (iconst cty (u64_from_imm64 y))))
+       (sge (fits_in_64 (ty_int bty)) x smin @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smin cty)))
        (subsume (iconst bty (imm64 1))))
 
 ;; slt(x, SMAX) == ne(x, SMAX).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThan) x smax @ (iconst cty (u64_from_imm64 y))))
+       (slt (fits_in_64 (ty_int bty)) x smax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smax cty)))
-       (icmp bty (IntCC.NotEqual) x smax))
+       (ne bty x smax))
 
 ;; sle(x, SMAX) == true.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedLessThanOrEqual) x smax @ (iconst cty (u64_from_imm64 y))))
+       (sle (fits_in_64 (ty_int bty)) x smax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smax cty)))
        (subsume (iconst bty (imm64 1))))
 
 ;; sgt(x, SMAX) == false.
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThan) x smax @ (iconst cty (u64_from_imm64 y))))
+       (sgt (fits_in_64 (ty_int bty)) x smax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smax cty)))
        (subsume (iconst bty (imm64 0))))
 
 ;; sge(x, SMAX) == eq(x, SMAX).
 (rule (simplify
-       (icmp (fits_in_64 (ty_int bty)) (IntCC.SignedGreaterThanOrEqual) x smax @ (iconst cty (u64_from_imm64 y))))
+       (sge (fits_in_64 (ty_int bty)) x smax @ (iconst cty (u64_from_imm64 y))))
        (if-let $true (u64_eq y (ty_smax cty)))
-       (icmp bty (IntCC.Equal) x smax))
+       (eq bty x smax))
 
 ;; 32-bit integers zero-extended to 64-bit integers are never negative
 (rule (simplify
-       (icmp (ty_int ty)
-             (IntCC.SignedLessThan)
+       (slt ty
              (uextend $I64 x @ (value_type $I32))
              (iconst _ (u64_from_imm64 0))))
       (iconst ty (imm64 0)))
 (rule (simplify
-       (icmp (ty_int ty)
-             (IntCC.SignedGreaterThanOrEqual)
+       (sge ty
              (uextend $I64 x @ (value_type $I32))
              (iconst _ (u64_from_imm64 0))))
       (iconst ty (imm64 1)))
 
 
 ;; Transform select-of-icmp into {u,s}{min,max} instructions where possible.
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedGreaterThan) x y) x y))
-      (smax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y) x y))
-      (smax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedGreaterThan) x y) x y))
-      (umax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y) x y))
-      (umax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedLessThan) x y) x y))
-      (smin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedLessThanOrEqual) x y) x y))
-      (smin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedLessThan) x y) x y))
-      (umin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y) x y))
-      (umin ty x y))
-
+(rule (simplify (select ty (sgt _ x y) x y)) (smax ty x y))
+(rule (simplify (select ty (sge _ x y) x y)) (smax ty x y))
+(rule (simplify (select ty (ugt _ x y) x y)) (umax ty x y))
+(rule (simplify (select ty (uge _ x y) x y)) (umax ty x y))
+(rule (simplify (select ty (slt _ x y) x y)) (smin ty x y))
+(rule (simplify (select ty (sle _ x y) x y)) (smin ty x y))
+(rule (simplify (select ty (ult _ x y) x y)) (umin ty x y))
+(rule (simplify (select ty (ule _ x y) x y)) (umin ty x y))
 
 ;; These are the same rules as above, but when the operands for select are swapped
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedLessThan) x y) y x))
-      (smax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedLessThanOrEqual) x y) y x))
-      (smax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedLessThan) x y) y x))
-      (umax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y) y x))
-      (umax ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedGreaterThan) x y) y x))
-      (smin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y) y x))
-      (smin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedGreaterThan) x y) y x))
-      (umin ty x y))
-(rule (simplify
-       (select ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y) y x))
-      (umin ty x y))
+(rule (simplify (select ty (slt _ x y) y x)) (smax ty x y))
+(rule (simplify (select ty (sle _ x y) y x)) (smax ty x y))
+(rule (simplify (select ty (ult _ x y) y x)) (umax ty x y))
+(rule (simplify (select ty (ule _ x y) y x)) (umax ty x y))
+(rule (simplify (select ty (sgt _ x y) y x)) (smin ty x y))
+(rule (simplify (select ty (sge _ x y) y x)) (smin ty x y))
+(rule (simplify (select ty (ugt _ x y) y x)) (umin ty x y))
+(rule (simplify (select ty (uge _ x y) y x)) (umin ty x y))
 
 ;; Transform bitselect-of-icmp into {u,s}{min,max} instructions where possible.
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedGreaterThan) x y) x y))
-      (smax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y) x y))
-      (smax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedGreaterThan) x y) x y))
-      (umax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y) x y))
-      (umax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedLessThan) x y) x y))
-      (smin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedLessThanOrEqual) x y) x y))
-      (smin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedLessThan) x y) x y))
-      (umin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y) x y))
-      (umin ty x y))
+(rule (simplify (bitselect ty (sgt _ x y) x y)) (smax ty x y))
+(rule (simplify (bitselect ty (sge _ x y) x y)) (smax ty x y))
+(rule (simplify (bitselect ty (ugt _ x y) x y)) (umax ty x y))
+(rule (simplify (bitselect ty (uge _ x y) x y)) (umax ty x y))
+(rule (simplify (bitselect ty (slt _ x y) x y)) (smin ty x y))
+(rule (simplify (bitselect ty (sle _ x y) x y)) (smin ty x y))
+(rule (simplify (bitselect ty (ult _ x y) x y)) (umin ty x y))
+(rule (simplify (bitselect ty (ule _ x y) x y)) (umin ty x y))
 
 ;; These are the same rules as above, but when the operands for select are swapped
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedLessThan) x y) y x))
-      (smax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedLessThanOrEqual) x y) y x))
-      (smax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedLessThan) x y) y x))
-      (umax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedLessThanOrEqual) x y) y x))
-      (umax ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedGreaterThan) x y) y x))
-      (smin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.SignedGreaterThanOrEqual) x y) y x))
-      (smin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedGreaterThan) x y) y x))
-      (umin ty x y))
-(rule (simplify
-       (bitselect ty (icmp _ (IntCC.UnsignedGreaterThanOrEqual) x y) y x))
-      (umin ty x y))
+(rule (simplify (bitselect ty (slt _ x y) y x)) (smax ty x y))
+(rule (simplify (bitselect ty (sle _ x y) y x)) (smax ty x y))
+(rule (simplify (bitselect ty (ult _ x y) y x)) (umax ty x y))
+(rule (simplify (bitselect ty (ule _ x y) y x)) (umax ty x y))
+(rule (simplify (bitselect ty (sgt _ x y) y x)) (smin ty x y))
+(rule (simplify (bitselect ty (sge _ x y) y x)) (smin ty x y))
+(rule (simplify (bitselect ty (ugt _ x y) y x)) (umin ty x y))
+(rule (simplify (bitselect ty (uge _ x y) y x)) (umin ty x y))
 
 ;; For floats convert fcmp lt into pseudo_min and gt into pseudo_max
 ;;
diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle
index 8228f72dd..012d5e29a 100644
--- a/cranelift/codegen/src/prelude.isle
+++ b/cranelift/codegen/src/prelude.isle
@@ -102,6 +102,9 @@
 (decl u64_as_u32 (u32) u64)
 (extern extractor u64_as_u32 u64_as_u32)
 
+(decl pure u64_as_i32 (u64) i32)
+(extern constructor u64_as_i32 u64_as_i32)
+
 ;;;; Primitive Arithmetic ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (decl pure u8_and (u8 u8) u8)
@@ -282,6 +285,36 @@
 
 ;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(decl eq (Type Value Value) Value)
+(extractor (eq ty x y) (icmp ty (IntCC.Equal) x y))
+
+(decl ne (Type Value Value) Value)
+(extractor (ne ty x y) (icmp ty (IntCC.NotEqual) x y))
+
+(decl ult (Type Value Value) Value)
+(extractor (ult ty x y) (icmp ty (IntCC.UnsignedLessThan) x y))
+
+(decl ule (Type Value Value) Value)
+(extractor (ule ty x y) (icmp ty (IntCC.UnsignedLessThanOrEqual) x y))
+
+(decl ugt (Type Value Value) Value)
+(extractor (ugt ty x y) (icmp ty (IntCC.UnsignedGreaterThan) x y))
+
+(decl uge (Type Value Value) Value)
+(extractor (uge ty x y) (icmp ty (IntCC.UnsignedGreaterThanOrEqual) x y))
+
+(decl slt (Type Value Value) Value)
+(extractor (slt ty x y) (icmp ty (IntCC.SignedLessThan) x y))
+
+(decl sle (Type Value Value) Value)
+(extractor (sle ty x y) (icmp ty (IntCC.SignedLessThanOrEqual) x y))
+
+(decl sgt (Type Value Value) Value)
+(extractor (sgt ty x y) (icmp ty (IntCC.SignedGreaterThan) x y))
+
+(decl sge (Type Value Value) Value)
+(extractor (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y))
+
 ;; An extractor that only matches types that can fit in 16 bits.
 (decl fits_in_16 (Type) Type)
 (extern extractor fits_in_16 fits_in_16)
diff --git a/cranelift/codegen/src/prelude_opt.isle b/cranelift/codegen/src/prelude_opt.isle
index d3fc0d1bb..099930f5b 100644
--- a/cranelift/codegen/src/prelude_opt.isle
+++ b/cranelift/codegen/src/prelude_opt.isle
@@ -17,6 +17,17 @@
 (decl value_array_3_ctor (Value Value Value) ValueArray3)
 (extern constructor value_array_3_ctor value_array_3_ctor)
 
+(rule (eq ty x y) (icmp ty (IntCC.Equal) x y))
+(rule (ne ty x y) (icmp ty (IntCC.NotEqual) x y))
+(rule (ult ty x y) (icmp ty (IntCC.UnsignedLessThan) x y))
+(rule (ule ty x y) (icmp ty (IntCC.UnsignedLessThanOrEqual) x y))
+(rule (ugt ty x y) (icmp ty (IntCC.UnsignedGreaterThan) x y))
+(rule (uge ty x y) (icmp ty (IntCC.UnsignedGreaterThanOrEqual) x y))
+(rule (slt ty x y) (icmp ty (IntCC.SignedLessThan) x y))
+(rule (sle ty x y) (icmp ty (IntCC.SignedLessThanOrEqual) x y))
+(rule (sgt ty x y) (icmp ty (IntCC.SignedGreaterThan) x y))
+(rule (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y))
+
 ;;;;; optimization toplevel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; The main matcher rule invoked by the toplevel driver.
diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml
index 140b39a12..014d0f076 100644
--- a/cranelift/fuzzgen/Cargo.toml
+++ b/cranelift/fuzzgen/Cargo.toml
@@ -16,4 +16,5 @@ cranelift-native = { workspace = true }
 
 anyhow = { workspace = true }
 arbitrary = "1.0.0"
+once_cell = { workspace = true }
 target-lexicon = { workspace = true, features = ["std"] }
diff --git a/cranelift/fuzzgen/src/cranelift_arbitrary.rs b/cranelift/fuzzgen/src/cranelift_arbitrary.rs
index ebafb55d8..b15622687 100644
--- a/cranelift/fuzzgen/src/cranelift_arbitrary.rs
+++ b/cranelift/fuzzgen/src/cranelift_arbitrary.rs
@@ -23,23 +23,26 @@ pub trait CraneliftArbitrary {
     fn datavalue(&mut self, ty: Type) -> Result<DataValue>;
 }
 
+/// Returns the set of types that are valid for value generation, dependent on architecture.
+pub fn types_for_architecture(architecture: Architecture) -> &'static [Type] {
+    // TODO: It would be nice if we could get these directly from cranelift
+    // TODO: RISCV does not support SIMD yet
+    let supports_simd = !matches!(architecture, Architecture::Riscv64(_));
+    if supports_simd {
+        &[
+            I8, I16, I32, I64, I128, // Scalar Integers
+            F32, F64, // Scalar Floats
+            I8X16, I16X8, I32X4, I64X2, // SIMD Integers
+            F32X4, F64X2, // SIMD Floats
+        ]
+    } else {
+        &[I8, I16, I32, I64, I128, F32, F64]
+    }
+}
+
 impl<'a> CraneliftArbitrary for &mut Unstructured<'a> {
     fn _type(&mut self, architecture: Architecture) -> Result<Type> {
-        // TODO: It would be nice if we could get these directly from cranelift
-        // TODO: RISCV does not support SIMD yet
-        let supports_simd = !matches!(architecture, Architecture::Riscv64(_));
-        let choices = if supports_simd {
-            &[
-                I8, I16, I32, I64, I128, // Scalar Integers
-                F32, F64, // Scalar Floats
-                I8X16, I16X8, I32X4, I64X2, // SIMD Integers
-                F32X4, F64X2, // SIMD Floats
-            ][..]
-        } else {
-            &[I8, I16, I32, I64, I128, F32, F64][..]
-        };
-
-        Ok(*self.choose(choices)?)
+        Ok(*self.choose(types_for_architecture(architecture))?)
     }
 
     fn callconv(&mut self) -> Result<CallConv> {
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index eb0425688..7679c5df3 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
 use arbitrary::{Arbitrary, Unstructured};
 use cranelift::codegen::data_value::DataValue;
 use cranelift::codegen::ir::immediates::Offset32;
-use cranelift::codegen::ir::instructions::InstructionFormat;
+use cranelift::codegen::ir::instructions::{InstructionFormat, ResolvedConstraint};
 use cranelift::codegen::ir::stackslot::StackSize;
 
 use cranelift::codegen::ir::{
@@ -17,6 +17,7 @@ use cranelift::prelude::{
     EntityRef, ExtFuncData, FloatCC, InstBuilder, IntCC, JumpTableData, MemFlags, StackSlotData,
     StackSlotKind,
 };
+use once_cell::sync::Lazy;
 use std::collections::HashMap;
 use std::ops::RangeInclusive;
 use target_lexicon::{Architecture, Triple};
@@ -27,8 +28,8 @@ fn insert_opcode(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    args: &'static [Type],
-    rets: &'static [Type],
+    args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let mut vals = Vec::with_capacity(args.len());
     for &arg in args.into_iter() {
@@ -71,8 +72,8 @@ fn insert_call(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    _args: &'static [Type],
-    _rets: &'static [Type],
+    _args: &[Type],
+    _rets: &[Type],
 ) -> Result<()> {
     assert_eq!(opcode, Opcode::Call, "only call handled at the moment");
     let (sig, func_ref) = fgen.u.choose(&fgen.resources.func_refs)?.clone();
@@ -90,8 +91,8 @@ fn insert_stack_load(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     _opcode: Opcode,
-    _args: &'static [Type],
-    rets: &'static [Type],
+    _args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let typevar = rets[0];
     let type_size = typevar.bytes();
@@ -109,8 +110,8 @@ fn insert_stack_store(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     _opcode: Opcode,
-    args: &'static [Type],
-    _rets: &'static [Type],
+    args: &[Type],
+    _rets: &[Type],
 ) -> Result<()> {
     let typevar = args[0];
     let type_size = typevar.bytes();
@@ -128,8 +129,8 @@ fn insert_cmp(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    args: &'static [Type],
-    rets: &'static [Type],
+    args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let lhs = fgen.get_variable_of_type(args[0])?;
     let lhs = builder.use_var(lhs);
@@ -179,8 +180,8 @@ fn insert_const(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     _opcode: Opcode,
-    _args: &'static [Type],
-    rets: &'static [Type],
+    _args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let typevar = rets[0];
     let var = fgen.get_variable_of_type(typevar)?;
@@ -192,8 +193,8 @@ fn insert_const(
 fn insert_bitcast(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
-    args: &'static [Type],
-    rets: &'static [Type],
+    args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let from_var = fgen.get_variable_of_type(args[0])?;
     let from_val = builder.use_var(from_var);
@@ -212,8 +213,8 @@ fn insert_load_store(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    args: &'static [Type],
-    rets: &'static [Type],
+    args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     if opcode == Opcode::Bitcast {
         return insert_bitcast(fgen, builder, args, rets);
@@ -270,8 +271,8 @@ fn insert_atomic_rmw(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     _: Opcode,
-    _: &'static [Type],
-    rets: &'static [Type],
+    _: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let ctrl_type = *rets.first().unwrap();
     let type_size = ctrl_type.bytes();
@@ -300,8 +301,8 @@ fn insert_atomic_cas(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     _: Opcode,
-    _: &'static [Type],
-    rets: &'static [Type],
+    _: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let ctrl_type = *rets.first().unwrap();
     let type_size = ctrl_type.bytes();
@@ -330,8 +331,8 @@ fn insert_shuffle(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    _: &'static [Type],
-    rets: &'static [Type],
+    _: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let ctrl_type = *rets.first().unwrap();
 
@@ -363,8 +364,8 @@ fn insert_ins_ext_lane(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     opcode: Opcode,
-    args: &'static [Type],
-    rets: &'static [Type],
+    args: &[Type],
+    rets: &[Type],
 ) -> Result<()> {
     let vector_type = *args.first().unwrap();
     let ret_type = *rets.first().unwrap();
@@ -392,28 +393,63 @@ type OpcodeInserter = fn(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
     Opcode,
-    &'static [Type],
-    &'static [Type],
+    &[Type],
+    &[Type],
 ) -> Result<()>;
 
+macro_rules! exceptions {
+    ($op:expr, $args:expr, $rets:expr, $(($($cases:pat),*)),* $(,)?) => {
+        match ($op, $args, $rets) {
+            $( ($($cases,)* ..) => return false, )*
+            _ => true,
+        }
+    }
+}
+
 /// Returns true if we believe this `OpcodeSignature` should compile correctly
 /// for the given target triple. We currently have a range of known issues
 /// with specific lowerings on specific backends, and we don't want to get
 /// fuzz bug reports for those. Over time our goal is to eliminate all of these
 /// exceptions.
 fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -> bool {
-    macro_rules! exceptions {
-        ( $(($($cases:pat),*)),* $(,)?) => {
-            match (op, args, rets) {
-                $( ($($cases,)* ..) => false, )*
-                _ => true,
+    // Rule out invalid combinations that we don't yet have a good way of rejecting with the
+    // instruction DSL type constraints.
+    match op {
+        Opcode::FcvtToUintSat | Opcode::FcvtToSintSat => {
+            assert_eq!(args.len(), 1);
+            assert_eq!(rets.len(), 1);
+
+            let arg = args[0];
+            let ret = args[0];
+
+            // Vector arguments must produce vector results, and scalar arguments must produce
+            // scalar results.
+            if arg.is_vector() != ret.is_vector() {
+                return false;
+            }
+
+            if arg.is_vector() && arg.is_vector() {
+                // Vector conversions must have the same number of lanes, and the lanes must be the
+                // same bit-width.
+                if arg.lane_count() != ret.lane_count() {
+                    return false;
+                }
+
+                if arg.lane_of().bits() != ret.lane_of().bits() {
+                    return false;
+                }
             }
         }
+
+        _ => {}
     }
 
     match triple.architecture {
         Architecture::X86_64 => {
             exceptions!(
+                op,
+                args,
+                rets,
                 (Opcode::IaddCout, &([I8, I8] | [I16, I16] | [I128, I128])),
                 (Opcode::Imul, &[I8X16, I8X16]),
                 // https://github.com/bytecodealliance/wasmtime/issues/5468
@@ -528,6 +564,9 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
 
         Architecture::Aarch64(_) => {
             exceptions!(
+                op,
+                args,
+                rets,
                 (Opcode::IaddCout, &[I128, I128]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4864
                 (Opcode::Udiv | Opcode::Sdiv, &[I128, I128]),
@@ -580,6 +619,9 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
 
         Architecture::S390x => {
             exceptions!(
+                op,
+                args,
+                rets,
                 (Opcode::IaddCout),
                 (
                     Opcode::Udiv | Opcode::Sdiv | Opcode::Urem | Opcode::Srem,
@@ -620,6 +662,9 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
             }
 
             exceptions!(
+                op,
+                args,
+                rets,
                 // TODO
                 (Opcode::IaddCout),
                 // TODO
@@ -662,935 +707,712 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
     }
 }
 
-type OpcodeSignature = (
-    Opcode,
-    &'static [Type], // Args
-    &'static [Type], // Rets
-);
-
-// TODO: Derive this from the `cranelift-meta` generator.
-#[rustfmt::skip]
-pub const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
-    (Opcode::Nop, &[], &[]),
-    // Iadd
-    (Opcode::Iadd, &[I8, I8], &[I8]),
-    (Opcode::Iadd, &[I16, I16], &[I16]),
-    (Opcode::Iadd, &[I32, I32], &[I32]),
-    (Opcode::Iadd, &[I64, I64], &[I64]),
-    (Opcode::Iadd, &[I128, I128], &[I128]),
-    (Opcode::Iadd, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Iadd, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Iadd, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Iadd, &[I64X2, I64X2], &[I64X2]),
-    // IaddCout
-    (Opcode::IaddCout, &[I8, I8], &[I8, I8]),
-    (Opcode::IaddCout, &[I16, I16], &[I16, I8]),
-    (Opcode::IaddCout, &[I32, I32], &[I32, I8]),
-    (Opcode::IaddCout, &[I64, I64], &[I64, I8]),
-    (Opcode::IaddCout, &[I128, I128], &[I128, I8]),
-    // UaddSat
-    (Opcode::UaddSat, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::UaddSat, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::UaddSat, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::UaddSat, &[I64X2, I64X2], &[I64X2]),
-    // SaddSat
-    (Opcode::SaddSat, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::SaddSat, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::SaddSat, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::SaddSat, &[I64X2, I64X2], &[I64X2]),
-    // Isub
-    (Opcode::Isub, &[I8, I8], &[I8]),
-    (Opcode::Isub, &[I16, I16], &[I16]),
-    (Opcode::Isub, &[I32, I32], &[I32]),
-    (Opcode::Isub, &[I64, I64], &[I64]),
-    (Opcode::Isub, &[I128, I128], &[I128]),
-    (Opcode::Isub, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Isub, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Isub, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Isub, &[I64X2, I64X2], &[I64X2]),
-    // UsubSat
-    (Opcode::UsubSat, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::UsubSat, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::UsubSat, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::UsubSat, &[I64X2, I64X2], &[I64X2]),
-    // SsubSat
-    (Opcode::SsubSat, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::SsubSat, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::SsubSat, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::SsubSat, &[I64X2, I64X2], &[I64X2]),
-    // Imul
-    (Opcode::Imul, &[I8, I8], &[I8]),
-    (Opcode::Imul, &[I16, I16], &[I16]),
-    (Opcode::Imul, &[I32, I32], &[I32]),
-    (Opcode::Imul, &[I64, I64], &[I64]),
-    (Opcode::Imul, &[I128, I128], &[I128]),
-    (Opcode::Imul, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Imul, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Imul, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Imul, &[I64X2, I64X2], &[I64X2]),
-    // Smulhi
-    (Opcode::Smulhi, &[I8, I8], &[I8]),
-    (Opcode::Smulhi, &[I16, I16], &[I16]),
-    (Opcode::Smulhi, &[I32, I32], &[I32]),
-    (Opcode::Smulhi, &[I64, I64], &[I64]),
-    (Opcode::Smulhi, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Smulhi, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Smulhi, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Smulhi, &[I64X2, I64X2], &[I64X2]),
-    // Umulhi
-    (Opcode::Umulhi, &[I8, I8], &[I8]),
-    (Opcode::Umulhi, &[I16, I16], &[I16]),
-    (Opcode::Umulhi, &[I32, I32], &[I32]),
-    (Opcode::Umulhi, &[I64, I64], &[I64]),
-    (Opcode::Umulhi, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Umulhi, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Umulhi, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Umulhi, &[I64X2, I64X2], &[I64X2]),
-    // Udiv
-    (Opcode::Udiv, &[I8, I8], &[I8]),
-    (Opcode::Udiv, &[I16, I16], &[I16]),
-    (Opcode::Udiv, &[I32, I32], &[I32]),
-    (Opcode::Udiv, &[I64, I64], &[I64]),
-    (Opcode::Udiv, &[I128, I128], &[I128]),
-    // Sdiv
-    (Opcode::Sdiv, &[I8, I8], &[I8]),
-    (Opcode::Sdiv, &[I16, I16], &[I16]),
-    (Opcode::Sdiv, &[I32, I32], &[I32]),
-    (Opcode::Sdiv, &[I64, I64], &[I64]),
-    (Opcode::Sdiv, &[I128, I128], &[I128]),
-    // Urem
-    (Opcode::Urem, &[I8, I8], &[I8]),
-    (Opcode::Urem, &[I16, I16], &[I16]),
-    (Opcode::Urem, &[I32, I32], &[I32]),
-    (Opcode::Urem, &[I64, I64], &[I64]),
-    (Opcode::Urem, &[I128, I128], &[I128]),
-    // Srem
-    (Opcode::Srem, &[I8, I8], &[I8]),
-    (Opcode::Srem, &[I16, I16], &[I16]),
-    (Opcode::Srem, &[I32, I32], &[I32]),
-    (Opcode::Srem, &[I64, I64], &[I64]),
-    (Opcode::Srem, &[I128, I128], &[I128]),
-    // Ineg
-    (Opcode::Ineg, &[I8], &[I8]),
-    (Opcode::Ineg, &[I16], &[I16]),
-    (Opcode::Ineg, &[I32], &[I32]),
-    (Opcode::Ineg, &[I64], &[I64]),
-    (Opcode::Ineg, &[I128], &[I128]),
-    // Iabs
-    (Opcode::Iabs, &[I8], &[I8]),
-    (Opcode::Iabs, &[I16], &[I16]),
-    (Opcode::Iabs, &[I32], &[I32]),
-    (Opcode::Iabs, &[I64], &[I64]),
-    (Opcode::Iabs, &[I128], &[I128]),
-    (Opcode::Iabs, &[I8X16], &[I8X16]),
-    (Opcode::Iabs, &[I16X8], &[I16X8]),
-    (Opcode::Iabs, &[I32X4], &[I32X4]),
-    (Opcode::Iabs, &[I64X2], &[I64X2]),
-    // Smin
-    (Opcode::Smin, &[I8, I8], &[I8]),
-    (Opcode::Smin, &[I16, I16], &[I16]),
-    (Opcode::Smin, &[I32, I32], &[I32]),
-    (Opcode::Smin, &[I64, I64], &[I64]),
-    (Opcode::Smin, &[I128, I128], &[I128]),
-    (Opcode::Smin, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Smin, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Smin, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Smin, &[I64X2, I64X2], &[I64X2]),
-    // Umin
-    (Opcode::Umin, &[I8, I8], &[I8]),
-    (Opcode::Umin, &[I16, I16], &[I16]),
-    (Opcode::Umin, &[I32, I32], &[I32]),
-    (Opcode::Umin, &[I64, I64], &[I64]),
-    (Opcode::Umin, &[I128, I128], &[I128]),
-    (Opcode::Umin, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Umin, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Umin, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Umin, &[I64X2, I64X2], &[I64X2]),
-    // Smax
-    (Opcode::Smax, &[I8, I8], &[I8]),
-    (Opcode::Smax, &[I16, I16], &[I16]),
-    (Opcode::Smax, &[I32, I32], &[I32]),
-    (Opcode::Smax, &[I64, I64], &[I64]),
-    (Opcode::Smax, &[I128, I128], &[I128]),
-    (Opcode::Smax, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Smax, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Smax, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Smax, &[I64X2, I64X2], &[I64X2]),
-    // Umax
-    (Opcode::Umax, &[I8, I8], &[I8]),
-    (Opcode::Umax, &[I16, I16], &[I16]),
-    (Opcode::Umax, &[I32, I32], &[I32]),
-    (Opcode::Umax, &[I64, I64], &[I64]),
-    (Opcode::Umax, &[I128, I128], &[I128]),
-    (Opcode::Umax, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Umax, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Umax, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Umax, &[I64X2, I64X2], &[I64X2]),
-    // Rotr
-    (Opcode::Rotr, &[I8, I8], &[I8]),
-    (Opcode::Rotr, &[I8, I16], &[I8]),
-    (Opcode::Rotr, &[I8, I32], &[I8]),
-    (Opcode::Rotr, &[I8, I64], &[I8]),
-    (Opcode::Rotr, &[I8, I128], &[I8]),
-    (Opcode::Rotr, &[I16, I8], &[I16]),
-    (Opcode::Rotr, &[I16, I16], &[I16]),
-    (Opcode::Rotr, &[I16, I32], &[I16]),
-    (Opcode::Rotr, &[I16, I64], &[I16]),
-    (Opcode::Rotr, &[I16, I128], &[I16]),
-    (Opcode::Rotr, &[I32, I8], &[I32]),
-    (Opcode::Rotr, &[I32, I16], &[I32]),
-    (Opcode::Rotr, &[I32, I32], &[I32]),
-    (Opcode::Rotr, &[I32, I64], &[I32]),
-    (Opcode::Rotr, &[I32, I128], &[I32]),
-    (Opcode::Rotr, &[I64, I8], &[I64]),
-    (Opcode::Rotr, &[I64, I16], &[I64]),
-    (Opcode::Rotr, &[I64, I32], &[I64]),
-    (Opcode::Rotr, &[I64, I64], &[I64]),
-    (Opcode::Rotr, &[I64, I128], &[I64]),
-    (Opcode::Rotr, &[I128, I8], &[I128]),
-    (Opcode::Rotr, &[I128, I16], &[I128]),
-    (Opcode::Rotr, &[I128, I32], &[I128]),
-    (Opcode::Rotr, &[I128, I64], &[I128]),
-    (Opcode::Rotr, &[I128, I128], &[I128]),
-    // Rotl
-    (Opcode::Rotl, &[I8, I8], &[I8]),
-    (Opcode::Rotl, &[I8, I16], &[I8]),
-    (Opcode::Rotl, &[I8, I32], &[I8]),
-    (Opcode::Rotl, &[I8, I64], &[I8]),
-    (Opcode::Rotl, &[I8, I128], &[I8]),
-    (Opcode::Rotl, &[I16, I8], &[I16]),
-    (Opcode::Rotl, &[I16, I16], &[I16]),
-    (Opcode::Rotl, &[I16, I32], &[I16]),
-    (Opcode::Rotl, &[I16, I64], &[I16]),
-    (Opcode::Rotl, &[I16, I128], &[I16]),
-    (Opcode::Rotl, &[I32, I8], &[I32]),
-    (Opcode::Rotl, &[I32, I16], &[I32]),
-    (Opcode::Rotl, &[I32, I32], &[I32]),
-    (Opcode::Rotl, &[I32, I64], &[I32]),
-    (Opcode::Rotl, &[I32, I128], &[I32]),
-    (Opcode::Rotl, &[I64, I8], &[I64]),
-    (Opcode::Rotl, &[I64, I16], &[I64]),
-    (Opcode::Rotl, &[I64, I32], &[I64]),
-    (Opcode::Rotl, &[I64, I64], &[I64]),
-    (Opcode::Rotl, &[I64, I128], &[I64]),
-    (Opcode::Rotl, &[I128, I8], &[I128]),
-    (Opcode::Rotl, &[I128, I16], &[I128]),
-    (Opcode::Rotl, &[I128, I32], &[I128]),
-    (Opcode::Rotl, &[I128, I64], &[I128]),
-    (Opcode::Rotl, &[I128, I128], &[I128]),
-    // Ishl
-    (Opcode::Ishl, &[I8, I8], &[I8]),
-    (Opcode::Ishl, &[I8, I16], &[I8]),
-    (Opcode::Ishl, &[I8, I32], &[I8]),
-    (Opcode::Ishl, &[I8, I64], &[I8]),
-    (Opcode::Ishl, &[I8, I128], &[I8]),
-    (Opcode::Ishl, &[I16, I8], &[I16]),
-    (Opcode::Ishl, &[I16, I16], &[I16]),
-    (Opcode::Ishl, &[I16, I32], &[I16]),
-    (Opcode::Ishl, &[I16, I64], &[I16]),
-    (Opcode::Ishl, &[I16, I128], &[I16]),
-    (Opcode::Ishl, &[I32, I8], &[I32]),
-    (Opcode::Ishl, &[I32, I16], &[I32]),
-    (Opcode::Ishl, &[I32, I32], &[I32]),
-    (Opcode::Ishl, &[I32, I64], &[I32]),
-    (Opcode::Ishl, &[I32, I128], &[I32]),
-    (Opcode::Ishl, &[I64, I8], &[I64]),
-    (Opcode::Ishl, &[I64, I16], &[I64]),
-    (Opcode::Ishl, &[I64, I32], &[I64]),
-    (Opcode::Ishl, &[I64, I64], &[I64]),
-    (Opcode::Ishl, &[I64, I128], &[I64]),
-    (Opcode::Ishl, &[I128, I8], &[I128]),
-    (Opcode::Ishl, &[I128, I16], &[I128]),
-    (Opcode::Ishl, &[I128, I32], &[I128]),
-    (Opcode::Ishl, &[I128, I64], &[I128]),
-    (Opcode::Ishl, &[I128, I128], &[I128]),
-    // Sshr
-    (Opcode::Sshr, &[I8, I8], &[I8]),
-    (Opcode::Sshr, &[I8, I16], &[I8]),
-    (Opcode::Sshr, &[I8, I32], &[I8]),
-    (Opcode::Sshr, &[I8, I64], &[I8]),
-    (Opcode::Sshr, &[I8, I128], &[I8]),
-    (Opcode::Sshr, &[I16, I8], &[I16]),
-    (Opcode::Sshr, &[I16, I16], &[I16]),
-    (Opcode::Sshr, &[I16, I32], &[I16]),
-    (Opcode::Sshr, &[I16, I64], &[I16]),
-    (Opcode::Sshr, &[I16, I128], &[I16]),
-    (Opcode::Sshr, &[I32, I8], &[I32]),
-    (Opcode::Sshr, &[I32, I16], &[I32]),
-    (Opcode::Sshr, &[I32, I32], &[I32]),
-    (Opcode::Sshr, &[I32, I64], &[I32]),
-    (Opcode::Sshr, &[I32, I128], &[I32]),
-    (Opcode::Sshr, &[I64, I8], &[I64]),
-    (Opcode::Sshr, &[I64, I16], &[I64]),
-    (Opcode::Sshr, &[I64, I32], &[I64]),
-    (Opcode::Sshr, &[I64, I64], &[I64]),
-    (Opcode::Sshr, &[I64, I128], &[I64]),
-    (Opcode::Sshr, &[I128, I8], &[I128]),
-    (Opcode::Sshr, &[I128, I16], &[I128]),
-    (Opcode::Sshr, &[I128, I32], &[I128]),
-    (Opcode::Sshr, &[I128, I64], &[I128]),
-    (Opcode::Sshr, &[I128, I128], &[I128]),
-    // Ushr
-    (Opcode::Ushr, &[I8, I8], &[I8]),
-    (Opcode::Ushr, &[I8, I16], &[I8]),
-    (Opcode::Ushr, &[I8, I32], &[I8]),
-    (Opcode::Ushr, &[I8, I64], &[I8]),
-    (Opcode::Ushr, &[I8, I128], &[I8]),
-    (Opcode::Ushr, &[I16, I8], &[I16]),
-    (Opcode::Ushr, &[I16, I16], &[I16]),
-    (Opcode::Ushr, &[I16, I32], &[I16]),
-    (Opcode::Ushr, &[I16, I64], &[I16]),
-    (Opcode::Ushr, &[I16, I128], &[I16]),
-    (Opcode::Ushr, &[I32, I8], &[I32]),
-    (Opcode::Ushr, &[I32, I16], &[I32]),
-    (Opcode::Ushr, &[I32, I32], &[I32]),
-    (Opcode::Ushr, &[I32, I64], &[I32]),
-    (Opcode::Ushr, &[I32, I128], &[I32]),
-    (Opcode::Ushr, &[I64, I8], &[I64]),
-    (Opcode::Ushr, &[I64, I16], &[I64]),
-    (Opcode::Ushr, &[I64, I32], &[I64]),
-    (Opcode::Ushr, &[I64, I64], &[I64]),
-    (Opcode::Ushr, &[I64, I128], &[I64]),
-    (Opcode::Ushr, &[I128, I8], &[I128]),
-    (Opcode::Ushr, &[I128, I16], &[I128]),
-    (Opcode::Ushr, &[I128, I32], &[I128]),
-    (Opcode::Ushr, &[I128, I64], &[I128]),
-    (Opcode::Ushr, &[I128, I128], &[I128]),
-    // Uextend
-    (Opcode::Uextend, &[I8], &[I16]),
-    (Opcode::Uextend, &[I8], &[I32]),
-    (Opcode::Uextend, &[I8], &[I64]),
-    (Opcode::Uextend, &[I8], &[I128]),
-    (Opcode::Uextend, &[I16], &[I32]),
-    (Opcode::Uextend, &[I16], &[I64]),
-    (Opcode::Uextend, &[I16], &[I128]),
-    (Opcode::Uextend, &[I32], &[I64]),
-    (Opcode::Uextend, &[I32], &[I128]),
-    (Opcode::Uextend, &[I64], &[I128]),
-    // Sextend
-    (Opcode::Sextend, &[I8], &[I16]),
-    (Opcode::Sextend, &[I8], &[I32]),
-    (Opcode::Sextend, &[I8], &[I64]),
-    (Opcode::Sextend, &[I8], &[I128]),
-    (Opcode::Sextend, &[I16], &[I32]),
-    (Opcode::Sextend, &[I16], &[I64]),
-    (Opcode::Sextend, &[I16], &[I128]),
-    (Opcode::Sextend, &[I32], &[I64]),
-    (Opcode::Sextend, &[I32], &[I128]),
-    (Opcode::Sextend, &[I64], &[I128]),
-    // Ireduce
-    (Opcode::Ireduce, &[I16], &[I8]),
-    (Opcode::Ireduce, &[I32], &[I8]),
-    (Opcode::Ireduce, &[I32], &[I16]),
-    (Opcode::Ireduce, &[I64], &[I8]),
-    (Opcode::Ireduce, &[I64], &[I16]),
-    (Opcode::Ireduce, &[I64], &[I32]),
-    (Opcode::Ireduce, &[I128], &[I8]),
-    (Opcode::Ireduce, &[I128], &[I16]),
-    (Opcode::Ireduce, &[I128], &[I32]),
-    (Opcode::Ireduce, &[I128], &[I64]),
-    // Isplit
-    (Opcode::Isplit, &[I128], &[I64, I64]),
-    // Iconcat
-    (Opcode::Iconcat, &[I64, I64], &[I128]),
-    // Band
-    (Opcode::Band, &[I8, I8], &[I8]),
-    (Opcode::Band, &[I16, I16], &[I16]),
-    (Opcode::Band, &[I32, I32], &[I32]),
-    (Opcode::Band, &[I64, I64], &[I64]),
-    (Opcode::Band, &[I128, I128], &[I128]),
-    (Opcode::Band, &[F32, F32], &[F32]),
-    (Opcode::Band, &[F64, F64], &[F64]),
-    (Opcode::Band, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Band, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Band, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Band, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::Band, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Band, &[F64X2, F64X2], &[F64X2]),
-    // Bor
-    (Opcode::Bor, &[I8, I8], &[I8]),
-    (Opcode::Bor, &[I16, I16], &[I16]),
-    (Opcode::Bor, &[I32, I32], &[I32]),
-    (Opcode::Bor, &[I64, I64], &[I64]),
-    (Opcode::Bor, &[I128, I128], &[I128]),
-    (Opcode::Bor, &[F32, F32], &[F32]),
-    (Opcode::Bor, &[F64, F64], &[F64]),
-    (Opcode::Bor, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Bor, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Bor, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Bor, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::Bor, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Bor, &[F64X2, F64X2], &[F64X2]),
-    // Bxor
-    (Opcode::Bxor, &[I8, I8], &[I8]),
-    (Opcode::Bxor, &[I16, I16], &[I16]),
-    (Opcode::Bxor, &[I32, I32], &[I32]),
-    (Opcode::Bxor, &[I64, I64], &[I64]),
-    (Opcode::Bxor, &[I128, I128], &[I128]),
-    (Opcode::Bxor, &[F32, F32], &[F32]),
-    (Opcode::Bxor, &[F64, F64], &[F64]),
-    (Opcode::Bxor, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Bxor, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Bxor, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Bxor, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::Bxor, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Bxor, &[F64X2, F64X2], &[F64X2]),
-    // Bnot
-    (Opcode::Bnot, &[I8], &[I8]),
-    (Opcode::Bnot, &[I16], &[I16]),
-    (Opcode::Bnot, &[I32], &[I32]),
-    (Opcode::Bnot, &[I64], &[I64]),
-    (Opcode::Bnot, &[I128], &[I128]),
-    (Opcode::Bnot, &[F32], &[F32]),
-    (Opcode::Bnot, &[F64], &[F64]),
-    (Opcode::Bnot, &[I8X16], &[I8X16]),
-    (Opcode::Bnot, &[I16X8], &[I16X8]),
-    (Opcode::Bnot, &[I32X4], &[I32X4]),
-    (Opcode::Bnot, &[I64X2], &[I64X2]),
-    (Opcode::Bnot, &[F32X4], &[F32X4]),
-    (Opcode::Bnot, &[F64X2], &[F64X2]),
-    // BandNot
-    (Opcode::BandNot, &[I8, I8], &[I8]),
-    (Opcode::BandNot, &[I16, I16], &[I16]),
-    (Opcode::BandNot, &[I32, I32], &[I32]),
-    (Opcode::BandNot, &[I64, I64], &[I64]),
-    (Opcode::BandNot, &[I128, I128], &[I128]),
-    (Opcode::BandNot, &[F32, F32], &[F32]),
-    (Opcode::BandNot, &[F64, F64], &[F64]),
-    (Opcode::BandNot, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::BandNot, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::BandNot, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::BandNot, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::BandNot, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::BandNot, &[F64X2, F64X2], &[F64X2]),
-    // BorNot
-    (Opcode::BorNot, &[I8, I8], &[I8]),
-    (Opcode::BorNot, &[I16, I16], &[I16]),
-    (Opcode::BorNot, &[I32, I32], &[I32]),
-    (Opcode::BorNot, &[I64, I64], &[I64]),
-    (Opcode::BorNot, &[I128, I128], &[I128]),
-    (Opcode::BorNot, &[F32, F32], &[F32]),
-    (Opcode::BorNot, &[F64, F64], &[F64]),
-    (Opcode::BorNot, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::BorNot, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::BorNot, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::BorNot, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::BorNot, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::BorNot, &[F64X2, F64X2], &[F64X2]),
-    // BxorNot
-    (Opcode::BxorNot, &[I8, I8], &[I8]),
-    (Opcode::BxorNot, &[I16, I16], &[I16]),
-    (Opcode::BxorNot, &[I32, I32], &[I32]),
-    (Opcode::BxorNot, &[I64, I64], &[I64]),
-    (Opcode::BxorNot, &[I128, I128], &[I128]),
-    (Opcode::BxorNot, &[F32, F32], &[F32]),
-    (Opcode::BxorNot, &[F64, F64], &[F64]),
-    (Opcode::BxorNot, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::BxorNot, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::BxorNot, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::BxorNot, &[I64X2, I64X2], &[I64X2]),
-    (Opcode::BxorNot, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::BxorNot, &[F64X2, F64X2], &[F64X2]),
-    // Bitrev
-    (Opcode::Bitrev, &[I8], &[I8]),
-    (Opcode::Bitrev, &[I16], &[I16]),
-    (Opcode::Bitrev, &[I32], &[I32]),
-    (Opcode::Bitrev, &[I64], &[I64]),
-    (Opcode::Bitrev, &[I128], &[I128]),
-    // Clz
-    (Opcode::Clz, &[I8], &[I8]),
-    (Opcode::Clz, &[I16], &[I16]),
-    (Opcode::Clz, &[I32], &[I32]),
-    (Opcode::Clz, &[I64], &[I64]),
-    (Opcode::Clz, &[I128], &[I128]),
-    // Cls
-    (Opcode::Cls, &[I8], &[I8]),
-    (Opcode::Cls, &[I16], &[I16]),
-    (Opcode::Cls, &[I32], &[I32]),
-    (Opcode::Cls, &[I64], &[I64]),
-    (Opcode::Cls, &[I128], &[I128]),
-    // Ctz
-    (Opcode::Ctz, &[I8], &[I8]),
-    (Opcode::Ctz, &[I16], &[I16]),
-    (Opcode::Ctz, &[I32], &[I32]),
-    (Opcode::Ctz, &[I64], &[I64]),
-    (Opcode::Ctz, &[I128], &[I128]),
-    // Popcnt
-    (Opcode::Popcnt, &[I8], &[I8]),
-    (Opcode::Popcnt, &[I16], &[I16]),
-    (Opcode::Popcnt, &[I32], &[I32]),
-    (Opcode::Popcnt, &[I64], &[I64]),
-    (Opcode::Popcnt, &[I128], &[I128]),
-    (Opcode::Popcnt, &[I8X16], &[I8X16]),
-    (Opcode::Popcnt, &[I16X8], &[I16X8]),
-    (Opcode::Popcnt, &[I32X4], &[I32X4]),
-    (Opcode::Popcnt, &[I64X2], &[I64X2]),
-    // Bmask
-    (Opcode::Bmask, &[I8], &[I8]),
-    (Opcode::Bmask, &[I16], &[I8]),
-    (Opcode::Bmask, &[I32], &[I8]),
-    (Opcode::Bmask, &[I64], &[I8]),
-    (Opcode::Bmask, &[I128], &[I8]),
-    (Opcode::Bmask, &[I8], &[I16]),
-    (Opcode::Bmask, &[I16], &[I16]),
-    (Opcode::Bmask, &[I32], &[I16]),
-    (Opcode::Bmask, &[I64], &[I16]),
-    (Opcode::Bmask, &[I128], &[I16]),
-    (Opcode::Bmask, &[I8], &[I32]),
-    (Opcode::Bmask, &[I16], &[I32]),
-    (Opcode::Bmask, &[I32], &[I32]),
-    (Opcode::Bmask, &[I64], &[I32]),
-    (Opcode::Bmask, &[I128], &[I32]),
-    (Opcode::Bmask, &[I8], &[I64]),
-    (Opcode::Bmask, &[I16], &[I64]),
-    (Opcode::Bmask, &[I32], &[I64]),
-    (Opcode::Bmask, &[I64], &[I64]),
-    (Opcode::Bmask, &[I128], &[I64]),
-    (Opcode::Bmask, &[I8], &[I128]),
-    (Opcode::Bmask, &[I16], &[I128]),
-    (Opcode::Bmask, &[I32], &[I128]),
-    (Opcode::Bmask, &[I64], &[I128]),
-    (Opcode::Bmask, &[I128], &[I128]),
-    // Bswap
-    (Opcode::Bswap, &[I16], &[I16]),
-    (Opcode::Bswap, &[I32], &[I32]),
-    (Opcode::Bswap, &[I64], &[I64]),
-    (Opcode::Bswap, &[I128], &[I128]),
-    // Bitselect
-    (Opcode::Bitselect, &[I8, I8, I8], &[I8]),
-    (Opcode::Bitselect, &[I16, I16, I16], &[I16]),
-    (Opcode::Bitselect, &[I32, I32, I32], &[I32]),
-    (Opcode::Bitselect, &[I64, I64, I64], &[I64]),
-    (Opcode::Bitselect, &[I128, I128, I128], &[I128]),
-    (Opcode::Bitselect, &[I8X16, I8X16, I8X16], &[I8X16]),
-    (Opcode::Bitselect, &[I16X8, I16X8, I16X8], &[I16X8]),
-    (Opcode::Bitselect, &[I32X4, I32X4, I32X4], &[I32X4]),
-    (Opcode::Bitselect, &[I64X2, I64X2, I64X2], &[I64X2]),
-    // Select
-    (Opcode::Select, &[I8, I8, I8], &[I8]),
-    (Opcode::Select, &[I8, I16, I16], &[I16]),
-    (Opcode::Select, &[I8, I32, I32], &[I32]),
-    (Opcode::Select, &[I8, I64, I64], &[I64]),
-    (Opcode::Select, &[I8, I128, I128], &[I128]),
-    (Opcode::Select, &[I16, I8, I8], &[I8]),
-    (Opcode::Select, &[I16, I16, I16], &[I16]),
-    (Opcode::Select, &[I16, I32, I32], &[I32]),
-    (Opcode::Select, &[I16, I64, I64], &[I64]),
-    (Opcode::Select, &[I16, I128, I128], &[I128]),
-    (Opcode::Select, &[I32, I8, I8], &[I8]),
-    (Opcode::Select, &[I32, I16, I16], &[I16]),
-    (Opcode::Select, &[I32, I32, I32], &[I32]),
-    (Opcode::Select, &[I32, I64, I64], &[I64]),
-    (Opcode::Select, &[I32, I128, I128], &[I128]),
-    (Opcode::Select, &[I64, I8, I8], &[I8]),
-    (Opcode::Select, &[I64, I16, I16], &[I16]),
-    (Opcode::Select, &[I64, I32, I32], &[I32]),
-    (Opcode::Select, &[I64, I64, I64], &[I64]),
-    (Opcode::Select, &[I64, I128, I128], &[I128]),
-    (Opcode::Select, &[I128, I8, I8], &[I8]),
-    (Opcode::Select, &[I128, I16, I16], &[I16]),
-    (Opcode::Select, &[I128, I32, I32], &[I32]),
-    (Opcode::Select, &[I128, I64, I64], &[I64]),
-    (Opcode::Select, &[I128, I128, I128], &[I128]),
-    // SelectSpectreGuard
-    (Opcode::SelectSpectreGuard, &[I8, I8, I8], &[I8]),
-    (Opcode::SelectSpectreGuard, &[I8, I16, I16], &[I16]),
-    (Opcode::SelectSpectreGuard, &[I8, I32, I32], &[I32]),
-    (Opcode::SelectSpectreGuard, &[I8, I64, I64], &[I64]),
-    (Opcode::SelectSpectreGuard, &[I8, I128, I128], &[I128]),
-    (Opcode::SelectSpectreGuard, &[I16, I8, I8], &[I8]),
-    (Opcode::SelectSpectreGuard, &[I16, I16, I16], &[I16]),
-    (Opcode::SelectSpectreGuard, &[I16, I32, I32], &[I32]),
-    (Opcode::SelectSpectreGuard, &[I16, I64, I64], &[I64]),
-    (Opcode::SelectSpectreGuard, &[I16, I128, I128], &[I128]),
-    (Opcode::SelectSpectreGuard, &[I32, I8, I8], &[I8]),
-    (Opcode::SelectSpectreGuard, &[I32, I16, I16], &[I16]),
-    (Opcode::SelectSpectreGuard, &[I32, I32, I32], &[I32]),
-    (Opcode::SelectSpectreGuard, &[I32, I64, I64], &[I64]),
-    (Opcode::SelectSpectreGuard, &[I32, I128, I128], &[I128]),
-    (Opcode::SelectSpectreGuard, &[I64, I8, I8], &[I8]),
-    (Opcode::SelectSpectreGuard, &[I64, I16, I16], &[I16]),
-    (Opcode::SelectSpectreGuard, &[I64, I32, I32], &[I32]),
-    (Opcode::SelectSpectreGuard, &[I64, I64, I64], &[I64]),
-    (Opcode::SelectSpectreGuard, &[I64, I128, I128], &[I128]),
-    (Opcode::SelectSpectreGuard, &[I128, I8, I8], &[I8]),
-    (Opcode::SelectSpectreGuard, &[I128, I16, I16], &[I16]),
-    (Opcode::SelectSpectreGuard, &[I128, I32, I32], &[I32]),
-    (Opcode::SelectSpectreGuard, &[I128, I64, I64], &[I64]),
-    (Opcode::SelectSpectreGuard, &[I128, I128, I128], &[I128]),
-    // Fadd
-    (Opcode::Fadd, &[F32, F32], &[F32]),
-    (Opcode::Fadd, &[F64, F64], &[F64]),
-    (Opcode::Fadd, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Fadd, &[F64X2, F64X2], &[F64X2]),
-    // Fmul
-    (Opcode::Fmul, &[F32, F32], &[F32]),
-    (Opcode::Fmul, &[F64, F64], &[F64]),
-    (Opcode::Fmul, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Fmul, &[F64X2, F64X2], &[F64X2]),
-    // Fsub
-    (Opcode::Fsub, &[F32, F32], &[F32]),
-    (Opcode::Fsub, &[F64, F64], &[F64]),
-    (Opcode::Fsub, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Fsub, &[F64X2, F64X2], &[F64X2]),
-    // Fdiv
-    (Opcode::Fdiv, &[F32, F32], &[F32]),
-    (Opcode::Fdiv, &[F64, F64], &[F64]),
-    (Opcode::Fdiv, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Fdiv, &[F64X2, F64X2], &[F64X2]),
-    // Fmin
-    (Opcode::Fmin, &[F32, F32], &[F32]),
-    (Opcode::Fmin, &[F64, F64], &[F64]),
-    // Fmax
-    (Opcode::Fmax, &[F32, F32], &[F32]),
-    (Opcode::Fmax, &[F64, F64], &[F64]),
-    // FminPseudo
-    (Opcode::FminPseudo, &[F32, F32], &[F32]),
-    (Opcode::FminPseudo, &[F64, F64], &[F64]),
-    // FmaxPseudo
-    (Opcode::FmaxPseudo, &[F32, F32], &[F32]),
-    (Opcode::FmaxPseudo, &[F64, F64], &[F64]),
-    // Fcopysign
-    (Opcode::Fcopysign, &[F32, F32], &[F32]),
-    (Opcode::Fcopysign, &[F64, F64], &[F64]),
-    (Opcode::Fcopysign, &[F32X4, F32X4], &[F32X4]),
-    (Opcode::Fcopysign, &[F64X2, F64X2], &[F64X2]),
-    // Fma
-    (Opcode::Fma, &[F32, F32, F32], &[F32]),
-    (Opcode::Fma, &[F64, F64, F64], &[F64]),
-    (Opcode::Fma, &[F32X4, F32X4, F32X4], &[F32X4]),
-    (Opcode::Fma, &[F64X2, F64X2, F64X2], &[F64X2]),
-    // Fabs
-    (Opcode::Fabs, &[F32], &[F32]),
-    (Opcode::Fabs, &[F64], &[F64]),
-    (Opcode::Fabs, &[F32X4], &[F32X4]),
-    (Opcode::Fabs, &[F64X2], &[F64X2]),
-    // Fneg
-    (Opcode::Fneg, &[F32], &[F32]),
-    (Opcode::Fneg, &[F64], &[F64]),
-    (Opcode::Fneg, &[F32X4], &[F32X4]),
-    (Opcode::Fneg, &[F64X2], &[F64X2]),
-    // Sqrt
-    (Opcode::Sqrt, &[F32], &[F32]),
-    (Opcode::Sqrt, &[F64], &[F64]),
-    (Opcode::Sqrt, &[F32X4], &[F32X4]),
-    (Opcode::Sqrt, &[F64X2], &[F64X2]),
-    // Ceil
-    (Opcode::Ceil, &[F32], &[F32]),
-    (Opcode::Ceil, &[F64], &[F64]),
-    (Opcode::Ceil, &[F32X4], &[F32X4]),
-    (Opcode::Ceil, &[F64X2], &[F64X2]),
-    // Floor
-    (Opcode::Floor, &[F32], &[F32]),
-    (Opcode::Floor, &[F64], &[F64]),
-    (Opcode::Floor, &[F32X4], &[F32X4]),
-    (Opcode::Floor, &[F64X2], &[F64X2]),
-    // Trunc
-    (Opcode::Trunc, &[F32], &[F32]),
-    (Opcode::Trunc, &[F64], &[F64]),
-    (Opcode::Trunc, &[F32X4], &[F32X4]),
-    (Opcode::Trunc, &[F64X2], &[F64X2]),
-    // Nearest
-    (Opcode::Nearest, &[F32], &[F32]),
-    (Opcode::Nearest, &[F64], &[F64]),
-    (Opcode::Nearest, &[F32X4], &[F32X4]),
-    (Opcode::Nearest, &[F64X2], &[F64X2]),
-    // Fpromote
-    (Opcode::Fpromote, &[F32], &[F64]),
-    // Fdemote
-    (Opcode::Fdemote, &[F64], &[F32]),
-    // FcvtToUint
-    (Opcode::FcvtToUint, &[F32], &[I8]),
-    (Opcode::FcvtToUint, &[F32], &[I16]),
-    (Opcode::FcvtToUint, &[F32], &[I32]),
-    (Opcode::FcvtToUint, &[F32], &[I64]),
-    (Opcode::FcvtToUint, &[F32], &[I128]),
-    (Opcode::FcvtToUint, &[F64], &[I8]),
-    (Opcode::FcvtToUint, &[F64], &[I16]),
-    (Opcode::FcvtToUint, &[F64], &[I32]),
-    (Opcode::FcvtToUint, &[F64], &[I64]),
-    (Opcode::FcvtToUint, &[F64], &[I128]),
-    // FcvtToUintSat
-    (Opcode::FcvtToUintSat, &[F32], &[I8]),
-    (Opcode::FcvtToUintSat, &[F32], &[I16]),
-    (Opcode::FcvtToUintSat, &[F32], &[I32]),
-    (Opcode::FcvtToUintSat, &[F32], &[I64]),
-    (Opcode::FcvtToUintSat, &[F32], &[I128]),
-    (Opcode::FcvtToUintSat, &[F64], &[I8]),
-    (Opcode::FcvtToUintSat, &[F64], &[I16]),
-    (Opcode::FcvtToUintSat, &[F64], &[I32]),
-    (Opcode::FcvtToUintSat, &[F64], &[I64]),
-    (Opcode::FcvtToUintSat, &[F64], &[I128]),
-    (Opcode::FcvtToUintSat, &[F32X4], &[I32X4]),
-    (Opcode::FcvtToUintSat, &[F64X2], &[I64X2]),
-    // FcvtToSint
-    (Opcode::FcvtToSint, &[F32], &[I8]),
-    (Opcode::FcvtToSint, &[F32], &[I16]),
-    (Opcode::FcvtToSint, &[F32], &[I32]),
-    (Opcode::FcvtToSint, &[F32], &[I64]),
-    (Opcode::FcvtToSint, &[F32], &[I128]),
-    (Opcode::FcvtToSint, &[F64], &[I8]),
-    (Opcode::FcvtToSint, &[F64], &[I16]),
-    (Opcode::FcvtToSint, &[F64], &[I32]),
-    (Opcode::FcvtToSint, &[F64], &[I64]),
-    (Opcode::FcvtToSint, &[F64], &[I128]),
-    // FcvtToSintSat
-    (Opcode::FcvtToSintSat, &[F32], &[I8]),
-    (Opcode::FcvtToSintSat, &[F32], &[I16]),
-    (Opcode::FcvtToSintSat, &[F32], &[I32]),
-    (Opcode::FcvtToSintSat, &[F32], &[I64]),
-    (Opcode::FcvtToSintSat, &[F32], &[I128]),
-    (Opcode::FcvtToSintSat, &[F64], &[I8]),
-    (Opcode::FcvtToSintSat, &[F64], &[I16]),
-    (Opcode::FcvtToSintSat, &[F64], &[I32]),
-    (Opcode::FcvtToSintSat, &[F64], &[I64]),
-    (Opcode::FcvtToSintSat, &[F64], &[I128]),
-    (Opcode::FcvtToSintSat, &[F32X4], &[I32X4]),
-    (Opcode::FcvtToSintSat, &[F64X2], &[I64X2]),
-    // FcvtFromUint
-    (Opcode::FcvtFromUint, &[I8], &[F32]),
-    (Opcode::FcvtFromUint, &[I16], &[F32]),
-    (Opcode::FcvtFromUint, &[I32], &[F32]),
-    (Opcode::FcvtFromUint, &[I64], &[F32]),
-    (Opcode::FcvtFromUint, &[I128], &[F32]),
-    (Opcode::FcvtFromUint, &[I8], &[F64]),
-    (Opcode::FcvtFromUint, &[I16], &[F64]),
-    (Opcode::FcvtFromUint, &[I32], &[F64]),
-    (Opcode::FcvtFromUint, &[I64], &[F64]),
-    (Opcode::FcvtFromUint, &[I128], &[F64]),
-    (Opcode::FcvtFromUint, &[I32X4], &[F32X4]),
-    (Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
-    // FcvtFromSint
-    (Opcode::FcvtFromSint, &[I8], &[F32]),
-    (Opcode::FcvtFromSint, &[I16], &[F32]),
-    (Opcode::FcvtFromSint, &[I32], &[F32]),
-    (Opcode::FcvtFromSint, &[I64], &[F32]),
-    (Opcode::FcvtFromSint, &[I128], &[F32]),
-    (Opcode::FcvtFromSint, &[I8], &[F64]),
-    (Opcode::FcvtFromSint, &[I16], &[F64]),
-    (Opcode::FcvtFromSint, &[I32], &[F64]),
-    (Opcode::FcvtFromSint, &[I64], &[F64]),
-    (Opcode::FcvtFromSint, &[I128], &[F64]),
-    (Opcode::FcvtFromSint, &[I32X4], &[F32X4]),
-    (Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
-    // FcvtLowFromSint
-    (Opcode::FcvtLowFromSint, &[I32X4], &[F64X2]),
-    // FvpromoteLow
-    (Opcode::FvpromoteLow, &[F32X4], &[F64X2]),
-    // Fvdemote
-    (Opcode::Fvdemote, &[F64X2], &[F32X4]),
-    // Fcmp
-    (Opcode::Fcmp, &[F32, F32], &[I8]),
-    (Opcode::Fcmp, &[F64, F64], &[I8]),
-    (Opcode::Fcmp, &[F32X4, F32X4], &[I32X4]),
-    (Opcode::Fcmp, &[F64X2, F64X2], &[I64X2]),
-    // Icmp
-    (Opcode::Icmp, &[I8, I8], &[I8]),
-    (Opcode::Icmp, &[I16, I16], &[I8]),
-    (Opcode::Icmp, &[I32, I32], &[I8]),
-    (Opcode::Icmp, &[I64, I64], &[I8]),
-    (Opcode::Icmp, &[I128, I128], &[I8]),
-    (Opcode::Icmp, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::Icmp, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::Icmp, &[I32X4, I32X4], &[I32X4]),
-    (Opcode::Icmp, &[I64X2, I64X2], &[I64X2]),
-    // Fence
-    (Opcode::Fence, &[], &[]),
-    // Stack Access
-    (Opcode::StackStore, &[I8], &[]),
-    (Opcode::StackStore, &[I16], &[]),
-    (Opcode::StackStore, &[I32], &[]),
-    (Opcode::StackStore, &[I64], &[]),
-    (Opcode::StackStore, &[I128], &[]),
-    (Opcode::StackStore, &[F32], &[]),
-    (Opcode::StackStore, &[F64], &[]),
-    (Opcode::StackStore, &[I8X16], &[]),
-    (Opcode::StackStore, &[I16X8], &[]),
-    (Opcode::StackStore, &[I32X4], &[]),
-    (Opcode::StackStore, &[I64X2], &[]),
-    (Opcode::StackStore, &[F32X4], &[]),
-    (Opcode::StackStore, &[F64X2], &[]),
-    (Opcode::StackLoad, &[], &[I8]),
-    (Opcode::StackLoad, &[], &[I16]),
-    (Opcode::StackLoad, &[], &[I32]),
-    (Opcode::StackLoad, &[], &[I64]),
-    (Opcode::StackLoad, &[], &[I128]),
-    (Opcode::StackLoad, &[], &[F32]),
-    (Opcode::StackLoad, &[], &[F64]),
-    (Opcode::StackLoad, &[], &[I8X16]),
-    (Opcode::StackLoad, &[], &[I16X8]),
-    (Opcode::StackLoad, &[], &[I32X4]),
-    (Opcode::StackLoad, &[], &[I64X2]),
-    (Opcode::StackLoad, &[], &[F32X4]),
-    (Opcode::StackLoad, &[], &[F64X2]),
-    // Loads
-    (Opcode::Load, &[], &[I8]),
-    (Opcode::Load, &[], &[I16]),
-    (Opcode::Load, &[], &[I32]),
-    (Opcode::Load, &[], &[I64]),
-    (Opcode::Load, &[], &[I128]),
-    (Opcode::Load, &[], &[F32]),
-    (Opcode::Load, &[], &[F64]),
-    (Opcode::Load, &[], &[I8X16]),
-    (Opcode::Load, &[], &[I16X8]),
-    (Opcode::Load, &[], &[I32X4]),
-    (Opcode::Load, &[], &[I64X2]),
-    (Opcode::Load, &[], &[F32X4]),
-    (Opcode::Load, &[], &[F64X2]),
-    // Special Loads
-    (Opcode::Uload8, &[], &[I16]),
-    (Opcode::Uload8, &[], &[I32]),
-    (Opcode::Uload8, &[], &[I64]),
-    (Opcode::Uload16, &[], &[I32]),
-    (Opcode::Uload16, &[], &[I64]),
-    (Opcode::Uload32, &[], &[I64]),
-    (Opcode::Sload8, &[], &[I16]),
-    (Opcode::Sload8, &[], &[I32]),
-    (Opcode::Sload8, &[], &[I64]),
-    (Opcode::Sload16, &[], &[I32]),
-    (Opcode::Sload16, &[], &[I64]),
-    (Opcode::Sload32, &[], &[I64]),
-    // TODO: Unimplemented in the interpreter
-    // Opcode::Uload8x8
-    // Opcode::Sload8x8
-    // Opcode::Uload16x4
-    // Opcode::Sload16x4
-    // Opcode::Uload32x2
-    // Opcode::Sload32x2
-    // AtomicLoad
-    (Opcode::AtomicLoad, &[], &[I8]),
-    (Opcode::AtomicLoad, &[], &[I16]),
-    (Opcode::AtomicLoad, &[], &[I32]),
-    (Opcode::AtomicLoad, &[], &[I64]),
-    // Stores
-    (Opcode::Store, &[I8], &[]),
-    (Opcode::Store, &[I16], &[]),
-    (Opcode::Store, &[I32], &[]),
-    (Opcode::Store, &[I64], &[]),
-    (Opcode::Store, &[I128], &[]),
-    (Opcode::Store, &[F32], &[]),
-    (Opcode::Store, &[F64], &[]),
-    (Opcode::Store, &[I8X16], &[]),
-    (Opcode::Store, &[I16X8], &[]),
-    (Opcode::Store, &[I32X4], &[]),
-    (Opcode::Store, &[I64X2], &[]),
-    (Opcode::Store, &[F32X4], &[]),
-    (Opcode::Store, &[F64X2], &[]),
-    // Special Stores
-    (Opcode::Istore8, &[I16], &[]),
-    (Opcode::Istore8, &[I32], &[]),
-    (Opcode::Istore8, &[I64], &[]),
-    (Opcode::Istore16, &[I32], &[]),
-    (Opcode::Istore16, &[I64], &[]),
-    (Opcode::Istore32, &[I64], &[]),
-    // AtomicStore
-    (Opcode::AtomicStore, &[I8], &[]),
-    (Opcode::AtomicStore, &[I16], &[]),
-    (Opcode::AtomicStore, &[I32], &[]),
-    (Opcode::AtomicStore, &[I64], &[]),
-    // AtomicRMW
-    (Opcode::AtomicRmw, &[I8, I8], &[I8]),
-    (Opcode::AtomicRmw, &[I16, I16], &[I16]),
-    (Opcode::AtomicRmw, &[I32, I32], &[I32]),
-    (Opcode::AtomicRmw, &[I64, I64], &[I64]),
-    // AtomicCas
-    (Opcode::AtomicCas, &[I8, I8], &[I8]),
-    (Opcode::AtomicCas, &[I16, I16], &[I16]),
-    (Opcode::AtomicCas, &[I32, I32], &[I32]),
-    (Opcode::AtomicCas, &[I64, I64], &[I64]),
-    // Bitcast
-    (Opcode::Bitcast, &[F32], &[I32]),
-    (Opcode::Bitcast, &[I32], &[F32]),
-    (Opcode::Bitcast, &[F64], &[I64]),
-    (Opcode::Bitcast, &[I64], &[F64]),
-    // Shuffle
-    (Opcode::Shuffle, &[I8X16, I8X16], &[I8X16]),
-    // Swizzle
-    (Opcode::Swizzle, &[I8X16, I8X16], &[I8X16]),
-    // Splat
-    (Opcode::Splat, &[I8], &[I8X16]),
-    (Opcode::Splat, &[I16], &[I16X8]),
-    (Opcode::Splat, &[I32], &[I32X4]),
-    (Opcode::Splat, &[I64], &[I64X2]),
-    (Opcode::Splat, &[F32], &[F32X4]),
-    (Opcode::Splat, &[F64], &[F64X2]),
-    // Insert Lane
-    (Opcode::Insertlane, &[I8X16, I8], &[I8X16]),
-    (Opcode::Insertlane, &[I16X8, I16], &[I16X8]),
-    (Opcode::Insertlane, &[I32X4, I32], &[I32X4]),
-    (Opcode::Insertlane, &[I64X2, I64], &[I64X2]),
-    (Opcode::Insertlane, &[F32X4, F32], &[F32X4]),
-    (Opcode::Insertlane, &[F64X2, F64], &[F64X2]),
-    // Extract Lane
-    (Opcode::Extractlane, &[I8X16], &[I8]),
-    (Opcode::Extractlane, &[I16X8], &[I16]),
-    (Opcode::Extractlane, &[I32X4], &[I32]),
-    (Opcode::Extractlane, &[I64X2], &[I64]),
-    (Opcode::Extractlane, &[F32X4], &[F32]),
-    (Opcode::Extractlane, &[F64X2], &[F64]),
-    // Snarrow
-    (Opcode::Snarrow, &[I64X2, I64X2], &[I32X4]),
-    (Opcode::Snarrow, &[I32X4, I32X4], &[I16X8]),
-    (Opcode::Snarrow, &[I16X8, I16X8], &[I8X16]),
-    // Unarrow
-    (Opcode::Unarrow, &[I64X2, I64X2], &[I32X4]),
-    (Opcode::Unarrow, &[I32X4, I32X4], &[I16X8]),
-    (Opcode::Unarrow, &[I16X8, I16X8], &[I8X16]),
-    // Uunarrow
-    (Opcode::Uunarrow, &[I64X2, I64X2], &[I32X4]),
-    (Opcode::Uunarrow, &[I32X4, I32X4], &[I16X8]),
-    (Opcode::Uunarrow, &[I16X8, I16X8], &[I8X16]),
-    // VhighBits
-    (Opcode::VhighBits, &[I8X16], &[I8]),
-    (Opcode::VhighBits, &[I16X8], &[I8]),
-    (Opcode::VhighBits, &[I32X4], &[I8]),
-    (Opcode::VhighBits, &[I64X2], &[I8]),
-    // VanyTrue
-    (Opcode::VanyTrue, &[I8X16], &[I8]),
-    (Opcode::VanyTrue, &[I16X8], &[I8]),
-    (Opcode::VanyTrue, &[I32X4], &[I8]),
-    (Opcode::VanyTrue, &[I64X2], &[I8]),
-    // SwidenLow
-    (Opcode::SwidenLow, &[I8X16], &[I16X8]),
-    (Opcode::SwidenLow, &[I16X8], &[I32X4]),
-    (Opcode::SwidenLow, &[I32X4], &[I64X2]),
-    // SwidenHigh
-    (Opcode::SwidenHigh, &[I8X16], &[I16X8]),
-    (Opcode::SwidenHigh, &[I16X8], &[I32X4]),
-    (Opcode::SwidenHigh, &[I32X4], &[I64X2]),
-    // UwidenLow
-    (Opcode::UwidenLow, &[I8X16], &[I16X8]),
-    (Opcode::UwidenLow, &[I16X8], &[I32X4]),
-    (Opcode::UwidenLow, &[I32X4], &[I64X2]),
-    // UwidenHigh
-    (Opcode::UwidenHigh, &[I8X16], &[I16X8]),
-    (Opcode::UwidenHigh, &[I16X8], &[I32X4]),
-    (Opcode::UwidenHigh, &[I32X4], &[I64X2]),
-    // SqmulRoundSat
-    (Opcode::SqmulRoundSat, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::SqmulRoundSat, &[I32X4, I32X4], &[I32X4]),
-    // IaddPairwise
-    (Opcode::IaddPairwise, &[I8X16, I8X16], &[I8X16]),
-    (Opcode::IaddPairwise, &[I16X8, I16X8], &[I16X8]),
-    (Opcode::IaddPairwise, &[I32X4, I32X4], &[I32X4]),
-    // Integer Consts
-    (Opcode::Iconst, &[], &[I8]),
-    (Opcode::Iconst, &[], &[I16]),
-    (Opcode::Iconst, &[], &[I32]),
-    (Opcode::Iconst, &[], &[I64]),
-    // Float Consts
-    (Opcode::F32const, &[], &[F32]),
-    (Opcode::F64const, &[], &[F64]),
-    // Vector Consts
-    (Opcode::Vconst, &[], &[I8X16]),
-    (Opcode::Vconst, &[], &[I16X8]),
-    (Opcode::Vconst, &[], &[I32X4]),
-    (Opcode::Vconst, &[], &[I64X2]),
-    (Opcode::Vconst, &[], &[F32X4]),
-    (Opcode::Vconst, &[], &[F64X2]),
-    // Call
-    (Opcode::Call, &[], &[]),
-];
+type OpcodeSignature = (Opcode, Vec<Type>, Vec<Type>);
+
+static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
+    let types = &[
+        I8, I16, I32, I64, I128, // Scalar Integers
+        F32, F64, // Scalar Floats
+        I8X16, I16X8, I32X4, I64X2, // SIMD Integers
+        F32X4, F64X2, // SIMD Floats
+    ];
+
+    Opcode::all()
+        .iter()
+        .filter(|op| {
+            match op {
+                // Control flow opcodes should not be generated through `generate_instructions`.
+                Opcode::BrTable | Opcode::Brif | Opcode::Jump | Opcode::Return => false,
+
+                // Constants are generated outside of `generate_instructions`
+                Opcode::Iconst => false,
+
+                // TODO: extract_vector raises exceptions during return type generation becuase it
+                // uses dynamic vectors.
+                Opcode::ExtractVector => false,
+
+                _ => true,
+            }
+        })
+        .flat_map(|op| {
+            let constraints = op.constraints();
+
+            let ctrl_types = if let Some(ctrls) = constraints.ctrl_typeset() {
+                Vec::from_iter(types.iter().copied().filter(|ty| ctrls.contains(*ty)))
+            } else {
+                vec![INVALID]
+            };
+
+            ctrl_types.into_iter().flat_map(move |ctrl_type| {
+                let rets = Vec::from_iter(
+                    (0..constraints.num_fixed_results())
+                        .map(|i| constraints.result_type(i, ctrl_type)),
+                );
+
+                // Cols is a vector whose length will match `num_fixed_value_arguments`, and whose
+                // elements will be vectors of types that are valid for that fixed argument
+                // position.
+                let mut cols = vec![];
+
+                for i in 0..constraints.num_fixed_value_arguments() {
+                    match constraints.value_argument_constraint(i, ctrl_type) {
+                        ResolvedConstraint::Bound(ty) => cols.push(Vec::from([ty])),
+                        ResolvedConstraint::Free(tys) => cols.push(Vec::from_iter(
+                            types.iter().copied().filter(|ty| tys.contains(*ty)),
+                        )),
+                    }
+                }
+
+                // Generate the cartesian product of cols to produce a vector of argument lists,
+                // argss. The argss vector is seeded with the empty argument list, so there's an
+                // initial value to be extended in the loop below.
+                let mut argss = vec![vec![]];
+                let mut cols = cols.as_slice();
+                while let Some((col, rest)) = cols.split_last() {
+                    cols = rest;
+
+                    let mut next = vec![];
+                    for current in argss.iter() {
+                        // Extend the front of each argument candidate with every type in `col`.
+                        for ty in col {
+                            let mut args = vec![*ty];
+                            args.extend_from_slice(&current);
+                            next.push(args);
+                        }
+                    }
+
+                    let _ = std::mem::replace(&mut argss, next);
+                }
+
+                argss.into_iter().map(move |args| (*op, args, rets.clone()))
+            })
+        })
+        .filter(|(op, args, rets)| {
+            // These op/signature combinations need to be vetted
+            exceptions!(
+                op,
+                args.as_slice(),
+                rets.as_slice(),
+                (Opcode::Debugtrap),
+                (Opcode::Trap),
+                (Opcode::Trapz),
+                (Opcode::ResumableTrap),
+                (Opcode::Trapnz),
+                (Opcode::ResumableTrapnz),
+                (Opcode::CallIndirect),
+                (Opcode::ReturnCall),
+                (Opcode::ReturnCallIndirect),
+                (Opcode::FuncAddr),
+                (Opcode::X86Pshufb),
+                (Opcode::AvgRound),
+                (Opcode::Uload8x8),
+                (Opcode::Sload8x8),
+                (Opcode::Uload16x4),
+                (Opcode::Sload16x4),
+                (Opcode::Uload32x2),
+                (Opcode::Sload32x2),
+                (Opcode::StackAddr),
+                (Opcode::DynamicStackLoad),
+                (Opcode::DynamicStackStore),
+                (Opcode::DynamicStackAddr),
+                (Opcode::GlobalValue),
+                (Opcode::SymbolValue),
+                (Opcode::TlsValue),
+                (Opcode::GetPinnedReg),
+                (Opcode::SetPinnedReg),
+                (Opcode::GetFramePointer),
+                (Opcode::GetStackPointer),
+                (Opcode::GetReturnAddress),
+                (Opcode::TableAddr),
+                (Opcode::Null),
+                (Opcode::X86Blendv),
+                (Opcode::VallTrue),
+                (Opcode::IcmpImm),
+                (Opcode::X86Pmulhrsw),
+                (Opcode::IaddImm),
+                (Opcode::ImulImm),
+                (Opcode::UdivImm),
+                (Opcode::SdivImm),
+                (Opcode::UremImm),
+                (Opcode::SremImm),
+                (Opcode::IrsubImm),
+                (Opcode::IaddCin),
+                (Opcode::IaddCarry),
+                (Opcode::UaddOverflowTrap),
+                (Opcode::IsubBin),
+                (Opcode::IsubBout),
+                (Opcode::IsubBorrow),
+                (Opcode::BandImm),
+                (Opcode::BorImm),
+                (Opcode::BxorImm),
+                (Opcode::RotlImm),
+                (Opcode::RotrImm),
+                (Opcode::IshlImm),
+                (Opcode::UshrImm),
+                (Opcode::SshrImm),
+                (Opcode::IsNull),
+                (Opcode::IsInvalid),
+                (Opcode::ScalarToVector),
+                (Opcode::X86Pmaddubsw),
+                (Opcode::X86Cvtt2dq),
+                (Opcode::Select, &[I8, F32, F32], &[F32]),
+                (Opcode::Select, &[I16, F32, F32], &[F32]),
+                (Opcode::Select, &[I32, F32, F32], &[F32]),
+                (Opcode::Select, &[I64, F32, F32], &[F32]),
+                (Opcode::Select, &[I128, F32, F32], &[F32]),
+                (Opcode::Select, &[I8, F64, F64], &[F64]),
+                (Opcode::Select, &[I16, F64, F64], &[F64]),
+                (Opcode::Select, &[I32, F64, F64], &[F64]),
+                (Opcode::Select, &[I64, F64, F64], &[F64]),
+                (Opcode::Select, &[I128, F64, F64], &[F64]),
+                (Opcode::Select, &[I8, I8X16, I8X16], &[I8X16]),
+                (Opcode::Select, &[I16, I8X16, I8X16], &[I8X16]),
+                (Opcode::Select, &[I32, I8X16, I8X16], &[I8X16]),
+                (Opcode::Select, &[I64, I8X16, I8X16], &[I8X16]),
+                (Opcode::Select, &[I128, I8X16, I8X16], &[I8X16]),
+                (Opcode::Select, &[I8, I16X8, I16X8], &[I16X8]),
+                (Opcode::Select, &[I16, I16X8, I16X8], &[I16X8]),
+                (Opcode::Select, &[I32, I16X8, I16X8], &[I16X8]),
+                (Opcode::Select, &[I64, I16X8, I16X8], &[I16X8]),
+                (Opcode::Select, &[I128, I16X8, I16X8], &[I16X8]),
+                (Opcode::Select, &[I8, I32X4, I32X4], &[I32X4]),
+                (Opcode::Select, &[I16, I32X4, I32X4], &[I32X4]),
+                (Opcode::Select, &[I32, I32X4, I32X4], &[I32X4]),
+                (Opcode::Select, &[I64, I32X4, I32X4], &[I32X4]),
+                (Opcode::Select, &[I128, I32X4, I32X4], &[I32X4]),
+                (Opcode::Select, &[I8, I64X2, I64X2], &[I64X2]),
+                (Opcode::Select, &[I16, I64X2, I64X2], &[I64X2]),
+                (Opcode::Select, &[I32, I64X2, I64X2], &[I64X2]),
+                (Opcode::Select, &[I64, I64X2, I64X2], &[I64X2]),
+                (Opcode::Select, &[I128, I64X2, I64X2], &[I64X2]),
+                (Opcode::Select, &[I8, F32X4, F32X4], &[F32X4]),
+                (Opcode::Select, &[I16, F32X4, F32X4], &[F32X4]),
+                (Opcode::Select, &[I32, F32X4, F32X4], &[F32X4]),
+                (Opcode::Select, &[I64, F32X4, F32X4], &[F32X4]),
+                (Opcode::Select, &[I128, F32X4, F32X4], &[F32X4]),
+                (Opcode::Select, &[I8, F64X2, F64X2], &[F64X2]),
+                (Opcode::Select, &[I16, F64X2, F64X2], &[F64X2]),
+                (Opcode::Select, &[I32, F64X2, F64X2], &[F64X2]),
+                (Opcode::Select, &[I64, F64X2, F64X2], &[F64X2]),
+                (Opcode::Select, &[I128, F64X2, F64X2], &[F64X2]),
+                (Opcode::SelectSpectreGuard, &[I8, F32, F32], &[F32]),
+                (Opcode::SelectSpectreGuard, &[I16, F32, F32], &[F32]),
+                (Opcode::SelectSpectreGuard, &[I32, F32, F32], &[F32]),
+                (Opcode::SelectSpectreGuard, &[I64, F32, F32], &[F32]),
+                (Opcode::SelectSpectreGuard, &[I128, F32, F32], &[F32]),
+                (Opcode::SelectSpectreGuard, &[I8, F64, F64], &[F64]),
+                (Opcode::SelectSpectreGuard, &[I16, F64, F64], &[F64]),
+                (Opcode::SelectSpectreGuard, &[I32, F64, F64], &[F64]),
+                (Opcode::SelectSpectreGuard, &[I64, F64, F64], &[F64]),
+                (Opcode::SelectSpectreGuard, &[I128, F64, F64], &[F64]),
+                (Opcode::SelectSpectreGuard, &[I8, I8X16, I8X16], &[I8X16]),
+                (Opcode::SelectSpectreGuard, &[I16, I8X16, I8X16], &[I8X16]),
+                (Opcode::SelectSpectreGuard, &[I32, I8X16, I8X16], &[I8X16]),
+                (Opcode::SelectSpectreGuard, &[I64, I8X16, I8X16], &[I8X16]),
+                (Opcode::SelectSpectreGuard, &[I128, I8X16, I8X16], &[I8X16]),
+                (Opcode::SelectSpectreGuard, &[I8, I16X8, I16X8], &[I16X8]),
+                (Opcode::SelectSpectreGuard, &[I16, I16X8, I16X8], &[I16X8]),
+                (Opcode::SelectSpectreGuard, &[I32, I16X8, I16X8], &[I16X8]),
+                (Opcode::SelectSpectreGuard, &[I64, I16X8, I16X8], &[I16X8]),
+                (Opcode::SelectSpectreGuard, &[I128, I16X8, I16X8], &[I16X8]),
+                (Opcode::SelectSpectreGuard, &[I8, I32X4, I32X4], &[I32X4]),
+                (Opcode::SelectSpectreGuard, &[I16, I32X4, I32X4], &[I32X4]),
+                (Opcode::SelectSpectreGuard, &[I32, I32X4, I32X4], &[I32X4]),
+                (Opcode::SelectSpectreGuard, &[I64, I32X4, I32X4], &[I32X4]),
+                (Opcode::SelectSpectreGuard, &[I128, I32X4, I32X4], &[I32X4]),
+                (Opcode::SelectSpectreGuard, &[I8, I64X2, I64X2], &[I64X2]),
+                (Opcode::SelectSpectreGuard, &[I16, I64X2, I64X2], &[I64X2]),
+                (Opcode::SelectSpectreGuard, &[I32, I64X2, I64X2], &[I64X2]),
+                (Opcode::SelectSpectreGuard, &[I64, I64X2, I64X2], &[I64X2]),
+                (Opcode::SelectSpectreGuard, &[I128, I64X2, I64X2], &[I64X2]),
+                (Opcode::SelectSpectreGuard, &[I8, F32X4, F32X4], &[F32X4]),
+                (Opcode::SelectSpectreGuard, &[I16, F32X4, F32X4], &[F32X4]),
+                (Opcode::SelectSpectreGuard, &[I32, F32X4, F32X4], &[F32X4]),
+                (Opcode::SelectSpectreGuard, &[I64, F32X4, F32X4], &[F32X4]),
+                (Opcode::SelectSpectreGuard, &[I128, F32X4, F32X4], &[F32X4]),
+                (Opcode::SelectSpectreGuard, &[I8, F64X2, F64X2], &[F64X2]),
+                (Opcode::SelectSpectreGuard, &[I16, F64X2, F64X2], &[F64X2]),
+                (Opcode::SelectSpectreGuard, &[I32, F64X2, F64X2], &[F64X2]),
+                (Opcode::SelectSpectreGuard, &[I64, F64X2, F64X2], &[F64X2]),
+                (Opcode::SelectSpectreGuard, &[I128, F64X2, F64X2], &[F64X2]),
+                (Opcode::Bitselect, &[F32, F32, F32], &[F32]),
+                (Opcode::Bitselect, &[F64, F64, F64], &[F64]),
+                (Opcode::Bitselect, &[F32X4, F32X4, F32X4], &[F32X4]),
+                (Opcode::Bitselect, &[F64X2, F64X2, F64X2], &[F64X2]),
+                (Opcode::VanyTrue, &[F32X4], &[I8]),
+                (Opcode::VanyTrue, &[F64X2], &[I8]),
+                (Opcode::VhighBits, &[F32X4], &[I8]),
+                (Opcode::VhighBits, &[F64X2], &[I8]),
+                (Opcode::VhighBits, &[I8X16], &[I16]),
+                (Opcode::VhighBits, &[I16X8], &[I16]),
+                (Opcode::VhighBits, &[I32X4], &[I16]),
+                (Opcode::VhighBits, &[I64X2], &[I16]),
+                (Opcode::VhighBits, &[F32X4], &[I16]),
+                (Opcode::VhighBits, &[F64X2], &[I16]),
+                (Opcode::VhighBits, &[I8X16], &[I32]),
+                (Opcode::VhighBits, &[I16X8], &[I32]),
+                (Opcode::VhighBits, &[I32X4], &[I32]),
+                (Opcode::VhighBits, &[I64X2], &[I32]),
+                (Opcode::VhighBits, &[F32X4], &[I32]),
+                (Opcode::VhighBits, &[F64X2], &[I32]),
+                (Opcode::VhighBits, &[I8X16], &[I64]),
+                (Opcode::VhighBits, &[I16X8], &[I64]),
+                (Opcode::VhighBits, &[I32X4], &[I64]),
+                (Opcode::VhighBits, &[I64X2], &[I64]),
+                (Opcode::VhighBits, &[F32X4], &[I64]),
+                (Opcode::VhighBits, &[F64X2], &[I64]),
+                (Opcode::VhighBits, &[I8X16], &[I128]),
+                (Opcode::VhighBits, &[I16X8], &[I128]),
+                (Opcode::VhighBits, &[I32X4], &[I128]),
+                (Opcode::VhighBits, &[I64X2], &[I128]),
+                (Opcode::VhighBits, &[F32X4], &[I128]),
+                (Opcode::VhighBits, &[F64X2], &[I128]),
+                (Opcode::VhighBits, &[I8X16], &[I8X16]),
+                (Opcode::VhighBits, &[I16X8], &[I8X16]),
+                (Opcode::VhighBits, &[I32X4], &[I8X16]),
+                (Opcode::VhighBits, &[I64X2], &[I8X16]),
+                (Opcode::VhighBits, &[F32X4], &[I8X16]),
+                (Opcode::VhighBits, &[F64X2], &[I8X16]),
+                (Opcode::VhighBits, &[I8X16], &[I16X8]),
+                (Opcode::VhighBits, &[I16X8], &[I16X8]),
+                (Opcode::VhighBits, &[I32X4], &[I16X8]),
+                (Opcode::VhighBits, &[I64X2], &[I16X8]),
+                (Opcode::VhighBits, &[F32X4], &[I16X8]),
+                (Opcode::VhighBits, &[F64X2], &[I16X8]),
+                (Opcode::VhighBits, &[I8X16], &[I32X4]),
+                (Opcode::VhighBits, &[I16X8], &[I32X4]),
+                (Opcode::VhighBits, &[I32X4], &[I32X4]),
+                (Opcode::VhighBits, &[I64X2], &[I32X4]),
+                (Opcode::VhighBits, &[F32X4], &[I32X4]),
+                (Opcode::VhighBits, &[F64X2], &[I32X4]),
+                (Opcode::VhighBits, &[I8X16], &[I64X2]),
+                (Opcode::VhighBits, &[I16X8], &[I64X2]),
+                (Opcode::VhighBits, &[I32X4], &[I64X2]),
+                (Opcode::VhighBits, &[I64X2], &[I64X2]),
+                (Opcode::VhighBits, &[F32X4], &[I64X2]),
+                (Opcode::VhighBits, &[F64X2], &[I64X2]),
+                (Opcode::Ineg, &[I8X16], &[I8X16]),
+                (Opcode::Ineg, &[I16X8], &[I16X8]),
+                (Opcode::Ineg, &[I32X4], &[I32X4]),
+                (Opcode::Ineg, &[I64X2], &[I64X2]),
+                (Opcode::Umulhi, &[I128, I128], &[I128]),
+                (Opcode::Smulhi, &[I128, I128], &[I128]),
+                // https://github.com/bytecodealliance/wasmtime/issues/6073
+                (Opcode::Iconcat, &[I32, I32], &[I64]),
+                (Opcode::Iconcat, &[I16, I16], &[I32]),
+                (Opcode::Iconcat, &[I8, I8], &[I16]),
+                // https://github.com/bytecodealliance/wasmtime/issues/6073
+                (Opcode::Isplit, &[I64], &[I32, I32]),
+                (Opcode::Isplit, &[I32], &[I16, I16]),
+                (Opcode::Isplit, &[I16], &[I8, I8]),
+                (Opcode::Rotl, &[I8X16, I8], &[I8X16]),
+                (Opcode::Rotl, &[I8X16, I16], &[I8X16]),
+                (Opcode::Rotl, &[I8X16, I32], &[I8X16]),
+                (Opcode::Rotl, &[I8X16, I64], &[I8X16]),
+                (Opcode::Rotl, &[I8X16, I128], &[I8X16]),
+                (Opcode::Rotl, &[I16X8, I8], &[I16X8]),
+                (Opcode::Rotl, &[I16X8, I16], &[I16X8]),
+                (Opcode::Rotl, &[I16X8, I32], &[I16X8]),
+                (Opcode::Rotl, &[I16X8, I64], &[I16X8]),
+                (Opcode::Rotl, &[I16X8, I128], &[I16X8]),
+                (Opcode::Rotl, &[I32X4, I8], &[I32X4]),
+                (Opcode::Rotl, &[I32X4, I16], &[I32X4]),
+                (Opcode::Rotl, &[I32X4, I32], &[I32X4]),
+                (Opcode::Rotl, &[I32X4, I64], &[I32X4]),
+                (Opcode::Rotl, &[I32X4, I128], &[I32X4]),
+                (Opcode::Rotl, &[I64X2, I8], &[I64X2]),
+                (Opcode::Rotl, &[I64X2, I16], &[I64X2]),
+                (Opcode::Rotl, &[I64X2, I32], &[I64X2]),
+                (Opcode::Rotl, &[I64X2, I64], &[I64X2]),
+                (Opcode::Rotl, &[I64X2, I128], &[I64X2]),
+                (Opcode::Rotr, &[I8X16, I8], &[I8X16]),
+                (Opcode::Rotr, &[I8X16, I16], &[I8X16]),
+                (Opcode::Rotr, &[I8X16, I32], &[I8X16]),
+                (Opcode::Rotr, &[I8X16, I64], &[I8X16]),
+                (Opcode::Rotr, &[I8X16, I128], &[I8X16]),
+                (Opcode::Rotr, &[I16X8, I8], &[I16X8]),
+                (Opcode::Rotr, &[I16X8, I16], &[I16X8]),
+                (Opcode::Rotr, &[I16X8, I32], &[I16X8]),
+                (Opcode::Rotr, &[I16X8, I64], &[I16X8]),
+                (Opcode::Rotr, &[I16X8, I128], &[I16X8]),
+                (Opcode::Rotr, &[I32X4, I8], &[I32X4]),
+                (Opcode::Rotr, &[I32X4, I16], &[I32X4]),
+                (Opcode::Rotr, &[I32X4, I32], &[I32X4]),
+                (Opcode::Rotr, &[I32X4, I64], &[I32X4]),
+                (Opcode::Rotr, &[I32X4, I128], &[I32X4]),
+                (Opcode::Rotr, &[I64X2, I8], &[I64X2]),
+                (Opcode::Rotr, &[I64X2, I16], &[I64X2]),
+                (Opcode::Rotr, &[I64X2, I32], &[I64X2]),
+                (Opcode::Rotr, &[I64X2, I64], &[I64X2]),
+                (Opcode::Rotr, &[I64X2, I128], &[I64X2]),
+                (Opcode::Ishl, &[I8X16, I8], &[I8X16]),
+                (Opcode::Ishl, &[I8X16, I16], &[I8X16]),
+                (Opcode::Ishl, &[I8X16, I32], &[I8X16]),
+                (Opcode::Ishl, &[I8X16, I64], &[I8X16]),
+                (Opcode::Ishl, &[I8X16, I128], &[I8X16]),
+                (Opcode::Ishl, &[I16X8, I8], &[I16X8]),
+                (Opcode::Ishl, &[I16X8, I16], &[I16X8]),
+                (Opcode::Ishl, &[I16X8, I32], &[I16X8]),
+                (Opcode::Ishl, &[I16X8, I64], &[I16X8]),
+                (Opcode::Ishl, &[I16X8, I128], &[I16X8]),
+                (Opcode::Ishl, &[I32X4, I8], &[I32X4]),
+                (Opcode::Ishl, &[I32X4, I16], &[I32X4]),
+                (Opcode::Ishl, &[I32X4, I32], &[I32X4]),
+                (Opcode::Ishl, &[I32X4, I64], &[I32X4]),
+                (Opcode::Ishl, &[I32X4, I128], &[I32X4]),
+                (Opcode::Ishl, &[I64X2, I8], &[I64X2]),
+                (Opcode::Ishl, &[I64X2, I16], &[I64X2]),
+                (Opcode::Ishl, &[I64X2, I32], &[I64X2]),
+                (Opcode::Ishl, &[I64X2, I64], &[I64X2]),
+                (Opcode::Ishl, &[I64X2, I128], &[I64X2]),
+                (Opcode::Ushr, &[I8X16, I8], &[I8X16]),
+                (Opcode::Ushr, &[I8X16, I16], &[I8X16]),
+                (Opcode::Ushr, &[I8X16, I32], &[I8X16]),
+                (Opcode::Ushr, &[I8X16, I64], &[I8X16]),
+                (Opcode::Ushr, &[I8X16, I128], &[I8X16]),
+                (Opcode::Ushr, &[I16X8, I8], &[I16X8]),
+                (Opcode::Ushr, &[I16X8, I16], &[I16X8]),
+                (Opcode::Ushr, &[I16X8, I32], &[I16X8]),
+                (Opcode::Ushr, &[I16X8, I64], &[I16X8]),
+                (Opcode::Ushr, &[I16X8, I128], &[I16X8]),
+                (Opcode::Ushr, &[I32X4, I8], &[I32X4]),
+                (Opcode::Ushr, &[I32X4, I16], &[I32X4]),
+                (Opcode::Ushr, &[I32X4, I32], &[I32X4]),
+                (Opcode::Ushr, &[I32X4, I64], &[I32X4]),
+                (Opcode::Ushr, &[I32X4, I128], &[I32X4]),
+                (Opcode::Ushr, &[I64X2, I8], &[I64X2]),
+                (Opcode::Ushr, &[I64X2, I16], &[I64X2]),
+                (Opcode::Ushr, &[I64X2, I32], &[I64X2]),
+                (Opcode::Ushr, &[I64X2, I64], &[I64X2]),
+                (Opcode::Ushr, &[I64X2, I128], &[I64X2]),
+                (Opcode::Sshr, &[I8X16, I8], &[I8X16]),
+                (Opcode::Sshr, &[I8X16, I16], &[I8X16]),
+                (Opcode::Sshr, &[I8X16, I32], &[I8X16]),
+                (Opcode::Sshr, &[I8X16, I64], &[I8X16]),
+                (Opcode::Sshr, &[I8X16, I128], &[I8X16]),
+                (Opcode::Sshr, &[I16X8, I8], &[I16X8]),
+                (Opcode::Sshr, &[I16X8, I16], &[I16X8]),
+                (Opcode::Sshr, &[I16X8, I32], &[I16X8]),
+                (Opcode::Sshr, &[I16X8, I64], &[I16X8]),
+                (Opcode::Sshr, &[I16X8, I128], &[I16X8]),
+                (Opcode::Sshr, &[I32X4, I8], &[I32X4]),
+                (Opcode::Sshr, &[I32X4, I16], &[I32X4]),
+                (Opcode::Sshr, &[I32X4, I32], &[I32X4]),
+                (Opcode::Sshr, &[I32X4, I64], &[I32X4]),
+                (Opcode::Sshr, &[I32X4, I128], &[I32X4]),
+                (Opcode::Sshr, &[I64X2, I8], &[I64X2]),
+                (Opcode::Sshr, &[I64X2, I16], &[I64X2]),
+                (Opcode::Sshr, &[I64X2, I32], &[I64X2]),
+                (Opcode::Sshr, &[I64X2, I64], &[I64X2]),
+                (Opcode::Sshr, &[I64X2, I128], &[I64X2]),
+                (Opcode::Fmin, &[F32X4, F32X4], &[F32X4]),
+                (Opcode::Fmin, &[F64X2, F64X2], &[F64X2]),
+                (Opcode::FminPseudo, &[F32X4, F32X4], &[F32X4]),
+                (Opcode::FminPseudo, &[F64X2, F64X2], &[F64X2]),
+                (Opcode::Fmax, &[F32X4, F32X4], &[F32X4]),
+                (Opcode::Fmax, &[F64X2, F64X2], &[F64X2]),
+                (Opcode::FmaxPseudo, &[F32X4, F32X4], &[F32X4]),
+                (Opcode::FmaxPseudo, &[F64X2, F64X2], &[F64X2]),
+                (Opcode::Bitcast, &[I8], &[I8]),
+                (Opcode::Bitcast, &[I16], &[I8]),
+                (Opcode::Bitcast, &[I32], &[I8]),
+                (Opcode::Bitcast, &[I64], &[I8]),
+                (Opcode::Bitcast, &[I128], &[I8]),
+                (Opcode::Bitcast, &[F32], &[I8]),
+                (Opcode::Bitcast, &[F64], &[I8]),
+                (Opcode::Bitcast, &[I8X16], &[I8]),
+                (Opcode::Bitcast, &[I16X8], &[I8]),
+                (Opcode::Bitcast, &[I32X4], &[I8]),
+                (Opcode::Bitcast, &[I64X2], &[I8]),
+                (Opcode::Bitcast, &[F32X4], &[I8]),
+                (Opcode::Bitcast, &[F64X2], &[I8]),
+                (Opcode::Bitcast, &[I8], &[I16]),
+                (Opcode::Bitcast, &[I16], &[I16]),
+                (Opcode::Bitcast, &[I32], &[I16]),
+                (Opcode::Bitcast, &[I64], &[I16]),
+                (Opcode::Bitcast, &[I128], &[I16]),
+                (Opcode::Bitcast, &[F32], &[I16]),
+                (Opcode::Bitcast, &[F64], &[I16]),
+                (Opcode::Bitcast, &[I8X16], &[I16]),
+                (Opcode::Bitcast, &[I16X8], &[I16]),
+                (Opcode::Bitcast, &[I32X4], &[I16]),
+                (Opcode::Bitcast, &[I64X2], &[I16]),
+                (Opcode::Bitcast, &[F32X4], &[I16]),
+                (Opcode::Bitcast, &[F64X2], &[I16]),
+                (Opcode::Bitcast, &[I8], &[I32]),
+                (Opcode::Bitcast, &[I16], &[I32]),
+                (Opcode::Bitcast, &[I32], &[I32]),
+                (Opcode::Bitcast, &[I64], &[I32]),
+                (Opcode::Bitcast, &[I128], &[I32]),
+                (Opcode::Bitcast, &[F64], &[I32]),
+                (Opcode::Bitcast, &[I8X16], &[I32]),
+                (Opcode::Bitcast, &[I16X8], &[I32]),
+                (Opcode::Bitcast, &[I32X4], &[I32]),
+                (Opcode::Bitcast, &[I64X2], &[I32]),
+                (Opcode::Bitcast, &[F32X4], &[I32]),
+                (Opcode::Bitcast, &[F64X2], &[I32]),
+                (Opcode::Bitcast, &[I8], &[I64]),
+                (Opcode::Bitcast, &[I16], &[I64]),
+                (Opcode::Bitcast, &[I32], &[I64]),
+                (Opcode::Bitcast, &[I64], &[I64]),
+                (Opcode::Bitcast, &[I128], &[I64]),
+                (Opcode::Bitcast, &[F32], &[I64]),
+                (Opcode::Bitcast, &[I8X16], &[I64]),
+                (Opcode::Bitcast, &[I16X8], &[I64]),
+                (Opcode::Bitcast, &[I32X4], &[I64]),
+                (Opcode::Bitcast, &[I64X2], &[I64]),
+                (Opcode::Bitcast, &[F32X4], &[I64]),
+                (Opcode::Bitcast, &[F64X2], &[I64]),
+                (Opcode::Bitcast, &[I8], &[I128]),
+                (Opcode::Bitcast, &[I16], &[I128]),
+                (Opcode::Bitcast, &[I32], &[I128]),
+                (Opcode::Bitcast, &[I64], &[I128]),
+                (Opcode::Bitcast, &[I128], &[I128]),
+                (Opcode::Bitcast, &[F32], &[I128]),
+                (Opcode::Bitcast, &[F64], &[I128]),
+                (Opcode::Bitcast, &[I8X16], &[I128]),
+                (Opcode::Bitcast, &[I16X8], &[I128]),
+                (Opcode::Bitcast, &[I32X4], &[I128]),
+                (Opcode::Bitcast, &[I64X2], &[I128]),
+                (Opcode::Bitcast, &[F32X4], &[I128]),
+                (Opcode::Bitcast, &[F64X2], &[I128]),
+                (Opcode::Bitcast, &[I8], &[F32]),
+                (Opcode::Bitcast, &[I16], &[F32]),
+                (Opcode::Bitcast, &[I64], &[F32]),
+                (Opcode::Bitcast, &[I128], &[F32]),
+                (Opcode::Bitcast, &[F32], &[F32]),
+                (Opcode::Bitcast, &[F64], &[F32]),
+                (Opcode::Bitcast, &[I8X16], &[F32]),
+                (Opcode::Bitcast, &[I16X8], &[F32]),
+                (Opcode::Bitcast, &[I32X4], &[F32]),
+                (Opcode::Bitcast, &[I64X2], &[F32]),
+                (Opcode::Bitcast, &[F32X4], &[F32]),
+                (Opcode::Bitcast, &[F64X2], &[F32]),
+                (Opcode::Bitcast, &[I8], &[F64]),
+                (Opcode::Bitcast, &[I16], &[F64]),
+                (Opcode::Bitcast, &[I32], &[F64]),
+                (Opcode::Bitcast, &[I128], &[F64]),
+                (Opcode::Bitcast, &[F32], &[F64]),
+                (Opcode::Bitcast, &[F64], &[F64]),
+                (Opcode::Bitcast, &[I8X16], &[F64]),
+                (Opcode::Bitcast, &[I16X8], &[F64]),
+                (Opcode::Bitcast, &[I32X4], &[F64]),
+                (Opcode::Bitcast, &[I64X2], &[F64]),
+                (Opcode::Bitcast, &[F32X4], &[F64]),
+                (Opcode::Bitcast, &[F64X2], &[F64]),
+                (Opcode::Bitcast, &[I8], &[I8X16]),
+                (Opcode::Bitcast, &[I16], &[I8X16]),
+                (Opcode::Bitcast, &[I32], &[I8X16]),
+                (Opcode::Bitcast, &[I64], &[I8X16]),
+                (Opcode::Bitcast, &[I128], &[I8X16]),
+                (Opcode::Bitcast, &[F32], &[I8X16]),
+                (Opcode::Bitcast, &[F64], &[I8X16]),
+                (Opcode::Bitcast, &[I8X16], &[I8X16]),
+                (Opcode::Bitcast, &[I16X8], &[I8X16]),
+                (Opcode::Bitcast, &[I32X4], &[I8X16]),
+                (Opcode::Bitcast, &[I64X2], &[I8X16]),
+                (Opcode::Bitcast, &[F32X4], &[I8X16]),
+                (Opcode::Bitcast, &[F64X2], &[I8X16]),
+                (Opcode::Bitcast, &[I8], &[I16X8]),
+                (Opcode::Bitcast, &[I16], &[I16X8]),
+                (Opcode::Bitcast, &[I32], &[I16X8]),
+                (Opcode::Bitcast, &[I64], &[I16X8]),
+                (Opcode::Bitcast, &[I128], &[I16X8]),
+                (Opcode::Bitcast, &[F32], &[I16X8]),
+                (Opcode::Bitcast, &[F64], &[I16X8]),
+                (Opcode::Bitcast, &[I8X16], &[I16X8]),
+                (Opcode::Bitcast, &[I16X8], &[I16X8]),
+                (Opcode::Bitcast, &[I32X4], &[I16X8]),
+                (Opcode::Bitcast, &[I64X2], &[I16X8]),
+                (Opcode::Bitcast, &[F32X4], &[I16X8]),
+                (Opcode::Bitcast, &[F64X2], &[I16X8]),
+                (Opcode::Bitcast, &[I8], &[I32X4]),
+                (Opcode::Bitcast, &[I16], &[I32X4]),
+                (Opcode::Bitcast, &[I32], &[I32X4]),
+                (Opcode::Bitcast, &[I64], &[I32X4]),
+                (Opcode::Bitcast, &[I128], &[I32X4]),
+                (Opcode::Bitcast, &[F32], &[I32X4]),
+                (Opcode::Bitcast, &[F64], &[I32X4]),
+                (Opcode::Bitcast, &[I8X16], &[I32X4]),
+                (Opcode::Bitcast, &[I16X8], &[I32X4]),
+                (Opcode::Bitcast, &[I32X4], &[I32X4]),
+                (Opcode::Bitcast, &[I64X2], &[I32X4]),
+                (Opcode::Bitcast, &[F32X4], &[I32X4]),
+                (Opcode::Bitcast, &[F64X2], &[I32X4]),
+                (Opcode::Bitcast, &[I8], &[I64X2]),
+                (Opcode::Bitcast, &[I16], &[I64X2]),
+                (Opcode::Bitcast, &[I32], &[I64X2]),
+                (Opcode::Bitcast, &[I64], &[I64X2]),
+                (Opcode::Bitcast, &[I128], &[I64X2]),
+                (Opcode::Bitcast, &[F32], &[I64X2]),
+                (Opcode::Bitcast, &[F64], &[I64X2]),
+                (Opcode::Bitcast, &[I8X16], &[I64X2]),
+                (Opcode::Bitcast, &[I16X8], &[I64X2]),
+                (Opcode::Bitcast, &[I32X4], &[I64X2]),
+                (Opcode::Bitcast, &[I64X2], &[I64X2]),
+                (Opcode::Bitcast, &[F32X4], &[I64X2]),
+                (Opcode::Bitcast, &[F64X2], &[I64X2]),
+                (Opcode::Bitcast, &[I8], &[F32X4]),
+                (Opcode::Bitcast, &[I16], &[F32X4]),
+                (Opcode::Bitcast, &[I32], &[F32X4]),
+                (Opcode::Bitcast, &[I64], &[F32X4]),
+                (Opcode::Bitcast, &[I128], &[F32X4]),
+                (Opcode::Bitcast, &[F32], &[F32X4]),
+                (Opcode::Bitcast, &[F64], &[F32X4]),
+                (Opcode::Bitcast, &[I8X16], &[F32X4]),
+                (Opcode::Bitcast, &[I16X8], &[F32X4]),
+                (Opcode::Bitcast, &[I32X4], &[F32X4]),
+                (Opcode::Bitcast, &[I64X2], &[F32X4]),
+                (Opcode::Bitcast, &[F32X4], &[F32X4]),
+                (Opcode::Bitcast, &[F64X2], &[F32X4]),
+                (Opcode::Bitcast, &[I8], &[F64X2]),
+                (Opcode::Bitcast, &[I16], &[F64X2]),
+                (Opcode::Bitcast, &[I32], &[F64X2]),
+                (Opcode::Bitcast, &[I64], &[F64X2]),
+                (Opcode::Bitcast, &[I128], &[F64X2]),
+                (Opcode::Bitcast, &[F32], &[F64X2]),
+                (Opcode::Bitcast, &[F64], &[F64X2]),
+                (Opcode::Bitcast, &[I8X16], &[F64X2]),
+                (Opcode::Bitcast, &[I16X8], &[F64X2]),
+                (Opcode::Bitcast, &[I32X4], &[F64X2]),
+                (Opcode::Bitcast, &[I64X2], &[F64X2]),
+                (Opcode::Bitcast, &[F32X4], &[F64X2]),
+                (Opcode::Bitcast, &[F64X2], &[F64X2]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I8]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I8]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I16]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I16]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I32]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I32]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I64]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I64]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I128]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I128]),
+                (Opcode::FcvtToUintSat, &[F32], &[I8X16]),
+                (Opcode::FcvtToUintSat, &[F64], &[I8X16]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I8X16]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I8X16]),
+                (Opcode::FcvtToUintSat, &[F32], &[I16X8]),
+                (Opcode::FcvtToUintSat, &[F64], &[I16X8]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I16X8]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I16X8]),
+                (Opcode::FcvtToUintSat, &[F32], &[I32X4]),
+                (Opcode::FcvtToUintSat, &[F64], &[I32X4]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I32X4]),
+                (Opcode::FcvtToUintSat, &[F32], &[I64X2]),
+                (Opcode::FcvtToUintSat, &[F64], &[I64X2]),
+                (Opcode::FcvtToUintSat, &[F32X4], &[I64X2]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I8]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I8]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I16]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I16]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I32]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I32]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I64]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I64]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I128]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I128]),
+                (Opcode::FcvtToSintSat, &[F32], &[I8X16]),
+                (Opcode::FcvtToSintSat, &[F64], &[I8X16]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I8X16]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I8X16]),
+                (Opcode::FcvtToSintSat, &[F32], &[I16X8]),
+                (Opcode::FcvtToSintSat, &[F64], &[I16X8]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I16X8]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I16X8]),
+                (Opcode::FcvtToSintSat, &[F32], &[I32X4]),
+                (Opcode::FcvtToSintSat, &[F64], &[I32X4]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I32X4]),
+                (Opcode::FcvtToSintSat, &[F32], &[I64X2]),
+                (Opcode::FcvtToSintSat, &[F64], &[I64X2]),
+                (Opcode::FcvtToSintSat, &[F32X4], &[I64X2]),
+                (Opcode::FcvtFromUint, &[I8X16], &[F32]),
+                (Opcode::FcvtFromUint, &[I16X8], &[F32]),
+                (Opcode::FcvtFromUint, &[I32X4], &[F32]),
+                (Opcode::FcvtFromUint, &[I64X2], &[F32]),
+                (Opcode::FcvtFromUint, &[I8X16], &[F64]),
+                (Opcode::FcvtFromUint, &[I16X8], &[F64]),
+                (Opcode::FcvtFromUint, &[I32X4], &[F64]),
+                (Opcode::FcvtFromUint, &[I64X2], &[F64]),
+                (Opcode::FcvtFromUint, &[I8], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I16], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I32], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I64], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I128], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I8X16], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I16X8], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I64X2], &[F32X4]),
+                (Opcode::FcvtFromUint, &[I8], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I16], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I32], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I64], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I128], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I8X16], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I16X8], &[F64X2]),
+                (Opcode::FcvtFromUint, &[I32X4], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I8X16], &[F32]),
+                (Opcode::FcvtFromSint, &[I16X8], &[F32]),
+                (Opcode::FcvtFromSint, &[I32X4], &[F32]),
+                (Opcode::FcvtFromSint, &[I64X2], &[F32]),
+                (Opcode::FcvtFromSint, &[I8X16], &[F64]),
+                (Opcode::FcvtFromSint, &[I16X8], &[F64]),
+                (Opcode::FcvtFromSint, &[I32X4], &[F64]),
+                (Opcode::FcvtFromSint, &[I64X2], &[F64]),
+                (Opcode::FcvtFromSint, &[I8], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I16], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I32], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I64], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I128], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I8X16], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I16X8], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I64X2], &[F32X4]),
+                (Opcode::FcvtFromSint, &[I8], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I16], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I32], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I64], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I128], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I8X16], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I16X8], &[F64X2]),
+                (Opcode::FcvtFromSint, &[I32X4], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I8], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I16], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I32], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I64], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I128], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I8X16], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I16X8], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I32X4], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I64X2], &[F32]),
+                (Opcode::FcvtLowFromSint, &[I8], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I16], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I32], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I64], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I128], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I8X16], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I16X8], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I32X4], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I64X2], &[F64]),
+                (Opcode::FcvtLowFromSint, &[I8], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I16], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I32], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I64], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I128], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I8X16], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I16X8], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I32X4], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I64X2], &[F32X4]),
+                (Opcode::FcvtLowFromSint, &[I8], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I16], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I32], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I64], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I128], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I8X16], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I16X8], &[F64X2]),
+                (Opcode::FcvtLowFromSint, &[I64X2], &[F64X2]),
+            )
+        })
+        .collect()
+});
 
 fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
     match fmt {
@@ -1632,7 +1454,10 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         | InstructionFormat::Brif
         | InstructionFormat::Jump
         | InstructionFormat::MultiAry => {
-            panic!("Control-flow instructions should be handled by 'insert_terminator'")
+            panic!(
+                "Control-flow instructions should be handled by 'insert_terminator': {:?}",
+                fmt
+            )
         }
     }
 }
@@ -1989,17 +1814,17 @@ where
     /// Fills the current block with random instructions
     fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
         for _ in 0..self.param(&self.config.instructions_per_block)? {
-            let (op, args, rets) = *self.u.choose(OPCODE_SIGNATURES)?;
+            let (op, args, rets) = self.u.choose(&OPCODE_SIGNATURES)?;
 
             // We filter out instructions that aren't supported by the target at this point instead
             // of building a single vector of valid instructions at the beginning of function
             // generation, to avoid invalidating the corpus when instructions are enabled/disabled.
-            if !valid_for_target(&self.target_triple, op, args, rets) {
+            if !valid_for_target(&self.target_triple, *op, &args, &rets) {
                 return Err(arbitrary::Error::IncorrectFormat.into());
             }
 
             let inserter = inserter_for_format(op.format());
-            inserter(self, builder, op, args, rets)?;
+            inserter(self, builder, *op, &args, &rets)?;
         }
 
         Ok(())
@@ -2340,191 +2165,3 @@ where
         Ok(func)
     }
 }
-
-#[cfg(test)]
-mod tests {
-
-    use super::OPCODE_SIGNATURES;
-    use cranelift::codegen::ir::{instructions::ResolvedConstraint, types::*, Opcode, Type};
-    use std::collections::HashMap;
-
-    /// The list of opcodes that we exclude from the analysis. This should only be instructions
-    /// that affect control flow, as everything else should be handled by the general machinery in
-    /// function_generator.
-    ///
-    /// NOTE: This list should not grow, and ultimately should just be the set of instructions that
-    /// need special handling outside of `generate_instructions`.
-    const SKIPPED_OPCODES: &[Opcode] = &[
-        // These opcodes have special handling in cranelift-fuzzgen
-        Opcode::Call,
-        Opcode::Return,
-        Opcode::Jump,
-        Opcode::Brif,
-        Opcode::BrTable,
-        // TODO: ExtractVector produces dynamic vectors, and those cause a panic in
-        // `constraint.result_type`.
-        Opcode::ExtractVector,
-        // These opcodes are known to have invalid signatures, but the specific types chosen are
-        // not used by the insertion strategy for that format. They can be removed when their
-        // entries in OPCODE_SIGNATURES have been fixed
-        Opcode::Load,
-        Opcode::Store,
-        Opcode::AtomicLoad,
-        Opcode::AtomicStore,
-        Opcode::AtomicCas,
-        Opcode::AtomicRmw,
-        Opcode::Sload8,
-        Opcode::Sload16,
-        Opcode::Sload32,
-        Opcode::Uload8,
-        Opcode::Uload16,
-        Opcode::Uload32,
-        Opcode::Istore8,
-        Opcode::Istore16,
-        Opcode::Istore32,
-    ];
-
-    /// This is the set of types that we know how to fuzz in cranelift. It's not specialized by
-    /// targets, as we expect any target-specific support for things like SIMD to be expressed in
-    /// the `function_generator::valid_for_target` predicate instead.
-    const TYPES: &[Type] = &[
-        I8, I16, I32, I64, I128, // Scalar Integers
-        F32, F64, // Scalar Floats
-        I8X16, I16X8, I32X4, I64X2, // SIMD Integers
-        F32X4, F64X2, // SIMD Floats
-    ];
-
-    /// Generate all instantiations of all opcodes in cranelift, minus those named in
-    /// [SKIPPED_OPCODES].
-    fn instruction_instantiations<'a>() -> Vec<(Opcode, Vec<Type>, Vec<Type>)> {
-        let mut insts = vec![];
-
-        for op in Opcode::all() {
-            if SKIPPED_OPCODES.contains(op) {
-                continue;
-            }
-
-            let constraints = op.constraints();
-
-            let ctrl_types = if let Some(ctrls) = constraints.ctrl_typeset() {
-                Vec::from_iter(TYPES.iter().copied().filter(|ty| ctrls.contains(*ty)))
-            } else {
-                vec![INVALID]
-            };
-
-            for ctrl_type in ctrl_types {
-                let rets = Vec::from_iter(
-                    (0..constraints.num_fixed_results())
-                        .map(|i| constraints.result_type(i, ctrl_type)),
-                );
-
-                // Cols is a vector whose length will match `num_fixed_value_arguments`, and whose
-                // elements will be vectors of types that are valid for that fixed argument
-                // position.
-                let mut cols = vec![];
-
-                for i in 0..constraints.num_fixed_value_arguments() {
-                    match constraints.value_argument_constraint(i, ctrl_type) {
-                        ResolvedConstraint::Bound(ty) => cols.push(Vec::from([ty])),
-                        ResolvedConstraint::Free(tys) => cols.push(Vec::from_iter(
-                            TYPES.iter().copied().filter(|ty| tys.contains(*ty)),
-                        )),
-                    }
-                }
-
-                // Generate the cartesian product of cols to produce a vector of argument lists,
-                // argss. The argss vector is seeded with the empty argument list, so there's an
-                // initial value to be extended in the loop below.
-                let mut argss = vec![vec![]];
-                let mut cols = cols.as_slice();
-                while let Some((col, rest)) = cols.split_last() {
-                    cols = rest;
-
-                    let mut next = vec![];
-                    for current in argss.iter() {
-                        // Extend the front of each argument candidate with every type in `col`.
-                        for ty in col {
-                            let mut args = vec![*ty];
-                            args.extend_from_slice(&current);
-                            next.push(args);
-                        }
-                    }
-
-                    let _ = std::mem::replace(&mut argss, next);
-                }
-
-                for args in argss {
-                    insts.push((*op, args, rets.clone()));
-                }
-            }
-        }
-
-        insts
-    }
-
-    #[derive(Eq, PartialEq, Debug)]
-    struct Inst<'a> {
-        args: &'a [Type],
-        rets: &'a [Type],
-    }
-
-    fn build_sig_map<'a, T>(sigs: &'a [(Opcode, T, T)]) -> HashMap<Opcode, Vec<Inst<'a>>>
-    where
-        T: AsRef<[Type]>,
-    {
-        let mut insts = HashMap::<Opcode, Vec<Inst<'a>>>::default();
-
-        for (op, args, rets) in sigs {
-            insts.entry(*op).or_default().push(Inst {
-                args: args.as_ref(),
-                rets: rets.as_ref(),
-            });
-        }
-
-        insts
-    }
-
-    #[test]
-    fn validate_opcode_signatures() {
-        // We could instead use the instruction constraints to validate the entries in
-        // OPCODE_SIGNATURES directly, but generating all instantiations gives us the ability to
-        // also generate the complement of signatures that we don't currently handle in the future.
-        let all_ops = instruction_instantiations();
-        let everything = build_sig_map(&all_ops);
-        let fuzzed = build_sig_map(OPCODE_SIGNATURES);
-
-        let mut found_errs = false;
-        let mut unknown = vec![];
-        for (op, insts) in fuzzed.iter() {
-            if let Some(known_insts) = everything.get(op) {
-                let invalid =
-                    Vec::from_iter(insts.iter().filter(|inst| !known_insts.contains(inst)));
-                if !invalid.is_empty() {
-                    found_errs = true;
-                    println!("# Invalid instantiations for Opcode::{:?}", op);
-                    for inst in invalid {
-                        println!("- args: `{:?}`, rets: `{:?}`", inst.args, inst.rets);
-                    }
-                }
-            } else {
-                if !SKIPPED_OPCODES.contains(op) {
-                    unknown.push(*op);
-                }
-            }
-        }
-
-        if !unknown.is_empty() {
-            found_errs = true;
-            println!();
-            println!("# Instructions without known instantiations");
-            for op in unknown {
-                println!("- Opcode::{:?}", op);
-            }
-        }
-
-        assert!(
-            !found_errs,
-            "Discovered inconsistent entries in OPCODE_SIGNATURES"
-        );
-    }
-}


Test patch:
diff --git a/cranelift/filetests/filetests/isa/riscv64/bitops.clif b/cranelift/filetests/filetests/isa/riscv64/bitops.clif
index afe894948..23aaa6ba5 100644
--- a/cranelift/filetests/filetests/isa/riscv64/bitops.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/bitops.clif
@@ -385,28 +385,25 @@ block0(v0: i128):
 
 ; VCode:
 ; block0:
-;   mv t0,a1
-;   clz a2,t0##ty=i64 tmp=a3 step=a1
+;   clz a2,a1##ty=i64 tmp=a4 step=a3
 ;   clz a6,a0##ty=i64 tmp=a4 step=a5
-;   li t3,64
-;   select_reg t0,a6,zero##condition=(t3 eq a2)
-;   add a0,a2,t0
+;   select_reg t3,a6,zero##condition=(a1 eq zero)
+;   add a0,a2,t3
 ;   li a1,0
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   ori t0, a1, 0
 ;   ori a2, zero, 0
-;   addi a1, zero, 0x40
-;   addi a3, zero, 1
-;   slli a3, a3, 0x3f
-;   blez a1, 0x1c
-;   and t5, a3, t0
+;   addi a3, zero, 0x40
+;   addi a4, zero, 1
+;   slli a4, a4, 0x3f
+;   blez a3, 0x1c
+;   and t5, a4, a1
 ;   bne zero, t5, 0x14
 ;   addi a2, a2, 1
-;   addi a1, a1, -1
-;   srli a3, a3, 1
+;   addi a3, a3, -1
+;   srli a4, a4, 1
 ;   j -0x18
 ;   ori a6, zero, 0
 ;   addi a5, zero, 0x40
@@ -419,12 +416,11 @@ block0(v0: i128):
 ;   addi a5, a5, -1
 ;   srli a4, a4, 1
 ;   j -0x18
-;   addi t3, zero, 0x40
-;   beq t3, a2, 0xc
-;   ori t0, zero, 0
+;   beqz a1, 0xc
+;   ori t3, zero, 0
 ;   j 8
-;   ori t0, a6, 0
-;   add a0, a2, t0
+;   ori t3, a6, 0
+;   add a0, a2, t3
 ;   mv a1, zero
 ;   ret
 
@@ -438,8 +434,8 @@ block0(v0: i8):
 ; block0:
 ;   slli t2,a0,56
 ;   srai a1,t2,56
-;   not a3,a0
-;   select_reg a5,a3,a0##condition=(a1 slt zero)
+;   not a3,a1
+;   select_reg a5,a3,a1##condition=(a1 slt zero)
 ;   clz t4,a5##ty=i8 tmp=a7 step=t3
 ;   addi a0,t4,-1
 ;   ret
@@ -448,9 +444,9 @@ block0(v0: i8):
 ; block0: ; offset 0x0
 ;   slli t2, a0, 0x38
 ;   srai a1, t2, 0x38
-;   not a3, a0
+;   not a3, a1
 ;   bltz a1, 0xc
-;   ori a5, a0, 0
+;   ori a5, a1, 0
 ;   j 8
 ;   ori a5, a3, 0
 ;   ori t4, zero, 0
@@ -477,8 +473,8 @@ block0(v0: i16):
 ; block0:
 ;   slli t2,a0,48
 ;   srai a1,t2,48
-;   not a3,a0
-;   select_reg a5,a3,a0##condition=(a1 slt zero)
+;   not a3,a1
+;   select_reg a5,a3,a1##condition=(a1 slt zero)
 ;   clz t4,a5##ty=i16 tmp=a7 step=t3
 ;   addi a0,t4,-1
 ;   ret
@@ -487,9 +483,9 @@ block0(v0: i16):
 ; block0: ; offset 0x0
 ;   slli t2, a0, 0x30
 ;   srai a1, t2, 0x30
-;   not a3, a0
+;   not a3, a1
 ;   bltz a1, 0xc
-;   ori a5, a0, 0
+;   ori a5, a1, 0
 ;   j 8
 ;   ori a5, a3, 0
 ;   ori t4, zero, 0
@@ -515,8 +511,8 @@ block0(v0: i32):
 ; VCode:
 ; block0:
 ;   sext.w t2,a0
-;   not a1,a0
-;   select_reg a3,a1,a0##condition=(t2 slt zero)
+;   not a1,t2
+;   select_reg a3,a1,t2##condition=(t2 slt zero)
 ;   clz a7,a3##ty=i32 tmp=a5 step=a6
 ;   addi a0,a7,-1
 ;   ret
@@ -524,9 +520,9 @@ block0(v0: i32):
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   sext.w t2, a0
-;   not a1, a0
+;   not a1, t2
 ;   bltz t2, 0xc
-;   ori a3, a0, 0
+;   ori a3, t2, 0
 ;   j 8
 ;   ori a3, a1, 0
 ;   ori a7, zero, 0
@@ -592,11 +588,10 @@ block0(v0: i128):
 ;   select_reg a6,a4,a1##condition=(a1 slt zero)
 ;   clz t0,a6##ty=i64 tmp=t3 step=t4
 ;   clz a1,a2##ty=i64 tmp=t2 step=a0
-;   li a3,64
-;   select_reg a5,a1,zero##condition=(a3 eq t0)
-;   add a7,t0,a5
-;   li t4,0
-;   addi a0,a7,-1
+;   select_reg a3,a1,zero##condition=(a6 eq zero)
+;   add a5,t0,a3
+;   li a7,0
+;   addi a0,a5,-1
 ;   li a1,0
 ;   ret
 ; 
@@ -632,14 +627,13 @@ block0(v0: i128):
 ;   addi a0, a0, -1
 ;   srli t2, t2, 1
 ;   j -0x18
-;   addi a3, zero, 0x40
-;   beq a3, t0, 0xc
-;   ori a5, zero, 0
+;   beqz a6, 0xc
+;   ori a3, zero, 0
 ;   j 8
-;   ori a5, a1, 0
-;   add a7, t0, a5
-;   mv t4, zero
-;   addi a0, a7, -1
+;   ori a3, a1, 0
+;   add a5, t0, a3
+;   mv a7, zero
+;   addi a0, a5, -1
 ;   mv a1, zero
 ;   ret
 
@@ -759,44 +753,42 @@ block0(v0: i128):
 
 ; VCode:
 ; block0:
-;   mv t0,a0
-;   ctz a2,t0##ty=i64 tmp=a0 step=a3
-;   ctz a6,a1##ty=i64 tmp=a4 step=a5
-;   li t3,64
-;   select_reg t0,a6,zero##condition=(t3 eq a2)
-;   add a0,a2,t0
+;   mv t4,a1
+;   ctz a2,t4##ty=i64 tmp=a3 step=a1
+;   ctz a6,a0##ty=i64 tmp=a4 step=a5
+;   select_reg t3,a2,zero##condition=(a0 eq zero)
+;   add a0,a6,t3
 ;   li a1,0
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   ori t0, a0, 0
+;   ori t4, a1, 0
 ;   ori a2, zero, 0
-;   addi a3, zero, 0x40
-;   addi a0, zero, 1
-;   blez a3, 0x1c
-;   and t5, a0, t0
+;   addi a1, zero, 0x40
+;   addi a3, zero, 1
+;   blez a1, 0x1c
+;   and t5, a3, t4
 ;   bne zero, t5, 0x14
 ;   addi a2, a2, 1
-;   addi a3, a3, -1
-;   slli a0, a0, 1
+;   addi a1, a1, -1
+;   slli a3, a3, 1
 ;   j -0x18
 ;   ori a6, zero, 0
 ;   addi a5, zero, 0x40
 ;   addi a4, zero, 1
 ;   blez a5, 0x1c
-;   and t5, a4, a1
+;   and t5, a4, a0
 ;   bne zero, t5, 0x14
 ;   addi a6, a6, 1
 ;   addi a5, a5, -1
 ;   slli a4, a4, 1
 ;   j -0x18
-;   addi t3, zero, 0x40
-;   beq t3, a2, 0xc
-;   ori t0, zero, 0
+;   beqz a0, 0xc
+;   ori t3, zero, 0
 ;   j 8
-;   ori t0, a6, 0
-;   add a0, a2, t0
+;   ori t3, a2, 0
+;   add a0, a6, t3
 ;   mv a1, zero
 ;   ret
 
diff --git a/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif
new file mode 100644
index 000000000..d6683cbe9
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/cls-zbb.clif
@@ -0,0 +1,162 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zbb
+
+
+function %cls_i8(i8) -> i8 {
+block0(v0: i8):
+    v1 = cls v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   sext.b t2,a0
+;   not a1,t2
+;   select_reg a3,a1,t2##condition=(t2 slt zero)
+;   andi a5,a3,255
+;   clz a7,a5
+;   addi t4,a7,-56
+;   addi a0,t4,-1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x93, 0x13, 0x45, 0x60
+;   not a1, t2
+;   bltz t2, 0xc
+;   ori a3, t2, 0
+;   j 8
+;   ori a3, a1, 0
+;   andi a5, a3, 0xff
+;   .byte 0x93, 0x98, 0x07, 0x60
+;   addi t4, a7, -0x38
+;   addi a0, t4, -1
+;   ret
+
+function %cls_i16(i16) -> i16 {
+block0(v0: i16):
+    v1 = cls v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   sext.h t2,a0
+;   not a1,t2
+;   select_reg a3,a1,t2##condition=(t2 slt zero)
+;   zext.h a5,a3
+;   clz a7,a5
+;   addi t4,a7,-48
+;   addi a0,t4,-1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x93, 0x13, 0x55, 0x60
+;   not a1, t2
+;   bltz t2, 0xc
+;   ori a3, t2, 0
+;   j 8
+;   ori a3, a1, 0
+;   .byte 0xbb, 0xc7, 0x06, 0x08
+;   .byte 0x93, 0x98, 0x07, 0x60
+;   addi t4, a7, -0x30
+;   addi a0, t4, -1
+;   ret
+
+function %cls_i32(i32) -> i32 {
+block0(v0: i32):
+    v1 = cls v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   sext.w t2,a0
+;   not a1,t2
+;   select_reg a3,a1,t2##condition=(t2 slt zero)
+;   clzw a5,a3
+;   addi a0,a5,-1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   sext.w t2, a0
+;   not a1, t2
+;   bltz t2, 0xc
+;   ori a3, t2, 0
+;   j 8
+;   ori a3, a1, 0
+;   .byte 0x9b, 0x97, 0x06, 0x60
+;   addi a0, a5, -1
+;   ret
+
+function %cls_i64(i64) -> i64 {
+block0(v0: i64):
+    v1 = cls v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   not t2,a0
+;   select_reg a1,t2,a0##condition=(a0 slt zero)
+;   clz a3,a1
+;   addi a0,a3,-1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   not t2, a0
+;   bltz a0, 0xc
+;   ori a1, a0, 0
+;   j 8
+;   ori a1, t2, 0
+;   .byte 0x93, 0x96, 0x05, 0x60
+;   addi a0, a3, -1
+;   ret
+
+function %cls_i128(i128) -> i128 {
+block0(v0: i128):
+    v1 = cls v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   not a2,a0
+;   select_reg a2,a2,a0##condition=(a1 slt zero)
+;   not a4,a1
+;   select_reg a6,a4,a1##condition=(a1 slt zero)
+;   clz t3,a6
+;   clz t0,a2
+;   select_reg t2,t0,zero##condition=(a6 eq zero)
+;   add a1,t3,t2
+;   li a3,0
+;   addi a0,a1,-1
+;   li a1,0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   not a2, a0
+;   bltz a1, 8
+;   ori a2, a0, 0
+;   not a4, a1
+;   bltz a1, 0xc
+;   ori a6, a1, 0
+;   j 8
+;   ori a6, a4, 0
+;   .byte 0x13, 0x1e, 0x08, 0x60
+;   .byte 0x93, 0x12, 0x06, 0x60
+;   beqz a6, 0xc
+;   ori t2, zero, 0
+;   j 8
+;   ori t2, t0, 0
+;   add a1, t3, t2
+;   mv a3, zero
+;   addi a0, a1, -1
+;   mv a1, zero
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif
new file mode 100644
index 000000000..ff9e83530
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/clz-zbb.clif
@@ -0,0 +1,103 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zbb
+
+function %clz_i8(i8) -> i8 {
+block0(v0: i8):
+    v1 = clz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   andi t2,a0,255
+;   clz a1,t2
+;   addi a0,a1,-56
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   andi t2, a0, 0xff
+;   .byte 0x93, 0x95, 0x03, 0x60
+;   addi a0, a1, -0x38
+;   ret
+
+function %clz_i16(i16) -> i16 {
+block0(v0: i16):
+    v1 = clz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   zext.h t2,a0
+;   clz a1,t2
+;   addi a0,a1,-48
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0xbb, 0x43, 0x05, 0x08
+;   .byte 0x93, 0x95, 0x03, 0x60
+;   addi a0, a1, -0x30
+;   ret
+
+function %clz_i32(i32) -> i32 {
+block0(v0: i32):
+    v1 = clz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   clzw a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x1b, 0x15, 0x05, 0x60
+;   ret
+
+function %clz_i64(i64) -> i64 {
+block0(v0: i64):
+    v1 = clz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   clz a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x13, 0x15, 0x05, 0x60
+;   ret
+
+function %clz_i128(i128) -> i128 {
+block0(v0: i128):
+    v1 = clz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   clz a2,a1
+;   clz a3,a0
+;   select_reg a4,a3,zero##condition=(a1 eq zero)
+;   add a0,a2,a4
+;   li a1,0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x13, 0x96, 0x05, 0x60
+;   .byte 0x93, 0x16, 0x05, 0x60
+;   beqz a1, 0xc
+;   ori a4, zero, 0
+;   j 8
+;   ori a4, a3, 0
+;   add a0, a2, a4
+;   mv a1, zero
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/ctz-zbb-zbs.clif b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb-zbs.clif
new file mode 100644
index 000000000..50e3137af
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb-zbs.clif
@@ -0,0 +1,71 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zbb has_zbs
+
+function %ctz_i8(i8) -> i8 {
+block0(v0: i8):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   bseti t2,a0,8
+;   ctzw a0,t2
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x93, 0x13, 0x85, 0x28
+;   .byte 0x1b, 0x95, 0x13, 0x60
+;   ret
+
+function %ctz_i16(i16) -> i16 {
+block0(v0: i16):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   bseti t2,a0,16
+;   ctzw a0,t2
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x93, 0x13, 0x05, 0x29
+;   .byte 0x1b, 0x95, 0x13, 0x60
+;   ret
+
+function %ctz_i32(i32) -> i32 {
+block0(v0: i32):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ctzw a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x1b, 0x15, 0x15, 0x60
+;   ret
+
+function %ctz_i64(i64) -> i64 {
+block0(v0: i64):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ctz a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x13, 0x15, 0x15, 0x60
+;   ret
diff --git a/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif
new file mode 100644
index 000000000..8ff7579b4
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/ctz-zbb.clif
@@ -0,0 +1,102 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zbb
+
+
+function %ctz_i8(i8) -> i8 {
+block0(v0: i8):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ori t2,a0,256
+;   ctzw a0,t2
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ori t2, a0, 0x100
+;   .byte 0x1b, 0x95, 0x13, 0x60
+;   ret
+
+function %ctz_i16(i16) -> i16 {
+block0(v0: i16):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   lui t2,16
+;   or a1,a0,t2
+;   ctzw a0,a1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   lui t2, 0x10
+;   or a1, a0, t2
+;   .byte 0x1b, 0x95, 0x15, 0x60
+;   ret
+
+function %ctz_i32(i32) -> i32 {
+block0(v0: i32):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ctzw a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x1b, 0x15, 0x15, 0x60
+;   ret
+
+function %ctz_i64(i64) -> i64 {
+block0(v0: i64):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ctz a0,a0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x13, 0x15, 0x15, 0x60
+;   ret
+
+function %ctz_i128(i128) -> i128 {
+block0(v0: i128):
+    v1 = ctz v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   ctz a1,a1
+;   ctz a2,a0
+;   select_reg a4,a1,zero##condition=(a0 eq zero)
+;   add a0,a2,a4
+;   li a1,0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x93, 0x95, 0x15, 0x60
+;   .byte 0x13, 0x16, 0x15, 0x60
+;   beqz a0, 0xc
+;   ori a4, zero, 0
+;   j 8
+;   ori a4, a1, 0
+;   add a0, a2, a4
+;   mv a1, zero
+;   ret
+
diff --git a/cranelift/filetests/filetests/runtests/cls.clif b/cranelift/filetests/filetests/runtests/cls.clif
index d87c26193..81e3f54ff 100644
--- a/cranelift/filetests/filetests/runtests/cls.clif
+++ b/cranelift/filetests/filetests/runtests/cls.clif
@@ -2,6 +2,7 @@ test interpret
 test run
 target aarch64
 target riscv64
+target riscv64 has_zbb
 target s390x
 ; not implemented on `x86_64`
 
diff --git a/cranelift/filetests/filetests/runtests/clz.clif b/cranelift/filetests/filetests/runtests/clz.clif
index 98355af69..22080462b 100644
--- a/cranelift/filetests/filetests/runtests/clz.clif
+++ b/cranelift/filetests/filetests/runtests/clz.clif
@@ -5,12 +5,14 @@ target s390x
 target x86_64
 target x86_64 has_lzcnt
 target riscv64
+target riscv64 has_zbb
 
 function %clz_i8(i8) -> i8 {
 block0(v0: i8):
     v1 = clz v0
     return v1
 }
+; run: %clz_i8(0) == 8
 ; run: %clz_i8(1) == 7
 ; run: %clz_i8(0x40) == 1
 ; run: %clz_i8(-1) == 0
@@ -20,6 +22,7 @@ block0(v0: i16):
     v1 = clz v0
     return v1
 }
+; run: %clz_i16(0) == 16
 ; run: %clz_i16(1) == 15
 ; run: %clz_i16(0x4000) == 1
 ; run: %clz_i16(-1) == 0
@@ -29,6 +32,7 @@ block0(v0: i32):
     v1 = clz v0
     return v1
 }
+; run: %clz_i32(0) == 32
 ; run: %clz_i32(1) == 31
 ; run: %clz_i32(0x40000000) == 1
 ; run: %clz_i32(-1) == 0
@@ -38,6 +42,7 @@ block0(v0: i64):
     v1 = clz v0
     return v1
 }
+; run: %clz_i64(0) == 64
 ; run: %clz_i64(1) == 63
 ; run: %clz_i64(0x4000000000000000) == 1
 ; run: %clz_i64(-1) == 0
diff --git a/cranelift/filetests/filetests/runtests/ctz.clif b/cranelift/filetests/filetests/runtests/ctz.clif
index 305163867..adef585fa 100644
--- a/cranelift/filetests/filetests/runtests/ctz.clif
+++ b/cranelift/filetests/filetests/runtests/ctz.clif
@@ -3,14 +3,17 @@ test run
 target aarch64
 target s390x
 target x86_64
-target riscv64
 target x86_64 has_bmi1
+target riscv64
+target riscv64 has_zbb
+target riscv64 has_zbb has_zbs
 
 function %ctz_i8(i8) -> i8 {
 block0(v0: i8):
     v1 = ctz v0
     return v1
 }
+; run: %ctz_i8(0) == 8
 ; run: %ctz_i8(1) == 0
 ; run: %ctz_i8(0x40) == 6
 ; run: %ctz_i8(-1) == 0
@@ -20,6 +23,7 @@ block0(v0: i16):
     v1 = ctz v0
     return v1
 }
+; run: %ctz_i16(0) == 16
 ; run: %ctz_i16(1) == 0
 ; run: %ctz_i16(0x4000) == 14
 ; run: %ctz_i16(-1) == 0
@@ -29,6 +33,7 @@ block0(v0: i32):
     v1 = ctz v0
     return v1
 }
+; run: %ctz_i32(0) == 32
 ; run: %ctz_i32(1) == 0
 ; run: %ctz_i32(0x40000000) == 30
 ; run: %ctz_i32(-1) == 0
@@ -38,6 +43,7 @@ block0(v0: i64):
     v1 = ctz v0
     return v1
 }
+; run: %ctz_i64(0) == 64
 ; run: %ctz_i64(1) == 0
 ; run: %ctz_i64(0x4000000000000000) == 62
 ; run: %ctz_i64(-1) == 0
diff --git a/cranelift/filetests/filetests/runtests/i128-bitops-count.clif b/cranelift/filetests/filetests/runtests/i128-bitops-count.clif
index 533fdce31..a57cc4308 100644
--- a/cranelift/filetests/filetests/runtests/i128-bitops-count.clif
+++ b/cranelift/filetests/filetests/runtests/i128-bitops-count.clif
@@ -4,6 +4,8 @@ target aarch64
 target s390x
 target x86_64
 target riscv64
+target riscv64 has_zbb
+target riscv64 has_zbb has_zbs
 
 function %ctz_i128(i128) -> i128 {
 block0(v0: i128):
diff --git a/cranelift/filetests/filetests/runtests/i128-cls.clif b/cranelift/filetests/filetests/runtests/i128-cls.clif
index cd9deac10..24fe9a172 100644
--- a/cranelift/filetests/filetests/runtests/i128-cls.clif
+++ b/cranelift/filetests/filetests/runtests/i128-cls.clif
@@ -1,6 +1,7 @@
 test run
 target aarch64
-target riscv64 
+target riscv64
+target riscv64 has_zbb
 target s390x
 
 function %cls_i128(i128) -> i128 {