Repository: tetratelabs/wazero
Pull request #1769 (2023-10-09 03:33:35)

wazevo(arm64): mandates SSA type spec on tmp VReg
detected by the fuzzer

#1496 


Patch:
diff --git a/internal/engine/wazevo/backend/compiler.go b/internal/engine/wazevo/backend/compiler.go
index 11312cfc..d6f321f2 100644
--- a/internal/engine/wazevo/backend/compiler.go
+++ b/internal/engine/wazevo/backend/compiler.go
@@ -73,10 +73,7 @@ type Compiler interface {
 	ResolveSignature(id ssa.SignatureID) *ssa.Signature
 
 	// AllocateVReg allocates a new virtual register of the given type.
-	AllocateVReg(regType regalloc.RegType) regalloc.VReg
-
-	// AllocateVRegWithSSAType allocates a new virtual register of the given type like AllocateVReg but also sets the ssa.Type.
-	AllocateVRegWithSSAType(regType regalloc.RegType, typ ssa.Type) regalloc.VReg
+	AllocateVReg(typ ssa.Type) regalloc.VReg
 
 	// MarkLowered is used to mark the given instruction as already lowered
 	// which tells the compiler to skip it when traversing.
@@ -234,7 +231,8 @@ func (c *compiler) assignVirtualRegisters() {
 		for i := 0; i < blk.Params(); i++ {
 			p := blk.Param(i)
 			pid := p.ID()
-			vreg := c.AllocateVReg(regalloc.RegTypeOf(p.Type()))
+			typ := p.Type()
+			vreg := c.AllocateVReg(typ)
 			c.ssaValueToVRegs[pid] = vreg
 			c.ssaValueDefinitions[pid] = SSAValueDefinition{BlockParamValue: p, BlkParamVReg: vreg}
 			c.ssaTypeOfVRegID[vreg.ID()] = p.Type()
@@ -247,7 +245,8 @@ func (c *compiler) assignVirtualRegisters() {
 			if r.Valid() {
 				id := r.ID()
 				ssaTyp := r.Type()
-				vReg := c.AllocateVReg(regalloc.RegTypeOf(ssaTyp))
+				typ := r.Type()
+				vReg := c.AllocateVReg(typ)
 				c.ssaValueToVRegs[id] = vReg
 				c.ssaValueDefinitions[id] = SSAValueDefinition{
 					Instr:    cur,
@@ -260,7 +259,7 @@ func (c *compiler) assignVirtualRegisters() {
 			for _, r := range rs {
 				id := r.ID()
 				ssaTyp := r.Type()
-				vReg := c.AllocateVReg(regalloc.RegTypeOf(ssaTyp))
+				vReg := c.AllocateVReg(ssaTyp)
 				c.ssaValueToVRegs[id] = vReg
 				c.ssaValueDefinitions[id] = SSAValueDefinition{
 					Instr:    cur,
@@ -275,21 +274,15 @@ func (c *compiler) assignVirtualRegisters() {
 
 	for i, retBlk := 0, builder.ReturnBlock(); i < retBlk.Params(); i++ {
 		typ := retBlk.Param(i).Type()
-		vReg := c.AllocateVReg(regalloc.RegTypeOf(typ))
+		vReg := c.AllocateVReg(typ)
 		c.returnVRegs = append(c.returnVRegs, vReg)
 		c.ssaTypeOfVRegID[vReg.ID()] = typ
 	}
 }
 
 // AllocateVReg implements Compiler.AllocateVReg.
-func (c *compiler) AllocateVReg(regType regalloc.RegType) regalloc.VReg {
-	r := regalloc.VReg(c.nextVRegID).SetRegType(regType)
-	c.nextVRegID++
-	return r
-}
-
-// AllocateVRegWithSSAType implements Compiler.AllocateVRegWithSSAType.
-func (c *compiler) AllocateVRegWithSSAType(regType regalloc.RegType, typ ssa.Type) regalloc.VReg {
+func (c *compiler) AllocateVReg(typ ssa.Type) regalloc.VReg {
+	regType := regalloc.RegTypeOf(typ)
 	r := regalloc.VReg(c.nextVRegID).SetRegType(regType)
 	c.ssaTypeOfVRegID[r.ID()] = typ
 	c.nextVRegID++
diff --git a/internal/engine/wazevo/backend/compiler_lower.go b/internal/engine/wazevo/backend/compiler_lower.go
index fba3a12f..3740ddd6 100644
--- a/internal/engine/wazevo/backend/compiler_lower.go
+++ b/internal/engine/wazevo/backend/compiler_lower.go
@@ -187,9 +187,10 @@ func (c *compiler) lowerBlockArguments(args []ssa.Value, succ ssa.BasicBlock) {
 		c.tempRegs = c.tempRegs[:0]
 		for i, edge := range c.varEdges {
 			src := edge[0]
-			temp := c.AllocateVReg(src.RegType())
+			typ := c.varEdgeTypes[i]
+			temp := c.AllocateVReg(typ)
 			c.tempRegs = append(c.tempRegs, temp)
-			c.mach.InsertMove(temp, src, c.varEdgeTypes[i])
+			c.mach.InsertMove(temp, src, typ)
 		}
 		// Then move the temporary registers to the destination.
 		for i, edge := range c.varEdges {
diff --git a/internal/engine/wazevo/backend/isa/arm64/abi.go b/internal/engine/wazevo/backend/isa/arm64/abi.go
index 0da0fd50..0b278d74 100644
--- a/internal/engine/wazevo/backend/isa/arm64/abi.go
+++ b/internal/engine/wazevo/backend/isa/arm64/abi.go
@@ -323,7 +323,7 @@ func (m *machine) resolveAddressModeForOffset(offset int64, dstBits byte, rn reg
 			m.lowerConstantI64(tmpRegVReg, offset)
 			indexReg = tmpRegVReg
 		} else {
-			indexReg = m.compiler.AllocateVRegWithSSAType(regalloc.RegTypeInt, ssa.TypeI64)
+			indexReg = m.compiler.AllocateVReg(ssa.TypeI64)
 			m.lowerConstantI64(indexReg, offset)
 		}
 		amode = addressMode{kind: addressModeKindRegReg, rn: rn, rm: indexReg, extOp: extendOpUXTX /* indicates index rm is 64-bit */}
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
index c2b156ad..5500d4cb 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
@@ -10,7 +10,7 @@ func (m *machine) lowerConstant(instr *ssa.Instruction) (vr regalloc.VReg) {
 	val := instr.Return()
 	valType := val.Type()
 
-	vr = m.compiler.AllocateVReg(regalloc.RegTypeOf(valType))
+	vr = m.compiler.AllocateVReg(valType)
 	m.InsertLoadConstant(instr, vr)
 	return
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 241e8fc4..ad26b82e 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -48,13 +48,13 @@ func (m *machine) lowerBrTable(i *ssa.Instruction) {
 	// mov  maxIndexReg #maximum_index
 	// subs wzr, index, maxIndexReg
 	// csel adjustedIndex, maxIndexReg, index, hs ;; if index is higher or equal than maxIndexReg.
-	maxIndexReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	maxIndexReg := m.compiler.AllocateVReg(ssa.TypeI32)
 	m.lowerConstantI32(maxIndexReg, int32(len(targets)-1))
 	subs := m.allocateInstr()
 	subs.asALU(aluOpSubS, operandNR(xzrVReg), indexOperand, operandNR(maxIndexReg), false)
 	m.insert(subs)
 	csel := m.allocateInstr()
-	adjustedIndex := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	adjustedIndex := m.compiler.AllocateVReg(ssa.TypeI32)
 	csel.asCSel(operandNR(adjustedIndex), operandNR(maxIndexReg), indexOperand, hs, false)
 	m.insert(csel)
 
@@ -697,7 +697,7 @@ func (m *machine) lowerShuffle(rd, rn, rm operand, lane1, lane2 uint64) {
 	//     lane1 := lane[7]<<56 | ... | lane[1]<<8 | lane[0]
 	//     lane2 := lane[15]<<56 | ... | lane[9]<<8 | lane[8]
 	// Thus, we can use loadFpuConst128.
-	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	tmp := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 	lfc := m.allocateInstr()
 	lfc.asLoadFpuConst128(tmp.nr(), lane1, lane2)
 	m.insert(lfc)
@@ -723,7 +723,7 @@ func (m *machine) lowerVShift(op ssa.Opcode, rd, rn, rm operand, arr vecArrangem
 		panic("unsupported arrangment " + arr.String())
 	}
 
-	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	tmp := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 
 	and := m.allocateInstr()
 	and.asALUBitmaskImm(aluOpAnd, tmp.nr(), rm.nr(), uint64(modulo), false)
@@ -753,7 +753,7 @@ func (m *machine) lowerVShift(op ssa.Opcode, rd, rn, rm operand, arr vecArrangem
 }
 
 func (m *machine) lowerVcheckTrue(op ssa.Opcode, rm, rd operand, arr vecArrangement) {
-	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeOf(ssa.TypeV128)))
+	tmp := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 
 	// Special case VallTrue for i64x2.
 	if op == ssa.OpcodeVallTrue && arr == vecArrangement2D {
@@ -811,9 +811,9 @@ func (m *machine) lowerVcheckTrue(op ssa.Opcode, rm, rd operand, arr vecArrangem
 }
 
 func (m *machine) lowerVhighBits(rm, rd operand, arr vecArrangement) {
-	r0 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeOf(ssa.TypeI64)))
-	v0 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeOf(ssa.TypeV128)))
-	v1 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeOf(ssa.TypeV128)))
+	r0 := operandNR(m.compiler.AllocateVReg(ssa.TypeI64))
+	v0 := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
+	v1 := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 
 	switch arr {
 	case vecArrangement16B: // ssa.VecLaneI8x16
@@ -1033,9 +1033,9 @@ func (m *machine) lowerVIMul(rd, rn, rm operand, arr vecArrangement) {
 		mul.asVecRRR(vecOpMul, rd, rn, rm, arr)
 		m.insert(mul)
 	} else {
-		tmp1 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-		tmp2 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-		tmp3 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+		tmp1 := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
+		tmp2 := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
+		tmp3 := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 
 		// Following the algorithm in https://chromium-review.googlesource.com/c/v8/v8/+/1781696
 		rev64 := m.allocateInstr()
@@ -1154,10 +1154,17 @@ func (m *machine) exitIfNot(execCtxVReg regalloc.VReg, c cond, cond64bit bool, c
 func (m *machine) lowerFcopysign(x, y, ret ssa.Value) {
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
 	rm := m.getOperand_NR(m.compiler.ValueDefinition(y), extModeNone)
-	tmpF := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-	tmpI := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
+	var tmpI, tmpF operand
+	_64 := x.Type() == ssa.TypeF64
+	if _64 {
+		tmpF = operandNR(m.compiler.AllocateVReg(ssa.TypeF64))
+		tmpI = operandNR(m.compiler.AllocateVReg(ssa.TypeI64))
+	} else {
+		tmpF = operandNR(m.compiler.AllocateVReg(ssa.TypeF32))
+		tmpI = operandNR(m.compiler.AllocateVReg(ssa.TypeI32))
+	}
 	rd := m.compiler.VRegOf(ret)
-	m.lowerFcopysignImpl(operandNR(rd), rn, rm, tmpI, tmpF, x.Type() == ssa.TypeF64)
+	m.lowerFcopysignImpl(operandNR(rd), rn, rm, tmpI, tmpF, _64)
 }
 
 func (m *machine) lowerFcopysignImpl(rd, rn, rm, tmpI, tmpF operand, _64bit bool) {
@@ -1243,7 +1250,7 @@ func (m *machine) lowerFpuToInt(rd, rn operand, ctx regalloc.VReg, signed, src64
 	m.insert(cvt)
 
 	if !nonTrapping {
-		tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		tmpReg := m.compiler.AllocateVReg(ssa.TypeI64)
 
 		// After the conversion, check the FPU flags.
 		getFlag := m.allocateInstr()
@@ -1551,7 +1558,12 @@ func (m *machine) lowerRotl(si *ssa.Instruction) {
 
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
 	rm := m.getOperand_NR(m.compiler.ValueDefinition(y), extModeNone)
-	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
+	var tmp operand
+	if _64 {
+		tmp = operandNR(m.compiler.AllocateVReg(ssa.TypeI64))
+	} else {
+		tmp = operandNR(m.compiler.AllocateVReg(ssa.TypeI32))
+	}
 	rd := operandNR(m.compiler.VRegOf(r))
 
 	// Encode rotl as neg + rotr: neg is a sub against the zero-reg.
@@ -1626,12 +1638,18 @@ func (m *machine) lowerCtz(x, result ssa.Value) {
 	rd := m.compiler.VRegOf(result)
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
 	rbit := m.allocateInstr()
-	tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
-	rbit.asBitRR(bitOpRbit, tmpReg, rn.nr(), x.Type().Bits() == 64)
+	_64 := x.Type().Bits() == 64
+	var tmpReg regalloc.VReg
+	if _64 {
+		tmpReg = m.compiler.AllocateVReg(ssa.TypeI64)
+	} else {
+		tmpReg = m.compiler.AllocateVReg(ssa.TypeI32)
+	}
+	rbit.asBitRR(bitOpRbit, tmpReg, rn.nr(), _64)
 	m.insert(rbit)
 
 	clz := m.allocateInstr()
-	clz.asBitRR(bitOpClz, rd, tmpReg, x.Type().Bits() == 64)
+	clz.asBitRR(bitOpClz, rd, tmpReg, _64)
 	m.insert(clz)
 }
 
@@ -1659,17 +1677,17 @@ func (m *machine) lowerPopcnt(x, result ssa.Value) {
 	rd := operandNR(m.compiler.VRegOf(result))
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
 
-	rf1 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	rf1 := operandNR(m.compiler.AllocateVReg(ssa.TypeF64))
 	ins := m.allocateInstr()
 	ins.asMovToVec(rf1, rn, vecArrangementD, vecIndex(0))
 	m.insert(ins)
 
-	rf2 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	rf2 := operandNR(m.compiler.AllocateVReg(ssa.TypeF64))
 	cnt := m.allocateInstr()
 	cnt.asVecMisc(vecOpCnt, rf2, rf1, vecArrangement16B)
 	m.insert(cnt)
 
-	rf3 := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	rf3 := operandNR(m.compiler.AllocateVReg(ssa.TypeF64))
 	uaddlv := m.allocateInstr()
 	uaddlv.asVecLanes(vecOpUaddlv, rf3, rf2, vecArrangement8B)
 	m.insert(uaddlv)
@@ -1681,7 +1699,7 @@ func (m *machine) lowerPopcnt(x, result ssa.Value) {
 
 // lowerExitWithCode lowers the lowerExitWithCode takes a context pointer as argument.
 func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.ExitCode) {
-	tmpReg1 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	tmpReg1 := m.compiler.AllocateVReg(ssa.TypeI32)
 	loadExitCodeConst := m.allocateInstr()
 	loadExitCodeConst.asMOVZ(tmpReg1, uint64(code), 0, true)
 
@@ -1693,7 +1711,7 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 		}, 32)
 
 	// In order to unwind the stack, we also need to push the current stack pointer:
-	tmp2 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	tmp2 := m.compiler.AllocateVReg(ssa.TypeI64)
 	movSpToTmp := m.allocateInstr()
 	movSpToTmp.asMove64(tmp2, spVReg)
 	strSpToExecCtx := m.allocateInstr()
@@ -1703,7 +1721,7 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 			rn:   execCtxVReg, imm: wazevoapi.ExecutionContextOffsetStackPointerBeforeGoCall.I64(),
 		}, 64)
 	// Also the address of this exit.
-	tmp3 := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	tmp3 := m.compiler.AllocateVReg(ssa.TypeI64)
 	currentAddrToTmp := m.allocateInstr()
 	currentAddrToTmp.asAdr(tmp3, 0)
 	storeCurrentAddrToExecCtx := m.allocateInstr()
@@ -1842,7 +1860,7 @@ func (m *machine) lowerSelect(c, x, y, result ssa.Value) {
 }
 
 func (m *machine) lowerSelectVec(rc, rn, rm, rd operand) {
-	tmp := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
+	tmp := operandNR(m.compiler.AllocateVReg(ssa.TypeI64))
 
 	// Sets all bits to 1 if rc is not zero.
 	alu := m.allocateInstr()
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 2035f419..246a4a88 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_operands.go
@@ -314,13 +314,13 @@ func (m *machine) getOperand_NR(def *backend.SSAValueDefinition, mode extMode) (
 	case mode == extModeNone:
 	case inBits == 32 && (mode == extModeZeroExtend32 || mode == extModeSignExtend32):
 	case inBits == 32 && mode == extModeZeroExtend64:
-		extended := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		extended := m.compiler.AllocateVReg(ssa.TypeI64)
 		ext := m.allocateInstr()
 		ext.asExtend(extended, v, 32, 64, false)
 		m.insert(ext)
 		r = extended
 	case inBits == 32 && mode == extModeSignExtend64:
-		extended := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		extended := m.compiler.AllocateVReg(ssa.TypeI64)
 		ext := m.allocateInstr()
 		ext.asExtend(extended, v, 32, 64, true)
 		m.insert(ext)
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_mem.go b/internal/engine/wazevo/backend/isa/arm64/lower_mem.go
index 737d5522..c8c6aa28 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_mem.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_mem.go
@@ -292,7 +292,7 @@ func (m *machine) lowerToAddressModeFromAddends(a32s []addend32, a64s []regalloc
 		base32, a32s = dequeue(a32s)
 
 		// First we need 64-bit base.
-		base := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		base := m.compiler.AllocateVReg(ssa.TypeI64)
 		baseExt := m.allocateInstr()
 		var signed bool
 		if base32.ext == extendOpSXTW {
@@ -309,7 +309,7 @@ func (m *machine) lowerToAddressModeFromAddends(a32s []addend32, a64s []regalloc
 			amode = addressMode{kind: addressModeKindRegUnsignedImm12, rn: base, imm: 0}
 		}
 	default: // Only static offsets.
-		tmpReg := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		tmpReg := m.compiler.AllocateVReg(ssa.TypeI64)
 		m.lowerConstantI64(tmpReg, offset)
 		amode = addressMode{kind: addressModeKindRegUnsignedImm12, rn: tmpReg, imm: 0}
 		offset = 0
@@ -402,14 +402,14 @@ func (m *machine) collectAddends(ptr ssa.Value) (addends32 []addend32, addends64
 }
 
 func (m *machine) addConstToReg64(r regalloc.VReg, c int64) (rd regalloc.VReg) {
-	rd = m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	rd = m.compiler.AllocateVReg(ssa.TypeI64)
 	alu := m.allocateInstr()
 	if imm12Op, ok := asImm12Operand(uint64(c)); ok {
 		alu.asALU(aluOpAdd, operandNR(rd), operandNR(r), imm12Op, true)
 	} else if imm12Op, ok = asImm12Operand(uint64(-c)); ok {
 		alu.asALU(aluOpSub, operandNR(rd), operandNR(r), imm12Op, true)
 	} else {
-		tmp := m.compiler.AllocateVReg(regalloc.RegTypeInt)
+		tmp := m.compiler.AllocateVReg(ssa.TypeI64)
 		m.load64bitConst(c, tmp)
 		alu.asALU(aluOpAdd, operandNR(rd), operandNR(r), operandNR(tmp), true)
 	}
@@ -418,7 +418,7 @@ func (m *machine) addConstToReg64(r regalloc.VReg, c int64) (rd regalloc.VReg) {
 }
 
 func (m *machine) addReg64ToReg64(rn, rm regalloc.VReg) (rd regalloc.VReg) {
-	rd = m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	rd = m.compiler.AllocateVReg(ssa.TypeI64)
 	alu := m.allocateInstr()
 	alu.asALU(aluOpAdd, operandNR(rd), operandNR(rn), operandNR(rm), true)
 	m.insert(alu)
@@ -426,7 +426,7 @@ func (m *machine) addReg64ToReg64(rn, rm regalloc.VReg) (rd regalloc.VReg) {
 }
 
 func (m *machine) addRegToReg64Ext(rn, rm regalloc.VReg, ext extendOp) (rd regalloc.VReg) {
-	rd = m.compiler.AllocateVReg(regalloc.RegTypeInt)
+	rd = m.compiler.AllocateVReg(ssa.TypeI64)
 	alu := m.allocateInstr()
 	alu.asALU(aluOpAdd, operandNR(rd), operandNR(rn), operandER(rm, ext, 64), true)
 	m.insert(alu)


Test patch:
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 e8db25f5..4acb0419 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr_test.go
@@ -834,10 +834,10 @@ ushl x1.16b, x2.16b, x1.16b
 
 func TestMachine_lowerSelectVec(t *testing.T) {
 	_, _, m := newSetupWithMockContext()
-	c := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
-	rn := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-	rm := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-	rd := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+	c := operandNR(m.compiler.AllocateVReg(ssa.TypeI32))
+	rn := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
+	rm := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
+	rd := operandNR(m.compiler.AllocateVReg(ssa.TypeV128))
 
 	require.Equal(t, 1, int(c.reg().ID()))
 	require.Equal(t, 2, int(rn.reg().ID()))
@@ -878,11 +878,19 @@ bit v5?.8b, v4?.8b, v2?.8b
 	} {
 		t.Run(fmt.Sprintf("64bit=%v", tc._64bit), func(t *testing.T) {
 			_, _, m := newSetupWithMockContext()
-			tmpI := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
-			tmpF := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-			rn := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-			rm := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
-			rd := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeFloat))
+			var typ, ftyp ssa.Type
+			if tc._64bit {
+				typ = ssa.TypeI64
+				ftyp = ssa.TypeF64
+			} else {
+				typ = ssa.TypeI32
+				ftyp = ssa.TypeF32
+			}
+			tmpI := operandNR(m.compiler.AllocateVReg(typ))
+			tmpF := operandNR(m.compiler.AllocateVReg(ftyp))
+			rn := operandNR(m.compiler.AllocateVReg(ftyp))
+			rm := operandNR(m.compiler.AllocateVReg(ftyp))
+			rd := operandNR(m.compiler.AllocateVReg(ftyp))
 
 			require.Equal(t, 1, int(tmpI.reg().ID()))
 			require.Equal(t, 2, int(tmpF.reg().ID()))
@@ -918,10 +926,16 @@ ror x4?, x2?, x1?
 	} {
 		t.Run(fmt.Sprintf("64bit=%v", tc._64bit), func(t *testing.T) {
 			_, _, m := newSetupWithMockContext()
-			tmpI := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
-			rn := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
-			rm := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
-			rd := operandNR(m.compiler.AllocateVReg(regalloc.RegTypeInt))
+			var typ ssa.Type
+			if tc._64bit {
+				typ = ssa.TypeI64
+			} else {
+				typ = ssa.TypeI32
+			}
+			tmpI := operandNR(m.compiler.AllocateVReg(typ))
+			rn := operandNR(m.compiler.AllocateVReg(typ))
+			rm := operandNR(m.compiler.AllocateVReg(typ))
+			rd := operandNR(m.compiler.AllocateVReg(typ))
 
 			require.Equal(t, 1, int(tmpI.reg().ID()))
 			require.Equal(t, 2, int(rn.reg().ID()))
diff --git a/internal/engine/wazevo/backend/isa/arm64/util_test.go b/internal/engine/wazevo/backend/isa/arm64/util_test.go
index 577e7dd4..1ab1ae76 100644
--- a/internal/engine/wazevo/backend/isa/arm64/util_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/util_test.go
@@ -74,9 +74,8 @@ func (m *mockCompiler) Emit4Bytes(b uint32) {
 	m.buf = append(m.buf, byte(b), byte(b>>8), byte(b>>16), byte(b>>24))
 }
 
-func (m *mockCompiler) Encode()                                                          {}
-func (m *mockCompiler) Buf() []byte                                                      { return m.buf }
-func (m *mockCompiler) AllocateVRegWithSSAType(regalloc.RegType, ssa.Type) regalloc.VReg { return 0 }
+func (m *mockCompiler) Encode()     {}
+func (m *mockCompiler) Buf() []byte { return m.buf }
 func (m *mockCompiler) TypeOf(v regalloc.VReg) (ret ssa.Type) {
 	return m.typeOf[v]
 }
@@ -100,8 +99,9 @@ func (m *mockCompiler) ResolveSignature(id ssa.SignatureID) *ssa.Signature {
 }
 
 // AllocateVReg implements backend.Compiler.
-func (m *mockCompiler) AllocateVReg(regType regalloc.RegType) regalloc.VReg {
+func (m *mockCompiler) AllocateVReg(typ ssa.Type) regalloc.VReg {
 	m.vRegCounter++
+	regType := regalloc.RegTypeOf(typ)
 	return regalloc.VReg(m.vRegCounter).SetRegType(regType)
 }