diff --git a/.github/workflows/spectest.yaml b/.github/workflows/spectest.yaml index 26ee01c904..c0a9918ac4 100644 --- a/.github/workflows/spectest.yaml +++ b/.github/workflows/spectest.yaml @@ -35,6 +35,7 @@ jobs: spec-version: - "v1" - "v2" + - "threads" steps: - uses: actions/checkout@v3 @@ -61,6 +62,7 @@ jobs: spec-version: - "v1" - "v2" + - "threads" steps: - uses: actions/checkout@v3 diff --git a/Makefile b/Makefile index 8e36cf8c51..3c142682e2 100644 --- a/Makefile +++ b/Makefile @@ -125,11 +125,15 @@ spectest_v2_dir := $(spectest_base_dir)/v2 spectest_v2_testdata_dir := $(spectest_v2_dir)/testdata # Latest draft state as of Dec 16, 2022. spec_version_v2 := 1782235239ddebaf2cb079b00fdaa2d2c4dedba3 +spectest_threads_dir := $(spectest_base_dir)/threads +spectest_threads_testdata_dir := $(spectest_threads_dir)/testdata +spec_version_threads := cc01bf0d17ba3fb1dc59fb7c5c725838aff18b50 .PHONY: build.spectest build.spectest: @$(MAKE) build.spectest.v1 @$(MAKE) build.spectest.v2 + @$(MAKE) build.spectest.threads .PHONY: build.spectest.v1 build.spectest.v1: # Note: wabt by default uses >1.0 features, so wast2json flags might drift as they include more. See WebAssembly/wabt#1878 @@ -169,9 +173,21 @@ build.spectest.v2: # Note: SIMD cases are placed in the "simd" subdirectory. wast2json --debug-names $$f; \ done +.PHONY: build.spectest.threads +build.spectest.threads: + @mkdir -p $(spectest_threads_testdata_dir) + @cd $(spectest_threads_testdata_dir) \ + && curl -sSL 'https://api.github.com/repos/WebAssembly/threads/contents/test/core?ref=$(spec_version_threads)' | jq -r '.[]| .download_url' | grep -E "atomic.wast" | xargs -Iurl curl -sJL url -O +# Fix broken CAS spectests +# https://github.com/WebAssembly/threads/issues/195#issuecomment-1318429506 + @cd $(spectest_threads_testdata_dir) && patch < ../atomic.wast.patch + @cd $(spectest_threads_testdata_dir) && for f in `find . -name '*.wast'`; do \ + wast2json --enable-threads --debug-names $$f; \ + done + .PHONY: test test: - @go test $(go_test_options) $$(go list ./... | grep -vE '$(spectest_v1_dir)|$(spectest_v2_dir)') + @go test $(go_test_options) $$(go list ./... | grep -vE '$(spectest_v1_dir)|$(spectest_v2_dir)|$(spectest_threads_dir)') @cd internal/version/testdata && go test $(go_test_options) ./... .PHONY: coverage @@ -185,6 +201,7 @@ coverage: ## Generate test coverage spectest: @$(MAKE) spectest.v1 @$(MAKE) spectest.v2 + @$(MAKE) spectest.threads spectest.v1: @go test $(go_test_options) $$(go list ./... | grep $(spectest_v1_dir)) @@ -192,6 +209,9 @@ spectest.v1: spectest.v2: @go test $(go_test_options) $$(go list ./... | grep $(spectest_v2_dir)) +spectest.threads: + @go test $(go_test_options) $$(go list ./... | grep $(spectest_threads_dir)) + golangci_lint_path := $(shell go env GOPATH)/bin/golangci-lint $(golangci_lint_path): diff --git a/api/features.go b/api/features.go index 04d35e18fc..22a74f41c9 100644 --- a/api/features.go +++ b/api/features.go @@ -143,6 +143,8 @@ const ( // Note: The instruction list is too long to enumerate in godoc. // See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md CoreFeatureSIMD + + // Update experimental/features.go when adding elements here. ) // SetEnabled enables or disables the feature or group of features. @@ -207,6 +209,9 @@ func featureName(f CoreFeatures) string { case CoreFeatureSIMD: // match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md return "simd" + case CoreFeatureSIMD << 1: // Defined in experimental/features.go + // match https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md + return "threads" } return "" } diff --git a/experimental/features.go b/experimental/features.go new file mode 100644 index 0000000000..964c201bb2 --- /dev/null +++ b/experimental/features.go @@ -0,0 +1,14 @@ +package experimental + +import "github.com/tetratelabs/wazero/api" + +// CoreFeaturesThreads enables threads instructions ("threads"). +// +// # Notes +// +// - This is not yet implemented by default, so you will need to use +// wazero.NewRuntimeConfigInterpreter +// - The instruction list is too long to enumerate in godoc. +// See https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md +// - Atomic operations are guest-only until api.Memory or otherwise expose them to host functions. +const CoreFeaturesThreads = api.CoreFeatureSIMD << 1 // TODO: Implement the compiler engine diff --git a/experimental/features_example_test.go b/experimental/features_example_test.go new file mode 100644 index 0000000000..333b086098 --- /dev/null +++ b/experimental/features_example_test.go @@ -0,0 +1,94 @@ +package experimental_test + +import ( + "context" + _ "embed" + "fmt" + "log" + "sync" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +// pthreadWasm was generated by the following: +// +// docker run -it --rm -v `pwd`/testdata:/workspace ghcr.io/webassembly/wasi-sdk:wasi-sdk-20 sh -c '$CC -o /workspace/pthread.wasm /workspace/pthread.c --target=wasm32-wasi-threads --sysroot=/wasi-sysroot -pthread -mexec-model=reactor -Wl,--export=run -Wl,--export=get' +// +// TODO: Use zig cc instead of wasi-sdk to compile when it supports wasm32-wasi-threads +// https://github.com/ziglang/zig/issues/15484 +// +//go:embed testdata/pthread.wasm +var pthreadWasm []byte + +// This shows how to use a WebAssembly module compiled with the threads feature. +func ExampleCoreFeaturesThreads() { + // Use a default context + ctx := context.Background() + + // Threads support is currently only supported with interpreter, so the config + // must explicitly specify it. + cfg := wazero.NewRuntimeConfigInterpreter() + + // Threads support must be enabled explicitly in addition to standard V2 features. + cfg = cfg.WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesThreads) + + r := wazero.NewRuntimeWithConfig(ctx, cfg) + defer r.Close(ctx) + + // Because we are using wasi-sdk to compile the guest, we must initialize WASI. + wasi_snapshot_preview1.MustInstantiate(ctx, r) + + mod, err := r.Instantiate(ctx, pthreadWasm) + if err != nil { + log.Panicln(err) + } + + // Channel to synchronize start of goroutines before running. + startCh := make(chan struct{}) + // Channel to synchronize end of goroutines. + endCh := make(chan struct{}) + + // Unfortunately, while memory accesses are thread-safe using atomic operations, compilers such + // as LLVM still have global state that is not handled thread-safe, preventing actually making + // concurrent invocations. We go ahead and add a global lock for now until this is resolved. + // TODO: Remove this lock once functions can actually be called concurrently. + var mu sync.Mutex + + // We start up 8 goroutines and run for 6000 iterations each. The count should reach + // 48000, at the end, but it would not if threads weren't working! + for i := 0; i < 8; i++ { + go func() { + defer func() { endCh <- struct{}{} }() + <-startCh + + mu.Lock() + defer mu.Unlock() + + // ExportedFunction must be called within each goroutine to have independent call stacks. + // This incurs some overhead, a sync.Pool can be used to reduce this overhead if neeeded. + fn := mod.ExportedFunction("run") + for i := 0; i < 6000; i++ { + _, err := fn.Call(ctx) + if err != nil { + log.Panicln(err) + } + } + }() + } + for i := 0; i < 8; i++ { + startCh <- struct{}{} + } + for i := 0; i < 8; i++ { + <-endCh + } + + res, err := mod.ExportedFunction("get").Call(ctx) + if err != nil { + log.Panicln(err) + } + fmt.Println(res[0]) + // Output: 48000 +} diff --git a/experimental/testdata/pthread.c b/experimental/testdata/pthread.c new file mode 100644 index 0000000000..d353cfbaf4 --- /dev/null +++ b/experimental/testdata/pthread.c @@ -0,0 +1,18 @@ +#include + +pthread_mutex_t mutex; +int count = 0; + +void run() { + pthread_mutex_lock(&mutex); + count++; + pthread_mutex_unlock(&mutex); +} + +int get() { + int res; + pthread_mutex_lock(&mutex); + res = count; + pthread_mutex_unlock(&mutex); + return res; +} diff --git a/experimental/testdata/pthread.wasm b/experimental/testdata/pthread.wasm new file mode 100755 index 0000000000..ce8abba2d4 Binary files /dev/null and b/experimental/testdata/pthread.wasm differ diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go index 1cf33b0b2b..7f3281550d 100644 --- a/internal/engine/interpreter/interpreter.go +++ b/internal/engine/interpreter/interpreter.go @@ -3896,6 +3896,368 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, m *wasm.ModuleInstance ce.pushValue(retLo) ce.pushValue(retHi) frame.pc++ + case wazeroir.OperationKindAtomicMemoryWait: + timeout := int64(ce.popValue()) + exp := ce.popValue() + offset := ce.popMemoryOffset(op) + if !memoryInst.Shared { + panic(wasmruntime.ErrRuntimeExpectedSharedMemory) + } + var cur uint64 + switch wazeroir.UnsignedType(op.B1) { + case wazeroir.UnsignedTypeI32: + if offset%4 != 0 { + panic(wasmruntime.ErrRuntimeUnalignedAtomic) + } + memoryInst.Mux.Lock() + val, ok := memoryInst.ReadUint32Le(offset) + memoryInst.Mux.Unlock() + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + cur = uint64(val) + case wazeroir.UnsignedTypeI64: + if offset%8 != 0 { + panic(wasmruntime.ErrRuntimeUnalignedAtomic) + } + memoryInst.Mux.Lock() + val, ok := memoryInst.ReadUint64Le(offset) + memoryInst.Mux.Unlock() + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + cur = val + } + if exp != cur { + ce.pushValue(1) + } else { + tooMany, timedOut := memoryInst.Wait(offset, timeout) + if tooMany { + panic(wasmruntime.ErrRuntimeTooManyWaiters) + } + if timedOut { + ce.pushValue(2) + } else { + ce.pushValue(0) + } + } + frame.pc++ + case wazeroir.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 wazeroir.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 wazeroir.OperationKindAtomicLoad: + offset := ce.popMemoryOffset(op) + switch wazeroir.UnsignedType(op.B1) { + case wazeroir.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 wazeroir.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 wazeroir.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 wazeroir.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 wazeroir.OperationKindAtomicStore: + val := ce.popValue() + offset := ce.popMemoryOffset(op) + switch wazeroir.UnsignedType(op.B1) { + case wazeroir.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 wazeroir.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 wazeroir.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 wazeroir.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 wazeroir.OperationKindAtomicRMW: + val := ce.popValue() + offset := ce.popMemoryOffset(op) + switch wazeroir.UnsignedType(op.B1) { + case wazeroir.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 wazeroir.AtomicArithmeticOp(op.B2) { + case wazeroir.AtomicArithmeticOpAdd: + newVal = old + uint32(val) + case wazeroir.AtomicArithmeticOpSub: + newVal = old - uint32(val) + case wazeroir.AtomicArithmeticOpAnd: + newVal = old & uint32(val) + case wazeroir.AtomicArithmeticOpOr: + newVal = old | uint32(val) + case wazeroir.AtomicArithmeticOpXor: + newVal = old ^ uint32(val) + case wazeroir.AtomicArithmeticOpNop: + newVal = uint32(val) + } + memoryInst.WriteUint32Le(offset, newVal) + memoryInst.Mux.Unlock() + ce.pushValue(uint64(old)) + case wazeroir.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 wazeroir.AtomicArithmeticOp(op.B2) { + case wazeroir.AtomicArithmeticOpAdd: + newVal = old + val + case wazeroir.AtomicArithmeticOpSub: + newVal = old - val + case wazeroir.AtomicArithmeticOpAnd: + newVal = old & val + case wazeroir.AtomicArithmeticOpOr: + newVal = old | val + case wazeroir.AtomicArithmeticOpXor: + newVal = old ^ val + case wazeroir.AtomicArithmeticOpNop: + newVal = val + } + memoryInst.WriteUint64Le(offset, newVal) + memoryInst.Mux.Unlock() + ce.pushValue(old) + } + frame.pc++ + case wazeroir.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 wazeroir.AtomicArithmeticOp(op.B2) { + case wazeroir.AtomicArithmeticOpAdd: + newVal = old + arg + case wazeroir.AtomicArithmeticOpSub: + newVal = old - arg + case wazeroir.AtomicArithmeticOpAnd: + newVal = old & arg + case wazeroir.AtomicArithmeticOpOr: + newVal = old | arg + case wazeroir.AtomicArithmeticOpXor: + newVal = old ^ arg + case wazeroir.AtomicArithmeticOpNop: + newVal = arg + } + memoryInst.WriteByte(offset, newVal) + memoryInst.Mux.Unlock() + ce.pushValue(uint64(old)) + frame.pc++ + case wazeroir.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 wazeroir.AtomicArithmeticOp(op.B2) { + case wazeroir.AtomicArithmeticOpAdd: + newVal = old + arg + case wazeroir.AtomicArithmeticOpSub: + newVal = old - arg + case wazeroir.AtomicArithmeticOpAnd: + newVal = old & arg + case wazeroir.AtomicArithmeticOpOr: + newVal = old | arg + case wazeroir.AtomicArithmeticOpXor: + newVal = old ^ arg + case wazeroir.AtomicArithmeticOpNop: + newVal = arg + } + memoryInst.WriteUint16Le(offset, newVal) + memoryInst.Mux.Unlock() + ce.pushValue(uint64(old)) + frame.pc++ + case wazeroir.OperationKindAtomicRMWCmpxchg: + rep := ce.popValue() + exp := ce.popValue() + offset := ce.popMemoryOffset(op) + switch wazeroir.UnsignedType(op.B1) { + case wazeroir.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 wazeroir.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 wazeroir.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 wazeroir.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++ } diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go index ab3c19a6ec..94e91a8e29 100644 --- a/internal/integration_test/engine/adhoc_test.go +++ b/internal/integration_test/engine/adhoc_test.go @@ -66,7 +66,9 @@ func runAllTests(t *testing.T, tests map[string]func(t *testing.T, r wazero.Runt testf := testf // pin t.Run(name, func(t *testing.T) { t.Parallel() - testf(t, wazero.NewRuntimeWithConfig(testCtx, config)) + r := wazero.NewRuntimeWithConfig(testCtx, config) + defer r.Close(testCtx) + testf(t, r) }) } } diff --git a/internal/integration_test/engine/testdata/threads/add.wasm b/internal/integration_test/engine/testdata/threads/add.wasm new file mode 100644 index 0000000000..aaa543ff7f Binary files /dev/null and b/internal/integration_test/engine/testdata/threads/add.wasm differ diff --git a/internal/integration_test/engine/testdata/threads/add.wat b/internal/integration_test/engine/testdata/threads/add.wat new file mode 100644 index 0000000000..014862c722 --- /dev/null +++ b/internal/integration_test/engine/testdata/threads/add.wat @@ -0,0 +1,38 @@ +(module + (memory 1 1 shared) + + (func (export "run32") + (i32.atomic.rmw.add (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run64") + (i64.atomic.rmw.add (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run32_8") + (i32.atomic.rmw8.add_u (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run32_16") + (i32.atomic.rmw16.add_u (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run64_8") + (i64.atomic.rmw8.add_u (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run64_16") + (i64.atomic.rmw16.add_u (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run64_32") + (i64.atomic.rmw32.add_u (i32.const 0) (i64.const 1)) + (drop) + ) +) diff --git a/internal/integration_test/engine/testdata/threads/mutex.wasm b/internal/integration_test/engine/testdata/threads/mutex.wasm new file mode 100644 index 0000000000..f2aa4957fe Binary files /dev/null and b/internal/integration_test/engine/testdata/threads/mutex.wasm differ diff --git a/internal/integration_test/engine/testdata/threads/mutex.wat b/internal/integration_test/engine/testdata/threads/mutex.wat new file mode 100644 index 0000000000..0ec7b55054 --- /dev/null +++ b/internal/integration_test/engine/testdata/threads/mutex.wat @@ -0,0 +1,312 @@ +(module + (memory 1 1 shared) + + (func $tryLockMutex32 + (param $mutexAddr i32) (result i32) + ;; Attempt to grab the mutex. The cmpxchg operation atomically + ;; does the following: + ;; - Loads the value at $mutexAddr. + ;; - If it is 0 (unlocked), set it to 1 (locked). + ;; - Return the originally loaded value. + (i32.atomic.rmw.cmpxchg + (local.get $mutexAddr) ;; mutex address + (i32.const 0) ;; expected value (0 => unlocked) + (i32.const 1)) ;; replacement value (1 => locked) + + ;; The top of the stack is the originally loaded value. + ;; If it is 0, this means we acquired the mutex. We want to + ;; return the inverse (1 means mutex acquired), so use i32.eqz + ;; as a logical not. + (i32.eqz) + ) + + ;; Lock a mutex at the given address, retrying until successful. + (func $lockMutex32 + (param $mutexAddr i32) + (block $done + (loop $retry + ;; Try to lock the mutex. $tryLockMutex returns 1 if the mutex + ;; was locked, and 0 otherwise. + (call $tryLockMutex32 (local.get $mutexAddr)) + (br_if $done) + + ;; Wait for the other agent to finish with mutex. + (memory.atomic.wait32 + (local.get $mutexAddr) ;; mutex address + (i32.const 1) ;; expected value (1 => locked) + (i64.const -1)) ;; infinite timeout + + ;; memory.atomic.wait32 returns: + ;; 0 => "ok", woken by another agent. + ;; 1 => "not-equal", loaded value != expected value + ;; 2 => "timed-out", the timeout expired + ;; + ;; Since there is an infinite timeout, only 0 or 1 will be returned. In + ;; either case we should try to acquire the mutex again, so we can + ;; ignore the result. + (drop) + + ;; Try to acquire the lock again. + (br $retry) + ) + ) + ) + + ;; Unlock a mutex at the given address. + (func $unlockMutex32 + (param $mutexAddr i32) + ;; Unlock the mutex. + (i32.atomic.store + (local.get $mutexAddr) ;; mutex address + (i32.const 0)) ;; 0 => unlocked + + ;; Notify one agent that is waiting on this lock. + (drop + (memory.atomic.notify + (local.get $mutexAddr) ;; mutex address + (i32.const 1))) ;; notify 1 waiter + ) + + (func (export "run32") + (call $lockMutex32 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex32 (i32.const 0)) + ) + + ;; Below functions are the same as above with different integer sizes so + ;; have comments elided see above to understand logic. + + (func $tryLockMutex64 + (param $mutexAddr i32) (result i32) + (i64.atomic.rmw.cmpxchg + (local.get $mutexAddr) + (i64.const 0) + (i64.const 1)) + (i64.eqz) + ) + (func $lockMutex64 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex64 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait64 + (local.get $mutexAddr) + (i64.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex64 + (param $mutexAddr i32) + (i64.atomic.store + (local.get $mutexAddr) + (i64.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run64") + (call $lockMutex64 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex64 (i32.const 0)) + ) + + (func $tryLockMutex32_8 + (param $mutexAddr i32) (result i32) + (i32.atomic.rmw8.cmpxchg_u + (local.get $mutexAddr) + (i32.const 0) + (i32.const 1)) + (i32.eqz) + ) + (func $lockMutex32_8 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex32_8 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait32 + (local.get $mutexAddr) + (i32.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex32_8 + (param $mutexAddr i32) + (i32.atomic.store8 + (local.get $mutexAddr) + (i32.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run32_8") + (call $lockMutex32_8 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex32_8 (i32.const 0)) + ) + + (func $tryLockMutex32_16 + (param $mutexAddr i32) (result i32) + (i32.atomic.rmw16.cmpxchg_u + (local.get $mutexAddr) + (i32.const 0) + (i32.const 1)) + (i32.eqz) + ) + (func $lockMutex32_16 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex32_16 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait32 + (local.get $mutexAddr) + (i32.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex32_16 + (param $mutexAddr i32) + (i32.atomic.store16 + (local.get $mutexAddr) + (i32.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run32_16") + (call $lockMutex32_16 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex32_16 (i32.const 0)) + ) + + (func $tryLockMutex64_8 + (param $mutexAddr i32) (result i32) + (i64.atomic.rmw8.cmpxchg_u + (local.get $mutexAddr) + (i64.const 0) + (i64.const 1)) + (i64.eqz) + ) + (func $lockMutex64_8 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex64_8 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait64 + (local.get $mutexAddr) + (i64.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex64_8 + (param $mutexAddr i32) + (i64.atomic.store8 + (local.get $mutexAddr) + (i64.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run64_8") + (call $lockMutex64_8 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex64_8 (i32.const 0)) + ) + + (func $tryLockMutex64_16 + (param $mutexAddr i32) (result i32) + (i64.atomic.rmw16.cmpxchg_u + (local.get $mutexAddr) + (i64.const 0) + (i64.const 1)) + (i64.eqz) + ) + (func $lockMutex64_16 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex64_16 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait64 + (local.get $mutexAddr) + (i64.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex64_16 + (param $mutexAddr i32) + (i64.atomic.store16 + (local.get $mutexAddr) + (i64.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run64_16") + (call $lockMutex64_16 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex64_16 (i32.const 0)) + ) + + (func $tryLockMutex64_32 + (param $mutexAddr i32) (result i32) + (i64.atomic.rmw32.cmpxchg_u + (local.get $mutexAddr) + (i64.const 0) + (i64.const 1)) + (i64.eqz) + ) + (func $lockMutex64_32 + (param $mutexAddr i32) + (block $done + (loop $retry + (call $tryLockMutex64_32 (local.get $mutexAddr)) + (br_if $done) + (memory.atomic.wait64 + (local.get $mutexAddr) + (i64.const 1) + (i64.const -1)) + (drop) + (br $retry) + ) + ) + ) + (func $unlockMutex64_32 + (param $mutexAddr i32) + (i64.atomic.store32 + (local.get $mutexAddr) + (i64.const 0)) + (drop + (memory.atomic.notify + (local.get $mutexAddr) + (i32.const 1))) + ) + (func (export "run64_32") + (call $lockMutex64_32 (i32.const 0)) + (i32.store (i32.const 8) (i32.load (i32.const 8)) (i32.add (i32.const 1))) + (call $unlockMutex64_32 (i32.const 0)) + ) +) diff --git a/internal/integration_test/engine/testdata/threads/sub.wasm b/internal/integration_test/engine/testdata/threads/sub.wasm new file mode 100644 index 0000000000..1ea85e0b93 Binary files /dev/null and b/internal/integration_test/engine/testdata/threads/sub.wasm differ diff --git a/internal/integration_test/engine/testdata/threads/sub.wat b/internal/integration_test/engine/testdata/threads/sub.wat new file mode 100644 index 0000000000..620947e3a6 --- /dev/null +++ b/internal/integration_test/engine/testdata/threads/sub.wat @@ -0,0 +1,38 @@ +(module + (memory 1 1 shared) + + (func (export "run32") + (i32.atomic.rmw.sub (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run64") + (i64.atomic.rmw.sub (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run32_8") + (i32.atomic.rmw8.sub_u (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run32_16") + (i32.atomic.rmw16.sub_u (i32.const 0) (i32.const 1)) + (drop) + ) + + (func (export "run64_8") + (i64.atomic.rmw8.sub_u (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run64_16") + (i64.atomic.rmw16.sub_u (i32.const 0) (i64.const 1)) + (drop) + ) + + (func (export "run64_32") + (i64.atomic.rmw32.sub_u (i32.const 0) (i64.const 1)) + (drop) + ) +) diff --git a/internal/integration_test/engine/testdata/threads/xor.wasm b/internal/integration_test/engine/testdata/threads/xor.wasm new file mode 100644 index 0000000000..7aacfc9690 Binary files /dev/null and b/internal/integration_test/engine/testdata/threads/xor.wasm differ diff --git a/internal/integration_test/engine/testdata/threads/xor.wat b/internal/integration_test/engine/testdata/threads/xor.wat new file mode 100644 index 0000000000..a466662bdb --- /dev/null +++ b/internal/integration_test/engine/testdata/threads/xor.wat @@ -0,0 +1,38 @@ +(module + (memory 1 1 shared) + + (func (export "run32") + (i32.atomic.rmw.xor (i32.const 0) (i32.const 0xFFFFFFFF)) + (drop) + ) + + (func (export "run64") + (i64.atomic.rmw.xor (i32.const 0) (i64.const 0xFFFFFFFFFFFFFFFF)) + (drop) + ) + + (func (export "run32_8") + (i32.atomic.rmw8.xor_u (i32.const 0) (i32.const 0xFFFFFFFF)) + (drop) + ) + + (func (export "run32_16") + (i32.atomic.rmw16.xor_u (i32.const 0) (i32.const 0xFFFFFFFF)) + (drop) + ) + + (func (export "run64_8") + (i64.atomic.rmw8.xor_u (i32.const 0) (i64.const 0xFFFFFFFFFFFFFFFF)) + (drop) + ) + + (func (export "run64_16") + (i64.atomic.rmw16.xor_u (i32.const 0) (i64.const 0xFFFFFFFFFFFFFFFF)) + (drop) + ) + + (func (export "run64_32") + (i64.atomic.rmw32.xor_u (i32.const 0) (i64.const 0xFFFFFFFFFFFFFFFF)) + (drop) + ) +) diff --git a/internal/integration_test/engine/threads_test.go b/internal/integration_test/engine/threads_test.go new file mode 100644 index 0000000000..7fd822028b --- /dev/null +++ b/internal/integration_test/engine/threads_test.go @@ -0,0 +1,264 @@ +package adhoc + +import ( + _ "embed" + "testing" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/internal/testing/hammer" + "github.com/tetratelabs/wazero/internal/testing/require" +) + +// We do not currently have hammer tests for bitwise and/or operations. The tests are designed to have +// input that changes deterministically every iteration, which is difficult to model with these operations. +// This is likely why atomic and/or do not show up in the wild very often if at all. +var ( + // memory.atomic.notify, memory.atomic.wait32, memory.atomic.wait64 + // i32.atomic.store, i32.atomic.rmw.cmpxchg + // i64.atomic.store, i64.atomic.rmw.cmpxchg + // i32.atomic.store8, i32.atomic.rmw8.cmpxchg_u + // i32.atomic.store16, i32.atomic.rmw16.cmpxchg_u + // i64.atomic.store8, i64.atomic.rmw8.cmpxchg_u + // i64.atomic.store16, i64.atomic.rmw16.cmpxchg_u + // i64.atomic.store32, i64.atomic.rmw32.cmpxchg_u + //go:embed testdata/threads/mutex.wasm + mutexWasm []byte + + // i32.atomic.rmw.add, i64.atomic.rmw.add, i32.atomic.rmw8.add_u, i32.atomic.rmw16.add_u, i64.atomic.rmw8.add_u, i64.atomic.rmw16.add_u, i64.atomic.rmw32.add_u + //go:embed testdata/threads/add.wasm + addWasm []byte + + // i32.atomic.rmw.sub, i64.atomic.rmw.sub, i32.atomic.rmw8.sub_u, i32.atomic.rmw16.sub_u, i64.atomic.rmw8.sub_u, i64.atomic.rmw16.sub_u, i64.atomic.rmw32.sub_u + //go:embed testdata/threads/sub.wasm + subWasm []byte + + // i32.atomic.rmw.xor, i64.atomic.rmw.xor, i32.atomic.rmw8.xor_u, i32.atomic.rmw16.xor_u, i64.atomic.rmw8.xor_u, i64.atomic.rmw16.xor_u, i64.atomic.rmw32.xor_u + //go:embed testdata/threads/xor.wasm + xorWasm []byte +) + +var threadTests = map[string]func(t *testing.T, r wazero.Runtime){ + "increment guarded by mutex": incrementGuardedByMutex, + "atomic add": atomicAdd, + "atomic sub": atomicSub, + "atomic xor": atomicXor, +} + +func TestThreadsNotEnabled(t *testing.T) { + r := wazero.NewRuntime(testCtx) + _, err := r.Instantiate(testCtx, mutexWasm) + require.EqualError(t, err, "section memory: shared memory requested but threads feature not enabled") +} + +func TestThreadsInterpreter(t *testing.T) { + runAllTests(t, threadTests, wazero.NewRuntimeConfigInterpreter().WithCoreFeatures(api.CoreFeaturesV2|experimental.CoreFeaturesThreads)) +} + +func incrementGuardedByMutex(t *testing.T, r wazero.Runtime) { + tests := []struct { + fn string + }{ + { + fn: "run32", + }, + { + fn: "run64", + }, + { + fn: "run32_8", + }, + { + fn: "run32_16", + }, + { + fn: "run64_8", + }, + { + fn: "run64_16", + }, + { + fn: "run64_32", + }, + } + for _, tc := range tests { + tt := tc + t.Run(tt.fn, func(t *testing.T) { + mod, err := r.Instantiate(testCtx, mutexWasm) + require.NoError(t, err) + + hammer.NewHammer(t, 8, 30000).Run(func(name string) { + _, err := mod.ExportedFunction(tt.fn).Call(testCtx) + require.NoError(t, err) + }, func() {}) + + // Cheat that LE encoding can read both 32 and 64 bits + res, ok := mod.Memory().ReadUint32Le(8) + require.True(t, ok) + require.Equal(t, uint32(8*30000), res) + }) + } +} + +func atomicAdd(t *testing.T, r wazero.Runtime) { + tests := []struct { + fn string + exp uint32 + }{ + { + fn: "run32", + exp: 8 * 30000, + }, + { + fn: "run64", + exp: 8 * 30000, + }, + { + fn: "run32_8", + // Overflows + exp: (8 * 30000) % (1 << 8), + }, + { + fn: "run32_16", + // Overflows + exp: (8 * 30000) % (1 << 16), + }, + { + fn: "run64_8", + // Overflows + exp: (8 * 30000) % (1 << 8), + }, + { + fn: "run64_16", + // Overflows + exp: (8 * 30000) % (1 << 16), + }, + { + fn: "run64_32", + exp: 8 * 30000, + }, + } + for _, tc := range tests { + tt := tc + t.Run(tt.fn, func(t *testing.T) { + mod, err := r.Instantiate(testCtx, addWasm) + require.NoError(t, err) + + hammer.NewHammer(t, 8, 30000).Run(func(name string) { + _, err := mod.ExportedFunction(tt.fn).Call(testCtx) + require.NoError(t, err) + }, func() {}) + + // Cheat that LE encoding can read both 32 and 64 bits + res, ok := mod.Memory().ReadUint32Le(0) + require.True(t, ok) + require.Equal(t, tt.exp, res) + }) + } +} + +func atomicSub(t *testing.T, r wazero.Runtime) { + tests := []struct { + fn string + exp int32 + }{ + { + fn: "run32", + exp: -(8 * 30000), + }, + { + fn: "run64", + exp: -(8 * 30000), + }, + { + fn: "run32_8", + // Overflows + exp: (1 << 8) - ((8 * 30000) % (1 << 8)), + }, + { + fn: "run32_16", + // Overflows + exp: (1 << 16) - ((8 * 30000) % (1 << 16)), + }, + { + fn: "run64_8", + // Overflows + exp: (1 << 8) - ((8 * 30000) % (1 << 8)), + }, + { + fn: "run64_16", + // Overflows + exp: (1 << 16) - ((8 * 30000) % (1 << 16)), + }, + { + fn: "run64_32", + exp: -(8 * 30000), + }, + } + for _, tc := range tests { + tt := tc + t.Run(tt.fn, func(t *testing.T) { + mod, err := r.Instantiate(testCtx, subWasm) + require.NoError(t, err) + + hammer.NewHammer(t, 8, 30000).Run(func(name string) { + _, err := mod.ExportedFunction(tt.fn).Call(testCtx) + require.NoError(t, err) + }, func() {}) + + // Cheat that LE encoding can read both 32 and 64 bits + res, ok := mod.Memory().ReadUint32Le(0) + require.True(t, ok) + require.Equal(t, tt.exp, int32(res)) + }) + } +} + +func atomicXor(t *testing.T, r wazero.Runtime) { + tests := []struct { + fn string + }{ + { + fn: "run32", + }, + { + fn: "run64", + }, + { + fn: "run32_8", + }, + { + fn: "run32_16", + }, + { + fn: "run64_8", + }, + { + fn: "run64_16", + }, + { + fn: "run64_32", + }, + } + for _, tc := range tests { + tt := tc + t.Run(tt.fn, func(t *testing.T) { + mod, err := r.Instantiate(testCtx, xorWasm) + require.NoError(t, err) + + mod.Memory().WriteUint32Le(0, 12345) + + hammer.NewHammer(t, 8, 30000).Run(func(name string) { + _, err := mod.ExportedFunction(tt.fn).Call(testCtx) + require.NoError(t, err) + }, func() {}) + + // Cheat that LE encoding can read both 32 and 64 bits + res, ok := mod.Memory().ReadUint32Le(0) + require.True(t, ok) + // Even number of iterations, the value should be unchanged. + require.Equal(t, uint32(12345), res) + }) + } +} diff --git a/internal/integration_test/spectest/spectest.go b/internal/integration_test/spectest/spectest.go index 253e6fd058..a91099f5cd 100644 --- a/internal/integration_test/spectest/spectest.go +++ b/internal/integration_test/spectest/spectest.go @@ -290,6 +290,8 @@ func (c command) expectedError() (err error) { panic("unreachable") } switch c.Text { + case "expected shared memory": + err = wasmruntime.ErrRuntimeExpectedSharedMemory case "out of bounds memory access": err = wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess case "indirect call type mismatch", "indirect call": @@ -302,6 +304,8 @@ func (c command) expectedError() (err error) { err = wasmruntime.ErrRuntimeInvalidConversionToInteger case "integer divide by zero": err = wasmruntime.ErrRuntimeIntegerDivideByZero + case "unaligned atomic": + err = wasmruntime.ErrRuntimeUnalignedAtomic case "unreachable": err = wasmruntime.ErrRuntimeUnreachable default: @@ -366,7 +370,7 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, fc filecache.Ca // If the go:embed path resolution was wrong, this fails. // https://github.com/tetratelabs/wazero/issues/247 - require.True(t, len(jsonfiles) > 1, "len(jsonfiles)=%d (not greater than one)", len(jsonfiles)) + require.True(t, len(jsonfiles) > 0, "len(jsonfiles)=%d (not greater than one)", len(jsonfiles)) for _, f := range jsonfiles { raw, err := testDataFS.ReadFile(f) diff --git a/internal/integration_test/spectest/threads/atomic.wast.patch b/internal/integration_test/spectest/threads/atomic.wast.patch new file mode 100644 index 0000000000..6b60b7fa7d --- /dev/null +++ b/internal/integration_test/spectest/threads/atomic.wast.patch @@ -0,0 +1,49 @@ +diff --git a/internal/integration_test/spectest/threads/testdata/atomic.wast b/internal/integration_test/spectest/threads/testdata/atomic.wast +index 66ad0ebb..40259a9a 100644 +--- a/internal/integration_test/spectest/threads/testdata/atomic.wast ++++ b/internal/integration_test/spectest/threads/testdata/atomic.wast +@@ -324,7 +324,7 @@ + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0x11111111) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +-(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) ++(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +@@ -332,7 +332,7 @@ + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0x11111111) (i32.const 0xcafecafe)) (i32.const 0x1111)) +-(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) ++(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +@@ -340,7 +340,7 @@ + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0x4242424242424242)) (i64.const 0x11)) +-(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) ++(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +@@ -348,7 +348,7 @@ + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +-(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) ++(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +@@ -356,7 +356,7 @@ + + (invoke "init" (i64.const 0x1111111111111111)) + (assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +-(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) ++(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + + ;; *.atomic.rmw*.cmpxchg (compare true) + diff --git a/internal/integration_test/spectest/threads/spec_test.go b/internal/integration_test/spectest/threads/spec_test.go new file mode 100644 index 0000000000..d6fc6b5d1d --- /dev/null +++ b/internal/integration_test/spectest/threads/spec_test.go @@ -0,0 +1,32 @@ +package spectest + +import ( + "context" + "embed" + "testing" + + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/internal/engine/compiler" + "github.com/tetratelabs/wazero/internal/engine/interpreter" + "github.com/tetratelabs/wazero/internal/integration_test/spectest" + "github.com/tetratelabs/wazero/internal/platform" +) + +//go:embed testdata/*.wasm +//go:embed testdata/*.json +var testcases embed.FS + +const enabledFeatures = api.CoreFeaturesV2 | experimental.CoreFeaturesThreads + +func TestCompiler(t *testing.T) { + t.Skip("compiler not implemented yet") + if !platform.CompilerSupported() { + t.Skip() + } + spectest.Run(t, testcases, context.Background(), nil, compiler.NewEngine, enabledFeatures) +} + +func TestInterpreter(t *testing.T) { + spectest.Run(t, testcases, context.Background(), nil, interpreter.NewEngine, enabledFeatures) +} diff --git a/internal/integration_test/spectest/threads/testdata/atomic.0.wasm b/internal/integration_test/spectest/threads/testdata/atomic.0.wasm new file mode 100644 index 0000000000..f10a2364ab Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.0.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.1.wasm b/internal/integration_test/spectest/threads/testdata/atomic.1.wasm new file mode 100644 index 0000000000..6abaa998fc Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.1.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.10.wasm b/internal/integration_test/spectest/threads/testdata/atomic.10.wasm new file mode 100644 index 0000000000..6372394c16 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.10.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.11.wasm b/internal/integration_test/spectest/threads/testdata/atomic.11.wasm new file mode 100644 index 0000000000..97c6a5d452 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.11.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.12.wasm b/internal/integration_test/spectest/threads/testdata/atomic.12.wasm new file mode 100644 index 0000000000..8d6c7982cf Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.12.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.13.wasm b/internal/integration_test/spectest/threads/testdata/atomic.13.wasm new file mode 100644 index 0000000000..cd71364c95 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.13.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.14.wasm b/internal/integration_test/spectest/threads/testdata/atomic.14.wasm new file mode 100644 index 0000000000..0cd1e79a6e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.14.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.15.wasm b/internal/integration_test/spectest/threads/testdata/atomic.15.wasm new file mode 100644 index 0000000000..72d14d418b Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.15.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.16.wasm b/internal/integration_test/spectest/threads/testdata/atomic.16.wasm new file mode 100644 index 0000000000..10a75acee9 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.16.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.17.wasm b/internal/integration_test/spectest/threads/testdata/atomic.17.wasm new file mode 100644 index 0000000000..1ce5767151 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.17.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.18.wasm b/internal/integration_test/spectest/threads/testdata/atomic.18.wasm new file mode 100644 index 0000000000..dc118fd72f Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.18.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.19.wasm b/internal/integration_test/spectest/threads/testdata/atomic.19.wasm new file mode 100644 index 0000000000..2f960972b7 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.19.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.2.wasm b/internal/integration_test/spectest/threads/testdata/atomic.2.wasm new file mode 100644 index 0000000000..fae839549b Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.2.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.20.wasm b/internal/integration_test/spectest/threads/testdata/atomic.20.wasm new file mode 100644 index 0000000000..cf50378e72 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.20.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.21.wasm b/internal/integration_test/spectest/threads/testdata/atomic.21.wasm new file mode 100644 index 0000000000..20b2224279 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.21.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.22.wasm b/internal/integration_test/spectest/threads/testdata/atomic.22.wasm new file mode 100644 index 0000000000..d392d3b7d4 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.22.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.23.wasm b/internal/integration_test/spectest/threads/testdata/atomic.23.wasm new file mode 100644 index 0000000000..b62233d4be Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.23.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.24.wasm b/internal/integration_test/spectest/threads/testdata/atomic.24.wasm new file mode 100644 index 0000000000..0b65cb9e0e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.24.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.25.wasm b/internal/integration_test/spectest/threads/testdata/atomic.25.wasm new file mode 100644 index 0000000000..c92c6fe0d9 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.25.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.26.wasm b/internal/integration_test/spectest/threads/testdata/atomic.26.wasm new file mode 100644 index 0000000000..2419c530c5 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.26.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.27.wasm b/internal/integration_test/spectest/threads/testdata/atomic.27.wasm new file mode 100644 index 0000000000..0a1ce7227d Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.27.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.28.wasm b/internal/integration_test/spectest/threads/testdata/atomic.28.wasm new file mode 100644 index 0000000000..86cc77e50e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.28.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.29.wasm b/internal/integration_test/spectest/threads/testdata/atomic.29.wasm new file mode 100644 index 0000000000..12f59aca02 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.29.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.3.wasm b/internal/integration_test/spectest/threads/testdata/atomic.3.wasm new file mode 100644 index 0000000000..9b42a7918b Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.3.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.30.wasm b/internal/integration_test/spectest/threads/testdata/atomic.30.wasm new file mode 100644 index 0000000000..c490b347d7 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.30.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.31.wasm b/internal/integration_test/spectest/threads/testdata/atomic.31.wasm new file mode 100644 index 0000000000..ffe5ce4b5d Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.31.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.32.wasm b/internal/integration_test/spectest/threads/testdata/atomic.32.wasm new file mode 100644 index 0000000000..3385de893e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.32.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.33.wasm b/internal/integration_test/spectest/threads/testdata/atomic.33.wasm new file mode 100644 index 0000000000..4931ccd2cb Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.33.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.34.wasm b/internal/integration_test/spectest/threads/testdata/atomic.34.wasm new file mode 100644 index 0000000000..b8926c5b32 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.34.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.35.wasm b/internal/integration_test/spectest/threads/testdata/atomic.35.wasm new file mode 100644 index 0000000000..a074dcad61 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.35.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.36.wasm b/internal/integration_test/spectest/threads/testdata/atomic.36.wasm new file mode 100644 index 0000000000..cf8170def0 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.36.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.37.wasm b/internal/integration_test/spectest/threads/testdata/atomic.37.wasm new file mode 100644 index 0000000000..2c65689fb6 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.37.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.38.wasm b/internal/integration_test/spectest/threads/testdata/atomic.38.wasm new file mode 100644 index 0000000000..caf73c4db3 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.38.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.39.wasm b/internal/integration_test/spectest/threads/testdata/atomic.39.wasm new file mode 100644 index 0000000000..30ec3c7d14 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.39.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.4.wasm b/internal/integration_test/spectest/threads/testdata/atomic.4.wasm new file mode 100644 index 0000000000..b0ab855012 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.4.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.40.wasm b/internal/integration_test/spectest/threads/testdata/atomic.40.wasm new file mode 100644 index 0000000000..415420c3e5 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.40.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.41.wasm b/internal/integration_test/spectest/threads/testdata/atomic.41.wasm new file mode 100644 index 0000000000..5ca0026342 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.41.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.42.wasm b/internal/integration_test/spectest/threads/testdata/atomic.42.wasm new file mode 100644 index 0000000000..9ee652ac29 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.42.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.43.wasm b/internal/integration_test/spectest/threads/testdata/atomic.43.wasm new file mode 100644 index 0000000000..11c714c0ea Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.43.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.44.wasm b/internal/integration_test/spectest/threads/testdata/atomic.44.wasm new file mode 100644 index 0000000000..a02870a75a Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.44.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.45.wasm b/internal/integration_test/spectest/threads/testdata/atomic.45.wasm new file mode 100644 index 0000000000..84c1ae33df Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.45.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.46.wasm b/internal/integration_test/spectest/threads/testdata/atomic.46.wasm new file mode 100644 index 0000000000..70dd2ceeaa Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.46.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.47.wasm b/internal/integration_test/spectest/threads/testdata/atomic.47.wasm new file mode 100644 index 0000000000..7a0bb72be0 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.47.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.48.wasm b/internal/integration_test/spectest/threads/testdata/atomic.48.wasm new file mode 100644 index 0000000000..3ba78ffe36 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.48.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.49.wasm b/internal/integration_test/spectest/threads/testdata/atomic.49.wasm new file mode 100644 index 0000000000..34627f5afd Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.49.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.5.wasm b/internal/integration_test/spectest/threads/testdata/atomic.5.wasm new file mode 100644 index 0000000000..92fc37f7b3 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.5.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.50.wasm b/internal/integration_test/spectest/threads/testdata/atomic.50.wasm new file mode 100644 index 0000000000..9cb7d1d911 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.50.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.51.wasm b/internal/integration_test/spectest/threads/testdata/atomic.51.wasm new file mode 100644 index 0000000000..16307598f0 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.51.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.52.wasm b/internal/integration_test/spectest/threads/testdata/atomic.52.wasm new file mode 100644 index 0000000000..8f89610dec Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.52.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.6.wasm b/internal/integration_test/spectest/threads/testdata/atomic.6.wasm new file mode 100644 index 0000000000..040840d250 Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.6.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.7.wasm b/internal/integration_test/spectest/threads/testdata/atomic.7.wasm new file mode 100644 index 0000000000..0e047d8f9e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.7.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.8.wasm b/internal/integration_test/spectest/threads/testdata/atomic.8.wasm new file mode 100644 index 0000000000..a4a26d28aa Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.8.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.9.wasm b/internal/integration_test/spectest/threads/testdata/atomic.9.wasm new file mode 100644 index 0000000000..0733777c3e Binary files /dev/null and b/internal/integration_test/spectest/threads/testdata/atomic.9.wasm differ diff --git a/internal/integration_test/spectest/threads/testdata/atomic.json b/internal/integration_test/spectest/threads/testdata/atomic.json new file mode 100644 index 0000000000..3da85baa05 --- /dev/null +++ b/internal/integration_test/spectest/threads/testdata/atomic.json @@ -0,0 +1,329 @@ +{"source_filename": "./atomic.wast", + "commands": [ + {"type": "module", "line": 3, "filename": "atomic.0.wasm"}, + {"type": "action", "line": 84, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "506097522914230528"}]}, "expected": []}, + {"type": "assert_return", "line": 86, "action": {"type": "invoke", "field": "i32.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "50462976"}]}, + {"type": "assert_return", "line": 87, "action": {"type": "invoke", "field": "i32.atomic.load", "args": [{"type": "i32", "value": "4"}]}, "expected": [{"type": "i32", "value": "117835012"}]}, + {"type": "assert_return", "line": 89, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "506097522914230528"}]}, + {"type": "assert_return", "line": 91, "action": {"type": "invoke", "field": "i32.atomic.load8_u", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_return", "line": 92, "action": {"type": "invoke", "field": "i32.atomic.load8_u", "args": [{"type": "i32", "value": "5"}]}, "expected": [{"type": "i32", "value": "5"}]}, + {"type": "assert_return", "line": 94, "action": {"type": "invoke", "field": "i32.atomic.load16_u", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "256"}]}, + {"type": "assert_return", "line": 95, "action": {"type": "invoke", "field": "i32.atomic.load16_u", "args": [{"type": "i32", "value": "6"}]}, "expected": [{"type": "i32", "value": "1798"}]}, + {"type": "assert_return", "line": 97, "action": {"type": "invoke", "field": "i64.atomic.load8_u", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "0"}]}, + {"type": "assert_return", "line": 98, "action": {"type": "invoke", "field": "i64.atomic.load8_u", "args": [{"type": "i32", "value": "5"}]}, "expected": [{"type": "i64", "value": "5"}]}, + {"type": "assert_return", "line": 100, "action": {"type": "invoke", "field": "i64.atomic.load16_u", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "256"}]}, + {"type": "assert_return", "line": 101, "action": {"type": "invoke", "field": "i64.atomic.load16_u", "args": [{"type": "i32", "value": "6"}]}, "expected": [{"type": "i64", "value": "1798"}]}, + {"type": "assert_return", "line": 103, "action": {"type": "invoke", "field": "i64.atomic.load32_u", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "50462976"}]}, + {"type": "assert_return", "line": 104, "action": {"type": "invoke", "field": "i64.atomic.load32_u", "args": [{"type": "i32", "value": "4"}]}, "expected": [{"type": "i64", "value": "117835012"}]}, + {"type": "action", "line": 108, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "0"}]}, "expected": []}, + {"type": "assert_return", "line": 110, "action": {"type": "invoke", "field": "i32.atomic.store", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "4293844428"}]}, "expected": []}, + {"type": "assert_return", "line": 111, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "4293844428"}]}, + {"type": "assert_return", "line": 113, "action": {"type": "invoke", "field": "i64.atomic.store", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "81985529216486895"}]}, "expected": []}, + {"type": "assert_return", "line": 114, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "81985529216486895"}]}, + {"type": "assert_return", "line": 116, "action": {"type": "invoke", "field": "i32.atomic.store8", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "66"}]}, "expected": []}, + {"type": "assert_return", "line": 117, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "81985529216451311"}]}, + {"type": "assert_return", "line": 119, "action": {"type": "invoke", "field": "i32.atomic.store16", "args": [{"type": "i32", "value": "4"}, {"type": "i32", "value": "34884"}]}, "expected": []}, + {"type": "assert_return", "line": 120, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "82059046171656943"}]}, + {"type": "assert_return", "line": 122, "action": {"type": "invoke", "field": "i64.atomic.store8", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "153"}]}, "expected": []}, + {"type": "assert_return", "line": 123, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "82059046171679215"}]}, + {"type": "assert_return", "line": 125, "action": {"type": "invoke", "field": "i64.atomic.store16", "args": [{"type": "i32", "value": "4"}, {"type": "i64", "value": "51966"}]}, "expected": []}, + {"type": "assert_return", "line": 126, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "82132412803029487"}]}, + {"type": "assert_return", "line": 128, "action": {"type": "invoke", "field": "i64.atomic.store32", "args": [{"type": "i32", "value": "4"}, {"type": "i64", "value": "3735928559"}]}, "expected": []}, + {"type": "assert_return", "line": 129, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "16045690983407131119"}]}, + {"type": "action", "line": 133, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 134, "action": {"type": "invoke", "field": "i32.atomic.rmw.add", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 135, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938552723337"}]}, + {"type": "action", "line": 137, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 138, "action": {"type": "invoke", "field": "i64.atomic.rmw.add", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 139, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1302123111102223123"}]}, + {"type": "action", "line": 141, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 142, "action": {"type": "invoke", "field": "i32.atomic.rmw8.add_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 143, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303646"}]}, + {"type": "action", "line": 145, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 146, "action": {"type": "invoke", "field": "i32.atomic.rmw16.add_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 147, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247355407"}]}, + {"type": "action", "line": 149, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 150, "action": {"type": "invoke", "field": "i64.atomic.rmw8.add_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 151, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303507"}]}, + {"type": "action", "line": 153, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 154, "action": {"type": "invoke", "field": "i64.atomic.rmw16.add_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 155, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247352320"}]}, + {"type": "action", "line": 157, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 158, "action": {"type": "invoke", "field": "i64.atomic.rmw32.add_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 159, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941648599030"}]}, + {"type": "action", "line": 163, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 164, "action": {"type": "invoke", "field": "i32.atomic.rmw.sub", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 165, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782942236850841"}]}, + {"type": "action", "line": 167, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 168, "action": {"type": "invoke", "field": "i64.atomic.rmw.sub", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 169, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1157442765392383759"}]}, + {"type": "action", "line": 171, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 172, "action": {"type": "invoke", "field": "i32.atomic.rmw8.sub_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 173, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303492"}]}, + {"type": "action", "line": 175, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 176, "action": {"type": "invoke", "field": "i32.atomic.rmw16.sub_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 177, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247317011"}]}, + {"type": "action", "line": 179, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 180, "action": {"type": "invoke", "field": "i64.atomic.rmw8.sub_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 181, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303631"}]}, + {"type": "action", "line": 183, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 184, "action": {"type": "invoke", "field": "i64.atomic.rmw16.sub_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 185, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247320098"}]}, + {"type": "action", "line": 187, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 188, "action": {"type": "invoke", "field": "i64.atomic.rmw32.sub_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 189, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782939140975148"}]}, + {"type": "action", "line": 193, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 194, "action": {"type": "invoke", "field": "i32.atomic.rmw.and", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 195, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938230460432"}]}, + {"type": "action", "line": 197, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 198, "action": {"type": "invoke", "field": "i64.atomic.rmw.and", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 199, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "72340172821233664"}]}, + {"type": "action", "line": 201, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 202, "action": {"type": "invoke", "field": "i32.atomic.rmw8.and_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 203, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303425"}]}, + {"type": "action", "line": 205, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 206, "action": {"type": "invoke", "field": "i32.atomic.rmw16.and_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 207, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247299088"}]}, + {"type": "action", "line": 209, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 210, "action": {"type": "invoke", "field": "i64.atomic.rmw8.and_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 211, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303424"}]}, + {"type": "action", "line": 213, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 214, "action": {"type": "invoke", "field": "i64.atomic.rmw16.and_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 215, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303169"}]}, + {"type": "action", "line": 217, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 218, "action": {"type": "invoke", "field": "i64.atomic.rmw32.and_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 219, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782937962086401"}]}, + {"type": "action", "line": 223, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 224, "action": {"type": "invoke", "field": "i32.atomic.rmw.or", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 225, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938283235193"}]}, + {"type": "action", "line": 227, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 228, "action": {"type": "invoke", "field": "i64.atomic.rmw.or", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 229, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938280989459"}]}, + {"type": "action", "line": 231, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 232, "action": {"type": "invoke", "field": "i32.atomic.rmw8.or_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 233, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303645"}]}, + {"type": "action", "line": 235, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 236, "action": {"type": "invoke", "field": "i32.atomic.rmw16.or_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 237, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247355391"}]}, + {"type": "action", "line": 239, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 240, "action": {"type": "invoke", "field": "i64.atomic.rmw8.or_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 241, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303507"}]}, + {"type": "action", "line": 243, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 244, "action": {"type": "invoke", "field": "i64.atomic.rmw16.or_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 245, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247348223"}]}, + {"type": "action", "line": 247, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 248, "action": {"type": "invoke", "field": "i64.atomic.rmw32.or_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 249, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941647484917"}]}, + {"type": "action", "line": 253, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 254, "action": {"type": "invoke", "field": "i32.atomic.rmw.xor", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 255, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938013747049"}]}, + {"type": "action", "line": 257, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 258, "action": {"type": "invoke", "field": "i64.atomic.rmw.xor", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 259, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1157442765459755795"}]}, + {"type": "action", "line": 261, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 262, "action": {"type": "invoke", "field": "i32.atomic.rmw8.xor_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 263, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303644"}]}, + {"type": "action", "line": 265, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 266, "action": {"type": "invoke", "field": "i32.atomic.rmw16.xor_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 267, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247355375"}]}, + {"type": "action", "line": 269, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 270, "action": {"type": "invoke", "field": "i64.atomic.rmw8.xor_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 271, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303507"}]}, + {"type": "action", "line": 273, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 274, "action": {"type": "invoke", "field": "i64.atomic.rmw16.xor_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 275, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247344126"}]}, + {"type": "action", "line": 277, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 278, "action": {"type": "invoke", "field": "i64.atomic.rmw32.xor_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 279, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941646370804"}]}, + {"type": "action", "line": 283, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 284, "action": {"type": "invoke", "field": "i32.atomic.rmw.xchg", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 285, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938266392184"}]}, + {"type": "action", "line": 287, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 288, "action": {"type": "invoke", "field": "i64.atomic.rmw.xchg", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 289, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "72340172854919682"}]}, + {"type": "action", "line": 291, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 292, "action": {"type": "invoke", "field": "i32.atomic.rmw8.xchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 293, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303629"}]}, + {"type": "action", "line": 295, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 296, "action": {"type": "invoke", "field": "i32.atomic.rmw16.xchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 297, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247351038"}]}, + {"type": "action", "line": 299, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 300, "action": {"type": "invoke", "field": "i64.atomic.rmw8.xchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 301, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303490"}]}, + {"type": "action", "line": 303, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 304, "action": {"type": "invoke", "field": "i64.atomic.rmw16.xchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 305, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247347951"}]}, + {"type": "action", "line": 307, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 308, "action": {"type": "invoke", "field": "i64.atomic.rmw32.xchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 309, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941362267877"}]}, + {"type": "action", "line": 313, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 314, "action": {"type": "invoke", "field": "i32.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 315, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 317, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 318, "action": {"type": "invoke", "field": "i64.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 319, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 321, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 322, "action": {"type": "invoke", "field": "i32.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 323, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 325, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 326, "action": {"type": "invoke", "field": "i32.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "286331153"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 327, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303629"}]}, + {"type": "action", "line": 329, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 330, "action": {"type": "invoke", "field": "i32.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 331, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 333, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 334, "action": {"type": "invoke", "field": "i32.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "286331153"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 335, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247351038"}]}, + {"type": "action", "line": 337, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 338, "action": {"type": "invoke", "field": "i64.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 339, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 341, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 342, "action": {"type": "invoke", "field": "i64.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "1229782938247303441"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 343, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303490"}]}, + {"type": "action", "line": 345, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 346, "action": {"type": "invoke", "field": "i64.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 347, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 349, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 350, "action": {"type": "invoke", "field": "i64.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "1229782938247303441"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 351, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247347951"}]}, + {"type": "action", "line": 353, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 354, "action": {"type": "invoke", "field": "i64.atomic.rmw32.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 355, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "action", "line": 357, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 358, "action": {"type": "invoke", "field": "i64.atomic.rmw32.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "1229782938247303441"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 359, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941362267877"}]}, + {"type": "action", "line": 363, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 364, "action": {"type": "invoke", "field": "i32.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "286331153"}, {"type": "i32", "value": "305419896"}]}, "expected": [{"type": "i32", "value": "286331153"}]}, + {"type": "assert_return", "line": 365, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938266392184"}]}, + {"type": "action", "line": 367, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 368, "action": {"type": "invoke", "field": "i64.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "1229782938247303441"}, {"type": "i64", "value": "72340172854919682"}]}, "expected": [{"type": "i64", "value": "1229782938247303441"}]}, + {"type": "assert_return", "line": 369, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "72340172854919682"}]}, + {"type": "action", "line": 371, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 372, "action": {"type": "invoke", "field": "i32.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "17"}, {"type": "i32", "value": "3452816845"}]}, "expected": [{"type": "i32", "value": "17"}]}, + {"type": "assert_return", "line": 373, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303629"}]}, + {"type": "action", "line": 375, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 376, "action": {"type": "invoke", "field": "i32.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "4369"}, {"type": "i32", "value": "3405695742"}]}, "expected": [{"type": "i32", "value": "4369"}]}, + {"type": "assert_return", "line": 377, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247351038"}]}, + {"type": "action", "line": 379, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 380, "action": {"type": "invoke", "field": "i64.atomic.rmw8.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "17"}, {"type": "i64", "value": "4774451407313060418"}]}, "expected": [{"type": "i64", "value": "17"}]}, + {"type": "assert_return", "line": 381, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247303490"}]}, + {"type": "action", "line": 383, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 384, "action": {"type": "invoke", "field": "i64.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "4369"}, {"type": "i64", "value": "13758425323549998831"}]}, "expected": [{"type": "i64", "value": "4369"}]}, + {"type": "assert_return", "line": 385, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782938247347951"}]}, + {"type": "action", "line": 387, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "1229782938247303441"}]}, "expected": []}, + {"type": "assert_return", "line": 388, "action": {"type": "invoke", "field": "i64.atomic.rmw32.cmpxchg_u", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "286331153"}, {"type": "i64", "value": "14608453322185352933"}]}, "expected": [{"type": "i64", "value": "286331153"}]}, + {"type": "assert_return", "line": 389, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "0"}]}, "expected": [{"type": "i64", "value": "1229782941362267877"}]}, + {"type": "assert_trap", "line": 394, "action": {"type": "invoke", "field": "i32.atomic.load", "args": [{"type": "i32", "value": "1"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 395, "action": {"type": "invoke", "field": "i64.atomic.load", "args": [{"type": "i32", "value": "1"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 396, "action": {"type": "invoke", "field": "i32.atomic.load16_u", "args": [{"type": "i32", "value": "1"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 397, "action": {"type": "invoke", "field": "i64.atomic.load16_u", "args": [{"type": "i32", "value": "1"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 398, "action": {"type": "invoke", "field": "i64.atomic.load32_u", "args": [{"type": "i32", "value": "1"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 399, "action": {"type": "invoke", "field": "i32.atomic.store", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": []}, + {"type": "assert_trap", "line": 400, "action": {"type": "invoke", "field": "i64.atomic.store", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": []}, + {"type": "assert_trap", "line": 401, "action": {"type": "invoke", "field": "i32.atomic.store16", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": []}, + {"type": "assert_trap", "line": 402, "action": {"type": "invoke", "field": "i64.atomic.store16", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": []}, + {"type": "assert_trap", "line": 403, "action": {"type": "invoke", "field": "i64.atomic.store32", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": []}, + {"type": "assert_trap", "line": 404, "action": {"type": "invoke", "field": "i32.atomic.rmw.add", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 405, "action": {"type": "invoke", "field": "i64.atomic.rmw.add", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 406, "action": {"type": "invoke", "field": "i32.atomic.rmw16.add_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 407, "action": {"type": "invoke", "field": "i64.atomic.rmw16.add_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 408, "action": {"type": "invoke", "field": "i64.atomic.rmw32.add_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 409, "action": {"type": "invoke", "field": "i32.atomic.rmw.sub", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 410, "action": {"type": "invoke", "field": "i64.atomic.rmw.sub", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 411, "action": {"type": "invoke", "field": "i32.atomic.rmw16.sub_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 412, "action": {"type": "invoke", "field": "i64.atomic.rmw16.sub_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 413, "action": {"type": "invoke", "field": "i64.atomic.rmw32.sub_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 414, "action": {"type": "invoke", "field": "i32.atomic.rmw.and", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 415, "action": {"type": "invoke", "field": "i64.atomic.rmw.and", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 416, "action": {"type": "invoke", "field": "i32.atomic.rmw16.and_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 417, "action": {"type": "invoke", "field": "i64.atomic.rmw16.and_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 418, "action": {"type": "invoke", "field": "i64.atomic.rmw32.and_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 419, "action": {"type": "invoke", "field": "i32.atomic.rmw.or", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 420, "action": {"type": "invoke", "field": "i64.atomic.rmw.or", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 421, "action": {"type": "invoke", "field": "i32.atomic.rmw16.or_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 422, "action": {"type": "invoke", "field": "i64.atomic.rmw16.or_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 423, "action": {"type": "invoke", "field": "i64.atomic.rmw32.or_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 424, "action": {"type": "invoke", "field": "i32.atomic.rmw.xor", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 425, "action": {"type": "invoke", "field": "i64.atomic.rmw.xor", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 426, "action": {"type": "invoke", "field": "i32.atomic.rmw16.xor_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 427, "action": {"type": "invoke", "field": "i64.atomic.rmw16.xor_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 428, "action": {"type": "invoke", "field": "i64.atomic.rmw32.xor_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 429, "action": {"type": "invoke", "field": "i32.atomic.rmw.xchg", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 430, "action": {"type": "invoke", "field": "i64.atomic.rmw.xchg", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 431, "action": {"type": "invoke", "field": "i32.atomic.rmw16.xchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 432, "action": {"type": "invoke", "field": "i64.atomic.rmw16.xchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 433, "action": {"type": "invoke", "field": "i64.atomic.rmw32.xchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 434, "action": {"type": "invoke", "field": "i32.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 435, "action": {"type": "invoke", "field": "i64.atomic.rmw.cmpxchg", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 436, "action": {"type": "invoke", "field": "i32.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 437, "action": {"type": "invoke", "field": "i64.atomic.rmw16.cmpxchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "assert_trap", "line": 438, "action": {"type": "invoke", "field": "i64.atomic.rmw32.cmpxchg_u", "args": [{"type": "i32", "value": "1"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i64"}]}, + {"type": "module", "line": 441, "filename": "atomic.1.wasm"}, + {"type": "action", "line": 454, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "281474976710655"}]}, "expected": []}, + {"type": "assert_return", "line": 457, "action": {"type": "invoke", "field": "memory.atomic.wait32", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "assert_return", "line": 458, "action": {"type": "invoke", "field": "memory.atomic.wait64", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "assert_return", "line": 461, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_trap", "line": 464, "action": {"type": "invoke", "field": "memory.atomic.wait32", "args": [{"type": "i32", "value": "65536"}, {"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "out of bounds memory access", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 465, "action": {"type": "invoke", "field": "memory.atomic.wait64", "args": [{"type": "i32", "value": "65536"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "out of bounds memory access", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 468, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "65536"}, {"type": "i32", "value": "0"}]}, "text": "out of bounds memory access", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 471, "action": {"type": "invoke", "field": "memory.atomic.wait32", "args": [{"type": "i32", "value": "65531"}, {"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 472, "action": {"type": "invoke", "field": "memory.atomic.wait64", "args": [{"type": "i32", "value": "65524"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 474, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "65531"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "module", "line": 477, "filename": "atomic.2.wasm"}, + {"type": "action", "line": 490, "action": {"type": "invoke", "field": "init", "args": [{"type": "i64", "value": "281474976710655"}]}, "expected": []}, + {"type": "assert_trap", "line": 492, "action": {"type": "invoke", "field": "memory.atomic.wait32", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "expected shared memory", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 493, "action": {"type": "invoke", "field": "memory.atomic.wait64", "args": [{"type": "i32", "value": "0"}, {"type": "i64", "value": "0"}, {"type": "i64", "value": "0"}]}, "text": "expected shared memory", "expected": [{"type": "i32"}]}, + {"type": "assert_return", "line": 496, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "0"}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_trap", "line": 499, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "65536"}, {"type": "i32", "value": "0"}]}, "text": "out of bounds memory access", "expected": [{"type": "i32"}]}, + {"type": "assert_trap", "line": 500, "action": {"type": "invoke", "field": "memory.atomic.notify", "args": [{"type": "i32", "value": "65531"}, {"type": "i32", "value": "0"}]}, "text": "unaligned atomic", "expected": [{"type": "i32"}]}, + {"type": "module", "line": 504, "filename": "atomic.3.wasm"}, + {"type": "module", "line": 557, "filename": "atomic.4.wasm"}, + {"type": "assert_return", "line": 561, "action": {"type": "invoke", "field": "fence", "args": []}, "expected": []}, + {"type": "assert_invalid", "line": 564, "filename": "atomic.5.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 565, "filename": "atomic.6.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 566, "filename": "atomic.7.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 567, "filename": "atomic.8.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 568, "filename": "atomic.9.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 569, "filename": "atomic.10.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 570, "filename": "atomic.11.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 571, "filename": "atomic.12.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 572, "filename": "atomic.13.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 573, "filename": "atomic.14.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 574, "filename": "atomic.15.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 575, "filename": "atomic.16.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 576, "filename": "atomic.17.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 577, "filename": "atomic.18.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 578, "filename": "atomic.19.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 579, "filename": "atomic.20.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 580, "filename": "atomic.21.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 581, "filename": "atomic.22.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 582, "filename": "atomic.23.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 583, "filename": "atomic.24.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 584, "filename": "atomic.25.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 585, "filename": "atomic.26.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 586, "filename": "atomic.27.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 587, "filename": "atomic.28.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 588, "filename": "atomic.29.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 589, "filename": "atomic.30.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 590, "filename": "atomic.31.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 591, "filename": "atomic.32.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 592, "filename": "atomic.33.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 593, "filename": "atomic.34.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 594, "filename": "atomic.35.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 595, "filename": "atomic.36.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 596, "filename": "atomic.37.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 597, "filename": "atomic.38.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 598, "filename": "atomic.39.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 599, "filename": "atomic.40.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 600, "filename": "atomic.41.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 601, "filename": "atomic.42.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 602, "filename": "atomic.43.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 603, "filename": "atomic.44.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 604, "filename": "atomic.45.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 605, "filename": "atomic.46.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 606, "filename": "atomic.47.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 607, "filename": "atomic.48.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 608, "filename": "atomic.49.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 609, "filename": "atomic.50.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 610, "filename": "atomic.51.wasm", "text": "unknown memory", "module_type": "binary"}, + {"type": "assert_invalid", "line": 611, "filename": "atomic.52.wasm", "text": "unknown memory", "module_type": "binary"}]} diff --git a/internal/integration_test/spectest/threads/testdata/atomic.wast b/internal/integration_test/spectest/threads/testdata/atomic.wast new file mode 100644 index 0000000000..40259a9ae7 --- /dev/null +++ b/internal/integration_test/spectest/threads/testdata/atomic.wast @@ -0,0 +1,611 @@ +;; atomic operations + +(module + (memory 1 1 shared) + + (func (export "init") (param $value i64) (i64.store (i32.const 0) (local.get $value))) + + (func (export "i32.atomic.load") (param $addr i32) (result i32) (i32.atomic.load (local.get $addr))) + (func (export "i64.atomic.load") (param $addr i32) (result i64) (i64.atomic.load (local.get $addr))) + (func (export "i32.atomic.load8_u") (param $addr i32) (result i32) (i32.atomic.load8_u (local.get $addr))) + (func (export "i32.atomic.load16_u") (param $addr i32) (result i32) (i32.atomic.load16_u (local.get $addr))) + (func (export "i64.atomic.load8_u") (param $addr i32) (result i64) (i64.atomic.load8_u (local.get $addr))) + (func (export "i64.atomic.load16_u") (param $addr i32) (result i64) (i64.atomic.load16_u (local.get $addr))) + (func (export "i64.atomic.load32_u") (param $addr i32) (result i64) (i64.atomic.load32_u (local.get $addr))) + + (func (export "i32.atomic.store") (param $addr i32) (param $value i32) (i32.atomic.store (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store") (param $addr i32) (param $value i64) (i64.atomic.store (local.get $addr) (local.get $value))) + (func (export "i32.atomic.store8") (param $addr i32) (param $value i32) (i32.atomic.store8 (local.get $addr) (local.get $value))) + (func (export "i32.atomic.store16") (param $addr i32) (param $value i32) (i32.atomic.store16 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store8") (param $addr i32) (param $value i64) (i64.atomic.store8 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store16") (param $addr i32) (param $value i64) (i64.atomic.store16 (local.get $addr) (local.get $value))) + (func (export "i64.atomic.store32") (param $addr i32) (param $value i64) (i64.atomic.store32 (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.add") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.add (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.add") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.add (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.add_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.add_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.add_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.add_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.add_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.add_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.sub") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.sub (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.sub") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.sub (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.sub_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.sub_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.sub_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.sub_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.sub_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.sub_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.and") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.and (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.and") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.and (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.and_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.and_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.and_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.and_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.and_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.and_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.or") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.or (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.or") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.or (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.or_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.or_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.or_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.or_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.or_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.or_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.xor") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.xor (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.xor") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.xor (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.xor_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.xor_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.xor_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.xor_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.xor_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.xor_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.xchg") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw.xchg (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw.xchg") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw.xchg (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw8.xchg_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw8.xchg_u (local.get $addr) (local.get $value))) + (func (export "i32.atomic.rmw16.xchg_u") (param $addr i32) (param $value i32) (result i32) (i32.atomic.rmw16.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw8.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw8.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw16.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw16.xchg_u (local.get $addr) (local.get $value))) + (func (export "i64.atomic.rmw32.xchg_u") (param $addr i32) (param $value i64) (result i64) (i64.atomic.rmw32.xchg_u (local.get $addr) (local.get $value))) + + (func (export "i32.atomic.rmw.cmpxchg") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw.cmpxchg (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw.cmpxchg") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw.cmpxchg (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i32.atomic.rmw8.cmpxchg_u") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw8.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i32.atomic.rmw16.cmpxchg_u") (param $addr i32) (param $expected i32) (param $value i32) (result i32) (i32.atomic.rmw16.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw8.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw8.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw16.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw16.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + (func (export "i64.atomic.rmw32.cmpxchg_u") (param $addr i32) (param $expected i64) (param $value i64) (result i64) (i64.atomic.rmw32.cmpxchg_u (local.get $addr) (local.get $expected) (local.get $value))) + +) + +;; *.atomic.load* + +(invoke "init" (i64.const 0x0706050403020100)) + +(assert_return (invoke "i32.atomic.load" (i32.const 0)) (i32.const 0x03020100)) +(assert_return (invoke "i32.atomic.load" (i32.const 4)) (i32.const 0x07060504)) + +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0706050403020100)) + +(assert_return (invoke "i32.atomic.load8_u" (i32.const 0)) (i32.const 0x00)) +(assert_return (invoke "i32.atomic.load8_u" (i32.const 5)) (i32.const 0x05)) + +(assert_return (invoke "i32.atomic.load16_u" (i32.const 0)) (i32.const 0x0100)) +(assert_return (invoke "i32.atomic.load16_u" (i32.const 6)) (i32.const 0x0706)) + +(assert_return (invoke "i64.atomic.load8_u" (i32.const 0)) (i64.const 0x00)) +(assert_return (invoke "i64.atomic.load8_u" (i32.const 5)) (i64.const 0x05)) + +(assert_return (invoke "i64.atomic.load16_u" (i32.const 0)) (i64.const 0x0100)) +(assert_return (invoke "i64.atomic.load16_u" (i32.const 6)) (i64.const 0x0706)) + +(assert_return (invoke "i64.atomic.load32_u" (i32.const 0)) (i64.const 0x03020100)) +(assert_return (invoke "i64.atomic.load32_u" (i32.const 4)) (i64.const 0x07060504)) + +;; *.atomic.store* + +(invoke "init" (i64.const 0x0000000000000000)) + +(assert_return (invoke "i32.atomic.store" (i32.const 0) (i32.const 0xffeeddcc))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x00000000ffeeddcc)) + +(assert_return (invoke "i64.atomic.store" (i32.const 0) (i64.const 0x0123456789abcdef))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123456789abcdef)) + +(assert_return (invoke "i32.atomic.store8" (i32.const 1) (i32.const 0x42))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123456789ab42ef)) + +(assert_return (invoke "i32.atomic.store16" (i32.const 4) (i32.const 0x8844))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123884489ab42ef)) + +(assert_return (invoke "i64.atomic.store8" (i32.const 1) (i64.const 0x99))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123884489ab99ef)) + +(assert_return (invoke "i64.atomic.store16" (i32.const 4) (i64.const 0xcafe))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0123cafe89ab99ef)) + +(assert_return (invoke "i64.atomic.store32" (i32.const 4) (i64.const 0xdeadbeef))) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0xdeadbeef89ab99ef)) + +;; *.atomic.rmw*.add + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.add" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111123456789)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.add" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1212121213131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.add_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111de)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.add_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dc0f)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.add_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.add_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111d000)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.add_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbccb7f6)) + +;; *.atomic.rmw*.sub + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.sub" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111fedcba99)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.sub" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x101010100f0f0f0f)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.sub_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111144)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.sub_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111114613)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.sub_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cf)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.sub_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111115222)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.sub_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111146556a2c)) + +;; *.atomic.rmw*.and + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.and" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111110101010)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.and" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010100000000)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.and_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111101)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.and_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111110010)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.and_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111100)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.and_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111001)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.and_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111100110001)) + +;; *.atomic.rmw*.or + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.or" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111113355779)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.or" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111113131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.or_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111dd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.or_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dbff)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.or_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.or_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111bfff)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.or_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbbbb7f5)) + +;; *.atomic.rmw*.xor + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.xor" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111103254769)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.xor" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1010101013131313)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.xor_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111dc)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.xor_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111dbef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.xor_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111153)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.xor_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111affe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.xor_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111dbaab7f4)) + +;; *.atomic.rmw*.xchg + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.xchg" (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111112345678)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.xchg" (i32.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010102020202)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.xchg_u" (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.xchg_u" (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.xchg_u" (i32.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.xchg_u" (i32.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.xchg_u" (i32.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + +;; *.atomic.rmw*.cmpxchg (compare false) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.cmpxchg" (i32.const 0) (i32.const 0) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.cmpxchg" (i32.const 0) (i64.const 0) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0x11111111) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0x11111111) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111111)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + +;; *.atomic.rmw*.cmpxchg (compare true) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw.cmpxchg" (i32.const 0) (i32.const 0x11111111) (i32.const 0x12345678)) (i32.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111112345678)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw.cmpxchg" (i32.const 0) (i64.const 0x1111111111111111) (i64.const 0x0101010102020202)) (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x0101010102020202)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw8.cmpxchg_u" (i32.const 0) (i32.const 0x11) (i32.const 0xcdcdcdcd)) (i32.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111111111cd)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 0) (i32.const 0x1111) (i32.const 0xcafecafe)) (i32.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111cafe)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw8.cmpxchg_u" (i32.const 0) (i64.const 0x11) (i64.const 0x4242424242424242)) (i64.const 0x11)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x1111111111111142)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 0) (i64.const 0x1111) (i64.const 0xbeefbeefbeefbeef)) (i64.const 0x1111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x111111111111beef)) + +(invoke "init" (i64.const 0x1111111111111111)) +(assert_return (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 0) (i64.const 0x11111111) (i64.const 0xcabba6e5cabba6e5)) (i64.const 0x11111111)) +(assert_return (invoke "i64.atomic.load" (i32.const 0)) (i64.const 0x11111111cabba6e5)) + + +;; unaligned accesses + +(assert_trap (invoke "i32.atomic.load" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.load16_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load16_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.load32_u" (i32.const 1)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.store" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.store16" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store16" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.store32" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.add" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.add" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.add_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.add_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.add_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.sub" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.sub" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.sub_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.sub_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.sub_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.and" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.and" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.and_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.and_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.and_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.or" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.or" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.or_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.or_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.or_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.xor" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.xor" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.xor_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.xor_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.xor_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.xchg" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.xchg" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.xchg_u" (i32.const 1) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.xchg_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.xchg_u" (i32.const 1) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw.cmpxchg" (i32.const 1) (i32.const 0) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw.cmpxchg" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i32.atomic.rmw16.cmpxchg_u" (i32.const 1) (i32.const 0) (i32.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw16.cmpxchg_u" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "i64.atomic.rmw32.cmpxchg_u" (i32.const 1) (i64.const 0) (i64.const 0)) "unaligned atomic") + +;; wait/notify +(module + (memory 1 1 shared) + + (func (export "init") (param $value i64) (i64.store (i32.const 0) (local.get $value))) + + (func (export "memory.atomic.notify") (param $addr i32) (param $count i32) (result i32) + (memory.atomic.notify (local.get 0) (local.get 1))) + (func (export "memory.atomic.wait32") (param $addr i32) (param $expected i32) (param $timeout i64) (result i32) + (memory.atomic.wait32 (local.get 0) (local.get 1) (local.get 2))) + (func (export "memory.atomic.wait64") (param $addr i32) (param $expected i64) (param $timeout i64) (result i32) + (memory.atomic.wait64 (local.get 0) (local.get 1) (local.get 2))) +) + +(invoke "init" (i64.const 0xffffffffffff)) + +;; wait returns immediately if values do not match +(assert_return (invoke "memory.atomic.wait32" (i32.const 0) (i32.const 0) (i64.const 0)) (i32.const 1)) +(assert_return (invoke "memory.atomic.wait64" (i32.const 0) (i64.const 0) (i64.const 0)) (i32.const 1)) + +;; notify always returns +(assert_return (invoke "memory.atomic.notify" (i32.const 0) (i32.const 0)) (i32.const 0)) + +;; OOB wait and notify always trap +(assert_trap (invoke "memory.atomic.wait32" (i32.const 65536) (i32.const 0) (i64.const 0)) "out of bounds memory access") +(assert_trap (invoke "memory.atomic.wait64" (i32.const 65536) (i64.const 0) (i64.const 0)) "out of bounds memory access") + +;; in particular, notify always traps even if waking 0 threads +(assert_trap (invoke "memory.atomic.notify" (i32.const 65536) (i32.const 0)) "out of bounds memory access") + +;; similarly, unaligned wait and notify always trap +(assert_trap (invoke "memory.atomic.wait32" (i32.const 65531) (i32.const 0) (i64.const 0)) "unaligned atomic") +(assert_trap (invoke "memory.atomic.wait64" (i32.const 65524) (i64.const 0) (i64.const 0)) "unaligned atomic") + +(assert_trap (invoke "memory.atomic.notify" (i32.const 65531) (i32.const 0)) "unaligned atomic") + +;; atomic.wait traps on unshared memory even if it wouldn't block +(module + (memory 1 1) + + (func (export "init") (param $value i64) (i64.store (i32.const 0) (local.get $value))) + + (func (export "memory.atomic.notify") (param $addr i32) (param $count i32) (result i32) + (memory.atomic.notify (local.get 0) (local.get 1))) + (func (export "memory.atomic.wait32") (param $addr i32) (param $expected i32) (param $timeout i64) (result i32) + (memory.atomic.wait32 (local.get 0) (local.get 1) (local.get 2))) + (func (export "memory.atomic.wait64") (param $addr i32) (param $expected i64) (param $timeout i64) (result i32) + (memory.atomic.wait64 (local.get 0) (local.get 1) (local.get 2))) +) + +(invoke "init" (i64.const 0xffffffffffff)) + +(assert_trap (invoke "memory.atomic.wait32" (i32.const 0) (i32.const 0) (i64.const 0)) "expected shared memory") +(assert_trap (invoke "memory.atomic.wait64" (i32.const 0) (i64.const 0) (i64.const 0)) "expected shared memory") + +;; notify still works +(assert_return (invoke "memory.atomic.notify" (i32.const 0) (i32.const 0)) (i32.const 0)) + +;; OOB and unaligned notify still trap +(assert_trap (invoke "memory.atomic.notify" (i32.const 65536) (i32.const 0)) "out of bounds memory access") +(assert_trap (invoke "memory.atomic.notify" (i32.const 65531) (i32.const 0)) "unaligned atomic") + + +;; unshared memory is OK +(module + (memory 1 1) + (func (drop (memory.atomic.notify (i32.const 0) (i32.const 0)))) + (func (drop (memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const 0)))) + (func (drop (memory.atomic.wait64 (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i32.atomic.load (i32.const 0)))) + (func (drop (i64.atomic.load (i32.const 0)))) + (func (drop (i32.atomic.load16_u (i32.const 0)))) + (func (drop (i64.atomic.load16_u (i32.const 0)))) + (func (drop (i64.atomic.load32_u (i32.const 0)))) + (func (i32.atomic.store (i32.const 0) (i32.const 0))) + (func (i64.atomic.store (i32.const 0) (i64.const 0))) + (func (i32.atomic.store16 (i32.const 0) (i32.const 0))) + (func (i64.atomic.store16 (i32.const 0) (i64.const 0))) + (func (i64.atomic.store32 (i32.const 0) (i64.const 0))) + (func (drop (i32.atomic.rmw.add (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.add (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.add_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.add_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.add_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.sub (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.sub (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.sub_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.sub_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.sub_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.and (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.and (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.and_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.and_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.and_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.or (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.or (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.or_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.or_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.or_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.xor (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.xor (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.xor_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.xor_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.xor_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.xchg (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.xchg (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.xchg_u (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.xchg_u (i32.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.xchg_u (i32.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw.cmpxchg (i32.const 0) (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw.cmpxchg (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i32.atomic.rmw16.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0)))) + (func (drop (i64.atomic.rmw16.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0)))) + (func (drop (i64.atomic.rmw32.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0)))) +) + +;; atomic.fence: no memory is ok +(module + (func (export "fence") (atomic.fence)) +) + +(assert_return (invoke "fence")) + +;; Fails with no memory +(assert_invalid (module (func (drop (memory.atomic.notify (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (memory.atomic.wait64 (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.load (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.load16_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load16_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.load32_u (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (i32.atomic.store (i32.const 0) (i32.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (i32.atomic.store16 (i32.const 0) (i32.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store16 (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (i64.atomic.store32 (i32.const 0) (i64.const 0)))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.add (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.add (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.add_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.add_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.add_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.sub (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.sub (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.sub_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.sub_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.sub_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.and (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.and (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.and_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.and_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.and_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.or (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.or (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.or_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.or_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.or_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.xor (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.xor (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.xor_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.xor_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.xor_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.xchg (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.xchg (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.xchg_u (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.xchg_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.xchg_u (i32.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw.cmpxchg (i32.const 0) (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw.cmpxchg (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i32.atomic.rmw16.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw16.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") +(assert_invalid (module (func (drop (i64.atomic.rmw32.cmpxchg_u (i32.const 0) (i64.const 0) (i64.const 0))))) "unknown memory") diff --git a/internal/testing/binaryencoding/import.go b/internal/testing/binaryencoding/import.go index 0646a19c0f..8abffde206 100644 --- a/internal/testing/binaryencoding/import.go +++ b/internal/testing/binaryencoding/import.go @@ -19,13 +19,13 @@ func EncodeImport(i *wasm.Import) []byte { data = append(data, leb128.EncodeUint32(i.DescFunc)...) case wasm.ExternTypeTable: data = append(data, wasm.RefTypeFuncref) - data = append(data, EncodeLimitsType(i.DescTable.Min, i.DescTable.Max)...) + data = append(data, EncodeLimitsType(i.DescTable.Min, i.DescTable.Max, false)...) case wasm.ExternTypeMemory: maxPtr := &i.DescMem.Max if !i.DescMem.IsMaxEncoded { maxPtr = nil } - data = append(data, EncodeLimitsType(i.DescMem.Min, maxPtr)...) + data = append(data, EncodeLimitsType(i.DescMem.Min, maxPtr, i.DescMem.IsShared)...) case wasm.ExternTypeGlobal: g := i.DescGlobal var mutable byte diff --git a/internal/testing/binaryencoding/limits.go b/internal/testing/binaryencoding/limits.go index 016e4dc7cc..ebc2421c42 100644 --- a/internal/testing/binaryencoding/limits.go +++ b/internal/testing/binaryencoding/limits.go @@ -7,9 +7,19 @@ import ( // EncodeLimitsType returns the `limitsType` (min, max) encoded in WebAssembly 1.0 (20191205) Binary Format. // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6 -func EncodeLimitsType(min uint32, max *uint32) []byte { - if max == nil { - return append(leb128.EncodeUint32(0x00), leb128.EncodeUint32(min)...) +// +// Extended in threads proposal: https://webassembly.github.io/threads/core/binary/types.html#limits +func EncodeLimitsType(min uint32, max *uint32, shared bool) []byte { + var flag uint32 + if max != nil { + flag = 0x01 + } + if shared { + flag |= 0x02 + } + ret := append(leb128.EncodeUint32(flag), leb128.EncodeUint32(min)...) + if max != nil { + ret = append(ret, leb128.EncodeUint32(*max)...) } - return append(leb128.EncodeUint32(0x01), append(leb128.EncodeUint32(min), leb128.EncodeUint32(*max)...)...) + return ret } diff --git a/internal/testing/binaryencoding/memory.go b/internal/testing/binaryencoding/memory.go index 938667a667..2c853330d5 100644 --- a/internal/testing/binaryencoding/memory.go +++ b/internal/testing/binaryencoding/memory.go @@ -12,5 +12,5 @@ func EncodeMemory(i *wasm.Memory) []byte { if !i.IsMaxEncoded { maxPtr = nil } - return EncodeLimitsType(i.Min, maxPtr) + return EncodeLimitsType(i.Min, maxPtr, i.IsShared) } diff --git a/internal/testing/binaryencoding/table.go b/internal/testing/binaryencoding/table.go index 2985dbdbbe..57f83db7f3 100644 --- a/internal/testing/binaryencoding/table.go +++ b/internal/testing/binaryencoding/table.go @@ -8,5 +8,5 @@ import ( // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table func EncodeTable(i *wasm.Table) []byte { - return append([]byte{i.Type}, EncodeLimitsType(i.Min, i.Max)...) + return append([]byte{i.Type}, EncodeLimitsType(i.Min, i.Max, false)...) } diff --git a/internal/wasm/binary/decoder.go b/internal/wasm/binary/decoder.go index 62000484ba..c4191dae95 100644 --- a/internal/wasm/binary/decoder.go +++ b/internal/wasm/binary/decoder.go @@ -115,7 +115,7 @@ func DecodeModule( case wasm.SectionIDTable: m.TableSection, err = decodeTableSection(r, enabledFeatures) case wasm.SectionIDMemory: - m.MemorySection, err = decodeMemorySection(r, memSizer, memoryLimitPages) + m.MemorySection, err = decodeMemorySection(r, enabledFeatures, memSizer, memoryLimitPages) case wasm.SectionIDGlobal: if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil { return nil, err // avoid re-wrapping the error. diff --git a/internal/wasm/binary/import.go b/internal/wasm/binary/import.go index 666636177c..39d310c557 100644 --- a/internal/wasm/binary/import.go +++ b/internal/wasm/binary/import.go @@ -39,7 +39,7 @@ func decodeImport( case wasm.ExternTypeTable: err = decodeTable(r, enabledFeatures, &ret.DescTable) case wasm.ExternTypeMemory: - ret.DescMem, err = decodeMemory(r, memorySizer, memoryLimitPages) + ret.DescMem, err = decodeMemory(r, enabledFeatures, memorySizer, memoryLimitPages) case wasm.ExternTypeGlobal: ret.DescGlobal, err = decodeGlobalType(r) default: diff --git a/internal/wasm/binary/limits.go b/internal/wasm/binary/limits.go index 1ec8d7c302..ff2d73b5fd 100644 --- a/internal/wasm/binary/limits.go +++ b/internal/wasm/binary/limits.go @@ -10,7 +10,9 @@ import ( // decodeLimitsType returns the `limitsType` (min, max) decoded with the WebAssembly 1.0 (20191205) Binary Format. // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#limits%E2%91%A6 -func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, err error) { +// +// Extended in threads proposal: https://webassembly.github.io/threads/core/binary/types.html#limits +func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, shared bool, err error) { var flag byte if flag, err = r.ReadByte(); err != nil { err = fmt.Errorf("read leading byte: %v", err) @@ -18,12 +20,12 @@ func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, err error) { } switch flag { - case 0x00: + case 0x00, 0x02: min, _, err = leb128.DecodeUint32(r) if err != nil { err = fmt.Errorf("read min of limit: %v", err) } - case 0x01: + case 0x01, 0x03: min, _, err = leb128.DecodeUint32(r) if err != nil { err = fmt.Errorf("read min of limit: %v", err) @@ -36,7 +38,10 @@ func decodeLimitsType(r *bytes.Reader) (min uint32, max *uint32, err error) { max = &m } default: - err = fmt.Errorf("%v for limits: %#x != 0x00 or 0x01", ErrInvalidByte, flag) + err = fmt.Errorf("%v for limits: %#x not in (0x00, 0x01, 0x02, 0x03)", ErrInvalidByte, flag) } + + shared = flag == 0x02 || flag == 0x03 + return } diff --git a/internal/wasm/binary/limits_test.go b/internal/wasm/binary/limits_test.go index e6f663937f..7e65b4d622 100644 --- a/internal/wasm/binary/limits_test.go +++ b/internal/wasm/binary/limits_test.go @@ -18,6 +18,7 @@ func TestLimitsType(t *testing.T) { name string min uint32 max *uint32 + shared bool expected []byte }{ { @@ -45,21 +46,52 @@ func TestLimitsType(t *testing.T) { max: &largest, expected: []byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xf, 0xff, 0xff, 0xff, 0xff, 0xf}, }, + { + name: "min 0, shared", + shared: true, + expected: []byte{0x2, 0}, + }, + { + name: "min 0, max 0, shared", + max: &zero, + shared: true, + expected: []byte{0x3, 0, 0}, + }, + { + name: "min largest, shared", + min: largest, + shared: true, + expected: []byte{0x2, 0xff, 0xff, 0xff, 0xff, 0xf}, + }, + { + name: "min 0, max largest, shared", + max: &largest, + shared: true, + expected: []byte{0x3, 0, 0xff, 0xff, 0xff, 0xff, 0xf}, + }, + { + name: "min largest max largest, shared", + min: largest, + max: &largest, + shared: true, + expected: []byte{0x3, 0xff, 0xff, 0xff, 0xff, 0xf, 0xff, 0xff, 0xff, 0xff, 0xf}, + }, } for _, tt := range tests { tc := tt - b := binaryencoding.EncodeLimitsType(tc.min, tc.max) + b := binaryencoding.EncodeLimitsType(tc.min, tc.max, tc.shared) t.Run(fmt.Sprintf("encode - %s", tc.name), func(t *testing.T) { require.Equal(t, tc.expected, b) }) t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) { - min, max, err := decodeLimitsType(bytes.NewReader(b)) + min, max, shared, err := decodeLimitsType(bytes.NewReader(b)) require.NoError(t, err) require.Equal(t, min, tc.min) require.Equal(t, max, tc.max) + require.Equal(t, shared, tc.shared) }) } } diff --git a/internal/wasm/binary/memory.go b/internal/wasm/binary/memory.go index fd6b32a670..ec8b37f3fb 100644 --- a/internal/wasm/binary/memory.go +++ b/internal/wasm/binary/memory.go @@ -2,7 +2,10 @@ package binary import ( "bytes" + "fmt" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/wasm" ) @@ -11,16 +14,21 @@ import ( // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory func decodeMemory( r *bytes.Reader, + enabledFeatures api.CoreFeatures, memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), memoryLimitPages uint32, ) (*wasm.Memory, error) { - min, maxP, err := decodeLimitsType(r) + min, maxP, shared, err := decodeLimitsType(r) if err != nil { return nil, err } + if shared && !enabledFeatures.IsEnabled(experimental.CoreFeaturesThreads) { + return nil, fmt.Errorf("shared memory requested but threads feature not enabled") + } + min, capacity, max := memorySizer(min, maxP) - mem := &wasm.Memory{Min: min, Cap: capacity, Max: max, IsMaxEncoded: maxP != nil} + mem := &wasm.Memory{Min: min, Cap: capacity, Max: max, IsMaxEncoded: maxP != nil, IsShared: shared} return mem, mem.Validate(memoryLimitPages) } diff --git a/internal/wasm/binary/memory_test.go b/internal/wasm/binary/memory_test.go index 32bfa188a3..d4dc1ef485 100644 --- a/internal/wasm/binary/memory_test.go +++ b/internal/wasm/binary/memory_test.go @@ -5,6 +5,8 @@ import ( "fmt" "testing" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/testing/binaryencoding" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/wasm" @@ -182,7 +184,7 @@ func TestMemoryType(t *testing.T) { expectedDecoded.Max = tmax } - binary, err := decodeMemory(bytes.NewReader(b), newMemorySizer(tmax, false), tmax) + binary, err := decodeMemory(bytes.NewReader(b), api.CoreFeaturesV2, newMemorySizer(tmax, false), tmax) require.NoError(t, err) require.Equal(t, binary, expectedDecoded) }) @@ -212,13 +214,21 @@ func TestDecodeMemoryType_Errors(t *testing.T) { input: []byte{0x1, 0, 0xff, 0xff, 0xff, 0xff, 0xf}, expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)", }, + { + name: "shared but no threads", + input: []byte{0x2, 0, 0x80, 0x80, 0x4}, + expectedErr: "shared memory requested but threads feature not enabled", + }, } for _, tt := range tests { tc := tt t.Run(tc.name, func(t *testing.T) { - _, err := decodeMemory(bytes.NewReader(tc.input), newMemorySizer(max, false), max) + // Allow test to work if threads is ever added to default features by explicitly removing threads features + features := api.CoreFeaturesV2 + features = features.SetEnabled(experimental.CoreFeaturesThreads, false) + _, err := decodeMemory(bytes.NewReader(tc.input), features, newMemorySizer(max, false), max) require.EqualError(t, err, tc.expectedErr) }) } diff --git a/internal/wasm/binary/section.go b/internal/wasm/binary/section.go index a4111572fa..622ee5923e 100644 --- a/internal/wasm/binary/section.go +++ b/internal/wasm/binary/section.go @@ -105,6 +105,7 @@ func decodeTableSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wa func decodeMemorySection( r *bytes.Reader, + enabledFeatures api.CoreFeatures, memorySizer memorySizer, memoryLimitPages uint32, ) (*wasm.Memory, error) { @@ -119,7 +120,7 @@ func decodeMemorySection( return nil, nil } - return decodeMemory(r, memorySizer, memoryLimitPages) + return decodeMemory(r, enabledFeatures, memorySizer, memoryLimitPages) } func decodeGlobalSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]wasm.Global, error) { diff --git a/internal/wasm/binary/section_test.go b/internal/wasm/binary/section_test.go index e64af77d40..dcebdbed17 100644 --- a/internal/wasm/binary/section_test.go +++ b/internal/wasm/binary/section_test.go @@ -104,7 +104,7 @@ func TestMemorySection(t *testing.T) { tc := tt t.Run(tc.name, func(t *testing.T) { - memories, err := decodeMemorySection(bytes.NewReader(tc.input), newMemorySizer(max, false), max) + memories, err := decodeMemorySection(bytes.NewReader(tc.input), api.CoreFeaturesV2, newMemorySizer(max, false), max) require.NoError(t, err) require.Equal(t, tc.expected, memories) }) @@ -134,7 +134,7 @@ func TestMemorySection_Errors(t *testing.T) { tc := tt t.Run(tc.name, func(t *testing.T) { - _, err := decodeMemorySection(bytes.NewReader(tc.input), newMemorySizer(max, false), max) + _, err := decodeMemorySection(bytes.NewReader(tc.input), api.CoreFeaturesV2, newMemorySizer(max, false), max) require.EqualError(t, err, tc.expectedErr) }) } diff --git a/internal/wasm/binary/table.go b/internal/wasm/binary/table.go index cb5aa75e7c..353ec75662 100644 --- a/internal/wasm/binary/table.go +++ b/internal/wasm/binary/table.go @@ -23,7 +23,8 @@ func decodeTable(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.Ta } } - ret.Min, ret.Max, err = decodeLimitsType(r) + var shared bool + ret.Min, ret.Max, shared, err = decodeLimitsType(r) if err != nil { return fmt.Errorf("read limits: %v", err) } @@ -35,5 +36,8 @@ func decodeTable(r *bytes.Reader, enabledFeatures api.CoreFeatures, ret *wasm.Ta return fmt.Errorf("table size minimum must not be greater than maximum") } } + if shared { + return fmt.Errorf("tables cannot be marked as shared") + } return } diff --git a/internal/wasm/binary/table_test.go b/internal/wasm/binary/table_test.go index 73ec6d4d98..7543a33c97 100644 --- a/internal/wasm/binary/table_test.go +++ b/internal/wasm/binary/table_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/testing/binaryencoding" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/wasm" @@ -93,6 +94,13 @@ func TestDecodeTableType_Errors(t *testing.T) { expectedErr: "table min must be at most 134217728", features: api.CoreFeatureReferenceTypes, }, + { + name: "shared", + input: []byte{wasm.RefTypeFuncref, 0x2, 0}, + expectedErr: "tables cannot be marked as shared", + // Shared tables are an error even if threads are enabled. + features: experimental.CoreFeaturesThreads, + }, } for _, tt := range tests { diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go index e9aef72c8d..b5390ebe85 100644 --- a/internal/wasm/func_validation.go +++ b/internal/wasm/func_validation.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/leb128" ) @@ -86,6 +87,8 @@ func (m *Module) validateFunctionWithMaxStackValues( instName = MiscInstructionName(body[pc+1]) } else if op == OpcodeVecPrefix { instName = VectorInstructionName(body[pc+1]) + } else if op == OpcodeAtomicPrefix { + instName = AtomicInstructionName(body[pc+1]) } else { instName = InstructionName(op) } @@ -1388,6 +1391,378 @@ func (m *Module) validateFunctionWithMaxStackValues( default: return fmt.Errorf("TODO: SIMD instruction %s will be implemented in #506", vectorInstructionName[vecOpcode]) } + } else if op == OpcodeAtomicPrefix { + pc++ + // Atomic instructions come with two bytes where the first byte is always OpcodeAtomicPrefix, + // and the second byte determines the actual instruction. + atomicOpcode := body[pc] + if err := enabledFeatures.RequireEnabled(experimental.CoreFeaturesThreads); err != nil { + return fmt.Errorf("%s invalid as %v", atomicInstructionName[atomicOpcode], err) + } + pc++ + + if atomicOpcode == OpcodeAtomicFence { + // No memory requirement and no arguments or return, however the immediate byte value must be 0. + imm := body[pc] + if imm != 0x0 { + return fmt.Errorf("invalid immediate value for %s", AtomicInstructionName(atomicOpcode)) + } + continue + } + + // All atomic operations except fence (checked above) require memory + if memory == nil { + return fmt.Errorf("memory must exist for %s", AtomicInstructionName(atomicOpcode)) + } + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + pc += read - 1 + switch atomicOpcode { + case OpcodeAtomicMemoryNotify: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicMemoryWait32: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicMemoryWait64: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Load: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64Load: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32Load8U: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Load32U: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32Store: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32Store8: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32Store16: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store8: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store16: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store32: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32RmwAdd, OpcodeAtomicI32RmwSub, OpcodeAtomicI32RmwAnd, OpcodeAtomicI32RmwOr, OpcodeAtomicI32RmwXor, OpcodeAtomicI32RmwXchg: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw8AddU, OpcodeAtomicI32Rmw8SubU, OpcodeAtomicI32Rmw8AndU, OpcodeAtomicI32Rmw8OrU, OpcodeAtomicI32Rmw8XorU, OpcodeAtomicI32Rmw8XchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw16AddU, OpcodeAtomicI32Rmw16SubU, OpcodeAtomicI32Rmw16AndU, OpcodeAtomicI32Rmw16OrU, OpcodeAtomicI32Rmw16XorU, OpcodeAtomicI32Rmw16XchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64RmwAdd, OpcodeAtomicI64RmwSub, OpcodeAtomicI64RmwAnd, OpcodeAtomicI64RmwOr, OpcodeAtomicI64RmwXor, OpcodeAtomicI64RmwXchg: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw8AddU, OpcodeAtomicI64Rmw8SubU, OpcodeAtomicI64Rmw8AndU, OpcodeAtomicI64Rmw8OrU, OpcodeAtomicI64Rmw8XorU, OpcodeAtomicI64Rmw8XchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw16AddU, OpcodeAtomicI64Rmw16SubU, OpcodeAtomicI64Rmw16AndU, OpcodeAtomicI64Rmw16OrU, OpcodeAtomicI64Rmw16XorU, OpcodeAtomicI64Rmw16XchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw32AddU, OpcodeAtomicI64Rmw32SubU, OpcodeAtomicI64Rmw32AndU, OpcodeAtomicI64Rmw32OrU, OpcodeAtomicI64Rmw32XorU, OpcodeAtomicI64Rmw32XchgU: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32RmwCmpxchg: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw8CmpxchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw16CmpxchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64RmwCmpxchg: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw8CmpxchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw16CmpxchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw32CmpxchgU: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + default: + return fmt.Errorf("invalid atomic opcode: 0x%x", atomicOpcode) + } } else if op == OpcodeBlock { br.Reset(body[pc+1:]) bt, num, err := DecodeBlockType(m.TypeSection, br, enabledFeatures) diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go index e571ad2e93..57edb5a372 100644 --- a/internal/wasm/func_validation_test.go +++ b/internal/wasm/func_validation_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/leb128" "github.com/tetratelabs/wazero/internal/testing/require" ) @@ -3678,3 +3679,1171 @@ func Test_SplitCallStack(t *testing.T) { }) } } + +func TestModule_funcValidation_Atomic(t *testing.T) { + t.Run("valid bytecode", func(t *testing.T) { + tests := []struct { + name string + body []byte + noDropBeforeReturn bool + }{ + { + name: "i32.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load8U, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load16U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load8U, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load16U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.load32_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load32U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store8, 0x0, 0x8, // alignment=2^0, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store16, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store8, 0x0, 0x8, // alignment=2^0, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store16, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store32, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AddU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAdd, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AddU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAdd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8SubU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwSub, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8SubU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwSub, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AndU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAnd, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AndU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAnd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8OrU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwOr, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8OrU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwOr, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XorU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXor, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XorU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXor, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXchg, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8CmpxchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwCmpxchg, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8CmpxchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwCmpxchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.wait32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait32, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "memory.atomic.wait64", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait64, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.notify", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicMemoryNotify, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "memory.atomic.fence", + body: []byte{ + OpcodeAtomicPrefix, OpcodeAtomicFence, 0x0, + }, + noDropBeforeReturn: true, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + body := append([]byte{}, tc.body...) + if !tt.noDropBeforeReturn { + body = append(body, OpcodeDrop) + } + body = append(body, OpcodeEnd) + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + + t.Run("with memory", func(t *testing.T) { + err := m.validateFunction(&stacks{}, experimental.CoreFeaturesThreads, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.NoError(t, err) + }) + + t.Run("without memory", func(t *testing.T) { + err := m.validateFunction(&stacks{}, experimental.CoreFeaturesThreads, + 0, []Index{0}, nil, nil, []Table{}, nil, bytes.NewReader(nil)) + // Only fence doesn't require memory + if tc.name == "memory.atomic.fence" { + require.NoError(t, err) + } else { + require.Error(t, err, fmt.Sprintf("memory must exist for %s", tc.name)) + } + }) + }) + } + }) + + t.Run("atomic.fence bad immediate", func(t *testing.T) { + body := []byte{ + OpcodeAtomicPrefix, OpcodeAtomicFence, 0x1, + OpcodeEnd, + } + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + err := m.validateFunction(&stacks{}, experimental.CoreFeaturesThreads, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.Error(t, err, "invalid immediate value for atomic.fence") + }) + + t.Run("bad alignment", func(t *testing.T) { + tests := []struct { + name string + body []byte + noDropBeforeReturn bool + }{ + { + name: "i32.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load16U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load16U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load32_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load32U, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store16, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store16, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store32, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store, 0x4, 0x8, // alignment=2^4, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAdd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AddU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAdd, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwSub, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32SubU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwSub, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAnd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AndU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAnd, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwOr, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32OrU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwOr, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXor, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XorU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXor, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XchgU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXchg, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwCmpxchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32CmpxchgU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwCmpxchg, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "memory.atomic.wait32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait32, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.wait64", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait64, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "memory.atomic.notify", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicMemoryNotify, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + body := append([]byte{}, tc.body...) + if !tt.noDropBeforeReturn { + body = append(body, OpcodeDrop) + } + body = append(body, OpcodeEnd) + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + err := m.validateFunction(&stacks{}, experimental.CoreFeaturesThreads, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.Error(t, err, "invalid memory alignment") + }) + } + }) +} diff --git a/internal/wasm/instruction.go b/internal/wasm/instruction.go index ba65e9a2e3..72b4baccfc 100644 --- a/internal/wasm/instruction.go +++ b/internal/wasm/instruction.go @@ -281,6 +281,10 @@ const ( // OpcodeVecPrefix is the prefix of all vector isntructions introduced in // CoreFeatureSIMD. OpcodeVecPrefix Opcode = 0xfd + + // OpcodeAtomicPrefix is the prefix of all atomic instructions introduced in + // FeatureThreads. + OpcodeAtomicPrefix Opcode = 0xfe ) // OpcodeMisc represents opcodes of the miscellaneous operations. @@ -623,6 +627,157 @@ const ( OpcodeVecF64x2PromoteLowF32x4Zero OpcodeVec = 0x5f ) +// OpcodeAtomic represents an opcode of atomic instructions which has +// multi-byte encoding and is prefixed by OpcodeAtomicPrefix. +// +// These opcodes are toggled with experimental.CoreFeaturesThreads. +type OpcodeAtomic = byte + +const ( + // OpcodeAtomicMemoryNotify represents the instruction memory.atomic.notify. + OpcodeAtomicMemoryNotify OpcodeAtomic = 0x00 + // OpcodeAtomicMemoryWait32 represents the instruction memory.atomic.wait32. + OpcodeAtomicMemoryWait32 OpcodeAtomic = 0x01 + // OpcodeAtomicMemoryWait64 represents the instruction memory.atomic.wait64. + OpcodeAtomicMemoryWait64 OpcodeAtomic = 0x02 + // OpcodeAtomicFence represents the instruction atomic.fence. + OpcodeAtomicFence OpcodeAtomic = 0x03 + + // OpcodeAtomicI32Load represents the instruction i32.atomic.load. + OpcodeAtomicI32Load OpcodeAtomic = 0x10 + // OpcodeAtomicI64Load represents the instruction i64.atomic.load. + OpcodeAtomicI64Load OpcodeAtomic = 0x11 + // OpcodeAtomicI32Load8U represents the instruction i32.atomic.load8_u. + OpcodeAtomicI32Load8U OpcodeAtomic = 0x12 + // OpcodeAtomicI32Load16U represents the instruction i32.atomic.load16_u. + OpcodeAtomicI32Load16U OpcodeAtomic = 0x13 + // OpcodeAtomicI64Load8U represents the instruction i64.atomic.load8_u. + OpcodeAtomicI64Load8U OpcodeAtomic = 0x14 + // OpcodeAtomicI64Load16U represents the instruction i64.atomic.load16_u. + OpcodeAtomicI64Load16U OpcodeAtomic = 0x15 + // OpcodeAtomicI64Load32U represents the instruction i64.atomic.load32_u. + OpcodeAtomicI64Load32U OpcodeAtomic = 0x16 + // OpcodeAtomicI32Store represents the instruction i32.atomic.store. + OpcodeAtomicI32Store OpcodeAtomic = 0x17 + // OpcodeAtomicI64Store represents the instruction i64.atomic.store. + OpcodeAtomicI64Store OpcodeAtomic = 0x18 + // OpcodeAtomicI32Store8 represents the instruction i32.atomic.store8. + OpcodeAtomicI32Store8 OpcodeAtomic = 0x19 + // OpcodeAtomicI32Store16 represents the instruction i32.atomic.store16. + OpcodeAtomicI32Store16 OpcodeAtomic = 0x1a + // OpcodeAtomicI64Store8 represents the instruction i64.atomic.store8. + OpcodeAtomicI64Store8 OpcodeAtomic = 0x1b + // OpcodeAtomicI64Store16 represents the instruction i64.atomic.store16. + OpcodeAtomicI64Store16 OpcodeAtomic = 0x1c + // OpcodeAtomicI64Store32 represents the instruction i64.atomic.store32. + OpcodeAtomicI64Store32 OpcodeAtomic = 0x1d + + // OpcodeAtomicI32RmwAdd represents the instruction i32.atomic.rmw.add. + OpcodeAtomicI32RmwAdd OpcodeAtomic = 0x1e + // OpcodeAtomicI64RmwAdd represents the instruction i64.atomic.rmw.add. + OpcodeAtomicI64RmwAdd OpcodeAtomic = 0x1f + // OpcodeAtomicI32Rmw8AddU represents the instruction i32.atomic.rmw8.add_u. + OpcodeAtomicI32Rmw8AddU OpcodeAtomic = 0x20 + // OpcodeAtomicI32Rmw16AddU represents the instruction i32.atomic.rmw16.add_u. + OpcodeAtomicI32Rmw16AddU OpcodeAtomic = 0x21 + // OpcodeAtomicI64Rmw8AddU represents the instruction i64.atomic.rmw8.add_u. + OpcodeAtomicI64Rmw8AddU OpcodeAtomic = 0x22 + // OpcodeAtomicI64Rmw16AddU represents the instruction i64.atomic.rmw16.add_u. + OpcodeAtomicI64Rmw16AddU OpcodeAtomic = 0x23 + // OpcodeAtomicI64Rmw32AddU represents the instruction i64.atomic.rmw32.add_u. + OpcodeAtomicI64Rmw32AddU OpcodeAtomic = 0x24 + + // OpcodeAtomicI32RmwSub represents the instruction i32.atomic.rmw.sub. + OpcodeAtomicI32RmwSub OpcodeAtomic = 0x25 + // OpcodeAtomicI64RmwSub represents the instruction i64.atomic.rmw.sub. + OpcodeAtomicI64RmwSub OpcodeAtomic = 0x26 + // OpcodeAtomicI32Rmw8SubU represents the instruction i32.atomic.rmw8.sub_u. + OpcodeAtomicI32Rmw8SubU OpcodeAtomic = 0x27 + // OpcodeAtomicI32Rmw16SubU represents the instruction i32.atomic.rmw16.sub_u. + OpcodeAtomicI32Rmw16SubU OpcodeAtomic = 0x28 + // OpcodeAtomicI64Rmw8SubU represents the instruction i64.atomic.rmw8.sub_u. + OpcodeAtomicI64Rmw8SubU OpcodeAtomic = 0x29 + // OpcodeAtomicI64Rmw16SubU represents the instruction i64.atomic.rmw16.sub_u. + OpcodeAtomicI64Rmw16SubU OpcodeAtomic = 0x2a + // OpcodeAtomicI64Rmw32SubU represents the instruction i64.atomic.rmw32.sub_u. + OpcodeAtomicI64Rmw32SubU OpcodeAtomic = 0x2b + + // OpcodeAtomicI32RmwAnd represents the instruction i32.atomic.rmw.and. + OpcodeAtomicI32RmwAnd OpcodeAtomic = 0x2c + // OpcodeAtomicI64RmwAnd represents the instruction i64.atomic.rmw.and. + OpcodeAtomicI64RmwAnd OpcodeAtomic = 0x2d + // OpcodeAtomicI32Rmw8AndU represents the instruction i32.atomic.rmw8.and_u. + OpcodeAtomicI32Rmw8AndU OpcodeAtomic = 0x2e + // OpcodeAtomicI32Rmw16AndU represents the instruction i32.atomic.rmw16.and_u. + OpcodeAtomicI32Rmw16AndU OpcodeAtomic = 0x2f + // OpcodeAtomicI64Rmw8AndU represents the instruction i64.atomic.rmw8.and_u. + OpcodeAtomicI64Rmw8AndU OpcodeAtomic = 0x30 + // OpcodeAtomicI64Rmw16AndU represents the instruction i64.atomic.rmw16.and_u. + OpcodeAtomicI64Rmw16AndU OpcodeAtomic = 0x31 + // OpcodeAtomicI64Rmw32AndU represents the instruction i64.atomic.rmw32.and_u. + OpcodeAtomicI64Rmw32AndU OpcodeAtomic = 0x32 + + // OpcodeAtomicI32RmwOr represents the instruction i32.atomic.rmw.or. + OpcodeAtomicI32RmwOr OpcodeAtomic = 0x33 + // OpcodeAtomicI64RmwOr represents the instruction i64.atomic.rmw.or. + OpcodeAtomicI64RmwOr OpcodeAtomic = 0x34 + // OpcodeAtomicI32Rmw8OrU represents the instruction i32.atomic.rmw8.or_u. + OpcodeAtomicI32Rmw8OrU OpcodeAtomic = 0x35 + // OpcodeAtomicI32Rmw16OrU represents the instruction i32.atomic.rmw16.or_u. + OpcodeAtomicI32Rmw16OrU OpcodeAtomic = 0x36 + // OpcodeAtomicI64Rmw8OrU represents the instruction i64.atomic.rmw8.or_u. + OpcodeAtomicI64Rmw8OrU OpcodeAtomic = 0x37 + // OpcodeAtomicI64Rmw16OrU represents the instruction i64.atomic.rmw16.or_u. + OpcodeAtomicI64Rmw16OrU OpcodeAtomic = 0x38 + // OpcodeAtomicI64Rmw32OrU represents the instruction i64.atomic.rmw32.or_u. + OpcodeAtomicI64Rmw32OrU OpcodeAtomic = 0x39 + + // OpcodeAtomicI32RmwXor represents the instruction i32.atomic.rmw.xor. + OpcodeAtomicI32RmwXor OpcodeAtomic = 0x3a + // OpcodeAtomicI64RmwXor represents the instruction i64.atomic.rmw.xor. + OpcodeAtomicI64RmwXor OpcodeAtomic = 0x3b + // OpcodeAtomicI32Rmw8XorU represents the instruction i32.atomic.rmw8.xor_u. + OpcodeAtomicI32Rmw8XorU OpcodeAtomic = 0x3c + // OpcodeAtomicI32Rmw16XorU represents the instruction i32.atomic.rmw16.xor_u. + OpcodeAtomicI32Rmw16XorU OpcodeAtomic = 0x3d + // OpcodeAtomicI64Rmw8XorU represents the instruction i64.atomic.rmw8.xor_u. + OpcodeAtomicI64Rmw8XorU OpcodeAtomic = 0x3e + // OpcodeAtomicI64Rmw16XorU represents the instruction i64.atomic.rmw16.xor_u. + OpcodeAtomicI64Rmw16XorU OpcodeAtomic = 0x3f + // OpcodeAtomicI64Rmw32XorU represents the instruction i64.atomic.rmw32.xor_u. + OpcodeAtomicI64Rmw32XorU OpcodeAtomic = 0x40 + + // OpcodeAtomicI32RmwXchg represents the instruction i32.atomic.rmw.xchg. + OpcodeAtomicI32RmwXchg OpcodeAtomic = 0x41 + // OpcodeAtomicI64RmwXchg represents the instruction i64.atomic.rmw.xchg. + OpcodeAtomicI64RmwXchg OpcodeAtomic = 0x42 + // OpcodeAtomicI32Rmw8XchgU represents the instruction i32.atomic.rmw8.xchg_u. + OpcodeAtomicI32Rmw8XchgU OpcodeAtomic = 0x43 + // OpcodeAtomicI32Rmw16XchgU represents the instruction i32.atomic.rmw16.xchg_u. + OpcodeAtomicI32Rmw16XchgU OpcodeAtomic = 0x44 + // OpcodeAtomicI64Rmw8XchgU represents the instruction i64.atomic.rmw8.xchg_u. + OpcodeAtomicI64Rmw8XchgU OpcodeAtomic = 0x45 + // OpcodeAtomicI64Rmw16XchgU represents the instruction i64.atomic.rmw16.xchg_u. + OpcodeAtomicI64Rmw16XchgU OpcodeAtomic = 0x46 + // OpcodeAtomicI64Rmw32XchgU represents the instruction i64.atomic.rmw32.xchg_u. + OpcodeAtomicI64Rmw32XchgU OpcodeAtomic = 0x47 + + // OpcodeAtomicI32RmwCmpxchg represents the instruction i32.atomic.rmw.cmpxchg. + OpcodeAtomicI32RmwCmpxchg OpcodeAtomic = 0x48 + // OpcodeAtomicI64RmwCmpxchg represents the instruction i64.atomic.rmw.cmpxchg. + OpcodeAtomicI64RmwCmpxchg OpcodeAtomic = 0x49 + // OpcodeAtomicI32Rmw8CmpxchgU represents the instruction i32.atomic.rmw8.cmpxchg_u. + OpcodeAtomicI32Rmw8CmpxchgU OpcodeAtomic = 0x4a + // OpcodeAtomicI32Rmw16CmpxchgU represents the instruction i32.atomic.rmw16.cmpxchg_u. + OpcodeAtomicI32Rmw16CmpxchgU OpcodeAtomic = 0x4b + // OpcodeAtomicI64Rmw8CmpxchgU represents the instruction i64.atomic.rmw8.cmpxchg_u. + OpcodeAtomicI64Rmw8CmpxchgU OpcodeAtomic = 0x4c + // OpcodeAtomicI64Rmw16CmpxchgU represents the instruction i64.atomic.rmw16.cmpxchg_u. + OpcodeAtomicI64Rmw16CmpxchgU OpcodeAtomic = 0x4d + // OpcodeAtomicI64Rmw32CmpxchgU represents the instruction i64.atomic.rmw32.cmpxchg_u. + OpcodeAtomicI64Rmw32CmpxchgU OpcodeAtomic = 0x4e +) + const ( OpcodeUnreachableName = "unreachable" OpcodeNopName = "nop" @@ -1548,3 +1703,164 @@ var vectorInstructionName = map[OpcodeVec]string{ func VectorInstructionName(oc OpcodeVec) (ret string) { return vectorInstructionName[oc] } + +const ( + OpcodeAtomicMemoryNotifyName = "memory.atomic.notify" + OpcodeAtomicMemoryWait32Name = "memory.atomic.wait32" + OpcodeAtomicMemoryWait64Name = "memory.atomic.wait64" + OpcodeAtomicFenceName = "atomic.fence" + + OpcodeAtomicI32LoadName = "i32.atomic.load" + OpcodeAtomicI64LoadName = "i64.atomic.load" + OpcodeAtomicI32Load8UName = "i32.atomic.load8_u" + OpcodeAtomicI32Load16UName = "i32.atomic.load16_u" + OpcodeAtomicI64Load8UName = "i64.atomic.load8_u" + OpcodeAtomicI64Load16UName = "i64.atomic.load16_u" + OpcodeAtomicI64Load32UName = "i64.atomic.load32_u" + OpcodeAtomicI32StoreName = "i32.atomic.store" + OpcodeAtomicI64StoreName = "i64.atomic.store" + OpcodeAtomicI32Store8Name = "i32.atomic.store8" + OpcodeAtomicI32Store16Name = "i32.atomic.store16" + OpcodeAtomicI64Store8Name = "i64.atomic.store8" + OpcodeAtomicI64Store16Name = "i64.atomic.store16" + OpcodeAtomicI64Store32Name = "i64.atomic.store32" + + OpcodeAtomicI32RmwAddName = "i32.atomic.rmw.add" + OpcodeAtomicI64RmwAddName = "i64.atomic.rmw.add" + OpcodeAtomicI32Rmw8AddUName = "i32.atomic.rmw8.add_u" + OpcodeAtomicI32Rmw16AddUName = "i32.atomic.rmw16.add_u" + OpcodeAtomicI64Rmw8AddUName = "i64.atomic.rmw8.add_u" + OpcodeAtomicI64Rmw16AddUName = "i64.atomic.rmw16.add_u" + OpcodeAtomicI64Rmw32AddUName = "i64.atomic.rmw32.add_u" + + OpcodeAtomicI32RmwSubName = "i32.atomic.rmw.sub" + OpcodeAtomicI64RmwSubName = "i64.atomic.rmw.sub" + OpcodeAtomicI32Rmw8SubUName = "i32.atomic.rmw8.sub_u" + OpcodeAtomicI32Rmw16SubUName = "i32.atomic.rmw16.sub_u" + OpcodeAtomicI64Rmw8SubUName = "i64.atomic.rmw8.sub_u" + OpcodeAtomicI64Rmw16SubUName = "i64.atomic.rmw16.sub_u" + OpcodeAtomicI64Rmw32SubUName = "i64.atomic.rmw32.sub_u" + + OpcodeAtomicI32RmwAndName = "i32.atomic.rmw.and" + OpcodeAtomicI64RmwAndName = "i64.atomic.rmw.and" + OpcodeAtomicI32Rmw8AndUName = "i32.atomic.rmw8.and_u" + OpcodeAtomicI32Rmw16AndUName = "i32.atomic.rmw16.and_u" + OpcodeAtomicI64Rmw8AndUName = "i64.atomic.rmw8.and_u" + OpcodeAtomicI64Rmw16AndUName = "i64.atomic.rmw16.and_u" + OpcodeAtomicI64Rmw32AndUName = "i64.atomic.rmw32.and_u" + + OpcodeAtomicI32RmwOrName = "i32.atomic.rmw.or" + OpcodeAtomicI64RmwOrName = "i64.atomic.rmw.or" + OpcodeAtomicI32Rmw8OrUName = "i32.atomic.rmw8.or_u" + OpcodeAtomicI32Rmw16OrUName = "i32.atomic.rmw16.or_u" + OpcodeAtomicI64Rmw8OrUName = "i64.atomic.rmw8.or_u" + OpcodeAtomicI64Rmw16OrUName = "i64.atomic.rmw16.or_u" + OpcodeAtomicI64Rmw32OrUName = "i64.atomic.rmw32.or_u" + + OpcodeAtomicI32RmwXorName = "i32.atomic.rmw.xor" + OpcodeAtomicI64RmwXorName = "i64.atomic.rmw.xor" + OpcodeAtomicI32Rmw8XorUName = "i32.atomic.rmw8.xor_u" + OpcodeAtomicI32Rmw16XorUName = "i32.atomic.rmw16.xor_u" + OpcodeAtomicI64Rmw8XorUName = "i64.atomic.rmw8.xor_u" + OpcodeAtomicI64Rmw16XorUName = "i64.atomic.rmw16.xor_u" + OpcodeAtomicI64Rmw32XorUName = "i64.atomic.rmw32.xor_u" + + OpcodeAtomicI32RmwXchgName = "i32.atomic.rmw.xchg" + OpcodeAtomicI64RmwXchgName = "i64.atomic.rmw.xchg" + OpcodeAtomicI32Rmw8XchgUName = "i32.atomic.rmw8.xchg_u" + OpcodeAtomicI32Rmw16XchgUName = "i32.atomic.rmw16.xchg_u" + OpcodeAtomicI64Rmw8XchgUName = "i64.atomic.rmw8.xchg_u" + OpcodeAtomicI64Rmw16XchgUName = "i64.atomic.rmw16.xchg_u" + OpcodeAtomicI64Rmw32XchgUName = "i64.atomic.rmw32.xchg_u" + + OpcodeAtomicI32RmwCmpxchgName = "i32.atomic.rmw.cmpxchg" + OpcodeAtomicI64RmwCmpxchgName = "i64.atomic.rmw.cmpxchg" + OpcodeAtomicI32Rmw8CmpxchgUName = "i32.atomic.rmw8.cmpxchg_u" + OpcodeAtomicI32Rmw16CmpxchgUName = "i32.atomic.rmw16.cmpxchg_u" + OpcodeAtomicI64Rmw8CmpxchgUName = "i64.atomic.rmw8.cmpxchg_u" + OpcodeAtomicI64Rmw16CmpxchgUName = "i64.atomic.rmw16.cmpxchg_u" + OpcodeAtomicI64Rmw32CmpxchgUName = "i64.atomic.rmw32.cmpxchg_u" +) + +var atomicInstructionName = map[OpcodeAtomic]string{ + OpcodeAtomicMemoryNotify: OpcodeAtomicMemoryNotifyName, + OpcodeAtomicMemoryWait32: OpcodeAtomicMemoryWait32Name, + OpcodeAtomicMemoryWait64: OpcodeAtomicMemoryWait64Name, + OpcodeAtomicFence: OpcodeAtomicFenceName, + + OpcodeAtomicI32Load: OpcodeAtomicI32LoadName, + OpcodeAtomicI64Load: OpcodeAtomicI64LoadName, + OpcodeAtomicI32Load8U: OpcodeAtomicI32Load8UName, + OpcodeAtomicI32Load16U: OpcodeAtomicI32Load16UName, + OpcodeAtomicI64Load8U: OpcodeAtomicI64Load8UName, + OpcodeAtomicI64Load16U: OpcodeAtomicI64Load16UName, + OpcodeAtomicI64Load32U: OpcodeAtomicI64Load32UName, + OpcodeAtomicI32Store: OpcodeAtomicI32StoreName, + OpcodeAtomicI64Store: OpcodeAtomicI64StoreName, + OpcodeAtomicI32Store8: OpcodeAtomicI32Store8Name, + OpcodeAtomicI32Store16: OpcodeAtomicI32Store16Name, + OpcodeAtomicI64Store8: OpcodeAtomicI64Store8Name, + OpcodeAtomicI64Store16: OpcodeAtomicI64Store16Name, + OpcodeAtomicI64Store32: OpcodeAtomicI64Store32Name, + + OpcodeAtomicI32RmwAdd: OpcodeAtomicI32RmwAddName, + OpcodeAtomicI64RmwAdd: OpcodeAtomicI64RmwAddName, + OpcodeAtomicI32Rmw8AddU: OpcodeAtomicI32Rmw8AddUName, + OpcodeAtomicI32Rmw16AddU: OpcodeAtomicI32Rmw16AddUName, + OpcodeAtomicI64Rmw8AddU: OpcodeAtomicI64Rmw8AddUName, + OpcodeAtomicI64Rmw16AddU: OpcodeAtomicI64Rmw16AddUName, + OpcodeAtomicI64Rmw32AddU: OpcodeAtomicI64Rmw32AddUName, + + OpcodeAtomicI32RmwSub: OpcodeAtomicI32RmwSubName, + OpcodeAtomicI64RmwSub: OpcodeAtomicI64RmwSubName, + OpcodeAtomicI32Rmw8SubU: OpcodeAtomicI32Rmw8SubUName, + OpcodeAtomicI32Rmw16SubU: OpcodeAtomicI32Rmw16SubUName, + OpcodeAtomicI64Rmw8SubU: OpcodeAtomicI64Rmw8SubUName, + OpcodeAtomicI64Rmw16SubU: OpcodeAtomicI64Rmw16SubUName, + OpcodeAtomicI64Rmw32SubU: OpcodeAtomicI64Rmw32SubUName, + + OpcodeAtomicI32RmwAnd: OpcodeAtomicI32RmwAndName, + OpcodeAtomicI64RmwAnd: OpcodeAtomicI64RmwAndName, + OpcodeAtomicI32Rmw8AndU: OpcodeAtomicI32Rmw8AndUName, + OpcodeAtomicI32Rmw16AndU: OpcodeAtomicI32Rmw16AndUName, + OpcodeAtomicI64Rmw8AndU: OpcodeAtomicI64Rmw8AndUName, + OpcodeAtomicI64Rmw16AndU: OpcodeAtomicI64Rmw16AndUName, + OpcodeAtomicI64Rmw32AndU: OpcodeAtomicI64Rmw32AndUName, + + OpcodeAtomicI32RmwOr: OpcodeAtomicI32RmwOrName, + OpcodeAtomicI64RmwOr: OpcodeAtomicI64RmwOrName, + OpcodeAtomicI32Rmw8OrU: OpcodeAtomicI32Rmw8OrUName, + OpcodeAtomicI32Rmw16OrU: OpcodeAtomicI32Rmw16OrUName, + OpcodeAtomicI64Rmw8OrU: OpcodeAtomicI64Rmw8OrUName, + OpcodeAtomicI64Rmw16OrU: OpcodeAtomicI64Rmw16OrUName, + OpcodeAtomicI64Rmw32OrU: OpcodeAtomicI64Rmw32OrUName, + + OpcodeAtomicI32RmwXor: OpcodeAtomicI32RmwXorName, + OpcodeAtomicI64RmwXor: OpcodeAtomicI64RmwXorName, + OpcodeAtomicI32Rmw8XorU: OpcodeAtomicI32Rmw8XorUName, + OpcodeAtomicI32Rmw16XorU: OpcodeAtomicI32Rmw16XorUName, + OpcodeAtomicI64Rmw8XorU: OpcodeAtomicI64Rmw8XorUName, + OpcodeAtomicI64Rmw16XorU: OpcodeAtomicI64Rmw16XorUName, + OpcodeAtomicI64Rmw32XorU: OpcodeAtomicI64Rmw32XorUName, + + OpcodeAtomicI32RmwXchg: OpcodeAtomicI32RmwXchgName, + OpcodeAtomicI64RmwXchg: OpcodeAtomicI64RmwXchgName, + OpcodeAtomicI32Rmw8XchgU: OpcodeAtomicI32Rmw8XchgUName, + OpcodeAtomicI32Rmw16XchgU: OpcodeAtomicI32Rmw16XchgUName, + OpcodeAtomicI64Rmw8XchgU: OpcodeAtomicI64Rmw8XchgUName, + OpcodeAtomicI64Rmw16XchgU: OpcodeAtomicI64Rmw16XchgUName, + OpcodeAtomicI64Rmw32XchgU: OpcodeAtomicI64Rmw32XchgUName, + + OpcodeAtomicI32RmwCmpxchg: OpcodeAtomicI32RmwCmpxchgName, + OpcodeAtomicI64RmwCmpxchg: OpcodeAtomicI64RmwCmpxchgName, + OpcodeAtomicI32Rmw8CmpxchgU: OpcodeAtomicI32Rmw8CmpxchgUName, + OpcodeAtomicI32Rmw16CmpxchgU: OpcodeAtomicI32Rmw16CmpxchgUName, + OpcodeAtomicI64Rmw8CmpxchgU: OpcodeAtomicI64Rmw8CmpxchgUName, + OpcodeAtomicI64Rmw16CmpxchgU: OpcodeAtomicI64Rmw16CmpxchgUName, + OpcodeAtomicI64Rmw32CmpxchgU: OpcodeAtomicI64Rmw32CmpxchgUName, +} + +// AtomicInstructionName returns the instruction name corresponding to the atomic Opcode. +func AtomicInstructionName(oc OpcodeAtomic) (ret string) { + return atomicInstructionName[oc] +} diff --git a/internal/wasm/memory.go b/internal/wasm/memory.go index e2d994f9cd..12ee8863cb 100644 --- a/internal/wasm/memory.go +++ b/internal/wasm/memory.go @@ -1,11 +1,13 @@ package wasm import ( + "container/list" "encoding/binary" "fmt" "math" "reflect" "sync" + "time" "unsafe" "github.com/tetratelabs/wazero/api" @@ -37,10 +39,16 @@ type MemoryInstance struct { Buffer []byte Min, Cap, Max uint32 - // mux is used to prevent overlapping calls to Grow. - mux sync.RWMutex + Shared bool + // Mux is used to prevent overlapping calls to Grow and implement atomic instructions in interpreter + // mode when Go does not provide atomic APIs to use. + Mux sync.RWMutex // definition is known at compile time. definition api.MemoryDefinition + + // waiters implements atomic wait and notify. It is implemented similarly to golang.org/x/sync/semaphore, + // with a fixed weight of 1 and no spurious notifications. + waiters map[uint32]*list.List } // NewMemoryInstance creates a new instance based on the parameters in the SectionIDMemory. @@ -52,6 +60,7 @@ func NewMemoryInstance(memSec *Memory) *MemoryInstance { Min: memSec.Min, Cap: memSec.Cap, Max: memSec.Max, + Shared: memSec.IsShared, } } @@ -181,8 +190,8 @@ func MemoryPagesToBytesNum(pages uint32) (bytesNum uint64) { // Grow implements the same method as documented on api.Memory. func (m *MemoryInstance) Grow(delta uint32) (result uint32, ok bool) { // We take write-lock here as the following might result in a new slice - m.mux.Lock() - defer m.mux.Unlock() + m.Mux.Lock() + defer m.Mux.Unlock() currentPages := memoryBytesNumToPages(uint64(len(m.Buffer))) if delta == 0 { @@ -284,3 +293,76 @@ func (m *MemoryInstance) writeUint64Le(offset uint32, v uint64) bool { binary.LittleEndian.PutUint64(m.Buffer[offset:], v) return true } + +// Wait suspends the caller until the offset is notified by a different agent. +func (m *MemoryInstance) Wait(offset uint32, timeout int64) (tooMany bool, timedOut bool) { + m.Mux.Lock() + + if m.waiters == nil { + m.waiters = make(map[uint32]*list.List) + } + + waiters := m.waiters[offset] + if waiters == nil { + waiters = list.New() + m.waiters[offset] = waiters + } + + // The specification requires a trap if the number of existing waiters + 1 == 2^32, so we add a check here. + // In practice, it is unlikely the application would ever accumulate such a large number of waiters as it + // indicates several GB of RAM used just for the list of waiters. + // https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#wait + if uint64(waiters.Len()+1) == 1<<32 { + m.Mux.Unlock() + tooMany = true + return + } + + ready := make(chan struct{}) + elem := waiters.PushBack(ready) + m.Mux.Unlock() + + if timeout < 0 { + <-ready + return + } else { + select { + case <-ready: + return + case <-time.After(time.Duration(timeout)): + // While we could see if the channel completed by now and ignore the timeout, similar to x/sync/semaphore, + // the Wasm spec doesn't specify this behavior, so we keep things simple by prioritizing the timeout. + m.Mux.Lock() + if ws := m.waiters[offset]; ws != nil { + ws.Remove(elem) + } + m.Mux.Unlock() + timedOut = true + return + } + } +} + +// Notify wakes up at most count waiters at the given offset. +func (m *MemoryInstance) Notify(offset uint32, count uint32) uint32 { + m.Mux.Lock() + defer m.Mux.Unlock() + + res := uint32(0) + ws := m.waiters[offset] + if ws == nil { + return 0 + } + + for num := ws.Len(); num > 0 && res < count; num = ws.Len() { + w := ws.Remove(ws.Front()).(chan struct{}) + close(w) + res++ + } + + if ws.Len() == 0 { + m.waiters[offset] = nil + } + + return res +} diff --git a/internal/wasm/memory_test.go b/internal/wasm/memory_test.go index 5957741d80..81fc090eb3 100644 --- a/internal/wasm/memory_test.go +++ b/internal/wasm/memory_test.go @@ -5,6 +5,7 @@ import ( "reflect" "strings" "testing" + "time" "unsafe" "github.com/tetratelabs/wazero/api" @@ -797,3 +798,140 @@ func BenchmarkWriteString(b *testing.B) { }) } } + +func TestMemoryInstance_WaitNotifyOnce(t *testing.T) { + t.Run("no waiters", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + notifyWaiters(t, mem, 0, 1, 0) + }) + + t.Run("single wait, notify", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + ch := make(chan string) + // Reuse same offset 3 times to verify reuse + for i := 0; i < 3; i++ { + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + + requireChannelEmpty(t, ch) + notifyWaiters(t, mem, 0, 1, 1) + require.Equal(t, "", <-ch) + + notifyWaiters(t, mem, 0, 1, 0) + } + }) + + t.Run("multiple waiters, notify all", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + ch := make(chan string) + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + + requireChannelEmpty(t, ch) + + notifyWaiters(t, mem, 0, 2, 2) + require.Equal(t, "", <-ch) + require.Equal(t, "", <-ch) + }) + + t.Run("multiple waiters, notify one", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + ch := make(chan string) + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + + requireChannelEmpty(t, ch) + notifyWaiters(t, mem, 0, 1, 1) + require.Equal(t, "", <-ch) + requireChannelEmpty(t, ch) + notifyWaiters(t, mem, 0, 1, 1) + require.Equal(t, "", <-ch) + }) + + t.Run("multiple offsets", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + ch := make(chan string) + go func() { + tooMany, timedOut := mem.Wait(0, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + go func() { + tooMany, timedOut := mem.Wait(1, -1) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + + requireChannelEmpty(t, ch) + notifyWaiters(t, mem, 0, 2, 1) + require.Equal(t, "", <-ch) + requireChannelEmpty(t, ch) + notifyWaiters(t, mem, 1, 2, 1) + require.Equal(t, "", <-ch) + }) + + t.Run("timeout", func(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1, Shared: true} + + ch := make(chan string) + go func() { + tooMany, timedOut := mem.Wait(0, 10 /* ns */) + propagateWaitResult(t, ch, tooMany, timedOut) + }() + + require.Equal(t, "timeout", <-ch) + }) +} + +func notifyWaiters(t *testing.T, mem *MemoryInstance, offset, count, exp int) { + t.Helper() + cur := 0 + tries := 0 + for cur < exp { + if tries > 100 { + t.Fatal("too many tries waiting for wait and notify to converge") + } + n := mem.Notify(uint32(offset), uint32(count)) + cur += int(n) + time.Sleep(1 * time.Millisecond) + tries++ + } +} + +func propagateWaitResult(t *testing.T, ch chan string, tooMany, timedOut bool) { + t.Helper() + if tooMany { + ch <- "too many" + } else if timedOut { + ch <- "timeout" + } else { + ch <- "" + } +} + +func requireChannelEmpty(t *testing.T, ch chan string) { + t.Helper() + select { + case <-ch: + t.Fatal("channel should be empty") + default: + // fallthrough + } +} diff --git a/internal/wasm/module.go b/internal/wasm/module.go index ba2c032ac2..83a7e52b24 100644 --- a/internal/wasm/module.go +++ b/internal/wasm/module.go @@ -750,6 +750,8 @@ type Memory struct { Min, Cap, Max uint32 // IsMaxEncoded true if the Max is encoded in the original binary. IsMaxEncoded bool + // IsShared true if the memory is shared for access from multiple agents. + IsShared bool } // Validate ensures values assigned to Min, Cap and Max are within valid thresholds. diff --git a/internal/wasm/store.go b/internal/wasm/store.go index 264242cc2b..99b9aee44e 100644 --- a/internal/wasm/store.go +++ b/internal/wasm/store.go @@ -123,7 +123,7 @@ type ( // prev and next hold the nodes in the linked list of ModuleInstance held by Store. prev, next *ModuleInstance // aliases holds the module names that are aliases of this module registered in the store. - // Access to this field must be guarded by s.mux. + // Access to this field must be guarded by s.Mux. // // Note: This is currently only used for spectests and will be nil in most cases. aliases []string diff --git a/internal/wasmruntime/errors.go b/internal/wasmruntime/errors.go index 43f951e0df..556e5de829 100644 --- a/internal/wasmruntime/errors.go +++ b/internal/wasmruntime/errors.go @@ -27,6 +27,12 @@ var ( ErrRuntimeInvalidTableAccess = New("invalid table access") // ErrRuntimeIndirectCallTypeMismatch indicates that the type check failed during call_indirect. ErrRuntimeIndirectCallTypeMismatch = New("indirect call type mismatch") + // ErrRuntimeUnalignedAtomic indicates that an atomic operation was made with incorrect memory alignment. + ErrRuntimeUnalignedAtomic = New("unaligned atomic") + // ErrRuntimeExpectedSharedMemory indicates that an operation was made against unshared memory when not allowed. + ErrRuntimeExpectedSharedMemory = New("expected shared memory") + // ErrRuntimeTooManyWaiters indicates that atomic.wait was called with too many waiters. + ErrRuntimeTooManyWaiters = New("too many waiters") ) // Error is returned by a wasm.Engine during the execution of Wasm functions, and they indicate that the Wasm runtime diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go index 47ddc92bfa..85b20139b5 100644 --- a/internal/wazeroir/compiler.go +++ b/internal/wazeroir/compiler.go @@ -379,6 +379,8 @@ func (c *Compiler) handleInstruction() error { instName = wasm.VectorInstructionName(c.body[c.pc+1]) } else if op == wasm.OpcodeMiscPrefix { instName = wasm.MiscInstructionName(c.body[c.pc+1]) + } else if op == wasm.OpcodeAtomicPrefix { + instName = wasm.AtomicInstructionName(c.body[c.pc+1]) } else { instName = wasm.InstructionName(op) } @@ -2848,6 +2850,546 @@ operatorSwitch: default: return fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp)) } + case wasm.OpcodeAtomicPrefix: + c.pc++ + atomicOp := c.body[c.pc] + switch atomicOp { + case wasm.OpcodeAtomicMemoryWait32: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryWait32Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicMemoryWait(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicMemoryWait64: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryWait64Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicMemoryWait(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicMemoryNotify: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicMemoryNotifyName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicMemoryNotify(imm), + ) + case wasm.OpcodeAtomicFence: + // Skip immediate value + c.pc++ + _ = c.body[c.pc] + c.emit( + NewOperationAtomicFence(), + ) + case wasm.OpcodeAtomicI32Load: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32LoadName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64Load: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64LoadName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI32Load8U: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Load8UName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad8(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI32Load16U: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Load16UName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad16(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64Load8U: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load8UName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad8(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Load16U: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load16UName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad16(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Load32U: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Load32UName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicLoad(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI32Store: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32StoreName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI32Store8: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Store8Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore8(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI32Store16: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Store16Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore16(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64Store: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64StoreName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Store8: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store8Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore8(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Store16: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store16Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore16(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Store32: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Store32Name) + if err != nil { + return err + } + c.emit( + NewOperationAtomicStore(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI32RmwAdd: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwAddName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI64RmwAdd: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwAddName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI32Rmw8AddU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8AddUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI64Rmw8AddU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8AddUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI32Rmw16AddU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16AddUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI64Rmw16AddU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16AddUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI64Rmw32AddU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32AddUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAdd), + ) + case wasm.OpcodeAtomicI32RmwSub: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwSubName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI64RmwSub: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwSubName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI32Rmw8SubU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8SubUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI64Rmw8SubU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8SubUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI32Rmw16SubU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16SubUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI64Rmw16SubU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16SubUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI64Rmw32SubU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32SubUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpSub), + ) + case wasm.OpcodeAtomicI32RmwAnd: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwAndName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI64RmwAnd: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwAndName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI32Rmw8AndU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8AndUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI64Rmw8AndU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8AndUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI32Rmw16AndU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16AndUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI64Rmw16AndU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16AndUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI64Rmw32AndU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32AndUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpAnd), + ) + case wasm.OpcodeAtomicI32RmwOr: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwOrName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI64RmwOr: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwOrName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI32Rmw8OrU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8OrUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI64Rmw8OrU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8OrUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI32Rmw16OrU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16OrUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI64Rmw16OrU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16OrUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI64Rmw32OrU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32OrUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpOr), + ) + case wasm.OpcodeAtomicI32RmwXor: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwXorName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI64RmwXor: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwXorName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI32Rmw8XorU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8XorUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI64Rmw8XorU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8XorUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI32Rmw16XorU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16XorUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI64Rmw16XorU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16XorUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI64Rmw32XorU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32XorUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpXor), + ) + case wasm.OpcodeAtomicI32RmwXchg: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwXchgName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI64RmwXchg: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwXchgName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI64, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI32Rmw8XchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8XchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI32, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI64Rmw8XchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8XchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8(UnsignedTypeI64, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI32Rmw16XchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16XchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI32, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI64Rmw16XchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16XchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16(UnsignedTypeI64, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI64Rmw32XchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32XchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW(UnsignedTypeI32, imm, AtomicArithmeticOpNop), + ) + case wasm.OpcodeAtomicI32RmwCmpxchg: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32RmwCmpxchgName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64RmwCmpxchg: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64RmwCmpxchgName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMWCmpxchg(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI32Rmw8CmpxchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw8CmpxchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64Rmw8CmpxchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw8CmpxchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI32Rmw16CmpxchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI32Rmw16CmpxchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI32, imm), + ) + case wasm.OpcodeAtomicI64Rmw16CmpxchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw16CmpxchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI64, imm), + ) + case wasm.OpcodeAtomicI64Rmw32CmpxchgU: + imm, err := c.readMemoryArg(wasm.OpcodeAtomicI64Rmw32CmpxchgUName) + if err != nil { + return err + } + c.emit( + NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, imm), + ) + } default: return fmt.Errorf("unsupported instruction in wazeroir: 0x%x", op) } diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go index b167bbf062..2520e56697 100644 --- a/internal/wazeroir/compiler_test.go +++ b/internal/wazeroir/compiler_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/internal/leb128" "github.com/tetratelabs/wazero/internal/testing/require" "github.com/tetratelabs/wazero/internal/wasm" @@ -3071,3 +3072,651 @@ func Test_ensureTermination(t *testing.T) { }) } } + +func TestCompiler_threads(t *testing.T) { + tests := []struct { + name string + body []byte + noDropBeforeReturn bool + expected UnionOperation + }{ + { + name: "i32.atomic.load8_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicLoad8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i32.atomic.load16_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load16U, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicLoad16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i32.atomic.load", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicLoad(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.load8_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicLoad8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i64.atomic.load16_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load16U, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicLoad16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i64.atomic.load32_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load32U, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicLoad(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.load", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicLoad(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}), + }, + { + name: "i32.atomic.store8", + body: []byte{ + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i32.atomic.store16", + body: []byte{ + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store16, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i32.atomic.store", + body: []byte{ + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.store8", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i64.atomic.store16", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store16, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i64.atomic.store32", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store32, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.store", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicStore(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}), + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AddU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i32.atomic.rmw.add", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAdd, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AddU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AddU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i64.atomic.rmw.add", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAdd, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpAdd), + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16SubU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwSub, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16SubU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32SubU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwSub, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpSub), + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AndU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i32.atomic.rmw.and", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAnd, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AndU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AndU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i64.atomic.rmw.and", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAnd, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpAnd), + }, + { + name: "i32.atomic.rmw8.or", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16OrU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i32.atomic.rmw.or", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwOr, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16OrU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32OrU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i64.atomic.rmw.or", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwOr, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpOr), + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XorU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXor, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XorU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XorU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXor, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpXor), + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXchg, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XchgU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXchg, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMW(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}, AtomicArithmeticOpNop), + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI32Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwCmpxchg, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeI64Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + expected: NewOperationAtomicRMW8Cmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x1, Offset: 0x8}), + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeI64Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^(2-1), offset=8 + }, + expected: NewOperationAtomicRMW16Cmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x2, Offset: 0x8}), + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32CmpxchgU, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeI64Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwCmpxchg, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicRMWCmpxchg(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}), + }, + { + name: "memory.atomic.wait32", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeI64Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait32, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicMemoryWait(UnsignedTypeI32, MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "memory.atomic.wait64", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI64Const, 0x1, + wasm.OpcodeI64Const, 0x2, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait64, 0x4, 0x8, // alignment=2^(4-1), offset=8 + }, + expected: NewOperationAtomicMemoryWait(UnsignedTypeI64, MemoryArg{Alignment: 0x4, Offset: 0x8}), + }, + { + name: "memory.atomic.notify", + body: []byte{ + wasm.OpcodeI32Const, 0x0, + wasm.OpcodeI32Const, 0x1, + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryNotify, 0x3, 0x8, // alignment=2^(3-1), offset=8 + }, + expected: NewOperationAtomicMemoryNotify(MemoryArg{Alignment: 0x3, Offset: 0x8}), + }, + { + name: "memory.atomic.fence", + body: []byte{ + wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicFence, 0x0, // consistency=0 + }, + noDropBeforeReturn: true, + expected: NewOperationAtomicFence(), + }, + } + + for _, tc := range tests { + tt := tc + t.Run(tt.name, func(t *testing.T) { + body := append([]byte{}, tt.body...) + if !tt.noDropBeforeReturn { + body = append(body, wasm.OpcodeDrop) + } + body = append(body, wasm.OpcodeEnd) + module := &wasm.Module{ + TypeSection: []wasm.FunctionType{v_v}, + FunctionSection: []wasm.Index{0}, + MemorySection: &wasm.Memory{}, + CodeSection: []wasm.Code{{Body: body}}, + } + c, err := NewCompiler(api.CoreFeaturesV2|experimental.CoreFeaturesThreads, 0, module, false) + require.NoError(t, err) + + res, err := c.Next() + require.NoError(t, err) + if tt.noDropBeforeReturn { + require.Equal(t, tc.expected, res.Operations[len(res.Operations)-2]) + } else { + require.Equal(t, tc.expected, res.Operations[len(res.Operations)-3]) + } + }) + } +} diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go index 4041944a48..684521bf69 100644 --- a/internal/wazeroir/operations.go +++ b/internal/wazeroir/operations.go @@ -415,6 +415,36 @@ func (o OperationKind) String() (ret string) { ret = "V128ITruncSatFromF" case OperationKindBuiltinFunctionCheckExitCode: ret = "BuiltinFunctionCheckExitCode" + case OperationKindAtomicMemoryWait: + ret = "OperationKindAtomicMemoryWait" + case OperationKindAtomicMemoryNotify: + ret = "OperationKindAtomicMemoryNotify" + case OperationKindAtomicFence: + ret = "OperationKindAtomicFence" + case OperationKindAtomicLoad: + ret = "OperationKindAtomicLoad" + case OperationKindAtomicLoad8: + ret = "OperationKindAtomicLoad8" + case OperationKindAtomicLoad16: + ret = "OperationKindAtomicLoad16" + case OperationKindAtomicStore: + ret = "OperationKindAtomicStore" + case OperationKindAtomicStore8: + ret = "OperationKindAtomicStore8" + case OperationKindAtomicStore16: + ret = "OperationKindAtomicStore16" + case OperationKindAtomicRMW: + ret = "OperationKindAtomicRMW" + case OperationKindAtomicRMW8: + ret = "OperationKindAtomicRMW8" + case OperationKindAtomicRMW16: + ret = "OperationKindAtomicRMW16" + case OperationKindAtomicRMWCmpxchg: + ret = "OperationKindAtomicRMWCmpxchg" + case OperationKindAtomicRMW8Cmpxchg: + ret = "OperationKindAtomicRMW8Cmpxchg" + case OperationKindAtomicRMW16Cmpxchg: + ret = "OperationKindAtomicRMW16Cmpxchg" default: panic(fmt.Errorf("unknown operation %d", o)) } @@ -705,10 +735,61 @@ const ( // OperationKindBuiltinFunctionCheckExitCode is the Kind for NewOperationBuiltinFunctionCheckExitCode. OperationKindBuiltinFunctionCheckExitCode + // OperationKindAtomicMemoryWait is the kind for NewOperationAtomicMemoryWait. + OperationKindAtomicMemoryWait + // OperationKindAtomicMemoryNotify is the kind for NewOperationAtomicMemoryNotify. + OperationKindAtomicMemoryNotify + // OperationKindAtomicFence is the kind for NewOperationAtomicFence. + OperationKindAtomicFence + // OperationKindAtomicLoad is the kind for NewOperationAtomicLoad. + OperationKindAtomicLoad + // OperationKindAtomicLoad8 is the kind for NewOperationAtomicLoad8. + OperationKindAtomicLoad8 + // OperationKindAtomicLoad16 is the kind for NewOperationAtomicLoad16. + OperationKindAtomicLoad16 + // OperationKindAtomicStore is the kind for NewOperationAtomicStore. + OperationKindAtomicStore + // OperationKindAtomicStore8 is the kind for NewOperationAtomicStore8. + OperationKindAtomicStore8 + // OperationKindAtomicStore16 is the kind for NewOperationAtomicStore16. + OperationKindAtomicStore16 + + // OperationKindAtomicRMW is the kind for NewOperationAtomicRMW. + OperationKindAtomicRMW + // OperationKindAtomicRMW8 is the kind for NewOperationAtomicRMW8. + OperationKindAtomicRMW8 + // OperationKindAtomicRMW16 is the kind for NewOperationAtomicRMW16. + OperationKindAtomicRMW16 + + // OperationKindAtomicRMWCmpxchg is the kind for NewOperationAtomicRMWCmpxchg. + OperationKindAtomicRMWCmpxchg + // OperationKindAtomicRMW8Cmpxchg is the kind for NewOperationAtomicRMW8Cmpxchg. + OperationKindAtomicRMW8Cmpxchg + // OperationKindAtomicRMW16Cmpxchg is the kind for NewOperationAtomicRMW16Cmpxchg. + OperationKindAtomicRMW16Cmpxchg + // operationKindEnd is always placed at the bottom of this iota definition to be used in the test. operationKindEnd ) +// AtomicArithmeticOp is the type for the operation kind of atomic arithmetic operations. +type AtomicArithmeticOp byte + +const ( + // AtomicArithmeticOpAdd is the kind for an add operation. + AtomicArithmeticOpAdd AtomicArithmeticOp = iota + // AtomicArithmeticOpSub is the kind for a sub operation. + AtomicArithmeticOpSub + // AtomicArithmeticOpAnd is the kind for a bitwise and operation. + AtomicArithmeticOpAnd + // AtomicArithmeticOpOr is the kind for a bitwise or operation. + AtomicArithmeticOpOr + // AtomicArithmeticOpXor is the kind for a bitwise xor operation. + AtomicArithmeticOpXor + // AtomicArithmeticOpNop is the kind for a nop operation. + AtomicArithmeticOpNop +) + // NewOperationBuiltinFunctionCheckExitCode is a constructor for UnionOperation with Kind OperationKindBuiltinFunctionCheckExitCode. // // OperationBuiltinFunctionCheckExitCode corresponds to the instruction to check the api.Module is already closed due to @@ -1017,6 +1098,23 @@ func (o UnionOperation) String() string { return fmt.Sprintf("%s.%sU", o.Kind, shapeName(o.B1)) } + case OperationKindAtomicMemoryWait, + OperationKindAtomicMemoryNotify, + OperationKindAtomicFence, + OperationKindAtomicLoad, + OperationKindAtomicLoad8, + OperationKindAtomicLoad16, + OperationKindAtomicStore, + OperationKindAtomicStore8, + OperationKindAtomicStore16, + OperationKindAtomicRMW, + OperationKindAtomicRMW8, + OperationKindAtomicRMW16, + OperationKindAtomicRMWCmpxchg, + OperationKindAtomicRMW8Cmpxchg, + OperationKindAtomicRMW16Cmpxchg: + return o.Kind.String() + default: panic(fmt.Sprintf("TODO: %v", o.Kind)) } @@ -2565,3 +2663,150 @@ func NewOperationV128Narrow(originShape Shape, signed bool) UnionOperation { func NewOperationV128ITruncSatFromF(originShape Shape, signed bool) UnionOperation { return UnionOperation{Kind: OperationKindV128ITruncSatFromF, B1: originShape, B3: signed} } + +// NewOperationAtomicMemoryWait is a constructor for UnionOperation with OperationKindAtomicMemoryWait. +// +// This corresponds to +// +// wasm.OpcodeAtomicWait32Name wasm.OpcodeAtomicWait64Name +func NewOperationAtomicMemoryWait(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicMemoryWait, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicMemoryNotify is a constructor for UnionOperation with OperationKindAtomicMemoryNotify. +// +// This corresponds to +// +// wasm.OpcodeAtomicNotifyName +func NewOperationAtomicMemoryNotify(arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicMemoryNotify, U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicFence is a constructor for UnionOperation with OperationKindAtomicFence. +// +// This corresponds to +// +// wasm.OpcodeAtomicFenceName +func NewOperationAtomicFence() UnionOperation { + return UnionOperation{Kind: OperationKindAtomicFence} +} + +// NewOperationAtomicLoad is a constructor for UnionOperation with OperationKindAtomicLoad. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32LoadName wasm.OpcodeAtomicI64LoadName +func NewOperationAtomicLoad(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicLoad, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicLoad8 is a constructor for UnionOperation with OperationKindAtomicLoad8. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32Load8UName wasm.OpcodeAtomicI64Load8UName +func NewOperationAtomicLoad8(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicLoad8, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicLoad16 is a constructor for UnionOperation with OperationKindAtomicLoad16. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32Load16UName wasm.OpcodeAtomicI64Load16UName +func NewOperationAtomicLoad16(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicLoad16, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicStore is a constructor for UnionOperation with OperationKindAtomicStore. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32StoreName wasm.OpcodeAtomicI64StoreName +func NewOperationAtomicStore(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicStore, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicStore8 is a constructor for UnionOperation with OperationKindAtomicStore8. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32Store8UName wasm.OpcodeAtomicI64Store8UName +func NewOperationAtomicStore8(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicStore8, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicStore16 is a constructor for UnionOperation with OperationKindAtomicStore16. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32Store16UName wasm.OpcodeAtomicI64Store16UName +func NewOperationAtomicStore16(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicStore16, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMW is a constructor for UnionOperation with OperationKindAtomicRMW. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMWAddName wasm.OpcodeAtomicI64RmwAddName +// wasm.OpcodeAtomicI32RMWSubName wasm.OpcodeAtomicI64RmwSubName +// wasm.OpcodeAtomicI32RMWAndName wasm.OpcodeAtomicI64RmwAndName +// wasm.OpcodeAtomicI32RMWOrName wasm.OpcodeAtomicI64RmwOrName +// wasm.OpcodeAtomicI32RMWXorName wasm.OpcodeAtomicI64RmwXorName +func NewOperationAtomicRMW(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMW, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMW8 is a constructor for UnionOperation with OperationKindAtomicRMW8. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMW8AddUName wasm.OpcodeAtomicI64Rmw8AddUName +// wasm.OpcodeAtomicI32RMW8SubUName wasm.OpcodeAtomicI64Rmw8SubUName +// wasm.OpcodeAtomicI32RMW8AndUName wasm.OpcodeAtomicI64Rmw8AndUName +// wasm.OpcodeAtomicI32RMW8OrUName wasm.OpcodeAtomicI64Rmw8OrUName +// wasm.OpcodeAtomicI32RMW8XorUName wasm.OpcodeAtomicI64Rmw8XorUName +func NewOperationAtomicRMW8(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMW8, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMW16 is a constructor for UnionOperation with OperationKindAtomicRMW16. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMW16AddUName wasm.OpcodeAtomicI64Rmw16AddUName +// wasm.OpcodeAtomicI32RMW16SubUName wasm.OpcodeAtomicI64Rmw16SubUName +// wasm.OpcodeAtomicI32RMW16AndUName wasm.OpcodeAtomicI64Rmw16AndUName +// wasm.OpcodeAtomicI32RMW16OrUName wasm.OpcodeAtomicI64Rmw16OrUName +// wasm.OpcodeAtomicI32RMW16XorUName wasm.OpcodeAtomicI64Rmw16XorUName +func NewOperationAtomicRMW16(unsignedType UnsignedType, arg MemoryArg, op AtomicArithmeticOp) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMW16, B1: byte(unsignedType), B2: byte(op), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMWCmpxchg is a constructor for UnionOperation with OperationKindAtomicRMWCmpxchg. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMWCmpxchgName wasm.OpcodeAtomicI64RmwCmpxchgName +func NewOperationAtomicRMWCmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMWCmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMW8Cmpxchg is a constructor for UnionOperation with OperationKindAtomicRMW8Cmpxchg. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMW8CmpxchgUName wasm.OpcodeAtomicI64Rmw8CmpxchgUName +func NewOperationAtomicRMW8Cmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMW8Cmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} + +// NewOperationAtomicRMW16Cmpxchg is a constructor for UnionOperation with OperationKindAtomicRMW16Cmpxchg. +// +// This corresponds to +// +// wasm.OpcodeAtomicI32RMW16CmpxchgUName wasm.OpcodeAtomicI64Rmw16CmpxchgUName +func NewOperationAtomicRMW16Cmpxchg(unsignedType UnsignedType, arg MemoryArg) UnionOperation { + return UnionOperation{Kind: OperationKindAtomicRMW16Cmpxchg, B1: byte(unsignedType), U1: uint64(arg.Alignment), U2: uint64(arg.Offset)} +} diff --git a/internal/wazeroir/signature.go b/internal/wazeroir/signature.go index 6d0f84447c..30a5868df6 100644 --- a/internal/wazeroir/signature.go +++ b/internal/wazeroir/signature.go @@ -233,6 +233,26 @@ var ( in: []UnsignedType{UnsignedTypeF64}, out: []UnsignedType{UnsignedTypeV128}, } + signature_I32I64_I64 = &signature{ + in: []UnsignedType{UnsignedTypeI32, UnsignedTypeI64}, + out: []UnsignedType{UnsignedTypeI64}, + } + signature_I32I32I64_I32 = &signature{ + in: []UnsignedType{UnsignedTypeI32, UnsignedTypeI32, UnsignedTypeI64}, + out: []UnsignedType{UnsignedTypeI32}, + } + signature_I32I64I64_I32 = &signature{ + in: []UnsignedType{UnsignedTypeI32, UnsignedTypeI64, UnsignedTypeI64}, + out: []UnsignedType{UnsignedTypeI32}, + } + signature_I32I32I32_I32 = &signature{ + in: []UnsignedType{UnsignedTypeI32, UnsignedTypeI32, UnsignedTypeI32}, + out: []UnsignedType{UnsignedTypeI32}, + } + signature_I32I64I64_I64 = &signature{ + in: []UnsignedType{UnsignedTypeI32, UnsignedTypeI64, UnsignedTypeI64}, + out: []UnsignedType{UnsignedTypeI64}, + } ) // wasmOpcodeSignature returns the signature of given Wasm opcode. @@ -586,6 +606,40 @@ func (c *Compiler) wasmOpcodeSignature(op wasm.Opcode, index uint32) (*signature default: return nil, fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp)) } + case wasm.OpcodeAtomicPrefix: + switch atomicOp := c.body[c.pc+1]; atomicOp { + case wasm.OpcodeAtomicMemoryNotify: + return signature_I32I32_I32, nil + case wasm.OpcodeAtomicMemoryWait32: + return signature_I32I32I64_I32, nil + case wasm.OpcodeAtomicMemoryWait64: + return signature_I32I64I64_I32, nil + case wasm.OpcodeAtomicFence: + return signature_None_None, nil + case wasm.OpcodeAtomicI32Load, wasm.OpcodeAtomicI32Load8U, wasm.OpcodeAtomicI32Load16U: + return signature_I32_I32, nil + case wasm.OpcodeAtomicI64Load, wasm.OpcodeAtomicI64Load8U, wasm.OpcodeAtomicI64Load16U, wasm.OpcodeAtomicI64Load32U: + return signature_I32_I64, nil + case wasm.OpcodeAtomicI32Store, wasm.OpcodeAtomicI32Store8, wasm.OpcodeAtomicI32Store16: + return signature_I32I32_None, nil + case wasm.OpcodeAtomicI64Store, wasm.OpcodeAtomicI64Store8, wasm.OpcodeAtomicI64Store16, wasm.OpcodeAtomicI64Store32: + return signature_I32I64_None, nil + case wasm.OpcodeAtomicI32RmwAdd, wasm.OpcodeAtomicI32RmwSub, wasm.OpcodeAtomicI32RmwAnd, wasm.OpcodeAtomicI32RmwOr, wasm.OpcodeAtomicI32RmwXor, wasm.OpcodeAtomicI32RmwXchg, + wasm.OpcodeAtomicI32Rmw8AddU, wasm.OpcodeAtomicI32Rmw8SubU, wasm.OpcodeAtomicI32Rmw8AndU, wasm.OpcodeAtomicI32Rmw8OrU, wasm.OpcodeAtomicI32Rmw8XorU, wasm.OpcodeAtomicI32Rmw8XchgU, + wasm.OpcodeAtomicI32Rmw16AddU, wasm.OpcodeAtomicI32Rmw16SubU, wasm.OpcodeAtomicI32Rmw16AndU, wasm.OpcodeAtomicI32Rmw16OrU, wasm.OpcodeAtomicI32Rmw16XorU, wasm.OpcodeAtomicI32Rmw16XchgU: + return signature_I32I32_I32, nil + case wasm.OpcodeAtomicI64RmwAdd, wasm.OpcodeAtomicI64RmwSub, wasm.OpcodeAtomicI64RmwAnd, wasm.OpcodeAtomicI64RmwOr, wasm.OpcodeAtomicI64RmwXor, wasm.OpcodeAtomicI64RmwXchg, + wasm.OpcodeAtomicI64Rmw8AddU, wasm.OpcodeAtomicI64Rmw8SubU, wasm.OpcodeAtomicI64Rmw8AndU, wasm.OpcodeAtomicI64Rmw8OrU, wasm.OpcodeAtomicI64Rmw8XorU, wasm.OpcodeAtomicI64Rmw8XchgU, + wasm.OpcodeAtomicI64Rmw16AddU, wasm.OpcodeAtomicI64Rmw16SubU, wasm.OpcodeAtomicI64Rmw16AndU, wasm.OpcodeAtomicI64Rmw16OrU, wasm.OpcodeAtomicI64Rmw16XorU, wasm.OpcodeAtomicI64Rmw16XchgU, + wasm.OpcodeAtomicI64Rmw32AddU, wasm.OpcodeAtomicI64Rmw32SubU, wasm.OpcodeAtomicI64Rmw32AndU, wasm.OpcodeAtomicI64Rmw32OrU, wasm.OpcodeAtomicI64Rmw32XorU, wasm.OpcodeAtomicI64Rmw32XchgU: + return signature_I32I64_I64, nil + case wasm.OpcodeAtomicI32RmwCmpxchg, wasm.OpcodeAtomicI32Rmw8CmpxchgU, wasm.OpcodeAtomicI32Rmw16CmpxchgU: + return signature_I32I32I32_I32, nil + case wasm.OpcodeAtomicI64RmwCmpxchg, wasm.OpcodeAtomicI64Rmw8CmpxchgU, wasm.OpcodeAtomicI64Rmw16CmpxchgU, wasm.OpcodeAtomicI64Rmw32CmpxchgU: + return signature_I32I64I64_I64, nil + default: + return nil, fmt.Errorf("unsupported atomic instruction in wazeroir: %s", wasm.AtomicInstructionName(atomicOp)) + } default: return nil, fmt.Errorf("unsupported instruction in wazeroir: 0x%x", op) } diff --git a/internal/wazeroir/signature_test.go b/internal/wazeroir/signature_test.go index 3969de7c42..518678366f 100644 --- a/internal/wazeroir/signature_test.go +++ b/internal/wazeroir/signature_test.go @@ -88,6 +88,341 @@ func TestCompiler_wasmOpcodeSignature(t *testing.T) { body: []byte{wasm.OpcodeMiscPrefix, wasm.OpcodeMiscTableCopy}, exp: signature_I32I32I32_None, }, + { + name: "i32.atomic.load8_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load8U}, + exp: signature_I32_I32, + }, + { + name: "i32.atomic.load16_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load16U}, + exp: signature_I32_I32, + }, + { + name: "i32.atomic.load", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Load}, + exp: signature_I32_I32, + }, + { + name: "i64.atomic.load8_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load8U}, + exp: signature_I32_I64, + }, + { + name: "i64.atomic.load16_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load16U}, + exp: signature_I32_I64, + }, + { + name: "i64.atomic.load32_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load32U}, + exp: signature_I32_I64, + }, + { + name: "i64.atomic.load", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Load}, + exp: signature_I32_I64, + }, + { + name: "i32.atomic.store8", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store8}, + exp: signature_I32I32_None, + }, + { + name: "i32.atomic.store16_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store16}, + exp: signature_I32I32_None, + }, + { + name: "i32.atomic.store", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Store}, + exp: signature_I32I32_None, + }, + { + name: "i64.atomic.store8", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store8}, + exp: signature_I32I64_None, + }, + { + name: "i64.atomic.store16", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store16}, + exp: signature_I32I64_None, + }, + { + name: "i64.atomic.store32", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store32}, + exp: signature_I32I64_None, + }, + { + name: "i64.atomic.store", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Store}, + exp: signature_I32I64_None, + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AddU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AddU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.add", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAdd}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AddU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AddU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AddU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.add", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAdd}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8SubU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16SubU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwSub}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8SubU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16SubU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32SubU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwSub}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8AndU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16AndU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.and", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwAnd}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8AndU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16AndU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32AndU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.and", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwAnd}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.or_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8OrU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16OrU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.or", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwOr}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8OrU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16OrU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32OrU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.or", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwOr}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XorU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XorU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXor}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XorU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XorU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XorU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXor}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8XchgU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16XchgU}, + exp: signature_I32I32_I32, + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwXchg}, + exp: signature_I32I32_I32, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8XchgU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16XchgU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32XchgU}, + exp: signature_I32I64_I64, + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwXchg}, + exp: signature_I32I64_I64, + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw8CmpxchgU}, + exp: signature_I32I32I32_I32, + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32Rmw16CmpxchgU}, + exp: signature_I32I32I32_I32, + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI32RmwCmpxchg}, + exp: signature_I32I32I32_I32, + }, + { + name: "i64.atomic.rmw8.cmpxchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw8CmpxchgU}, + exp: signature_I32I64I64_I64, + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw16CmpxchgU}, + exp: signature_I32I64I64_I64, + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64Rmw32CmpxchgU}, + exp: signature_I32I64I64_I64, + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicI64RmwCmpxchg}, + exp: signature_I32I64I64_I64, + }, + { + name: "memory.atomic.wait32", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait32}, + exp: signature_I32I32I64_I32, + }, + { + name: "memory.atomic.wait64", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryWait64}, + exp: signature_I32I64I64_I32, + }, + { + name: "memory.atomic.notify", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicMemoryNotify}, + exp: signature_I32I32_I32, + }, + { + name: "memory.atomic.fence", + body: []byte{wasm.OpcodeAtomicPrefix, wasm.OpcodeAtomicFence}, + exp: signature_None_None, + }, } for _, tt := range tests {