Repository: bytecodealliance/wasmtime
Pull request #9244 (2024-09-13 17:21:23)

Use inline GC barriers for accessing Wasm globals
Previously, we would call out to the runtime via libcalls to get and set Wasm globals of types that are managed by the GC. This change introduces inline barriers for these operations, which should improve performance, and removes unnecessary libcall implementations.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index eb7dc5e51..e1b9b0466 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -338,7 +338,6 @@ jobs:
   # here isn't to run tests, mostly just serve as a double-check that Rust code
   # compiles and is likely to work everywhere else.
   micro_checks:
-    needs: determine
     name: Check ${{matrix.name}}
     strategy:
       fail-fast: true
@@ -421,7 +420,6 @@ jobs:
 
   # Checks for no_std support, ensure that crates can build on a no_std target
   no_std_checks:
-    needs: determine
     name: no_std checks
     runs-on: ubuntu-latest
     env:
@@ -445,7 +443,6 @@ jobs:
 
   # Check that Clippy lints are passing.
   clippy:
-    needs: determine
     name: Clippy
     runs-on: ubuntu-latest
     env:
@@ -469,7 +466,6 @@ jobs:
   # (e.g. Android NDK) and we haven't factored support for those things out into
   # a parallel jobs yet.
   monolith_checks:
-    needs: determine
     name: Monolith Checks
     runs-on: ubuntu-latest
     env:
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index ceb8eddbb..180852eaf 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -1912,21 +1912,25 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             ty.is_vmgcref_type(),
             "We only use GlobalVariable::Custom for VMGcRef types"
         );
+        let cranelift_wasm::WasmValType::Ref(ty) = ty else {
+            unreachable!()
+        };
 
-        // TODO: use `GcCompiler::translate_read_gc_reference` for GC-reference
-        // globals instead of a libcall.
-        let libcall = gc::gc_ref_global_get_builtin(ty, self, &mut builder.func)?;
-
-        let vmctx = self.vmctx_val(&mut builder.cursor());
-
-        let global_index_arg = builder.ins().iconst(I32, index.as_u32() as i64);
-        let call_inst = builder.ins().call(libcall, &[vmctx, global_index_arg]);
+        let (gv, offset) = self.get_global_location(builder.func, index);
+        let gv = builder.ins().global_value(self.pointer_type(), gv);
+        let src = builder.ins().iadd_imm(gv, i64::from(offset));
 
-        let val = builder.func.dfg.first_result(call_inst);
-        if ty.is_vmgcref_type_and_not_i31() {
-            builder.declare_value_needs_stack_map(val);
+        if let WasmHeapType::I31 = ty.heap_type {
+            gc::unbarriered_load_gc_ref(self, builder, ty.heap_type, src, ir::MemFlags::trusted())
+        } else {
+            gc::gc_compiler(self).translate_read_gc_reference(
+                self,
+                builder,
+                ty,
+                src,
+                ir::MemFlags::trusted(),
+            )
         }
-        Ok(val)
     }
 
     fn translate_custom_global_set(
@@ -1940,19 +1944,33 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             ty.is_vmgcref_type(),
             "We only use GlobalVariable::Custom for VMGcRef types"
         );
+        let cranelift_wasm::WasmValType::Ref(ty) = ty else {
+            unreachable!()
+        };
 
-        // TODO: use `GcCompiler::translate_write_gc_reference` for GC-reference
-        // globals instead of a libcall.
-        let libcall = gc::gc_ref_global_set_builtin(ty, self, &mut builder.func)?;
-
-        let vmctx = self.vmctx_val(&mut builder.cursor());
-
-        let global_index_arg = builder.ins().iconst(I32, index.as_u32() as i64);
-        builder
-            .ins()
-            .call(libcall, &[vmctx, global_index_arg, value]);
+        let (gv, offset) = self.get_global_location(builder.func, index);
+        let gv = builder.ins().global_value(self.pointer_type(), gv);
+        let src = builder.ins().iadd_imm(gv, i64::from(offset));
 
-        Ok(())
+        if let WasmHeapType::I31 = ty.heap_type {
+            gc::unbarriered_store_gc_ref(
+                self,
+                builder,
+                ty.heap_type,
+                src,
+                value,
+                ir::MemFlags::trusted(),
+            )
+        } else {
+            gc::gc_compiler(self).translate_write_gc_reference(
+                self,
+                builder,
+                ty,
+                src,
+                value,
+                ir::MemFlags::trusted(),
+            )
+        }
     }
 
     fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<Heap> {
diff --git a/crates/cranelift/src/gc.rs b/crates/cranelift/src/gc.rs
index 0583dfee0..8e5f5db82 100644
--- a/crates/cranelift/src/gc.rs
+++ b/crates/cranelift/src/gc.rs
@@ -8,7 +8,7 @@
 use crate::func_environ::FuncEnvironment;
 use cranelift_codegen::ir;
 use cranelift_frontend::FunctionBuilder;
-use cranelift_wasm::{WasmHeapType, WasmRefType, WasmResult, WasmValType};
+use cranelift_wasm::{WasmHeapType, WasmRefType, WasmResult};
 
 #[cfg(feature = "gc")]
 mod enabled;
@@ -80,28 +80,6 @@ pub fn gc_ref_table_fill_builtin(
     imp::gc_ref_table_fill_builtin(ty, func_env, func)
 }
 
-/// Get the index and signature of the built-in function for doing `global.get`
-/// on a GC reference global.
-pub fn gc_ref_global_get_builtin(
-    ty: WasmValType,
-    func_env: &mut FuncEnvironment<'_>,
-    func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    debug_assert!(ty.is_vmgcref_type());
-    imp::gc_ref_global_get_builtin(ty, func_env, func)
-}
-
-/// Get the index and signature of the built-in function for doing `global.set`
-/// on a GC reference global.
-pub fn gc_ref_global_set_builtin(
-    ty: WasmValType,
-    func_env: &mut FuncEnvironment<'_>,
-    func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    debug_assert!(ty.is_vmgcref_type());
-    imp::gc_ref_global_set_builtin(ty, func_env, func)
-}
-
 /// A trait for different collectors to emit any GC barriers they might require.
 pub trait GcCompiler {
     /// Emit a read barrier for when we are cloning a GC reference onto the Wasm
diff --git a/crates/cranelift/src/gc/disabled.rs b/crates/cranelift/src/gc/disabled.rs
index 308806299..7822439d7 100644
--- a/crates/cranelift/src/gc/disabled.rs
+++ b/crates/cranelift/src/gc/disabled.rs
@@ -4,7 +4,7 @@ use super::GcCompiler;
 use crate::func_environ::FuncEnvironment;
 use cranelift_codegen::ir;
 use cranelift_frontend::FunctionBuilder;
-use cranelift_wasm::{wasm_unsupported, WasmHeapType, WasmRefType, WasmResult, WasmValType};
+use cranelift_wasm::{wasm_unsupported, WasmHeapType, WasmRefType, WasmResult};
 
 /// Get the default GC compiler.
 pub fn gc_compiler(_: &FuncEnvironment<'_>) -> Box<dyn GcCompiler> {
@@ -60,28 +60,6 @@ pub fn gc_ref_table_fill_builtin(
     ))
 }
 
-pub fn gc_ref_global_get_builtin(
-    ty: WasmValType,
-    _func_env: &mut FuncEnvironment<'_>,
-    _func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    Err(wasm_unsupported!(
-        "support for `{ty}` disabled at compile time because the `gc` cargo \
-         feature was not enabled"
-    ))
-}
-
-pub fn gc_ref_global_set_builtin(
-    ty: WasmValType,
-    _func_env: &mut FuncEnvironment<'_>,
-    _func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    Err(wasm_unsupported!(
-        "support for `{ty}` disabled at compile time because the `gc` cargo \
-         feature was not enabled"
-    ))
-}
-
 struct DisabledGcCompiler;
 
 impl GcCompiler for DisabledGcCompiler {
diff --git a/crates/cranelift/src/gc/enabled.rs b/crates/cranelift/src/gc/enabled.rs
index f91a2492c..f9c8deae9 100644
--- a/crates/cranelift/src/gc/enabled.rs
+++ b/crates/cranelift/src/gc/enabled.rs
@@ -2,9 +2,7 @@ use super::GcCompiler;
 use crate::func_environ::FuncEnvironment;
 use cranelift_codegen::ir::{self, condcodes::IntCC, InstBuilder};
 use cranelift_frontend::FunctionBuilder;
-use cranelift_wasm::{
-    TargetEnvironment, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult, WasmValType,
-};
+use cranelift_wasm::{TargetEnvironment, WasmHeapTopType, WasmHeapType, WasmRefType, WasmResult};
 use wasmtime_environ::{PtrSize, I31_DISCRIMINANT, NON_NULL_NON_I31_MASK};
 
 /// Get the default GC compiler.
@@ -62,24 +60,6 @@ pub fn gc_ref_table_fill_builtin(
     Ok(func_env.builtin_functions.table_fill_gc_ref(func))
 }
 
-pub fn gc_ref_global_get_builtin(
-    ty: WasmValType,
-    func_env: &mut FuncEnvironment<'_>,
-    func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    debug_assert!(ty.is_vmgcref_type());
-    Ok(func_env.builtin_functions.gc_ref_global_get(func))
-}
-
-pub fn gc_ref_global_set_builtin(
-    ty: WasmValType,
-    func_env: &mut FuncEnvironment<'_>,
-    func: &mut ir::Function,
-) -> WasmResult<ir::FuncRef> {
-    debug_assert!(ty.is_vmgcref_type());
-    Ok(func_env.builtin_functions.gc_ref_global_set(func))
-}
-
 impl FuncEnvironment<'_> {
     /// Get the GC heap's base pointer and bound.
     fn get_gc_heap_base_bound(&mut self, builder: &mut FunctionBuilder) -> (ir::Value, ir::Value) {
diff --git a/crates/environ/src/builtin.rs b/crates/environ/src/builtin.rs
index f42b0b5dc..03a9e776c 100644
--- a/crates/environ/src/builtin.rs
+++ b/crates/environ/src/builtin.rs
@@ -79,16 +79,6 @@ macro_rules! foreach_builtin_function {
             #[cfg(feature = "gc")]
             gc(vmctx: vmctx, root: reference) -> reference;
 
-            // Implementation of Wasm's `global.get` instruction for globals
-            // containing GC references.
-            #[cfg(feature = "gc")]
-            gc_ref_global_get(vmctx: vmctx, global: i32) -> reference;
-
-            // Implementation of Wasm's `global.set` instruction for globals
-            // containing GC references.
-            #[cfg(feature = "gc")]
-            gc_ref_global_set(vmctx: vmctx, global: i32, val: reference);
-
             // Returns an index for Wasm's `table.grow` instruction for GC references.
             #[cfg(feature = "gc")]
             table_grow_gc_ref(vmctx: vmctx, table: i32, delta: i64, init: reference) -> pointer;
diff --git a/crates/wasmtime/src/runtime/vm/libcalls.rs b/crates/wasmtime/src/runtime/vm/libcalls.rs
index bd8638459..ecc94f62f 100644
--- a/crates/wasmtime/src/runtime/vm/libcalls.rs
+++ b/crates/wasmtime/src/runtime/vm/libcalls.rs
@@ -437,43 +437,6 @@ unsafe fn gc(instance: &mut Instance, gc_ref: u32) -> Result<u32> {
     }
 }
 
-/// Perform a Wasm `global.get` for GC reference globals.
-#[cfg(feature = "gc")]
-unsafe fn gc_ref_global_get(instance: &mut Instance, index: u32) -> Result<u32> {
-    use core::num::NonZeroUsize;
-
-    let index = wasmtime_environ::GlobalIndex::from_u32(index);
-    let global = instance.defined_or_imported_global_ptr(index);
-    let gc_store = (*instance.store()).gc_store();
-
-    if gc_store
-        .gc_heap
-        .need_gc_before_entering_wasm(NonZeroUsize::new(1).unwrap())
-    {
-        (*instance.store()).gc(None)?;
-    }
-
-    match (*global).as_gc_ref() {
-        None => Ok(0),
-        Some(gc_ref) => {
-            let gc_ref = gc_store.clone_gc_ref(gc_ref);
-            let ret = gc_ref.as_raw_u32();
-            gc_store.expose_gc_ref_to_wasm(gc_ref);
-            Ok(ret)
-        }
-    }
-}
-
-/// Perform a Wasm `global.set` for GC reference globals.
-#[cfg(feature = "gc")]
-unsafe fn gc_ref_global_set(instance: &mut Instance, index: u32, gc_ref: u32) {
-    let index = wasmtime_environ::GlobalIndex::from_u32(index);
-    let global = instance.defined_or_imported_global_ptr(index);
-    let gc_ref = VMGcRef::from_raw_u32(gc_ref);
-    let gc_store = (*instance.store()).gc_store();
-    (*global).write_gc_ref(gc_store, gc_ref.as_ref());
-}
-
 // Implementation of `memory.atomic.notify` for locally defined memories.
 #[cfg(feature = "threads")]
 fn memory_atomic_notify(


Test patch:
diff --git a/tests/disas/gc/externref-globals.wat b/tests/disas/gc/externref-globals.wat
new file mode 100644
index 000000000..95ce3ce51
--- /dev/null
+++ b/tests/disas/gc/externref-globals.wat
@@ -0,0 +1,186 @@
+;;! target = "x86_64"
+;;! flags = "-W function-references,gc"
+;;! test = "optimize"
+
+(module
+  (global $x (mut externref) (ref.null extern))
+  (func (export "get") (result externref)
+    (global.get $x)
+  )
+  (func (export "set") (param externref)
+    (global.set $x (local.get 0))
+  )
+)
+
+;; function u0:0(i64 vmctx, i64) -> i32 tail {
+;;     ss0 = explicit_slot 4, align = 4
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     sig0 = (i64 vmctx, i32) -> i32 system_v
+;;     fn0 = colocated u1:26 sig0
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64):
+;;                                     v43 = iconst.i64 96
+;; @0034                               v4 = iadd v0, v43  ; v43 = 96
+;; @0034                               v5 = load.i32 notrap aligned v4
+;;                                     v44 = stack_addr.i64 ss0
+;;                                     store notrap v5, v44
+;;                                     v46 = iconst.i32 0
+;; @0034                               v6 = icmp eq v5, v46  ; v46 = 0
+;; @0034                               brif v6, block5, block2
+;;
+;;                                 block2:
+;; @0034                               v8 = load.i64 notrap aligned v0+56
+;; @0034                               v9 = load.i64 notrap aligned v8
+;; @0034                               v10 = load.i64 notrap aligned v8+8
+;; @0034                               v11 = icmp eq v9, v10
+;; @0034                               brif v11, block3, block4
+;;
+;;                                 block4:
+;; @0034                               v15 = uextend.i64 v5
+;; @0034                               v16 = iconst.i64 8
+;; @0034                               v17 = uadd_overflow_trap v15, v16, user65535  ; v16 = 8
+;; @0034                               v19 = uadd_overflow_trap v17, v16, user65535  ; v16 = 8
+;; @0034                               v14 = load.i64 notrap aligned readonly v0+48
+;; @0034                               v20 = icmp ult v19, v14
+;; @0034                               brif v20, block7, block6
+;;
+;;                                 block6 cold:
+;; @0034                               trap user65535
+;;
+;;                                 block7:
+;; @0034                               v13 = load.i64 notrap aligned readonly v0+40
+;; @0034                               v21 = iadd v13, v17
+;; @0034                               v22 = load.i64 notrap aligned v21
+;;                                     v40 = load.i32 notrap v44
+;; @0034                               v27 = uextend.i64 v40
+;;                                     v54 = iconst.i64 8
+;; @0034                               v29 = uadd_overflow_trap v27, v54, user65535  ; v54 = 8
+;; @0034                               v31 = uadd_overflow_trap v29, v54, user65535  ; v54 = 8
+;; @0034                               v32 = icmp ult v31, v14
+;; @0034                               brif v32, block9, block8
+;;
+;;                                 block8 cold:
+;; @0034                               trap user65535
+;;
+;;                                 block9:
+;;                                     v48 = iconst.i64 1
+;; @0034                               v23 = iadd.i64 v22, v48  ; v48 = 1
+;; @0034                               v33 = iadd.i64 v13, v29
+;; @0034                               store notrap aligned v23, v33
+;;                                     v39 = load.i32 notrap v44
+;; @0034                               store notrap aligned v39, v9
+;;                                     v51 = iconst.i64 4
+;; @0034                               v34 = iadd.i64 v9, v51  ; v51 = 4
+;; @0034                               store notrap aligned v34, v8
+;; @0034                               jump block5
+;;
+;;                                 block3 cold:
+;; @0034                               v36 = call fn0(v0, v5), stack_map=[i32 @ ss0+0]
+;; @0034                               jump block5
+;;
+;;                                 block5:
+;;                                     v37 = load.i32 notrap v44
+;; @0036                               jump block1
+;;
+;;                                 block1:
+;; @0036                               return v37
+;; }
+;;
+;; function u0:1(i64 vmctx, i64, i32) tail {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     sig0 = (i64 vmctx, i32 uext) system_v
+;;     fn0 = colocated u1:25 sig0
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32):
+;;                                     v54 = iconst.i64 96
+;; @003b                               v4 = iadd v0, v54  ; v54 = 96
+;; @003b                               v5 = load.i32 notrap aligned v4
+;;                                     v55 = iconst.i32 0
+;; @003b                               v6 = icmp eq v2, v55  ; v55 = 0
+;; @003b                               brif v6, block3, block2
+;;
+;;                                 block2:
+;; @003b                               v10 = uextend.i64 v2
+;; @003b                               v34 = iconst.i64 8
+;; @003b                               v12 = uadd_overflow_trap v10, v34, user65535  ; v34 = 8
+;; @003b                               v14 = uadd_overflow_trap v12, v34, user65535  ; v34 = 8
+;; @003b                               v32 = load.i64 notrap aligned readonly v0+48
+;; @003b                               v15 = icmp ult v14, v32
+;; @003b                               brif v15, block9, block8
+;;
+;;                                 block8 cold:
+;; @003b                               trap user65535
+;;
+;;                                 block9:
+;; @003b                               v31 = load.i64 notrap aligned readonly v0+40
+;; @003b                               v16 = iadd v31, v12
+;; @003b                               v17 = load.i64 notrap aligned v16
+;; @003b                               brif.i8 v15, block11, block10
+;;
+;;                                 block10 cold:
+;; @003b                               trap user65535
+;;
+;;                                 block11:
+;;                                     v56 = iconst.i64 1
+;; @003b                               v18 = iadd.i64 v17, v56  ; v56 = 1
+;; @003b                               store notrap aligned v18, v16
+;; @003b                               jump block3
+;;
+;;                                 block3:
+;;                                     v60 = iadd.i64 v0, v54  ; v54 = 96
+;; @003b                               store.i32 notrap aligned v2, v60
+;;                                     v61 = iconst.i32 0
+;;                                     v62 = icmp.i32 eq v5, v61  ; v61 = 0
+;; @003b                               brif v62, block7, block4
+;;
+;;                                 block4:
+;; @003b                               v33 = uextend.i64 v5
+;;                                     v63 = iconst.i64 8
+;; @003b                               v35 = uadd_overflow_trap v33, v63, user65535  ; v63 = 8
+;; @003b                               v37 = uadd_overflow_trap v35, v63, user65535  ; v63 = 8
+;;                                     v64 = load.i64 notrap aligned readonly v0+48
+;; @003b                               v38 = icmp ult v37, v64
+;; @003b                               brif v38, block13, block12
+;;
+;;                                 block12 cold:
+;; @003b                               trap user65535
+;;
+;;                                 block13:
+;;                                     v65 = load.i64 notrap aligned readonly v0+40
+;; @003b                               v39 = iadd v65, v35
+;; @003b                               v40 = load.i64 notrap aligned v39
+;;                                     v58 = iconst.i64 -1
+;; @003b                               v41 = iadd v40, v58  ; v58 = -1
+;;                                     v59 = iconst.i64 0
+;; @003b                               v42 = icmp eq v41, v59  ; v59 = 0
+;; @003b                               brif v42, block5, block6
+;;
+;;                                 block5 cold:
+;; @003b                               call fn0(v0, v5)
+;; @003b                               jump block7
+;;
+;;                                 block6:
+;; @003b                               brif.i8 v38, block15, block14
+;;
+;;                                 block14 cold:
+;; @003b                               trap user65535
+;;
+;;                                 block15:
+;;                                     v66 = iadd.i64 v40, v58  ; v58 = -1
+;; @003b                               store notrap aligned v66, v39
+;; @003b                               jump block7
+;;
+;;                                 block7:
+;; @003d                               jump block1
+;;
+;;                                 block1:
+;; @003d                               return
+;; }
diff --git a/tests/disas/gc/i31ref-globals.wat b/tests/disas/gc/i31ref-globals.wat
new file mode 100644
index 000000000..b172c6f35
--- /dev/null
+++ b/tests/disas/gc/i31ref-globals.wat
@@ -0,0 +1,47 @@
+;;! target = "x86_64"
+;;! flags = "-W function-references,gc"
+;;! test = "optimize"
+
+(module
+  (global $x (mut i31ref) (ref.i31 (i32.const 42)))
+  (func (export "get") (result i31ref)
+    (global.get $x)
+  )
+  (func (export "set") (param i31ref)
+    (global.set $x (local.get 0))
+  )
+)
+
+;; function u0:0(i64 vmctx, i64) -> i32 tail {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64):
+;;                                     v6 = iconst.i64 96
+;; @0036                               v4 = iadd v0, v6  ; v6 = 96
+;; @0036                               v5 = load.i32 notrap aligned v4
+;; @0038                               jump block1
+;;
+;;                                 block1:
+;; @0038                               return v5
+;; }
+;;
+;; function u0:1(i64 vmctx, i64, i32) tail {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0+8
+;;     gv2 = load.i64 notrap aligned gv1
+;;     gv3 = vmctx
+;;     stack_limit = gv2
+;;
+;;                                 block0(v0: i64, v1: i64, v2: i32):
+;;                                     v5 = iconst.i64 96
+;; @003d                               v4 = iadd v0, v5  ; v5 = 96
+;; @003d                               store notrap aligned v2, v4
+;; @003f                               jump block1
+;;
+;;                                 block1:
+;; @003f                               return
+;; }
diff --git a/tests/disas/ref-func-0.wat b/tests/disas/ref-func-0.wat
index d0dcc9d94..9b527227c 100644
--- a/tests/disas/ref-func-0.wat
+++ b/tests/disas/ref-func-0.wat
@@ -15,28 +15,137 @@
 
 ;; function u0:1(i64 vmctx, i64) -> i32, i32, i64, i64 tail {
 ;;     ss0 = explicit_slot 4, align = 4
+;;     ss1 = explicit_slot 4, align = 4
 ;;     gv0 = vmctx
 ;;     gv1 = load.i64 notrap aligned readonly gv0+8
 ;;     gv2 = load.i64 notrap aligned gv1
 ;;     gv3 = vmctx
-;;     sig0 = (i64 vmctx, i32 uext) -> i32 system_v
-;;     fn0 = colocated u1:27 sig0
+;;     sig0 = (i64 vmctx, i32) -> i32 system_v
+;;     fn0 = colocated u1:26 sig0
 ;;     stack_limit = gv2
 ;;
 ;;                                 block0(v0: i64, v1: i64):
 ;; @008f                               v6 = global_value.i64 gv3
-;; @008f                               v7 = iconst.i32 0
-;; @008f                               v8 = call fn0(v6, v7)  ; v7 = 0
+;; @008f                               v7 = iadd_imm v6, 112
+;; @008f                               v8 = load.i32 notrap aligned v7
 ;;                                     stack_store v8, ss0
-;; @0091                               v9 = global_value.i64 gv3
-;; @0091                               v10 = iconst.i32 1
-;; @0091                               v11 = call fn0(v9, v10), stack_map=[i32 @ ss0+0]  ; v10 = 1
-;; @0093                               v12 = global_value.i64 gv3
-;; @0093                               v13 = load.i64 notrap aligned table v12+144
-;; @0095                               v14 = global_value.i64 gv3
-;; @0095                               v15 = load.i64 notrap aligned table v14+160
-;;                                     v16 = stack_load.i32 ss0
-;; @0097                               jump block1(v16, v11, v13, v15)
+;;                                     v89 = stack_load.i32 ss0
+;; @008f                               v9 = icmp_imm eq v89, 0
+;; @008f                               brif v9, block5, block2
+;;
+;;                                 block2:
+;; @008f                               v10 = global_value.i64 gv3
+;; @008f                               v11 = load.i64 notrap aligned v10+56
+;; @008f                               v12 = load.i64 notrap aligned v11
+;; @008f                               v13 = load.i64 notrap aligned v11+8
+;; @008f                               v14 = icmp eq v12, v13
+;; @008f                               brif v14, block3, block4
+;;
+;;                                 block4:
+;; @008f                               v15 = global_value.i64 gv3
+;; @008f                               v16 = load.i64 notrap aligned readonly v15+40
+;; @008f                               v17 = load.i64 notrap aligned readonly v15+48
+;;                                     v88 = stack_load.i32 ss0
+;; @008f                               v18 = uextend.i64 v88
+;; @008f                               v19 = iconst.i64 8
+;; @008f                               v20 = uadd_overflow_trap v18, v19, user65535  ; v19 = 8
+;; @008f                               v21 = iconst.i64 8
+;; @008f                               v22 = uadd_overflow_trap v20, v21, user65535  ; v21 = 8
+;; @008f                               v23 = icmp ult v22, v17
+;; @008f                               trapz v23, user65535
+;; @008f                               v24 = iadd v16, v20
+;; @008f                               v25 = load.i64 notrap aligned v24
+;; @008f                               v26 = iadd_imm v25, 1
+;; @008f                               v27 = global_value.i64 gv3
+;; @008f                               v28 = load.i64 notrap aligned readonly v27+40
+;; @008f                               v29 = load.i64 notrap aligned readonly v27+48
+;;                                     v87 = stack_load.i32 ss0
+;; @008f                               v30 = uextend.i64 v87
+;; @008f                               v31 = iconst.i64 8
+;; @008f                               v32 = uadd_overflow_trap v30, v31, user65535  ; v31 = 8
+;; @008f                               v33 = iconst.i64 8
+;; @008f                               v34 = uadd_overflow_trap v32, v33, user65535  ; v33 = 8
+;; @008f                               v35 = icmp ult v34, v29
+;; @008f                               trapz v35, user65535
+;; @008f                               v36 = iadd v28, v32
+;; @008f                               store notrap aligned v26, v36
+;;                                     v86 = stack_load.i32 ss0
+;; @008f                               store notrap aligned v86, v12
+;; @008f                               v37 = iadd_imm.i64 v12, 4
+;; @008f                               store notrap aligned v37, v11
+;; @008f                               jump block5
+;;
+;;                                 block3 cold:
+;; @008f                               v38 = global_value.i64 gv3
+;;                                     v85 = stack_load.i32 ss0
+;; @008f                               v39 = call fn0(v38, v85), stack_map=[i32 @ ss0+0]
+;; @008f                               jump block5
+;;
+;;                                 block5:
+;; @0091                               v40 = global_value.i64 gv3
+;; @0091                               v41 = iadd_imm v40, 128
+;; @0091                               v42 = load.i32 notrap aligned v41
+;;                                     stack_store v42, ss1
+;;                                     v84 = stack_load.i32 ss1
+;; @0091                               v43 = icmp_imm eq v84, 0
+;; @0091                               brif v43, block9, block6
+;;
+;;                                 block6:
+;; @0091                               v44 = global_value.i64 gv3
+;; @0091                               v45 = load.i64 notrap aligned v44+56
+;; @0091                               v46 = load.i64 notrap aligned v45
+;; @0091                               v47 = load.i64 notrap aligned v45+8
+;; @0091                               v48 = icmp eq v46, v47
+;; @0091                               brif v48, block7, block8
+;;
+;;                                 block8:
+;; @0091                               v49 = global_value.i64 gv3
+;; @0091                               v50 = load.i64 notrap aligned readonly v49+40
+;; @0091                               v51 = load.i64 notrap aligned readonly v49+48
+;;                                     v83 = stack_load.i32 ss1
+;; @0091                               v52 = uextend.i64 v83
+;; @0091                               v53 = iconst.i64 8
+;; @0091                               v54 = uadd_overflow_trap v52, v53, user65535  ; v53 = 8
+;; @0091                               v55 = iconst.i64 8
+;; @0091                               v56 = uadd_overflow_trap v54, v55, user65535  ; v55 = 8
+;; @0091                               v57 = icmp ult v56, v51
+;; @0091                               trapz v57, user65535
+;; @0091                               v58 = iadd v50, v54
+;; @0091                               v59 = load.i64 notrap aligned v58
+;; @0091                               v60 = iadd_imm v59, 1
+;; @0091                               v61 = global_value.i64 gv3
+;; @0091                               v62 = load.i64 notrap aligned readonly v61+40
+;; @0091                               v63 = load.i64 notrap aligned readonly v61+48
+;;                                     v82 = stack_load.i32 ss1
+;; @0091                               v64 = uextend.i64 v82
+;; @0091                               v65 = iconst.i64 8
+;; @0091                               v66 = uadd_overflow_trap v64, v65, user65535  ; v65 = 8
+;; @0091                               v67 = iconst.i64 8
+;; @0091                               v68 = uadd_overflow_trap v66, v67, user65535  ; v67 = 8
+;; @0091                               v69 = icmp ult v68, v63
+;; @0091                               trapz v69, user65535
+;; @0091                               v70 = iadd v62, v66
+;; @0091                               store notrap aligned v60, v70
+;;                                     v81 = stack_load.i32 ss1
+;; @0091                               store notrap aligned v81, v46
+;; @0091                               v71 = iadd_imm.i64 v46, 4
+;; @0091                               store notrap aligned v71, v45
+;; @0091                               jump block9
+;;
+;;                                 block7 cold:
+;; @0091                               v72 = global_value.i64 gv3
+;;                                     v80 = stack_load.i32 ss1
+;; @0091                               v73 = call fn0(v72, v80), stack_map=[i32 @ ss0+0, i32 @ ss1+0]
+;; @0091                               jump block9
+;;
+;;                                 block9:
+;; @0093                               v74 = global_value.i64 gv3
+;; @0093                               v75 = load.i64 notrap aligned table v74+144
+;; @0095                               v76 = global_value.i64 gv3
+;; @0095                               v77 = load.i64 notrap aligned table v76+160
+;;                                     v78 = stack_load.i32 ss0
+;;                                     v79 = stack_load.i32 ss1
+;; @0097                               jump block1(v78, v79, v75, v77)
 ;;
 ;;                                 block1(v2: i32, v3: i32, v4: i64, v5: i64):
 ;; @0097                               return v2, v3, v4, v5