Repository: bytecodealliance/wasmtime
Pull request #5872 (2023-02-24 00:19:36)

Test all backends when a runtest is modified
As any backend can be affected by modifying runtests, test all backends when one of them is modified.

I tested this by making a change to a runtest file and reverting it, to verify that all backends were tested when that file was changed.
<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->



Patch:
diff --git a/cranelift/codegen/meta/src/gen_inst.rs b/cranelift/codegen/meta/src/gen_inst.rs
index db43caef6..01ccefb7e 100644
--- a/cranelift/codegen/meta/src/gen_inst.rs
+++ b/cranelift/codegen/meta/src/gen_inst.rs
@@ -1405,7 +1405,9 @@ fn gen_common_isle(
         IsleTarget::Opt => "Value",
     };
     for inst in instructions {
-        if isle_target == IsleTarget::Opt && inst.format.has_value_list {
+        if isle_target == IsleTarget::Opt
+            && (inst.format.has_value_list || inst.value_results.len() != 1)
+        {
             continue;
         }
 
diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs
index 7c2428102..afd32dd40 100644
--- a/cranelift/codegen/src/isa/aarch64/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/mod.rs
@@ -239,223 +239,3 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder {
         },
     }
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::cursor::{Cursor, FuncCursor};
-    use crate::dominator_tree::DominatorTree;
-    use crate::flowgraph::ControlFlowGraph;
-    use crate::ir::types::*;
-    use crate::ir::{AbiParam, Function, InstBuilder, JumpTableData, Signature, UserFuncName};
-    use crate::isa::CallConv;
-    use crate::settings;
-    use crate::settings::Configurable;
-    use core::str::FromStr;
-    use target_lexicon::Triple;
-
-    #[test]
-    fn test_compile_function() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-
-        let mut pos = FuncCursor::new(&mut func);
-        pos.insert_block(bb0);
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().return_(&[v1]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
-        let backend = AArch64Backend::new_with_flags(
-            Triple::from_str("aarch64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let buffer = backend
-            .compile_function(&mut func, &domtree, false)
-            .unwrap()
-            .buffer;
-        let code = buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following command
-        // on it to update:
-        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
-        //
-        // 0:   52824682        mov     w2, #0x1234                     // #4660
-        // 4:   0b020000        add     w0, w0, w2
-        // 8:   d65f03c0        ret
-
-        let golden = vec![130, 70, 130, 82, 0, 0, 2, 11, 192, 3, 95, 214];
-
-        assert_eq!(code, &golden[..]);
-    }
-
-    #[test]
-    fn test_branch_lowering() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-        let bb1 = func.dfg.make_block();
-        let bb2 = func.dfg.make_block();
-        let bb3 = func.dfg.make_block();
-
-        let mut pos = FuncCursor::new(&mut func);
-        pos.insert_block(bb0);
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().brif(v1, bb1, &[], bb2, &[]);
-        pos.insert_block(bb1);
-        pos.ins().brif(v1, bb2, &[], bb3, &[]);
-        pos.insert_block(bb2);
-        let v2 = pos.ins().iadd(v1, v0);
-        pos.ins().brif(v2, bb2, &[], bb1, &[]);
-        pos.insert_block(bb3);
-        let v3 = pos.ins().isub(v1, v0);
-        pos.ins().return_(&[v3]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
-        let backend = AArch64Backend::new_with_flags(
-            Triple::from_str("aarch64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following command
-        // on it to update:
-        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
-        //
-        //   0:   52824689        mov     w9, #0x1234                     // #4660
-        //   4:   0b09000b        add     w11, w0, w9
-        //   8:   2a0b03ea        mov     w10, w11
-        //   c:   b40000ca        cbz     x10, 0x24
-        //  10:   2a0b03ed        mov     w13, w11
-        //  14:   b500008d        cbnz    x13, 0x24
-        //  18:   5282468e        mov     w14, #0x1234                    // #4660
-        //  1c:   4b0e0160        sub     w0, w11, w14
-        //  20:   d65f03c0        ret
-        //  24:   5282468f        mov     w15, #0x1234                    // #4660
-        //  28:   0b0f0161        add     w1, w11, w15
-        //  2c:   2a0103e0        mov     w0, w1
-        //  30:   b5ffffa0        cbnz    x0, 0x24
-        //  34:   17fffff7        b       0x10
-
-        let golden = vec![
-            137, 70, 130, 82, 11, 0, 9, 11, 234, 3, 11, 42, 202, 0, 0, 180, 237, 3, 11, 42, 141, 0,
-            0, 181, 142, 70, 130, 82, 96, 1, 14, 75, 192, 3, 95, 214, 143, 70, 130, 82, 97, 1, 15,
-            11, 224, 3, 1, 42, 160, 255, 255, 181, 247, 255, 255, 23,
-        ];
-
-        assert_eq!(code, &golden[..]);
-    }
-
-    #[test]
-    fn test_br_table() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-        let bb1 = func.dfg.make_block();
-        let bb2 = func.dfg.make_block();
-        let bb3 = func.dfg.make_block();
-
-        let mut pos = FuncCursor::new(&mut func);
-
-        pos.insert_block(bb0);
-        let jt_data = JumpTableData::new(
-            pos.func.dfg.block_call(bb3, &[]),
-            &[
-                pos.func.dfg.block_call(bb1, &[]),
-                pos.func.dfg.block_call(bb2, &[]),
-            ],
-        );
-        let jt = pos.func.create_jump_table(jt_data);
-        pos.ins().br_table(arg0, jt);
-
-        pos.insert_block(bb1);
-        let v1 = pos.ins().iconst(I32, 1);
-        pos.ins().return_(&[v1]);
-
-        pos.insert_block(bb2);
-        let v2 = pos.ins().iconst(I32, 2);
-        pos.ins().return_(&[v2]);
-
-        pos.insert_block(bb3);
-        let v3 = pos.ins().iconst(I32, 3);
-        pos.ins().return_(&[v3]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        shared_flags_builder.set("enable_verifier", "true").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = aarch64_settings::Flags::new(&shared_flags, aarch64_settings::builder());
-        let backend = AArch64Backend::new_with_flags(
-            Triple::from_str("aarch64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following command
-        // on it to update:
-        // > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
-        //
-        //   0:   7100081f        cmp     w0, #0x2
-        //   4:   540001a2        b.cs    0x38  // b.hs, b.nlast
-        //   8:   9a8023e8        csel    x8, xzr, x0, cs  // cs = hs, nlast
-        //   c:   d503229f        csdb
-        //  10:   10000087        adr     x7, 0x20
-        //  14:   b8a858e8        ldrsw   x8, [x7, w8, uxtw #2]
-        //  18:   8b0800e7        add     x7, x7, x8
-        //  1c:   d61f00e0        br      x7
-        //  20:   00000010        udf     #16
-        //  24:   00000008        udf     #8
-        //  28:   52800040        mov     w0, #0x2                        // #2
-        //  2c:   d65f03c0        ret
-        //  30:   52800020        mov     w0, #0x1                        // #1
-        //  34:   d65f03c0        ret
-        //  38:   52800060        mov     w0, #0x3                        // #3
-        //  3c:   d65f03c0        ret
-
-        let golden = vec![
-            31, 8, 0, 113, 162, 1, 0, 84, 232, 35, 128, 154, 159, 34, 3, 213, 135, 0, 0, 16, 232,
-            88, 168, 184, 231, 0, 8, 139, 224, 0, 31, 214, 16, 0, 0, 0, 8, 0, 0, 0, 64, 0, 128, 82,
-            192, 3, 95, 214, 32, 0, 128, 82, 192, 3, 95, 214, 96, 0, 128, 82, 192, 3, 95, 214,
-        ];
-
-        assert_eq!(code, &golden[..]);
-    }
-}
diff --git a/cranelift/codegen/src/isa/riscv64/mod.rs b/cranelift/codegen/src/isa/riscv64/mod.rs
index 1a068a14d..53d903481 100644
--- a/cranelift/codegen/src/isa/riscv64/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/mod.rs
@@ -214,64 +214,3 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder {
         },
     }
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::cursor::{Cursor, FuncCursor};
-    use crate::dominator_tree::DominatorTree;
-    use crate::flowgraph::ControlFlowGraph;
-    use crate::ir::types::*;
-    use crate::ir::{AbiParam, Function, InstBuilder, Signature, UserFuncName};
-    use crate::isa::CallConv;
-    use crate::settings;
-    use crate::settings::Configurable;
-    use core::str::FromStr;
-    use target_lexicon::Triple;
-
-    #[test]
-    fn test_compile_function() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-
-        let mut pos = FuncCursor::new(&mut func);
-        pos.insert_block(bb0);
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().return_(&[v1]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = riscv_settings::Flags::new(&shared_flags, riscv_settings::builder());
-        let backend = Riscv64Backend::new_with_flags(
-            Triple::from_str("riscv64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let buffer = backend.compile_function(&mut func, &domtree, true).unwrap();
-        let code = buffer.buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following command
-        // on it to update:
-        // > riscv64-linux-gnu-objdump -b binary -D <file> -m riscv
-        //
-        // 0:   000015b7                lui     a1,0x1
-        // 4:   23458593                addi    a1,a1,564 # 0x1234
-        // 8:   00b5053b                .4byte  0xb5053b
-        // c:   00008067                ret
-
-        let golden = vec![
-            183, 21, 0, 0, 147, 133, 69, 35, 59, 5, 181, 0, 103, 128, 0, 0,
-        ];
-        assert_eq!(code, &golden[..]);
-    }
-}
diff --git a/cranelift/codegen/src/isa/s390x/mod.rs b/cranelift/codegen/src/isa/s390x/mod.rs
index 893e30ec1..6a6dad94c 100644
--- a/cranelift/codegen/src/isa/s390x/mod.rs
+++ b/cranelift/codegen/src/isa/s390x/mod.rs
@@ -211,130 +211,3 @@ pub fn isa_builder(triple: Triple) -> IsaBuilder {
         },
     }
 }
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::cursor::{Cursor, FuncCursor};
-    use crate::dominator_tree::DominatorTree;
-    use crate::flowgraph::ControlFlowGraph;
-    use crate::ir::types::*;
-    use crate::ir::UserFuncName;
-    use crate::ir::{AbiParam, Function, InstBuilder, Signature};
-    use crate::isa::CallConv;
-    use crate::settings;
-    use crate::settings::Configurable;
-    use core::str::FromStr;
-    use target_lexicon::Triple;
-
-    #[test]
-    fn test_compile_function() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-
-        let mut pos = FuncCursor::new(&mut func);
-        pos.insert_block(bb0);
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().return_(&[v1]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = s390x_settings::Flags::new(&shared_flags, s390x_settings::builder());
-        let backend = S390xBackend::new_with_flags(
-            Triple::from_str("s390x").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // ahi %r2, 0x1234
-        // br %r14
-        let golden = vec![0xa7, 0x2a, 0x12, 0x34, 0x07, 0xfe];
-
-        assert_eq!(code, &golden[..]);
-    }
-
-    #[test]
-    fn test_branch_lowering() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-        let bb1 = func.dfg.make_block();
-        let bb2 = func.dfg.make_block();
-        let bb3 = func.dfg.make_block();
-
-        let mut pos = FuncCursor::new(&mut func);
-        pos.insert_block(bb0);
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().brif(v1, bb1, &[], bb2, &[]);
-        pos.insert_block(bb1);
-        pos.ins().brif(v1, bb2, &[], bb3, &[]);
-        pos.insert_block(bb2);
-        let v2 = pos.ins().iadd(v1, v0);
-        pos.ins().brif(v2, bb2, &[], bb1, &[]);
-        pos.insert_block(bb3);
-        let v3 = pos.ins().isub(v1, v0);
-        pos.ins().return_(&[v3]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = s390x_settings::Flags::new(&shared_flags, s390x_settings::builder());
-        let backend = S390xBackend::new_with_flags(
-            Triple::from_str("s390x").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // FIXME: the branching logic should be optimized more
-
-        // To update this comment, write the golden bytes to a file, and run the following command
-        // on it to update:
-        // > s390x-linux-gnu-objdump -b binary -D <file> -m s390
-        //
-        //  0:   a7 2a 12 34             ahi     %r2,4660
-        //  4:   a7 2e 00 00             chi     %r2,0
-        //  8:   c0 94 00 00 00 0b       jgnlh   0x1e
-        //  e:   a7 2e 00 00             chi     %r2,0
-        // 12:   c0 64 00 00 00 06       jglh    0x1e
-        // 18:   a7 2a ed cc             ahi     %r2,-4660
-        // 1c:   07 fe                   br      %r14
-        // 1e:   ec 32 12 34 00 d8       ahik    %r3,%r2,4660
-        // 24:   a7 3e 00 00             chi     %r3,0
-        // 28:   c0 64 ff ff ff fb       jglh    0x1e
-        // 2e:   c0 f4 ff ff ff f0       jg      0xe
-
-        let golden = vec![
-            167, 42, 18, 52, 167, 46, 0, 0, 192, 148, 0, 0, 0, 11, 167, 46, 0, 0, 192, 100, 0, 0,
-            0, 6, 167, 42, 237, 204, 7, 254, 236, 50, 18, 52, 0, 216, 167, 62, 0, 0, 192, 100, 255,
-            255, 255, 251, 192, 244, 255, 255, 255, 240,
-        ];
-
-        assert_eq!(code, &golden[..]);
-    }
-}
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index 68f3c566a..34e3145c5 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -4049,16 +4049,10 @@
             ;; wasm-table index) and then 64-bits (address addend). The small
             ;; lie about the I64 type is benign, since the temporary is dead
             ;; after this instruction (and its Cranelift type is thus unused).
-            (tmp2 WritableGpr (temp_writable_gpr))
-
-            (size OperandSize (raw_operand_size_of_type ty))
-
-            (jt_size u32 (jump_table_size jt_targets)))
+            (tmp2 WritableGpr (temp_writable_gpr)))
 
-        (with_flags_side_effect
-          (x64_cmp size (RegMemImm.Imm jt_size) idx)
-          (ConsumesFlags.ConsumesFlagsSideEffect
-            (MInst.JmpTableSeq idx tmp1 tmp2 default_target jt_targets)))))
+          (SideEffectNoResult.Inst
+            (MInst.JmpTableSeq idx tmp1 tmp2 default_target jt_targets))))
 
 ;;;; iadd_pairwise constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index 3674c6c29..e43746c42 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1596,42 +1596,15 @@ pub(crate) fn emit(
             // maximum range of 2 GB. If we later consider using shorter-range label references,
             // this will need to be revisited.
 
-            // Save index in a tmp (the live range of ridx only goes to start of this
-            // sequence; rtmp1 or rtmp2 may overwrite it).
-
-            // We generate the following sequence:
-            // ;; generated by lowering: cmp #jmp_table_size, %idx
-            // jnb $default_target
-            // movl %idx, %tmp2
-            // mov $0, %tmp1
-            // cmovnb %tmp1, %tmp2 ;; Spectre mitigation.
+            // We generate the following sequence. Note that the only read of %idx is before the
+            // write to %tmp2, so regalloc may use the same register for both; fix x64/inst/mod.rs
+            // if you change this.
             // lea start_of_jump_table_offset(%rip), %tmp1
-            // movslq [%tmp1, %tmp2, 4], %tmp2 ;; shift of 2, viz. multiply index by 4
+            // movslq [%tmp1, %idx, 4], %tmp2 ;; shift of 2, viz. multiply index by 4
             // addq %tmp2, %tmp1
             // j *%tmp1
             // $start_of_jump_table:
             // -- jump table entries
-            one_way_jmp(sink, CC::NB, *default_target); // idx unsigned >= jmp table size
-
-            // Copy the index (and make sure to clear the high 32-bits lane of tmp2).
-            let inst = Inst::movzx_rm_r(ExtMode::LQ, RegMem::reg(idx), tmp2);
-            inst.emit(&[], sink, info, state);
-
-            // Zero `tmp1` to overwrite `tmp2` with zeroes on the
-            // out-of-bounds case (Spectre mitigation using CMOV).
-            // Note that we need to do this with a move-immediate
-            // form, because we cannot clobber the flags.
-            let inst = Inst::imm(OperandSize::Size32, 0, tmp1);
-            inst.emit(&[], sink, info, state);
-
-            // Spectre mitigation: CMOV to zero the index if the out-of-bounds branch above misspeculated.
-            let inst = Inst::cmove(
-                OperandSize::Size64,
-                CC::NB,
-                RegMem::reg(tmp1.to_reg()),
-                tmp2,
-            );
-            inst.emit(&[], sink, info, state);
 
             // Load base address of jump table.
             let start_of_jumptable = sink.get_label();
@@ -1645,7 +1618,7 @@ pub(crate) fn emit(
                 RegMem::mem(Amode::imm_reg_reg_shift(
                     0,
                     Gpr::new(tmp1.to_reg()).unwrap(),
-                    Gpr::new(tmp2.to_reg()).unwrap(),
+                    Gpr::new(idx).unwrap(),
                     2,
                 )),
                 tmp2,
@@ -1668,7 +1641,7 @@ pub(crate) fn emit(
             // Emit jump table (table of 32-bit offsets).
             sink.bind_label(start_of_jumptable);
             let jt_off = sink.cur_offset();
-            for &target in targets.iter() {
+            for &target in targets.iter().chain(std::iter::once(default_target)) {
                 let word_off = sink.cur_offset();
                 // off_into_table is an addend here embedded in the label to be later patched at
                 // the end of codegen. The offset is initially relative to this jump table entry;
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 4e15b2aea..b0002c59f 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -2216,7 +2216,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         } => {
             collector.reg_use(*idx);
             collector.reg_early_def(*tmp1);
-            collector.reg_early_def(*tmp2);
+            // In the sequence emitted for this pseudoinstruction in emit.rs,
+            // tmp2 is only written after idx is read, so it doesn't need to be
+            // an early def.
+            collector.reg_def(*tmp2);
         }
 
         Inst::JmpUnknown { target } => {
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index 9945f0869..ff14669e7 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -2923,7 +2923,14 @@
 ;; Rules for `br_table` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (rule (lower_branch (br_table idx @ (value_type ty) _) (jump_table_targets default_target jt_targets))
-      (emit_side_effect (jmp_table_seq ty idx default_target jt_targets)))
+      (let ((size OperandSize (raw_operand_size_of_type ty))
+            (jt_size u32 (jump_table_size jt_targets))
+            (size_reg Reg (imm ty (u32_as_u64 jt_size)))
+            (idx_reg Gpr (extend_to_gpr idx $I64 (ExtendKind.Zero)))
+            (clamped_idx Reg (with_flags_reg
+              (x64_cmp size size_reg idx_reg)
+              (cmove ty (CC.B) idx_reg size_reg))))
+      (emit_side_effect (jmp_table_seq ty clamped_idx default_target jt_targets))))
 
 ;; Rules for `select_spectre_guard` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs
index 67c3475f7..7c085ad58 100644
--- a/cranelift/codegen/src/isa/x64/mod.rs
+++ b/cranelift/codegen/src/isa/x64/mod.rs
@@ -233,161 +233,8 @@ fn isa_constructor(
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::cursor::{Cursor, FuncCursor};
-    use crate::dominator_tree::DominatorTree;
-    use crate::flowgraph::ControlFlowGraph;
-    use crate::ir::{types::*, RelSourceLoc, SourceLoc, UserFuncName, ValueLabel, ValueLabelStart};
-    use crate::ir::{AbiParam, Function, InstBuilder, JumpTableData, Signature};
-    use crate::isa::CallConv;
     use crate::settings;
     use crate::settings::Configurable;
-    use core::str::FromStr;
-    use cranelift_entity::EntityRef;
-    use target_lexicon::Triple;
-
-    /// We have to test cold blocks by observing final machine code,
-    /// rather than VCode, because the VCode orders blocks in lowering
-    /// order, not emission order. (The exact difference between the
-    /// two is that cold blocks are sunk in the latter.) We might as
-    /// well do the test here, where we have a backend to use.
-    #[test]
-    fn test_cold_blocks() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-        // Add debug info: this tests the debug machinery wrt cold
-        // blocks as well.
-        func.dfg.collect_debug_info();
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-        let bb1 = func.dfg.make_block();
-        let bb2 = func.dfg.make_block();
-        let bb3 = func.dfg.make_block();
-        let bb1_param = func.dfg.append_block_param(bb1, I32);
-        let bb3_param = func.dfg.append_block_param(bb3, I32);
-
-        let mut pos = FuncCursor::new(&mut func);
-
-        pos.insert_block(bb0);
-        pos.set_srcloc(SourceLoc::new(1));
-        let v0 = pos.ins().iconst(I32, 0x1234);
-        pos.set_srcloc(SourceLoc::new(2));
-        let v1 = pos.ins().iadd(arg0, v0);
-        pos.ins().brif(v1, bb1, &[v1], bb2, &[]);
-
-        pos.insert_block(bb1);
-        pos.set_srcloc(SourceLoc::new(3));
-        let v2 = pos.ins().isub(v1, v0);
-        pos.set_srcloc(SourceLoc::new(4));
-        let v3 = pos.ins().iadd(v2, bb1_param);
-        pos.ins().brif(v1, bb2, &[], bb3, &[v3]);
-
-        pos.func.layout.set_cold(bb2);
-        pos.insert_block(bb2);
-        pos.set_srcloc(SourceLoc::new(5));
-        let v4 = pos.ins().iadd(v1, v0);
-        pos.ins().brif(v4, bb2, &[], bb1, &[v4]);
-
-        pos.insert_block(bb3);
-        pos.set_srcloc(SourceLoc::new(6));
-        pos.ins().return_(&[bb3_param]);
-
-        // Create some debug info. Make one label that follows all the
-        // values around. Note that this is usually done via an API on
-        // the FunctionBuilder, but that's in cranelift_frontend
-        // (i.e., a higher level of the crate DAG) so we have to build
-        // it manually here.
-        pos.func.dfg.values_labels.as_mut().unwrap().insert(
-            v0,
-            crate::ir::ValueLabelAssignments::Starts(vec![ValueLabelStart {
-                from: RelSourceLoc::new(1),
-                label: ValueLabel::new(1),
-            }]),
-        );
-        pos.func.dfg.values_labels.as_mut().unwrap().insert(
-            v1,
-            crate::ir::ValueLabelAssignments::Starts(vec![ValueLabelStart {
-                from: RelSourceLoc::new(2),
-                label: ValueLabel::new(1),
-            }]),
-        );
-        pos.func.dfg.values_labels.as_mut().unwrap().insert(
-            v2,
-            crate::ir::ValueLabelAssignments::Starts(vec![ValueLabelStart {
-                from: RelSourceLoc::new(3),
-                label: ValueLabel::new(1),
-            }]),
-        );
-        pos.func.dfg.values_labels.as_mut().unwrap().insert(
-            v3,
-            crate::ir::ValueLabelAssignments::Starts(vec![ValueLabelStart {
-                from: RelSourceLoc::new(4),
-                label: ValueLabel::new(1),
-            }]),
-        );
-        pos.func.dfg.values_labels.as_mut().unwrap().insert(
-            v4,
-            crate::ir::ValueLabelAssignments::Starts(vec![ValueLabelStart {
-                from: RelSourceLoc::new(5),
-                label: ValueLabel::new(1),
-            }]),
-        );
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        shared_flags_builder.set("enable_verifier", "true").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = x64_settings::Flags::new(&shared_flags, x64_settings::builder());
-        let backend = X64Backend::new_with_flags(
-            Triple::from_str("x86_64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following
-        // command on it:
-        // > objdump -b binary -D <file> -m i386:x86-64 -M intel
-        //
-        //  0:   55                      push   rbp
-        //  1:   48 89 e5                mov    rbp,rsp
-        //  4:   48 89 fe                mov    rsi,rdi
-        //  7:   81 c6 34 12 00 00       add    esi,0x1234
-        //  d:   85 f6                   test   esi,esi
-        //  f:   0f 84 21 00 00 00       je     0x36
-        // 15:   49 89 f0                mov    r8,rsi
-        // 18:   48 89 f0                mov    rax,rsi
-        // 1b:   81 e8 34 12 00 00       sub    eax,0x1234
-        // 21:   44 01 c0                add    eax,r8d
-        // 24:   85 f6                   test   esi,esi
-        // 26:   0f 85 0a 00 00 00       jne    0x36
-        // 2c:   48 89 ec                mov    rsp,rbp
-        // 2f:   5d                      pop    rbp
-        // 30:   c3                      ret
-        // 31:   e9 e2 ff ff ff          jmp    0x18
-        // 36:   49 89 f0                mov    r8,rsi
-        // 39:   41 81 c0 34 12 00 00    add    r8d,0x1234
-        // 40:   45 85 c0                test   r8d,r8d
-        // 43:   0f 84 cf ff ff ff       je     0x18
-        // 49:   e9 e8 ff ff ff          jmp    0x36
-
-        let golden = vec![
-            85, 72, 137, 229, 72, 137, 254, 129, 198, 52, 18, 0, 0, 133, 246, 15, 132, 33, 0, 0, 0,
-            73, 137, 240, 72, 137, 240, 129, 232, 52, 18, 0, 0, 68, 1, 192, 133, 246, 15, 133, 10,
-            0, 0, 0, 72, 137, 236, 93, 195, 233, 226, 255, 255, 255, 73, 137, 240, 65, 129, 192,
-            52, 18, 0, 0, 69, 133, 192, 15, 132, 207, 255, 255, 255, 233, 232, 255, 255, 255,
-        ];
-
-        assert_eq!(code, &golden[..]);
-    }
 
     // Check that feature tests for SIMD work correctly.
     #[test]
@@ -405,105 +252,4 @@ mod test {
             Err(CodegenError::Unsupported(_)),
         ));
     }
-
-    // Check that br_table lowers properly. We can't test this with an
-    // ordinary compile-test because the br_table pseudoinstruction
-    // expands during emission.
-    #[test]
-    fn br_table() {
-        let name = UserFuncName::testcase("test0");
-        let mut sig = Signature::new(CallConv::SystemV);
-        sig.params.push(AbiParam::new(I32));
-        sig.returns.push(AbiParam::new(I32));
-        let mut func = Function::with_name_signature(name, sig);
-
-        let bb0 = func.dfg.make_block();
-        let arg0 = func.dfg.append_block_param(bb0, I32);
-        let bb1 = func.dfg.make_block();
-        let bb2 = func.dfg.make_block();
-        let bb3 = func.dfg.make_block();
-
-        let mut pos = FuncCursor::new(&mut func);
-
-        pos.insert_block(bb0);
-        let jt_data = JumpTableData::new(
-            pos.func.dfg.block_call(bb3, &[]),
-            &[
-                pos.func.dfg.block_call(bb1, &[]),
-                pos.func.dfg.block_call(bb2, &[]),
-            ],
-        );
-        let jt = pos.func.create_jump_table(jt_data);
-        pos.ins().br_table(arg0, jt);
-
-        pos.insert_block(bb1);
-        let v1 = pos.ins().iconst(I32, 1);
-        pos.ins().return_(&[v1]);
-
-        pos.insert_block(bb2);
-        let v2 = pos.ins().iconst(I32, 2);
-        pos.ins().return_(&[v2]);
-
-        pos.insert_block(bb3);
-        let v3 = pos.ins().iconst(I32, 3);
-        pos.ins().return_(&[v3]);
-
-        let mut shared_flags_builder = settings::builder();
-        shared_flags_builder.set("opt_level", "none").unwrap();
-        shared_flags_builder.set("enable_verifier", "true").unwrap();
-        let shared_flags = settings::Flags::new(shared_flags_builder);
-        let isa_flags = x64_settings::Flags::new(&shared_flags, x64_settings::builder());
-        let backend = X64Backend::new_with_flags(
-            Triple::from_str("x86_64").unwrap(),
-            shared_flags,
-            isa_flags,
-        );
-        let cfg = ControlFlowGraph::with_function(&func);
-        let domtree = DominatorTree::with_function(&func, &cfg);
-        let result = backend
-            .compile_function(&mut func, &domtree, /* want_disasm = */ false)
-            .unwrap();
-        let code = result.buffer.data();
-
-        // To update this comment, write the golden bytes to a file, and run the following
-        // command on it:
-        // > objdump -b binary -D <file> -m i386:x86-64 -M intel
-        //
-        //  0:   55                      push   rbp
-        //  1:   48 89 e5                mov    rbp,rsp
-        //  4:   83 ff 02                cmp    edi,0x2
-        //  7:   0f 83 3b 00 00 00       jae    0x48
-        //  d:   44 8b d7                mov    r10d,edi
-        // 10:   41 b9 00 00 00 00       mov    r9d,0x0
-        // 16:   4d 0f 43 d1             cmovae r10,r9
-        // 1a:   4c 8d 0d 0b 00 00 00    lea    r9,[rip+0xb]        # 0x2c
-        // 21:   4f 63 54 91 00          movsxd r10,DWORD PTR [r9+r10*4+0x0]
-        // 26:   4d 01 d1                add    r9,r10
-        // 29:   41 ff e1                jmp    r9
-        // 2c:   12 00                   adc    al,BYTE PTR [rax]
-        // 2e:   00 00                   add    BYTE PTR [rax],al
-        // 30:   08 00                   or     BYTE PTR [rax],al
-        // 32:   00 00                   add    BYTE PTR [rax],al
-        // 34:   b8 02 00 00 00          mov    eax,0x2
-        // 39:   48 89 ec                mov    rsp,rbp
-        // 3c:   5d                      pop    rbp
-        // 3d:   c3                      ret
-        // 3e:   b8 01 00 00 00          mov    eax,0x1
-        // 43:   48 89 ec                mov    rsp,rbp
-        // 46:   5d                      pop    rbp
-        // 47:   c3                      ret
-        // 48:   b8 03 00 00 00          mov    eax,0x3
-        // 4d:   48 89 ec                mov    rsp,rbp
-        // 50:   5d                      pop    rbp
-        // 51:   c3                      ret
-
-        let golden = vec![
-            85, 72, 137, 229, 131, 255, 2, 15, 131, 59, 0, 0, 0, 68, 139, 215, 65, 185, 0, 0, 0, 0,
-            77, 15, 67, 209, 76, 141, 13, 11, 0, 0, 0, 79, 99, 84, 145, 0, 77, 1, 209, 65, 255,
-            225, 18, 0, 0, 0, 8, 0, 0, 0, 184, 2, 0, 0, 0, 72, 137, 236, 93, 195, 184, 1, 0, 0, 0,
-            72, 137, 236, 93, 195, 184, 3, 0, 0, 0, 72, 137, 236, 93, 195,
-        ];
-
-        assert_eq!(code, &golden[..]);
-    }
 }
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index 2ed29e6a3..b656dc2ea 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -785,7 +785,6 @@ pub fn set_fuel<T>(store: &mut Store<T>, fuel: u64) {
 /// arbitrary types and values.
 pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbitrary::Result<()> {
     use crate::generators::component_types;
-    use anyhow::Result;
     use component_fuzz_util::{TestCase, EXPORT_FUNCTION, IMPORT_FUNCTION};
     use component_test_util::FuncExt;
     use wasmtime::component::{Component, Linker, Val};
diff --git a/crates/runtime/src/parking_spot.rs b/crates/runtime/src/parking_spot.rs
index dee1019b2..33f9bce0f 100644
--- a/crates/runtime/src/parking_spot.rs
+++ b/crates/runtime/src/parking_spot.rs
@@ -104,17 +104,53 @@ impl ParkingSpot {
             let spot = inner.get_mut(&key).expect("failed to get spot");
 
             if timed_out {
-                if let Some(timeout) = timeout {
-                    if Instant::now() < timeout {
-                        // Did not sleep long enough, try again.
-                        continue;
-                    }
+                // If waiting on the cvar timed out then due to how system cvars
+                // are implemented we may need to continue to sleep longer. If
+                // the deadline has not been reached then turn the crank again
+                // and go back to sleep.
+                if Instant::now() < timeout.unwrap() {
+                    continue;
+                }
+
+                // Opportunistically consume `to_unpark` signals even on
+                // timeout. From the perspective of `unpark` this "agent" raced
+                // between its own timeout and receiving the unpark signal, but
+                // from unpark's perspective it's definitely going to wake up N
+                // agents as returned from the `unpark` return value.
+                //
+                // Note that this may actually prevent other threads from
+                // getting unparked. For example:
+                //
+                // * Thread A parks with a timeout
+                // * Thread B parks with no timeout
+                // * Thread C decides to unpark 1 thread
+                // * Thread A's cvar wakes up due to a timeout, blocks on the
+                //   lock
+                // * Thread C finishes unpark and signals the cvar once
+                // * Thread B wakes up
+                // * Thread A and B contend for the lock and A wins
+                // * A consumes the "to_unpark" value
+                // * B goes back to sleep since `to_unpark == 0`, thinking that
+                //   a spurious wakeup happened.
+                //
+                // It's believed that this is ok, however, since from C's
+                // perspective one agent was still woken up and is allowed to
+                // continue, notably A in this case. C doesn't know that A raced
+                // with B and "stole" its wakeup signal.
+                if spot.to_unpark > 0 {
+                    spot.to_unpark -= 1;
                 }
             } else {
                 if spot.to_unpark == 0 {
+                    // If no timeout happen but nothing has unparked this spot (as
+                    // signaled through `to_unpark`) then this is indicative of a
+                    // spurious wakeup. In this situation turn the crank again and
+                    // go back to sleep as this interface doesn't allow for spurious
+                    // wakeups.
                     continue;
                 }
-
+                // No timeout happened, and some other thread registered to
+                // unpark this thread, so consume one unpark notification.
                 spot.to_unpark -= 1;
             }
 
@@ -176,55 +212,53 @@ impl ParkingSpot {
 #[cfg(test)]
 mod tests {
     use super::ParkingSpot;
-    use once_cell::sync::Lazy;
     use std::ptr::addr_of;
     use std::sync::atomic::{AtomicU64, Ordering};
     use std::thread;
-
-    static PARKING_SPOT: Lazy<ParkingSpot> = Lazy::new(ParkingSpot::default);
-
-    static ATOMIC: AtomicU64 = AtomicU64::new(0);
+    use std::time::{Duration, Instant};
 
     #[test]
     fn atomic_wait_notify() {
-        let thread1 = thread::spawn(|| {
-            let atomic_key = addr_of!(ATOMIC) as u64;
-            ATOMIC.store(1, Ordering::SeqCst);
-            PARKING_SPOT.unpark(atomic_key, u32::MAX);
-            PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) == 1, None);
-        });
+        let parking_spot = &ParkingSpot::default();
+        let atomic = &AtomicU64::new(0);
+
+        thread::scope(|s| {
+            let atomic_key = addr_of!(atomic) as u64;
+            let thread1 = s.spawn(move || {
+                atomic.store(1, Ordering::SeqCst);
+                parking_spot.unpark(atomic_key, u32::MAX);
+                parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) == 1, None);
+            });
+
+            let thread2 = s.spawn(move || {
+                while atomic.load(Ordering::SeqCst) != 1 {
+                    parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) != 1, None);
+                }
+                atomic.store(2, Ordering::SeqCst);
+                parking_spot.unpark(atomic_key, u32::MAX);
+                parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) == 2, None);
+            });
+
+            let thread3 = s.spawn(move || {
+                while atomic.load(Ordering::SeqCst) != 2 {
+                    parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) != 2, None);
+                }
+                atomic.store(3, Ordering::SeqCst);
+                parking_spot.unpark(atomic_key, u32::MAX);
 
-        let thread2 = thread::spawn(|| {
-            let atomic_key = addr_of!(ATOMIC) as u64;
-            while ATOMIC.load(Ordering::SeqCst) != 1 {
-                PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) != 1, None);
-            }
-            ATOMIC.store(2, Ordering::SeqCst);
-            PARKING_SPOT.unpark(atomic_key, u32::MAX);
-            PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) == 2, None);
-        });
+                parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) == 3, None);
+            });
 
-        let thread3 = thread::spawn(|| {
-            let atomic_key = addr_of!(ATOMIC) as u64;
-            while ATOMIC.load(Ordering::SeqCst) != 2 {
-                PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) != 2, None);
+            while atomic.load(Ordering::SeqCst) != 3 {
+                parking_spot.park(atomic_key, || atomic.load(Ordering::SeqCst) != 3, None);
             }
-            ATOMIC.store(3, Ordering::SeqCst);
-            PARKING_SPOT.unpark(atomic_key, u32::MAX);
+            atomic.store(4, Ordering::SeqCst);
+            parking_spot.unpark(atomic_key, u32::MAX);
 
-            PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) == 3, None);
+            thread1.join().unwrap();
+            thread2.join().unwrap();
+            thread3.join().unwrap();
         });
-
-        let atomic_key = addr_of!(ATOMIC) as u64;
-        while ATOMIC.load(Ordering::SeqCst) != 3 {
-            PARKING_SPOT.park(atomic_key, || ATOMIC.load(Ordering::SeqCst) != 3, None);
-        }
-        ATOMIC.store(4, Ordering::SeqCst);
-        PARKING_SPOT.unpark(atomic_key, u32::MAX);
-
-        thread1.join().unwrap();
-        thread2.join().unwrap();
-        thread3.join().unwrap();
     }
 
     mod parking_lot {
@@ -302,47 +336,53 @@ mod tests {
             num_threads: u32,
             num_single_unparks: u32,
         ) {
-            let mut tests = Vec::with_capacity(num_latches);
-
-            for _ in 0..num_latches {
-                let test = Arc::new(SingleLatchTest::new(num_threads));
-                let mut threads = Vec::with_capacity(num_threads as _);
-                for _ in 0..num_threads {
-                    let test = test.clone();
-                    threads.push(thread::spawn(move || test.run()));
+            let spot = ParkingSpot::default();
+
+            thread::scope(|s| {
+                let mut tests = Vec::with_capacity(num_latches);
+
+                for _ in 0..num_latches {
+                    let test = Arc::new(SingleLatchTest::new(num_threads, &spot));
+                    let mut threads = Vec::with_capacity(num_threads as _);
+                    for _ in 0..num_threads {
+                        let test = test.clone();
+                        threads.push(s.spawn(move || test.run()));
+                    }
+                    tests.push((test, threads));
                 }
-                tests.push((test, threads));
-            }
 
-            for unpark_index in 0..num_single_unparks {
-                thread::sleep(delay);
-                for (test, _) in &tests {
-                    test.unpark_one(unpark_index);
+                for unpark_index in 0..num_single_unparks {
+                    thread::sleep(delay);
+                    for (test, _) in &tests {
+                        test.unpark_one(unpark_index);
+                    }
                 }
-            }
 
-            for (test, threads) in tests {
-                test.finish(num_single_unparks);
-                for thread in threads {
-                    thread.join().expect("Test thread panic");
+                for (test, threads) in tests {
+                    test.finish(num_single_unparks);
+                    for thread in threads {
+                        thread.join().expect("Test thread panic");
+                    }
                 }
-            }
+            });
         }
 
-        struct SingleLatchTest {
+        struct SingleLatchTest<'a> {
             semaphore: AtomicIsize,
             num_awake: AtomicU32,
             /// Total number of threads participating in this test.
             num_threads: u32,
+            spot: &'a ParkingSpot,
         }
 
-        impl SingleLatchTest {
-            pub fn new(num_threads: u32) -> Self {
+        impl<'a> SingleLatchTest<'a> {
+            pub fn new(num_threads: u32, spot: &'a ParkingSpot) -> Self {
                 Self {
                     // This implements a fair (FIFO) semaphore, and it starts out unavailable.
                     semaphore: AtomicIsize::new(0),
                     num_awake: AtomicU32::new(0),
                     num_threads,
+                    spot,
                 }
             }
 
@@ -373,14 +413,14 @@ mod tests {
                 // still be threads that has not yet parked.
                 while num_threads_left > 0 {
                     let mut num_waiting_on_address = 0;
-                    PARKING_SPOT.with_lot(self.semaphore_addr(), |thread_data| {
+                    self.spot.with_lot(self.semaphore_addr(), |thread_data| {
                         num_waiting_on_address = thread_data.num_parked;
                     });
                     assert!(num_waiting_on_address <= num_threads_left);
 
                     let num_awake_before_unpark = self.num_awake.load(Ordering::SeqCst);
 
-                    let num_unparked = PARKING_SPOT.unpark(self.semaphore_addr(), u32::MAX);
+                    let num_unparked = self.spot.unpark(self.semaphore_addr(), u32::MAX);
                     assert!(num_unparked >= num_waiting_on_address);
                     assert!(num_unparked <= num_threads_left);
 
@@ -398,7 +438,7 @@ mod tests {
 
                 // Make sure no thread is parked on our semaphore address
                 let mut num_waiting_on_address = 0;
-                PARKING_SPOT.with_lot(self.semaphore_addr(), |thread_data| {
+                self.spot.with_lot(self.semaphore_addr(), |thread_data| {
                     num_waiting_on_address = thread_data.num_parked;
                 });
                 assert_eq!(num_waiting_on_address, 0);
@@ -414,7 +454,7 @@ mod tests {
 
                 // We need to wait.
                 let validate = || true;
-                PARKING_SPOT.park(self.semaphore_addr(), validate, None);
+                self.spot.park(self.semaphore_addr(), validate, None);
             }
 
             pub fn up(&self) {
@@ -426,7 +466,7 @@ mod tests {
                     // the thread we want to pass ownership to has decremented the semaphore counter,
                     // but not yet parked.
                     loop {
-                        match PARKING_SPOT.unpark(self.semaphore_addr(), 1) {
+                        match self.spot.unpark(self.semaphore_addr(), 1) {
                             1 => break,
                             0 => (),
                             i => panic!("Should not wake up {i} threads"),
@@ -440,4 +480,42 @@ mod tests {
             }
         }
     }
+
+    #[test]
+    fn wait_with_timeout() {
+        let parking_spot = &ParkingSpot::default();
+        let atomic = &AtomicU64::new(0);
+
+        thread::scope(|s| {
+            let atomic_key = addr_of!(atomic) as u64;
+
+            const N: u64 = 5;
+            const M: u64 = 1000;
+
+            let thread = s.spawn(move || {
+                while atomic.load(Ordering::SeqCst) != N * M {
+                    let timeout = Instant::now() + Duration::from_millis(1);
+                    parking_spot.park(
+                        atomic_key,
+                        || atomic.load(Ordering::SeqCst) != N * M,
+                        Some(timeout),
+                    );
+                }
+            });
+
+            let mut threads = vec![thread];
+            for _ in 0..N {
+                threads.push(s.spawn(move || {
+                    for _ in 0..M {
+                        atomic.fetch_add(1, Ordering::SeqCst);
+                        parking_spot.unpark(atomic_key, 1);
+                    }
+                }));
+            }
+
+            for thread in threads {
+                thread.join().unwrap();
+            }
+        });
+    }
 }
diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs
index f1f88c2d7..15212fb92 100644
--- a/crates/wasmtime/src/component/mod.rs
+++ b/crates/wasmtime/src/component/mod.rs
@@ -82,7 +82,6 @@ pub(crate) use self::store::ComponentStoreData;
 /// Then you can interact with the generated bindings like so:
 ///
 /// ```rust,ignore
-/// use anyhow::Result;
 /// use wasmtime::component::*;
 /// use wasmtime::{Config, Engine, Store};
 ///
@@ -97,12 +96,12 @@ pub(crate) use self::store::ComponentStoreData;
 /// impl HelloWorldImports for MyState {
 ///     // Note the `Result` return value here where `Ok` is returned back to
 ///     // the component and `Err` will raise a trap.
-///     fn name(&mut self) -> Result<String> {
+///     fn name(&mut self) -> wasmtime::Result<String> {
 ///         Ok(self.name.clone())
 ///     }
 /// }
 ///
-/// fn main() -> Result<()> {
+/// fn main() -> wasmtime::Result<()> {
 ///     // Configure an `Engine` and compile the `Component` that is being run for
 ///     // the application.
 ///     let mut config = Config::new();
@@ -168,7 +167,6 @@ pub(crate) use self::store::ComponentStoreData;
 /// Then you can interact with the generated bindings like so:
 ///
 /// ```rust,ignore
-/// use anyhow::Result;
 /// use wasmtime::component::*;
 /// use wasmtime::{Config, Engine, Store};
 ///
@@ -180,16 +178,16 @@ pub(crate) use self::store::ComponentStoreData;
 ///
 /// // Note that the trait here is per-interface and within a submodule now.
 /// impl host::Host for MyState {
-///     fn gen_random_integer(&mut self) -> Result<u32> {
+///     fn gen_random_integer(&mut self) -> wasmtime::Result<u32> {
 ///         Ok(rand::thread_rng().gen())
 ///     }
 ///
-///     fn sha256(&mut self, bytes: Vec<u8>) -> Result<String> {
+///     fn sha256(&mut self, bytes: Vec<u8>) -> wasmtime::Result<String> {
 ///         // ...
 ///     }
 /// }
 ///
-/// fn main() -> Result<()> {
+/// fn main() -> wasmtime::Result<()> {
 ///     let mut config = Config::new();
 ///     config.wasm_component_model(true);
 ///     let engine = Engine::new(&config)?;
diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs
index a11bdd940..dd54cdc77 100644
--- a/crates/wasmtime/src/lib.rs
+++ b/crates/wasmtime/src/lib.rs
@@ -27,10 +27,9 @@
 //! An example of using Wasmtime looks like:
 //!
 //! ```
-//! use anyhow::Result;
 //! use wasmtime::*;
 //!
-//! fn main() -> Result<()> {
+//! fn main() -> wasmtime::Result<()> {
 //!     // Modules can be compiled through either the text or binary format
 //!     let engine = Engine::default();
 //!     let wat = r#"
@@ -141,10 +140,9 @@
 //! For example we can reimplement the above example with a `Linker`:
 //!
 //! ```
-//! use anyhow::Result;
 //! use wasmtime::*;
 //!
-//! fn main() -> Result<()> {
+//! fn main() -> wasmtime::Result<()> {
 //!     let engine = Engine::default();
 //!     let wat = r#"
 //!         (module
@@ -301,11 +299,10 @@
 //! An example of using WASI looks like:
 //!
 //! ```no_run
-//! # use anyhow::Result;
 //! # use wasmtime::*;
 //! use wasmtime_wasi::sync::WasiCtxBuilder;
 //!
-//! # fn main() -> Result<()> {
+//! # fn main() -> wasmtime::Result<()> {
 //! // Compile our module and create a `Linker` which has WASI functions defined
 //! // within it.
 //! let engine = Engine::default();
@@ -333,7 +330,7 @@
 //! use std::str;
 //!
 //! # use wasmtime::*;
-//! # fn main() -> anyhow::Result<()> {
+//! # fn main() -> wasmtime::Result<()> {
 //! let mut store = Store::default();
 //! let log_str = Func::wrap(&mut store, |mut caller: Caller<'_, ()>, ptr: i32, len: i32| {
 //!     // Use our `caller` context to learn about the memory export of the
@@ -427,6 +424,13 @@ pub use crate::trap::*;
 pub use crate::types::*;
 pub use crate::values::*;
 
+/// A convenience wrapper for `Result<T, anyhow::Error>`.
+///
+/// This type can be used to interact with `wasmtimes`'s extensive use
+/// of `anyhow::Error` while still not directly depending on `anyhow`.
+/// This type alias is identical to `anyhow::Result`.
+pub use anyhow::{Error, Result};
+
 #[cfg(feature = "component-model")]
 pub mod component;
 
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 9fdde5317..21f029613 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -179,7 +179,7 @@ impl Wasmtime {
                     "
                         pub fn new(
                             __exports: &mut wasmtime::component::ExportInstance<'_, '_>,
-                        ) -> anyhow::Result<{camel}> {{
+                        ) -> wasmtime::Result<{camel}> {{
                     "
                 );
                 let mut fields = Vec::new();
@@ -267,7 +267,7 @@ impl Wasmtime {
                     mut store: impl wasmtime::AsContextMut<Data = T>,
                     component: &wasmtime::component::Component,
                     linker: &wasmtime::component::Linker<T>,
-                ) -> anyhow::Result<(Self, wasmtime::component::Instance)> {{
+                ) -> wasmtime::Result<(Self, wasmtime::component::Instance)> {{
                     let instance = linker.instantiate{async__}(&mut store, component){await_}?;
                     Ok((Self::new(store, &instance)?, instance))
                 }}
@@ -278,7 +278,7 @@ impl Wasmtime {
                 pub {async_} fn instantiate_pre<T {send}>(
                     mut store: impl wasmtime::AsContextMut<Data = T>,
                     instance_pre: &wasmtime::component::InstancePre<T>,
-                ) -> anyhow::Result<(Self, wasmtime::component::Instance)> {{
+                ) -> wasmtime::Result<(Self, wasmtime::component::Instance)> {{
                     let instance = instance_pre.instantiate{async__}(&mut store){await_}?;
                     Ok((Self::new(store, &instance)?, instance))
                 }}
@@ -294,7 +294,7 @@ impl Wasmtime {
                 pub fn new(
                     mut store: impl wasmtime::AsContextMut,
                     instance: &wasmtime::component::Instance,
-                ) -> anyhow::Result<Self> {{
+                ) -> wasmtime::Result<Self> {{
                     let mut store = store.as_context_mut();
                     let mut exports = instance.exports(&mut store);
                     let mut __exports = exports.root();
@@ -397,7 +397,7 @@ impl Wasmtime {
                 pub fn add_to_linker<T, U>(
                     linker: &mut wasmtime::component::Linker<T>,
                     get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
-                ) -> anyhow::Result<()>
+                ) -> wasmtime::Result<()>
                     where U: \
             "
         );
@@ -442,7 +442,7 @@ impl Wasmtime {
                 pub fn add_root_to_linker<T, U>(
                     linker: &mut wasmtime::component::Linker<T>,
                     get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
-                ) -> anyhow::Result<()>
+                ) -> wasmtime::Result<()>
                     where U: {world_trait}{maybe_send}
                 {{
                     let mut linker = linker.root();
@@ -978,7 +978,7 @@ impl<'a> InterfaceGenerator<'a> {
                 pub fn add_to_linker<T, U>(
                     linker: &mut wasmtime::component::Linker<T>,
                     get: impl Fn(&mut T) -> &mut U + Send + Sync + Copy + 'static,
-                ) -> anyhow::Result<()>
+                ) -> wasmtime::Result<()>
                     where {where_clause},
                 {{
             "
@@ -1124,9 +1124,9 @@ impl<'a> InterfaceGenerator<'a> {
             self.push_str(&error_typename);
             self.push_str(">");
         } else {
-            // All other functions get their return values wrapped in an anyhow::Result.
+            // All other functions get their return values wrapped in an wasmtime::Result.
             // Returning the anyhow::Error case can be used to trap.
-            self.push_str("anyhow::Result<");
+            self.push_str("wasmtime::Result<");
             self.print_result_ty(&func.results, TypeMode::Owned);
             self.push_str(">");
         }
@@ -1174,7 +1174,7 @@ impl<'a> InterfaceGenerator<'a> {
             self.print_ty(&param.1, TypeMode::AllBorrowed("'_"));
             self.push_str(",");
         }
-        self.src.push_str(") -> anyhow::Result<");
+        self.src.push_str(") -> wasmtime::Result<");
         self.print_result_ty(&func.results, TypeMode::Owned);
 
         if self.gen.opts.async_ {


Test patch:
diff --git a/ci/build-test-matrix.js b/ci/build-test-matrix.js
index d0dd51b93..9983464d1 100644
--- a/ci/build-test-matrix.js
+++ b/ci/build-test-matrix.js
@@ -97,6 +97,13 @@ function myFilter(item) {
   if (item.filter && commits.includes(`prtest:${item.filter}`)) {
     return true;
   }
+
+  // If any runtest was modified, re-run the whole test suite as those can
+  // target any backend.
+  if (names.includes(`cranelift/filetests/filetests/runtests`)) {
+    return true;
+  }
+
   return false;
 }
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/cold-blocks.clif b/cranelift/filetests/filetests/isa/aarch64/cold-blocks.clif
new file mode 100644
index 000000000..329dd5fb0
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/cold-blocks.clif
@@ -0,0 +1,71 @@
+test compile precise-output
+
+target aarch64
+
+function %no_cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   mov w5, w0
+;   cbnz x5, label1 ; b label2
+; block1:
+;   b label3
+; block2:
+;   movz w0, #97
+;   b label3
+; block3:
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov w5, w0
+;   cbnz x5, #0xc
+; block1: ; offset 0x8
+;   mov w0, #0x61
+; block2: ; offset 0xc
+;   ret
+
+function %cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2 cold:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   mov w5, w0
+;   cbnz x5, label1 ; b label2
+; block1:
+;   b label3
+; block3:
+;   ret
+; block2:
+;   movz w0, #97
+;   b label3
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov w5, w0
+;   cbz x5, #0xc
+; block1: ; offset 0x8
+;   ret
+; block2: ; offset 0xc
+;   mov w0, #0x61
+;   b #8
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif
new file mode 100644
index 000000000..0b7a8cde0
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/cold-blocks.clif
@@ -0,0 +1,71 @@
+test compile precise-output
+
+target riscv64
+
+function %no_cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   sext.w a2,a0
+;   bne a2,zero,taken(label1),not_taken(label2)
+; block1:
+;   j label3
+; block2:
+;   li a0,97
+;   j label3
+; block3:
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   sext.w a2, a0
+;   bnez a2, 8
+; block1: ; offset 0x8
+;   addi a0, zero, 0x61
+; block2: ; offset 0xc
+;   ret
+
+function %cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2 cold:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   sext.w a2,a0
+;   bne a2,zero,taken(label1),not_taken(label2)
+; block1:
+;   j label3
+; block3:
+;   ret
+; block2:
+;   li a0,97
+;   j label3
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   sext.w a2, a0
+;   beqz a2, 8
+; block1: ; offset 0x8
+;   ret
+; block2: ; offset 0xc
+;   addi a0, zero, 0x61
+;   j -8
+
diff --git a/cranelift/filetests/filetests/isa/s390x/cold-blocks.clif b/cranelift/filetests/filetests/isa/s390x/cold-blocks.clif
new file mode 100644
index 000000000..9bb8e8ecd
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/s390x/cold-blocks.clif
@@ -0,0 +1,71 @@
+test compile precise-output
+
+target s390x
+
+function %no_cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   chi %r2, 0
+;   jglh label1 ; jg label2
+; block1:
+;   jg label3
+; block2:
+;   lhi %r2, 97
+;   jg label3
+; block3:
+;   br %r14
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   chi %r2, 0
+;   jglh 0xe
+; block1: ; offset 0xa
+;   lhi %r2, 0x61
+; block2: ; offset 0xe
+;   br %r14
+
+function %cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2 cold:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+; block0:
+;   chi %r2, 0
+;   jglh label1 ; jg label2
+; block1:
+;   jg label3
+; block3:
+;   br %r14
+; block2:
+;   lhi %r2, 97
+;   jg label3
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   chi %r2, 0
+;   jgnlh 0xc
+; block1: ; offset 0xa
+;   br %r14
+; block2: ; offset 0xc
+;   lhi %r2, 0x61
+;   jg 0xa
+
diff --git a/cranelift/filetests/filetests/isa/x64/branches.clif b/cranelift/filetests/filetests/isa/x64/branches.clif
index e22f9cc56..01d9c3abd 100644
--- a/cranelift/filetests/filetests/isa/x64/branches.clif
+++ b/cranelift/filetests/filetests/isa/x64/branches.clif
@@ -314,8 +314,11 @@ block2:
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   cmpl    $2, %edi
-;   br_table %rdi, %r8, %r9
+;   movl    $2, %r9d
+;   movl    %edi, %r10d
+;   cmpl    %r9d, %r10d
+;   cmovbl  %r10d, %r9d, %r9d
+;   br_table %r9, %rax, %rcx
 ; block1:
 ;   jmp     label4
 ; block2:
@@ -336,25 +339,28 @@ block2:
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   cmpl $2, %edi
-;   jae 0x40
-;   movl %edi, %r9d
-;   movl $0, %r8d
-;   cmovaeq %r8, %r9
-;   leaq 0xb(%rip), %r8
-;   movslq (%r8, %r9, 4), %r9
-;   addq %r9, %r8
-;   jmpq *%r8
-;   adcb $0, %al
+;   movl $2, %r9d
+;   movl %edi, %r10d
+;   cmpl %r9d, %r10d
+;   cmovbl %r10d, %r9d
+;   leaq 0xa(%rip), %rax
+;   movslq (%rax, %r9, 4), %rcx
+;   addq %rcx, %rax
+;   jmpq *%rax
+;   sbbb %al, (%rax)
 ;   addb %al, (%rax)
-; block2: ; offset 0x34
-;   jmp 0x40
-; block3: ; offset 0x39
+;   adcl %eax, (%rax)
+;   addb %al, (%rax)
+;   sbbb %al, (%rax)
+;   addb %al, (%rax)
+; block2: ; offset 0x31
+;   jmp 0x3d
+; block3: ; offset 0x36
 ;   xorl %eax, %eax
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
-; block4: ; offset 0x40
+; block4: ; offset 0x3d
 ;   movl $1, %eax
 ;   movq %rbp, %rsp
 ;   popq %rbp
@@ -756,27 +762,30 @@ block5(v5: i32):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   cmpl    $4, %edi
-;   br_table %rdi, %rsi, %rax
+;   movl    $4, %eax
+;   movl    %edi, %ecx
+;   cmpl    %eax, %ecx
+;   cmovbl  %ecx, %eax, %eax
+;   br_table %rax, %r9, %r10
 ; block1:
 ;   jmp     label4
 ; block2:
 ;   jmp     label4
 ; block3:
-;   movl    $3, %r9d
+;   movl    $3, %esi
 ;   jmp     label7
 ; block4:
-;   movl    $2, %r9d
+;   movl    $2, %esi
 ;   jmp     label7
 ; block5:
-;   movl    $1, %r9d
+;   movl    $1, %esi
 ;   jmp     label7
 ; block6:
-;   movl    $4, %r9d
+;   movl    $4, %esi
 ;   jmp     label7
 ; block7:
 ;   movq    %rdi, %rax
-;   addl    %eax, %r9d, %eax
+;   addl    %eax, %esi, %eax
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -786,37 +795,38 @@ block5(v5: i32):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   cmpl $4, %edi
-;   jae 0x5f
-;   movl %edi, %eax
-;   movl $0, %esi
-;   cmovaeq %rsi, %rax
-;   leaq 0xa(%rip), %rsi
-;   movslq (%rsi, %rax, 4), %rax
-;   addq %rax, %rsi
-;   jmpq *%rsi
-;   subl (%rax), %eax
+;   movl $4, %eax
+;   movl %edi, %ecx
+;   cmpl %eax, %ecx
+;   cmovbl %ecx, %eax
+;   leaq 0xb(%rip), %r9
+;   movslq (%r9, %rax, 4), %r10
+;   addq %r10, %r9
+;   jmpq *%r9
+;   subl $0x23000000, %eax
 ;   addb %al, (%rax)
-;   andb %al, (%rax)
+;   addb %ah, (%rbx)
 ;   addb %al, (%rax)
-;   andb %al, (%rax)
+;   addb %bl, (%rcx)
 ;   addb %al, (%rax)
-; block2: ; offset 0x39
-;   jmp 0x49
-; block3: ; offset 0x3e
-;   movl $3, %r9d
-;   jmp 0x65
-; block4: ; offset 0x49
-;   movl $2, %r9d
-;   jmp 0x65
-; block5: ; offset 0x54
-;   movl $1, %r9d
-;   jmp 0x65
-; block6: ; offset 0x5f
-;   movl $4, %r9d
-; block7: ; offset 0x65
+;   addb %dh, (%rdi)
+;   addb %al, (%rax)
+; block2: ; offset 0x36
+;   jmp 0x45
+; block3: ; offset 0x3b
+;   movl $3, %esi
+;   jmp 0x5e
+; block4: ; offset 0x45
+;   movl $2, %esi
+;   jmp 0x5e
+; block5: ; offset 0x4f
+;   movl $1, %esi
+;   jmp 0x5e
+; block6: ; offset 0x59
+;   movl $4, %esi
+; block7: ; offset 0x5e
 ;   movq %rdi, %rax
-;   addl %r9d, %eax
+;   addl %esi, %eax
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -837,25 +847,28 @@ block1(v5: i32):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   movl    $1, %edx
-;   movl    $2, %r8d
-;   movl    $3, %r9d
+;   movl    $1, %r10d
+;   movl    $2, %r11d
+;   movl    $3, %esi
 ;   movl    $4, %eax
-;   cmpl    $4, %edi
-;   br_table %rdi, %r11, %r10
+;   movl    $4, %r8d
+;   movl    %edi, %r9d
+;   cmpl    %r8d, %r9d
+;   cmovbl  %r9d, %r8d, %r8d
+;   br_table %r8, %rdi, %rcx
 ; block1:
 ;   jmp     label6
 ; block2:
-;   movq    %rdx, %rax
+;   movq    %r10, %rax
 ;   jmp     label6
 ; block3:
-;   movq    %r8, %rax
+;   movq    %r11, %rax
 ;   jmp     label6
 ; block4:
-;   movq    %r8, %rax
+;   movq    %r11, %rax
 ;   jmp     label6
 ; block5:
-;   movq    %r9, %rax
+;   movq    %rsi, %rax
 ;   jmp     label6
 ; block6:
 ;   movq    %rbp, %rsp
@@ -867,37 +880,42 @@ block1(v5: i32):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movl $1, %edx
-;   movl $2, %r8d
-;   movl $3, %r9d
+;   movl $1, %r10d
+;   movl $2, %r11d
+;   movl $3, %esi
 ;   movl $4, %eax
-;   cmpl $4, %edi
-;   jae 0x72
-;   movl %edi, %r10d
-;   movl $0, %r11d
-;   cmovaeq %r11, %r10
-;   leaq 0xb(%rip), %r11
-;   movslq (%r11, %r10, 4), %r10
-;   addq %r10, %r11
-;   jmpq *%r11
-;   adcl $0x1d000000, %eax
+;   movl $4, %r8d
+;   movl %edi, %r9d
+;   cmpl %r8d, %r9d
+;   cmovbl %r9d, %r8d
+;   leaq 0xa(%rip), %rdi
+;   movslq (%rdi, %r8, 4), %rcx
+;   addq %rcx, %rdi
+;   jmpq *%rdi
+;   sbbl %eax, (%rax)
+;   addb %al, (%rax)
+;   andl %eax, (%rax)
+;   addb %al, (%rax)
+;   subl %eax, (%rax)
+;   addb %al, (%rax)
+;   xorl %eax, (%rax)
 ;   addb %al, (%rax)
-;   addb %ah, 0x2d000000(%rip)
+;   xorb $0, %al
 ;   addb %al, (%rax)
-; block2: ; offset 0x52
-;   jmp 0x72
-; block3: ; offset 0x57
-;   movq %rdx, %rax
-;   jmp 0x72
-; block4: ; offset 0x5f
-;   movq %r8, %rax
-;   jmp 0x72
-; block5: ; offset 0x67
-;   movq %r8, %rax
-;   jmp 0x72
-; block6: ; offset 0x6f
-;   movq %r9, %rax
-; block7: ; offset 0x72
+; block2: ; offset 0x4f
+;   jmp 0x6f
+; block3: ; offset 0x54
+;   movq %r10, %rax
+;   jmp 0x6f
+; block4: ; offset 0x5c
+;   movq %r11, %rax
+;   jmp 0x6f
+; block5: ; offset 0x64
+;   movq %r11, %rax
+;   jmp 0x6f
+; block6: ; offset 0x6c
+;   movq %rsi, %rax
+; block7: ; offset 0x6f
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/cold-blocks.clif b/cranelift/filetests/filetests/isa/x64/cold-blocks.clif
new file mode 100644
index 000000000..76a5f5374
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/cold-blocks.clif
@@ -0,0 +1,96 @@
+test compile precise-output
+
+target x86_64
+
+function %no_cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   testl   %edi, %edi
+;   jnz     label1; j label2
+; block1:
+;   movq    %rdi, %rax
+;   jmp     label3
+; block2:
+;   movl    $97, %eax
+;   jmp     label3
+; block3:
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   testl %edi, %edi
+;   je 0x14
+; block2: ; offset 0xc
+;   movq %rdi, %rax
+;   jmp 0x19
+; block3: ; offset 0x14
+;   movl $0x61, %eax
+; block4: ; offset 0x19
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %cold_annotation(i32) -> i32 {
+block0(v0: i32):
+    brif v0, block1(v0), block2
+
+block1(v1: i32):
+    return v1
+
+block2 cold:
+    v2 = iconst.i32 97
+    jump block1(v2)
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   testl   %edi, %edi
+;   jnz     label1; j label2
+; block1:
+;   movq    %rdi, %rax
+;   jmp     label3
+; block3:
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; block2:
+;   movl    $97, %eax
+;   jmp     label3
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   testl %edi, %edi
+;   je 0x14
+; block2: ; offset 0xc
+;   movq %rdi, %rax
+; block3: ; offset 0xf
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+; block4: ; offset 0x14
+;   movl $0x61, %eax
+;   jmp 0xf
+
diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs
index cdf4c5c9e..01cc0fabf 100644
--- a/tests/all/memory_creator.rs
+++ b/tests/all/memory_creator.rs
@@ -57,7 +57,7 @@ mod not_for_windows {
             Some(self.size - self.guard_size)
         }
 
-        fn grow_to(&mut self, new_size: usize) -> Result<(), anyhow::Error> {
+        fn grow_to(&mut self, new_size: usize) -> wasmtime::Result<()> {
             println!("grow to {:x}", new_size);
             let delta = new_size - self.used_wasm_bytes;
             unsafe {