Repository: tetratelabs/wazero
Pull request #1713 (2023-09-15 02:24:13)

wazevo: refactors calling Go convention
this makes it possible to call Go functions for any length of parameters,
and this enables the function listeners impl in the subsequent PR.

#1496 


Patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go b/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
index fb7924a4..4452163c 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_entry_preamble.go
@@ -150,10 +150,10 @@ func (a *abiImpl) constructEntryPreamble() (root *instruction) {
 	//      mov tmp, sp ;; sp cannot be str'ed directly.
 	// 		str sp, [savedExecutionContextPtr, #OriginalStackPointer]
 	// 		str lr, [savedExecutionContextPtr, #GoReturnAddress]
-	cur = a.loadOrStoreAtExecutionContext(fpVReg, wazevoapi.ExecutionContextOffsets.OriginalFramePointer, true, cur)
+	cur = a.loadOrStoreAtExecutionContext(fpVReg, wazevoapi.ExecutionContextOffsetOriginalFramePointer, true, cur)
 	cur = a.move64(tmpRegVReg, spVReg, cur)
-	cur = a.loadOrStoreAtExecutionContext(tmpRegVReg, wazevoapi.ExecutionContextOffsets.OriginalStackPointer, true, cur)
-	cur = a.loadOrStoreAtExecutionContext(lrVReg, wazevoapi.ExecutionContextOffsets.GoReturnAddress, true, cur)
+	cur = a.loadOrStoreAtExecutionContext(tmpRegVReg, wazevoapi.ExecutionContextOffsetOriginalStackPointer, true, cur)
+	cur = a.loadOrStoreAtExecutionContext(lrVReg, wazevoapi.ExecutionContextOffsetGoReturnAddress, true, cur)
 
 	// Next, adjust the Go-allocated stack pointer to reserve the arg/result spaces.
 	// 		sub x28, x28, #stackSlotSize
@@ -208,10 +208,10 @@ func (a *abiImpl) constructEntryPreamble() (root *instruction) {
 	//      mov sp, tmp ;; sp cannot be str'ed directly.
 	// 		ldr lr, [savedExecutionContextPtr, #GoReturnAddress]
 	// 		ret ;; --> return to the Go code
-	cur = a.loadOrStoreAtExecutionContext(fpVReg, wazevoapi.ExecutionContextOffsets.OriginalFramePointer, false, cur)
-	cur = a.loadOrStoreAtExecutionContext(tmpRegVReg, wazevoapi.ExecutionContextOffsets.OriginalStackPointer, false, cur)
+	cur = a.loadOrStoreAtExecutionContext(fpVReg, wazevoapi.ExecutionContextOffsetOriginalFramePointer, false, cur)
+	cur = a.loadOrStoreAtExecutionContext(tmpRegVReg, wazevoapi.ExecutionContextOffsetOriginalStackPointer, false, cur)
 	cur = a.move64(spVReg, tmpRegVReg, cur)
-	cur = a.loadOrStoreAtExecutionContext(lrVReg, wazevoapi.ExecutionContextOffsets.GoReturnAddress, false, cur)
+	cur = a.loadOrStoreAtExecutionContext(lrVReg, wazevoapi.ExecutionContextOffsetGoReturnAddress, false, cur)
 	retInst := a.m.allocateInstr()
 	retInst.asRet(nil)
 	linkInstr(cur, retInst)
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go b/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
index 2c7e1940..a34219b2 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_go_call.go
@@ -14,6 +14,11 @@ var calleeSavedRegistersPlusLinkRegSorted = []regalloc.VReg{
 
 // CompileGoFunctionTrampoline implements backend.Machine.
 func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *ssa.Signature, needModuleContextPtr bool) []byte {
+	argBegin := 1 // Skips exec context by default.
+	if needModuleContextPtr {
+		argBegin++
+	}
+
 	abi := m.getOrCreateABIImpl(sig)
 	m.currentABI = abi
 
@@ -24,19 +29,49 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 	// Execution context is always the first argument.
 	execCtrPtr := x0VReg
 
-	// To properly unwind the stack, we need to create a function frame though it's not necessary for the Go function call.
+	// In the following, we create the following stack frame:
+	//
+	//                   (high address)
+	//                +-----------------+
+	//                |     .......     |
+	//                |      ret Y      |  <----+
+	//                |     .......     |       |
+	//                |      ret 0      |       |
+	//                |      arg X      |       |  size_of_arg_ret
+	//                |     .......     |       |
+	//                |      arg 1      |       |
+	//     SP ------> |      arg 0      |  <----+
+	//                | size_of_arg_ret |
+	//                |  ReturnAddress  |
+	//                +-----------------+ <----+
+	//                |  arg[N]/ret[M]  |      |
+	//                |   ............  |      | goCallStackSize
+	//                |  arg[1]/ret[1]  |      |
+	//                |  arg[0]/ret[0]  | <----+
+	//                |     xxxxxx      |  ;; unused space to make it 16-byte aligned.
+	//                |   frame_size    |
+	//                +-----------------+
+	//                   (low address)
+	//
+	// where the region of "arg[0]/ret[0] ... arg[N]/ret[M]" is the stack used by the Go functions,
+	// therefore will be accessed as the usual []uint64. So that's where we need to pass/receive
+	// the arguments/return values.
+
+	const callFrameSize = 32 // == frame_size + xxxxxx + ReturnAddress + size_of_arg_ret.
+
+	// First of all, we should allocate the stack for the Go function call if necessary.
+	goCallStackSize := goFunctionCallRequiredStackSize(sig, argBegin)
+	cur = m.insertStackBoundsCheck(goCallStackSize+callFrameSize, cur)
+
+	// Next is to create "ReturnAddress + size_of_arg_ret".
 	cur = m.createReturnAddrAndSizeOfArgRetSlot(cur)
-	cur = m.createFrameSizeSlot(cur)
-	// ^^ In total, 32 bytes are allocated for the frame.
 
 	// Save the callee saved registers.
 	cur = m.saveRegistersInExecutionContext(cur, calleeSavedRegistersPlusLinkRegSorted)
 
-	// Next, we need to store all the arguments to the execution context.
-
-	argBegin := 1 // Skips exec context by default.
+	// Next, we need to store all the arguments to the stack in the typical Wasm stack style.
 	if needModuleContextPtr {
-		offset := wazevoapi.ExecutionContextOffsets.GoFunctionCallCalleeModuleContextOpaque.I64()
+		offset := wazevoapi.ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque.I64()
 		if !offsetFitsInAddressModeKindRegUnsignedImm12(64, offset) {
 			panic("BUG: too large or un-aligned offset for goFunctionCallCalleeModuleContextOpaque in execution context")
 		}
@@ -47,19 +82,16 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 		amode := addressMode{kind: addressModeKindRegUnsignedImm12, rn: execCtrPtr, imm: offset}
 		store.asStore(operandNR(moduleCtrPtr), amode, 64)
 		cur = linkInstr(cur, store)
-
-		argBegin++
 	}
 
-	stackPtrReg := x15VReg // Caller save, so we can use it for whatever we want.
-	imm12Op, ok := asImm12Operand(wazevoapi.ExecutionContextOffsets.GoFunctionCallStackBegin.U64())
-	if !ok {
-		panic("BUG: too large or un-aligned offset for Go function call stack in execution context")
-	}
+	// Advances the stack pointer.
+	cur = m.addsAddOrSubStackPointer(cur, spVReg, goCallStackSize, false, true)
 
-	goCallStackPtrCalc := m.allocateInstr()
-	goCallStackPtrCalc.asALU(aluOpAdd, operandNR(stackPtrReg), operandNR(execCtrPtr), imm12Op, true)
-	cur = linkInstr(cur, goCallStackPtrCalc)
+	// Copy the pointer to x15VReg.
+	stackPtrReg := x15VReg // Caller save, so we can use it for whatever we want.
+	copySp := m.allocateInstr()
+	copySp.asMove64(stackPtrReg, spVReg)
+	cur = linkInstr(cur, copySp)
 
 	for _, arg := range abi.args[argBegin:] {
 		if arg.Kind == backend.ABIArgKindReg {
@@ -84,6 +116,9 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 		}
 	}
 
+	// Finally, now that we've advanced SP to arg[0]/ret[0], we allocate `frame_size + xxxxxx `.
+	cur = m.createFrameSizeSlot(cur, goCallStackSize)
+
 	// Set the exit status on the execution context.
 	cur = m.setExitCode(cur, x0VReg, exitCode)
 
@@ -96,12 +131,10 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 	// After the call, we need to restore the callee saved registers.
 	cur = m.restoreRegistersInExecutionContext(cur, calleeSavedRegistersPlusLinkRegSorted)
 
-	// Finally, we move the results into the right places from the execution context's Go function call stack.
-	goCallStackPtrCalc2 := m.allocateInstr()
-	goCallStackPtrCalc2.asALU(aluOpAdd, operandNR(stackPtrReg), operandNR(execCtrPtr), imm12Op, true)
-	goCallStackPtrCalc2.prev = cur
-	cur.next = goCallStackPtrCalc2
-	cur = goCallStackPtrCalc2
+	// Get the pointer to the arg[0]/ret[0]: We need to skip `frame_size + xxxxxx `.
+	if len(abi.rets) > 0 {
+		cur = m.addsAddOrSubStackPointer(cur, stackPtrReg, 16, true, true)
+	}
 
 	for i := range abi.rets {
 		r := &abi.rets[i]
@@ -140,7 +173,7 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 	}
 
 	// Removes the stack frame!
-	cur = m.addsAddOrSubStackPointer(cur, spVReg, 32, true, true)
+	cur = m.addsAddOrSubStackPointer(cur, spVReg, goCallStackSize+callFrameSize, true, true)
 
 	ret := m.allocateInstrAfterLowering()
 	ret.asRet(nil)
@@ -151,7 +184,7 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 }
 
 func (m *machine) saveRegistersInExecutionContext(cur *instruction, regs []regalloc.VReg) *instruction {
-	offset := wazevoapi.ExecutionContextOffsets.SavedRegistersBegin.I64()
+	offset := wazevoapi.ExecutionContextOffsetSavedRegistersBegin.I64()
 	for _, v := range regs {
 		store := m.allocateInstrAfterLowering()
 		var sizeInBits byte
@@ -176,7 +209,7 @@ func (m *machine) saveRegistersInExecutionContext(cur *instruction, regs []regal
 }
 
 func (m *machine) restoreRegistersInExecutionContext(cur *instruction, regs []regalloc.VReg) *instruction {
-	offset := wazevoapi.ExecutionContextOffsets.SavedRegistersBegin.I64()
+	offset := wazevoapi.ExecutionContextOffsetSavedRegistersBegin.I64()
 	for _, v := range regs {
 		load := m.allocateInstrAfterLowering()
 		var as func(dst operand, amode addressMode, sizeInBits byte)
@@ -228,7 +261,7 @@ func (m *machine) setExitCode(cur *instruction, execCtr regalloc.VReg, exitCode
 	setExistStatus.asStore(operandNR(constReg),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsets.ExitCodeOffset.I64(),
+			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsetExitCodeOffset.I64(),
 		}, 32)
 	cur = linkInstr(cur, setExistStatus)
 	return cur
@@ -245,7 +278,7 @@ func (m *machine) storeReturnAddressAndExit(cur *instruction) *instruction {
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
 			// Execution context is always the first argument.
-			rn: x0VReg, imm: wazevoapi.ExecutionContextOffsets.GoCallReturnAddress.I64(),
+			rn: x0VReg, imm: wazevoapi.ExecutionContextOffsetGoCallReturnAddress.I64(),
 		}, 64)
 	cur = linkInstr(cur, storeReturnAddr)
 
@@ -268,8 +301,36 @@ func (m *machine) saveCurrentStackPointer(cur *instruction, execCtr regalloc.VRe
 	strSp.asStore(operandNR(tmpRegVReg),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsets.StackPointerBeforeGoCall.I64(),
+			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsetStackPointerBeforeGoCall.I64(),
 		}, 64)
 	cur = linkInstr(cur, strSp)
 	return cur
 }
+
+// goFunctionCallRequiredStackSize returns the size of the stack required for the Go function call.
+func goFunctionCallRequiredStackSize(sig *ssa.Signature, argBegin int) (ret int64) {
+	var paramNeededInBytes, resultNeededInBytes int64
+	for _, p := range sig.Params[argBegin:] {
+		s := int64(p.Size())
+		if s < 8 {
+			s = 8 // We use uint64 for all basic types, except SIMD v128.
+		}
+		paramNeededInBytes += s
+	}
+	for _, r := range sig.Results {
+		s := int64(r.Size())
+		if s < 8 {
+			s = 8 // We use uint64 for all basic types, except SIMD v128.
+		}
+		resultNeededInBytes += s
+	}
+
+	if paramNeededInBytes > resultNeededInBytes {
+		ret = paramNeededInBytes
+	} else {
+		ret = resultNeededInBytes
+	}
+	// Align to 16 bytes.
+	ret = (ret + 15) &^ 15
+	return
+}
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index b079b6fe..a361c9e7 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -1538,7 +1538,7 @@ func encodeExitSequence(c backend.Compiler, ctxReg regalloc.VReg) {
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
 			rn:   ctxReg,
-			imm:  wazevoapi.ExecutionContextOffsets.GoReturnAddress.I64(),
+			imm:  wazevoapi.ExecutionContextOffsetGoReturnAddress.I64(),
 		},
 	)
 
@@ -1548,7 +1548,7 @@ func encodeExitSequence(c backend.Compiler, ctxReg regalloc.VReg) {
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
 			rn:   ctxReg,
-			imm:  wazevoapi.ExecutionContextOffsets.OriginalFramePointer.I64(),
+			imm:  wazevoapi.ExecutionContextOffsetOriginalFramePointer.I64(),
 		},
 	)
 
@@ -1558,7 +1558,7 @@ func encodeExitSequence(c backend.Compiler, ctxReg regalloc.VReg) {
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
 			rn:   ctxReg,
-			imm:  wazevoapi.ExecutionContextOffsets.OriginalStackPointer.I64(),
+			imm:  wazevoapi.ExecutionContextOffsetOriginalStackPointer.I64(),
 		},
 	)
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 2efeb3a5..3e942ae7 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -854,7 +854,7 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 	setExitCode.asStore(operandNR(tmpReg1),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.ExitCodeOffset.I64(),
+			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsetExitCodeOffset.I64(),
 		}, 32)
 
 	// In order to unwind the stack, we also need to push the current stack pointer:
@@ -865,7 +865,7 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 	strSpToExecCtx.asStore(operandNR(tmp2),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.StackPointerBeforeGoCall.I64(),
+			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsetStackPointerBeforeGoCall.I64(),
 		}, 64)
 	// Also the address of this exit.
 	tmp3 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
@@ -875,7 +875,7 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 	storeCurrentAddrToExecCtx.asStore(operandNR(tmp3),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.GoCallReturnAddress.I64(),
+			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsetGoCallReturnAddress.I64(),
 		}, 64)
 
 	exitSeq := m.allocateInstr()
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue.go b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue.go
index 89c6e76f..64410aa1 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue.go
@@ -141,7 +141,7 @@ func (m *machine) SetupPrologue() {
 	//                                            +-----------------+ <---- SP
 	//            (low address)
 	//
-	cur = m.createFrameSizeSlot(cur)
+	cur = m.createFrameSizeSlot(cur, m.frameSize())
 
 	linkInstr(cur, prevInitInst)
 }
@@ -163,9 +163,9 @@ func (m *machine) createReturnAddrAndSizeOfArgRetSlot(cur *instruction) *instruc
 	return cur
 }
 
-func (m *machine) createFrameSizeSlot(cur *instruction) *instruction {
+func (m *machine) createFrameSizeSlot(cur *instruction, s int64) *instruction {
 	var frameSizeReg regalloc.VReg
-	if s := m.frameSize(); s > 0 {
+	if s > 0 {
 		cur = m.lowerConstantI64AndInsert(cur, tmpRegVReg, s)
 		frameSizeReg = tmpRegVReg
 	} else {
@@ -343,7 +343,7 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 	ldr.asULoad(operandNR(tmp2), addressMode{
 		kind: addressModeKindRegUnsignedImm12,
 		rn:   x0VReg, // execution context is always the first argument.
-		imm:  wazevoapi.ExecutionContextOffsets.StackBottomPtr.I64(),
+		imm:  wazevoapi.ExecutionContextOffsetStackBottomPtr.I64(),
 	}, 64)
 	cur = linkInstr(cur, ldr)
 
@@ -366,7 +366,7 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 			addressMode{
 				kind: addressModeKindRegUnsignedImm12,
 				// Execution context is always the first argument.
-				rn: x0VReg, imm: wazevoapi.ExecutionContextOffsets.StackGrowRequiredSize.I64(),
+				rn: x0VReg, imm: wazevoapi.ExecutionContextOffsetStackGrowRequiredSize.I64(),
 			}, 64)
 
 		cur = linkInstr(cur, setRequiredStackSize)
@@ -376,7 +376,7 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 	ldrAddress.asULoad(operandNR(tmpRegVReg), addressMode{
 		kind: addressModeKindRegUnsignedImm12,
 		rn:   x0VReg, // execution context is always the first argument
-		imm:  wazevoapi.ExecutionContextOffsets.StackGrowCallTrampolineAddress.I64(),
+		imm:  wazevoapi.ExecutionContextOffsetStackGrowCallTrampolineAddress.I64(),
 	}, 64)
 	cur = linkInstr(cur, ldrAddress)
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
index fff36ae3..4f092735 100644
--- a/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
+++ b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
@@ -59,3 +59,27 @@ func UnwindStack(sp, top uintptr) (returnAddresses []uintptr) {
 	}
 	return
 }
+
+// GoCallStackView is a function to get a view of the stack before a Go call, which
+// is the view of the stack allocated in CompileGoFunctionTrampoline.
+func GoCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
+	//                (high address)
+	//         ^  +-----------------+ <----+
+	//         |  |  arg[N]/ret[M]  |      |
+	//   view  |  |  ............   |      | frame_size
+	//         |  |  arg[1]/ret[1]  |      |
+	//         v  |  arg[0]/ret[0]  | <----+
+	//            |     xxxxxx      |  ;; unused space to make it 16-byte aligned.
+	//            |   frame_size    |
+	//            +-----------------+ <---- stackPointerBeforeGoCall
+	//               (low address)
+	size := *stackPointerBeforeGoCall
+	var view []uint64
+	{
+		sh := (*reflect.SliceHeader)(unsafe.Pointer(&view))
+		sh.Data = uintptr(unsafe.Pointer(stackPointerBeforeGoCall)) + 16 // skips the (frame_size, xxxxx).
+		sh.Len = int(size)
+		sh.Cap = int(size)
+	}
+	return view
+}
diff --git a/internal/engine/wazevo/call_engine.go b/internal/engine/wazevo/call_engine.go
index 1f16a7cc..2e516b59 100644
--- a/internal/engine/wazevo/call_engine.go
+++ b/internal/engine/wazevo/call_engine.go
@@ -58,7 +58,7 @@ type (
 		// goCallReturnAddress holds the return address to go back to the caller of the Go function.
 		goCallReturnAddress *byte
 		// stackPointerBeforeGoCall holds the stack pointer before calling a Go function.
-		stackPointerBeforeGoCall uintptr
+		stackPointerBeforeGoCall *uint64
 		// stackGrowRequiredSize holds the required size of stack grow.
 		stackGrowRequiredSize uintptr
 		// memoryGrowTrampolineAddress holds the address of memory grow trampoline function.
@@ -72,13 +72,9 @@ type (
 		savedRegisters [64][2]uint64
 		// goFunctionCallCalleeModuleContextOpaque is the pointer to the target Go function's moduleContextOpaque.
 		goFunctionCallCalleeModuleContextOpaque uintptr
-		// goFunctionCallStack is used to pass/receive parameters/results for Go function calls.
-		goFunctionCallStack [goFunctionCallStackSize]uint64
 	}
 )
 
-const goFunctionCallStackSize = 128
-
 var initialStackSize uint64 = 512
 
 func (c *callEngine) init() {
@@ -160,7 +156,7 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 		if r := recover(); r != nil {
 			builder := wasmdebug.NewErrorBuilder()
 			c.addFrame(builder, uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress)))
-			returnAddrs := unwindStack(c.execCtx.stackPointerBeforeGoCall, c.stackTop)
+			returnAddrs := unwindStack(uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)), c.stackTop)
 			for _, retAddr := range returnAddrs[:len(returnAddrs)-1] { // the last return addr is the trampoline, so we skip it.
 				c.addFrame(builder, retAddr)
 			}
@@ -199,7 +195,8 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 		case wazevoapi.ExitCodeGrowMemory:
 			mod := c.callerModuleInstance()
 			mem := mod.MemoryInstance
-			argRes := &c.execCtx.goFunctionCallStack[0]
+			s := goCallStackView(c.execCtx.stackPointerBeforeGoCall)
+			argRes := &s[0]
 			if res, ok := mem.Grow(uint32(*argRes)); !ok {
 				*argRes = uint64(0xffffffff) // = -1 in signed 32-bit integer.
 			} else {
@@ -215,20 +212,20 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 				}
 			}
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
-			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeCallGoFunction:
 			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
 			f := hostModuleGoFuncFromOpaque[api.GoFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
-			f.Call(ctx, c.execCtx.goFunctionCallStack[:])
+			f.Call(ctx, goCallStackView(c.execCtx.stackPointerBeforeGoCall))
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
-			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeCallGoModuleFunction:
 			index := wazevoapi.GoFunctionIndexFromExitCode(ec)
 			f := hostModuleGoFuncFromOpaque[api.GoModuleFunction](index, c.execCtx.goFunctionCallCalleeModuleContextOpaque)
 			mod := c.callerModuleInstance()
-			f.Call(ctx, mod, c.execCtx.goFunctionCallStack[:])
+			f.Call(ctx, mod, goCallStackView(c.execCtx.stackPointerBeforeGoCall))
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
-			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeCheckModuleExitCode:
 			// Note: this operation must be done in Go, not native code. The reason is that
 			// native code cannot be preempted and that means it can block forever if there are not
@@ -237,7 +234,7 @@ func (c *callEngine) callWithStack(ctx context.Context, paramResultStack []uint6
 				panic(err)
 			}
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
-			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
+			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall)))
 		case wazevoapi.ExitCodeUnreachable:
 			panic(wasmruntime.ErrRuntimeUnreachable)
 		case wazevoapi.ExitCodeMemoryOutOfBounds:
@@ -286,7 +283,7 @@ func (c *callEngine) growStack() (newSP uintptr, err error) {
 	newLen := 2*currentLen + c.execCtx.stackGrowRequiredSize
 	newStack := make([]byte, newLen)
 
-	relSp := c.stackTop - c.execCtx.stackPointerBeforeGoCall
+	relSp := c.stackTop - uintptr(unsafe.Pointer(c.execCtx.stackPointerBeforeGoCall))
 
 	// Copy the existing contents in the previous Go-allocated stack into the new one.
 	var prevStackAligned, newStackAligned []byte
diff --git a/internal/engine/wazevo/engine.go b/internal/engine/wazevo/engine.go
index 1e7d0a73..82fa6af1 100644
--- a/internal/engine/wazevo/engine.go
+++ b/internal/engine/wazevo/engine.go
@@ -289,10 +289,6 @@ func (e *engine) compileHostModule(ctx context.Context, module *wasm.Module) (*c
 
 		typIndex := module.FunctionSection[i]
 		typ := &module.TypeSection[typIndex]
-		if typ.ParamNumInUint64 >= goFunctionCallStackSize || typ.ResultNumInUint64 >= goFunctionCallStackSize {
-			return nil, fmt.Errorf("too many params or results for a host function (maximum %d): %v",
-				goFunctionCallStackSize, typ)
-		}
 
 		// We can relax until the index fits together in ExitCode as we do in wazevoapi.ExitCodeCallGoModuleFunctionWithIndex.
 		// However, 1 << 16 should be large enough for a real use case.
diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go
index bcb198c0..01abb214 100644
--- a/internal/engine/wazevo/frontend/lower.go
+++ b/internal/engine/wazevo/frontend/lower.go
@@ -823,7 +823,7 @@ func (c *Compiler) lowerCurrentOpcode() {
 		pages := state.pop()
 		loadPtr := builder.AllocateInstruction().
 			AsLoad(c.execCtxPtrValue,
-				wazevoapi.ExecutionContextOffsets.MemoryGrowTrampolineAddress.U32(),
+				wazevoapi.ExecutionContextOffsetMemoryGrowTrampolineAddress.U32(),
 				ssa.TypeI64,
 			).Insert(builder).Return()
 
@@ -1015,7 +1015,7 @@ func (c *Compiler) lowerCurrentOpcode() {
 		if c.ensureTermination {
 			checkModuleExitCodePtr := builder.AllocateInstruction().
 				AsLoad(c.execCtxPtrValue,
-					wazevoapi.ExecutionContextOffsets.CheckModuleExitCodeTrampolineAddress.U32(),
+					wazevoapi.ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress.U32(),
 					ssa.TypeI64,
 				).Insert(builder).Return()
 
@@ -1932,7 +1932,7 @@ func (c *Compiler) storeCallerModuleContext() {
 	execCtx := c.execCtxPtrValue
 	store := builder.AllocateInstruction()
 	store.AsStore(ssa.OpcodeStore,
-		c.moduleCtxPtrValue, execCtx, wazevoapi.ExecutionContextOffsets.CallerModuleContextPtr.U32())
+		c.moduleCtxPtrValue, execCtx, wazevoapi.ExecutionContextOffsetCallerModuleContextPtr.U32())
 	builder.InsertInstruction(store)
 }
 
diff --git a/internal/engine/wazevo/machine.go b/internal/engine/wazevo/machine.go
index 08bb7339..70c666b9 100644
--- a/internal/engine/wazevo/machine.go
+++ b/internal/engine/wazevo/machine.go
@@ -24,3 +24,12 @@ func unwindStack(sp, top uintptr) (returnAddresses []uintptr) {
 		panic("unsupported architecture")
 	}
 }
+
+func goCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
+	switch runtime.GOARCH {
+	case "arm64":
+		return arm64.GoCallStackView(stackPointerBeforeGoCall)
+	default:
+		panic("unsupported architecture")
+	}
+}
diff --git a/internal/engine/wazevo/wazevoapi/offsetdata.go b/internal/engine/wazevo/wazevoapi/offsetdata.go
index 68d8e5e9..2405dd9f 100644
--- a/internal/engine/wazevo/wazevoapi/offsetdata.go
+++ b/internal/engine/wazevo/wazevoapi/offsetdata.go
@@ -1,6 +1,8 @@
 package wazevoapi
 
-import "github.com/tetratelabs/wazero/internal/wasm"
+import (
+	"github.com/tetratelabs/wazero/internal/wasm"
+)
 
 const (
 	// FunctionInstanceSize is the size of wazevo.functionInstance.
@@ -13,58 +15,36 @@ const (
 	FunctionInstanceTypeIDOffset = 16
 )
 
-var ExecutionContextOffsets = ExecutionContextOffsetData{
-	ExitCodeOffset:                          0,
-	CallerModuleContextPtr:                  8,
-	OriginalFramePointer:                    16,
-	OriginalStackPointer:                    24,
-	GoReturnAddress:                         32,
-	StackBottomPtr:                          40,
-	GoCallReturnAddress:                     48,
-	StackPointerBeforeGoCall:                56,
-	StackGrowRequiredSize:                   64,
-	MemoryGrowTrampolineAddress:             72,
-	StackGrowCallTrampolineAddress:          80,
-	CheckModuleExitCodeTrampolineAddress:    88,
-	SavedRegistersBegin:                     96,
-	GoFunctionCallCalleeModuleContextOpaque: 1120,
-	GoFunctionCallStackBegin:                1128,
-}
-
-// ExecutionContextOffsetData allows the compilers to get the information about offsets to the fields of wazevo.executionContext,
-// which are necessary for compiling various instructions. This is globally unique.
-type ExecutionContextOffsetData struct {
-	// ExitCodeOffset is an offset of `exitCode` field in wazevo.executionContext
-	ExitCodeOffset Offset
-	// CallerModuleContextPtr is an offset of `callerModuleContextPtr` field in wazevo.executionContext
-	CallerModuleContextPtr Offset
-	// CallerModuleContextPtr is an offset of `originalFramePointer` field in wazevo.executionContext
-	OriginalFramePointer Offset
-	// OriginalStackPointer is an offset of `originalStackPointer` field in wazevo.executionContext
-	OriginalStackPointer Offset
-	// GoReturnAddress is an offset of `goReturnAddress` field in wazevo.executionContext
-	GoReturnAddress Offset
-	// StackBottomPtr is an offset of `stackBottomPtr` field in wazevo.executionContext
-	StackBottomPtr Offset
-	// GoCallReturnAddress is an offset of `goCallReturnAddress` field in wazevo.executionContext
-	GoCallReturnAddress Offset
-	// StackPointerBeforeGoCall is an offset of `StackPointerBeforeGoCall` field in wazevo.executionContext
-	StackPointerBeforeGoCall Offset
-	// StackGrowRequiredSize is an offset of `stackGrowRequiredSize` field in wazevo.executionContext
-	StackGrowRequiredSize Offset
-	// MemoryGrowTrampolineAddress is an offset of `memoryGrowTrampolineAddress` field in wazevo.executionContext
-	MemoryGrowTrampolineAddress Offset
-	// stackGrowCallTrampolineAddress is an offset of `stackGrowCallTrampolineAddress` field in wazevo.executionContext.
-	StackGrowCallTrampolineAddress Offset
-	// CheckModuleExitCodeTrampolineAddress is an offset of `checkModuleExitCodeTrampolineAddress` field in wazevo.executionContext.
-	CheckModuleExitCodeTrampolineAddress Offset
-	// GoCallReturnAddress is an offset of the first element of `savedRegisters` field in wazevo.executionContext
-	SavedRegistersBegin Offset
-	// GoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext
-	GoFunctionCallCalleeModuleContextOpaque Offset
-	// GoFunctionCallStackBegin is an offset of the first element of `goFunctionCallStack` field in wazevo.executionContext
-	GoFunctionCallStackBegin Offset
-}
+const (
+	// ExecutionContextOffsetExitCodeOffset is an offset of `exitCode` field in wazevo.executionContext
+	ExecutionContextOffsetExitCodeOffset Offset = 0
+	// ExecutionContextOffsetCallerModuleContextPtr is an offset of `callerModuleContextPtr` field in wazevo.executionContext
+	ExecutionContextOffsetCallerModuleContextPtr Offset = 8
+	// ExecutionContextOffsetOriginalFramePointer is an offset of `originalFramePointer` field in wazevo.executionContext
+	ExecutionContextOffsetOriginalFramePointer Offset = 16
+	// ExecutionContextOffsetOriginalStackPointer is an offset of `originalStackPointer` field in wazevo.executionContext
+	ExecutionContextOffsetOriginalStackPointer Offset = 24
+	// ExecutionContextOffsetGoReturnAddress is an offset of `goReturnAddress` field in wazevo.executionContext
+	ExecutionContextOffsetGoReturnAddress Offset = 32
+	// ExecutionContextOffsetStackBottomPtr is an offset of `stackBottomPtr` field in wazevo.executionContext
+	ExecutionContextOffsetStackBottomPtr Offset = 40
+	// ExecutionContextOffsetGoCallReturnAddress is an offset of `goCallReturnAddress` field in wazevo.executionContext
+	ExecutionContextOffsetGoCallReturnAddress Offset = 48
+	// ExecutionContextOffsetStackPointerBeforeGoCall is an offset of `StackPointerBeforeGoCall` field in wazevo.executionContext
+	ExecutionContextOffsetStackPointerBeforeGoCall Offset = 56
+	// ExecutionContextOffsetStackGrowRequiredSize is an offset of `stackGrowRequiredSize` field in wazevo.executionContext
+	ExecutionContextOffsetStackGrowRequiredSize Offset = 64
+	// ExecutionContextOffsetMemoryGrowTrampolineAddress is an offset of `memoryGrowTrampolineAddress` field in wazevo.executionContext
+	ExecutionContextOffsetMemoryGrowTrampolineAddress Offset = 72
+	// ExecutionContextOffsetStackGrowCallTrampolineAddress is an offset of `stackGrowCallTrampolineAddress` field in wazevo.executionContext.
+	ExecutionContextOffsetStackGrowCallTrampolineAddress Offset = 80
+	// ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress is an offset of `checkModuleExitCodeTrampolineAddress` field in wazevo.executionContext.
+	ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress Offset = 88
+	// ExecutionContextOffsetSavedRegistersBegin is an offset of the first element of `savedRegisters` field in wazevo.executionContext
+	ExecutionContextOffsetSavedRegistersBegin Offset = 96
+	// ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque is an offset of `goFunctionCallCalleeModuleContextOpaque` field in wazevo.executionContext
+	ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque Offset = 1120
+)
 
 // ModuleContextOffsetData allows the compilers to get the information about offsets to the fields of wazevo.moduleContextOpaque,
 // This is unique per module.


Test patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go b/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
index 6d159015..4b7410d9 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_go_call_test.go
@@ -42,8 +42,15 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 			},
 			needModuleContextPtr: true,
 			exp: `
+	sub x27, sp, #0x40
+	ldr x11, [x0, #0x28]
+	subs xzr, x27, x11
+	b.ge #0x14
+	orr x27, xzr, #0x40
+	str x27, [x0, #0x40]
+	ldr x27, [x0, #0x50]
+	bl x27
 	stp x30, xzr, [sp, #-0x10]!
-	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -69,8 +76,11 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	str q30, [x0, #0x1c0]
 	str q31, [x0, #0x1d0]
 	str x1, [x0, #0x460]
-	add x15, x0, #0x468
+	sub sp, sp, #0x20
+	mov x15, sp
 	str d0, [x15], #0x8
+	orr x27, xzr, #0x20
+	str x27, [sp, #-0x10]!
 	movz w17, #0x6406, lsl 0
 	str w17, [x0]
 	mov x27, sp
@@ -102,7 +112,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	ldr q29, [x0, #0x1b0]
 	ldr q30, [x0, #0x1c0]
 	ldr q31, [x0, #0x1d0]
-	add x15, x0, #0x468
+	add x15, sp, #0x10
 	ldr w27, [x15], #0x8
 	mov w0, w27
 	ldr x27, [x15], #0x8
@@ -111,7 +121,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	mov v0.8b, v17.8b
 	ldr d17, [x15], #0x8
 	mov v1.8b, v17.8b
-	add sp, sp, #0x20
+	add sp, sp, #0x40
 	ret
 `,
 		},
@@ -124,8 +134,15 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 			},
 			needModuleContextPtr: true,
 			exp: `
+	sub x27, sp, #0x40
+	ldr x11, [x0, #0x28]
+	subs xzr, x27, x11
+	b.ge #0x14
+	orr x27, xzr, #0x40
+	str x27, [x0, #0x40]
+	ldr x27, [x0, #0x50]
+	bl x27
 	stp x30, xzr, [sp, #-0x10]!
-	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -151,11 +168,14 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	str q30, [x0, #0x1c0]
 	str q31, [x0, #0x1d0]
 	str x1, [x0, #0x460]
-	add x15, x0, #0x468
+	sub sp, sp, #0x20
+	mov x15, sp
 	str d0, [x15], #0x8
 	str d1, [x15], #0x8
 	str x2, [x15], #0x8
 	str x3, [x15], #0x8
+	orr x27, xzr, #0x20
+	str x27, [sp, #-0x10]!
 	movz w17, #0x6406, lsl 0
 	str w17, [x0]
 	mov x27, sp
@@ -187,8 +207,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	ldr q29, [x0, #0x1b0]
 	ldr q30, [x0, #0x1c0]
 	ldr q31, [x0, #0x1d0]
-	add x15, x0, #0x468
-	add sp, sp, #0x20
+	add sp, sp, #0x40
 	ret
 `,
 		},
@@ -200,8 +219,15 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 				Results: []ssa.Type{ssa.TypeI32},
 			},
 			exp: `
+	sub x27, sp, #0x30
+	ldr x11, [x0, #0x28]
+	subs xzr, x27, x11
+	b.ge #0x14
+	orr x27, xzr, #0x30
+	str x27, [x0, #0x40]
+	ldr x27, [x0, #0x50]
+	bl x27
 	stp x30, xzr, [sp, #-0x10]!
-	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -226,8 +252,11 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	str q29, [x0, #0x1b0]
 	str q30, [x0, #0x1c0]
 	str q31, [x0, #0x1d0]
-	add x15, x0, #0x468
+	sub sp, sp, #0x10
+	mov x15, sp
 	str x1, [x15], #0x8
+	orr x27, xzr, #0x10
+	str x27, [sp, #-0x10]!
 	orr w17, wzr, #0x2
 	str w17, [x0]
 	mov x27, sp
@@ -259,10 +288,10 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	ldr q29, [x0, #0x1b0]
 	ldr q30, [x0, #0x1c0]
 	ldr q31, [x0, #0x1d0]
-	add x15, x0, #0x468
+	add x15, sp, #0x10
 	ldr w27, [x15], #0x8
 	mov w0, w27
-	add sp, sp, #0x20
+	add sp, sp, #0x30
 	ret
 `,
 		},
@@ -279,3 +308,50 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 		})
 	}
 }
+
+func Test_goFunctionCallRequiredStackSize(t *testing.T) {
+	for _, tc := range []struct {
+		name     string
+		sig      *ssa.Signature
+		argBegin int
+		exp      int64
+	}{
+		{
+			name: "no param",
+			sig:  &ssa.Signature{},
+			exp:  0,
+		},
+		{
+			name: "only param",
+			sig:  &ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeV128}},
+			exp:  32,
+		},
+		{
+			name: "only result",
+			sig:  &ssa.Signature{Results: []ssa.Type{ssa.TypeI64, ssa.TypeV128, ssa.TypeI32}},
+			exp:  32,
+		},
+		{
+			name: "param < result",
+			sig:  &ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeV128}, Results: []ssa.Type{ssa.TypeI64, ssa.TypeV128, ssa.TypeI32}},
+			exp:  32,
+		},
+		{
+			name: "param > result",
+			sig:  &ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeV128, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64, ssa.TypeV128}},
+			exp:  32,
+		},
+		{
+			name:     "param < result / argBegin=2",
+			argBegin: 2,
+			sig:      &ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeV128, ssa.TypeI32}, Results: []ssa.Type{ssa.TypeI64, ssa.TypeV128}},
+			exp:      32,
+		},
+	} {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			requiredSize := goFunctionCallRequiredStackSize(tc.sig, tc.argBegin)
+			require.Equal(t, tc.exp, requiredSize)
+		})
+	}
+}
diff --git a/internal/engine/wazevo/call_engine_test.go b/internal/engine/wazevo/call_engine_test.go
index c7c7123e..f8b88423 100644
--- a/internal/engine/wazevo/call_engine_test.go
+++ b/internal/engine/wazevo/call_engine_test.go
@@ -32,7 +32,7 @@ func TestCallEngine_growStack(t *testing.T) {
 			stackTop: uintptr(unsafe.Pointer(&s[15])),
 			execCtx: executionContext{
 				stackGrowRequiredSize:    160,
-				stackPointerBeforeGoCall: uintptr(unsafe.Pointer(&s[10])),
+				stackPointerBeforeGoCall: (*uint64)(unsafe.Pointer(&s[10])),
 			},
 		}
 		newSP, err := c.growStack()
diff --git a/internal/engine/wazevo/wazevo_test.go b/internal/engine/wazevo/wazevo_test.go
index 6057693b..0a835a25 100644
--- a/internal/engine/wazevo/wazevo_test.go
+++ b/internal/engine/wazevo/wazevo_test.go
@@ -46,23 +46,20 @@ func TestEngine_DeleteCompiledModule(t *testing.T) {
 }
 
 func Test_ExecutionContextOffsets(t *testing.T) {
-	offsets := wazevoapi.ExecutionContextOffsets
-
 	var execCtx executionContext
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.exitCode)), offsets.ExitCodeOffset)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.callerModuleContextPtr)), offsets.CallerModuleContextPtr)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.originalFramePointer)), offsets.OriginalFramePointer)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.originalStackPointer)), offsets.OriginalStackPointer)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goReturnAddress)), offsets.GoReturnAddress)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goCallReturnAddress)), offsets.GoCallReturnAddress)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackPointerBeforeGoCall)), offsets.StackPointerBeforeGoCall)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowRequiredSize)), offsets.StackGrowRequiredSize)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.memoryGrowTrampolineAddress)), offsets.MemoryGrowTrampolineAddress)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowCallTrampolineAddress)), offsets.StackGrowCallTrampolineAddress)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.checkModuleExitCodeTrampolineAddress)), offsets.CheckModuleExitCodeTrampolineAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.exitCode)), wazevoapi.ExecutionContextOffsetExitCodeOffset)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.callerModuleContextPtr)), wazevoapi.ExecutionContextOffsetCallerModuleContextPtr)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.originalFramePointer)), wazevoapi.ExecutionContextOffsetOriginalFramePointer)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.originalStackPointer)), wazevoapi.ExecutionContextOffsetOriginalStackPointer)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goReturnAddress)), wazevoapi.ExecutionContextOffsetGoReturnAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goCallReturnAddress)), wazevoapi.ExecutionContextOffsetGoCallReturnAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackPointerBeforeGoCall)), wazevoapi.ExecutionContextOffsetStackPointerBeforeGoCall)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowRequiredSize)), wazevoapi.ExecutionContextOffsetStackGrowRequiredSize)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.memoryGrowTrampolineAddress)), wazevoapi.ExecutionContextOffsetMemoryGrowTrampolineAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.stackGrowCallTrampolineAddress)), wazevoapi.ExecutionContextOffsetStackGrowCallTrampolineAddress)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.checkModuleExitCodeTrampolineAddress)), wazevoapi.ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress)
 	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters))%16, wazevoapi.Offset(0),
 		"SavedRegistersBegin must be aligned to 16 bytes")
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), offsets.SavedRegistersBegin)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goFunctionCallCalleeModuleContextOpaque)), offsets.GoFunctionCallCalleeModuleContextOpaque)
-	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goFunctionCallStack)), offsets.GoFunctionCallStackBegin)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.savedRegisters)), wazevoapi.ExecutionContextOffsetSavedRegistersBegin)
+	require.Equal(t, wazevoapi.Offset(unsafe.Offsetof(execCtx.goFunctionCallCalleeModuleContextOpaque)), wazevoapi.ExecutionContextOffsetGoFunctionCallCalleeModuleContextOpaque)
 }