2024-05-27 15:46:15 +00:00
package interpreter
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"strings"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/wasm"
)
type controlFrameKind byte
const (
controlFrameKindBlockWithContinuationLabel controlFrameKind = iota
controlFrameKindBlockWithoutContinuationLabel
controlFrameKindFunction
controlFrameKindLoop
controlFrameKindIfWithElse
controlFrameKindIfWithoutElse
)
type (
controlFrame struct {
frameID uint32
2024-08-15 00:08:55 +00:00
// originalStackLenWithoutParam holds the number of values on the stack
2024-05-27 15:46:15 +00:00
// when Start executing this control frame minus params for the block.
originalStackLenWithoutParam int
2024-08-15 00:08:55 +00:00
// originalStackLenWithoutParamUint64 is almost the same as originalStackLenWithoutParam
// except that it holds the number of values on the stack in uint64.
originalStackLenWithoutParamUint64 int
blockType * wasm . FunctionType
kind controlFrameKind
2024-05-27 15:46:15 +00:00
}
controlFrames struct { frames [ ] controlFrame }
)
func ( c * controlFrame ) ensureContinuation ( ) {
// Make sure that if the frame is block and doesn't have continuation,
// change the Kind so we can emit the continuation block
// later when we reach the End instruction of this frame.
if c . kind == controlFrameKindBlockWithoutContinuationLabel {
c . kind = controlFrameKindBlockWithContinuationLabel
}
}
func ( c * controlFrame ) asLabel ( ) label {
switch c . kind {
case controlFrameKindBlockWithContinuationLabel ,
controlFrameKindBlockWithoutContinuationLabel :
return newLabel ( labelKindContinuation , c . frameID )
case controlFrameKindLoop :
return newLabel ( labelKindHeader , c . frameID )
case controlFrameKindFunction :
return newLabel ( labelKindReturn , 0 )
case controlFrameKindIfWithElse ,
controlFrameKindIfWithoutElse :
return newLabel ( labelKindContinuation , c . frameID )
}
panic ( fmt . Sprintf ( "unreachable: a bug in interpreterir implementation: %v" , c . kind ) )
}
func ( c * controlFrames ) functionFrame ( ) * controlFrame {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase.
return & c . frames [ 0 ]
}
func ( c * controlFrames ) get ( n int ) * controlFrame {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase.
return & c . frames [ len ( c . frames ) - n - 1 ]
}
func ( c * controlFrames ) top ( ) * controlFrame {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase.
return & c . frames [ len ( c . frames ) - 1 ]
}
func ( c * controlFrames ) empty ( ) bool {
return len ( c . frames ) == 0
}
func ( c * controlFrames ) pop ( ) ( frame * controlFrame ) {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase.
frame = c . top ( )
c . frames = c . frames [ : len ( c . frames ) - 1 ]
return
}
func ( c * controlFrames ) push ( frame controlFrame ) {
c . frames = append ( c . frames , frame )
}
func ( c * compiler ) initializeStack ( ) {
// Reuse the existing slice.
c . localIndexToStackHeightInUint64 = c . localIndexToStackHeightInUint64 [ : 0 ]
var current int
for _ , lt := range c . sig . Params {
c . localIndexToStackHeightInUint64 = append ( c . localIndexToStackHeightInUint64 , current )
if lt == wasm . ValueTypeV128 {
current ++
}
current ++
}
if c . callFrameStackSizeInUint64 > 0 {
// We reserve the stack slots for result values below the return call frame slots.
if diff := c . sig . ResultNumInUint64 - c . sig . ParamNumInUint64 ; diff > 0 {
current += diff
}
}
// Non-func param locals Start after the return call frame.
current += c . callFrameStackSizeInUint64
for _ , lt := range c . localTypes {
c . localIndexToStackHeightInUint64 = append ( c . localIndexToStackHeightInUint64 , current )
if lt == wasm . ValueTypeV128 {
current ++
}
current ++
}
// Push function arguments.
for _ , t := range c . sig . Params {
c . stackPush ( wasmValueTypeTounsignedType ( t ) )
}
if c . callFrameStackSizeInUint64 > 0 {
// Reserve the stack slots for results.
for i := 0 ; i < c . sig . ResultNumInUint64 - c . sig . ParamNumInUint64 ; i ++ {
c . stackPush ( unsignedTypeI64 )
}
// Reserve the stack slots for call frame.
for i := 0 ; i < c . callFrameStackSizeInUint64 ; i ++ {
c . stackPush ( unsignedTypeI64 )
}
}
}
// compiler is in charge of lowering raw Wasm function body to get compilationResult.
// This is created per *wasm.Module and reused for all functions in it to reduce memory allocations.
type compiler struct {
module * wasm . Module
enabledFeatures api . CoreFeatures
callFrameStackSizeInUint64 int
stack [ ] unsignedType
2024-08-15 00:08:55 +00:00
// stackLenInUint64 is the length of the stack in uint64.
stackLenInUint64 int
currentFrameID uint32
controlFrames controlFrames
unreachableState struct {
2024-05-27 15:46:15 +00:00
on bool
depth int
}
pc , currentOpPC uint64
result compilationResult
// body holds the code for the function's body where Wasm instructions are stored.
body [ ] byte
// sig is the function type of the target function.
sig * wasm . FunctionType
// localTypes holds the target function locals' value types except function params.
localTypes [ ] wasm . ValueType
// localIndexToStackHeightInUint64 maps the local index (starting with function params) to the stack height
// where the local is places. This is the necessary mapping for functions who contain vector type locals.
localIndexToStackHeightInUint64 [ ] int
// types hold all the function types in the module where the targe function exists.
types [ ] wasm . FunctionType
// funcs holds the type indexes for all declared functions in the module where the target function exists.
funcs [ ] uint32
// globals holds the global types for all declared globals in the module where the target function exists.
globals [ ] wasm . GlobalType
// needSourceOffset is true if this module requires DWARF based stack trace.
needSourceOffset bool
// bodyOffsetInCodeSection is the offset of the body of this function in the original Wasm binary's code section.
bodyOffsetInCodeSection uint64
ensureTermination bool
// Pre-allocated bytes.Reader to be used in various places.
br * bytes . Reader
funcTypeToSigs funcTypeToIRSignatures
next int
}
//lint:ignore U1000 for debugging only.
func ( c * compiler ) stackDump ( ) string {
strs := make ( [ ] string , 0 , len ( c . stack ) )
for _ , s := range c . stack {
strs = append ( strs , s . String ( ) )
}
return "[" + strings . Join ( strs , ", " ) + "]"
}
func ( c * compiler ) markUnreachable ( ) {
c . unreachableState . on = true
}
func ( c * compiler ) resetUnreachable ( ) {
c . unreachableState . on = false
}
// memoryType is the type of memory in a compiled module.
type memoryType byte
const (
// memoryTypeNone indicates there is no memory.
memoryTypeNone memoryType = iota
// memoryTypeStandard indicates there is a non-shared memory.
memoryTypeStandard
// memoryTypeShared indicates there is a shared memory.
memoryTypeShared
)
type compilationResult struct {
// Operations holds interpreterir operations compiled from Wasm instructions in a Wasm function.
Operations [ ] unionOperation
// IROperationSourceOffsetsInWasmBinary is index-correlated with Operation and maps each operation to the corresponding source instruction's
// offset in the original WebAssembly binary.
// Non nil only when the given Wasm module has the DWARF section.
IROperationSourceOffsetsInWasmBinary [ ] uint64
// LabelCallers maps label to the number of callers to that label.
// Here "callers" means that the call-sites which jumps to the label with br, br_if or br_table
// instructions.
//
// Note: zero possible and allowed in wasm. e.g.
//
// (block
// (br 0)
// (block i32.const 1111)
// )
//
// This example the label corresponding to `(block i32.const 1111)` is never be reached at runtime because `br 0` exits the function before we reach there
LabelCallers map [ label ] uint32
// UsesMemory is true if this function might use memory.
UsesMemory bool
// The following fields are per-module values, not per-function.
// Globals holds all the declarations of globals in the module from which this function is compiled.
Globals [ ] wasm . GlobalType
// Functions holds all the declarations of function in the module from which this function is compiled, including itself.
Functions [ ] wasm . Index
// Types holds all the types in the module from which this function is compiled.
Types [ ] wasm . FunctionType
// Memory indicates the type of memory of the module.
Memory memoryType
// HasTable is true if the module from which this function is compiled has table declaration.
HasTable bool
// HasDataInstances is true if the module has data instances which might be used by memory.init or data.drop instructions.
HasDataInstances bool
// HasDataInstances is true if the module has element instances which might be used by table.init or elem.drop instructions.
HasElementInstances bool
}
// newCompiler returns the new *compiler for the given parameters.
// Use compiler.Next function to get compilation result per function.
func newCompiler ( enabledFeatures api . CoreFeatures , callFrameStackSizeInUint64 int , module * wasm . Module , ensureTermination bool ) ( * compiler , error ) {
functions , globals , mem , tables , err := module . AllDeclarations ( )
if err != nil {
return nil , err
}
hasTable , hasDataInstances , hasElementInstances := len ( tables ) > 0 ,
len ( module . DataSection ) > 0 , len ( module . ElementSection ) > 0
var mt memoryType
switch {
case mem == nil :
mt = memoryTypeNone
case mem . IsShared :
mt = memoryTypeShared
default :
mt = memoryTypeStandard
}
types := module . TypeSection
c := & compiler {
module : module ,
enabledFeatures : enabledFeatures ,
controlFrames : controlFrames { } ,
callFrameStackSizeInUint64 : callFrameStackSizeInUint64 ,
result : compilationResult {
Globals : globals ,
Functions : functions ,
Types : types ,
Memory : mt ,
HasTable : hasTable ,
HasDataInstances : hasDataInstances ,
HasElementInstances : hasElementInstances ,
LabelCallers : map [ label ] uint32 { } ,
} ,
globals : globals ,
funcs : functions ,
types : types ,
ensureTermination : ensureTermination ,
br : bytes . NewReader ( nil ) ,
funcTypeToSigs : funcTypeToIRSignatures {
indirectCalls : make ( [ ] * signature , len ( types ) ) ,
directCalls : make ( [ ] * signature , len ( types ) ) ,
wasmTypes : types ,
} ,
needSourceOffset : module . DWARFLines != nil ,
}
return c , nil
}
// Next returns the next compilationResult for this compiler.
func ( c * compiler ) Next ( ) ( * compilationResult , error ) {
funcIndex := c . next
code := & c . module . CodeSection [ funcIndex ]
sig := & c . types [ c . module . FunctionSection [ funcIndex ] ]
// Reset the previous result.
c . result . Operations = c . result . Operations [ : 0 ]
c . result . IROperationSourceOffsetsInWasmBinary = c . result . IROperationSourceOffsetsInWasmBinary [ : 0 ]
c . result . UsesMemory = false
// Clears the existing entries in LabelCallers.
for frameID := uint32 ( 0 ) ; frameID <= c . currentFrameID ; frameID ++ {
for k := labelKind ( 0 ) ; k < labelKindNum ; k ++ {
delete ( c . result . LabelCallers , newLabel ( k , frameID ) )
}
}
// Reset the previous states.
c . pc = 0
c . currentOpPC = 0
c . currentFrameID = 0
2024-08-15 00:08:55 +00:00
c . stackLenInUint64 = 0
2024-05-27 15:46:15 +00:00
c . unreachableState . on , c . unreachableState . depth = false , 0
if err := c . compile ( sig , code . Body , code . LocalTypes , code . BodyOffsetInCodeSection ) ; err != nil {
return nil , err
}
c . next ++
return & c . result , nil
}
// Compile lowers given function instance into interpreterir operations
// so that the resulting operations can be consumed by the interpreter
// or the compiler compilation engine.
func ( c * compiler ) compile ( sig * wasm . FunctionType , body [ ] byte , localTypes [ ] wasm . ValueType , bodyOffsetInCodeSection uint64 ) error {
// Set function specific fields.
c . body = body
c . localTypes = localTypes
c . sig = sig
c . bodyOffsetInCodeSection = bodyOffsetInCodeSection
// Reuses the underlying slices.
c . stack = c . stack [ : 0 ]
c . controlFrames . frames = c . controlFrames . frames [ : 0 ]
c . initializeStack ( )
// Emit const expressions for locals.
// Note that here we don't take function arguments
// into account, meaning that callers must push
// arguments before entering into the function body.
for _ , t := range c . localTypes {
c . emitDefaultValue ( t )
}
// Insert the function control frame.
c . controlFrames . push ( controlFrame {
frameID : c . nextFrameID ( ) ,
blockType : c . sig ,
kind : controlFrameKindFunction ,
} )
// Now, enter the function body.
for ! c . controlFrames . empty ( ) && c . pc < uint64 ( len ( c . body ) ) {
if err := c . handleInstruction ( ) ; err != nil {
return fmt . Errorf ( "handling instruction: %w" , err )
}
}
return nil
}
// Translate the current Wasm instruction to interpreterir's operations,
// and emit the results into c.results.
func ( c * compiler ) handleInstruction ( ) error {
op := c . body [ c . pc ]
c . currentOpPC = c . pc
if false {
var instName string
if op == wasm . OpcodeVecPrefix {
instName = wasm . VectorInstructionName ( c . body [ c . pc + 1 ] )
} else if op == wasm . OpcodeAtomicPrefix {
instName = wasm . AtomicInstructionName ( c . body [ c . pc + 1 ] )
} else if op == wasm . OpcodeMiscPrefix {
instName = wasm . MiscInstructionName ( c . body [ c . pc + 1 ] )
} else {
instName = wasm . InstructionName ( op )
}
fmt . Printf ( "handling %s, unreachable_state(on=%v,depth=%d), stack=%v\n" ,
instName , c . unreachableState . on , c . unreachableState . depth , c . stack ,
)
}
var peekValueType unsignedType
if len ( c . stack ) > 0 {
peekValueType = c . stackPeek ( )
}
// Modify the stack according the current instruction.
// Note that some instructions will read "index" in
// applyToStack and advance c.pc inside the function.
index , err := c . applyToStack ( op )
if err != nil {
return fmt . Errorf ( "apply stack failed for %s: %w" , wasm . InstructionName ( op ) , err )
}
// Now we handle each instruction, and
// emit the corresponding interpreterir operations to the results.
operatorSwitch :
switch op {
case wasm . OpcodeUnreachable :
c . emit ( newOperationUnreachable ( ) )
c . markUnreachable ( )
case wasm . OpcodeNop :
// Nop is noop!
case wasm . OpcodeBlock :
c . br . Reset ( c . body [ c . pc + 1 : ] )
bt , num , err := wasm . DecodeBlockType ( c . types , c . br , c . enabledFeatures )
if err != nil {
return fmt . Errorf ( "reading block type for block instruction: %w" , err )
}
c . pc += num
if c . unreachableState . on {
// If it is currently in unreachable,
// just remove the entire block.
c . unreachableState . depth ++
break operatorSwitch
}
// Create a new frame -- entering this block.
frame := controlFrame {
2024-08-15 00:08:55 +00:00
frameID : c . nextFrameID ( ) ,
originalStackLenWithoutParam : len ( c . stack ) - len ( bt . Params ) ,
originalStackLenWithoutParamUint64 : c . stackLenInUint64 - bt . ParamNumInUint64 ,
kind : controlFrameKindBlockWithoutContinuationLabel ,
blockType : bt ,
2024-05-27 15:46:15 +00:00
}
c . controlFrames . push ( frame )
case wasm . OpcodeLoop :
c . br . Reset ( c . body [ c . pc + 1 : ] )
bt , num , err := wasm . DecodeBlockType ( c . types , c . br , c . enabledFeatures )
if err != nil {
return fmt . Errorf ( "reading block type for loop instruction: %w" , err )
}
c . pc += num
if c . unreachableState . on {
// If it is currently in unreachable,
// just remove the entire block.
c . unreachableState . depth ++
break operatorSwitch
}
// Create a new frame -- entering loop.
frame := controlFrame {
2024-08-15 00:08:55 +00:00
frameID : c . nextFrameID ( ) ,
originalStackLenWithoutParam : len ( c . stack ) - len ( bt . Params ) ,
originalStackLenWithoutParamUint64 : c . stackLenInUint64 - bt . ParamNumInUint64 ,
kind : controlFrameKindLoop ,
blockType : bt ,
2024-05-27 15:46:15 +00:00
}
c . controlFrames . push ( frame )
// Prep labels for inside and the continuation of this loop.
loopLabel := newLabel ( labelKindHeader , frame . frameID )
c . result . LabelCallers [ loopLabel ] ++
// Emit the branch operation to enter inside the loop.
c . emit ( newOperationBr ( loopLabel ) )
c . emit ( newOperationLabel ( loopLabel ) )
// Insert the exit code check on the loop header, which is the only necessary point in the function body
// to prevent infinite loop.
//
// Note that this is a little aggressive: this checks the exit code regardless the loop header is actually
// the loop. In other words, this checks even when no br/br_if/br_table instructions jumping to this loop
// exist. However, in reality, that shouldn't be an issue since such "noop" loop header will highly likely be
// optimized out by almost all guest language compilers which have the control flow optimization passes.
if c . ensureTermination {
c . emit ( newOperationBuiltinFunctionCheckExitCode ( ) )
}
case wasm . OpcodeIf :
c . br . Reset ( c . body [ c . pc + 1 : ] )
bt , num , err := wasm . DecodeBlockType ( c . types , c . br , c . enabledFeatures )
if err != nil {
return fmt . Errorf ( "reading block type for if instruction: %w" , err )
}
c . pc += num
if c . unreachableState . on {
// If it is currently in unreachable,
// just remove the entire block.
c . unreachableState . depth ++
break operatorSwitch
}
// Create a new frame -- entering if.
frame := controlFrame {
2024-08-15 00:08:55 +00:00
frameID : c . nextFrameID ( ) ,
originalStackLenWithoutParam : len ( c . stack ) - len ( bt . Params ) ,
originalStackLenWithoutParamUint64 : c . stackLenInUint64 - bt . ParamNumInUint64 ,
2024-05-27 15:46:15 +00:00
// Note this will be set to controlFrameKindIfWithElse
// when else opcode found later.
kind : controlFrameKindIfWithoutElse ,
blockType : bt ,
}
c . controlFrames . push ( frame )
// Prep labels for if and else of this if.
thenLabel := newLabel ( labelKindHeader , frame . frameID )
elseLabel := newLabel ( labelKindElse , frame . frameID )
c . result . LabelCallers [ thenLabel ] ++
c . result . LabelCallers [ elseLabel ] ++
// Emit the branch operation to enter the then block.
c . emit ( newOperationBrIf ( thenLabel , elseLabel , nopinclusiveRange ) )
c . emit ( newOperationLabel ( thenLabel ) )
case wasm . OpcodeElse :
frame := c . controlFrames . top ( )
if c . unreachableState . on && c . unreachableState . depth > 0 {
// If it is currently in unreachable, and the nested if,
// just remove the entire else block.
break operatorSwitch
} else if c . unreachableState . on {
// If it is currently in unreachable, and the non-nested if,
// reset the stack so we can correctly handle the else block.
top := c . controlFrames . top ( )
2024-08-15 00:08:55 +00:00
c . stackSwitchAt ( top )
2024-05-27 15:46:15 +00:00
top . kind = controlFrameKindIfWithElse
// Re-push the parameters to the if block so that else block can use them.
for _ , t := range frame . blockType . Params {
c . stackPush ( wasmValueTypeTounsignedType ( t ) )
}
// We are no longer unreachable in else frame,
// so emit the correct label, and reset the unreachable state.
elseLabel := newLabel ( labelKindElse , frame . frameID )
c . resetUnreachable ( )
c . emit (
newOperationLabel ( elseLabel ) ,
)
break operatorSwitch
}
// Change the Kind of this If block, indicating that
// the if has else block.
frame . kind = controlFrameKindIfWithElse
// We need to reset the stack so that
// the values pushed inside the then block
// do not affect the else block.
dropOp := newOperationDrop ( c . getFrameDropRange ( frame , false ) )
// Reset the stack manipulated by the then block, and re-push the block param types to the stack.
2024-08-15 00:08:55 +00:00
c . stackSwitchAt ( frame )
2024-05-27 15:46:15 +00:00
for _ , t := range frame . blockType . Params {
c . stackPush ( wasmValueTypeTounsignedType ( t ) )
}
// Prep labels for else and the continuation of this if block.
elseLabel := newLabel ( labelKindElse , frame . frameID )
continuationLabel := newLabel ( labelKindContinuation , frame . frameID )
c . result . LabelCallers [ continuationLabel ] ++
// Emit the instructions for exiting the if loop,
// and then the initiation of else block.
c . emit ( dropOp )
// Jump to the continuation of this block.
c . emit ( newOperationBr ( continuationLabel ) )
// Initiate the else block.
c . emit ( newOperationLabel ( elseLabel ) )
case wasm . OpcodeEnd :
if c . unreachableState . on && c . unreachableState . depth > 0 {
c . unreachableState . depth --
break operatorSwitch
} else if c . unreachableState . on {
c . resetUnreachable ( )
frame := c . controlFrames . pop ( )
if c . controlFrames . empty ( ) {
return nil
}
2024-08-15 00:08:55 +00:00
c . stackSwitchAt ( frame )
2024-05-27 15:46:15 +00:00
for _ , t := range frame . blockType . Results {
c . stackPush ( wasmValueTypeTounsignedType ( t ) )
}
continuationLabel := newLabel ( labelKindContinuation , frame . frameID )
if frame . kind == controlFrameKindIfWithoutElse {
// Emit the else label.
elseLabel := newLabel ( labelKindElse , frame . frameID )
c . result . LabelCallers [ continuationLabel ] ++
c . emit ( newOperationLabel ( elseLabel ) )
c . emit ( newOperationBr ( continuationLabel ) )
c . emit ( newOperationLabel ( continuationLabel ) )
} else {
c . emit (
newOperationLabel ( continuationLabel ) ,
)
}
break operatorSwitch
}
frame := c . controlFrames . pop ( )
// We need to reset the stack so that
// the values pushed inside the block.
dropOp := newOperationDrop ( c . getFrameDropRange ( frame , true ) )
2024-08-15 00:08:55 +00:00
c . stackSwitchAt ( frame )
2024-05-27 15:46:15 +00:00
// Push the result types onto the stack.
for _ , t := range frame . blockType . Results {
c . stackPush ( wasmValueTypeTounsignedType ( t ) )
}
// Emit the instructions according to the Kind of the current control frame.
switch frame . kind {
case controlFrameKindFunction :
if ! c . controlFrames . empty ( ) {
// Should never happen. If so, there's a bug in the translation.
panic ( "bug: found more function control frames" )
}
// Return from function.
c . emit ( dropOp )
c . emit ( newOperationBr ( newLabel ( labelKindReturn , 0 ) ) )
case controlFrameKindIfWithoutElse :
// This case we have to emit "empty" else label.
elseLabel := newLabel ( labelKindElse , frame . frameID )
continuationLabel := newLabel ( labelKindContinuation , frame . frameID )
c . result . LabelCallers [ continuationLabel ] += 2
c . emit ( dropOp )
c . emit ( newOperationBr ( continuationLabel ) )
// Emit the else which soon branches into the continuation.
c . emit ( newOperationLabel ( elseLabel ) )
c . emit ( newOperationBr ( continuationLabel ) )
// Initiate the continuation.
c . emit ( newOperationLabel ( continuationLabel ) )
case controlFrameKindBlockWithContinuationLabel ,
controlFrameKindIfWithElse :
continuationLabel := newLabel ( labelKindContinuation , frame . frameID )
c . result . LabelCallers [ continuationLabel ] ++
c . emit ( dropOp )
c . emit ( newOperationBr ( continuationLabel ) )
c . emit ( newOperationLabel ( continuationLabel ) )
case controlFrameKindLoop , controlFrameKindBlockWithoutContinuationLabel :
c . emit (
dropOp ,
)
default :
// Should never happen. If so, there's a bug in the translation.
panic ( fmt . Errorf ( "bug: invalid control frame Kind: 0x%x" , frame . kind ) )
}
case wasm . OpcodeBr :
targetIndex , n , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "read the target for br_if: %w" , err )
}
c . pc += n
if c . unreachableState . on {
// If it is currently in unreachable, br is no-op.
break operatorSwitch
}
targetFrame := c . controlFrames . get ( int ( targetIndex ) )
targetFrame . ensureContinuation ( )
dropOp := newOperationDrop ( c . getFrameDropRange ( targetFrame , false ) )
targetID := targetFrame . asLabel ( )
c . result . LabelCallers [ targetID ] ++
c . emit ( dropOp )
c . emit ( newOperationBr ( targetID ) )
// Br operation is stack-polymorphic, and mark the state as unreachable.
// That means subsequent instructions in the current control frame are "unreachable"
// and can be safely removed.
c . markUnreachable ( )
case wasm . OpcodeBrIf :
targetIndex , n , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "read the target for br_if: %w" , err )
}
c . pc += n
if c . unreachableState . on {
// If it is currently in unreachable, br-if is no-op.
break operatorSwitch
}
targetFrame := c . controlFrames . get ( int ( targetIndex ) )
targetFrame . ensureContinuation ( )
drop := c . getFrameDropRange ( targetFrame , false )
target := targetFrame . asLabel ( )
c . result . LabelCallers [ target ] ++
continuationLabel := newLabel ( labelKindHeader , c . nextFrameID ( ) )
c . result . LabelCallers [ continuationLabel ] ++
c . emit ( newOperationBrIf ( target , continuationLabel , drop ) )
// Start emitting else block operations.
c . emit ( newOperationLabel ( continuationLabel ) )
case wasm . OpcodeBrTable :
c . br . Reset ( c . body [ c . pc + 1 : ] )
r := c . br
numTargets , n , err := leb128 . DecodeUint32 ( r )
if err != nil {
return fmt . Errorf ( "error reading number of targets in br_table: %w" , err )
}
c . pc += n
if c . unreachableState . on {
// If it is currently in unreachable, br_table is no-op.
// But before proceeding to the next instruction, we must advance the pc
// according to the number of br_table targets.
for i := uint32 ( 0 ) ; i <= numTargets ; i ++ { // inclusive as we also need to read the index of default target.
_ , n , err := leb128 . DecodeUint32 ( r )
if err != nil {
return fmt . Errorf ( "error reading target %d in br_table: %w" , i , err )
}
c . pc += n
}
break operatorSwitch
}
// Read the branch targets.
s := numTargets * 2
targetLabels := make ( [ ] uint64 , 2 + s ) // (label, inclusiveRange) * (default+numTargets)
for i := uint32 ( 0 ) ; i < s ; i += 2 {
l , n , err := leb128 . DecodeUint32 ( r )
if err != nil {
return fmt . Errorf ( "error reading target %d in br_table: %w" , i , err )
}
c . pc += n
targetFrame := c . controlFrames . get ( int ( l ) )
targetFrame . ensureContinuation ( )
drop := c . getFrameDropRange ( targetFrame , false )
targetLabel := targetFrame . asLabel ( )
targetLabels [ i ] = uint64 ( targetLabel )
targetLabels [ i + 1 ] = drop . AsU64 ( )
c . result . LabelCallers [ targetLabel ] ++
}
// Prep default target control frame.
l , n , err := leb128 . DecodeUint32 ( r )
if err != nil {
return fmt . Errorf ( "error reading default target of br_table: %w" , err )
}
c . pc += n
defaultTargetFrame := c . controlFrames . get ( int ( l ) )
defaultTargetFrame . ensureContinuation ( )
defaultTargetDrop := c . getFrameDropRange ( defaultTargetFrame , false )
defaultLabel := defaultTargetFrame . asLabel ( )
c . result . LabelCallers [ defaultLabel ] ++
targetLabels [ s ] = uint64 ( defaultLabel )
targetLabels [ s + 1 ] = defaultTargetDrop . AsU64 ( )
c . emit ( newOperationBrTable ( targetLabels ) )
// br_table operation is stack-polymorphic, and mark the state as unreachable.
// That means subsequent instructions in the current control frame are "unreachable"
// and can be safely removed.
c . markUnreachable ( )
case wasm . OpcodeReturn :
functionFrame := c . controlFrames . functionFrame ( )
dropOp := newOperationDrop ( c . getFrameDropRange ( functionFrame , false ) )
// Cleanup the stack and then jmp to function frame's continuation (meaning return).
c . emit ( dropOp )
c . emit ( newOperationBr ( functionFrame . asLabel ( ) ) )
// Return operation is stack-polymorphic, and mark the state as unreachable.
// That means subsequent instructions in the current control frame are "unreachable"
// and can be safely removed.
c . markUnreachable ( )
case wasm . OpcodeCall :
c . emit (
newOperationCall ( index ) ,
)
case wasm . OpcodeCallIndirect :
typeIndex := index
tableIndex , n , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "read target for br_table: %w" , err )
}
c . pc += n
c . emit (
newOperationCallIndirect ( typeIndex , tableIndex ) ,
)
case wasm . OpcodeDrop :
r := inclusiveRange { Start : 0 , End : 0 }
if peekValueType == unsignedTypeV128 {
// inclusiveRange is the range in uint64 representation, so dropping a vector value on top
// should be translated as drop [0..1] inclusively.
r . End ++
}
c . emit ( newOperationDrop ( r ) )
case wasm . OpcodeSelect :
// If it is on the unreachable state, ignore the instruction.
if c . unreachableState . on {
break operatorSwitch
}
isTargetVector := c . stackPeek ( ) == unsignedTypeV128
c . emit (
newOperationSelect ( isTargetVector ) ,
)
case wasm . OpcodeTypedSelect :
// Skips two bytes: vector size fixed to 1, and the value type for select.
c . pc += 2
// If it is on the unreachable state, ignore the instruction.
if c . unreachableState . on {
break operatorSwitch
}
// Typed select is semantically equivalent to select at runtime.
isTargetVector := c . stackPeek ( ) == unsignedTypeV128
c . emit (
newOperationSelect ( isTargetVector ) ,
)
case wasm . OpcodeLocalGet :
depth := c . localDepth ( index )
if isVector := c . localType ( index ) == wasm . ValueTypeV128 ; ! isVector {
c . emit (
// -1 because we already manipulated the stack before
// called localDepth ^^.
newOperationPick ( depth - 1 , isVector ) ,
)
} else {
c . emit (
// -2 because we already manipulated the stack before
// called localDepth ^^.
newOperationPick ( depth - 2 , isVector ) ,
)
}
case wasm . OpcodeLocalSet :
depth := c . localDepth ( index )
isVector := c . localType ( index ) == wasm . ValueTypeV128
if isVector {
c . emit (
// +2 because we already popped the operands for this operation from the c.stack before
// called localDepth ^^,
newOperationSet ( depth + 2 , isVector ) ,
)
} else {
c . emit (
// +1 because we already popped the operands for this operation from the c.stack before
// called localDepth ^^,
newOperationSet ( depth + 1 , isVector ) ,
)
}
case wasm . OpcodeLocalTee :
depth := c . localDepth ( index )
isVector := c . localType ( index ) == wasm . ValueTypeV128
if isVector {
c . emit ( newOperationPick ( 1 , isVector ) )
c . emit ( newOperationSet ( depth + 2 , isVector ) )
} else {
c . emit (
newOperationPick ( 0 , isVector ) )
c . emit ( newOperationSet ( depth + 1 , isVector ) )
}
case wasm . OpcodeGlobalGet :
c . emit (
newOperationGlobalGet ( index ) ,
)
case wasm . OpcodeGlobalSet :
c . emit (
newOperationGlobalSet ( index ) ,
)
case wasm . OpcodeI32Load :
imm , err := c . readMemoryArg ( wasm . OpcodeI32LoadName )
if err != nil {
return err
}
c . emit ( newOperationLoad ( unsignedTypeI32 , imm ) )
case wasm . OpcodeI64Load :
imm , err := c . readMemoryArg ( wasm . OpcodeI64LoadName )
if err != nil {
return err
}
c . emit ( newOperationLoad ( unsignedTypeI64 , imm ) )
case wasm . OpcodeF32Load :
imm , err := c . readMemoryArg ( wasm . OpcodeF32LoadName )
if err != nil {
return err
}
c . emit ( newOperationLoad ( unsignedTypeF32 , imm ) )
case wasm . OpcodeF64Load :
imm , err := c . readMemoryArg ( wasm . OpcodeF64LoadName )
if err != nil {
return err
}
c . emit ( newOperationLoad ( unsignedTypeF64 , imm ) )
case wasm . OpcodeI32Load8S :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Load8SName )
if err != nil {
return err
}
c . emit ( newOperationLoad8 ( signedInt32 , imm ) )
case wasm . OpcodeI32Load8U :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Load8UName )
if err != nil {
return err
}
c . emit ( newOperationLoad8 ( signedUint32 , imm ) )
case wasm . OpcodeI32Load16S :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Load16SName )
if err != nil {
return err
}
c . emit ( newOperationLoad16 ( signedInt32 , imm ) )
case wasm . OpcodeI32Load16U :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Load16UName )
if err != nil {
return err
}
c . emit ( newOperationLoad16 ( signedUint32 , imm ) )
case wasm . OpcodeI64Load8S :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load8SName )
if err != nil {
return err
}
c . emit ( newOperationLoad8 ( signedInt64 , imm ) )
case wasm . OpcodeI64Load8U :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load8UName )
if err != nil {
return err
}
c . emit ( newOperationLoad8 ( signedUint64 , imm ) )
case wasm . OpcodeI64Load16S :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load16SName )
if err != nil {
return err
}
c . emit ( newOperationLoad16 ( signedInt64 , imm ) )
case wasm . OpcodeI64Load16U :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load16UName )
if err != nil {
return err
}
c . emit ( newOperationLoad16 ( signedUint64 , imm ) )
case wasm . OpcodeI64Load32S :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load32SName )
if err != nil {
return err
}
c . emit ( newOperationLoad32 ( true , imm ) )
case wasm . OpcodeI64Load32U :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Load32UName )
if err != nil {
return err
}
c . emit ( newOperationLoad32 ( false , imm ) )
case wasm . OpcodeI32Store :
imm , err := c . readMemoryArg ( wasm . OpcodeI32StoreName )
if err != nil {
return err
}
c . emit (
newOperationStore ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeI64Store :
imm , err := c . readMemoryArg ( wasm . OpcodeI64StoreName )
if err != nil {
return err
}
c . emit (
newOperationStore ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeF32Store :
imm , err := c . readMemoryArg ( wasm . OpcodeF32StoreName )
if err != nil {
return err
}
c . emit (
newOperationStore ( unsignedTypeF32 , imm ) ,
)
case wasm . OpcodeF64Store :
imm , err := c . readMemoryArg ( wasm . OpcodeF64StoreName )
if err != nil {
return err
}
c . emit (
newOperationStore ( unsignedTypeF64 , imm ) ,
)
case wasm . OpcodeI32Store8 :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Store8Name )
if err != nil {
return err
}
c . emit (
newOperationStore8 ( imm ) ,
)
case wasm . OpcodeI32Store16 :
imm , err := c . readMemoryArg ( wasm . OpcodeI32Store16Name )
if err != nil {
return err
}
c . emit (
newOperationStore16 ( imm ) ,
)
case wasm . OpcodeI64Store8 :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Store8Name )
if err != nil {
return err
}
c . emit (
newOperationStore8 ( imm ) ,
)
case wasm . OpcodeI64Store16 :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Store16Name )
if err != nil {
return err
}
c . emit (
newOperationStore16 ( imm ) ,
)
case wasm . OpcodeI64Store32 :
imm , err := c . readMemoryArg ( wasm . OpcodeI64Store32Name )
if err != nil {
return err
}
c . emit (
newOperationStore32 ( imm ) ,
)
case wasm . OpcodeMemorySize :
c . result . UsesMemory = true
c . pc ++ // Skip the reserved one byte.
c . emit (
newOperationMemorySize ( ) ,
)
case wasm . OpcodeMemoryGrow :
c . result . UsesMemory = true
c . pc ++ // Skip the reserved one byte.
c . emit (
newOperationMemoryGrow ( ) ,
)
case wasm . OpcodeI32Const :
val , num , err := leb128 . LoadInt32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationConstI32 ( uint32 ( val ) ) ,
)
case wasm . OpcodeI64Const :
val , num , err := leb128 . LoadInt64 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i64.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationConstI64 ( uint64 ( val ) ) ,
)
case wasm . OpcodeF32Const :
v := math . Float32frombits ( binary . LittleEndian . Uint32 ( c . body [ c . pc + 1 : ] ) )
c . pc += 4
c . emit (
newOperationConstF32 ( v ) ,
)
case wasm . OpcodeF64Const :
v := math . Float64frombits ( binary . LittleEndian . Uint64 ( c . body [ c . pc + 1 : ] ) )
c . pc += 8
c . emit (
newOperationConstF64 ( v ) ,
)
case wasm . OpcodeI32Eqz :
c . emit (
newOperationEqz ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Eq :
c . emit (
newOperationEq ( unsignedTypeI32 ) ,
)
case wasm . OpcodeI32Ne :
c . emit (
newOperationNe ( unsignedTypeI32 ) ,
)
case wasm . OpcodeI32LtS :
c . emit (
newOperationLt ( signedTypeInt32 ) ,
)
case wasm . OpcodeI32LtU :
c . emit (
newOperationLt ( signedTypeUint32 ) ,
)
case wasm . OpcodeI32GtS :
c . emit (
newOperationGt ( signedTypeInt32 ) ,
)
case wasm . OpcodeI32GtU :
c . emit (
newOperationGt ( signedTypeUint32 ) ,
)
case wasm . OpcodeI32LeS :
c . emit (
newOperationLe ( signedTypeInt32 ) ,
)
case wasm . OpcodeI32LeU :
c . emit (
newOperationLe ( signedTypeUint32 ) ,
)
case wasm . OpcodeI32GeS :
c . emit (
newOperationGe ( signedTypeInt32 ) ,
)
case wasm . OpcodeI32GeU :
c . emit (
newOperationGe ( signedTypeUint32 ) ,
)
case wasm . OpcodeI64Eqz :
c . emit (
newOperationEqz ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Eq :
c . emit (
newOperationEq ( unsignedTypeI64 ) ,
)
case wasm . OpcodeI64Ne :
c . emit (
newOperationNe ( unsignedTypeI64 ) ,
)
case wasm . OpcodeI64LtS :
c . emit (
newOperationLt ( signedTypeInt64 ) ,
)
case wasm . OpcodeI64LtU :
c . emit (
newOperationLt ( signedTypeUint64 ) ,
)
case wasm . OpcodeI64GtS :
c . emit (
newOperationGt ( signedTypeInt64 ) ,
)
case wasm . OpcodeI64GtU :
c . emit (
newOperationGt ( signedTypeUint64 ) ,
)
case wasm . OpcodeI64LeS :
c . emit (
newOperationLe ( signedTypeInt64 ) ,
)
case wasm . OpcodeI64LeU :
c . emit (
newOperationLe ( signedTypeUint64 ) ,
)
case wasm . OpcodeI64GeS :
c . emit (
newOperationGe ( signedTypeInt64 ) ,
)
case wasm . OpcodeI64GeU :
c . emit (
newOperationGe ( signedTypeUint64 ) ,
)
case wasm . OpcodeF32Eq :
c . emit (
newOperationEq ( unsignedTypeF32 ) ,
)
case wasm . OpcodeF32Ne :
c . emit (
newOperationNe ( unsignedTypeF32 ) ,
)
case wasm . OpcodeF32Lt :
c . emit (
newOperationLt ( signedTypeFloat32 ) ,
)
case wasm . OpcodeF32Gt :
c . emit (
newOperationGt ( signedTypeFloat32 ) ,
)
case wasm . OpcodeF32Le :
c . emit (
newOperationLe ( signedTypeFloat32 ) ,
)
case wasm . OpcodeF32Ge :
c . emit (
newOperationGe ( signedTypeFloat32 ) ,
)
case wasm . OpcodeF64Eq :
c . emit (
newOperationEq ( unsignedTypeF64 ) ,
)
case wasm . OpcodeF64Ne :
c . emit (
newOperationNe ( unsignedTypeF64 ) ,
)
case wasm . OpcodeF64Lt :
c . emit (
newOperationLt ( signedTypeFloat64 ) ,
)
case wasm . OpcodeF64Gt :
c . emit (
newOperationGt ( signedTypeFloat64 ) ,
)
case wasm . OpcodeF64Le :
c . emit (
newOperationLe ( signedTypeFloat64 ) ,
)
case wasm . OpcodeF64Ge :
c . emit (
newOperationGe ( signedTypeFloat64 ) ,
)
case wasm . OpcodeI32Clz :
c . emit (
newOperationClz ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Ctz :
c . emit (
newOperationCtz ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Popcnt :
c . emit (
newOperationPopcnt ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Add :
c . emit (
newOperationAdd ( unsignedTypeI32 ) ,
)
case wasm . OpcodeI32Sub :
c . emit (
newOperationSub ( unsignedTypeI32 ) ,
)
case wasm . OpcodeI32Mul :
c . emit (
newOperationMul ( unsignedTypeI32 ) ,
)
case wasm . OpcodeI32DivS :
c . emit (
newOperationDiv ( signedTypeInt32 ) ,
)
case wasm . OpcodeI32DivU :
c . emit (
newOperationDiv ( signedTypeUint32 ) ,
)
case wasm . OpcodeI32RemS :
c . emit (
newOperationRem ( signedInt32 ) ,
)
case wasm . OpcodeI32RemU :
c . emit (
newOperationRem ( signedUint32 ) ,
)
case wasm . OpcodeI32And :
c . emit (
newOperationAnd ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Or :
c . emit (
newOperationOr ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Xor :
c . emit (
newOperationXor ( unsignedInt64 ) ,
)
case wasm . OpcodeI32Shl :
c . emit (
newOperationShl ( unsignedInt32 ) ,
)
case wasm . OpcodeI32ShrS :
c . emit (
newOperationShr ( signedInt32 ) ,
)
case wasm . OpcodeI32ShrU :
c . emit (
newOperationShr ( signedUint32 ) ,
)
case wasm . OpcodeI32Rotl :
c . emit (
newOperationRotl ( unsignedInt32 ) ,
)
case wasm . OpcodeI32Rotr :
c . emit (
newOperationRotr ( unsignedInt32 ) ,
)
case wasm . OpcodeI64Clz :
c . emit (
newOperationClz ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Ctz :
c . emit (
newOperationCtz ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Popcnt :
c . emit (
newOperationPopcnt ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Add :
c . emit (
newOperationAdd ( unsignedTypeI64 ) ,
)
case wasm . OpcodeI64Sub :
c . emit (
newOperationSub ( unsignedTypeI64 ) ,
)
case wasm . OpcodeI64Mul :
c . emit (
newOperationMul ( unsignedTypeI64 ) ,
)
case wasm . OpcodeI64DivS :
c . emit (
newOperationDiv ( signedTypeInt64 ) ,
)
case wasm . OpcodeI64DivU :
c . emit (
newOperationDiv ( signedTypeUint64 ) ,
)
case wasm . OpcodeI64RemS :
c . emit (
newOperationRem ( signedInt64 ) ,
)
case wasm . OpcodeI64RemU :
c . emit (
newOperationRem ( signedUint64 ) ,
)
case wasm . OpcodeI64And :
c . emit (
newOperationAnd ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Or :
c . emit (
newOperationOr ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Xor :
c . emit (
newOperationXor ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Shl :
c . emit (
newOperationShl ( unsignedInt64 ) ,
)
case wasm . OpcodeI64ShrS :
c . emit (
newOperationShr ( signedInt64 ) ,
)
case wasm . OpcodeI64ShrU :
c . emit (
newOperationShr ( signedUint64 ) ,
)
case wasm . OpcodeI64Rotl :
c . emit (
newOperationRotl ( unsignedInt64 ) ,
)
case wasm . OpcodeI64Rotr :
c . emit (
newOperationRotr ( unsignedInt64 ) ,
)
case wasm . OpcodeF32Abs :
c . emit (
newOperationAbs ( f32 ) ,
)
case wasm . OpcodeF32Neg :
c . emit (
newOperationNeg ( f32 ) ,
)
case wasm . OpcodeF32Ceil :
c . emit (
newOperationCeil ( f32 ) ,
)
case wasm . OpcodeF32Floor :
c . emit (
newOperationFloor ( f32 ) ,
)
case wasm . OpcodeF32Trunc :
c . emit (
newOperationTrunc ( f32 ) ,
)
case wasm . OpcodeF32Nearest :
c . emit (
newOperationNearest ( f32 ) ,
)
case wasm . OpcodeF32Sqrt :
c . emit (
newOperationSqrt ( f32 ) ,
)
case wasm . OpcodeF32Add :
c . emit (
newOperationAdd ( unsignedTypeF32 ) ,
)
case wasm . OpcodeF32Sub :
c . emit (
newOperationSub ( unsignedTypeF32 ) ,
)
case wasm . OpcodeF32Mul :
c . emit (
newOperationMul ( unsignedTypeF32 ) ,
)
case wasm . OpcodeF32Div :
c . emit (
newOperationDiv ( signedTypeFloat32 ) ,
)
case wasm . OpcodeF32Min :
c . emit (
newOperationMin ( f32 ) ,
)
case wasm . OpcodeF32Max :
c . emit (
newOperationMax ( f32 ) ,
)
case wasm . OpcodeF32Copysign :
c . emit (
newOperationCopysign ( f32 ) ,
)
case wasm . OpcodeF64Abs :
c . emit (
newOperationAbs ( f64 ) ,
)
case wasm . OpcodeF64Neg :
c . emit (
newOperationNeg ( f64 ) ,
)
case wasm . OpcodeF64Ceil :
c . emit (
newOperationCeil ( f64 ) ,
)
case wasm . OpcodeF64Floor :
c . emit (
newOperationFloor ( f64 ) ,
)
case wasm . OpcodeF64Trunc :
c . emit (
newOperationTrunc ( f64 ) ,
)
case wasm . OpcodeF64Nearest :
c . emit (
newOperationNearest ( f64 ) ,
)
case wasm . OpcodeF64Sqrt :
c . emit (
newOperationSqrt ( f64 ) ,
)
case wasm . OpcodeF64Add :
c . emit (
newOperationAdd ( unsignedTypeF64 ) ,
)
case wasm . OpcodeF64Sub :
c . emit (
newOperationSub ( unsignedTypeF64 ) ,
)
case wasm . OpcodeF64Mul :
c . emit (
newOperationMul ( unsignedTypeF64 ) ,
)
case wasm . OpcodeF64Div :
c . emit (
newOperationDiv ( signedTypeFloat64 ) ,
)
case wasm . OpcodeF64Min :
c . emit (
newOperationMin ( f64 ) ,
)
case wasm . OpcodeF64Max :
c . emit (
newOperationMax ( f64 ) ,
)
case wasm . OpcodeF64Copysign :
c . emit (
newOperationCopysign ( f64 ) ,
)
case wasm . OpcodeI32WrapI64 :
c . emit (
newOperationI32WrapFromI64 ( ) ,
)
case wasm . OpcodeI32TruncF32S :
c . emit (
newOperationITruncFromF ( f32 , signedInt32 , false ) ,
)
case wasm . OpcodeI32TruncF32U :
c . emit (
newOperationITruncFromF ( f32 , signedUint32 , false ) ,
)
case wasm . OpcodeI32TruncF64S :
c . emit (
newOperationITruncFromF ( f64 , signedInt32 , false ) ,
)
case wasm . OpcodeI32TruncF64U :
c . emit (
newOperationITruncFromF ( f64 , signedUint32 , false ) ,
)
case wasm . OpcodeI64ExtendI32S :
c . emit (
newOperationExtend ( true ) ,
)
case wasm . OpcodeI64ExtendI32U :
c . emit (
newOperationExtend ( false ) ,
)
case wasm . OpcodeI64TruncF32S :
c . emit (
newOperationITruncFromF ( f32 , signedInt64 , false ) ,
)
case wasm . OpcodeI64TruncF32U :
c . emit (
newOperationITruncFromF ( f32 , signedUint64 , false ) ,
)
case wasm . OpcodeI64TruncF64S :
c . emit (
newOperationITruncFromF ( f64 , signedInt64 , false ) ,
)
case wasm . OpcodeI64TruncF64U :
c . emit (
newOperationITruncFromF ( f64 , signedUint64 , false ) ,
)
case wasm . OpcodeF32ConvertI32S :
c . emit (
newOperationFConvertFromI ( signedInt32 , f32 ) ,
)
case wasm . OpcodeF32ConvertI32U :
c . emit (
newOperationFConvertFromI ( signedUint32 , f32 ) ,
)
case wasm . OpcodeF32ConvertI64S :
c . emit (
newOperationFConvertFromI ( signedInt64 , f32 ) ,
)
case wasm . OpcodeF32ConvertI64U :
c . emit (
newOperationFConvertFromI ( signedUint64 , f32 ) ,
)
case wasm . OpcodeF32DemoteF64 :
c . emit (
newOperationF32DemoteFromF64 ( ) ,
)
case wasm . OpcodeF64ConvertI32S :
c . emit (
newOperationFConvertFromI ( signedInt32 , f64 ) ,
)
case wasm . OpcodeF64ConvertI32U :
c . emit (
newOperationFConvertFromI ( signedUint32 , f64 ) ,
)
case wasm . OpcodeF64ConvertI64S :
c . emit (
newOperationFConvertFromI ( signedInt64 , f64 ) ,
)
case wasm . OpcodeF64ConvertI64U :
c . emit (
newOperationFConvertFromI ( signedUint64 , f64 ) ,
)
case wasm . OpcodeF64PromoteF32 :
c . emit (
newOperationF64PromoteFromF32 ( ) ,
)
case wasm . OpcodeI32ReinterpretF32 :
c . emit (
newOperationI32ReinterpretFromF32 ( ) ,
)
case wasm . OpcodeI64ReinterpretF64 :
c . emit (
newOperationI64ReinterpretFromF64 ( ) ,
)
case wasm . OpcodeF32ReinterpretI32 :
c . emit (
newOperationF32ReinterpretFromI32 ( ) ,
)
case wasm . OpcodeF64ReinterpretI64 :
c . emit (
newOperationF64ReinterpretFromI64 ( ) ,
)
case wasm . OpcodeI32Extend8S :
c . emit (
newOperationSignExtend32From8 ( ) ,
)
case wasm . OpcodeI32Extend16S :
c . emit (
newOperationSignExtend32From16 ( ) ,
)
case wasm . OpcodeI64Extend8S :
c . emit (
newOperationSignExtend64From8 ( ) ,
)
case wasm . OpcodeI64Extend16S :
c . emit (
newOperationSignExtend64From16 ( ) ,
)
case wasm . OpcodeI64Extend32S :
c . emit (
newOperationSignExtend64From32 ( ) ,
)
case wasm . OpcodeRefFunc :
c . pc ++
index , num , err := leb128 . LoadUint32 ( c . body [ c . pc : ] )
if err != nil {
return fmt . Errorf ( "failed to read function index for ref.func: %v" , err )
}
c . pc += num - 1
c . emit (
newOperationRefFunc ( index ) ,
)
case wasm . OpcodeRefNull :
c . pc ++ // Skip the type of reftype as every ref value is opaque pointer.
c . emit (
newOperationConstI64 ( 0 ) ,
)
case wasm . OpcodeRefIsNull :
// Simply compare the opaque pointer (i64) with zero.
c . emit (
newOperationEqz ( unsignedInt64 ) ,
)
case wasm . OpcodeTableGet :
c . pc ++
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc : ] )
if err != nil {
return fmt . Errorf ( "failed to read function index for table.get: %v" , err )
}
c . pc += num - 1
c . emit (
newOperationTableGet ( tableIndex ) ,
)
case wasm . OpcodeTableSet :
c . pc ++
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc : ] )
if err != nil {
return fmt . Errorf ( "failed to read function index for table.set: %v" , err )
}
c . pc += num - 1
c . emit (
newOperationTableSet ( tableIndex ) ,
)
case wasm . OpcodeMiscPrefix :
c . pc ++
// A misc opcode is encoded as an unsigned variable 32-bit integer.
miscOp , num , err := leb128 . LoadUint32 ( c . body [ c . pc : ] )
if err != nil {
return fmt . Errorf ( "failed to read misc opcode: %v" , err )
}
c . pc += num - 1
switch byte ( miscOp ) {
case wasm . OpcodeMiscI32TruncSatF32S :
c . emit (
newOperationITruncFromF ( f32 , signedInt32 , true ) ,
)
case wasm . OpcodeMiscI32TruncSatF32U :
c . emit (
newOperationITruncFromF ( f32 , signedUint32 , true ) ,
)
case wasm . OpcodeMiscI32TruncSatF64S :
c . emit (
newOperationITruncFromF ( f64 , signedInt32 , true ) ,
)
case wasm . OpcodeMiscI32TruncSatF64U :
c . emit (
newOperationITruncFromF ( f64 , signedUint32 , true ) ,
)
case wasm . OpcodeMiscI64TruncSatF32S :
c . emit (
newOperationITruncFromF ( f32 , signedInt64 , true ) ,
)
case wasm . OpcodeMiscI64TruncSatF32U :
c . emit (
newOperationITruncFromF ( f32 , signedUint64 , true ) ,
)
case wasm . OpcodeMiscI64TruncSatF64S :
c . emit (
newOperationITruncFromF ( f64 , signedInt64 , true ) ,
)
case wasm . OpcodeMiscI64TruncSatF64U :
c . emit (
newOperationITruncFromF ( f64 , signedUint64 , true ) ,
)
case wasm . OpcodeMiscMemoryInit :
c . result . UsesMemory = true
dataIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num + 1 // +1 to skip the memory index which is fixed to zero.
c . emit (
newOperationMemoryInit ( dataIndex ) ,
)
case wasm . OpcodeMiscDataDrop :
dataIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationDataDrop ( dataIndex ) ,
)
case wasm . OpcodeMiscMemoryCopy :
c . result . UsesMemory = true
c . pc += 2 // +2 to skip two memory indexes which are fixed to zero.
c . emit (
newOperationMemoryCopy ( ) ,
)
case wasm . OpcodeMiscMemoryFill :
c . result . UsesMemory = true
c . pc += 1 // +1 to skip the memory index which is fixed to zero.
c . emit (
newOperationMemoryFill ( ) ,
)
case wasm . OpcodeMiscTableInit :
elemIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
// Read table index which is fixed to zero currently.
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationTableInit ( elemIndex , tableIndex ) ,
)
case wasm . OpcodeMiscElemDrop :
elemIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationElemDrop ( elemIndex ) ,
)
case wasm . OpcodeMiscTableCopy :
// Read the source table inde.g.
dst , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
// Read the destination table inde.g.
src , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationTableCopy ( src , dst ) ,
)
case wasm . OpcodeMiscTableGrow :
// Read the source table inde.g.
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationTableGrow ( tableIndex ) ,
)
case wasm . OpcodeMiscTableSize :
// Read the source table inde.g.
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationTableSize ( tableIndex ) ,
)
case wasm . OpcodeMiscTableFill :
// Read the source table index.
tableIndex , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return fmt . Errorf ( "reading i32.const value: %v" , err )
}
c . pc += num
c . emit (
newOperationTableFill ( tableIndex ) ,
)
default :
return fmt . Errorf ( "unsupported misc instruction in interpreterir: 0x%x" , op )
}
case wasm . OpcodeVecPrefix :
c . pc ++
switch vecOp := c . body [ c . pc ] ; vecOp {
case wasm . OpcodeVecV128Const :
c . pc ++
lo := binary . LittleEndian . Uint64 ( c . body [ c . pc : c . pc + 8 ] )
c . pc += 8
hi := binary . LittleEndian . Uint64 ( c . body [ c . pc : c . pc + 8 ] )
c . emit (
newOperationV128Const ( lo , hi ) ,
)
c . pc += 7
case wasm . OpcodeVecV128Load :
arg , err := c . readMemoryArg ( wasm . OpcodeI32LoadName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType128 , arg ) ,
)
case wasm . OpcodeVecV128Load8x8s :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load8x8SName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType8x8s , arg ) ,
)
case wasm . OpcodeVecV128Load8x8u :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load8x8UName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType8x8u , arg ) ,
)
case wasm . OpcodeVecV128Load16x4s :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load16x4SName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType16x4s , arg ) ,
)
case wasm . OpcodeVecV128Load16x4u :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load16x4UName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType16x4u , arg ) ,
)
case wasm . OpcodeVecV128Load32x2s :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load32x2SName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType32x2s , arg ) ,
)
case wasm . OpcodeVecV128Load32x2u :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load32x2UName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType32x2u , arg ) ,
)
case wasm . OpcodeVecV128Load8Splat :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load8SplatName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType8Splat , arg ) ,
)
case wasm . OpcodeVecV128Load16Splat :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load16SplatName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType16Splat , arg ) ,
)
case wasm . OpcodeVecV128Load32Splat :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load32SplatName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType32Splat , arg ) ,
)
case wasm . OpcodeVecV128Load64Splat :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load64SplatName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType64Splat , arg ) ,
)
case wasm . OpcodeVecV128Load32zero :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load32zeroName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType32zero , arg ) ,
)
case wasm . OpcodeVecV128Load64zero :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load64zeroName )
if err != nil {
return err
}
c . emit (
newOperationV128Load ( v128LoadType64zero , arg ) ,
)
case wasm . OpcodeVecV128Load8Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load8LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128LoadLane ( laneIndex , 8 , arg ) ,
)
case wasm . OpcodeVecV128Load16Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load16LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128LoadLane ( laneIndex , 16 , arg ) ,
)
case wasm . OpcodeVecV128Load32Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load32LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128LoadLane ( laneIndex , 32 , arg ) ,
)
case wasm . OpcodeVecV128Load64Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Load64LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128LoadLane ( laneIndex , 64 , arg ) ,
)
case wasm . OpcodeVecV128Store :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128StoreName )
if err != nil {
return err
}
c . emit (
newOperationV128Store ( arg ) ,
)
case wasm . OpcodeVecV128Store8Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Store8LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128StoreLane ( laneIndex , 8 , arg ) ,
)
case wasm . OpcodeVecV128Store16Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Store16LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128StoreLane ( laneIndex , 16 , arg ) ,
)
case wasm . OpcodeVecV128Store32Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Store32LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128StoreLane ( laneIndex , 32 , arg ) ,
)
case wasm . OpcodeVecV128Store64Lane :
arg , err := c . readMemoryArg ( wasm . OpcodeVecV128Store64LaneName )
if err != nil {
return err
}
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128StoreLane ( laneIndex , 64 , arg ) ,
)
case wasm . OpcodeVecI8x16ExtractLaneS :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , true , shapeI8x16 ) ,
)
case wasm . OpcodeVecI8x16ExtractLaneU :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8ExtractLaneS :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , true , shapeI16x8 ) ,
)
case wasm . OpcodeVecI16x8ExtractLaneU :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4ExtractLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2ExtractLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4ExtractLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2ExtractLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ExtractLane ( laneIndex , false , shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2ReplaceLane :
c . pc ++
laneIndex := c . body [ c . pc ]
c . emit (
newOperationV128ReplaceLane ( laneIndex , shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16Splat :
c . emit (
newOperationV128Splat ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8Splat :
c . emit (
newOperationV128Splat ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Splat :
c . emit (
newOperationV128Splat ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Splat :
c . emit (
newOperationV128Splat ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Splat :
c . emit (
newOperationV128Splat ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Splat :
c . emit (
newOperationV128Splat ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16Swizzle :
c . emit (
newOperationV128Swizzle ( ) ,
)
case wasm . OpcodeVecV128i8x16Shuffle :
c . pc ++
lanes := make ( [ ] uint64 , 16 )
for i := uint64 ( 0 ) ; i < 16 ; i ++ {
lanes [ i ] = uint64 ( c . body [ c . pc + i ] )
}
op := newOperationV128Shuffle ( lanes )
c . emit ( op )
c . pc += 15
case wasm . OpcodeVecV128AnyTrue :
c . emit (
newOperationV128AnyTrue ( ) ,
)
case wasm . OpcodeVecI8x16AllTrue :
c . emit (
newOperationV128AllTrue ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8AllTrue :
c . emit (
newOperationV128AllTrue ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4AllTrue :
c . emit (
newOperationV128AllTrue ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2AllTrue :
c . emit (
newOperationV128AllTrue ( shapeI64x2 ) ,
)
case wasm . OpcodeVecI8x16BitMask :
c . emit (
newOperationV128BitMask ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8BitMask :
c . emit (
newOperationV128BitMask ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4BitMask :
c . emit (
newOperationV128BitMask ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2BitMask :
c . emit (
newOperationV128BitMask ( shapeI64x2 ) ,
)
case wasm . OpcodeVecV128And :
c . emit (
newOperationV128And ( ) ,
)
case wasm . OpcodeVecV128Not :
c . emit (
newOperationV128Not ( ) ,
)
case wasm . OpcodeVecV128Or :
c . emit (
newOperationV128Or ( ) ,
)
case wasm . OpcodeVecV128Xor :
c . emit (
newOperationV128Xor ( ) ,
)
case wasm . OpcodeVecV128Bitselect :
c . emit (
newOperationV128Bitselect ( ) ,
)
case wasm . OpcodeVecV128AndNot :
c . emit (
newOperationV128AndNot ( ) ,
)
case wasm . OpcodeVecI8x16Shl :
c . emit (
newOperationV128Shl ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI8x16ShrS :
c . emit (
newOperationV128Shr ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI8x16ShrU :
c . emit (
newOperationV128Shr ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI16x8Shl :
c . emit (
newOperationV128Shl ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI16x8ShrS :
c . emit (
newOperationV128Shr ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI16x8ShrU :
c . emit (
newOperationV128Shr ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI32x4Shl :
c . emit (
newOperationV128Shl ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI32x4ShrS :
c . emit (
newOperationV128Shr ( shapeI32x4 , true ) ,
)
case wasm . OpcodeVecI32x4ShrU :
c . emit (
newOperationV128Shr ( shapeI32x4 , false ) ,
)
case wasm . OpcodeVecI64x2Shl :
c . emit (
newOperationV128Shl ( shapeI64x2 ) ,
)
case wasm . OpcodeVecI64x2ShrS :
c . emit (
newOperationV128Shr ( shapeI64x2 , true ) ,
)
case wasm . OpcodeVecI64x2ShrU :
c . emit (
newOperationV128Shr ( shapeI64x2 , false ) ,
)
case wasm . OpcodeVecI8x16Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16Eq ) ,
)
case wasm . OpcodeVecI8x16Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16Ne ) ,
)
case wasm . OpcodeVecI8x16LtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16LtS ) ,
)
case wasm . OpcodeVecI8x16LtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16LtU ) ,
)
case wasm . OpcodeVecI8x16GtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16GtS ) ,
)
case wasm . OpcodeVecI8x16GtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16GtU ) ,
)
case wasm . OpcodeVecI8x16LeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16LeS ) ,
)
case wasm . OpcodeVecI8x16LeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16LeU ) ,
)
case wasm . OpcodeVecI8x16GeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16GeS ) ,
)
case wasm . OpcodeVecI8x16GeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI8x16GeU ) ,
)
case wasm . OpcodeVecI16x8Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8Eq ) ,
)
case wasm . OpcodeVecI16x8Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8Ne ) ,
)
case wasm . OpcodeVecI16x8LtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8LtS ) ,
)
case wasm . OpcodeVecI16x8LtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8LtU ) ,
)
case wasm . OpcodeVecI16x8GtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8GtS ) ,
)
case wasm . OpcodeVecI16x8GtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8GtU ) ,
)
case wasm . OpcodeVecI16x8LeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8LeS ) ,
)
case wasm . OpcodeVecI16x8LeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8LeU ) ,
)
case wasm . OpcodeVecI16x8GeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8GeS ) ,
)
case wasm . OpcodeVecI16x8GeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI16x8GeU ) ,
)
case wasm . OpcodeVecI32x4Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4Eq ) ,
)
case wasm . OpcodeVecI32x4Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4Ne ) ,
)
case wasm . OpcodeVecI32x4LtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4LtS ) ,
)
case wasm . OpcodeVecI32x4LtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4LtU ) ,
)
case wasm . OpcodeVecI32x4GtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4GtS ) ,
)
case wasm . OpcodeVecI32x4GtU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4GtU ) ,
)
case wasm . OpcodeVecI32x4LeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4LeS ) ,
)
case wasm . OpcodeVecI32x4LeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4LeU ) ,
)
case wasm . OpcodeVecI32x4GeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4GeS ) ,
)
case wasm . OpcodeVecI32x4GeU :
c . emit (
newOperationV128Cmp ( v128CmpTypeI32x4GeU ) ,
)
case wasm . OpcodeVecI64x2Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2Eq ) ,
)
case wasm . OpcodeVecI64x2Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2Ne ) ,
)
case wasm . OpcodeVecI64x2LtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2LtS ) ,
)
case wasm . OpcodeVecI64x2GtS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2GtS ) ,
)
case wasm . OpcodeVecI64x2LeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2LeS ) ,
)
case wasm . OpcodeVecI64x2GeS :
c . emit (
newOperationV128Cmp ( v128CmpTypeI64x2GeS ) ,
)
case wasm . OpcodeVecF32x4Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Eq ) ,
)
case wasm . OpcodeVecF32x4Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Ne ) ,
)
case wasm . OpcodeVecF32x4Lt :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Lt ) ,
)
case wasm . OpcodeVecF32x4Gt :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Gt ) ,
)
case wasm . OpcodeVecF32x4Le :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Le ) ,
)
case wasm . OpcodeVecF32x4Ge :
c . emit (
newOperationV128Cmp ( v128CmpTypeF32x4Ge ) ,
)
case wasm . OpcodeVecF64x2Eq :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Eq ) ,
)
case wasm . OpcodeVecF64x2Ne :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Ne ) ,
)
case wasm . OpcodeVecF64x2Lt :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Lt ) ,
)
case wasm . OpcodeVecF64x2Gt :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Gt ) ,
)
case wasm . OpcodeVecF64x2Le :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Le ) ,
)
case wasm . OpcodeVecF64x2Ge :
c . emit (
newOperationV128Cmp ( v128CmpTypeF64x2Ge ) ,
)
case wasm . OpcodeVecI8x16Neg :
c . emit (
newOperationV128Neg ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8Neg :
c . emit (
newOperationV128Neg ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Neg :
c . emit (
newOperationV128Neg ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Neg :
c . emit (
newOperationV128Neg ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Neg :
c . emit (
newOperationV128Neg ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Neg :
c . emit (
newOperationV128Neg ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16Add :
c . emit (
newOperationV128Add ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8Add :
c . emit (
newOperationV128Add ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Add :
c . emit (
newOperationV128Add ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Add :
c . emit (
newOperationV128Add ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Add :
c . emit (
newOperationV128Add ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Add :
c . emit (
newOperationV128Add ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16Sub :
c . emit (
newOperationV128Sub ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8Sub :
c . emit (
newOperationV128Sub ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Sub :
c . emit (
newOperationV128Sub ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Sub :
c . emit (
newOperationV128Sub ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Sub :
c . emit (
newOperationV128Sub ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Sub :
c . emit (
newOperationV128Sub ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16AddSatS :
c . emit (
newOperationV128AddSat ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI8x16AddSatU :
c . emit (
newOperationV128AddSat ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI16x8AddSatS :
c . emit (
newOperationV128AddSat ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI16x8AddSatU :
c . emit (
newOperationV128AddSat ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI8x16SubSatS :
c . emit (
newOperationV128SubSat ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI8x16SubSatU :
c . emit (
newOperationV128SubSat ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI16x8SubSatS :
c . emit (
newOperationV128SubSat ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI16x8SubSatU :
c . emit (
newOperationV128SubSat ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI16x8Mul :
c . emit (
newOperationV128Mul ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Mul :
c . emit (
newOperationV128Mul ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Mul :
c . emit (
newOperationV128Mul ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Mul :
c . emit (
newOperationV128Mul ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Mul :
c . emit (
newOperationV128Mul ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF32x4Sqrt :
c . emit (
newOperationV128Sqrt ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Sqrt :
c . emit (
newOperationV128Sqrt ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF32x4Div :
c . emit (
newOperationV128Div ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Div :
c . emit (
newOperationV128Div ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16Abs :
c . emit (
newOperationV128Abs ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI8x16Popcnt :
c . emit (
newOperationV128Popcnt ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8Abs :
c . emit (
newOperationV128Abs ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4Abs :
c . emit (
newOperationV128Abs ( shapeI32x4 ) ,
)
case wasm . OpcodeVecI64x2Abs :
c . emit (
newOperationV128Abs ( shapeI64x2 ) ,
)
case wasm . OpcodeVecF32x4Abs :
c . emit (
newOperationV128Abs ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Abs :
c . emit (
newOperationV128Abs ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI8x16MinS :
c . emit (
newOperationV128Min ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI8x16MinU :
c . emit (
newOperationV128Min ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI8x16MaxS :
c . emit (
newOperationV128Max ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI8x16MaxU :
c . emit (
newOperationV128Max ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI8x16AvgrU :
c . emit (
newOperationV128AvgrU ( shapeI8x16 ) ,
)
case wasm . OpcodeVecI16x8MinS :
c . emit (
newOperationV128Min ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI16x8MinU :
c . emit (
newOperationV128Min ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI16x8MaxS :
c . emit (
newOperationV128Max ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI16x8MaxU :
c . emit (
newOperationV128Max ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI16x8AvgrU :
c . emit (
newOperationV128AvgrU ( shapeI16x8 ) ,
)
case wasm . OpcodeVecI32x4MinS :
c . emit (
newOperationV128Min ( shapeI32x4 , true ) ,
)
case wasm . OpcodeVecI32x4MinU :
c . emit (
newOperationV128Min ( shapeI32x4 , false ) ,
)
case wasm . OpcodeVecI32x4MaxS :
c . emit (
newOperationV128Max ( shapeI32x4 , true ) ,
)
case wasm . OpcodeVecI32x4MaxU :
c . emit (
newOperationV128Max ( shapeI32x4 , false ) ,
)
case wasm . OpcodeVecF32x4Min :
c . emit (
newOperationV128Min ( shapeF32x4 , false ) ,
)
case wasm . OpcodeVecF32x4Max :
c . emit (
newOperationV128Max ( shapeF32x4 , false ) ,
)
case wasm . OpcodeVecF64x2Min :
c . emit (
newOperationV128Min ( shapeF64x2 , false ) ,
)
case wasm . OpcodeVecF64x2Max :
c . emit (
newOperationV128Max ( shapeF64x2 , false ) ,
)
case wasm . OpcodeVecF32x4Pmin :
c . emit (
newOperationV128Pmin ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF32x4Pmax :
c . emit (
newOperationV128Pmax ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Pmin :
c . emit (
newOperationV128Pmin ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF64x2Pmax :
c . emit (
newOperationV128Pmax ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF32x4Ceil :
c . emit (
newOperationV128Ceil ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF32x4Floor :
c . emit (
newOperationV128Floor ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF32x4Trunc :
c . emit (
newOperationV128Trunc ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF32x4Nearest :
c . emit (
newOperationV128Nearest ( shapeF32x4 ) ,
)
case wasm . OpcodeVecF64x2Ceil :
c . emit (
newOperationV128Ceil ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF64x2Floor :
c . emit (
newOperationV128Floor ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF64x2Trunc :
c . emit (
newOperationV128Trunc ( shapeF64x2 ) ,
)
case wasm . OpcodeVecF64x2Nearest :
c . emit (
newOperationV128Nearest ( shapeF64x2 ) ,
)
case wasm . OpcodeVecI16x8ExtendLowI8x16S :
c . emit (
newOperationV128Extend ( shapeI8x16 , true , true ) ,
)
case wasm . OpcodeVecI16x8ExtendHighI8x16S :
c . emit (
newOperationV128Extend ( shapeI8x16 , true , false ) ,
)
case wasm . OpcodeVecI16x8ExtendLowI8x16U :
c . emit (
newOperationV128Extend ( shapeI8x16 , false , true ) ,
)
case wasm . OpcodeVecI16x8ExtendHighI8x16U :
c . emit (
newOperationV128Extend ( shapeI8x16 , false , false ) ,
)
case wasm . OpcodeVecI32x4ExtendLowI16x8S :
c . emit (
newOperationV128Extend ( shapeI16x8 , true , true ) ,
)
case wasm . OpcodeVecI32x4ExtendHighI16x8S :
c . emit (
newOperationV128Extend ( shapeI16x8 , true , false ) ,
)
case wasm . OpcodeVecI32x4ExtendLowI16x8U :
c . emit (
newOperationV128Extend ( shapeI16x8 , false , true ) ,
)
case wasm . OpcodeVecI32x4ExtendHighI16x8U :
c . emit (
newOperationV128Extend ( shapeI16x8 , false , false ) ,
)
case wasm . OpcodeVecI64x2ExtendLowI32x4S :
c . emit (
newOperationV128Extend ( shapeI32x4 , true , true ) ,
)
case wasm . OpcodeVecI64x2ExtendHighI32x4S :
c . emit (
newOperationV128Extend ( shapeI32x4 , true , false ) ,
)
case wasm . OpcodeVecI64x2ExtendLowI32x4U :
c . emit (
newOperationV128Extend ( shapeI32x4 , false , true ) ,
)
case wasm . OpcodeVecI64x2ExtendHighI32x4U :
c . emit (
newOperationV128Extend ( shapeI32x4 , false , false ) ,
)
case wasm . OpcodeVecI16x8Q15mulrSatS :
c . emit (
newOperationV128Q15mulrSatS ( ) ,
)
case wasm . OpcodeVecI16x8ExtMulLowI8x16S :
c . emit (
newOperationV128ExtMul ( shapeI8x16 , true , true ) ,
)
case wasm . OpcodeVecI16x8ExtMulHighI8x16S :
c . emit (
newOperationV128ExtMul ( shapeI8x16 , true , false ) ,
)
case wasm . OpcodeVecI16x8ExtMulLowI8x16U :
c . emit (
newOperationV128ExtMul ( shapeI8x16 , false , true ) ,
)
case wasm . OpcodeVecI16x8ExtMulHighI8x16U :
c . emit (
newOperationV128ExtMul ( shapeI8x16 , false , false ) ,
)
case wasm . OpcodeVecI32x4ExtMulLowI16x8S :
c . emit (
newOperationV128ExtMul ( shapeI16x8 , true , true ) ,
)
case wasm . OpcodeVecI32x4ExtMulHighI16x8S :
c . emit (
newOperationV128ExtMul ( shapeI16x8 , true , false ) ,
)
case wasm . OpcodeVecI32x4ExtMulLowI16x8U :
c . emit (
newOperationV128ExtMul ( shapeI16x8 , false , true ) ,
)
case wasm . OpcodeVecI32x4ExtMulHighI16x8U :
c . emit (
newOperationV128ExtMul ( shapeI16x8 , false , false ) ,
)
case wasm . OpcodeVecI64x2ExtMulLowI32x4S :
c . emit (
newOperationV128ExtMul ( shapeI32x4 , true , true ) ,
)
case wasm . OpcodeVecI64x2ExtMulHighI32x4S :
c . emit (
newOperationV128ExtMul ( shapeI32x4 , true , false ) ,
)
case wasm . OpcodeVecI64x2ExtMulLowI32x4U :
c . emit (
newOperationV128ExtMul ( shapeI32x4 , false , true ) ,
)
case wasm . OpcodeVecI64x2ExtMulHighI32x4U :
c . emit (
newOperationV128ExtMul ( shapeI32x4 , false , false ) ,
)
case wasm . OpcodeVecI16x8ExtaddPairwiseI8x16S :
c . emit (
newOperationV128ExtAddPairwise ( shapeI8x16 , true ) ,
)
case wasm . OpcodeVecI16x8ExtaddPairwiseI8x16U :
c . emit (
newOperationV128ExtAddPairwise ( shapeI8x16 , false ) ,
)
case wasm . OpcodeVecI32x4ExtaddPairwiseI16x8S :
c . emit (
newOperationV128ExtAddPairwise ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI32x4ExtaddPairwiseI16x8U :
c . emit (
newOperationV128ExtAddPairwise ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecF64x2PromoteLowF32x4Zero :
c . emit (
newOperationV128FloatPromote ( ) ,
)
case wasm . OpcodeVecF32x4DemoteF64x2Zero :
c . emit (
newOperationV128FloatDemote ( ) ,
)
case wasm . OpcodeVecF32x4ConvertI32x4S :
c . emit (
newOperationV128FConvertFromI ( shapeF32x4 , true ) ,
)
case wasm . OpcodeVecF32x4ConvertI32x4U :
c . emit (
newOperationV128FConvertFromI ( shapeF32x4 , false ) ,
)
case wasm . OpcodeVecF64x2ConvertLowI32x4S :
c . emit (
newOperationV128FConvertFromI ( shapeF64x2 , true ) ,
)
case wasm . OpcodeVecF64x2ConvertLowI32x4U :
c . emit (
newOperationV128FConvertFromI ( shapeF64x2 , false ) ,
)
case wasm . OpcodeVecI32x4DotI16x8S :
c . emit (
newOperationV128Dot ( ) ,
)
case wasm . OpcodeVecI8x16NarrowI16x8S :
c . emit (
newOperationV128Narrow ( shapeI16x8 , true ) ,
)
case wasm . OpcodeVecI8x16NarrowI16x8U :
c . emit (
newOperationV128Narrow ( shapeI16x8 , false ) ,
)
case wasm . OpcodeVecI16x8NarrowI32x4S :
c . emit (
newOperationV128Narrow ( shapeI32x4 , true ) ,
)
case wasm . OpcodeVecI16x8NarrowI32x4U :
c . emit (
newOperationV128Narrow ( shapeI32x4 , false ) ,
)
case wasm . OpcodeVecI32x4TruncSatF32x4S :
c . emit (
newOperationV128ITruncSatFromF ( shapeF32x4 , true ) ,
)
case wasm . OpcodeVecI32x4TruncSatF32x4U :
c . emit (
newOperationV128ITruncSatFromF ( shapeF32x4 , false ) ,
)
case wasm . OpcodeVecI32x4TruncSatF64x2SZero :
c . emit (
newOperationV128ITruncSatFromF ( shapeF64x2 , true ) ,
)
case wasm . OpcodeVecI32x4TruncSatF64x2UZero :
c . emit (
newOperationV128ITruncSatFromF ( shapeF64x2 , false ) ,
)
default :
return fmt . Errorf ( "unsupported vector instruction in interpreterir: %s" , wasm . VectorInstructionName ( vecOp ) )
}
case wasm . OpcodeAtomicPrefix :
c . pc ++
atomicOp := c . body [ c . pc ]
switch atomicOp {
case wasm . OpcodeAtomicMemoryWait32 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicMemoryWait32Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicMemoryWait ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicMemoryWait64 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicMemoryWait64Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicMemoryWait ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicMemoryNotify :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicMemoryNotifyName )
if err != nil {
return err
}
c . emit (
newOperationAtomicMemoryNotify ( imm ) ,
)
case wasm . OpcodeAtomicFence :
// Skip immediate value
c . pc ++
_ = c . body [ c . pc ]
c . emit (
newOperationAtomicFence ( ) ,
)
case wasm . OpcodeAtomicI32Load :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32LoadName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64Load :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64LoadName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI32Load8U :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Load8UName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad8 ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI32Load16U :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Load16UName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad16 ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64Load8U :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Load8UName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad8 ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Load16U :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Load16UName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad16 ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Load32U :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Load32UName )
if err != nil {
return err
}
c . emit (
newOperationAtomicLoad ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI32Store :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32StoreName )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI32Store8 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Store8Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore8 ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI32Store16 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Store16Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore16 ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64Store :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64StoreName )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Store8 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Store8Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore8 ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Store16 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Store16Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore16 ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Store32 :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Store32Name )
if err != nil {
return err
}
c . emit (
newOperationAtomicStore ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI32RmwAdd :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwAddName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI64RmwAdd :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwAddName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI32Rmw8AddU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8AddUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI64Rmw8AddU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8AddUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI32Rmw16AddU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16AddUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI64Rmw16AddU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16AddUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI64Rmw32AddU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32AddUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpAdd ) ,
)
case wasm . OpcodeAtomicI32RmwSub :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwSubName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI64RmwSub :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwSubName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI32Rmw8SubU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8SubUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI64Rmw8SubU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8SubUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI32Rmw16SubU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16SubUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI64Rmw16SubU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16SubUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI64Rmw32SubU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32SubUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpSub ) ,
)
case wasm . OpcodeAtomicI32RmwAnd :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwAndName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI64RmwAnd :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwAndName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI32Rmw8AndU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8AndUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI64Rmw8AndU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8AndUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI32Rmw16AndU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16AndUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI64Rmw16AndU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16AndUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI64Rmw32AndU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32AndUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpAnd ) ,
)
case wasm . OpcodeAtomicI32RmwOr :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwOrName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI64RmwOr :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwOrName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI32Rmw8OrU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8OrUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI64Rmw8OrU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8OrUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI32Rmw16OrU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16OrUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI64Rmw16OrU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16OrUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI64Rmw32OrU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32OrUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpOr ) ,
)
case wasm . OpcodeAtomicI32RmwXor :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwXorName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI64RmwXor :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwXorName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI32Rmw8XorU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8XorUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI64Rmw8XorU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8XorUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI32Rmw16XorU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16XorUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI64Rmw16XorU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16XorUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI64Rmw32XorU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32XorUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpXor ) ,
)
case wasm . OpcodeAtomicI32RmwXchg :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwXchgName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI64RmwXchg :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwXchgName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI64 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI32Rmw8XchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8XchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI32 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI64Rmw8XchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8XchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8 ( unsignedTypeI64 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI32Rmw16XchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16XchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI32 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI64Rmw16XchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16XchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16 ( unsignedTypeI64 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI64Rmw32XchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32XchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW ( unsignedTypeI32 , imm , atomicArithmeticOpNop ) ,
)
case wasm . OpcodeAtomicI32RmwCmpxchg :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32RmwCmpxchgName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMWCmpxchg ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64RmwCmpxchg :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64RmwCmpxchgName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMWCmpxchg ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI32Rmw8CmpxchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw8CmpxchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8Cmpxchg ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64Rmw8CmpxchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw8CmpxchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW8Cmpxchg ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI32Rmw16CmpxchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI32Rmw16CmpxchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16Cmpxchg ( unsignedTypeI32 , imm ) ,
)
case wasm . OpcodeAtomicI64Rmw16CmpxchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw16CmpxchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMW16Cmpxchg ( unsignedTypeI64 , imm ) ,
)
case wasm . OpcodeAtomicI64Rmw32CmpxchgU :
imm , err := c . readMemoryArg ( wasm . OpcodeAtomicI64Rmw32CmpxchgUName )
if err != nil {
return err
}
c . emit (
newOperationAtomicRMWCmpxchg ( unsignedTypeI32 , imm ) ,
)
default :
return fmt . Errorf ( "unsupported atomic instruction in interpreterir: %s" , wasm . AtomicInstructionName ( atomicOp ) )
}
default :
return fmt . Errorf ( "unsupported instruction in interpreterir: 0x%x" , op )
}
// Move the program counter to point to the next instruction.
c . pc ++
return nil
}
func ( c * compiler ) nextFrameID ( ) ( id uint32 ) {
id = c . currentFrameID + 1
c . currentFrameID ++
return
}
func ( c * compiler ) applyToStack ( opcode wasm . Opcode ) ( index uint32 , err error ) {
switch opcode {
case
// These are the opcodes that is coupled with "index" immediate
// and it DOES affect the signature of opcode.
wasm . OpcodeCall ,
wasm . OpcodeCallIndirect ,
wasm . OpcodeLocalGet ,
wasm . OpcodeLocalSet ,
wasm . OpcodeLocalTee ,
wasm . OpcodeGlobalGet ,
wasm . OpcodeGlobalSet :
// Assumes that we are at the opcode now so skip it before read immediates.
v , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return 0 , fmt . Errorf ( "reading immediates: %w" , err )
}
c . pc += num
index = v
default :
// Note that other opcodes are free of index
// as it doesn't affect the signature of opt code.
// In other words, the "index" argument of wasmOpcodeSignature
// is ignored there.
}
if c . unreachableState . on {
return 0 , nil
}
// Retrieve the signature of the opcode.
s , err := c . wasmOpcodeSignature ( opcode , index )
if err != nil {
return 0 , err
}
// Manipulate the stack according to the signature.
// Note that the following algorithm assumes that
// the unknown type is unique in the signature,
// and is determined by the actual type on the stack.
// The determined type is stored in this typeParam.
var typeParam unsignedType
var typeParamFound bool
for i := range s . in {
want := s . in [ len ( s . in ) - 1 - i ]
actual := c . stackPop ( )
if want == unsignedTypeUnknown && typeParamFound {
want = typeParam
} else if want == unsignedTypeUnknown {
want = actual
typeParam = want
typeParamFound = true
}
if want != actual {
return 0 , fmt . Errorf ( "input signature mismatch: want %s but have %s" , want , actual )
}
}
for _ , target := range s . out {
if target == unsignedTypeUnknown && ! typeParamFound {
return 0 , fmt . Errorf ( "cannot determine type of unknown result" )
} else if target == unsignedTypeUnknown {
c . stackPush ( typeParam )
} else {
c . stackPush ( target )
}
}
return index , nil
}
func ( c * compiler ) stackPeek ( ) ( ret unsignedType ) {
ret = c . stack [ len ( c . stack ) - 1 ]
return
}
2024-08-15 00:08:55 +00:00
func ( c * compiler ) stackSwitchAt ( frame * controlFrame ) {
c . stack = c . stack [ : frame . originalStackLenWithoutParam ]
c . stackLenInUint64 = frame . originalStackLenWithoutParamUint64
}
2024-05-27 15:46:15 +00:00
func ( c * compiler ) stackPop ( ) ( ret unsignedType ) {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase.
ret = c . stack [ len ( c . stack ) - 1 ]
c . stack = c . stack [ : len ( c . stack ) - 1 ]
2024-08-15 00:08:55 +00:00
c . stackLenInUint64 -= 1 + int ( unsignedTypeV128 & ret >> 2 )
2024-05-27 15:46:15 +00:00
return
}
func ( c * compiler ) stackPush ( ts unsignedType ) {
c . stack = append ( c . stack , ts )
2024-08-15 00:08:55 +00:00
c . stackLenInUint64 += 1 + int ( unsignedTypeV128 & ts >> 2 )
2024-05-27 15:46:15 +00:00
}
// emit adds the operations into the result.
func ( c * compiler ) emit ( op unionOperation ) {
if ! c . unreachableState . on {
switch op . Kind {
case operationKindDrop :
// If the drop range is nil,
// we could remove such operations.
// That happens when drop operation is unnecessary.
// i.e. when there's no need to adjust stack before jmp.
if int64 ( op . U1 ) == - 1 {
return
}
}
c . result . Operations = append ( c . result . Operations , op )
if c . needSourceOffset {
c . result . IROperationSourceOffsetsInWasmBinary = append ( c . result . IROperationSourceOffsetsInWasmBinary ,
c . currentOpPC + c . bodyOffsetInCodeSection )
}
}
}
// Emit const expression with default values of the given type.
func ( c * compiler ) emitDefaultValue ( t wasm . ValueType ) {
switch t {
case wasm . ValueTypeI32 :
c . stackPush ( unsignedTypeI32 )
c . emit ( newOperationConstI32 ( 0 ) )
case wasm . ValueTypeI64 , wasm . ValueTypeExternref , wasm . ValueTypeFuncref :
c . stackPush ( unsignedTypeI64 )
c . emit ( newOperationConstI64 ( 0 ) )
case wasm . ValueTypeF32 :
c . stackPush ( unsignedTypeF32 )
c . emit ( newOperationConstF32 ( 0 ) )
case wasm . ValueTypeF64 :
c . stackPush ( unsignedTypeF64 )
c . emit ( newOperationConstF64 ( 0 ) )
case wasm . ValueTypeV128 :
c . stackPush ( unsignedTypeV128 )
c . emit ( newOperationV128Const ( 0 , 0 ) )
}
}
// Returns the "depth" (starting from top of the stack)
// of the n-th local.
func ( c * compiler ) localDepth ( index wasm . Index ) int {
height := c . localIndexToStackHeightInUint64 [ index ]
2024-08-15 00:08:55 +00:00
return c . stackLenInUint64 - 1 - height
2024-05-27 15:46:15 +00:00
}
func ( c * compiler ) localType ( index wasm . Index ) ( t wasm . ValueType ) {
if params := uint32 ( len ( c . sig . Params ) ) ; index < params {
t = c . sig . Params [ index ]
} else {
t = c . localTypes [ index - params ]
}
return
}
// getFrameDropRange returns the range (starting from top of the stack) that spans across the (uint64) stack. The range is
// supposed to be dropped from the stack when the given frame exists or branch into it.
//
// * frame is the control frame which the call-site is trying to branch into or exit.
// * isEnd true if the call-site is handling wasm.OpcodeEnd.
func ( c * compiler ) getFrameDropRange ( frame * controlFrame , isEnd bool ) inclusiveRange {
var start int
if ! isEnd && frame . kind == controlFrameKindLoop {
// If this is not End and the call-site is trying to branch into the Loop control frame,
// we have to Start executing from the beginning of the loop block.
// Therefore, we have to pass the inputs to the frame.
start = frame . blockType . ParamNumInUint64
} else {
start = frame . blockType . ResultNumInUint64
}
2024-08-15 00:08:55 +00:00
end := c . stackLenInUint64 - 1 - frame . originalStackLenWithoutParamUint64
2024-05-27 15:46:15 +00:00
if start <= end {
return inclusiveRange { Start : int32 ( start ) , End : int32 ( end ) }
} else {
return nopinclusiveRange
}
}
func ( c * compiler ) readMemoryArg ( tag string ) ( memoryArg , error ) {
c . result . UsesMemory = true
alignment , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return memoryArg { } , fmt . Errorf ( "reading alignment for %s: %w" , tag , err )
}
c . pc += num
offset , num , err := leb128 . LoadUint32 ( c . body [ c . pc + 1 : ] )
if err != nil {
return memoryArg { } , fmt . Errorf ( "reading offset for %s: %w" , tag , err )
}
c . pc += num
return memoryArg { Offset : offset , Alignment : alignment } , nil
}