Repository: tetratelabs/wazero
Pull request #2288 (2024-07-10 17:32:37)

backend: stop allocating ValueDefinitions
```
goos: darwin
goarch: arm64
pkg: github.com/tetratelabs/wazero
                      │  old.txt   │             new.txt              │
                      │   sec/op   │   sec/op    vs base              │
Compilation/wazero-10   1.536 ± 1%   1.535 ± 0%       ~ (p=0.589 n=6)
Compilation/zig-10      3.405 ± 1%   3.411 ± 0%       ~ (p=0.240 n=6)
Compilation/zz-10       14.03 ± 0%   14.05 ± 0%       ~ (p=0.310 n=6)
geomean                 4.186        4.190       +0.09%

                      │   old.txt    │              new.txt               │
                      │     B/op     │     B/op      vs base              │
Compilation/wazero-10   273.4Mi ± 0%   257.4Mi ± 0%  -5.84% (p=0.002 n=6)
Compilation/zig-10      594.7Mi ± 0%   592.2Mi ± 0%  -0.42% (p=0.002 n=6)
Compilation/zz-10       532.4Mi ± 0%   525.7Mi ± 0%  -1.26% (p=0.002 n=6)
geomean                 442.4Mi        431.2Mi       -2.53%

                      │   old.txt   │              new.txt              │
                      │  allocs/op  │  allocs/op   vs base              │
Compilation/wazero-10   485.3k ± 0%   485.3k ± 0%       ~ (p=0.699 n=6)
Compilation/zig-10      275.5k ± 0%   275.6k ± 0%       ~ (p=0.394 n=6)
Compilation/zz-10       626.9k ± 0%   627.0k ± 0%       ~ (p=0.394 n=6)
geomean                 437.6k        437.7k       +0.03%
```

#2182 


Patch:
diff --git a/internal/engine/wazevo/backend/compiler.go b/internal/engine/wazevo/backend/compiler.go
index aea55ee7..62d36501 100644
--- a/internal/engine/wazevo/backend/compiler.go
+++ b/internal/engine/wazevo/backend/compiler.go
@@ -69,7 +69,7 @@ type Compiler interface {
 	AllocateVReg(typ ssa.Type) regalloc.VReg
 
 	// ValueDefinition returns the definition of the given value.
-	ValueDefinition(ssa.Value) *SSAValueDefinition
+	ValueDefinition(ssa.Value) SSAValueDefinition
 
 	// VRegOf returns the virtual register of the given ssa.Value.
 	VRegOf(value ssa.Value) regalloc.VReg
@@ -79,13 +79,13 @@ type Compiler interface {
 
 	// MatchInstr returns true if the given definition is from an instruction with the given opcode, the current group ID,
 	// and a refcount of 1. That means, the instruction can be merged/swapped within the current instruction group.
-	MatchInstr(def *SSAValueDefinition, opcode ssa.Opcode) bool
+	MatchInstr(def SSAValueDefinition, opcode ssa.Opcode) bool
 
 	// MatchInstrOneOf is the same as MatchInstr but for multiple opcodes. If it matches one of ssa.Opcode,
 	// this returns the opcode. Otherwise, this returns ssa.OpcodeInvalid.
 	//
 	// Note: caller should be careful to avoid excessive allocation on opcodes slice.
-	MatchInstrOneOf(def *SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode
+	MatchInstrOneOf(def SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode
 
 	// AddRelocationInfo appends the relocation information for the function reference at the current buffer offset.
 	AddRelocationInfo(funcRef ssa.FuncRef)
@@ -126,8 +126,7 @@ type compiler struct {
 	nextVRegID regalloc.VRegID
 	// ssaValueToVRegs maps ssa.ValueID to regalloc.VReg.
 	ssaValueToVRegs [] /* VRegID to */ regalloc.VReg
-	// ssaValueDefinitions maps ssa.ValueID to its definition.
-	ssaValueDefinitions []SSAValueDefinition
+	ssaValuesInfo   []ssa.ValueInfo
 	// returnVRegs is the list of virtual registers that store the return values.
 	returnVRegs  []regalloc.VReg
 	varEdges     [][2]regalloc.VReg
@@ -204,14 +203,10 @@ func (c *compiler) setCurrentGroupID(gid ssa.InstructionGroupID) {
 // assignVirtualRegisters assigns a virtual register to each ssa.ValueID Valid in the ssa.Builder.
 func (c *compiler) assignVirtualRegisters() {
 	builder := c.ssaBuilder
-	refCounts := builder.ValuesInfo()
+	c.ssaValuesInfo = builder.ValuesInfo()
 
-	need := len(refCounts)
-	if need >= len(c.ssaValueToVRegs) {
-		c.ssaValueToVRegs = append(c.ssaValueToVRegs, make([]regalloc.VReg, need+1)...)
-	}
-	if need >= len(c.ssaValueDefinitions) {
-		c.ssaValueDefinitions = append(c.ssaValueDefinitions, make([]SSAValueDefinition, need+1)...)
+	if diff := len(c.ssaValuesInfo) - len(c.ssaValueToVRegs); diff > 0 {
+		c.ssaValueToVRegs = append(c.ssaValueToVRegs, make([]regalloc.VReg, diff+1)...)
 	}
 
 	for blk := builder.BlockIteratorReversePostOrderBegin(); blk != nil; blk = builder.BlockIteratorReversePostOrderNext() {
@@ -222,7 +217,6 @@ func (c *compiler) assignVirtualRegisters() {
 			typ := p.Type()
 			vreg := c.AllocateVReg(typ)
 			c.ssaValueToVRegs[pid] = vreg
-			c.ssaValueDefinitions[pid] = SSAValueDefinition{V: p}
 			c.ssaTypeOfVRegID[vreg.ID()] = p.Type()
 		}
 
@@ -235,11 +229,6 @@ func (c *compiler) assignVirtualRegisters() {
 				typ := r.Type()
 				vReg := c.AllocateVReg(typ)
 				c.ssaValueToVRegs[id] = vReg
-				c.ssaValueDefinitions[id] = SSAValueDefinition{
-					Instr:    cur,
-					V:        r,
-					RefCount: refCounts[id].RefCount,
-				}
 				c.ssaTypeOfVRegID[vReg.ID()] = ssaTyp
 			}
 			for _, r := range rs {
@@ -247,11 +236,6 @@ func (c *compiler) assignVirtualRegisters() {
 				ssaTyp := r.Type()
 				vReg := c.AllocateVReg(ssaTyp)
 				c.ssaValueToVRegs[id] = vReg
-				c.ssaValueDefinitions[id] = SSAValueDefinition{
-					Instr:    cur,
-					V:        r,
-					RefCount: refCounts[id].RefCount,
-				}
 				c.ssaTypeOfVRegID[vReg.ID()] = ssaTyp
 			}
 		}
@@ -293,8 +277,12 @@ func (c *compiler) Init() {
 }
 
 // ValueDefinition implements Compiler.ValueDefinition.
-func (c *compiler) ValueDefinition(value ssa.Value) *SSAValueDefinition {
-	return &c.ssaValueDefinitions[value.ID()]
+func (c *compiler) ValueDefinition(value ssa.Value) SSAValueDefinition {
+	return SSAValueDefinition{
+		V:        value,
+		Instr:    c.ssaBuilder.InstructionOfValue(value),
+		RefCount: c.ssaValuesInfo[value.ID()].RefCount,
+	}
 }
 
 // VRegOf implements Compiler.VRegOf.
@@ -313,7 +301,7 @@ func (c *compiler) TypeOf(v regalloc.VReg) ssa.Type {
 }
 
 // MatchInstr implements Compiler.MatchInstr.
-func (c *compiler) MatchInstr(def *SSAValueDefinition, opcode ssa.Opcode) bool {
+func (c *compiler) MatchInstr(def SSAValueDefinition, opcode ssa.Opcode) bool {
 	instr := def.Instr
 	return def.IsFromInstr() &&
 		instr.Opcode() == opcode &&
@@ -322,7 +310,7 @@ func (c *compiler) MatchInstr(def *SSAValueDefinition, opcode ssa.Opcode) bool {
 }
 
 // MatchInstrOneOf implements Compiler.MatchInstrOneOf.
-func (c *compiler) MatchInstrOneOf(def *SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
+func (c *compiler) MatchInstrOneOf(def SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
 	instr := def.Instr
 	if !def.IsFromInstr() {
 		return ssa.OpcodeInvalid
diff --git a/internal/engine/wazevo/backend/compiler_lower.go b/internal/engine/wazevo/backend/compiler_lower.go
index 669364c3..735cfa3d 100644
--- a/internal/engine/wazevo/backend/compiler_lower.go
+++ b/internal/engine/wazevo/backend/compiler_lower.go
@@ -152,12 +152,12 @@ func (c *compiler) lowerBlockArguments(args []ssa.Value, succ ssa.BasicBlock) {
 		src := args[i]
 
 		dstReg := c.VRegOf(dst)
-		srcDef := c.ssaValueDefinitions[src.ID()]
-		if srcDef.IsFromInstr() && srcDef.Instr.Constant() {
+		srcInstr := c.ssaBuilder.InstructionOfValue(src)
+		if srcInstr != nil && srcInstr.Constant() {
 			c.constEdges = append(c.constEdges, struct {
 				cInst *ssa.Instruction
 				dst   regalloc.VReg
-			}{cInst: srcDef.Instr, dst: dstReg})
+			}{cInst: srcInstr, dst: dstReg})
 		} else {
 			srcReg := c.VRegOf(src)
 			// Even when the src=dst, insert the move so that we can keep such registers keep-alive.
diff --git a/internal/engine/wazevo/backend/isa/amd64/lower_mem.go b/internal/engine/wazevo/backend/isa/amd64/lower_mem.go
index 818f2dbf..befe8c64 100644
--- a/internal/engine/wazevo/backend/isa/amd64/lower_mem.go
+++ b/internal/engine/wazevo/backend/isa/amd64/lower_mem.go
@@ -130,7 +130,7 @@ func (m *machine) lowerAddendsToAmode(x, y addend, offBase uint32) *amode {
 	}
 }
 
-func (m *machine) lowerAddend(x *backend.SSAValueDefinition) addend {
+func (m *machine) lowerAddend(x backend.SSAValueDefinition) addend {
 	if !x.IsFromInstr() {
 		return addend{m.c.VRegOf(x.V), 0, 0}
 	}
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine.go b/internal/engine/wazevo/backend/isa/amd64/machine.go
index 476b4f16..aeeb6b64 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine.go
@@ -1601,21 +1601,24 @@ func (m *machine) lowerExitIfTrueWithCode(execCtx regalloc.VReg, cond ssa.Value,
 	jmpIf.asJmpIf(condFromSSAIntCmpCond(c).invert(), newOperandLabel(l))
 }
 
-func (m *machine) tryLowerBandToFlag(x, y *backend.SSAValueDefinition) (ok bool) {
-	var target *backend.SSAValueDefinition
+func (m *machine) tryLowerBandToFlag(x, y backend.SSAValueDefinition) (ok bool) {
+	var target backend.SSAValueDefinition
+	var got bool
 	if x.IsFromInstr() && x.Instr.Constant() && x.Instr.ConstantVal() == 0 {
 		if m.c.MatchInstr(y, ssa.OpcodeBand) {
 			target = y
+			got = true
 		}
 	}
 
 	if y.IsFromInstr() && y.Instr.Constant() && y.Instr.ConstantVal() == 0 {
 		if m.c.MatchInstr(x, ssa.OpcodeBand) {
 			target = x
+			got = true
 		}
 	}
 
-	if target == nil {
+	if !got {
 		return false
 	}
 
@@ -1947,9 +1950,9 @@ func (m *machine) lowerCall(si *ssa.Instruction) {
 
 // callerGenVRegToFunctionArg is the opposite of GenFunctionArgToVReg, which is used to generate the
 // caller side of the function call.
-func (m *machine) callerGenVRegToFunctionArg(a *backend.FunctionABI, argIndex int, reg regalloc.VReg, def *backend.SSAValueDefinition, stackSlotSize int64) {
+func (m *machine) callerGenVRegToFunctionArg(a *backend.FunctionABI, argIndex int, reg regalloc.VReg, def backend.SSAValueDefinition, stackSlotSize int64) {
 	arg := &a.Args[argIndex]
-	if def != nil && def.IsFromInstr() {
+	if def.IsFromInstr() {
 		// Constant instructions are inlined.
 		if inst := def.Instr; inst.Constant() {
 			m.insertLoadConstant(inst, reg)
@@ -2210,7 +2213,7 @@ func (m *machine) ResolveRelocations(refToBinaryOffset []int, binary []byte, rel
 // CallTrampolineIslandInfo implements backend.Machine CallTrampolineIslandInfo.
 func (m *machine) CallTrampolineIslandInfo(_ int) (_, _ int, _ error) { return }
 
-func (m *machine) lowerIcmpToFlag(xd, yd *backend.SSAValueDefinition, _64 bool) {
+func (m *machine) lowerIcmpToFlag(xd, yd backend.SSAValueDefinition, _64 bool) {
 	x := m.getOperand_Reg(xd)
 	y := m.getOperand_Mem_Imm32_Reg(yd)
 	cmp := m.allocateInstr().asCmpRmiR(true, y, x.reg(), _64)
diff --git a/internal/engine/wazevo/backend/isa/amd64/operands.go b/internal/engine/wazevo/backend/isa/amd64/operands.go
index 46fb0f68..78797568 100644
--- a/internal/engine/wazevo/backend/isa/amd64/operands.go
+++ b/internal/engine/wazevo/backend/isa/amd64/operands.go
@@ -252,7 +252,7 @@ func (a *amode) String() string {
 	}
 }
 
-func (m *machine) getOperand_Mem_Reg(def *backend.SSAValueDefinition) (op operand) {
+func (m *machine) getOperand_Mem_Reg(def backend.SSAValueDefinition) (op operand) {
 	if !def.IsFromInstr() {
 		return newOperandReg(m.c.VRegOf(def.V))
 	}
@@ -272,7 +272,7 @@ func (m *machine) getOperand_Mem_Reg(def *backend.SSAValueDefinition) (op operan
 	return m.getOperand_Reg(def)
 }
 
-func (m *machine) getOperand_Mem_Imm32_Reg(def *backend.SSAValueDefinition) (op operand) {
+func (m *machine) getOperand_Mem_Imm32_Reg(def backend.SSAValueDefinition) (op operand) {
 	if !def.IsFromInstr() {
 		return newOperandReg(m.c.VRegOf(def.V))
 	}
@@ -287,7 +287,7 @@ func (m *machine) getOperand_Mem_Imm32_Reg(def *backend.SSAValueDefinition) (op
 	return m.getOperand_Imm32_Reg(def)
 }
 
-func (m *machine) getOperand_Imm32_Reg(def *backend.SSAValueDefinition) (op operand) {
+func (m *machine) getOperand_Imm32_Reg(def backend.SSAValueDefinition) (op operand) {
 	if !def.IsFromInstr() {
 		return newOperandReg(m.c.VRegOf(def.V))
 	}
@@ -323,7 +323,7 @@ func asImm32(val uint64, allowSignExt bool) (uint32, bool) {
 	return u32val, true
 }
 
-func (m *machine) getOperand_Reg(def *backend.SSAValueDefinition) (op operand) {
+func (m *machine) getOperand_Reg(def backend.SSAValueDefinition) (op operand) {
 	var v regalloc.VReg
 	if instr := def.Instr; instr != nil && instr.Constant() {
 		// We inline all the constant instructions so that we could reduce the register usage.
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi.go b/internal/engine/wazevo/backend/isa/arm64/abi.go
index c5e24a57..d1eaa7cd 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi.go
@@ -182,9 +182,9 @@ func (m *machine) LowerReturns(rets []ssa.Value) {
 
 // callerGenVRegToFunctionArg is the opposite of GenFunctionArgToVReg, which is used to generate the
 // caller side of the function call.
-func (m *machine) callerGenVRegToFunctionArg(a *backend.FunctionABI, argIndex int, reg regalloc.VReg, def *backend.SSAValueDefinition, slotBegin int64) {
+func (m *machine) callerGenVRegToFunctionArg(a *backend.FunctionABI, argIndex int, reg regalloc.VReg, def backend.SSAValueDefinition, slotBegin int64) {
 	arg := &a.Args[argIndex]
-	if def != nil && def.IsFromInstr() {
+	if def.IsFromInstr() {
 		// Constant instructions are inlined.
 		if inst := def.Instr; inst.Constant() {
 			val := inst.Return()
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go
index 1f564356..7a398c3d 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go
@@ -162,7 +162,7 @@ func (o operand) assignReg(v regalloc.VReg) operand {
 //
 // `mode` is used to extend the operand if the bit length is smaller than mode.bits().
 // If the operand can be expressed as operandKindImm12, `mode` is ignored.
-func (m *machine) getOperand_Imm12_ER_SR_NR(def *backend.SSAValueDefinition, mode extMode) (op operand) {
+func (m *machine) getOperand_Imm12_ER_SR_NR(def backend.SSAValueDefinition, mode extMode) (op operand) {
 	if !def.IsFromInstr() {
 		return operandNR(m.compiler.VRegOf(def.V))
 	}
@@ -179,7 +179,7 @@ func (m *machine) getOperand_Imm12_ER_SR_NR(def *backend.SSAValueDefinition, mod
 
 // getOperand_MaybeNegatedImm12_ER_SR_NR is almost the same as getOperand_Imm12_ER_SR_NR, but this might negate the immediate value.
 // If the immediate value is negated, the second return value is true, otherwise always false.
-func (m *machine) getOperand_MaybeNegatedImm12_ER_SR_NR(def *backend.SSAValueDefinition, mode extMode) (op operand, negatedImm12 bool) {
+func (m *machine) getOperand_MaybeNegatedImm12_ER_SR_NR(def backend.SSAValueDefinition, mode extMode) (op operand, negatedImm12 bool) {
 	if !def.IsFromInstr() {
 		return operandNR(m.compiler.VRegOf(def.V)), false
 	}
@@ -208,7 +208,7 @@ func (m *machine) getOperand_MaybeNegatedImm12_ER_SR_NR(def *backend.SSAValueDef
 // ensureValueNR returns an operand of either operandKindER, operandKindSR, or operandKindNR from the given value (defined by `def).
 //
 // `mode` is used to extend the operand if the bit length is smaller than mode.bits().
-func (m *machine) getOperand_ER_SR_NR(def *backend.SSAValueDefinition, mode extMode) (op operand) {
+func (m *machine) getOperand_ER_SR_NR(def backend.SSAValueDefinition, mode extMode) (op operand) {
 	if !def.IsFromInstr() {
 		return operandNR(m.compiler.VRegOf(def.V))
 	}
@@ -251,7 +251,7 @@ func (m *machine) getOperand_ER_SR_NR(def *backend.SSAValueDefinition, mode extM
 // ensureValueNR returns an operand of either operandKindSR or operandKindNR from the given value (defined by `def).
 //
 // `mode` is used to extend the operand if the bit length is smaller than mode.bits().
-func (m *machine) getOperand_SR_NR(def *backend.SSAValueDefinition, mode extMode) (op operand) {
+func (m *machine) getOperand_SR_NR(def backend.SSAValueDefinition, mode extMode) (op operand) {
 	if !def.IsFromInstr() {
 		return operandNR(m.compiler.VRegOf(def.V))
 	}
@@ -273,7 +273,7 @@ func (m *machine) getOperand_SR_NR(def *backend.SSAValueDefinition, mode extMode
 }
 
 // getOperand_ShiftImm_NR returns an operand of either operandKindShiftImm or operandKindNR from the given value (defined by `def).
-func (m *machine) getOperand_ShiftImm_NR(def *backend.SSAValueDefinition, mode extMode, shiftBitWidth byte) (op operand) {
+func (m *machine) getOperand_ShiftImm_NR(def backend.SSAValueDefinition, mode extMode, shiftBitWidth byte) (op operand) {
 	if !def.IsFromInstr() {
 		return operandNR(m.compiler.VRegOf(def.V))
 	}
@@ -289,7 +289,7 @@ func (m *machine) getOperand_ShiftImm_NR(def *backend.SSAValueDefinition, mode e
 // ensureValueNR returns an operand of operandKindNR from the given value (defined by `def).
 //
 // `mode` is used to extend the operand if the bit length is smaller than mode.bits().
-func (m *machine) getOperand_NR(def *backend.SSAValueDefinition, mode extMode) (op operand) {
+func (m *machine) getOperand_NR(def backend.SSAValueDefinition, mode extMode) (op operand) {
 	var v regalloc.VReg
 	if def.IsFromInstr() && def.Instr.Constant() {
 		// We inline all the constant instructions so that we could reduce the register usage.
diff --git a/internal/engine/wazevo/backend/vdef.go b/internal/engine/wazevo/backend/vdef.go
index 3c66a94a..47a275a3 100644
--- a/internal/engine/wazevo/backend/vdef.go
+++ b/internal/engine/wazevo/backend/vdef.go
@@ -5,7 +5,6 @@ import (
 )
 
 // SSAValueDefinition represents a definition of an SSA value.
-// TODO: this eventually should be deleted.
 type SSAValueDefinition struct {
 	V ssa.Value
 	// Instr is not nil if this is a definition from an instruction.
@@ -14,6 +13,7 @@ type SSAValueDefinition struct {
 	RefCount uint32
 }
 
+// IsFromInstr returns true if this definition is from an instruction.
 func (d *SSAValueDefinition) IsFromInstr() bool {
 	return d.Instr != nil
 }
diff --git a/internal/engine/wazevo/ssa/builder.go b/internal/engine/wazevo/ssa/builder.go
index 950e5767..43dd7d29 100644
--- a/internal/engine/wazevo/ssa/builder.go
+++ b/internal/engine/wazevo/ssa/builder.go
@@ -132,6 +132,9 @@ type Builder interface {
 
 	// BasicBlock returns the BasicBlock of the given ID.
 	BasicBlock(id BasicBlockID) BasicBlock
+
+	// InstructionOfValue returns the Instruction that produces the given Value or nil if the Value is not produced by any Instruction.
+	InstructionOfValue(v Value) *Instruction
 }
 
 // NewBuilder returns a new Builder implementation.
@@ -776,9 +779,9 @@ func (b *builder) LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock {
 	return b.sparseTree.findLCA(blk1.ID(), blk2.ID())
 }
 
-// instructionOfValue returns the instruction that produces the given Value, or nil
+// InstructionOfValue returns the instruction that produces the given Value, or nil
 // if the Value is not produced by any instruction.
-func (b *builder) instructionOfValue(v Value) *Instruction {
+func (b *builder) InstructionOfValue(v Value) *Instruction {
 	instrID := v.instructionID()
 	if instrID <= 0 {
 		return nil
diff --git a/internal/engine/wazevo/ssa/pass.go b/internal/engine/wazevo/ssa/pass.go
index 6ee26a3e..b9763791 100644
--- a/internal/engine/wazevo/ssa/pass.go
+++ b/internal/engine/wazevo/ssa/pass.go
@@ -281,28 +281,28 @@ func passDeadCodeEliminationOpt(b *builder) {
 
 		v1, v2, v3, vs := live.Args()
 		if v1.Valid() {
-			producingInst := b.instructionOfValue(v1)
+			producingInst := b.InstructionOfValue(v1)
 			if producingInst != nil {
 				liveInstructions = append(liveInstructions, producingInst)
 			}
 		}
 
 		if v2.Valid() {
-			producingInst := b.instructionOfValue(v2)
+			producingInst := b.InstructionOfValue(v2)
 			if producingInst != nil {
 				liveInstructions = append(liveInstructions, producingInst)
 			}
 		}
 
 		if v3.Valid() {
-			producingInst := b.instructionOfValue(v3)
+			producingInst := b.InstructionOfValue(v3)
 			if producingInst != nil {
 				liveInstructions = append(liveInstructions, producingInst)
 			}
 		}
 
 		for _, v := range vs {
-			producingInst := b.instructionOfValue(v)
+			producingInst := b.InstructionOfValue(v)
 			if producingInst != nil {
 				liveInstructions = append(liveInstructions, producingInst)
 			}
@@ -362,7 +362,7 @@ func passNopInstElimination(b *builder) {
 			// TODO: add more logics here.
 			case OpcodeIshl, OpcodeSshr, OpcodeUshr:
 				x, amount := cur.Arg2()
-				definingInst := b.instructionOfValue(amount)
+				definingInst := b.InstructionOfValue(amount)
 				if definingInst == nil {
 					// If there's no defining instruction, that means the amount is coming from the parameter.
 					continue


Test patch:
diff --git a/internal/engine/wazevo/backend/compiler_lower_test.go b/internal/engine/wazevo/backend/compiler_lower_test.go
index 851b4409..0674e264 100644
--- a/internal/engine/wazevo/backend/compiler_lower_test.go
+++ b/internal/engine/wazevo/backend/compiler_lower_test.go
@@ -57,7 +57,6 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 				}
 
 				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) {
 					require.Equal(t, 4, len(insertedConstInstructions))
@@ -85,7 +84,6 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 					insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
 				}}
 				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.
 				return c, []ssa.Value{v2, v1, v3 /* Swaps v1, v2 and pass v3 as-is. */}, blk, func(t *testing.T) {
@@ -123,7 +121,6 @@ func TestCompiler_lowerBlockArguments(t *testing.T) {
 					insertMoves = append(insertMoves, struct{ src, dst regalloc.VReg }{src: src, dst: dst})
 				}}
 				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) {
 					require.Equal(t, 1, len(insertMoves))
diff --git a/internal/engine/wazevo/backend/isa/amd64/lower_mem_test.go b/internal/engine/wazevo/backend/isa/amd64/lower_mem_test.go
index 1b3006c0..2516ab29 100644
--- a/internal/engine/wazevo/backend/isa/amd64/lower_mem_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/lower_mem_test.go
@@ -34,9 +34,9 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				iadd := b.AllocateInstruction().AsIadd(iconst1.Return(), iconst2.Return()).Insert(b)
 				ptr = iadd.Return()
 				offset = 3
-				ctx.definitions[iconst1.Return()] = &backend.SSAValueDefinition{Instr: iconst1}
-				ctx.definitions[iconst2.Return()] = &backend.SSAValueDefinition{Instr: iconst2}
-				ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[iconst1.Return()] = backend.SSAValueDefinition{Instr: iconst1}
+				ctx.definitions[iconst2.Return()] = backend.SSAValueDefinition{Instr: iconst2}
+				ctx.definitions[ptr] = backend.SSAValueDefinition{Instr: iadd}
 				return
 			},
 			insts: []string{
@@ -52,10 +52,10 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				iadd := b.AllocateInstruction().AsIadd(iconst1.Return(), p).Insert(b)
 				ptr = iadd.Return()
 				offset = 3
-				ctx.definitions[iconst1.Return()] = &backend.SSAValueDefinition{Instr: iconst1}
+				ctx.definitions[iconst1.Return()] = backend.SSAValueDefinition{Instr: iconst1}
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
-				ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
+				ctx.definitions[ptr] = backend.SSAValueDefinition{Instr: iadd}
 				return
 			},
 			am: newAmodeImmReg(1+3, raxVReg),
@@ -69,10 +69,10 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				ptr = iadd.Return()
 				offset = 3
 				ctx.vRegMap[p1] = raxVReg
-				ctx.definitions[p1] = &backend.SSAValueDefinition{V: p1}
+				ctx.definitions[p1] = backend.SSAValueDefinition{V: p1}
 				ctx.vRegMap[p2] = rcxVReg
-				ctx.definitions[p2] = &backend.SSAValueDefinition{V: p2}
-				ctx.definitions[ptr] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[p2] = backend.SSAValueDefinition{V: p2}
+				ctx.definitions[ptr] = backend.SSAValueDefinition{Instr: iadd}
 				return
 			},
 			am: newAmodeRegRegShift(3, raxVReg, rcxVReg, 0),
@@ -83,7 +83,7 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				ptr = b.CurrentBlock().AddParam(b, ssa.TypeI64)
 				offset = 1 << 31
 				ctx.vRegMap[ptr] = raxVReg
-				ctx.definitions[ptr] = &backend.SSAValueDefinition{V: ptr}
+				ctx.definitions[ptr] = backend.SSAValueDefinition{V: ptr}
 				return
 			},
 			insts: []string{
@@ -98,8 +98,8 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) (ptr ssa.Value, offset uint32) {
 				iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b)
 				uextend := b.AllocateInstruction().AsUExtend(iconst32.Return(), 32, 64).Insert(b)
-				ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32}
-				ctx.definitions[uextend.Return()] = &backend.SSAValueDefinition{Instr: uextend}
+				ctx.definitions[iconst32.Return()] = backend.SSAValueDefinition{Instr: iconst32}
+				ctx.definitions[uextend.Return()] = backend.SSAValueDefinition{Instr: uextend}
 				return uextend.Return(), 0
 			},
 			insts: []string{
@@ -114,9 +114,9 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				iconst64 := b.AllocateInstruction().AsIconst64(2).Insert(b)
 				ishl := b.AllocateInstruction().AsIshl(p, iconst64.Return()).Insert(b)
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
-				ctx.definitions[iconst64.Return()] = &backend.SSAValueDefinition{Instr: iconst64}
-				ctx.definitions[ishl.Return()] = &backend.SSAValueDefinition{Instr: ishl}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
+				ctx.definitions[iconst64.Return()] = backend.SSAValueDefinition{Instr: iconst64}
+				ctx.definitions[ishl.Return()] = backend.SSAValueDefinition{Instr: ishl}
 				return ishl.Return(), 1 << 30
 			},
 			insts: []string{
@@ -133,12 +133,12 @@ func TestMachine_lowerToAddressMode(t *testing.T) {
 				ishl := b.AllocateInstruction().AsIshl(p1, const2.Return()).Insert(b)
 				iadd := b.AllocateInstruction().AsIadd(p2, ishl.Return()).Insert(b)
 				ctx.vRegMap[p1] = raxVReg
-				ctx.definitions[p1] = &backend.SSAValueDefinition{V: p1}
+				ctx.definitions[p1] = backend.SSAValueDefinition{V: p1}
 				ctx.vRegMap[p2] = rcxVReg
-				ctx.definitions[p2] = &backend.SSAValueDefinition{V: p2}
-				ctx.definitions[const2.Return()] = &backend.SSAValueDefinition{Instr: const2}
-				ctx.definitions[ishl.Return()] = &backend.SSAValueDefinition{Instr: ishl}
-				ctx.definitions[iadd.Return()] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[p2] = backend.SSAValueDefinition{V: p2}
+				ctx.definitions[const2.Return()] = backend.SSAValueDefinition{Instr: const2}
+				ctx.definitions[ishl.Return()] = backend.SSAValueDefinition{Instr: ishl}
+				ctx.definitions[iadd.Return()] = backend.SSAValueDefinition{Instr: iadd}
 				return iadd.Return(), 1 << 30
 			},
 			am: newAmodeRegRegShift(1<<30, rcxVReg, raxVReg, 2),
@@ -179,7 +179,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			name: "uextend const32",
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b)
-				ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32}
+				ctx.definitions[iconst32.Return()] = backend.SSAValueDefinition{Instr: iconst32}
 				return b.AllocateInstruction().AsUExtend(iconst32.Return(), 32, 64).Insert(b)
 			},
 			exp: addend{regalloc.VRegInvalid, 123, 0},
@@ -189,7 +189,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				p := b.CurrentBlock().AddParam(b, ssa.TypeI32)
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 				return b.AllocateInstruction().AsUExtend(p, 32, 64).Insert(b)
 			},
 			exp: addend{raxVReg, 0, 0},
@@ -199,7 +199,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				p := b.CurrentBlock().AddParam(b, ssa.TypeI32)
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 				return b.AllocateInstruction().AsUExtend(p, 32, 64).Insert(b)
 			},
 			exp: addend{raxVReg, 0, 0},
@@ -208,7 +208,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			name: "sextend const32",
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				iconst32 := b.AllocateInstruction().AsIconst32(123).Insert(b)
-				ctx.definitions[iconst32.Return()] = &backend.SSAValueDefinition{Instr: iconst32}
+				ctx.definitions[iconst32.Return()] = backend.SSAValueDefinition{Instr: iconst32}
 				return b.AllocateInstruction().AsSExtend(iconst32.Return(), 32, 64).Insert(b)
 			},
 			exp: addend{regalloc.VRegInvalid, 123, 0},
@@ -218,7 +218,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				p := b.CurrentBlock().AddParam(b, ssa.TypeI32)
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 				return b.AllocateInstruction().AsSExtend(p, 32, 64).Insert(b)
 			},
 			exp: addend{raxVReg, 0, 0},
@@ -228,7 +228,7 @@ func TestMachine_lowerAddendFromInstr(t *testing.T) {
 			in: func(ctx *mockCompiler, b ssa.Builder, m *machine) *ssa.Instruction {
 				p := b.CurrentBlock().AddParam(b, ssa.TypeI32)
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 				return b.AllocateInstruction().AsSExtend(p, 32, 64).Insert(b)
 			},
 			exp: addend{raxVReg, 0, 0},
diff --git a/internal/engine/wazevo/backend/isa/amd64/machine_test.go b/internal/engine/wazevo/backend/isa/amd64/machine_test.go
index b02a5bd7..41cf87e9 100644
--- a/internal/engine/wazevo/backend/isa/amd64/machine_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/machine_test.go
@@ -39,34 +39,34 @@ func Test_asImm32(t *testing.T) {
 func TestMachine_getOperand_Reg(t *testing.T) {
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) *backend.SSAValueDefinition
+		setup        func(*mockCompiler, ssa.Builder, *machine) backend.SSAValueDefinition
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				ctx.vRegMap[1234] = raxVReg
-				return &backend.SSAValueDefinition{V: 1234, Instr: nil}
+				return backend.SSAValueDefinition{V: 1234, Instr: nil}
 			},
 			exp: newOperandReg(raxVReg),
 		},
 
 		{
 			name: "const instr",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				instr := builder.AllocateInstruction()
 				instr.AsIconst32(0xf00000f)
 				builder.InsertInstruction(instr)
 				ctx.vRegCounter = 99
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			exp:          newOperandReg(regalloc.VReg(100).SetRegType(regalloc.RegTypeInt)),
 			instructions: []string{"movl $251658255, %r100d?"},
 		},
 		{
 			name: "non const instr (single-return)",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				c := builder.AllocateInstruction()
 				sig := &ssa.Signature{Results: []ssa.Type{ssa.TypeI64}}
 				builder.DeclareSignature(sig)
@@ -74,13 +74,13 @@ func TestMachine_getOperand_Reg(t *testing.T) {
 				builder.InsertInstruction(c)
 				r := c.Return()
 				ctx.vRegMap[r] = regalloc.VReg(50)
-				return &backend.SSAValueDefinition{V: r}
+				return backend.SSAValueDefinition{V: r}
 			},
 			exp: newOperandReg(regalloc.VReg(50)),
 		},
 		{
 			name: "non const instr (multi-return)",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				c := builder.AllocateInstruction()
 				sig := &ssa.Signature{Results: []ssa.Type{ssa.TypeI64, ssa.TypeF64, ssa.TypeF64}}
 				builder.DeclareSignature(sig)
@@ -88,7 +88,7 @@ func TestMachine_getOperand_Reg(t *testing.T) {
 				builder.InsertInstruction(c)
 				_, rs := c.Returns()
 				ctx.vRegMap[rs[1]] = regalloc.VReg(50)
-				return &backend.SSAValueDefinition{V: rs[1]}
+				return backend.SSAValueDefinition{V: rs[1]}
 			},
 			exp: newOperandReg(regalloc.VReg(50)),
 		},
@@ -106,27 +106,27 @@ func TestMachine_getOperand_Reg(t *testing.T) {
 func TestMachine_getOperand_Imm32_Reg(t *testing.T) {
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) *backend.SSAValueDefinition
+		setup        func(*mockCompiler, ssa.Builder, *machine) backend.SSAValueDefinition
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param falls back to getOperand_Reg",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				ctx.vRegMap[1234] = raxVReg
-				return &backend.SSAValueDefinition{V: 1234, Instr: nil}
+				return backend.SSAValueDefinition{V: 1234, Instr: nil}
 			},
 			exp: newOperandReg(raxVReg),
 		},
 		{
 			name: "const imm 32",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				instr := builder.AllocateInstruction()
 				instr.AsIconst32(0xf00000f)
 				builder.InsertInstruction(instr)
 				ctx.vRegCounter = 99
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			exp: newOperandImm32(0xf00000f),
 		},
@@ -150,39 +150,39 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) *backend.SSAValueDefinition
+		setup        func(*mockCompiler, ssa.Builder, *machine) backend.SSAValueDefinition
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param falls back to getOperand_Imm32_Reg",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				ctx.vRegMap[1234] = raxVReg
-				return &backend.SSAValueDefinition{V: 1234, Instr: nil}
+				return backend.SSAValueDefinition{V: 1234, Instr: nil}
 			},
 			exp: newOperandReg(raxVReg),
 		},
 		{
 			name: "amode with block param",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				blk := builder.CurrentBlock()
 				ptr := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[ptr] = raxVReg
-				ctx.definitions[ptr] = &backend.SSAValueDefinition{V: ptr}
+				ctx.definitions[ptr] = backend.SSAValueDefinition{V: ptr}
 				instr := builder.AllocateInstruction()
 				instr.AsLoad(ptr, 123, ssa.TypeI64).Insert(builder)
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			exp: newOperandMem(newAmodeImmReg(123, raxVReg)),
 		},
 		{
 			name: "amode with iconst",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				iconst := builder.AllocateInstruction().AsIconst64(456).Insert(builder)
 				instr := builder.AllocateInstruction()
 				instr.AsLoad(iconst.Return(), 123, ssa.TypeI64).Insert(builder)
-				ctx.definitions[iconst.Return()] = &backend.SSAValueDefinition{Instr: iconst}
-				return &backend.SSAValueDefinition{Instr: instr}
+				ctx.definitions[iconst.Return()] = backend.SSAValueDefinition{Instr: iconst}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			instructions: []string{
 				"movabsq $579, %r1?", // r1 := 123+456
@@ -191,17 +191,17 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 		},
 		{
 			name: "amode with iconst and extend",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				iconst := builder.AllocateInstruction().AsIconst32(0xffffff).Insert(builder)
 				uextend := builder.AllocateInstruction().AsUExtend(iconst.Return(), 32, 64).Insert(builder)
 
 				instr := builder.AllocateInstruction()
 				instr.AsLoad(uextend.Return(), 123, ssa.TypeI64).Insert(builder)
 
-				ctx.definitions[uextend.Return()] = &backend.SSAValueDefinition{Instr: uextend}
-				ctx.definitions[iconst.Return()] = &backend.SSAValueDefinition{Instr: iconst}
+				ctx.definitions[uextend.Return()] = backend.SSAValueDefinition{Instr: uextend}
+				ctx.definitions[iconst.Return()] = backend.SSAValueDefinition{Instr: iconst}
 
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			instructions: []string{
 				fmt.Sprintf("movabsq $%d, %%r1?", 0xffffff+123),
@@ -210,17 +210,17 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 		},
 		{
 			name: "amode with iconst and extend",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				iconst := builder.AllocateInstruction().AsIconst32(456).Insert(builder)
 				uextend := builder.AllocateInstruction().AsUExtend(iconst.Return(), 32, 64).Insert(builder)
 
 				instr := builder.AllocateInstruction()
 				instr.AsLoad(uextend.Return(), 123, ssa.TypeI64).Insert(builder)
 
-				ctx.definitions[uextend.Return()] = &backend.SSAValueDefinition{Instr: uextend}
-				ctx.definitions[iconst.Return()] = &backend.SSAValueDefinition{Instr: iconst}
+				ctx.definitions[uextend.Return()] = backend.SSAValueDefinition{Instr: uextend}
+				ctx.definitions[iconst.Return()] = backend.SSAValueDefinition{Instr: iconst}
 
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			instructions: []string{
 				fmt.Sprintf("movabsq $%d, %%r1?", 456+123),
@@ -229,7 +229,7 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 		},
 		{
 			name: "amode with iconst and add",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				p := builder.CurrentBlock().AddParam(builder, ssa.TypeI64)
 				iconst := builder.AllocateInstruction().AsIconst64(456).Insert(builder)
 				iadd := builder.AllocateInstruction().AsIadd(iconst.Return(), p).Insert(builder)
@@ -238,17 +238,17 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 				instr.AsLoad(iadd.Return(), 789, ssa.TypeI64).Insert(builder)
 
 				ctx.vRegMap[p] = raxVReg
-				ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
-				ctx.definitions[iconst.Return()] = &backend.SSAValueDefinition{Instr: iconst}
-				ctx.definitions[iadd.Return()] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[p] = backend.SSAValueDefinition{V: p}
+				ctx.definitions[iconst.Return()] = backend.SSAValueDefinition{Instr: iconst}
+				ctx.definitions[iadd.Return()] = backend.SSAValueDefinition{Instr: iadd}
 
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			exp: newOperandMem(newAmodeImmReg(456+789, raxVReg)),
 		},
 		{
 			name: "amode with iconst, block param and add",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) *backend.SSAValueDefinition {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) backend.SSAValueDefinition {
 				iconst1 := builder.AllocateInstruction().AsIconst64(456).Insert(builder)
 				iconst2 := builder.AllocateInstruction().AsIconst64(123).Insert(builder)
 				iadd := builder.AllocateInstruction().AsIadd(iconst1.Return(), iconst2.Return()).Insert(builder)
@@ -256,11 +256,11 @@ func Test_machine_getOperand_Mem_Imm32_Reg(t *testing.T) {
 				instr := builder.AllocateInstruction()
 				instr.AsLoad(iadd.Return(), 789, ssa.TypeI64).Insert(builder)
 
-				ctx.definitions[iconst1.Return()] = &backend.SSAValueDefinition{Instr: iconst1}
-				ctx.definitions[iconst2.Return()] = &backend.SSAValueDefinition{Instr: iconst2}
-				ctx.definitions[iadd.Return()] = &backend.SSAValueDefinition{Instr: iadd}
+				ctx.definitions[iconst1.Return()] = backend.SSAValueDefinition{Instr: iconst1}
+				ctx.definitions[iconst2.Return()] = backend.SSAValueDefinition{Instr: iconst2}
+				ctx.definitions[iadd.Return()] = backend.SSAValueDefinition{Instr: iadd}
 
-				return &backend.SSAValueDefinition{Instr: instr}
+				return backend.SSAValueDefinition{Instr: instr}
 			},
 			instructions: []string{
 				fmt.Sprintf("movabsq $%d, %%r1?", 123+456+789),
@@ -306,7 +306,7 @@ L2:
 func Test_machine_lowerClz(t *testing.T) {
 	for _, tc := range []struct {
 		name     string
-		setup    func(*mockCompiler, ssa.Builder, *machine) *backend.SSAValueDefinition
+		setup    func(*mockCompiler, ssa.Builder, *machine) backend.SSAValueDefinition
 		cpuFlags platform.CpuFeatureFlags
 		typ      ssa.Type
 		exp      string
@@ -367,7 +367,7 @@ L2:
 			m.cpuFeatures = tc.cpuFlags
 
 			ctx.vRegMap[p] = raxVReg
-			ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+			ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 			ctx.vRegMap[0] = rcxVReg
 			instr := &ssa.Instruction{}
 			instr.AsClz(p)
@@ -382,7 +382,7 @@ L2:
 func TestMachine_lowerCtz(t *testing.T) {
 	for _, tc := range []struct {
 		name     string
-		setup    func(*mockCompiler, ssa.Builder, *machine) *backend.SSAValueDefinition
+		setup    func(*mockCompiler, ssa.Builder, *machine) backend.SSAValueDefinition
 		cpuFlags platform.CpuFeatureFlags
 		typ      ssa.Type
 		exp      string
@@ -441,7 +441,7 @@ L2:
 			m.cpuFeatures = tc.cpuFlags
 
 			ctx.vRegMap[p] = raxVReg
-			ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+			ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 			ctx.vRegMap[0] = rcxVReg
 			instr := &ssa.Instruction{}
 			instr.AsCtz(p)
diff --git a/internal/engine/wazevo/backend/isa/amd64/util_test.go b/internal/engine/wazevo/backend/isa/amd64/util_test.go
index 5344974a..d5bdd4f1 100644
--- a/internal/engine/wazevo/backend/isa/amd64/util_test.go
+++ b/internal/engine/wazevo/backend/isa/amd64/util_test.go
@@ -24,7 +24,7 @@ type mockCompiler struct {
 	currentGID  ssa.InstructionGroupID
 	vRegCounter int
 	vRegMap     map[ssa.Value]regalloc.VReg
-	definitions map[ssa.Value]*backend.SSAValueDefinition
+	definitions map[ssa.Value]backend.SSAValueDefinition
 	sigs        map[ssa.SignatureID]*ssa.Signature
 	typeOf      map[regalloc.VRegID]ssa.Type
 	relocs      []backend.RelocationInfo
@@ -76,7 +76,7 @@ func (m *mockCompiler) Init()                                {}
 func newMockCompilationContext() *mockCompiler { //nolint
 	return &mockCompiler{
 		vRegMap:     make(map[ssa.Value]regalloc.VReg),
-		definitions: make(map[ssa.Value]*backend.SSAValueDefinition),
+		definitions: make(map[ssa.Value]backend.SSAValueDefinition),
 		typeOf:      map[regalloc.VRegID]ssa.Type{},
 	}
 }
@@ -96,10 +96,10 @@ func (m *mockCompiler) AllocateVReg(typ ssa.Type) regalloc.VReg {
 }
 
 // ValueDefinition implements backend.Compiler.
-func (m *mockCompiler) ValueDefinition(value ssa.Value) *backend.SSAValueDefinition {
+func (m *mockCompiler) ValueDefinition(value ssa.Value) backend.SSAValueDefinition {
 	definition, exists := m.definitions[value]
 	if !exists {
-		return nil
+		return backend.SSAValueDefinition{V: value}
 	}
 	return definition
 }
@@ -114,7 +114,7 @@ func (m *mockCompiler) VRegOf(value ssa.Value) regalloc.VReg {
 }
 
 // MatchInstr implements backend.Compiler.
-func (m *mockCompiler) MatchInstr(def *backend.SSAValueDefinition, opcode ssa.Opcode) bool {
+func (m *mockCompiler) MatchInstr(def backend.SSAValueDefinition, opcode ssa.Opcode) bool {
 	instr := def.Instr
 	return def.IsFromInstr() &&
 		instr.Opcode() == opcode &&
@@ -123,7 +123,7 @@ func (m *mockCompiler) MatchInstr(def *backend.SSAValueDefinition, opcode ssa.Op
 }
 
 // MatchInstrOneOf implements backend.Compiler.
-func (m *mockCompiler) MatchInstrOneOf(def *backend.SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
+func (m *mockCompiler) MatchInstrOneOf(def backend.SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
 	for _, opcode := range opcodes {
 		if m.MatchInstr(def, opcode) {
 			return opcode
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi_test.go b/internal/engine/wazevo/backend/isa/arm64/abi_test.go
index 6f36a60e..58b9e91d 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi_test.go
@@ -41,8 +41,8 @@ func TestAbiImpl_callerGenVRegToFunctionArg_constant_inlining(t *testing.T) {
 	f64 := builder.AllocateInstruction().AsF64const(3.14).Insert(builder)
 	abi := &backend.FunctionABI{}
 	abi.Init(&ssa.Signature{Params: []ssa.Type{ssa.TypeI64, ssa.TypeF64}}, intParamResultRegs, floatParamResultRegs)
-	m.callerGenVRegToFunctionArg(abi, 0, regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), &backend.SSAValueDefinition{Instr: i64, RefCount: 1}, 0)
-	m.callerGenVRegToFunctionArg(abi, 1, regalloc.VReg(50).SetRegType(regalloc.RegTypeFloat), &backend.SSAValueDefinition{Instr: f64, RefCount: 1}, 0)
+	m.callerGenVRegToFunctionArg(abi, 0, regalloc.VReg(100).SetRegType(regalloc.RegTypeInt), backend.SSAValueDefinition{Instr: i64, RefCount: 1}, 0)
+	m.callerGenVRegToFunctionArg(abi, 1, regalloc.VReg(50).SetRegType(regalloc.RegTypeFloat), backend.SSAValueDefinition{Instr: f64, RefCount: 1}, 0)
 	require.Equal(t, `movz x100?, #0xa, lsl 0
 mov x0, x100?
 ldr d50?, #8; b 16; data.f64 3.140000
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands_test.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands_test.go
index ba7520fb..1a78039f 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands_test.go
@@ -31,28 +31,28 @@ func Test_asImm12(t *testing.T) {
 func TestMachine_getOperand_NR(t *testing.T) {
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode)
+		setup        func(*mockCompiler, ssa.Builder, *machine) (def backend.SSAValueDefinition, mode extMode)
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param - no extend",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeZeroExtend64
 			},
 			exp: operandNR(regToVReg(x4)),
 		},
 		{
 			name: "block param - zero extend",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI32)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeZeroExtend64
 			},
 			exp:          operandNR(regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)),
@@ -60,11 +60,11 @@ func TestMachine_getOperand_NR(t *testing.T) {
 		},
 		{
 			name: "block param - sign extend",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI32)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeSignExtend64
 			},
 			exp:          operandNR(regalloc.VReg(1).SetRegType(regalloc.RegTypeInt)),
@@ -72,11 +72,11 @@ func TestMachine_getOperand_NR(t *testing.T) {
 		},
 		{
 			name: "const instr",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				instr := builder.AllocateInstruction()
 				instr.AsIconst32(0xf00000f)
 				builder.InsertInstruction(instr)
-				def = &backend.SSAValueDefinition{Instr: instr, V: instr.Return()}
+				def = backend.SSAValueDefinition{Instr: instr, V: instr.Return()}
 				ctx.vRegCounter = 99
 				return
 			},
@@ -88,7 +88,7 @@ func TestMachine_getOperand_NR(t *testing.T) {
 		},
 		{
 			name: "non const instr",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				c := builder.AllocateInstruction()
 				sig := &ssa.Signature{Results: []ssa.Type{ssa.TypeI64, ssa.TypeF64, ssa.TypeF64}}
 				builder.DeclareSignature(sig)
@@ -96,7 +96,7 @@ func TestMachine_getOperand_NR(t *testing.T) {
 				builder.InsertInstruction(c)
 				_, rs := c.Returns()
 				ctx.vRegMap[rs[1]] = regalloc.VReg(50)
-				def = &backend.SSAValueDefinition{Instr: c, V: rs[1]}
+				def = backend.SSAValueDefinition{Instr: c, V: rs[1]}
 				return
 			},
 			exp: operandNR(regalloc.VReg(50)),
@@ -115,7 +115,7 @@ func TestMachine_getOperand_NR(t *testing.T) {
 func TestMachine_getOperand_SR_NR(t *testing.T) {
 	ishlWithConstAmount := func(
 		ctx *mockCompiler, builder ssa.Builder, m *machine,
-	) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+	) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 		blk := builder.CurrentBlock()
 		// (p1+p2) << amount
 		p1 := blk.AddParam(builder, ssa.TypeI64)
@@ -136,15 +136,15 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 		builder.InsertInstruction(ishl)
 
 		ctx.vRegMap[p1] = regalloc.VReg(1)
-		ctx.definitions[p1] = &backend.SSAValueDefinition{V: p1}
+		ctx.definitions[p1] = backend.SSAValueDefinition{V: p1}
 		ctx.vRegMap[p2] = regalloc.VReg(2)
-		ctx.definitions[p2] = &backend.SSAValueDefinition{V: p2}
-		ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, V: addResult}
-		ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, V: amountVal}
+		ctx.definitions[p2] = backend.SSAValueDefinition{V: p2}
+		ctx.definitions[addResult] = backend.SSAValueDefinition{Instr: add, V: addResult}
+		ctx.definitions[amountVal] = backend.SSAValueDefinition{Instr: amount, V: amountVal}
 
 		ctx.vRegMap[addResult] = regalloc.VReg(1234)
 		ctx.vRegMap[ishl.Return()] = regalloc.VReg(10)
-		def = &backend.SSAValueDefinition{Instr: ishl, V: ishl.Return()}
+		def = backend.SSAValueDefinition{Instr: ishl, V: ishl.Return()}
 		mode = extModeNone
 		verify = func(t *testing.T) {
 			require.True(t, ishl.Lowered())
@@ -155,24 +155,24 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T))
+		setup        func(*mockCompiler, ssa.Builder, *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T))
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeNone, func(t *testing.T) {}
 			},
 			exp: operandNR(regToVReg(x4)),
 		},
 		{
 			name: "ishl but not const amount",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				blk := builder.CurrentBlock()
 				// (p1+p2) << p3
 				p1 := blk.AddParam(builder, ssa.TypeI64)
@@ -188,15 +188,15 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 				builder.InsertInstruction(ishl)
 
 				ctx.vRegMap[p1] = regalloc.VReg(1)
-				ctx.definitions[p1] = &backend.SSAValueDefinition{V: p1}
+				ctx.definitions[p1] = backend.SSAValueDefinition{V: p1}
 				ctx.vRegMap[p2] = regalloc.VReg(2)
-				ctx.definitions[p2] = &backend.SSAValueDefinition{V: p2}
+				ctx.definitions[p2] = backend.SSAValueDefinition{V: p2}
 				ctx.vRegMap[p3] = regalloc.VReg(3)
-				ctx.definitions[p3] = &backend.SSAValueDefinition{V: p3}
-				ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, V: addResult}
+				ctx.definitions[p3] = backend.SSAValueDefinition{V: p3}
+				ctx.definitions[addResult] = backend.SSAValueDefinition{Instr: add, V: addResult}
 				ctx.vRegMap[addResult] = regalloc.VReg(1234) // whatever is fine.
 				ctx.vRegMap[ishl.Return()] = regalloc.VReg(10)
-				def = &backend.SSAValueDefinition{Instr: ishl, V: ishl.Return()}
+				def = backend.SSAValueDefinition{Instr: ishl, V: ishl.Return()}
 				return def, extModeNone, func(t *testing.T) {}
 			},
 			exp: operandNR(regalloc.VReg(10)),
@@ -210,7 +210,7 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 			name: "ishl with const amount with i32",
 			setup: func(
 				ctx *mockCompiler, builder ssa.Builder, m *machine,
-			) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				blk := builder.CurrentBlock()
 				// (p1+p2) << amount
 				p1 := blk.AddParam(builder, ssa.TypeI32)
@@ -231,15 +231,15 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 				builder.InsertInstruction(ishl)
 
 				ctx.vRegMap[p1] = regalloc.VReg(1)
-				ctx.definitions[p1] = &backend.SSAValueDefinition{V: p1}
+				ctx.definitions[p1] = backend.SSAValueDefinition{V: p1}
 				ctx.vRegMap[p2] = regalloc.VReg(2)
-				ctx.definitions[p2] = &backend.SSAValueDefinition{V: p2}
-				ctx.definitions[addResult] = &backend.SSAValueDefinition{Instr: add, V: addResult}
-				ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, V: amountVal}
+				ctx.definitions[p2] = backend.SSAValueDefinition{V: p2}
+				ctx.definitions[addResult] = backend.SSAValueDefinition{Instr: add, V: addResult}
+				ctx.definitions[amountVal] = backend.SSAValueDefinition{Instr: amount, V: amountVal}
 
 				ctx.vRegMap[addResult] = regalloc.VReg(1234)
 				ctx.vRegMap[ishl.Return()] = regalloc.VReg(10)
-				def = &backend.SSAValueDefinition{Instr: ishl}
+				def = backend.SSAValueDefinition{Instr: ishl}
 				mode = extModeNone
 				verify = func(t *testing.T) {
 					require.True(t, ishl.Lowered())
@@ -253,7 +253,7 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 			name: "ishl with const amount with const shift target",
 			setup: func(
 				ctx *mockCompiler, builder ssa.Builder, m *machine,
-			) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				const nextVReg = 100
 				ctx.vRegCounter = nextVReg - 1
 
@@ -265,11 +265,11 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 				ishl.AsIshl(target.Return(), amountVal)
 				builder.InsertInstruction(ishl)
 
-				ctx.definitions[targetVal] = &backend.SSAValueDefinition{Instr: target, V: targetVal}
-				ctx.definitions[amountVal] = &backend.SSAValueDefinition{Instr: amount, V: amountVal}
+				ctx.definitions[targetVal] = backend.SSAValueDefinition{Instr: target, V: targetVal}
+				ctx.definitions[amountVal] = backend.SSAValueDefinition{Instr: amount, V: amountVal}
 				ctx.vRegMap[targetVal] = regalloc.VReg(1234)
 				ctx.vRegMap[ishl.Return()] = regalloc.VReg(10)
-				def = &backend.SSAValueDefinition{Instr: ishl}
+				def = backend.SSAValueDefinition{Instr: ishl}
 				mode = extModeNone
 				verify = func(t *testing.T) {
 					require.True(t, ishl.Lowered())
@@ -282,7 +282,7 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 		},
 		{
 			name: "ishl with const amount but group id is different",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				def, mode, _ = ishlWithConstAmount(ctx, builder, m)
 				ctx.currentGID = 1230
 				return
@@ -291,7 +291,7 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 		},
 		{
 			name: "ishl with const amount but ref count is larger than 1",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				def, mode, _ = ishlWithConstAmount(ctx, builder, m)
 				def.RefCount = 10
 				return
@@ -315,7 +315,7 @@ func TestMachine_getOperand_SR_NR(t *testing.T) {
 func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 	const nextVReg = 100
 	type testCase struct {
-		setup        func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T))
+		setup        func(*mockCompiler, ssa.Builder, *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T))
 		exp          operand
 		instructions []string
 	}
@@ -331,11 +331,11 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 
 	t.Run("block param", func(t *testing.T) {
 		runner(testCase{
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeZeroExtend64, func(t *testing.T) {}
 			},
 			exp: operandNR(regToVReg(x4)),
@@ -360,7 +360,7 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 			{from: 32, to: 64, signed: false, exp: operandER(regalloc.VReg(10), extendOpUXTW, 64)},
 		} {
 			runner(testCase{
-				setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+				setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 					blk := builder.CurrentBlock()
 					v := blk.AddParam(builder, ssa.TypeI64)
 					ext := builder.AllocateInstruction()
@@ -372,8 +372,8 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 					builder.InsertInstruction(ext)
 					extArg := ext.Arg()
 					ctx.vRegMap[extArg] = regalloc.VReg(10)
-					ctx.definitions[v] = &backend.SSAValueDefinition{V: extArg}
-					def = &backend.SSAValueDefinition{Instr: ext, V: ext.Return()}
+					ctx.definitions[v] = backend.SSAValueDefinition{V: extArg}
+					def = backend.SSAValueDefinition{Instr: ext, V: ext.Return()}
 					return def, extModeNone, func(t *testing.T) {
 						require.True(t, ext.Lowered())
 					}
@@ -568,7 +568,7 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 		} {
 			t.Run(c.name, func(t *testing.T) {
 				runner(testCase{
-					setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
+					setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode, verify func(t *testing.T)) {
 						blk := builder.CurrentBlock()
 						v := blk.AddParam(builder, ssa.TypeI64)
 						ext := builder.AllocateInstruction()
@@ -583,11 +583,11 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 						ctx.vRegMap[ext.Return()] = resultVReg
 						if c.extArgConst {
 							iconst := builder.AllocateInstruction().AsIconst32(0xffff).Insert(builder)
-							m.compiler.(*mockCompiler).definitions[extArg] = &backend.SSAValueDefinition{Instr: iconst, V: iconst.Return()}
+							m.compiler.(*mockCompiler).definitions[extArg] = backend.SSAValueDefinition{Instr: iconst, V: iconst.Return()}
 						} else {
-							m.compiler.(*mockCompiler).definitions[extArg] = &backend.SSAValueDefinition{V: extArg}
+							m.compiler.(*mockCompiler).definitions[extArg] = backend.SSAValueDefinition{V: extArg}
 						}
-						def = &backend.SSAValueDefinition{Instr: ext}
+						def = backend.SSAValueDefinition{Instr: ext}
 						return def, c.mode, func(t *testing.T) {
 							require.Equal(t, c.lowered, ext.Lowered())
 						}
@@ -603,28 +603,28 @@ func TestMachine_getOperand_ER_SR_NR(t *testing.T) {
 func TestMachine_getOperand_Imm12_ER_SR_NR(t *testing.T) {
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode)
+		setup        func(*mockCompiler, ssa.Builder, *machine) (def backend.SSAValueDefinition, mode extMode)
 		exp          operand
 		instructions []string
 	}{
 		{
 			name: "block param",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeZeroExtend64
 			},
 			exp: operandNR(regToVReg(x4)),
 		},
 		{
 			name: "const imm 12 no shift",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(0xfff)
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst}
+				def = backend.SSAValueDefinition{Instr: cinst}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
@@ -632,11 +632,11 @@ func TestMachine_getOperand_Imm12_ER_SR_NR(t *testing.T) {
 		},
 		{
 			name: "const imm 12 with shift bit",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(0xabc_000)
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst}
+				def = backend.SSAValueDefinition{Instr: cinst}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
@@ -656,29 +656,29 @@ func TestMachine_getOperand_Imm12_ER_SR_NR(t *testing.T) {
 func TestMachine_getOperand_MaybeNegatedImm12_ER_SR_NR(t *testing.T) {
 	for _, tc := range []struct {
 		name         string
-		setup        func(*mockCompiler, ssa.Builder, *machine) (def *backend.SSAValueDefinition, mode extMode)
+		setup        func(*mockCompiler, ssa.Builder, *machine) (def backend.SSAValueDefinition, mode extMode)
 		exp          operand
 		expNegated   bool
 		instructions []string
 	}{
 		{
 			name: "block param",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				blk := builder.CurrentBlock()
 				v := blk.AddParam(builder, ssa.TypeI64)
 				ctx.vRegMap[v] = regToVReg(x4)
-				def = &backend.SSAValueDefinition{V: v}
+				def = backend.SSAValueDefinition{V: v}
 				return def, extModeZeroExtend64
 			},
 			exp: operandNR(regToVReg(x4)),
 		},
 		{
 			name: "const imm 12 no shift",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(0xfff)
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst}
+				def = backend.SSAValueDefinition{Instr: cinst}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
@@ -686,12 +686,12 @@ func TestMachine_getOperand_MaybeNegatedImm12_ER_SR_NR(t *testing.T) {
 		},
 		{
 			name: "const negative imm 12 no shift",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				c := int32(-1)
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(uint32(c))
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst, V: cinst.Return()}
+				def = backend.SSAValueDefinition{Instr: cinst, V: cinst.Return()}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
@@ -700,11 +700,11 @@ func TestMachine_getOperand_MaybeNegatedImm12_ER_SR_NR(t *testing.T) {
 		},
 		{
 			name: "const imm 12 with shift bit",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(0xabc_000)
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst}
+				def = backend.SSAValueDefinition{Instr: cinst}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
@@ -712,12 +712,12 @@ func TestMachine_getOperand_MaybeNegatedImm12_ER_SR_NR(t *testing.T) {
 		},
 		{
 			name: "const negated imm 12 with shift bit",
-			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def *backend.SSAValueDefinition, mode extMode) {
+			setup: func(ctx *mockCompiler, builder ssa.Builder, m *machine) (def backend.SSAValueDefinition, mode extMode) {
 				c := int32(-0xabc_000)
 				cinst := builder.AllocateInstruction()
 				cinst.AsIconst32(uint32(c))
 				builder.InsertInstruction(cinst)
-				def = &backend.SSAValueDefinition{Instr: cinst, V: cinst.Return()}
+				def = backend.SSAValueDefinition{Instr: cinst, V: cinst.Return()}
 				ctx.currentGID = 0xff // const can be merged anytime, regardless of the group id.
 				return
 			},
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 9b8858a3..28101934 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
@@ -46,9 +46,9 @@ func TestMachine_LowerConditionalBranch(t *testing.T) {
 		cmpVal := cmpInstr.Return()
 		ctx.vRegMap[cmpVal] = 3
 
-		ctx.definitions[val1] = &backend.SSAValueDefinition{V: val1}
-		ctx.definitions[val2] = &backend.SSAValueDefinition{V: val2}
-		ctx.definitions[cmpVal] = &backend.SSAValueDefinition{Instr: cmpInstr}
+		ctx.definitions[val1] = backend.SSAValueDefinition{V: val1}
+		ctx.definitions[val2] = backend.SSAValueDefinition{V: val2}
+		ctx.definitions[cmpVal] = backend.SSAValueDefinition{Instr: cmpInstr}
 		b := builder.AllocateInstruction()
 		if brz {
 			b.AsBrz(cmpVal, ssa.ValuesNil, builder.AllocateBasicBlock())
@@ -74,9 +74,9 @@ func TestMachine_LowerConditionalBranch(t *testing.T) {
 		icmp.AsIcmp(v1, v2, ssa.IntegerCmpCondEqual)
 		builder.InsertInstruction(icmp)
 		icmpVal := icmp.Return()
-		ctx.definitions[v1] = &backend.SSAValueDefinition{V: v1}
-		ctx.definitions[v2] = &backend.SSAValueDefinition{Instr: iconst}
-		ctx.definitions[icmpVal] = &backend.SSAValueDefinition{Instr: icmp}
+		ctx.definitions[v1] = backend.SSAValueDefinition{V: v1}
+		ctx.definitions[v2] = backend.SSAValueDefinition{Instr: iconst}
+		ctx.definitions[icmpVal] = backend.SSAValueDefinition{Instr: icmp}
 		ctx.vRegMap[v1], ctx.vRegMap[v2], ctx.vRegMap[icmpVal] = intToVReg(1), intToVReg(2), intToVReg(3)
 		b := builder.AllocateInstruction()
 		if brz {
@@ -105,7 +105,7 @@ func TestMachine_LowerConditionalBranch(t *testing.T) {
 				icmp.AsIcmp(v1, v2, ssa.IntegerCmpCondEqual)
 				builder.InsertInstruction(icmp)
 				icmpVal := icmp.Return()
-				ctx.definitions[icmpVal] = &backend.SSAValueDefinition{Instr: icmp, V: icmpVal}
+				ctx.definitions[icmpVal] = backend.SSAValueDefinition{Instr: icmp, V: icmpVal}
 				ctx.vRegMap[v1], ctx.vRegMap[v2], ctx.vRegMap[icmpVal] = intToVReg(1), intToVReg(2), intToVReg(3)
 
 				brz := builder.AllocateInstruction()
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go b/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go
index 2dce0633..f3a4384d 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_mem_test.go
@@ -297,28 +297,28 @@ func TestMachine_collectAddends(t *testing.T) {
 	addParam := func(ctx *mockCompiler, b ssa.Builder, typ ssa.Type) ssa.Value {
 		p := b.CurrentBlock().AddParam(b, typ)
 		ctx.vRegMap[p] = v1000
-		ctx.definitions[p] = &backend.SSAValueDefinition{V: p}
+		ctx.definitions[p] = backend.SSAValueDefinition{V: p}
 		return p
 	}
 	insertI32Const := func(m *mockCompiler, b ssa.Builder, v uint32) *ssa.Instruction {
 		inst := b.AllocateInstruction()
 		inst.AsIconst32(v)
 		b.InsertInstruction(inst)
-		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst}
+		m.definitions[inst.Return()] = backend.SSAValueDefinition{Instr: inst}
 		return inst
 	}
 	insertI64Const := func(m *mockCompiler, b ssa.Builder, v uint64) *ssa.Instruction {
 		inst := b.AllocateInstruction()
 		inst.AsIconst64(v)
 		b.InsertInstruction(inst)
-		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst, V: inst.Return()}
+		m.definitions[inst.Return()] = backend.SSAValueDefinition{Instr: inst, V: inst.Return()}
 		return inst
 	}
 	insertIadd := func(m *mockCompiler, b ssa.Builder, lhs, rhs ssa.Value) *ssa.Instruction {
 		inst := b.AllocateInstruction()
 		inst.AsIadd(lhs, rhs)
 		b.InsertInstruction(inst)
-		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst, V: inst.Return()}
+		m.definitions[inst.Return()] = backend.SSAValueDefinition{Instr: inst, V: inst.Return()}
 		return inst
 	}
 	insertExt := func(m *mockCompiler, b ssa.Builder, v ssa.Value, from, to byte, signed bool) *ssa.Instruction {
@@ -329,7 +329,7 @@ func TestMachine_collectAddends(t *testing.T) {
 			inst.AsUExtend(v, from, to)
 		}
 		b.InsertInstruction(inst)
-		m.definitions[inst.Return()] = &backend.SSAValueDefinition{Instr: inst, V: v}
+		m.definitions[inst.Return()] = backend.SSAValueDefinition{Instr: inst, V: v}
 		return inst
 	}
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/util_test.go b/internal/engine/wazevo/backend/isa/arm64/util_test.go
index bac89885..6f44a852 100644
--- a/internal/engine/wazevo/backend/isa/arm64/util_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/util_test.go
@@ -55,7 +55,7 @@ type mockCompiler struct {
 	currentGID  ssa.InstructionGroupID
 	vRegCounter int
 	vRegMap     map[ssa.Value]regalloc.VReg
-	definitions map[ssa.Value]*backend.SSAValueDefinition
+	definitions map[ssa.Value]backend.SSAValueDefinition
 	sigs        map[ssa.SignatureID]*ssa.Signature
 	typeOf      map[regalloc.VRegID]ssa.Type
 	ssaBuilder  ssa.Builder
@@ -108,7 +108,7 @@ func (m *mockCompiler) Init()                                {}
 func newMockCompilationContext() *mockCompiler {
 	return &mockCompiler{
 		vRegMap:     make(map[ssa.Value]regalloc.VReg),
-		definitions: make(map[ssa.Value]*backend.SSAValueDefinition),
+		definitions: make(map[ssa.Value]backend.SSAValueDefinition),
 		typeOf:      map[regalloc.VRegID]ssa.Type{},
 	}
 }
@@ -128,10 +128,10 @@ func (m *mockCompiler) AllocateVReg(typ ssa.Type) regalloc.VReg {
 }
 
 // ValueDefinition implements backend.Compiler.
-func (m *mockCompiler) ValueDefinition(value ssa.Value) *backend.SSAValueDefinition {
+func (m *mockCompiler) ValueDefinition(value ssa.Value) backend.SSAValueDefinition {
 	definition, exists := m.definitions[value]
 	if !exists {
-		return nil
+		return backend.SSAValueDefinition{}
 	}
 	return definition
 }
@@ -146,7 +146,7 @@ func (m *mockCompiler) VRegOf(value ssa.Value) regalloc.VReg {
 }
 
 // MatchInstr implements backend.Compiler.
-func (m *mockCompiler) MatchInstr(def *backend.SSAValueDefinition, opcode ssa.Opcode) bool {
+func (m *mockCompiler) MatchInstr(def backend.SSAValueDefinition, opcode ssa.Opcode) bool {
 	instr := def.Instr
 	return def.IsFromInstr() &&
 		instr.Opcode() == opcode &&
@@ -155,7 +155,7 @@ func (m *mockCompiler) MatchInstr(def *backend.SSAValueDefinition, opcode ssa.Op
 }
 
 // MatchInstrOneOf implements backend.Compiler.
-func (m *mockCompiler) MatchInstrOneOf(def *backend.SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
+func (m *mockCompiler) MatchInstrOneOf(def backend.SSAValueDefinition, opcodes []ssa.Opcode) ssa.Opcode {
 	for _, opcode := range opcodes {
 		if m.MatchInstr(def, opcode) {
 			return opcode