Repository: tetratelabs/wazero
Pull request #1686 (2023-09-05 01:27:22)

wazevo: adds tests for high register pressure
#1496 


Patch:
diff --git a/internal/engine/wazevo/backend/compiler.go b/internal/engine/wazevo/backend/compiler.go
index d766fcc3..14f0f4bb 100644
--- a/internal/engine/wazevo/backend/compiler.go
+++ b/internal/engine/wazevo/backend/compiler.go
@@ -11,18 +11,23 @@ import (
 )
 
 // NewCompiler returns a new Compiler that can generate a machine code.
-func NewCompiler(mach Machine, builder ssa.Builder) Compiler {
-	return newCompiler(mach, builder)
+func NewCompiler(ctx context.Context, mach Machine, builder ssa.Builder) Compiler {
+	return newCompiler(ctx, mach, builder)
 }
 
-func newCompiler(mach Machine, builder ssa.Builder) *compiler {
+func newCompiler(ctx context.Context, mach Machine, builder ssa.Builder) *compiler {
+	registerSetDebug := false
+	if wazevoapi.RegAllocValidationEnabled {
+		registerSetDebug = wazevoapi.IsHighRegisterPressure(ctx)
+	}
+
 	c := &compiler{
 		mach: mach, ssaBuilder: builder,
 		alreadyLowered:  make(map[*ssa.Instruction]bool),
 		vRegSet:         make(map[regalloc.VReg]bool),
 		ssaTypeOfVRegID: make(map[regalloc.VRegID]ssa.Type),
 		nextVRegID:      0,
-		regAlloc:        regalloc.NewAllocator(mach.RegisterInfo()),
+		regAlloc:        regalloc.NewAllocator(mach.RegisterInfo(registerSetDebug)),
 	}
 	mach.SetCompiler(c)
 	return c
@@ -147,21 +152,21 @@ type compiler struct {
 func (c *compiler) Compile(ctx context.Context) ([]byte, []RelocationInfo, int, error) {
 	c.Lower()
 	if wazevoapi.PrintSSAToBackendIRLowering {
-		fmt.Printf("[[[after lowering]]]%s\n", c.Format())
+		fmt.Printf("[[[after lowering for %s ]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), c.Format())
 	}
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
 		wazevoapi.VerifyOrSetDeterministicCompilationContextValue(ctx, "After lowering to ISA specific IR", c.Format())
 	}
 	c.RegAlloc()
 	if wazevoapi.PrintRegisterAllocated {
-		fmt.Printf("[[[after regalloc]]]%s\n", c.Format())
+		fmt.Printf("[[[after regalloc for %s]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), c.Format())
 	}
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
 		wazevoapi.VerifyOrSetDeterministicCompilationContextValue(ctx, "After Register Allocation", c.Format())
 	}
 	c.Finalize()
 	if wazevoapi.PrintFinalizedMachineCode {
-		fmt.Printf("[[[after finalize]]]%s\n", c.Format())
+		fmt.Printf("[[[after finalize for %s]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), c.Format())
 	}
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
 		wazevoapi.VerifyOrSetDeterministicCompilationContextValue(ctx, "After Finalization", c.Format())
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi.go b/internal/engine/wazevo/backend/isa/arm64/abi.go
index 3ab4b05f..7c037a02 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi.go
@@ -210,8 +210,7 @@ func (a *abiImpl) CalleeGenVRegsToFunctionReturns(rets []ssa.Value) {
 		if def := a.m.compiler.ValueDefinition(ret); def.IsFromInstr() {
 			// Constant instructions are inlined.
 			if inst := def.Instr; inst.Constant() {
-				a.m.InsertLoadConstant(inst, r.Reg)
-				continue
+				a.m.InsertLoadConstant(inst, reg)
 			}
 		}
 		if r.Kind == backend.ABIArgKindReg {
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index 084b70bd..d5b4ba95 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -1761,11 +1761,17 @@ func (i *instruction) size() int64 {
 	case nop0:
 		return 0
 	case loadFpuConst32:
+		if i.u1 == 0 {
+			return 4 // zero loading can be encoded as a single instruction.
+		}
 		return 4 + 4 + 4
 	case loadFpuConst64:
+		if i.u1 == 0 {
+			return 4 // zero loading can be encoded as a single instruction.
+		}
 		return 4 + 4 + 8
 	case loadFpuConst128:
-		return 4 + 4 + 12
+		panic("TODO")
 	case brTableSequence:
 		return 4*4 + int64(len(i.targets))*4
 	default:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index 271d9e1a..622cd40d 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -106,9 +106,19 @@ func (i *instruction) encode(c backend.Compiler) {
 		}
 		c.Emit4Bytes(encodePreOrPostIndexLoadStorePair64(pre, kind == loadP64, rn, rt, rt2, amode.imm))
 	case loadFpuConst32:
-		encodeLoadFpuConst32(c, regNumberInEncoding[i.rd.realReg()], i.u1)
+		rd := regNumberInEncoding[i.rd.realReg()]
+		if i.u1 == 0 {
+			c.Emit4Bytes(encodeVecRRR(vecOpEOR, rd, rd, rd, vecArrangement8B))
+		} else {
+			encodeLoadFpuConst32(c, rd, i.u1)
+		}
 	case loadFpuConst64:
-		encodeLoadFpuConst64(c, regNumberInEncoding[i.rd.realReg()], i.u1)
+		rd := regNumberInEncoding[i.rd.realReg()]
+		if i.u1 == 0 {
+			c.Emit4Bytes(encodeVecRRR(vecOpEOR, rd, rd, rd, vecArrangement8B))
+		} else {
+			encodeLoadFpuConst64(c, regNumberInEncoding[i.rd.realReg()], i.u1)
+		}
 	case aluRRRR:
 		c.Emit4Bytes(encodeAluRRRR(
 			aluOp(i.u1),
@@ -591,7 +601,7 @@ func encodeLoadFpuConst32(c backend.Compiler, rd uint32, rawF32 uint64) {
 
 // encodeLoadFpuConst64 encodes the following three instructions:
 //
-//	ldr x8, #8  ;; literal load of data.f64
+//	ldr d8, #8  ;; literal load of data.f64
 //	b 12           ;; skip the data
 //	data.f64 xxxxxxx
 func encodeLoadFpuConst64(c backend.Compiler, rd uint32, rawF64 uint64) {
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
index d3cf5a43..f88affe4 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
@@ -28,21 +28,11 @@ func (m *machine) InsertLoadConstant(instr *ssa.Instruction, vr regalloc.VReg) {
 	switch valType {
 	case ssa.TypeF32:
 		loadF := m.allocateInstr()
-		if v == 0 {
-			// Fast path for zero. Use v30VReg (least likely to be allocated real register) so that we don't put pressure on the register allocator.
-			loadF.asVecRRR(vecOpEOR, operandNR(vr), operandNR(v30VReg), operandNR(v30VReg), vecArrangement8B)
-		} else {
-			loadF.asLoadFpuConst32(vr, v)
-		}
+		loadF.asLoadFpuConst32(vr, v)
 		m.insert(loadF)
 	case ssa.TypeF64:
 		loadF := m.allocateInstr()
-		if v == 0 {
-			// Fast path for zero. Use v30VReg (least likely to be allocated real register) so that we don't put pressure on the register allocator.
-			loadF.asVecRRR(vecOpEOR, operandNR(vr), operandNR(v30VReg), operandNR(v30VReg), vecArrangement8B)
-		} else {
-			loadF.asLoadFpuConst64(vr, v)
-		}
+		loadF.asLoadFpuConst64(vr, v)
 		m.insert(loadF)
 	case ssa.TypeI32:
 		if v == 0 {
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
index ea0a5297..6dc107b6 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine_regalloc.go
@@ -220,7 +220,20 @@ func (r *regAllocInstrImpl) IsCopy() bool {
 }
 
 // RegisterInfo implements backend.Machine.
-func (m *machine) RegisterInfo() *regalloc.RegisterInfo {
+func (m *machine) RegisterInfo(debug bool) *regalloc.RegisterInfo {
+	if debug {
+		regInfoDebug := &regalloc.RegisterInfo{}
+		regInfoDebug.CalleeSavedRegisters = regInfo.CalleeSavedRegisters
+		regInfoDebug.CallerSavedRegisters = regInfo.CallerSavedRegisters
+		regInfoDebug.RealRegToVReg = regInfo.RealRegToVReg
+		regInfoDebug.RealRegName = regInfo.RealRegName
+		regInfoDebug.AllocatableRegisters[regalloc.RegTypeFloat] = []regalloc.RealReg{
+			v7, v6, v5, v4, v3, v2, v1, v0, // Allocatable sets == Argument registers.
+		}
+		// TODO: tests for high pressured int registers.
+		regInfoDebug.AllocatableRegisters[regalloc.RegTypeInt] = regInfo.AllocatableRegisters[regalloc.RegTypeInt]
+		return regInfoDebug
+	}
 	return regInfo
 }
 
diff --git a/internal/engine/wazevo/backend/machine.go b/internal/engine/wazevo/backend/machine.go
index dacca011..f051221c 100644
--- a/internal/engine/wazevo/backend/machine.go
+++ b/internal/engine/wazevo/backend/machine.go
@@ -14,7 +14,9 @@ type (
 
 		// RegisterInfo returns the set of registers that can be used for register allocation.
 		// This is only called once, and the result is shared across all compilations.
-		RegisterInfo() *regalloc.RegisterInfo
+		//
+		// If debug is true, this returns the register set for debugging purpose.
+		RegisterInfo(debug bool) *regalloc.RegisterInfo
 
 		// InitializeABI initializes the FunctionABI for the given signature.
 		InitializeABI(sig *ssa.Signature)
diff --git a/internal/engine/wazevo/backend/regalloc/regalloc.go b/internal/engine/wazevo/backend/regalloc/regalloc.go
index c8f874a9..d4fa30ee 100644
--- a/internal/engine/wazevo/backend/regalloc/regalloc.go
+++ b/internal/engine/wazevo/backend/regalloc/regalloc.go
@@ -55,8 +55,10 @@ type (
 	// Allocator is a register allocator.
 	Allocator struct {
 		// regInfo is static per ABI/ISA, and is initialized by the machine during Machine.PrepareRegisterAllocator.
-		regInfo                  *RegisterInfo
-		allocatableSet           map[RealReg]struct{}
+		regInfo *RegisterInfo
+		// allocatableSet is a set of allocatable RealReg derived from regInfo. Static per ABI/ISA.
+		allocatableSet map[RealReg]struct{}
+		// allocatedRegSet is a set of RealReg that are allocated during the allocation phase. This is reset per function.
 		allocatedRegSet          map[RealReg]struct{}
 		allocatedCalleeSavedRegs []VReg
 		nodePool                 wazevoapi.Pool[node]
@@ -473,13 +475,6 @@ func (a *Allocator) Reset() {
 		a.vRegIDToNode[i] = nil
 	}
 	rr := a.realRegs[:0]
-	for r := range a.allocatableSet {
-		rr = append(rr, r)
-	}
-	for _, r := range rr {
-		delete(a.allocatableSet, r)
-	}
-	rr = rr[:0]
 	for r := range a.allocatedRegSet {
 		rr = append(rr, r)
 	}
diff --git a/internal/engine/wazevo/engine.go b/internal/engine/wazevo/engine.go
index 8e5a81f6..366a6ce0 100644
--- a/internal/engine/wazevo/engine.go
+++ b/internal/engine/wazevo/engine.go
@@ -65,9 +65,9 @@ func (c compiledFunctionOffset) nativeBegin() int {
 var _ wasm.Engine = (*engine)(nil)
 
 // NewEngine returns the implementation of wasm.Engine.
-func NewEngine(_ context.Context, _ api.CoreFeatures, _ filecache.Cache) wasm.Engine {
+func NewEngine(ctx context.Context, _ api.CoreFeatures, _ filecache.Cache) wasm.Engine {
 	e := &engine{compiledModules: make(map[wasm.ModuleID]*compiledModule), refToBinaryOffset: make(map[ssa.FuncRef]int)}
-	e.compileBuiltinFunctions()
+	e.compileBuiltinFunctions(ctx)
 	return e
 }
 
@@ -98,7 +98,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 	cm := &compiledModule{offsets: wazevoapi.NewModuleContextOffsetData(module)}
 
 	if module.IsHostModule {
-		return e.compileHostModule(module)
+		return e.compileHostModule(ctx, module)
 	}
 
 	importedFns, localFns := int(module.ImportFunctionCount), len(module.FunctionSection)
@@ -124,7 +124,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 	ssaBuilder := ssa.NewBuilder()
 	fe := frontend.NewFrontendCompiler(module, ssaBuilder, &cm.offsets)
 	machine := newMachine()
-	be := backend.NewCompiler(machine, ssaBuilder)
+	be := backend.NewCompiler(ctx, machine, ssaBuilder)
 
 	totalSize := 0 // Total binary size of the executable.
 	cm.functionOffsets = make([]compiledFunctionOffset, localFns)
@@ -136,6 +136,15 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 
 		fidx := wasm.Index(i + importedFns)
 
+		if wazevoapi.NeedFunctionNameInContext {
+			def := module.FunctionDefinition(fidx)
+			name := def.DebugName()
+			if len(def.ExportNames()) > 0 {
+				name = def.ExportNames()[0]
+			}
+			ctx = wazevoapi.SetCurrentFunctionName(ctx, fmt.Sprintf("[%d/%d] \"%s\"", i, len(module.CodeSection)-1, name))
+		}
+
 		_, needGoEntryPreamble := exportedFnIndex[fidx]
 		if sf := module.StartSection; sf != nil && *sf == fidx {
 			needGoEntryPreamble = true
@@ -169,8 +178,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
 		bodies[i] = body
 		totalSize += len(body)
 		if wazevoapi.PrintMachineCodeHexPerFunction {
-			fmt.Printf("[[[machine code SSA for %d/%d %s]]]\n%s\n",
-				i, len(module.CodeSection)-1, exportedFnIndex[fidx], hex.EncodeToString(body))
+			fmt.Printf("[[[machine code for %s]]]\n%s\n\n", wazevoapi.GetCurrentFunctionName(ctx), hex.EncodeToString(body))
 		}
 	}
 
@@ -228,17 +236,10 @@ func (e *engine) compileLocalWasmFunction(
 	}
 
 	if wazevoapi.PrintSSA {
-		def := module.FunctionDefinition(functionIndex)
-		fmt.Printf("[[[SSA for %d/%d %s]]]%s\n", localFunctionIndex, len(module.CodeSection)-1, def.Debugname, ssaBuilder.Format())
+		fmt.Printf("[[[SSA for %s]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), ssaBuilder.Format())
 	}
 
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
-		def := module.FunctionDefinition(functionIndex)
-		name := def.DebugName()
-		if len(def.ExportNames()) > 0 {
-			name = def.ExportNames()[0]
-		}
-		ctx = wazevoapi.DeterministicCompilationVerifierSetCurrentFunctionName(ctx, fmt.Sprintf("[%d/%d]:\"%s\"", localFunctionIndex, len(module.CodeSection)-1, name))
 		wazevoapi.VerifyOrSetDeterministicCompilationContextValue(ctx, "SSA", ssaBuilder.Format())
 	}
 
@@ -246,8 +247,7 @@ func (e *engine) compileLocalWasmFunction(
 	ssaBuilder.RunPasses()
 
 	if wazevoapi.PrintOptimizedSSA {
-		def := module.FunctionDefinition(functionIndex)
-		fmt.Printf("[[[Optimized SSA for %d/%d %s]]]%s\n", localFunctionIndex, len(module.CodeSection)-1, def.Debugname, ssaBuilder.Format())
+		fmt.Printf("[[[Optimized SSA for %s]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), ssaBuilder.Format())
 	}
 
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
@@ -258,8 +258,7 @@ func (e *engine) compileLocalWasmFunction(
 	ssaBuilder.LayoutBlocks()
 
 	if wazevoapi.PrintBlockLaidOutSSA {
-		def := module.FunctionDefinition(functionIndex)
-		fmt.Printf("[[[Laidout SSA for %d/%d %s]]]%s\n", localFunctionIndex, len(module.CodeSection)-1, def.Debugname, ssaBuilder.Format())
+		fmt.Printf("[[[Laidout SSA for %s]]]%s\n", wazevoapi.GetCurrentFunctionName(ctx), ssaBuilder.Format())
 	}
 
 	if wazevoapi.DeterministicCompilationVerifierEnabled {
@@ -279,9 +278,9 @@ func (e *engine) compileLocalWasmFunction(
 	return copied, rels, goPreambleSize, nil
 }
 
-func (e *engine) compileHostModule(module *wasm.Module) (*compiledModule, error) {
+func (e *engine) compileHostModule(ctx context.Context, module *wasm.Module) (*compiledModule, error) {
 	machine := newMachine()
-	be := backend.NewCompiler(machine, ssa.NewBuilder())
+	be := backend.NewCompiler(ctx, machine, ssa.NewBuilder())
 
 	num := len(module.CodeSection)
 	cm := &compiledModule{}
@@ -429,9 +428,9 @@ func (e *engine) NewModuleEngine(m *wasm.Module, mi *wasm.ModuleInstance) (wasm.
 	return me, nil
 }
 
-func (e *engine) compileBuiltinFunctions() {
+func (e *engine) compileBuiltinFunctions(ctx context.Context) {
 	machine := newMachine()
-	be := backend.NewCompiler(machine, ssa.NewBuilder())
+	be := backend.NewCompiler(ctx, machine, ssa.NewBuilder())
 	e.builtinFunctions = &builtinFunctions{}
 
 	{
diff --git a/internal/engine/wazevo/wazevoapi/debug_consts.go b/internal/engine/wazevo/wazevoapi/debug_options.go
similarity index 81%
rename from internal/engine/wazevo/wazevoapi/debug_consts.go
rename to internal/engine/wazevo/wazevoapi/debug_options.go
index ce357025..f98d3648 100644
--- a/internal/engine/wazevo/wazevoapi/debug_consts.go
+++ b/internal/engine/wazevo/wazevoapi/debug_options.go
@@ -96,11 +96,6 @@ func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context
 	return ret
 }
 
-// DeterministicCompilationVerifierSetCurrentFunctionName sets the current function name to the given `functionName`.
-func DeterministicCompilationVerifierSetCurrentFunctionName(ctx context.Context, functionName string) context.Context {
-	return context.WithValue(ctx, currentFunctionNameKey{}, functionName)
-}
-
 // VerifyOrSetDeterministicCompilationContextValue verifies that the `newValue` is the same as the previous value for the given `scope`
 // and the current function name. If the previous value doesn't exist, it sets the value to the given `newValue`.
 //
@@ -133,3 +128,38 @@ This is mostly due to (but might not be limited to):
 		os.Exit(1)
 	}
 }
+
+// nolint
+const NeedFunctionNameInContext = PrintSSA ||
+	PrintOptimizedSSA ||
+	PrintBlockLaidOutSSA ||
+	PrintSSAToBackendIRLowering ||
+	PrintRegisterAllocated ||
+	PrintFinalizedMachineCode ||
+	PrintMachineCodeHexPerFunction ||
+	DeterministicCompilationVerifierEnabled
+
+// SetCurrentFunctionName sets the current function name to the given `functionName`.
+func SetCurrentFunctionName(ctx context.Context, functionName string) context.Context {
+	return context.WithValue(ctx, currentFunctionNameKey{}, functionName)
+}
+
+// GetCurrentFunctionName returns the current function name.
+func GetCurrentFunctionName(ctx context.Context) string {
+	return ctx.Value(currentFunctionNameKey{}).(string)
+}
+
+// ----- High Register Pressure -----
+
+type highRegisterPressureContextKey struct{}
+
+// EnableHighRegisterPressure enables the high register pressure mode.
+func EnableHighRegisterPressure(ctx context.Context) context.Context {
+	ctx = context.WithValue(ctx, highRegisterPressureContextKey{}, true)
+	return ctx
+}
+
+// IsHighRegisterPressure returns true if the current compilation is under high register pressure.
+func IsHighRegisterPressure(ctx context.Context) bool {
+	return ctx.Value(highRegisterPressureContextKey{}) != nil
+}


Test patch:
diff --git a/internal/engine/wazevo/backend/backend_test.go b/internal/engine/wazevo/backend/backend_test.go
index 643246e4..b7cbe8a3 100644
--- a/internal/engine/wazevo/backend/backend_test.go
+++ b/internal/engine/wazevo/backend/backend_test.go
@@ -1,6 +1,7 @@
 package backend_test
 
 import (
+	"context"
 	"encoding/hex"
 	"fmt"
 	"os"
@@ -80,10 +81,14 @@ L1 (SSA Block: blk0):
 			name: "consts", m: testcases.Constants.Module,
 			afterLoweringARM64: `
 L1 (SSA Block: blk0):
-	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
+	ldr d5?, #8; b 16; data.f64 64.000000
+	mov v1.8b, v5?.8b
+	ldr s4?, #8; b 8; data.f32 32.000000
+	mov v0.8b, v4?.8b
+	orr x3?, xzr, #0x2
+	mov x1, x3?
+	orr w2?, wzr, #0x1
+	mov x0, x2?
 	ret
 `,
 			afterFinalizeARM64: `
@@ -169,7 +174,8 @@ L1 (SSA Block: blk0):
 			afterLoweringARM64: `
 L1 (SSA Block: blk0):
 	mov x2?, x2
-	mov x1, xzr
+	mov x3?, xzr
+	mov x1, x3?
 	mov x0, x2?
 	ret
 `,
@@ -411,10 +417,12 @@ L1 (SSA Block: blk0):
 	cbnz w4?, L2
 L3 (SSA Block: blk2):
 L4 (SSA Block: blk3):
-	mov x0, xzr
+	mov x2?, xzr
+	mov x0, x2?
 	ret
 L2 (SSA Block: blk1):
-	mov x0, xzr
+	mov x3?, xzr
+	mov x0, x3?
 	ret
 `,
 			afterFinalizeARM64: `
@@ -493,7 +501,8 @@ L4 (SSA Block: blk5):
 L3 (SSA Block: blk4):
 L5 (SSA Block: blk3):
 L6 (SSA Block: blk2):
-	mov x0, xzr
+	mov x3?, xzr
+	mov x0, x3?
 	ret
 `,
 			afterFinalizeARM64: `
@@ -2974,27 +2983,33 @@ L2 (SSA Block: blk7):
 	b L9
 L3 (SSA Block: blk8):
 L10 (SSA Block: blk5):
-	orr w0, wzr, #0xc
+	orr w3?, wzr, #0xc
+	mov x0, x3?
 	ret
 L4 (SSA Block: blk9):
 L11 (SSA Block: blk4):
-	movz w0, #0xd, lsl 0
+	movz w4?, #0xd, lsl 0
+	mov x0, x4?
 	ret
 L5 (SSA Block: blk10):
 L12 (SSA Block: blk3):
-	orr w0, wzr, #0xe
+	orr w5?, wzr, #0xe
+	mov x0, x5?
 	ret
 L6 (SSA Block: blk11):
 L13 (SSA Block: blk2):
-	orr w0, wzr, #0xf
+	orr w6?, wzr, #0xf
+	mov x0, x6?
 	ret
 L7 (SSA Block: blk12):
 L14 (SSA Block: blk1):
-	orr w0, wzr, #0x10
+	orr w7?, wzr, #0x10
+	mov x0, x7?
 	ret
 L8 (SSA Block: blk13):
 L9 (SSA Block: blk6):
-	movz w0, #0xb, lsl 0
+	movz w8?, #0xb, lsl 0
+	mov x0, x8?
 	ret
 `,
 			afterFinalizeARM64: `
@@ -3045,7 +3060,7 @@ L9 (SSA Block: blk6):
 			fc := frontend.NewFrontendCompiler(tc.m, ssab, &offset)
 			machine := newMachine()
 			machine.DisableStackCheck()
-			be := backend.NewCompiler(machine, ssab)
+			be := backend.NewCompiler(context.Background(), machine, ssab)
 
 			// Lowers the Wasm to SSA.
 			typeIndex := tc.m.FunctionSection[tc.targetIndex]
diff --git a/internal/engine/wazevo/backend/compiler_lower_test.go b/internal/engine/wazevo/backend/compiler_lower_test.go
index 0eb79d45..bfdc8747 100644
--- a/internal/engine/wazevo/backend/compiler_lower_test.go
+++ b/internal/engine/wazevo/backend/compiler_lower_test.go
@@ -1,6 +1,7 @@
 package backend
 
 import (
+	"context"
 	"testing"
 
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc"
@@ -51,7 +52,7 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 					},
 				}
 
-				c := newCompiler(m, builder)
+				c := newCompiler(context.Background(), m, builder)
 				c.ssaValueDefinitions = []SSAValueDefinition{{Instr: i1}, {Instr: i2}, {Instr: f1}, {Instr: f2}}
 				c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3, 4, 5, 6, 7}
 				return c, []ssa.Value{i1.Return(), i2.Return(), f1.Return(), f2.Return()}, succ, func(t *testing.T) {
@@ -79,7 +80,7 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 				m := &mockMachine{insertMove: func(dst, src regalloc.VReg) {
 					insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
 				}}
-				c := newCompiler(m, builder)
+				c := newCompiler(context.Background(), m, builder)
 				c.ssaValueDefinitions = []SSAValueDefinition{{}, {}, {}, {}}
 				c.ssaValueToVRegs = []regalloc.VReg{0, 1, 2, 3}
 				c.nextVRegID = 100 // Temporary reg should start with 100.
@@ -117,7 +118,7 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 				m := &mockMachine{insertMove: func(dst, src regalloc.VReg) {
 					insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
 				}}
-				c := newCompiler(m, builder)
+				c := newCompiler(context.Background(), m, builder)
 				c.ssaValueDefinitions = []SSAValueDefinition{{}, {}}
 				c.ssaValueToVRegs = []regalloc.VReg{0, 1}
 				return c, []ssa.Value{add.Return()}, blk, func(t *testing.T) {
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 3563fd07..c7e7d630 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -178,9 +178,11 @@ func TestInstruction_encode(t *testing.T) {
 		}},
 		{want: "30000010", setup: func(i *instruction) { i.asAdr(v16VReg, 4) }},
 		{want: "50050030", setup: func(i *instruction) { i.asAdr(v16VReg, 169) }},
+		{want: "101e302e", setup: func(i *instruction) { i.asLoadFpuConst32(v16VReg, uint64(math.Float32bits(0))) }},
 		{want: "5000001c020000140000803f", setup: func(i *instruction) {
 			i.asLoadFpuConst32(v16VReg, uint64(math.Float32bits(1.0)))
 		}},
+		{want: "101e302e", setup: func(i *instruction) { i.asLoadFpuConst64(v16VReg, uint64(math.Float32bits(0))) }},
 		{want: "5000005c03000014000000000000f03f", setup: func(i *instruction) {
 			i.asLoadFpuConst64(v16VReg, math.Float64bits(1.0))
 		}},
diff --git a/internal/engine/wazevo/backend/isa/arm64/util_test.go b/internal/engine/wazevo/backend/isa/arm64/util_test.go
index c9c0f336..f97cb8ab 100644
--- a/internal/engine/wazevo/backend/isa/arm64/util_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/util_test.go
@@ -25,7 +25,7 @@ func formatEmittedInstructionsInCurrentBlock(m *machine) string {
 func newSetup() (ssa.Builder, *machine) {
 	m := NewBackend().(*machine)
 	ssaB := ssa.NewBuilder()
-	backend.NewCompiler(m, ssaB)
+	backend.NewCompiler(context.Background(), m, ssaB)
 	blk := ssaB.AllocateBasicBlock()
 	ssaB.SetCurrentBlock(blk)
 	return ssaB, m
diff --git a/internal/engine/wazevo/backend/machine_test.go b/internal/engine/wazevo/backend/machine_test.go
index f2a361cb..5e44a698 100644
--- a/internal/engine/wazevo/backend/machine_test.go
+++ b/internal/engine/wazevo/backend/machine_test.go
@@ -52,7 +52,7 @@ func (m mockMachine) ResolveRelativeAddresses() {}
 func (m mockMachine) Function() (f regalloc.Function) { return }
 
 // RegisterInfo implements Machine.RegisterInfo.
-func (m mockMachine) RegisterInfo() *regalloc.RegisterInfo {
+func (m mockMachine) RegisterInfo(bool) *regalloc.RegisterInfo {
 	if m.rinfo != nil {
 		return m.rinfo
 	}
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index d6021565..c1ed6dcc 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -12,6 +12,7 @@ import (
 	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo"
 	"github.com/tetratelabs/wazero/internal/engine/wazevo/testcases"
+	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
 	"github.com/tetratelabs/wazero/internal/filecache"
 	"github.com/tetratelabs/wazero/internal/integration_test/spectest"
 	v1 "github.com/tetratelabs/wazero/internal/integration_test/spectest/v1"
@@ -108,8 +109,15 @@ func TestSpectestV1(t *testing.T) {
 		{name: "unwind"},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
-			spectest.RunCase(t, v1.Testcases, tc.name, context.Background(), config,
-				-1, 0, math.MaxInt)
+			t.Run("normal", func(t *testing.T) {
+				spectest.RunCase(t, v1.Testcases, tc.name, context.Background(), config,
+					-1, 0, math.MaxInt)
+			})
+			t.Run("reg high pressure", func(t *testing.T) {
+				ctx := wazevoapi.EnableHighRegisterPressure(context.Background())
+				spectest.RunCase(t, v1.Testcases, tc.name, ctx, config,
+					-1, 0, math.MaxInt)
+			})
 		})
 	}
 }