Files
wazero/internal/engine/interpreter/interpreter.go
Takeshi Yoneda 35500f9b85 Introduces Cache API (#1016)
This introduces the new API wazero.Cache interface which can be passed to wazero.RuntimeConfig. 
Users can configure this to share the underlying compilation cache across multiple wazero.Runtime. 
And along the way, this deletes the experimental file cache API as it's replaced by this new API.

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com>
2023-01-10 09:32:42 +09:00

4406 lines
144 KiB
Go

package interpreter
import (
"context"
"encoding/binary"
"fmt"
"math"
"math/bits"
"strings"
"sync"
"unsafe"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
"github.com/tetratelabs/wazero/internal/filecache"
"github.com/tetratelabs/wazero/internal/moremath"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wasmdebug"
"github.com/tetratelabs/wazero/internal/wasmruntime"
"github.com/tetratelabs/wazero/internal/wazeroir"
)
// callStackCeiling is the maximum WebAssembly call frame stack height. This allows wazero to raise
// wasm.ErrCallStackOverflow instead of overflowing the Go runtime.
//
// The default value should suffice for most use cases. Those wishing to change this can via `go build -ldflags`.
var callStackCeiling = 2000
// engine is an interpreter implementation of wasm.Engine
type engine struct {
enabledFeatures api.CoreFeatures
codes map[wasm.ModuleID][]*code // guarded by mutex.
mux sync.RWMutex
}
func NewEngine(_ context.Context, enabledFeatures api.CoreFeatures, _ filecache.Cache) wasm.Engine {
return &engine{
enabledFeatures: enabledFeatures,
codes: map[wasm.ModuleID][]*code{},
}
}
// Close implements the same method as documented on wasm.Engine.
func (e *engine) Close() (err error) {
return
}
// CompiledModuleCount implements the same method as documented on wasm.Engine.
func (e *engine) CompiledModuleCount() uint32 {
return uint32(len(e.codes))
}
// DeleteCompiledModule implements the same method as documented on wasm.Engine.
func (e *engine) DeleteCompiledModule(m *wasm.Module) {
e.deleteCodes(m)
}
func (e *engine) deleteCodes(module *wasm.Module) {
e.mux.Lock()
defer e.mux.Unlock()
delete(e.codes, module.ID)
}
func (e *engine) addCodes(module *wasm.Module, fs []*code) {
e.mux.Lock()
defer e.mux.Unlock()
e.codes[module.ID] = fs
}
func (e *engine) getCodes(module *wasm.Module) (fs []*code, ok bool) {
e.mux.RLock()
defer e.mux.RUnlock()
fs, ok = e.codes[module.ID]
return
}
// moduleEngine implements wasm.ModuleEngine
type moduleEngine struct {
// name is the name the module was instantiated with used for error handling.
name string
// codes are the compiled functions in a module instances.
// The index is module instance-scoped.
functions []function
// parentEngine holds *engine from which this module engine is created from.
parentEngine *engine
}
// callEngine holds context per moduleEngine.Call, and shared across all the
// function calls originating from the same moduleEngine.Call execution.
type callEngine struct {
// stack contains the operands.
// Note that all the values are represented as uint64.
stack []uint64
// frames are the function call stack.
frames []*callFrame
// compiled is the initial function for this call engine.
compiled *function
// source is the FunctionInstance from which compiled is created from.
source *wasm.FunctionInstance
}
func (e *moduleEngine) newCallEngine(source *wasm.FunctionInstance, compiled *function) *callEngine {
return &callEngine{source: source, compiled: compiled}
}
func (ce *callEngine) pushValue(v uint64) {
ce.stack = append(ce.stack, v)
}
func (ce *callEngine) popValue() (v uint64) {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase
// and wazeroir translation
// before compilation.
stackTopIndex := len(ce.stack) - 1
v = ce.stack[stackTopIndex]
ce.stack = ce.stack[:stackTopIndex]
return
}
// peekValues peeks api.ValueType values from the stack and returns them.
func (ce *callEngine) peekValues(count int) []uint64 {
if count == 0 {
return nil
}
stackLen := len(ce.stack)
return ce.stack[stackLen-count : stackLen]
}
func (ce *callEngine) drop(r *wazeroir.InclusiveRange) {
// No need to check stack bound
// as we can assume that all the operations
// are valid thanks to validateFunction
// at module validation phase
// and wazeroir translation
// before compilation.
if r == nil {
return
} else if r.Start == 0 {
ce.stack = ce.stack[:len(ce.stack)-1-r.End]
} else {
newStack := ce.stack[:len(ce.stack)-1-r.End]
newStack = append(newStack, ce.stack[len(ce.stack)-r.Start:]...)
ce.stack = newStack
}
}
func (ce *callEngine) pushFrame(frame *callFrame) {
if callStackCeiling <= len(ce.frames) {
panic(wasmruntime.ErrRuntimeStackOverflow)
}
ce.frames = append(ce.frames, frame)
}
func (ce *callEngine) popFrame() (frame *callFrame) {
// No need to check stack bound as we can assume that all the operations are valid thanks to validateFunction at
// module validation phase and wazeroir translation before compilation.
oneLess := len(ce.frames) - 1
frame = ce.frames[oneLess]
ce.frames = ce.frames[:oneLess]
return
}
type callFrame struct {
// pc is the program counter representing the current position in code.body.
pc uint64
// f is the compiled function used in this function frame.
f *function
}
type code struct {
source *wasm.Module
body []*interpreterOp
listener experimental.FunctionListener
hostFn interface{}
isHostFunction bool
}
type function struct {
source *wasm.FunctionInstance
parent *code
}
// functionFromUintptr resurrects the original *function from the given uintptr
// which comes from either funcref table or OpcodeRefFunc instruction.
func functionFromUintptr(ptr uintptr) *function {
// Wraps ptrs as the double pointer in order to avoid the unsafe access as detected by race detector.
//
// For example, if we have (*function)(unsafe.Pointer(ptr)) instead, then the race detector's "checkptr"
// subroutine wanrs as "checkptr: pointer arithmetic result points to invalid allocation"
// https://github.com/golang/go/blob/1ce7fcf139417d618c2730010ede2afb41664211/src/runtime/checkptr.go#L69
var wrapped *uintptr = &ptr
return *(**function)(unsafe.Pointer(wrapped))
}
// interpreterOp is the compilation (engine.lowerIR) result of a wazeroir.Operation.
//
// Not all operations result in an interpreterOp, e.g. wazeroir.OperationI32ReinterpretFromF32, and some operations are
// more complex than others, e.g. wazeroir.OperationBrTable.
//
// Note: This is a form of union type as it can store fields needed for any operation. Hence, most fields are opaque and
// only relevant when in context of its kind.
type interpreterOp struct {
// kind determines how to interpret the other fields in this struct.
kind wazeroir.OperationKind
b1, b2 byte
b3 bool
us []uint64
rs []*wazeroir.InclusiveRange
sourcePC uint64
}
// interpreter mode doesn't maintain call frames in the stack, so pass the zero size to the IR.
const callFrameStackSize = 0
// CompileModule implements the same method as documented on wasm.Engine.
func (e *engine) CompileModule(ctx context.Context, module *wasm.Module, listeners []experimental.FunctionListener) error {
if _, ok := e.getCodes(module); ok { // cache hit!
return nil
}
funcs := make([]*code, len(module.FunctionSection))
irs, err := wazeroir.CompileFunctions(ctx, e.enabledFeatures, callFrameStackSize, module)
if err != nil {
return err
}
for i, ir := range irs {
var lsn experimental.FunctionListener
if i < len(listeners) {
lsn = listeners[i]
}
// If this is the host function, there's nothing to do as the runtime representation of
// host function in interpreter is its Go function itself as opposed to Wasm functions,
// which need to be compiled down to wazeroir.
var compiled *code
if ir.GoFunc != nil {
compiled = &code{hostFn: ir.GoFunc, listener: lsn}
} else {
compiled, err = e.lowerIR(ir)
if err != nil {
def := module.FunctionDefinitionSection[uint32(i)+module.ImportFuncCount()]
return fmt.Errorf("failed to lower func[%s] to wazeroir: %w", def.DebugName(), err)
}
compiled.listener = lsn
}
compiled.source = module
compiled.isHostFunction = ir.IsHostFunction
funcs[i] = compiled
}
e.addCodes(module, funcs)
return nil
}
// NewModuleEngine implements the same method as documented on wasm.Engine.
func (e *engine) NewModuleEngine(name string, module *wasm.Module, functions []wasm.FunctionInstance) (wasm.ModuleEngine, error) {
me := &moduleEngine{
name: name,
parentEngine: e,
functions: make([]function, len(functions)),
}
imported := int(module.ImportFuncCount())
for i, f := range functions[:imported] {
cf := f.Module.Engine.(*moduleEngine).functions[f.Idx]
me.functions[i] = cf
}
codes, ok := e.getCodes(module)
if !ok {
return nil, fmt.Errorf("source module for %s must be compiled before instantiation", name)
}
for i, c := range codes {
offset := i + imported
f := &functions[offset]
me.functions[offset] = function{source: f, parent: c}
}
return me, nil
}
// lowerIR lowers the wazeroir operations to engine friendly struct.
func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) {
hasSourcePCs := len(ir.IROperationSourceOffsetsInWasmBinary) > 0
ops := ir.Operations
ret := &code{}
labelAddress := map[string]uint64{}
onLabelAddressResolved := map[string][]func(addr uint64){}
for i, original := range ops {
op := &interpreterOp{kind: original.Kind()}
if hasSourcePCs {
op.sourcePC = ir.IROperationSourceOffsetsInWasmBinary[i]
}
switch o := original.(type) {
case *wazeroir.OperationUnreachable:
case *wazeroir.OperationLabel:
labelKey := o.Label.String()
address := uint64(len(ret.body))
labelAddress[labelKey] = address
for _, cb := range onLabelAddressResolved[labelKey] {
cb(address)
}
delete(onLabelAddressResolved, labelKey)
// We just ignore the label operation
// as we translate branch operations to the direct address jmp.
continue
case *wazeroir.OperationBr:
op.us = make([]uint64, 1)
if o.Target.IsReturnTarget() {
// Jmp to the end of the possible binary.
op.us[0] = math.MaxUint64
} else {
labelKey := o.Target.String()
addr, ok := labelAddress[labelKey]
if !ok {
// If this is the forward jump (e.g. to the continuation of if, etc.),
// the target is not emitted yet, so resolve the address later.
onLabelAddressResolved[labelKey] = append(onLabelAddressResolved[labelKey],
func(addr uint64) {
op.us[0] = addr
},
)
} else {
op.us[0] = addr
}
}
case *wazeroir.OperationBrIf:
op.rs = make([]*wazeroir.InclusiveRange, 2)
op.us = make([]uint64, 2)
for i, target := range []*wazeroir.BranchTargetDrop{o.Then, o.Else} {
op.rs[i] = target.ToDrop
if target.Target.IsReturnTarget() {
// Jmp to the end of the possible binary.
op.us[i] = math.MaxUint64
} else {
labelKey := target.Target.String()
addr, ok := labelAddress[labelKey]
if !ok {
i := i
// If this is the forward jump (e.g. to the continuation of if, etc.),
// the target is not emitted yet, so resolve the address later.
onLabelAddressResolved[labelKey] = append(onLabelAddressResolved[labelKey],
func(addr uint64) {
op.us[i] = addr
},
)
} else {
op.us[i] = addr
}
}
}
case *wazeroir.OperationBrTable:
targets := append([]*wazeroir.BranchTargetDrop{o.Default}, o.Targets...)
op.rs = make([]*wazeroir.InclusiveRange, len(targets))
op.us = make([]uint64, len(targets))
for i, target := range targets {
op.rs[i] = target.ToDrop
if target.Target.IsReturnTarget() {
// Jmp to the end of the possible binary.
op.us[i] = math.MaxUint64
} else {
labelKey := target.Target.String()
addr, ok := labelAddress[labelKey]
if !ok {
i := i // pin index for later resolution
// If this is the forward jump (e.g. to the continuation of if, etc.),
// the target is not emitted yet, so resolve the address later.
onLabelAddressResolved[labelKey] = append(onLabelAddressResolved[labelKey],
func(addr uint64) {
op.us[i] = addr
},
)
} else {
op.us[i] = addr
}
}
}
case *wazeroir.OperationCall:
op.us = make([]uint64, 1)
op.us = []uint64{uint64(o.FunctionIndex)}
case *wazeroir.OperationCallIndirect:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.TypeIndex)
op.us[1] = uint64(o.TableIndex)
case *wazeroir.OperationDrop:
op.rs = make([]*wazeroir.InclusiveRange, 1)
op.rs[0] = o.Depth
case *wazeroir.OperationSelect:
op.b3 = o.IsTargetVector
case *wazeroir.OperationPick:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.Depth)
op.b3 = o.IsTargetVector
case *wazeroir.OperationSet:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.Depth)
op.b3 = o.IsTargetVector
case *wazeroir.OperationGlobalGet:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.Index)
case *wazeroir.OperationGlobalSet:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.Index)
case *wazeroir.OperationLoad:
op.b1 = byte(o.Type)
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationLoad8:
op.b1 = byte(o.Type)
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationLoad16:
op.b1 = byte(o.Type)
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationLoad32:
if o.Signed {
op.b1 = 1
}
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationStore:
op.b1 = byte(o.Type)
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationStore8:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationStore16:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationStore32:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationMemorySize:
case *wazeroir.OperationMemoryGrow:
case *wazeroir.OperationConstI32:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.Value)
case *wazeroir.OperationConstI64:
op.us = make([]uint64, 1)
op.us[0] = o.Value
case *wazeroir.OperationConstF32:
op.us = make([]uint64, 1)
op.us[0] = uint64(math.Float32bits(o.Value))
case *wazeroir.OperationConstF64:
op.us = make([]uint64, 1)
op.us[0] = math.Float64bits(o.Value)
case *wazeroir.OperationEq:
op.b1 = byte(o.Type)
case *wazeroir.OperationNe:
op.b1 = byte(o.Type)
case *wazeroir.OperationEqz:
op.b1 = byte(o.Type)
case *wazeroir.OperationLt:
op.b1 = byte(o.Type)
case *wazeroir.OperationGt:
op.b1 = byte(o.Type)
case *wazeroir.OperationLe:
op.b1 = byte(o.Type)
case *wazeroir.OperationGe:
op.b1 = byte(o.Type)
case *wazeroir.OperationAdd:
op.b1 = byte(o.Type)
case *wazeroir.OperationSub:
op.b1 = byte(o.Type)
case *wazeroir.OperationMul:
op.b1 = byte(o.Type)
case *wazeroir.OperationClz:
op.b1 = byte(o.Type)
case *wazeroir.OperationCtz:
op.b1 = byte(o.Type)
case *wazeroir.OperationPopcnt:
op.b1 = byte(o.Type)
case *wazeroir.OperationDiv:
op.b1 = byte(o.Type)
case *wazeroir.OperationRem:
op.b1 = byte(o.Type)
case *wazeroir.OperationAnd:
op.b1 = byte(o.Type)
case *wazeroir.OperationOr:
op.b1 = byte(o.Type)
case *wazeroir.OperationXor:
op.b1 = byte(o.Type)
case *wazeroir.OperationShl:
op.b1 = byte(o.Type)
case *wazeroir.OperationShr:
op.b1 = byte(o.Type)
case *wazeroir.OperationRotl:
op.b1 = byte(o.Type)
case *wazeroir.OperationRotr:
op.b1 = byte(o.Type)
case *wazeroir.OperationAbs:
op.b1 = byte(o.Type)
case *wazeroir.OperationNeg:
op.b1 = byte(o.Type)
case *wazeroir.OperationCeil:
op.b1 = byte(o.Type)
case *wazeroir.OperationFloor:
op.b1 = byte(o.Type)
case *wazeroir.OperationTrunc:
op.b1 = byte(o.Type)
case *wazeroir.OperationNearest:
op.b1 = byte(o.Type)
case *wazeroir.OperationSqrt:
op.b1 = byte(o.Type)
case *wazeroir.OperationMin:
op.b1 = byte(o.Type)
case *wazeroir.OperationMax:
op.b1 = byte(o.Type)
case *wazeroir.OperationCopysign:
op.b1 = byte(o.Type)
case *wazeroir.OperationI32WrapFromI64:
case *wazeroir.OperationITruncFromF:
op.b1 = byte(o.InputType)
op.b2 = byte(o.OutputType)
op.b3 = o.NonTrapping
case *wazeroir.OperationFConvertFromI:
op.b1 = byte(o.InputType)
op.b2 = byte(o.OutputType)
case *wazeroir.OperationF32DemoteFromF64:
case *wazeroir.OperationF64PromoteFromF32:
case *wazeroir.OperationI32ReinterpretFromF32,
*wazeroir.OperationI64ReinterpretFromF64,
*wazeroir.OperationF32ReinterpretFromI32,
*wazeroir.OperationF64ReinterpretFromI64:
// Reinterpret ops are essentially nop for engine mode
// because we treat all values as uint64, and Reinterpret* is only used at module
// validation phase where we check type soundness of all the operations.
// So just eliminate the ops.
continue
case *wazeroir.OperationExtend:
if o.Signed {
op.b1 = 1
}
case *wazeroir.OperationSignExtend32From8, *wazeroir.OperationSignExtend32From16, *wazeroir.OperationSignExtend64From8,
*wazeroir.OperationSignExtend64From16, *wazeroir.OperationSignExtend64From32:
case *wazeroir.OperationMemoryInit:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.DataIndex)
case *wazeroir.OperationDataDrop:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.DataIndex)
case *wazeroir.OperationMemoryCopy:
case *wazeroir.OperationMemoryFill:
case *wazeroir.OperationTableInit:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.ElemIndex)
op.us[1] = uint64(o.TableIndex)
case *wazeroir.OperationElemDrop:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.ElemIndex)
case *wazeroir.OperationTableCopy:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.SrcTableIndex)
op.us[1] = uint64(o.DstTableIndex)
case *wazeroir.OperationRefFunc:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.FunctionIndex)
case *wazeroir.OperationTableGet:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.TableIndex)
case *wazeroir.OperationTableSet:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.TableIndex)
case *wazeroir.OperationTableSize:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.TableIndex)
case *wazeroir.OperationTableGrow:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.TableIndex)
case *wazeroir.OperationTableFill:
op.us = make([]uint64, 1)
op.us[0] = uint64(o.TableIndex)
case *wazeroir.OperationV128Const:
op.us = make([]uint64, 2)
op.us[0] = o.Lo
op.us[1] = o.Hi
case *wazeroir.OperationV128Add:
op.b1 = o.Shape
case *wazeroir.OperationV128Sub:
op.b1 = o.Shape
case *wazeroir.OperationV128Load:
op.b1 = o.Type
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationV128LoadLane:
op.b1 = o.LaneSize
op.b2 = o.LaneIndex
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationV128Store:
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationV128StoreLane:
op.b1 = o.LaneSize
op.b2 = o.LaneIndex
op.us = make([]uint64, 2)
op.us[0] = uint64(o.Arg.Alignment)
op.us[1] = uint64(o.Arg.Offset)
case *wazeroir.OperationV128ExtractLane:
op.b1 = o.Shape
op.b2 = o.LaneIndex
op.b3 = o.Signed
case *wazeroir.OperationV128ReplaceLane:
op.b1 = o.Shape
op.b2 = o.LaneIndex
case *wazeroir.OperationV128Splat:
op.b1 = o.Shape
case *wazeroir.OperationV128Shuffle:
op.us = make([]uint64, 16)
for i, l := range o.Lanes {
op.us[i] = uint64(l)
}
case *wazeroir.OperationV128Swizzle:
case *wazeroir.OperationV128AnyTrue:
case *wazeroir.OperationV128AllTrue:
op.b1 = o.Shape
case *wazeroir.OperationV128BitMask:
op.b1 = o.Shape
case *wazeroir.OperationV128And:
case *wazeroir.OperationV128Not:
case *wazeroir.OperationV128Or:
case *wazeroir.OperationV128Xor:
case *wazeroir.OperationV128Bitselect:
case *wazeroir.OperationV128AndNot:
case *wazeroir.OperationV128Shr:
op.b1 = o.Shape
op.b3 = o.Signed
case *wazeroir.OperationV128Shl:
op.b1 = o.Shape
case *wazeroir.OperationV128Cmp:
op.b1 = o.Type
case *wazeroir.OperationV128AddSat:
op.b1 = o.Shape
op.b3 = o.Signed
case *wazeroir.OperationV128SubSat:
op.b1 = o.Shape
op.b3 = o.Signed
case *wazeroir.OperationV128Mul:
op.b1 = o.Shape
case *wazeroir.OperationV128Div:
op.b1 = o.Shape
case *wazeroir.OperationV128Neg:
op.b1 = o.Shape
case *wazeroir.OperationV128Sqrt:
op.b1 = o.Shape
case *wazeroir.OperationV128Abs:
op.b1 = o.Shape
case *wazeroir.OperationV128Popcnt:
case *wazeroir.OperationV128Min:
op.b1 = o.Shape
op.b3 = o.Signed
case *wazeroir.OperationV128Max:
op.b1 = o.Shape
op.b3 = o.Signed
case *wazeroir.OperationV128AvgrU:
op.b1 = o.Shape
case *wazeroir.OperationV128Pmin:
op.b1 = o.Shape
case *wazeroir.OperationV128Pmax:
op.b1 = o.Shape
case *wazeroir.OperationV128Ceil:
op.b1 = o.Shape
case *wazeroir.OperationV128Floor:
op.b1 = o.Shape
case *wazeroir.OperationV128Trunc:
op.b1 = o.Shape
case *wazeroir.OperationV128Nearest:
op.b1 = o.Shape
case *wazeroir.OperationV128Extend:
op.b1 = o.OriginShape
if o.Signed {
op.b2 = 1
}
op.b3 = o.UseLow
case *wazeroir.OperationV128ExtMul:
op.b1 = o.OriginShape
if o.Signed {
op.b2 = 1
}
op.b3 = o.UseLow
case *wazeroir.OperationV128Q15mulrSatS:
case *wazeroir.OperationV128ExtAddPairwise:
op.b1 = o.OriginShape
op.b3 = o.Signed
case *wazeroir.OperationV128FloatPromote:
case *wazeroir.OperationV128FloatDemote:
case *wazeroir.OperationV128FConvertFromI:
op.b1 = o.DestinationShape
op.b3 = o.Signed
case *wazeroir.OperationV128Dot:
case *wazeroir.OperationV128Narrow:
op.b1 = o.OriginShape
op.b3 = o.Signed
case *wazeroir.OperationV128ITruncSatFromF:
op.b1 = o.OriginShape
op.b3 = o.Signed
default:
panic(fmt.Errorf("BUG: unimplemented operation %s", op.kind.String()))
}
ret.body = append(ret.body, op)
}
if len(onLabelAddressResolved) > 0 {
keys := make([]string, 0, len(onLabelAddressResolved))
for key := range onLabelAddressResolved {
keys = append(keys, key)
}
return nil, fmt.Errorf("labels are not defined: %s", strings.Join(keys, ","))
}
return ret, nil
}
// Name implements the same method as documented on wasm.ModuleEngine.
func (e *moduleEngine) Name() string {
return e.name
}
// CreateFuncElementInstance implements the same method as documented on wasm.ModuleEngine.
func (e *moduleEngine) CreateFuncElementInstance(indexes []*wasm.Index) *wasm.ElementInstance {
refs := make([]wasm.Reference, len(indexes))
for i, index := range indexes {
if index != nil {
refs[i] = uintptr(unsafe.Pointer(&e.functions[*index]))
}
}
return &wasm.ElementInstance{
References: refs,
Type: wasm.RefTypeFuncref,
}
}
// FunctionInstanceReference implements the same method as documented on wasm.ModuleEngine.
func (e *moduleEngine) FunctionInstanceReference(funcIndex wasm.Index) wasm.Reference {
return uintptr(unsafe.Pointer(&e.functions[funcIndex]))
}
// NewCallEngine implements the same method as documented on wasm.ModuleEngine.
func (e *moduleEngine) NewCallEngine(_ *wasm.CallContext, f *wasm.FunctionInstance) (ce wasm.CallEngine, err error) {
// Note: The input parameters are pre-validated, so a compiled function is only absent on close. Updates to
// code on close aren't locked, neither is this read.
compiled := &e.functions[f.Idx]
return e.newCallEngine(f, compiled), nil
}
// LookupFunction implements the same method as documented on wasm.ModuleEngine.
func (e *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (idx wasm.Index, err error) {
if tableOffset >= uint32(len(t.References)) {
err = wasmruntime.ErrRuntimeInvalidTableAccess
return
}
rawPtr := t.References[tableOffset]
if rawPtr == 0 {
err = wasmruntime.ErrRuntimeInvalidTableAccess
return
}
tf := functionFromUintptr(rawPtr)
if tf.source.TypeID != typeId {
err = wasmruntime.ErrRuntimeIndirectCallTypeMismatch
return
}
idx = tf.source.Idx
return
}
// Call implements the same method as documented on wasm.CallEngine.
func (ce *callEngine) Call(ctx context.Context, m *wasm.CallContext, params []uint64) (results []uint64, err error) {
return ce.call(ctx, m, ce.compiled, params)
}
func (ce *callEngine) call(ctx context.Context, m *wasm.CallContext, tf *function, params []uint64) (results []uint64, err error) {
ft := tf.source.Type
paramSignature := ft.ParamNumInUint64
paramCount := len(params)
if paramSignature != paramCount {
return nil, fmt.Errorf("expected %d params, but passed %d", paramSignature, paramCount)
}
defer func() {
// If the module closed during the call, and the call didn't err for another reason, set an ExitError.
if err == nil {
err = m.FailIfClosed()
}
// TODO: ^^ Will not fail if the function was imported from a closed module.
if v := recover(); v != nil {
err = ce.recoverOnCall(v)
}
}()
for _, param := range params {
ce.pushValue(param)
}
ce.callFunction(ctx, m, tf)
// This returns a safe copy of the results, instead of a slice view. If we
// returned a re-slice, the caller could accidentally or purposefully
// corrupt the stack of subsequent calls.
results = wasm.PopValues(ft.ResultNumInUint64, ce.popValue)
return
}
// recoverOnCall takes the recovered value `recoverOnCall`, and wraps it
// with the call frame stack traces. Also, reset the state of callEngine
// so that it can be used for the subsequent calls.
func (ce *callEngine) recoverOnCall(v interface{}) (err error) {
builder := wasmdebug.NewErrorBuilder()
frameCount := len(ce.frames)
for i := 0; i < frameCount; i++ {
frame := ce.popFrame()
def := frame.f.source.Definition
var sources []string
if body := frame.f.parent.body; body != nil {
sources = frame.f.parent.source.DWARFLines.Line(body[frame.pc].sourcePC)
}
builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), sources)
}
err = builder.FromRecovered(v)
// Allows the reuse of CallEngine.
ce.stack, ce.frames = ce.stack[:0], ce.frames[:0]
return
}
func (ce *callEngine) callFunction(ctx context.Context, callCtx *wasm.CallContext, f *function) {
if f.parent.hostFn != nil {
ce.callGoFuncWithStack(ctx, callCtx, f)
} else if lsn := f.parent.listener; lsn != nil {
ce.callNativeFuncWithListener(ctx, callCtx, f, lsn)
} else {
ce.callNativeFunc(ctx, callCtx, f)
}
}
func (ce *callEngine) callGoFunc(ctx context.Context, callCtx *wasm.CallContext, f *function, stack []uint64) {
lsn := f.parent.listener
callCtx = callCtx.WithMemory(ce.callerMemory())
if lsn != nil {
params := stack[:f.source.Type.ParamNumInUint64]
ctx = lsn.Before(ctx, callCtx, f.source.Definition, params)
}
frame := &callFrame{f: f}
ce.pushFrame(frame)
fn := f.parent.hostFn
switch fn := fn.(type) {
case api.GoModuleFunction:
fn.Call(ctx, callCtx, stack)
case api.GoFunction:
fn.Call(ctx, stack)
}
ce.popFrame()
if lsn != nil {
// TODO: This doesn't get the error due to use of panic to propagate them.
results := stack[:f.source.Type.ResultNumInUint64]
lsn.After(ctx, callCtx, f.source.Definition, nil, results)
}
}
func (ce *callEngine) callNativeFunc(ctx context.Context, callCtx *wasm.CallContext, f *function) {
frame := &callFrame{f: f}
moduleInst := f.source.Module
functions := moduleInst.Engine.(*moduleEngine).functions
var memoryInst *wasm.MemoryInstance
if f.parent.isHostFunction {
memoryInst = ce.callerMemory()
} else {
memoryInst = moduleInst.Memory
}
globals := moduleInst.Globals
tables := moduleInst.Tables
typeIDs := f.source.Module.TypeIDs
dataInstances := f.source.Module.DataInstances
elementInstances := f.source.Module.ElementInstances
ce.pushFrame(frame)
body := frame.f.parent.body
bodyLen := uint64(len(body))
for frame.pc < bodyLen {
op := body[frame.pc]
// TODO: add description of each operation/case
// on, for example, how many args are used,
// how the stack is modified, etc.
switch op.kind {
case wazeroir.OperationKindUnreachable:
panic(wasmruntime.ErrRuntimeUnreachable)
case wazeroir.OperationKindBr:
frame.pc = op.us[0]
case wazeroir.OperationKindBrIf:
if ce.popValue() > 0 {
ce.drop(op.rs[0])
frame.pc = op.us[0]
} else {
ce.drop(op.rs[1])
frame.pc = op.us[1]
}
case wazeroir.OperationKindBrTable:
if v := uint64(ce.popValue()); v < uint64(len(op.us)-1) {
ce.drop(op.rs[v+1])
frame.pc = op.us[v+1]
} else {
// Default branch.
ce.drop(op.rs[0])
frame.pc = op.us[0]
}
case wazeroir.OperationKindCall:
ce.callFunction(ctx, callCtx, &functions[op.us[0]])
frame.pc++
case wazeroir.OperationKindCallIndirect:
offset := ce.popValue()
table := tables[op.us[1]]
if offset >= uint64(len(table.References)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
}
rawPtr := table.References[offset]
if rawPtr == 0 {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
}
tf := functionFromUintptr(rawPtr)
if tf.source.TypeID != typeIDs[op.us[0]] {
panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
}
ce.callFunction(ctx, callCtx, tf)
frame.pc++
case wazeroir.OperationKindDrop:
ce.drop(op.rs[0])
frame.pc++
case wazeroir.OperationKindSelect:
c := ce.popValue()
if op.b3 { // Target is vector.
x2Hi, x2Lo := ce.popValue(), ce.popValue()
if c == 0 {
_, _ = ce.popValue(), ce.popValue() // discard the x1's lo and hi bits.
ce.pushValue(x2Lo)
ce.pushValue(x2Hi)
}
} else {
v2 := ce.popValue()
if c == 0 {
_ = ce.popValue()
ce.pushValue(v2)
}
}
frame.pc++
case wazeroir.OperationKindPick:
index := len(ce.stack) - 1 - int(op.us[0])
ce.pushValue(ce.stack[index])
if op.b3 { // V128 value target.
ce.pushValue(ce.stack[index+1])
}
frame.pc++
case wazeroir.OperationKindSet:
if op.b3 { // V128 value target.
lowIndex := len(ce.stack) - 1 - int(op.us[0])
highIndex := lowIndex + 1
hi, lo := ce.popValue(), ce.popValue()
ce.stack[lowIndex], ce.stack[highIndex] = lo, hi
} else {
index := len(ce.stack) - 1 - int(op.us[0])
ce.stack[index] = ce.popValue()
}
frame.pc++
case wazeroir.OperationKindGlobalGet:
g := globals[op.us[0]]
ce.pushValue(g.Val)
if g.Type.ValType == wasm.ValueTypeV128 {
ce.pushValue(g.ValHi)
}
frame.pc++
case wazeroir.OperationKindGlobalSet:
g := globals[op.us[0]]
if g.Type.ValType == wasm.ValueTypeV128 {
g.ValHi = ce.popValue()
}
g.Val = ce.popValue()
frame.pc++
case wazeroir.OperationKindLoad:
offset := ce.popMemoryOffset(op)
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32, wazeroir.UnsignedTypeF32:
if val, ok := memoryInst.ReadUint32Le(offset); !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else {
ce.pushValue(uint64(val))
}
case wazeroir.UnsignedTypeI64, wazeroir.UnsignedTypeF64:
if val, ok := memoryInst.ReadUint64Le(offset); !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else {
ce.pushValue(val)
}
}
frame.pc++
case wazeroir.OperationKindLoad8:
val, ok := memoryInst.ReadByte(ce.popMemoryOffset(op))
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
switch wazeroir.SignedInt(op.b1) {
case wazeroir.SignedInt32:
ce.pushValue(uint64(uint32(int8(val))))
case wazeroir.SignedInt64:
ce.pushValue(uint64(int8(val)))
case wazeroir.SignedUint32, wazeroir.SignedUint64:
ce.pushValue(uint64(val))
}
frame.pc++
case wazeroir.OperationKindLoad16:
val, ok := memoryInst.ReadUint16Le(ce.popMemoryOffset(op))
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
switch wazeroir.SignedInt(op.b1) {
case wazeroir.SignedInt32:
ce.pushValue(uint64(uint32(int16(val))))
case wazeroir.SignedInt64:
ce.pushValue(uint64(int16(val)))
case wazeroir.SignedUint32, wazeroir.SignedUint64:
ce.pushValue(uint64(val))
}
frame.pc++
case wazeroir.OperationKindLoad32:
val, ok := memoryInst.ReadUint32Le(ce.popMemoryOffset(op))
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if op.b1 == 1 { // Signed
ce.pushValue(uint64(int32(val)))
} else {
ce.pushValue(uint64(val))
}
frame.pc++
case wazeroir.OperationKindStore:
val := ce.popValue()
offset := ce.popMemoryOffset(op)
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32, wazeroir.UnsignedTypeF32:
if !memoryInst.WriteUint32Le(offset, uint32(val)) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
case wazeroir.UnsignedTypeI64, wazeroir.UnsignedTypeF64:
if !memoryInst.WriteUint64Le(offset, val) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
}
frame.pc++
case wazeroir.OperationKindStore8:
val := byte(ce.popValue())
offset := ce.popMemoryOffset(op)
if !memoryInst.WriteByte(offset, val) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
frame.pc++
case wazeroir.OperationKindStore16:
val := uint16(ce.popValue())
offset := ce.popMemoryOffset(op)
if !memoryInst.WriteUint16Le(offset, val) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
frame.pc++
case wazeroir.OperationKindStore32:
val := uint32(ce.popValue())
offset := ce.popMemoryOffset(op)
if !memoryInst.WriteUint32Le(offset, val) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
frame.pc++
case wazeroir.OperationKindMemorySize:
ce.pushValue(uint64(memoryInst.PageSize()))
frame.pc++
case wazeroir.OperationKindMemoryGrow:
n := ce.popValue()
if res, ok := memoryInst.Grow(uint32(n)); !ok {
ce.pushValue(uint64(0xffffffff)) // = -1 in signed 32-bit integer.
} else {
ce.pushValue(uint64(res))
}
frame.pc++
case wazeroir.OperationKindConstI32, wazeroir.OperationKindConstI64,
wazeroir.OperationKindConstF32, wazeroir.OperationKindConstF64:
ce.pushValue(op.us[0])
frame.pc++
case wazeroir.OperationKindEq:
var b bool
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32:
v2, v1 := ce.popValue(), ce.popValue()
b = uint32(v1) == uint32(v2)
case wazeroir.UnsignedTypeI64:
v2, v1 := ce.popValue(), ce.popValue()
b = v1 == v2
case wazeroir.UnsignedTypeF32:
v2, v1 := ce.popValue(), ce.popValue()
b = math.Float32frombits(uint32(v2)) == math.Float32frombits(uint32(v1))
case wazeroir.UnsignedTypeF64:
v2, v1 := ce.popValue(), ce.popValue()
b = math.Float64frombits(v2) == math.Float64frombits(v1)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindNe:
var b bool
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32, wazeroir.UnsignedTypeI64:
v2, v1 := ce.popValue(), ce.popValue()
b = v1 != v2
case wazeroir.UnsignedTypeF32:
v2, v1 := ce.popValue(), ce.popValue()
b = math.Float32frombits(uint32(v2)) != math.Float32frombits(uint32(v1))
case wazeroir.UnsignedTypeF64:
v2, v1 := ce.popValue(), ce.popValue()
b = math.Float64frombits(v2) != math.Float64frombits(v1)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindEqz:
if ce.popValue() == 0 {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindLt:
v2 := ce.popValue()
v1 := ce.popValue()
var b bool
switch wazeroir.SignedType(op.b1) {
case wazeroir.SignedTypeInt32:
b = int32(v1) < int32(v2)
case wazeroir.SignedTypeInt64:
b = int64(v1) < int64(v2)
case wazeroir.SignedTypeUint32, wazeroir.SignedTypeUint64:
b = v1 < v2
case wazeroir.SignedTypeFloat32:
b = math.Float32frombits(uint32(v1)) < math.Float32frombits(uint32(v2))
case wazeroir.SignedTypeFloat64:
b = math.Float64frombits(v1) < math.Float64frombits(v2)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindGt:
v2 := ce.popValue()
v1 := ce.popValue()
var b bool
switch wazeroir.SignedType(op.b1) {
case wazeroir.SignedTypeInt32:
b = int32(v1) > int32(v2)
case wazeroir.SignedTypeInt64:
b = int64(v1) > int64(v2)
case wazeroir.SignedTypeUint32, wazeroir.SignedTypeUint64:
b = v1 > v2
case wazeroir.SignedTypeFloat32:
b = math.Float32frombits(uint32(v1)) > math.Float32frombits(uint32(v2))
case wazeroir.SignedTypeFloat64:
b = math.Float64frombits(v1) > math.Float64frombits(v2)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindLe:
v2 := ce.popValue()
v1 := ce.popValue()
var b bool
switch wazeroir.SignedType(op.b1) {
case wazeroir.SignedTypeInt32:
b = int32(v1) <= int32(v2)
case wazeroir.SignedTypeInt64:
b = int64(v1) <= int64(v2)
case wazeroir.SignedTypeUint32, wazeroir.SignedTypeUint64:
b = v1 <= v2
case wazeroir.SignedTypeFloat32:
b = math.Float32frombits(uint32(v1)) <= math.Float32frombits(uint32(v2))
case wazeroir.SignedTypeFloat64:
b = math.Float64frombits(v1) <= math.Float64frombits(v2)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindGe:
v2 := ce.popValue()
v1 := ce.popValue()
var b bool
switch wazeroir.SignedType(op.b1) {
case wazeroir.SignedTypeInt32:
b = int32(v1) >= int32(v2)
case wazeroir.SignedTypeInt64:
b = int64(v1) >= int64(v2)
case wazeroir.SignedTypeUint32, wazeroir.SignedTypeUint64:
b = v1 >= v2
case wazeroir.SignedTypeFloat32:
b = math.Float32frombits(uint32(v1)) >= math.Float32frombits(uint32(v2))
case wazeroir.SignedTypeFloat64:
b = math.Float64frombits(v1) >= math.Float64frombits(v2)
}
if b {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindAdd:
v2 := ce.popValue()
v1 := ce.popValue()
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32:
v := uint32(v1) + uint32(v2)
ce.pushValue(uint64(v))
case wazeroir.UnsignedTypeI64:
ce.pushValue(v1 + v2)
case wazeroir.UnsignedTypeF32:
ce.pushValue(addFloat32bits(uint32(v1), uint32(v2)))
case wazeroir.UnsignedTypeF64:
v := math.Float64frombits(v1) + math.Float64frombits(v2)
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindSub:
v2 := ce.popValue()
v1 := ce.popValue()
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32:
ce.pushValue(uint64(uint32(v1) - uint32(v2)))
case wazeroir.UnsignedTypeI64:
ce.pushValue(v1 - v2)
case wazeroir.UnsignedTypeF32:
ce.pushValue(subFloat32bits(uint32(v1), uint32(v2)))
case wazeroir.UnsignedTypeF64:
v := math.Float64frombits(v1) - math.Float64frombits(v2)
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindMul:
v2 := ce.popValue()
v1 := ce.popValue()
switch wazeroir.UnsignedType(op.b1) {
case wazeroir.UnsignedTypeI32:
ce.pushValue(uint64(uint32(v1) * uint32(v2)))
case wazeroir.UnsignedTypeI64:
ce.pushValue(v1 * v2)
case wazeroir.UnsignedTypeF32:
ce.pushValue(mulFloat32bits(uint32(v1), uint32(v2)))
case wazeroir.UnsignedTypeF64:
v := math.Float64frombits(v2) * math.Float64frombits(v1)
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindClz:
v := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(bits.LeadingZeros32(uint32(v))))
} else {
// UnsignedInt64
ce.pushValue(uint64(bits.LeadingZeros64(v)))
}
frame.pc++
case wazeroir.OperationKindCtz:
v := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(bits.TrailingZeros32(uint32(v))))
} else {
// UnsignedInt64
ce.pushValue(uint64(bits.TrailingZeros64(v)))
}
frame.pc++
case wazeroir.OperationKindPopcnt:
v := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(bits.OnesCount32(uint32(v))))
} else {
// UnsignedInt64
ce.pushValue(uint64(bits.OnesCount64(v)))
}
frame.pc++
case wazeroir.OperationKindDiv:
// If an integer, check we won't divide by zero.
t := wazeroir.SignedType(op.b1)
v2, v1 := ce.popValue(), ce.popValue()
switch t {
case wazeroir.SignedTypeFloat32, wazeroir.SignedTypeFloat64: // not integers
default:
if v2 == 0 {
panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
}
}
switch t {
case wazeroir.SignedTypeInt32:
d := int32(v2)
n := int32(v1)
if n == math.MinInt32 && d == -1 {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
ce.pushValue(uint64(uint32(n / d)))
case wazeroir.SignedTypeInt64:
d := int64(v2)
n := int64(v1)
if n == math.MinInt64 && d == -1 {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
ce.pushValue(uint64(n / d))
case wazeroir.SignedTypeUint32:
d := uint32(v2)
n := uint32(v1)
ce.pushValue(uint64(n / d))
case wazeroir.SignedTypeUint64:
d := v2
n := v1
ce.pushValue(n / d)
case wazeroir.SignedTypeFloat32:
ce.pushValue(divFloat32bits(uint32(v1), uint32(v2)))
case wazeroir.SignedTypeFloat64:
ce.pushValue(math.Float64bits(math.Float64frombits(v1) / math.Float64frombits(v2)))
}
frame.pc++
case wazeroir.OperationKindRem:
v2, v1 := ce.popValue(), ce.popValue()
if v2 == 0 {
panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
}
switch wazeroir.SignedInt(op.b1) {
case wazeroir.SignedInt32:
d := int32(v2)
n := int32(v1)
ce.pushValue(uint64(uint32(n % d)))
case wazeroir.SignedInt64:
d := int64(v2)
n := int64(v1)
ce.pushValue(uint64(n % d))
case wazeroir.SignedUint32:
d := uint32(v2)
n := uint32(v1)
ce.pushValue(uint64(n % d))
case wazeroir.SignedUint64:
d := v2
n := v1
ce.pushValue(n % d)
}
frame.pc++
case wazeroir.OperationKindAnd:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(uint32(v2) & uint32(v1)))
} else {
// UnsignedInt64
ce.pushValue(uint64(v2 & v1))
}
frame.pc++
case wazeroir.OperationKindOr:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(uint32(v2) | uint32(v1)))
} else {
// UnsignedInt64
ce.pushValue(uint64(v2 | v1))
}
frame.pc++
case wazeroir.OperationKindXor:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(uint32(v2) ^ uint32(v1)))
} else {
// UnsignedInt64
ce.pushValue(uint64(v2 ^ v1))
}
frame.pc++
case wazeroir.OperationKindShl:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(uint32(v1) << (uint32(v2) % 32)))
} else {
// UnsignedInt64
ce.pushValue(v1 << (v2 % 64))
}
frame.pc++
case wazeroir.OperationKindShr:
v2 := ce.popValue()
v1 := ce.popValue()
switch wazeroir.SignedInt(op.b1) {
case wazeroir.SignedInt32:
ce.pushValue(uint64(uint32(int32(v1) >> (uint32(v2) % 32))))
case wazeroir.SignedInt64:
ce.pushValue(uint64(int64(v1) >> (v2 % 64)))
case wazeroir.SignedUint32:
ce.pushValue(uint64(uint32(v1) >> (uint32(v2) % 32)))
case wazeroir.SignedUint64:
ce.pushValue(v1 >> (v2 % 64))
}
frame.pc++
case wazeroir.OperationKindRotl:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(bits.RotateLeft32(uint32(v1), int(v2))))
} else {
// UnsignedInt64
ce.pushValue(uint64(bits.RotateLeft64(v1, int(v2))))
}
frame.pc++
case wazeroir.OperationKindRotr:
v2 := ce.popValue()
v1 := ce.popValue()
if op.b1 == 0 {
// UnsignedInt32
ce.pushValue(uint64(bits.RotateLeft32(uint32(v1), -int(v2))))
} else {
// UnsignedInt64
ce.pushValue(uint64(bits.RotateLeft64(v1, -int(v2))))
}
frame.pc++
case wazeroir.OperationKindAbs:
if op.b1 == 0 {
// Float32
const mask uint32 = 1 << 31
ce.pushValue(uint64(uint32(ce.popValue()) &^ mask))
} else {
// Float64
const mask uint64 = 1 << 63
ce.pushValue(ce.popValue() &^ mask)
}
frame.pc++
case wazeroir.OperationKindNeg:
if op.b1 == 0 {
// Float32
v := -math.Float32frombits(uint32(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := -math.Float64frombits(ce.popValue())
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindCeil:
if op.b1 == 0 {
// Float32
v := moremath.WasmCompatCeilF32(math.Float32frombits(uint32(ce.popValue())))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := moremath.WasmCompatCeilF64(math.Float64frombits(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindFloor:
if op.b1 == 0 {
// Float32
v := moremath.WasmCompatFloorF32(math.Float32frombits(uint32(ce.popValue())))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := moremath.WasmCompatFloorF64(math.Float64frombits(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindTrunc:
if op.b1 == 0 {
// Float32
v := moremath.WasmCompatTruncF32(math.Float32frombits(uint32(ce.popValue())))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := moremath.WasmCompatTruncF64(math.Float64frombits(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindNearest:
if op.b1 == 0 {
// Float32
f := math.Float32frombits(uint32(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(moremath.WasmCompatNearestF32(f))))
} else {
// Float64
f := math.Float64frombits(ce.popValue())
ce.pushValue(math.Float64bits(moremath.WasmCompatNearestF64(f)))
}
frame.pc++
case wazeroir.OperationKindSqrt:
if op.b1 == 0 {
// Float32
v := math.Sqrt(float64(math.Float32frombits(uint32(ce.popValue()))))
ce.pushValue(uint64(math.Float32bits(float32(v))))
} else {
// Float64
v := math.Sqrt(math.Float64frombits(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
frame.pc++
case wazeroir.OperationKindMin:
if op.b1 == 0 {
// Float32
ce.pushValue(WasmCompatMin32bits(uint32(ce.popValue()), uint32(ce.popValue())))
} else {
v2 := math.Float64frombits(ce.popValue())
v1 := math.Float64frombits(ce.popValue())
ce.pushValue(math.Float64bits(moremath.WasmCompatMin64(v1, v2)))
}
frame.pc++
case wazeroir.OperationKindMax:
if op.b1 == 0 {
ce.pushValue(WasmCompatMax32bits(uint32(ce.popValue()), uint32(ce.popValue())))
} else {
// Float64
v2 := math.Float64frombits(ce.popValue())
v1 := math.Float64frombits(ce.popValue())
ce.pushValue(math.Float64bits(moremath.WasmCompatMax64(v1, v2)))
}
frame.pc++
case wazeroir.OperationKindCopysign:
if op.b1 == 0 {
// Float32
v2 := uint32(ce.popValue())
v1 := uint32(ce.popValue())
const signbit = 1 << 31
ce.pushValue(uint64(v1&^signbit | v2&signbit))
} else {
// Float64
v2 := ce.popValue()
v1 := ce.popValue()
const signbit = 1 << 63
ce.pushValue(v1&^signbit | v2&signbit)
}
frame.pc++
case wazeroir.OperationKindI32WrapFromI64:
ce.pushValue(uint64(uint32(ce.popValue())))
frame.pc++
case wazeroir.OperationKindITruncFromF:
if op.b1 == 0 {
// Float32
switch wazeroir.SignedInt(op.b2) {
case wazeroir.SignedInt32:
v := math.Trunc(float64(math.Float32frombits(uint32(ce.popValue()))))
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
v = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < math.MinInt32 || v > math.MaxInt32 {
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing sources.
if v < 0 {
v = math.MinInt32
} else {
v = math.MaxInt32
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(uint32(int32(v))))
case wazeroir.SignedInt64:
v := math.Trunc(float64(math.Float32frombits(uint32(ce.popValue()))))
res := int64(v)
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
res = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < math.MinInt64 || v >= math.MaxInt64 {
// Note: math.MaxInt64 is rounded up to math.MaxInt64+1 in 64-bit float representation,
// and that's why we use '>=' not '>' to check overflow.
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing sources.
if v < 0 {
res = math.MinInt64
} else {
res = math.MaxInt64
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(res))
case wazeroir.SignedUint32:
v := math.Trunc(float64(math.Float32frombits(uint32(ce.popValue()))))
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
v = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < 0 || v > math.MaxUint32 {
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
v = 0
} else {
v = math.MaxUint32
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(uint32(v)))
case wazeroir.SignedUint64:
v := math.Trunc(float64(math.Float32frombits(uint32(ce.popValue()))))
res := uint64(v)
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
res = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < 0 || v >= math.MaxUint64 {
// Note: math.MaxUint64 is rounded up to math.MaxUint64+1 in 64-bit float representation,
// and that's why we use '>=' not '>' to check overflow.
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
res = 0
} else {
res = math.MaxUint64
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(res)
}
} else {
// Float64
switch wazeroir.SignedInt(op.b2) {
case wazeroir.SignedInt32:
v := math.Trunc(math.Float64frombits(ce.popValue()))
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
v = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < math.MinInt32 || v > math.MaxInt32 {
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
v = math.MinInt32
} else {
v = math.MaxInt32
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(uint32(int32(v))))
case wazeroir.SignedInt64:
v := math.Trunc(math.Float64frombits(ce.popValue()))
res := int64(v)
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
res = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < math.MinInt64 || v >= math.MaxInt64 {
// Note: math.MaxInt64 is rounded up to math.MaxInt64+1 in 64-bit float representation,
// and that's why we use '>=' not '>' to check overflow.
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
res = math.MinInt64
} else {
res = math.MaxInt64
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(res))
case wazeroir.SignedUint32:
v := math.Trunc(math.Float64frombits(ce.popValue()))
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
v = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < 0 || v > math.MaxUint32 {
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
v = 0
} else {
v = math.MaxUint32
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(uint64(uint32(v)))
case wazeroir.SignedUint64:
v := math.Trunc(math.Float64frombits(ce.popValue()))
res := uint64(v)
if math.IsNaN(v) { // NaN cannot be compared with themselves, so we have to use IsNaN
if op.b3 {
// non-trapping conversion must cast nan to zero.
res = 0
} else {
panic(wasmruntime.ErrRuntimeInvalidConversionToInteger)
}
} else if v < 0 || v >= math.MaxUint64 {
// Note: math.MaxUint64 is rounded up to math.MaxUint64+1 in 64-bit float representation,
// and that's why we use '>=' not '>' to check overflow.
if op.b3 {
// non-trapping conversion must "saturate" the value for overflowing source.
if v < 0 {
res = 0
} else {
res = math.MaxUint64
}
} else {
panic(wasmruntime.ErrRuntimeIntegerOverflow)
}
}
ce.pushValue(res)
}
}
frame.pc++
case wazeroir.OperationKindFConvertFromI:
switch wazeroir.SignedInt(op.b1) {
case wazeroir.SignedInt32:
if op.b2 == 0 {
// Float32
v := float32(int32(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := float64(int32(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
case wazeroir.SignedInt64:
if op.b2 == 0 {
// Float32
v := float32(int64(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := float64(int64(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
case wazeroir.SignedUint32:
if op.b2 == 0 {
// Float32
v := float32(uint32(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := float64(uint32(ce.popValue()))
ce.pushValue(math.Float64bits(v))
}
case wazeroir.SignedUint64:
if op.b2 == 0 {
// Float32
v := float32(ce.popValue())
ce.pushValue(uint64(math.Float32bits(v)))
} else {
// Float64
v := float64(ce.popValue())
ce.pushValue(math.Float64bits(v))
}
}
frame.pc++
case wazeroir.OperationKindF32DemoteFromF64:
v := float32(math.Float64frombits(ce.popValue()))
ce.pushValue(uint64(math.Float32bits(v)))
frame.pc++
case wazeroir.OperationKindF64PromoteFromF32:
v := float64(math.Float32frombits(uint32(ce.popValue())))
ce.pushValue(math.Float64bits(v))
frame.pc++
case wazeroir.OperationKindExtend:
if op.b1 == 1 {
// Signed.
v := int64(int32(ce.popValue()))
ce.pushValue(uint64(v))
} else {
v := uint64(uint32(ce.popValue()))
ce.pushValue(v)
}
frame.pc++
case wazeroir.OperationKindSignExtend32From8:
v := uint32(int8(ce.popValue()))
ce.pushValue(uint64(v))
frame.pc++
case wazeroir.OperationKindSignExtend32From16:
v := uint32(int16(ce.popValue()))
ce.pushValue(uint64(v))
frame.pc++
case wazeroir.OperationKindSignExtend64From8:
v := int64(int8(ce.popValue()))
ce.pushValue(uint64(v))
frame.pc++
case wazeroir.OperationKindSignExtend64From16:
v := int64(int16(ce.popValue()))
ce.pushValue(uint64(v))
frame.pc++
case wazeroir.OperationKindSignExtend64From32:
v := int64(int32(ce.popValue()))
ce.pushValue(uint64(v))
frame.pc++
case wazeroir.OperationKindMemoryInit:
dataInstance := dataInstances[op.us[0]]
copySize := ce.popValue()
inDataOffset := ce.popValue()
inMemoryOffset := ce.popValue()
if inDataOffset+copySize > uint64(len(dataInstance)) ||
inMemoryOffset+copySize > uint64(len(memoryInst.Buffer)) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else if copySize != 0 {
copy(memoryInst.Buffer[inMemoryOffset:inMemoryOffset+copySize], dataInstance[inDataOffset:])
}
frame.pc++
case wazeroir.OperationKindDataDrop:
dataInstances[op.us[0]] = nil
frame.pc++
case wazeroir.OperationKindMemoryCopy:
memLen := uint64(len(memoryInst.Buffer))
copySize := ce.popValue()
sourceOffset := ce.popValue()
destinationOffset := ce.popValue()
if sourceOffset+copySize > memLen || destinationOffset+copySize > memLen {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else if copySize != 0 {
copy(memoryInst.Buffer[destinationOffset:],
memoryInst.Buffer[sourceOffset:sourceOffset+copySize])
}
frame.pc++
case wazeroir.OperationKindMemoryFill:
fillSize := ce.popValue()
value := byte(ce.popValue())
offset := ce.popValue()
if fillSize+offset > uint64(len(memoryInst.Buffer)) {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
} else if fillSize != 0 {
// Uses the copy trick for faster filling buffer.
// https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
buf := memoryInst.Buffer[offset : offset+fillSize]
buf[0] = value
for i := 1; i < len(buf); i *= 2 {
copy(buf[i:], buf[:i])
}
}
frame.pc++
case wazeroir.OperationKindTableInit:
elementInstance := elementInstances[op.us[0]]
copySize := ce.popValue()
inElementOffset := ce.popValue()
inTableOffset := ce.popValue()
table := tables[op.us[1]]
if inElementOffset+copySize > uint64(len(elementInstance.References)) ||
inTableOffset+copySize > uint64(len(table.References)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
} else if copySize != 0 {
copy(table.References[inTableOffset:inTableOffset+copySize], elementInstance.References[inElementOffset:])
}
frame.pc++
case wazeroir.OperationKindElemDrop:
elementInstances[op.us[0]].References = nil
frame.pc++
case wazeroir.OperationKindTableCopy:
srcTable, dstTable := tables[op.us[0]].References, tables[op.us[1]].References
copySize := ce.popValue()
sourceOffset := ce.popValue()
destinationOffset := ce.popValue()
if sourceOffset+copySize > uint64(len(srcTable)) || destinationOffset+copySize > uint64(len(dstTable)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
} else if copySize != 0 {
copy(dstTable[destinationOffset:], srcTable[sourceOffset:sourceOffset+copySize])
}
frame.pc++
case wazeroir.OperationKindRefFunc:
ce.pushValue(uint64(uintptr(unsafe.Pointer(&functions[op.us[0]]))))
frame.pc++
case wazeroir.OperationKindTableGet:
table := tables[op.us[0]]
offset := ce.popValue()
if offset >= uint64(len(table.References)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
}
ce.pushValue(uint64(table.References[offset]))
frame.pc++
case wazeroir.OperationKindTableSet:
table := tables[op.us[0]]
ref := ce.popValue()
offset := ce.popValue()
if offset >= uint64(len(table.References)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
}
table.References[offset] = uintptr(ref) // externrefs are opaque uint64.
frame.pc++
case wazeroir.OperationKindTableSize:
table := tables[op.us[0]]
ce.pushValue(uint64(len(table.References)))
frame.pc++
case wazeroir.OperationKindTableGrow:
table := tables[op.us[0]]
num, ref := ce.popValue(), ce.popValue()
ret := table.Grow(uint32(num), uintptr(ref))
ce.pushValue(uint64(ret))
frame.pc++
case wazeroir.OperationKindTableFill:
table := tables[op.us[0]]
num := ce.popValue()
ref := uintptr(ce.popValue())
offset := ce.popValue()
if num+offset > uint64(len(table.References)) {
panic(wasmruntime.ErrRuntimeInvalidTableAccess)
} else if num > 0 {
// Uses the copy trick for faster filling the region with the value.
// https://gist.github.com/taylorza/df2f89d5f9ab3ffd06865062a4cf015d
targetRegion := table.References[offset : offset+num]
targetRegion[0] = ref
for i := 1; i < len(targetRegion); i *= 2 {
copy(targetRegion[i:], targetRegion[:i])
}
}
frame.pc++
case wazeroir.OperationKindV128Const:
lo, hi := op.us[0], op.us[1]
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Add:
yHigh, yLow := ce.popValue(), ce.popValue()
xHigh, xLow := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
ce.pushValue(
uint64(uint8(xLow>>8)+uint8(yLow>>8))<<8 | uint64(uint8(xLow)+uint8(yLow)) |
uint64(uint8(xLow>>24)+uint8(yLow>>24))<<24 | uint64(uint8(xLow>>16)+uint8(yLow>>16))<<16 |
uint64(uint8(xLow>>40)+uint8(yLow>>40))<<40 | uint64(uint8(xLow>>32)+uint8(yLow>>32))<<32 |
uint64(uint8(xLow>>56)+uint8(yLow>>56))<<56 | uint64(uint8(xLow>>48)+uint8(yLow>>48))<<48,
)
ce.pushValue(
uint64(uint8(xHigh>>8)+uint8(yHigh>>8))<<8 | uint64(uint8(xHigh)+uint8(yHigh)) |
uint64(uint8(xHigh>>24)+uint8(yHigh>>24))<<24 | uint64(uint8(xHigh>>16)+uint8(yHigh>>16))<<16 |
uint64(uint8(xHigh>>40)+uint8(yHigh>>40))<<40 | uint64(uint8(xHigh>>32)+uint8(yHigh>>32))<<32 |
uint64(uint8(xHigh>>56)+uint8(yHigh>>56))<<56 | uint64(uint8(xHigh>>48)+uint8(yHigh>>48))<<48,
)
case wazeroir.ShapeI16x8:
ce.pushValue(
uint64(uint16(xLow>>16+yLow>>16))<<16 | uint64(uint16(xLow)+uint16(yLow)) |
uint64(uint16(xLow>>48+yLow>>48))<<48 | uint64(uint16(xLow>>32+yLow>>32))<<32,
)
ce.pushValue(
uint64(uint16(xHigh>>16)+uint16(yHigh>>16))<<16 | uint64(uint16(xHigh)+uint16(yHigh)) |
uint64(uint16(xHigh>>48)+uint16(yHigh>>48))<<48 | uint64(uint16(xHigh>>32)+uint16(yHigh>>32))<<32,
)
case wazeroir.ShapeI32x4:
ce.pushValue(uint64(uint32(xLow>>32)+uint32(yLow>>32))<<32 | uint64(uint32(xLow)+uint32(yLow)))
ce.pushValue(uint64(uint32(xHigh>>32)+uint32(yHigh>>32))<<32 | uint64(uint32(xHigh)+uint32(yHigh)))
case wazeroir.ShapeI64x2:
ce.pushValue(xLow + yLow)
ce.pushValue(xHigh + yHigh)
case wazeroir.ShapeF32x4:
ce.pushValue(
addFloat32bits(uint32(xLow), uint32(yLow)) | addFloat32bits(uint32(xLow>>32), uint32(yLow>>32))<<32,
)
ce.pushValue(
addFloat32bits(uint32(xHigh), uint32(yHigh)) | addFloat32bits(uint32(xHigh>>32), uint32(yHigh>>32))<<32,
)
case wazeroir.ShapeF64x2:
ce.pushValue(math.Float64bits(math.Float64frombits(xLow) + math.Float64frombits(yLow)))
ce.pushValue(math.Float64bits(math.Float64frombits(xHigh) + math.Float64frombits(yHigh)))
}
frame.pc++
case wazeroir.OperationKindV128Sub:
yHigh, yLow := ce.popValue(), ce.popValue()
xHigh, xLow := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
ce.pushValue(
uint64(uint8(xLow>>8)-uint8(yLow>>8))<<8 | uint64(uint8(xLow)-uint8(yLow)) |
uint64(uint8(xLow>>24)-uint8(yLow>>24))<<24 | uint64(uint8(xLow>>16)-uint8(yLow>>16))<<16 |
uint64(uint8(xLow>>40)-uint8(yLow>>40))<<40 | uint64(uint8(xLow>>32)-uint8(yLow>>32))<<32 |
uint64(uint8(xLow>>56)-uint8(yLow>>56))<<56 | uint64(uint8(xLow>>48)-uint8(yLow>>48))<<48,
)
ce.pushValue(
uint64(uint8(xHigh>>8)-uint8(yHigh>>8))<<8 | uint64(uint8(xHigh)-uint8(yHigh)) |
uint64(uint8(xHigh>>24)-uint8(yHigh>>24))<<24 | uint64(uint8(xHigh>>16)-uint8(yHigh>>16))<<16 |
uint64(uint8(xHigh>>40)-uint8(yHigh>>40))<<40 | uint64(uint8(xHigh>>32)-uint8(yHigh>>32))<<32 |
uint64(uint8(xHigh>>56)-uint8(yHigh>>56))<<56 | uint64(uint8(xHigh>>48)-uint8(yHigh>>48))<<48,
)
case wazeroir.ShapeI16x8:
ce.pushValue(
uint64(uint16(xLow>>16)-uint16(yLow>>16))<<16 | uint64(uint16(xLow)-uint16(yLow)) |
uint64(uint16(xLow>>48)-uint16(yLow>>48))<<48 | uint64(uint16(xLow>>32)-uint16(yLow>>32))<<32,
)
ce.pushValue(
uint64(uint16(xHigh>>16)-uint16(yHigh>>16))<<16 | uint64(uint16(xHigh)-uint16(yHigh)) |
uint64(uint16(xHigh>>48)-uint16(yHigh>>48))<<48 | uint64(uint16(xHigh>>32)-uint16(yHigh>>32))<<32,
)
case wazeroir.ShapeI32x4:
ce.pushValue(uint64(uint32(xLow>>32-yLow>>32))<<32 | uint64(uint32(xLow)-uint32(yLow)))
ce.pushValue(uint64(uint32(xHigh>>32-yHigh>>32))<<32 | uint64(uint32(xHigh)-uint32(yHigh)))
case wazeroir.ShapeI64x2:
ce.pushValue(xLow - yLow)
ce.pushValue(xHigh - yHigh)
case wazeroir.ShapeF32x4:
ce.pushValue(
subFloat32bits(uint32(xLow), uint32(yLow)) | subFloat32bits(uint32(xLow>>32), uint32(yLow>>32))<<32,
)
ce.pushValue(
subFloat32bits(uint32(xHigh), uint32(yHigh)) | subFloat32bits(uint32(xHigh>>32), uint32(yHigh>>32))<<32,
)
case wazeroir.ShapeF64x2:
ce.pushValue(math.Float64bits(math.Float64frombits(xLow) - math.Float64frombits(yLow)))
ce.pushValue(math.Float64bits(math.Float64frombits(xHigh) - math.Float64frombits(yHigh)))
}
frame.pc++
case wazeroir.OperationKindV128Load:
offset := ce.popMemoryOffset(op)
switch op.b1 {
case wazeroir.V128LoadType128:
lo, ok := memoryInst.ReadUint64Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(lo)
hi, ok := memoryInst.ReadUint64Le(offset + 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(hi)
case wazeroir.V128LoadType8x8s:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(
uint64(uint16(int8(data[3])))<<48 | uint64(uint16(int8(data[2])))<<32 | uint64(uint16(int8(data[1])))<<16 | uint64(uint16(int8(data[0]))),
)
ce.pushValue(
uint64(uint16(int8(data[7])))<<48 | uint64(uint16(int8(data[6])))<<32 | uint64(uint16(int8(data[5])))<<16 | uint64(uint16(int8(data[4]))),
)
case wazeroir.V128LoadType8x8u:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(
uint64(data[3])<<48 | uint64(data[2])<<32 | uint64(data[1])<<16 | uint64(data[0]),
)
ce.pushValue(
uint64(data[7])<<48 | uint64(data[6])<<32 | uint64(data[5])<<16 | uint64(data[4]),
)
case wazeroir.V128LoadType16x4s:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(
uint64(int16(binary.LittleEndian.Uint16(data[2:])))<<32 |
uint64(uint32(int16(binary.LittleEndian.Uint16(data)))),
)
ce.pushValue(
uint64(uint32(int16(binary.LittleEndian.Uint16(data[6:]))))<<32 |
uint64(uint32(int16(binary.LittleEndian.Uint16(data[4:])))),
)
case wazeroir.V128LoadType16x4u:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(
uint64(binary.LittleEndian.Uint16(data[2:]))<<32 | uint64(binary.LittleEndian.Uint16(data)),
)
ce.pushValue(
uint64(binary.LittleEndian.Uint16(data[6:]))<<32 | uint64(binary.LittleEndian.Uint16(data[4:])),
)
case wazeroir.V128LoadType32x2s:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(uint64(int32(binary.LittleEndian.Uint32(data))))
ce.pushValue(uint64(int32(binary.LittleEndian.Uint32(data[4:]))))
case wazeroir.V128LoadType32x2u:
data, ok := memoryInst.Read(offset, 8)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(uint64(binary.LittleEndian.Uint32(data)))
ce.pushValue(uint64(binary.LittleEndian.Uint32(data[4:])))
case wazeroir.V128LoadType8Splat:
v, ok := memoryInst.ReadByte(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
v8 := uint64(v)<<56 | uint64(v)<<48 | uint64(v)<<40 | uint64(v)<<32 |
uint64(v)<<24 | uint64(v)<<16 | uint64(v)<<8 | uint64(v)
ce.pushValue(v8)
ce.pushValue(v8)
case wazeroir.V128LoadType16Splat:
v, ok := memoryInst.ReadUint16Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
v4 := uint64(v)<<48 | uint64(v)<<32 | uint64(v)<<16 | uint64(v)
ce.pushValue(v4)
ce.pushValue(v4)
case wazeroir.V128LoadType32Splat:
v, ok := memoryInst.ReadUint32Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
vv := uint64(v)<<32 | uint64(v)
ce.pushValue(vv)
ce.pushValue(vv)
case wazeroir.V128LoadType64Splat:
lo, ok := memoryInst.ReadUint64Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(lo)
ce.pushValue(lo)
case wazeroir.V128LoadType32zero:
lo, ok := memoryInst.ReadUint32Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(uint64(lo))
ce.pushValue(0)
case wazeroir.V128LoadType64zero:
lo, ok := memoryInst.ReadUint64Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
ce.pushValue(lo)
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindV128LoadLane:
hi, lo := ce.popValue(), ce.popValue()
offset := ce.popMemoryOffset(op)
switch op.b1 {
case 8:
b, ok := memoryInst.ReadByte(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if op.b2 < 8 {
s := op.b2 << 3
lo = (lo & ^(0xff << s)) | uint64(b)<<s
} else {
s := (op.b2 - 8) << 3
hi = (hi & ^(0xff << s)) | uint64(b)<<s
}
case 16:
b, ok := memoryInst.ReadUint16Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if op.b2 < 4 {
s := op.b2 << 4
lo = (lo & ^(0xff_ff << s)) | uint64(b)<<s
} else {
s := (op.b2 - 4) << 4
hi = (hi & ^(0xff_ff << s)) | uint64(b)<<s
}
case 32:
b, ok := memoryInst.ReadUint32Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if op.b2 < 2 {
s := op.b2 << 5
lo = (lo & ^(0xff_ff_ff_ff << s)) | uint64(b)<<s
} else {
s := (op.b2 - 2) << 5
hi = (hi & ^(0xff_ff_ff_ff << s)) | uint64(b)<<s
}
case 64:
b, ok := memoryInst.ReadUint64Le(offset)
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if op.b2 == 0 {
lo = b
} else {
hi = b
}
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Store:
hi, lo := ce.popValue(), ce.popValue()
offset := ce.popMemoryOffset(op)
if ok := memoryInst.WriteUint64Le(offset, lo); !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
if ok := memoryInst.WriteUint64Le(offset+8, hi); !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
frame.pc++
case wazeroir.OperationKindV128StoreLane:
hi, lo := ce.popValue(), ce.popValue()
offset := ce.popMemoryOffset(op)
var ok bool
switch op.b1 {
case 8:
if op.b2 < 8 {
ok = memoryInst.WriteByte(offset, byte(lo>>(op.b2*8)))
} else {
ok = memoryInst.WriteByte(offset, byte(hi>>((op.b2-8)*8)))
}
case 16:
if op.b2 < 4 {
ok = memoryInst.WriteUint16Le(offset, uint16(lo>>(op.b2*16)))
} else {
ok = memoryInst.WriteUint16Le(offset, uint16(hi>>((op.b2-4)*16)))
}
case 32:
if op.b2 < 2 {
ok = memoryInst.WriteUint32Le(offset, uint32(lo>>(op.b2*32)))
} else {
ok = memoryInst.WriteUint32Le(offset, uint32(hi>>((op.b2-2)*32)))
}
case 64:
if op.b2 == 0 {
ok = memoryInst.WriteUint64Le(offset, lo)
} else {
ok = memoryInst.WriteUint64Le(offset, hi)
}
}
if !ok {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
frame.pc++
case wazeroir.OperationKindV128ReplaceLane:
v := ce.popValue()
hi, lo := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
if op.b2 < 8 {
s := op.b2 << 3
lo = (lo & ^(0xff << s)) | uint64(byte(v))<<s
} else {
s := (op.b2 - 8) << 3
hi = (hi & ^(0xff << s)) | uint64(byte(v))<<s
}
case wazeroir.ShapeI16x8:
if op.b2 < 4 {
s := op.b2 << 4
lo = (lo & ^(0xff_ff << s)) | uint64(uint16(v))<<s
} else {
s := (op.b2 - 4) << 4
hi = (hi & ^(0xff_ff << s)) | uint64(uint16(v))<<s
}
case wazeroir.ShapeI32x4, wazeroir.ShapeF32x4:
if op.b2 < 2 {
s := op.b2 << 5
lo = (lo & ^(0xff_ff_ff_ff << s)) | uint64(uint32(v))<<s
} else {
s := (op.b2 - 2) << 5
hi = (hi & ^(0xff_ff_ff_ff << s)) | uint64(uint32(v))<<s
}
case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2:
if op.b2 == 0 {
lo = v
} else {
hi = v
}
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128ExtractLane:
hi, lo := ce.popValue(), ce.popValue()
var v uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
var u8 byte
if op.b2 < 8 {
u8 = byte(lo >> (op.b2 * 8))
} else {
u8 = byte(hi >> ((op.b2 - 8) * 8))
}
if op.b3 {
// sign-extend.
v = uint64(uint32(int8(u8)))
} else {
v = uint64(u8)
}
case wazeroir.ShapeI16x8:
var u16 uint16
if op.b2 < 4 {
u16 = uint16(lo >> (op.b2 * 16))
} else {
u16 = uint16(hi >> ((op.b2 - 4) * 16))
}
if op.b3 {
// sign-extend.
v = uint64(uint32(int16(u16)))
} else {
v = uint64(u16)
}
case wazeroir.ShapeI32x4, wazeroir.ShapeF32x4:
if op.b2 < 2 {
v = uint64(uint32(lo >> (op.b2 * 32)))
} else {
v = uint64(uint32(hi >> ((op.b2 - 2) * 32)))
}
case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2:
if op.b2 == 0 {
v = lo
} else {
v = hi
}
}
ce.pushValue(v)
frame.pc++
case wazeroir.OperationKindV128Splat:
v := ce.popValue()
var hi, lo uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
v8 := uint64(byte(v))<<56 | uint64(byte(v))<<48 | uint64(byte(v))<<40 | uint64(byte(v))<<32 |
uint64(byte(v))<<24 | uint64(byte(v))<<16 | uint64(byte(v))<<8 | uint64(byte(v))
hi, lo = v8, v8
case wazeroir.ShapeI16x8:
v4 := uint64(uint16(v))<<48 | uint64(uint16(v))<<32 | uint64(uint16(v))<<16 | uint64(uint16(v))
hi, lo = v4, v4
case wazeroir.ShapeI32x4, wazeroir.ShapeF32x4:
v2 := uint64(uint32(v))<<32 | uint64(uint32(v))
lo, hi = v2, v2
case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2:
lo, hi = v, v
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Swizzle:
idxHi, idxLo := ce.popValue(), ce.popValue()
baseHi, baseLo := ce.popValue(), ce.popValue()
var newVal [16]byte
for i := 0; i < 16; i++ {
var id byte
if i < 8 {
id = byte(idxLo >> (i * 8))
} else {
id = byte(idxHi >> ((i - 8) * 8))
}
if id < 8 {
newVal[i] = byte(baseLo >> (id * 8))
} else if id < 16 {
newVal[i] = byte(baseHi >> ((id - 8) * 8))
}
}
ce.pushValue(binary.LittleEndian.Uint64(newVal[:8]))
ce.pushValue(binary.LittleEndian.Uint64(newVal[8:]))
frame.pc++
case wazeroir.OperationKindV128Shuffle:
xHi, xLo, yHi, yLo := ce.popValue(), ce.popValue(), ce.popValue(), ce.popValue()
var newVal [16]byte
for i, l := range op.us {
if l < 8 {
newVal[i] = byte(yLo >> (l * 8))
} else if l < 16 {
newVal[i] = byte(yHi >> ((l - 8) * 8))
} else if l < 24 {
newVal[i] = byte(xLo >> ((l - 16) * 8))
} else if l < 32 {
newVal[i] = byte(xHi >> ((l - 24) * 8))
}
}
ce.pushValue(binary.LittleEndian.Uint64(newVal[:8]))
ce.pushValue(binary.LittleEndian.Uint64(newVal[8:]))
frame.pc++
case wazeroir.OperationKindV128AnyTrue:
hi, lo := ce.popValue(), ce.popValue()
if hi != 0 || lo != 0 {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindV128AllTrue:
hi, lo := ce.popValue(), ce.popValue()
var ret bool
switch op.b1 {
case wazeroir.ShapeI8x16:
ret = (uint8(lo) != 0) && (uint8(lo>>8) != 0) && (uint8(lo>>16) != 0) && (uint8(lo>>24) != 0) &&
(uint8(lo>>32) != 0) && (uint8(lo>>40) != 0) && (uint8(lo>>48) != 0) && (uint8(lo>>56) != 0) &&
(uint8(hi) != 0) && (uint8(hi>>8) != 0) && (uint8(hi>>16) != 0) && (uint8(hi>>24) != 0) &&
(uint8(hi>>32) != 0) && (uint8(hi>>40) != 0) && (uint8(hi>>48) != 0) && (uint8(hi>>56) != 0)
case wazeroir.ShapeI16x8:
ret = (uint16(lo) != 0) && (uint16(lo>>16) != 0) && (uint16(lo>>32) != 0) && (uint16(lo>>48) != 0) &&
(uint16(hi) != 0) && (uint16(hi>>16) != 0) && (uint16(hi>>32) != 0) && (uint16(hi>>48) != 0)
case wazeroir.ShapeI32x4:
ret = (uint32(lo) != 0) && (uint32(lo>>32) != 0) &&
(uint32(hi) != 0) && (uint32(hi>>32) != 0)
case wazeroir.ShapeI64x2:
ret = (lo != 0) &&
(hi != 0)
}
if ret {
ce.pushValue(1)
} else {
ce.pushValue(0)
}
frame.pc++
case wazeroir.OperationKindV128BitMask:
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#bitmask-extraction
hi, lo := ce.popValue(), ce.popValue()
var res uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 8; i++ {
if int8(lo>>(i*8)) < 0 {
res |= 1 << i
}
}
for i := 0; i < 8; i++ {
if int8(hi>>(i*8)) < 0 {
res |= 1 << (i + 8)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 4; i++ {
if int16(lo>>(i*16)) < 0 {
res |= 1 << i
}
}
for i := 0; i < 4; i++ {
if int16(hi>>(i*16)) < 0 {
res |= 1 << (i + 4)
}
}
case wazeroir.ShapeI32x4:
for i := 0; i < 2; i++ {
if int32(lo>>(i*32)) < 0 {
res |= 1 << i
}
}
for i := 0; i < 2; i++ {
if int32(hi>>(i*32)) < 0 {
res |= 1 << (i + 2)
}
}
case wazeroir.ShapeI64x2:
if int64(lo) < 0 {
res |= 0b01
}
if int(hi) < 0 {
res |= 0b10
}
}
ce.pushValue(res)
frame.pc++
case wazeroir.OperationKindV128And:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
ce.pushValue(x1Lo & x2Lo)
ce.pushValue(x1Hi & x2Hi)
frame.pc++
case wazeroir.OperationKindV128Not:
hi, lo := ce.popValue(), ce.popValue()
ce.pushValue(^lo)
ce.pushValue(^hi)
frame.pc++
case wazeroir.OperationKindV128Or:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
ce.pushValue(x1Lo | x2Lo)
ce.pushValue(x1Hi | x2Hi)
frame.pc++
case wazeroir.OperationKindV128Xor:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
ce.pushValue(x1Lo ^ x2Lo)
ce.pushValue(x1Hi ^ x2Hi)
frame.pc++
case wazeroir.OperationKindV128Bitselect:
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#bitwise-select
cHi, cLo := ce.popValue(), ce.popValue()
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
// v128.or(v128.and(v1, c), v128.and(v2, v128.not(c)))
ce.pushValue((x1Lo & cLo) | (x2Lo & (^cLo)))
ce.pushValue((x1Hi & cHi) | (x2Hi & (^cHi)))
frame.pc++
case wazeroir.OperationKindV128AndNot:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
ce.pushValue(x1Lo & (^x2Lo))
ce.pushValue(x1Hi & (^x2Hi))
frame.pc++
case wazeroir.OperationKindV128Shl:
s := ce.popValue()
hi, lo := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
s = s % 8
lo = uint64(uint8(lo<<s)) |
uint64(uint8((lo>>8)<<s))<<8 |
uint64(uint8((lo>>16)<<s))<<16 |
uint64(uint8((lo>>24)<<s))<<24 |
uint64(uint8((lo>>32)<<s))<<32 |
uint64(uint8((lo>>40)<<s))<<40 |
uint64(uint8((lo>>48)<<s))<<48 |
uint64(uint8((lo>>56)<<s))<<56
hi = uint64(uint8(hi<<s)) |
uint64(uint8((hi>>8)<<s))<<8 |
uint64(uint8((hi>>16)<<s))<<16 |
uint64(uint8((hi>>24)<<s))<<24 |
uint64(uint8((hi>>32)<<s))<<32 |
uint64(uint8((hi>>40)<<s))<<40 |
uint64(uint8((hi>>48)<<s))<<48 |
uint64(uint8((hi>>56)<<s))<<56
case wazeroir.ShapeI16x8:
s = s % 16
lo = uint64(uint16(lo<<s)) |
uint64(uint16((lo>>16)<<s))<<16 |
uint64(uint16((lo>>32)<<s))<<32 |
uint64(uint16((lo>>48)<<s))<<48
hi = uint64(uint16(hi<<s)) |
uint64(uint16((hi>>16)<<s))<<16 |
uint64(uint16((hi>>32)<<s))<<32 |
uint64(uint16((hi>>48)<<s))<<48
case wazeroir.ShapeI32x4:
s = s % 32
lo = uint64(uint32(lo<<s)) | uint64(uint32((lo>>32)<<s))<<32
hi = uint64(uint32(hi<<s)) | uint64(uint32((hi>>32)<<s))<<32
case wazeroir.ShapeI64x2:
s = s % 64
lo = lo << s
hi = hi << s
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Shr:
s := ce.popValue()
hi, lo := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
s = s % 8
if op.b3 { // signed
lo = uint64(uint8(int8(lo)>>s)) |
uint64(uint8(int8(lo>>8)>>s))<<8 |
uint64(uint8(int8(lo>>16)>>s))<<16 |
uint64(uint8(int8(lo>>24)>>s))<<24 |
uint64(uint8(int8(lo>>32)>>s))<<32 |
uint64(uint8(int8(lo>>40)>>s))<<40 |
uint64(uint8(int8(lo>>48)>>s))<<48 |
uint64(uint8(int8(lo>>56)>>s))<<56
hi = uint64(uint8(int8(hi)>>s)) |
uint64(uint8(int8(hi>>8)>>s))<<8 |
uint64(uint8(int8(hi>>16)>>s))<<16 |
uint64(uint8(int8(hi>>24)>>s))<<24 |
uint64(uint8(int8(hi>>32)>>s))<<32 |
uint64(uint8(int8(hi>>40)>>s))<<40 |
uint64(uint8(int8(hi>>48)>>s))<<48 |
uint64(uint8(int8(hi>>56)>>s))<<56
} else {
lo = uint64(uint8(lo)>>s) |
uint64(uint8(lo>>8)>>s)<<8 |
uint64(uint8(lo>>16)>>s)<<16 |
uint64(uint8(lo>>24)>>s)<<24 |
uint64(uint8(lo>>32)>>s)<<32 |
uint64(uint8(lo>>40)>>s)<<40 |
uint64(uint8(lo>>48)>>s)<<48 |
uint64(uint8(lo>>56)>>s)<<56
hi = uint64(uint8(hi)>>s) |
uint64(uint8(hi>>8)>>s)<<8 |
uint64(uint8(hi>>16)>>s)<<16 |
uint64(uint8(hi>>24)>>s)<<24 |
uint64(uint8(hi>>32)>>s)<<32 |
uint64(uint8(hi>>40)>>s)<<40 |
uint64(uint8(hi>>48)>>s)<<48 |
uint64(uint8(hi>>56)>>s)<<56
}
case wazeroir.ShapeI16x8:
s = s % 16
if op.b3 { // signed
lo = uint64(uint16(int16(lo)>>s)) |
uint64(uint16(int16(lo>>16)>>s))<<16 |
uint64(uint16(int16(lo>>32)>>s))<<32 |
uint64(uint16(int16(lo>>48)>>s))<<48
hi = uint64(uint16(int16(hi)>>s)) |
uint64(uint16(int16(hi>>16)>>s))<<16 |
uint64(uint16(int16(hi>>32)>>s))<<32 |
uint64(uint16(int16(hi>>48)>>s))<<48
} else {
lo = uint64(uint16(lo)>>s) |
uint64(uint16(lo>>16)>>s)<<16 |
uint64(uint16(lo>>32)>>s)<<32 |
uint64(uint16(lo>>48)>>s)<<48
hi = uint64(uint16(hi)>>s) |
uint64(uint16(hi>>16)>>s)<<16 |
uint64(uint16(hi>>32)>>s)<<32 |
uint64(uint16(hi>>48)>>s)<<48
}
case wazeroir.ShapeI32x4:
s = s % 32
if op.b3 {
lo = uint64(uint32(int32(lo)>>s)) | uint64(uint32(int32(lo>>32)>>s))<<32
hi = uint64(uint32(int32(hi)>>s)) | uint64(uint32(int32(hi>>32)>>s))<<32
} else {
lo = uint64(uint32(lo)>>s) | uint64(uint32(lo>>32)>>s)<<32
hi = uint64(uint32(hi)>>s) | uint64(uint32(hi>>32)>>s)<<32
}
case wazeroir.ShapeI64x2:
s = s % 64
if op.b3 { // signed
lo = uint64(int64(lo) >> s)
hi = uint64(int64(hi) >> s)
} else {
lo = lo >> s
hi = hi >> s
}
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Cmp:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
var result []bool
switch op.b1 {
case wazeroir.V128CmpTypeI8x16Eq:
result = []bool{
byte(x1Lo>>0) == byte(x2Lo>>0), byte(x1Lo>>8) == byte(x2Lo>>8),
byte(x1Lo>>16) == byte(x2Lo>>16), byte(x1Lo>>24) == byte(x2Lo>>24),
byte(x1Lo>>32) == byte(x2Lo>>32), byte(x1Lo>>40) == byte(x2Lo>>40),
byte(x1Lo>>48) == byte(x2Lo>>48), byte(x1Lo>>56) == byte(x2Lo>>56),
byte(x1Hi>>0) == byte(x2Hi>>0), byte(x1Hi>>8) == byte(x2Hi>>8),
byte(x1Hi>>16) == byte(x2Hi>>16), byte(x1Hi>>24) == byte(x2Hi>>24),
byte(x1Hi>>32) == byte(x2Hi>>32), byte(x1Hi>>40) == byte(x2Hi>>40),
byte(x1Hi>>48) == byte(x2Hi>>48), byte(x1Hi>>56) == byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16Ne:
result = []bool{
byte(x1Lo>>0) != byte(x2Lo>>0), byte(x1Lo>>8) != byte(x2Lo>>8),
byte(x1Lo>>16) != byte(x2Lo>>16), byte(x1Lo>>24) != byte(x2Lo>>24),
byte(x1Lo>>32) != byte(x2Lo>>32), byte(x1Lo>>40) != byte(x2Lo>>40),
byte(x1Lo>>48) != byte(x2Lo>>48), byte(x1Lo>>56) != byte(x2Lo>>56),
byte(x1Hi>>0) != byte(x2Hi>>0), byte(x1Hi>>8) != byte(x2Hi>>8),
byte(x1Hi>>16) != byte(x2Hi>>16), byte(x1Hi>>24) != byte(x2Hi>>24),
byte(x1Hi>>32) != byte(x2Hi>>32), byte(x1Hi>>40) != byte(x2Hi>>40),
byte(x1Hi>>48) != byte(x2Hi>>48), byte(x1Hi>>56) != byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16LtS:
result = []bool{
int8(x1Lo>>0) < int8(x2Lo>>0), int8(x1Lo>>8) < int8(x2Lo>>8),
int8(x1Lo>>16) < int8(x2Lo>>16), int8(x1Lo>>24) < int8(x2Lo>>24),
int8(x1Lo>>32) < int8(x2Lo>>32), int8(x1Lo>>40) < int8(x2Lo>>40),
int8(x1Lo>>48) < int8(x2Lo>>48), int8(x1Lo>>56) < int8(x2Lo>>56),
int8(x1Hi>>0) < int8(x2Hi>>0), int8(x1Hi>>8) < int8(x2Hi>>8),
int8(x1Hi>>16) < int8(x2Hi>>16), int8(x1Hi>>24) < int8(x2Hi>>24),
int8(x1Hi>>32) < int8(x2Hi>>32), int8(x1Hi>>40) < int8(x2Hi>>40),
int8(x1Hi>>48) < int8(x2Hi>>48), int8(x1Hi>>56) < int8(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16LtU:
result = []bool{
byte(x1Lo>>0) < byte(x2Lo>>0), byte(x1Lo>>8) < byte(x2Lo>>8),
byte(x1Lo>>16) < byte(x2Lo>>16), byte(x1Lo>>24) < byte(x2Lo>>24),
byte(x1Lo>>32) < byte(x2Lo>>32), byte(x1Lo>>40) < byte(x2Lo>>40),
byte(x1Lo>>48) < byte(x2Lo>>48), byte(x1Lo>>56) < byte(x2Lo>>56),
byte(x1Hi>>0) < byte(x2Hi>>0), byte(x1Hi>>8) < byte(x2Hi>>8),
byte(x1Hi>>16) < byte(x2Hi>>16), byte(x1Hi>>24) < byte(x2Hi>>24),
byte(x1Hi>>32) < byte(x2Hi>>32), byte(x1Hi>>40) < byte(x2Hi>>40),
byte(x1Hi>>48) < byte(x2Hi>>48), byte(x1Hi>>56) < byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16GtS:
result = []bool{
int8(x1Lo>>0) > int8(x2Lo>>0), int8(x1Lo>>8) > int8(x2Lo>>8),
int8(x1Lo>>16) > int8(x2Lo>>16), int8(x1Lo>>24) > int8(x2Lo>>24),
int8(x1Lo>>32) > int8(x2Lo>>32), int8(x1Lo>>40) > int8(x2Lo>>40),
int8(x1Lo>>48) > int8(x2Lo>>48), int8(x1Lo>>56) > int8(x2Lo>>56),
int8(x1Hi>>0) > int8(x2Hi>>0), int8(x1Hi>>8) > int8(x2Hi>>8),
int8(x1Hi>>16) > int8(x2Hi>>16), int8(x1Hi>>24) > int8(x2Hi>>24),
int8(x1Hi>>32) > int8(x2Hi>>32), int8(x1Hi>>40) > int8(x2Hi>>40),
int8(x1Hi>>48) > int8(x2Hi>>48), int8(x1Hi>>56) > int8(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16GtU:
result = []bool{
byte(x1Lo>>0) > byte(x2Lo>>0), byte(x1Lo>>8) > byte(x2Lo>>8),
byte(x1Lo>>16) > byte(x2Lo>>16), byte(x1Lo>>24) > byte(x2Lo>>24),
byte(x1Lo>>32) > byte(x2Lo>>32), byte(x1Lo>>40) > byte(x2Lo>>40),
byte(x1Lo>>48) > byte(x2Lo>>48), byte(x1Lo>>56) > byte(x2Lo>>56),
byte(x1Hi>>0) > byte(x2Hi>>0), byte(x1Hi>>8) > byte(x2Hi>>8),
byte(x1Hi>>16) > byte(x2Hi>>16), byte(x1Hi>>24) > byte(x2Hi>>24),
byte(x1Hi>>32) > byte(x2Hi>>32), byte(x1Hi>>40) > byte(x2Hi>>40),
byte(x1Hi>>48) > byte(x2Hi>>48), byte(x1Hi>>56) > byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16LeS:
result = []bool{
int8(x1Lo>>0) <= int8(x2Lo>>0), int8(x1Lo>>8) <= int8(x2Lo>>8),
int8(x1Lo>>16) <= int8(x2Lo>>16), int8(x1Lo>>24) <= int8(x2Lo>>24),
int8(x1Lo>>32) <= int8(x2Lo>>32), int8(x1Lo>>40) <= int8(x2Lo>>40),
int8(x1Lo>>48) <= int8(x2Lo>>48), int8(x1Lo>>56) <= int8(x2Lo>>56),
int8(x1Hi>>0) <= int8(x2Hi>>0), int8(x1Hi>>8) <= int8(x2Hi>>8),
int8(x1Hi>>16) <= int8(x2Hi>>16), int8(x1Hi>>24) <= int8(x2Hi>>24),
int8(x1Hi>>32) <= int8(x2Hi>>32), int8(x1Hi>>40) <= int8(x2Hi>>40),
int8(x1Hi>>48) <= int8(x2Hi>>48), int8(x1Hi>>56) <= int8(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16LeU:
result = []bool{
byte(x1Lo>>0) <= byte(x2Lo>>0), byte(x1Lo>>8) <= byte(x2Lo>>8),
byte(x1Lo>>16) <= byte(x2Lo>>16), byte(x1Lo>>24) <= byte(x2Lo>>24),
byte(x1Lo>>32) <= byte(x2Lo>>32), byte(x1Lo>>40) <= byte(x2Lo>>40),
byte(x1Lo>>48) <= byte(x2Lo>>48), byte(x1Lo>>56) <= byte(x2Lo>>56),
byte(x1Hi>>0) <= byte(x2Hi>>0), byte(x1Hi>>8) <= byte(x2Hi>>8),
byte(x1Hi>>16) <= byte(x2Hi>>16), byte(x1Hi>>24) <= byte(x2Hi>>24),
byte(x1Hi>>32) <= byte(x2Hi>>32), byte(x1Hi>>40) <= byte(x2Hi>>40),
byte(x1Hi>>48) <= byte(x2Hi>>48), byte(x1Hi>>56) <= byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16GeS:
result = []bool{
int8(x1Lo>>0) >= int8(x2Lo>>0), int8(x1Lo>>8) >= int8(x2Lo>>8),
int8(x1Lo>>16) >= int8(x2Lo>>16), int8(x1Lo>>24) >= int8(x2Lo>>24),
int8(x1Lo>>32) >= int8(x2Lo>>32), int8(x1Lo>>40) >= int8(x2Lo>>40),
int8(x1Lo>>48) >= int8(x2Lo>>48), int8(x1Lo>>56) >= int8(x2Lo>>56),
int8(x1Hi>>0) >= int8(x2Hi>>0), int8(x1Hi>>8) >= int8(x2Hi>>8),
int8(x1Hi>>16) >= int8(x2Hi>>16), int8(x1Hi>>24) >= int8(x2Hi>>24),
int8(x1Hi>>32) >= int8(x2Hi>>32), int8(x1Hi>>40) >= int8(x2Hi>>40),
int8(x1Hi>>48) >= int8(x2Hi>>48), int8(x1Hi>>56) >= int8(x2Hi>>56),
}
case wazeroir.V128CmpTypeI8x16GeU:
result = []bool{
byte(x1Lo>>0) >= byte(x2Lo>>0), byte(x1Lo>>8) >= byte(x2Lo>>8),
byte(x1Lo>>16) >= byte(x2Lo>>16), byte(x1Lo>>24) >= byte(x2Lo>>24),
byte(x1Lo>>32) >= byte(x2Lo>>32), byte(x1Lo>>40) >= byte(x2Lo>>40),
byte(x1Lo>>48) >= byte(x2Lo>>48), byte(x1Lo>>56) >= byte(x2Lo>>56),
byte(x1Hi>>0) >= byte(x2Hi>>0), byte(x1Hi>>8) >= byte(x2Hi>>8),
byte(x1Hi>>16) >= byte(x2Hi>>16), byte(x1Hi>>24) >= byte(x2Hi>>24),
byte(x1Hi>>32) >= byte(x2Hi>>32), byte(x1Hi>>40) >= byte(x2Hi>>40),
byte(x1Hi>>48) >= byte(x2Hi>>48), byte(x1Hi>>56) >= byte(x2Hi>>56),
}
case wazeroir.V128CmpTypeI16x8Eq:
result = []bool{
uint16(x1Lo>>0) == uint16(x2Lo>>0), uint16(x1Lo>>16) == uint16(x2Lo>>16),
uint16(x1Lo>>32) == uint16(x2Lo>>32), uint16(x1Lo>>48) == uint16(x2Lo>>48),
uint16(x1Hi>>0) == uint16(x2Hi>>0), uint16(x1Hi>>16) == uint16(x2Hi>>16),
uint16(x1Hi>>32) == uint16(x2Hi>>32), uint16(x1Hi>>48) == uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8Ne:
result = []bool{
uint16(x1Lo>>0) != uint16(x2Lo>>0), uint16(x1Lo>>16) != uint16(x2Lo>>16),
uint16(x1Lo>>32) != uint16(x2Lo>>32), uint16(x1Lo>>48) != uint16(x2Lo>>48),
uint16(x1Hi>>0) != uint16(x2Hi>>0), uint16(x1Hi>>16) != uint16(x2Hi>>16),
uint16(x1Hi>>32) != uint16(x2Hi>>32), uint16(x1Hi>>48) != uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8LtS:
result = []bool{
int16(x1Lo>>0) < int16(x2Lo>>0), int16(x1Lo>>16) < int16(x2Lo>>16),
int16(x1Lo>>32) < int16(x2Lo>>32), int16(x1Lo>>48) < int16(x2Lo>>48),
int16(x1Hi>>0) < int16(x2Hi>>0), int16(x1Hi>>16) < int16(x2Hi>>16),
int16(x1Hi>>32) < int16(x2Hi>>32), int16(x1Hi>>48) < int16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8LtU:
result = []bool{
uint16(x1Lo>>0) < uint16(x2Lo>>0), uint16(x1Lo>>16) < uint16(x2Lo>>16),
uint16(x1Lo>>32) < uint16(x2Lo>>32), uint16(x1Lo>>48) < uint16(x2Lo>>48),
uint16(x1Hi>>0) < uint16(x2Hi>>0), uint16(x1Hi>>16) < uint16(x2Hi>>16),
uint16(x1Hi>>32) < uint16(x2Hi>>32), uint16(x1Hi>>48) < uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8GtS:
result = []bool{
int16(x1Lo>>0) > int16(x2Lo>>0), int16(x1Lo>>16) > int16(x2Lo>>16),
int16(x1Lo>>32) > int16(x2Lo>>32), int16(x1Lo>>48) > int16(x2Lo>>48),
int16(x1Hi>>0) > int16(x2Hi>>0), int16(x1Hi>>16) > int16(x2Hi>>16),
int16(x1Hi>>32) > int16(x2Hi>>32), int16(x1Hi>>48) > int16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8GtU:
result = []bool{
uint16(x1Lo>>0) > uint16(x2Lo>>0), uint16(x1Lo>>16) > uint16(x2Lo>>16),
uint16(x1Lo>>32) > uint16(x2Lo>>32), uint16(x1Lo>>48) > uint16(x2Lo>>48),
uint16(x1Hi>>0) > uint16(x2Hi>>0), uint16(x1Hi>>16) > uint16(x2Hi>>16),
uint16(x1Hi>>32) > uint16(x2Hi>>32), uint16(x1Hi>>48) > uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8LeS:
result = []bool{
int16(x1Lo>>0) <= int16(x2Lo>>0), int16(x1Lo>>16) <= int16(x2Lo>>16),
int16(x1Lo>>32) <= int16(x2Lo>>32), int16(x1Lo>>48) <= int16(x2Lo>>48),
int16(x1Hi>>0) <= int16(x2Hi>>0), int16(x1Hi>>16) <= int16(x2Hi>>16),
int16(x1Hi>>32) <= int16(x2Hi>>32), int16(x1Hi>>48) <= int16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8LeU:
result = []bool{
uint16(x1Lo>>0) <= uint16(x2Lo>>0), uint16(x1Lo>>16) <= uint16(x2Lo>>16),
uint16(x1Lo>>32) <= uint16(x2Lo>>32), uint16(x1Lo>>48) <= uint16(x2Lo>>48),
uint16(x1Hi>>0) <= uint16(x2Hi>>0), uint16(x1Hi>>16) <= uint16(x2Hi>>16),
uint16(x1Hi>>32) <= uint16(x2Hi>>32), uint16(x1Hi>>48) <= uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8GeS:
result = []bool{
int16(x1Lo>>0) >= int16(x2Lo>>0), int16(x1Lo>>16) >= int16(x2Lo>>16),
int16(x1Lo>>32) >= int16(x2Lo>>32), int16(x1Lo>>48) >= int16(x2Lo>>48),
int16(x1Hi>>0) >= int16(x2Hi>>0), int16(x1Hi>>16) >= int16(x2Hi>>16),
int16(x1Hi>>32) >= int16(x2Hi>>32), int16(x1Hi>>48) >= int16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI16x8GeU:
result = []bool{
uint16(x1Lo>>0) >= uint16(x2Lo>>0), uint16(x1Lo>>16) >= uint16(x2Lo>>16),
uint16(x1Lo>>32) >= uint16(x2Lo>>32), uint16(x1Lo>>48) >= uint16(x2Lo>>48),
uint16(x1Hi>>0) >= uint16(x2Hi>>0), uint16(x1Hi>>16) >= uint16(x2Hi>>16),
uint16(x1Hi>>32) >= uint16(x2Hi>>32), uint16(x1Hi>>48) >= uint16(x2Hi>>48),
}
case wazeroir.V128CmpTypeI32x4Eq:
result = []bool{
uint32(x1Lo>>0) == uint32(x2Lo>>0), uint32(x1Lo>>32) == uint32(x2Lo>>32),
uint32(x1Hi>>0) == uint32(x2Hi>>0), uint32(x1Hi>>32) == uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4Ne:
result = []bool{
uint32(x1Lo>>0) != uint32(x2Lo>>0), uint32(x1Lo>>32) != uint32(x2Lo>>32),
uint32(x1Hi>>0) != uint32(x2Hi>>0), uint32(x1Hi>>32) != uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4LtS:
result = []bool{
int32(x1Lo>>0) < int32(x2Lo>>0), int32(x1Lo>>32) < int32(x2Lo>>32),
int32(x1Hi>>0) < int32(x2Hi>>0), int32(x1Hi>>32) < int32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4LtU:
result = []bool{
uint32(x1Lo>>0) < uint32(x2Lo>>0), uint32(x1Lo>>32) < uint32(x2Lo>>32),
uint32(x1Hi>>0) < uint32(x2Hi>>0), uint32(x1Hi>>32) < uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4GtS:
result = []bool{
int32(x1Lo>>0) > int32(x2Lo>>0), int32(x1Lo>>32) > int32(x2Lo>>32),
int32(x1Hi>>0) > int32(x2Hi>>0), int32(x1Hi>>32) > int32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4GtU:
result = []bool{
uint32(x1Lo>>0) > uint32(x2Lo>>0), uint32(x1Lo>>32) > uint32(x2Lo>>32),
uint32(x1Hi>>0) > uint32(x2Hi>>0), uint32(x1Hi>>32) > uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4LeS:
result = []bool{
int32(x1Lo>>0) <= int32(x2Lo>>0), int32(x1Lo>>32) <= int32(x2Lo>>32),
int32(x1Hi>>0) <= int32(x2Hi>>0), int32(x1Hi>>32) <= int32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4LeU:
result = []bool{
uint32(x1Lo>>0) <= uint32(x2Lo>>0), uint32(x1Lo>>32) <= uint32(x2Lo>>32),
uint32(x1Hi>>0) <= uint32(x2Hi>>0), uint32(x1Hi>>32) <= uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4GeS:
result = []bool{
int32(x1Lo>>0) >= int32(x2Lo>>0), int32(x1Lo>>32) >= int32(x2Lo>>32),
int32(x1Hi>>0) >= int32(x2Hi>>0), int32(x1Hi>>32) >= int32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI32x4GeU:
result = []bool{
uint32(x1Lo>>0) >= uint32(x2Lo>>0), uint32(x1Lo>>32) >= uint32(x2Lo>>32),
uint32(x1Hi>>0) >= uint32(x2Hi>>0), uint32(x1Hi>>32) >= uint32(x2Hi>>32),
}
case wazeroir.V128CmpTypeI64x2Eq:
result = []bool{x1Lo == x2Lo, x1Hi == x2Hi}
case wazeroir.V128CmpTypeI64x2Ne:
result = []bool{x1Lo != x2Lo, x1Hi != x2Hi}
case wazeroir.V128CmpTypeI64x2LtS:
result = []bool{int64(x1Lo) < int64(x2Lo), int64(x1Hi) < int64(x2Hi)}
case wazeroir.V128CmpTypeI64x2GtS:
result = []bool{int64(x1Lo) > int64(x2Lo), int64(x1Hi) > int64(x2Hi)}
case wazeroir.V128CmpTypeI64x2LeS:
result = []bool{int64(x1Lo) <= int64(x2Lo), int64(x1Hi) <= int64(x2Hi)}
case wazeroir.V128CmpTypeI64x2GeS:
result = []bool{int64(x1Lo) >= int64(x2Lo), int64(x1Hi) >= int64(x2Hi)}
case wazeroir.V128CmpTypeF32x4Eq:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) == math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) == math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) == math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) == math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF32x4Ne:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) != math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) != math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) != math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) != math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF32x4Lt:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) < math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) < math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) < math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) < math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF32x4Gt:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) > math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) > math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) > math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) > math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF32x4Le:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) <= math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) <= math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) <= math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) <= math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF32x4Ge:
result = []bool{
math.Float32frombits(uint32(x1Lo>>0)) >= math.Float32frombits(uint32(x2Lo>>0)),
math.Float32frombits(uint32(x1Lo>>32)) >= math.Float32frombits(uint32(x2Lo>>32)),
math.Float32frombits(uint32(x1Hi>>0)) >= math.Float32frombits(uint32(x2Hi>>0)),
math.Float32frombits(uint32(x1Hi>>32)) >= math.Float32frombits(uint32(x2Hi>>32)),
}
case wazeroir.V128CmpTypeF64x2Eq:
result = []bool{
math.Float64frombits(x1Lo) == math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) == math.Float64frombits(x2Hi),
}
case wazeroir.V128CmpTypeF64x2Ne:
result = []bool{
math.Float64frombits(x1Lo) != math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) != math.Float64frombits(x2Hi),
}
case wazeroir.V128CmpTypeF64x2Lt:
result = []bool{
math.Float64frombits(x1Lo) < math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) < math.Float64frombits(x2Hi),
}
case wazeroir.V128CmpTypeF64x2Gt:
result = []bool{
math.Float64frombits(x1Lo) > math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) > math.Float64frombits(x2Hi),
}
case wazeroir.V128CmpTypeF64x2Le:
result = []bool{
math.Float64frombits(x1Lo) <= math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) <= math.Float64frombits(x2Hi),
}
case wazeroir.V128CmpTypeF64x2Ge:
result = []bool{
math.Float64frombits(x1Lo) >= math.Float64frombits(x2Lo),
math.Float64frombits(x1Hi) >= math.Float64frombits(x2Hi),
}
}
var retLo, retHi uint64
laneNum := len(result)
switch laneNum {
case 16:
for i, b := range result {
if b {
if i < 8 {
retLo |= 0xff << (i * 8)
} else {
retHi |= 0xff << ((i - 8) * 8)
}
}
}
case 8:
for i, b := range result {
if b {
if i < 4 {
retLo |= 0xffff << (i * 16)
} else {
retHi |= 0xffff << ((i - 4) * 16)
}
}
}
case 4:
for i, b := range result {
if b {
if i < 2 {
retLo |= 0xffff_ffff << (i * 32)
} else {
retHi |= 0xffff_ffff << ((i - 2) * 32)
}
}
}
case 2:
if result[0] {
retLo = ^uint64(0)
}
if result[1] {
retHi = ^uint64(0)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128AddSat:
x2hi, x2Lo := ce.popValue(), ce.popValue()
x1hi, x1Lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
// Lane-wise addition while saturating the overflowing values.
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#saturating-integer-addition
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 16; i++ {
var v, w byte
if i < 8 {
v, w = byte(x1Lo>>(i*8)), byte(x2Lo>>(i*8))
} else {
v, w = byte(x1hi>>((i-8)*8)), byte(x2hi>>((i-8)*8))
}
var uv uint64
if op.b3 { // signed
if subbed := int64(int8(v)) + int64(int8(w)); subbed < math.MinInt8 {
uv = uint64(byte(0x80))
} else if subbed > math.MaxInt8 {
uv = uint64(byte(0x7f))
} else {
uv = uint64(byte(int8(subbed)))
}
} else {
if subbed := int64(v) + int64(w); subbed < 0 {
uv = uint64(byte(0))
} else if subbed > math.MaxUint8 {
uv = uint64(byte(0xff))
} else {
uv = uint64(byte(subbed))
}
}
if i < 8 { // first 8 lanes are on lower 64bits.
retLo |= uv << (i * 8)
} else {
retHi |= uv << ((i - 8) * 8)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 8; i++ {
var v, w uint16
if i < 4 {
v, w = uint16(x1Lo>>(i*16)), uint16(x2Lo>>(i*16))
} else {
v, w = uint16(x1hi>>((i-4)*16)), uint16(x2hi>>((i-4)*16))
}
var uv uint64
if op.b3 { // signed
if added := int64(int16(v)) + int64(int16(w)); added < math.MinInt16 {
uv = uint64(uint16(0x8000))
} else if added > math.MaxInt16 {
uv = uint64(uint16(0x7fff))
} else {
uv = uint64(uint16(int16(added)))
}
} else {
if added := int64(v) + int64(w); added < 0 {
uv = uint64(uint16(0))
} else if added > math.MaxUint16 {
uv = uint64(uint16(0xffff))
} else {
uv = uint64(uint16(added))
}
}
if i < 4 { // first 4 lanes are on lower 64bits.
retLo |= uv << (i * 16)
} else {
retHi |= uv << ((i - 4) * 16)
}
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128SubSat:
x2hi, x2Lo := ce.popValue(), ce.popValue()
x1hi, x1Lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
// Lane-wise subtraction while saturating the overflowing values.
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#saturating-integer-subtraction
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 16; i++ {
var v, w byte
if i < 8 {
v, w = byte(x1Lo>>(i*8)), byte(x2Lo>>(i*8))
} else {
v, w = byte(x1hi>>((i-8)*8)), byte(x2hi>>((i-8)*8))
}
var uv uint64
if op.b3 { // signed
if subbed := int64(int8(v)) - int64(int8(w)); subbed < math.MinInt8 {
uv = uint64(byte(0x80))
} else if subbed > math.MaxInt8 {
uv = uint64(byte(0x7f))
} else {
uv = uint64(byte(int8(subbed)))
}
} else {
if subbed := int64(v) - int64(w); subbed < 0 {
uv = uint64(byte(0))
} else if subbed > math.MaxUint8 {
uv = uint64(byte(0xff))
} else {
uv = uint64(byte(subbed))
}
}
if i < 8 {
retLo |= uv << (i * 8)
} else {
retHi |= uv << ((i - 8) * 8)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 8; i++ {
var v, w uint16
if i < 4 {
v, w = uint16(x1Lo>>(i*16)), uint16(x2Lo>>(i*16))
} else {
v, w = uint16(x1hi>>((i-4)*16)), uint16(x2hi>>((i-4)*16))
}
var uv uint64
if op.b3 { // signed
if subbed := int64(int16(v)) - int64(int16(w)); subbed < math.MinInt16 {
uv = uint64(uint16(0x8000))
} else if subbed > math.MaxInt16 {
uv = uint64(uint16(0x7fff))
} else {
uv = uint64(uint16(int16(subbed)))
}
} else {
if subbed := int64(v) - int64(w); subbed < 0 {
uv = uint64(uint16(0))
} else if subbed > math.MaxUint16 {
uv = uint64(uint16(0xffff))
} else {
uv = uint64(uint16(subbed))
}
}
if i < 4 {
retLo |= uv << (i * 16)
} else {
retHi |= uv << ((i - 4) * 16)
}
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Mul:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI16x8:
retHi = uint64(uint16(x1hi)*uint16(x2hi)) | (uint64(uint16(x1hi>>16)*uint16(x2hi>>16)) << 16) |
(uint64(uint16(x1hi>>32)*uint16(x2hi>>32)) << 32) | (uint64(uint16(x1hi>>48)*uint16(x2hi>>48)) << 48)
retLo = uint64(uint16(x1lo)*uint16(x2lo)) | (uint64(uint16(x1lo>>16)*uint16(x2lo>>16)) << 16) |
(uint64(uint16(x1lo>>32)*uint16(x2lo>>32)) << 32) | (uint64(uint16(x1lo>>48)*uint16(x2lo>>48)) << 48)
case wazeroir.ShapeI32x4:
retHi = uint64(uint32(x1hi)*uint32(x2hi)) | (uint64(uint32(x1hi>>32)*uint32(x2hi>>32)) << 32)
retLo = uint64(uint32(x1lo)*uint32(x2lo)) | (uint64(uint32(x1lo>>32)*uint32(x2lo>>32)) << 32)
case wazeroir.ShapeI64x2:
retHi = x1hi * x2hi
retLo = x1lo * x2lo
case wazeroir.ShapeF32x4:
retHi = mulFloat32bits(uint32(x1hi), uint32(x2hi)) | mulFloat32bits(uint32(x1hi>>32), uint32(x2hi>>32))<<32
retLo = mulFloat32bits(uint32(x1lo), uint32(x2lo)) | mulFloat32bits(uint32(x1lo>>32), uint32(x2lo>>32))<<32
case wazeroir.ShapeF64x2:
retHi = math.Float64bits(math.Float64frombits(x1hi) * math.Float64frombits(x2hi))
retLo = math.Float64bits(math.Float64frombits(x1lo) * math.Float64frombits(x2lo))
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Div:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
if op.b1 == wazeroir.ShapeF64x2 {
retHi = math.Float64bits(math.Float64frombits(x1hi) / math.Float64frombits(x2hi))
retLo = math.Float64bits(math.Float64frombits(x1lo) / math.Float64frombits(x2lo))
} else {
retHi = divFloat32bits(uint32(x1hi), uint32(x2hi)) | divFloat32bits(uint32(x1hi>>32), uint32(x2hi>>32))<<32
retLo = divFloat32bits(uint32(x1lo), uint32(x2lo)) | divFloat32bits(uint32(x1lo>>32), uint32(x2lo>>32))<<32
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Neg:
hi, lo := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
lo = uint64(-byte(lo)) | (uint64(-byte(lo>>8)) << 8) |
(uint64(-byte(lo>>16)) << 16) | (uint64(-byte(lo>>24)) << 24) |
(uint64(-byte(lo>>32)) << 32) | (uint64(-byte(lo>>40)) << 40) |
(uint64(-byte(lo>>48)) << 48) | (uint64(-byte(lo>>56)) << 56)
hi = uint64(-byte(hi)) | (uint64(-byte(hi>>8)) << 8) |
(uint64(-byte(hi>>16)) << 16) | (uint64(-byte(hi>>24)) << 24) |
(uint64(-byte(hi>>32)) << 32) | (uint64(-byte(hi>>40)) << 40) |
(uint64(-byte(hi>>48)) << 48) | (uint64(-byte(hi>>56)) << 56)
case wazeroir.ShapeI16x8:
hi = uint64(-uint16(hi)) | (uint64(-uint16(hi>>16)) << 16) |
(uint64(-uint16(hi>>32)) << 32) | (uint64(-uint16(hi>>48)) << 48)
lo = uint64(-uint16(lo)) | (uint64(-uint16(lo>>16)) << 16) |
(uint64(-uint16(lo>>32)) << 32) | (uint64(-uint16(lo>>48)) << 48)
case wazeroir.ShapeI32x4:
hi = uint64(-uint32(hi)) | (uint64(-uint32(hi>>32)) << 32)
lo = uint64(-uint32(lo)) | (uint64(-uint32(lo>>32)) << 32)
case wazeroir.ShapeI64x2:
hi = -hi
lo = -lo
case wazeroir.ShapeF32x4:
hi = uint64(math.Float32bits(-math.Float32frombits(uint32(hi)))) |
(uint64(math.Float32bits(-math.Float32frombits(uint32(hi>>32)))) << 32)
lo = uint64(math.Float32bits(-math.Float32frombits(uint32(lo)))) |
(uint64(math.Float32bits(-math.Float32frombits(uint32(lo>>32)))) << 32)
case wazeroir.ShapeF64x2:
hi = math.Float64bits(-math.Float64frombits(hi))
lo = math.Float64bits(-math.Float64frombits(lo))
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Sqrt:
hi, lo := ce.popValue(), ce.popValue()
if op.b1 == wazeroir.ShapeF64x2 {
hi = math.Float64bits(math.Sqrt(math.Float64frombits(hi)))
lo = math.Float64bits(math.Sqrt(math.Float64frombits(lo)))
} else {
hi = uint64(math.Float32bits(float32(math.Sqrt(float64(math.Float32frombits(uint32(hi))))))) |
(uint64(math.Float32bits(float32(math.Sqrt(float64(math.Float32frombits(uint32(hi>>32))))))) << 32)
lo = uint64(math.Float32bits(float32(math.Sqrt(float64(math.Float32frombits(uint32(lo))))))) |
(uint64(math.Float32bits(float32(math.Sqrt(float64(math.Float32frombits(uint32(lo>>32))))))) << 32)
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Abs:
hi, lo := ce.popValue(), ce.popValue()
switch op.b1 {
case wazeroir.ShapeI8x16:
lo = uint64(i8Abs(byte(lo))) | (uint64(i8Abs(byte(lo>>8))) << 8) |
(uint64(i8Abs(byte(lo>>16))) << 16) | (uint64(i8Abs(byte(lo>>24))) << 24) |
(uint64(i8Abs(byte(lo>>32))) << 32) | (uint64(i8Abs(byte(lo>>40))) << 40) |
(uint64(i8Abs(byte(lo>>48))) << 48) | (uint64(i8Abs(byte(lo>>56))) << 56)
hi = uint64(i8Abs(byte(hi))) | (uint64(i8Abs(byte(hi>>8))) << 8) |
(uint64(i8Abs(byte(hi>>16))) << 16) | (uint64(i8Abs(byte(hi>>24))) << 24) |
(uint64(i8Abs(byte(hi>>32))) << 32) | (uint64(i8Abs(byte(hi>>40))) << 40) |
(uint64(i8Abs(byte(hi>>48))) << 48) | (uint64(i8Abs(byte(hi>>56))) << 56)
case wazeroir.ShapeI16x8:
hi = uint64(i16Abs(uint16(hi))) | (uint64(i16Abs(uint16(hi>>16))) << 16) |
(uint64(i16Abs(uint16(hi>>32))) << 32) | (uint64(i16Abs(uint16(hi>>48))) << 48)
lo = uint64(i16Abs(uint16(lo))) | (uint64(i16Abs(uint16(lo>>16))) << 16) |
(uint64(i16Abs(uint16(lo>>32))) << 32) | (uint64(i16Abs(uint16(lo>>48))) << 48)
case wazeroir.ShapeI32x4:
hi = uint64(i32Abs(uint32(hi))) | (uint64(i32Abs(uint32(hi>>32))) << 32)
lo = uint64(i32Abs(uint32(lo))) | (uint64(i32Abs(uint32(lo>>32))) << 32)
case wazeroir.ShapeI64x2:
if int64(hi) < 0 {
hi = -hi
}
if int64(lo) < 0 {
lo = -lo
}
case wazeroir.ShapeF32x4:
hi = hi &^ (1<<31 | 1<<63)
lo = lo &^ (1<<31 | 1<<63)
case wazeroir.ShapeF64x2:
hi = hi &^ (1 << 63)
lo = lo &^ (1 << 63)
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Popcnt:
hi, lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
for i := 0; i < 16; i++ {
var v byte
if i < 8 {
v = byte(lo >> (i * 8))
} else {
v = byte(hi >> ((i - 8) * 8))
}
var cnt uint64
for i := 0; i < 8; i++ {
if (v>>i)&0b1 != 0 {
cnt++
}
}
if i < 8 {
retLo |= cnt << (i * 8)
} else {
retHi |= cnt << ((i - 8) * 8)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Min:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
if op.b3 { // signed
retLo = uint64(i8MinS(uint8(x1lo>>8), uint8(x2lo>>8)))<<8 | uint64(i8MinS(uint8(x1lo), uint8(x2lo))) |
uint64(i8MinS(uint8(x1lo>>24), uint8(x2lo>>24)))<<24 | uint64(i8MinS(uint8(x1lo>>16), uint8(x2lo>>16)))<<16 |
uint64(i8MinS(uint8(x1lo>>40), uint8(x2lo>>40)))<<40 | uint64(i8MinS(uint8(x1lo>>32), uint8(x2lo>>32)))<<32 |
uint64(i8MinS(uint8(x1lo>>56), uint8(x2lo>>56)))<<56 | uint64(i8MinS(uint8(x1lo>>48), uint8(x2lo>>48)))<<48
retHi = uint64(i8MinS(uint8(x1hi>>8), uint8(x2hi>>8)))<<8 | uint64(i8MinS(uint8(x1hi), uint8(x2hi))) |
uint64(i8MinS(uint8(x1hi>>24), uint8(x2hi>>24)))<<24 | uint64(i8MinS(uint8(x1hi>>16), uint8(x2hi>>16)))<<16 |
uint64(i8MinS(uint8(x1hi>>40), uint8(x2hi>>40)))<<40 | uint64(i8MinS(uint8(x1hi>>32), uint8(x2hi>>32)))<<32 |
uint64(i8MinS(uint8(x1hi>>56), uint8(x2hi>>56)))<<56 | uint64(i8MinS(uint8(x1hi>>48), uint8(x2hi>>48)))<<48
} else {
retLo = uint64(i8MinU(uint8(x1lo>>8), uint8(x2lo>>8)))<<8 | uint64(i8MinU(uint8(x1lo), uint8(x2lo))) |
uint64(i8MinU(uint8(x1lo>>24), uint8(x2lo>>24)))<<24 | uint64(i8MinU(uint8(x1lo>>16), uint8(x2lo>>16)))<<16 |
uint64(i8MinU(uint8(x1lo>>40), uint8(x2lo>>40)))<<40 | uint64(i8MinU(uint8(x1lo>>32), uint8(x2lo>>32)))<<32 |
uint64(i8MinU(uint8(x1lo>>56), uint8(x2lo>>56)))<<56 | uint64(i8MinU(uint8(x1lo>>48), uint8(x2lo>>48)))<<48
retHi = uint64(i8MinU(uint8(x1hi>>8), uint8(x2hi>>8)))<<8 | uint64(i8MinU(uint8(x1hi), uint8(x2hi))) |
uint64(i8MinU(uint8(x1hi>>24), uint8(x2hi>>24)))<<24 | uint64(i8MinU(uint8(x1hi>>16), uint8(x2hi>>16)))<<16 |
uint64(i8MinU(uint8(x1hi>>40), uint8(x2hi>>40)))<<40 | uint64(i8MinU(uint8(x1hi>>32), uint8(x2hi>>32)))<<32 |
uint64(i8MinU(uint8(x1hi>>56), uint8(x2hi>>56)))<<56 | uint64(i8MinU(uint8(x1hi>>48), uint8(x2hi>>48)))<<48
}
case wazeroir.ShapeI16x8:
if op.b3 { // signed
retLo = uint64(i16MinS(uint16(x1lo), uint16(x2lo))) |
uint64(i16MinS(uint16(x1lo>>16), uint16(x2lo>>16)))<<16 |
uint64(i16MinS(uint16(x1lo>>32), uint16(x2lo>>32)))<<32 |
uint64(i16MinS(uint16(x1lo>>48), uint16(x2lo>>48)))<<48
retHi = uint64(i16MinS(uint16(x1hi), uint16(x2hi))) |
uint64(i16MinS(uint16(x1hi>>16), uint16(x2hi>>16)))<<16 |
uint64(i16MinS(uint16(x1hi>>32), uint16(x2hi>>32)))<<32 |
uint64(i16MinS(uint16(x1hi>>48), uint16(x2hi>>48)))<<48
} else {
retLo = uint64(i16MinU(uint16(x1lo), uint16(x2lo))) |
uint64(i16MinU(uint16(x1lo>>16), uint16(x2lo>>16)))<<16 |
uint64(i16MinU(uint16(x1lo>>32), uint16(x2lo>>32)))<<32 |
uint64(i16MinU(uint16(x1lo>>48), uint16(x2lo>>48)))<<48
retHi = uint64(i16MinU(uint16(x1hi), uint16(x2hi))) |
uint64(i16MinU(uint16(x1hi>>16), uint16(x2hi>>16)))<<16 |
uint64(i16MinU(uint16(x1hi>>32), uint16(x2hi>>32)))<<32 |
uint64(i16MinU(uint16(x1hi>>48), uint16(x2hi>>48)))<<48
}
case wazeroir.ShapeI32x4:
if op.b3 { // signed
retLo = uint64(i32MinS(uint32(x1lo), uint32(x2lo))) |
uint64(i32MinS(uint32(x1lo>>32), uint32(x2lo>>32)))<<32
retHi = uint64(i32MinS(uint32(x1hi), uint32(x2hi))) |
uint64(i32MinS(uint32(x1hi>>32), uint32(x2hi>>32)))<<32
} else {
retLo = uint64(i32MinU(uint32(x1lo), uint32(x2lo))) |
uint64(i32MinU(uint32(x1lo>>32), uint32(x2lo>>32)))<<32
retHi = uint64(i32MinU(uint32(x1hi), uint32(x2hi))) |
uint64(i32MinU(uint32(x1hi>>32), uint32(x2hi>>32)))<<32
}
case wazeroir.ShapeF32x4:
retHi = WasmCompatMin32bits(uint32(x1hi), uint32(x2hi)) |
WasmCompatMin32bits(uint32(x1hi>>32), uint32(x2hi>>32))<<32
retLo = WasmCompatMin32bits(uint32(x1lo), uint32(x2lo)) |
WasmCompatMin32bits(uint32(x1lo>>32), uint32(x2lo>>32))<<32
case wazeroir.ShapeF64x2:
retHi = math.Float64bits(moremath.WasmCompatMin64(
math.Float64frombits(x1hi),
math.Float64frombits(x2hi),
))
retLo = math.Float64bits(moremath.WasmCompatMin64(
math.Float64frombits(x1lo),
math.Float64frombits(x2lo),
))
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Max:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
if op.b3 { // signed
retLo = uint64(i8MaxS(uint8(x1lo>>8), uint8(x2lo>>8)))<<8 | uint64(i8MaxS(uint8(x1lo), uint8(x2lo))) |
uint64(i8MaxS(uint8(x1lo>>24), uint8(x2lo>>24)))<<24 | uint64(i8MaxS(uint8(x1lo>>16), uint8(x2lo>>16)))<<16 |
uint64(i8MaxS(uint8(x1lo>>40), uint8(x2lo>>40)))<<40 | uint64(i8MaxS(uint8(x1lo>>32), uint8(x2lo>>32)))<<32 |
uint64(i8MaxS(uint8(x1lo>>56), uint8(x2lo>>56)))<<56 | uint64(i8MaxS(uint8(x1lo>>48), uint8(x2lo>>48)))<<48
retHi = uint64(i8MaxS(uint8(x1hi>>8), uint8(x2hi>>8)))<<8 | uint64(i8MaxS(uint8(x1hi), uint8(x2hi))) |
uint64(i8MaxS(uint8(x1hi>>24), uint8(x2hi>>24)))<<24 | uint64(i8MaxS(uint8(x1hi>>16), uint8(x2hi>>16)))<<16 |
uint64(i8MaxS(uint8(x1hi>>40), uint8(x2hi>>40)))<<40 | uint64(i8MaxS(uint8(x1hi>>32), uint8(x2hi>>32)))<<32 |
uint64(i8MaxS(uint8(x1hi>>56), uint8(x2hi>>56)))<<56 | uint64(i8MaxS(uint8(x1hi>>48), uint8(x2hi>>48)))<<48
} else {
retLo = uint64(i8MaxU(uint8(x1lo>>8), uint8(x2lo>>8)))<<8 | uint64(i8MaxU(uint8(x1lo), uint8(x2lo))) |
uint64(i8MaxU(uint8(x1lo>>24), uint8(x2lo>>24)))<<24 | uint64(i8MaxU(uint8(x1lo>>16), uint8(x2lo>>16)))<<16 |
uint64(i8MaxU(uint8(x1lo>>40), uint8(x2lo>>40)))<<40 | uint64(i8MaxU(uint8(x1lo>>32), uint8(x2lo>>32)))<<32 |
uint64(i8MaxU(uint8(x1lo>>56), uint8(x2lo>>56)))<<56 | uint64(i8MaxU(uint8(x1lo>>48), uint8(x2lo>>48)))<<48
retHi = uint64(i8MaxU(uint8(x1hi>>8), uint8(x2hi>>8)))<<8 | uint64(i8MaxU(uint8(x1hi), uint8(x2hi))) |
uint64(i8MaxU(uint8(x1hi>>24), uint8(x2hi>>24)))<<24 | uint64(i8MaxU(uint8(x1hi>>16), uint8(x2hi>>16)))<<16 |
uint64(i8MaxU(uint8(x1hi>>40), uint8(x2hi>>40)))<<40 | uint64(i8MaxU(uint8(x1hi>>32), uint8(x2hi>>32)))<<32 |
uint64(i8MaxU(uint8(x1hi>>56), uint8(x2hi>>56)))<<56 | uint64(i8MaxU(uint8(x1hi>>48), uint8(x2hi>>48)))<<48
}
case wazeroir.ShapeI16x8:
if op.b3 { // signed
retLo = uint64(i16MaxS(uint16(x1lo), uint16(x2lo))) |
uint64(i16MaxS(uint16(x1lo>>16), uint16(x2lo>>16)))<<16 |
uint64(i16MaxS(uint16(x1lo>>32), uint16(x2lo>>32)))<<32 |
uint64(i16MaxS(uint16(x1lo>>48), uint16(x2lo>>48)))<<48
retHi = uint64(i16MaxS(uint16(x1hi), uint16(x2hi))) |
uint64(i16MaxS(uint16(x1hi>>16), uint16(x2hi>>16)))<<16 |
uint64(i16MaxS(uint16(x1hi>>32), uint16(x2hi>>32)))<<32 |
uint64(i16MaxS(uint16(x1hi>>48), uint16(x2hi>>48)))<<48
} else {
retLo = uint64(i16MaxU(uint16(x1lo), uint16(x2lo))) |
uint64(i16MaxU(uint16(x1lo>>16), uint16(x2lo>>16)))<<16 |
uint64(i16MaxU(uint16(x1lo>>32), uint16(x2lo>>32)))<<32 |
uint64(i16MaxU(uint16(x1lo>>48), uint16(x2lo>>48)))<<48
retHi = uint64(i16MaxU(uint16(x1hi), uint16(x2hi))) |
uint64(i16MaxU(uint16(x1hi>>16), uint16(x2hi>>16)))<<16 |
uint64(i16MaxU(uint16(x1hi>>32), uint16(x2hi>>32)))<<32 |
uint64(i16MaxU(uint16(x1hi>>48), uint16(x2hi>>48)))<<48
}
case wazeroir.ShapeI32x4:
if op.b3 { // signed
retLo = uint64(i32MaxS(uint32(x1lo), uint32(x2lo))) |
uint64(i32MaxS(uint32(x1lo>>32), uint32(x2lo>>32)))<<32
retHi = uint64(i32MaxS(uint32(x1hi), uint32(x2hi))) |
uint64(i32MaxS(uint32(x1hi>>32), uint32(x2hi>>32)))<<32
} else {
retLo = uint64(i32MaxU(uint32(x1lo), uint32(x2lo))) |
uint64(i32MaxU(uint32(x1lo>>32), uint32(x2lo>>32)))<<32
retHi = uint64(i32MaxU(uint32(x1hi), uint32(x2hi))) |
uint64(i32MaxU(uint32(x1hi>>32), uint32(x2hi>>32)))<<32
}
case wazeroir.ShapeF32x4:
retHi = WasmCompatMax32bits(uint32(x1hi), uint32(x2hi)) |
WasmCompatMax32bits(uint32(x1hi>>32), uint32(x2hi>>32))<<32
retLo = WasmCompatMax32bits(uint32(x1lo), uint32(x2lo)) |
WasmCompatMax32bits(uint32(x1lo>>32), uint32(x2lo>>32))<<32
case wazeroir.ShapeF64x2:
retHi = math.Float64bits(moremath.WasmCompatMax64(
math.Float64frombits(x1hi),
math.Float64frombits(x2hi),
))
retLo = math.Float64bits(moremath.WasmCompatMax64(
math.Float64frombits(x1lo),
math.Float64frombits(x2lo),
))
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128AvgrU:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
retLo = uint64(i8RoundingAverage(uint8(x1lo>>8), uint8(x2lo>>8)))<<8 | uint64(i8RoundingAverage(uint8(x1lo), uint8(x2lo))) |
uint64(i8RoundingAverage(uint8(x1lo>>24), uint8(x2lo>>24)))<<24 | uint64(i8RoundingAverage(uint8(x1lo>>16), uint8(x2lo>>16)))<<16 |
uint64(i8RoundingAverage(uint8(x1lo>>40), uint8(x2lo>>40)))<<40 | uint64(i8RoundingAverage(uint8(x1lo>>32), uint8(x2lo>>32)))<<32 |
uint64(i8RoundingAverage(uint8(x1lo>>56), uint8(x2lo>>56)))<<56 | uint64(i8RoundingAverage(uint8(x1lo>>48), uint8(x2lo>>48)))<<48
retHi = uint64(i8RoundingAverage(uint8(x1hi>>8), uint8(x2hi>>8)))<<8 | uint64(i8RoundingAverage(uint8(x1hi), uint8(x2hi))) |
uint64(i8RoundingAverage(uint8(x1hi>>24), uint8(x2hi>>24)))<<24 | uint64(i8RoundingAverage(uint8(x1hi>>16), uint8(x2hi>>16)))<<16 |
uint64(i8RoundingAverage(uint8(x1hi>>40), uint8(x2hi>>40)))<<40 | uint64(i8RoundingAverage(uint8(x1hi>>32), uint8(x2hi>>32)))<<32 |
uint64(i8RoundingAverage(uint8(x1hi>>56), uint8(x2hi>>56)))<<56 | uint64(i8RoundingAverage(uint8(x1hi>>48), uint8(x2hi>>48)))<<48
case wazeroir.ShapeI16x8:
retLo = uint64(i16RoundingAverage(uint16(x1lo), uint16(x2lo))) |
uint64(i16RoundingAverage(uint16(x1lo>>16), uint16(x2lo>>16)))<<16 |
uint64(i16RoundingAverage(uint16(x1lo>>32), uint16(x2lo>>32)))<<32 |
uint64(i16RoundingAverage(uint16(x1lo>>48), uint16(x2lo>>48)))<<48
retHi = uint64(i16RoundingAverage(uint16(x1hi), uint16(x2hi))) |
uint64(i16RoundingAverage(uint16(x1hi>>16), uint16(x2hi>>16)))<<16 |
uint64(i16RoundingAverage(uint16(x1hi>>32), uint16(x2hi>>32)))<<32 |
uint64(i16RoundingAverage(uint16(x1hi>>48), uint16(x2hi>>48)))<<48
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Pmin:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
if op.b1 == wazeroir.ShapeF32x4 {
if flt32(math.Float32frombits(uint32(x2lo)), math.Float32frombits(uint32(x1lo))) {
retLo = x2lo & 0x00000000_ffffffff
} else {
retLo = x1lo & 0x00000000_ffffffff
}
if flt32(math.Float32frombits(uint32(x2lo>>32)), math.Float32frombits(uint32(x1lo>>32))) {
retLo |= x2lo & 0xffffffff_00000000
} else {
retLo |= x1lo & 0xffffffff_00000000
}
if flt32(math.Float32frombits(uint32(x2hi)), math.Float32frombits(uint32(x1hi))) {
retHi = x2hi & 0x00000000_ffffffff
} else {
retHi = x1hi & 0x00000000_ffffffff
}
if flt32(math.Float32frombits(uint32(x2hi>>32)), math.Float32frombits(uint32(x1hi>>32))) {
retHi |= x2hi & 0xffffffff_00000000
} else {
retHi |= x1hi & 0xffffffff_00000000
}
} else {
if flt64(math.Float64frombits(x2lo), math.Float64frombits(x1lo)) {
retLo = x2lo
} else {
retLo = x1lo
}
if flt64(math.Float64frombits(x2hi), math.Float64frombits(x1hi)) {
retHi = x2hi
} else {
retHi = x1hi
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Pmax:
x2hi, x2lo := ce.popValue(), ce.popValue()
x1hi, x1lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
if op.b1 == wazeroir.ShapeF32x4 {
if flt32(math.Float32frombits(uint32(x1lo)), math.Float32frombits(uint32(x2lo))) {
retLo = x2lo & 0x00000000_ffffffff
} else {
retLo = x1lo & 0x00000000_ffffffff
}
if flt32(math.Float32frombits(uint32(x1lo>>32)), math.Float32frombits(uint32(x2lo>>32))) {
retLo |= x2lo & 0xffffffff_00000000
} else {
retLo |= x1lo & 0xffffffff_00000000
}
if flt32(math.Float32frombits(uint32(x1hi)), math.Float32frombits(uint32(x2hi))) {
retHi = x2hi & 0x00000000_ffffffff
} else {
retHi = x1hi & 0x00000000_ffffffff
}
if flt32(math.Float32frombits(uint32(x1hi>>32)), math.Float32frombits(uint32(x2hi>>32))) {
retHi |= x2hi & 0xffffffff_00000000
} else {
retHi |= x1hi & 0xffffffff_00000000
}
} else {
if flt64(math.Float64frombits(x1lo), math.Float64frombits(x2lo)) {
retLo = x2lo
} else {
retLo = x1lo
}
if flt64(math.Float64frombits(x1hi), math.Float64frombits(x2hi)) {
retHi = x2hi
} else {
retHi = x1hi
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Ceil:
hi, lo := ce.popValue(), ce.popValue()
if op.b1 == wazeroir.ShapeF32x4 {
lo = uint64(math.Float32bits(moremath.WasmCompatCeilF32(math.Float32frombits(uint32(lo))))) |
(uint64(math.Float32bits(moremath.WasmCompatCeilF32(math.Float32frombits(uint32(lo>>32))))) << 32)
hi = uint64(math.Float32bits(moremath.WasmCompatCeilF32(math.Float32frombits(uint32(hi))))) |
(uint64(math.Float32bits(moremath.WasmCompatCeilF32(math.Float32frombits(uint32(hi>>32))))) << 32)
} else {
lo = math.Float64bits(moremath.WasmCompatCeilF64(math.Float64frombits(lo)))
hi = math.Float64bits(moremath.WasmCompatCeilF64(math.Float64frombits(hi)))
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Floor:
hi, lo := ce.popValue(), ce.popValue()
if op.b1 == wazeroir.ShapeF32x4 {
lo = uint64(math.Float32bits(moremath.WasmCompatFloorF32(math.Float32frombits(uint32(lo))))) |
(uint64(math.Float32bits(moremath.WasmCompatFloorF32(math.Float32frombits(uint32(lo>>32))))) << 32)
hi = uint64(math.Float32bits(moremath.WasmCompatFloorF32(math.Float32frombits(uint32(hi))))) |
(uint64(math.Float32bits(moremath.WasmCompatFloorF32(math.Float32frombits(uint32(hi>>32))))) << 32)
} else {
lo = math.Float64bits(moremath.WasmCompatFloorF64(math.Float64frombits(lo)))
hi = math.Float64bits(moremath.WasmCompatFloorF64(math.Float64frombits(hi)))
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Trunc:
hi, lo := ce.popValue(), ce.popValue()
if op.b1 == wazeroir.ShapeF32x4 {
lo = uint64(math.Float32bits(moremath.WasmCompatTruncF32(math.Float32frombits(uint32(lo))))) |
(uint64(math.Float32bits(moremath.WasmCompatTruncF32(math.Float32frombits(uint32(lo>>32))))) << 32)
hi = uint64(math.Float32bits(moremath.WasmCompatTruncF32(math.Float32frombits(uint32(hi))))) |
(uint64(math.Float32bits(moremath.WasmCompatTruncF32(math.Float32frombits(uint32(hi>>32))))) << 32)
} else {
lo = math.Float64bits(moremath.WasmCompatTruncF64(math.Float64frombits(lo)))
hi = math.Float64bits(moremath.WasmCompatTruncF64(math.Float64frombits(hi)))
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Nearest:
hi, lo := ce.popValue(), ce.popValue()
if op.b1 == wazeroir.ShapeF32x4 {
lo = uint64(math.Float32bits(moremath.WasmCompatNearestF32(math.Float32frombits(uint32(lo))))) |
(uint64(math.Float32bits(moremath.WasmCompatNearestF32(math.Float32frombits(uint32(lo>>32))))) << 32)
hi = uint64(math.Float32bits(moremath.WasmCompatNearestF32(math.Float32frombits(uint32(hi))))) |
(uint64(math.Float32bits(moremath.WasmCompatNearestF32(math.Float32frombits(uint32(hi>>32))))) << 32)
} else {
lo = math.Float64bits(moremath.WasmCompatNearestF64(math.Float64frombits(lo)))
hi = math.Float64bits(moremath.WasmCompatNearestF64(math.Float64frombits(hi)))
}
ce.pushValue(lo)
ce.pushValue(hi)
frame.pc++
case wazeroir.OperationKindV128Extend:
hi, lo := ce.popValue(), ce.popValue()
var origin uint64
if op.b3 { // use lower 64 bits
origin = lo
} else {
origin = hi
}
signed := op.b2 == 1
var retHi, retLo uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 8; i++ {
v8 := byte(origin >> (i * 8))
var v16 uint16
if signed {
v16 = uint16(int8(v8))
} else {
v16 = uint16(v8)
}
if i < 4 {
retLo |= uint64(v16) << (i * 16)
} else {
retHi |= uint64(v16) << ((i - 4) * 16)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 4; i++ {
v16 := uint16(origin >> (i * 16))
var v32 uint32
if signed {
v32 = uint32(int16(v16))
} else {
v32 = uint32(v16)
}
if i < 2 {
retLo |= uint64(v32) << (i * 32)
} else {
retHi |= uint64(v32) << ((i - 2) * 32)
}
}
case wazeroir.ShapeI32x4:
v32Lo := uint32(origin)
v32Hi := uint32(origin >> 32)
if signed {
retLo = uint64(int32(v32Lo))
retHi = uint64(int32(v32Hi))
} else {
retLo = uint64(v32Lo)
retHi = uint64(v32Hi)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128ExtMul:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
var x1, x2 uint64
if op.b3 { // use lower 64 bits
x1, x2 = x1Lo, x2Lo
} else {
x1, x2 = x1Hi, x2Hi
}
signed := op.b2 == 1
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 8; i++ {
v1, v2 := byte(x1>>(i*8)), byte(x2>>(i*8))
var v16 uint16
if signed {
v16 = uint16(int16(int8(v1)) * int16(int8(v2)))
} else {
v16 = uint16(v1) * uint16(v2)
}
if i < 4 {
retLo |= uint64(v16) << (i * 16)
} else {
retHi |= uint64(v16) << ((i - 4) * 16)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 4; i++ {
v1, v2 := uint16(x1>>(i*16)), uint16(x2>>(i*16))
var v32 uint32
if signed {
v32 = uint32(int32(int16(v1)) * int32(int16(v2)))
} else {
v32 = uint32(v1) * uint32(v2)
}
if i < 2 {
retLo |= uint64(v32) << (i * 32)
} else {
retHi |= uint64(v32) << ((i - 2) * 32)
}
}
case wazeroir.ShapeI32x4:
v1Lo, v2Lo := uint32(x1), uint32(x2)
v1Hi, v2Hi := uint32(x1>>32), uint32(x2>>32)
if signed {
retLo = uint64(int64(int32(v1Lo)) * int64(int32(v2Lo)))
retHi = uint64(int64(int32(v1Hi)) * int64(int32(v2Hi)))
} else {
retLo = uint64(v1Lo) * uint64(v2Lo)
retHi = uint64(v1Hi) * uint64(v2Hi)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Q15mulrSatS:
x2hi, x2Lo := ce.popValue(), ce.popValue()
x1hi, x1Lo := ce.popValue(), ce.popValue()
var retLo, retHi uint64
for i := 0; i < 8; i++ {
var v, w int16
if i < 4 {
v, w = int16(uint16(x1Lo>>(i*16))), int16(uint16(x2Lo>>(i*16)))
} else {
v, w = int16(uint16(x1hi>>((i-4)*16))), int16(uint16(x2hi>>((i-4)*16)))
}
var uv uint64
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#saturating-integer-q-format-rounding-multiplication
if calc := ((int32(v) * int32(w)) + 0x4000) >> 15; calc < math.MinInt16 {
uv = uint64(uint16(0x8000))
} else if calc > math.MaxInt16 {
uv = uint64(uint16(0x7fff))
} else {
uv = uint64(uint16(int16(calc)))
}
if i < 4 {
retLo |= uv << (i * 16)
} else {
retHi |= uv << ((i - 4) * 16)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128ExtAddPairwise:
hi, lo := ce.popValue(), ce.popValue()
signed := op.b3
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI8x16:
for i := 0; i < 8; i++ {
var v1, v2 byte
if i < 4 {
v1, v2 = byte(lo>>((i*2)*8)), byte(lo>>((i*2+1)*8))
} else {
v1, v2 = byte(hi>>(((i-4)*2)*8)), byte(hi>>(((i-4)*2+1)*8))
}
var v16 uint16
if signed {
v16 = uint16(int16(int8(v1)) + int16(int8(v2)))
} else {
v16 = uint16(v1) + uint16(v2)
}
if i < 4 {
retLo |= uint64(v16) << (i * 16)
} else {
retHi |= uint64(v16) << ((i - 4) * 16)
}
}
case wazeroir.ShapeI16x8:
for i := 0; i < 4; i++ {
var v1, v2 uint16
if i < 2 {
v1, v2 = uint16(lo>>((i*2)*16)), uint16(lo>>((i*2+1)*16))
} else {
v1, v2 = uint16(hi>>(((i-2)*2)*16)), uint16(hi>>(((i-2)*2+1)*16))
}
var v32 uint32
if signed {
v32 = uint32(int32(int16(v1)) + int32(int16(v2)))
} else {
v32 = uint32(v1) + uint32(v2)
}
if i < 2 {
retLo |= uint64(v32) << (i * 32)
} else {
retHi |= uint64(v32) << ((i - 2) * 32)
}
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128FloatPromote:
_, toPromote := ce.popValue(), ce.popValue()
ce.pushValue(math.Float64bits(float64(math.Float32frombits(uint32(toPromote)))))
ce.pushValue(math.Float64bits(float64(math.Float32frombits(uint32(toPromote >> 32)))))
frame.pc++
case wazeroir.OperationKindV128FloatDemote:
hi, lo := ce.popValue(), ce.popValue()
ce.pushValue(
uint64(math.Float32bits(float32(math.Float64frombits(lo)))) |
(uint64(math.Float32bits(float32(math.Float64frombits(hi)))) << 32),
)
ce.pushValue(0)
frame.pc++
case wazeroir.OperationKindV128FConvertFromI:
hi, lo := ce.popValue(), ce.popValue()
v1, v2, v3, v4 := uint32(lo), uint32(lo>>32), uint32(hi), uint32(hi>>32)
signed := op.b3
var retLo, retHi uint64
switch op.b1 { // Destination shape.
case wazeroir.ShapeF32x4: // f32x4 from signed/unsigned i32x4
if signed {
retLo = uint64(math.Float32bits(float32(int32(v1)))) |
(uint64(math.Float32bits(float32(int32(v2)))) << 32)
retHi = uint64(math.Float32bits(float32(int32(v3)))) |
(uint64(math.Float32bits(float32(int32(v4)))) << 32)
} else {
retLo = uint64(math.Float32bits(float32(v1))) |
(uint64(math.Float32bits(float32(v2))) << 32)
retHi = uint64(math.Float32bits(float32(v3))) |
(uint64(math.Float32bits(float32(v4))) << 32)
}
case wazeroir.ShapeF64x2: // f64x2 from signed/unsigned i32x4
if signed {
retLo, retHi = math.Float64bits(float64(int32(v1))), math.Float64bits(float64(int32(v2)))
} else {
retLo, retHi = math.Float64bits(float64(v1)), math.Float64bits(float64(v2))
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Narrow:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
signed := op.b3
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeI16x8: // signed/unsigned i16x8 to i8x16
for i := 0; i < 8; i++ {
var v16 uint16
if i < 4 {
v16 = uint16(x1Lo >> (i * 16))
} else {
v16 = uint16(x1Hi >> ((i - 4) * 16))
}
var v byte
if signed {
if s := int16(v16); s > math.MaxInt8 {
v = math.MaxInt8
} else if s < math.MinInt8 {
s = math.MinInt8
v = byte(s)
} else {
v = byte(v16)
}
} else {
if s := int16(v16); s > math.MaxUint8 {
v = math.MaxUint8
} else if s < 0 {
v = 0
} else {
v = byte(v16)
}
}
retLo |= uint64(v) << (i * 8)
}
for i := 0; i < 8; i++ {
var v16 uint16
if i < 4 {
v16 = uint16(x2Lo >> (i * 16))
} else {
v16 = uint16(x2Hi >> ((i - 4) * 16))
}
var v byte
if signed {
if s := int16(v16); s > math.MaxInt8 {
v = math.MaxInt8
} else if s < math.MinInt8 {
s = math.MinInt8
v = byte(s)
} else {
v = byte(v16)
}
} else {
if s := int16(v16); s > math.MaxUint8 {
v = math.MaxUint8
} else if s < 0 {
v = 0
} else {
v = byte(v16)
}
}
retHi |= uint64(v) << (i * 8)
}
case wazeroir.ShapeI32x4: // signed/unsigned i32x4 to i16x8
for i := 0; i < 4; i++ {
var v32 uint32
if i < 2 {
v32 = uint32(x1Lo >> (i * 32))
} else {
v32 = uint32(x1Hi >> ((i - 2) * 32))
}
var v uint16
if signed {
if s := int32(v32); s > math.MaxInt16 {
v = math.MaxInt16
} else if s < math.MinInt16 {
s = math.MinInt16
v = uint16(s)
} else {
v = uint16(v32)
}
} else {
if s := int32(v32); s > math.MaxUint16 {
v = math.MaxUint16
} else if s < 0 {
v = 0
} else {
v = uint16(v32)
}
}
retLo |= uint64(v) << (i * 16)
}
for i := 0; i < 4; i++ {
var v32 uint32
if i < 2 {
v32 = uint32(x2Lo >> (i * 32))
} else {
v32 = uint32(x2Hi >> ((i - 2) * 32))
}
var v uint16
if signed {
if s := int32(v32); s > math.MaxInt16 {
v = math.MaxInt16
} else if s < math.MinInt16 {
s = math.MinInt16
v = uint16(s)
} else {
v = uint16(v32)
}
} else {
if s := int32(v32); s > math.MaxUint16 {
v = math.MaxUint16
} else if s < 0 {
v = 0
} else {
v = uint16(v32)
}
}
retHi |= uint64(v) << (i * 16)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
case wazeroir.OperationKindV128Dot:
x2Hi, x2Lo := ce.popValue(), ce.popValue()
x1Hi, x1Lo := ce.popValue(), ce.popValue()
ce.pushValue(
uint64(uint32(int32(int16(x1Lo>>0))*int32(int16(x2Lo>>0))+int32(int16(x1Lo>>16))*int32(int16(x2Lo>>16)))) |
(uint64(uint32(int32(int16(x1Lo>>32))*int32(int16(x2Lo>>32))+int32(int16(x1Lo>>48))*int32(int16(x2Lo>>48)))) << 32),
)
ce.pushValue(
uint64(uint32(int32(int16(x1Hi>>0))*int32(int16(x2Hi>>0))+int32(int16(x1Hi>>16))*int32(int16(x2Hi>>16)))) |
(uint64(uint32(int32(int16(x1Hi>>32))*int32(int16(x2Hi>>32))+int32(int16(x1Hi>>48))*int32(int16(x2Hi>>48)))) << 32),
)
frame.pc++
case wazeroir.OperationKindV128ITruncSatFromF:
hi, lo := ce.popValue(), ce.popValue()
signed := op.b3
var retLo, retHi uint64
switch op.b1 {
case wazeroir.ShapeF32x4: // f32x4 to i32x4
for i, f64 := range [4]float64{
math.Trunc(float64(math.Float32frombits(uint32(lo)))),
math.Trunc(float64(math.Float32frombits(uint32(lo >> 32)))),
math.Trunc(float64(math.Float32frombits(uint32(hi)))),
math.Trunc(float64(math.Float32frombits(uint32(hi >> 32)))),
} {
var v uint32
if math.IsNaN(f64) {
v = 0
} else if signed {
if f64 < math.MinInt32 {
f64 = math.MinInt32
} else if f64 > math.MaxInt32 {
f64 = math.MaxInt32
}
v = uint32(int32(f64))
} else {
if f64 < 0 {
f64 = 0
} else if f64 > math.MaxUint32 {
f64 = math.MaxUint32
}
v = uint32(f64)
}
if i < 2 {
retLo |= uint64(v) << (i * 32)
} else {
retHi |= uint64(v) << ((i - 2) * 32)
}
}
case wazeroir.ShapeF64x2: // f64x2 to i32x4
for i, f := range [2]float64{
math.Trunc(math.Float64frombits(lo)),
math.Trunc(math.Float64frombits(hi)),
} {
var v uint32
if math.IsNaN(f) {
v = 0
} else if signed {
if f < math.MinInt32 {
f = math.MinInt32
} else if f > math.MaxInt32 {
f = math.MaxInt32
}
v = uint32(int32(f))
} else {
if f < 0 {
f = 0
} else if f > math.MaxUint32 {
f = math.MaxUint32
}
v = uint32(f)
}
retLo |= uint64(v) << (i * 32)
}
}
ce.pushValue(retLo)
ce.pushValue(retHi)
frame.pc++
}
}
ce.popFrame()
}
// callerMemory returns the caller context memory.
func (ce *callEngine) callerMemory() *wasm.MemoryInstance {
// Search through the call frame stack from the top until we find a non host function.
for i := len(ce.frames) - 1; i >= 0; i-- {
f := ce.frames[i].f
if !f.parent.isHostFunction {
return f.source.Module.Memory
}
}
return nil
}
func WasmCompatMax32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(moremath.WasmCompatMax32(
math.Float32frombits(v1),
math.Float32frombits(v2),
)))
}
func WasmCompatMin32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(moremath.WasmCompatMin32(
math.Float32frombits(v1),
math.Float32frombits(v2),
)))
}
func addFloat32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(math.Float32frombits(v1) + math.Float32frombits(v2)))
}
func subFloat32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(math.Float32frombits(v1) - math.Float32frombits(v2)))
}
func mulFloat32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(math.Float32frombits(v1) * math.Float32frombits(v2)))
}
func divFloat32bits(v1, v2 uint32) uint64 {
return uint64(math.Float32bits(math.Float32frombits(v1) / math.Float32frombits(v2)))
}
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/numerics.html#xref-exec-numerics-op-flt-mathrm-flt-n-z-1-z-2
func flt32(z1, z2 float32) bool {
if z1 != z1 || z2 != z2 {
return false
} else if z1 == z2 {
return false
} else if math.IsInf(float64(z1), 1) {
return false
} else if math.IsInf(float64(z1), -1) {
return true
} else if math.IsInf(float64(z2), 1) {
return true
} else if math.IsInf(float64(z2), -1) {
return false
}
return z1 < z2
}
// https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/exec/numerics.html#xref-exec-numerics-op-flt-mathrm-flt-n-z-1-z-2
func flt64(z1, z2 float64) bool {
if z1 != z1 || z2 != z2 {
return false
} else if z1 == z2 {
return false
} else if math.IsInf(z1, 1) {
return false
} else if math.IsInf(z1, -1) {
return true
} else if math.IsInf(z2, 1) {
return true
} else if math.IsInf(z2, -1) {
return false
}
return z1 < z2
}
func i8RoundingAverage(v1, v2 byte) byte {
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#lane-wise-integer-rounding-average
return byte((uint16(v1) + uint16(v2) + uint16(1)) / 2)
}
func i16RoundingAverage(v1, v2 uint16) uint16 {
// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md#lane-wise-integer-rounding-average
return uint16((uint32(v1) + uint32(v2) + 1) / 2)
}
func i8Abs(v byte) byte {
if i := int8(v); i < 0 {
return byte(-i)
} else {
return byte(i)
}
}
func i8MaxU(v1, v2 byte) byte {
if v1 < v2 {
return v2
} else {
return v1
}
}
func i8MinU(v1, v2 byte) byte {
if v1 > v2 {
return v2
} else {
return v1
}
}
func i8MaxS(v1, v2 byte) byte {
if int8(v1) < int8(v2) {
return v2
} else {
return v1
}
}
func i8MinS(v1, v2 byte) byte {
if int8(v1) > int8(v2) {
return v2
} else {
return v1
}
}
func i16MaxU(v1, v2 uint16) uint16 {
if v1 < v2 {
return v2
} else {
return v1
}
}
func i16MinU(v1, v2 uint16) uint16 {
if v1 > v2 {
return v2
} else {
return v1
}
}
func i16MaxS(v1, v2 uint16) uint16 {
if int16(v1) < int16(v2) {
return v2
} else {
return v1
}
}
func i16MinS(v1, v2 uint16) uint16 {
if int16(v1) > int16(v2) {
return v2
} else {
return v1
}
}
func i32MaxU(v1, v2 uint32) uint32 {
if v1 < v2 {
return v2
} else {
return v1
}
}
func i32MinU(v1, v2 uint32) uint32 {
if v1 > v2 {
return v2
} else {
return v1
}
}
func i32MaxS(v1, v2 uint32) uint32 {
if int32(v1) < int32(v2) {
return v2
} else {
return v1
}
}
func i32MinS(v1, v2 uint32) uint32 {
if int32(v1) > int32(v2) {
return v2
} else {
return v1
}
}
func i16Abs(v uint16) uint16 {
if i := int16(v); i < 0 {
return uint16(-i)
} else {
return uint16(i)
}
}
func i32Abs(v uint32) uint32 {
if i := int32(v); i < 0 {
return uint32(-i)
} else {
return uint32(i)
}
}
func (ce *callEngine) callNativeFuncWithListener(ctx context.Context, callCtx *wasm.CallContext, f *function, fnl experimental.FunctionListener) context.Context {
if f.parent.isHostFunction {
callCtx = callCtx.WithMemory(ce.callerMemory())
}
ctx = fnl.Before(ctx, callCtx, f.source.Definition, ce.peekValues(len(f.source.Type.Params)))
ce.callNativeFunc(ctx, callCtx, f)
// TODO: This doesn't get the error due to use of panic to propagate them.
fnl.After(ctx, callCtx, f.source.Definition, nil, ce.peekValues(len(f.source.Type.Results)))
return ctx
}
// popMemoryOffset takes a memory offset off the stack for use in load and store instructions.
// As the top of stack value is 64-bit, this ensures it is in range before returning it.
func (ce *callEngine) popMemoryOffset(op *interpreterOp) uint32 {
// TODO: Document what 'us' is and why we expect to look at value 1.
offset := op.us[1] + ce.popValue()
if offset > math.MaxUint32 {
panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
}
return uint32(offset)
}
func (ce *callEngine) callGoFuncWithStack(ctx context.Context, callCtx *wasm.CallContext, f *function) {
paramLen := f.source.Type.ParamNumInUint64
resultLen := f.source.Type.ResultNumInUint64
stackLen := paramLen
// In the interpreter engine, ce.stack may only have capacity to store
// parameters. Grow when there are more results than parameters.
if growLen := resultLen - paramLen; growLen > 0 {
for i := 0; i < growLen; i++ {
ce.stack = append(ce.stack, 0)
}
stackLen += growLen
}
// Pass the stack elements to the go function.
stack := ce.stack[len(ce.stack)-stackLen:]
ce.callGoFunc(ctx, callCtx, f, stack)
// Shrink the stack when there were more parameters than results.
if shrinkLen := paramLen - resultLen; shrinkLen > 0 {
ce.stack = ce.stack[0 : len(ce.stack)-shrinkLen]
}
}