Repository: bytecodealliance/wasmtime
Pull request #8171 (2024-03-18 20:22:04)

cranelift-wasm: Attach table OOB traps to loads/stores
Currently, every access to a table element does a bounds-check with a conditional branch to a block that explicitly traps.

Instead, when SPECTRE mitigations are enabled, let's change the address computation to return a null pointer for out-of-bounds accesses, and then allow the subsequent load or store to trap.

This is less code in that case since we can reuse instructions we needed anyway.

In addition, when the table has constant size and the element index is a constant and mid-end optimization is enabled, this allows the bounds-check to be constant folded away. Later, #8139 will let us optimize away the select_spectre_guard instruction in this case too.

Once we also implement #8160, `tests/disas/typed-funcrefs.wat` should be almost as fast as native indirect function calls.

jameysharp: Returning the memflags is an interesting option! Let me think about that a bit.
jameysharp: Yeah, returning `MemFlags` from `prepare_table_addr` tidied things up nicely. Thanks for the suggestion, Alex!

Patch:
diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs
index 78ee7faf4..eeecefc71 100644
--- a/cranelift/wasm/src/environ/dummy.rs
+++ b/cranelift/wasm/src/environ/dummy.rs
@@ -530,8 +530,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
         let pointer_type = self.pointer_type();
         self.ensure_table_exists(builder.func, table_index);
         let table = self.tables[table_index].as_ref().unwrap();
-        let table_entry_addr = table.prepare_table_addr(builder, index, pointer_type, true);
-        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
+        let (table_entry_addr, flags) =
+            table.prepare_table_addr(builder, index, pointer_type, true);
         let value = builder
             .ins()
             .load(self.reference_type(), flags, table_entry_addr, 0);
@@ -548,8 +548,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
         let pointer_type = self.pointer_type();
         self.ensure_table_exists(builder.func, table_index);
         let table = self.tables[table_index].as_ref().unwrap();
-        let table_entry_addr = table.prepare_table_addr(builder, index, pointer_type, true);
-        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
+        let (table_entry_addr, flags) =
+            table.prepare_table_addr(builder, index, pointer_type, true);
         builder.ins().store(flags, value, table_entry_addr, 0);
         Ok(())
     }
diff --git a/cranelift/wasm/src/table.rs b/cranelift/wasm/src/table.rs
index ed00221a9..bb5466670 100644
--- a/cranelift/wasm/src/table.rs
+++ b/cranelift/wasm/src/table.rs
@@ -50,7 +50,7 @@ impl TableData {
         mut index: ir::Value,
         addr_ty: ir::Type,
         enable_table_access_spectre_mitigation: bool,
-    ) -> ir::Value {
+    ) -> (ir::Value, ir::MemFlags) {
         let index_ty = pos.func.dfg.value_type(index);
 
         // Start with the bounds check. Trap if `index + 1 > bound`.
@@ -60,16 +60,10 @@ impl TableData {
         let oob = pos
             .ins()
             .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
-        pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds);
 
-        // If Spectre mitigations are enabled, we will use a comparison to
-        // short-circuit the computed table element address to the start
-        // of the table on the misspeculation path when out-of-bounds.
-        let spectre_oob_cmp = if enable_table_access_spectre_mitigation {
-            Some((index, bound))
-        } else {
-            None
-        };
+        if !enable_table_access_spectre_mitigation {
+            pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds);
+        }
 
         // Convert `index` to `addr_ty`.
         if index_ty != addr_ty {
@@ -91,14 +85,20 @@ impl TableData {
 
         let element_addr = pos.ins().iadd(base, offset);
 
-        if let Some((index, bound)) = spectre_oob_cmp {
-            let cond = pos
-                .ins()
-                .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
-            // If out-of-bounds, choose the table base on the misspeculation path.
-            pos.ins().select_spectre_guard(cond, base, element_addr)
+        let base_flags = ir::MemFlags::new()
+            .with_aligned()
+            .with_alias_region(Some(ir::AliasRegion::Table));
+        if enable_table_access_spectre_mitigation {
+            // Short-circuit the computed table element address to a null pointer
+            // when out-of-bounds. The consumer of this address will trap when
+            // trying to access it.
+            let zero = pos.ins().iconst(addr_ty, 0);
+            (
+                pos.ins().select_spectre_guard(oob, zero, element_addr),
+                base_flags.with_trap_code(Some(ir::TrapCode::TableOutOfBounds)),
+            )
         } else {
-            element_addr
+            (element_addr, base_flags.with_trap_code(None))
         }
     }
 }
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index d46b70cd6..6104a9411 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -955,13 +955,12 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
         // contents, we check for a null entry here, and
         // if null, we take a slow-path that invokes a
         // libcall.
-        let table_entry_addr = table_data.prepare_table_addr(
+        let (table_entry_addr, flags) = table_data.prepare_table_addr(
             builder,
             index,
             pointer_type,
             self.isa.flags().enable_table_access_spectre_mitigation(),
         );
-        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
         let value = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
         // Mask off the "initialized bit". See documentation on
         // FUNCREF_INIT_BIT in crates/environ/src/ref_bits.rs for more
@@ -1548,13 +1547,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                 // Load the table element.
                 self.ensure_table_exists(builder.func, table_index);
                 let table_data = self.tables[table_index].as_ref().unwrap();
-                let elem_addr = table_data.prepare_table_addr(
+                let (elem_addr, flags) = table_data.prepare_table_addr(
                     builder,
                     index,
                     pointer_type,
                     self.isa.flags().enable_table_access_spectre_mitigation(),
                 );
-                let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                 let elem = builder.ins().load(reference_type, flags, elem_addr, 0);
 
                 let elem_is_null = builder.ins().is_null(elem);
@@ -1659,7 +1657,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
                 match plan.style {
                     TableStyle::CallerChecksSignature => {
-                        let table_entry_addr = table_data.prepare_table_addr(
+                        let (table_entry_addr, flags) = table_data.prepare_table_addr(
                             builder,
                             index,
                             pointer_type,
@@ -1671,8 +1669,6 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                         let value_with_init_bit = builder
                             .ins()
                             .bor_imm(value, Imm64::from(FUNCREF_INIT_BIT as i64));
-                        let flags =
-                            ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                         builder
                             .ins()
                             .store(flags, value_with_init_bit, table_entry_addr, 0);
@@ -1736,14 +1732,16 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                 // not recorded in the stack map (which would lead to the GC
                 // saving a reference to a deallocated object, and then using it
                 // after its been freed).
-                let table_entry_addr = table_data.prepare_table_addr(
+                let (table_entry_addr, flags) = table_data.prepare_table_addr(
                     builder,
                     index,
                     pointer_type,
                     self.isa.flags().enable_table_access_spectre_mitigation(),
                 );
-                let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                 let current_elem = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
+
+                // After the load, a store to the same address can't trap.
+                let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                 builder.ins().store(flags, value, table_entry_addr, 0);
 
                 // If value is not null, increment `value`'s ref count.


Test patch:
diff --git a/tests/disas/icall-simd.wat b/tests/disas/icall-simd.wat
index e6f33895d..12253e10c 100644
--- a/tests/disas/icall-simd.wat
+++ b/tests/disas/icall-simd.wat
@@ -23,14 +23,13 @@
 ;;                                 block0(v0: i64, v1: i64, v2: i32, v3: i8x16):
 ;; @0033                               v5 = iconst.i32 23
 ;; @0033                               v6 = icmp uge v2, v5  ; v5 = 23
-;; @0033                               trapnz v6, table_oob
 ;; @0033                               v7 = uextend.i64 v2
 ;; @0033                               v8 = global_value.i64 gv4
 ;; @0033                               v9 = ishl_imm v7, 3
 ;; @0033                               v10 = iadd v8, v9
-;; @0033                               v11 = icmp uge v2, v5  ; v5 = 23
-;; @0033                               v12 = select_spectre_guard v11, v8, v10
-;; @0033                               v13 = load.i64 notrap aligned table v12
+;; @0033                               v11 = iconst.i64 0
+;; @0033                               v12 = select_spectre_guard v6, v11, v10  ; v11 = 0
+;; @0033                               v13 = load.i64 table_oob aligned table v12
 ;; @0033                               v14 = band_imm v13, -2
 ;; @0033                               brif v13, block3(v14), block2
 ;;
diff --git a/tests/disas/icall.wat b/tests/disas/icall.wat
index ec6f668c1..e810b78ee 100644
--- a/tests/disas/icall.wat
+++ b/tests/disas/icall.wat
@@ -23,14 +23,13 @@
 ;;                                 block0(v0: i64, v1: i64, v2: i32, v3: f32):
 ;; @0033                               v5 = iconst.i32 23
 ;; @0033                               v6 = icmp uge v2, v5  ; v5 = 23
-;; @0033                               trapnz v6, table_oob
 ;; @0033                               v7 = uextend.i64 v2
 ;; @0033                               v8 = global_value.i64 gv4
 ;; @0033                               v9 = ishl_imm v7, 3
 ;; @0033                               v10 = iadd v8, v9
-;; @0033                               v11 = icmp uge v2, v5  ; v5 = 23
-;; @0033                               v12 = select_spectre_guard v11, v8, v10
-;; @0033                               v13 = load.i64 notrap aligned table v12
+;; @0033                               v11 = iconst.i64 0
+;; @0033                               v12 = select_spectre_guard v6, v11, v10  ; v11 = 0
+;; @0033                               v13 = load.i64 table_oob aligned table v12
 ;; @0033                               v14 = band_imm v13, -2
 ;; @0033                               brif v13, block3(v14), block2
 ;;
diff --git a/tests/disas/table-get-fixed-size.wat b/tests/disas/table-get-fixed-size.wat
index b0e7dd4c2..fbdaf794d 100644
--- a/tests/disas/table-get-fixed-size.wat
+++ b/tests/disas/table-get-fixed-size.wat
@@ -33,20 +33,14 @@
 ;; @0052                               v3 = iconst.i32 0
 ;; @0054                               v4 = iconst.i32 7
 ;; @0054                               v5 = icmp uge v3, v4  ; v3 = 0, v4 = 7
-;; @0054                               brif v5, block6, block7
-;;
-;;                                 block6 cold:
-;; @0054                               trap table_oob
-;;
-;;                                 block7:
 ;; @0054                               v6 = uextend.i64 v3  ; v3 = 0
 ;; @0054                               v7 = load.i64 notrap aligned v25+72
 ;;                                     v26 = iconst.i64 3
 ;; @0054                               v8 = ishl v6, v26  ; v26 = 3
 ;; @0054                               v9 = iadd v7, v8
-;; @0054                               v10 = icmp.i32 uge v3, v4  ; v3 = 0, v4 = 7
-;; @0054                               v11 = select_spectre_guard v10, v7, v9
-;; @0054                               v12 = load.r64 notrap aligned table v11
+;; @0054                               v10 = iconst.i64 0
+;; @0054                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @0054                               v12 = load.r64 table_oob aligned table v11
 ;;                                     v2 -> v12
 ;; @0054                               v13 = is_null v12
 ;; @0054                               brif v13, block2, block3
@@ -99,20 +93,14 @@
 ;;                                     v25 -> v0
 ;; @005b                               v4 = iconst.i32 7
 ;; @005b                               v5 = icmp uge v2, v4  ; v4 = 7
-;; @005b                               brif v5, block6, block7
-;;
-;;                                 block6 cold:
-;; @005b                               trap table_oob
-;;
-;;                                 block7:
 ;; @005b                               v6 = uextend.i64 v2
 ;; @005b                               v7 = load.i64 notrap aligned v25+72
 ;;                                     v26 = iconst.i64 3
 ;; @005b                               v8 = ishl v6, v26  ; v26 = 3
 ;; @005b                               v9 = iadd v7, v8
-;; @005b                               v10 = icmp.i32 uge v2, v4  ; v4 = 7
-;; @005b                               v11 = select_spectre_guard v10, v7, v9
-;; @005b                               v12 = load.r64 notrap aligned table v11
+;; @005b                               v10 = iconst.i64 0
+;; @005b                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @005b                               v12 = load.r64 table_oob aligned table v11
 ;;                                     v3 -> v12
 ;; @005b                               v13 = is_null v12
 ;; @005b                               brif v13, block2, block3
diff --git a/tests/disas/table-get.wat b/tests/disas/table-get.wat
index 992d62451..0cbee62e2 100644
--- a/tests/disas/table-get.wat
+++ b/tests/disas/table-get.wat
@@ -34,20 +34,14 @@
 ;; @0051                               v3 = iconst.i32 0
 ;; @0053                               v4 = load.i32 notrap aligned v25+80
 ;; @0053                               v5 = icmp uge v3, v4  ; v3 = 0
-;; @0053                               brif v5, block6, block7
-;;
-;;                                 block6 cold:
-;; @0053                               trap table_oob
-;;
-;;                                 block7:
 ;; @0053                               v6 = uextend.i64 v3  ; v3 = 0
 ;; @0053                               v7 = load.i64 notrap aligned v26+72
 ;;                                     v27 = iconst.i64 3
 ;; @0053                               v8 = ishl v6, v27  ; v27 = 3
 ;; @0053                               v9 = iadd v7, v8
-;; @0053                               v10 = icmp.i32 uge v3, v4  ; v3 = 0
-;; @0053                               v11 = select_spectre_guard v10, v7, v9
-;; @0053                               v12 = load.r64 notrap aligned table v11
+;; @0053                               v10 = iconst.i64 0
+;; @0053                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @0053                               v12 = load.r64 table_oob aligned table v11
 ;;                                     v2 -> v12
 ;; @0053                               v13 = is_null v12
 ;; @0053                               brif v13, block2, block3
@@ -102,20 +96,14 @@
 ;;                                     v26 -> v0
 ;; @005a                               v4 = load.i32 notrap aligned v25+80
 ;; @005a                               v5 = icmp uge v2, v4
-;; @005a                               brif v5, block6, block7
-;;
-;;                                 block6 cold:
-;; @005a                               trap table_oob
-;;
-;;                                 block7:
 ;; @005a                               v6 = uextend.i64 v2
 ;; @005a                               v7 = load.i64 notrap aligned v26+72
 ;;                                     v27 = iconst.i64 3
 ;; @005a                               v8 = ishl v6, v27  ; v27 = 3
 ;; @005a                               v9 = iadd v7, v8
-;; @005a                               v10 = icmp.i32 uge v2, v4
-;; @005a                               v11 = select_spectre_guard v10, v7, v9
-;; @005a                               v12 = load.r64 notrap aligned table v11
+;; @005a                               v10 = iconst.i64 0
+;; @005a                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @005a                               v12 = load.r64 table_oob aligned table v11
 ;;                                     v3 -> v12
 ;; @005a                               v13 = is_null v12
 ;; @005a                               brif v13, block2, block3
diff --git a/tests/disas/table-set-fixed-size.wat b/tests/disas/table-set-fixed-size.wat
index ceb2da584..f501bf1b7 100644
--- a/tests/disas/table-set-fixed-size.wat
+++ b/tests/disas/table-set-fixed-size.wat
@@ -34,22 +34,16 @@
 ;; @0052                               v3 = iconst.i32 0
 ;; @0056                               v4 = iconst.i32 7
 ;; @0056                               v5 = icmp uge v3, v4  ; v3 = 0, v4 = 7
-;; @0056                               brif v5, block7, block8
-;;
-;;                                 block7 cold:
-;; @0056                               trap table_oob
-;;
-;;                                 block8:
 ;; @0056                               v6 = uextend.i64 v3  ; v3 = 0
 ;; @0056                               v7 = load.i64 notrap aligned v23+72
 ;;                                     v24 = iconst.i64 3
 ;; @0056                               v8 = ishl v6, v24  ; v24 = 3
 ;; @0056                               v9 = iadd v7, v8
-;; @0056                               v10 = icmp.i32 uge v3, v4  ; v3 = 0, v4 = 7
-;; @0056                               v11 = select_spectre_guard v10, v7, v9
-;; @0056                               v12 = load.i64 notrap aligned table v11
-;; @0056                               store.r64 notrap aligned table v2, v11
-;; @0056                               v13 = is_null.r64 v2
+;; @0056                               v10 = iconst.i64 0
+;; @0056                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @0056                               v12 = load.i64 table_oob aligned table v11
+;; @0056                               store notrap aligned table v2, v11
+;; @0056                               v13 = is_null v2
 ;; @0056                               brif v13, block3, block2
 ;;
 ;;                                 block2:
@@ -102,22 +96,16 @@
 ;;                                     v23 -> v0
 ;; @005f                               v4 = iconst.i32 7
 ;; @005f                               v5 = icmp uge v2, v4  ; v4 = 7
-;; @005f                               brif v5, block7, block8
-;;
-;;                                 block7 cold:
-;; @005f                               trap table_oob
-;;
-;;                                 block8:
 ;; @005f                               v6 = uextend.i64 v2
 ;; @005f                               v7 = load.i64 notrap aligned v23+72
 ;;                                     v24 = iconst.i64 3
 ;; @005f                               v8 = ishl v6, v24  ; v24 = 3
 ;; @005f                               v9 = iadd v7, v8
-;; @005f                               v10 = icmp.i32 uge v2, v4  ; v4 = 7
-;; @005f                               v11 = select_spectre_guard v10, v7, v9
-;; @005f                               v12 = load.i64 notrap aligned table v11
-;; @005f                               store.r64 notrap aligned table v3, v11
-;; @005f                               v13 = is_null.r64 v3
+;; @005f                               v10 = iconst.i64 0
+;; @005f                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @005f                               v12 = load.i64 table_oob aligned table v11
+;; @005f                               store notrap aligned table v3, v11
+;; @005f                               v13 = is_null v3
 ;; @005f                               brif v13, block3, block2
 ;;
 ;;                                 block2:
diff --git a/tests/disas/table-set.wat b/tests/disas/table-set.wat
index 93b71d52a..f792fb23a 100644
--- a/tests/disas/table-set.wat
+++ b/tests/disas/table-set.wat
@@ -35,22 +35,16 @@
 ;; @0051                               v3 = iconst.i32 0
 ;; @0055                               v4 = load.i32 notrap aligned v23+80
 ;; @0055                               v5 = icmp uge v3, v4  ; v3 = 0
-;; @0055                               brif v5, block7, block8
-;;
-;;                                 block7 cold:
-;; @0055                               trap table_oob
-;;
-;;                                 block8:
 ;; @0055                               v6 = uextend.i64 v3  ; v3 = 0
 ;; @0055                               v7 = load.i64 notrap aligned v24+72
 ;;                                     v25 = iconst.i64 3
 ;; @0055                               v8 = ishl v6, v25  ; v25 = 3
 ;; @0055                               v9 = iadd v7, v8
-;; @0055                               v10 = icmp.i32 uge v3, v4  ; v3 = 0
-;; @0055                               v11 = select_spectre_guard v10, v7, v9
-;; @0055                               v12 = load.i64 notrap aligned table v11
-;; @0055                               store.r64 notrap aligned table v2, v11
-;; @0055                               v13 = is_null.r64 v2
+;; @0055                               v10 = iconst.i64 0
+;; @0055                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @0055                               v12 = load.i64 table_oob aligned table v11
+;; @0055                               store notrap aligned table v2, v11
+;; @0055                               v13 = is_null v2
 ;; @0055                               brif v13, block3, block2
 ;;
 ;;                                 block2:
@@ -105,22 +99,16 @@
 ;;                                     v24 -> v0
 ;; @005e                               v4 = load.i32 notrap aligned v23+80
 ;; @005e                               v5 = icmp uge v2, v4
-;; @005e                               brif v5, block7, block8
-;;
-;;                                 block7 cold:
-;; @005e                               trap table_oob
-;;
-;;                                 block8:
 ;; @005e                               v6 = uextend.i64 v2
 ;; @005e                               v7 = load.i64 notrap aligned v24+72
 ;;                                     v25 = iconst.i64 3
 ;; @005e                               v8 = ishl v6, v25  ; v25 = 3
 ;; @005e                               v9 = iadd v7, v8
-;; @005e                               v10 = icmp.i32 uge v2, v4
-;; @005e                               v11 = select_spectre_guard v10, v7, v9
-;; @005e                               v12 = load.i64 notrap aligned table v11
-;; @005e                               store.r64 notrap aligned table v3, v11
-;; @005e                               v13 = is_null.r64 v3
+;; @005e                               v10 = iconst.i64 0
+;; @005e                               v11 = select_spectre_guard v5, v10, v9  ; v10 = 0
+;; @005e                               v12 = load.i64 table_oob aligned table v11
+;; @005e                               store notrap aligned table v3, v11
+;; @005e                               v13 = is_null v3
 ;; @005e                               brif v13, block3, block2
 ;;
 ;;                                 block2:
diff --git a/tests/disas/typed-funcrefs.wat b/tests/disas/typed-funcrefs.wat
index 07f52e6aa..10ba6a955 100644
--- a/tests/disas/typed-funcrefs.wat
+++ b/tests/disas/typed-funcrefs.wat
@@ -112,6 +112,7 @@
 
         local.get $sum)
 )
+
 ;; function u0:0(i64 vmctx, i64, i32, i32, i32, i32) -> i32 fast {
 ;;     gv0 = vmctx
 ;;     gv1 = load.i64 notrap aligned readonly gv0+8
@@ -149,19 +150,13 @@
 ;;                                     v31 -> v3
 ;;                                     v32 -> v4
 ;;                                     v33 -> v5
-;;                                     v62 = iconst.i8 0
-;; @0048                               brif v62, block6, block7  ; v62 = 0
-;;
-;;                                 block6 cold:
-;; @0048                               trap table_oob
-;;
-;;                                 block7:
 ;; @0048                               v12 = load.i64 notrap aligned v0+72
-;;                                     v79 = iconst.i8 0
+;;                                     v62 = iconst.i8 0
+;; @0048                               v15 = iconst.i64 0
 ;;                                     v70 = iconst.i64 8
 ;; @0048                               v14 = iadd v12, v70  ; v70 = 8
-;; @0048                               v16 = select_spectre_guard v79, v12, v14  ; v79 = 0
-;; @0048                               v17 = load.i64 notrap aligned table v16
+;; @0048                               v16 = select_spectre_guard v62, v15, v14  ; v62 = 0, v15 = 0
+;; @0048                               v17 = load.i64 table_oob aligned table v16
 ;;                                     v58 = iconst.i64 -2
 ;; @0048                               v18 = band v17, v58  ; v58 = -2
 ;; @0048                               brif v17, block3(v18), block2
@@ -179,29 +174,23 @@
 ;; @004a                               v25 = load.i64 null_reference aligned readonly v19+16
 ;; @004a                               v26 = load.i64 notrap aligned readonly v19+32
 ;; @004a                               v27 = call_indirect sig1, v25(v26, v0, v2, v3, v4, v5)
-;;                                     v80 = iconst.i8 0
-;; @005b                               brif v80, block8, block9  ; v80 = 0
-;;
-;;                                 block8 cold:
-;; @005b                               trap table_oob
-;;
-;;                                 block9:
 ;; @005b                               v38 = load.i64 notrap aligned v0+72
-;;                                     v81 = iconst.i8 0
+;;                                     v79 = iconst.i8 0
+;;                                     v80 = iconst.i64 0
 ;;                                     v78 = iconst.i64 16
 ;; @005b                               v40 = iadd v38, v78  ; v78 = 16
-;; @005b                               v42 = select_spectre_guard v81, v38, v40  ; v81 = 0
-;; @005b                               v43 = load.i64 notrap aligned table v42
-;;                                     v82 = iconst.i64 -2
-;;                                     v83 = band v43, v82  ; v82 = -2
-;; @005b                               brif v43, block5(v83), block4
+;; @005b                               v42 = select_spectre_guard v79, v80, v40  ; v79 = 0, v80 = 0
+;; @005b                               v43 = load.i64 table_oob aligned table v42
+;;                                     v81 = iconst.i64 -2
+;;                                     v82 = band v43, v81  ; v81 = -2
+;; @005b                               brif v43, block5(v82), block4
 ;;
 ;;                                 block4 cold:
-;;                                     v84 = load.i64 notrap aligned readonly v0+56
-;;                                     v85 = load.i64 notrap aligned readonly v84+72
-;;                                     v86 = iconst.i32 0
+;;                                     v83 = load.i64 notrap aligned readonly v0+56
+;;                                     v84 = load.i64 notrap aligned readonly v83+72
+;;                                     v85 = iconst.i32 0
 ;; @0059                               v34 = iconst.i32 2
-;; @005b                               v50 = call_indirect sig0, v85(v0, v86, v34)  ; v86 = 0, v34 = 2
+;; @005b                               v50 = call_indirect sig0, v84(v0, v85, v34)  ; v85 = 0, v34 = 2
 ;; @005b                               jump block5(v50)
 ;;
 ;;                                 block5(v45: i64):
@@ -237,19 +226,13 @@
 ;;                                     v31 -> v3
 ;;                                     v32 -> v4
 ;;                                     v33 -> v5
-;;                                     v62 = iconst.i8 0
-;; @0075                               brif v62, block6, block7  ; v62 = 0
-;;
-;;                                 block6 cold:
-;; @0075                               trap table_oob
-;;
-;;                                 block7:
 ;; @0075                               v12 = load.i64 notrap aligned v0+72
-;;                                     v79 = iconst.i8 0
+;;                                     v62 = iconst.i8 0
+;; @0075                               v15 = iconst.i64 0
 ;;                                     v70 = iconst.i64 8
 ;; @0075                               v14 = iadd v12, v70  ; v70 = 8
-;; @0075                               v16 = select_spectre_guard v79, v12, v14  ; v79 = 0
-;; @0075                               v17 = load.i64 notrap aligned table v16
+;; @0075                               v16 = select_spectre_guard v62, v15, v14  ; v62 = 0, v15 = 0
+;; @0075                               v17 = load.i64 table_oob aligned table v16
 ;;                                     v58 = iconst.i64 -2
 ;; @0075                               v18 = band v17, v58  ; v58 = -2
 ;; @0075                               brif v17, block3(v18), block2
@@ -267,29 +250,23 @@
 ;; @0075                               v25 = load.i64 icall_null aligned readonly v19+16
 ;; @0075                               v26 = load.i64 notrap aligned readonly v19+32
 ;; @0075                               v27 = call_indirect sig0, v25(v26, v0, v2, v3, v4, v5)
-;;                                     v80 = iconst.i8 0
-;; @0087                               brif v80, block8, block9  ; v80 = 0
-;;
-;;                                 block8 cold:
-;; @0087                               trap table_oob
-;;
-;;                                 block9:
 ;; @0087                               v38 = load.i64 notrap aligned v0+72
-;;                                     v81 = iconst.i8 0
+;;                                     v79 = iconst.i8 0
+;;                                     v80 = iconst.i64 0
 ;;                                     v78 = iconst.i64 16
 ;; @0087                               v40 = iadd v38, v78  ; v78 = 16
-;; @0087                               v42 = select_spectre_guard v81, v38, v40  ; v81 = 0
-;; @0087                               v43 = load.i64 notrap aligned table v42
-;;                                     v82 = iconst.i64 -2
-;;                                     v83 = band v43, v82  ; v82 = -2
-;; @0087                               brif v43, block5(v83), block4
+;; @0087                               v42 = select_spectre_guard v79, v80, v40  ; v79 = 0, v80 = 0
+;; @0087                               v43 = load.i64 table_oob aligned table v42
+;;                                     v81 = iconst.i64 -2
+;;                                     v82 = band v43, v81  ; v81 = -2
+;; @0087                               brif v43, block5(v82), block4
 ;;
 ;;                                 block4 cold:
-;;                                     v84 = load.i64 notrap aligned readonly v0+56
-;;                                     v85 = load.i64 notrap aligned readonly v84+72
-;;                                     v86 = iconst.i32 0
+;;                                     v83 = load.i64 notrap aligned readonly v0+56
+;;                                     v84 = load.i64 notrap aligned readonly v83+72
+;;                                     v85 = iconst.i32 0
 ;; @0085                               v34 = iconst.i32 2
-;; @0087                               v50 = call_indirect sig1, v85(v0, v86, v34)  ; v86 = 0, v34 = 2
+;; @0087                               v50 = call_indirect sig1, v84(v0, v85, v34)  ; v85 = 0, v34 = 2
 ;; @0087                               jump block5(v50)
 ;;
 ;;                                 block5(v45: i64):