Repository: tetratelabs/wazero
Pull request #1699 (2023-09-12 05:35:41)

wazevo: support for stack traces
#1496 


Patch:
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 dfb335f3..2c7e1940 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,9 @@ var calleeSavedRegistersPlusLinkRegSorted = []regalloc.VReg{
 
 // CompileGoFunctionTrampoline implements backend.Machine.
 func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *ssa.Signature, needModuleContextPtr bool) []byte {
+	abi := m.getOrCreateABIImpl(sig)
+	m.currentABI = abi
+
 	cur := m.allocateInstr()
 	cur.asNop0()
 	m.rootInstr = cur
@@ -21,6 +24,11 @@ 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.
+	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)
 
@@ -53,7 +61,6 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 	goCallStackPtrCalc.asALU(aluOpAdd, operandNR(stackPtrReg), operandNR(execCtrPtr), imm12Op, true)
 	cur = linkInstr(cur, goCallStackPtrCalc)
 
-	abi := m.getOrCreateABIImpl(sig)
 	for _, arg := range abi.args[argBegin:] {
 		if arg.Kind == backend.ABIArgKindReg {
 			store := m.allocateInstr()
@@ -132,6 +139,9 @@ func (m *machine) CompileGoFunctionTrampoline(exitCode wazevoapi.ExitCode, sig *
 		}
 	}
 
+	// Removes the stack frame!
+	cur = m.addsAddOrSubStackPointer(cur, spVReg, 32, true, true)
+
 	ret := m.allocateInstrAfterLowering()
 	ret.asRet(nil)
 	linkInstr(cur, ret)
@@ -258,7 +268,7 @@ func (m *machine) saveCurrentStackPointer(cur *instruction, execCtr regalloc.VRe
 	strSp.asStore(operandNR(tmpRegVReg),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
-			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsets.StackPointerBeforeGrow.I64(),
+			rn:   execCtr, imm: wazevoapi.ExecutionContextOffsets.StackPointerBeforeGoCall.I64(),
 		}, 64)
 	cur = linkInstr(cur, strSp)
 	return cur
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index 34bb60f7..84715002 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -45,6 +45,7 @@ const (
 )
 
 var defKinds = [numInstructionKinds]defKind{
+	adr:             defKindRD,
 	aluRRR:          defKindRD,
 	aluRRRR:         defKindRD,
 	aluRRImm12:      defKindRD,
@@ -212,6 +213,7 @@ var useKinds = [numInstructionKinds]useKind{
 	intToFpu:        useKindRN,
 	movToFPSR:       useKindRN,
 	movFromFPSR:     useKindNone,
+	adr:             useKindNone,
 }
 
 // uses returns the list of regalloc.VReg that are used by the instruction.
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index ed1418b0..fc123ab3 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -385,12 +385,13 @@ func (m *machine) lowerFcopysign(x, y, ret ssa.Value) {
 	//
 
 	setMSB := m.allocateInstr()
+	tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
 	if x.Type() == ssa.TypeF32 {
-		m.lowerConstantI32(tmpRegVReg, math.MinInt32)
-		setMSB.asMovToVec(tmpF, operandNR(tmpRegVReg), vecArrangementS, vecIndex(0))
+		m.lowerConstantI32(tmpReg, math.MinInt32)
+		setMSB.asMovToVec(tmpF, operandNR(tmpReg), vecArrangementS, vecIndex(0))
 	} else {
-		m.lowerConstantI64(tmpRegVReg, math.MinInt64)
-		setMSB.asMovToVec(tmpF, operandNR(tmpRegVReg), vecArrangementD, vecIndex(0))
+		m.lowerConstantI64(tmpReg, math.MinInt64)
+		setMSB.asMovToVec(tmpF, operandNR(tmpReg), vecArrangementD, vecIndex(0))
 	}
 	m.insert(setMSB)
 
@@ -459,15 +460,17 @@ func (m *machine) lowerFpuToInt(rd, rn operand, ctx regalloc.VReg, signed, src64
 	m.insert(cvt)
 
 	if !nonTrapping {
+		tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+
 		// After the conversion, check the FPU flags.
 		getFlag := m.allocateInstr()
-		getFlag.asMovFromFPSR(tmpRegVReg)
+		getFlag.asMovFromFPSR(tmpReg)
 		m.insert(getFlag)
 
 		// Check if the conversion was undefined by comparing the status with 1.
 		// See https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/FPSR--Floating-point-Status-Register
 		alu := m.allocateInstr()
-		alu.asALU(aluOpSubS, operandNR(xzrVReg), operandNR(tmpRegVReg), operandImm12(1, 0), true)
+		alu.asALU(aluOpSubS, operandNR(xzrVReg), operandNR(tmpReg), operandImm12(1, 0), true)
 		m.insert(alu)
 
 		// If it is not undefined, we can return the result.
@@ -700,11 +703,12 @@ func (m *machine) lowerCtz(x, result ssa.Value) {
 	rd := m.compiler.VRegOf(result)
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
 	rbit := m.allocateInstr()
-	rbit.asBitRR(bitOpRbit, tmpRegVReg, rn.nr(), x.Type().Bits() == 64)
+	tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	rbit.asBitRR(bitOpRbit, tmpReg, rn.nr(), x.Type().Bits() == 64)
 	m.insert(rbit)
 
 	clz := m.allocateInstr()
-	clz.asBitRR(bitOpClz, rd, tmpRegVReg, x.Type().Bits() == 64)
+	clz.asBitRR(bitOpClz, rd, tmpReg, x.Type().Bits() == 64)
 	m.insert(clz)
 }
 
@@ -752,25 +756,49 @@ func (m *machine) lowerPopcnt(x, result ssa.Value) {
 	m.insert(mov)
 }
 
-const exitWithCodeEncodingSize = exitSequenceSize + 8
-
 // lowerExitWithCode lowers the lowerExitWithCode takes a context pointer as argument.
 func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.ExitCode) {
+	tmpReg1 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
 	loadExitCodeConst := m.allocateInstr()
-	loadExitCodeConst.asMOVZ(tmpRegVReg, uint64(code), 0, true)
+	loadExitCodeConst.asMOVZ(tmpReg1, uint64(code), 0, true)
 
 	setExitCode := m.allocateInstr()
-	setExitCode.asStore(operandNR(tmpRegVReg),
+	setExitCode.asStore(operandNR(tmpReg1),
 		addressMode{
 			kind: addressModeKindRegUnsignedImm12,
 			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.ExitCodeOffset.I64(),
 		}, 32)
 
+	// In order to unwind the stack, we also need to push the current stack pointer:
+	tmp2 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	movSpToTmp := m.allocateInstr()
+	movSpToTmp.asMove64(tmp2, spVReg)
+	strSpToExecCtx := m.allocateInstr()
+	strSpToExecCtx.asStore(operandNR(tmp2),
+		addressMode{
+			kind: addressModeKindRegUnsignedImm12,
+			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.StackPointerBeforeGoCall.I64(),
+		}, 64)
+	// Also the address of this exit.
+	tmp3 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	currentAddrToTmp := m.allocateInstr()
+	currentAddrToTmp.asAdr(tmp3, 0)
+	storeCurrentAddrToExecCtx := m.allocateInstr()
+	storeCurrentAddrToExecCtx.asStore(operandNR(tmp3),
+		addressMode{
+			kind: addressModeKindRegUnsignedImm12,
+			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsets.GoCallReturnAddress.I64(),
+		}, 64)
+
 	exitSeq := m.allocateInstr()
 	exitSeq.asExitSequence(execCtxVReg)
 
 	m.insert(loadExitCodeConst)
 	m.insert(setExitCode)
+	m.insert(movSpToTmp)
+	m.insert(strSpToExecCtx)
+	m.insert(currentAddrToTmp)
+	m.insert(storeCurrentAddrToExecCtx)
 	m.insert(exitSeq)
 }
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine.go b/internal/engine/wazevo/backend/isa/arm64/machine.go
index add5f38a..2ebdcdd9 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine.go
@@ -468,7 +468,7 @@ func (m *machine) InsertReturn() {
 	m.insert(i)
 }
 
-func (m *machine) getVRegSpillSlotOffset(id regalloc.VRegID, size byte) int64 {
+func (m *machine) getVRegSpillSlotOffsetFromSP(id regalloc.VRegID, size byte) int64 {
 	offset, ok := m.spillSlots[id]
 	if !ok {
 		offset = m.spillSlotSize
@@ -476,7 +476,7 @@ func (m *machine) getVRegSpillSlotOffset(id regalloc.VRegID, size byte) int64 {
 		m.spillSlots[id] = offset
 		m.spillSlotSize += int64(size)
 	}
-	return offset
+	return offset + m.clobberedRegSlotSize() + 16 // spill slot starts above the clobbered registers and the frame size.
 }
 
 func (m *machine) clobberedRegSlotSize() int64 {
@@ -484,7 +484,9 @@ func (m *machine) clobberedRegSlotSize() int64 {
 }
 
 func (m *machine) arg0OffsetFromSP() int64 {
-	return m.spillSlotSize + m.clobberedRegSlotSize() + 16 /* 16-byte aligned return address */
+	return m.frameSize() +
+		16 + // 16-byte aligned return address
+		16 // frame size saved below the clobbered registers.
 }
 
 func (m *machine) ret0OffsetFromSP() int64 {
@@ -493,7 +495,15 @@ func (m *machine) ret0OffsetFromSP() int64 {
 
 func (m *machine) requiredStackSize() int64 {
 	return m.maxRequiredStackSizeForCalls +
-		m.clobberedRegSlotSize() +
-		m.spillSlotSize +
-		16 // 16-byte aligned return address.
+		m.frameSize() +
+		16 + // 16-byte aligned return address.
+		16 // frame size saved below the clobbered registers.
+}
+
+func (m *machine) frameSize() int64 {
+	s := m.clobberedRegSlotSize() + m.spillSlotSize
+	if s&0xf != 0 {
+		panic(fmt.Errorf("BUG: frame size %d is not 16-byte aligned", s))
+	}
+	return s
 }
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 da35792c..f6340674 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
@@ -16,25 +16,21 @@ func (m *machine) SetupPrologue() {
 	//                   (high address)                    (high address)
 	//                 +-----------------+               +------------------+
 	//                 |     .......     |               |     .......      |
-	//                 |      ret Y      |               |      ret Y       |
-	//                 |     .......     |               |     .......      |
-	//                 |      ret 0      |               |      ret 0       |
-	//                 |      arg X      |               |      arg X       |
-	//                 |     .......     |     ====>     |     .......      |
-	//                 |      arg 1      |               |      arg 1       |
-	//                 |      arg 0      |               |      arg 0       |
-	//         SP----> |-----------------|               |      xxxxx       |
-	//                                                   |   ret address    |
+	//                 |      ret Y      |               |      ret Y       | <----+
+	//                 |     .......     |               |     .......      |      |
+	//                 |      ret 0      |               |      ret 0       |      |
+	//                 |      arg X      |               |      arg X       |      |  size_of_arg_ret.
+	//                 |     .......     |     ====>     |     .......      |      |
+	//                 |      arg 1      |               |      arg 1       |      |
+	//                 |      arg 0      |               |      arg 0       | <----+
+	//         SP----> |-----------------|               |  size_of_arg_ret |
+	//                                                   |  return address  |
 	//                                                   +------------------+ <---- SP
 	//                    (low address)                     (low address)
 
-	// Saves the return address (lr) and below the SP.
-	str := m.allocateInstrAfterLowering()
-	amode := addressModePreOrPostIndex(spVReg, -16 /* stack pointer must be 16-byte aligned. */, true /* decrement before store */)
-	str.asStore(operandNR(lrVReg), amode, 64)
-	cur.next = str
-	str.prev = cur
-	cur = str
+	// Saves the return address (lr) and the size_of_arg_ret below the SP.
+	// size_of_arg_ret is used for stack unwinding.
+	cur = m.createReturnAddrAndSizeOfArgRetSlot(cur)
 
 	if !m.stackBoundsCheckDisabled {
 		cur = m.insertStackBoundsCheck(m.requiredStackSize(), cur)
@@ -65,7 +61,7 @@ func (m *machine) SetupPrologue() {
 		//          |     .......      |
 		//          |      arg 1       |
 		//          |      arg 0       |
-		//          |      xxxxx       |
+		//          |  size_of_arg_ret |
 		//          |   ReturnAddress  |
 		//          +------------------+
 		//          |   spill slot M   |
@@ -88,7 +84,7 @@ func (m *machine) SetupPrologue() {
 		//          |     .......     |             |     .......     |
 		//          |      arg 1      |             |      arg 1      |
 		//          |      arg 0      |             |      arg 0      |
-		//          |      xxxxx      |             |      xxxxx      |
+		//          | size_of_arg_ret |             | size_of_arg_ret |
 		//          |   ReturnAddress |             |  ReturnAddress  |
 		//          +-----------------+    ====>    +-----------------+
 		//          |   ...........   |             |   ...........   |
@@ -101,7 +97,7 @@ func (m *machine) SetupPrologue() {
 		//                                          |   ............  |
 		//                                          |   clobbered 0   |
 		//                                          +-----------------+
-		//                                              (high address)
+		//                                             (low address)
 		//
 		_amode := addressModePreOrPostIndex(spVReg,
 			-16,  // stack pointer must be 16-byte aligned.
@@ -111,15 +107,78 @@ func (m *machine) SetupPrologue() {
 			// TODO: pair stores to reduce the number of instructions.
 			store := m.allocateInstrAfterLowering()
 			store.asStore(operandNR(vr), _amode, regTypeToRegisterSizeInBits(vr.RegType()))
-			cur.next = store
-			store.prev = cur
-			cur = store
+			cur = linkInstr(cur, store)
 		}
 	}
 
-	cur.next = prevInitInst
+	// We push the frame size into the stack to make it possible to unwind stack:
+	//
+	//
+	//            (high address)                  (high address)
+	//         +-----------------+                +-----------------+
+	//         |     .......     |                |     .......     |
+	//         |      ret Y      |                |      ret Y      |
+	//         |     .......     |                |     .......     |
+	//         |      ret 0      |                |      ret 0      |
+	//         |      arg X      |                |      arg X      |
+	//         |     .......     |                |     .......     |
+	//         |      arg 1      |                |      arg 1      |
+	//         |      arg 0      |                |      arg 0      |
+	//         | size_of_arg_ret |                | size_of_arg_ret |
+	//         |  ReturnAddress  |                |  ReturnAddress  |
+	//         +-----------------+      ==>       +-----------------+ <----+
+	//         |   ...........   |                |   ...........   |      |
+	//         |   spill slot M  |                |   spill slot M  |      |
+	//         |   ............  |                |   ............  |      |
+	//         |   spill slot 2  |                |   spill slot 2  |      |
+	//         |   spill slot 1  |                |   spill slot 1  |      | frame size
+	//         |   spill slot 1  |                |   spill slot 1  |      |
+	//         |   clobbered N   |                |   clobbered N   |      |
+	//         |   ............  |                |   ............  |      |
+	//         |   clobbered 0   |                |   clobbered 0   | <----+
+	// SP--->  +-----------------+                |     xxxxxx      |  ;; unused space to make it 16-byte aligned.
+	//                                            |   frame_size    |
+	//                                            +-----------------+ <---- SP
+	//            (low address)
+	//
+	cur = m.createFrameSizeSlot(cur)
+
+	linkInstr(cur, prevInitInst)
+}
+
+func (m *machine) createReturnAddrAndSizeOfArgRetSlot(cur *instruction) *instruction {
+	// Saves the return address (lr) and the size_of_arg_ret below the SP.
+	// size_of_arg_ret is used for stack unwinding.
+	var sizeOfArgRetReg regalloc.VReg
+	if s := m.currentABI.alignedArgResultStackSlotSize(); s > 0 {
+		cur = m.lowerConstantI64AndInsert(cur, tmpRegVReg, s)
+		sizeOfArgRetReg = tmpRegVReg
+	} else {
+		sizeOfArgRetReg = xzrVReg
+	}
+	pstr := m.allocateInstrAfterLowering()
+	amode := addressModePreOrPostIndex(spVReg, -16, true /* decrement before store */)
+	pstr.asStorePair64(lrVReg, sizeOfArgRetReg, amode)
+	cur = linkInstr(cur, pstr)
+	return cur
+}
 
-	prevInitInst.prev = cur
+func (m *machine) createFrameSizeSlot(cur *instruction) *instruction {
+	var frameSizeReg regalloc.VReg
+	if s := m.frameSize(); s > 0 {
+		cur = m.lowerConstantI64AndInsert(cur, tmpRegVReg, s)
+		frameSizeReg = tmpRegVReg
+	} else {
+		frameSizeReg = xzrVReg
+	}
+	_amode := addressModePreOrPostIndex(spVReg,
+		-16,  // stack pointer must be 16-byte aligned.
+		true, // Decrement before store.
+	)
+	store := m.allocateInstrAfterLowering()
+	store.asStore(operandNR(frameSizeReg), _amode, 64)
+	cur = linkInstr(cur, store)
+	return cur
 }
 
 // SetupEpilogue implements backend.Machine.
@@ -146,6 +205,9 @@ func (m *machine) SetupEpilogue() {
 func (m *machine) setupEpilogueAfter(cur *instruction) {
 	prevNext := cur.next
 
+	// We've stored the frame size in the prologue, and now that we are about to return from this function, we won't need it anymore.
+	cur = m.addsAddOrSubStackPointer(cur, spVReg, 16, true, true)
+
 	// First we need to restore the clobbered registers.
 	if len(m.clobberedRegs) > 0 {
 		//            (high address)
@@ -187,9 +249,7 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
 			case 128: // save vector reg.
 				load.asFpuLoad(operandNR(vr), amode, 128)
 			}
-			cur.next = load
-			load.prev = cur
-			cur = load
+			cur = linkInstr(cur, load)
 		}
 	}
 
@@ -237,11 +297,9 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
 	ldr := m.allocateInstrAfterLowering()
 	amode := addressModePreOrPostIndex(spVReg, 16 /* stack pointer must be 16-byte aligned. */, false /* increment after loads */)
 	ldr.asULoad(operandNR(lrVReg), amode, 64)
-	cur.next = ldr
-	ldr.prev = cur
+	cur = linkInstr(cur, ldr)
 
-	ldr.next = prevNext
-	prevNext.prev = ldr
+	linkInstr(cur, prevNext)
 }
 
 // saveRequiredRegs is the set of registers that must be saved/restored during growing stack when there's insufficient
@@ -287,23 +345,17 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 		rn:   x0VReg, // execution context is always the first argument.
 		imm:  wazevoapi.ExecutionContextOffsets.StackBottomPtr.I64(),
 	}, 64)
-	ldr.prev = cur
-	cur.next = ldr
-	cur = ldr
+	cur = linkInstr(cur, ldr)
 
 	// subs xzr, tmp, tmp2
 	subs := m.allocateInstrAfterLowering()
 	subs.asALU(aluOpSubS, operandNR(xzrVReg), operandNR(tmpRegVReg), operandNR(tmp2), true)
-	subs.prev = cur
-	cur.next = subs
-	cur = subs
+	cur = linkInstr(cur, subs)
 
 	// b.ge #imm
 	cbr := m.allocateInstr()
 	cbr.asCondBr(ge.asCond(), invalidLabel, false /* ignored */)
-	cbr.prev = cur
-	cur.next = cbr
-	cur = cbr
+	cur = linkInstr(cur, cbr)
 
 	// Set the required stack size and set it to the exec context.
 	{
@@ -316,9 +368,8 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 				// Execution context is always the first argument.
 				rn: x0VReg, imm: wazevoapi.ExecutionContextOffsets.StackGrowRequiredSize.I64(),
 			}, 64)
-		setRequiredStackSize.prev = cur
-		cur.next = setRequiredStackSize
-		cur = setRequiredStackSize
+
+		cur = linkInstr(cur, setRequiredStackSize)
 	}
 
 	ldrAddress := m.allocateInstr()
@@ -327,17 +378,13 @@ func (m *machine) insertStackBoundsCheck(requiredStackSize int64, cur *instructi
 		rn:   x0VReg, // execution context is always the first argument
 		imm:  wazevoapi.ExecutionContextOffsets.StackGrowCallSequenceAddress.I64(),
 	}, 64)
-	ldrAddress.prev = cur
-	cur.next = ldrAddress
-	cur = ldrAddress
+	cur = linkInstr(cur, ldrAddress)
 
 	// Then jumps to the stack grow call sequence's address, meaning
 	// transferring the control to the code compiled by CompileStackGrowCallSequence.
 	bl := m.allocateInstr()
 	bl.asCallIndirect(tmpRegVReg, nil)
-	bl.prev = cur
-	cur.next = bl
-	cur = bl
+	cur = linkInstr(cur, bl)
 
 	// Now that we know the entire code, we can finalize how many bytes
 	// we have to skip when the stack size is sufficient.
@@ -376,9 +423,7 @@ func (m *machine) CompileStackGrowCallSequence() []byte {
 	// Then goes back the original address of this stack grow call.
 	ret := m.allocateInstr()
 	ret.asRet(nil)
-	ret.prev = cur
-	cur.next = ret
-	cur = ret
+	linkInstr(cur, ret)
 
 	m.encode(m.rootInstr)
 	return m.compiler.Buf()
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
index 68993bf2..51047034 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
@@ -285,7 +285,7 @@ func (m *machine) insertStoreRegisterAt(v regalloc.VReg, instr *instruction, aft
 		cur, prevNext = instr.prev, instr
 	}
 
-	offsetFromSP := m.getVRegSpillSlotOffset(v.ID(), typ.Size()) + m.clobberedRegSlotSize()
+	offsetFromSP := m.getVRegSpillSlotOffsetFromSP(v.ID(), typ.Size())
 	var amode addressMode
 	cur, amode = m.resolveAddressModeForOffsetAndInsert(cur, offsetFromSP, typ.Bits(), spVReg)
 	store := m.allocateInstrAfterLowering()
@@ -309,7 +309,7 @@ func (m *machine) insertReloadRegisterAt(v regalloc.VReg, instr *instruction, af
 		cur, prevNext = instr.prev, instr
 	}
 
-	offsetFromSP := m.getVRegSpillSlotOffset(v.ID(), typ.Size()) + m.clobberedRegSlotSize()
+	offsetFromSP := m.getVRegSpillSlotOffsetFromSP(v.ID(), typ.Size())
 	var amode addressMode
 	cur, amode = m.resolveAddressModeForOffsetAndInsert(cur, offsetFromSP, typ.Bits(), spVReg)
 	load := m.allocateInstrAfterLowering()
diff --git a/internal/engine/wazevo/backend/isa/arm64/reg.go b/internal/engine/wazevo/backend/isa/arm64/reg.go
index 9f0ee1cb..81e6edc4 100644
--- a/internal/engine/wazevo/backend/isa/arm64/reg.go
+++ b/internal/engine/wazevo/backend/isa/arm64/reg.go
@@ -158,6 +158,7 @@ var (
 	// lr (link register) holds the return address at the function entry.
 	lrVReg = x30VReg
 	// tmpReg is used to perform spill/load on large stack offsets, and load large constants.
+	// Therefore, be cautious to use this register in the middle of the compilation, especially before the register allocation.
 	// This is the same as golang/go, but it's only described in the source code:
 	// https://github.com/golang/go/blob/18e17e2cb12837ea2c8582ecdb0cc780f49a1aac/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go#L59
 	// https://github.com/golang/go/blob/18e17e2cb12837ea2c8582ecdb0cc780f49a1aac/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go#L13-L15
diff --git a/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
new file mode 100644
index 00000000..fff36ae3
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go
@@ -0,0 +1,61 @@
+package arm64
+
+import (
+	"encoding/binary"
+	"reflect"
+	"unsafe"
+)
+
+// UnwindStack is a function to unwind the stack.
+// The implementation must be aligned with the ABI/Calling convention as in machine_pro_epi_logue.go/abi.go.
+func UnwindStack(sp, top uintptr) (returnAddresses []uintptr) {
+	l := int(top - sp)
+
+	var stackBuf []byte
+	{
+		// TODO: use unsafe.Slice after floor version is set to Go 1.20.
+		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&stackBuf))
+		hdr.Data = sp
+		hdr.Len = l
+		hdr.Cap = l
+	}
+
+	for i := uint64(0); i < uint64(l); {
+		//       (high address)
+		//    +-----------------+
+		//    |     .......     |
+		//    |      ret Y      |  <----+
+		//    |     .......     |       |
+		//    |      ret 0      |       |
+		//    |      arg X      |       |  size_of_arg_ret
+		//    |     .......     |       |
+		//    |      arg 1      |       |
+		//    |      arg 0      |  <----+
+		//    | size_of_arg_ret |
+		//    |  ReturnAddress  |
+		//    +-----------------+ <----+
+		//    |   ...........   |      |
+		//    |   spill slot M  |      |
+		//    |   ............  |      |
+		//    |   spill slot 2  |      |
+		//    |   spill slot 1  |      | frame size
+		//    |   spill slot 1  |      |
+		//    |   clobbered N   |      |
+		//    |   ............  |      |
+		//    |   clobbered 0   | <----+
+		//    |     xxxxxx      |  ;; unused space to make it 16-byte aligned.
+		//    |   frame_size    |
+		//    +-----------------+ <---- SP
+		//       (low address)
+
+		frameSize := binary.LittleEndian.Uint64(stackBuf[i:])
+		i += frameSize +
+			16 // frame size + aligned space.
+		retAddr := binary.LittleEndian.Uint64(stackBuf[i:])
+		i += 8 // ret addr.
+		sizeOfArgRet := binary.LittleEndian.Uint64(stackBuf[i:])
+		i += 8 + sizeOfArgRet
+		returnAddresses = append(returnAddresses, uintptr(retAddr))
+	}
+	return
+}
diff --git a/internal/engine/wazevo/call_engine.go b/internal/engine/wazevo/call_engine.go
index 3d9fc869..958bc920 100644
--- a/internal/engine/wazevo/call_engine.go
+++ b/internal/engine/wazevo/call_engine.go
@@ -10,6 +10,7 @@ import (
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
 	"github.com/tetratelabs/wazero/internal/internalapi"
 	"github.com/tetratelabs/wazero/internal/wasm"
+	"github.com/tetratelabs/wazero/internal/wasmdebug"
 	"github.com/tetratelabs/wazero/internal/wasmruntime"
 )
 
@@ -110,13 +111,38 @@ func (c *callEngine) Call(ctx context.Context, params ...uint64) ([]uint64, erro
 	return paramResultSlice[:c.numberOfResults], nil
 }
 
+func (c *callEngine) addFrame(builder wasmdebug.ErrorBuilder, addr uintptr) {
+	eng := c.parent.parent.parent
+	cm := eng.compiledModuleOfAddr(addr)
+	if cm != nil {
+		index := cm.functionIndexOf(addr)
+		def := cm.module.FunctionDefinition(cm.module.ImportFunctionCount + index)
+		// TODO: DWARF.
+		builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), nil)
+	}
+}
+
 // CallWithStack implements api.Function.
-func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint64) error {
+func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint64) (err error) {
 	var paramResultPtr *uint64
 	if len(paramResultStack) > 0 {
 		paramResultPtr = &paramResultStack[0]
 	}
 
+	defer func() {
+		if r := recover(); r != nil {
+			builder := wasmdebug.NewErrorBuilder()
+			c.addFrame(builder, uintptr(unsafe.Pointer(c.execCtx.goCallReturnAddress)))
+			returnAddrs := unwindStack(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)
+			}
+			err = builder.FromRecovered(r)
+
+			// TODO: Abort listener.
+		}
+	}()
+
 	entrypoint(c.executable, c.execCtxPtr, c.parent.opaquePtr, paramResultPtr, c.stackTop)
 	for {
 		switch ec := c.execCtx.exitCode; ec & wazevoapi.ExitCodeMask {
@@ -129,10 +155,6 @@ func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint6
 			}
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, newsp)
-		case wazevoapi.ExitCodeUnreachable:
-			return wasmruntime.ErrRuntimeUnreachable
-		case wazevoapi.ExitCodeMemoryOutOfBounds:
-			return wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess
 		case wazevoapi.ExitCodeGrowMemory:
 			mod := c.callerModuleInstance()
 			mem := mod.MemoryInstance
@@ -166,18 +188,22 @@ func (c *callEngine) CallWithStack(ctx context.Context, paramResultStack []uint6
 			f.Call(ctx, mod, c.execCtx.goFunctionCallStack[:])
 			c.execCtx.exitCode = wazevoapi.ExitCodeOK
 			afterGoFunctionCallEntrypoint(c.execCtx.goCallReturnAddress, c.execCtxPtr, c.execCtx.stackPointerBeforeGoCall)
+		case wazevoapi.ExitCodeUnreachable:
+			panic(wasmruntime.ErrRuntimeUnreachable)
+		case wazevoapi.ExitCodeMemoryOutOfBounds:
+			panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
 		case wazevoapi.ExitCodeTableOutOfBounds:
-			return wasmruntime.ErrRuntimeInvalidTableAccess
+			panic(wasmruntime.ErrRuntimeInvalidTableAccess)
 		case wazevoapi.ExitCodeIndirectCallNullPointer:
-			return wasmruntime.ErrRuntimeInvalidTableAccess
+			panic(wasmruntime.ErrRuntimeInvalidTableAccess)
 		case wazevoapi.ExitCodeIndirectCallTypeMismatch:
-			return wasmruntime.ErrRuntimeIndirectCallTypeMismatch
+			panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
 		case wazevoapi.ExitCodeIntegerOverflow:
-			return wasmruntime.ErrRuntimeIntegerOverflow
+			panic(wasmruntime.ErrRuntimeIntegerOverflow)
 		case wazevoapi.ExitCodeIntegerDivisionByZero:
-			return wasmruntime.ErrRuntimeIntegerDivideByZero
+			panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
 		case wazevoapi.ExitCodeInvalidConversionToInteger:
-			return wasmruntime.ErrRuntimeInvalidConversionToInteger
+			panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
 		default:
 			panic("BUG")
 		}
diff --git a/internal/engine/wazevo/engine.go b/internal/engine/wazevo/engine.go
index c48ed2a7..e0cb4a34 100644
--- a/internal/engine/wazevo/engine.go
+++ b/internal/engine/wazevo/engine.go
@@ -5,7 +5,9 @@ import (
 	"encoding/hex"
 	"fmt"
 	"runtime"
+	"sort"
 	"sync"
+	"unsafe"
 
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/experimental"
@@ -21,9 +23,13 @@ import (
 type (
 	// engine implements wasm.Engine.
 	engine struct {
-		compiledModules   map[wasm.ModuleID]*compiledModule
-		mux               sync.RWMutex
-		rels              []backend.RelocationInfo
+		compiledModules map[wasm.ModuleID]*compiledModule
+		// sortedCompiledModules is a list of compiled modules sorted by the initial address of the executable.
+		sortedCompiledModules []*compiledModule
+		mux                   sync.RWMutex
+		// rels is a list of relocations to be resolved. This is reused for each compilation to avoid allocation.
+		rels []backend.RelocationInfo
+		// refToBinaryOffset is reused for each compilation to avoid allocation.
 		refToBinaryOffset map[ssa.FuncRef]int
 		// builtinFunctions is hods compiled builtin function trampolines.
 		builtinFunctions *builtinFunctions
@@ -40,8 +46,11 @@ type (
 
 	// compiledModule is a compiled variant of a wasm.Module and ready to be used for instantiation.
 	compiledModule struct {
-		executable      []byte
+		executable []byte
+		// functionOffsets maps a local function index to compiledFunctionOffset.
 		functionOffsets []compiledFunctionOffset
+		parent          *engine
+		module          *wasm.Module
 
 		// The followings are only available for non host modules.
 
@@ -100,7 +109,7 @@ func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listene
 
 func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) (*compiledModule, error) {
 	e.rels = e.rels[:0]
-	cm := &compiledModule{offsets: wazevoapi.NewModuleContextOffsetData(module)}
+	cm := &compiledModule{offsets: wazevoapi.NewModuleContextOffsetData(module), parent: e, module: module}
 
 	if module.IsHostModule {
 		return e.compileHostModule(ctx, module)
@@ -286,7 +295,7 @@ func (e *engine) compileHostModule(ctx context.Context, module *wasm.Module) (*c
 	be := backend.NewCompiler(ctx, machine, ssa.NewBuilder())
 
 	num := len(module.CodeSection)
-	cm := &compiledModule{}
+	cm := &compiledModule{module: module}
 	cm.functionOffsets = make([]compiledFunctionOffset, num)
 	totalSize := 0 // Total binary size of the executable.
 	bodies := make([][]byte, num)
@@ -379,6 +388,7 @@ func (e *engine) Close() (err error) {
 		cm.executable = nil
 		cm.functionOffsets = nil
 	}
+	e.sortedCompiledModules = nil
 	e.compiledModules = nil
 	e.builtinFunctions = nil
 	return nil
@@ -395,13 +405,58 @@ func (e *engine) CompiledModuleCount() uint32 {
 func (e *engine) DeleteCompiledModule(m *wasm.Module) {
 	e.mux.Lock()
 	defer e.mux.Unlock()
-	delete(e.compiledModules, m.ID)
+	cm, ok := e.compiledModules[m.ID]
+	if ok {
+		cm.parent = nil
+		if len(cm.executable) > 0 {
+			e.deleteCompiledModuleFromSortedList(cm)
+		}
+		delete(e.compiledModules, m.ID)
+	}
 }
 
 func (e *engine) addCompiledModule(m *wasm.Module, cm *compiledModule) {
 	e.mux.Lock()
 	defer e.mux.Unlock()
 	e.compiledModules[m.ID] = cm
+	if len(cm.executable) > 0 {
+		e.addCompiledModuleToSortedList(cm)
+	}
+}
+
+func (e *engine) addCompiledModuleToSortedList(cm *compiledModule) {
+	ptr := uintptr(unsafe.Pointer(&cm.executable[0]))
+
+	index := sort.Search(len(e.sortedCompiledModules), func(i int) bool {
+		return uintptr(unsafe.Pointer(&e.sortedCompiledModules[i].executable[0])) >= ptr
+	})
+	e.sortedCompiledModules = append(e.sortedCompiledModules, nil)
+	copy(e.sortedCompiledModules[index+1:], e.sortedCompiledModules[index:])
+	e.sortedCompiledModules[index] = cm
+}
+
+func (e *engine) deleteCompiledModuleFromSortedList(cm *compiledModule) {
+	ptr := uintptr(unsafe.Pointer(&cm.executable[0]))
+
+	index := sort.Search(len(e.sortedCompiledModules), func(i int) bool {
+		return uintptr(unsafe.Pointer(&e.sortedCompiledModules[i].executable[0])) >= ptr
+	})
+	copy(e.sortedCompiledModules[index:], e.sortedCompiledModules[index+1:])
+	e.sortedCompiledModules = e.sortedCompiledModules[:len(e.sortedCompiledModules)-1]
+}
+
+func (e *engine) compiledModuleOfAddr(addr uintptr) *compiledModule {
+	e.mux.RLock()
+	defer e.mux.RUnlock()
+
+	index := sort.Search(len(e.sortedCompiledModules), func(i int) bool {
+		return uintptr(unsafe.Pointer(&e.sortedCompiledModules[i].executable[0])) > addr
+	})
+	index -= 1
+	if index < 0 {
+		return nil
+	}
+	return e.sortedCompiledModules[index]
 }
 
 // NewModuleEngine implements wasm.Engine.
@@ -492,3 +547,16 @@ func mmapExecutable(src []byte) []byte {
 	}
 	return executable
 }
+
+func (cm *compiledModule) functionIndexOf(addr uintptr) wasm.Index {
+	addr -= uintptr(unsafe.Pointer(&cm.executable[0]))
+	offset := cm.functionOffsets
+	index := sort.Search(len(offset), func(i int) bool {
+		return offset[i].offset > int(addr)
+	})
+	index -= 1
+	if index < 0 {
+		panic("BUG")
+	}
+	return wasm.Index(index)
+}
diff --git a/internal/engine/wazevo/machine.go b/internal/engine/wazevo/machine.go
index ffc4b84a..08bb7339 100644
--- a/internal/engine/wazevo/machine.go
+++ b/internal/engine/wazevo/machine.go
@@ -15,3 +15,12 @@ func newMachine() backend.Machine {
 		panic("unsupported architecture")
 	}
 }
+
+func unwindStack(sp, top uintptr) (returnAddresses []uintptr) {
+	switch runtime.GOARCH {
+	case "arm64":
+		return arm64.UnwindStack(sp, top)
+	default:
+		panic("unsupported architecture")
+	}
+}
diff --git a/internal/engine/wazevo/wazevoapi/offsetdata.go b/internal/engine/wazevo/wazevoapi/offsetdata.go
index c37417ed..fb70d082 100644
--- a/internal/engine/wazevo/wazevoapi/offsetdata.go
+++ b/internal/engine/wazevo/wazevoapi/offsetdata.go
@@ -21,7 +21,7 @@ var ExecutionContextOffsets = ExecutionContextOffsetData{
 	GoReturnAddress:                         32,
 	StackBottomPtr:                          40,
 	GoCallReturnAddress:                     48,
-	StackPointerBeforeGrow:                  56,
+	StackPointerBeforeGoCall:                56,
 	StackGrowRequiredSize:                   64,
 	MemoryGrowTrampolineAddress:             72,
 	StackGrowCallSequenceAddress:            80,
@@ -47,8 +47,8 @@ type ExecutionContextOffsetData struct {
 	StackBottomPtr Offset
 	// GoCallReturnAddress is an offset of `goCallReturnAddress` field in wazevo.executionContext
 	GoCallReturnAddress Offset
-	// GoCallReturnAddress is an offset of `stackPointerBeforeGoCall` field in wazevo.executionContext
-	StackPointerBeforeGrow 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


Test patch:
diff --git a/internal/engine/wazevo/backend/backend_test.go b/internal/engine/wazevo/backend/backend_test.go
index 248e803a..c0d471a3 100644
--- a/internal/engine/wazevo/backend/backend_test.go
+++ b/internal/engine/wazevo/backend/backend_test.go
@@ -54,7 +54,9 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -63,7 +65,8 @@ L1 (SSA Block: blk0):
 			name: "selects", m: testcases.Selects.Module,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	subs xzr, x4, x5
 	csel w0, w2, w3, eq
 	subs wzr, w3, wzr
@@ -73,6 +76,7 @@ L1 (SSA Block: blk0):
 	fcmp s0, s1
 	fcsel d1, d2, d3, ne
 	mov v0.8b, v8.8b
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -93,11 +97,13 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	ldr d1, #8; b 16; data.f64 64.000000
 	ldr s0, #8; b 8; data.f32 32.000000
 	orr x1, xzr, #0x2
 	orr w0, wzr, #0x1
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -115,9 +121,11 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	add w8, w2, w3
 	sub w0, w8, w2
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -150,7 +158,8 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	add x8, x2, x2
 	sub x0, x8, x2
 	fadd s8, s0, s0
@@ -165,6 +174,7 @@ L1 (SSA Block: blk0):
 	fdiv d8, d8, d1
 	fmax d8, d8, d1
 	fmin d1, d8, d1
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -181,9 +191,11 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x1, xzr
 	mov x0, x2
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -200,9 +212,11 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x1, x2
 	mov x0, x3
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -220,10 +234,12 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	mov x1, x2
 	mov x0, x3
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -237,8 +253,10 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -253,21 +271,31 @@ L1 (SSA Block: blk0):
 L3 (SSA Block: blk1):
 	ret
 L2 (SSA Block: blk2):
-	movz x27, #0x3, lsl 0
-	str w27, [x0?]
+	movz x4?, #0x3, lsl 0
+	str w4?, [x0?]
+	mov x5?, sp
+	str x5?, [x0?, #0x38]
+	adr x6?, #0x0
+	str x6?, [x0?, #0x30]
 	exit_sequence x0?
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, xzr
-	cbz w8, #0xc L2
+	cbz w8, #0x10 L2
 L3 (SSA Block: blk1):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L2 (SSA Block: blk2):
-	movz x27, #0x3, lsl 0
-	str w27, [x0]
+	movz x8, #0x3, lsl 0
+	str w8, [x0]
+	mov x8, sp
+	str x8, [x0, #0x38]
+	adr x8, #0x0
+	str x8, [x0, #0x30]
 	exit_sequence x0
 `,
 		},
@@ -280,7 +308,8 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	b #0x0 (L2)
 `,
@@ -302,7 +331,8 @@ L5 (SSA Block: blk2):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	orr w8, wzr, #0x1
 	cbz w8, #0x8 L3
@@ -311,6 +341,7 @@ L4 (SSA Block: blk4):
 L3 (SSA Block: blk3):
 L5 (SSA Block: blk2):
 	mov x0, x2
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -329,13 +360,15 @@ L3 (SSA Block: blk3):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	orr w8, wzr, #0x1
 	cbz w8, #0x8 L3
 L4 (SSA Block: blk4):
 	b #-0x8 (L2)
 L3 (SSA Block: blk3):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -349,8 +382,10 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -372,13 +407,15 @@ L4 (SSA Block: blk3):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, xzr
 	cbnz w8, #0x8 (L2)
 L3 (SSA Block: blk2):
 	b #0x4 (L4)
 L2 (SSA Block: blk1):
 L4 (SSA Block: blk3):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -397,14 +434,17 @@ L4 (SSA Block: blk3):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, xzr
-	cbnz w8, #0xc (L2)
+	cbnz w8, #0x10 (L2)
 L3 (SSA Block: blk2):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L2 (SSA Block: blk1):
 L4 (SSA Block: blk3):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -427,16 +467,19 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, xzr
-	cbnz w8, #0x10 (L2)
+	cbnz w8, #0x14 (L2)
 L3 (SSA Block: blk2):
 L4 (SSA Block: blk3):
 	mov x0, xzr
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L2 (SSA Block: blk1):
 	mov x0, xzr
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -459,7 +502,8 @@ L4 (SSA Block: blk3):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	cbnz w2, #0xc (L2)
 L3 (SSA Block: blk2):
 	mov x0, x3
@@ -467,6 +511,7 @@ L3 (SSA Block: blk2):
 L2 (SSA Block: blk1):
 	mov x0, x2
 L4 (SSA Block: blk3):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -482,9 +527,11 @@ L2 (SSA Block: blk1):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	mov x0, x2
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -507,7 +554,8 @@ L6 (SSA Block: blk2):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 L2 (SSA Block: blk1):
 	cbz w2, #0x8 L3
 L4 (SSA Block: blk5):
@@ -516,6 +564,7 @@ L3 (SSA Block: blk4):
 L5 (SSA Block: blk3):
 L6 (SSA Block: blk2):
 	mov x0, xzr
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -539,18 +588,20 @@ L5 (SSA Block: blk3):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, x2
 L2 (SSA Block: blk1):
 	cbnz w8, #0x8 (L4)
-	b #0xc (L3)
+	b #0x10 (L3)
 L4 (SSA Block: blk5):
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L3 (SSA Block: blk4):
 L5 (SSA Block: blk3):
 	orr w8, wzr, #0x1
-	b #-0x14 (L2)
+	b #-0x18 (L2)
 `,
 		},
 		{
@@ -585,34 +636,37 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	sub sp, sp, #0x10
+	orr x27, xzr, #0x10
+	str x27, [sp, #-0x10]!
 	mov x9, x0
 	mov x8, x1
 	str x8, [x9, #0x8]
 	mov x0, x9
 	mov x1, x8
-	str x9, [sp]
-	str x8, [sp, #0x8]
+	str x9, [sp, #0x10]
+	str x8, [sp, #0x18]
 	bl f1
-	ldr x8, [sp, #0x8]
-	ldr x9, [sp]
+	ldr x8, [sp, #0x18]
+	ldr x9, [sp, #0x10]
 	mov x2, x0
 	str x8, [x9, #0x8]
 	mov x0, x9
 	mov x1, x8
 	movz w3, #0x5, lsl 0
-	str x9, [sp]
-	str x8, [sp, #0x8]
+	str x9, [sp, #0x10]
+	str x8, [sp, #0x18]
 	bl f2
-	ldr x8, [sp, #0x8]
-	ldr x9, [sp]
+	ldr x8, [sp, #0x18]
+	ldr x9, [sp, #0x10]
 	mov x2, x0
 	str x8, [x9, #0x8]
 	mov x0, x9
 	mov x1, x8
 	bl f3
 	add sp, sp, #0x10
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -677,7 +731,8 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, x2
 	mov x9, x3
 	mov v8.8b, v0.8b
@@ -726,6 +781,7 @@ L1 (SSA Block: blk0):
 	str d9, [sp, #0xc8]
 	bl f1
 	add sp, sp, #0xd0
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -834,10 +890,13 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0xc0
+	stp x30, x27, [sp, #-0x10]!
 	str x19, [sp, #-0x10]!
 	str q18, [sp, #-0x10]!
 	str q19, [sp, #-0x10]!
+	orr x27, xzr, #0x30
+	str x27, [sp, #-0x10]!
 	str x1, [x0, #0x8]
 	sub sp, sp, #0xc0
 	bl f1
@@ -866,30 +925,31 @@ L1 (SSA Block: blk0):
 	ldr s9, [sp, #0xb0]
 	ldr d8, [sp, #0xb8]
 	add sp, sp, #0xc0
-	str d8, [sp, #0xf8]
-	str s9, [sp, #0xf0]
-	str x8, [sp, #0xe8]
-	str w9, [sp, #0xe0]
-	str d10, [sp, #0xd8]
-	str s11, [sp, #0xd0]
-	str x10, [sp, #0xc8]
-	str w11, [sp, #0xc0]
-	str d12, [sp, #0xb8]
-	str s13, [sp, #0xb0]
-	str x12, [sp, #0xa8]
-	str w13, [sp, #0xa0]
-	str d14, [sp, #0x98]
-	str s15, [sp, #0x90]
-	str x14, [sp, #0x88]
-	str w15, [sp, #0x80]
-	str d16, [sp, #0x78]
-	str s17, [sp, #0x70]
-	str x16, [sp, #0x68]
-	str w17, [sp, #0x60]
-	str d18, [sp, #0x58]
-	str s19, [sp, #0x50]
-	str x18, [sp, #0x48]
-	str w19, [sp, #0x40]
+	str d8, [sp, #0x108]
+	str s9, [sp, #0x100]
+	str x8, [sp, #0xf8]
+	str w9, [sp, #0xf0]
+	str d10, [sp, #0xe8]
+	str s11, [sp, #0xe0]
+	str x10, [sp, #0xd8]
+	str w11, [sp, #0xd0]
+	str d12, [sp, #0xc8]
+	str s13, [sp, #0xc0]
+	str x12, [sp, #0xb8]
+	str w13, [sp, #0xb0]
+	str d14, [sp, #0xa8]
+	str s15, [sp, #0xa0]
+	str x14, [sp, #0x98]
+	str w15, [sp, #0x90]
+	str d16, [sp, #0x88]
+	str s17, [sp, #0x80]
+	str x16, [sp, #0x78]
+	str w17, [sp, #0x70]
+	str d18, [sp, #0x68]
+	str s19, [sp, #0x60]
+	str x18, [sp, #0x58]
+	str w19, [sp, #0x50]
+	add sp, sp, #0x10
 	ldr q19, [sp], #0x10
 	ldr q18, [sp], #0x10
 	ldr x19, [sp], #0x10
@@ -913,11 +973,14 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
-	ldr s0, [sp, #0x30]
-	ldr d1, [sp, #0x78]
+	movz x27, #0xd0, lsl 0
+	stp x30, x27, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
+	ldr s0, [sp, #0x40]
+	ldr d1, [sp, #0x88]
 	mov x1, x7
 	mov x0, x2
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -974,33 +1037,35 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0xc0
+	stp x30, x27, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x0, x2
 	mov x1, x3
-	str d1, [sp, #0xc8]
-	str s0, [sp, #0xc0]
-	str x1, [sp, #0xb8]
-	str w0, [sp, #0xb0]
-	str d1, [sp, #0xa8]
-	str s0, [sp, #0xa0]
-	str x1, [sp, #0x98]
-	str w0, [sp, #0x90]
-	str d1, [sp, #0x88]
-	str s0, [sp, #0x80]
-	str x1, [sp, #0x78]
-	str w0, [sp, #0x70]
-	str d1, [sp, #0x68]
-	str s0, [sp, #0x60]
-	str x1, [sp, #0x58]
-	str w0, [sp, #0x50]
-	str d1, [sp, #0x48]
-	str s0, [sp, #0x40]
-	str x1, [sp, #0x38]
-	str w0, [sp, #0x30]
-	str d1, [sp, #0x28]
-	str s0, [sp, #0x20]
-	str x1, [sp, #0x18]
-	str w0, [sp, #0x10]
+	str d1, [sp, #0xd8]
+	str s0, [sp, #0xd0]
+	str x1, [sp, #0xc8]
+	str w0, [sp, #0xc0]
+	str d1, [sp, #0xb8]
+	str s0, [sp, #0xb0]
+	str x1, [sp, #0xa8]
+	str w0, [sp, #0xa0]
+	str d1, [sp, #0x98]
+	str s0, [sp, #0x90]
+	str x1, [sp, #0x88]
+	str w0, [sp, #0x80]
+	str d1, [sp, #0x78]
+	str s0, [sp, #0x70]
+	str x1, [sp, #0x68]
+	str w0, [sp, #0x60]
+	str d1, [sp, #0x58]
+	str s0, [sp, #0x50]
+	str x1, [sp, #0x48]
+	str w0, [sp, #0x40]
+	str d1, [sp, #0x38]
+	str s0, [sp, #0x30]
+	str x1, [sp, #0x28]
+	str w0, [sp, #0x20]
 	mov v7.8b, v1.8b
 	mov v6.8b, v0.8b
 	mov x7, x1
@@ -1013,6 +1078,7 @@ L1 (SSA Block: blk0):
 	mov v2.8b, v0.8b
 	mov x3, x1
 	mov x2, x0
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1105,10 +1171,13 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	movz x27, #0x190, lsl 0
+	stp x30, x27, [sp, #-0x10]!
 	str x19, [sp, #-0x10]!
 	str q18, [sp, #-0x10]!
 	str q19, [sp, #-0x10]!
+	orr x27, xzr, #0x30
+	str x27, [sp, #-0x10]!
 	mov x8, x2
 	mov x9, x3
 	mov v8.8b, v0.8b
@@ -1121,58 +1190,59 @@ L1 (SSA Block: blk0):
 	mov x19, x7
 	mov v12.8b, v4.8b
 	mov v13.8b, v5.8b
-	ldr w18, [sp, #0x40]
-	ldr x17, [sp, #0x48]
+	ldr w18, [sp, #0x50]
+	ldr x17, [sp, #0x58]
 	mov v14.8b, v6.8b
 	mov v19.8b, v7.8b
-	ldr w16, [sp, #0x50]
-	ldr x15, [sp, #0x58]
-	ldr s18, [sp, #0x60]
-	ldr d17, [sp, #0x68]
-	ldr w14, [sp, #0x70]
-	ldr x13, [sp, #0x78]
-	ldr s16, [sp, #0x80]
-	ldr d15, [sp, #0x88]
-	ldr w7, [sp, #0x90]
-	ldr x6, [sp, #0x98]
-	ldr s7, [sp, #0xa0]
-	ldr d6, [sp, #0xa8]
-	ldr w5, [sp, #0xb0]
-	ldr x4, [sp, #0xb8]
-	ldr s5, [sp, #0xc0]
-	ldr d4, [sp, #0xc8]
-	ldr w3, [sp, #0xd0]
-	ldr x2, [sp, #0xd8]
-	ldr s3, [sp, #0xe0]
-	ldr d2, [sp, #0xe8]
-	ldr w1, [sp, #0xf0]
-	ldr x0, [sp, #0xf8]
-	ldr s1, [sp, #0x100]
-	ldr d0, [sp, #0x108]
-	str w8, [sp, #0x1c8]
-	str x9, [sp, #0x1c0]
-	str s8, [sp, #0x1b8]
-	str d9, [sp, #0x1b0]
-	str w10, [sp, #0x1a8]
-	str x11, [sp, #0x1a0]
-	str s10, [sp, #0x198]
-	str d11, [sp, #0x190]
-	str w12, [sp, #0x188]
-	str x19, [sp, #0x180]
-	str s12, [sp, #0x178]
-	str d13, [sp, #0x170]
-	str w18, [sp, #0x168]
-	str x17, [sp, #0x160]
-	str s14, [sp, #0x158]
-	str d19, [sp, #0x150]
-	str w16, [sp, #0x148]
-	str x15, [sp, #0x140]
-	str s18, [sp, #0x138]
-	str d17, [sp, #0x130]
-	str w14, [sp, #0x128]
-	str x13, [sp, #0x120]
-	str s16, [sp, #0x118]
-	str d15, [sp, #0x110]
+	ldr w16, [sp, #0x60]
+	ldr x15, [sp, #0x68]
+	ldr s18, [sp, #0x70]
+	ldr d17, [sp, #0x78]
+	ldr w14, [sp, #0x80]
+	ldr x13, [sp, #0x88]
+	ldr s16, [sp, #0x90]
+	ldr d15, [sp, #0x98]
+	ldr w7, [sp, #0xa0]
+	ldr x6, [sp, #0xa8]
+	ldr s7, [sp, #0xb0]
+	ldr d6, [sp, #0xb8]
+	ldr w5, [sp, #0xc0]
+	ldr x4, [sp, #0xc8]
+	ldr s5, [sp, #0xd0]
+	ldr d4, [sp, #0xd8]
+	ldr w3, [sp, #0xe0]
+	ldr x2, [sp, #0xe8]
+	ldr s3, [sp, #0xf0]
+	ldr d2, [sp, #0xf8]
+	ldr w1, [sp, #0x100]
+	ldr x0, [sp, #0x108]
+	ldr s1, [sp, #0x110]
+	ldr d0, [sp, #0x118]
+	str w8, [sp, #0x1d8]
+	str x9, [sp, #0x1d0]
+	str s8, [sp, #0x1c8]
+	str d9, [sp, #0x1c0]
+	str w10, [sp, #0x1b8]
+	str x11, [sp, #0x1b0]
+	str s10, [sp, #0x1a8]
+	str d11, [sp, #0x1a0]
+	str w12, [sp, #0x198]
+	str x19, [sp, #0x190]
+	str s12, [sp, #0x188]
+	str d13, [sp, #0x180]
+	str w18, [sp, #0x178]
+	str x17, [sp, #0x170]
+	str s14, [sp, #0x168]
+	str d19, [sp, #0x160]
+	str w16, [sp, #0x158]
+	str x15, [sp, #0x150]
+	str s18, [sp, #0x148]
+	str d17, [sp, #0x140]
+	str w14, [sp, #0x138]
+	str x13, [sp, #0x130]
+	str s16, [sp, #0x128]
+	str d15, [sp, #0x120]
+	add sp, sp, #0x10
 	ldr q19, [sp], #0x10
 	ldr q18, [sp], #0x10
 	ldr x19, [sp], #0x10
@@ -1253,10 +1323,13 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0x60
+	stp x30, x27, [sp, #-0x10]!
 	str x19, [sp, #-0x10]!
 	str x20, [sp, #-0x10]!
 	str x21, [sp, #-0x10]!
+	orr x27, xzr, #0x30
+	str x27, [sp, #-0x10]!
 	mov x8, x2
 	mov x20, x3
 	mov x19, x4
@@ -1301,18 +1374,19 @@ L1 (SSA Block: blk0):
 	cset x8, hs
 	subs xzr, x19, x21
 	cset x19, hs
-	str w19, [sp, #0x98]
-	str w8, [sp, #0x90]
-	str w9, [sp, #0x88]
-	str w10, [sp, #0x80]
-	str w11, [sp, #0x78]
-	str w12, [sp, #0x70]
-	str w13, [sp, #0x68]
-	str w14, [sp, #0x60]
-	str w15, [sp, #0x58]
-	str w16, [sp, #0x50]
-	str w17, [sp, #0x48]
-	str w18, [sp, #0x40]
+	str w19, [sp, #0xa8]
+	str w8, [sp, #0xa0]
+	str w9, [sp, #0x98]
+	str w10, [sp, #0x90]
+	str w11, [sp, #0x88]
+	str w12, [sp, #0x80]
+	str w13, [sp, #0x78]
+	str w14, [sp, #0x70]
+	str w15, [sp, #0x68]
+	str w16, [sp, #0x60]
+	str w17, [sp, #0x58]
+	str w18, [sp, #0x50]
+	add sp, sp, #0x10
 	ldr x21, [sp], #0x10
 	ldr x20, [sp], #0x10
 	ldr x19, [sp], #0x10
@@ -1354,7 +1428,9 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0x10
+	stp x30, x27, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x10, x2
 	mov x8, x4
 	mov x9, x5
@@ -1369,8 +1445,9 @@ L1 (SSA Block: blk0):
 	sub x9, xzr, x9
 	ror x10, x8, x9
 	ror x8, x8, x9
-	str x8, [sp, #0x18]
-	str x10, [sp, #0x10]
+	str x8, [sp, #0x28]
+	str x10, [sp, #0x20]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1412,7 +1489,9 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0x20
+	stp x30, x27, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x9, x2
 	mov x10, x3
 	mov x8, x4
@@ -1429,10 +1508,11 @@ L1 (SSA Block: blk0):
 	asr w9, w9, 0x1f
 	asr x11, x8, x11
 	asr x8, x8, 0x20
-	str x8, [sp, #0x28]
-	str x11, [sp, #0x20]
-	str w9, [sp, #0x18]
-	str w10, [sp, #0x10]
+	str x8, [sp, #0x38]
+	str x11, [sp, #0x30]
+	str w9, [sp, #0x28]
+	str w10, [sp, #0x20]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1462,7 +1542,8 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, x2
 	mov x9, x3
 	sxtw x0, w8
@@ -1472,6 +1553,7 @@ L1 (SSA Block: blk0):
 	sxtw x4, w9
 	sxtb w5, w8
 	sxth w6, w8
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1483,15 +1565,15 @@ L1 (SSA Block: blk0):
 	mov x2?, x2
 	mov x3?, x3
 	clz w4?, w2?
-	rbit w27, w2?
-	clz w5?, w27
-	ins v13?.d[0], x2?
-	cnt v14?.16b, v13?.16b
-	uaddlv h15?, v14?.8b
-	mov x6?, v15?.d[0]
+	rbit w17?, w2?
+	clz w5?, w17?
+	ins v14?.d[0], x2?
+	cnt v15?.16b, v14?.16b
+	uaddlv h16?, v15?.8b
+	mov x6?, v16?.d[0]
 	clz x7?, x3?
-	rbit x27, x3?
-	clz x8?, x27
+	rbit x13?, x3?
+	clz x8?, x13?
 	ins v10?.d[0], x3?
 	cnt v11?.16b, v10?.16b
 	uaddlv h12?, v11?.8b
@@ -1506,22 +1588,24 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, x3
 	clz w0, w2
-	rbit w27, w2
-	clz w1, w27
+	rbit w9, w2
+	clz w1, w9
 	ins v8.d[0], x2
 	cnt v8.16b, v8.16b
 	uaddlv h8, v8.8b
 	mov x2, v8.d[0]
 	clz x3, x8
-	rbit x27, x8
-	clz x4, x27
+	rbit x9, x8
+	clz x4, x9
 	ins v8.d[0], x8
 	cnt v8.16b, v8.16b
 	uaddlv h8, v8.8b
 	mov x5, v8.d[0]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1575,7 +1659,9 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0x20
+	stp x30, x27, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	fcmp s0, s1
 	cset x0, eq
 	fcmp s0, s1
@@ -1600,10 +1686,11 @@ L1 (SSA Block: blk0):
 	cset x9, ls
 	fcmp d2, d3
 	cset x8, ge
-	str w8, [sp, #0x28]
-	str w9, [sp, #0x20]
-	str w10, [sp, #0x18]
-	str w11, [sp, #0x10]
+	str w8, [sp, #0x38]
+	str w9, [sp, #0x30]
+	str w10, [sp, #0x28]
+	str w11, [sp, #0x20]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1613,130 +1700,196 @@ L1 (SSA Block: blk0):
 			m:    testcases.FloatConversions.Module,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, x0
 	msr fpsr, xzr
 	fcvtzs x0, d0
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L17)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L17)
 	fcmp d0, d0
-	b.vc #0x24, (L16)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L16)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L16:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L17:
 	msr fpsr, xzr
 	fcvtzs x1, s1
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L15)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L15)
 	fcmp s1, s1
-	b.vc #0x24, (L14)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L14)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L14:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L15:
 	msr fpsr, xzr
 	fcvtzs w2, d0
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L13)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L13)
 	fcmp d0, d0
-	b.vc #0x24, (L12)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L12)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L12:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L13:
 	msr fpsr, xzr
 	fcvtzs w3, s1
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L11)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L11)
 	fcmp s1, s1
-	b.vc #0x24, (L10)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L10)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L10:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L11:
 	msr fpsr, xzr
 	fcvtzu x4, d0
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L9)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L9)
 	fcmp d0, d0
-	b.vc #0x24, (L8)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L8)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L8:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L9:
 	msr fpsr, xzr
 	fcvtzu x5, s1
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L7)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L7)
 	fcmp s1, s1
-	b.vc #0x24, (L6)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L6)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L6:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L7:
 	msr fpsr, xzr
 	fcvtzu w6, d0
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L5)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L5)
 	fcmp d0, d0
-	b.vc #0x24, (L4)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L4)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L4:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L5:
 	msr fpsr, xzr
 	fcvtzu w7, s1
-	mrs x27 fpsr
-	subs xzr, x27, #0x1
-	b.ne #0x4c, (L3)
+	mrs x9 fpsr
+	subs xzr, x9, #0x1
+	b.ne #0x6c, (L3)
 	fcmp s1, s1
-	b.vc #0x24, (L2)
-	movz x27, #0xc, lsl 0
-	str w27, [x8]
+	b.vc #0x34, (L2)
+	movz x9, #0xc, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L2:
-	movz x27, #0xb, lsl 0
-	str w27, [x8]
+	movz x9, #0xb, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L3:
 	fcvt s0, d0
 	fcvt d1, s1
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1746,7 +1899,8 @@ L3:
 			m:    testcases.NonTrappingFloatConversions.Module,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	fcvtzs x0, d0
 	fcvtzs x1, s1
 	fcvtzs w2, d0
@@ -1755,6 +1909,7 @@ L1 (SSA Block: blk0):
 	fcvtzu x5, s1
 	fcvtzu w6, d0
 	fcvtzu w7, s1
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -1890,7 +2045,7 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	str x19, [sp, #-0x10]!
 	str x20, [sp, #-0x10]!
 	str x21, [sp, #-0x10]!
@@ -1909,6 +2064,8 @@ L1 (SSA Block: blk0):
 	str q25, [sp, #-0x10]!
 	str q26, [sp, #-0x10]!
 	str q27, [sp, #-0x10]!
+	movz x27, #0x120, lsl 0
+	str x27, [sp, #-0x10]!
 	orr w8, wzr, #0x1
 	madd w8, w2, w8, wzr
 	orr w9, wzr, #0x2
@@ -2027,6 +2184,7 @@ L1 (SSA Block: blk0):
 	fadd s10, s10, s11
 	fadd s9, s9, s10
 	fadd s0, s8, s9
+	add sp, sp, #0x10
 	ldr q27, [sp], #0x10
 	ldr q26, [sp], #0x10
 	ldr q25, [sp], #0x10
@@ -2047,79 +2205,6 @@ L1 (SSA Block: blk0):
 	ldr x19, [sp], #0x10
 	ldr x30, [sp], #0x10
 	ret
-`,
-		},
-		{
-			name: "recursive_fibonacci", m: testcases.FibonacciRecursive.Module,
-			afterLoweringARM64: `
-L1 (SSA Block: blk0):
-	mov x0?, x0
-	mov x1?, x1
-	mov x2?, x2
-	subs wzr, w2?, #0x2
-	b.lt L2
-L3 (SSA Block: blk2):
-L4 (SSA Block: blk3):
-	sub w6?, w2?, #0x1
-	str x1?, [x0?, #0x8]
-	mov x0, x0?
-	mov x1, x1?
-	mov x2, x6?
-	bl f0
-	mov x7?, x0
-	sub w9?, w2?, #0x2
-	str x1?, [x0?, #0x8]
-	mov x0, x0?
-	mov x1, x1?
-	mov x2, x9?
-	bl f0
-	mov x10?, x0
-	add w11?, w7?, w10?
-	mov x0, x11?
-	ret
-L2 (SSA Block: blk1):
-	mov x0, x2?
-	ret
-`,
-			afterFinalizeARM64: `
-L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
-	sub sp, sp, #0x20
-	mov x9, x0
-	mov x8, x1
-	mov x11, x2
-	subs wzr, w11, #0x2
-	b.lt #0x60, (L2)
-L3 (SSA Block: blk2):
-L4 (SSA Block: blk3):
-	sub w2, w11, #0x1
-	str x8, [x9, #0x8]
-	mov x0, x9
-	mov x1, x8
-	str w11, [sp]
-	str x9, [sp, #0x4]
-	str x8, [sp, #0xc]
-	bl f0
-	ldr x8, [sp, #0xc]
-	ldr x9, [sp, #0x4]
-	ldr w11, [sp]
-	mov x10, x0
-	sub w2, w11, #0x2
-	str x8, [x9, #0x8]
-	mov x0, x9
-	mov x1, x8
-	str w10, [sp, #0x14]
-	bl f0
-	ldr w10, [sp, #0x14]
-	add w0, w10, w0
-	add sp, sp, #0x20
-	ldr x30, [sp], #0x10
-	ret
-L2 (SSA Block: blk1):
-	mov x0, x11
-	add sp, sp, #0x20
-	ldr x30, [sp], #0x10
-	ret
 `,
 		},
 		{
@@ -2143,13 +2228,15 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x3, x2
 	str x1, [x0, #0x8]
 	ldr x8, [x1, #0x8]
 	ldr x1, [x1, #0x10]
 	mov x2, x3
 	bl x8
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -2166,8 +2253,12 @@ L1 (SSA Block: blk0):
 	add x6?, x4?, #0x4
 	subs xzr, x5?, x6?
 	b.hs L2
-	movz x27, #0x4, lsl 0
-	str w27, [x0?]
+	movz x12?, #0x4, lsl 0
+	str w12?, [x0?]
+	mov x13?, sp
+	str x13?, [x0?, #0x38]
+	adr x14?, #0x0
+	str x14?, [x0?, #0x30]
 	exit_sequence x0?
 L2:
 	ldr x8?, [x1?, #0x8]
@@ -2178,19 +2269,25 @@ L2:
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	uxtw x8, w2
 	ldr w10, [x1, #0x10]
 	add x9, x8, #0x4
 	subs xzr, x10, x9
-	b.hs #0x24, (L2)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	b.hs #0x34, (L2)
+	movz x9, #0x4, lsl 0
+	str w9, [x0]
+	mov x9, sp
+	str x9, [x0, #0x38]
+	adr x9, #0x0
+	str x9, [x0, #0x30]
 	exit_sequence x0
 L2:
 	ldr x9, [x1, #0x8]
 	add x8, x9, x8
 	ldr w0, [x8]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -2199,108 +2296,146 @@ L2:
 			name: "memory_stores", m: testcases.MemoryStores.Module,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	mov x8, xzr
 	uxtw x10, w8
 	ldr w8, [x1, #0x10]
 	add x9, x10, #0x4
 	subs xzr, x8, x9
-	b.hs #0x24, (L10)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	b.hs #0x34, (L10)
+	movz x9, #0x4, lsl 0
+	str w9, [x0]
+	mov x9, sp
+	str x9, [x0, #0x38]
+	adr x9, #0x0
+	str x9, [x0, #0x30]
 	exit_sequence x0
 L10:
 	ldr x9, [x1, #0x8]
 	add x10, x9, x10
 	str w2, [x10]
 	orr w10, wzr, #0x8
-	uxtw x11, w10
-	add x10, x11, #0x8
-	subs xzr, x8, x10
-	b.hs #0x24, (L9)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x8
+	subs xzr, x8, x11
+	b.hs #0x34, (L9)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L9:
-	add x10, x9, x11
+	add x10, x9, x10
 	str x3, [x10]
 	orr w10, wzr, #0x10
-	uxtw x11, w10
-	add x10, x11, #0x4
-	subs xzr, x8, x10
-	b.hs #0x24, (L8)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x4
+	subs xzr, x8, x11
+	b.hs #0x34, (L8)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L8:
-	add x10, x9, x11
+	add x10, x9, x10
 	str s0, [x10]
 	orr w10, wzr, #0x18
-	uxtw x11, w10
-	add x10, x11, #0x8
-	subs xzr, x8, x10
-	b.hs #0x24, (L7)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x8
+	subs xzr, x8, x11
+	b.hs #0x34, (L7)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L7:
-	add x10, x9, x11
+	add x10, x9, x10
 	str d1, [x10]
 	orr w10, wzr, #0x20
-	uxtw x11, w10
-	add x10, x11, #0x1
-	subs xzr, x8, x10
-	b.hs #0x24, (L6)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x1
+	subs xzr, x8, x11
+	b.hs #0x34, (L6)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L6:
-	add x10, x9, x11
+	add x10, x9, x10
 	strb w2, [x10]
 	movz w10, #0x28, lsl 0
-	uxtw x11, w10
-	add x10, x11, #0x2
-	subs xzr, x8, x10
-	b.hs #0x24, (L5)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x2
+	subs xzr, x8, x11
+	b.hs #0x34, (L5)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L5:
-	add x10, x9, x11
+	add x10, x9, x10
 	strh w2, [x10]
 	orr w10, wzr, #0x30
-	uxtw x11, w10
-	add x10, x11, #0x1
-	subs xzr, x8, x10
-	b.hs #0x24, (L4)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x1
+	subs xzr, x8, x11
+	b.hs #0x34, (L4)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L4:
-	add x10, x9, x11
+	add x10, x9, x10
 	strb w3, [x10]
 	orr w10, wzr, #0x38
-	uxtw x11, w10
-	add x10, x11, #0x2
-	subs xzr, x8, x10
-	b.hs #0x24, (L3)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x2
+	subs xzr, x8, x11
+	b.hs #0x34, (L3)
+	movz x11, #0x4, lsl 0
+	str w11, [x0]
+	mov x11, sp
+	str x11, [x0, #0x38]
+	adr x11, #0x0
+	str x11, [x0, #0x30]
 	exit_sequence x0
 L3:
-	add x10, x9, x11
+	add x10, x9, x10
 	strh w3, [x10]
 	orr w10, wzr, #0x40
-	uxtw x11, w10
-	add x10, x11, #0x4
-	subs xzr, x8, x10
-	b.hs #0x24, (L2)
-	movz x27, #0x4, lsl 0
-	str w27, [x0]
+	uxtw x10, w10
+	add x11, x10, #0x4
+	subs xzr, x8, x11
+	b.hs #0x34, (L2)
+	movz x8, #0x4, lsl 0
+	str w8, [x0]
+	mov x8, sp
+	str x8, [x0, #0x38]
+	adr x8, #0x0
+	str x8, [x0, #0x30]
 	exit_sequence x0
 L2:
-	add x8, x9, x11
+	add x8, x9, x10
 	str w3, [x8]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -2309,7 +2444,8 @@ L2:
 			name: "memory_loads", m: testcases.MemoryLoads.Module,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	orr x27, xzr, #0x80
+	stp x30, x27, [sp, #-0x10]!
 	str x19, [sp, #-0x10]!
 	str x20, [sp, #-0x10]!
 	str x21, [sp, #-0x10]!
@@ -2318,306 +2454,421 @@ L1 (SSA Block: blk0):
 	str x24, [sp, #-0x10]!
 	str x25, [sp, #-0x10]!
 	str x26, [sp, #-0x10]!
+	orr x27, xzr, #0x80
+	str x27, [sp, #-0x10]!
 	mov x8, x0
-	uxtw x11, w2
+	mov x24, x2
+	uxtw x11, w24
 	ldr w9, [x1, #0x10]
 	add x10, x11, #0x4
 	subs xzr, x9, x10
-	b.hs #0x24, (L29)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L29)
+	movz x10, #0x4, lsl 0
+	str w10, [x8]
+	mov x10, sp
+	str x10, [x8, #0x38]
+	adr x10, #0x0
+	str x10, [x8, #0x30]
 	exit_sequence x8
 L29:
 	ldr x10, [x1, #0x8]
 	add x11, x10, x11
 	ldr w0, [x11]
-	uxtw x12, w2
-	add x11, x12, #0x8
-	subs xzr, x9, x11
-	b.hs #0x24, (L28)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	uxtw x11, w24
+	add x12, x11, #0x8
+	subs xzr, x9, x12
+	b.hs #0x34, (L28)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L28:
-	add x11, x10, x12
+	add x11, x10, x11
 	ldr x1, [x11]
-	uxtw x12, w2
-	add x11, x12, #0x4
-	subs xzr, x9, x11
-	b.hs #0x24, (L27)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	uxtw x11, w24
+	add x12, x11, #0x4
+	subs xzr, x9, x12
+	b.hs #0x34, (L27)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L27:
-	add x11, x10, x12
+	add x11, x10, x11
 	ldr s0, [x11]
-	uxtw x12, w2
-	add x11, x12, #0x8
-	subs xzr, x9, x11
-	b.hs #0x24, (L26)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	uxtw x11, w24
+	add x12, x11, #0x8
+	subs xzr, x9, x12
+	b.hs #0x34, (L26)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L26:
-	add x11, x10, x12
+	add x11, x10, x11
 	ldr d1, [x11]
-	uxtw x12, w2
-	add x11, x12, #0x13
-	subs xzr, x9, x11
-	b.hs #0x24, (L25)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	uxtw x11, w24
+	add x12, x11, #0x13
+	subs xzr, x9, x12
+	b.hs #0x34, (L25)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L25:
-	add x11, x10, x12
-	ldr w11, [x11, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x17
+	add x11, x10, x11
+	ldr w2, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x17
 	subs xzr, x9, x12
-	b.hs #0x24, (L24)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L24)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L24:
-	add x12, x10, x13
-	ldr x3, [x12, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x13
+	add x11, x10, x11
+	ldr x3, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x13
 	subs xzr, x9, x12
-	b.hs #0x24, (L23)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L23)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L23:
-	add x12, x10, x13
-	ldr s2, [x12, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x17
+	add x11, x10, x11
+	ldr s2, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x17
 	subs xzr, x9, x12
-	b.hs #0x24, (L22)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L22)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L22:
-	add x12, x10, x13
-	ldr d3, [x12, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x1
+	add x11, x10, x11
+	ldr d3, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x1
 	subs xzr, x9, x12
-	b.hs #0x24, (L21)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L21)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L21:
-	add x12, x10, x13
-	ldrsb w4, [x12]
-	uxtw x13, w2
-	add x12, x13, #0x10
+	add x11, x10, x11
+	ldrsb w4, [x11]
+	uxtw x11, w24
+	add x12, x11, #0x10
 	subs xzr, x9, x12
-	b.hs #0x24, (L20)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L20)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L20:
-	add x12, x10, x13
-	ldrsb w5, [x12, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x1
+	add x11, x10, x11
+	ldrsb w5, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x1
 	subs xzr, x9, x12
-	b.hs #0x24, (L19)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L19)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L19:
-	add x12, x10, x13
-	ldrb w6, [x12]
-	uxtw x13, w2
-	add x12, x13, #0x10
+	add x11, x10, x11
+	ldrb w6, [x11]
+	uxtw x11, w24
+	add x12, x11, #0x10
 	subs xzr, x9, x12
-	b.hs #0x24, (L18)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L18)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L18:
-	add x12, x10, x13
-	ldrb w7, [x12, #0xf]
-	uxtw x13, w2
-	add x12, x13, #0x2
+	add x11, x10, x11
+	ldrb w7, [x11, #0xf]
+	uxtw x11, w24
+	add x12, x11, #0x2
 	subs xzr, x9, x12
-	b.hs #0x24, (L17)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L17)
+	movz x12, #0x4, lsl 0
+	str w12, [x8]
+	mov x12, sp
+	str x12, [x8, #0x38]
+	adr x12, #0x0
+	str x12, [x8, #0x30]
 	exit_sequence x8
 L17:
-	add x12, x10, x13
-	ldrsh w12, [x12]
-	uxtw x14, w2
-	add x13, x14, #0x11
+	add x11, x10, x11
+	ldrsh w11, [x11]
+	uxtw x12, w24
+	add x13, x12, #0x11
 	subs xzr, x9, x13
-	b.hs #0x24, (L16)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L16)
+	movz x13, #0x4, lsl 0
+	str w13, [x8]
+	mov x13, sp
+	str x13, [x8, #0x38]
+	adr x13, #0x0
+	str x13, [x8, #0x30]
 	exit_sequence x8
 L16:
-	add x13, x10, x14
-	ldrsh w13, [x13, #0xf]
-	uxtw x15, w2
-	add x14, x15, #0x2
+	add x12, x10, x12
+	ldrsh w12, [x12, #0xf]
+	uxtw x13, w24
+	add x14, x13, #0x2
 	subs xzr, x9, x14
-	b.hs #0x24, (L15)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L15)
+	movz x14, #0x4, lsl 0
+	str w14, [x8]
+	mov x14, sp
+	str x14, [x8, #0x38]
+	adr x14, #0x0
+	str x14, [x8, #0x30]
 	exit_sequence x8
 L15:
-	add x14, x10, x15
-	ldrh w14, [x14]
-	uxtw x16, w2
-	add x15, x16, #0x11
+	add x13, x10, x13
+	ldrh w13, [x13]
+	uxtw x14, w24
+	add x15, x14, #0x11
 	subs xzr, x9, x15
-	b.hs #0x24, (L14)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L14)
+	movz x15, #0x4, lsl 0
+	str w15, [x8]
+	mov x15, sp
+	str x15, [x8, #0x38]
+	adr x15, #0x0
+	str x15, [x8, #0x30]
 	exit_sequence x8
 L14:
-	add x15, x10, x16
-	ldrh w15, [x15, #0xf]
-	uxtw x17, w2
-	add x16, x17, #0x1
+	add x14, x10, x14
+	ldrh w14, [x14, #0xf]
+	uxtw x15, w24
+	add x16, x15, #0x1
 	subs xzr, x9, x16
-	b.hs #0x24, (L13)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L13)
+	movz x16, #0x4, lsl 0
+	str w16, [x8]
+	mov x16, sp
+	str x16, [x8, #0x38]
+	adr x16, #0x0
+	str x16, [x8, #0x30]
 	exit_sequence x8
 L13:
-	add x16, x10, x17
-	ldrsb w16, [x16]
-	uxtw x18, w2
-	add x17, x18, #0x10
+	add x15, x10, x15
+	ldrsb w15, [x15]
+	uxtw x16, w24
+	add x17, x16, #0x10
 	subs xzr, x9, x17
-	b.hs #0x24, (L12)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L12)
+	movz x17, #0x4, lsl 0
+	str w17, [x8]
+	mov x17, sp
+	str x17, [x8, #0x38]
+	adr x17, #0x0
+	str x17, [x8, #0x30]
 	exit_sequence x8
 L12:
-	add x17, x10, x18
-	ldrsb w17, [x17, #0xf]
-	uxtw x19, w2
-	add x18, x19, #0x1
+	add x16, x10, x16
+	ldrsb w16, [x16, #0xf]
+	uxtw x17, w24
+	add x18, x17, #0x1
 	subs xzr, x9, x18
-	b.hs #0x24, (L11)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L11)
+	movz x18, #0x4, lsl 0
+	str w18, [x8]
+	mov x18, sp
+	str x18, [x8, #0x38]
+	adr x18, #0x0
+	str x18, [x8, #0x30]
 	exit_sequence x8
 L11:
-	add x18, x10, x19
-	ldrb w18, [x18]
-	uxtw x20, w2
-	add x19, x20, #0x10
+	add x17, x10, x17
+	ldrb w17, [x17]
+	uxtw x18, w24
+	add x19, x18, #0x10
 	subs xzr, x9, x19
-	b.hs #0x24, (L10)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L10)
+	movz x19, #0x4, lsl 0
+	str w19, [x8]
+	mov x19, sp
+	str x19, [x8, #0x38]
+	adr x19, #0x0
+	str x19, [x8, #0x30]
 	exit_sequence x8
 L10:
-	add x19, x10, x20
-	ldrb w19, [x19, #0xf]
-	uxtw x21, w2
-	add x20, x21, #0x2
+	add x18, x10, x18
+	ldrb w18, [x18, #0xf]
+	uxtw x19, w24
+	add x20, x19, #0x2
 	subs xzr, x9, x20
-	b.hs #0x24, (L9)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L9)
+	movz x20, #0x4, lsl 0
+	str w20, [x8]
+	mov x20, sp
+	str x20, [x8, #0x38]
+	adr x20, #0x0
+	str x20, [x8, #0x30]
 	exit_sequence x8
 L9:
-	add x20, x10, x21
-	ldrsh w20, [x20]
-	uxtw x22, w2
-	add x21, x22, #0x11
+	add x19, x10, x19
+	ldrsh w19, [x19]
+	uxtw x20, w24
+	add x21, x20, #0x11
 	subs xzr, x9, x21
-	b.hs #0x24, (L8)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L8)
+	movz x21, #0x4, lsl 0
+	str w21, [x8]
+	mov x21, sp
+	str x21, [x8, #0x38]
+	adr x21, #0x0
+	str x21, [x8, #0x30]
 	exit_sequence x8
 L8:
-	add x21, x10, x22
-	ldrsh w21, [x21, #0xf]
-	uxtw x23, w2
-	add x22, x23, #0x2
+	add x20, x10, x20
+	ldrsh w20, [x20, #0xf]
+	uxtw x21, w24
+	add x22, x21, #0x2
 	subs xzr, x9, x22
-	b.hs #0x24, (L7)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L7)
+	movz x22, #0x4, lsl 0
+	str w22, [x8]
+	mov x22, sp
+	str x22, [x8, #0x38]
+	adr x22, #0x0
+	str x22, [x8, #0x30]
 	exit_sequence x8
 L7:
-	add x22, x10, x23
-	ldrh w22, [x22]
-	uxtw x24, w2
-	add x23, x24, #0x11
+	add x21, x10, x21
+	ldrh w21, [x21]
+	uxtw x22, w24
+	add x23, x22, #0x11
 	subs xzr, x9, x23
-	b.hs #0x24, (L6)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L6)
+	movz x23, #0x4, lsl 0
+	str w23, [x8]
+	mov x23, sp
+	str x23, [x8, #0x38]
+	adr x23, #0x0
+	str x23, [x8, #0x30]
 	exit_sequence x8
 L6:
-	add x23, x10, x24
-	ldrh w23, [x23, #0xf]
-	uxtw x25, w2
-	add x24, x25, #0x4
-	subs xzr, x9, x24
-	b.hs #0x24, (L5)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	add x22, x10, x22
+	ldrh w22, [x22, #0xf]
+	uxtw x23, w24
+	add x25, x23, #0x4
+	subs xzr, x9, x25
+	b.hs #0x34, (L5)
+	movz x25, #0x4, lsl 0
+	str w25, [x8]
+	mov x25, sp
+	str x25, [x8, #0x38]
+	adr x25, #0x0
+	str x25, [x8, #0x30]
 	exit_sequence x8
 L5:
-	add x24, x10, x25
-	ldrs w24, [x24]
-	uxtw x26, w2
-	add x25, x26, #0x13
-	subs xzr, x9, x25
-	b.hs #0x24, (L4)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	add x23, x10, x23
+	ldrs w23, [x23]
+	uxtw x25, w24
+	add x26, x25, #0x13
+	subs xzr, x9, x26
+	b.hs #0x34, (L4)
+	movz x26, #0x4, lsl 0
+	str w26, [x8]
+	mov x26, sp
+	str x26, [x8, #0x38]
+	adr x26, #0x0
+	str x26, [x8, #0x30]
 	exit_sequence x8
 L4:
-	add x25, x10, x26
+	add x25, x10, x25
 	ldrs w25, [x25, #0xf]
-	uxtw x29, w2
-	add x26, x29, #0x4
-	subs xzr, x9, x26
-	b.hs #0x24, (L3)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	uxtw x26, w24
+	add x29, x26, #0x4
+	subs xzr, x9, x29
+	b.hs #0x34, (L3)
+	movz x29, #0x4, lsl 0
+	str w29, [x8]
+	mov x29, sp
+	str x29, [x8, #0x38]
+	adr x29, #0x0
+	str x29, [x8, #0x30]
 	exit_sequence x8
 L3:
-	add x26, x10, x29
+	add x26, x10, x26
 	ldr w26, [x26]
-	uxtw x30, w2
-	add x29, x30, #0x13
+	uxtw x24, w24
+	add x29, x24, #0x13
 	subs xzr, x9, x29
-	b.hs #0x24, (L2)
-	movz x27, #0x4, lsl 0
-	str w27, [x8]
+	b.hs #0x34, (L2)
+	movz x9, #0x4, lsl 0
+	str w9, [x8]
+	mov x9, sp
+	str x9, [x8, #0x38]
+	adr x9, #0x0
+	str x9, [x8, #0x30]
 	exit_sequence x8
 L2:
-	add x8, x10, x30
+	add x8, x10, x24
 	ldr w8, [x8, #0xf]
-	str x8, [sp, #0x108]
-	str x26, [sp, #0x100]
-	str x25, [sp, #0xf8]
-	str x24, [sp, #0xf0]
-	str x23, [sp, #0xe8]
-	str x22, [sp, #0xe0]
-	str x21, [sp, #0xd8]
-	str x20, [sp, #0xd0]
-	str x19, [sp, #0xc8]
-	str x18, [sp, #0xc0]
-	str x17, [sp, #0xb8]
-	str x16, [sp, #0xb0]
-	str w15, [sp, #0xa8]
-	str w14, [sp, #0xa0]
-	str w13, [sp, #0x98]
-	str w12, [sp, #0x90]
-	mov x2, x11
+	str x8, [sp, #0x118]
+	str x26, [sp, #0x110]
+	str x25, [sp, #0x108]
+	str x23, [sp, #0x100]
+	str x22, [sp, #0xf8]
+	str x21, [sp, #0xf0]
+	str x20, [sp, #0xe8]
+	str x19, [sp, #0xe0]
+	str x18, [sp, #0xd8]
+	str x17, [sp, #0xd0]
+	str x16, [sp, #0xc8]
+	str x15, [sp, #0xc0]
+	str w14, [sp, #0xb8]
+	str w13, [sp, #0xb0]
+	str w12, [sp, #0xa8]
+	str w11, [sp, #0xa0]
+	add sp, sp, #0x10
 	ldr x26, [sp], #0x10
 	ldr x25, [sp], #0x10
 	ldr x24, [sp], #0x10
@@ -2669,8 +2920,10 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	sub sp, sp, #0x20
+	orr x27, xzr, #0x20
+	str x27, [sp, #-0x10]!
 	mov x10, x1
 	ldr x8, [x10, #0x8]
 	ldr w8, [x8, #0x8]
@@ -2682,17 +2935,17 @@ L1 (SSA Block: blk0):
 	ldr d1, [x11, #0x8]
 	str x10, [x0, #0x8]
 	mov x1, x10
-	str x10, [sp]
-	str w8, [sp, #0x8]
-	str x9, [sp, #0xc]
-	str s0, [sp, #0x14]
-	str d1, [sp, #0x18]
+	str x10, [sp, #0x10]
+	str w8, [sp, #0x18]
+	str x9, [sp, #0x1c]
+	str s0, [sp, #0x24]
+	str d1, [sp, #0x28]
 	bl f1
-	ldr d1, [sp, #0x18]
-	ldr s0, [sp, #0x14]
-	ldr x9, [sp, #0xc]
-	ldr w8, [sp, #0x8]
-	ldr x10, [sp]
+	ldr d1, [sp, #0x28]
+	ldr s0, [sp, #0x24]
+	ldr x9, [sp, #0x1c]
+	ldr w8, [sp, #0x18]
+	ldr x10, [sp, #0x10]
 	ldr x11, [x10, #0x8]
 	ldr w2, [x11, #0x8]
 	ldr x11, [x10, #0x10]
@@ -2703,6 +2956,7 @@ L1 (SSA Block: blk0):
 	ldr d3, [x10, #0x8]
 	mov x1, x9
 	mov x0, x8
+	add sp, sp, #0x10
 	add sp, sp, #0x20
 	ldr x30, [sp], #0x10
 	ret
@@ -2731,7 +2985,8 @@ L1 (SSA Block: blk0):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	ldr x9, [x1, #0x8]
 	orr w8, wzr, #0x1
 	str w8, [x9, #0x8]
@@ -2744,6 +2999,7 @@ L1 (SSA Block: blk0):
 	ldr x8, [x1, #0x20]
 	ldr d8, #8; b 16; data.f64 4.000000
 	str d8, [x8, #0x8]
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -2794,41 +3050,48 @@ L9 (SSA Block: blk6):
 `,
 			afterFinalizeARM64: `
 L1 (SSA Block: blk0):
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	orr w8, wzr, #0x6
 	subs wzr, w2, w8
 	csel w8, w8, w2, hs
-	adr x27, #16; ldrsw x8, [x27, x8, UXTW 2]; add x27, x27, x8; br x27; [0x1c 0x20 0x2c 0x38 0x44 0x50 0x5c]
+	adr x27, #16; ldrsw x8, [x27, x8, UXTW 2]; add x27, x27, x8; br x27; [0x1c 0x20 0x30 0x40 0x50 0x60 0x70]
 L2 (SSA Block: blk7):
-	b #0x40 (L9)
+	b #0x54 (L9)
 L3 (SSA Block: blk8):
 L10 (SSA Block: blk5):
 	orr w0, wzr, #0xc
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L4 (SSA Block: blk9):
 L11 (SSA Block: blk4):
 	movz w0, #0xd, lsl 0
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L5 (SSA Block: blk10):
 L12 (SSA Block: blk3):
 	orr w0, wzr, #0xe
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L6 (SSA Block: blk11):
 L13 (SSA Block: blk2):
 	orr w0, wzr, #0xf
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L7 (SSA Block: blk12):
 L14 (SSA Block: blk1):
 	orr w0, wzr, #0x10
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 L8 (SSA Block: blk13):
 L9 (SSA Block: blk6):
 	movz w0, #0xb, lsl 0
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
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 a1c8f249..6d159015 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,6 +42,8 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 			},
 			needModuleContextPtr: true,
 			exp: `
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -109,6 +111,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
 	ret
 `,
 		},
@@ -121,6 +124,8 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 			},
 			needModuleContextPtr: true,
 			exp: `
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -183,6 +188,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	ldr q30, [x0, #0x1c0]
 	ldr q31, [x0, #0x1d0]
 	add x15, x0, #0x468
+	add sp, sp, #0x20
 	ret
 `,
 		},
@@ -194,6 +200,8 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 				Results: []ssa.Type{ssa.TypeI32},
 			},
 			exp: `
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	str x19, [x0, #0x60]
 	str x20, [x0, #0x70]
 	str x21, [x0, #0x80]
@@ -254,6 +262,7 @@ func TestMachine_CompileGoFunctionTrampoline(t *testing.T) {
 	add x15, x0, #0x468
 	ldr w27, [x15], #0x8
 	mov w0, w27
+	add sp, sp, #0x20
 	ret
 `,
 		},
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
index 00846c98..8314a3ae 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -9,7 +9,6 @@ import (
 
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
-	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
 
@@ -393,6 +392,9 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "e17b81a9", setup: func(i *instruction) {
 			i.asStorePair64(x1VReg, x30VReg, addressModePreOrPostIndex(spVReg, 16, true))
 		}},
+		{want: "e17f81a9", setup: func(i *instruction) {
+			i.asStorePair64(x1VReg, xzrVReg, addressModePreOrPostIndex(spVReg, 16, true))
+		}},
 		{want: "20000014", setup: func(i *instruction) {
 			i.asBr(dummyLabel)
 			i.brOffsetResolved(0x80)
@@ -983,6 +985,7 @@ func TestInstruction_encoding_store_encoding(t *testing.T) {
 		{k: store32, amode: amodePreIndex2, rn: x5VReg, want: "e50f10b8"},
 		{k: store64, amode: amodePreIndex1, rn: x5VReg, want: "c5af00f8"},
 		{k: store64, amode: amodePreIndex2, rn: x5VReg, want: "e50f10f8"},
+		{k: store64, amode: amodePreIndex2, rn: xzrVReg, want: "ff0f10f8"},
 		{k: fpuStore32, amode: amodePreIndex1, rn: v5VReg, want: "c5af00bc"},
 		{k: fpuStore32, amode: amodePreIndex2, rn: v5VReg, want: "e50f10bc"},
 		{k: fpuStore64, amode: amodePreIndex1, rn: v5VReg, want: "c5af00fc"},
@@ -1106,15 +1109,6 @@ func Test_encodeExitSequence(t *testing.T) {
 	})
 }
 
-func Test_lowerExitWithCodeEncodingSize(t *testing.T) {
-	compiler, _, m := newSetupWithMockContext()
-	m.lowerExitWithCode(x10VReg, wazevoapi.ExitCodeGrowStack)
-	m.FlushPendingInstructions()
-	require.NotNil(t, m.perBlockHead)
-	m.encode(m.perBlockHead)
-	require.Equal(t, exitWithCodeEncodingSize, len(compiler.Buf()))
-}
-
 func Test_encodeBrTableSequence(t *testing.T) {
 	m := &mockCompiler{}
 	i := &instruction{kind: brTableSequence, targets: []uint32{1, 2, 3, 4, 5}}
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
index b3415ab9..2f94f8fc 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
@@ -1,7 +1,6 @@
 package arm64
 
 import (
-	"encoding/hex"
 	"fmt"
 	"strings"
 	"testing"
@@ -301,8 +300,12 @@ func TestMachine_lowerIDiv(t *testing.T) {
 			exp: `
 udiv w1?, w2?, w3?
 cbnz w3?, L1
-movz x27, #0xa, lsl 0
-str w27, [x65535?]
+movz x1?, #0xa, lsl 0
+str w1?, [x65535?]
+mov x2?, sp
+str x2?, [x65535?, #0x38]
+adr x3?, #0x0
+str x3?, [x65535?, #0x30]
 exit_sequence x65535?
 L1:
 `,
@@ -310,38 +313,58 @@ L1:
 		{name: "32bit signed", _64bit: false, signed: true, exp: `
 sdiv w1?, w2?, w3?
 cbnz w3?, L1
-movz x27, #0xa, lsl 0
-str w27, [x65535?]
+movz x1?, #0xa, lsl 0
+str w1?, [x65535?]
+mov x2?, sp
+str x2?, [x65535?, #0x38]
+adr x3?, #0x0
+str x3?, [x65535?, #0x30]
 exit_sequence x65535?
 L1:
 adds wzr, w3?, #0x1
 ccmp w2?, #0x1, #0x0, eq
 b.vc L2
-movz x27, #0xb, lsl 0
-str w27, [x65535?]
+movz x4?, #0xb, lsl 0
+str w4?, [x65535?]
+mov x5?, sp
+str x5?, [x65535?, #0x38]
+adr x6?, #0x0
+str x6?, [x65535?, #0x30]
 exit_sequence x65535?
 L2:
 `},
 		{name: "64bit unsigned", _64bit: true, signed: false, exp: `
 udiv x1?, x2?, x3?
 cbnz w3?, L1
-movz x27, #0xa, lsl 0
-str w27, [x65535?]
+movz x1?, #0xa, lsl 0
+str w1?, [x65535?]
+mov x2?, sp
+str x2?, [x65535?, #0x38]
+adr x3?, #0x0
+str x3?, [x65535?, #0x30]
 exit_sequence x65535?
 L1:
 `},
 		{name: "64bit signed", _64bit: true, signed: true, exp: `
 sdiv x1?, x2?, x3?
 cbnz w3?, L1
-movz x27, #0xa, lsl 0
-str w27, [x65535?]
+movz x1?, #0xa, lsl 0
+str w1?, [x65535?]
+mov x2?, sp
+str x2?, [x65535?, #0x38]
+adr x3?, #0x0
+str x3?, [x65535?, #0x30]
 exit_sequence x65535?
 L1:
 adds xzr, x3?, #0x1
 ccmp x2?, #0x1, #0x0, eq
 b.vc L2
-movz x27, #0xb, lsl 0
-str w27, [x65535?]
+movz x4?, #0xb, lsl 0
+str w4?, [x65535?]
+mov x5?, sp
+str x5?, [x65535?, #0x38]
+adr x6?, #0x0
+str x6?, [x65535?, #0x30]
 exit_sequence x65535?
 L2:
 `},
@@ -359,22 +382,27 @@ L2:
 	}
 }
 
-func Test_exitWithCodeEncodingSize(t *testing.T) {
+func TestMachine_exitWithCode(t *testing.T) {
 	_, _, m := newSetupWithMockContext()
 	m.lowerExitWithCode(x1VReg, wazevoapi.ExitCodeGrowStack)
 	m.FlushPendingInstructions()
 	m.encode(m.perBlockHead)
-	buf := m.compiler.Buf()
-	require.Equal(t, "3b0080d23b0000b93d0840f93e1040f93b0c40f97f030091c0035fd600000014", hex.EncodeToString(buf))
-	require.Equal(t, exitWithCodeEncodingSize, len(buf))
+	require.Equal(t, `
+movz x1?, #0x1, lsl 0
+str w1?, [x1]
+mov x2?, sp
+str x2?, [x1, #0x38]
+adr x3?, #0x0
+str x3?, [x1, #0x30]
+exit_sequence x1
+`, "\n"+formatEmittedInstructionsInCurrentBlock(m)+"\n")
 }
 
 func TestMachine_lowerFpuToInt(t *testing.T) {
 	for _, tc := range []struct {
-		name          string
-		nontrapping   bool
-		expectedAsm   string
-		expectedBytes string
+		name        string
+		nontrapping bool
+		expectedAsm string
 	}{
 		{
 			name:        "trapping",
@@ -382,21 +410,28 @@ func TestMachine_lowerFpuToInt(t *testing.T) {
 			expectedAsm: `
 msr fpsr, xzr
 fcvtzu w1, s2
-mrs x27 fpsr
-subs xzr, x27, #0x1
+mrs x1? fpsr
+subs xzr, x1?, #0x1
 b.ne L2
 fcmp w2, w2
 b.vc L1
-movz x27, #0xc, lsl 0
-str w27, [x15]
+movz x2?, #0xc, lsl 0
+str w2?, [x15]
+mov x3?, sp
+str x3?, [x15, #0x38]
+adr x4?, #0x0
+str x4?, [x15, #0x30]
 exit_sequence x15
 L1:
-movz x27, #0xb, lsl 0
-str w27, [x15]
+movz x5?, #0xb, lsl 0
+str w5?, [x15]
+mov x6?, sp
+str x6?, [x15, #0x38]
+adr x7?, #0x0
+str x7?, [x15, #0x30]
 exit_sequence x15
 L2:
 `,
-			expectedBytes: "3f441bd54100391e3b443bd57f0700f1010000544020221e070000549b0180d2fb0100b9fd0940f9fe1140f9fb0d40f97f030091c0035fd6000000147b0180d2fb0100b9fd0940f9fe1140f9fb0d40f97f030091c0035fd600000014",
 		},
 		{
 			name:        "nontrapping",
@@ -404,7 +439,6 @@ L2:
 			expectedAsm: `
 fcvtzu w1, s2
 `,
-			expectedBytes: "4100391e",
 		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
@@ -414,8 +448,6 @@ fcvtzu w1, s2
 
 			m.FlushPendingInstructions()
 			m.encode(m.perBlockHead)
-			buf := m.compiler.Buf()
-			require.Equal(t, tc.expectedBytes, hex.EncodeToString(buf))
 		})
 	}
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
index e2534d96..75ddbf24 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_pro_epi_logue_test.go
@@ -19,15 +19,18 @@ func TestMachine_SetupPrologue(t *testing.T) {
 		{
 			spillSlotSize: 0,
 			exp: `
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
+	str xzr, [sp, #-0x10]!
 	udf
 `,
 		},
 		{
 			spillSlotSize: 16,
 			exp: `
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	sub sp, sp, #0x10
+	orr x27, xzr, #0x10
+	str x27, [sp, #-0x10]!
 	udf
 `,
 		},
@@ -35,11 +38,13 @@ func TestMachine_SetupPrologue(t *testing.T) {
 			spillSlotSize: 0,
 			clobberedRegs: []regalloc.VReg{v18VReg, v19VReg, x18VReg, x25VReg},
 			exp: `
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	str q18, [sp, #-0x10]!
 	str q19, [sp, #-0x10]!
 	str x18, [sp, #-0x10]!
 	str x25, [sp, #-0x10]!
+	orr x27, xzr, #0x40
+	str x27, [sp, #-0x10]!
 	udf
 `,
 		},
@@ -47,12 +52,14 @@ func TestMachine_SetupPrologue(t *testing.T) {
 			spillSlotSize: 320,
 			clobberedRegs: []regalloc.VReg{v18VReg, v19VReg, x18VReg, x25VReg},
 			exp: `
-	str x30, [sp, #-0x10]!
+	stp x30, xzr, [sp, #-0x10]!
 	sub sp, sp, #0x140
 	str q18, [sp, #-0x10]!
 	str q19, [sp, #-0x10]!
 	str x18, [sp, #-0x10]!
 	str x25, [sp, #-0x10]!
+	orr x27, xzr, #0x180
+	str x27, [sp, #-0x10]!
 	udf
 `,
 		},
@@ -90,6 +97,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 	}{
 		{
 			exp: `
+	add sp, sp, #0x10
 	ldr x30, [sp], #0x10
 	ret
 `,
@@ -98,6 +106,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 		},
 		{
 			exp: `
+	add sp, sp, #0x10
 	add sp, sp, #0x50
 	ldr x30, [sp], #0x10
 	ret
@@ -107,6 +116,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 		},
 		{
 			exp: `
+	add sp, sp, #0x10
 	ldr q27, [sp], #0x10
 	ldr q18, [sp], #0x10
 	ldr x30, [sp], #0x10
@@ -116,6 +126,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 		},
 		{
 			exp: `
+	add sp, sp, #0x10
 	ldr x25, [sp], #0x10
 	ldr x18, [sp], #0x10
 	ldr q27, [sp], #0x10
@@ -127,6 +138,7 @@ func TestMachine_SetupEpilogue(t *testing.T) {
 		},
 		{
 			exp: `
+	add sp, sp, #0x10
 	ldr x25, [sp], #0x10
 	ldr x18, [sp], #0x10
 	ldr q27, [sp], #0x10
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc_test.go b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc_test.go
index 293176d1..0221d3af 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc_test.go
@@ -1,6 +1,7 @@
 package arm64
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
@@ -62,7 +63,7 @@ func TestRegAllocFunctionImpl_ReversePostOrderBlockIterator(t *testing.T) {
 
 func TestRegAllocFunctionImpl_ReloadRegisterAfter(t *testing.T) {
 	ctx, _, m := newSetupWithMockContext()
-	m.clobberedRegs = make([]regalloc.VReg, 4) // This will make the beginning of the spill slot at 4 * 16 bytes = 64.
+	m.clobberedRegs = make([]regalloc.VReg, 3) // This will make the beginning of the spill slot at (3 + 1(frame size))* 16 bytes = 64.
 
 	ctx.typeOf = map[regalloc.VReg]ssa.Type{x1VReg: ssa.TypeI64, v1VReg: ssa.TypeF64}
 	i1, i2 := m.allocateNop(), m.allocateNop()
@@ -93,7 +94,7 @@ func TestRegAllocFunctionImpl_ReloadRegisterAfter(t *testing.T) {
 
 func TestRegAllocFunctionImpl_StoreRegisterBefore(t *testing.T) {
 	ctx, _, m := newSetupWithMockContext()
-	m.clobberedRegs = make([]regalloc.VReg, 4) // This will make the beginning of the spill slot at 4 * 16 bytes = 64.
+	m.clobberedRegs = make([]regalloc.VReg, 3) // This will make the beginning of the spill slot at (3 + 1(frame size))* 16 bytes = 64.
 
 	ctx.typeOf = map[regalloc.VReg]ssa.Type{x1VReg: ssa.TypeI64, v1VReg: ssa.TypeF64}
 	i1, i2 := m.allocateNop(), m.allocateNop()
@@ -131,8 +132,8 @@ func TestMachine_insertStoreRegisterAt(t *testing.T) {
 			spillSlotSize: 0,
 			expected: `
 	udf
-	str x1, [sp]
-	str d1, [sp, #0x8]
+	str x1, [sp, #0x10]
+	str d1, [sp, #0x18]
 	exit_sequence x30
 `,
 		},
@@ -140,9 +141,10 @@ func TestMachine_insertStoreRegisterAt(t *testing.T) {
 			spillSlotSize: 0xffff,
 			expected: `
 	udf
-	movz x27, #0xffff, lsl 0
+	movz x27, #0xf, lsl 0
+	movk x27, #0x1, lsl 16
 	str x1, [sp, x27]
-	movz x27, #0x7, lsl 0
+	movz x27, #0x17, lsl 0
 	movk x27, #0x1, lsl 16
 	str d1, [sp, x27]
 	exit_sequence x30
@@ -152,9 +154,10 @@ func TestMachine_insertStoreRegisterAt(t *testing.T) {
 			spillSlotSize: 0xffff_00,
 			expected: `
 	udf
-	orr x27, xzr, #0xffff00
+	movz x27, #0xff10, lsl 0
+	movk x27, #0xff, lsl 16
 	str x1, [sp, x27]
-	movz x27, #0xff08, lsl 0
+	movz x27, #0xff18, lsl 0
 	movk x27, #0xff, lsl 16
 	str d1, [sp, x27]
 	exit_sequence x30
@@ -186,6 +189,7 @@ func TestMachine_insertStoreRegisterAt(t *testing.T) {
 						m.insertStoreRegisterAt(v1VReg, i2, after)
 					}
 					m.rootInstr = i1
+					fmt.Println(m.Format())
 					require.Equal(t, tc.expected, m.Format())
 				})
 			}
@@ -202,8 +206,8 @@ func TestMachine_insertReloadRegisterAt(t *testing.T) {
 			spillSlotSize: 0,
 			expected: `
 	udf
-	ldr x1, [sp]
-	ldr d1, [sp, #0x8]
+	ldr x1, [sp, #0x10]
+	ldr d1, [sp, #0x18]
 	exit_sequence x30
 `,
 		},
@@ -211,9 +215,10 @@ func TestMachine_insertReloadRegisterAt(t *testing.T) {
 			spillSlotSize: 0xffff,
 			expected: `
 	udf
-	movz x27, #0xffff, lsl 0
+	movz x27, #0xf, lsl 0
+	movk x27, #0x1, lsl 16
 	ldr x1, [sp, x27]
-	movz x27, #0x7, lsl 0
+	movz x27, #0x17, lsl 0
 	movk x27, #0x1, lsl 16
 	ldr d1, [sp, x27]
 	exit_sequence x30
@@ -223,9 +228,10 @@ func TestMachine_insertReloadRegisterAt(t *testing.T) {
 			spillSlotSize: 0xffff_00,
 			expected: `
 	udf
-	orr x27, xzr, #0xffff00
+	movz x27, #0xff10, lsl 0
+	movk x27, #0xff, lsl 16
 	ldr x1, [sp, x27]
-	movz x27, #0xff08, lsl 0
+	movz x27, #0xff18, lsl 0
 	movk x27, #0xff, lsl 16
 	ldr d1, [sp, x27]
 	exit_sequence x30
@@ -257,6 +263,8 @@ func TestMachine_insertReloadRegisterAt(t *testing.T) {
 						m.insertReloadRegisterAt(v1VReg, i2, after)
 					}
 					m.rootInstr = i1
+
+					fmt.Println(m.Format())
 					require.Equal(t, tc.expected, m.Format())
 				})
 			}
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_test.go b/internal/engine/wazevo/backend/isa/arm64/machine_test.go
index b7560e92..7c54a436 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_test.go
@@ -3,6 +3,7 @@ package arm64
 import (
 	"testing"
 
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -75,3 +76,51 @@ func TestMachine_resolveAddressingMode(t *testing.T) {
 `, m.Format())
 	})
 }
+
+func TestMachine_clobberedRegSlotSize(t *testing.T) {
+	m := &machine{clobberedRegs: make([]regalloc.VReg, 10)}
+	require.Equal(t, int64(160), m.clobberedRegSlotSize())
+}
+
+func TestMachine_frameSize(t *testing.T) {
+	m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8}
+	require.Equal(t, int64(16*18), m.frameSize())
+}
+
+func TestMachine_requiredStackSize(t *testing.T) {
+	m := &machine{
+		clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8,
+		maxRequiredStackSizeForCalls: 320,
+	}
+	require.Equal(t, int64(16*18)+int64(320)+32, m.requiredStackSize())
+}
+
+func TestMachine_arg0OffsetFromSP(t *testing.T) {
+	m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8}
+	require.Equal(t, int64(16*18)+32, m.arg0OffsetFromSP())
+}
+
+func TestMachine_ret0OffsetFromSP(t *testing.T) {
+	m := &machine{
+		clobberedRegs: make([]regalloc.VReg, 10), spillSlotSize: 16 * 8,
+		currentABI: &abiImpl{argStackSize: 180},
+	}
+	require.Equal(t, int64(16*18)+32+180, m.ret0OffsetFromSP())
+}
+
+func TestMachine_getVRegSpillSlotOffsetFromSP(t *testing.T) {
+	m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlots: make(map[regalloc.VRegID]int64)}
+	id := regalloc.VRegID(1)
+	offset := m.getVRegSpillSlotOffsetFromSP(id, 8)
+	require.Equal(t, int64(160)+16, offset)
+	require.Equal(t, int64(8), m.spillSlotSize)
+	_, ok := m.spillSlots[id]
+	require.True(t, ok)
+
+	id = 100
+	offset = m.getVRegSpillSlotOffsetFromSP(id, 16)
+	require.Equal(t, int64(160)+16+8, offset)
+	require.Equal(t, int64(24), m.spillSlotSize)
+	_, ok = m.spillSlots[id]
+	require.True(t, ok)
+}
diff --git a/internal/engine/wazevo/backend/isa/arm64/unwind_stack_test.go b/internal/engine/wazevo/backend/isa/arm64/unwind_stack_test.go
new file mode 100644
index 00000000..50e12bf7
--- /dev/null
+++ b/internal/engine/wazevo/backend/isa/arm64/unwind_stack_test.go
@@ -0,0 +1,112 @@
+package arm64
+
+import (
+	"encoding/binary"
+	"testing"
+	"unsafe"
+
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+func TestUnwindStack(t *testing.T) {
+	for _, tc := range []struct {
+		name     string
+		contents []uint64
+		exp      []uintptr
+	}{
+		{
+			name: "top / with frame and arg-ret-space",
+			contents: []uint64{
+				32,                 // Frame size: 16 byte-aligned.
+				0,                  // reserved.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xaa,               // return address.
+				48,                 // size_of_arg_ret: 16-byte aligned.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+			},
+			exp: []uintptr{0xaa},
+		},
+		{
+			name: "top / without frame / with arg-ret-space",
+			contents: []uint64{
+				0,                  // Frame size: 16 byte-aligned.
+				0,                  // reserved.
+				0xaa,               // return address.
+				48,                 // size_of_arg_ret: 16-byte aligned.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+			},
+			exp: []uintptr{0xaa},
+		},
+		{
+			name: "top / without frame and arg-ret-space",
+			contents: []uint64{
+				0,    // Frame size: 16 byte-aligned.
+				0,    // reserved.
+				0xaa, // return address.
+				0,    // size_of_arg_ret: 16-byte aligned.
+			},
+			exp: []uintptr{0xaa},
+		},
+		{
+			name: "three frames",
+			contents: []uint64{
+				// ------------ first frame -------------
+				32,                 // Frame size: 16 byte-aligned.
+				0,                  // reserved.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xffffffffffffffff, // in frame.
+				0xaa,               // return address.
+				48,                 // size_of_arg_ret: 16-byte aligned.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				// ------------ second frame -----------------
+				0,                  // Frame size: 16 byte-aligned.
+				0,                  // reserved.
+				0xbb,               // return address.
+				48,                 // size_of_arg_ret: 16-byte aligned.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				0xeeeeeeeeeeeeeeee, // in arg-ret space.
+				// ------------ third frame -----------------
+				0,    // Frame size: 16 byte-aligned.
+				0,    // reserved.
+				0xcc, // return address.
+				0,    // size_of_arg_ret: 16-byte aligned.
+			},
+			exp: []uintptr{0xaa, 0xbb, 0xcc},
+		},
+	} {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			buf := make([]byte, len(tc.contents)*8+1)
+			for i, v := range tc.contents {
+				binary.LittleEndian.PutUint64(buf[i*8:], v)
+			}
+			sp := uintptr(unsafe.Pointer(&buf[0]))
+			returnAddresses := UnwindStack(sp, uintptr(unsafe.Pointer(&buf[len(buf)-1])))
+			require.Equal(t, tc.exp, returnAddresses)
+		})
+	}
+}
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index 116808b3..84320e86 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -388,7 +388,7 @@ func TestE2E(t *testing.T) {
 					require.NotNil(t, f)
 					result, err := f.Call(ctx, cc.params...)
 					if cc.expErr != "" {
-						require.EqualError(t, err, cc.expErr)
+						require.Contains(t, err.Error(), cc.expErr)
 					} else {
 						require.NoError(t, err)
 						require.Equal(t, len(cc.expResults), len(result))
@@ -600,3 +600,102 @@ func TestE2E_reexported_memory(t *testing.T) {
 	require.Equal(t, mem, m2Inst.Memory())
 	require.Equal(t, uint32(11), mem.Size()/65536)
 }
+
+func TestStackUnwind_panic_in_host(t *testing.T) {
+	unreachable := &wasm.Module{
+		ImportFunctionCount: 1,
+		ImportSection:       []wasm.Import{{Module: "host", Name: "cause_unreachable", Type: wasm.ExternTypeFunc, DescFunc: 0}},
+		TypeSection:         []wasm.FunctionType{{}},
+		ExportSection:       []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 1}},
+		FunctionSection:     []wasm.Index{0, 0, 0},
+		CodeSection: []wasm.Code{
+			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
+			{Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}},
+			{Body: []byte{wasm.OpcodeCall, 0, wasm.OpcodeEnd}}, // call host.cause_unreachable.
+		},
+		NameSection: &wasm.NameSection{
+			FunctionNames: wasm.NameMap{
+				wasm.NameAssoc{Index: 0, Name: "host.unreachable"},
+				wasm.NameAssoc{Index: 1, Name: "main"},
+				wasm.NameAssoc{Index: 2, Name: "one"},
+				wasm.NameAssoc{Index: 3, Name: "two"},
+			},
+		},
+	}
+
+	config := wazero.NewRuntimeConfigCompiler()
+
+	// Configure the new optimizing backend!
+	wazevo.ConfigureWazevo(config)
+
+	ctx := context.Background()
+	r := wazero.NewRuntimeWithConfig(ctx, config)
+	defer func() {
+		require.NoError(t, r.Close(ctx))
+	}()
+
+	callUnreachable := func() {
+		panic("panic in host function")
+	}
+
+	_, err := r.NewHostModuleBuilder("host").
+		NewFunctionBuilder().WithFunc(callUnreachable).Export("cause_unreachable").
+		Instantiate(ctx)
+	require.NoError(t, err)
+
+	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
+	require.NoError(t, err)
+	defer module.Close(ctx)
+
+	_, err = module.ExportedFunction("main").Call(ctx)
+	exp := `panic in host function (recovered by wazero)
+wasm stack trace:
+	host.cause_unreachable()
+	.two()
+	.one()
+	.main()`
+	require.Equal(t, exp, err.Error())
+}
+
+func TestStackUnwind_unreachable(t *testing.T) {
+	unreachable := &wasm.Module{
+		TypeSection:     []wasm.FunctionType{{}},
+		ExportSection:   []wasm.Export{{Name: "main", Type: wasm.ExternTypeFunc, Index: 0}},
+		FunctionSection: []wasm.Index{0, 0, 0},
+		CodeSection: []wasm.Code{
+			{Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}},
+			{Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
+			{Body: []byte{wasm.OpcodeUnreachable, wasm.OpcodeEnd}},
+		},
+		NameSection: &wasm.NameSection{
+			FunctionNames: wasm.NameMap{
+				wasm.NameAssoc{Index: 0, Name: "main"},
+				wasm.NameAssoc{Index: 1, Name: "one"},
+				wasm.NameAssoc{Index: 2, Name: "two"},
+			},
+		},
+	}
+
+	config := wazero.NewRuntimeConfigCompiler()
+
+	// Configure the new optimizing backend!
+	wazevo.ConfigureWazevo(config)
+
+	ctx := context.Background()
+	r := wazero.NewRuntimeWithConfig(ctx, config)
+	defer func() {
+		require.NoError(t, r.Close(ctx))
+	}()
+
+	module, err := r.Instantiate(ctx, binaryencoding.EncodeModule(unreachable))
+	require.NoError(t, err)
+	defer module.Close(ctx)
+
+	_, err = module.ExportedFunction("main").Call(ctx)
+	exp := `wasm error: unreachable
+wasm stack trace:
+	.two()
+	.one()
+	.main()`
+	require.Equal(t, exp, err.Error())
+}
diff --git a/internal/engine/wazevo/engine_test.go b/internal/engine/wazevo/engine_test.go
index 1cc43286..ae6b13ca 100644
--- a/internal/engine/wazevo/engine_test.go
+++ b/internal/engine/wazevo/engine_test.go
@@ -3,7 +3,9 @@ package wazevo
 import (
 	"context"
 	"fmt"
+	"reflect"
 	"testing"
+	"unsafe"
 
 	"github.com/tetratelabs/wazero/internal/platform"
 	"github.com/tetratelabs/wazero/internal/testing/require"
@@ -76,3 +78,107 @@ func TestEngine_CompileModule(t *testing.T) {
 		v(k)
 	}
 }
+
+func TestEngine_sortedCompiledModules(t *testing.T) {
+	getCM := func(addr uintptr) *compiledModule {
+		var buf []byte
+		{
+			// TODO: use unsafe.Slice after floor version is set to Go 1.20.
+			hdr := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
+			hdr.Data = addr
+			hdr.Len = 1
+			hdr.Cap = 1
+		}
+		cm := &compiledModule{executable: buf}
+		return cm
+	}
+
+	requireEqualExisting := func(t *testing.T, e *engine, expected []uintptr) {
+		actual := make([]uintptr, 0)
+		for _, cm := range e.sortedCompiledModules {
+			actual = append(actual, uintptr(unsafe.Pointer(&cm.executable[0])))
+		}
+		require.Equal(t, expected, actual)
+	}
+
+	m1 := getCM(1)
+	m100 := getCM(100)
+	m5 := getCM(5)
+	m10 := getCM(10)
+
+	t.Run("add", func(t *testing.T) {
+		e := &engine{}
+		e.addCompiledModuleToSortedList(m1)
+		e.addCompiledModuleToSortedList(m100)
+		e.addCompiledModuleToSortedList(m5)
+		e.addCompiledModuleToSortedList(m10)
+		requireEqualExisting(t, e, []uintptr{1, 5, 10, 100})
+	})
+	t.Run("delete", func(t *testing.T) {
+		e := &engine{}
+		e.addCompiledModuleToSortedList(m1)
+		e.addCompiledModuleToSortedList(m100)
+		e.addCompiledModuleToSortedList(m5)
+		e.addCompiledModuleToSortedList(m10)
+		e.deleteCompiledModuleFromSortedList(m100)
+		require.Equal(t, 3, len(e.sortedCompiledModules))
+		requireEqualExisting(t, e, []uintptr{1, 5, 10})
+		e.deleteCompiledModuleFromSortedList(m1)
+		requireEqualExisting(t, e, []uintptr{5, 10})
+		e.deleteCompiledModuleFromSortedList(m10)
+		requireEqualExisting(t, e, []uintptr{5})
+		e.deleteCompiledModuleFromSortedList(m5)
+		requireEqualExisting(t, e, []uintptr{})
+	})
+
+	t.Run("OfAddr", func(t *testing.T) {
+		e := &engine{}
+		e.addCompiledModuleToSortedList(m1)
+		e.addCompiledModuleToSortedList(m100)
+		e.addCompiledModuleToSortedList(m5)
+		e.addCompiledModuleToSortedList(m10)
+
+		require.Equal(t, nil, e.compiledModuleOfAddr(0))
+		require.Equal(t, unsafe.Pointer(m1), unsafe.Pointer(e.compiledModuleOfAddr(1)))
+		require.Equal(t, unsafe.Pointer(m1), unsafe.Pointer(e.compiledModuleOfAddr(4)))
+		require.Equal(t, unsafe.Pointer(m5), unsafe.Pointer(e.compiledModuleOfAddr(5)))
+		require.Equal(t, unsafe.Pointer(m5), unsafe.Pointer(e.compiledModuleOfAddr(9)))
+		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(10)))
+		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(11)))
+		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(12)))
+		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(50)))
+		require.Equal(t, unsafe.Pointer(m10), unsafe.Pointer(e.compiledModuleOfAddr(99)))
+		require.Equal(t, unsafe.Pointer(m100), unsafe.Pointer(e.compiledModuleOfAddr(100)))
+		require.Equal(t, unsafe.Pointer(m100), unsafe.Pointer(e.compiledModuleOfAddr(10000)))
+	})
+}
+
+func TestCompiledModule_functionIndexOf(t *testing.T) {
+	const executableAddr = 0xaaaa
+	var executable []byte
+	{
+		// TODO: use unsafe.Slice after floor version is set to Go 1.20.
+		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&executable))
+		hdr.Data = executableAddr
+		hdr.Len = 0xffff
+		hdr.Cap = 0xffff
+	}
+
+	cm := &compiledModule{
+		executable: executable,
+		functionOffsets: []compiledFunctionOffset{
+			{offset: 0, goPreambleSize: 100},
+			{offset: 500, goPreambleSize: 200},
+			{offset: 1000, goPreambleSize: 0},
+			{offset: 2000, goPreambleSize: 0},
+		},
+	}
+	require.Equal(t, wasm.Index(0), cm.functionIndexOf(executableAddr))
+	require.Equal(t, wasm.Index(0), cm.functionIndexOf(executableAddr+499))
+	require.Equal(t, wasm.Index(1), cm.functionIndexOf(executableAddr+500))
+	require.Equal(t, wasm.Index(1), cm.functionIndexOf(executableAddr+999))
+	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1000))
+	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1500))
+	require.Equal(t, wasm.Index(2), cm.functionIndexOf(executableAddr+1999))
+	require.Equal(t, wasm.Index(3), cm.functionIndexOf(executableAddr+2000))
+}
diff --git a/internal/engine/wazevo/wazevo_test.go b/internal/engine/wazevo/wazevo_test.go
index 0f86ccea..f4bf741f 100644
--- a/internal/engine/wazevo/wazevo_test.go
+++ b/internal/engine/wazevo/wazevo_test.go
@@ -39,7 +39,7 @@ func TestEngine_DeleteCompiledModule(t *testing.T) {
 	e, ok := NewEngine(ctx, api.CoreFeaturesV1, nil).(*engine)
 	require.True(t, ok)
 	id := wasm.ModuleID{0xaa}
-	e.compiledModules[id] = &compiledModule{}
+	e.addCompiledModule(&wasm.Module{ID: id}, &compiledModule{executable: make([]byte, 1)})
 	require.Equal(t, uint32(1), e.CompiledModuleCount())
 	e.DeleteCompiledModule(&wasm.Module{ID: id})
 	require.Equal(t, uint32(0), e.CompiledModuleCount())
@@ -55,7 +55,7 @@ func Test_ExecutionContextOffsets(t *testing.T) {
 	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.StackPointerBeforeGrow)
+	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.stackGrowCallSequenceAddress)), offsets.StackGrowCallSequenceAddress)