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 struct {
values []ssa .Value
controlFrames []controlFrame
unreachable bool
unreachableDepth int
tmpForBrTable []uint32
pc int
}
controlFrame struct {
kind controlFrameKind
originalStackLenWithoutParam int
blk,
followingBlock ssa .BasicBlock
blockType *wasm .FunctionType
clonedArgs ssa .Values
}
controlFrameKind byte
)
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
)
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 )
}
}
func (ctrl *controlFrame ) isLoop () bool {
return ctrl .kind == controlFrameKindLoop
}
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 ]
}
func (c *Compiler ) lowerBody (entryBlk ssa .BasicBlock ) {
c .ssaBuilder .Seal (entryBlk )
if c .needListener {
c .callListenerBefore ()
}
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 {
c .finalizeKnownSafeBoundsAtTheEndOfBlock (blkBeforeLowering .ID ())
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 ++
miscOpUint , num , err := leb128 .LoadUint32 (c .wasmFunctionBody [state .pc :])
if err != nil {
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
}
loadTableInstancePtr := builder .AllocateInstruction ()
loadTableInstancePtr .AsLoad (c .moduleCtxPtrValue , c .offset .TableOffset (int (tableIndex )).U32 (), ssa .TypeI64 )
builder .InsertInstruction (loadTableInstancePtr )
tableInstancePtr := loadTableInstancePtr .Return ()
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 ()
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
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 ()
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 ()
tableBaseAddr := c .loadTableBaseAddr (tableInstancePtr )
addr := builder .AllocateInstruction ().AsIadd (tableBaseAddr , offsetInBytes ).Insert (builder ).Return ()
beforeLoop := builder .AllocateBasicBlock ()
loopBlk := builder .AllocateBasicBlock ()
loopVar := loopBlk .AddParam (builder , ssa .TypeI64 )
followingBlk := builder .AllocateBasicBlock ()
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 )
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 ()
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 ++
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 ()
c .boundsCheckInMemory (c .getMemoryLenValue (false ), offset , fillSize )
addr := builder .AllocateInstruction ().AsIadd (c .getMemoryBaseValue (false ), offset ).Insert (builder ).Return ()
beforeLoop := builder .AllocateBasicBlock ()
loopBlk := builder .AllocateBasicBlock ()
loopVar := loopBlk .AddParam (builder , ssa .TypeI64 )
followingBlk := builder .AllocateBasicBlock ()
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 )
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 ()
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 ++
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 )
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 )
tableInstancePtr := c .boundsCheckInTable (tableIndex , offsetInTable , copySize )
c .boundsCheckInDataOrElementInstance (elemInstPtr , offsetInElementInstance , copySize , wazevoapi .ExitCodeTableOutOfBounds )
three := builder .AllocateInstruction ().AsIconst64 (3 ).Insert (builder ).Return ()
tableOffsetInBytes := builder .AllocateInstruction ().AsIshl (offsetInTable , three ).Insert (builder ).Return ()
tableBaseAddr := c .loadTableBaseAddr (tableInstancePtr )
dstAddr := builder .AllocateInstruction ().AsIadd (tableBaseAddr , tableOffsetInBytes ).Insert (builder ).Return ()
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 )
state .push (builder .MustFindValue (variable ))
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
}
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 ++
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 ++
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 )
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 :
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 :]...)
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 ()
c .addBlockParamsFromWasmTypes (bt .Results , followingBlk )
args := c .allocateVarLengthValues (len (bt .Params ))
args = args .Append (builder .VarLengthPool (), state .values [len (state .values )-len (bt .Params ):]...)
brz := builder .AllocateInstruction ()
brz .AsBrz (v , ssa .ValuesNil , elseBlk )
builder .InsertInstruction (brz )
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 )
builder .Seal (thenBlk )
builder .Seal (elseBlk )
case wasm .OpcodeElse :
ifctrl := state .ctrlPeekAt (0 )
if unreachable := state .unreachable ; unreachable && state .unreachableDepth > 0 {
break
}
ifctrl .kind = controlFrameKindIfWithElse
if !state .unreachable {
followingBlk := ifctrl .followingBlock
args := c .nPeekDup (len (ifctrl .blockType .Results ))
c .insertJumpToBlock (args , followingBlk )
} else {
state .unreachable = false
}
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 {
args := c .nPeekDup (len (ctrl .blockType .Results ))
c .insertJumpToBlock (args , followingBlk )
} else {
state .unreachable = false
}
switch ctrl .kind {
case controlFrameKindFunction :
break
case controlFrameKindLoop :
builder .Seal (ctrl .blk )
case controlFrameKindIfWithoutElse :
elseBlk := ctrl .blk
builder .SetCurrentBlock (elseBlk )
c .insertJumpToBlock (ctrl .clonedArgs , followingBlk )
}
builder .Seal (followingBlk )
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 () {
current := builder .CurrentBlock ()
targetBlk = builder .AllocateBasicBlock ()
builder .SetCurrentBlock (targetBlk )
sealTargetBlk = true
c .callListenerAfter ()
instr := builder .AllocateInstruction ()
instr .AsReturn (args )
builder .InsertInstruction (instr )
args = ssa .ValuesNil
builder .SetCurrentBlock (current )
}
brnz := builder .AllocateInstruction ()
brnz .AsBrnz (v , args , targetBlk )
builder .InsertInstruction (brnz )
if sealTargetBlk {
builder .Seal (targetBlk )
}
elseBlk := builder .AllocateBasicBlock ()
c .insertJumpToBlock (ssa .ValuesNil , elseBlk )
builder .Seal (elseBlk )
builder .SetCurrentBlock (elseBlk )
case wasm .OpcodeBrTable :
labels := state .tmpForBrTable [:0 ]
labelCount := c .readI32u ()
for i := 0 ; i < int (labelCount ); i ++ {
labels = append (labels , c .readI32u ())
}
labels = append (labels , c .readI32u ())
if state .unreachable {
break
}
index := state .pop ()
if labelCount == 0 {
targetBlk , argNum := state .brTargetArgNumFor (labels [0 ])
args := c .nPeekDup (argNum )
c .insertJumpToBlock (args , targetBlk )
} else {
c .lowerBrTable (labels , index )
}
state .tmpForBrTable = labels
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 {
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 )
args = args .Append (builder .VarLengthPool (), vs ...)
call .AsCall (FunctionIndexToFuncRef (fnIndex ), sig , args )
builder .InsertInstruction (call )
} else {
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" {
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 ++
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 ++
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 {
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
loadTableInstancePtr := builder .AllocateInstruction ()
loadTableInstancePtr .AsLoad (c .moduleCtxPtrValue , c .offset .TableOffset (int (tableIndex )).U32 (), ssa .TypeI64 )
builder .InsertInstruction (loadTableInstancePtr )
tableInstancePtr := loadTableInstancePtr .Return ()
loadTableLen := builder .AllocateInstruction ()
loadTableLen .AsLoad (tableInstancePtr , tableInstanceLenOffset , ssa .TypeI32 )
builder .InsertInstruction (loadTableLen )
tableLen := loadTableLen .Return ()
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 )
loadTableBaseAddress := builder .AllocateInstruction ()
loadTableBaseAddress .AsLoad (tableInstancePtr , tableInstanceBaseAddressOffset , ssa .TypeI64 )
builder .InsertInstruction (loadTableBaseAddress )
tableBase := loadTableBaseAddress .Return ()
multiplyBy8 := builder .AllocateInstruction ()
three := builder .AllocateInstruction ()
three .AsIconst64 (3 )
builder .InsertInstruction (three )
multiplyBy8 .AsIshl (elementOffsetInTable , three .Return ())
builder .InsertInstruction (multiplyBy8 )
targetOffsetInTableMultipliedBy8 := multiplyBy8 .Return ()
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 ()
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 )
loadTypeID := builder .AllocateInstruction ()
loadTypeID .AsLoad (functionInstancePtr , wazevoapi .FunctionInstanceTypeIDOffset , ssa .TypeI32 )
builder .InsertInstruction (loadTypeID )
actualTypeID := loadTypeID .Return ()
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 , ssa .TypeI32 )
builder .InsertInstruction (loadExpectedTypeID )
expectedTypeID := loadExpectedTypeID .Return ()
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 )
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 ...)
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 ()
}
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 () {
address = known .absoluteAddr
if ceil <= known .bound {
if !address .Valid () {
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
}
return
}
}
ceilConst := builder .AllocateInstruction ()
ceilConst .AsIconst64 (ceil )
builder .InsertInstruction (ceilConst )
extBaseAddr := builder .AllocateInstruction ().
AsUExtend (baseAddr , 32 , 64 ).
Insert (builder ).
Return ()
memLen := c .getMemoryLenValue (false )
baseAddrPlusCeil := builder .AllocateInstruction ()
baseAddrPlusCeil .AsIadd (extBaseAddr , ceilConst .Return ())
builder .InsertInstruction (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 )
if address == ssa .ValueInvalid {
memBase := c .getMemoryBaseValue (false )
address = builder .AllocateInstruction ().
AsIadd (memBase , extBaseAddr ).Insert (builder ).Return ()
}
c .recordKnownSafeBound (baseAddrID , ceil , address )
return
}
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
}
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 () {
if c .needMemory && !c .memoryShared {
c .reloadMemoryBaseLen ()
}
for _ , index := range c .mutableGlobalVariablesIndexes {
_ = c .getWasmGlobalValue (index , true )
}
}
func (c *Compiler ) reloadMemoryBaseLen () {
_ = c .getMemoryBaseValue (true )
_ = c .getMemoryLenValue (true )
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 )
}
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 )
}
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 )
}
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 )
}
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 )
}
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
}
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 )
}
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
}
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
}
c .loweringState .values = c .loweringState .values [:originalStackLen ]
c .ssaBuilder .SetCurrentBlock (targetBlk )
for i := 0 ; i < targetBlk .Params (); i ++ {
value := targetBlk .Param (i )
c .loweringState .push (value )
}
}
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 )
}
varPool := builder .VarLengthPool ()
trampolineBlockIDs := varPool .Allocate (len (labels ))
currentBlk := builder .CurrentBlock ()
for _ , l := range labels {
args := c .nPeekDup (numArgs )
targetBlk , _ := state .brTargetArgNumFor (l )
trampoline := builder .AllocateBasicBlock ()
builder .SetCurrentBlock (trampoline )
c .insertJumpToBlock (args , targetBlk )
trampolineBlockIDs = trampolineBlockIDs .Append (builder .VarLengthPool (), ssa .Value (trampoline .ID ()))
}
builder .SetCurrentBlock (currentBlk )
brTable := builder .AllocateInstruction ()
brTable .AsBrTable (index , trampolineBlockIDs )
builder .InsertInstruction (brTable )
for _ , trampolineID := range trampolineBlockIDs .View () {
builder .Seal (builder .BasicBlock (ssa .BasicBlockID (trampolineID )))
}
}
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 , 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 , 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
)
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 ()
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 ()
index = index * elementOrDataInstanceSize
indexExt := builder .AllocateInstruction ().AsIconst64 (uint64 (index )).Insert (builder ).Return ()
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 ()
tableInstancePtr = builder .AllocateInstruction ().
AsLoad (c .moduleCtxPtrValue , c .offset .TableOffset (int (tableIndex )).U32 (), ssa .TypeI64 ).
Insert (builder ).Return ()
tableLen := builder .AllocateInstruction ().
AsLoad (tableInstancePtr , tableInstanceLenOffset , ssa .TypeI32 ).Insert (builder ).Return ()
tableLenExt := builder .AllocateInstruction ().AsUExtend (tableLen , 32 , 64 ).Insert (builder ).Return ()
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 )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .