summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go4268
1 files changed, 4268 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go
new file mode 100644
index 000000000..5096a6365
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/frontend/lower.go
@@ -0,0 +1,4268 @@
+package frontend
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "runtime"
+ "strings"
+
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
+ "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
+ "github.com/tetratelabs/wazero/internal/leb128"
+ "github.com/tetratelabs/wazero/internal/wasm"
+)
+
+type (
+ // loweringState is used to keep the state of lowering.
+ loweringState struct {
+ // values holds the values on the Wasm stack.
+ values []ssa.Value
+ controlFrames []controlFrame
+ unreachable bool
+ unreachableDepth int
+ tmpForBrTable []uint32
+ pc int
+ }
+ controlFrame struct {
+ kind controlFrameKind
+ // originalStackLen holds the number of values on the Wasm stack
+ // when start executing this control frame minus params for the block.
+ originalStackLenWithoutParam int
+ // blk is the loop header if this is loop, and is the else-block if this is an if frame.
+ blk,
+ // followingBlock is the basic block we enter if we reach "end" of block.
+ followingBlock ssa.BasicBlock
+ blockType *wasm.FunctionType
+ // clonedArgs hold the arguments to Else block.
+ clonedArgs ssa.Values
+ }
+
+ controlFrameKind byte
+)
+
+// String implements fmt.Stringer for debugging.
+func (l *loweringState) String() string {
+ var str []string
+ for _, v := range l.values {
+ str = append(str, fmt.Sprintf("v%v", v.ID()))
+ }
+ var frames []string
+ for i := range l.controlFrames {
+ frames = append(frames, l.controlFrames[i].kind.String())
+ }
+ return fmt.Sprintf("\n\tunreachable=%v(depth=%d)\n\tstack: %s\n\tcontrol frames: %s",
+ l.unreachable, l.unreachableDepth,
+ strings.Join(str, ", "),
+ strings.Join(frames, ", "),
+ )
+}
+
+const (
+ controlFrameKindFunction = iota + 1
+ controlFrameKindLoop
+ controlFrameKindIfWithElse
+ controlFrameKindIfWithoutElse
+ controlFrameKindBlock
+)
+
+// String implements fmt.Stringer for debugging.
+func (k controlFrameKind) String() string {
+ switch k {
+ case controlFrameKindFunction:
+ return "function"
+ case controlFrameKindLoop:
+ return "loop"
+ case controlFrameKindIfWithElse:
+ return "if_with_else"
+ case controlFrameKindIfWithoutElse:
+ return "if_without_else"
+ case controlFrameKindBlock:
+ return "block"
+ default:
+ panic(k)
+ }
+}
+
+// isLoop returns true if this is a loop frame.
+func (ctrl *controlFrame) isLoop() bool {
+ return ctrl.kind == controlFrameKindLoop
+}
+
+// reset resets the state of loweringState for reuse.
+func (l *loweringState) reset() {
+ l.values = l.values[:0]
+ l.controlFrames = l.controlFrames[:0]
+ l.pc = 0
+ l.unreachable = false
+ l.unreachableDepth = 0
+}
+
+func (l *loweringState) peek() (ret ssa.Value) {
+ tail := len(l.values) - 1
+ return l.values[tail]
+}
+
+func (l *loweringState) pop() (ret ssa.Value) {
+ tail := len(l.values) - 1
+ ret = l.values[tail]
+ l.values = l.values[:tail]
+ return
+}
+
+func (l *loweringState) push(ret ssa.Value) {
+ l.values = append(l.values, ret)
+}
+
+func (c *Compiler) nPeekDup(n int) ssa.Values {
+ if n == 0 {
+ return ssa.ValuesNil
+ }
+
+ l := c.state()
+ tail := len(l.values)
+
+ args := c.allocateVarLengthValues(n)
+ args = args.Append(c.ssaBuilder.VarLengthPool(), l.values[tail-n:tail]...)
+ return args
+}
+
+func (l *loweringState) ctrlPop() (ret controlFrame) {
+ tail := len(l.controlFrames) - 1
+ ret = l.controlFrames[tail]
+ l.controlFrames = l.controlFrames[:tail]
+ return
+}
+
+func (l *loweringState) ctrlPush(ret controlFrame) {
+ l.controlFrames = append(l.controlFrames, ret)
+}
+
+func (l *loweringState) ctrlPeekAt(n int) (ret *controlFrame) {
+ tail := len(l.controlFrames) - 1
+ return &l.controlFrames[tail-n]
+}
+
+// lowerBody lowers the body of the Wasm function to the SSA form.
+func (c *Compiler) lowerBody(entryBlk ssa.BasicBlock) {
+ c.ssaBuilder.Seal(entryBlk)
+
+ if c.needListener {
+ c.callListenerBefore()
+ }
+
+ // Pushes the empty control frame which corresponds to the function return.
+ c.loweringState.ctrlPush(controlFrame{
+ kind: controlFrameKindFunction,
+ blockType: c.wasmFunctionTyp,
+ followingBlock: c.ssaBuilder.ReturnBlock(),
+ })
+
+ for c.loweringState.pc < len(c.wasmFunctionBody) {
+ blkBeforeLowering := c.ssaBuilder.CurrentBlock()
+ c.lowerCurrentOpcode()
+ blkAfterLowering := c.ssaBuilder.CurrentBlock()
+ if blkBeforeLowering != blkAfterLowering {
+ // In Wasm, once a block exits, that means we've done compiling the block.
+ // Therefore, we finalize the known bounds at the end of the block for the exiting block.
+ c.finalizeKnownSafeBoundsAtTheEndOfBlock(blkBeforeLowering.ID())
+ // After that, we initialize the known bounds for the new compilation target block.
+ c.initializeCurrentBlockKnownBounds()
+ }
+ }
+}
+
+func (c *Compiler) state() *loweringState {
+ return &c.loweringState
+}
+
+func (c *Compiler) lowerCurrentOpcode() {
+ op := c.wasmFunctionBody[c.loweringState.pc]
+
+ if c.needSourceOffsetInfo {
+ c.ssaBuilder.SetCurrentSourceOffset(
+ ssa.SourceOffset(c.loweringState.pc) + ssa.SourceOffset(c.wasmFunctionBodyOffsetInCodeSection),
+ )
+ }
+
+ builder := c.ssaBuilder
+ state := c.state()
+ switch op {
+ case wasm.OpcodeI32Const:
+ c := c.readI32s()
+ if state.unreachable {
+ break
+ }
+
+ iconst := builder.AllocateInstruction().AsIconst32(uint32(c)).Insert(builder)
+ value := iconst.Return()
+ state.push(value)
+ case wasm.OpcodeI64Const:
+ c := c.readI64s()
+ if state.unreachable {
+ break
+ }
+ iconst := builder.AllocateInstruction().AsIconst64(uint64(c)).Insert(builder)
+ value := iconst.Return()
+ state.push(value)
+ case wasm.OpcodeF32Const:
+ f32 := c.readF32()
+ if state.unreachable {
+ break
+ }
+ f32const := builder.AllocateInstruction().
+ AsF32const(f32).
+ Insert(builder).
+ Return()
+ state.push(f32const)
+ case wasm.OpcodeF64Const:
+ f64 := c.readF64()
+ if state.unreachable {
+ break
+ }
+ f64const := builder.AllocateInstruction().
+ AsF64const(f64).
+ Insert(builder).
+ Return()
+ state.push(f64const)
+ case wasm.OpcodeI32Add, wasm.OpcodeI64Add:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ iadd := builder.AllocateInstruction()
+ iadd.AsIadd(x, y)
+ builder.InsertInstruction(iadd)
+ value := iadd.Return()
+ state.push(value)
+ case wasm.OpcodeI32Sub, wasm.OpcodeI64Sub:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsIsub(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeF32Add, wasm.OpcodeF64Add:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ iadd := builder.AllocateInstruction()
+ iadd.AsFadd(x, y)
+ builder.InsertInstruction(iadd)
+ value := iadd.Return()
+ state.push(value)
+ case wasm.OpcodeI32Mul, wasm.OpcodeI64Mul:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ imul := builder.AllocateInstruction()
+ imul.AsImul(x, y)
+ builder.InsertInstruction(imul)
+ value := imul.Return()
+ state.push(value)
+ case wasm.OpcodeF32Sub, wasm.OpcodeF64Sub:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsFsub(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeF32Mul, wasm.OpcodeF64Mul:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsFmul(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeF32Div, wasm.OpcodeF64Div:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsFdiv(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeF32Max, wasm.OpcodeF64Max:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsFmax(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeF32Min, wasm.OpcodeF64Min:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ isub := builder.AllocateInstruction()
+ isub.AsFmin(x, y)
+ builder.InsertInstruction(isub)
+ value := isub.Return()
+ state.push(value)
+ case wasm.OpcodeI64Extend8S:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(true, 8, 64)
+ case wasm.OpcodeI64Extend16S:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(true, 16, 64)
+ case wasm.OpcodeI64Extend32S, wasm.OpcodeI64ExtendI32S:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(true, 32, 64)
+ case wasm.OpcodeI64ExtendI32U:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(false, 32, 64)
+ case wasm.OpcodeI32Extend8S:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(true, 8, 32)
+ case wasm.OpcodeI32Extend16S:
+ if state.unreachable {
+ break
+ }
+ c.insertIntegerExtend(true, 16, 32)
+ case wasm.OpcodeI32Eqz, wasm.OpcodeI64Eqz:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ zero := builder.AllocateInstruction()
+ if op == wasm.OpcodeI32Eqz {
+ zero.AsIconst32(0)
+ } else {
+ zero.AsIconst64(0)
+ }
+ builder.InsertInstruction(zero)
+ icmp := builder.AllocateInstruction().
+ AsIcmp(x, zero.Return(), ssa.IntegerCmpCondEqual).
+ Insert(builder).
+ Return()
+ state.push(icmp)
+ case wasm.OpcodeI32Eq, wasm.OpcodeI64Eq:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondEqual)
+ case wasm.OpcodeI32Ne, wasm.OpcodeI64Ne:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondNotEqual)
+ case wasm.OpcodeI32LtS, wasm.OpcodeI64LtS:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondSignedLessThan)
+ case wasm.OpcodeI32LtU, wasm.OpcodeI64LtU:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondUnsignedLessThan)
+ case wasm.OpcodeI32GtS, wasm.OpcodeI64GtS:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondSignedGreaterThan)
+ case wasm.OpcodeI32GtU, wasm.OpcodeI64GtU:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondUnsignedGreaterThan)
+ case wasm.OpcodeI32LeS, wasm.OpcodeI64LeS:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondSignedLessThanOrEqual)
+ case wasm.OpcodeI32LeU, wasm.OpcodeI64LeU:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondUnsignedLessThanOrEqual)
+ case wasm.OpcodeI32GeS, wasm.OpcodeI64GeS:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondSignedGreaterThanOrEqual)
+ case wasm.OpcodeI32GeU, wasm.OpcodeI64GeU:
+ if state.unreachable {
+ break
+ }
+ c.insertIcmp(ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
+
+ case wasm.OpcodeF32Eq, wasm.OpcodeF64Eq:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondEqual)
+ case wasm.OpcodeF32Ne, wasm.OpcodeF64Ne:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondNotEqual)
+ case wasm.OpcodeF32Lt, wasm.OpcodeF64Lt:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondLessThan)
+ case wasm.OpcodeF32Gt, wasm.OpcodeF64Gt:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondGreaterThan)
+ case wasm.OpcodeF32Le, wasm.OpcodeF64Le:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondLessThanOrEqual)
+ case wasm.OpcodeF32Ge, wasm.OpcodeF64Ge:
+ if state.unreachable {
+ break
+ }
+ c.insertFcmp(ssa.FloatCmpCondGreaterThanOrEqual)
+ case wasm.OpcodeF32Neg, wasm.OpcodeF64Neg:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsFneg(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Sqrt, wasm.OpcodeF64Sqrt:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsSqrt(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Abs, wasm.OpcodeF64Abs:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsFabs(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Copysign, wasm.OpcodeF64Copysign:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ v := builder.AllocateInstruction().AsFcopysign(x, y).Insert(builder).Return()
+ state.push(v)
+
+ case wasm.OpcodeF32Ceil, wasm.OpcodeF64Ceil:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsCeil(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Floor, wasm.OpcodeF64Floor:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsFloor(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Trunc, wasm.OpcodeF64Trunc:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsTrunc(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeF32Nearest, wasm.OpcodeF64Nearest:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ v := builder.AllocateInstruction().AsNearest(x).Insert(builder).Return()
+ state.push(v)
+ case wasm.OpcodeI64TruncF64S, wasm.OpcodeI64TruncF32S,
+ wasm.OpcodeI32TruncF64S, wasm.OpcodeI32TruncF32S,
+ wasm.OpcodeI64TruncF64U, wasm.OpcodeI64TruncF32U,
+ wasm.OpcodeI32TruncF64U, wasm.OpcodeI32TruncF32U:
+ if state.unreachable {
+ break
+ }
+ ret := builder.AllocateInstruction().AsFcvtToInt(
+ state.pop(),
+ c.execCtxPtrValue,
+ op == wasm.OpcodeI64TruncF64S || op == wasm.OpcodeI64TruncF32S || op == wasm.OpcodeI32TruncF32S || op == wasm.OpcodeI32TruncF64S,
+ op == wasm.OpcodeI64TruncF64S || op == wasm.OpcodeI64TruncF32S || op == wasm.OpcodeI64TruncF64U || op == wasm.OpcodeI64TruncF32U,
+ false,
+ ).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeMiscPrefix:
+ state.pc++
+ // A misc opcode is encoded as an unsigned variable 32-bit integer.
+ miscOpUint, num, err := leb128.LoadUint32(c.wasmFunctionBody[state.pc:])
+ if err != nil {
+ // In normal conditions this should never happen because the function has passed validation.
+ panic(fmt.Sprintf("failed to read misc opcode: %v", err))
+ }
+ state.pc += int(num - 1)
+ miscOp := wasm.OpcodeMisc(miscOpUint)
+ switch miscOp {
+ case wasm.OpcodeMiscI64TruncSatF64S, wasm.OpcodeMiscI64TruncSatF32S,
+ wasm.OpcodeMiscI32TruncSatF64S, wasm.OpcodeMiscI32TruncSatF32S,
+ wasm.OpcodeMiscI64TruncSatF64U, wasm.OpcodeMiscI64TruncSatF32U,
+ wasm.OpcodeMiscI32TruncSatF64U, wasm.OpcodeMiscI32TruncSatF32U:
+ if state.unreachable {
+ break
+ }
+ ret := builder.AllocateInstruction().AsFcvtToInt(
+ state.pop(),
+ c.execCtxPtrValue,
+ miscOp == wasm.OpcodeMiscI64TruncSatF64S || miscOp == wasm.OpcodeMiscI64TruncSatF32S || miscOp == wasm.OpcodeMiscI32TruncSatF32S || miscOp == wasm.OpcodeMiscI32TruncSatF64S,
+ miscOp == wasm.OpcodeMiscI64TruncSatF64S || miscOp == wasm.OpcodeMiscI64TruncSatF32S || miscOp == wasm.OpcodeMiscI64TruncSatF64U || miscOp == wasm.OpcodeMiscI64TruncSatF32U,
+ true,
+ ).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeMiscTableSize:
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ // Load the table.
+ loadTableInstancePtr := builder.AllocateInstruction()
+ loadTableInstancePtr.AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64)
+ builder.InsertInstruction(loadTableInstancePtr)
+ tableInstancePtr := loadTableInstancePtr.Return()
+
+ // Load the table's length.
+ loadTableLen := builder.AllocateInstruction().
+ AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32).
+ Insert(builder)
+ state.push(loadTableLen.Return())
+
+ case wasm.OpcodeMiscTableGrow:
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ c.storeCallerModuleContext()
+
+ tableIndexVal := builder.AllocateInstruction().AsIconst32(tableIndex).Insert(builder).Return()
+
+ num := state.pop()
+ r := state.pop()
+
+ tableGrowPtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetTableGrowTrampolineAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ args := c.allocateVarLengthValues(4, c.execCtxPtrValue, tableIndexVal, num, r)
+ callGrowRet := builder.
+ AllocateInstruction().
+ AsCallIndirect(tableGrowPtr, &c.tableGrowSig, args).
+ Insert(builder).Return()
+ state.push(callGrowRet)
+
+ case wasm.OpcodeMiscTableCopy:
+ dstTableIndex := c.readI32u()
+ srcTableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ copySize := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ srcOffset := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ dstOffset := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+ // Out of bounds check.
+ dstTableInstancePtr := c.boundsCheckInTable(dstTableIndex, dstOffset, copySize)
+ srcTableInstancePtr := c.boundsCheckInTable(srcTableIndex, srcOffset, copySize)
+
+ dstTableBaseAddr := c.loadTableBaseAddr(dstTableInstancePtr)
+ srcTableBaseAddr := c.loadTableBaseAddr(srcTableInstancePtr)
+
+ three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+
+ dstOffsetInBytes := builder.AllocateInstruction().AsIshl(dstOffset, three).Insert(builder).Return()
+ dstAddr := builder.AllocateInstruction().AsIadd(dstTableBaseAddr, dstOffsetInBytes).Insert(builder).Return()
+ srcOffsetInBytes := builder.AllocateInstruction().AsIshl(srcOffset, three).Insert(builder).Return()
+ srcAddr := builder.AllocateInstruction().AsIadd(srcTableBaseAddr, srcOffsetInBytes).Insert(builder).Return()
+
+ copySizeInBytes := builder.AllocateInstruction().AsIshl(copySize, three).Insert(builder).Return()
+ c.callMemmove(dstAddr, srcAddr, copySizeInBytes)
+
+ case wasm.OpcodeMiscMemoryCopy:
+ state.pc += 2 // +2 to skip two memory indexes which are fixed to zero.
+ if state.unreachable {
+ break
+ }
+
+ copySize := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ srcOffset := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ dstOffset := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+ // Out of bounds check.
+ memLen := c.getMemoryLenValue(false)
+ c.boundsCheckInMemory(memLen, dstOffset, copySize)
+ c.boundsCheckInMemory(memLen, srcOffset, copySize)
+
+ memBase := c.getMemoryBaseValue(false)
+ dstAddr := builder.AllocateInstruction().AsIadd(memBase, dstOffset).Insert(builder).Return()
+ srcAddr := builder.AllocateInstruction().AsIadd(memBase, srcOffset).Insert(builder).Return()
+
+ c.callMemmove(dstAddr, srcAddr, copySize)
+
+ case wasm.OpcodeMiscTableFill:
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ fillSize := state.pop()
+ value := state.pop()
+ offset := state.pop()
+
+ fillSizeExt := builder.
+ AllocateInstruction().AsUExtend(fillSize, 32, 64).Insert(builder).Return()
+ offsetExt := builder.
+ AllocateInstruction().AsUExtend(offset, 32, 64).Insert(builder).Return()
+ tableInstancePtr := c.boundsCheckInTable(tableIndex, offsetExt, fillSizeExt)
+
+ three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+ offsetInBytes := builder.AllocateInstruction().AsIshl(offsetExt, three).Insert(builder).Return()
+ fillSizeInBytes := builder.AllocateInstruction().AsIshl(fillSizeExt, three).Insert(builder).Return()
+
+ // Calculate the base address of the table.
+ tableBaseAddr := c.loadTableBaseAddr(tableInstancePtr)
+ addr := builder.AllocateInstruction().AsIadd(tableBaseAddr, offsetInBytes).Insert(builder).Return()
+
+ // Prepare the loop and following block.
+ beforeLoop := builder.AllocateBasicBlock()
+ loopBlk := builder.AllocateBasicBlock()
+ loopVar := loopBlk.AddParam(builder, ssa.TypeI64)
+ followingBlk := builder.AllocateBasicBlock()
+
+ // Uses the copy trick for faster filling buffer like memory.fill, but in this case we copy 8 bytes at a time.
+ // buf := memoryInst.Buffer[offset : offset+fillSize]
+ // buf[0:8] = value
+ // for i := 8; i < fillSize; i *= 2 { Begin with 8 bytes.
+ // copy(buf[i:], buf[:i])
+ // }
+
+ // Insert the jump to the beforeLoop block; If the fillSize is zero, then jump to the following block to skip entire logics.
+ zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+ ifFillSizeZero := builder.AllocateInstruction().AsIcmp(fillSizeExt, zero, ssa.IntegerCmpCondEqual).
+ Insert(builder).Return()
+ builder.AllocateInstruction().AsBrnz(ifFillSizeZero, ssa.ValuesNil, followingBlk).Insert(builder)
+ c.insertJumpToBlock(ssa.ValuesNil, beforeLoop)
+
+ // buf[0:8] = value
+ builder.SetCurrentBlock(beforeLoop)
+ builder.AllocateInstruction().AsStore(ssa.OpcodeStore, value, addr, 0).Insert(builder)
+ initValue := builder.AllocateInstruction().AsIconst64(8).Insert(builder).Return()
+ c.insertJumpToBlock(c.allocateVarLengthValues(1, initValue), loopBlk)
+
+ builder.SetCurrentBlock(loopBlk)
+ dstAddr := builder.AllocateInstruction().AsIadd(addr, loopVar).Insert(builder).Return()
+
+ // If loopVar*2 > fillSizeInBytes, then count must be fillSizeInBytes-loopVar.
+ var count ssa.Value
+ {
+ loopVarDoubled := builder.AllocateInstruction().AsIadd(loopVar, loopVar).Insert(builder).Return()
+ loopVarDoubledLargerThanFillSize := builder.
+ AllocateInstruction().AsIcmp(loopVarDoubled, fillSizeInBytes, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual).
+ Insert(builder).Return()
+ diff := builder.AllocateInstruction().AsIsub(fillSizeInBytes, loopVar).Insert(builder).Return()
+ count = builder.AllocateInstruction().AsSelect(loopVarDoubledLargerThanFillSize, diff, loopVar).Insert(builder).Return()
+ }
+
+ c.callMemmove(dstAddr, addr, count)
+
+ shiftAmount := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+ newLoopVar := builder.AllocateInstruction().AsIshl(loopVar, shiftAmount).Insert(builder).Return()
+ loopVarLessThanFillSize := builder.AllocateInstruction().
+ AsIcmp(newLoopVar, fillSizeInBytes, ssa.IntegerCmpCondUnsignedLessThan).Insert(builder).Return()
+
+ builder.AllocateInstruction().
+ AsBrnz(loopVarLessThanFillSize, c.allocateVarLengthValues(1, newLoopVar), loopBlk).
+ Insert(builder)
+
+ c.insertJumpToBlock(ssa.ValuesNil, followingBlk)
+ builder.SetCurrentBlock(followingBlk)
+
+ builder.Seal(beforeLoop)
+ builder.Seal(loopBlk)
+ builder.Seal(followingBlk)
+
+ case wasm.OpcodeMiscMemoryFill:
+ state.pc++ // Skip the memory index which is fixed to zero.
+ if state.unreachable {
+ break
+ }
+
+ fillSize := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ value := state.pop()
+ offset := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+ // Out of bounds check.
+ c.boundsCheckInMemory(c.getMemoryLenValue(false), offset, fillSize)
+
+ // Calculate the base address:
+ addr := builder.AllocateInstruction().AsIadd(c.getMemoryBaseValue(false), offset).Insert(builder).Return()
+
+ // Uses the copy trick for faster filling buffer: https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
+ // buf := memoryInst.Buffer[offset : offset+fillSize]
+ // buf[0] = value
+ // for i := 1; i < fillSize; i *= 2 {
+ // copy(buf[i:], buf[:i])
+ // }
+
+ // Prepare the loop and following block.
+ beforeLoop := builder.AllocateBasicBlock()
+ loopBlk := builder.AllocateBasicBlock()
+ loopVar := loopBlk.AddParam(builder, ssa.TypeI64)
+ followingBlk := builder.AllocateBasicBlock()
+
+ // Insert the jump to the beforeLoop block; If the fillSize is zero, then jump to the following block to skip entire logics.
+ zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+ ifFillSizeZero := builder.AllocateInstruction().AsIcmp(fillSize, zero, ssa.IntegerCmpCondEqual).
+ Insert(builder).Return()
+ builder.AllocateInstruction().AsBrnz(ifFillSizeZero, ssa.ValuesNil, followingBlk).Insert(builder)
+ c.insertJumpToBlock(ssa.ValuesNil, beforeLoop)
+
+ // buf[0] = value
+ builder.SetCurrentBlock(beforeLoop)
+ builder.AllocateInstruction().AsStore(ssa.OpcodeIstore8, value, addr, 0).Insert(builder)
+ initValue := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+ c.insertJumpToBlock(c.allocateVarLengthValues(1, initValue), loopBlk)
+
+ builder.SetCurrentBlock(loopBlk)
+ dstAddr := builder.AllocateInstruction().AsIadd(addr, loopVar).Insert(builder).Return()
+
+ // If loopVar*2 > fillSizeExt, then count must be fillSizeExt-loopVar.
+ var count ssa.Value
+ {
+ loopVarDoubled := builder.AllocateInstruction().AsIadd(loopVar, loopVar).Insert(builder).Return()
+ loopVarDoubledLargerThanFillSize := builder.
+ AllocateInstruction().AsIcmp(loopVarDoubled, fillSize, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual).
+ Insert(builder).Return()
+ diff := builder.AllocateInstruction().AsIsub(fillSize, loopVar).Insert(builder).Return()
+ count = builder.AllocateInstruction().AsSelect(loopVarDoubledLargerThanFillSize, diff, loopVar).Insert(builder).Return()
+ }
+
+ c.callMemmove(dstAddr, addr, count)
+
+ shiftAmount := builder.AllocateInstruction().AsIconst64(1).Insert(builder).Return()
+ newLoopVar := builder.AllocateInstruction().AsIshl(loopVar, shiftAmount).Insert(builder).Return()
+ loopVarLessThanFillSize := builder.AllocateInstruction().
+ AsIcmp(newLoopVar, fillSize, ssa.IntegerCmpCondUnsignedLessThan).Insert(builder).Return()
+
+ builder.AllocateInstruction().
+ AsBrnz(loopVarLessThanFillSize, c.allocateVarLengthValues(1, newLoopVar), loopBlk).
+ Insert(builder)
+
+ c.insertJumpToBlock(ssa.ValuesNil, followingBlk)
+ builder.SetCurrentBlock(followingBlk)
+
+ builder.Seal(beforeLoop)
+ builder.Seal(loopBlk)
+ builder.Seal(followingBlk)
+
+ case wasm.OpcodeMiscMemoryInit:
+ index := c.readI32u()
+ state.pc++ // Skip the memory index which is fixed to zero.
+ if state.unreachable {
+ break
+ }
+
+ copySize := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ offsetInDataInstance := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ offsetInMemory := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+ dataInstPtr := c.dataOrElementInstanceAddr(index, c.offset.DataInstances1stElement)
+
+ // Bounds check.
+ c.boundsCheckInMemory(c.getMemoryLenValue(false), offsetInMemory, copySize)
+ c.boundsCheckInDataOrElementInstance(dataInstPtr, offsetInDataInstance, copySize, wazevoapi.ExitCodeMemoryOutOfBounds)
+
+ dataInstBaseAddr := builder.AllocateInstruction().AsLoad(dataInstPtr, 0, ssa.TypeI64).Insert(builder).Return()
+ srcAddr := builder.AllocateInstruction().AsIadd(dataInstBaseAddr, offsetInDataInstance).Insert(builder).Return()
+
+ memBase := c.getMemoryBaseValue(false)
+ dstAddr := builder.AllocateInstruction().AsIadd(memBase, offsetInMemory).Insert(builder).Return()
+
+ c.callMemmove(dstAddr, srcAddr, copySize)
+
+ case wasm.OpcodeMiscTableInit:
+ elemIndex := c.readI32u()
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ copySize := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ offsetInElementInstance := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+ offsetInTable := builder.
+ AllocateInstruction().AsUExtend(state.pop(), 32, 64).Insert(builder).Return()
+
+ elemInstPtr := c.dataOrElementInstanceAddr(elemIndex, c.offset.ElementInstances1stElement)
+
+ // Bounds check.
+ tableInstancePtr := c.boundsCheckInTable(tableIndex, offsetInTable, copySize)
+ c.boundsCheckInDataOrElementInstance(elemInstPtr, offsetInElementInstance, copySize, wazevoapi.ExitCodeTableOutOfBounds)
+
+ three := builder.AllocateInstruction().AsIconst64(3).Insert(builder).Return()
+ // Calculates the destination address in the table.
+ tableOffsetInBytes := builder.AllocateInstruction().AsIshl(offsetInTable, three).Insert(builder).Return()
+ tableBaseAddr := c.loadTableBaseAddr(tableInstancePtr)
+ dstAddr := builder.AllocateInstruction().AsIadd(tableBaseAddr, tableOffsetInBytes).Insert(builder).Return()
+
+ // Calculates the source address in the element instance.
+ srcOffsetInBytes := builder.AllocateInstruction().AsIshl(offsetInElementInstance, three).Insert(builder).Return()
+ elemInstBaseAddr := builder.AllocateInstruction().AsLoad(elemInstPtr, 0, ssa.TypeI64).Insert(builder).Return()
+ srcAddr := builder.AllocateInstruction().AsIadd(elemInstBaseAddr, srcOffsetInBytes).Insert(builder).Return()
+
+ copySizeInBytes := builder.AllocateInstruction().AsIshl(copySize, three).Insert(builder).Return()
+ c.callMemmove(dstAddr, srcAddr, copySizeInBytes)
+
+ case wasm.OpcodeMiscElemDrop:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ c.dropDataOrElementInstance(index, c.offset.ElementInstances1stElement)
+
+ case wasm.OpcodeMiscDataDrop:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ c.dropDataOrElementInstance(index, c.offset.DataInstances1stElement)
+
+ default:
+ panic("Unknown MiscOp " + wasm.MiscInstructionName(miscOp))
+ }
+
+ case wasm.OpcodeI32ReinterpretF32:
+ if state.unreachable {
+ break
+ }
+ reinterpret := builder.AllocateInstruction().
+ AsBitcast(state.pop(), ssa.TypeI32).
+ Insert(builder).Return()
+ state.push(reinterpret)
+
+ case wasm.OpcodeI64ReinterpretF64:
+ if state.unreachable {
+ break
+ }
+ reinterpret := builder.AllocateInstruction().
+ AsBitcast(state.pop(), ssa.TypeI64).
+ Insert(builder).Return()
+ state.push(reinterpret)
+
+ case wasm.OpcodeF32ReinterpretI32:
+ if state.unreachable {
+ break
+ }
+ reinterpret := builder.AllocateInstruction().
+ AsBitcast(state.pop(), ssa.TypeF32).
+ Insert(builder).Return()
+ state.push(reinterpret)
+
+ case wasm.OpcodeF64ReinterpretI64:
+ if state.unreachable {
+ break
+ }
+ reinterpret := builder.AllocateInstruction().
+ AsBitcast(state.pop(), ssa.TypeF64).
+ Insert(builder).Return()
+ state.push(reinterpret)
+
+ case wasm.OpcodeI32DivS, wasm.OpcodeI64DivS:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ result := builder.AllocateInstruction().AsSDiv(x, y, c.execCtxPtrValue).Insert(builder).Return()
+ state.push(result)
+
+ case wasm.OpcodeI32DivU, wasm.OpcodeI64DivU:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ result := builder.AllocateInstruction().AsUDiv(x, y, c.execCtxPtrValue).Insert(builder).Return()
+ state.push(result)
+
+ case wasm.OpcodeI32RemS, wasm.OpcodeI64RemS:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ result := builder.AllocateInstruction().AsSRem(x, y, c.execCtxPtrValue).Insert(builder).Return()
+ state.push(result)
+
+ case wasm.OpcodeI32RemU, wasm.OpcodeI64RemU:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ result := builder.AllocateInstruction().AsURem(x, y, c.execCtxPtrValue).Insert(builder).Return()
+ state.push(result)
+
+ case wasm.OpcodeI32And, wasm.OpcodeI64And:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ and := builder.AllocateInstruction()
+ and.AsBand(x, y)
+ builder.InsertInstruction(and)
+ value := and.Return()
+ state.push(value)
+ case wasm.OpcodeI32Or, wasm.OpcodeI64Or:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ or := builder.AllocateInstruction()
+ or.AsBor(x, y)
+ builder.InsertInstruction(or)
+ value := or.Return()
+ state.push(value)
+ case wasm.OpcodeI32Xor, wasm.OpcodeI64Xor:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ xor := builder.AllocateInstruction()
+ xor.AsBxor(x, y)
+ builder.InsertInstruction(xor)
+ value := xor.Return()
+ state.push(value)
+ case wasm.OpcodeI32Shl, wasm.OpcodeI64Shl:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ ishl := builder.AllocateInstruction()
+ ishl.AsIshl(x, y)
+ builder.InsertInstruction(ishl)
+ value := ishl.Return()
+ state.push(value)
+ case wasm.OpcodeI32ShrU, wasm.OpcodeI64ShrU:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ ishl := builder.AllocateInstruction()
+ ishl.AsUshr(x, y)
+ builder.InsertInstruction(ishl)
+ value := ishl.Return()
+ state.push(value)
+ case wasm.OpcodeI32ShrS, wasm.OpcodeI64ShrS:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ ishl := builder.AllocateInstruction()
+ ishl.AsSshr(x, y)
+ builder.InsertInstruction(ishl)
+ value := ishl.Return()
+ state.push(value)
+ case wasm.OpcodeI32Rotl, wasm.OpcodeI64Rotl:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ rotl := builder.AllocateInstruction()
+ rotl.AsRotl(x, y)
+ builder.InsertInstruction(rotl)
+ value := rotl.Return()
+ state.push(value)
+ case wasm.OpcodeI32Rotr, wasm.OpcodeI64Rotr:
+ if state.unreachable {
+ break
+ }
+ y, x := state.pop(), state.pop()
+ rotr := builder.AllocateInstruction()
+ rotr.AsRotr(x, y)
+ builder.InsertInstruction(rotr)
+ value := rotr.Return()
+ state.push(value)
+ case wasm.OpcodeI32Clz, wasm.OpcodeI64Clz:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ clz := builder.AllocateInstruction()
+ clz.AsClz(x)
+ builder.InsertInstruction(clz)
+ value := clz.Return()
+ state.push(value)
+ case wasm.OpcodeI32Ctz, wasm.OpcodeI64Ctz:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ ctz := builder.AllocateInstruction()
+ ctz.AsCtz(x)
+ builder.InsertInstruction(ctz)
+ value := ctz.Return()
+ state.push(value)
+ case wasm.OpcodeI32Popcnt, wasm.OpcodeI64Popcnt:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ popcnt := builder.AllocateInstruction()
+ popcnt.AsPopcnt(x)
+ builder.InsertInstruction(popcnt)
+ value := popcnt.Return()
+ state.push(value)
+
+ case wasm.OpcodeI32WrapI64:
+ if state.unreachable {
+ break
+ }
+ x := state.pop()
+ wrap := builder.AllocateInstruction().AsIreduce(x, ssa.TypeI32).Insert(builder).Return()
+ state.push(wrap)
+ case wasm.OpcodeGlobalGet:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ v := c.getWasmGlobalValue(index, false)
+ state.push(v)
+ case wasm.OpcodeGlobalSet:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ v := state.pop()
+ c.setWasmGlobalValue(index, v)
+ case wasm.OpcodeLocalGet:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ variable := c.localVariable(index)
+ if _, ok := c.m.NonStaticLocals[c.wasmLocalFunctionIndex][index]; ok {
+ state.push(builder.MustFindValue(variable))
+ } else {
+ // If a local is static, we can simply find it in the entry block which is either a function param
+ // or a zero value. This fast pass helps to avoid the overhead of searching the entire function plus
+ // avoid adding unnecessary block arguments.
+ // TODO: I think this optimization should be done in a SSA pass like passRedundantPhiEliminationOpt,
+ // but somehow there's some corner cases that it fails to optimize.
+ state.push(builder.MustFindValueInBlk(variable, c.ssaBuilder.EntryBlock()))
+ }
+ case wasm.OpcodeLocalSet:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ variable := c.localVariable(index)
+ newValue := state.pop()
+ builder.DefineVariableInCurrentBB(variable, newValue)
+
+ case wasm.OpcodeLocalTee:
+ index := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ variable := c.localVariable(index)
+ newValue := state.peek()
+ builder.DefineVariableInCurrentBB(variable, newValue)
+
+ case wasm.OpcodeSelect, wasm.OpcodeTypedSelect:
+ if op == wasm.OpcodeTypedSelect {
+ state.pc += 2 // ignores the type which is only needed during validation.
+ }
+
+ if state.unreachable {
+ break
+ }
+
+ cond := state.pop()
+ v2 := state.pop()
+ v1 := state.pop()
+
+ sl := builder.AllocateInstruction().
+ AsSelect(cond, v1, v2).
+ Insert(builder).
+ Return()
+ state.push(sl)
+
+ case wasm.OpcodeMemorySize:
+ state.pc++ // skips the memory index.
+ if state.unreachable {
+ break
+ }
+
+ var memSizeInBytes ssa.Value
+ if c.offset.LocalMemoryBegin < 0 {
+ memInstPtr := builder.AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue, c.offset.ImportedMemoryBegin.U32(), ssa.TypeI64).
+ Insert(builder).
+ Return()
+
+ memSizeInBytes = builder.AllocateInstruction().
+ AsLoad(memInstPtr, memoryInstanceBufSizeOffset, ssa.TypeI32).
+ Insert(builder).
+ Return()
+ } else {
+ 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().
+ AsUshr(memSizeInBytes, amount.Return()).
+ Insert(builder).
+ Return()
+ state.push(memSize)
+
+ case wasm.OpcodeMemoryGrow:
+ state.pc++ // skips the memory index.
+ if state.unreachable {
+ break
+ }
+
+ c.storeCallerModuleContext()
+
+ pages := state.pop()
+ memoryGrowPtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetMemoryGrowTrampolineAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ args := c.allocateVarLengthValues(1, c.execCtxPtrValue, pages)
+ callGrowRet := builder.
+ AllocateInstruction().
+ AsCallIndirect(memoryGrowPtr, &c.memoryGrowSig, args).
+ Insert(builder).Return()
+ state.push(callGrowRet)
+
+ // After the memory grow, reload the cached memory base and len.
+ c.reloadMemoryBaseLen()
+
+ case wasm.OpcodeI32Store,
+ wasm.OpcodeI64Store,
+ wasm.OpcodeF32Store,
+ wasm.OpcodeF64Store,
+ wasm.OpcodeI32Store8,
+ wasm.OpcodeI32Store16,
+ wasm.OpcodeI64Store8,
+ wasm.OpcodeI64Store16,
+ wasm.OpcodeI64Store32:
+
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+ var opSize uint64
+ var opcode ssa.Opcode
+ switch op {
+ case wasm.OpcodeI32Store, wasm.OpcodeF32Store:
+ opcode = ssa.OpcodeStore
+ opSize = 4
+ case wasm.OpcodeI64Store, wasm.OpcodeF64Store:
+ opcode = ssa.OpcodeStore
+ opSize = 8
+ case wasm.OpcodeI32Store8, wasm.OpcodeI64Store8:
+ opcode = ssa.OpcodeIstore8
+ opSize = 1
+ case wasm.OpcodeI32Store16, wasm.OpcodeI64Store16:
+ opcode = ssa.OpcodeIstore16
+ opSize = 2
+ case wasm.OpcodeI64Store32:
+ opcode = ssa.OpcodeIstore32
+ opSize = 4
+ default:
+ panic("BUG")
+ }
+
+ value := state.pop()
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
+ builder.AllocateInstruction().
+ AsStore(opcode, value, addr, offset).
+ Insert(builder)
+
+ case wasm.OpcodeI32Load,
+ wasm.OpcodeI64Load,
+ wasm.OpcodeF32Load,
+ wasm.OpcodeF64Load,
+ wasm.OpcodeI32Load8S,
+ wasm.OpcodeI32Load8U,
+ wasm.OpcodeI32Load16S,
+ wasm.OpcodeI32Load16U,
+ wasm.OpcodeI64Load8S,
+ wasm.OpcodeI64Load8U,
+ wasm.OpcodeI64Load16S,
+ wasm.OpcodeI64Load16U,
+ wasm.OpcodeI64Load32S,
+ wasm.OpcodeI64Load32U:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ var opSize uint64
+ switch op {
+ case wasm.OpcodeI32Load, wasm.OpcodeF32Load:
+ opSize = 4
+ case wasm.OpcodeI64Load, wasm.OpcodeF64Load:
+ opSize = 8
+ case wasm.OpcodeI32Load8S, wasm.OpcodeI32Load8U:
+ opSize = 1
+ case wasm.OpcodeI32Load16S, wasm.OpcodeI32Load16U:
+ opSize = 2
+ case wasm.OpcodeI64Load8S, wasm.OpcodeI64Load8U:
+ opSize = 1
+ case wasm.OpcodeI64Load16S, wasm.OpcodeI64Load16U:
+ opSize = 2
+ case wasm.OpcodeI64Load32S, wasm.OpcodeI64Load32U:
+ opSize = 4
+ default:
+ panic("BUG")
+ }
+
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
+ load := builder.AllocateInstruction()
+ switch op {
+ case wasm.OpcodeI32Load:
+ load.AsLoad(addr, offset, ssa.TypeI32)
+ case wasm.OpcodeI64Load:
+ load.AsLoad(addr, offset, ssa.TypeI64)
+ case wasm.OpcodeF32Load:
+ load.AsLoad(addr, offset, ssa.TypeF32)
+ case wasm.OpcodeF64Load:
+ load.AsLoad(addr, offset, ssa.TypeF64)
+ case wasm.OpcodeI32Load8S:
+ load.AsExtLoad(ssa.OpcodeSload8, addr, offset, false)
+ case wasm.OpcodeI32Load8U:
+ load.AsExtLoad(ssa.OpcodeUload8, addr, offset, false)
+ case wasm.OpcodeI32Load16S:
+ load.AsExtLoad(ssa.OpcodeSload16, addr, offset, false)
+ case wasm.OpcodeI32Load16U:
+ load.AsExtLoad(ssa.OpcodeUload16, addr, offset, false)
+ case wasm.OpcodeI64Load8S:
+ load.AsExtLoad(ssa.OpcodeSload8, addr, offset, true)
+ case wasm.OpcodeI64Load8U:
+ load.AsExtLoad(ssa.OpcodeUload8, addr, offset, true)
+ case wasm.OpcodeI64Load16S:
+ load.AsExtLoad(ssa.OpcodeSload16, addr, offset, true)
+ case wasm.OpcodeI64Load16U:
+ load.AsExtLoad(ssa.OpcodeUload16, addr, offset, true)
+ case wasm.OpcodeI64Load32S:
+ load.AsExtLoad(ssa.OpcodeSload32, addr, offset, true)
+ case wasm.OpcodeI64Load32U:
+ load.AsExtLoad(ssa.OpcodeUload32, addr, offset, true)
+ default:
+ panic("BUG")
+ }
+ builder.InsertInstruction(load)
+ state.push(load.Return())
+ case wasm.OpcodeBlock:
+ // Note: we do not need to create a BB for this as that would always have only one predecessor
+ // which is the current BB, and therefore it's always ok to merge them in any way.
+
+ bt := c.readBlockType()
+
+ if state.unreachable {
+ state.unreachableDepth++
+ break
+ }
+
+ followingBlk := builder.AllocateBasicBlock()
+ c.addBlockParamsFromWasmTypes(bt.Results, followingBlk)
+
+ state.ctrlPush(controlFrame{
+ kind: controlFrameKindBlock,
+ originalStackLenWithoutParam: len(state.values) - len(bt.Params),
+ followingBlock: followingBlk,
+ blockType: bt,
+ })
+ case wasm.OpcodeLoop:
+ bt := c.readBlockType()
+
+ if state.unreachable {
+ state.unreachableDepth++
+ break
+ }
+
+ loopHeader, afterLoopBlock := builder.AllocateBasicBlock(), builder.AllocateBasicBlock()
+ c.addBlockParamsFromWasmTypes(bt.Params, loopHeader)
+ c.addBlockParamsFromWasmTypes(bt.Results, afterLoopBlock)
+
+ originalLen := len(state.values) - len(bt.Params)
+ state.ctrlPush(controlFrame{
+ originalStackLenWithoutParam: originalLen,
+ kind: controlFrameKindLoop,
+ blk: loopHeader,
+ followingBlock: afterLoopBlock,
+ blockType: bt,
+ })
+
+ args := c.allocateVarLengthValues(originalLen)
+ args = args.Append(builder.VarLengthPool(), state.values[originalLen:]...)
+
+ // Insert the jump to the header of loop.
+ br := builder.AllocateInstruction()
+ br.AsJump(args, loopHeader)
+ builder.InsertInstruction(br)
+
+ c.switchTo(originalLen, loopHeader)
+
+ if c.ensureTermination {
+ checkModuleExitCodePtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetCheckModuleExitCodeTrampolineAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ args := c.allocateVarLengthValues(1, c.execCtxPtrValue)
+ builder.AllocateInstruction().
+ AsCallIndirect(checkModuleExitCodePtr, &c.checkModuleExitCodeSig, args).
+ Insert(builder)
+ }
+ case wasm.OpcodeIf:
+ bt := c.readBlockType()
+
+ if state.unreachable {
+ state.unreachableDepth++
+ break
+ }
+
+ v := state.pop()
+ thenBlk, elseBlk, followingBlk := builder.AllocateBasicBlock(), builder.AllocateBasicBlock(), builder.AllocateBasicBlock()
+
+ // We do not make the Wasm-level block parameters as SSA-level block params for if-else blocks
+ // since they won't be PHI and the definition is unique.
+
+ // On the other hand, the following block after if-else-end will likely have
+ // multiple definitions (one in Then and another in Else blocks).
+ c.addBlockParamsFromWasmTypes(bt.Results, followingBlk)
+
+ args := c.allocateVarLengthValues(len(bt.Params))
+ args = args.Append(builder.VarLengthPool(), state.values[len(state.values)-len(bt.Params):]...)
+
+ // Insert the conditional jump to the Else block.
+ brz := builder.AllocateInstruction()
+ brz.AsBrz(v, ssa.ValuesNil, elseBlk)
+ builder.InsertInstruction(brz)
+
+ // Then, insert the jump to the Then block.
+ br := builder.AllocateInstruction()
+ br.AsJump(ssa.ValuesNil, thenBlk)
+ builder.InsertInstruction(br)
+
+ state.ctrlPush(controlFrame{
+ kind: controlFrameKindIfWithoutElse,
+ originalStackLenWithoutParam: len(state.values) - len(bt.Params),
+ blk: elseBlk,
+ followingBlock: followingBlk,
+ blockType: bt,
+ clonedArgs: args,
+ })
+
+ builder.SetCurrentBlock(thenBlk)
+
+ // Then and Else (if exists) have only one predecessor.
+ builder.Seal(thenBlk)
+ builder.Seal(elseBlk)
+ case wasm.OpcodeElse:
+ ifctrl := state.ctrlPeekAt(0)
+ if unreachable := state.unreachable; unreachable && state.unreachableDepth > 0 {
+ // If it is currently in unreachable and is a nested if,
+ // we just remove the entire else block.
+ break
+ }
+
+ ifctrl.kind = controlFrameKindIfWithElse
+ if !state.unreachable {
+ // If this Then block is currently reachable, we have to insert the branching to the following BB.
+ followingBlk := ifctrl.followingBlock // == the BB after if-then-else.
+ args := c.nPeekDup(len(ifctrl.blockType.Results))
+ c.insertJumpToBlock(args, followingBlk)
+ } else {
+ state.unreachable = false
+ }
+
+ // Reset the stack so that we can correctly handle the else block.
+ state.values = state.values[:ifctrl.originalStackLenWithoutParam]
+ elseBlk := ifctrl.blk
+ for _, arg := range ifctrl.clonedArgs.View() {
+ state.push(arg)
+ }
+
+ builder.SetCurrentBlock(elseBlk)
+
+ case wasm.OpcodeEnd:
+ if state.unreachableDepth > 0 {
+ state.unreachableDepth--
+ break
+ }
+
+ ctrl := state.ctrlPop()
+ followingBlk := ctrl.followingBlock
+
+ unreachable := state.unreachable
+ if !unreachable {
+ // Top n-th args will be used as a result of the current control frame.
+ args := c.nPeekDup(len(ctrl.blockType.Results))
+
+ // Insert the unconditional branch to the target.
+ c.insertJumpToBlock(args, followingBlk)
+ } else { // recover from the unreachable state.
+ state.unreachable = false
+ }
+
+ switch ctrl.kind {
+ case controlFrameKindFunction:
+ break // This is the very end of function.
+ case controlFrameKindLoop:
+ // Loop header block can be reached from any br/br_table contained in the loop,
+ // so now that we've reached End of it, we can seal it.
+ builder.Seal(ctrl.blk)
+ case controlFrameKindIfWithoutElse:
+ // If this is the end of Then block, we have to emit the empty Else block.
+ elseBlk := ctrl.blk
+ builder.SetCurrentBlock(elseBlk)
+ c.insertJumpToBlock(ctrl.clonedArgs, followingBlk)
+ }
+
+ builder.Seal(followingBlk)
+
+ // Ready to start translating the following block.
+ c.switchTo(ctrl.originalStackLenWithoutParam, followingBlk)
+
+ case wasm.OpcodeBr:
+ labelIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ targetBlk, argNum := state.brTargetArgNumFor(labelIndex)
+ args := c.nPeekDup(argNum)
+ c.insertJumpToBlock(args, targetBlk)
+
+ state.unreachable = true
+
+ case wasm.OpcodeBrIf:
+ labelIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ v := state.pop()
+
+ targetBlk, argNum := state.brTargetArgNumFor(labelIndex)
+ args := c.nPeekDup(argNum)
+ var sealTargetBlk bool
+ if c.needListener && targetBlk.ReturnBlock() { // In this case, we have to call the listener before returning.
+ // Save the currently active block.
+ current := builder.CurrentBlock()
+
+ // Allocate the trampoline block to the return where we call the listener.
+ targetBlk = builder.AllocateBasicBlock()
+ builder.SetCurrentBlock(targetBlk)
+ sealTargetBlk = true
+
+ c.callListenerAfter()
+
+ instr := builder.AllocateInstruction()
+ instr.AsReturn(args)
+ builder.InsertInstruction(instr)
+
+ args = ssa.ValuesNil
+
+ // Revert the current block.
+ builder.SetCurrentBlock(current)
+ }
+
+ // Insert the conditional jump to the target block.
+ brnz := builder.AllocateInstruction()
+ brnz.AsBrnz(v, args, targetBlk)
+ builder.InsertInstruction(brnz)
+
+ if sealTargetBlk {
+ builder.Seal(targetBlk)
+ }
+
+ // Insert the unconditional jump to the Else block which corresponds to after br_if.
+ elseBlk := builder.AllocateBasicBlock()
+ c.insertJumpToBlock(ssa.ValuesNil, elseBlk)
+
+ // Now start translating the instructions after br_if.
+ builder.Seal(elseBlk) // Else of br_if has the current block as the only one successor.
+ builder.SetCurrentBlock(elseBlk)
+
+ case wasm.OpcodeBrTable:
+ labels := state.tmpForBrTable
+ labels = labels[:0]
+ labelCount := c.readI32u()
+ for i := 0; i < int(labelCount); i++ {
+ labels = append(labels, c.readI32u())
+ }
+ labels = append(labels, c.readI32u()) // default label.
+ if state.unreachable {
+ break
+ }
+
+ index := state.pop()
+ if labelCount == 0 { // If this br_table is empty, we can just emit the unconditional jump.
+ targetBlk, argNum := state.brTargetArgNumFor(labels[0])
+ args := c.nPeekDup(argNum)
+ c.insertJumpToBlock(args, targetBlk)
+ } else {
+ c.lowerBrTable(labels, index)
+ }
+ state.unreachable = true
+
+ case wasm.OpcodeNop:
+ case wasm.OpcodeReturn:
+ if state.unreachable {
+ break
+ }
+ if c.needListener {
+ c.callListenerAfter()
+ }
+
+ results := c.nPeekDup(c.results())
+ instr := builder.AllocateInstruction()
+
+ instr.AsReturn(results)
+ builder.InsertInstruction(instr)
+ state.unreachable = true
+
+ case wasm.OpcodeUnreachable:
+ if state.unreachable {
+ break
+ }
+ exit := builder.AllocateInstruction()
+ exit.AsExitWithCode(c.execCtxPtrValue, wazevoapi.ExitCodeUnreachable)
+ builder.InsertInstruction(exit)
+ state.unreachable = true
+
+ case wasm.OpcodeCallIndirect:
+ typeIndex := c.readI32u()
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ c.lowerCallIndirect(typeIndex, tableIndex)
+
+ case wasm.OpcodeCall:
+ fnIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ var typIndex wasm.Index
+ if fnIndex < c.m.ImportFunctionCount {
+ // Before transfer the control to the callee, we have to store the current module's moduleContextPtr
+ // into execContext.callerModuleContextPtr in case when the callee is a Go function.
+ c.storeCallerModuleContext()
+ var fi int
+ for i := range c.m.ImportSection {
+ imp := &c.m.ImportSection[i]
+ if imp.Type == wasm.ExternTypeFunc {
+ if fi == int(fnIndex) {
+ typIndex = imp.DescFunc
+ break
+ }
+ fi++
+ }
+ }
+ } else {
+ typIndex = c.m.FunctionSection[fnIndex-c.m.ImportFunctionCount]
+ }
+ typ := &c.m.TypeSection[typIndex]
+
+ argN := len(typ.Params)
+ tail := len(state.values) - argN
+ vs := state.values[tail:]
+ state.values = state.values[:tail]
+ args := c.allocateVarLengthValues(2+len(vs), c.execCtxPtrValue)
+
+ sig := c.signatures[typ]
+ call := builder.AllocateInstruction()
+ if fnIndex >= c.m.ImportFunctionCount {
+ args = args.Append(builder.VarLengthPool(), c.moduleCtxPtrValue) // This case the callee module is itself.
+ args = args.Append(builder.VarLengthPool(), vs...)
+ call.AsCall(FunctionIndexToFuncRef(fnIndex), sig, args)
+ builder.InsertInstruction(call)
+ } else {
+ // This case we have to read the address of the imported function from the module context.
+ moduleCtx := c.moduleCtxPtrValue
+ loadFuncPtr, loadModuleCtxPtr := builder.AllocateInstruction(), builder.AllocateInstruction()
+ funcPtrOffset, moduleCtxPtrOffset, _ := c.offset.ImportedFunctionOffset(fnIndex)
+ loadFuncPtr.AsLoad(moduleCtx, funcPtrOffset.U32(), ssa.TypeI64)
+ loadModuleCtxPtr.AsLoad(moduleCtx, moduleCtxPtrOffset.U32(), ssa.TypeI64)
+ builder.InsertInstruction(loadFuncPtr)
+ builder.InsertInstruction(loadModuleCtxPtr)
+
+ args = args.Append(builder.VarLengthPool(), loadModuleCtxPtr.Return())
+ args = args.Append(builder.VarLengthPool(), vs...)
+ call.AsCallIndirect(loadFuncPtr.Return(), sig, args)
+ builder.InsertInstruction(call)
+ }
+
+ first, rest := call.Returns()
+ if first.Valid() {
+ state.push(first)
+ }
+ for _, v := range rest {
+ state.push(v)
+ }
+
+ c.reloadAfterCall()
+
+ case wasm.OpcodeDrop:
+ if state.unreachable {
+ break
+ }
+ _ = state.pop()
+ case wasm.OpcodeF64ConvertI32S, wasm.OpcodeF64ConvertI64S, wasm.OpcodeF64ConvertI32U, wasm.OpcodeF64ConvertI64U:
+ if state.unreachable {
+ break
+ }
+ result := builder.AllocateInstruction().AsFcvtFromInt(
+ state.pop(),
+ op == wasm.OpcodeF64ConvertI32S || op == wasm.OpcodeF64ConvertI64S,
+ true,
+ ).Insert(builder).Return()
+ state.push(result)
+ case wasm.OpcodeF32ConvertI32S, wasm.OpcodeF32ConvertI64S, wasm.OpcodeF32ConvertI32U, wasm.OpcodeF32ConvertI64U:
+ if state.unreachable {
+ break
+ }
+ result := builder.AllocateInstruction().AsFcvtFromInt(
+ state.pop(),
+ op == wasm.OpcodeF32ConvertI32S || op == wasm.OpcodeF32ConvertI64S,
+ false,
+ ).Insert(builder).Return()
+ state.push(result)
+ case wasm.OpcodeF32DemoteF64:
+ if state.unreachable {
+ break
+ }
+ cvt := builder.AllocateInstruction()
+ cvt.AsFdemote(state.pop())
+ builder.InsertInstruction(cvt)
+ state.push(cvt.Return())
+ case wasm.OpcodeF64PromoteF32:
+ if state.unreachable {
+ break
+ }
+ cvt := builder.AllocateInstruction()
+ cvt.AsFpromote(state.pop())
+ builder.InsertInstruction(cvt)
+ state.push(cvt.Return())
+
+ case wasm.OpcodeVecPrefix:
+ state.pc++
+ vecOp := c.wasmFunctionBody[state.pc]
+ switch vecOp {
+ case wasm.OpcodeVecV128Const:
+ state.pc++
+ lo := binary.LittleEndian.Uint64(c.wasmFunctionBody[state.pc:])
+ state.pc += 8
+ hi := binary.LittleEndian.Uint64(c.wasmFunctionBody[state.pc:])
+ state.pc += 7
+ if state.unreachable {
+ break
+ }
+ ret := builder.AllocateInstruction().AsVconst(lo, hi).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Load:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), 16)
+ load := builder.AllocateInstruction()
+ load.AsLoad(addr, offset, ssa.TypeV128)
+ builder.InsertInstruction(load)
+ state.push(load.Return())
+ case wasm.OpcodeVecV128Load8Lane, wasm.OpcodeVecV128Load16Lane, wasm.OpcodeVecV128Load32Lane:
+ _, offset := c.readMemArg()
+ state.pc++
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ var loadOp ssa.Opcode
+ var opSize uint64
+ switch vecOp {
+ case wasm.OpcodeVecV128Load8Lane:
+ loadOp, lane, opSize = ssa.OpcodeUload8, ssa.VecLaneI8x16, 1
+ case wasm.OpcodeVecV128Load16Lane:
+ loadOp, lane, opSize = ssa.OpcodeUload16, ssa.VecLaneI16x8, 2
+ case wasm.OpcodeVecV128Load32Lane:
+ loadOp, lane, opSize = ssa.OpcodeUload32, ssa.VecLaneI32x4, 4
+ }
+ laneIndex := c.wasmFunctionBody[state.pc]
+ vector := state.pop()
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
+ load := builder.AllocateInstruction().
+ AsExtLoad(loadOp, addr, offset, false).
+ Insert(builder).Return()
+ ret := builder.AllocateInstruction().
+ AsInsertlane(vector, load, laneIndex, lane).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Load64Lane:
+ _, offset := c.readMemArg()
+ state.pc++
+ if state.unreachable {
+ break
+ }
+ laneIndex := c.wasmFunctionBody[state.pc]
+ vector := state.pop()
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), 8)
+ load := builder.AllocateInstruction().
+ AsLoad(addr, offset, ssa.TypeI64).
+ Insert(builder).Return()
+ ret := builder.AllocateInstruction().
+ AsInsertlane(vector, load, laneIndex, ssa.VecLaneI64x2).
+ Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecV128Load32zero, wasm.OpcodeVecV128Load64zero:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ var scalarType ssa.Type
+ switch vecOp {
+ case wasm.OpcodeVecV128Load32zero:
+ scalarType = ssa.TypeF32
+ case wasm.OpcodeVecV128Load64zero:
+ scalarType = ssa.TypeF64
+ }
+
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), uint64(scalarType.Size()))
+
+ ret := builder.AllocateInstruction().
+ AsVZeroExtLoad(addr, offset, scalarType).
+ Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecV128Load8x8u, wasm.OpcodeVecV128Load8x8s,
+ wasm.OpcodeVecV128Load16x4u, wasm.OpcodeVecV128Load16x4s,
+ wasm.OpcodeVecV128Load32x2u, wasm.OpcodeVecV128Load32x2s:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ var signed bool
+ switch vecOp {
+ case wasm.OpcodeVecV128Load8x8s:
+ signed = true
+ fallthrough
+ case wasm.OpcodeVecV128Load8x8u:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecV128Load16x4s:
+ signed = true
+ fallthrough
+ case wasm.OpcodeVecV128Load16x4u:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecV128Load32x2s:
+ signed = true
+ fallthrough
+ case wasm.OpcodeVecV128Load32x2u:
+ lane = ssa.VecLaneI32x4
+ }
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), 8)
+ load := builder.AllocateInstruction().
+ AsLoad(addr, offset, ssa.TypeF64).
+ Insert(builder).Return()
+ ret := builder.AllocateInstruction().
+ AsWiden(load, lane, signed, true).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Load8Splat, wasm.OpcodeVecV128Load16Splat,
+ wasm.OpcodeVecV128Load32Splat, wasm.OpcodeVecV128Load64Splat:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ var opSize uint64
+ switch vecOp {
+ case wasm.OpcodeVecV128Load8Splat:
+ lane, opSize = ssa.VecLaneI8x16, 1
+ case wasm.OpcodeVecV128Load16Splat:
+ lane, opSize = ssa.VecLaneI16x8, 2
+ case wasm.OpcodeVecV128Load32Splat:
+ lane, opSize = ssa.VecLaneI32x4, 4
+ case wasm.OpcodeVecV128Load64Splat:
+ lane, opSize = ssa.VecLaneI64x2, 8
+ }
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
+ ret := builder.AllocateInstruction().
+ AsLoadSplat(addr, offset, lane).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Store:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+ value := state.pop()
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), 16)
+ builder.AllocateInstruction().
+ AsStore(ssa.OpcodeStore, value, addr, offset).
+ Insert(builder)
+ case wasm.OpcodeVecV128Store8Lane, wasm.OpcodeVecV128Store16Lane,
+ wasm.OpcodeVecV128Store32Lane, wasm.OpcodeVecV128Store64Lane:
+ _, offset := c.readMemArg()
+ state.pc++
+ if state.unreachable {
+ break
+ }
+ laneIndex := c.wasmFunctionBody[state.pc]
+ var storeOp ssa.Opcode
+ var lane ssa.VecLane
+ var opSize uint64
+ switch vecOp {
+ case wasm.OpcodeVecV128Store8Lane:
+ storeOp, lane, opSize = ssa.OpcodeIstore8, ssa.VecLaneI8x16, 1
+ case wasm.OpcodeVecV128Store16Lane:
+ storeOp, lane, opSize = ssa.OpcodeIstore16, ssa.VecLaneI16x8, 2
+ case wasm.OpcodeVecV128Store32Lane:
+ storeOp, lane, opSize = ssa.OpcodeIstore32, ssa.VecLaneI32x4, 4
+ case wasm.OpcodeVecV128Store64Lane:
+ storeOp, lane, opSize = ssa.OpcodeStore, ssa.VecLaneI64x2, 8
+ }
+ vector := state.pop()
+ baseAddr := state.pop()
+ addr := c.memOpSetup(baseAddr, uint64(offset), opSize)
+ value := builder.AllocateInstruction().
+ AsExtractlane(vector, laneIndex, lane, false).
+ Insert(builder).Return()
+ builder.AllocateInstruction().
+ AsStore(storeOp, value, addr, offset).
+ Insert(builder)
+ case wasm.OpcodeVecV128Not:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVbnot(v1).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128And:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVband(v1, v2).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128AndNot:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVbandnot(v1, v2).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Or:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVbor(v1, v2).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Xor:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVbxor(v1, v2).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128Bitselect:
+ if state.unreachable {
+ break
+ }
+ c := state.pop()
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVbitselect(c, v1, v2).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128AnyTrue:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVanyTrue(v1).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16AllTrue, wasm.OpcodeVecI16x8AllTrue, wasm.OpcodeVecI32x4AllTrue, wasm.OpcodeVecI64x2AllTrue:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16AllTrue:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8AllTrue:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4AllTrue:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2AllTrue:
+ lane = ssa.VecLaneI64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVallTrue(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16BitMask, wasm.OpcodeVecI16x8BitMask, wasm.OpcodeVecI32x4BitMask, wasm.OpcodeVecI64x2BitMask:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16BitMask:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8BitMask:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4BitMask:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2BitMask:
+ lane = ssa.VecLaneI64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVhighBits(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Abs, wasm.OpcodeVecI16x8Abs, wasm.OpcodeVecI32x4Abs, wasm.OpcodeVecI64x2Abs:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Abs:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Abs:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Abs:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Abs:
+ lane = ssa.VecLaneI64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVIabs(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Neg, wasm.OpcodeVecI16x8Neg, wasm.OpcodeVecI32x4Neg, wasm.OpcodeVecI64x2Neg:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Neg:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Neg:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Neg:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Neg:
+ lane = ssa.VecLaneI64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVIneg(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Popcnt:
+ if state.unreachable {
+ break
+ }
+ lane := ssa.VecLaneI8x16
+ v1 := state.pop()
+
+ ret := builder.AllocateInstruction().AsVIpopcnt(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Add, wasm.OpcodeVecI16x8Add, wasm.OpcodeVecI32x4Add, wasm.OpcodeVecI64x2Add:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Add:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Add:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Add:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Add:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVIadd(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16AddSatS, wasm.OpcodeVecI16x8AddSatS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16AddSatS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8AddSatS:
+ lane = ssa.VecLaneI16x8
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVSaddSat(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16AddSatU, wasm.OpcodeVecI16x8AddSatU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16AddSatU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8AddSatU:
+ lane = ssa.VecLaneI16x8
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVUaddSat(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16SubSatS, wasm.OpcodeVecI16x8SubSatS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16SubSatS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8SubSatS:
+ lane = ssa.VecLaneI16x8
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVSsubSat(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16SubSatU, wasm.OpcodeVecI16x8SubSatU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16SubSatU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8SubSatU:
+ lane = ssa.VecLaneI16x8
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVUsubSat(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI8x16Sub, wasm.OpcodeVecI16x8Sub, wasm.OpcodeVecI32x4Sub, wasm.OpcodeVecI64x2Sub:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Sub:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Sub:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Sub:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Sub:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVIsub(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16MinS, wasm.OpcodeVecI16x8MinS, wasm.OpcodeVecI32x4MinS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16MinS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8MinS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4MinS:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVImin(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16MinU, wasm.OpcodeVecI16x8MinU, wasm.OpcodeVecI32x4MinU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16MinU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8MinU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4MinU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVUmin(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16MaxS, wasm.OpcodeVecI16x8MaxS, wasm.OpcodeVecI32x4MaxS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16MaxS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8MaxS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4MaxS:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVImax(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16MaxU, wasm.OpcodeVecI16x8MaxU, wasm.OpcodeVecI32x4MaxU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16MaxU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8MaxU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4MaxU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVUmax(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16AvgrU, wasm.OpcodeVecI16x8AvgrU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16AvgrU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8AvgrU:
+ lane = ssa.VecLaneI16x8
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVAvgRound(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI16x8Mul, wasm.OpcodeVecI32x4Mul, wasm.OpcodeVecI64x2Mul:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI16x8Mul:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Mul:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Mul:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVImul(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI16x8Q15mulrSatS:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsSqmulRoundSat(v1, v2, ssa.VecLaneI16x8).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Eq, wasm.OpcodeVecI16x8Eq, wasm.OpcodeVecI32x4Eq, wasm.OpcodeVecI64x2Eq:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Eq:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Eq:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Eq:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Eq:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Ne, wasm.OpcodeVecI16x8Ne, wasm.OpcodeVecI32x4Ne, wasm.OpcodeVecI64x2Ne:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Ne:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Ne:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Ne:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Ne:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondNotEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16LtS, wasm.OpcodeVecI16x8LtS, wasm.OpcodeVecI32x4LtS, wasm.OpcodeVecI64x2LtS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16LtS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8LtS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4LtS:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2LtS:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondSignedLessThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16LtU, wasm.OpcodeVecI16x8LtU, wasm.OpcodeVecI32x4LtU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16LtU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8LtU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4LtU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondUnsignedLessThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16LeS, wasm.OpcodeVecI16x8LeS, wasm.OpcodeVecI32x4LeS, wasm.OpcodeVecI64x2LeS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16LeS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8LeS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4LeS:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2LeS:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondSignedLessThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16LeU, wasm.OpcodeVecI16x8LeU, wasm.OpcodeVecI32x4LeU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16LeU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8LeU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4LeU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondUnsignedLessThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16GtS, wasm.OpcodeVecI16x8GtS, wasm.OpcodeVecI32x4GtS, wasm.OpcodeVecI64x2GtS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16GtS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8GtS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4GtS:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2GtS:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondSignedGreaterThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16GtU, wasm.OpcodeVecI16x8GtU, wasm.OpcodeVecI32x4GtU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16GtU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8GtU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4GtU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondUnsignedGreaterThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16GeS, wasm.OpcodeVecI16x8GeS, wasm.OpcodeVecI32x4GeS, wasm.OpcodeVecI64x2GeS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16GeS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8GeS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4GeS:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2GeS:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondSignedGreaterThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16GeU, wasm.OpcodeVecI16x8GeU, wasm.OpcodeVecI32x4GeU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16GeU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8GeU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4GeU:
+ lane = ssa.VecLaneI32x4
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVIcmp(v1, v2, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Max, wasm.OpcodeVecF64x2Max:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Max:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Max:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFmax(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Abs, wasm.OpcodeVecF64x2Abs:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Abs:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Abs:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFabs(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Min, wasm.OpcodeVecF64x2Min:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Min:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Min:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFmin(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Neg, wasm.OpcodeVecF64x2Neg:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Neg:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Neg:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFneg(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Sqrt, wasm.OpcodeVecF64x2Sqrt:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Sqrt:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Sqrt:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVSqrt(v1, lane).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecF32x4Add, wasm.OpcodeVecF64x2Add:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Add:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Add:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFadd(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Sub, wasm.OpcodeVecF64x2Sub:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Sub:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Sub:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFsub(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Mul, wasm.OpcodeVecF64x2Mul:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Mul:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Mul:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFmul(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Div, wasm.OpcodeVecF64x2Div:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Div:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Div:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFdiv(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI16x8ExtaddPairwiseI8x16S, wasm.OpcodeVecI16x8ExtaddPairwiseI8x16U:
+ if state.unreachable {
+ break
+ }
+ v := state.pop()
+ signed := vecOp == wasm.OpcodeVecI16x8ExtaddPairwiseI8x16S
+ ret := builder.AllocateInstruction().AsExtIaddPairwise(v, ssa.VecLaneI8x16, signed).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI32x4ExtaddPairwiseI16x8S, wasm.OpcodeVecI32x4ExtaddPairwiseI16x8U:
+ if state.unreachable {
+ break
+ }
+ v := state.pop()
+ signed := vecOp == wasm.OpcodeVecI32x4ExtaddPairwiseI16x8S
+ ret := builder.AllocateInstruction().AsExtIaddPairwise(v, ssa.VecLaneI16x8, signed).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI16x8ExtMulLowI8x16S, wasm.OpcodeVecI16x8ExtMulLowI8x16U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI8x16, ssa.VecLaneI16x8,
+ vecOp == wasm.OpcodeVecI16x8ExtMulLowI8x16S, true)
+ state.push(ret)
+
+ case wasm.OpcodeVecI16x8ExtMulHighI8x16S, wasm.OpcodeVecI16x8ExtMulHighI8x16U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI8x16, ssa.VecLaneI16x8,
+ vecOp == wasm.OpcodeVecI16x8ExtMulHighI8x16S, false)
+ state.push(ret)
+
+ case wasm.OpcodeVecI32x4ExtMulLowI16x8S, wasm.OpcodeVecI32x4ExtMulLowI16x8U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI16x8, ssa.VecLaneI32x4,
+ vecOp == wasm.OpcodeVecI32x4ExtMulLowI16x8S, true)
+ state.push(ret)
+
+ case wasm.OpcodeVecI32x4ExtMulHighI16x8S, wasm.OpcodeVecI32x4ExtMulHighI16x8U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI16x8, ssa.VecLaneI32x4,
+ vecOp == wasm.OpcodeVecI32x4ExtMulHighI16x8S, false)
+ state.push(ret)
+ case wasm.OpcodeVecI64x2ExtMulLowI32x4S, wasm.OpcodeVecI64x2ExtMulLowI32x4U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI32x4, ssa.VecLaneI64x2,
+ vecOp == wasm.OpcodeVecI64x2ExtMulLowI32x4S, true)
+ state.push(ret)
+
+ case wasm.OpcodeVecI64x2ExtMulHighI32x4S, wasm.OpcodeVecI64x2ExtMulHighI32x4U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := c.lowerExtMul(
+ v1, v2,
+ ssa.VecLaneI32x4, ssa.VecLaneI64x2,
+ vecOp == wasm.OpcodeVecI64x2ExtMulHighI32x4S, false)
+ state.push(ret)
+
+ case wasm.OpcodeVecI32x4DotI16x8S:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+
+ ret := builder.AllocateInstruction().AsWideningPairwiseDotProductS(v1, v2).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecF32x4Eq, wasm.OpcodeVecF64x2Eq:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Eq:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Eq:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Ne, wasm.OpcodeVecF64x2Ne:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Ne:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Ne:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondNotEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Lt, wasm.OpcodeVecF64x2Lt:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Lt:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Lt:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondLessThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Le, wasm.OpcodeVecF64x2Le:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Le:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Le:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondLessThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Gt, wasm.OpcodeVecF64x2Gt:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Gt:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Gt:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondGreaterThan, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Ge, wasm.OpcodeVecF64x2Ge:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Ge:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Ge:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcmp(v1, v2, ssa.FloatCmpCondGreaterThanOrEqual, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Ceil, wasm.OpcodeVecF64x2Ceil:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Ceil:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Ceil:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVCeil(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Floor, wasm.OpcodeVecF64x2Floor:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Floor:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Floor:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVFloor(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Trunc, wasm.OpcodeVecF64x2Trunc:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Trunc:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Trunc:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVTrunc(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Nearest, wasm.OpcodeVecF64x2Nearest:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Nearest:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Nearest:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVNearest(v1, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Pmin, wasm.OpcodeVecF64x2Pmin:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Pmin:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Pmin:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVMinPseudo(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4Pmax, wasm.OpcodeVecF64x2Pmax:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecF32x4Pmax:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Pmax:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVMaxPseudo(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI32x4TruncSatF32x4S, wasm.OpcodeVecI32x4TruncSatF32x4U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcvtToIntSat(v1, ssa.VecLaneF32x4, vecOp == wasm.OpcodeVecI32x4TruncSatF32x4S).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI32x4TruncSatF64x2SZero, wasm.OpcodeVecI32x4TruncSatF64x2UZero:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcvtToIntSat(v1, ssa.VecLaneF64x2, vecOp == wasm.OpcodeVecI32x4TruncSatF64x2SZero).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4ConvertI32x4S, wasm.OpcodeVecF32x4ConvertI32x4U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsVFcvtFromInt(v1, ssa.VecLaneF32x4, vecOp == wasm.OpcodeVecF32x4ConvertI32x4S).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF64x2ConvertLowI32x4S, wasm.OpcodeVecF64x2ConvertLowI32x4U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ if runtime.GOARCH == "arm64" {
+ // TODO: this is weird. fix.
+ v1 = builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI32x4, vecOp == wasm.OpcodeVecF64x2ConvertLowI32x4S, true).Insert(builder).Return()
+ }
+ ret := builder.AllocateInstruction().
+ AsVFcvtFromInt(v1, ssa.VecLaneF64x2, vecOp == wasm.OpcodeVecF64x2ConvertLowI32x4S).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16NarrowI16x8S, wasm.OpcodeVecI8x16NarrowI16x8U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsNarrow(v1, v2, ssa.VecLaneI16x8, vecOp == wasm.OpcodeVecI8x16NarrowI16x8S).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI16x8NarrowI32x4S, wasm.OpcodeVecI16x8NarrowI32x4U:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsNarrow(v1, v2, ssa.VecLaneI32x4, vecOp == wasm.OpcodeVecI16x8NarrowI32x4S).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI16x8ExtendLowI8x16S, wasm.OpcodeVecI16x8ExtendLowI8x16U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI8x16, vecOp == wasm.OpcodeVecI16x8ExtendLowI8x16S, true).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI16x8ExtendHighI8x16S, wasm.OpcodeVecI16x8ExtendHighI8x16U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI8x16, vecOp == wasm.OpcodeVecI16x8ExtendHighI8x16S, false).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI32x4ExtendLowI16x8S, wasm.OpcodeVecI32x4ExtendLowI16x8U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI16x8, vecOp == wasm.OpcodeVecI32x4ExtendLowI16x8S, true).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI32x4ExtendHighI16x8S, wasm.OpcodeVecI32x4ExtendHighI16x8U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI16x8, vecOp == wasm.OpcodeVecI32x4ExtendHighI16x8S, false).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI64x2ExtendLowI32x4S, wasm.OpcodeVecI64x2ExtendLowI32x4U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI32x4, vecOp == wasm.OpcodeVecI64x2ExtendLowI32x4S, true).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI64x2ExtendHighI32x4S, wasm.OpcodeVecI64x2ExtendHighI32x4U:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsWiden(v1, ssa.VecLaneI32x4, vecOp == wasm.OpcodeVecI64x2ExtendHighI32x4S, false).
+ Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecF64x2PromoteLowF32x4Zero:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsFvpromoteLow(v1, ssa.VecLaneF32x4).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecF32x4DemoteF64x2Zero:
+ if state.unreachable {
+ break
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().
+ AsFvdemote(v1, ssa.VecLaneF64x2).
+ Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16Shl, wasm.OpcodeVecI16x8Shl, wasm.OpcodeVecI32x4Shl, wasm.OpcodeVecI64x2Shl:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Shl:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Shl:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Shl:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Shl:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVIshl(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16ShrS, wasm.OpcodeVecI16x8ShrS, wasm.OpcodeVecI32x4ShrS, wasm.OpcodeVecI64x2ShrS:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16ShrS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8ShrS:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4ShrS:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2ShrS:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVSshr(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16ShrU, wasm.OpcodeVecI16x8ShrU, wasm.OpcodeVecI32x4ShrU, wasm.OpcodeVecI64x2ShrU:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16ShrU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8ShrU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4ShrU:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2ShrU:
+ lane = ssa.VecLaneI64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsVUshr(v1, v2, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecI8x16ExtractLaneS, wasm.OpcodeVecI16x8ExtractLaneS:
+ state.pc++
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16ExtractLaneS:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8ExtractLaneS:
+ lane = ssa.VecLaneI16x8
+ }
+ v1 := state.pop()
+ index := c.wasmFunctionBody[state.pc]
+ ext := builder.AllocateInstruction().AsExtractlane(v1, index, lane, true).Insert(builder).Return()
+ state.push(ext)
+ case wasm.OpcodeVecI8x16ExtractLaneU, wasm.OpcodeVecI16x8ExtractLaneU,
+ wasm.OpcodeVecI32x4ExtractLane, wasm.OpcodeVecI64x2ExtractLane,
+ wasm.OpcodeVecF32x4ExtractLane, wasm.OpcodeVecF64x2ExtractLane:
+ state.pc++ // Skip the immediate value.
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16ExtractLaneU:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8ExtractLaneU:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4ExtractLane:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2ExtractLane:
+ lane = ssa.VecLaneI64x2
+ case wasm.OpcodeVecF32x4ExtractLane:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2ExtractLane:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ index := c.wasmFunctionBody[state.pc]
+ ext := builder.AllocateInstruction().AsExtractlane(v1, index, lane, false).Insert(builder).Return()
+ state.push(ext)
+ case wasm.OpcodeVecI8x16ReplaceLane, wasm.OpcodeVecI16x8ReplaceLane,
+ wasm.OpcodeVecI32x4ReplaceLane, wasm.OpcodeVecI64x2ReplaceLane,
+ wasm.OpcodeVecF32x4ReplaceLane, wasm.OpcodeVecF64x2ReplaceLane:
+ state.pc++
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16ReplaceLane:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8ReplaceLane:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4ReplaceLane:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2ReplaceLane:
+ lane = ssa.VecLaneI64x2
+ case wasm.OpcodeVecF32x4ReplaceLane:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2ReplaceLane:
+ lane = ssa.VecLaneF64x2
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ index := c.wasmFunctionBody[state.pc]
+ ret := builder.AllocateInstruction().AsInsertlane(v1, v2, index, lane).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeVecV128i8x16Shuffle:
+ state.pc++
+ laneIndexes := c.wasmFunctionBody[state.pc : state.pc+16]
+ state.pc += 15
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsShuffle(v1, v2, laneIndexes).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI8x16Swizzle:
+ if state.unreachable {
+ break
+ }
+ v2 := state.pop()
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsSwizzle(v1, v2, ssa.VecLaneI8x16).Insert(builder).Return()
+ state.push(ret)
+
+ case wasm.OpcodeVecI8x16Splat,
+ wasm.OpcodeVecI16x8Splat,
+ wasm.OpcodeVecI32x4Splat,
+ wasm.OpcodeVecI64x2Splat,
+ wasm.OpcodeVecF32x4Splat,
+ wasm.OpcodeVecF64x2Splat:
+ if state.unreachable {
+ break
+ }
+ var lane ssa.VecLane
+ switch vecOp {
+ case wasm.OpcodeVecI8x16Splat:
+ lane = ssa.VecLaneI8x16
+ case wasm.OpcodeVecI16x8Splat:
+ lane = ssa.VecLaneI16x8
+ case wasm.OpcodeVecI32x4Splat:
+ lane = ssa.VecLaneI32x4
+ case wasm.OpcodeVecI64x2Splat:
+ lane = ssa.VecLaneI64x2
+ case wasm.OpcodeVecF32x4Splat:
+ lane = ssa.VecLaneF32x4
+ case wasm.OpcodeVecF64x2Splat:
+ lane = ssa.VecLaneF64x2
+ }
+ v1 := state.pop()
+ ret := builder.AllocateInstruction().AsSplat(v1, lane).Insert(builder).Return()
+ state.push(ret)
+
+ default:
+ panic("TODO: unsupported vector instruction: " + wasm.VectorInstructionName(vecOp))
+ }
+ case wasm.OpcodeAtomicPrefix:
+ state.pc++
+ atomicOp := c.wasmFunctionBody[state.pc]
+ switch atomicOp {
+ case wasm.OpcodeAtomicMemoryWait32, wasm.OpcodeAtomicMemoryWait64:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ c.storeCallerModuleContext()
+
+ var opSize uint64
+ var trampoline wazevoapi.Offset
+ var sig *ssa.Signature
+ switch atomicOp {
+ case wasm.OpcodeAtomicMemoryWait32:
+ opSize = 4
+ trampoline = wazevoapi.ExecutionContextOffsetMemoryWait32TrampolineAddress
+ sig = &c.memoryWait32Sig
+ case wasm.OpcodeAtomicMemoryWait64:
+ opSize = 8
+ trampoline = wazevoapi.ExecutionContextOffsetMemoryWait64TrampolineAddress
+ sig = &c.memoryWait64Sig
+ }
+
+ timeout := state.pop()
+ exp := state.pop()
+ baseAddr := state.pop()
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), opSize)
+
+ memoryWaitPtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ trampoline.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ args := c.allocateVarLengthValues(3, c.execCtxPtrValue, timeout, exp, addr)
+ memoryWaitRet := builder.AllocateInstruction().
+ AsCallIndirect(memoryWaitPtr, sig, args).
+ Insert(builder).Return()
+ state.push(memoryWaitRet)
+ case wasm.OpcodeAtomicMemoryNotify:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ c.storeCallerModuleContext()
+ count := state.pop()
+ baseAddr := state.pop()
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), 4)
+
+ memoryNotifyPtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetMemoryNotifyTrampolineAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+ args := c.allocateVarLengthValues(2, c.execCtxPtrValue, count, addr)
+ memoryNotifyRet := builder.AllocateInstruction().
+ AsCallIndirect(memoryNotifyPtr, &c.memoryNotifySig, args).
+ Insert(builder).Return()
+ state.push(memoryNotifyRet)
+ case wasm.OpcodeAtomicI32Load, wasm.OpcodeAtomicI64Load, wasm.OpcodeAtomicI32Load8U, wasm.OpcodeAtomicI32Load16U, wasm.OpcodeAtomicI64Load8U, wasm.OpcodeAtomicI64Load16U, wasm.OpcodeAtomicI64Load32U:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ baseAddr := state.pop()
+
+ var size uint64
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64Load:
+ size = 8
+ case wasm.OpcodeAtomicI32Load, wasm.OpcodeAtomicI64Load32U:
+ size = 4
+ case wasm.OpcodeAtomicI32Load16U, wasm.OpcodeAtomicI64Load16U:
+ size = 2
+ case wasm.OpcodeAtomicI32Load8U, wasm.OpcodeAtomicI64Load8U:
+ size = 1
+ }
+
+ var typ ssa.Type
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64Load, wasm.OpcodeAtomicI64Load32U, wasm.OpcodeAtomicI64Load16U, wasm.OpcodeAtomicI64Load8U:
+ typ = ssa.TypeI64
+ case wasm.OpcodeAtomicI32Load, wasm.OpcodeAtomicI32Load16U, wasm.OpcodeAtomicI32Load8U:
+ typ = ssa.TypeI32
+ }
+
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), size)
+ res := builder.AllocateInstruction().AsAtomicLoad(addr, size, typ).Insert(builder).Return()
+ state.push(res)
+ case wasm.OpcodeAtomicI32Store, wasm.OpcodeAtomicI64Store, wasm.OpcodeAtomicI32Store8, wasm.OpcodeAtomicI32Store16, wasm.OpcodeAtomicI64Store8, wasm.OpcodeAtomicI64Store16, wasm.OpcodeAtomicI64Store32:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ val := state.pop()
+ baseAddr := state.pop()
+
+ var size uint64
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64Store:
+ size = 8
+ case wasm.OpcodeAtomicI32Store, wasm.OpcodeAtomicI64Store32:
+ size = 4
+ case wasm.OpcodeAtomicI32Store16, wasm.OpcodeAtomicI64Store16:
+ size = 2
+ case wasm.OpcodeAtomicI32Store8, wasm.OpcodeAtomicI64Store8:
+ size = 1
+ }
+
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), size)
+ builder.AllocateInstruction().AsAtomicStore(addr, val, size).Insert(builder)
+ case wasm.OpcodeAtomicI32RmwAdd, wasm.OpcodeAtomicI64RmwAdd, wasm.OpcodeAtomicI32Rmw8AddU, wasm.OpcodeAtomicI32Rmw16AddU, wasm.OpcodeAtomicI64Rmw8AddU, wasm.OpcodeAtomicI64Rmw16AddU, wasm.OpcodeAtomicI64Rmw32AddU,
+ wasm.OpcodeAtomicI32RmwSub, wasm.OpcodeAtomicI64RmwSub, wasm.OpcodeAtomicI32Rmw8SubU, wasm.OpcodeAtomicI32Rmw16SubU, wasm.OpcodeAtomicI64Rmw8SubU, wasm.OpcodeAtomicI64Rmw16SubU, wasm.OpcodeAtomicI64Rmw32SubU,
+ wasm.OpcodeAtomicI32RmwAnd, wasm.OpcodeAtomicI64RmwAnd, wasm.OpcodeAtomicI32Rmw8AndU, wasm.OpcodeAtomicI32Rmw16AndU, wasm.OpcodeAtomicI64Rmw8AndU, wasm.OpcodeAtomicI64Rmw16AndU, wasm.OpcodeAtomicI64Rmw32AndU,
+ wasm.OpcodeAtomicI32RmwOr, wasm.OpcodeAtomicI64RmwOr, wasm.OpcodeAtomicI32Rmw8OrU, wasm.OpcodeAtomicI32Rmw16OrU, wasm.OpcodeAtomicI64Rmw8OrU, wasm.OpcodeAtomicI64Rmw16OrU, wasm.OpcodeAtomicI64Rmw32OrU,
+ wasm.OpcodeAtomicI32RmwXor, wasm.OpcodeAtomicI64RmwXor, wasm.OpcodeAtomicI32Rmw8XorU, wasm.OpcodeAtomicI32Rmw16XorU, wasm.OpcodeAtomicI64Rmw8XorU, wasm.OpcodeAtomicI64Rmw16XorU, wasm.OpcodeAtomicI64Rmw32XorU,
+ wasm.OpcodeAtomicI32RmwXchg, wasm.OpcodeAtomicI64RmwXchg, wasm.OpcodeAtomicI32Rmw8XchgU, wasm.OpcodeAtomicI32Rmw16XchgU, wasm.OpcodeAtomicI64Rmw8XchgU, wasm.OpcodeAtomicI64Rmw16XchgU, wasm.OpcodeAtomicI64Rmw32XchgU:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ val := state.pop()
+ baseAddr := state.pop()
+
+ var rmwOp ssa.AtomicRmwOp
+ var size uint64
+ switch atomicOp {
+ case wasm.OpcodeAtomicI32RmwAdd, wasm.OpcodeAtomicI64RmwAdd, wasm.OpcodeAtomicI32Rmw8AddU, wasm.OpcodeAtomicI32Rmw16AddU, wasm.OpcodeAtomicI64Rmw8AddU, wasm.OpcodeAtomicI64Rmw16AddU, wasm.OpcodeAtomicI64Rmw32AddU:
+ rmwOp = ssa.AtomicRmwOpAdd
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwAdd:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwAdd, wasm.OpcodeAtomicI64Rmw32AddU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16AddU, wasm.OpcodeAtomicI64Rmw16AddU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8AddU, wasm.OpcodeAtomicI64Rmw8AddU:
+ size = 1
+ }
+ case wasm.OpcodeAtomicI32RmwSub, wasm.OpcodeAtomicI64RmwSub, wasm.OpcodeAtomicI32Rmw8SubU, wasm.OpcodeAtomicI32Rmw16SubU, wasm.OpcodeAtomicI64Rmw8SubU, wasm.OpcodeAtomicI64Rmw16SubU, wasm.OpcodeAtomicI64Rmw32SubU:
+ rmwOp = ssa.AtomicRmwOpSub
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwSub:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwSub, wasm.OpcodeAtomicI64Rmw32SubU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16SubU, wasm.OpcodeAtomicI64Rmw16SubU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8SubU, wasm.OpcodeAtomicI64Rmw8SubU:
+ size = 1
+ }
+ case wasm.OpcodeAtomicI32RmwAnd, wasm.OpcodeAtomicI64RmwAnd, wasm.OpcodeAtomicI32Rmw8AndU, wasm.OpcodeAtomicI32Rmw16AndU, wasm.OpcodeAtomicI64Rmw8AndU, wasm.OpcodeAtomicI64Rmw16AndU, wasm.OpcodeAtomicI64Rmw32AndU:
+ rmwOp = ssa.AtomicRmwOpAnd
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwAnd:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwAnd, wasm.OpcodeAtomicI64Rmw32AndU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16AndU, wasm.OpcodeAtomicI64Rmw16AndU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8AndU, wasm.OpcodeAtomicI64Rmw8AndU:
+ size = 1
+ }
+ case wasm.OpcodeAtomicI32RmwOr, wasm.OpcodeAtomicI64RmwOr, wasm.OpcodeAtomicI32Rmw8OrU, wasm.OpcodeAtomicI32Rmw16OrU, wasm.OpcodeAtomicI64Rmw8OrU, wasm.OpcodeAtomicI64Rmw16OrU, wasm.OpcodeAtomicI64Rmw32OrU:
+ rmwOp = ssa.AtomicRmwOpOr
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwOr:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwOr, wasm.OpcodeAtomicI64Rmw32OrU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16OrU, wasm.OpcodeAtomicI64Rmw16OrU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8OrU, wasm.OpcodeAtomicI64Rmw8OrU:
+ size = 1
+ }
+ case wasm.OpcodeAtomicI32RmwXor, wasm.OpcodeAtomicI64RmwXor, wasm.OpcodeAtomicI32Rmw8XorU, wasm.OpcodeAtomicI32Rmw16XorU, wasm.OpcodeAtomicI64Rmw8XorU, wasm.OpcodeAtomicI64Rmw16XorU, wasm.OpcodeAtomicI64Rmw32XorU:
+ rmwOp = ssa.AtomicRmwOpXor
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwXor:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwXor, wasm.OpcodeAtomicI64Rmw32XorU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16XorU, wasm.OpcodeAtomicI64Rmw16XorU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8XorU, wasm.OpcodeAtomicI64Rmw8XorU:
+ size = 1
+ }
+ case wasm.OpcodeAtomicI32RmwXchg, wasm.OpcodeAtomicI64RmwXchg, wasm.OpcodeAtomicI32Rmw8XchgU, wasm.OpcodeAtomicI32Rmw16XchgU, wasm.OpcodeAtomicI64Rmw8XchgU, wasm.OpcodeAtomicI64Rmw16XchgU, wasm.OpcodeAtomicI64Rmw32XchgU:
+ rmwOp = ssa.AtomicRmwOpXchg
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwXchg:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwXchg, wasm.OpcodeAtomicI64Rmw32XchgU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16XchgU, wasm.OpcodeAtomicI64Rmw16XchgU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8XchgU, wasm.OpcodeAtomicI64Rmw8XchgU:
+ size = 1
+ }
+ }
+
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), size)
+ res := builder.AllocateInstruction().AsAtomicRmw(rmwOp, addr, val, size).Insert(builder).Return()
+ state.push(res)
+ case wasm.OpcodeAtomicI32RmwCmpxchg, wasm.OpcodeAtomicI64RmwCmpxchg, wasm.OpcodeAtomicI32Rmw8CmpxchgU, wasm.OpcodeAtomicI32Rmw16CmpxchgU, wasm.OpcodeAtomicI64Rmw8CmpxchgU, wasm.OpcodeAtomicI64Rmw16CmpxchgU, wasm.OpcodeAtomicI64Rmw32CmpxchgU:
+ _, offset := c.readMemArg()
+ if state.unreachable {
+ break
+ }
+
+ repl := state.pop()
+ exp := state.pop()
+ baseAddr := state.pop()
+
+ var size uint64
+ switch atomicOp {
+ case wasm.OpcodeAtomicI64RmwCmpxchg:
+ size = 8
+ case wasm.OpcodeAtomicI32RmwCmpxchg, wasm.OpcodeAtomicI64Rmw32CmpxchgU:
+ size = 4
+ case wasm.OpcodeAtomicI32Rmw16CmpxchgU, wasm.OpcodeAtomicI64Rmw16CmpxchgU:
+ size = 2
+ case wasm.OpcodeAtomicI32Rmw8CmpxchgU, wasm.OpcodeAtomicI64Rmw8CmpxchgU:
+ size = 1
+ }
+ addr := c.atomicMemOpSetup(baseAddr, uint64(offset), size)
+ res := builder.AllocateInstruction().AsAtomicCas(addr, exp, repl, size).Insert(builder).Return()
+ state.push(res)
+ case wasm.OpcodeAtomicFence:
+ order := c.readByte()
+ if state.unreachable {
+ break
+ }
+ if c.needMemory {
+ builder.AllocateInstruction().AsFence(order).Insert(builder)
+ }
+ default:
+ panic("TODO: unsupported atomic instruction: " + wasm.AtomicInstructionName(atomicOp))
+ }
+ case wasm.OpcodeRefFunc:
+ funcIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+
+ c.storeCallerModuleContext()
+
+ funcIndexVal := builder.AllocateInstruction().AsIconst32(funcIndex).Insert(builder).Return()
+
+ refFuncPtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetRefFuncTrampolineAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ args := c.allocateVarLengthValues(2, c.execCtxPtrValue, funcIndexVal)
+ refFuncRet := builder.
+ AllocateInstruction().
+ AsCallIndirect(refFuncPtr, &c.refFuncSig, args).
+ Insert(builder).Return()
+ state.push(refFuncRet)
+
+ case wasm.OpcodeRefNull:
+ c.loweringState.pc++ // skips the reference type as we treat both of them as i64(0).
+ if state.unreachable {
+ break
+ }
+ ret := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+ state.push(ret)
+ case wasm.OpcodeRefIsNull:
+ if state.unreachable {
+ break
+ }
+ r := state.pop()
+ zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder)
+ icmp := builder.AllocateInstruction().
+ AsIcmp(r, zero.Return(), ssa.IntegerCmpCondEqual).
+ Insert(builder).
+ Return()
+ state.push(icmp)
+ case wasm.OpcodeTableSet:
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ r := state.pop()
+ targetOffsetInTable := state.pop()
+
+ elementAddr := c.lowerAccessTableWithBoundsCheck(tableIndex, targetOffsetInTable)
+ builder.AllocateInstruction().AsStore(ssa.OpcodeStore, r, elementAddr, 0).Insert(builder)
+
+ case wasm.OpcodeTableGet:
+ tableIndex := c.readI32u()
+ if state.unreachable {
+ break
+ }
+ targetOffsetInTable := state.pop()
+ elementAddr := c.lowerAccessTableWithBoundsCheck(tableIndex, targetOffsetInTable)
+ loaded := builder.AllocateInstruction().AsLoad(elementAddr, 0, ssa.TypeI64).Insert(builder).Return()
+ state.push(loaded)
+ default:
+ panic("TODO: unsupported in wazevo yet: " + wasm.InstructionName(op))
+ }
+
+ if wazevoapi.FrontEndLoggingEnabled {
+ fmt.Println("--------- Translated " + wasm.InstructionName(op) + " --------")
+ fmt.Println("state: " + c.loweringState.String())
+ fmt.Println(c.formatBuilder())
+ fmt.Println("--------------------------")
+ }
+ c.loweringState.pc++
+}
+
+func (c *Compiler) lowerExtMul(v1, v2 ssa.Value, from, to ssa.VecLane, signed, low bool) ssa.Value {
+ // TODO: The sequence `Widen; Widen; VIMul` can be substituted for a single instruction on some ISAs.
+ builder := c.ssaBuilder
+
+ v1lo := builder.AllocateInstruction().AsWiden(v1, from, signed, low).Insert(builder).Return()
+ v2lo := builder.AllocateInstruction().AsWiden(v2, from, signed, low).Insert(builder).Return()
+
+ return builder.AllocateInstruction().AsVImul(v1lo, v2lo, to).Insert(builder).Return()
+}
+
+const (
+ tableInstanceBaseAddressOffset = 0
+ tableInstanceLenOffset = tableInstanceBaseAddressOffset + 8
+)
+
+func (c *Compiler) lowerAccessTableWithBoundsCheck(tableIndex uint32, elementOffsetInTable ssa.Value) (elementAddress ssa.Value) {
+ builder := c.ssaBuilder
+
+ // Load the table.
+ loadTableInstancePtr := builder.AllocateInstruction()
+ loadTableInstancePtr.AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64)
+ builder.InsertInstruction(loadTableInstancePtr)
+ tableInstancePtr := loadTableInstancePtr.Return()
+
+ // Load the table's length.
+ loadTableLen := builder.AllocateInstruction()
+ loadTableLen.AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32)
+ builder.InsertInstruction(loadTableLen)
+ tableLen := loadTableLen.Return()
+
+ // Compare the length and the target, and trap if out of bounds.
+ checkOOB := builder.AllocateInstruction()
+ checkOOB.AsIcmp(elementOffsetInTable, tableLen, ssa.IntegerCmpCondUnsignedGreaterThanOrEqual)
+ builder.InsertInstruction(checkOOB)
+ exitIfOOB := builder.AllocateInstruction()
+ exitIfOOB.AsExitIfTrueWithCode(c.execCtxPtrValue, checkOOB.Return(), wazevoapi.ExitCodeTableOutOfBounds)
+ builder.InsertInstruction(exitIfOOB)
+
+ // Get the base address of wasm.TableInstance.References.
+ loadTableBaseAddress := builder.AllocateInstruction()
+ loadTableBaseAddress.AsLoad(tableInstancePtr, tableInstanceBaseAddressOffset, ssa.TypeI64)
+ builder.InsertInstruction(loadTableBaseAddress)
+ tableBase := loadTableBaseAddress.Return()
+
+ // Calculate the address of the target function. First we need to multiply targetOffsetInTable by 8 (pointer size).
+ multiplyBy8 := builder.AllocateInstruction()
+ three := builder.AllocateInstruction()
+ three.AsIconst64(3)
+ builder.InsertInstruction(three)
+ multiplyBy8.AsIshl(elementOffsetInTable, three.Return())
+ builder.InsertInstruction(multiplyBy8)
+ targetOffsetInTableMultipliedBy8 := multiplyBy8.Return()
+
+ // Then add the multiplied value to the base which results in the address of the target function (*wazevo.functionInstance)
+ calcElementAddressInTable := builder.AllocateInstruction()
+ calcElementAddressInTable.AsIadd(tableBase, targetOffsetInTableMultipliedBy8)
+ builder.InsertInstruction(calcElementAddressInTable)
+ return calcElementAddressInTable.Return()
+}
+
+func (c *Compiler) lowerCallIndirect(typeIndex, tableIndex uint32) {
+ builder := c.ssaBuilder
+ state := c.state()
+
+ elementOffsetInTable := state.pop()
+ functionInstancePtrAddress := c.lowerAccessTableWithBoundsCheck(tableIndex, elementOffsetInTable)
+ loadFunctionInstancePtr := builder.AllocateInstruction()
+ loadFunctionInstancePtr.AsLoad(functionInstancePtrAddress, 0, ssa.TypeI64)
+ builder.InsertInstruction(loadFunctionInstancePtr)
+ functionInstancePtr := loadFunctionInstancePtr.Return()
+
+ // Check if it is not the null pointer.
+ zero := builder.AllocateInstruction()
+ zero.AsIconst64(0)
+ builder.InsertInstruction(zero)
+ checkNull := builder.AllocateInstruction()
+ checkNull.AsIcmp(functionInstancePtr, zero.Return(), ssa.IntegerCmpCondEqual)
+ builder.InsertInstruction(checkNull)
+ exitIfNull := builder.AllocateInstruction()
+ exitIfNull.AsExitIfTrueWithCode(c.execCtxPtrValue, checkNull.Return(), wazevoapi.ExitCodeIndirectCallNullPointer)
+ builder.InsertInstruction(exitIfNull)
+
+ // We need to do the type check. First, load the target function instance's typeID.
+ loadTypeID := builder.AllocateInstruction()
+ loadTypeID.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceTypeIDOffset, ssa.TypeI32)
+ builder.InsertInstruction(loadTypeID)
+ actualTypeID := loadTypeID.Return()
+
+ // Next, we load the expected TypeID:
+ loadTypeIDsBegin := builder.AllocateInstruction()
+ loadTypeIDsBegin.AsLoad(c.moduleCtxPtrValue, c.offset.TypeIDs1stElement.U32(), ssa.TypeI64)
+ builder.InsertInstruction(loadTypeIDsBegin)
+ typeIDsBegin := loadTypeIDsBegin.Return()
+
+ loadExpectedTypeID := builder.AllocateInstruction()
+ loadExpectedTypeID.AsLoad(typeIDsBegin, uint32(typeIndex)*4 /* size of wasm.FunctionTypeID */, ssa.TypeI32)
+ builder.InsertInstruction(loadExpectedTypeID)
+ expectedTypeID := loadExpectedTypeID.Return()
+
+ // Check if the type ID matches.
+ checkTypeID := builder.AllocateInstruction()
+ checkTypeID.AsIcmp(actualTypeID, expectedTypeID, ssa.IntegerCmpCondNotEqual)
+ builder.InsertInstruction(checkTypeID)
+ exitIfNotMatch := builder.AllocateInstruction()
+ exitIfNotMatch.AsExitIfTrueWithCode(c.execCtxPtrValue, checkTypeID.Return(), wazevoapi.ExitCodeIndirectCallTypeMismatch)
+ builder.InsertInstruction(exitIfNotMatch)
+
+ // Now ready to call the function. Load the executable and moduleContextOpaquePtr from the function instance.
+ loadExecutablePtr := builder.AllocateInstruction()
+ loadExecutablePtr.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceExecutableOffset, ssa.TypeI64)
+ builder.InsertInstruction(loadExecutablePtr)
+ executablePtr := loadExecutablePtr.Return()
+ loadModuleContextOpaquePtr := builder.AllocateInstruction()
+ loadModuleContextOpaquePtr.AsLoad(functionInstancePtr, wazevoapi.FunctionInstanceModuleContextOpaquePtrOffset, ssa.TypeI64)
+ builder.InsertInstruction(loadModuleContextOpaquePtr)
+ moduleContextOpaquePtr := loadModuleContextOpaquePtr.Return()
+
+ typ := &c.m.TypeSection[typeIndex]
+ tail := len(state.values) - len(typ.Params)
+ vs := state.values[tail:]
+ state.values = state.values[:tail]
+ args := c.allocateVarLengthValues(2+len(vs), c.execCtxPtrValue, moduleContextOpaquePtr)
+ args = args.Append(builder.VarLengthPool(), vs...)
+
+ // Before transfer the control to the callee, we have to store the current module's moduleContextPtr
+ // into execContext.callerModuleContextPtr in case when the callee is a Go function.
+ c.storeCallerModuleContext()
+
+ call := builder.AllocateInstruction()
+ call.AsCallIndirect(executablePtr, c.signatures[typ], args)
+ builder.InsertInstruction(call)
+
+ first, rest := call.Returns()
+ if first.Valid() {
+ state.push(first)
+ }
+ for _, v := range rest {
+ state.push(v)
+ }
+
+ c.reloadAfterCall()
+}
+
+// memOpSetup inserts the bounds check and calculates the address of the memory operation (loads/stores).
+func (c *Compiler) memOpSetup(baseAddr ssa.Value, constOffset, operationSizeInBytes uint64) (address ssa.Value) {
+ address = ssa.ValueInvalid
+ builder := c.ssaBuilder
+
+ baseAddrID := baseAddr.ID()
+ ceil := constOffset + operationSizeInBytes
+ if known := c.getKnownSafeBound(baseAddrID); known.valid() {
+ // We reuse the calculated absolute address even if the bound is not known to be safe.
+ address = known.absoluteAddr
+ if ceil <= known.bound {
+ if !address.Valid() {
+ // This means that, the bound is known to be safe, but the memory base might have changed.
+ // So, we re-calculate the address.
+ memBase := c.getMemoryBaseValue(false)
+ extBaseAddr := builder.AllocateInstruction().
+ AsUExtend(baseAddr, 32, 64).
+ Insert(builder).
+ Return()
+ address = builder.AllocateInstruction().
+ AsIadd(memBase, extBaseAddr).Insert(builder).Return()
+ known.absoluteAddr = address // Update the absolute address for the subsequent memory access.
+ }
+ return
+ }
+ }
+
+ ceilConst := builder.AllocateInstruction()
+ ceilConst.AsIconst64(ceil)
+ builder.InsertInstruction(ceilConst)
+
+ // We calculate the offset in 64-bit space.
+ extBaseAddr := builder.AllocateInstruction().
+ AsUExtend(baseAddr, 32, 64).
+ Insert(builder).
+ Return()
+
+ // Note: memLen is already zero extended to 64-bit space at the load time.
+ memLen := c.getMemoryLenValue(false)
+
+ // baseAddrPlusCeil = baseAddr + ceil
+ baseAddrPlusCeil := builder.AllocateInstruction()
+ baseAddrPlusCeil.AsIadd(extBaseAddr, ceilConst.Return())
+ builder.InsertInstruction(baseAddrPlusCeil)
+
+ // Check for out of bounds memory access: `memLen >= baseAddrPlusCeil`.
+ cmp := builder.AllocateInstruction()
+ cmp.AsIcmp(memLen, baseAddrPlusCeil.Return(), ssa.IntegerCmpCondUnsignedLessThan)
+ builder.InsertInstruction(cmp)
+ exitIfNZ := builder.AllocateInstruction()
+ exitIfNZ.AsExitIfTrueWithCode(c.execCtxPtrValue, cmp.Return(), wazevoapi.ExitCodeMemoryOutOfBounds)
+ builder.InsertInstruction(exitIfNZ)
+
+ // Load the value from memBase + extBaseAddr.
+ if address == ssa.ValueInvalid { // Reuse the value if the memBase is already calculated at this point.
+ memBase := c.getMemoryBaseValue(false)
+ address = builder.AllocateInstruction().
+ AsIadd(memBase, extBaseAddr).Insert(builder).Return()
+ }
+
+ // Record the bound ceil for this baseAddr is known to be safe for the subsequent memory access in the same block.
+ c.recordKnownSafeBound(baseAddrID, ceil, address)
+ return
+}
+
+// atomicMemOpSetup inserts the bounds check and calculates the address of the memory operation (loads/stores), including
+// the constant offset and performs an alignment check on the final address.
+func (c *Compiler) atomicMemOpSetup(baseAddr ssa.Value, constOffset, operationSizeInBytes uint64) (address ssa.Value) {
+ builder := c.ssaBuilder
+
+ addrWithoutOffset := c.memOpSetup(baseAddr, constOffset, operationSizeInBytes)
+ var addr ssa.Value
+ if constOffset == 0 {
+ addr = addrWithoutOffset
+ } else {
+ offset := builder.AllocateInstruction().AsIconst64(constOffset).Insert(builder).Return()
+ addr = builder.AllocateInstruction().AsIadd(addrWithoutOffset, offset).Insert(builder).Return()
+ }
+
+ c.memAlignmentCheck(addr, operationSizeInBytes)
+
+ return addr
+}
+
+func (c *Compiler) memAlignmentCheck(addr ssa.Value, operationSizeInBytes uint64) {
+ if operationSizeInBytes == 1 {
+ return // No alignment restrictions when accessing a byte
+ }
+ var checkBits uint64
+ switch operationSizeInBytes {
+ case 2:
+ checkBits = 0b1
+ case 4:
+ checkBits = 0b11
+ case 8:
+ checkBits = 0b111
+ }
+
+ builder := c.ssaBuilder
+
+ mask := builder.AllocateInstruction().AsIconst64(checkBits).Insert(builder).Return()
+ masked := builder.AllocateInstruction().AsBand(addr, mask).Insert(builder).Return()
+ zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+ cmp := builder.AllocateInstruction().AsIcmp(masked, zero, ssa.IntegerCmpCondNotEqual).Insert(builder).Return()
+ builder.AllocateInstruction().AsExitIfTrueWithCode(c.execCtxPtrValue, cmp, wazevoapi.ExitCodeUnalignedAtomic).Insert(builder)
+}
+
+func (c *Compiler) callMemmove(dst, src, size ssa.Value) {
+ args := c.allocateVarLengthValues(3, dst, src, size)
+ if size.Type() != ssa.TypeI64 {
+ panic("TODO: memmove size must be i64")
+ }
+
+ builder := c.ssaBuilder
+ memmovePtr := builder.AllocateInstruction().
+ AsLoad(c.execCtxPtrValue,
+ wazevoapi.ExecutionContextOffsetMemmoveAddress.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+ builder.AllocateInstruction().AsCallGoRuntimeMemmove(memmovePtr, &c.memmoveSig, args).Insert(builder)
+}
+
+func (c *Compiler) reloadAfterCall() {
+ // Note that when these are not used in the following instructions, they will be optimized out.
+ // So in any ways, we define them!
+
+ // After calling any function, memory buffer might have changed. So we need to re-define the variable.
+ // However, if the memory is shared, we don't need to reload the memory base and length as the base will never change.
+ if c.needMemory && !c.memoryShared {
+ c.reloadMemoryBaseLen()
+ }
+
+ // Also, any mutable Global can change.
+ for _, index := range c.mutableGlobalVariablesIndexes {
+ _ = c.getWasmGlobalValue(index, true)
+ }
+}
+
+func (c *Compiler) reloadMemoryBaseLen() {
+ _ = c.getMemoryBaseValue(true)
+ _ = c.getMemoryLenValue(true)
+
+ // This function being called means that the memory base might have changed.
+ // Therefore, we need to clear the absolute addresses recorded in the known safe bounds
+ // because we cache the absolute address of the memory access per each base offset.
+ c.resetAbsoluteAddressInSafeBounds()
+}
+
+func (c *Compiler) setWasmGlobalValue(index wasm.Index, v ssa.Value) {
+ variable := c.globalVariables[index]
+ opaqueOffset := c.offset.GlobalInstanceOffset(index)
+
+ builder := c.ssaBuilder
+ if index < c.m.ImportGlobalCount {
+ loadGlobalInstPtr := builder.AllocateInstruction()
+ loadGlobalInstPtr.AsLoad(c.moduleCtxPtrValue, uint32(opaqueOffset), ssa.TypeI64)
+ builder.InsertInstruction(loadGlobalInstPtr)
+
+ store := builder.AllocateInstruction()
+ store.AsStore(ssa.OpcodeStore, v, loadGlobalInstPtr.Return(), uint32(0))
+ builder.InsertInstruction(store)
+
+ } else {
+ store := builder.AllocateInstruction()
+ store.AsStore(ssa.OpcodeStore, v, c.moduleCtxPtrValue, uint32(opaqueOffset))
+ builder.InsertInstruction(store)
+ }
+
+ // The value has changed to `v`, so we record it.
+ builder.DefineVariableInCurrentBB(variable, v)
+}
+
+func (c *Compiler) getWasmGlobalValue(index wasm.Index, forceLoad bool) ssa.Value {
+ variable := c.globalVariables[index]
+ typ := c.globalVariablesTypes[index]
+ opaqueOffset := c.offset.GlobalInstanceOffset(index)
+
+ builder := c.ssaBuilder
+ if !forceLoad {
+ if v := builder.FindValueInLinearPath(variable); v.Valid() {
+ return v
+ }
+ }
+
+ var load *ssa.Instruction
+ if index < c.m.ImportGlobalCount {
+ loadGlobalInstPtr := builder.AllocateInstruction()
+ loadGlobalInstPtr.AsLoad(c.moduleCtxPtrValue, uint32(opaqueOffset), ssa.TypeI64)
+ builder.InsertInstruction(loadGlobalInstPtr)
+ load = builder.AllocateInstruction().
+ AsLoad(loadGlobalInstPtr.Return(), uint32(0), typ)
+ } else {
+ load = builder.AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue, uint32(opaqueOffset), typ)
+ }
+
+ v := load.Insert(builder).Return()
+ builder.DefineVariableInCurrentBB(variable, v)
+ return v
+}
+
+const (
+ memoryInstanceBufOffset = 0
+ memoryInstanceBufSizeOffset = memoryInstanceBufOffset + 8
+)
+
+func (c *Compiler) getMemoryBaseValue(forceReload bool) ssa.Value {
+ builder := c.ssaBuilder
+ variable := c.memoryBaseVariable
+ if !forceReload {
+ if v := builder.FindValueInLinearPath(variable); v.Valid() {
+ return v
+ }
+ }
+
+ var ret 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()
+
+ loadBufPtr := builder.AllocateInstruction()
+ loadBufPtr.AsLoad(memInstPtr, memoryInstanceBufOffset, ssa.TypeI64)
+ builder.InsertInstruction(loadBufPtr)
+ ret = loadBufPtr.Return()
+ } else {
+ load := builder.AllocateInstruction()
+ load.AsLoad(c.moduleCtxPtrValue, c.offset.LocalMemoryBase().U32(), ssa.TypeI64)
+ builder.InsertInstruction(load)
+ ret = load.Return()
+ }
+
+ builder.DefineVariableInCurrentBB(variable, ret)
+ return ret
+}
+
+func (c *Compiler) getMemoryLenValue(forceReload bool) ssa.Value {
+ variable := c.memoryLenVariable
+ builder := c.ssaBuilder
+ if !forceReload && !c.memoryShared {
+ if v := builder.FindValueInLinearPath(variable); v.Valid() {
+ return v
+ }
+ }
+
+ var ret 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()
+ if c.memoryShared {
+ sizeOffset := builder.AllocateInstruction().AsIconst64(memoryInstanceBufSizeOffset).Insert(builder).Return()
+ addr := builder.AllocateInstruction().AsIadd(memInstPtr, sizeOffset).Insert(builder).Return()
+ loadBufSizePtr.AsAtomicLoad(addr, 8, ssa.TypeI64)
+ } else {
+ loadBufSizePtr.AsLoad(memInstPtr, memoryInstanceBufSizeOffset, ssa.TypeI64)
+ }
+ builder.InsertInstruction(loadBufSizePtr)
+
+ ret = loadBufSizePtr.Return()
+ } else {
+ load := builder.AllocateInstruction()
+ if c.memoryShared {
+ lenOffset := builder.AllocateInstruction().AsIconst64(c.offset.LocalMemoryLen().U64()).Insert(builder).Return()
+ addr := builder.AllocateInstruction().AsIadd(c.moduleCtxPtrValue, lenOffset).Insert(builder).Return()
+ load.AsAtomicLoad(addr, 8, ssa.TypeI64)
+ } else {
+ load.AsExtLoad(ssa.OpcodeUload32, c.moduleCtxPtrValue, c.offset.LocalMemoryLen().U32(), true)
+ }
+ builder.InsertInstruction(load)
+ ret = load.Return()
+ }
+
+ builder.DefineVariableInCurrentBB(variable, ret)
+ return ret
+}
+
+func (c *Compiler) insertIcmp(cond ssa.IntegerCmpCond) {
+ state, builder := c.state(), c.ssaBuilder
+ y, x := state.pop(), state.pop()
+ cmp := builder.AllocateInstruction()
+ cmp.AsIcmp(x, y, cond)
+ builder.InsertInstruction(cmp)
+ value := cmp.Return()
+ state.push(value)
+}
+
+func (c *Compiler) insertFcmp(cond ssa.FloatCmpCond) {
+ state, builder := c.state(), c.ssaBuilder
+ y, x := state.pop(), state.pop()
+ cmp := builder.AllocateInstruction()
+ cmp.AsFcmp(x, y, cond)
+ builder.InsertInstruction(cmp)
+ value := cmp.Return()
+ state.push(value)
+}
+
+// storeCallerModuleContext stores the current module's moduleContextPtr into execContext.callerModuleContextPtr.
+func (c *Compiler) storeCallerModuleContext() {
+ builder := c.ssaBuilder
+ execCtx := c.execCtxPtrValue
+ store := builder.AllocateInstruction()
+ store.AsStore(ssa.OpcodeStore,
+ c.moduleCtxPtrValue, execCtx, wazevoapi.ExecutionContextOffsetCallerModuleContextPtr.U32())
+ builder.InsertInstruction(store)
+}
+
+func (c *Compiler) readByte() byte {
+ v := c.wasmFunctionBody[c.loweringState.pc+1]
+ c.loweringState.pc++
+ return v
+}
+
+func (c *Compiler) readI32u() uint32 {
+ v, n, err := leb128.LoadUint32(c.wasmFunctionBody[c.loweringState.pc+1:])
+ if err != nil {
+ panic(err) // shouldn't be reached since compilation comes after validation.
+ }
+ c.loweringState.pc += int(n)
+ return v
+}
+
+func (c *Compiler) readI32s() int32 {
+ v, n, err := leb128.LoadInt32(c.wasmFunctionBody[c.loweringState.pc+1:])
+ if err != nil {
+ panic(err) // shouldn't be reached since compilation comes after validation.
+ }
+ c.loweringState.pc += int(n)
+ return v
+}
+
+func (c *Compiler) readI64s() int64 {
+ v, n, err := leb128.LoadInt64(c.wasmFunctionBody[c.loweringState.pc+1:])
+ if err != nil {
+ panic(err) // shouldn't be reached since compilation comes after validation.
+ }
+ c.loweringState.pc += int(n)
+ return v
+}
+
+func (c *Compiler) readF32() float32 {
+ v := math.Float32frombits(binary.LittleEndian.Uint32(c.wasmFunctionBody[c.loweringState.pc+1:]))
+ c.loweringState.pc += 4
+ return v
+}
+
+func (c *Compiler) readF64() float64 {
+ v := math.Float64frombits(binary.LittleEndian.Uint64(c.wasmFunctionBody[c.loweringState.pc+1:]))
+ c.loweringState.pc += 8
+ return v
+}
+
+// readBlockType reads the block type from the current position of the bytecode reader.
+func (c *Compiler) readBlockType() *wasm.FunctionType {
+ state := c.state()
+
+ c.br.Reset(c.wasmFunctionBody[state.pc+1:])
+ bt, num, err := wasm.DecodeBlockType(c.m.TypeSection, c.br, api.CoreFeaturesV2)
+ if err != nil {
+ panic(err) // shouldn't be reached since compilation comes after validation.
+ }
+ state.pc += int(num)
+
+ return bt
+}
+
+func (c *Compiler) readMemArg() (align, offset uint32) {
+ state := c.state()
+
+ align, num, err := leb128.LoadUint32(c.wasmFunctionBody[state.pc+1:])
+ if err != nil {
+ panic(fmt.Errorf("read memory align: %v", err))
+ }
+
+ state.pc += int(num)
+ offset, num, err = leb128.LoadUint32(c.wasmFunctionBody[state.pc+1:])
+ if err != nil {
+ panic(fmt.Errorf("read memory offset: %v", err))
+ }
+
+ state.pc += int(num)
+ return align, offset
+}
+
+// insertJumpToBlock inserts a jump instruction to the given block in the current block.
+func (c *Compiler) insertJumpToBlock(args ssa.Values, targetBlk ssa.BasicBlock) {
+ if targetBlk.ReturnBlock() {
+ if c.needListener {
+ c.callListenerAfter()
+ }
+ }
+
+ builder := c.ssaBuilder
+ jmp := builder.AllocateInstruction()
+ jmp.AsJump(args, targetBlk)
+ builder.InsertInstruction(jmp)
+}
+
+func (c *Compiler) insertIntegerExtend(signed bool, from, to byte) {
+ state := c.state()
+ builder := c.ssaBuilder
+ v := state.pop()
+ extend := builder.AllocateInstruction()
+ if signed {
+ extend.AsSExtend(v, from, to)
+ } else {
+ extend.AsUExtend(v, from, to)
+ }
+ builder.InsertInstruction(extend)
+ value := extend.Return()
+ state.push(value)
+}
+
+func (c *Compiler) switchTo(originalStackLen int, targetBlk ssa.BasicBlock) {
+ if targetBlk.Preds() == 0 {
+ c.loweringState.unreachable = true
+ }
+
+ // Now we should adjust the stack and start translating the continuation block.
+ c.loweringState.values = c.loweringState.values[:originalStackLen]
+
+ c.ssaBuilder.SetCurrentBlock(targetBlk)
+
+ // At this point, blocks params consist only of the Wasm-level parameters,
+ // (since it's added only when we are trying to resolve variable *inside* this block).
+ for i := 0; i < targetBlk.Params(); i++ {
+ value := targetBlk.Param(i)
+ c.loweringState.push(value)
+ }
+}
+
+// results returns the number of results of the current function.
+func (c *Compiler) results() int {
+ return len(c.wasmFunctionTyp.Results)
+}
+
+func (c *Compiler) lowerBrTable(labels []uint32, index ssa.Value) {
+ state := c.state()
+ builder := c.ssaBuilder
+
+ f := state.ctrlPeekAt(int(labels[0]))
+ var numArgs int
+ if f.isLoop() {
+ numArgs = len(f.blockType.Params)
+ } else {
+ numArgs = len(f.blockType.Results)
+ }
+
+ targets := make([]ssa.BasicBlock, len(labels))
+
+ // We need trampoline blocks since depending on the target block structure, we might end up inserting moves before jumps,
+ // which cannot be done with br_table. Instead, we can do such per-block moves in the trampoline blocks.
+ // At the linking phase (very end of the backend), we can remove the unnecessary jumps, and therefore no runtime overhead.
+ currentBlk := builder.CurrentBlock()
+ for i, l := range labels {
+ // Args are always on the top of the stack. Note that we should not share the args slice
+ // among the jump instructions since the args are modified during passes (e.g. redundant phi elimination).
+ args := c.nPeekDup(numArgs)
+ targetBlk, _ := state.brTargetArgNumFor(l)
+ trampoline := builder.AllocateBasicBlock()
+ builder.SetCurrentBlock(trampoline)
+ c.insertJumpToBlock(args, targetBlk)
+ targets[i] = trampoline
+ }
+ builder.SetCurrentBlock(currentBlk)
+
+ // If the target block has no arguments, we can just jump to the target block.
+ brTable := builder.AllocateInstruction()
+ brTable.AsBrTable(index, targets)
+ builder.InsertInstruction(brTable)
+
+ for _, trampoline := range targets {
+ builder.Seal(trampoline)
+ }
+}
+
+func (l *loweringState) brTargetArgNumFor(labelIndex uint32) (targetBlk ssa.BasicBlock, argNum int) {
+ targetFrame := l.ctrlPeekAt(int(labelIndex))
+ if targetFrame.isLoop() {
+ targetBlk, argNum = targetFrame.blk, len(targetFrame.blockType.Params)
+ } else {
+ targetBlk, argNum = targetFrame.followingBlock, len(targetFrame.blockType.Results)
+ }
+ return
+}
+
+func (c *Compiler) callListenerBefore() {
+ c.storeCallerModuleContext()
+
+ builder := c.ssaBuilder
+ beforeListeners1stElement := builder.AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue,
+ c.offset.BeforeListenerTrampolines1stElement.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ beforeListenerPtr := builder.AllocateInstruction().
+ AsLoad(beforeListeners1stElement, uint32(c.wasmFunctionTypeIndex)*8 /* 8 bytes per index */, ssa.TypeI64).Insert(builder).Return()
+
+ entry := builder.EntryBlock()
+ ps := entry.Params()
+
+ args := c.allocateVarLengthValues(ps, c.execCtxPtrValue,
+ builder.AllocateInstruction().AsIconst32(c.wasmLocalFunctionIndex).Insert(builder).Return())
+ for i := 2; i < ps; i++ {
+ args = args.Append(builder.VarLengthPool(), entry.Param(i))
+ }
+
+ beforeSig := c.listenerSignatures[c.wasmFunctionTyp][0]
+ builder.AllocateInstruction().
+ AsCallIndirect(beforeListenerPtr, beforeSig, args).
+ Insert(builder)
+}
+
+func (c *Compiler) callListenerAfter() {
+ c.storeCallerModuleContext()
+
+ builder := c.ssaBuilder
+ afterListeners1stElement := builder.AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue,
+ c.offset.AfterListenerTrampolines1stElement.U32(),
+ ssa.TypeI64,
+ ).Insert(builder).Return()
+
+ afterListenerPtr := builder.AllocateInstruction().
+ AsLoad(afterListeners1stElement,
+ uint32(c.wasmFunctionTypeIndex)*8 /* 8 bytes per index */, ssa.TypeI64).
+ Insert(builder).
+ Return()
+
+ afterSig := c.listenerSignatures[c.wasmFunctionTyp][1]
+ args := c.allocateVarLengthValues(
+ c.results()+2,
+ c.execCtxPtrValue,
+ builder.AllocateInstruction().AsIconst32(c.wasmLocalFunctionIndex).Insert(builder).Return(),
+ )
+
+ l := c.state()
+ tail := len(l.values)
+ args = args.Append(c.ssaBuilder.VarLengthPool(), l.values[tail-c.results():tail]...)
+ builder.AllocateInstruction().
+ AsCallIndirect(afterListenerPtr, afterSig, args).
+ Insert(builder)
+}
+
+const (
+ elementOrDataInstanceLenOffset = 8
+ elementOrDataInstanceSize = 24
+)
+
+// dropInstance inserts instructions to drop the element/data instance specified by the given index.
+func (c *Compiler) dropDataOrElementInstance(index uint32, firstItemOffset wazevoapi.Offset) {
+ builder := c.ssaBuilder
+ instPtr := c.dataOrElementInstanceAddr(index, firstItemOffset)
+
+ zero := builder.AllocateInstruction().AsIconst64(0).Insert(builder).Return()
+
+ // Clear the instance.
+ builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, 0).Insert(builder)
+ builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, elementOrDataInstanceLenOffset).Insert(builder)
+ builder.AllocateInstruction().AsStore(ssa.OpcodeStore, zero, instPtr, elementOrDataInstanceLenOffset+8).Insert(builder)
+}
+
+func (c *Compiler) dataOrElementInstanceAddr(index uint32, firstItemOffset wazevoapi.Offset) ssa.Value {
+ builder := c.ssaBuilder
+
+ _1stItemPtr := builder.
+ AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue, firstItemOffset.U32(), ssa.TypeI64).
+ Insert(builder).Return()
+
+ // Each data/element instance is a slice, so we need to multiply index by 16 to get the offset of the target instance.
+ index = index * elementOrDataInstanceSize
+ indexExt := builder.AllocateInstruction().AsIconst64(uint64(index)).Insert(builder).Return()
+ // Then, add the offset to the address of the instance.
+ instPtr := builder.AllocateInstruction().AsIadd(_1stItemPtr, indexExt).Insert(builder).Return()
+ return instPtr
+}
+
+func (c *Compiler) boundsCheckInDataOrElementInstance(instPtr, offsetInInstance, copySize ssa.Value, exitCode wazevoapi.ExitCode) {
+ builder := c.ssaBuilder
+ dataInstLen := builder.AllocateInstruction().
+ AsLoad(instPtr, elementOrDataInstanceLenOffset, ssa.TypeI64).
+ Insert(builder).Return()
+ ceil := builder.AllocateInstruction().AsIadd(offsetInInstance, copySize).Insert(builder).Return()
+ cmp := builder.AllocateInstruction().
+ AsIcmp(dataInstLen, ceil, ssa.IntegerCmpCondUnsignedLessThan).
+ Insert(builder).
+ Return()
+ builder.AllocateInstruction().
+ AsExitIfTrueWithCode(c.execCtxPtrValue, cmp, exitCode).
+ Insert(builder)
+}
+
+func (c *Compiler) boundsCheckInTable(tableIndex uint32, offset, size ssa.Value) (tableInstancePtr ssa.Value) {
+ builder := c.ssaBuilder
+ dstCeil := builder.AllocateInstruction().AsIadd(offset, size).Insert(builder).Return()
+
+ // Load the table.
+ tableInstancePtr = builder.AllocateInstruction().
+ AsLoad(c.moduleCtxPtrValue, c.offset.TableOffset(int(tableIndex)).U32(), ssa.TypeI64).
+ Insert(builder).Return()
+
+ // Load the table's length.
+ tableLen := builder.AllocateInstruction().
+ AsLoad(tableInstancePtr, tableInstanceLenOffset, ssa.TypeI32).Insert(builder).Return()
+ tableLenExt := builder.AllocateInstruction().AsUExtend(tableLen, 32, 64).Insert(builder).Return()
+
+ // Compare the length and the target, and trap if out of bounds.
+ checkOOB := builder.AllocateInstruction()
+ checkOOB.AsIcmp(tableLenExt, dstCeil, ssa.IntegerCmpCondUnsignedLessThan)
+ builder.InsertInstruction(checkOOB)
+ exitIfOOB := builder.AllocateInstruction()
+ exitIfOOB.AsExitIfTrueWithCode(c.execCtxPtrValue, checkOOB.Return(), wazevoapi.ExitCodeTableOutOfBounds)
+ builder.InsertInstruction(exitIfOOB)
+ return
+}
+
+func (c *Compiler) loadTableBaseAddr(tableInstancePtr ssa.Value) ssa.Value {
+ builder := c.ssaBuilder
+ loadTableBaseAddress := builder.
+ AllocateInstruction().
+ AsLoad(tableInstancePtr, tableInstanceBaseAddressOffset, ssa.TypeI64).
+ Insert(builder)
+ return loadTableBaseAddress.Return()
+}
+
+func (c *Compiler) boundsCheckInMemory(memLen, offset, size ssa.Value) {
+ builder := c.ssaBuilder
+ ceil := builder.AllocateInstruction().AsIadd(offset, size).Insert(builder).Return()
+ cmp := builder.AllocateInstruction().
+ AsIcmp(memLen, ceil, ssa.IntegerCmpCondUnsignedLessThan).
+ Insert(builder).
+ Return()
+ builder.AllocateInstruction().
+ AsExitIfTrueWithCode(c.execCtxPtrValue, cmp, wazevoapi.ExitCodeMemoryOutOfBounds).
+ Insert(builder)
+}