Repository: bytecodealliance/wasmtime
Pull request #8579 (2024-05-07 21:15:58)

wasmtime(gc): Fix wasm-to-native trampoline lookup for subtyping
Previously, we would look up a wasm-to-native trampoline in the Wasm module based on the host function's type. With Wasm GC and subtyping, this becomes problematic because a Wasm module can import a function of type `T` but the host can define a function of type `U` where `U <: T`. And if the Wasm has never defined type `U` then it wouldn't have a trampoline for it. But our trampolines don't actually care, they treat all reference values within the same type hierarchy identically. So the trampoline for `T` would have worked in practice. But once we find a trampoline for a function, we cache it and reuse it every time that function is used in the same store again. Even if the function is imported with its precise type somewhere else. So then we would have a trampoline of the wrong type. But this happened to be okay in practice because the trampolines happen not to inspect their arguments or do anything with them other than forward them between calling convention locations. But relying on that accidental invariant seems fragile and like a gun aimed at the future's feet.

This commit makes that invariant non-accidental, centering it and hopefully making it less fragile by doing so, by making every function type have an associated "trampoline type". A trampoline type is the original function type but where all the reference types in its params and results are replaced with the nullable top versions, e.g. `(ref $my_struct)` is replaced with `(ref null any)`. Often a function type is its own associated trampoline type, as is the case for all functions that don't have take or return any references, for example. Then, all trampoline lookup begins by first getting the trampoline type of the actual function type, or actual import type, and then only afterwards finding for the pre-compiled trampoline in the Wasm module.

Fixes https://github.com/bytecodealliance/wasmtime/issues/8432

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index d83c1677c..1a64118f5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -847,7 +847,7 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime-types",
  "wat",
 ]
@@ -2753,7 +2753,7 @@ dependencies = [
  "cargo_metadata",
  "heck",
  "wasmtime",
- "wit-component 0.206.0",
+ "wit-component 0.207.0",
 ]
 
 [[package]]
@@ -3089,7 +3089,7 @@ name = "verify-component-adapter"
 version = "22.0.0"
 dependencies = [
  "anyhow",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wat",
 ]
 
@@ -3181,7 +3181,7 @@ dependencies = [
  "byte-array-literals",
  "object 0.33.0",
  "wasi",
- "wasm-encoder 0.206.0",
+ "wasm-encoder 0.207.0",
  "wit-bindgen",
 ]
 
@@ -3250,9 +3250,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d759312e1137f199096d80a70be685899cd7d3d09c572836bb2e9b69b4dc3b1e"
+checksum = "d996306fb3aeaee0d9157adbe2f670df0236caf19f6728b221e92d0f27b3fe17"
 dependencies = [
  "leb128",
 ]
@@ -3275,9 +3275,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-metadata"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84435ac34e29de6154fdb180ebbe2bdde96336f7ad0990003c43a21bd441303f"
+checksum = "b2c44e62d325ce9253f88c01f0f67be121356767d12f2f13e701fdcd99e1f5b0"
 dependencies = [
  "anyhow",
  "indexmap 2.2.6",
@@ -3285,36 +3285,36 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder 0.206.0",
- "wasmparser 0.206.0",
+ "wasm-encoder 0.207.0",
+ "wasmparser 0.207.0",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afd8f7a4dc2b33e16b5fe96e31922ab3d5351abd93e404fcdc5bdd67d91915f0"
+checksum = "17f1f13b2d32934450e1092213385bf98411c9448a4418b79d54640df0280c36"
 dependencies = [
  "egg",
  "log",
  "rand",
  "thiserror",
- "wasm-encoder 0.206.0",
- "wasmparser 0.206.0",
+ "wasm-encoder 0.207.0",
+ "wasmparser 0.207.0",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "797d1293e8c8ce440c22dc283b7db0f68035929928f1736f12f4d9bf8fd2a24d"
+checksum = "604b90d8a005c9dfb783c5cfba074429f9c7f3f7d6aea5ba79a4657413fdb1ad"
 dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
  "indexmap 2.2.6",
  "leb128",
- "wasm-encoder 0.206.0",
+ "wasm-encoder 0.207.0",
 ]
 
 [[package]]
@@ -3370,9 +3370,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39192edb55d55b41963db40fd49b0b542156f04447b5b512744a91d38567bdbc"
+checksum = "e19bb9f8ab07616da582ef8adb24c54f1424c7ec876720b7da9db8ec0626c92c"
 dependencies = [
  "ahash",
  "bitflags 2.4.1",
@@ -3392,12 +3392,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dec456f9cc479792c9920055cd499ae1d13f85e98f9443dfe4ea5751b21f3b0"
+checksum = "9c2d8a7b4dabb460208e6b4334d9db5766e84505038b2529e69c3d07ac619115"
 dependencies = [
  "anyhow",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
 ]
 
 [[package]]
@@ -3440,8 +3440,8 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-common",
- "wasm-encoder 0.206.0",
- "wasmparser 0.206.0",
+ "wasm-encoder 0.207.0",
+ "wasmparser 0.207.0",
  "wasmtime-asm-macros",
  "wasmtime-cache",
  "wasmtime-component-macro",
@@ -3583,7 +3583,7 @@ dependencies = [
  "tracing",
  "walkdir",
  "wasi-common",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3596,10 +3596,10 @@ dependencies = [
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 206.0.0",
+ "wast 207.0.0",
  "wat",
  "windows-sys 0.52.0",
- "wit-component 0.206.0",
+ "wit-component 0.207.0",
 ]
 
 [[package]]
@@ -3632,7 +3632,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser 0.206.0",
+ "wit-parser 0.207.0",
 ]
 
 [[package]]
@@ -3656,7 +3656,7 @@ dependencies = [
  "object 0.33.0",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
 ]
@@ -3679,8 +3679,8 @@ dependencies = [
  "serde",
  "serde_derive",
  "target-lexicon",
- "wasm-encoder 0.206.0",
- "wasmparser 0.206.0",
+ "wasm-encoder 0.207.0",
+ "wasmparser 0.207.0",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -3695,7 +3695,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger",
  "libfuzzer-sys",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -3752,7 +3752,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -3773,12 +3773,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder 0.206.0",
+ "wasm-encoder 0.207.0",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -3817,7 +3817,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "smallvec",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
 ]
 
 [[package]]
@@ -3924,7 +3924,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 206.0.0",
+ "wast 207.0.0",
 ]
 
 [[package]]
@@ -3936,7 +3936,7 @@ dependencies = [
  "gimli",
  "object 0.33.0",
  "target-lexicon",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
  "winch-codegen",
@@ -3949,7 +3949,7 @@ dependencies = [
  "anyhow",
  "heck",
  "indexmap 2.2.6",
- "wit-parser 0.206.0",
+ "wit-parser 0.207.0",
 ]
 
 [[package]]
@@ -3967,24 +3967,24 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "206.0.0"
+version = "207.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68586953ee4960b1f5d84ebf26df3b628b17e6173bc088e0acfbce431469795a"
+checksum = "0e40be9fd494bfa501309487d2dc0b3f229be6842464ecbdc54eac2679c84c93"
 dependencies = [
  "bumpalo",
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.206.0",
+ "wasm-encoder 0.207.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.206.0"
+version = "1.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da4c6f2606276c6e991aebf441b2fc92c517807393f039992a3e0ad873efe4ad"
+checksum = "8eb2b15e2d5f300f5e1209e7dc237f2549edbd4203655b6c6cab5cf180561ee7"
 dependencies = [
- "wast 206.0.0",
+ "wast 207.0.0",
 ]
 
 [[package]]
@@ -4104,7 +4104,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
 ]
@@ -4355,9 +4355,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81e1ae4cace3706e29a8cf1f728b792255ecdc02653b9f4130ab6cc64f435a67"
+checksum = "a411ff9c471737091b2c1a738a25031029fc4d0b8f1a60bef0e68906e9f6534b"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -4366,10 +4366,10 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.206.0",
- "wasm-metadata 0.206.0",
- "wasmparser 0.206.0",
- "wit-parser 0.206.0",
+ "wasm-encoder 0.207.0",
+ "wasm-metadata 0.207.0",
+ "wasmparser 0.207.0",
+ "wit-parser 0.207.0",
 ]
 
 [[package]]
@@ -4392,9 +4392,9 @@ dependencies = [
 
 [[package]]
 name = "wit-parser"
-version = "0.206.0"
+version = "0.207.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d226de4e95e052cb664d2bdad2f31d9cad5117038a3439568f078d1afd8842fe"
+checksum = "78c83dab33a9618d86cfe3563cc864deffd08c17efc5db31a3b7cd1edeffe6e1"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -4405,7 +4405,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.206.0",
+ "wasmparser 0.207.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index edcadb58c..e20e9770c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -231,15 +231,15 @@ rustix = "0.38.31"
 wit-bindgen = { version = "0.22.0", default-features = false }
 
 # wasm-tools family:
-wasmparser = { version = "0.206.0", default-features = false }
-wat = "1.206.0"
-wast = "206.0.0"
-wasmprinter = "0.206.0"
-wasm-encoder = "0.206.0"
-wasm-smith = "0.206.0"
-wasm-mutate = "0.206.0"
-wit-parser = "0.206.0"
-wit-component = "0.206.0"
+wasmparser = { version = "0.207.0", default-features = false }
+wat = "1.207.0"
+wast = "207.0.0"
+wasmprinter = "0.207.0"
+wasm-encoder = "0.207.0"
+wasm-smith = "0.207.0"
+wasm-mutate = "0.207.0"
+wit-parser = "0.207.0"
+wit-component = "0.207.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
diff --git a/RELEASES.md b/RELEASES.md
index 046c122a3..d065a86b6 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -92,6 +92,24 @@ Unreleased.
 
 --------------------------------------------------------------------------------
 
+## 20.0.2
+
+Released 2024-05-07.
+
+### Added
+
+* Improve error in CMake for when Cargo is not found.
+  [#8497](https://github.com/bytecodealliance/wasmtime/issues/8497)
+
+* Use `--release` in CMake with MinSizeRel and RelWithDebInfo.
+  [#8549](https://github.com/bytecodealliance/wasmtime/issues/8549)
+
+* Add a `WASMTIME_FASTEST_RUNTIME` configuration option for CMake which enables
+  LTO and other related optimization options.
+  [#8554](https://github.com/bytecodealliance/wasmtime/issues/8554)
+
+--------------------------------------------------------------------------------
+
 ## 20.0.1
 
 Released 2024-05-03.
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 9dc389d11..6358ad17b 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -168,17 +168,11 @@ fn enc_cbr(op_31_24: u32, off_18_0: u32, op_4: u32, cond: u32) -> u32 {
 fn enc_conditional_br(
     taken: BranchTarget,
     kind: CondBrKind,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> u32 {
     match kind {
-        CondBrKind::Zero(reg) => {
-            let reg = allocs.next(reg);
-            enc_cmpbr(0b1_011010_0, taken.as_offset19_or_zero(), reg)
-        }
-        CondBrKind::NotZero(reg) => {
-            let reg = allocs.next(reg);
-            enc_cmpbr(0b1_011010_1, taken.as_offset19_or_zero(), reg)
-        }
+        CondBrKind::Zero(reg) => enc_cmpbr(0b1_011010_0, taken.as_offset19_or_zero(), reg),
+        CondBrKind::NotZero(reg) => enc_cmpbr(0b1_011010_1, taken.as_offset19_or_zero(), reg),
         CondBrKind::Cond(c) => enc_cbr(0b01010100, taken.as_offset19_or_zero(), 0b0, c.bits()),
     }
 }
@@ -750,10 +744,6 @@ impl MachInstEmit for Inst {
                 rn,
                 rm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 debug_assert!(match alu_op {
                     ALUOp::SDiv | ALUOp::UDiv | ALUOp::SMulH | ALUOp::UMulH =>
                         size == OperandSize::Size64,
@@ -807,11 +797,6 @@ impl MachInstEmit for Inst {
                 rn,
                 ra,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let ra = allocs.next(ra);
-
                 let (top11, bit15) = match alu_op {
                     ALUOp3::MAdd => (0b0_00_11011_000, 0),
                     ALUOp3::MSub => (0b0_00_11011_000, 1),
@@ -834,8 +819,6 @@ impl MachInstEmit for Inst {
                 rn,
                 ref imm12,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let top8 = match alu_op {
                     ALUOp::Add => 0b000_10001,
                     ALUOp::Sub => 0b010_10001,
@@ -859,8 +842,6 @@ impl MachInstEmit for Inst {
                 rn,
                 ref imml,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (top9, inv) = match alu_op {
                     ALUOp::Orr => (0b001_100100, false),
                     ALUOp::And => (0b000_100100, false),
@@ -883,8 +864,6 @@ impl MachInstEmit for Inst {
                 rn,
                 ref immshift,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let amt = immshift.value();
                 let (top10, immr, imms) = match alu_op {
                     ALUOp::RotR => (0b0001001110, machreg_to_gpr(rn), u32::from(amt)),
@@ -922,9 +901,6 @@ impl MachInstEmit for Inst {
                 rm,
                 ref shiftop,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let top11: u32 = match alu_op {
                     ALUOp::Add => 0b000_01011000,
                     ALUOp::AddS => 0b001_01011000,
@@ -953,9 +929,6 @@ impl MachInstEmit for Inst {
                 rm,
                 extendop,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let top11: u32 = match alu_op {
                     ALUOp::Add => 0b00001011001,
                     ALUOp::Sub => 0b01001011001,
@@ -971,8 +944,6 @@ impl MachInstEmit for Inst {
             &Inst::BitRR {
                 op, size, rd, rn, ..
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (op1, op2) = match op {
                     BitOp::RBit => (0b00000, 0b000000),
                     BitOp::Clz => (0b00000, 0b000100),
@@ -996,7 +967,6 @@ impl MachInstEmit for Inst {
             | &Inst::FpuLoad32 { rd, ref mem, flags }
             | &Inst::FpuLoad64 { rd, ref mem, flags }
             | &Inst::FpuLoad128 { rd, ref mem, flags } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
                 let access_ty = self.mem_type().unwrap();
                 let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state);
@@ -1032,23 +1002,23 @@ impl MachInstEmit for Inst {
 
                 match &mem {
                     &AMode::Unscaled { rn, simm9 } => {
-                        let reg = allocs.next(rn);
+                        let reg = rn;
                         sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd));
                     }
                     &AMode::UnsignedOffset { rn, uimm12 } => {
-                        let reg = allocs.next(rn);
+                        let reg = rn;
                         sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd));
                     }
                     &AMode::RegReg { rn, rm } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         sink.put4(enc_ldst_reg(
                             op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd,
                         ));
                     }
                     &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         let extendop = match &mem {
                             &AMode::RegScaled { .. } => None,
                             &AMode::RegScaledExtended { extendop, .. } => Some(extendop),
@@ -1059,8 +1029,8 @@ impl MachInstEmit for Inst {
                         ));
                     }
                     &AMode::RegExtended { rn, rm, extendop } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         sink.put4(enc_ldst_reg(
                             op,
                             r1,
@@ -1137,7 +1107,6 @@ impl MachInstEmit for Inst {
             | &Inst::FpuStore32 { rd, ref mem, flags }
             | &Inst::FpuStore64 { rd, ref mem, flags }
             | &Inst::FpuStore128 { rd, ref mem, flags } => {
-                let rd = allocs.next(rd);
                 let mem = mem.clone();
                 let access_ty = self.mem_type().unwrap();
                 let (mem_insts, mem) = mem_finalize(Some(sink), &mem, access_ty, state);
@@ -1164,23 +1133,23 @@ impl MachInstEmit for Inst {
 
                 match &mem {
                     &AMode::Unscaled { rn, simm9 } => {
-                        let reg = allocs.next(rn);
+                        let reg = rn;
                         sink.put4(enc_ldst_simm9(op, simm9, 0b00, reg, rd));
                     }
                     &AMode::UnsignedOffset { rn, uimm12 } => {
-                        let reg = allocs.next(rn);
+                        let reg = rn;
                         sink.put4(enc_ldst_uimm12(op, uimm12, reg, rd));
                     }
                     &AMode::RegReg { rn, rm } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         sink.put4(enc_ldst_reg(
                             op, r1, r2, /* scaled = */ false, /* extendop = */ None, rd,
                         ));
                     }
                     &AMode::RegScaled { rn, rm } | &AMode::RegScaledExtended { rn, rm, .. } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         let extendop = match &mem {
                             &AMode::RegScaled { .. } => None,
                             &AMode::RegScaledExtended { extendop, .. } => Some(extendop),
@@ -1191,8 +1160,8 @@ impl MachInstEmit for Inst {
                         ));
                     }
                     &AMode::RegExtended { rn, rm, extendop } => {
-                        let r1 = allocs.next(rn);
-                        let r2 = allocs.next(rm);
+                        let r1 = rn;
+                        let r2 = rm;
                         sink.put4(enc_ldst_reg(
                             op,
                             r1,
@@ -1231,8 +1200,6 @@ impl MachInstEmit for Inst {
                 ref mem,
                 flags,
             } => {
-                let rt = allocs.next(rt);
-                let rt2 = allocs.next(rt2);
                 let mem = mem.clone();
                 if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual store instruction starts.
@@ -1241,7 +1208,6 @@ impl MachInstEmit for Inst {
                 match &mem {
                     &PairAMode::SignedOffset { reg, simm7 } => {
                         assert_eq!(simm7.scale_ty, I64);
-                        let reg = allocs.next(reg);
                         sink.put4(enc_ldst_pair(0b1010100100, simm7, reg, rt, rt2));
                     }
                     &PairAMode::SPPreIndexed { simm7 } => {
@@ -1262,8 +1228,8 @@ impl MachInstEmit for Inst {
                 ref mem,
                 flags,
             } => {
-                let rt = allocs.next(rt.to_reg());
-                let rt2 = allocs.next(rt2.to_reg());
+                let rt = rt.to_reg();
+                let rt2 = rt2.to_reg();
                 let mem = mem.clone();
                 if let Some(trap_code) = flags.trap_code() {
                     // Register the offset at which the actual load instruction starts.
@@ -1273,7 +1239,6 @@ impl MachInstEmit for Inst {
                 match &mem {
                     &PairAMode::SignedOffset { reg, simm7 } => {
                         assert_eq!(simm7.scale_ty, I64);
-                        let reg = allocs.next(reg);
                         sink.put4(enc_ldst_pair(0b1010100101, simm7, reg, rt, rt2));
                     }
                     &PairAMode::SPPreIndexed { simm7 } => {
@@ -1300,8 +1265,8 @@ impl MachInstEmit for Inst {
                 ref mem,
                 flags,
             } => {
-                let rt = allocs.next(rt.to_reg());
-                let rt2 = allocs.next(rt2.to_reg());
+                let rt = rt.to_reg();
+                let rt2 = rt2.to_reg();
                 let mem = mem.clone();
 
                 if let Some(trap_code) = flags.trap_code() {
@@ -1318,7 +1283,6 @@ impl MachInstEmit for Inst {
                 match &mem {
                     &PairAMode::SignedOffset { reg, simm7 } => {
                         assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
-                        let reg = allocs.next(reg);
                         sink.put4(enc_ldst_vec_pair(opc, 0b10, true, simm7, reg, rt, rt2));
                     }
                     &PairAMode::SPPreIndexed { simm7 } => {
@@ -1345,8 +1309,6 @@ impl MachInstEmit for Inst {
                 ref mem,
                 flags,
             } => {
-                let rt = allocs.next(rt);
-                let rt2 = allocs.next(rt2);
                 let mem = mem.clone();
 
                 if let Some(trap_code) = flags.trap_code() {
@@ -1363,7 +1325,6 @@ impl MachInstEmit for Inst {
                 match &mem {
                     &PairAMode::SignedOffset { reg, simm7 } => {
                         assert!(simm7.scale_ty == F64 || simm7.scale_ty == I8X16);
-                        let reg = allocs.next(reg);
                         sink.put4(enc_ldst_vec_pair(opc, 0b10, false, simm7, reg, rt, rt2));
                     }
                     &PairAMode::SPPreIndexed { simm7 } => {
@@ -1379,8 +1340,6 @@ impl MachInstEmit for Inst {
                 }
             }
             &Inst::Mov { size, rd, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rm = allocs.next(rm);
                 assert!(rd.to_reg().class() == rm.class());
                 assert!(rm.class() == RegClass::Int);
 
@@ -1415,7 +1374,6 @@ impl MachInstEmit for Inst {
                 }
             }
             &Inst::MovFromPReg { rd, rm } => {
-                let rd = allocs.next_writable(rd);
                 let rm: Reg = rm.into();
                 debug_assert!([
                     regs::fp_reg(),
@@ -1431,7 +1389,6 @@ impl MachInstEmit for Inst {
             }
             &Inst::MovToPReg { rd, rm } => {
                 let rd: Writable<Reg> = Writable::from_reg(rd.into());
-                let rm = allocs.next(rm);
                 debug_assert!([
                     regs::fp_reg(),
                     regs::stack_reg(),
@@ -1445,33 +1402,22 @@ impl MachInstEmit for Inst {
                 Inst::Mov { size, rd, rm }.emit(&[], sink, emit_info, state);
             }
             &Inst::MovWide { op, rd, imm, size } => {
-                let rd = allocs.next_writable(rd);
                 sink.put4(enc_move_wide(op, rd, imm, size));
             }
             &Inst::MovK { rd, rn, imm, size } => {
-                let rn = allocs.next(rn);
-                let rd = allocs.next_writable(rd);
                 debug_assert_eq!(rn, rd.to_reg());
                 sink.put4(enc_movk(rd, imm, size));
             }
             &Inst::CSel { rd, rn, rm, cond } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_csel(rd, rn, rm, cond, 0, 0));
             }
             &Inst::CSNeg { rd, rn, rm, cond } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_csel(rd, rn, rm, cond, 1, 1));
             }
             &Inst::CSet { rd, cond } => {
-                let rd = allocs.next_writable(rd);
                 sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 0, 1));
             }
             &Inst::CSetm { rd, cond } => {
-                let rd = allocs.next_writable(rd);
                 sink.put4(enc_csel(rd, zero_reg(), zero_reg(), cond.invert(), 1, 0));
             }
             &Inst::CCmp {
@@ -1481,8 +1427,6 @@ impl MachInstEmit for Inst {
                 nzcv,
                 cond,
             } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_ccmp(size, rn, rm, nzcv, cond));
             }
             &Inst::CCmpImm {
@@ -1492,7 +1436,6 @@ impl MachInstEmit for Inst {
                 nzcv,
                 cond,
             } => {
-                let rn = allocs.next(rn);
                 sink.put4(enc_ccmp_imm(size, rn, imm, nzcv, cond));
             }
             &Inst::AtomicRMW {
@@ -1503,10 +1446,6 @@ impl MachInstEmit for Inst {
                 rn,
                 flags,
             } => {
-                let rs = allocs.next(rs);
-                let rt = allocs.next_writable(rt);
-                let rn = allocs.next(rn);
-
                 if let Some(trap_code) = flags.trap_code() {
                     sink.add_trap(trap_code);
                 }
@@ -1699,11 +1638,7 @@ impl MachInstEmit for Inst {
                 ty,
                 flags,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rs = allocs.next(rs);
                 debug_assert_eq!(rd.to_reg(), rs);
-                let rt = allocs.next(rt);
-                let rn = allocs.next(rn);
                 let size = match ty {
                     I8 => 0b00,
                     I16 => 0b01,
@@ -1799,9 +1734,6 @@ impl MachInstEmit for Inst {
                 rn,
                 flags,
             } => {
-                let rn = allocs.next(rn);
-                let rt = allocs.next_writable(rt);
-
                 if let Some(trap_code) = flags.trap_code() {
                     sink.add_trap(trap_code);
                 }
@@ -1814,9 +1746,6 @@ impl MachInstEmit for Inst {
                 rn,
                 flags,
             } => {
-                let rn = allocs.next(rn);
-                let rt = allocs.next(rt);
-
                 if let Some(trap_code) = flags.trap_code() {
                     sink.add_trap(trap_code);
                 }
@@ -1830,23 +1759,15 @@ impl MachInstEmit for Inst {
                 sink.put4(0xd503229f);
             }
             &Inst::FpuMove32 { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 sink.put4(enc_fpurr(0b000_11110_00_1_000000_10000, rd, rn));
             }
             &Inst::FpuMove64 { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 sink.put4(enc_fpurr(0b000_11110_01_1_000000_10000, rd, rn));
             }
             &Inst::FpuMove128 { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 sink.put4(enc_vecmov(/* 16b = */ true, rd, rn));
             }
             &Inst::FpuMoveFromVec { rd, rn, idx, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (imm5, shift, mask) = match size.lane_size() {
                     ScalarSize::Size32 => (0b00100, 3, 0b011),
                     ScalarSize::Size64 => (0b01000, 4, 0b001),
@@ -1862,8 +1783,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::FpuExtend { rd, rn, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 sink.put4(enc_fpurr(
                     0b000_11110_00_1_000000_10000 | (size.ftype() << 12),
                     rd,
@@ -1876,8 +1795,6 @@ impl MachInstEmit for Inst {
                 rd,
                 rn,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let top22 = match fpu_op {
                     FPUOp1::Abs => 0b000_11110_00_1_000001_10000,
                     FPUOp1::Neg => 0b000_11110_00_1_000010_10000,
@@ -1901,9 +1818,6 @@ impl MachInstEmit for Inst {
                 rn,
                 rm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let top22 = match fpu_op {
                     FPUOp2::Add => 0b000_11110_00_1_00000_001010,
                     FPUOp2::Sub => 0b000_11110_00_1_00000_001110,
@@ -1915,34 +1829,27 @@ impl MachInstEmit for Inst {
                 let top22 = top22 | size.ftype() << 12;
                 sink.put4(enc_fpurrr(top22, rd, rn, rm));
             }
-            &Inst::FpuRRI { fpu_op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                match fpu_op {
-                    FPUOpRI::UShr32(imm) => {
-                        debug_assert_eq!(32, imm.lane_size_in_bits);
-                        sink.put4(
-                            0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000
-                                | imm.enc() << 16
-                                | machreg_to_vec(rn) << 5
-                                | machreg_to_vec(rd.to_reg()),
-                        )
-                    }
-                    FPUOpRI::UShr64(imm) => {
-                        debug_assert_eq!(64, imm.lane_size_in_bits);
-                        sink.put4(
-                            0b01_1_111110_0000000_00_0_0_0_1_00000_00000
-                                | imm.enc() << 16
-                                | machreg_to_vec(rn) << 5
-                                | machreg_to_vec(rd.to_reg()),
-                        )
-                    }
+            &Inst::FpuRRI { fpu_op, rd, rn } => match fpu_op {
+                FPUOpRI::UShr32(imm) => {
+                    debug_assert_eq!(32, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b0_0_1_011110_0000000_00_0_0_0_1_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
                 }
-            }
+                FPUOpRI::UShr64(imm) => {
+                    debug_assert_eq!(64, imm.lane_size_in_bits);
+                    sink.put4(
+                        0b01_1_111110_0000000_00_0_0_0_1_00000_00000
+                            | imm.enc() << 16
+                            | machreg_to_vec(rn) << 5
+                            | machreg_to_vec(rd.to_reg()),
+                    )
+                }
+            },
             &Inst::FpuRRIMod { fpu_op, rd, ri, rn } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
-                let rn = allocs.next(rn);
                 debug_assert_eq!(rd.to_reg(), ri);
                 match fpu_op {
                     FPUOpRIMod::Sli64(imm) => {
@@ -1973,10 +1880,6 @@ impl MachInstEmit for Inst {
                 rm,
                 ra,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let ra = allocs.next(ra);
                 let top17 = match fpu_op {
                     FPUOp3::MAdd => 0b000_11111_00_0_00000_0,
                 };
@@ -1984,8 +1887,6 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
             }
             &Inst::VecMisc { op, rd, rn, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (q, enc_size) = size.enc_size();
                 let (u, bits_12_16, size) = match op {
                     VecMisc2::Not => (0b1, 0b00101, 0b00),
@@ -2140,8 +2041,6 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_vec_rr_misc((q << 1) | u, size, bits_12_16, rd, rn));
             }
             &Inst::VecLanes { op, rd, rn, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (q, size) = match size {
                     VectorSize::Size8x8 => (0b0, 0b00),
                     VectorSize::Size8x16 => (0b1, 0b00),
@@ -2163,8 +2062,6 @@ impl MachInstEmit for Inst {
                 size,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (is_shr, mut template) = match op {
                     VecShiftImmOp::Ushr => (true, 0b_001_011110_0000_000_000001_00000_00000_u32),
                     VecShiftImmOp::Sshr => (true, 0b_000_011110_0000_000_000001_00000_00000_u32),
@@ -2210,10 +2107,7 @@ impl MachInstEmit for Inst {
                 size,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
                 let (is_shr, mut template) = match op {
                     VecShiftImmModOp::Sli => (false, 0b_001_011110_0000_000_010101_00000_00000_u32),
                 };
@@ -2250,9 +2144,6 @@ impl MachInstEmit for Inst {
                 sink.put4(template | (immh_immb << 16) | (rn_enc << 5) | rd_enc);
             }
             &Inst::VecExtract { rd, rn, rm, imm4 } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 if imm4 < 16 {
                     let template = 0b_01_101110_000_00000_0_0000_0_00000_00000_u32;
                     let rm_enc = machreg_to_vec(rm);
@@ -2269,24 +2160,13 @@ impl MachInstEmit for Inst {
                 }
             }
             &Inst::VecTbl { rd, rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let rd = allocs.next_writable(rd);
                 sink.put4(enc_tbl(/* is_extension = */ false, 0b00, rd, rn, rm));
             }
             &Inst::VecTblExt { rd, ri, rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
                 sink.put4(enc_tbl(/* is_extension = */ true, 0b00, rd, rn, rm));
             }
             &Inst::VecTbl2 { rd, rn, rn2, rm } => {
-                let rn = allocs.next(rn);
-                let rn2 = allocs.next(rn2);
-                let rm = allocs.next(rm);
-                let rd = allocs.next_writable(rd);
                 assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
                 sink.put4(enc_tbl(/* is_extension = */ false, 0b01, rd, rn, rm));
             }
@@ -2297,23 +2177,14 @@ impl MachInstEmit for Inst {
                 rn2,
                 rm,
             } => {
-                let rn = allocs.next(rn);
-                let rn2 = allocs.next(rn2);
-                let rm = allocs.next(rm);
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
                 assert_eq!(machreg_to_vec(rn2), (machreg_to_vec(rn) + 1) % 32);
                 sink.put4(enc_tbl(/* is_extension = */ true, 0b01, rd, rn, rm));
             }
             &Inst::FpuCmp { size, rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_fcmp(size, rn, rm));
             }
             &Inst::FpuToInt { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let top16 = match op {
                     // FCVTZS (32/32-bit)
                     FpuToIntOp::F32ToI32 => 0b000_11110_00_1_11_000,
@@ -2335,8 +2206,6 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_fputoint(top16, rd, rn));
             }
             &Inst::IntToFpu { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let top16 = match op {
                     // SCVTF (32/32-bit)
                     IntToFpuOp::I32ToF32 => 0b000_11110_00_1_00_010,
@@ -2358,20 +2227,12 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_inttofpu(top16, rd, rn));
             }
             &Inst::FpuCSel32 { rd, rn, rm, cond } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size32));
             }
             &Inst::FpuCSel64 { rd, rn, rm, cond } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 sink.put4(enc_fcsel(rd, rn, rm, cond, ScalarSize::Size64));
             }
             &Inst::FpuRound { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let top22 = match op {
                     FpuRoundMode::Minus32 => 0b000_11110_00_1_001_010_10000,
                     FpuRoundMode::Minus64 => 0b000_11110_01_1_001_010_10000,
@@ -2385,8 +2246,6 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_fround(top22, rd, rn));
             }
             &Inst::MovToFpu { rd, rn, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let template = match size {
                     ScalarSize::Size32 => 0b000_11110_00_1_00_111_000000_00000_00000,
                     ScalarSize::Size64 => 0b100_11110_01_1_00_111_000000_00000_00000,
@@ -2395,7 +2254,6 @@ impl MachInstEmit for Inst {
                 sink.put4(template | (machreg_to_gpr(rn) << 5) | machreg_to_vec(rd.to_reg()));
             }
             &Inst::FpuMoveFPImm { rd, imm, size } => {
-                let rd = allocs.next_writable(rd);
                 let size_code = match size {
                     ScalarSize::Size32 => 0b00,
                     ScalarSize::Size64 => 0b01,
@@ -2415,10 +2273,7 @@ impl MachInstEmit for Inst {
                 idx,
                 size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
                 let (imm5, shift) = match size.lane_size() {
                     ScalarSize::Size8 => (0b00001, 1),
                     ScalarSize::Size16 => (0b00010, 2),
@@ -2436,8 +2291,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::MovFromVec { rd, rn, idx, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (q, imm5, shift, mask) = match size {
                     ScalarSize::Size8 => (0b0, 0b00001, 1, 0b1111),
                     ScalarSize::Size16 => (0b0, 0b00010, 2, 0b0111),
@@ -2462,8 +2315,6 @@ impl MachInstEmit for Inst {
                 size,
                 scalar_size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (imm5, shift, half) = match size {
                     VectorSize::Size8x8 => (0b00001, 1, true),
                     VectorSize::Size8x16 => (0b00001, 1, false),
@@ -2490,8 +2341,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::VecDup { rd, rn, size } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let q = size.is_128bits() as u32;
                 let imm5 = match size.lane_size() {
                     ScalarSize::Size8 => 0b00001,
@@ -2509,8 +2358,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::VecDupFromFpu { rd, rn, size, lane } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let q = size.is_128bits() as u32;
                 let imm5 = match size.lane_size() {
                     ScalarSize::Size8 => {
@@ -2540,7 +2387,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::VecDupFPImm { rd, imm, size } => {
-                let rd = allocs.next_writable(rd);
                 let imm = imm.enc_bits();
                 let op = match size.lane_size() {
                     ScalarSize::Size32 => 0,
@@ -2557,7 +2403,6 @@ impl MachInstEmit for Inst {
                 invert,
                 size,
             } => {
-                let rd = allocs.next_writable(rd);
                 let (imm, shift, shift_ones) = imm.value();
                 let (op, cmode) = match size.lane_size() {
                     ScalarSize::Size8 => {
@@ -2606,8 +2451,6 @@ impl MachInstEmit for Inst {
                 high_half,
                 lane_size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let immh = match lane_size {
                     ScalarSize::Size16 => 0b001,
                     ScalarSize::Size32 => 0b010,
@@ -2633,8 +2476,6 @@ impl MachInstEmit for Inst {
                 rn,
                 high_half,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (u, size, bits_12_16) = match op {
                     VecRRLongOp::Fcvtl16 => (0b0, 0b00, 0b10111),
                     VecRRLongOp::Fcvtl32 => (0b0, 0b01, 0b10111),
@@ -2664,8 +2505,6 @@ impl MachInstEmit for Inst {
                 lane_size,
                 ..
             } => {
-                let rn = allocs.next(rn);
-                let rd = allocs.next_writable(rd);
                 let high_half = match self {
                     &Inst::VecRRNarrowLow { .. } => false,
                     &Inst::VecRRNarrowHigh { .. } => true,
@@ -2709,10 +2548,7 @@ impl MachInstEmit for Inst {
                 src_idx,
                 size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
                 let (imm5, shift) = match size.lane_size() {
                     ScalarSize::Size8 => (0b00001, 1),
                     ScalarSize::Size16 => (0b00010, 2),
@@ -2734,8 +2570,6 @@ impl MachInstEmit for Inst {
                 );
             }
             &Inst::VecRRPair { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let bits_12_16 = match op {
                     VecPairOp::Addp => 0b11011,
                 };
@@ -2749,9 +2583,6 @@ impl MachInstEmit for Inst {
                 alu_op,
                 high_half,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let (u, size, bit14) = match alu_op {
                     VecRRRLongOp::Smull8 => (0b0, 0b00, 0b1),
                     VecRRRLongOp::Smull16 => (0b0, 0b01, 0b1),
@@ -2778,11 +2609,7 @@ impl MachInstEmit for Inst {
                 alu_op,
                 high_half,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let (u, size, bit14) = match alu_op {
                     VecRRRLongModOp::Umlal8 => (0b1, 0b00, 0b0),
                     VecRRRLongModOp::Umlal16 => (0b1, 0b01, 0b0),
@@ -2799,8 +2626,6 @@ impl MachInstEmit for Inst {
                 ));
             }
             &Inst::VecRRPairLong { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (u, size) = match op {
                     VecRRPairLongOp::Saddlp8 => (0b0, 0b0),
                     VecRRPairLongOp::Uaddlp8 => (0b1, 0b0),
@@ -2817,9 +2642,6 @@ impl MachInstEmit for Inst {
                 alu_op,
                 size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let (q, enc_size) = size.enc_size();
                 let is_float = match alu_op {
                     VecALUOp::Fcmeq
@@ -2928,11 +2750,7 @@ impl MachInstEmit for Inst {
                 alu_op,
                 size,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let (q, _enc_size) = size.enc_size();
 
                 let (top11, bit15_10) = match alu_op {
@@ -2955,11 +2773,7 @@ impl MachInstEmit for Inst {
                 size,
                 idx,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let idx = u32::from(idx);
 
                 let (q, _size) = size.enc_size();
@@ -2991,8 +2805,6 @@ impl MachInstEmit for Inst {
                 size,
                 flags,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (q, size) = size.enc_size();
 
                 if let Some(trap_code) = flags.trap_code() {
@@ -3003,9 +2815,6 @@ impl MachInstEmit for Inst {
                 sink.put4(enc_ldst_vec(q, size, rn, rd));
             }
             &Inst::VecCSel { rd, rn, rm, cond } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 /* Emit this:
                       b.cond  else
                       mov     rd, rm
@@ -3047,11 +2856,9 @@ impl MachInstEmit for Inst {
                 sink.bind_label(out_label, &mut state.ctrl_plane);
             }
             &Inst::MovToNZCV { rn } => {
-                let rn = allocs.next(rn);
                 sink.put4(0xd51b4200 | machreg_to_gpr(rn));
             }
             &Inst::MovFromNZCV { rd } => {
-                let rd = allocs.next_writable(rd);
                 sink.put4(0xd53b4200 | machreg_to_gpr(rd.to_reg()));
             }
             &Inst::Extend {
@@ -3061,8 +2868,6 @@ impl MachInstEmit for Inst {
                 from_bits: 1,
                 to_bits,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 assert!(to_bits <= 64);
                 // Reduce zero-extend-from-1-bit to:
                 // - and rd, rn, #1
@@ -3085,8 +2890,6 @@ impl MachInstEmit for Inst {
                 from_bits: 32,
                 to_bits: 64,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let mov = Inst::Mov {
                     size: OperandSize::Size32,
                     rd,
@@ -3101,8 +2904,6 @@ impl MachInstEmit for Inst {
                 from_bits,
                 to_bits,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let (opc, size) = if signed {
                     (0b00, OperandSize::from_bits(to_bits))
                 } else {
@@ -3163,7 +2964,7 @@ impl MachInstEmit for Inst {
                 if let Some(s) = state.take_stack_map() {
                     sink.add_stack_map(StackMapExtent::UpcomingBytes(4), s);
                 }
-                let rn = allocs.next(info.rn);
+                let rn = info.rn;
                 sink.put4(0b1101011_0001_11111_000000_00000_00000 | (machreg_to_gpr(rn) << 5));
                 if info.opcode.is_call() {
                     sink.add_call_site(info.opcode);
@@ -3195,8 +2996,6 @@ impl MachInstEmit for Inst {
                 start_off = sink.cur_offset();
             }
             &Inst::ReturnCallInd { callee, ref info } => {
-                let callee = allocs.next(callee);
-
                 emit_return_call_common_sequence(&mut allocs, sink, emit_info, state, info);
 
                 Inst::IndirectBr {
@@ -3242,7 +3041,6 @@ impl MachInstEmit for Inst {
                 rn,
                 bit,
             } => {
-                let rn = allocs.next(rn);
                 // Emit the conditional branch first
                 let cond_off = sink.cur_offset();
                 if let Some(l) = taken.as_label() {
@@ -3273,7 +3071,6 @@ impl MachInstEmit for Inst {
                 sink.use_label_at_offset(off, label, LabelUse::Branch19);
             }
             &Inst::IndirectBr { rn, .. } => {
-                let rn = allocs.next(rn);
                 sink.put4(enc_br(rn));
             }
             &Inst::Nop0 => {}
@@ -3291,13 +3088,11 @@ impl MachInstEmit for Inst {
                 sink.put_data(Inst::TRAP_OPCODE);
             }
             &Inst::Adr { rd, off } => {
-                let rd = allocs.next_writable(rd);
                 assert!(off > -(1 << 20));
                 assert!(off < (1 << 20));
                 sink.put4(enc_adr(off, rd));
             }
             &Inst::Adrp { rd, off } => {
-                let rd = allocs.next_writable(rd);
                 assert!(off > -(1 << 20));
                 assert!(off < (1 << 20));
                 sink.put4(enc_adrp(off, rd));
@@ -3316,9 +3111,6 @@ impl MachInstEmit for Inst {
                 ref targets,
                 ..
             } => {
-                let ridx = allocs.next(ridx);
-                let rtmp1 = allocs.next_writable(rtmp1);
-                let rtmp2 = allocs.next_writable(rtmp2);
                 // This sequence is *one* instruction in the vcode, and is expanded only here at
                 // emission time, because we cannot allow the regalloc to insert spills/reloads in
                 // the middle; we depend on hardcoded PC-rel addressing below.
@@ -3402,8 +3194,6 @@ impl MachInstEmit for Inst {
                 ref name,
                 offset,
             } => {
-                let rd = allocs.next_writable(rd);
-
                 if emit_info.0.is_pic() {
                     // See this CE Example for the variations of this with and without BTI & PAUTH
                     // https://godbolt.org/z/ncqjbbvvn
@@ -3451,7 +3241,6 @@ impl MachInstEmit for Inst {
                 }
             }
             &Inst::LoadAddr { rd, ref mem } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
                 let (mem_insts, mem) = mem_finalize(Some(sink), &mem, I8, state);
                 for inst in mem_insts.into_iter() {
@@ -3460,15 +3249,15 @@ impl MachInstEmit for Inst {
 
                 let (reg, index_reg, offset) = match mem {
                     AMode::RegExtended { rn, rm, extendop } => {
-                        let r = allocs.next(rn);
+                        let r = rn;
                         (r, Some((rm, extendop)), 0)
                     }
                     AMode::Unscaled { rn, simm9 } => {
-                        let r = allocs.next(rn);
+                        let r = rn;
                         (r, None, simm9.value())
                     }
                     AMode::UnsignedOffset { rn, uimm12 } => {
-                        let r = allocs.next(rn);
+                        let r = rn;
                         (r, None, uimm12.value() as i32)
                     }
                     _ => panic!("Unsupported case for LoadAddr: {:?}", mem),
@@ -3578,8 +3367,6 @@ impl MachInstEmit for Inst {
                 rd,
                 tmp,
             } => {
-                let rd = allocs.next_writable(rd);
-                let tmp = allocs.next_writable(tmp);
                 assert_eq!(xreg(0), rd.to_reg());
 
                 // See the original proposal for TLSDESC.
@@ -3663,7 +3450,6 @@ impl MachInstEmit for Inst {
                 // blr x1 ; Call the function pointer with the descriptor address in x0
                 // ; x0 now contains the TLV address
 
-                let rd = allocs.next_writable(rd);
                 assert_eq!(xreg(0), rd.to_reg());
                 let rtmp = writable_xreg(1);
 
@@ -3707,8 +3493,6 @@ impl MachInstEmit for Inst {
 
             &Inst::StackProbeLoop { start, end, step } => {
                 assert!(emit_info.0.enable_probestack());
-                let start = allocs.next_writable(start);
-                let end = allocs.next(end);
 
                 // The loop generated here uses `start` as a counter register to
                 // count backwards until negating it exceeds `end`. In other
@@ -3791,14 +3575,14 @@ impl MachInstEmit for Inst {
 }
 
 fn emit_return_call_common_sequence(
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
     sink: &mut MachBuffer<Inst>,
     emit_info: &EmitInfo,
     state: &mut EmitState,
     info: &ReturnCallInfo,
 ) {
     for u in info.uses.iter() {
-        let _ = allocs.next(u.vreg);
+        let _ = u.vreg;
     }
 
     for inst in
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 7bed4c982..7ad8c2e6c 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -2773,7 +2773,6 @@ impl Inst {
                 // this logic between `emit()` and `show_rru()` -- a separate 1-to-N
                 // expansion stage (i.e., legalization, but without the slow edit-in-place
                 // of the existing legalization framework).
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
                 let (mem_insts, mem) = mem_finalize(None, &mem, I8, state);
                 let mut ret = String::new();
diff --git a/cranelift/codegen/src/isa/aarch64/inst/regs.rs b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
index 880fd51a9..803659fea 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/regs.rs
@@ -176,8 +176,7 @@ fn show_reg(reg: Reg) -> String {
     }
 }
 
-pub fn pretty_print_reg(reg: Reg, allocs: &mut AllocationConsumer) -> String {
-    let reg = allocs.next(reg);
+pub fn pretty_print_reg(reg: Reg, _allocs: &mut AllocationConsumer) -> String {
     show_reg(reg)
 }
 
@@ -254,26 +253,23 @@ pub fn show_vreg_element(reg: Reg, idx: u8, size: ScalarSize) -> String {
     format!("{}{}[{}]", s, suffix, idx)
 }
 
-pub fn pretty_print_ireg(reg: Reg, size: OperandSize, allocs: &mut AllocationConsumer) -> String {
-    let reg = allocs.next(reg);
+pub fn pretty_print_ireg(reg: Reg, size: OperandSize, _allocs: &mut AllocationConsumer) -> String {
     show_ireg_sized(reg, size)
 }
 
 pub fn pretty_print_vreg_scalar(
     reg: Reg,
     size: ScalarSize,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> String {
-    let reg = allocs.next(reg);
     show_vreg_scalar(reg, size)
 }
 
 pub fn pretty_print_vreg_vector(
     reg: Reg,
     size: VectorSize,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> String {
-    let reg = allocs.next(reg);
     show_vreg_vector(reg, size)
 }
 
@@ -281,8 +277,7 @@ pub fn pretty_print_vreg_element(
     reg: Reg,
     idx: usize,
     size: ScalarSize,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> String {
-    let reg = allocs.next(reg);
     show_vreg_element(reg, idx as u8, size)
 }
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 6a6704b16..e38a3d110 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -19,6 +19,11 @@
       (rd WritableReg)
       (imm Imm20))
 
+    (Fli
+      (ty Type)
+      (imm FliConstant)
+      (rd WritableReg))
+
     ;; An ALU operation with one register sources and a register destination.
     (FpuRR
       (alu_op FpuOPRR)
@@ -411,6 +416,8 @@
   (QNaN)
 ))
 
+(type FliConstant (primitive FliConstant))
+
 (type FpuOPRR (enum
   ;; RV32F Standard Extension
   (FsqrtS)
@@ -447,8 +454,10 @@
   (FcvtWuD)
   (FcvtDW)
   (FcvtDWU)
-  ;; bitmapip
 
+  ;; Zfa Extension
+  (FroundS)
+  (FroundD)
 ))
 
 (type LoadOP (enum
@@ -1549,6 +1558,20 @@
 (rule (rv_fmaxm $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxmS) $F32 (FRM.RUP) rs1 rs2))
 (rule (rv_fmaxm $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxmD) $F64 (FRM.RUP) rs1 rs2))
 
+;; Helper for emitting the `fround` instruction.
+(decl rv_fround (Type FRM FReg) FReg)
+(rule (rv_fround $F32 frm rs) (fpu_rr (FpuOPRR.FroundS) $F32 frm rs))
+(rule (rv_fround $F64 frm rs) (fpu_rr (FpuOPRR.FroundD) $F64 frm rs))
+
+;; Helper for emitting the `fli` instruction.
+(decl rv_fli (Type FliConstant) FReg)
+(rule (rv_fli ty imm)
+      (let ((dst WritableFReg (temp_writable_freg))
+            (_ Unit (emit (MInst.Fli ty
+                                     imm
+                                     dst))))
+        dst))
+
 ;; `Zba` Extension Instructions
 
 ;; Helper for emitting the `adduw` ("Add Unsigned Word") instruction.
@@ -1778,6 +1801,14 @@
 
 
 
+;; Helper for generating a FliConstant from a u64 constant
+(decl pure partial fli_constant_from_u64 (Type u64) FliConstant)
+(extern constructor fli_constant_from_u64 fli_constant_from_u64)
+
+;; Helper for generating a FliConstant from a u64 negated constant
+(decl pure partial fli_constant_from_negated_u64 (Type u64) FliConstant)
+(extern constructor fli_constant_from_negated_u64 fli_constant_from_negated_u64)
+
 ;; Helper for generating a i64 from a pair of Imm20 and Imm12 constants
 (decl i64_generate_imm (Imm20 Imm12) i64)
 (extern extractor i64_generate_imm i64_generate_imm)
@@ -1795,14 +1826,30 @@
 ;; TODO: Load floats using `fld` instead of `ld`
 (decl imm (Type u64) Reg)
 
-;; Refs get loaded as integers.
-(rule 5 (imm $R32 c) (imm $I32 c))
-(rule 5 (imm $R64 c) (imm $I64 c))
+;; If Zfa is enabled, we can load certain constants with the `fli` instruction.
+(rule 7 (imm (ty_scalar_float ty) imm)
+  (if-let $true (has_zfa))
+  (if-let const (fli_constant_from_u64 ty imm))
+  (rv_fli ty const))
 
-;; Floats get loaded as integers and then moved into an F register.
+;; It is beneficial to load the negated constant with `fli` and then negate it
+;; in a register.
+;;
+;; For f64's this saves one instruction, and for f32's it avoids
+;; having to allocate an integer register, reducing integer register pressure.
+(rule 6 (imm (ty_scalar_float ty) imm)
+  (if-let $true (has_zfa))
+  (if-let const (fli_constant_from_negated_u64 ty imm))
+  (rv_fneg ty (rv_fli ty const)))
+
+;; Otherwise floats get loaded as integers and then moved into an F register.
 (rule 5 (imm $F32 c) (gen_bitcast (imm $I32 c) $I32 $F32))
 (rule 5 (imm $F64 c) (gen_bitcast (imm $I64 c) $I64 $F64))
 
+;; Refs get loaded as integers.
+(rule 5 (imm $R32 c) (imm $I32 c))
+(rule 5 (imm $R64 c) (imm $I64 c))
+
 ;; Try to match just an imm12
 (rule 4 (imm (ty_int ty) c)
   (if-let (i64_generate_imm (imm20_is_zero) imm12) (i64_sextend_u64 ty c))
@@ -2470,7 +2517,7 @@
 (rule (float_round_fcvt $F64 frm rs) (rv_fcvtdl frm (rv_fcvtld frm rs)))
 
 (decl gen_float_round (FRM FReg Type) FReg)
-(rule (gen_float_round frm rs ty)
+(rule 0 (gen_float_round frm rs ty)
   (let (;; if rs is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits
         ;; in mantissa, the result is the same as src, check for these cases first.
         (max FReg (imm ty (float_int_max ty)))
@@ -2491,6 +2538,10 @@
     ;; Check if the value cannot be rounded exactly and return the source input if so
     (gen_select_freg (cmp_eqz exact) corrected_nan rounded)))
 
+;; With Zfa we can use the dedicated `fround` instruction.
+(rule 1 (gen_float_round frm rs ty)
+  (if-let $true (has_zfa))
+  (rv_fround ty frm rs))
 
 
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs
index 561d18da8..d8e59f8b9 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/args.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs
@@ -312,6 +312,116 @@ impl IntegerCompare {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct FliConstant(u8);
+
+impl FliConstant {
+    pub(crate) fn new(value: u8) -> Self {
+        debug_assert!(value <= 31, "Invalid FliConstant: {}", value);
+        Self(value)
+    }
+
+    pub(crate) fn maybe_from_u64(ty: Type, imm: u64) -> Option<Self> {
+        // Convert the value into an F64, this allows us to represent
+        // values from both f32 and f64 in the same value.
+        let value = match ty {
+            F32 => f32::from_bits(imm as u32) as f64,
+            F64 => f64::from_bits(imm),
+            _ => unimplemented!(),
+        };
+
+        Some(match (ty, value) {
+            (_, f) if f == -1.0 => Self::new(0),
+
+            // Since f64 can represent all f32 values, f32::min_positive won't be
+            // the same as f64::min_positive, so we need to check for both indepenendtly
+            (F32, f) if f == (f32::MIN_POSITIVE as f64) => Self::new(1),
+            (F64, f) if f == f64::MIN_POSITIVE => Self::new(1),
+
+            (_, f) if f == 2.0f64.powi(-16) => Self::new(2),
+            (_, f) if f == 2.0f64.powi(-15) => Self::new(3),
+            (_, f) if f == 2.0f64.powi(-8) => Self::new(4),
+            (_, f) if f == 2.0f64.powi(-7) => Self::new(5),
+            (_, f) if f == 0.0625 => Self::new(6),
+            (_, f) if f == 0.125 => Self::new(7),
+            (_, f) if f == 0.25 => Self::new(8),
+            (_, f) if f == 0.3125 => Self::new(9),
+            (_, f) if f == 0.375 => Self::new(10),
+            (_, f) if f == 0.4375 => Self::new(11),
+            (_, f) if f == 0.5 => Self::new(12),
+            (_, f) if f == 0.625 => Self::new(13),
+            (_, f) if f == 0.75 => Self::new(14),
+            (_, f) if f == 0.875 => Self::new(15),
+            (_, f) if f == 1.0 => Self::new(16),
+            (_, f) if f == 1.25 => Self::new(17),
+            (_, f) if f == 1.5 => Self::new(18),
+            (_, f) if f == 1.75 => Self::new(19),
+            (_, f) if f == 2.0 => Self::new(20),
+            (_, f) if f == 2.5 => Self::new(21),
+            (_, f) if f == 3.0 => Self::new(22),
+            (_, f) if f == 4.0 => Self::new(23),
+            (_, f) if f == 8.0 => Self::new(24),
+            (_, f) if f == 16.0 => Self::new(25),
+            (_, f) if f == 128.0 => Self::new(26),
+            (_, f) if f == 256.0 => Self::new(27),
+            (_, f) if f == 32768.0 => Self::new(28),
+            (_, f) if f == 65536.0 => Self::new(29),
+            (_, f) if f == f64::INFINITY => Self::new(30),
+
+            // NaN's are not guaranteed to preserve the sign / payload bits, so we need to check
+            // the original bits directly.
+            (F32, f) if f.is_nan() && imm == 0x7fc0_0000 => Self::new(31), // Canonical NaN
+            (F64, f) if f.is_nan() && imm == 0x7ff8_0000_0000_0000 => Self::new(31), // Canonical NaN
+            _ => return None,
+        })
+    }
+
+    pub(crate) fn format(self) -> &'static str {
+        // The preferred assembly syntax for entries 1, 30, and 31 is min, inf, and nan, respectively.
+        // For entries 0 through 29 (including entry 1), the assembler will accept decimal constants
+        // in C-like syntax.
+        match self.0 {
+            0 => "-1.0",
+            1 => "min",
+            2 => "2^-16",
+            3 => "2^-15",
+            4 => "2^-8",
+            5 => "2^-7",
+            6 => "0.0625",
+            7 => "0.125",
+            8 => "0.25",
+            9 => "0.3125",
+            10 => "0.375",
+            11 => "0.4375",
+            12 => "0.5",
+            13 => "0.625",
+            14 => "0.75",
+            15 => "0.875",
+            16 => "1.0",
+            17 => "1.25",
+            18 => "1.5",
+            19 => "1.75",
+            20 => "2.0",
+            21 => "2.5",
+            22 => "3.0",
+            23 => "4.0",
+            24 => "8.0",
+            25 => "16.0",
+            26 => "128.0",
+            27 => "256.0",
+            28 => "32768.0",
+            29 => "65536.0",
+            30 => "inf",
+            31 => "nan",
+            _ => panic!("Invalid FliConstant"),
+        }
+    }
+
+    pub(crate) fn bits(self) -> u8 {
+        self.0
+    }
+}
+
 impl FpuOPRRRR {
     pub(crate) fn op_name(self) -> &'static str {
         match self {
@@ -376,6 +486,8 @@ impl FpuOPRR {
             Self::FcvtWuD => "fcvt.wu.d",
             Self::FcvtDW => "fcvt.d.w",
             Self::FcvtDWU => "fcvt.d.wu",
+            Self::FroundS => "fround.s",
+            Self::FroundD => "fround.d",
         }
     }
 
@@ -392,14 +504,6 @@ impl FpuOPRR {
             _ => false,
         }
     }
-    // move from x register to float register.
-    pub(crate) fn move_x_to_f_op(ty: Type) -> Self {
-        match ty {
-            F32 => Self::FmvWX,
-            F64 => Self::FmvDX,
-            _ => unreachable!("ty:{:?}", ty),
-        }
-    }
 
     pub(crate) fn op_code(self) -> u32 {
         match self {
@@ -428,7 +532,9 @@ impl FpuOPRR {
             | FpuOPRR::FcvtWD
             | FpuOPRR::FcvtWuD
             | FpuOPRR::FcvtDW
-            | FpuOPRR::FcvtDWU => 0b1010011,
+            | FpuOPRR::FcvtDWU
+            | FpuOPRR::FroundS
+            | FpuOPRR::FroundD => 0b1010011,
         }
     }
 
@@ -460,6 +566,8 @@ impl FpuOPRR {
             FpuOPRR::FcvtDW => 0b00000,
             FpuOPRR::FcvtDWU => 0b00001,
             FpuOPRR::FsqrtD => 0b00000,
+            FpuOPRR::FroundS => 0b00100,
+            FpuOPRR::FroundD => 0b00100,
         }
     }
     pub(crate) fn funct7(self) -> u32 {
@@ -482,8 +590,8 @@ impl FpuOPRR {
             FpuOPRR::FcvtDL => 0b1101001,
             FpuOPRR::FcvtDLu => 0b1101001,
             FpuOPRR::FmvDX => 0b1111001,
-            FpuOPRR::FcvtSD => 0b0100000,
-            FpuOPRR::FcvtDS => 0b0100001,
+            FpuOPRR::FcvtSD | FpuOPRR::FroundS => 0b0100000,
+            FpuOPRR::FcvtDS | FpuOPRR::FroundD => 0b0100001,
             FpuOPRR::FclassD => 0b1110001,
             FpuOPRR::FcvtWD => 0b1100001,
             FpuOPRR::FcvtWuD => 0b1100001,
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index f7318af99..f3a5ea4d3 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -149,6 +149,7 @@ impl Inst {
             | Inst::Nop4
             | Inst::BrTable { .. }
             | Inst::Auipc { .. }
+            | Inst::Fli { .. }
             | Inst::Lui { .. }
             | Inst::LoadInlineConst { .. }
             | Inst::AluRRR { .. }
@@ -875,6 +876,9 @@ impl Inst {
                 let x: u32 = 0b0110111 | reg_to_gpr_num(rd.to_reg()) << 7 | (imm.bits() << 12);
                 sink.put4(x);
             }
+            &Inst::Fli { rd, ty, imm } => {
+                sink.put4(encode_fli(ty, imm, rd));
+            }
             &Inst::LoadInlineConst { rd, ty, imm } => {
                 let data = &imm.to_le_bytes()[..ty.bytes() as usize];
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/encode.rs b/cranelift/codegen/src/isa/riscv64/inst/encode.rs
index 4ad7d46e3..801d10f32 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/encode.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/encode.rs
@@ -652,3 +652,24 @@ pub fn encode_zcbmem_load(op: ZcbMemOp, rd: WritableReg, base: Reg, imm: Uimm2)
 pub fn encode_zcbmem_store(op: ZcbMemOp, src: Reg, base: Reg, imm: Uimm2) -> u16 {
     encode_zcbmem_bits(op, src, base, imm)
 }
+
+pub fn encode_fli(ty: Type, imm: FliConstant, rd: WritableReg) -> u32 {
+    // FLI.{S,D} is encoded as a FMV.{W,D} instruction with rs2 set to the
+    // immediate value to be loaded.
+    let op = match ty {
+        F32 => FpuOPRR::FmvWX,
+        F64 => FpuOPRR::FmvDX,
+        _ => unreachable!(),
+    };
+    let frm = 0; // FRM is hard coded to 0 in both instructions
+    let rs2 = 1; // rs2 set to 1 is what differentiates FLI from FMV
+
+    let mut bits = 0;
+    bits |= unsigned_field_width(op.op_code(), 7);
+    bits |= reg_to_gpr_num(rd.to_reg()) << 7;
+    bits |= unsigned_field_width(frm, 3) << 12;
+    bits |= unsigned_field_width(imm.bits() as u32, 5) << 15;
+    bits |= unsigned_field_width(rs2, 6) << 20;
+    bits |= unsigned_field_width(op.funct7(), 7) << 25;
+    bits
+}
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index 33fd8c04c..c4c14dc1f 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -236,42 +236,6 @@ impl Inst {
             .expect("code range is too big.")
     }
 
-    /// Create instructions that load a 32-bit floating-point constant.
-    pub fn load_fp_constant32<F: FnMut(Type) -> Writable<Reg>>(
-        rd: Writable<Reg>,
-        const_data: u32,
-        mut alloc_tmp: F,
-    ) -> SmallVec<[Inst; 4]> {
-        let mut insts = SmallVec::new();
-        let tmp = alloc_tmp(I64);
-        insts.extend(Self::load_constant_u32(tmp, const_data as u64));
-        insts.push(Inst::FpuRR {
-            frm: FRM::RNE,
-            alu_op: FpuOPRR::move_x_to_f_op(F32),
-            rd,
-            rs: tmp.to_reg(),
-        });
-        insts
-    }
-
-    /// Create instructions that load a 64-bit floating-point constant.
-    pub fn load_fp_constant64<F: FnMut(Type) -> Writable<Reg>>(
-        rd: Writable<Reg>,
-        const_data: u64,
-        mut alloc_tmp: F,
-    ) -> SmallVec<[Inst; 4]> {
-        let mut insts = SmallInstVec::new();
-        let tmp = alloc_tmp(I64);
-        insts.extend(Self::load_constant_u64(tmp, const_data));
-        insts.push(Inst::FpuRR {
-            frm: FRM::RNE,
-            alu_op: FpuOPRR::move_x_to_f_op(F64),
-            rd,
-            rs: tmp.to_reg(),
-        });
-        insts
-    }
-
     /// Generic constructor for a load (zero-extending where appropriate).
     pub fn gen_load(into_reg: Writable<Reg>, mem: AMode, ty: Type, flags: MemFlags) -> Inst {
         if ty.is_vector() {
@@ -346,6 +310,7 @@ fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
         }
         Inst::Auipc { rd, .. } => collector.reg_def(rd),
         Inst::Lui { rd, .. } => collector.reg_def(rd),
+        Inst::Fli { rd, .. } => collector.reg_def(rd),
         Inst::LoadInlineConst { rd, .. } => collector.reg_def(rd),
         Inst::AluRRR { rd, rs1, rs2, .. } => {
             collector.reg_use(rs1);
@@ -929,10 +894,7 @@ pub fn reg_name(reg: Reg) -> String {
 
 impl Inst {
     fn print_with_state(&self, _state: &mut EmitState, allocs: &mut AllocationConsumer) -> String {
-        let format_reg = |reg: Reg, allocs: &mut AllocationConsumer| -> String {
-            let reg = allocs.next(reg);
-            reg_name(reg)
-        };
+        let format_reg = |reg: Reg, _allocs: &mut AllocationConsumer| -> String { reg_name(reg) };
 
         let format_vec_amode = |amode: &VecAMode, _allocs: &mut AllocationConsumer| -> String {
             match amode {
@@ -1172,6 +1134,17 @@ impl Inst {
                     imm.as_i32()
                 )
             }
+            &Inst::Fli { rd, ty, imm } => {
+                let rd_s = format_reg(rd.to_reg(), allocs);
+                let imm_s = imm.format();
+                let suffix = match ty {
+                    F32 => "s",
+                    F64 => "d",
+                    _ => unreachable!(),
+                };
+
+                format!("fli.{suffix} {rd_s},{imm_s}")
+            }
             &Inst::LoadInlineConst { rd, imm, .. } => {
                 let rd = format_reg(rd.to_reg(), allocs);
                 let mut buf = String::new();
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 64f32bd47..f55ecd958 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -189,6 +189,20 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
         Imm12::from_i16(imm.as_i16() & (x as i16))
     }
 
+    fn fli_constant_from_u64(&mut self, ty: Type, imm: u64) -> Option<FliConstant> {
+        FliConstant::maybe_from_u64(ty, imm)
+    }
+
+    fn fli_constant_from_negated_u64(&mut self, ty: Type, imm: u64) -> Option<FliConstant> {
+        let negated_imm = match ty {
+            F64 => imm ^ 0x8000000000000000,
+            F32 => imm ^ 0x80000000,
+            _ => unimplemented!(),
+        };
+
+        FliConstant::maybe_from_u64(ty, negated_imm)
+    }
+
     fn i64_generate_imm(&mut self, imm: i64) -> Option<(Imm20, Imm12)> {
         MInst::generate_imm(imm as u64)
     }
diff --git a/cranelift/codegen/src/isa/s390x/inst/args.rs b/cranelift/codegen/src/isa/s390x/inst/args.rs
index 294ac2113..293a7b081 100644
--- a/cranelift/codegen/src/isa/s390x/inst/args.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/args.rs
@@ -216,13 +216,11 @@ impl Cond {
 }
 
 impl PrettyPrint for MemArg {
-    fn pretty_print(&self, _: u8, allocs: &mut AllocationConsumer) -> String {
+    fn pretty_print(&self, _: u8, _allocs: &mut AllocationConsumer) -> String {
         match self {
             &MemArg::BXD12 {
                 base, index, disp, ..
             } => {
-                let base = allocs.next(base);
-                let index = allocs.next(index);
                 if base != zero_reg() {
                     if index != zero_reg() {
                         format!(
@@ -245,8 +243,6 @@ impl PrettyPrint for MemArg {
             &MemArg::BXD20 {
                 base, index, disp, ..
             } => {
-                let base = allocs.next(base);
-                let index = allocs.next(index);
                 if base != zero_reg() {
                     if index != zero_reg() {
                         format!(
diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs
index cee096d92..2e6b30d8b 100644
--- a/cranelift/codegen/src/isa/s390x/inst/emit.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs
@@ -1417,10 +1417,6 @@ impl Inst {
 
         match self {
             &Inst::AluRRR { alu_op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, have_rr) = match alu_op {
                     ALUOp::Add32 => (0xb9f8, true),        // ARK
                     ALUOp::Add64 => (0xb9e8, true),        // AGRK
@@ -1468,9 +1464,6 @@ impl Inst {
                 rn,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 if rd.to_reg() == rn {
                     let inst = Inst::AluRSImm16 {
                         alu_op,
@@ -1489,10 +1482,7 @@ impl Inst {
                 }
             }
             &Inst::AluRR { alu_op, rd, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 let (opcode, is_rre) = match alu_op {
                     ALUOp::Add32 => (0x1a, false),              // AR
@@ -1530,8 +1520,6 @@ impl Inst {
                 ri,
                 ref mem,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
                 let mem = mem.clone();
 
@@ -1576,8 +1564,6 @@ impl Inst {
                 ri,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match alu_op {
@@ -1595,8 +1581,6 @@ impl Inst {
                 ri,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match alu_op {
@@ -1614,8 +1598,6 @@ impl Inst {
                 ri,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match alu_op {
@@ -1633,8 +1615,6 @@ impl Inst {
                 ri,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match (alu_op, imm.shift) {
@@ -1660,8 +1640,6 @@ impl Inst {
                 ri,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match (alu_op, imm.shift) {
@@ -1680,32 +1658,26 @@ impl Inst {
             }
 
             &Inst::SMulWide { rd, rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
 
                 let opcode = 0xb9ec; // MGRK
                 put(sink, &enc_rrf_ab(opcode, rd1.to_reg(), rn, rm, 0));
             }
             &Inst::UMulWide { rd, ri, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd2.to_reg(), ri);
 
                 let opcode = 0xb986; // MLGR
                 put(sink, &enc_rre(opcode, rd1.to_reg(), rn));
             }
             &Inst::SDivMod32 { rd, ri, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd2.to_reg(), ri);
 
                 let opcode = 0xb91d; // DSGFR
@@ -1713,11 +1685,9 @@ impl Inst {
                 put_with_trap(sink, &enc_rre(opcode, rd1.to_reg(), rn), trap_code);
             }
             &Inst::SDivMod64 { rd, ri, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd2.to_reg(), ri);
 
                 let opcode = 0xb90d; // DSGR
@@ -1725,12 +1695,11 @@ impl Inst {
                 put_with_trap(sink, &enc_rre(opcode, rd1.to_reg(), rn), trap_code);
             }
             &Inst::UDivMod32 { rd, ri, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
-                let ri1 = allocs.next(ri.hi);
-                let ri2 = allocs.next(ri.lo);
+                let ri1 = ri.hi;
+                let ri2 = ri.lo;
                 debug_assert_eq!(rd1.to_reg(), ri1);
                 debug_assert_eq!(rd2.to_reg(), ri2);
 
@@ -1739,12 +1708,11 @@ impl Inst {
                 put_with_trap(sink, &enc_rre(opcode, rd1.to_reg(), rn), trap_code);
             }
             &Inst::UDivMod64 { rd, ri, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
-                let ri1 = allocs.next(ri.hi);
-                let ri2 = allocs.next(ri.lo);
+                let ri1 = ri.hi;
+                let ri2 = ri.lo;
                 debug_assert_eq!(rd1.to_reg(), ri1);
                 debug_assert_eq!(rd2.to_reg(), ri2);
 
@@ -1753,9 +1721,8 @@ impl Inst {
                 put_with_trap(sink, &enc_rre(opcode, rd1.to_reg(), rn), trap_code);
             }
             &Inst::Flogr { rd, rn } => {
-                let rn = allocs.next(rn);
-                let rd1 = allocs.next_writable(rd.hi);
-                let rd2 = allocs.next_writable(rd.lo);
+                let rd1 = rd.hi;
+                let rd2 = rd.lo;
                 debug_assert_valid_regpair!(rd1.to_reg(), rd2.to_reg());
 
                 let opcode = 0xb983; // FLOGR
@@ -1769,10 +1736,6 @@ impl Inst {
                 shift_imm,
                 shift_reg,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let shift_reg = allocs.next(shift_reg);
-
                 let opcode = match shift_op {
                     ShiftOp::RotL32 => 0xeb1d, // RLL
                     ShiftOp::RotL64 => 0xeb1c, // RLLG
@@ -1798,10 +1761,7 @@ impl Inst {
                 end_bit,
                 rotate_amt,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
 
                 let opcode = match op {
                     RxSBGOp::Insert => 0xec59, // RISBGN
@@ -1830,9 +1790,6 @@ impl Inst {
                 end_bit,
                 rotate_amt,
             } => {
-                let rd = allocs.next(rd);
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     RxSBGOp::And => 0xec54, // RNSBG
                     RxSBGOp::Or => 0xec56,  // ROSBG
@@ -1853,9 +1810,6 @@ impl Inst {
             }
 
             &Inst::UnaryRR { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 match op {
                     UnaryOp::Abs32 => {
                         let opcode = 0x10; // LPR
@@ -1907,9 +1861,6 @@ impl Inst {
                 from_bits,
                 to_bits,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let opcode = match (signed, from_bits, to_bits) {
                     (_, 1, 32) => 0xb926,      // LBR
                     (_, 1, 64) => 0xb906,      // LGBR
@@ -1932,9 +1883,6 @@ impl Inst {
             }
 
             &Inst::CmpRR { op, rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, is_rre) = match op {
                     CmpOp::CmpS32 => (0x19, false),       // CR
                     CmpOp::CmpS64 => (0xb920, true),      // CGR
@@ -1951,7 +1899,6 @@ impl Inst {
                 }
             }
             &Inst::CmpRX { op, rn, ref mem } => {
-                let rn = allocs.next(rn);
                 let mem = mem.clone();
 
                 let (opcode_rx, opcode_rxy, opcode_ril) = match op {
@@ -1971,8 +1918,6 @@ impl Inst {
                 );
             }
             &Inst::CmpRSImm16 { op, rn, imm } => {
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     CmpOp::CmpS32 => 0xa7e, // CHI
                     CmpOp::CmpS64 => 0xa7f, // CGHI
@@ -1981,8 +1926,6 @@ impl Inst {
                 put(sink, &enc_ri_a(opcode, rn, imm as u16));
             }
             &Inst::CmpRSImm32 { op, rn, imm } => {
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     CmpOp::CmpS32 => 0xc2d, // CFI
                     CmpOp::CmpS64 => 0xc2c, // CGFI
@@ -1991,8 +1934,6 @@ impl Inst {
                 put(sink, &enc_ril_a(opcode, rn, imm as u32));
             }
             &Inst::CmpRUImm32 { op, rn, imm } => {
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     CmpOp::CmpL32 => 0xc2f, // CLFI
                     CmpOp::CmpL64 => 0xc2e, // CLGFI
@@ -2007,9 +1948,6 @@ impl Inst {
                 cond,
                 trap_code,
             } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let opcode = match op {
                     CmpOp::CmpS32 => 0xb972, // CRT
                     CmpOp::CmpS64 => 0xb960, // CGRT
@@ -2030,8 +1968,6 @@ impl Inst {
                 cond,
                 trap_code,
             } => {
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     CmpOp::CmpS32 => 0xec72, // CIT
                     CmpOp::CmpS64 => 0xec70, // CGIT
@@ -2050,8 +1986,6 @@ impl Inst {
                 cond,
                 trap_code,
             } => {
-                let rn = allocs.next(rn);
-
                 let opcode = match op {
                     CmpOp::CmpL32 => 0xec73, // CLFIT
                     CmpOp::CmpL64 => 0xec71, // CLGIT
@@ -2066,8 +2000,6 @@ impl Inst {
                 rn,
                 ref mem,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
                 let mem = mem.clone();
 
                 let opcode = match alu_op {
@@ -2142,10 +2074,7 @@ impl Inst {
                 rn,
                 ref mem,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
                 let mem = mem.clone();
 
                 let (opcode_rs, opcode_rsy) = match self {
@@ -2178,7 +2107,6 @@ impl Inst {
             | &Inst::LoadRev16 { rd, ref mem }
             | &Inst::LoadRev32 { rd, ref mem }
             | &Inst::LoadRev64 { rd, ref mem } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
 
                 let (opcode_rx, opcode_rxy, opcode_ril) = match self {
@@ -2212,7 +2140,6 @@ impl Inst {
             | &Inst::StoreRev16 { rd, ref mem }
             | &Inst::StoreRev32 { rd, ref mem }
             | &Inst::StoreRev64 { rd, ref mem } => {
-                let rd = allocs.next(rd);
                 let mem = mem.clone();
 
                 let (opcode_rx, opcode_rxy, opcode_ril) = match self {
@@ -2298,7 +2225,6 @@ impl Inst {
             }
 
             &Inst::LoadAddr { rd, ref mem } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
 
                 let opcode_rx = Some(0x41); // LA
@@ -2311,70 +2237,47 @@ impl Inst {
             }
 
             &Inst::Mov64 { rd, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rm = allocs.next(rm);
-
                 let opcode = 0xb904; // LGR
                 put(sink, &enc_rre(opcode, rd.to_reg(), rm));
             }
             &Inst::MovPReg { rd, rm } => {
                 let rm: Reg = rm.into();
                 debug_assert!([regs::gpr(0), regs::gpr(14), regs::gpr(15)].contains(&rm));
-                let rd = allocs.next_writable(rd);
                 Inst::Mov64 { rd, rm }.emit(&[], sink, emit_info, state);
             }
             &Inst::Mov32 { rd, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rm = allocs.next(rm);
-
                 let opcode = 0x18; // LR
                 put(sink, &enc_rr(opcode, rd.to_reg(), rm));
             }
             &Inst::Mov32Imm { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xc09; // IILF
                 put(sink, &enc_ril_a(opcode, rd.to_reg(), imm));
             }
             &Inst::Mov32SImm16 { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa78; // LHI
                 put(sink, &enc_ri_a(opcode, rd.to_reg(), imm as u16));
             }
             &Inst::Mov64SImm16 { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa79; // LGHI
                 put(sink, &enc_ri_a(opcode, rd.to_reg(), imm as u16));
             }
             &Inst::Mov64SImm32 { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xc01; // LGFI
                 put(sink, &enc_ril_a(opcode, rd.to_reg(), imm as u32));
             }
             &Inst::CMov32 { rd, cond, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 let opcode = 0xb9f2; // LOCR
                 put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rm, cond.bits(), 0));
             }
             &Inst::CMov64 { rd, cond, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 let opcode = 0xb9e2; // LOCGR
                 put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rm, cond.bits(), 0));
             }
             &Inst::CMov32SImm16 { rd, cond, ri, imm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = 0xec42; // LOCHI
@@ -2384,8 +2287,6 @@ impl Inst {
                 );
             }
             &Inst::CMov64SImm16 { rd, cond, ri, imm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = 0xec46; // LOCGHI
@@ -2395,8 +2296,6 @@ impl Inst {
                 );
             }
             &Inst::Mov64UImm16Shifted { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = match imm.shift {
                     0 => 0xa5f, // LLILL
                     1 => 0xa5e, // LLILH
@@ -2407,8 +2306,6 @@ impl Inst {
                 put(sink, &enc_ri_a(opcode, rd.to_reg(), imm.bits));
             }
             &Inst::Mov64UImm32Shifted { rd, imm } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = match imm.shift {
                     0 => 0xc0f, // LLILF
                     1 => 0xc0e, // LLIHF
@@ -2417,8 +2314,6 @@ impl Inst {
                 put(sink, &enc_ril_a(opcode, rd.to_reg(), imm.bits));
             }
             &Inst::Insert64UImm16Shifted { rd, ri, imm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match imm.shift {
@@ -2431,8 +2326,6 @@ impl Inst {
                 put(sink, &enc_ri_a(opcode, rd.to_reg(), imm.bits));
             }
             &Inst::Insert64UImm32Shifted { rd, ri, imm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match imm.shift {
@@ -2443,14 +2336,11 @@ impl Inst {
                 put(sink, &enc_ril_a(opcode, rd.to_reg(), imm.bits));
             }
             &Inst::LoadAR { rd, ar } => {
-                let rd = allocs.next_writable(rd);
                 let opcode = 0xb24f; // EAR
                 put(sink, &enc_rre(opcode, rd.to_reg(), gpr(ar)));
             }
 
             &Inst::InsertAR { rd, ri, ar } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = 0xb24f; // EAR
@@ -2460,8 +2350,6 @@ impl Inst {
                 rd,
                 ref symbol_reloc,
             } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa75; // BRAS
                 let reg = writable_spilltmp_reg().to_reg();
                 put(sink, &enc_ri_b(opcode, reg, 12));
@@ -2479,9 +2367,6 @@ impl Inst {
             }
 
             &Inst::FpuMove32 { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 if is_fpr(rd.to_reg()) && is_fpr(rn) {
                     let opcode = 0x38; // LER
                     put(sink, &enc_rr(opcode, rd.to_reg(), rn));
@@ -2491,9 +2376,6 @@ impl Inst {
                 }
             }
             &Inst::FpuMove64 { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 if is_fpr(rd.to_reg()) && is_fpr(rn) {
                     let opcode = 0x28; // LDR
                     put(sink, &enc_rr(opcode, rd.to_reg(), rn));
@@ -2503,10 +2385,7 @@ impl Inst {
                 }
             }
             &Inst::FpuCMov32 { rd, cond, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 if is_fpr(rd.to_reg()) && is_fpr(rm) {
                     let opcode = 0xa74; // BCR
@@ -2521,10 +2400,7 @@ impl Inst {
                 }
             }
             &Inst::FpuCMov64 { rd, cond, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 if is_fpr(rd.to_reg()) && is_fpr(rm) {
                     let opcode = 0xa74; // BCR
@@ -2539,8 +2415,6 @@ impl Inst {
                 }
             }
             &Inst::LoadFpuConst32 { rd, const_data } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa75; // BRAS
                 let reg = writable_spilltmp_reg().to_reg();
                 put(sink, &enc_ri_b(opcode, reg, 8));
@@ -2554,8 +2428,6 @@ impl Inst {
                 inst.emit(&[], sink, emit_info, state);
             }
             &Inst::LoadFpuConst64 { rd, const_data } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa75; // BRAS
                 let reg = writable_spilltmp_reg().to_reg();
                 put(sink, &enc_ri_b(opcode, reg, 12));
@@ -2569,9 +2441,6 @@ impl Inst {
                 inst.emit(&[], sink, emit_info, state);
             }
             &Inst::FpuRR { fpu_op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let (opcode, m3, m4, m5, opcode_fpr) = match fpu_op {
                     FPUOp1::Abs32 => (0xe7cc, 2, 8, 2, Some(0xb300)), // WFPSO, LPEBR
                     FPUOp1::Abs64 => (0xe7cc, 3, 8, 2, Some(0xb310)), // WFPSO, LPDBR
@@ -2599,10 +2468,6 @@ impl Inst {
                 }
             }
             &Inst::FpuRRR { fpu_op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, m4, m5, m6, opcode_fpr) = match fpu_op {
                     FPUOp2::Add32 => (0xe7e3, 2, 8, 0, Some(0xb30a)), // WFA, AEBR
                     FPUOp2::Add64 => (0xe7e3, 3, 8, 0, Some(0xb31a)), // WFA, ADBR
@@ -2651,11 +2516,6 @@ impl Inst {
                 rm,
                 ra,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let ra = allocs.next(ra);
-
                 let (opcode, m5, m6, opcode_fpr) = match fpu_op {
                     FPUOp3::MAdd32 => (0xe78f, 8, 2, Some(0xb30e)), // WFMA, MAEBR
                     FPUOp3::MAdd64 => (0xe78f, 8, 3, Some(0xb31e)), // WFMA, MADBR
@@ -2673,9 +2533,6 @@ impl Inst {
                 }
             }
             &Inst::FpuRound { op, mode, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let mode = match mode {
                     FpuRoundMode::Current => 0,
                     FpuRoundMode::ToNearest => 1,
@@ -2719,9 +2576,6 @@ impl Inst {
                 }
             }
             &Inst::FpuCmp32 { rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 if is_fpr(rn) && is_fpr(rm) {
                     let opcode = 0xb309; // CEBR
                     put(sink, &enc_rre(opcode, rn, rm));
@@ -2731,9 +2585,6 @@ impl Inst {
                 }
             }
             &Inst::FpuCmp64 { rn, rm } => {
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 if is_fpr(rn) && is_fpr(rm) {
                     let opcode = 0xb319; // CDBR
                     put(sink, &enc_rre(opcode, rn, rm));
@@ -2744,10 +2595,6 @@ impl Inst {
             }
 
             &Inst::VecRRR { op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, m4) = match op {
                     VecBinaryOp::Add8x16 => (0xe7f3, 0),       // VAB
                     VecBinaryOp::Add16x8 => (0xe7f3, 1),       // VAH
@@ -2841,9 +2688,6 @@ impl Inst {
                 put(sink, &enc_vrr_c(opcode, rd.to_reg(), rn, rm, m4, 0, 0));
             }
             &Inst::VecRR { op, rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let (opcode, m3) = match op {
                     VecUnaryOp::Abs8x16 => (0xe7df, 0),         // VLPB
                     VecUnaryOp::Abs16x8 => (0xe7df, 1),         // VLPH
@@ -2888,10 +2732,6 @@ impl Inst {
                 shift_imm,
                 shift_reg,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let shift_reg = allocs.next(shift_reg);
-
                 let (opcode, m4) = match shift_op {
                     VecShiftOp::RotL8x16 => (0xe733, 0), // VERLLB
                     VecShiftOp::RotL16x8 => (0xe733, 1), // VERLLH
@@ -2916,20 +2756,10 @@ impl Inst {
                 );
             }
             &Inst::VecSelect { rd, rn, rm, ra } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let ra = allocs.next(ra);
-
                 let opcode = 0xe78d; // VSEL
                 put(sink, &enc_vrr_e(opcode, rd.to_reg(), rn, rm, ra, 0, 0));
             }
             &Inst::VecPermute { rd, rn, rm, ra } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-                let ra = allocs.next(ra);
-
                 let opcode = 0xe78c; // VPERM
                 put(sink, &enc_vrr_e(opcode, rd.to_reg(), rn, rm, ra, 0, 0));
             }
@@ -2940,19 +2770,12 @@ impl Inst {
                 idx1,
                 idx2,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
                 let m4 = (idx1 & 1) * 4 + (idx2 & 1);
 
                 let opcode = 0xe784; // VPDI
                 put(sink, &enc_vrr_c(opcode, rd.to_reg(), rn, rm, m4, 0, 0));
             }
             &Inst::VecIntCmp { op, rd, rn, rm } | &Inst::VecIntCmpS { op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, m4) = match op {
                     VecIntCmpOp::CmpEq8x16 => (0xe7f8, 0),  // VCEQB
                     VecIntCmpOp::CmpEq16x8 => (0xe7f8, 1),  // VCEQH
@@ -2976,10 +2799,6 @@ impl Inst {
                 put(sink, &enc_vrr_b(opcode, rd.to_reg(), rn, rm, m4, m5));
             }
             &Inst::VecFloatCmp { op, rd, rn, rm } | &Inst::VecFloatCmpS { op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (opcode, m4) = match op {
                     VecFloatCmpOp::CmpEq32x4 => (0xe7e8, 2),   // VFCESB
                     VecFloatCmpOp::CmpEq64x2 => (0xe7e8, 3),   // VFCEDB
@@ -2999,9 +2818,6 @@ impl Inst {
             &Inst::VecInt128SCmpHi { tmp, rn, rm } | &Inst::VecInt128UCmpHi { tmp, rn, rm } => {
                 // Synthetic instruction to compare 128-bit values.
                 // Sets CC 1 if rn > rm, sets a different CC otherwise.
-                let tmp = allocs.next_writable(tmp);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
 
                 // Use VECTOR ELEMENT COMPARE to compare the high parts.
                 // Swap the inputs to get:
@@ -3044,7 +2860,6 @@ impl Inst {
             | &Inst::VecLoadElt16Rev { rd, ref mem }
             | &Inst::VecLoadElt32Rev { rd, ref mem }
             | &Inst::VecLoadElt64Rev { rd, ref mem } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
 
                 let (opcode, m3) = match self {
@@ -3068,7 +2883,6 @@ impl Inst {
             | &Inst::VecStoreElt16Rev { rd, ref mem }
             | &Inst::VecStoreElt32Rev { rd, ref mem }
             | &Inst::VecStoreElt64Rev { rd, ref mem } => {
-                let rd = allocs.next(rd);
                 let mem = mem.clone();
 
                 let (opcode, m3) = match self {
@@ -3086,7 +2900,6 @@ impl Inst {
             }
             &Inst::VecLoadReplicate { size, rd, ref mem }
             | &Inst::VecLoadReplicateRev { size, rd, ref mem } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
 
                 let (opcode, m3) = match (self, size) {
@@ -3103,17 +2916,11 @@ impl Inst {
             }
 
             &Inst::VecMov { rd, rn } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let opcode = 0xe756; // VLR
                 put(sink, &enc_vrr_a(opcode, rd.to_reg(), rn, 0, 0, 0));
             }
             &Inst::VecCMov { rd, cond, ri, rm } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rm = allocs.next(rm);
 
                 let opcode = 0xa74; // BCR
                 put(sink, &enc_ri_c(opcode, cond.invert().bits(), 4 + 6));
@@ -3121,16 +2928,10 @@ impl Inst {
                 put(sink, &enc_vrr_a(opcode, rd.to_reg(), rm, 0, 0, 0));
             }
             &Inst::MovToVec128 { rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let opcode = 0xe762; // VLVGP
                 put(sink, &enc_vrr_f(opcode, rd.to_reg(), rn, rm));
             }
             &Inst::VecLoadConst { rd, const_data } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa75; // BRAS
                 let reg = writable_spilltmp_reg().to_reg();
                 put(sink, &enc_ri_b(opcode, reg, 20));
@@ -3148,8 +2949,6 @@ impl Inst {
                 rd,
                 const_data,
             } => {
-                let rd = allocs.next_writable(rd);
-
                 let opcode = 0xa75; // BRAS
                 let reg = writable_spilltmp_reg().to_reg();
                 put(sink, &enc_ri_b(opcode, reg, (4 + size / 8) as i32));
@@ -3164,7 +2963,6 @@ impl Inst {
                 inst.emit(&[], sink, emit_info, state);
             }
             &Inst::VecImmByteMask { rd, mask } => {
-                let rd = allocs.next_writable(rd);
                 let opcode = 0xe744; // VGBM
                 put(sink, &enc_vri_a(opcode, rd.to_reg(), mask, 0));
             }
@@ -3174,7 +2972,6 @@ impl Inst {
                 start_bit,
                 end_bit,
             } => {
-                let rd = allocs.next_writable(rd);
                 let (opcode, m4) = match size {
                     8 => (0xe746, 0),  // VGMB
                     16 => (0xe746, 1), // VGMH
@@ -3188,7 +2985,6 @@ impl Inst {
                 );
             }
             &Inst::VecImmReplicate { size, rd, imm } => {
-                let rd = allocs.next_writable(rd);
                 let (opcode, m3) = match size {
                     8 => (0xe745, 0),  // VREPIB
                     16 => (0xe745, 1), // VREPIH
@@ -3212,8 +3008,6 @@ impl Inst {
                 ref mem,
                 lane_imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
                 let mem = mem.clone();
 
@@ -3252,7 +3046,6 @@ impl Inst {
                 ref mem,
                 lane_imm,
             } => {
-                let rd = allocs.next_writable(rd);
                 let mem = mem.clone();
 
                 let (opcode_vrx, opcode_rx, opcode_rxy) = match (self, size) {
@@ -3296,7 +3089,6 @@ impl Inst {
                 ref mem,
                 lane_imm,
             } => {
-                let rd = allocs.next(rd);
                 let mem = mem.clone();
 
                 let (opcode_vrx, opcode_rx, opcode_rxy) = match (self, size) {
@@ -3335,11 +3127,7 @@ impl Inst {
                 lane_imm,
                 lane_reg,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
-                let rn = allocs.next(rn);
-                let lane_reg = allocs.next(lane_reg);
 
                 let (opcode_vrs, m4) = match size {
                     8 => (0xe722, 0),  // VLVGB
@@ -3360,10 +3148,6 @@ impl Inst {
                 lane_imm,
                 lane_reg,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let lane_reg = allocs.next(lane_reg);
-
                 let (opcode_vrs, m4, opcode_rre) = match size {
                     8 => (0xe722, 0, None),          // VLVGB
                     16 => (0xe722, 1, None),         // VLVGH
@@ -3391,10 +3175,6 @@ impl Inst {
                 lane_imm,
                 lane_reg,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let lane_reg = allocs.next(lane_reg);
-
                 let (opcode_vrs, m4, opcode_rre) = match size {
                     8 => (0xe721, 0, None),          // VLGVB
                     16 => (0xe721, 1, None),         // VLGVH
@@ -3418,8 +3198,6 @@ impl Inst {
                 imm,
                 lane_imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let ri = allocs.next(ri);
                 debug_assert_eq!(rd.to_reg(), ri);
 
                 let opcode = match size {
@@ -3440,9 +3218,6 @@ impl Inst {
                 rn,
                 lane_imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 let (opcode, m4) = match size {
                     8 => (0xe74d, 0),  // VREPB
                     16 => (0xe74d, 1), // VREPH
@@ -3485,7 +3260,7 @@ impl Inst {
             }
             &Inst::CallInd { link, ref info } => {
                 debug_assert_eq!(link.to_reg(), gpr(14));
-                let rn = allocs.next(info.rn);
+                let rn = info.rn;
 
                 let opcode = 0x0d; // BASR
                 if let Some(s) = state.take_stack_map() {
@@ -3513,8 +3288,6 @@ impl Inst {
                 put(sink, &enc_ril_c(opcode, 15, 0));
             }
             &Inst::IndirectBr { rn, .. } => {
-                let rn = allocs.next(rn);
-
                 let opcode = 0x07; // BCR
                 put(sink, &enc_rr(opcode, gpr(15), rn));
             }
@@ -3572,8 +3345,6 @@ impl Inst {
                 put(sink, &enc[4..6]);
             }
             &Inst::JTSequence { ridx, ref targets } => {
-                let ridx = allocs.next(ridx);
-
                 let table_label = sink.get_label();
 
                 // This sequence is *one* instruction in the vcode, and is expanded only here at
diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs
index 8fb823cc8..3784dd593 100644
--- a/cranelift/codegen/src/isa/s390x/inst/mod.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs
@@ -1192,10 +1192,6 @@ impl Inst {
             &Inst::Nop0 => "nop-zero-len".to_string(),
             &Inst::Nop2 => "nop".to_string(),
             &Inst::AluRRR { alu_op, rd, rn, rm } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-                let rm = allocs.next(rm);
-
                 let (op, have_rr) = match alu_op {
                     ALUOp::Add32 => ("ark", true),
                     ALUOp::Add64 => ("agrk", true),
@@ -1245,9 +1241,6 @@ impl Inst {
                 rn,
                 imm,
             } => {
-                let rd = allocs.next_writable(rd);
-                let rn = allocs.next(rn);
-
                 if rd.to_reg() == rn {
                     let inst = Inst::AluRSImm16 {
                         alu_op,
@@ -2109,7 +2102,7 @@ impl Inst {
             }
             &Inst::FpuCMov32 { rd, cond, ri, rm } => {
                 let (rd, rd_fpr) = pretty_print_fpr(rd.to_reg(), allocs);
-                let _ri = allocs.next(ri);
+                let _ri = ri;
                 let (rm, rm_fpr) = pretty_print_fpr(rm, allocs);
                 if rd_fpr.is_some() && rm_fpr.is_some() {
                     let cond = cond.invert().pretty_print_default();
@@ -2121,7 +2114,7 @@ impl Inst {
             }
             &Inst::FpuCMov64 { rd, cond, ri, rm } => {
                 let (rd, rd_fpr) = pretty_print_fpr(rd.to_reg(), allocs);
-                let _ri = allocs.next(ri);
+                let _ri = ri;
                 let (rm, rm_fpr) = pretty_print_fpr(rm, allocs);
                 if rd_fpr.is_some() && rm_fpr.is_some() {
                     let cond = cond.invert().pretty_print_default();
@@ -2856,7 +2849,7 @@ impl Inst {
                 };
 
                 let (rd, _) = pretty_print_fpr(rd.to_reg(), allocs);
-                let _ri = allocs.next(ri);
+                let _ri = ri;
                 let mem = mem.clone();
                 let (mem_str, mem) = mem_finalize_for_show(
                     &mem,
diff --git a/cranelift/codegen/src/isa/s390x/inst/regs.rs b/cranelift/codegen/src/isa/s390x/inst/regs.rs
index 29a4ef8c3..40abbb6b4 100644
--- a/cranelift/codegen/src/isa/s390x/inst/regs.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/regs.rs
@@ -99,14 +99,13 @@ pub fn maybe_show_fpr(reg: Reg) -> Option<String> {
     None
 }
 
-pub fn pretty_print_reg(reg: Reg, allocs: &mut AllocationConsumer) -> String {
-    let reg = allocs.next(reg);
+pub fn pretty_print_reg(reg: Reg, _allocs: &mut AllocationConsumer) -> String {
     show_reg(reg)
 }
 
-pub fn pretty_print_regpair(pair: RegPair, allocs: &mut AllocationConsumer) -> String {
-    let hi = allocs.next(pair.hi);
-    let lo = allocs.next(pair.lo);
+pub fn pretty_print_regpair(pair: RegPair, _allocs: &mut AllocationConsumer) -> String {
+    let hi = pair.hi;
+    let lo = pair.lo;
     if let Some(hi_reg) = hi.to_real_reg() {
         if let Some(lo_reg) = lo.to_real_reg() {
             assert!(
@@ -122,9 +121,13 @@ pub fn pretty_print_regpair(pair: RegPair, allocs: &mut AllocationConsumer) -> S
     format!("{}/{}", show_reg(hi), show_reg(lo))
 }
 
-pub fn pretty_print_reg_mod(rd: Writable<Reg>, ri: Reg, allocs: &mut AllocationConsumer) -> String {
-    let output = allocs.next_writable(rd).to_reg();
-    let input = allocs.next(ri);
+pub fn pretty_print_reg_mod(
+    rd: Writable<Reg>,
+    ri: Reg,
+    _allocs: &mut AllocationConsumer,
+) -> String {
+    let output = rd.to_reg();
+    let input = ri;
     if output == input {
         show_reg(output)
     } else {
@@ -135,12 +138,12 @@ pub fn pretty_print_reg_mod(rd: Writable<Reg>, ri: Reg, allocs: &mut AllocationC
 pub fn pretty_print_regpair_mod(
     rd: WritableRegPair,
     ri: RegPair,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> String {
-    let rd_hi = allocs.next(rd.hi.to_reg());
-    let rd_lo = allocs.next(rd.lo.to_reg());
-    let ri_hi = allocs.next(ri.hi);
-    let ri_lo = allocs.next(ri.lo);
+    let rd_hi = rd.hi.to_reg();
+    let rd_lo = rd.lo.to_reg();
+    let ri_hi = ri.hi;
+    let ri_lo = ri.lo;
     if rd_hi == ri_hi {
         show_reg(rd_hi)
     } else {
@@ -157,11 +160,10 @@ pub fn pretty_print_regpair_mod(
 pub fn pretty_print_regpair_mod_lo(
     rd: WritableRegPair,
     ri: Reg,
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
 ) -> String {
-    let rd_hi = allocs.next(rd.hi.to_reg());
-    let rd_lo = allocs.next(rd.lo.to_reg());
-    let ri = allocs.next(ri);
+    let rd_hi = rd.hi.to_reg();
+    let rd_lo = rd.lo.to_reg();
     if rd_lo == ri {
         show_reg(rd_hi)
     } else {
@@ -174,7 +176,6 @@ pub fn pretty_print_regpair_mod_lo(
     }
 }
 
-pub fn pretty_print_fpr(reg: Reg, allocs: &mut AllocationConsumer) -> (String, Option<String>) {
-    let reg = allocs.next(reg);
+pub fn pretty_print_fpr(reg: Reg, _allocs: &mut AllocationConsumer) -> (String, Option<String>) {
     (show_reg(reg), maybe_show_fpr(reg))
 }
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index 3305e231f..92d70d592 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -153,8 +153,8 @@ pub(crate) fn emit(
             src2,
             dst: reg_g,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let reg_g = allocs.next(reg_g.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let reg_g = reg_g.to_reg().to_reg();
             debug_assert_eq!(src1, reg_g);
             let src2 = src2.clone().to_reg_mem_imm().clone();
 
@@ -237,8 +237,7 @@ pub(crate) fn emit(
         }
 
         &Inst::AluConstOp { op, size, dst } => {
-            let dst = WritableGpr::from_writable_reg(allocs.next_writable(dst.to_writable_reg()))
-                .unwrap();
+            let dst = WritableGpr::from_writable_reg(dst.to_writable_reg()).unwrap();
             emit(
                 &Inst::AluRmiR {
                     size,
@@ -260,7 +259,7 @@ pub(crate) fn emit(
             src2,
             op,
         } => {
-            let src2 = allocs.next(src2.to_reg());
+            let src2 = src2.to_reg();
             let src1_dst = src1_dst.finalize(state, sink).clone();
 
             let opcode = match op {
@@ -303,8 +302,8 @@ pub(crate) fn emit(
             use AluRmROpcode::*;
             use LegacyPrefixes as LP;
 
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -340,7 +339,7 @@ pub(crate) fn emit(
         }
 
         Inst::UnaryRmR { size, op, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let rex_flags = RexFlags::from(*size);
             use UnaryRmROpcode::*;
             let prefix = match size {
@@ -365,7 +364,6 @@ pub(crate) fn emit(
 
             match src.clone().into() {
                 RegMem::Reg { reg: src } => {
-                    let src = allocs.next(src);
                     emit_std_reg_reg(sink, prefix, opcode, num_opcodes, dst, src, rex_flags);
                 }
                 RegMem::Mem { addr: src } => {
@@ -376,7 +374,7 @@ pub(crate) fn emit(
         }
 
         Inst::UnaryRmRVex { size, op, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -407,7 +405,7 @@ pub(crate) fn emit(
             dst,
             imm,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -431,8 +429,8 @@ pub(crate) fn emit(
         }
 
         Inst::Not { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src, dst);
             let rex_flags = RexFlags::from((*size, dst));
             let (opcode, prefix) = match size {
@@ -448,8 +446,8 @@ pub(crate) fn emit(
         }
 
         Inst::Neg { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src, dst);
             let rex_flags = RexFlags::from((*size, dst));
             let (opcode, prefix) = match size {
@@ -486,10 +484,10 @@ pub(crate) fn emit(
                     dst_remainder,
                     ..
                 } => {
-                    let dividend_lo = allocs.next(dividend_lo.to_reg());
-                    let dividend_hi = allocs.next(dividend_hi.to_reg());
-                    let dst_quotient = allocs.next(dst_quotient.to_reg().to_reg());
-                    let dst_remainder = allocs.next(dst_remainder.to_reg().to_reg());
+                    let dividend_lo = dividend_lo.to_reg();
+                    let dividend_hi = dividend_hi.to_reg();
+                    let dst_quotient = dst_quotient.to_reg().to_reg();
+                    let dst_remainder = dst_remainder.to_reg().to_reg();
                     debug_assert_eq!(dividend_lo, regs::rax());
                     debug_assert_eq!(dividend_hi, regs::rdx());
                     debug_assert_eq!(dst_quotient, regs::rax());
@@ -497,8 +495,8 @@ pub(crate) fn emit(
                     *size
                 }
                 Inst::Div8 { dividend, dst, .. } => {
-                    let dividend = allocs.next(dividend.to_reg());
-                    let dst = allocs.next(dst.to_reg().to_reg());
+                    let dividend = dividend.to_reg();
+                    let dst = dst.to_reg().to_reg();
                     debug_assert_eq!(dividend, regs::rax());
                     debug_assert_eq!(dst, regs::rax());
                     OperandSize::Size8
@@ -556,9 +554,9 @@ pub(crate) fn emit(
             dst_lo,
             dst_hi,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst_lo = allocs.next(dst_lo.to_reg().to_reg());
-            let dst_hi = allocs.next(dst_hi.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst_lo = dst_lo.to_reg().to_reg();
+            let dst_hi = dst_hi.to_reg().to_reg();
             debug_assert_eq!(src1, regs::rax());
             debug_assert_eq!(dst_lo, regs::rax());
             debug_assert_eq!(dst_hi, regs::rdx());
@@ -590,8 +588,8 @@ pub(crate) fn emit(
             src2,
             dst,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src1, regs::rax());
             debug_assert_eq!(dst, regs::rax());
             let src2 = src2.clone().to_reg_mem().clone();
@@ -630,8 +628,8 @@ pub(crate) fn emit(
             src2,
             dst,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src1, dst);
             let src2 = src2.clone().to_reg_mem().clone();
 
@@ -655,7 +653,7 @@ pub(crate) fn emit(
             src2,
             dst,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src1 = src1.clone().to_reg_mem().clone();
 
             let rex = RexFlags::from(*size);
@@ -689,8 +687,8 @@ pub(crate) fn emit(
         }
 
         Inst::SignExtendData { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src, regs::rax());
             if *size == OperandSize::Size8 {
                 debug_assert_eq!(dst, regs::rax());
@@ -715,7 +713,7 @@ pub(crate) fn emit(
         }
 
         Inst::CheckedSRemSeq { divisor, .. } | Inst::CheckedSRemSeq8 { divisor, .. } => {
-            let divisor = allocs.next(divisor.to_reg());
+            let divisor = divisor.to_reg();
 
             // Validate that the register constraints of the dividend and the
             // destination are all as expected.
@@ -728,10 +726,10 @@ pub(crate) fn emit(
                     size,
                     ..
                 } => {
-                    let dividend_lo = allocs.next(dividend_lo.to_reg());
-                    let dividend_hi = allocs.next(dividend_hi.to_reg());
-                    let dst_quotient = allocs.next(dst_quotient.to_reg().to_reg());
-                    let dst_remainder = allocs.next(dst_remainder.to_reg().to_reg());
+                    let dividend_lo = dividend_lo.to_reg();
+                    let dividend_hi = dividend_hi.to_reg();
+                    let dst_quotient = dst_quotient.to_reg().to_reg();
+                    let dst_remainder = dst_remainder.to_reg().to_reg();
                     debug_assert_eq!(dividend_lo, regs::rax());
                     debug_assert_eq!(dividend_hi, regs::rdx());
                     debug_assert_eq!(dst_quotient, regs::rax());
@@ -739,8 +737,8 @@ pub(crate) fn emit(
                     (regs::rdx(), *size)
                 }
                 Inst::CheckedSRemSeq8 { dividend, dst, .. } => {
-                    let dividend = allocs.next(dividend.to_reg());
-                    let dst = allocs.next(dst.to_reg().to_reg());
+                    let dividend = dividend.to_reg();
+                    let dst = dst.to_reg().to_reg();
                     debug_assert_eq!(dividend, regs::rax());
                     debug_assert_eq!(dst, regs::rax());
                     (regs::rax(), OperandSize::Size8)
@@ -817,7 +815,7 @@ pub(crate) fn emit(
             simm64,
             dst,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let enc_dst = int_reg_enc(dst);
             if *dst_size == OperandSize::Size64 {
                 if low32_will_sign_extend_to_64(*simm64) {
@@ -873,8 +871,8 @@ pub(crate) fn emit(
         }
 
         Inst::MovRR { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             emit_std_reg_reg(
                 sink,
                 LegacyPrefixes::None,
@@ -891,13 +889,12 @@ pub(crate) fn emit(
             debug_assert!([regs::rsp(), regs::rbp(), regs::pinned_reg()].contains(&src));
             let src = Gpr::new(src).unwrap();
             let size = OperandSize::Size64;
-            let dst = WritableGpr::from_writable_reg(allocs.next_writable(dst.to_writable_reg()))
-                .unwrap();
+            let dst = WritableGpr::from_writable_reg(dst.to_writable_reg()).unwrap();
             Inst::MovRR { size, src, dst }.emit(&[], sink, info, state);
         }
 
         Inst::MovToPReg { src, dst } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let src = Gpr::new(src).unwrap();
             let dst: Reg = (*dst).into();
             debug_assert!([regs::rsp(), regs::rbp(), regs::pinned_reg()].contains(&dst));
@@ -907,7 +904,7 @@ pub(crate) fn emit(
         }
 
         Inst::MovzxRmR { ext_mode, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let (opcodes, num_opcodes, mut rex_flags) = match ext_mode {
                 ExtMode::BL => {
                     // MOVZBL is (REX.W==0) 0F B6 /r
@@ -941,7 +938,6 @@ pub(crate) fn emit(
 
             match src.clone().to_reg_mem() {
                 RegMem::Reg { reg: src } => {
-                    let src = allocs.next(src);
                     match ext_mode {
                         ExtMode::BL | ExtMode::BQ => {
                             // A redundant REX prefix must be emitted for certain register inputs.
@@ -978,7 +974,7 @@ pub(crate) fn emit(
         }
 
         Inst::Mov64MR { src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = &src.finalize(state, sink).clone();
 
             emit_std_reg_mem(
@@ -994,7 +990,7 @@ pub(crate) fn emit(
         }
 
         Inst::LoadEffectiveAddress { addr, dst, size } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let amode = addr.finalize(state, sink).clone();
 
             // If this `lea` can actually get encoded as an `add` then do that
@@ -1074,7 +1070,7 @@ pub(crate) fn emit(
         }
 
         Inst::MovsxRmR { ext_mode, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let (opcodes, num_opcodes, mut rex_flags) = match ext_mode {
                 ExtMode::BL => {
                     // MOVSBL is (REX.W==0) 0F BE /r
@@ -1100,7 +1096,6 @@ pub(crate) fn emit(
 
             match src.clone().to_reg_mem() {
                 RegMem::Reg { reg: src } => {
-                    let src = allocs.next(src);
                     match ext_mode {
                         ExtMode::BL | ExtMode::BQ => {
                             // A redundant REX prefix must be emitted for certain register inputs.
@@ -1137,7 +1132,7 @@ pub(crate) fn emit(
         }
 
         Inst::MovRM { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let dst = &dst.finalize(state, sink).clone();
 
             let prefix = match size {
@@ -1169,8 +1164,8 @@ pub(crate) fn emit(
             num_bits,
             dst,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src, dst);
             let subopcode = match kind {
                 ShiftKind::RotateLeft => 0,
@@ -1183,7 +1178,6 @@ pub(crate) fn emit(
             let rex_flags = RexFlags::from((*size, dst));
             match num_bits.as_imm8_reg() {
                 &Imm8Reg::Reg { reg } => {
-                    let reg = allocs.next(reg);
                     debug_assert_eq!(reg, regs::rcx());
                     let (opcode, prefix) = match size {
                         OperandSize::Size8 => (0xD2, LegacyPrefixes::None),
@@ -1225,8 +1219,8 @@ pub(crate) fn emit(
             src2,
             dst,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src1, dst);
             let rex = RexFlags::clear_w();
             let prefix = LegacyPrefixes::_66;
@@ -1264,7 +1258,6 @@ pub(crate) fn emit(
 
                 match src2 {
                     RegMemImm::Reg { reg } => {
-                        let reg = allocs.next(reg);
                         emit_std_reg_reg(sink, prefix, opcode_bytes, 2, dst, reg, rex);
                     }
                     RegMemImm::Mem { addr } => {
@@ -1282,7 +1275,7 @@ pub(crate) fn emit(
             src2: src_e,
             opcode,
         } => {
-            let reg_g = allocs.next(reg_g.to_reg());
+            let reg_g = reg_g.to_reg();
 
             let is_cmp = match opcode {
                 CmpOpcode::Cmp => true,
@@ -1298,7 +1291,6 @@ pub(crate) fn emit(
 
             match src_e.clone().to_reg_mem_imm() {
                 RegMemImm::Reg { reg: reg_e } => {
-                    let reg_e = allocs.next(reg_e);
                     if *size == OperandSize::Size8 {
                         // Check whether the E register forces the use of a redundant REX.
                         rex.always_emit_if_8bit_needed(reg_e);
@@ -1358,7 +1350,7 @@ pub(crate) fn emit(
         }
 
         Inst::Setcc { cc, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let opcode = 0x0f90 + cc.get_enc() as u32;
             let mut rex_flags = RexFlags::clear_w();
             rex_flags.always_emit();
@@ -1374,8 +1366,8 @@ pub(crate) fn emit(
         }
 
         Inst::Bswap { size, src, dst } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src, dst);
             let enc_reg = int_reg_enc(dst);
 
@@ -1395,8 +1387,8 @@ pub(crate) fn emit(
             alternative,
             dst,
         } => {
-            let alternative = allocs.next(alternative.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let alternative = alternative.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(alternative, dst);
             let rex_flags = RexFlags::from(*size);
             let prefix = match size {
@@ -1408,7 +1400,6 @@ pub(crate) fn emit(
             let opcode = 0x0F40 + cc.get_enc() as u32;
             match consequent.clone().to_reg_mem() {
                 RegMem::Reg { reg } => {
-                    let reg = allocs.next(reg);
                     emit_std_reg_reg(sink, prefix, opcode, 2, dst, reg, rex_flags);
                 }
                 RegMem::Mem { addr } => {
@@ -1425,10 +1416,10 @@ pub(crate) fn emit(
             alternative,
             dst,
         } => {
-            let alternative = allocs.next(alternative.to_reg());
-            let dst = allocs.next_writable(dst.to_writable_reg());
+            let alternative = alternative.to_reg();
+            let dst = dst.to_writable_reg();
             debug_assert_eq!(alternative, dst.to_reg());
-            let consequent = allocs.next(consequent.clone().to_reg());
+            let consequent = consequent.clone().to_reg();
 
             // Lowering of the Select IR opcode when the input is an fcmp relies on the fact that
             // this doesn't clobber flags. Make sure to not do so here.
@@ -1493,7 +1484,7 @@ pub(crate) fn emit(
         }
 
         Inst::Pop64 { dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let enc_dst = int_reg_enc(dst);
             if enc_dst >= 8 {
                 // 0x41 == REX.{W=0, B=1}.  It seems that REX.W is irrelevant here.
@@ -1510,7 +1501,7 @@ pub(crate) fn emit(
             assert!(info.flags.enable_probestack());
             assert!(guard_size.is_power_of_two());
 
-            let tmp = allocs.next_writable(*tmp);
+            let tmp = *tmp;
 
             // Number of probes that we need to perform
             let probe_count = align_to(*frame_size, *guard_size) / guard_size;
@@ -1647,7 +1638,7 @@ pub(crate) fn emit(
             callee,
             info: call_info,
         } => {
-            let callee = allocs.next(*callee);
+            let callee = *callee;
 
             emit_return_call_common_sequence(allocs, sink, info, state, &call_info);
 
@@ -1814,10 +1805,6 @@ pub(crate) fn emit(
             ref default_target,
             ..
         } => {
-            let idx = allocs.next(idx);
-            let tmp1 = allocs.next_writable(tmp1);
-            let tmp2 = allocs.next_writable(tmp2);
-
             // This sequence is *one* instruction in the vcode, and is expanded only here at
             // emission time, because we cannot allow the regalloc to insert spills/reloads in
             // the middle; we depend on hardcoded PC-rel addressing below.
@@ -1935,7 +1922,7 @@ pub(crate) fn emit(
             src: src_e,
             dst: reg_g,
         } => {
-            let reg_g = allocs.next(reg_g.to_reg().to_reg());
+            let reg_g = reg_g.to_reg().to_reg();
             let src_e = src_e.clone().to_reg_mem().clone();
 
             let rex = RexFlags::clear_w();
@@ -1988,7 +1975,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmUnaryRmRImm { op, src, dst, imm } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = src.clone().to_reg_mem().clone();
             let rex = RexFlags::clear_w();
 
@@ -2016,7 +2003,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmUnaryRmREvex { op, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2043,7 +2030,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmUnaryRmRImmEvex { op, src, dst, imm } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2093,8 +2080,8 @@ pub(crate) fn emit(
             src2: src_e,
             dst: reg_g,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let reg_g = allocs.next(reg_g.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let reg_g = reg_g.to_reg().to_reg();
             let src_e = src_e.clone().to_reg_mem().clone();
             debug_assert_eq!(src1, reg_g);
 
@@ -2229,10 +2216,10 @@ pub(crate) fn emit(
             dst,
             mask,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let mask = allocs.next(mask.to_reg());
+            let src1 = src1.to_reg();
+            let mask = mask.to_reg();
             debug_assert_eq!(mask, regs::xmm0());
-            let reg_g = allocs.next(dst.to_reg().to_reg());
+            let reg_g = dst.to_reg().to_reg();
             debug_assert_eq!(src1, reg_g);
             let src_e = src2.clone().to_reg_mem().clone();
 
@@ -2264,8 +2251,8 @@ pub(crate) fn emit(
             use LegacyPrefixes as LP;
             use OpcodeMap as OM;
 
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = src2.clone().to_reg_mem_imm().clone();
 
             // When the opcode is commutative, src1 is xmm{0..7}, and src2 is
@@ -2454,8 +2441,8 @@ pub(crate) fn emit(
             dst,
             imm,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2493,8 +2480,8 @@ pub(crate) fn emit(
             dst,
             imm,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2530,10 +2517,10 @@ pub(crate) fn emit(
             src3,
             dst,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(src1, dst);
-            let src2 = allocs.next(src2.to_reg());
+            let src2 = src2.to_reg();
             let src3 = match src3.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2583,15 +2570,15 @@ pub(crate) fn emit(
             mask,
             dst,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
                 }
                 RegMem::Mem { addr } => RegisterOrAmode::Amode(addr.finalize(state, sink)),
             };
-            let mask = allocs.next(mask.to_reg());
+            let mask = mask.to_reg();
 
             let opcode = match op {
                 AvxOpcode::Vblendvps => 0x4A,
@@ -2613,7 +2600,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmUnaryRmRVex { op, src, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2677,7 +2664,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmUnaryRmRImmVex { op, src, dst, imm } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2717,7 +2704,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmMovRMVex { op, src, dst } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let dst = dst.clone().finalize(state, sink);
 
             let (prefix, map, opcode) = match op {
@@ -2739,7 +2726,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmMovRMImmVex { op, src, dst, imm } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let dst = dst.clone().finalize(state, sink);
 
             let (w, prefix, map, opcode) = match op {
@@ -2762,8 +2749,8 @@ pub(crate) fn emit(
         }
 
         Inst::XmmToGprImmVex { op, src, dst, imm } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
 
             let (w, prefix, map, opcode) = match op {
                 AvxOpcode::Vpextrb => (false, LegacyPrefixes::_66, OpcodeMap::_0F3A, 0x14),
@@ -2790,8 +2777,8 @@ pub(crate) fn emit(
             dst,
             dst_size,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
 
             let (prefix, map, opcode) = match op {
                 // vmovd/vmovq are differentiated by `w`
@@ -2830,7 +2817,7 @@ pub(crate) fn emit(
             dst,
             src_size,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             let src = match src.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2859,7 +2846,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmCmpRmRVex { op, src1, src2 } => {
-            let src1 = allocs.next(src1.to_reg());
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -2898,17 +2885,17 @@ pub(crate) fn emit(
             dst,
         } => {
             let reused_src = match inst {
-                Inst::XmmRmREvex3 { src1, .. } => Some(allocs.next(src1.to_reg())),
+                Inst::XmmRmREvex3 { src1, .. } => Some(src1.to_reg()),
                 _ => None,
             };
-            let src1 = allocs.next(src1.to_reg());
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
                 }
                 RegMem::Mem { addr } => RegisterOrAmode::Amode(addr.finalize(state, sink)),
             };
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             if let Some(src1) = reused_src {
                 debug_assert_eq!(src1, dst);
             }
@@ -2939,9 +2926,9 @@ pub(crate) fn emit(
             rhs,
             dst,
         } => {
-            let rhs = allocs.next(rhs.to_reg());
-            let lhs = allocs.next(lhs.to_reg());
-            let dst = allocs.next_writable(dst.to_writable_reg());
+            let rhs = rhs.to_reg();
+            let lhs = lhs.to_reg();
+            let dst = dst.to_writable_reg();
             debug_assert_eq!(rhs, dst.to_reg());
 
             // Generates the following sequence:
@@ -3034,8 +3021,8 @@ pub(crate) fn emit(
             imm,
             size,
         } => {
-            let src1 = allocs.next(*src1);
-            let dst = allocs.next(dst.to_reg());
+            let src1 = *src1;
+            let dst = dst.to_reg();
             let src2 = src2.clone();
             debug_assert_eq!(src1, dst);
 
@@ -3089,7 +3076,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmMovRM { op, src, dst } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let dst = dst.clone();
 
             let (prefix, opcode) = match op {
@@ -3107,7 +3094,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmMovRMImm { op, src, dst, imm } => {
-            let src = allocs.next(src.to_reg());
+            let src = src.to_reg();
             let dst = dst.clone();
 
             let (w, prefix, opcode) = match op {
@@ -3133,8 +3120,8 @@ pub(crate) fn emit(
             dst,
             dst_size,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
 
             let (prefix, opcode, dst_first) = match op {
                 SseOpcode::Cvttss2si => (LegacyPrefixes::_F3, 0x0F2C, true),
@@ -3156,8 +3143,8 @@ pub(crate) fn emit(
         Inst::XmmToGprImm { op, src, dst, imm } => {
             use OperandSize as OS;
 
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src = src.to_reg();
+            let dst = dst.to_reg().to_reg();
 
             let (prefix, opcode, opcode_bytes, dst_size, dst_first) = match op {
                 SseOpcode::Pextrb => (LegacyPrefixes::_66, 0x0F3A14, 3, OS::Size32, false),
@@ -3179,7 +3166,7 @@ pub(crate) fn emit(
             dst: reg_g,
             src_size,
         } => {
-            let reg_g = allocs.next(reg_g.to_reg().to_reg());
+            let reg_g = reg_g.to_reg().to_reg();
             let src_e = src_e.clone().to_reg_mem().clone();
 
             let (prefix, opcode) = match op {
@@ -3201,7 +3188,7 @@ pub(crate) fn emit(
         }
 
         Inst::XmmCmpRmR { op, src1, src2 } => {
-            let src1 = allocs.next(src1.to_reg());
+            let src1 = src1.to_reg();
             let src2 = src2.clone().to_reg_mem().clone();
 
             let rex = RexFlags::clear_w();
@@ -3230,8 +3217,8 @@ pub(crate) fn emit(
             dst,
             src2_size,
         } => {
-            let src1 = allocs.next(src1.to_reg());
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let src1 = src1.to_reg();
+            let dst = dst.to_reg().to_reg();
             assert_eq!(src1, dst);
             let src2 = src2.clone().to_reg_mem().clone();
 
@@ -3259,8 +3246,8 @@ pub(crate) fn emit(
             dst,
             src2_size,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
-            let src1 = allocs.next(src1.to_reg());
+            let dst = dst.to_reg().to_reg();
+            let src1 = src1.to_reg();
             let src2 = match src2.clone().to_reg_mem().clone() {
                 RegMem::Reg { reg } => {
                     RegisterOrAmode::Register(reg.to_real_reg().unwrap().hw_enc().into())
@@ -3296,10 +3283,10 @@ pub(crate) fn emit(
             tmp_gpr1,
             tmp_gpr2,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next_writable(dst.to_writable_reg());
-            let tmp_gpr1 = allocs.next_writable(tmp_gpr1.to_writable_reg());
-            let tmp_gpr2 = allocs.next_writable(tmp_gpr2.to_writable_reg());
+            let src = src.to_reg();
+            let dst = dst.to_writable_reg();
+            let tmp_gpr1 = tmp_gpr1.to_writable_reg();
+            let tmp_gpr2 = tmp_gpr2.to_writable_reg();
 
             // Note: this sequence is specific to 64-bit mode; a 32-bit mode would require a
             // different sequence.
@@ -3418,10 +3405,10 @@ pub(crate) fn emit(
             tmp_gpr,
             tmp_xmm,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next_writable(dst.to_writable_reg());
-            let tmp_gpr = allocs.next_writable(tmp_gpr.to_writable_reg());
-            let tmp_xmm = allocs.next_writable(tmp_xmm.to_writable_reg());
+            let src = src.to_reg();
+            let dst = dst.to_writable_reg();
+            let tmp_gpr = tmp_gpr.to_writable_reg();
+            let tmp_xmm = tmp_xmm.to_writable_reg();
 
             // Emits the following common sequence:
             //
@@ -3601,11 +3588,11 @@ pub(crate) fn emit(
             tmp_xmm,
             tmp_xmm2,
         } => {
-            let src = allocs.next(src.to_reg());
-            let dst = allocs.next_writable(dst.to_writable_reg());
-            let tmp_gpr = allocs.next_writable(tmp_gpr.to_writable_reg());
-            let tmp_xmm = allocs.next_writable(tmp_xmm.to_writable_reg());
-            let tmp_xmm2 = allocs.next_writable(tmp_xmm2.to_writable_reg());
+            let src = src.to_reg();
+            let dst = dst.to_writable_reg();
+            let tmp_gpr = tmp_gpr.to_writable_reg();
+            let tmp_xmm = tmp_xmm.to_writable_reg();
+            let tmp_xmm2 = tmp_xmm2.to_writable_reg();
 
             // The only difference in behavior between saturating and non-saturating is how we
             // handle errors. Emits the following sequence:
@@ -3801,7 +3788,7 @@ pub(crate) fn emit(
             offset,
             distance,
         } => {
-            let dst = allocs.next(dst.to_reg());
+            let dst = dst.to_reg();
 
             if info.flags.is_pic() {
                 // Generates: movq symbol@GOTPCREL(%rip), %dst
@@ -3854,9 +3841,9 @@ pub(crate) fn emit(
             mem,
             dst_old,
         } => {
-            let replacement = allocs.next(*replacement);
-            let expected = allocs.next(*expected);
-            let dst_old = allocs.next(dst_old.to_reg());
+            let replacement = *replacement;
+            let expected = *expected;
+            let dst_old = dst_old.to_reg();
             let mem = mem.clone();
 
             debug_assert_eq!(expected, regs::rax());
@@ -3884,9 +3871,9 @@ pub(crate) fn emit(
             temp,
             dst_old,
         } => {
-            let operand = allocs.next(*operand);
-            let temp = allocs.next_writable(*temp);
-            let dst_old = allocs.next_writable(*dst_old);
+            let operand = *operand;
+            let temp = *temp;
+            let dst_old = *dst_old;
             debug_assert_eq!(dst_old.to_reg(), regs::rax());
             let mem = mem.finalize(state, sink).clone();
 
@@ -4106,7 +4093,7 @@ pub(crate) fn emit(
         }
 
         Inst::ElfTlsGetAddr { ref symbol, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(dst, regs::rax());
 
             // N.B.: Must be exactly this byte sequence; the linker requires it,
@@ -4135,7 +4122,7 @@ pub(crate) fn emit(
         }
 
         Inst::MachOTlsGetAddr { ref symbol, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(dst, regs::rax());
 
             // movq gv@tlv(%rip), %rdi
@@ -4155,11 +4142,11 @@ pub(crate) fn emit(
             dst,
             tmp,
         } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
+            let dst = dst.to_reg().to_reg();
             debug_assert_eq!(dst, regs::rax());
 
             // tmp is used below directly as %rcx
-            let tmp = allocs.next(tmp.to_reg().to_reg());
+            let tmp = tmp.to_reg().to_reg();
             debug_assert_eq!(tmp, regs::rcx());
 
             // See: https://gcc.godbolt.org/z/M8or9x6ss
@@ -4231,7 +4218,7 @@ pub(crate) fn emit(
 ///
 /// * Move the return address into its stack slot.
 fn emit_return_call_common_sequence(
-    allocs: &mut AllocationConsumer,
+    _allocs: &mut AllocationConsumer,
     sink: &mut MachBuffer<Inst>,
     info: &EmitInfo,
     state: &mut EmitState,
@@ -4243,10 +4230,10 @@ fn emit_return_call_common_sequence(
                  but the current implementation relies on them being present"
     );
 
-    let tmp = allocs.next_writable(call_info.tmp.to_writable_reg());
+    let tmp = call_info.tmp.to_writable_reg();
 
     for u in call_info.uses.iter() {
-        let _ = allocs.next(u.vreg);
+        let _ = u.vreg;
     }
 
     for inst in
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 593f1620c..c4f8ee45b 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -1083,7 +1083,7 @@ impl PrettyPrint for Inst {
                 dst,
             } => {
                 let src1 = pretty_print_reg(src1.to_reg(), 8, allocs);
-                let mask = allocs.next(mask.to_reg());
+                let mask = mask.to_reg();
                 let mask = if mask.is_virtual() {
                     format!(" <{}>", show_ireg_sized(mask, 8))
                 } else {
@@ -1862,7 +1862,7 @@ impl PrettyPrint for Inst {
                 tmp,
             } => {
                 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8, allocs);
-                let tmp = allocs.next(tmp.to_reg().to_reg());
+                let tmp = tmp.to_reg().to_reg();
 
                 let mut s = format!("{dst} = coff_tls_get_addr {symbol:?}");
                 if tmp.is_virtual() {
diff --git a/cranelift/codegen/src/isa/x64/inst/regs.rs b/cranelift/codegen/src/isa/x64/inst/regs.rs
index 4bd0b3311..dea1f0e45 100644
--- a/cranelift/codegen/src/isa/x64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/x64/inst/regs.rs
@@ -270,7 +270,6 @@ pub fn show_ireg_sized(reg: Reg, size: u8) -> String {
 // words, we can't pretty-print a `Reg` all by itself in a build that
 // may have multiple backends; but we can pretty-print one as part of
 // an x64 Inst or x64 RegMemImm.)
-pub fn pretty_print_reg(reg: Reg, size: u8, allocs: &mut AllocationConsumer) -> String {
-    let reg = allocs.next(reg);
+pub fn pretty_print_reg(reg: Reg, size: u8, _allocs: &mut AllocationConsumer) -> String {
     show_ireg_sized(reg, size)
 }
diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs
index 269c95c8a..ac2a8c7d1 100644
--- a/cranelift/codegen/src/machinst/reg.rs
+++ b/cranelift/codegen/src/machinst/reg.rs
@@ -503,14 +503,6 @@ impl AllocationConsumer {
         debug_assert!(allocs.is_empty());
         Self
     }
-
-    pub fn next(&mut self, pre_regalloc_reg: Reg) -> Reg {
-        pre_regalloc_reg
-    }
-
-    pub fn next_writable(&mut self, pre_regalloc_reg: Writable<Reg>) -> Writable<Reg> {
-        pre_regalloc_reg
-    }
 }
 
 impl std::default::Default for AllocationConsumer {
diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml
index 836bfc474..f3a7ddc15 100644
--- a/cranelift/wasm/Cargo.toml
+++ b/cranelift/wasm/Cargo.toml
@@ -15,7 +15,7 @@ edition.workspace = true
 workspace = true
 
 [dependencies]
-wasmparser = { workspace = true }
+wasmparser = { workspace = true, features = ['validate'] }
 cranelift-codegen = { workspace = true }
 cranelift-entity = { workspace = true }
 cranelift-frontend = { workspace = true }
diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs
index c9c89633b..d497d8e09 100644
--- a/cranelift/wasm/src/sections_translator.rs
+++ b/cranelift/wasm/src/sections_translator.rs
@@ -85,7 +85,7 @@ pub fn parse_import_section<'data>(
                 environ.declare_global_import(ty, import.module, import.name)?;
             }
             TypeRef::Table(ty) => {
-                let ty = environ.convert_table_type(&ty);
+                let ty = environ.convert_table_type(&ty)?;
                 environ.declare_table_import(ty, import.module, import.name)?;
             }
         }
@@ -124,7 +124,7 @@ pub fn parse_table_section(
     environ.reserve_tables(tables.count())?;
 
     for entry in tables {
-        let ty = environ.convert_table_type(&entry?.ty);
+        let ty = environ.convert_table_type(&entry?.ty)?;
         environ.declare_table(ty)?;
     }
 
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index a098e59c8..8eb93325a 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -19,7 +19,7 @@ postcard = { workspace = true }
 cpp_demangle = { version = "0.4.3", optional = true }
 cranelift-entity = { workspace = true }
 wasmtime-types = { workspace = true }
-wasmparser = { workspace = true }
+wasmparser = { workspace = true, features = ['validate'] }
 indexmap = { workspace = true, features = ["serde"] }
 serde = { workspace = true }
 serde_derive = { workspace = true }
diff --git a/crates/environ/src/compile/module_environ.rs b/crates/environ/src/compile/module_environ.rs
index dd36d2989..274d4a15f 100644
--- a/crates/environ/src/compile/module_environ.rs
+++ b/crates/environ/src/compile/module_environ.rs
@@ -322,7 +322,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
                         }
                         TypeRef::Table(ty) => {
                             self.result.module.num_imported_tables += 1;
-                            EntityType::Table(self.convert_table_type(&ty))
+                            EntityType::Table(self.convert_table_type(&ty)?)
                         }
 
                         // doesn't get past validation
@@ -353,7 +353,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
 
                 for entry in tables {
                     let wasmparser::Table { ty, init } = entry?;
-                    let table = self.convert_table_type(&ty);
+                    let table = self.convert_table_type(&ty)?;
                     let plan = TablePlan::for_table(table, &self.tunables);
                     self.result.module.table_plans.push(plan);
                     let init = match init {
diff --git a/crates/environ/src/compile/module_types.rs b/crates/environ/src/compile/module_types.rs
index 25e3fc779..cf66a169d 100644
--- a/crates/environ/src/compile/module_types.rs
+++ b/crates/environ/src/compile/module_types.rs
@@ -1,9 +1,9 @@
 use crate::{EntityRef, Module, ModuleTypes, TypeConvert};
-use std::{collections::HashMap, ops::Index};
+use std::{borrow::Cow, collections::HashMap, ops::Index};
 use wasmparser::{UnpackedIndex, Validator, ValidatorId};
 use wasmtime_types::{
     EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, TypeIndex,
-    WasmCompositeType, WasmHeapType, WasmResult, WasmSubType,
+    WasmCompositeType, WasmFuncType, WasmHeapType, WasmResult, WasmSubType,
 };
 
 /// A type marking the start of a recursion group's definition.
@@ -31,6 +31,12 @@ pub struct ModuleTypesBuilder {
     /// The canonicalized and deduplicated set of types we are building.
     types: ModuleTypes,
 
+    /// The set of trampoline-compatible function types we have already added to
+    /// `self.types`. We do this additional level of deduping, on top of what
+    /// `wasmparser` already does, so we can quickly and easily get the
+    /// trampoline type for a given function type if we've already interned one.
+    trampoline_types: HashMap<WasmFuncType, ModuleInternedTypeIndex>,
+
     /// A map from already-interned `wasmparser` types to their corresponding
     /// Wasmtime type.
     wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
@@ -49,6 +55,7 @@ impl ModuleTypesBuilder {
         Self {
             validator_id: validator.id(),
             types: ModuleTypes::default(),
+            trampoline_types: HashMap::default(),
             wasmparser_to_wasmtime: HashMap::default(),
             already_seen: HashMap::default(),
             defining_rec_group: None,
@@ -110,7 +117,63 @@ impl ModuleTypesBuilder {
             self.wasm_sub_type_in_rec_group(id, wasm_ty);
         }
 
-        Ok(self.end_rec_group(rec_group_id))
+        let rec_group_index = self.end_rec_group(rec_group_id);
+
+        // Iterate over all the types we just defined and make sure that every
+        // function type has an associated trampoline type. This needs to happen
+        // *after* we finish defining the rec group because we may need to
+        // intern new function types, which would conflict with the contiguous
+        // range of type indices we pre-reserved for the rec group elements.
+        for ty in self.rec_group_elements(rec_group_index) {
+            if self.types[ty].is_func() {
+                let trampoline = self.intern_trampoline_type(ty);
+                self.types.set_trampoline_type(ty, trampoline);
+            }
+        }
+
+        Ok(rec_group_index)
+    }
+
+    /// Get or create the trampoline function type for the given function
+    /// type. Returns the interned type index of the trampoline function type.
+    fn intern_trampoline_type(
+        &mut self,
+        for_func_ty: ModuleInternedTypeIndex,
+    ) -> ModuleInternedTypeIndex {
+        let trampoline = self.types[for_func_ty].unwrap_func().trampoline_type();
+
+        if let Some(idx) = self.trampoline_types.get(&trampoline) {
+            // We've already interned this trampoline type; reuse it.
+            *idx
+        } else {
+            // We have not already interned this trampoline type.
+            match trampoline {
+                // The trampoline type is the same as the original function
+                // type. We can reuse the definition and its index, but still
+                // need to intern the type into our `trampoline_types` map so we
+                // can reuse it in the future.
+                Cow::Borrowed(f) => {
+                    self.trampoline_types.insert(f.clone(), for_func_ty);
+                    for_func_ty
+                }
+                // The trampoline type is different from the original function
+                // type. Define the trampoline type and then intern it in
+                // `trampoline_types` so we can reuse it in the future.
+                Cow::Owned(f) => {
+                    let idx = self.types.push(WasmSubType {
+                        composite_type: WasmCompositeType::Func(f.clone()),
+                    });
+
+                    // The trampoline type is its own trampoline type.
+                    self.types.set_trampoline_type(idx, idx);
+
+                    let next = self.types.next_ty();
+                    self.types.push_rec_group(idx..next);
+                    self.trampoline_types.insert(f, idx);
+                    idx
+                }
+            }
+        }
     }
 
     /// Start defining a recursion group.
@@ -248,6 +311,19 @@ impl ModuleTypesBuilder {
     pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
         self.types.wasm_types()
     }
+
+    /// Get an iterator over all function types and their associated trampoline
+    /// type.
+    pub fn trampoline_types(
+        &self,
+    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
+        self.types.trampoline_types()
+    }
+
+    /// Get the associated trampoline type for the given function type.
+    pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
+        self.types.trampoline_type(ty)
+    }
 }
 
 // Forward the indexing impl to the internal `ModuleTypes`
diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs
index 15cfc2b44..7d68b6ba2 100644
--- a/crates/environ/src/component/types_builder.rs
+++ b/crates/environ/src/component/types_builder.rs
@@ -379,7 +379,7 @@ impl ComponentTypesBuilder {
                     .intern_type(&module, types, *id)?
                     .into()
             }),
-            types::EntityType::Table(ty) => EntityType::Table(self.convert_table_type(ty)),
+            types::EntityType::Table(ty) => EntityType::Table(self.convert_table_type(ty)?),
             types::EntityType::Memory(ty) => EntityType::Memory(ty.clone().into()),
             types::EntityType::Global(ty) => EntityType::Global(self.convert_global_type(ty)),
             types::EntityType::Tag(_) => bail!("exceptions proposal not implemented"),
diff --git a/crates/environ/src/module_artifacts.rs b/crates/environ/src/module_artifacts.rs
index 09a3695d4..44bb70843 100644
--- a/crates/environ/src/module_artifacts.rs
+++ b/crates/environ/src/module_artifacts.rs
@@ -34,7 +34,7 @@ pub struct WasmFunctionInfo {
 
 /// Description of where a function is located in the text section of a
 /// compiled image.
-#[derive(Copy, Clone, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
 pub struct FunctionLoc {
     /// The byte offset from the start of the text section where this
     /// function starts.
diff --git a/crates/environ/src/module_types.rs b/crates/environ/src/module_types.rs
index 13f1736bc..ae86fb977 100644
--- a/crates/environ/src/module_types.rs
+++ b/crates/environ/src/module_types.rs
@@ -1,19 +1,18 @@
 use crate::PrimaryMap;
 use core::ops::{Index, Range};
+use cranelift_entity::{packed_option::PackedOption, SecondaryMap};
 use serde_derive::{Deserialize, Serialize};
 use wasmtime_types::{ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmSubType};
 
 /// All types used in a core wasm module.
 ///
-/// At this time this only contains function types. Note, though, that function
-/// types are deduplicated within this [`ModuleTypes`].
-///
 /// Note that accesing this type is primarily done through the `Index`
 /// implementations for this type.
 #[derive(Default, Serialize, Deserialize)]
 pub struct ModuleTypes {
     rec_groups: PrimaryMap<ModuleInternedRecGroupIndex, Range<ModuleInternedTypeIndex>>,
     wasm_types: PrimaryMap<ModuleInternedTypeIndex, WasmSubType>,
+    trampoline_types: SecondaryMap<ModuleInternedTypeIndex, PackedOption<ModuleInternedTypeIndex>>,
 }
 
 impl ModuleTypes {
@@ -58,6 +57,55 @@ impl ModuleTypes {
         self.wasm_types.push(ty)
     }
 
+    /// Iterate over the trampoline function types that this module requires.
+    ///
+    /// Yields pairs of (1) a function type and (2) its associated trampoline
+    /// type. They might be the same.
+    ///
+    /// See the docs for `WasmFuncType::trampoline_type` for details on
+    /// trampoline types.
+    pub fn trampoline_types(
+        &self,
+    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
+        self.trampoline_types
+            .iter()
+            .filter_map(|(k, v)| v.expand().map(|v| (k, v)))
+    }
+
+    /// Get the trampoline type for the given function type.
+    ///
+    /// See the docs for `WasmFuncType::trampoline_type` for details on
+    /// trampoline types.
+    pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
+        debug_assert!(self[ty].is_func());
+        self.trampoline_types[ty].unwrap()
+    }
+}
+
+/// Methods that only exist for `ModuleTypesBuilder`.
+#[cfg(feature = "compile")]
+impl ModuleTypes {
+    /// Associate `trampoline_ty` as the trampoline type for `for_ty`.
+    ///
+    /// This is really only for use by the `ModuleTypesBuilder`.
+    pub fn set_trampoline_type(
+        &mut self,
+        for_ty: ModuleInternedTypeIndex,
+        trampoline_ty: ModuleInternedTypeIndex,
+    ) {
+        use cranelift_entity::packed_option::ReservedValue;
+
+        debug_assert!(!for_ty.is_reserved_value());
+        debug_assert!(!trampoline_ty.is_reserved_value());
+        debug_assert!(self.wasm_types[for_ty].is_func());
+        debug_assert!(self.trampoline_types[for_ty].is_none());
+        debug_assert!(self.wasm_types[trampoline_ty]
+            .unwrap_func()
+            .is_trampoline_type());
+
+        self.trampoline_types[for_ty] = Some(trampoline_ty).into();
+    }
+
     /// Adds a new rec group to this interned list of types.
     pub fn push_rec_group(
         &mut self,
diff --git a/crates/fuzzing/src/generators/table_ops.rs b/crates/fuzzing/src/generators/table_ops.rs
index 7e879367c..a2fc1f0c3 100644
--- a/crates/fuzzing/src/generators/table_ops.rs
+++ b/crates/fuzzing/src/generators/table_ops.rs
@@ -83,8 +83,9 @@ impl TableOps {
         let mut tables = TableSection::new();
         tables.table(TableType {
             element_type: RefType::EXTERNREF,
-            minimum: self.table_size as u32,
+            minimum: self.table_size as u64,
             maximum: None,
+            table64: false,
         });
 
         // Define our globals.
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 51488bfbe..1d9de647a 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -12,6 +12,7 @@ pub use wasmparser;
 #[doc(hidden)]
 pub use alloc::format as __format;
 
+use alloc::borrow::Cow;
 use alloc::boxed::Box;
 use core::{fmt, ops::Range};
 use cranelift_entity::entity_impl;
@@ -222,6 +223,20 @@ impl WasmValType {
             _ => false,
         }
     }
+
+    fn trampoline_type(&self) -> Self {
+        match self {
+            WasmValType::Ref(r) => WasmValType::Ref(WasmRefType {
+                nullable: true,
+                heap_type: r.heap_type.top().into(),
+            }),
+            WasmValType::I32
+            | WasmValType::I64
+            | WasmValType::F32
+            | WasmValType::F64
+            | WasmValType::V128 => self.clone(),
+        }
+    }
 }
 
 /// WebAssembly reference type -- equivalent of `wasmparser`'s RefType
@@ -601,6 +616,48 @@ impl WasmFuncType {
     pub fn non_i31_gc_ref_returns_count(&self) -> usize {
         self.non_i31_gc_ref_returns_count
     }
+
+    /// Is this function type compatible with trampoline usage in Wasmtime?
+    pub fn is_trampoline_type(&self) -> bool {
+        self.params().iter().all(|p| *p == p.trampoline_type())
+            && self.returns().iter().all(|r| *r == r.trampoline_type())
+    }
+
+    /// Get the version of this function type that is suitable for usage as a
+    /// trampoline in Wasmtime.
+    ///
+    /// If this function is suitable for trampoline usage as-is, then a borrowed
+    /// `Cow` is returned. If it must be tweaked for trampoline usage, then an
+    /// owned `Cow` is returned.
+    ///
+    /// ## What is a trampoline type?
+    ///
+    /// All reference types in parameters and results are mapped to their
+    /// nullable top type, e.g. `(ref $my_struct_type)` becomes `(ref null
+    /// any)`.
+    ///
+    /// This allows us to share trampolines between functions whose signatures
+    /// both map to the same trampoline type. It also allows the host to satisfy
+    /// a Wasm module's function import of type `S` with a function of type `T`
+    /// where `T <: S`, even when the Wasm module never defines the type `T`
+    /// (and might never even be able to!)
+    ///
+    /// The flip side is that this adds a constraint to our trampolines: they
+    /// can only pass references around (e.g. move a reference from one calling
+    /// convention's location to another's) and may not actually inspect the
+    /// references themselves (unless the trampolines start doing explicit,
+    /// fallible downcasts, but if we ever need that, then we might want to
+    /// redesign this stuff).
+    pub fn trampoline_type(&self) -> Cow<'_, Self> {
+        if self.is_trampoline_type() {
+            return Cow::Borrowed(self);
+        }
+
+        Cow::Owned(Self::new(
+            self.params().iter().map(|p| p.trampoline_type()).collect(),
+            self.returns().iter().map(|r| r.trampoline_type()).collect(),
+        ))
+    }
 }
 
 /// Represents storage types introduced in the GC spec for array and struct fields.
@@ -1445,12 +1502,15 @@ pub trait TypeConvert {
     }
 
     /// Converts a wasmparser table type into a wasmtime type
-    fn convert_table_type(&self, ty: &wasmparser::TableType) -> Table {
-        Table {
-            wasm_ty: self.convert_ref_type(ty.element_type),
-            minimum: ty.initial,
-            maximum: ty.maximum,
+    fn convert_table_type(&self, ty: &wasmparser::TableType) -> WasmResult<Table> {
+        if ty.table64 {
+            return Err(wasm_unsupported!("wasm memory64: 64-bit table type"));
         }
+        Ok(Table {
+            wasm_ty: self.convert_ref_type(ty.element_type),
+            minimum: ty.initial.try_into().unwrap(),
+            maximum: ty.maximum.map(|i| i.try_into().unwrap()),
+        })
     }
 
     fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
diff --git a/crates/wasi-preview1-component-adapter/verify/Cargo.toml b/crates/wasi-preview1-component-adapter/verify/Cargo.toml
index 093ce0678..d351b2bc2 100644
--- a/crates/wasi-preview1-component-adapter/verify/Cargo.toml
+++ b/crates/wasi-preview1-component-adapter/verify/Cargo.toml
@@ -9,6 +9,6 @@ publish = false
 workspace = true
 
 [dependencies]
-wasmparser = { workspace = true, features = ['std'] }
+wasmparser = { workspace = true, features = ['std', 'validate'] }
 wat = { workspace = true }
 anyhow = { workspace = true, features = ['std'] }
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 4078b7fe4..65449702d 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -491,21 +491,25 @@ impl<'a> CompileInputs<'a> {
             }
         }
 
-        for (interned_index, interned_ty) in types.wasm_types() {
-            if let Some(wasm_func_ty) = interned_ty.as_func() {
-                self.push_input(move |compiler| {
-                    let trampoline = compiler.compile_wasm_to_native_trampoline(wasm_func_ty)?;
-                    Ok(CompileOutput {
-                        key: CompileKey::wasm_to_native_trampoline(interned_index),
-                        symbol: format!(
-                            "signatures[{}]::wasm_to_native_trampoline",
-                            interned_index.as_u32()
-                        ),
-                        function: CompiledFunction::Function(trampoline),
-                        info: None,
-                    })
-                });
+        let mut trampoline_types_seen = HashSet::new();
+        for (_func_type_index, trampoline_type_index) in types.trampoline_types() {
+            let is_new = trampoline_types_seen.insert(trampoline_type_index);
+            if !is_new {
+                continue;
             }
+            let trampoline_func_ty = types[trampoline_type_index].unwrap_func();
+            self.push_input(move |compiler| {
+                let trampoline = compiler.compile_wasm_to_native_trampoline(trampoline_func_ty)?;
+                Ok(CompileOutput {
+                    key: CompileKey::wasm_to_native_trampoline(trampoline_type_index),
+                    symbol: format!(
+                        "signatures[{}]::wasm_to_native_trampoline",
+                        trampoline_type_index.as_u32()
+                    ),
+                    function: CompiledFunction::Function(trampoline),
+                    info: None,
+                })
+            });
         }
     }
 
@@ -835,17 +839,19 @@ impl FunctionIndices {
                         })
                         .collect();
 
-                let unique_and_sorted_sigs = translation
+                let unique_and_sorted_trampoline_sigs = translation
                     .module
                     .types
                     .iter()
                     .map(|(_, ty)| *ty)
                     .filter(|idx| types[*idx].is_func())
+                    .map(|idx| types.trampoline_type(idx))
                     .collect::<BTreeSet<_>>();
-                let wasm_to_native_trampolines = unique_and_sorted_sigs
+                let wasm_to_native_trampolines = unique_and_sorted_trampoline_sigs
                     .iter()
                     .map(|idx| {
-                        let key = CompileKey::wasm_to_native_trampoline(*idx);
+                        let trampoline = types.trampoline_type(*idx);
+                        let key = CompileKey::wasm_to_native_trampoline(trampoline);
                         let compiled = wasm_to_native_trampolines[&key];
                         (*idx, symbol_ids_and_locs[compiled.unwrap_function()].1)
                     })
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index f3796c79c..7c7da779c 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -264,6 +264,9 @@ impl Config {
             ret.cranelift_opt_level(OptLevel::Speed);
         }
 
+        // Not yet implemented in Wasmtime
+        ret.features.set(WasmFeatures::EXTENDED_CONST, false);
+
         // Conditionally enabled features depending on compile-time crate
         // features. Note that if these features are disabled then `Config` has
         // no way of re-enabling them.
diff --git a/crates/wasmtime/src/runtime/func.rs b/crates/wasmtime/src/runtime/func.rs
index 3579a2049..9430c811b 100644
--- a/crates/wasmtime/src/runtime/func.rs
+++ b/crates/wasmtime/src/runtime/func.rs
@@ -1304,8 +1304,8 @@ impl Func {
 
                     let sig = self.type_index(store.store_data());
                     module.runtime_info().wasm_to_native_trampoline(sig).expect(
-                        "must have a wasm-to-native trampoline for this signature if the Wasm \
-                         module is importing a function of this signature",
+                        "if the wasm is importing a function of a given type, it must have the \
+                         type's trampoline",
                     )
                 },
                 native_call: f.as_ref().native_call,
diff --git a/crates/wasmtime/src/runtime/instantiate.rs b/crates/wasmtime/src/runtime/instantiate.rs
index 6dc82b287..627a15aa9 100644
--- a/crates/wasmtime/src/runtime/instantiate.rs
+++ b/crates/wasmtime/src/runtime/instantiate.rs
@@ -192,10 +192,14 @@ impl CompiledModule {
     /// `VMFuncRef::wasm_call` for `Func::wrap`-style host funcrefs
     /// that don't have access to a compiler when created.
     pub fn wasm_to_native_trampoline(&self, signature: ModuleInternedTypeIndex) -> &[u8] {
-        let idx = self
+        let idx = match self
             .wasm_to_native_trampolines
             .binary_search_by_key(&signature, |entry| entry.0)
-            .expect("should have a Wasm-to-native trampline for all signatures");
+        {
+            Ok(idx) => idx,
+            Err(_) => panic!("missing trampoline for {signature:?}"),
+        };
+
         let (_, loc) = self.wasm_to_native_trampolines[idx];
         &self.text()[loc.start as usize..][..loc.length as usize]
     }
diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs
index 24048e88f..3d8f169c5 100644
--- a/crates/wasmtime/src/runtime/module.rs
+++ b/crates/wasmtime/src/runtime/module.rs
@@ -172,6 +172,14 @@ impl fmt::Debug for Module {
     }
 }
 
+impl fmt::Debug for ModuleInner {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ModuleInner")
+            .field("name", &self.module.module().name.as_ref())
+            .finish_non_exhaustive()
+    }
+}
+
 impl Module {
     /// Creates a new WebAssembly `Module` from the given in-memory `bytes`.
     ///
@@ -1104,10 +1112,28 @@ impl crate::runtime::vm::ModuleRuntimeInfo for ModuleInner {
         &self,
         signature: VMSharedTypeIndex,
     ) -> Option<NonNull<VMWasmCallFunction>> {
-        let sig = self.code.signatures().module_local_type(signature)?;
+        log::trace!("Looking up trampoline for {signature:?}");
+        let trampoline_shared_ty = self.engine.signatures().trampoline_type(signature);
+        let trampoline_module_ty = self
+            .code
+            .signatures()
+            .trampoline_type(trampoline_shared_ty)?;
+        debug_assert!(self
+            .engine
+            .signatures()
+            .borrow(
+                self.code
+                    .signatures()
+                    .shared_type(trampoline_module_ty)
+                    .unwrap()
+            )
+            .unwrap()
+            .unwrap_func()
+            .is_trampoline_type());
+
         let ptr = self
             .module
-            .wasm_to_native_trampoline(sig)
+            .wasm_to_native_trampoline(trampoline_module_ty)
             .as_ptr()
             .cast::<VMWasmCallFunction>()
             .cast_mut();
diff --git a/crates/wasmtime/src/runtime/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs
index ecc420f20..3d196520f 100644
--- a/crates/wasmtime/src/runtime/type_registry.rs
+++ b/crates/wasmtime/src/runtime/type_registry.rs
@@ -6,6 +6,7 @@
 use crate::prelude::*;
 use crate::sync::RwLock;
 use crate::Engine;
+use alloc::borrow::Cow;
 use alloc::sync::Arc;
 use core::iter;
 use core::{
@@ -18,10 +19,11 @@ use core::{
         Ordering::{AcqRel, Acquire},
     },
 };
-use hashbrown::{HashMap, HashSet};
+use hashbrown::HashSet;
 use wasmtime_environ::{
-    iter_entity_range, EngineOrModuleTypeIndex, ModuleInternedTypeIndex, ModuleTypes, PrimaryMap,
-    SecondaryMap, TypeTrace, VMSharedTypeIndex, WasmRecGroup, WasmSubType,
+    iter_entity_range, packed_option::PackedOption, EngineOrModuleTypeIndex,
+    ModuleInternedTypeIndex, ModuleTypes, PrimaryMap, SecondaryMap, TypeTrace, VMSharedTypeIndex,
+    WasmRecGroup, WasmSubType,
 };
 use wasmtime_slab::{Id as SlabId, Slab};
 
@@ -85,7 +87,7 @@ pub struct TypeCollection {
     engine: Engine,
     rec_groups: Vec<RecGroupEntry>,
     types: PrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
-    reverse_types: HashMap<VMSharedTypeIndex, ModuleInternedTypeIndex>,
+    trampolines: SecondaryMap<VMSharedTypeIndex, PackedOption<ModuleInternedTypeIndex>>,
 }
 
 impl Debug for TypeCollection {
@@ -94,28 +96,35 @@ impl Debug for TypeCollection {
             engine: _,
             rec_groups,
             types,
-            reverse_types: _,
+            trampolines,
         } = self;
         f.debug_struct("TypeCollection")
             .field("rec_groups", rec_groups)
             .field("types", types)
+            .field("trampolines", trampolines)
             .finish_non_exhaustive()
     }
 }
 
 impl TypeCollection {
     /// Creates a type collection for a module given the module's types.
-    pub fn new_for_module(engine: &Engine, types: &ModuleTypes) -> Self {
+    pub fn new_for_module(engine: &Engine, module_types: &ModuleTypes) -> Self {
         let engine = engine.clone();
         let registry = engine.signatures();
-        let (rec_groups, types) = registry.0.write().register_module_types(types);
-        let reverse_types = types.iter().map(|(k, v)| (*v, k)).collect();
+        let (rec_groups, types) = registry.0.write().register_module_types(module_types);
+
+        let mut trampolines = SecondaryMap::with_capacity(types.len());
+        for (module_ty, trampoline) in module_types.trampoline_types() {
+            let shared_ty = types[module_ty];
+            let trampoline_ty = registry.trampoline_type(shared_ty);
+            trampolines[trampoline_ty] = Some(trampoline).into();
+        }
 
         Self {
             engine,
             rec_groups,
             types,
-            reverse_types,
+            trampolines,
         }
     }
 
@@ -134,9 +143,19 @@ impl TypeCollection {
         self.types.get(index).copied()
     }
 
-    /// Get the module-local type index for the given shared type index.
-    pub fn module_local_type(&self, index: VMSharedTypeIndex) -> Option<ModuleInternedTypeIndex> {
-        self.reverse_types.get(&index).copied()
+    /// Get the module-level type index of the trampoline type for the given
+    /// engine-level function type, if any.
+    ///
+    /// This allows callers to look up the pre-compiled wasm-to-native
+    /// trampoline in this type collection's associated module.
+    ///
+    /// See the docs for `WasmFuncType::trampoline_type` for details on
+    /// trampoline types.
+    #[inline]
+    pub fn trampoline_type(&self, ty: VMSharedTypeIndex) -> Option<ModuleInternedTypeIndex> {
+        let trampoline_ty = self.trampolines[ty].expand();
+        log::trace!("TypeCollection::trampoline_type({ty:?}) -> {trampoline_ty:?}");
+        trampoline_ty
     }
 }
 
@@ -437,6 +456,16 @@ struct TypeRegistryInner {
     // `RecGroupEntry`.
     type_to_rec_group: SecondaryMap<VMSharedTypeIndex, Option<RecGroupEntry>>,
 
+    // A map from each registered function type to its trampoline type.
+    //
+    // Note that when a function type is its own trampoline type, then we omit
+    // the entry in this map as a memory optimization. This means that if only
+    // core Wasm function types are ever used, then we will never allocate any
+    // backing storage for this map. As a nice bonus, this also avoids cycles (a
+    // function type referencing itself) that our naive reference counting
+    // doesn't play well with.
+    type_to_trampoline: SecondaryMap<VMSharedTypeIndex, PackedOption<VMSharedTypeIndex>>,
+
     // An explicit stack of entries that we are in the middle of dropping. Used
     // to avoid recursion when dropping a type that is holding the last
     // reference to another type, etc...
@@ -589,6 +618,35 @@ impl TypeRegistryInner {
             self.type_to_rec_group[ty] = Some(entry.clone());
         }
 
+        // Finally, make sure to register the trampoline type for each function
+        // type in the rec group.
+        for shared_type_index in entry.0.shared_type_indices.iter().copied() {
+            let slab_id = shared_type_index_to_slab_id(shared_type_index);
+            if let Some(f) = self.types[slab_id].as_func() {
+                match f.trampoline_type() {
+                    Cow::Borrowed(_) => {
+                        // The function type is its own trampoline type. Leave
+                        // its entry in `type_to_trampoline` empty to signal
+                        // this.
+                    }
+                    Cow::Owned(trampoline) => {
+                        // This will recursively call into rec group
+                        // registration, but at most once since trampoline
+                        // function types are their own trampoline type.
+                        let trampoline_entry = self.register_singleton_rec_group(WasmSubType {
+                            composite_type: wasmtime_environ::WasmCompositeType::Func(trampoline),
+                        });
+                        let trampoline_index = trampoline_entry.0.shared_type_indices[0];
+                        log::trace!(
+                            "Registering trampoline {trampoline_index:?} for function type {shared_type_index:?}"
+                        );
+                        debug_assert_ne!(shared_type_index, trampoline_index);
+                        self.type_to_trampoline[shared_type_index] = Some(trampoline_index).into();
+                    }
+                }
+            }
+        }
+
         entry
     }
 
@@ -732,13 +790,28 @@ impl TypeRegistryInner {
             self.hash_consing_map.remove(&entry);
 
             // Similarly, remove the rec group's types from the registry, as
-            // well as their entries from the reverse type-to-rec-group map.
+            // well as their entries from the reverse type-to-rec-group
+            // map. Additionally, stop holding a strong reference from each
+            // function type in the rec group to that function type's trampoline
+            // type.
             for ty in entry.0.shared_type_indices.iter().copied() {
                 log::trace!("removing {ty:?} from registry");
 
                 let removed_entry = self.type_to_rec_group[ty].take();
                 debug_assert_eq!(removed_entry.unwrap(), entry);
 
+                if let Some(trampoline_ty) =
+                    self.type_to_trampoline.get(ty).and_then(|x| x.expand())
+                {
+                    self.type_to_trampoline[ty] = None.into();
+                    let trampoline_entry = self.type_to_rec_group[trampoline_ty].as_ref().unwrap();
+                    if trampoline_entry
+                        .decref("removing reference from a function type to its trampoline type")
+                    {
+                        self.drop_stack.push(trampoline_entry.clone());
+                    }
+                }
+
                 let id = shared_type_index_to_slab_id(ty);
                 self.types.dealloc(id);
             }
@@ -753,27 +826,33 @@ impl TypeRegistryInner {
 #[cfg(debug_assertions)]
 impl Drop for TypeRegistryInner {
     fn drop(&mut self) {
+        log::trace!("Dropping type registry: {self:#?}");
         let TypeRegistryInner {
             hash_consing_map,
             types,
             type_to_rec_group,
+            type_to_trampoline,
             drop_stack,
         } = self;
         assert!(
             hash_consing_map.is_empty(),
-            "type registry not empty: hash consing map is not empty"
+            "type registry not empty: hash consing map is not empty: {hash_consing_map:#?}"
         );
         assert!(
             types.is_empty(),
-            "type registry not empty: types slab is not empty"
+            "type registry not empty: types slab is not empty: {types:#?}"
         );
         assert!(
             type_to_rec_group.is_empty() || type_to_rec_group.values().all(|x| x.is_none()),
-            "type registry not empty: type-to-rec-group map is not empty"
+            "type registry not empty: type-to-rec-group map is not empty: {type_to_rec_group:#?}"
+        );
+        assert!(
+            type_to_trampoline.is_empty() || type_to_trampoline.values().all(|x| x.is_none()),
+            "type registry not empty: type-to-trampoline map is not empty: {type_to_trampoline:#?}"
         );
         assert!(
             drop_stack.is_empty(),
-            "type registry not empty: drop stack is not empty"
+            "type registry not empty: drop stack is not empty: {drop_stack:#?}"
         );
     }
 }
@@ -804,4 +883,28 @@ impl TypeRegistry {
         let inner = self.0.read();
         inner.types.get(id).cloned()
     }
+
+    /// Get the trampoline type for the given function type index.
+    ///
+    /// Panics for non-function type indices.
+    pub fn trampoline_type(&self, index: VMSharedTypeIndex) -> VMSharedTypeIndex {
+        let slab_id = shared_type_index_to_slab_id(index);
+        let inner = self.0.read();
+
+        let ty = &inner.types[slab_id];
+        debug_assert!(
+            ty.is_func(),
+            "cannot get the trampoline type of a non-function type: {index:?} = {ty:?}"
+        );
+
+        let trampoline_ty = match inner.type_to_trampoline.get(index).and_then(|x| x.expand()) {
+            Some(ty) => ty,
+            None => {
+                // The function type is its own trampoline type.
+                index
+            }
+        };
+        log::trace!("TypeRegistry::trampoline_type({index:?}) -> {trampoline_ty:?}");
+        trampoline_ty
+    }
 }
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 6c8e184a8..1b2cddce9 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1532,6 +1532,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1568,6 +1574,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1676,6 +1688,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1712,6 +1730,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -2162,6 +2186,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "207.0.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.201.0"
 when = "2024-02-27"
@@ -2198,6 +2228,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -2528,6 +2564,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2564,6 +2606,12 @@ when = "2024-04-25"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.207.0"
+when = "2024-05-07"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"


Test patch:
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
index b7c0df9cc..43b31e2c7 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
@@ -2084,6 +2084,26 @@ fn test_riscv64_binemit() {
         0x22b59553,
     ));
 
+    insns.push(TestUnit::new(
+        Inst::Fli {
+            ty: F32,
+            rd: writable_fa0(),
+            imm: FliConstant::new(0),
+        },
+        "fli.s fa0,-1.0",
+        0xf0100553,
+    ));
+
+    insns.push(TestUnit::new(
+        Inst::Fli {
+            ty: F64,
+            rd: writable_fa0(),
+            imm: FliConstant::new(13),
+        },
+        "fli.d fa0,0.625",
+        0xf2168553,
+    ));
+
     let (flags, isa_flags) = make_test_flags();
     let emit_info = EmitInfo::new(flags, isa_flags);
 
diff --git a/cranelift/filetests/filetests/isa/riscv64/zfa.clif b/cranelift/filetests/filetests/isa/riscv64/zfa.clif
index 60193e684..4c3345bb5 100644
--- a/cranelift/filetests/filetests/isa/riscv64/zfa.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/zfa.clif
@@ -68,3 +68,606 @@ block0(v0: f64, v1: f64):
 ;   .byte 0x53, 0x35, 0xb5, 0x2a
 ;   ret
 
+
+
+function %ceil_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = ceil v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.s fa0,fa0,rup
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x35, 0x45, 0x40
+;   ret
+
+
+function %ceil_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = ceil v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.d fa0,fa0,rup
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x35, 0x45, 0x42
+;   ret
+
+
+function %floor_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = floor v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.s fa0,fa0,rdn
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x25, 0x45, 0x40
+;   ret
+
+
+function %floor_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = floor v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.d fa0,fa0,rdn
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x25, 0x45, 0x42
+;   ret
+
+
+function %trunc_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = trunc v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.s fa0,fa0,rtz
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x15, 0x45, 0x40
+;   ret
+
+
+function %trunc_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = trunc v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.d fa0,fa0,rtz
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x15, 0x45, 0x42
+;   ret
+
+
+function %nearest_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = nearest v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.s fa0,fa0,rne
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x05, 0x45, 0x40
+;   ret
+
+
+function %nearest_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = nearest v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   fround.d fa0,fa0,rne
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x53, 0x05, 0x45, 0x42
+;   ret
+
+
+function %fli_s() -> f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32 {
+block0:
+    v0 = f32const -0x1.p0
+    v1 = f32const 0x1.p-126
+    v2 = f32const 0x1.p-16
+    v3 = f32const 0x1.p-15
+    v4 = f32const 0x1.p-8
+    v5 = f32const 0x1.p-7
+    v6 = f32const 0x1.p-4
+    v7 = f32const 0x1.p-3
+    v8 = f32const 0x1.p-2
+    v9 = f32const 0x1.4p-2
+    v10 = f32const 0x1.8p-2
+    v11 = f32const 0x1.cp-2
+    v12 = f32const 0x1.p-1
+    v13 = f32const 0x1.4p-1
+    v14 = f32const 0x1.8p-1
+    v15 = f32const 0x1.cp-1
+    v16 = f32const 0x1.p0
+    v17 = f32const 0x1.4p0
+    v18 = f32const 0x1.8p0
+    v19 = f32const 0x1.cp0
+    v20 = f32const 0x1.p1
+    v21 = f32const 0x1.4p1
+    v22 = f32const 0x1.8p1
+    v23 = f32const 0x1.p2
+    v24 = f32const 0x1.p3
+    v25 = f32const 0x1.p4
+    v26 = f32const 0x1.p7
+    v27 = f32const 0x1.p8
+    v28 = f32const 0x1.p15
+    v29 = f32const 0x1.p16
+    v30 = f32const +Inf
+    v31 = f32const +NaN
+
+    return v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+;   addi sp,sp,-96
+;   fsd fs0,88(sp)
+;   fsd fs2,80(sp)
+;   fsd fs3,72(sp)
+;   fsd fs4,64(sp)
+;   fsd fs5,56(sp)
+;   fsd fs6,48(sp)
+;   fsd fs7,40(sp)
+;   fsd fs8,32(sp)
+;   fsd fs9,24(sp)
+;   fsd fs10,16(sp)
+;   fsd fs11,8(sp)
+; block0:
+;   fli.s fa0,-1.0
+;   fli.s fa1,min
+;   fli.s fa3,2^-16
+;   fli.s fa4,2^-15
+;   fli.s fa5,2^-8
+;   fli.s fa2,2^-7
+;   fli.s ft7,0.0625
+;   fli.s fa6,0.125
+;   fli.s fa7,0.25
+;   fli.s ft8,0.3125
+;   fli.s ft9,0.375
+;   fli.s ft10,0.4375
+;   fli.s ft11,0.5
+;   fli.s fs0,0.625
+;   fli.s fs1,0.75
+;   fli.s fs2,0.875
+;   fli.s fs3,1.0
+;   fli.s fs4,1.25
+;   fli.s fs5,1.5
+;   fli.s fs6,1.75
+;   fli.s fs7,2.0
+;   fli.s fs8,2.5
+;   fli.s fs9,3.0
+;   fli.s fs10,4.0
+;   fli.s fs11,8.0
+;   fli.s ft0,16.0
+;   fli.s ft1,128.0
+;   fli.s ft2,256.0
+;   fli.s ft3,32768.0
+;   fli.s ft4,65536.0
+;   fli.s ft5,inf
+;   fli.s ft6,nan
+;   fsw fa3,0(a0)
+;   fsw fa4,8(a0)
+;   fsw fa5,16(a0)
+;   fsw fa2,24(a0)
+;   fsw ft7,32(a0)
+;   fsw fa6,40(a0)
+;   fsw fa7,48(a0)
+;   fsw ft8,56(a0)
+;   fsw ft9,64(a0)
+;   fsw ft10,72(a0)
+;   fsw ft11,80(a0)
+;   fsw fs0,88(a0)
+;   fsw fs1,96(a0)
+;   fsw fs2,104(a0)
+;   fsw fs3,112(a0)
+;   fsw fs4,120(a0)
+;   fsw fs5,128(a0)
+;   fsw fs6,136(a0)
+;   fsw fs7,144(a0)
+;   fsw fs8,152(a0)
+;   fsw fs9,160(a0)
+;   fsw fs10,168(a0)
+;   fsw fs11,176(a0)
+;   fsw ft0,184(a0)
+;   fsw ft1,192(a0)
+;   fsw ft2,200(a0)
+;   fsw ft3,208(a0)
+;   fsw ft4,216(a0)
+;   fsw ft5,224(a0)
+;   fsw ft6,232(a0)
+;   fld fs0,88(sp)
+;   fld fs2,80(sp)
+;   fld fs3,72(sp)
+;   fld fs4,64(sp)
+;   fld fs5,56(sp)
+;   fld fs6,48(sp)
+;   fld fs7,40(sp)
+;   fld fs8,32(sp)
+;   fld fs9,24(sp)
+;   fld fs10,16(sp)
+;   fld fs11,8(sp)
+;   addi sp,sp,96
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+;   addi sp, sp, -0x60
+;   fsd fs0, 0x58(sp)
+;   fsd fs2, 0x50(sp)
+;   fsd fs3, 0x48(sp)
+;   fsd fs4, 0x40(sp)
+;   fsd fs5, 0x38(sp)
+;   fsd fs6, 0x30(sp)
+;   fsd fs7, 0x28(sp)
+;   fsd fs8, 0x20(sp)
+;   fsd fs9, 0x18(sp)
+;   fsd fs10, 0x10(sp)
+;   fsd fs11, 8(sp)
+; block1: ; offset 0x40
+;   .byte 0x53, 0x05, 0x10, 0xf0
+;   .byte 0xd3, 0x85, 0x10, 0xf0
+;   .byte 0xd3, 0x06, 0x11, 0xf0
+;   .byte 0x53, 0x87, 0x11, 0xf0
+;   .byte 0xd3, 0x07, 0x12, 0xf0
+;   .byte 0x53, 0x86, 0x12, 0xf0
+;   .byte 0xd3, 0x03, 0x13, 0xf0
+;   .byte 0x53, 0x88, 0x13, 0xf0
+;   .byte 0xd3, 0x08, 0x14, 0xf0
+;   .byte 0x53, 0x8e, 0x14, 0xf0
+;   .byte 0xd3, 0x0e, 0x15, 0xf0
+;   .byte 0x53, 0x8f, 0x15, 0xf0
+;   .byte 0xd3, 0x0f, 0x16, 0xf0
+;   .byte 0x53, 0x84, 0x16, 0xf0
+;   .byte 0xd3, 0x04, 0x17, 0xf0
+;   .byte 0x53, 0x89, 0x17, 0xf0
+;   .byte 0xd3, 0x09, 0x18, 0xf0
+;   .byte 0x53, 0x8a, 0x18, 0xf0
+;   .byte 0xd3, 0x0a, 0x19, 0xf0
+;   .byte 0x53, 0x8b, 0x19, 0xf0
+;   .byte 0xd3, 0x0b, 0x1a, 0xf0
+;   .byte 0x53, 0x8c, 0x1a, 0xf0
+;   .byte 0xd3, 0x0c, 0x1b, 0xf0
+;   .byte 0x53, 0x8d, 0x1b, 0xf0
+;   .byte 0xd3, 0x0d, 0x1c, 0xf0
+;   .byte 0x53, 0x80, 0x1c, 0xf0
+;   .byte 0xd3, 0x00, 0x1d, 0xf0
+;   .byte 0x53, 0x81, 0x1d, 0xf0
+;   .byte 0xd3, 0x01, 0x1e, 0xf0
+;   .byte 0x53, 0x82, 0x1e, 0xf0
+;   .byte 0xd3, 0x02, 0x1f, 0xf0
+;   .byte 0x53, 0x83, 0x1f, 0xf0
+;   fsw fa3, 0(a0)
+;   fsw fa4, 8(a0)
+;   fsw fa5, 0x10(a0)
+;   fsw fa2, 0x18(a0)
+;   fsw ft7, 0x20(a0)
+;   fsw fa6, 0x28(a0)
+;   fsw fa7, 0x30(a0)
+;   fsw ft8, 0x38(a0)
+;   fsw ft9, 0x40(a0)
+;   fsw ft10, 0x48(a0)
+;   fsw ft11, 0x50(a0)
+;   fsw fs0, 0x58(a0)
+;   fsw fs1, 0x60(a0)
+;   fsw fs2, 0x68(a0)
+;   fsw fs3, 0x70(a0)
+;   fsw fs4, 0x78(a0)
+;   fsw fs5, 0x80(a0)
+;   fsw fs6, 0x88(a0)
+;   fsw fs7, 0x90(a0)
+;   fsw fs8, 0x98(a0)
+;   fsw fs9, 0xa0(a0)
+;   fsw fs10, 0xa8(a0)
+;   fsw fs11, 0xb0(a0)
+;   fsw ft0, 0xb8(a0)
+;   fsw ft1, 0xc0(a0)
+;   fsw ft2, 0xc8(a0)
+;   fsw ft3, 0xd0(a0)
+;   fsw ft4, 0xd8(a0)
+;   fsw ft5, 0xe0(a0)
+;   fsw ft6, 0xe8(a0)
+;   fld fs0, 0x58(sp)
+;   fld fs2, 0x50(sp)
+;   fld fs3, 0x48(sp)
+;   fld fs4, 0x40(sp)
+;   fld fs5, 0x38(sp)
+;   fld fs6, 0x30(sp)
+;   fld fs7, 0x28(sp)
+;   fld fs8, 0x20(sp)
+;   fld fs9, 0x18(sp)
+;   fld fs10, 0x10(sp)
+;   fld fs11, 8(sp)
+;   addi sp, sp, 0x60
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %fli_d() -> f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64 {
+block0:
+    v0 = f64const -0x1.p0
+    v1 = f64const 0x1.p-1022
+    v2 = f64const 0x1.p-16
+    v3 = f64const 0x1.p-15
+    v4 = f64const 0x1.p-8
+    v5 = f64const 0x1.p-7
+    v6 = f64const 0x1.p-4
+    v7 = f64const 0x1.p-3
+    v8 = f64const 0x1.p-2
+    v9 = f64const 0x1.4p-2
+    v10 = f64const 0x1.8p-2
+    v11 = f64const 0x1.cp-2
+    v12 = f64const 0x1.p-1
+    v13 = f64const 0x1.4p-1
+    v14 = f64const 0x1.8p-1
+    v15 = f64const 0x1.cp-1
+    v16 = f64const 0x1.p0
+    v17 = f64const 0x1.4p0
+    v18 = f64const 0x1.8p0
+    v19 = f64const 0x1.cp0
+    v20 = f64const 0x1.p1
+    v21 = f64const 0x1.4p1
+    v22 = f64const 0x1.8p1
+    v23 = f64const 0x1.p2
+    v24 = f64const 0x1.p3
+    v25 = f64const 0x1.p4
+    v26 = f64const 0x1.p7
+    v27 = f64const 0x1.p8
+    v28 = f64const 0x1.p15
+    v29 = f64const 0x1.p16
+    v30 = f64const +Inf
+    v31 = f64const +NaN
+    return v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+;   addi sp,sp,-96
+;   fsd fs0,88(sp)
+;   fsd fs2,80(sp)
+;   fsd fs3,72(sp)
+;   fsd fs4,64(sp)
+;   fsd fs5,56(sp)
+;   fsd fs6,48(sp)
+;   fsd fs7,40(sp)
+;   fsd fs8,32(sp)
+;   fsd fs9,24(sp)
+;   fsd fs10,16(sp)
+;   fsd fs11,8(sp)
+; block0:
+;   fli.d fa0,-1.0
+;   fli.d fa1,min
+;   fli.d fa3,2^-16
+;   fli.d fa4,2^-15
+;   fli.d fa5,2^-8
+;   fli.d fa2,2^-7
+;   fli.d ft7,0.0625
+;   fli.d fa6,0.125
+;   fli.d fa7,0.25
+;   fli.d ft8,0.3125
+;   fli.d ft9,0.375
+;   fli.d ft10,0.4375
+;   fli.d ft11,0.5
+;   fli.d fs0,0.625
+;   fli.d fs1,0.75
+;   fli.d fs2,0.875
+;   fli.d fs3,1.0
+;   fli.d fs4,1.25
+;   fli.d fs5,1.5
+;   fli.d fs6,1.75
+;   fli.d fs7,2.0
+;   fli.d fs8,2.5
+;   fli.d fs9,3.0
+;   fli.d fs10,4.0
+;   fli.d fs11,8.0
+;   fli.d ft0,16.0
+;   fli.d ft1,128.0
+;   fli.d ft2,256.0
+;   fli.d ft3,32768.0
+;   fli.d ft4,65536.0
+;   fli.d ft5,inf
+;   fli.d ft6,nan
+;   fsd fa3,0(a0)
+;   fsd fa4,8(a0)
+;   fsd fa5,16(a0)
+;   fsd fa2,24(a0)
+;   fsd ft7,32(a0)
+;   fsd fa6,40(a0)
+;   fsd fa7,48(a0)
+;   fsd ft8,56(a0)
+;   fsd ft9,64(a0)
+;   fsd ft10,72(a0)
+;   fsd ft11,80(a0)
+;   fsd fs0,88(a0)
+;   fsd fs1,96(a0)
+;   fsd fs2,104(a0)
+;   fsd fs3,112(a0)
+;   fsd fs4,120(a0)
+;   fsd fs5,128(a0)
+;   fsd fs6,136(a0)
+;   fsd fs7,144(a0)
+;   fsd fs8,152(a0)
+;   fsd fs9,160(a0)
+;   fsd fs10,168(a0)
+;   fsd fs11,176(a0)
+;   fsd ft0,184(a0)
+;   fsd ft1,192(a0)
+;   fsd ft2,200(a0)
+;   fsd ft3,208(a0)
+;   fsd ft4,216(a0)
+;   fsd ft5,224(a0)
+;   fsd ft6,232(a0)
+;   fld fs0,88(sp)
+;   fld fs2,80(sp)
+;   fld fs3,72(sp)
+;   fld fs4,64(sp)
+;   fld fs5,56(sp)
+;   fld fs6,48(sp)
+;   fld fs7,40(sp)
+;   fld fs8,32(sp)
+;   fld fs9,24(sp)
+;   fld fs10,16(sp)
+;   fld fs11,8(sp)
+;   addi sp,sp,96
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+;   addi sp, sp, -0x60
+;   fsd fs0, 0x58(sp)
+;   fsd fs2, 0x50(sp)
+;   fsd fs3, 0x48(sp)
+;   fsd fs4, 0x40(sp)
+;   fsd fs5, 0x38(sp)
+;   fsd fs6, 0x30(sp)
+;   fsd fs7, 0x28(sp)
+;   fsd fs8, 0x20(sp)
+;   fsd fs9, 0x18(sp)
+;   fsd fs10, 0x10(sp)
+;   fsd fs11, 8(sp)
+; block1: ; offset 0x40
+;   .byte 0x53, 0x05, 0x10, 0xf2
+;   .byte 0xd3, 0x85, 0x10, 0xf2
+;   .byte 0xd3, 0x06, 0x11, 0xf2
+;   .byte 0x53, 0x87, 0x11, 0xf2
+;   .byte 0xd3, 0x07, 0x12, 0xf2
+;   .byte 0x53, 0x86, 0x12, 0xf2
+;   .byte 0xd3, 0x03, 0x13, 0xf2
+;   .byte 0x53, 0x88, 0x13, 0xf2
+;   .byte 0xd3, 0x08, 0x14, 0xf2
+;   .byte 0x53, 0x8e, 0x14, 0xf2
+;   .byte 0xd3, 0x0e, 0x15, 0xf2
+;   .byte 0x53, 0x8f, 0x15, 0xf2
+;   .byte 0xd3, 0x0f, 0x16, 0xf2
+;   .byte 0x53, 0x84, 0x16, 0xf2
+;   .byte 0xd3, 0x04, 0x17, 0xf2
+;   .byte 0x53, 0x89, 0x17, 0xf2
+;   .byte 0xd3, 0x09, 0x18, 0xf2
+;   .byte 0x53, 0x8a, 0x18, 0xf2
+;   .byte 0xd3, 0x0a, 0x19, 0xf2
+;   .byte 0x53, 0x8b, 0x19, 0xf2
+;   .byte 0xd3, 0x0b, 0x1a, 0xf2
+;   .byte 0x53, 0x8c, 0x1a, 0xf2
+;   .byte 0xd3, 0x0c, 0x1b, 0xf2
+;   .byte 0x53, 0x8d, 0x1b, 0xf2
+;   .byte 0xd3, 0x0d, 0x1c, 0xf2
+;   .byte 0x53, 0x80, 0x1c, 0xf2
+;   .byte 0xd3, 0x00, 0x1d, 0xf2
+;   .byte 0x53, 0x81, 0x1d, 0xf2
+;   .byte 0xd3, 0x01, 0x1e, 0xf2
+;   .byte 0x53, 0x82, 0x1e, 0xf2
+;   .byte 0xd3, 0x02, 0x1f, 0xf2
+;   .byte 0x53, 0x83, 0x1f, 0xf2
+;   fsd fa3, 0(a0)
+;   fsd fa4, 8(a0)
+;   fsd fa5, 0x10(a0)
+;   fsd fa2, 0x18(a0)
+;   fsd ft7, 0x20(a0)
+;   fsd fa6, 0x28(a0)
+;   fsd fa7, 0x30(a0)
+;   fsd ft8, 0x38(a0)
+;   fsd ft9, 0x40(a0)
+;   fsd ft10, 0x48(a0)
+;   fsd ft11, 0x50(a0)
+;   fsd fs0, 0x58(a0)
+;   fsd fs1, 0x60(a0)
+;   fsd fs2, 0x68(a0)
+;   fsd fs3, 0x70(a0)
+;   fsd fs4, 0x78(a0)
+;   fsd fs5, 0x80(a0)
+;   fsd fs6, 0x88(a0)
+;   fsd fs7, 0x90(a0)
+;   fsd fs8, 0x98(a0)
+;   fsd fs9, 0xa0(a0)
+;   fsd fs10, 0xa8(a0)
+;   fsd fs11, 0xb0(a0)
+;   fsd ft0, 0xb8(a0)
+;   fsd ft1, 0xc0(a0)
+;   fsd ft2, 0xc8(a0)
+;   fsd ft3, 0xd0(a0)
+;   fsd ft4, 0xd8(a0)
+;   fsd ft5, 0xe0(a0)
+;   fsd ft6, 0xe8(a0)
+;   fld fs0, 0x58(sp)
+;   fld fs2, 0x50(sp)
+;   fld fs3, 0x48(sp)
+;   fld fs4, 0x40(sp)
+;   fld fs5, 0x38(sp)
+;   fld fs6, 0x30(sp)
+;   fld fs7, 0x28(sp)
+;   fld fs8, 0x20(sp)
+;   fld fs9, 0x18(sp)
+;   fld fs10, 0x10(sp)
+;   fld fs11, 8(sp)
+;   addi sp, sp, 0x60
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
diff --git a/cranelift/filetests/filetests/runtests/ceil.clif b/cranelift/filetests/filetests/runtests/ceil.clif
index 408f35bd1..9ac7f24a3 100644
--- a/cranelift/filetests/filetests/runtests/ceil.clif
+++ b/cranelift/filetests/filetests/runtests/ceil.clif
@@ -7,6 +7,7 @@ target x86_64 sse42 has_avx
 target aarch64
 target s390x
 target riscv64
+target riscv64 has_zfa
 target riscv64 has_c has_zcb
 
 function %ceil_f32(f32) -> f32 {
diff --git a/cranelift/filetests/filetests/runtests/f32const.clif b/cranelift/filetests/filetests/runtests/f32const.clif
new file mode 100644
index 000000000..208220e33
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/f32const.clif
@@ -0,0 +1,61 @@
+test interpret
+test run
+target x86_64
+target aarch64
+target s390x
+target riscv64
+target riscv64 has_zfa
+target riscv64 has_c has_zcb
+
+
+;; These values are special for RISC-V since it has a dedicated
+;; instruction to generate them.
+
+function %special_f32_values() -> f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32 {
+block0:
+    v0 = f32const -0x1.p0
+    v1 = f32const 0x1.p-126
+    v2 = f32const 0x1.p-16
+    v3 = f32const 0x1.p-15
+    v4 = f32const 0x1.p-8
+    v5 = f32const 0x1.p-7
+    v6 = f32const 0x1.p-4
+    v7 = f32const 0x1.p-3
+    v8 = f32const 0x1.p-2
+    v9 = f32const 0x1.4p-2
+    v10 = f32const 0x1.8p-2
+    v11 = f32const 0x1.cp-2
+    v12 = f32const 0x1.p-1
+    v13 = f32const 0x1.4p-1
+    v14 = f32const 0x1.8p-1
+    v15 = f32const 0x1.cp-1
+    v16 = f32const 0x1.p0
+    v17 = f32const 0x1.4p0
+    v18 = f32const 0x1.8p0
+    v19 = f32const 0x1.cp0
+    v20 = f32const 0x1.p1
+    v21 = f32const 0x1.4p1
+    v22 = f32const 0x1.8p1
+    v23 = f32const 0x1.p2
+    v24 = f32const 0x1.p3
+    v25 = f32const 0x1.p4
+    v26 = f32const 0x1.p7
+    v27 = f32const 0x1.p8
+    v28 = f32const 0x1.p15
+    v29 = f32const 0x1.p16
+    v30 = f32const +Inf
+    v31 = f32const +NaN
+
+    return v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31
+}
+
+; run: %special_f32_values() == [-0x1.p0, 0x1.p-126, 0x1.p-16, 0x1.p-15, 0x1.p-8, 0x1.p-7, 0x1.p-4, 0x1.p-3, 0x1.p-2, 0x1.4p-2, 0x1.8p-2, 0x1.cp-2, 0x1.p-1, 0x1.4p-1, 0x1.8p-1, 0x1.cp-1, 0x1.p0, 0x1.4p0, 0x1.8p0, 0x1.cp0, 0x1.p1, 0x1.4p1, 0x1.8p1, 0x1.p2, 0x1.p3, 0x1.p4, 0x1.p7, 0x1.p8, 0x1.p15, 0x1.p16, +Inf, +NaN]
+
+
+function %f32const_neg_nan() -> f32 {
+block0:
+    v0 = f32const -NaN
+    return v0
+}
+
+; run: %f32const_neg_nan() == -NaN
diff --git a/cranelift/filetests/filetests/runtests/f64const.clif b/cranelift/filetests/filetests/runtests/f64const.clif
new file mode 100644
index 000000000..cb0d63a44
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/f64const.clif
@@ -0,0 +1,60 @@
+test interpret
+test run
+target x86_64
+target aarch64
+target s390x
+target riscv64
+target riscv64 has_zfa
+target riscv64 has_c has_zcb
+
+
+;; These values are special for RISC-V since it has a dedicated
+;; instruction to generate them.
+
+function %special_f64_values() -> f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64 {
+block0:
+    v0 = f64const -0x1.p0
+    v1 = f64const 0x1.p-1022
+    v2 = f64const 0x1.p-16
+    v3 = f64const 0x1.p-15
+    v4 = f64const 0x1.p-8
+    v5 = f64const 0x1.p-7
+    v6 = f64const 0x1.p-4
+    v7 = f64const 0x1.p-3
+    v8 = f64const 0x1.p-2
+    v9 = f64const 0x1.4p-2
+    v10 = f64const 0x1.8p-2
+    v11 = f64const 0x1.cp-2
+    v12 = f64const 0x1.p-1
+    v13 = f64const 0x1.4p-1
+    v14 = f64const 0x1.8p-1
+    v15 = f64const 0x1.cp-1
+    v16 = f64const 0x1.p0
+    v17 = f64const 0x1.4p0
+    v18 = f64const 0x1.8p0
+    v19 = f64const 0x1.cp0
+    v20 = f64const 0x1.p1
+    v21 = f64const 0x1.4p1
+    v22 = f64const 0x1.8p1
+    v23 = f64const 0x1.p2
+    v24 = f64const 0x1.p3
+    v25 = f64const 0x1.p4
+    v26 = f64const 0x1.p7
+    v27 = f64const 0x1.p8
+    v28 = f64const 0x1.p15
+    v29 = f64const 0x1.p16
+    v30 = f64const +Inf
+    v31 = f64const +NaN
+
+    return v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31
+}
+
+; run: %special_f64_values() == [-0x1.p0, 0x1.p-1022, 0x1.p-16, 0x1.p-15, 0x1.p-8, 0x1.p-7, 0x1.p-4, 0x1.p-3, 0x1.p-2, 0x1.4p-2, 0x1.8p-2, 0x1.cp-2, 0x1.p-1, 0x1.4p-1, 0x1.8p-1, 0x1.cp-1, 0x1.p0, 0x1.4p0, 0x1.8p0, 0x1.cp0, 0x1.p1, 0x1.4p1, 0x1.8p1, 0x1.p2, 0x1.p3, 0x1.p4, 0x1.p7, 0x1.p8, 0x1.p15, 0x1.p16, +Inf, +NaN]
+
+function %f64const_neg_nan() -> f64 {
+block0:
+    v0 = f64const -NaN
+    return v0
+}
+
+; run: %f64const_neg_nan() == -NaN
diff --git a/cranelift/filetests/filetests/runtests/floor.clif b/cranelift/filetests/filetests/runtests/floor.clif
index 986061afb..3c49a0a9d 100644
--- a/cranelift/filetests/filetests/runtests/floor.clif
+++ b/cranelift/filetests/filetests/runtests/floor.clif
@@ -7,6 +7,7 @@ target x86_64 sse42 has_avx
 target aarch64
 target s390x
 target riscv64
+target riscv64 has_zfa
 target riscv64 has_c has_zcb
 
 function %floor_f32(f32) -> f32 {
diff --git a/cranelift/filetests/filetests/runtests/nearest.clif b/cranelift/filetests/filetests/runtests/nearest.clif
index ee50e7b38..90496d407 100644
--- a/cranelift/filetests/filetests/runtests/nearest.clif
+++ b/cranelift/filetests/filetests/runtests/nearest.clif
@@ -7,6 +7,7 @@ target x86_64 sse42 has_avx
 target aarch64
 target s390x
 target riscv64
+target riscv64 has_zfa
 target riscv64 has_c has_zcb
 
 function %nearest_f32(f32) -> f32 {
diff --git a/cranelift/filetests/filetests/runtests/trunc.clif b/cranelift/filetests/filetests/runtests/trunc.clif
index c858e6505..d2386d425 100644
--- a/cranelift/filetests/filetests/runtests/trunc.clif
+++ b/cranelift/filetests/filetests/runtests/trunc.clif
@@ -7,6 +7,7 @@ target x86_64 sse42 has_avx
 target aarch64
 target s390x
 target riscv64
+target riscv64 has_zfa
 target riscv64 has_c has_zcb
 
 function %trunc_f32(f32) -> f32 {
diff --git a/tests/all/func.rs b/tests/all/func.rs
index ecf8feb52..fa79d8a0c 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -2136,3 +2136,123 @@ fn call_func_with_funcref_both_typed_and_untyped() -> Result<()> {
     f1.call(&mut store, &[Val::FuncRef(Some(f2))], &mut [])?;
     Ok(())
 }
+
+#[test]
+#[cfg_attr(miri, ignore)]
+fn wasm_to_host_trampolines_and_subtyping() -> Result<()> {
+    let _ = env_logger::try_init();
+
+    let mut config = Config::new();
+    config.wasm_function_references(true);
+    config.wasm_gc(true);
+
+    let engine = Engine::new(&config)?;
+
+    let module = Module::new(
+        &engine,
+        r#"
+            (module
+                (type $ty (func (result (ref null extern))))
+
+                (import "" "a" (func $imported_func (type $ty)))
+                (import "" "return_func" (func $return_func (result (ref $ty))))
+                (global $g (export "g") (mut (ref null $ty)) (ref.null $ty))
+                (table $t (export "t") 10 10 (ref null $ty))
+
+                (func (export "f")
+                    (drop (call $imported_func))
+                    (drop (call_ref $ty (call $return_func)))
+                    (drop (call_ref $ty (global.get $g)))
+                    (drop (call_ref $ty (table.get $t (i32.const 0))))
+                )
+            )
+        "#,
+    )?;
+
+    // This defines functions of type `(func (result (ref extern)))` and not of
+    // type `(func (result (ref null extern)))` that the Wasm imports. But it is
+    // still a subtype of the Wasm import's type, so we should be able to use
+    // the Wasm's precompiled trampolines with this host function.
+    //
+    // But this means we also need to look up the trampoline by the caller's
+    // type, rather than the callee's type.
+    //
+    // So take each kind of function we can define, and give it to Wasm in every
+    // way that Wasm can receive a function, and make sure we find the correct
+    // trampolines in all cases and everything works.
+    //
+    // Note that we create multiple versions of the same function by calling
+    // these constructors multiple times in the loop below. This is to avoid the
+    // scenario where we reuse the function, the first time it is exposed to
+    // Wasm its trampoline is filled in, and then all subsequent uses don't
+    // actually exercise their associated code paths for finding and
+    // initializing their trampoline.
+    let func_ty = FuncType::new(&engine, [], [RefType::new(false, HeapType::Extern).into()]);
+    let func_ctors: Vec<Box<dyn Fn(&mut Store<_>) -> Func>> = vec![
+        Box::new(|store| {
+            Func::wrap(store, |mut caller: Caller<'_, ()>| {
+                ExternRef::new(&mut caller, 100)
+            })
+        }),
+        Box::new(|store| {
+            Func::new(
+                store,
+                func_ty.clone(),
+                |mut caller: Caller<'_, ()>, _args, results| {
+                    results[0] = ExternRef::new(&mut caller, 200)?.into();
+                    Ok(())
+                },
+            )
+        }),
+    ];
+
+    let return_func_ty = FuncType::new(
+        &engine,
+        [],
+        [RefType::new(false, HeapType::ConcreteFunc(func_ty.clone())).into()],
+    );
+
+    for make_func in func_ctors {
+        let mut store = Store::new(&engine, ());
+
+        let imported_func = make_func(&mut store);
+        assert!(FuncType::eq(&imported_func.ty(&store), &func_ty));
+
+        let returned_func = make_func(&mut store);
+        assert!(FuncType::eq(&returned_func.ty(&store), &func_ty));
+
+        let return_func = Func::new(
+            &mut store,
+            return_func_ty.clone(),
+            move |_caller, _args, results| {
+                results[0] = returned_func.clone().into();
+                Ok(())
+            },
+        );
+
+        let instance = Instance::new(
+            &mut store,
+            &module,
+            &[imported_func.into(), return_func.into()],
+        )?;
+
+        let g = make_func(&mut store);
+        assert!(FuncType::eq(&g.ty(&store), &func_ty));
+        instance
+            .get_global(&mut store, "g")
+            .unwrap()
+            .set(&mut store, g.into())?;
+
+        let t = make_func(&mut store);
+        assert!(FuncType::eq(&t.ty(&store), &func_ty));
+        instance
+            .get_table(&mut store, "t")
+            .unwrap()
+            .set(&mut store, 0, t.into())?;
+
+        let f = instance.get_typed_func::<(), ()>(&mut store, "f")?;
+        f.call(&mut store, ())?;
+    }
+
+    Ok(())
+}