summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
diff options
context:
space:
mode:
authorLibravatar Terin Stock <terinjokes@gmail.com>2025-03-09 17:47:56 +0100
committerLibravatar Terin Stock <terinjokes@gmail.com>2025-03-10 01:59:49 +0100
commit3ac1ee16f377d31a0fb80c8dae28b6239ac4229e (patch)
treef61faa581feaaeaba2542b9f2b8234a590684413 /vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
parent[chore] update URLs to forked source (diff)
downloadgotosocial-3ac1ee16f377d31a0fb80c8dae28b6239ac4229e.tar.xz
[chore] remove vendor
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go4596
1 files changed, 0 insertions, 4596 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go b/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
deleted file mode 100644
index 5b5e6e9d0..000000000
--- a/vendor/github.com/tetratelabs/wazero/internal/engine/interpreter/interpreter.go
+++ /dev/null
@@ -1,4596 +0,0 @@
-package interpreter
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "math"
- "math/bits"
- "sync"
- "unsafe"
-
- "github.com/tetratelabs/wazero/api"
- "github.com/tetratelabs/wazero/experimental"
- "github.com/tetratelabs/wazero/internal/expctxkeys"
- "github.com/tetratelabs/wazero/internal/filecache"
- "github.com/tetratelabs/wazero/internal/internalapi"
- "github.com/tetratelabs/wazero/internal/moremath"
- "github.com/tetratelabs/wazero/internal/wasm"
- "github.com/tetratelabs/wazero/internal/wasmdebug"
- "github.com/tetratelabs/wazero/internal/wasmruntime"
-)
-
-// 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
- compiledFunctions map[wasm.ModuleID][]compiledFunction // guarded by mutex.
- mux sync.RWMutex
-}
-
-func NewEngine(_ context.Context, enabledFeatures api.CoreFeatures, _ filecache.Cache) wasm.Engine {
- return &engine{
- enabledFeatures: enabledFeatures,
- compiledFunctions: map[wasm.ModuleID][]compiledFunction{},
- }
-}
-
-// 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.compiledFunctions))
-}
-
-// DeleteCompiledModule implements the same method as documented on wasm.Engine.
-func (e *engine) DeleteCompiledModule(m *wasm.Module) {
- e.deleteCompiledFunctions(m)
-}
-
-func (e *engine) deleteCompiledFunctions(module *wasm.Module) {
- e.mux.Lock()
- defer e.mux.Unlock()
- delete(e.compiledFunctions, module.ID)
-}
-
-func (e *engine) addCompiledFunctions(module *wasm.Module, fs []compiledFunction) {
- e.mux.Lock()
- defer e.mux.Unlock()
- e.compiledFunctions[module.ID] = fs
-}
-
-func (e *engine) getCompiledFunctions(module *wasm.Module) (fs []compiledFunction, ok bool) {
- e.mux.RLock()
- defer e.mux.RUnlock()
- fs, ok = e.compiledFunctions[module.ID]
- return
-}
-
-// moduleEngine implements wasm.ModuleEngine
-type moduleEngine struct {
- // 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
-}
-
-// GetGlobalValue implements the same method as documented on wasm.ModuleEngine.
-func (e *moduleEngine) GetGlobalValue(wasm.Index) (lo, hi uint64) {
- panic("BUG: GetGlobalValue should never be called on interpreter mode")
-}
-
-// SetGlobalValue implements the same method as documented on wasm.ModuleEngine.
-func (e *moduleEngine) SetGlobalValue(idx wasm.Index, lo, hi uint64) {
- panic("BUG: SetGlobalValue should never be called on interpreter mode")
-}
-
-// OwnsGlobals implements the same method as documented on wasm.ModuleEngine.
-func (e *moduleEngine) OwnsGlobals() bool { return false }
-
-// MemoryGrown implements wasm.ModuleEngine.
-func (e *moduleEngine) MemoryGrown() {}
-
-// callEngine holds context per moduleEngine.Call, and shared across all the
-// function calls originating from the same moduleEngine.Call execution.
-//
-// This implements api.Function.
-type callEngine struct {
- internalapi.WazeroOnlyType
-
- // stack contains the operands.
- // Note that all the values are represented as uint64.
- stack []uint64
-
- // frames are the function call stack.
- frames []*callFrame
-
- // f is the initial function for this call engine.
- f *function
-
- // stackiterator for Listeners to walk frames and stack.
- stackIterator stackIterator
-}
-
-func (e *moduleEngine) newCallEngine(compiled *function) *callEngine {
- return &callEngine{f: compiled}
-}
-
-func (ce *callEngine) pushValue(v uint64) {
- ce.stack = append(ce.stack, v)
-}
-
-func (ce *callEngine) pushValues(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 interpreterir translation
- // before compilation.
- stackTopIndex := len(ce.stack) - 1
- v = ce.stack[stackTopIndex]
- ce.stack = ce.stack[:stackTopIndex]
- return
-}
-
-func (ce *callEngine) popValues(v []uint64) {
- stackTopIndex := len(ce.stack) - len(v)
- copy(v, ce.stack[stackTopIndex:])
- ce.stack = ce.stack[:stackTopIndex]
-}
-
-// 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(raw uint64) {
- r := inclusiveRangeFromU64(raw)
- if r.Start == -1 {
- return
- } else if r.Start == 0 {
- ce.stack = ce.stack[:int32(len(ce.stack))-1-r.End]
- } else {
- newStack := ce.stack[:int32(len(ce.stack))-1-r.End]
- newStack = append(newStack, ce.stack[int32(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 interpreterir 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
- // base index in the frame of this function, used to detect the count of
- // values on the stack.
- base int
-}
-
-type compiledFunction struct {
- source *wasm.Module
- body []unionOperation
- listener experimental.FunctionListener
- offsetsInWasmBinary []uint64
- hostFn interface{}
- ensureTermination bool
- index wasm.Index
-}
-
-type function struct {
- funcType *wasm.FunctionType
- moduleInstance *wasm.ModuleInstance
- typeID wasm.FunctionTypeID
- parent *compiledFunction
-}
-
-// 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))
-}
-
-type snapshot struct {
- stack []uint64
- frames []*callFrame
- pc uint64
-
- ret []uint64
-
- ce *callEngine
-}
-
-// Snapshot implements the same method as documented on experimental.Snapshotter.
-func (ce *callEngine) Snapshot() experimental.Snapshot {
- stack := make([]uint64, len(ce.stack))
- copy(stack, ce.stack)
-
- frames := make([]*callFrame, len(ce.frames))
- copy(frames, ce.frames)
-
- return &snapshot{
- stack: stack,
- frames: frames,
- ce: ce,
- }
-}
-
-// Restore implements the same method as documented on experimental.Snapshot.
-func (s *snapshot) Restore(ret []uint64) {
- s.ret = ret
- panic(s)
-}
-
-func (s *snapshot) doRestore() {
- ce := s.ce
-
- ce.stack = s.stack
- ce.frames = s.frames
- ce.frames[len(ce.frames)-1].pc = s.pc
-
- copy(ce.stack[len(ce.stack)-len(s.ret):], s.ret)
-}
-
-// Error implements the same method on error.
-func (s *snapshot) Error() string {
- return "unhandled snapshot restore, this generally indicates restore was called from a different " +
- "exported function invocation than snapshot"
-}
-
-// stackIterator implements experimental.StackIterator.
-type stackIterator struct {
- stack []uint64
- frames []*callFrame
- started bool
- fn *function
- pc uint64
-}
-
-func (si *stackIterator) reset(stack []uint64, frames []*callFrame, f *function) {
- si.fn = f
- si.pc = 0
- si.stack = stack
- si.frames = frames
- si.started = false
-}
-
-func (si *stackIterator) clear() {
- si.stack = nil
- si.frames = nil
- si.started = false
- si.fn = nil
-}
-
-// Next implements the same method as documented on experimental.StackIterator.
-func (si *stackIterator) Next() bool {
- if !si.started {
- si.started = true
- return true
- }
-
- if len(si.frames) == 0 {
- return false
- }
-
- frame := si.frames[len(si.frames)-1]
- si.stack = si.stack[:frame.base]
- si.fn = frame.f
- si.pc = frame.pc
- si.frames = si.frames[:len(si.frames)-1]
- return true
-}
-
-// Function implements the same method as documented on
-// experimental.StackIterator.
-func (si *stackIterator) Function() experimental.InternalFunction {
- return internalFunction{si.fn}
-}
-
-// ProgramCounter implements the same method as documented on
-// experimental.StackIterator.
-func (si *stackIterator) ProgramCounter() experimental.ProgramCounter {
- return experimental.ProgramCounter(si.pc)
-}
-
-// internalFunction implements experimental.InternalFunction.
-type internalFunction struct{ *function }
-
-// Definition implements the same method as documented on
-// experimental.InternalFunction.
-func (f internalFunction) Definition() api.FunctionDefinition {
- return f.definition()
-}
-
-// SourceOffsetForPC implements the same method as documented on
-// experimental.InternalFunction.
-func (f internalFunction) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 {
- offsetsMap := f.parent.offsetsInWasmBinary
- if uint64(pc) < uint64(len(offsetsMap)) {
- return offsetsMap[pc]
- }
- return 0
-}
-
-// 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(_ context.Context, module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) error {
- if _, ok := e.getCompiledFunctions(module); ok { // cache hit!
- return nil
- }
-
- funcs := make([]compiledFunction, len(module.FunctionSection))
- irCompiler, err := newCompiler(e.enabledFeatures, callFrameStackSize, module, ensureTermination)
- if err != nil {
- return err
- }
- imported := module.ImportFunctionCount
- for i := range module.CodeSection {
- var lsn experimental.FunctionListener
- if i < len(listeners) {
- lsn = listeners[i]
- }
-
- compiled := &funcs[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
- if codeSeg := &module.CodeSection[i]; codeSeg.GoFunc != nil {
- compiled.hostFn = codeSeg.GoFunc
- } else {
- ir, err := irCompiler.Next()
- if err != nil {
- return err
- }
- err = e.lowerIR(ir, compiled)
- if err != nil {
- def := module.FunctionDefinition(uint32(i) + module.ImportFunctionCount)
- return fmt.Errorf("failed to lower func[%s] to interpreterir: %w", def.DebugName(), err)
- }
- }
- compiled.source = module
- compiled.ensureTermination = ensureTermination
- compiled.listener = lsn
- compiled.index = imported + uint32(i)
- }
- e.addCompiledFunctions(module, funcs)
- return nil
-}
-
-// NewModuleEngine implements the same method as documented on wasm.Engine.
-func (e *engine) NewModuleEngine(module *wasm.Module, instance *wasm.ModuleInstance) (wasm.ModuleEngine, error) {
- me := &moduleEngine{
- parentEngine: e,
- functions: make([]function, len(module.FunctionSection)+int(module.ImportFunctionCount)),
- }
-
- codes, ok := e.getCompiledFunctions(module)
- if !ok {
- return nil, errors.New("source module must be compiled before instantiation")
- }
-
- for i := range codes {
- c := &codes[i]
- offset := i + int(module.ImportFunctionCount)
- typeIndex := module.FunctionSection[i]
- me.functions[offset] = function{
- moduleInstance: instance,
- typeID: instance.TypeIDs[typeIndex],
- funcType: &module.TypeSection[typeIndex],
- parent: c,
- }
- }
- return me, nil
-}
-
-// lowerIR lowers the interpreterir operations to engine friendly struct.
-func (e *engine) lowerIR(ir *compilationResult, ret *compiledFunction) error {
- // Copy the body from the result.
- ret.body = make([]unionOperation, len(ir.Operations))
- copy(ret.body, ir.Operations)
- // Also copy the offsets if necessary.
- if offsets := ir.IROperationSourceOffsetsInWasmBinary; len(offsets) > 0 {
- ret.offsetsInWasmBinary = make([]uint64, len(offsets))
- copy(ret.offsetsInWasmBinary, offsets)
- }
-
- labelAddressResolutions := [labelKindNum][]uint64{}
-
- // First, we iterate all labels, and resolve the address.
- for i := range ret.body {
- op := &ret.body[i]
- switch op.Kind {
- case operationKindLabel:
- label := label(op.U1)
- address := uint64(i)
-
- kind, fid := label.Kind(), label.FrameID()
- frameToAddresses := labelAddressResolutions[label.Kind()]
- // Expand the slice if necessary.
- if diff := fid - len(frameToAddresses) + 1; diff > 0 {
- for j := 0; j < diff; j++ {
- frameToAddresses = append(frameToAddresses, 0)
- }
- }
- frameToAddresses[fid] = address
- labelAddressResolutions[kind] = frameToAddresses
- }
- }
-
- // Then resolve the label as the index to the body.
- for i := range ret.body {
- op := &ret.body[i]
- switch op.Kind {
- case operationKindBr:
- e.setLabelAddress(&op.U1, label(op.U1), labelAddressResolutions)
- case operationKindBrIf:
- e.setLabelAddress(&op.U1, label(op.U1), labelAddressResolutions)
- e.setLabelAddress(&op.U2, label(op.U2), labelAddressResolutions)
- case operationKindBrTable:
- for j := 0; j < len(op.Us); j += 2 {
- target := op.Us[j]
- e.setLabelAddress(&op.Us[j], label(target), labelAddressResolutions)
- }
- }
- }
- return nil
-}
-
-func (e *engine) setLabelAddress(op *uint64, label label, labelAddressResolutions [labelKindNum][]uint64) {
- if label.IsReturnTarget() {
- // Jmp to the end of the possible binary.
- *op = math.MaxUint64
- } else {
- *op = labelAddressResolutions[label.Kind()][label.FrameID()]
- }
-}
-
-// ResolveImportedFunction implements wasm.ModuleEngine.
-func (e *moduleEngine) ResolveImportedFunction(index, descFunc, indexInImportedModule wasm.Index, importedModuleEngine wasm.ModuleEngine) {
- imported := importedModuleEngine.(*moduleEngine)
- e.functions[index] = imported.functions[indexInImportedModule]
-}
-
-// ResolveImportedMemory implements wasm.ModuleEngine.
-func (e *moduleEngine) ResolveImportedMemory(wasm.ModuleEngine) {}
-
-// DoneInstantiation implements wasm.ModuleEngine.
-func (e *moduleEngine) DoneInstantiation() {}
-
-// 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]))
-}
-
-// NewFunction implements the same method as documented on wasm.ModuleEngine.
-func (e *moduleEngine) NewFunction(index wasm.Index) (ce api.Function) {
- // 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[index]
- return e.newCallEngine(compiled)
-}
-
-// LookupFunction implements the same method as documented on wasm.ModuleEngine.
-func (e *moduleEngine) LookupFunction(t *wasm.TableInstance, typeId wasm.FunctionTypeID, tableOffset wasm.Index) (*wasm.ModuleInstance, wasm.Index) {
- if tableOffset >= uint32(len(t.References)) {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
- rawPtr := t.References[tableOffset]
- if rawPtr == 0 {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
-
- tf := functionFromUintptr(rawPtr)
- if tf.typeID != typeId {
- panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
- }
- return tf.moduleInstance, tf.parent.index
-}
-
-// Definition implements the same method as documented on api.Function.
-func (ce *callEngine) Definition() api.FunctionDefinition {
- return ce.f.definition()
-}
-
-func (f *function) definition() api.FunctionDefinition {
- compiled := f.parent
- return compiled.source.FunctionDefinition(compiled.index)
-}
-
-// Call implements the same method as documented on api.Function.
-func (ce *callEngine) Call(ctx context.Context, params ...uint64) (results []uint64, err error) {
- ft := ce.f.funcType
- if n := ft.ParamNumInUint64; n != len(params) {
- return nil, fmt.Errorf("expected %d params, but passed %d", n, len(params))
- }
- return ce.call(ctx, params, nil)
-}
-
-// CallWithStack implements the same method as documented on api.Function.
-func (ce *callEngine) CallWithStack(ctx context.Context, stack []uint64) error {
- params, results, err := wasm.SplitCallStack(ce.f.funcType, stack)
- if err != nil {
- return err
- }
- _, err = ce.call(ctx, params, results)
- return err
-}
-
-func (ce *callEngine) call(ctx context.Context, params, results []uint64) (_ []uint64, err error) {
- m := ce.f.moduleInstance
- if ce.f.parent.ensureTermination {
- select {
- case <-ctx.Done():
- // If the provided context is already done, close the call context
- // and return the error.
- m.CloseWithCtxErr(ctx)
- return nil, m.FailIfClosed()
- default:
- }
- }
-
- if ctx.Value(expctxkeys.EnableSnapshotterKey{}) != nil {
- ctx = context.WithValue(ctx, expctxkeys.SnapshotterKey{}, ce)
- }
-
- 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(ctx, m, v)
- }
- }()
-
- ce.pushValues(params)
-
- if ce.f.parent.ensureTermination {
- done := m.CloseModuleOnCanceledOrTimeout(ctx)
- defer done()
- }
-
- ce.callFunction(ctx, m, ce.f)
-
- // 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.
- ft := ce.f.funcType
- if results == nil && ft.ResultNumInUint64 > 0 {
- results = make([]uint64, ft.ResultNumInUint64)
- }
- ce.popValues(results)
- return results, nil
-}
-
-// functionListenerInvocation captures arguments needed to perform function
-// listener invocations when unwinding the call stack.
-type functionListenerInvocation struct {
- experimental.FunctionListener
- def api.FunctionDefinition
-}
-
-// 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(ctx context.Context, m *wasm.ModuleInstance, v interface{}) (err error) {
- if s, ok := v.(*snapshot); ok {
- // A snapshot that wasn't handled was created by a different call engine possibly from a nested wasm invocation,
- // let it propagate up to be handled by the caller.
- panic(s)
- }
-
- builder := wasmdebug.NewErrorBuilder()
- frameCount := len(ce.frames)
- functionListeners := make([]functionListenerInvocation, 0, 16)
-
- if frameCount > wasmdebug.MaxFrames {
- frameCount = wasmdebug.MaxFrames
- }
- for i := 0; i < frameCount; i++ {
- frame := ce.popFrame()
- f := frame.f
- def := f.definition()
- var sources []string
- if parent := frame.f.parent; parent.body != nil && len(parent.offsetsInWasmBinary) > 0 {
- sources = parent.source.DWARFLines.Line(parent.offsetsInWasmBinary[frame.pc])
- }
- builder.AddFrame(def.DebugName(), def.ParamTypes(), def.ResultTypes(), sources)
- if f.parent.listener != nil {
- functionListeners = append(functionListeners, functionListenerInvocation{
- FunctionListener: f.parent.listener,
- def: f.definition(),
- })
- }
- }
-
- err = builder.FromRecovered(v)
- for i := range functionListeners {
- functionListeners[i].Abort(ctx, m, functionListeners[i].def, err)
- }
-
- // Allows the reuse of CallEngine.
- ce.stack, ce.frames = ce.stack[:0], ce.frames[:0]
- return
-}
-
-func (ce *callEngine) callFunction(ctx context.Context, m *wasm.ModuleInstance, f *function) {
- if f.parent.hostFn != nil {
- ce.callGoFuncWithStack(ctx, m, f)
- } else if lsn := f.parent.listener; lsn != nil {
- ce.callNativeFuncWithListener(ctx, m, f, lsn)
- } else {
- ce.callNativeFunc(ctx, m, f)
- }
-}
-
-func (ce *callEngine) callGoFunc(ctx context.Context, m *wasm.ModuleInstance, f *function, stack []uint64) {
- typ := f.funcType
- lsn := f.parent.listener
- if lsn != nil {
- params := stack[:typ.ParamNumInUint64]
- ce.stackIterator.reset(ce.stack, ce.frames, f)
- lsn.Before(ctx, m, f.definition(), params, &ce.stackIterator)
- ce.stackIterator.clear()
- }
- frame := &callFrame{f: f, base: len(ce.stack)}
- ce.pushFrame(frame)
-
- fn := f.parent.hostFn
- switch fn := fn.(type) {
- case api.GoModuleFunction:
- fn.Call(ctx, m, 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[:typ.ResultNumInUint64]
- lsn.After(ctx, m, f.definition(), results)
- }
-}
-
-func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance, f *function) {
- frame := &callFrame{f: f, base: len(ce.stack)}
- moduleInst := f.moduleInstance
- functions := moduleInst.Engine.(*moduleEngine).functions
- memoryInst := moduleInst.MemoryInstance
- globals := moduleInst.Globals
- tables := moduleInst.Tables
- typeIDs := moduleInst.TypeIDs
- dataInstances := moduleInst.DataInstances
- elementInstances := moduleInst.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 operationKindBuiltinFunctionCheckExitCode:
- if err := m.FailIfClosed(); err != nil {
- panic(err)
- }
- frame.pc++
- case operationKindUnreachable:
- panic(wasmruntime.ErrRuntimeUnreachable)
- case operationKindBr:
- frame.pc = op.U1
- case operationKindBrIf:
- if ce.popValue() > 0 {
- ce.drop(op.U3)
- frame.pc = op.U1
- } else {
- frame.pc = op.U2
- }
- case operationKindBrTable:
- v := ce.popValue()
- defaultAt := uint64(len(op.Us))/2 - 1
- if v > defaultAt {
- v = defaultAt
- }
- v *= 2
- ce.drop(op.Us[v+1])
- frame.pc = op.Us[v]
- case operationKindCall:
- func() {
- if ctx.Value(expctxkeys.EnableSnapshotterKey{}) != nil {
- defer func() {
- if r := recover(); r != nil {
- if s, ok := r.(*snapshot); ok && s.ce == ce {
- s.doRestore()
- frame = ce.frames[len(ce.frames)-1]
- body = frame.f.parent.body
- bodyLen = uint64(len(body))
- } else {
- panic(r)
- }
- }
- }()
- }
- ce.callFunction(ctx, f.moduleInstance, &functions[op.U1])
- }()
- frame.pc++
- case operationKindCallIndirect:
- offset := ce.popValue()
- table := tables[op.U2]
- if offset >= uint64(len(table.References)) {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
- rawPtr := table.References[offset]
- if rawPtr == 0 {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
-
- tf := functionFromUintptr(rawPtr)
- if tf.typeID != typeIDs[op.U1] {
- panic(wasmruntime.ErrRuntimeIndirectCallTypeMismatch)
- }
-
- ce.callFunction(ctx, f.moduleInstance, tf)
- frame.pc++
- case operationKindDrop:
- ce.drop(op.U1)
- frame.pc++
- case 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 operationKindPick:
- index := len(ce.stack) - 1 - int(op.U1)
- ce.pushValue(ce.stack[index])
- if op.B3 { // V128 value target.
- ce.pushValue(ce.stack[index+1])
- }
- frame.pc++
- case operationKindSet:
- if op.B3 { // V128 value target.
- lowIndex := len(ce.stack) - 1 - int(op.U1)
- 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.U1)
- ce.stack[index] = ce.popValue()
- }
- frame.pc++
- case operationKindGlobalGet:
- g := globals[op.U1]
- ce.pushValue(g.Val)
- if g.Type.ValType == wasm.ValueTypeV128 {
- ce.pushValue(g.ValHi)
- }
- frame.pc++
- case operationKindGlobalSet:
- g := globals[op.U1]
- if g.Type.ValType == wasm.ValueTypeV128 {
- g.ValHi = ce.popValue()
- }
- g.Val = ce.popValue()
- frame.pc++
- case operationKindLoad:
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32, unsignedTypeF32:
- if val, ok := memoryInst.ReadUint32Le(offset); !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- } else {
- ce.pushValue(uint64(val))
- }
- case unsignedTypeI64, unsignedTypeF64:
- if val, ok := memoryInst.ReadUint64Le(offset); !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- } else {
- ce.pushValue(val)
- }
- }
- frame.pc++
- case operationKindLoad8:
- val, ok := memoryInst.ReadByte(ce.popMemoryOffset(op))
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
-
- switch signedInt(op.B1) {
- case signedInt32:
- ce.pushValue(uint64(uint32(int8(val))))
- case signedInt64:
- ce.pushValue(uint64(int8(val)))
- case signedUint32, signedUint64:
- ce.pushValue(uint64(val))
- }
- frame.pc++
- case operationKindLoad16:
-
- val, ok := memoryInst.ReadUint16Le(ce.popMemoryOffset(op))
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
-
- switch signedInt(op.B1) {
- case signedInt32:
- ce.pushValue(uint64(uint32(int16(val))))
- case signedInt64:
- ce.pushValue(uint64(int16(val)))
- case signedUint32, signedUint64:
- ce.pushValue(uint64(val))
- }
- frame.pc++
- case 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 operationKindStore:
- val := ce.popValue()
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32, unsignedTypeF32:
- if !memoryInst.WriteUint32Le(offset, uint32(val)) {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- case unsignedTypeI64, unsignedTypeF64:
- if !memoryInst.WriteUint64Le(offset, val) {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- }
- frame.pc++
- case operationKindStore8:
- val := byte(ce.popValue())
- offset := ce.popMemoryOffset(op)
- if !memoryInst.WriteByte(offset, val) {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case operationKindStore16:
- val := uint16(ce.popValue())
- offset := ce.popMemoryOffset(op)
- if !memoryInst.WriteUint16Le(offset, val) {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case operationKindStore32:
- val := uint32(ce.popValue())
- offset := ce.popMemoryOffset(op)
- if !memoryInst.WriteUint32Le(offset, val) {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case operationKindMemorySize:
- ce.pushValue(uint64(memoryInst.Pages()))
- frame.pc++
- case 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 operationKindConstI32, operationKindConstI64,
- operationKindConstF32, operationKindConstF64:
- ce.pushValue(op.U1)
- frame.pc++
- case operationKindEq:
- var b bool
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- v2, v1 := ce.popValue(), ce.popValue()
- b = uint32(v1) == uint32(v2)
- case unsignedTypeI64:
- v2, v1 := ce.popValue(), ce.popValue()
- b = v1 == v2
- case unsignedTypeF32:
- v2, v1 := ce.popValue(), ce.popValue()
- b = math.Float32frombits(uint32(v2)) == math.Float32frombits(uint32(v1))
- case 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 operationKindNe:
- var b bool
- switch unsignedType(op.B1) {
- case unsignedTypeI32, unsignedTypeI64:
- v2, v1 := ce.popValue(), ce.popValue()
- b = v1 != v2
- case unsignedTypeF32:
- v2, v1 := ce.popValue(), ce.popValue()
- b = math.Float32frombits(uint32(v2)) != math.Float32frombits(uint32(v1))
- case 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 operationKindEqz:
- if ce.popValue() == 0 {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindLt:
- v2 := ce.popValue()
- v1 := ce.popValue()
- var b bool
- switch signedType(op.B1) {
- case signedTypeInt32:
- b = int32(v1) < int32(v2)
- case signedTypeInt64:
- b = int64(v1) < int64(v2)
- case signedTypeUint32, signedTypeUint64:
- b = v1 < v2
- case signedTypeFloat32:
- b = math.Float32frombits(uint32(v1)) < math.Float32frombits(uint32(v2))
- case signedTypeFloat64:
- b = math.Float64frombits(v1) < math.Float64frombits(v2)
- }
- if b {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindGt:
- v2 := ce.popValue()
- v1 := ce.popValue()
- var b bool
- switch signedType(op.B1) {
- case signedTypeInt32:
- b = int32(v1) > int32(v2)
- case signedTypeInt64:
- b = int64(v1) > int64(v2)
- case signedTypeUint32, signedTypeUint64:
- b = v1 > v2
- case signedTypeFloat32:
- b = math.Float32frombits(uint32(v1)) > math.Float32frombits(uint32(v2))
- case signedTypeFloat64:
- b = math.Float64frombits(v1) > math.Float64frombits(v2)
- }
- if b {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindLe:
- v2 := ce.popValue()
- v1 := ce.popValue()
- var b bool
- switch signedType(op.B1) {
- case signedTypeInt32:
- b = int32(v1) <= int32(v2)
- case signedTypeInt64:
- b = int64(v1) <= int64(v2)
- case signedTypeUint32, signedTypeUint64:
- b = v1 <= v2
- case signedTypeFloat32:
- b = math.Float32frombits(uint32(v1)) <= math.Float32frombits(uint32(v2))
- case signedTypeFloat64:
- b = math.Float64frombits(v1) <= math.Float64frombits(v2)
- }
- if b {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindGe:
- v2 := ce.popValue()
- v1 := ce.popValue()
- var b bool
- switch signedType(op.B1) {
- case signedTypeInt32:
- b = int32(v1) >= int32(v2)
- case signedTypeInt64:
- b = int64(v1) >= int64(v2)
- case signedTypeUint32, signedTypeUint64:
- b = v1 >= v2
- case signedTypeFloat32:
- b = math.Float32frombits(uint32(v1)) >= math.Float32frombits(uint32(v2))
- case signedTypeFloat64:
- b = math.Float64frombits(v1) >= math.Float64frombits(v2)
- }
- if b {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindAdd:
- v2 := ce.popValue()
- v1 := ce.popValue()
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- v := uint32(v1) + uint32(v2)
- ce.pushValue(uint64(v))
- case unsignedTypeI64:
- ce.pushValue(v1 + v2)
- case unsignedTypeF32:
- ce.pushValue(addFloat32bits(uint32(v1), uint32(v2)))
- case unsignedTypeF64:
- v := math.Float64frombits(v1) + math.Float64frombits(v2)
- ce.pushValue(math.Float64bits(v))
- }
- frame.pc++
- case operationKindSub:
- v2 := ce.popValue()
- v1 := ce.popValue()
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- ce.pushValue(uint64(uint32(v1) - uint32(v2)))
- case unsignedTypeI64:
- ce.pushValue(v1 - v2)
- case unsignedTypeF32:
- ce.pushValue(subFloat32bits(uint32(v1), uint32(v2)))
- case unsignedTypeF64:
- v := math.Float64frombits(v1) - math.Float64frombits(v2)
- ce.pushValue(math.Float64bits(v))
- }
- frame.pc++
- case operationKindMul:
- v2 := ce.popValue()
- v1 := ce.popValue()
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- ce.pushValue(uint64(uint32(v1) * uint32(v2)))
- case unsignedTypeI64:
- ce.pushValue(v1 * v2)
- case unsignedTypeF32:
- ce.pushValue(mulFloat32bits(uint32(v1), uint32(v2)))
- case unsignedTypeF64:
- v := math.Float64frombits(v2) * math.Float64frombits(v1)
- ce.pushValue(math.Float64bits(v))
- }
- frame.pc++
- case 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 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 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 operationKindDiv:
- // If an integer, check we won't divide by zero.
- t := signedType(op.B1)
- v2, v1 := ce.popValue(), ce.popValue()
- switch t {
- case signedTypeFloat32, signedTypeFloat64: // not integers
- default:
- if v2 == 0 {
- panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
- }
- }
-
- switch t {
- case signedTypeInt32:
- d := int32(v2)
- n := int32(v1)
- if n == math.MinInt32 && d == -1 {
- panic(wasmruntime.ErrRuntimeIntegerOverflow)
- }
- ce.pushValue(uint64(uint32(n / d)))
- case signedTypeInt64:
- d := int64(v2)
- n := int64(v1)
- if n == math.MinInt64 && d == -1 {
- panic(wasmruntime.ErrRuntimeIntegerOverflow)
- }
- ce.pushValue(uint64(n / d))
- case signedTypeUint32:
- d := uint32(v2)
- n := uint32(v1)
- ce.pushValue(uint64(n / d))
- case signedTypeUint64:
- d := v2
- n := v1
- ce.pushValue(n / d)
- case signedTypeFloat32:
- ce.pushValue(divFloat32bits(uint32(v1), uint32(v2)))
- case signedTypeFloat64:
- ce.pushValue(math.Float64bits(math.Float64frombits(v1) / math.Float64frombits(v2)))
- }
- frame.pc++
- case operationKindRem:
- v2, v1 := ce.popValue(), ce.popValue()
- if v2 == 0 {
- panic(wasmruntime.ErrRuntimeIntegerDivideByZero)
- }
- switch signedInt(op.B1) {
- case signedInt32:
- d := int32(v2)
- n := int32(v1)
- ce.pushValue(uint64(uint32(n % d)))
- case signedInt64:
- d := int64(v2)
- n := int64(v1)
- ce.pushValue(uint64(n % d))
- case signedUint32:
- d := uint32(v2)
- n := uint32(v1)
- ce.pushValue(uint64(n % d))
- case signedUint64:
- d := v2
- n := v1
- ce.pushValue(n % d)
- }
- frame.pc++
- case 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 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 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 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 operationKindShr:
- v2 := ce.popValue()
- v1 := ce.popValue()
- switch signedInt(op.B1) {
- case signedInt32:
- ce.pushValue(uint64(uint32(int32(v1) >> (uint32(v2) % 32))))
- case signedInt64:
- ce.pushValue(uint64(int64(v1) >> (v2 % 64)))
- case signedUint32:
- ce.pushValue(uint64(uint32(v1) >> (uint32(v2) % 32)))
- case signedUint64:
- ce.pushValue(v1 >> (v2 % 64))
- }
- frame.pc++
- case 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 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 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 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 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 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 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 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 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 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 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 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 operationKindI32WrapFromI64:
- ce.pushValue(uint64(uint32(ce.popValue())))
- frame.pc++
- case operationKindITruncFromF:
- if op.B1 == 0 {
- // float32
- switch signedInt(op.B2) {
- case 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 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 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 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 signedInt(op.B2) {
- case 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 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 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 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 operationKindFConvertFromI:
- switch signedInt(op.B1) {
- case 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 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 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 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 operationKindF32DemoteFromF64:
- v := float32(math.Float64frombits(ce.popValue()))
- ce.pushValue(uint64(math.Float32bits(v)))
- frame.pc++
- case operationKindF64PromoteFromF32:
- v := float64(math.Float32frombits(uint32(ce.popValue())))
- ce.pushValue(math.Float64bits(v))
- frame.pc++
- case 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 operationKindSignExtend32From8:
- v := uint32(int8(ce.popValue()))
- ce.pushValue(uint64(v))
- frame.pc++
- case operationKindSignExtend32From16:
- v := uint32(int16(ce.popValue()))
- ce.pushValue(uint64(v))
- frame.pc++
- case operationKindSignExtend64From8:
- v := int64(int8(ce.popValue()))
- ce.pushValue(uint64(v))
- frame.pc++
- case operationKindSignExtend64From16:
- v := int64(int16(ce.popValue()))
- ce.pushValue(uint64(v))
- frame.pc++
- case operationKindSignExtend64From32:
- v := int64(int32(ce.popValue()))
- ce.pushValue(uint64(v))
- frame.pc++
- case operationKindMemoryInit:
- dataInstance := dataInstances[op.U1]
- 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 operationKindDataDrop:
- dataInstances[op.U1] = nil
- frame.pc++
- case 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 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 operationKindTableInit:
- elementInstance := elementInstances[op.U1]
- copySize := ce.popValue()
- inElementOffset := ce.popValue()
- inTableOffset := ce.popValue()
- table := tables[op.U2]
- if inElementOffset+copySize > uint64(len(elementInstance)) ||
- inTableOffset+copySize > uint64(len(table.References)) {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- } else if copySize != 0 {
- copy(table.References[inTableOffset:inTableOffset+copySize], elementInstance[inElementOffset:])
- }
- frame.pc++
- case operationKindElemDrop:
- elementInstances[op.U1] = nil
- frame.pc++
- case operationKindTableCopy:
- srcTable, dstTable := tables[op.U1].References, tables[op.U2].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 operationKindRefFunc:
- ce.pushValue(uint64(uintptr(unsafe.Pointer(&functions[op.U1]))))
- frame.pc++
- case operationKindTableGet:
- table := tables[op.U1]
-
- offset := ce.popValue()
- if offset >= uint64(len(table.References)) {
- panic(wasmruntime.ErrRuntimeInvalidTableAccess)
- }
-
- ce.pushValue(uint64(table.References[offset]))
- frame.pc++
- case operationKindTableSet:
- table := tables[op.U1]
- 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 operationKindTableSize:
- table := tables[op.U1]
- ce.pushValue(uint64(len(table.References)))
- frame.pc++
- case operationKindTableGrow:
- table := tables[op.U1]
- num, ref := ce.popValue(), ce.popValue()
- ret := table.Grow(uint32(num), uintptr(ref))
- ce.pushValue(uint64(ret))
- frame.pc++
- case operationKindTableFill:
- table := tables[op.U1]
- 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 operationKindV128Const:
- lo, hi := op.U1, op.U2
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case operationKindV128Add:
- yHigh, yLow := ce.popValue(), ce.popValue()
- xHigh, xLow := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 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 shapeI64x2:
- ce.pushValue(xLow + yLow)
- ce.pushValue(xHigh + yHigh)
- case 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 shapeF64x2:
- ce.pushValue(math.Float64bits(math.Float64frombits(xLow) + math.Float64frombits(yLow)))
- ce.pushValue(math.Float64bits(math.Float64frombits(xHigh) + math.Float64frombits(yHigh)))
- }
- frame.pc++
- case operationKindV128Sub:
- yHigh, yLow := ce.popValue(), ce.popValue()
- xHigh, xLow := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 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 shapeI64x2:
- ce.pushValue(xLow - yLow)
- ce.pushValue(xHigh - yHigh)
- case 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 shapeF64x2:
- ce.pushValue(math.Float64bits(math.Float64frombits(xLow) - math.Float64frombits(yLow)))
- ce.pushValue(math.Float64bits(math.Float64frombits(xHigh) - math.Float64frombits(yHigh)))
- }
- frame.pc++
- case operationKindV128Load:
- offset := ce.popMemoryOffset(op)
- switch op.B1 {
- case 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 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 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 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 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 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 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 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 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 v128LoadType32Splat:
- v, ok := memoryInst.ReadUint32Le(offset)
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- vv := uint64(v)<<32 | uint64(v)
- ce.pushValue(vv)
- ce.pushValue(vv)
- case v128LoadType64Splat:
- lo, ok := memoryInst.ReadUint64Le(offset)
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(lo)
- ce.pushValue(lo)
- case v128LoadType32zero:
- lo, ok := memoryInst.ReadUint32Le(offset)
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(uint64(lo))
- ce.pushValue(0)
- case v128LoadType64zero:
- lo, ok := memoryInst.ReadUint64Le(offset)
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(lo)
- ce.pushValue(0)
- }
- frame.pc++
- case 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 operationKindV128Store:
- hi, lo := ce.popValue(), ce.popValue()
- offset := ce.popMemoryOffset(op)
- // Write the upper bytes first to trigger an early error if the memory access is out of bounds.
- // Otherwise, the lower bytes might be written to memory, but the upper bytes might not.
- if uint64(offset)+8 > math.MaxUint32 {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if ok := memoryInst.WriteUint64Le(offset+8, hi); !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if ok := memoryInst.WriteUint64Le(offset, lo); !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case 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 operationKindV128ReplaceLane:
- v := ce.popValue()
- hi, lo := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 shapeI32x4, 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 shapeI64x2, shapeF64x2:
- if op.B2 == 0 {
- lo = v
- } else {
- hi = v
- }
- }
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case operationKindV128ExtractLane:
- hi, lo := ce.popValue(), ce.popValue()
- var v uint64
- switch op.B1 {
- case 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 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 shapeI32x4, shapeF32x4:
- if op.B2 < 2 {
- v = uint64(uint32(lo >> (op.B2 * 32)))
- } else {
- v = uint64(uint32(hi >> ((op.B2 - 2) * 32)))
- }
- case shapeI64x2, shapeF64x2:
- if op.B2 == 0 {
- v = lo
- } else {
- v = hi
- }
- }
- ce.pushValue(v)
- frame.pc++
- case operationKindV128Splat:
- v := ce.popValue()
- var hi, lo uint64
- switch op.B1 {
- case 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 shapeI16x8:
- v4 := uint64(uint16(v))<<48 | uint64(uint16(v))<<32 | uint64(uint16(v))<<16 | uint64(uint16(v))
- hi, lo = v4, v4
- case shapeI32x4, shapeF32x4:
- v2 := uint64(uint32(v))<<32 | uint64(uint32(v))
- lo, hi = v2, v2
- case shapeI64x2, shapeF64x2:
- lo, hi = v, v
- }
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case 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 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 operationKindV128AnyTrue:
- hi, lo := ce.popValue(), ce.popValue()
- if hi != 0 || lo != 0 {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case operationKindV128AllTrue:
- hi, lo := ce.popValue(), ce.popValue()
- var ret bool
- switch op.B1 {
- case 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 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 shapeI32x4:
- ret = (uint32(lo) != 0) && (uint32(lo>>32) != 0) &&
- (uint32(hi) != 0) && (uint32(hi>>32) != 0)
- case shapeI64x2:
- ret = (lo != 0) &&
- (hi != 0)
- }
- if ret {
- ce.pushValue(1)
- } else {
- ce.pushValue(0)
- }
- frame.pc++
- case 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 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 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 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 shapeI64x2:
- if int64(lo) < 0 {
- res |= 0b01
- }
- if int(hi) < 0 {
- res |= 0b10
- }
- }
- ce.pushValue(res)
- frame.pc++
- case operationKindV128And:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- ce.pushValue(x1Lo & x2Lo)
- ce.pushValue(x1Hi & x2Hi)
- frame.pc++
- case operationKindV128Not:
- hi, lo := ce.popValue(), ce.popValue()
- ce.pushValue(^lo)
- ce.pushValue(^hi)
- frame.pc++
- case operationKindV128Or:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- ce.pushValue(x1Lo | x2Lo)
- ce.pushValue(x1Hi | x2Hi)
- frame.pc++
- case operationKindV128Xor:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- ce.pushValue(x1Lo ^ x2Lo)
- ce.pushValue(x1Hi ^ x2Hi)
- frame.pc++
- case 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 operationKindV128AndNot:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- ce.pushValue(x1Lo & (^x2Lo))
- ce.pushValue(x1Hi & (^x2Hi))
- frame.pc++
- case operationKindV128Shl:
- s := ce.popValue()
- hi, lo := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 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 shapeI64x2:
- s = s % 64
- lo = lo << s
- hi = hi << s
- }
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case operationKindV128Shr:
- s := ce.popValue()
- hi, lo := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 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 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 operationKindV128Cmp:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- var result []bool
- switch op.B1 {
- case 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 v128CmpTypeI64x2Eq:
- result = []bool{x1Lo == x2Lo, x1Hi == x2Hi}
- case v128CmpTypeI64x2Ne:
- result = []bool{x1Lo != x2Lo, x1Hi != x2Hi}
- case v128CmpTypeI64x2LtS:
- result = []bool{int64(x1Lo) < int64(x2Lo), int64(x1Hi) < int64(x2Hi)}
- case v128CmpTypeI64x2GtS:
- result = []bool{int64(x1Lo) > int64(x2Lo), int64(x1Hi) > int64(x2Hi)}
- case v128CmpTypeI64x2LeS:
- result = []bool{int64(x1Lo) <= int64(x2Lo), int64(x1Hi) <= int64(x2Hi)}
- case v128CmpTypeI64x2GeS:
- result = []bool{int64(x1Lo) >= int64(x2Lo), int64(x1Hi) >= int64(x2Hi)}
- case 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 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 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 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 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 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 v128CmpTypeF64x2Eq:
- result = []bool{
- math.Float64frombits(x1Lo) == math.Float64frombits(x2Lo),
- math.Float64frombits(x1Hi) == math.Float64frombits(x2Hi),
- }
- case v128CmpTypeF64x2Ne:
- result = []bool{
- math.Float64frombits(x1Lo) != math.Float64frombits(x2Lo),
- math.Float64frombits(x1Hi) != math.Float64frombits(x2Hi),
- }
- case v128CmpTypeF64x2Lt:
- result = []bool{
- math.Float64frombits(x1Lo) < math.Float64frombits(x2Lo),
- math.Float64frombits(x1Hi) < math.Float64frombits(x2Hi),
- }
- case v128CmpTypeF64x2Gt:
- result = []bool{
- math.Float64frombits(x1Lo) > math.Float64frombits(x2Lo),
- math.Float64frombits(x1Hi) > math.Float64frombits(x2Hi),
- }
- case v128CmpTypeF64x2Le:
- result = []bool{
- math.Float64frombits(x1Lo) <= math.Float64frombits(x2Lo),
- math.Float64frombits(x1Hi) <= math.Float64frombits(x2Hi),
- }
- case 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 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 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 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 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 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 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 operationKindV128Mul:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 shapeI64x2:
- retHi = x1hi * x2hi
- retLo = x1lo * x2lo
- case 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 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 operationKindV128Div:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- if op.B1 == 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 operationKindV128Neg:
- hi, lo := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 shapeI32x4:
- hi = uint64(-uint32(hi)) | (uint64(-uint32(hi>>32)) << 32)
- lo = uint64(-uint32(lo)) | (uint64(-uint32(lo>>32)) << 32)
- case shapeI64x2:
- hi = -hi
- lo = -lo
- case 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 shapeF64x2:
- hi = math.Float64bits(-math.Float64frombits(hi))
- lo = math.Float64bits(-math.Float64frombits(lo))
- }
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case operationKindV128Sqrt:
- hi, lo := ce.popValue(), ce.popValue()
- if op.B1 == 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 operationKindV128Abs:
- hi, lo := ce.popValue(), ce.popValue()
- switch op.B1 {
- case 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 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 shapeI32x4:
- hi = uint64(i32Abs(uint32(hi))) | (uint64(i32Abs(uint32(hi>>32))) << 32)
- lo = uint64(i32Abs(uint32(lo))) | (uint64(i32Abs(uint32(lo>>32))) << 32)
- case shapeI64x2:
- if int64(hi) < 0 {
- hi = -hi
- }
- if int64(lo) < 0 {
- lo = -lo
- }
- case shapeF32x4:
- hi = hi &^ (1<<31 | 1<<63)
- lo = lo &^ (1<<31 | 1<<63)
- case shapeF64x2:
- hi = hi &^ (1 << 63)
- lo = lo &^ (1 << 63)
- }
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case 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 operationKindV128Min:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 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 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 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 operationKindV128Max:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 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 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 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 operationKindV128AvgrU:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 operationKindV128Pmin:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- if op.B1 == 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 operationKindV128Pmax:
- x2hi, x2lo := ce.popValue(), ce.popValue()
- x1hi, x1lo := ce.popValue(), ce.popValue()
- var retLo, retHi uint64
- if op.B1 == 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 operationKindV128Ceil:
- hi, lo := ce.popValue(), ce.popValue()
- if op.B1 == 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 operationKindV128Floor:
- hi, lo := ce.popValue(), ce.popValue()
- if op.B1 == 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 operationKindV128Trunc:
- hi, lo := ce.popValue(), ce.popValue()
- if op.B1 == 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 operationKindV128Nearest:
- hi, lo := ce.popValue(), ce.popValue()
- if op.B1 == 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 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 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 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 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 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 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 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 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 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 operationKindV128ExtAddPairwise:
- hi, lo := ce.popValue(), ce.popValue()
-
- signed := op.B3
-
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 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 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 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 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 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 operationKindV128Narrow:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- signed := op.B3
-
- var retLo, retHi uint64
- switch op.B1 {
- case 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 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 operationKindV128Dot:
- x2Hi, x2Lo := ce.popValue(), ce.popValue()
- x1Hi, x1Lo := ce.popValue(), ce.popValue()
- lo, hi := v128Dot(x1Hi, x1Lo, x2Hi, x2Lo)
- ce.pushValue(lo)
- ce.pushValue(hi)
- frame.pc++
- case operationKindV128ITruncSatFromF:
- hi, lo := ce.popValue(), ce.popValue()
- signed := op.B3
- var retLo, retHi uint64
-
- switch op.B1 {
- case 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 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++
- case operationKindAtomicMemoryWait:
- timeout := int64(ce.popValue())
- exp := ce.popValue()
- offset := ce.popMemoryOffset(op)
- // Runtime instead of validation error because the spec intends to allow binaries to include
- // such instructions as long as they are not executed.
- if !memoryInst.Shared {
- panic(wasmruntime.ErrRuntimeExpectedSharedMemory)
- }
-
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- if int(offset) > len(memoryInst.Buffer)-4 {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(memoryInst.Wait32(offset, uint32(exp), timeout, func(mem *wasm.MemoryInstance, offset uint32) uint32 {
- mem.Mux.Lock()
- defer mem.Mux.Unlock()
- value, _ := mem.ReadUint32Le(offset)
- return value
- }))
- case unsignedTypeI64:
- if offset%8 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- if int(offset) > len(memoryInst.Buffer)-8 {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(memoryInst.Wait64(offset, exp, timeout, func(mem *wasm.MemoryInstance, offset uint32) uint64 {
- mem.Mux.Lock()
- defer mem.Mux.Unlock()
- value, _ := mem.ReadUint64Le(offset)
- return value
- }))
- }
- frame.pc++
- case operationKindAtomicMemoryNotify:
- count := ce.popValue()
- offset := ce.popMemoryOffset(op)
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- // Just a bounds check
- if offset >= memoryInst.Size() {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- res := memoryInst.Notify(offset, uint32(count))
- ce.pushValue(uint64(res))
- frame.pc++
- case operationKindAtomicFence:
- // Memory not required for fence only
- if memoryInst != nil {
- // An empty critical section can be used as a synchronization primitive, which is what
- // fence is. Probably, there are no spectests or defined behavior to confirm this yet.
- memoryInst.Mux.Lock()
- memoryInst.Mux.Unlock() //nolint:staticcheck
- }
- frame.pc++
- case operationKindAtomicLoad:
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- val, ok := memoryInst.ReadUint32Le(offset)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(uint64(val))
- case unsignedTypeI64:
- if offset%8 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- val, ok := memoryInst.ReadUint64Le(offset)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(val)
- }
- frame.pc++
- case operationKindAtomicLoad8:
- offset := ce.popMemoryOffset(op)
- memoryInst.Mux.Lock()
- val, ok := memoryInst.ReadByte(offset)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(uint64(val))
- frame.pc++
- case operationKindAtomicLoad16:
- offset := ce.popMemoryOffset(op)
- if offset%2 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- val, ok := memoryInst.ReadUint16Le(offset)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- ce.pushValue(uint64(val))
- frame.pc++
- case operationKindAtomicStore:
- val := ce.popValue()
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- ok := memoryInst.WriteUint32Le(offset, uint32(val))
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- case unsignedTypeI64:
- if offset%8 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- ok := memoryInst.WriteUint64Le(offset, val)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- }
- frame.pc++
- case operationKindAtomicStore8:
- val := byte(ce.popValue())
- offset := ce.popMemoryOffset(op)
- memoryInst.Mux.Lock()
- ok := memoryInst.WriteByte(offset, val)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case operationKindAtomicStore16:
- val := uint16(ce.popValue())
- offset := ce.popMemoryOffset(op)
- if offset%2 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- ok := memoryInst.WriteUint16Le(offset, val)
- memoryInst.Mux.Unlock()
- if !ok {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- frame.pc++
- case operationKindAtomicRMW:
- val := ce.popValue()
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint32Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- var newVal uint32
- switch atomicArithmeticOp(op.B2) {
- case atomicArithmeticOpAdd:
- newVal = old + uint32(val)
- case atomicArithmeticOpSub:
- newVal = old - uint32(val)
- case atomicArithmeticOpAnd:
- newVal = old & uint32(val)
- case atomicArithmeticOpOr:
- newVal = old | uint32(val)
- case atomicArithmeticOpXor:
- newVal = old ^ uint32(val)
- case atomicArithmeticOpNop:
- newVal = uint32(val)
- }
- memoryInst.WriteUint32Le(offset, newVal)
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- case unsignedTypeI64:
- if offset%8 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint64Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- var newVal uint64
- switch atomicArithmeticOp(op.B2) {
- case atomicArithmeticOpAdd:
- newVal = old + val
- case atomicArithmeticOpSub:
- newVal = old - val
- case atomicArithmeticOpAnd:
- newVal = old & val
- case atomicArithmeticOpOr:
- newVal = old | val
- case atomicArithmeticOpXor:
- newVal = old ^ val
- case atomicArithmeticOpNop:
- newVal = val
- }
- memoryInst.WriteUint64Le(offset, newVal)
- memoryInst.Mux.Unlock()
- ce.pushValue(old)
- }
- frame.pc++
- case operationKindAtomicRMW8:
- val := ce.popValue()
- offset := ce.popMemoryOffset(op)
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadByte(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- arg := byte(val)
- var newVal byte
- switch atomicArithmeticOp(op.B2) {
- case atomicArithmeticOpAdd:
- newVal = old + arg
- case atomicArithmeticOpSub:
- newVal = old - arg
- case atomicArithmeticOpAnd:
- newVal = old & arg
- case atomicArithmeticOpOr:
- newVal = old | arg
- case atomicArithmeticOpXor:
- newVal = old ^ arg
- case atomicArithmeticOpNop:
- newVal = arg
- }
- memoryInst.WriteByte(offset, newVal)
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- frame.pc++
- case operationKindAtomicRMW16:
- val := ce.popValue()
- offset := ce.popMemoryOffset(op)
- if offset%2 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint16Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- arg := uint16(val)
- var newVal uint16
- switch atomicArithmeticOp(op.B2) {
- case atomicArithmeticOpAdd:
- newVal = old + arg
- case atomicArithmeticOpSub:
- newVal = old - arg
- case atomicArithmeticOpAnd:
- newVal = old & arg
- case atomicArithmeticOpOr:
- newVal = old | arg
- case atomicArithmeticOpXor:
- newVal = old ^ arg
- case atomicArithmeticOpNop:
- newVal = arg
- }
- memoryInst.WriteUint16Le(offset, newVal)
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- frame.pc++
- case operationKindAtomicRMWCmpxchg:
- rep := ce.popValue()
- exp := ce.popValue()
- offset := ce.popMemoryOffset(op)
- switch unsignedType(op.B1) {
- case unsignedTypeI32:
- if offset%4 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint32Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if old == uint32(exp) {
- memoryInst.WriteUint32Le(offset, uint32(rep))
- }
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- case unsignedTypeI64:
- if offset%8 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint64Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if old == exp {
- memoryInst.WriteUint64Le(offset, rep)
- }
- memoryInst.Mux.Unlock()
- ce.pushValue(old)
- }
- frame.pc++
- case operationKindAtomicRMW8Cmpxchg:
- rep := byte(ce.popValue())
- exp := byte(ce.popValue())
- offset := ce.popMemoryOffset(op)
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadByte(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if old == exp {
- memoryInst.WriteByte(offset, rep)
- }
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- frame.pc++
- case operationKindAtomicRMW16Cmpxchg:
- rep := uint16(ce.popValue())
- exp := uint16(ce.popValue())
- offset := ce.popMemoryOffset(op)
- if offset%2 != 0 {
- panic(wasmruntime.ErrRuntimeUnalignedAtomic)
- }
- memoryInst.Mux.Lock()
- old, ok := memoryInst.ReadUint16Le(offset)
- if !ok {
- memoryInst.Mux.Unlock()
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- if old == exp {
- memoryInst.WriteUint16Le(offset, rep)
- }
- memoryInst.Mux.Unlock()
- ce.pushValue(uint64(old))
- frame.pc++
- default:
- frame.pc++
- }
- }
- ce.popFrame()
-}
-
-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, m *wasm.ModuleInstance, f *function, fnl experimental.FunctionListener) context.Context {
- def, typ := f.definition(), f.funcType
-
- ce.stackIterator.reset(ce.stack, ce.frames, f)
- fnl.Before(ctx, m, def, ce.peekValues(typ.ParamNumInUint64), &ce.stackIterator)
- ce.stackIterator.clear()
- ce.callNativeFunc(ctx, m, f)
- fnl.After(ctx, m, def, ce.peekValues(typ.ResultNumInUint64))
- 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 *unionOperation) uint32 {
- offset := op.U2 + ce.popValue()
- if offset > math.MaxUint32 {
- panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess)
- }
- return uint32(offset)
-}
-
-func (ce *callEngine) callGoFuncWithStack(ctx context.Context, m *wasm.ModuleInstance, f *function) {
- typ := f.funcType
- paramLen := typ.ParamNumInUint64
- resultLen := typ.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, m, 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]
- }
-}
-
-// v128Dot performs a dot product of two 64-bit vectors.
-// Note: for some reason (which I suspect is due to a bug in Go compiler's regalloc),
-// inlining this function causes a bug which happens **only when** we run with -race AND arm64 AND Go 1.22.
-func v128Dot(x1Hi, x1Lo, x2Hi, x2Lo uint64) (uint64, uint64) {
- r1 := int32(int16(x1Lo>>0)) * int32(int16(x2Lo>>0))
- r2 := int32(int16(x1Lo>>16)) * int32(int16(x2Lo>>16))
- r3 := int32(int16(x1Lo>>32)) * int32(int16(x2Lo>>32))
- r4 := int32(int16(x1Lo>>48)) * int32(int16(x2Lo>>48))
- r5 := int32(int16(x1Hi>>0)) * int32(int16(x2Hi>>0))
- r6 := int32(int16(x1Hi>>16)) * int32(int16(x2Hi>>16))
- r7 := int32(int16(x1Hi>>32)) * int32(int16(x2Hi>>32))
- r8 := int32(int16(x1Hi>>48)) * int32(int16(x2Hi>>48))
- return uint64(uint32(r1+r2)) | (uint64(uint32(r3+r4)) << 32), uint64(uint32(r5+r6)) | (uint64(uint32(r7+r8)) << 32)
-}