Repository: tetratelabs/wazero
Pull request #1659 (2023-08-24 06:16:22)

wazevo: passes br_if, switch and other spectests
#1496 


Patch:
diff --git a/internal/engine/wazevo/backend/compiler_lower.go b/internal/engine/wazevo/backend/compiler_lower.go
index c5f58915..97f11274 100644
--- a/internal/engine/wazevo/backend/compiler_lower.go
+++ b/internal/engine/wazevo/backend/compiler_lower.go
@@ -11,7 +11,7 @@ import (
 func (c *compiler) Lower() {
 	c.assignVirtualRegisters()
 	c.mach.InitializeABI(c.ssaBuilder.Signature())
-	c.mach.StartLoweringFunction(c.ssaBuilder.Blocks())
+	c.mach.StartLoweringFunction(c.ssaBuilder.BlockIDMax())
 	c.lowerBlocks()
 	c.mach.EndLoweringFunction()
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index c321e2f1..1ea2dbb2 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -60,6 +60,7 @@ var defKinds = [numInstructionKinds]defKind{
 	mov64:           defKindRD,
 	fpuMov64:        defKindRD,
 	fpuMov128:       defKindRD,
+	fpuRR:           defKindRD,
 	fpuRRR:          defKindRD,
 	nop0:            defKindNone,
 	call:            defKindCall,
@@ -160,6 +161,7 @@ var useKinds = [numInstructionKinds]useKind{
 	mov64:           useKindRN,
 	fpuMov64:        useKindRN,
 	fpuMov128:       useKindRN,
+	fpuRR:           useKindRN,
 	fpuRRR:          useKindRNRM,
 	nop0:            useKindNone,
 	call:            useKindCall,
@@ -655,6 +657,15 @@ func (i *instruction) asFpuRRR(op fpuBinOp, rd, rn, rm operand, dst64bit bool) {
 	}
 }
 
+func (i *instruction) asFpuRR(op fpuUniOp, rd, rn operand, dst64bit bool) {
+	i.kind = fpuRR
+	i.u1 = uint64(op)
+	i.rd, i.rn = rd, rn
+	if dst64bit {
+		i.u3 = 1
+	}
+}
+
 func (i *instruction) asExtend(rd, rn regalloc.VReg, fromBits, toBits byte, signed bool) {
 	i.kind = extend
 	i.rn, i.rd = operandNR(rn), operandNR(rd)
@@ -867,7 +878,9 @@ func (i *instruction) String() (str string) {
 	case fpuMovFromVec:
 		panic("TODO")
 	case fpuRR:
-		panic("TODO")
+		size := is64SizeBitToSize(i.u3)
+		str = fmt.Sprintf("%s %s, %s", fpuUniOp(i.u1).String(),
+			formatVRegSized(i.rd.nr(), size), formatVRegSized(i.rn.nr(), size))
 	case fpuRRR:
 		size := is64SizeBitToSize(i.u3)
 		str = fmt.Sprintf("%s %s, %s, %s", fpuBinOp(i.u1).String(),
@@ -972,19 +985,10 @@ func (i *instruction) String() (str string) {
 	case vecRRR:
 		panic("TODO")
 	case vecMisc:
-		switch op := vecOp(i.u1); op {
-		case vecOpCvt32To64:
-			str = fmt.Sprintf("%s %s, %s",
-				vecOp(i.u1),
-				formatVRegSized(i.rd.nr(), 64),
-				formatVRegSized(i.rn.nr(), 32),
-			)
-		default:
-			str = fmt.Sprintf("%s %s, %s",
-				vecOp(i.u1),
-				formatVRegVec(i.rd.nr(), vecArrangement(i.u2), vecIndexNone),
-				formatVRegVec(i.rn.nr(), vecArrangement(i.u2), vecIndexNone))
-		}
+		str = fmt.Sprintf("%s %s, %s",
+			vecOp(i.u1),
+			formatVRegVec(i.rd.nr(), vecArrangement(i.u2), vecIndexNone),
+			formatVRegVec(i.rn.nr(), vecArrangement(i.u2), vecIndexNone))
 	case vecLanes:
 		arr := vecArrangement(i.u2)
 		var destArr vecArrangement
@@ -1401,8 +1405,6 @@ func (b vecOp) String() string {
 		return "cnt"
 	case vecOpUaddlv:
 		return "uaddlv"
-	case vecOpCvt32To64:
-		return "fcvt"
 	}
 	panic(int(b))
 }
@@ -1410,7 +1412,6 @@ func (b vecOp) String() string {
 const (
 	vecOpCnt vecOp = iota
 	vecOpUaddlv
-	vecOpCvt32To64
 )
 
 // bitOp determines the type of bitwise operation. Instructions whose kind is one of
@@ -1435,6 +1436,25 @@ const (
 	bitOpClz
 )
 
+// fpuUniOp represents a unary floating-point unit (FPU) operation.
+type fpuUniOp byte
+
+const (
+	fpuUniOpNeg = iota
+	fpuUniCvt32To64
+)
+
+// String implements the fmt.Stringer.
+func (f fpuUniOp) String() string {
+	switch f {
+	case fpuUniOpNeg:
+		return "fneg"
+	case fpuUniCvt32To64:
+		return "fcvt"
+	}
+	panic(int(f))
+}
+
 // fpuBinOp represents a binary floating-point unit (FPU) operation.
 type fpuBinOp byte
 
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index 5ec880e5..89f0b002 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -241,22 +241,13 @@ func (i *instruction) encode(c backend.Compiler) {
 			vecIndex(i.u2),
 		))
 	case vecMisc:
-		switch op := vecOp(i.u1); op {
-		case vecOpCnt:
-			c.Emit4Bytes(encodeAdvancedSIMDTwoMisc(
-				vecOp(i.u1),
-				regNumberInEncoding[i.rd.realReg()],
-				regNumberInEncoding[i.rn.realReg()],
-				vecArrangement(i.u2),
-			))
-		case vecOpCvt32To64:
-			c.Emit4Bytes(encodeFloatDataOneSource(
-				vecOp(i.u1),
-				regNumberInEncoding[i.rd.realReg()],
-				regNumberInEncoding[i.rn.realReg()],
-				vecArrangement(i.u2),
-			))
-		}
+		c.Emit4Bytes(encodeAdvancedSIMDTwoMisc(
+			vecOp(i.u1),
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			vecArrangement(i.u2),
+		))
+
 	case vecLanes:
 		c.Emit4Bytes(encodeVecLanes(
 			vecOp(i.u1),
@@ -268,6 +259,13 @@ func (i *instruction) encode(c backend.Compiler) {
 		encodeBrTableSequence(c, i.rn.reg(), i.targets)
 	case intToFpu:
 		c.Emit4Bytes(encodeCnvBetweenFloatInt(i))
+	case fpuRR:
+		c.Emit4Bytes(encodeFloatDataOneSource(
+			vecOp(i.u1),
+			regNumberInEncoding[i.rd.realReg()],
+			regNumberInEncoding[i.rn.realReg()],
+			i.u3 == 1,
+		))
 	default:
 		panic(i.String())
 	}
@@ -275,11 +273,16 @@ func (i *instruction) encode(c backend.Compiler) {
 
 // encodeFloatDataOneSource encodes as "Floating-point data-processing (1 source)" in
 // https://developer.arm.com/documentation/ddi0596/2020-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en#simd-dp
-func encodeFloatDataOneSource(op vecOp, rd, rn uint32, _ vecArrangement) uint32 {
+func encodeFloatDataOneSource(op vecOp, rd, rn uint32, dst64bit bool) uint32 {
 	var opcode, ptype uint32
 	switch op {
-	case vecOpCvt32To64:
+	case fpuUniCvt32To64:
 		opcode = 0b000101
+	case fpuUniOpNeg:
+		opcode = 0b000010
+		if dst64bit {
+			ptype = 0b01
+		}
 	default:
 		panic("BUG")
 	}
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
index cfc480cd..f88affe4 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_constant.go
@@ -72,7 +72,7 @@ func (m *machine) lowerConstantI32(dst regalloc.VReg, c int32) {
 	} else if t := const16bitAligned(int64(^c)); t >= 0 {
 		// Also, if the inverse of the const can fit within 16-bit range, do the same ^^.
 		m.insertMOVN(dst, uint64(^c>>(16*t)), t, false)
-	} else if isBitMaskImmediate(uint64(c)) {
+	} else if isBitMaskImmediate(uint64(uint32(c))) {
 		m.lowerConstViaBitMaskImmediate(uint64(c), dst, false)
 	} else {
 		// Otherwise, we use MOVZ and MOVK to load it.
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 9266d62b..3b9fe370 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -217,7 +217,7 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		rn := m.getOperand_NR(m.compiler.ValueDefinition(v), extModeNone)
 		rd := operandNR(m.compiler.VRegOf(instr.Return()))
 		cnt := m.allocateInstr()
-		cnt.asVecMisc(vecOpCvt32To64, rd, rn, vecArrangementNone)
+		cnt.asFpuRR(fpuUniCvt32To64, rd, rn, true)
 		m.insert(cnt)
 	case ssa.OpcodeIreduce:
 		rn := m.getOperand_NR(m.compiler.ValueDefinition(instr.UnaryData()), extModeNone)
@@ -230,12 +230,64 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		mov := m.allocateInstr()
 		mov.asMove32(rd, rn.reg())
 		m.insert(mov)
+	case ssa.OpcodeFneg:
+		m.lowerFneg(instr)
+	case ssa.OpcodeBitcast:
+		m.lowerBitcast(instr)
 	default:
 		panic("TODO: lowering " + instr.Opcode().String())
 	}
 	m.FlushPendingInstructions()
 }
 
+func (m *machine) lowerBitcast(instr *ssa.Instruction) {
+	v, dstType := instr.BitcastData()
+	srcType := v.Type()
+	rn := m.getOperand_NR(m.compiler.ValueDefinition(v), extModeNone)
+	rd := operandNR(m.compiler.VRegOf(instr.Return()))
+	srcInt := srcType.IsInt()
+	dstInt := dstType.IsInt()
+	switch {
+	case srcInt && !dstInt: // Int to Float:
+		mov := m.allocateInstr()
+		var arr vecArrangement
+		if srcType.Bits() == 64 {
+			arr = vecArrangementD
+		} else {
+			arr = vecArrangementS
+		}
+		mov.asMovToVec(rd, rn, arr, vecIndex(0))
+		m.insert(mov)
+	case !srcInt && dstInt: // Float to Int:
+		mov := m.allocateInstr()
+		var arr vecArrangement
+		if dstType.Bits() == 64 {
+			arr = vecArrangementD
+		} else {
+			arr = vecArrangementS
+		}
+		mov.asMovFromVec(rd, rn, arr, vecIndex(0))
+		m.insert(mov)
+	default:
+		panic("TODO?BUG?")
+	}
+}
+
+func (m *machine) lowerFneg(instr *ssa.Instruction) {
+	x := instr.UnaryData()
+	rn := m.getOperand_NR(m.compiler.ValueDefinition(x), extModeNone)
+	rd := operandNR(m.compiler.VRegOf(instr.Return()))
+
+	neg := m.allocateInstr()
+	switch x.Type() {
+	case ssa.TypeF32, ssa.TypeF64:
+		neg.asFpuRR(fpuUniOpNeg, rd, rn, x.Type().Bits() == 64)
+	default:
+		panic("TODO: vector neg")
+	}
+	m.insert(neg)
+}
+
 func (m *machine) lowerIntToFpu(dst, src ssa.Value, signed, src64bit, dst64bit bool) {
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(src), extModeNone)
 	rd := operandNR(m.compiler.VRegOf(dst))
@@ -529,7 +581,9 @@ func (m *machine) lowerExitWithCode(execCtxVReg regalloc.VReg, code wazevoapi.Ex
 
 func (m *machine) lowerIcmpToFlag(x, y ssa.Value, signed bool) {
 	if x.Type() != y.Type() {
-		panic("TODO(maybe): support icmp with different types")
+		panic(
+			fmt.Sprintf("TODO(maybe): support icmp with different types: v%d=%s != v%d=%s",
+				x.ID(), x.Type(), y.ID(), y.Type()))
 	}
 
 	extMod := extModeOf(x.Type(), signed)
diff --git a/internal/engine/wazevo/backend/isa/arm64/machine.go b/internal/engine/wazevo/backend/isa/arm64/machine.go
index 65fe1f6b..42585230 100644
--- a/internal/engine/wazevo/backend/isa/arm64/machine.go
+++ b/internal/engine/wazevo/backend/isa/arm64/machine.go
@@ -172,10 +172,11 @@ func (m *machine) SetCompiler(ctx backend.Compiler) {
 }
 
 // StartLoweringFunction implements backend.Machine.
-func (m *machine) StartLoweringFunction(n int) {
-	if len(m.ssaBlockIDToLabels) <= n {
+func (m *machine) StartLoweringFunction(max ssa.BasicBlockID) {
+	imax := int(max)
+	if len(m.ssaBlockIDToLabels) <= imax {
 		// Eagerly allocate labels for the blocks since the underlying slice will be used for the next iteration.
-		m.ssaBlockIDToLabels = append(m.ssaBlockIDToLabels, make([]label, n+1)...)
+		m.ssaBlockIDToLabels = append(m.ssaBlockIDToLabels, make([]label, imax+1)...)
 	}
 }
 
diff --git a/internal/engine/wazevo/backend/machine.go b/internal/engine/wazevo/backend/machine.go
index 19cdb681..dacca011 100644
--- a/internal/engine/wazevo/backend/machine.go
+++ b/internal/engine/wazevo/backend/machine.go
@@ -27,8 +27,8 @@ type (
 		SetCompiler(Compiler)
 
 		// StartLoweringFunction is called when the lowering of the given function is started.
-		// numBlks is the number of ssa.BasicBlock(s) existing in the function.
-		StartLoweringFunction(numBlks int)
+		// maximumBlockID is the maximum value of ssa.BasicBlockID existing in the function.
+		StartLoweringFunction(maximumBlockID ssa.BasicBlockID)
 
 		// StartBlock is called when the compilation of the given block is started.
 		// The order of this being called is the reverse post order of the ssa.BasicBlock(s) as we iterate with
diff --git a/internal/engine/wazevo/engine.go b/internal/engine/wazevo/engine.go
index e9fcd263..10136333 100644
--- a/internal/engine/wazevo/engine.go
+++ b/internal/engine/wazevo/engine.go
@@ -140,14 +140,14 @@ func (e *engine) CompileModule(_ context.Context, module *wasm.Module, _ []exper
 		ssaBuilder.RunPasses()
 
 		if debug {
-			fmt.Printf("[[[optimized SSA]]]%s", ssaBuilder.Format())
+			fmt.Printf("[[[optimized SSA]]]%s\n", ssaBuilder.Format())
 		}
 
 		// Finalize the layout of SSA blocks which might use the optimization results.
 		ssaBuilder.LayoutBlocks()
 
 		if debug {
-			fmt.Printf("[[[laidout SSA]]]%s", ssaBuilder.Format())
+			fmt.Printf("[[[laidout SSA]]]%s\n", ssaBuilder.Format())
 		}
 
 		// Now our ssaBuilder contains the necessary information to further lower them to
@@ -176,6 +176,9 @@ func (e *engine) CompileModule(_ context.Context, module *wasm.Module, _ []exper
 		copy(copied, body)
 		bodies[i] = copied
 		totalSize += len(body)
+		if debug {
+			fmt.Println(hex.EncodeToString(body))
+		}
 	}
 
 	// Allocate executable memory and then copy the generated machine code.
@@ -193,8 +196,6 @@ func (e *engine) CompileModule(_ context.Context, module *wasm.Module, _ []exper
 	// Resolve relocations for local function calls.
 	machine.ResolveRelocations(e.refToBinaryOffset, executable, e.rels)
 
-	fmt.Println(hex.EncodeToString(executable))
-
 	if runtime.GOARCH == "arm64" {
 		// On arm64, we cannot give all of rwx at the same time, so we change it to exec.
 		if err = platform.MprotectRX(executable); err != nil {
diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go
index 66380eac..a172ab75 100644
--- a/internal/engine/wazevo/frontend/lower.go
+++ b/internal/engine/wazevo/frontend/lower.go
@@ -328,12 +328,12 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		} else {
 			zero.AsIconst64(0)
 		}
-		zero.AsIconst32(0)
 		builder.InsertInstruction(zero)
-		icmp := builder.AllocateInstruction()
-		icmp.AsIcmp(x, zero.Return(), ssa.IntegerCmpCondEqual)
-		builder.InsertInstruction(icmp)
-		state.push(icmp.Return())
+		icmp := builder.AllocateInstruction().
+			AsIcmp(x, zero.Return(), ssa.IntegerCmpCondEqual).
+			Insert(builder).
+			Return()
+		state.push(icmp)
 	case wasm.OpcodeI32Eq, wasm.OpcodeI64Eq:
 		if state.unreachable {
 			return
@@ -420,11 +420,45 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 			return
 		}
 		x := state.pop()
-		neg := builder.AllocateInstruction()
-		neg.AsFneg(x)
-		builder.InsertInstruction(neg)
-		value := neg.Return()
-		state.push(value)
+		negated := builder.AllocateInstruction().AsFneg(x).Insert(builder).Return()
+		state.push(negated)
+	case wasm.OpcodeI64TruncF64S:
+	case wasm.OpcodeI32ReinterpretF32:
+		if state.unreachable {
+			return
+		}
+		reinterpret := builder.AllocateInstruction().
+			AsBitcast(state.pop(), ssa.TypeI32).
+			Insert(builder).Return()
+		state.push(reinterpret)
+
+	case wasm.OpcodeI64ReinterpretF64:
+		if state.unreachable {
+			return
+		}
+		reinterpret := builder.AllocateInstruction().
+			AsBitcast(state.pop(), ssa.TypeI64).
+			Insert(builder).Return()
+		state.push(reinterpret)
+
+	case wasm.OpcodeF32ReinterpretI32:
+		if state.unreachable {
+			return
+		}
+		reinterpret := builder.AllocateInstruction().
+			AsBitcast(state.pop(), ssa.TypeF32).
+			Insert(builder).Return()
+		state.push(reinterpret)
+
+	case wasm.OpcodeF64ReinterpretI64:
+		if state.unreachable {
+			return
+		}
+		reinterpret := builder.AllocateInstruction().
+			AsBitcast(state.pop(), ssa.TypeF64).
+			Insert(builder).Return()
+		state.push(reinterpret)
+
 	case wasm.OpcodeI32And, wasm.OpcodeI64And:
 		if state.unreachable {
 			return
@@ -564,6 +598,9 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		}
 		variable := c.localVariable(index)
 		v := builder.MustFindValue(variable)
+		if false {
+			builder.AnnotateValue(v, fmt.Sprintf("v%d@local[%d]", v.ID(), index))
+		}
 		state.push(v)
 	case wasm.OpcodeLocalSet:
 		index := c.readI32u()
@@ -596,10 +633,11 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		v2 := state.pop()
 		v1 := state.pop()
 
-		sl := builder.AllocateInstruction()
-		sl.AsSelect(cond, v1, v2)
-		builder.InsertInstruction(sl)
-		state.push(sl.Return())
+		sl := builder.AllocateInstruction().
+			AsSelect(cond, v1, v2).
+			Insert(builder).
+			Return()
+		state.push(sl)
 
 	case wasm.OpcodeMemorySize:
 		state.pc++ // skips the memory index.
@@ -609,30 +647,30 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 
 		var memSizeInBytes ssa.Value
 		if c.offset.LocalMemoryBegin < 0 {
-			loadMemInstPtr := builder.AllocateInstruction()
-			loadMemInstPtr.AsLoad(c.moduleCtxPtrValue, c.offset.ImportedMemoryBegin.U32(), ssa.TypeI64)
-			builder.InsertInstruction(loadMemInstPtr)
-			memInstPtr := loadMemInstPtr.Return()
-
-			loadBufSizePtr := builder.AllocateInstruction()
-			loadBufSizePtr.AsLoad(memInstPtr, memoryInstanceBufSizeOffset, ssa.TypeI64)
-			builder.InsertInstruction(loadBufSizePtr)
-			memSizeInBytes = loadBufSizePtr.Return()
-
+			memInstPtr := builder.AllocateInstruction().
+				AsLoad(c.moduleCtxPtrValue, c.offset.ImportedMemoryBegin.U32(), ssa.TypeI64).
+				Insert(builder).
+				Return()
+
+			memSizeInBytes = builder.AllocateInstruction().
+				AsLoad(memInstPtr, memoryInstanceBufSizeOffset, ssa.TypeI64).
+				Insert(builder).
+				Return()
 		} else {
-			load := builder.AllocateInstruction()
-			load.AsLoad(c.moduleCtxPtrValue, c.offset.LocalMemoryLen().U32(), ssa.TypeI32)
-			builder.InsertInstruction(load)
-			memSizeInBytes = load.Return()
+			memSizeInBytes = builder.AllocateInstruction().
+				AsLoad(c.moduleCtxPtrValue, c.offset.LocalMemoryLen().U32(), ssa.TypeI32).
+				Insert(builder).
+				Return()
 		}
 
 		amount := builder.AllocateInstruction()
 		amount.AsIconst32(uint32(wasm.MemoryPageSizeInBits))
 		builder.InsertInstruction(amount)
-		memSize := builder.AllocateInstruction()
-		memSize.AsUshr(memSizeInBytes, amount.Return())
-		builder.InsertInstruction(memSize)
-		state.push(memSize.Return())
+		memSize := builder.AllocateInstruction().
+			AsUshr(memSizeInBytes, amount.Return()).
+			Insert(builder).
+			Return()
+		state.push(memSize)
 
 	case wasm.OpcodeMemoryGrow:
 		state.pc++ // skips the memory index.
@@ -643,18 +681,20 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		c.storeCallerModuleContext()
 
 		pages := state.pop()
-		loadPtr := builder.AllocateInstruction()
-		loadPtr.AsLoad(c.execCtxPtrValue,
-			wazevoapi.ExecutionContextOffsets.MemoryGrowTrampolineAddress.U32(), ssa.TypeI64)
-		builder.InsertInstruction(loadPtr)
+		loadPtr := builder.AllocateInstruction().
+			AsLoad(c.execCtxPtrValue,
+				wazevoapi.ExecutionContextOffsets.MemoryGrowTrampolineAddress.U32(),
+				ssa.TypeI64,
+			).Insert(builder).Return()
 
 		// TODO: reuse the slice.
 		args := []ssa.Value{c.execCtxPtrValue, pages}
-		callGrow := builder.AllocateInstruction()
-		callGrow.AsCallIndirect(loadPtr.Return(), &c.memoryGrowSig, args)
-		builder.InsertInstruction(callGrow)
 
-		state.push(callGrow.Return())
+		callGrowRet := builder.
+			AllocateInstruction().
+			AsCallIndirect(loadPtr, &c.memoryGrowSig, args).
+			Insert(builder).Return()
+		state.push(callGrowRet)
 
 		// After the memory grow, reload the cached memory base and len.
 		c.reloadMemoryBaseLen()
@@ -699,10 +739,9 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		value := state.pop()
 		baseAddr := state.pop()
 		addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
-
-		store := builder.AllocateInstruction()
-		store.AsStore(opcode, value, addr, offset)
-		builder.InsertInstruction(store)
+		builder.AllocateInstruction().
+			AsStore(opcode, value, addr, offset).
+			Insert(builder)
 
 	case wasm.OpcodeI32Load,
 		wasm.OpcodeI64Load,
@@ -1357,7 +1396,7 @@ func (c *Compiler) getWasmGlobalValue(index wasm.Index, forceLoad bool) ssa.Valu
 
 	builder := c.ssaBuilder
 	if !forceLoad {
-		if v := builder.FindValue(variable); v.Valid() {
+		if v := builder.FindValueInLinearPath(variable); v.Valid() {
 			return v
 		}
 	}
@@ -1383,7 +1422,7 @@ func (c *Compiler) getMemoryBaseValue(forceReload bool) ssa.Value {
 	builder := c.ssaBuilder
 	variable := c.memoryBaseVariable
 	if !forceReload {
-		if v := builder.FindValue(variable); v.Valid() {
+		if v := builder.FindValueInLinearPath(variable); v.Valid() {
 			return v
 		}
 	}
@@ -1414,7 +1453,7 @@ func (c *Compiler) getMemoryLenValue(forceReload bool) ssa.Value {
 	variable := c.memoryLenVariable
 	builder := c.ssaBuilder
 	if !forceReload {
-		if v := builder.FindValue(variable); v.Valid() {
+		if v := builder.FindValueInLinearPath(variable); v.Valid() {
 			return v
 		}
 	}
diff --git a/internal/engine/wazevo/ssa/basic_block.go b/internal/engine/wazevo/ssa/basic_block.go
index a916db84..dcc1969a 100644
--- a/internal/engine/wazevo/ssa/basic_block.go
+++ b/internal/engine/wazevo/ssa/basic_block.go
@@ -285,13 +285,6 @@ func (bb *basicBlock) FormatHeader(b Builder) string {
 	if len(bb.preds) > 0 {
 		preds := make([]string, 0, len(bb.preds))
 		for _, pred := range bb.preds {
-			if len(pred.branch.vs) != len(bb.params) {
-				panic(fmt.Sprintf(
-					"BUG: len(argument at %s) != len(params at %s): %d != %d: %s",
-					pred.blk.Name(), bb.Name(),
-					len(bb.params), len(pred.branch.vs), pred.branch.Format(b),
-				))
-			}
 			if pred.blk.invalid {
 				continue
 			}
@@ -318,13 +311,22 @@ func (bb *basicBlock) validate(b *builder) {
 						pred.branch.Format(b), bb.Name(), target.Name()))
 				}
 			}
-			if len(pred.branch.vs) != len(bb.params) {
+
+			var exp int
+			if bb.ReturnBlock() {
+				exp = len(b.currentSignature.Results)
+			} else {
+				exp = len(bb.params)
+			}
+
+			if len(pred.branch.vs) != exp {
 				panic(fmt.Sprintf(
 					"BUG: len(argument at %s) != len(params at %s): %d != %d: %s",
 					pred.blk.Name(), bb.Name(),
-					len(bb.params), len(pred.branch.vs), pred.branch.Format(b),
+					len(pred.branch.vs), len(bb.params), pred.branch.Format(b),
 				))
 			}
+
 		}
 	}
 }
diff --git a/internal/engine/wazevo/ssa/builder.go b/internal/engine/wazevo/ssa/builder.go
index 45b5c294..a9b4f1d6 100644
--- a/internal/engine/wazevo/ssa/builder.go
+++ b/internal/engine/wazevo/ssa/builder.go
@@ -16,8 +16,8 @@ type Builder interface {
 	// Signature returns the Signature of the currently-compiled function.
 	Signature() *Signature
 
-	// Blocks returns the number of BasicBlocks(s) existing in the currently-compiled function.
-	Blocks() int
+	// BlockIDMax returns the maximum value of BasicBlocksID existing in the currently-compiled function.
+	BlockIDMax() BasicBlockID
 
 	// AllocateBasicBlock creates a basic block in SSA function.
 	AllocateBasicBlock() BasicBlock
@@ -51,9 +51,8 @@ type Builder interface {
 	// MustFindValue searches the latest definition of the given Variable and returns the result.
 	MustFindValue(variable Variable) Value
 
-	// FindValue is the same as FindValue except that if the Value is not defined anywhere yet,
-	// the given callback `create` is called to create a new Value.
-	FindValue(variable Variable) Value
+	// FindValueInLinearPath TODO
+	FindValueInLinearPath(variable Variable) Value
 
 	// Seal declares that we've known all the predecessors to this block and were added via AddPred.
 	// After calling this, AddPred will be forbidden.
@@ -177,9 +176,6 @@ type builder struct {
 	donePasses bool
 	// doneBlockLayout is true if LayoutBlocks is called.
 	doneBlockLayout bool
-
-	// vs is reused by builder.FindValue.
-	vs []Value
 }
 
 // ReturnBlock implements Builder.ReturnBlock.
@@ -366,85 +362,81 @@ func (b *builder) allocateValue(typ Type) (v Value) {
 	return
 }
 
-// MustFindValue implements Builder.MustFindValue.
-func (b *builder) MustFindValue(variable Variable) Value {
-	typ := b.definedVariableType(variable)
-	return b.findValue(typ, variable, b.currentBB, true)
+// FindValueInLinearPath implements Builder.FindValue.
+func (b *builder) FindValueInLinearPath(variable Variable) Value {
+	return b.findValueInLinearPath(variable, b.currentBB)
+}
+
+func (b *builder) findValueInLinearPath(variable Variable, blk *basicBlock) Value {
+	if val, ok := blk.lastDefinitions[variable]; ok {
+		return val
+	} else if !blk.sealed {
+		return ValueInvalid
+	}
+
+	if pred := blk.singlePred; pred != nil {
+		// If this block is sealed and have only one predecessor,
+		// we can use the value in that block without ambiguity on definition.
+		return b.findValueInLinearPath(variable, pred)
+	}
+	if len(blk.preds) == 1 {
+		panic("BUG")
+	}
+	return ValueInvalid
 }
 
-// FindValue implements Builder.FindValue.
-func (b *builder) FindValue(variable Variable) Value {
+// MustFindValue implements Builder.MustFindValue.
+func (b *builder) MustFindValue(variable Variable) Value {
 	typ := b.definedVariableType(variable)
-	return b.findValue(typ, variable, b.currentBB, false)
+	return b.findValue(typ, variable, b.currentBB)
 }
 
 // findValue recursively tries to find the latest definition of a `variable`. The algorithm is described in
 // the section 2 of the paper https://link.springer.com/content/pdf/10.1007/978-3-642-37051-9_6.pdf.
 //
 // TODO: reimplement this in iterative, not recursive, to avoid stack overflow.
-func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock, must bool) Value {
+func (b *builder) findValue(typ Type, variable Variable, blk *basicBlock) Value {
 	if val, ok := blk.lastDefinitions[variable]; ok {
 		// The value is already defined in this block!
 		return val
 	} else if !blk.sealed { // Incomplete CFG as in the paper.
-		if must {
-			// If this is not sealed, that means it might have additional unknown predecessor later on.
-			// So we temporarily define the placeholder value here (not add as a parameter yet!),
-			// and record it as unknown.
-			// The unknown values are resolved when we call seal this block via BasicBlock.Seal().
-			value := b.allocateValue(typ)
-			if debug {
-				fmt.Printf("adding unknown value placeholder for %s at %d\n", variable, blk.id)
-			}
-			blk.lastDefinitions[variable] = value
-			blk.unknownValues[variable] = value
-			return value
+		// If this is not sealed, that means it might have additional unknown predecessor later on.
+		// So we temporarily define the placeholder value here (not add as a parameter yet!),
+		// and record it as unknown.
+		// The unknown values are resolved when we call seal this block via BasicBlock.Seal().
+		value := b.allocateValue(typ)
+		if debug {
+			fmt.Printf("adding unknown value placeholder for %s at %d\n", variable, blk.id)
 		}
-		return ValueInvalid
+		blk.lastDefinitions[variable] = value
+		blk.unknownValues[variable] = value
+		return value
 	}
 
 	if pred := blk.singlePred; pred != nil {
 		// If this block is sealed and have only one predecessor,
 		// we can use the value in that block without ambiguity on definition.
-		return b.findValue(typ, variable, pred, must)
+		return b.findValue(typ, variable, pred)
 	} else if len(blk.preds) == 0 {
-		// This case the value is not defined.
-		if must {
-			panic("BUG: value is not defined for " + variable.String())
-		}
-		return ValueInvalid
+		panic("BUG: value is not defined for " + variable.String())
 	}
 
-	b.vs = b.vs[:0]
 	// If this block has multiple predecessors, we have to gather the definitions,
 	// and treat them as an argument to this block.
 	//
-	// First gather all the definitions.
-	for i := range blk.preds {
-		pred := &blk.preds[i]
-		// Find the definition in the predecessor recursively.
-		value := b.findValue(typ, variable, pred.blk, must)
-		b.vs = append(b.vs, value)
-	}
-	if !must {
-		// If this is not a must, the value definition might be incomplete.
-		for _, v := range b.vs {
-			if !v.Valid() {
-				// If one of them is invalid, the value is not defined.
-				return ValueInvalid
-			}
-		}
-	}
-	// So the next thing we do now is to define a new parameter to this block which may or may not be redundant, but
-	// later we eliminate trivial params in an optimization pass.
+	// The first thing is to define a new parameter to this block which may or may not be redundant, but
+	// later we eliminate trivial params in an optimization pass. This must be done before finding the
+	// definitions in the predecessors so that we can break the cycle.
 	paramValue := blk.AddParam(b, typ)
 	b.DefineVariable(variable, paramValue, blk)
+
 	// After the new param is added, we have to manipulate the original branching instructions
 	// in predecessors so that they would pass the definition of `variable` as the argument to
 	// the newly added PHI.
 	for i := range blk.preds {
 		pred := &blk.preds[i]
-		pred.branch.addArgumentBranchInst(b.vs[i])
+		value := b.findValue(typ, variable, pred.blk)
+		pred.branch.addArgumentBranchInst(value)
 	}
 	return paramValue
 }
@@ -462,7 +454,7 @@ func (b *builder) Seal(raw BasicBlock) {
 		blk.addParamOn(typ, phiValue)
 		for i := range blk.preds {
 			pred := &blk.preds[i]
-			predValue := b.findValue(typ, variable, pred.blk, false)
+			predValue := b.findValue(typ, variable, pred.blk)
 			if !predValue.Valid() {
 				panic("BUG: value is not defined anywhere in the predecessors in the CFG")
 			}
@@ -602,6 +594,10 @@ func (b *builder) resolveArgumentAlias(instr *Instruction) {
 		instr.v2 = b.resolveAlias(instr.v2)
 	}
 
+	if instr.v3.Valid() {
+		instr.v3 = b.resolveAlias(instr.v3)
+	}
+
 	for i, v := range instr.vs {
 		instr.vs[i] = b.resolveAlias(v)
 	}
@@ -639,9 +635,9 @@ func (b *builder) isDominatedBy(n *basicBlock, d *basicBlock) bool {
 	return n == d
 }
 
-// Blocks implements Builder.Blocks.
-func (b *builder) Blocks() int {
-	return len(b.reversePostOrderedBasicBlocks)
+// BlockIDMax implements Builder.BlockIDMax.
+func (b *builder) BlockIDMax() BasicBlockID {
+	return BasicBlockID(b.basicBlocksPool.Allocated())
 }
 
 // LayoutBlocks implements Builder.LayoutBlocks. This re-organizes builder.reversePostOrderedBasicBlocks.
diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go
index 22374f22..1b46fade 100644
--- a/internal/engine/wazevo/ssa/instructions.go
+++ b/internal/engine/wazevo/ssa/instructions.go
@@ -870,6 +870,7 @@ var instructionSideEffects = [opcodeEnd]sideEffect{
 	OpcodeFcvtFromSint:       sideEffectFalse,
 	OpcodeFcvtFromUint:       sideEffectFalse,
 	OpcodeFpromote:           sideEffectFalse,
+	OpcodeBitcast:            sideEffectFalse,
 	OpcodeIreduce:            sideEffectFalse,
 }
 
@@ -885,6 +886,7 @@ func (i *Instruction) HasSideEffects() bool {
 // instructionReturnTypes provides the function to determine the return types of an instruction.
 var instructionReturnTypes = [opcodeEnd]returnTypesFn{
 	OpcodeBand:      returnTypesFnSingle,
+	OpcodeBitcast:   returnTypesFnSingle,
 	OpcodeBor:       returnTypesFnSingle,
 	OpcodeBxor:      returnTypesFnSingle,
 	OpcodeRotl:      returnTypesFnSingle,
@@ -971,11 +973,12 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{
 }
 
 // AsLoad initializes this instruction as a store instruction with OpcodeLoad.
-func (i *Instruction) AsLoad(ptr Value, offset uint32, typ Type) {
+func (i *Instruction) AsLoad(ptr Value, offset uint32, typ Type) *Instruction {
 	i.opcode = OpcodeLoad
 	i.v = ptr
 	i.u64 = uint64(offset)
 	i.typ = typ
+	return i
 }
 
 // AsExtLoad initializes this instruction as a store instruction with OpcodeLoad.
@@ -996,7 +999,7 @@ func (i *Instruction) LoadData() (ptr Value, offset uint32, typ Type) {
 }
 
 // AsStore initializes this instruction as a store instruction with OpcodeStore.
-func (i *Instruction) AsStore(storeOp Opcode, value, ptr Value, offset uint32) {
+func (i *Instruction) AsStore(storeOp Opcode, value, ptr Value, offset uint32) *Instruction {
 	i.opcode = storeOp
 	i.v = value
 	i.v2 = ptr
@@ -1015,6 +1018,7 @@ func (i *Instruction) AsStore(storeOp Opcode, value, ptr Value, offset uint32) {
 		panic("invalid store opcode" + storeOp.String())
 	}
 	i.u64 = uint64(offset) | dstSize<<32
+	return i
 }
 
 // StoreData returns the operands for a store instruction.
@@ -1118,19 +1122,21 @@ func (i *Instruction) AsBxor(x, amount Value) {
 }
 
 // AsIshl initializes this instruction as an integer shift left instruction with OpcodeIshl.
-func (i *Instruction) AsIshl(x, amount Value) {
+func (i *Instruction) AsIshl(x, amount Value) *Instruction {
 	i.opcode = OpcodeIshl
 	i.v = x
 	i.v2 = amount
 	i.typ = x.Type()
+	return i
 }
 
 // AsUshr initializes this instruction as an integer unsigned shift right (logical shift right) instruction with OpcodeUshr.
-func (i *Instruction) AsUshr(x, amount Value) {
+func (i *Instruction) AsUshr(x, amount Value) *Instruction {
 	i.opcode = OpcodeUshr
 	i.v = x
 	i.v2 = amount
 	i.typ = x.Type()
+	return i
 }
 
 // AsSshr initializes this instruction as an integer signed shift right (arithmetic shift right) instruction with OpcodeSshr.
@@ -1380,13 +1386,14 @@ func (i *Instruction) CallData() (ref FuncRef, sigID SignatureID, args []Value)
 }
 
 // AsCallIndirect initializes this instruction as a call-indirect instruction with OpcodeCallIndirect.
-func (i *Instruction) AsCallIndirect(funcPtr Value, sig *Signature, args []Value) {
+func (i *Instruction) AsCallIndirect(funcPtr Value, sig *Signature, args []Value) *Instruction {
 	i.opcode = OpcodeCallIndirect
 	i.typ = TypeF64
 	i.vs = args
 	i.v = Value(sig.ID)
 	i.v2 = funcPtr
 	sig.used = true
+	return i
 }
 
 // CallIndirectData returns the call indirect data for this instruction necessary for backends.
@@ -1427,10 +1434,24 @@ func (i *Instruction) UnaryData() Value {
 }
 
 // AsFneg initializes this instruction as an instruction with OpcodeFneg.
-func (i *Instruction) AsFneg(x Value) {
+func (i *Instruction) AsFneg(x Value) *Instruction {
 	i.opcode = OpcodeFneg
 	i.v = x
 	i.typ = x.Type()
+	return i
+}
+
+// AsBitcast initializes this instruction as an instruction with OpcodeBitcast.
+func (i *Instruction) AsBitcast(x Value, dstType Type) *Instruction {
+	i.opcode = OpcodeBitcast
+	i.v = x
+	i.typ = dstType
+	return i
+}
+
+// BitcastData returns the operands for a bitcast instruction.
+func (i *Instruction) BitcastData() (x Value, dstType Type) {
+	return i.v, i.typ
 }
 
 // AsFpromote initializes this instruction as an instruction with OpcodeFpromote.
@@ -1490,12 +1511,13 @@ func (i *Instruction) ExtendData() (from, to byte, signed bool) {
 }
 
 // AsSelect initializes this instruction as an unsigned extension instruction with OpcodeSelect.
-func (i *Instruction) AsSelect(c, x, y Value) {
+func (i *Instruction) AsSelect(c, x, y Value) *Instruction {
 	i.opcode = OpcodeSelect
 	i.v = c
 	i.v2 = x
 	i.v3 = y
 	i.typ = x.Type()
+	return i
 }
 
 // SelectData returns the select data for this instruction necessary for backends.
@@ -1605,7 +1627,7 @@ func (i *Instruction) Format(b Builder) string {
 		instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b))
 	case OpcodeUndefined:
 	case OpcodeClz, OpcodeCtz, OpcodePopcnt, OpcodeFneg, OpcodeFcvtFromSint, OpcodeFcvtFromUint, OpcodeFpromote,
-		OpcodeIreduce:
+		OpcodeIreduce, OpcodeBitcast:
 		instSuffix = " " + i.v.Format(b)
 	default:
 		panic(fmt.Sprintf("TODO: format for %s", i.opcode))
diff --git a/internal/engine/wazevo/ssa/pass.go b/internal/engine/wazevo/ssa/pass.go
index 07b23951..6a32e8b1 100644
--- a/internal/engine/wazevo/ssa/pass.go
+++ b/internal/engine/wazevo/ssa/pass.go
@@ -45,6 +45,10 @@ func passDeadBlockEliminationOpt(b *builder) {
 			panic(fmt.Sprintf("%s is not sealed", reachableBlk))
 		}
 
+		if true {
+			reachableBlk.validate(b)
+		}
+
 		for _, succ := range reachableBlk.success {
 			if _, ok := b.blkVisited[succ]; ok {
 				continue


Test patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
index 010e2513..4bc8f550 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -506,8 +506,12 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "4158200e", setup: func(i *instruction) { i.asVecMisc(vecOpCnt, operandNR(v1VReg), operandNR(v2VReg), vecArrangement8B) }},
 		{want: "4158204e", setup: func(i *instruction) { i.asVecMisc(vecOpCnt, operandNR(v1VReg), operandNR(v2VReg), vecArrangement16B) }},
 		{want: "41c0221e", setup: func(i *instruction) {
-			i.asVecMisc(vecOpCvt32To64, operandNR(v1VReg), operandNR(v2VReg), vecArrangementNone)
+			i.asFpuRR(fpuUniCvt32To64, operandNR(v1VReg), operandNR(v2VReg), true)
 		}},
+		{want: "4140211e", setup: func(i *instruction) {
+			i.asFpuRR(fpuUniOpNeg, operandNR(v1VReg), operandNR(v2VReg), false)
+		}},
+		{want: "4140611e", setup: func(i *instruction) { i.asFpuRR(fpuUniOpNeg, operandNR(v1VReg), operandNR(v2VReg), true) }},
 		{want: "4201231e4201631e4201239e4201639e4201221e4201621e4201229e4201629e", setup: func(i *instruction) {
 			i.asNop0()
 			cur := i
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_constant_test.go b/internal/engine/wazevo/backend/isa/arm64/lower_constant_test.go
index 0fa236b0..92cc5efc 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_constant_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_constant_test.go
@@ -82,7 +82,10 @@ func TestMachine_lowerConstantI32(t *testing.T) {
 		{val: 0xffff_0000, exp: []string{"movz w0, #0xffff, lsl 16"}},
 		{val: 0xffff_fffe, exp: []string{"movn w0, #0x1, lsl 0"}},
 		{val: 0x2, exp: []string{"orr w0, wzr, #0x2"}},
-		{val: 0x80000001, exp: []string{"orr w0, wzr, #0x80000001"}},
+		{val: 0x80000001, exp: []string{
+			"movz w0, #0x1, lsl 0",
+			"movk w0, #0x8000, lsl 16",
+		}},
 		{val: 0xf00000f, exp: []string{
 			"movz w0, #0xf, lsl 0",
 			"movk w0, #0xf00, lsl 16",
diff --git a/internal/engine/wazevo/backend/machine_test.go b/internal/engine/wazevo/backend/machine_test.go
index ef71d7ec..f2a361cb 100644
--- a/internal/engine/wazevo/backend/machine_test.go
+++ b/internal/engine/wazevo/backend/machine_test.go
@@ -9,7 +9,7 @@ import (
 // mockMachine implements Machine for testing.
 type mockMachine struct {
 	abi                    mockABI
-	startLoweringFunction  func(numBlks int)
+	startLoweringFunction  func(id ssa.BasicBlockID)
 	startBlock             func(block ssa.BasicBlock)
 	lowerSingleBranch      func(b *ssa.Instruction)
 	lowerConditionalBranch func(b *ssa.Instruction)
@@ -75,8 +75,8 @@ func (m mockMachine) ABI() FunctionABI { return m.abi }
 func (m mockMachine) SetCompiler(Compiler) {}
 
 // StartLoweringFunction implements Machine.StartLoweringFunction.
-func (m mockMachine) StartLoweringFunction(numBlks int) {
-	m.startLoweringFunction(numBlks)
+func (m mockMachine) StartLoweringFunction(id ssa.BasicBlockID) {
+	m.startLoweringFunction(id)
 }
 
 // StartBlock implements Machine.StartBlock.
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index ea124581..d939393c 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -38,28 +38,54 @@ func TestSpectestV1(t *testing.T) {
 		name string
 	}{
 		{name: "address"},
+		{name: "align"},
 		{name: "br"},
+		{name: "br_if"},
 		{name: "br_table"},
 		{name: "break-drop"},
 		{name: "block"},
 		{name: "binary"},
 		{name: "binary-leb128"},
+		//{name: "call"},
+		//{name: "call_indirect"},
 		{name: "comments"},
 		{name: "custom"},
 		{name: "const"},
 		{name: "data"},
 		{name: "elem"},
+		{name: "endianness"},
 		{name: "exports"},
+		{name: "f32_cmp"},
+		{name: "f64_cmp"},
+		//{name: "fac"},
+		//{name: "float_exprs"},
+		{name: "float_literals"},
+		{name: "func"},
+		{name: "func_ptrs"},
+		{name: "forward"},
 		{name: "globals"},
 		{name: "if"},
-		{name: "names"},
+		{name: "imports"},
+		//{name: "int_exprs"},
+		{name: "int_literals"},
+		{name: "labels"},
+		//{name: "left-to-right"},
 		{name: "load"},
+		//{name: "loop"},
+		{name: "local_get"},
+		//{name: "local_set"},
+		//{name: "local_tee"},
+		{name: "memory"},
 		{name: "memory_size"},
 		{name: "memory_grow"},
+		{name: "memory_redundancy"},
+		{name: "memory_trap"},
+		{name: "names"},
 		{name: "nop"},
 		{name: "return"},
 		{name: "select"},
 		{name: "store"},
+		{name: "switch"},
 		{name: "type"},
 		{name: "unreachable"},
 		{name: "unreached-invalid"},
diff --git a/internal/engine/wazevo/ssa/builder_test.go b/internal/engine/wazevo/ssa/builder_test.go
index 38b223d5..3cca81d8 100644
--- a/internal/engine/wazevo/ssa/builder_test.go
+++ b/internal/engine/wazevo/ssa/builder_test.go
@@ -555,6 +555,7 @@ func TestBuilder_LayoutBlocks(t *testing.T) {
 			name: "loop with output",
 			exp:  []BasicBlockID{0x0, 0x2, 0x4, 0x1, 0x3, 0x6, 0x5},
 			setup: func(b *builder) {
+				b.currentSignature = &Signature{Results: []Type{TypeI32}}
 				b0, b1, b2, b3 := b.allocateBasicBlock(), b.allocateBasicBlock(), b.allocateBasicBlock(), b.allocateBasicBlock()
 
 				b.SetCurrentBlock(b0)