From a7c95e43524df46e0080ee61e7f30d463f8e5400 Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Fri, 16 Jan 2026 23:05:23 +0000 Subject: [PATCH 1/2] Add parsing/printing for relaxed atomics in RMW --- src/binaryen-c.cpp | 18 +-- src/ir/properties.h | 7 +- src/parser/contexts.h | 10 +- src/parser/parsers.h | 14 ++- src/passes/Print.cpp | 1 + src/tools/fuzzing/fuzzing.cpp | 3 +- src/wasm-builder.h | 6 +- src/wasm-delegations-fields.def | 1 + src/wasm-ir-builder.h | 8 +- src/wasm.h | 1 + src/wasm/wasm-binary.cpp | 21 ++-- src/wasm/wasm-ir-builder.cpp | 12 +- src/wasm/wasm-stack.cpp | 2 +- src/wasm/wasm-validator.cpp | 50 +++++++-- test/spec/relaxed-atomics.wast | 191 +++++++++++--------------------- 15 files changed, 177 insertions(+), 168 deletions(-) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index e38e60c24ed..50d83a5b1fc 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -1382,15 +1382,15 @@ BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, BinaryenExpressionRef value, BinaryenType type, const char* memoryName) { - return static_cast( - Builder(*(Module*)module) - .makeAtomicRMW(AtomicRMWOp(op), - bytes, - offset, - (Expression*)ptr, - (Expression*)value, - Type(type), - getMemoryName(module, memoryName))); + return Builder(*(Module*)module) + .makeAtomicRMW(AtomicRMWOp(op), + bytes, + offset, + (Expression*)ptr, + (Expression*)value, + Type(type), + getMemoryName(module, memoryName), + MemoryOrder::SeqCst); } BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, BinaryenIndex bytes, diff --git a/src/ir/properties.h b/src/ir/properties.h index e1865bea562..e28a519e540 100644 --- a/src/ir/properties.h +++ b/src/ir/properties.h @@ -507,8 +507,11 @@ inline MemoryOrder getMemoryOrder(Expression* curr) { if (auto* store = curr->dynCast()) { return store->order; } - if (curr->is() || curr->is() || - curr->is() || curr->is()) { + if (auto* rmw = curr->dynCast()) { + return rmw->order; + } + if (curr->is() || curr->is() || + curr->is()) { return MemoryOrder::SeqCst; } return MemoryOrder::Unordered; diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 1ac3a7c525f..82083ca82ed 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -575,7 +575,8 @@ struct NullInstrParserCtx { Type, int, MemoryIdxT*, - MemargT) { + MemargT, + MemoryOrder) { return Ok{}; } Result<> makeAtomicCmpxchg( @@ -2274,11 +2275,12 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Type type, int bytes, Name* mem, - Memarg memarg) { + Memarg memarg, + MemoryOrder order) { auto m = getMemory(pos, mem); CHECK_ERR(m); - return withLoc(pos, - irBuilder.makeAtomicRMW(op, bytes, memarg.offset, type, *m)); + return withLoc( + pos, irBuilder.makeAtomicRMW(op, bytes, memarg.offset, type, *m, order)); } Result<> makeAtomicCmpxchg(Index pos, diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 77122643d40..b1eb6c71b9d 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -1815,10 +1815,20 @@ Result<> makeAtomicRMW(Ctx& ctx, uint8_t bytes) { auto mem = maybeMemidx(ctx); CHECK_ERR(mem); + + auto maybeOrder = maybeMemOrder(ctx); + CHECK_ERR(maybeOrder); + auto arg = memarg(ctx, bytes); CHECK_ERR(arg); - return ctx.makeAtomicRMW( - pos, annotations, op, type, bytes, mem.getPtr(), *arg); + return ctx.makeAtomicRMW(pos, + annotations, + op, + type, + bytes, + mem.getPtr(), + *arg, + maybeOrder ? *maybeOrder : MemoryOrder::SeqCst); } template diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index e54d3eebdff..6eba429fa44 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -661,6 +661,7 @@ struct PrintExpressionContents } restoreNormalColor(o); printMemoryName(curr->memory, o, wasm); + printMemoryOrder(curr->order); if (curr->offset) { o << " offset=" << curr->offset; } diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 22e48ef4ac3..1aeaf9c8576 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -4770,7 +4770,8 @@ Expression* TranslateToFuzzReader::makeAtomic(Type type) { ptr, value, type, - wasm.memories[0]->name); + wasm.memories[0]->name, + MemoryOrder::SeqCst); } else { auto* expected = make(type); auto* replacement = make(type); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 104b8385ed9..0d09fa661a1 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -466,7 +466,8 @@ class Builder { Expression* ptr, Expression* value, Type type, - Name memory) { + Name memory, + MemoryOrder order) { auto* ret = wasm.allocator.alloc(); ret->op = op; ret->bytes = bytes; @@ -474,8 +475,9 @@ class Builder { ret->ptr = ptr; ret->value = value; ret->type = type; - ret->finalize(); ret->memory = memory; + ret->order = order; + ret->finalize(); return ret; } AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 8cb5df64838..143528b769e 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -375,6 +375,7 @@ DELEGATE_FIELD_CHILD(AtomicRMW, ptr) DELEGATE_FIELD_INT(AtomicRMW, op) DELEGATE_FIELD_INT(AtomicRMW, bytes) DELEGATE_FIELD_ADDRESS(AtomicRMW, offset) +DELEGATE_FIELD_INT(AtomicRMW, order) DELEGATE_FIELD_NAME_KIND(AtomicRMW, memory, ModuleItemKind::Memory) DELEGATE_FIELD_CASE_END(AtomicRMW) diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index bbc21afb083..5f06ddd9b39 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -160,8 +160,12 @@ class IRBuilder : public UnifiedExpressionVisitor> { unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order); Result<> makeAtomicStore( unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order); - Result<> makeAtomicRMW( - AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem); + Result<> makeAtomicRMW(AtomicRMWOp op, + unsigned bytes, + Address offset, + Type type, + Name mem, + MemoryOrder order); Result<> makeAtomicCmpxchg(unsigned bytes, Address offset, Type type, Name mem); Result<> makeAtomicWait(Type type, Address offset, Name mem); diff --git a/src/wasm.h b/src/wasm.h index ab6323b5f35..07f015c0172 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1035,6 +1035,7 @@ class AtomicRMW : public SpecificExpression { Expression* ptr; Expression* value; Name memory; + MemoryOrder order = MemoryOrder::SeqCst; void finalize(); }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 96fd205f908..1aa002f4e03 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3704,31 +3704,38 @@ Result<> WasmBinaryReader::readInst() { #define RMW(op) \ case BinaryConsts::I32AtomicRMW##op: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 4, offset, Type::i32, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 4, offset, Type::i32, mem, memoryOrder); \ } \ case BinaryConsts::I32AtomicRMW##op##8U: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 1, offset, Type::i32, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 1, offset, Type::i32, mem, memoryOrder); \ } \ case BinaryConsts::I32AtomicRMW##op##16U: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 2, offset, Type::i32, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 2, offset, Type::i32, mem, memoryOrder); \ } \ case BinaryConsts::I64AtomicRMW##op: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 8, offset, Type::i64, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 8, offset, Type::i64, mem, memoryOrder); \ } \ case BinaryConsts::I64AtomicRMW##op##8U: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 1, offset, Type::i64, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 1, offset, Type::i64, mem, memoryOrder); \ } \ case BinaryConsts::I64AtomicRMW##op##16U: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 2, offset, Type::i64, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 2, offset, Type::i64, mem, memoryOrder); \ } \ case BinaryConsts::I64AtomicRMW##op##32U: { \ auto [mem, align, offset, memoryOrder] = getRMWMemarg(); \ - return builder.makeAtomicRMW(RMW##op, 4, offset, Type::i64, mem); \ + return builder.makeAtomicRMW( \ + RMW##op, 4, offset, Type::i64, mem, memoryOrder); \ } RMW(Add); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 79e3b563e0a..0144c8d1ea6 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1503,14 +1503,18 @@ Result<> IRBuilder::makeAtomicStore( return Ok{}; } -Result<> IRBuilder::makeAtomicRMW( - AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem) { +Result<> IRBuilder::makeAtomicRMW(AtomicRMWOp op, + unsigned bytes, + Address offset, + Type type, + Name mem, + MemoryOrder order) { AtomicRMW curr; curr.memory = mem; curr.type = type; CHECK_ERR(visitAtomicRMW(&curr)); - push( - builder.makeAtomicRMW(op, bytes, offset, curr.ptr, curr.value, type, mem)); + push(builder.makeAtomicRMW( + op, bytes, offset, curr.ptr, curr.value, type, mem, order)); return Ok{}; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index ce2d6c737fd..5d6302596fa 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -540,7 +540,7 @@ void BinaryInstWriter::visitAtomicRMW(AtomicRMW* curr) { curr->bytes, curr->offset, curr->memory, - MemoryOrder::SeqCst, + curr->order, /*isRMW=*/true); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 4e79976579f..6cf82bb43bc 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1108,11 +1108,17 @@ void FunctionValidator::visitLoad(Load* curr) { curr, "Atomic load should be i32 or i64"); } - if (curr->order == MemoryOrder::AcqRel) { - shouldBeTrue(getModule()->features.hasRelaxedAtomics(), - curr, - "Acquire/release operations require relaxed atomics " - "[--enable-relaxed-atomics]"); + switch (curr->order) { + case MemoryOrder::AcqRel: { + shouldBeTrue(getModule()->features.hasRelaxedAtomics(), + curr, + "Acquire/release operations require relaxed atomics " + "[--enable-relaxed-atomics]"); + break; + } + case MemoryOrder::Unordered: + case MemoryOrder::SeqCst: + break; } if (curr->type == Type::v128) { shouldBeTrue(getModule()->features.hasSIMD(), @@ -1147,11 +1153,17 @@ void FunctionValidator::visitStore(Store* curr) { curr, "Atomic store should be i32 or i64"); } - if (curr->order == MemoryOrder::AcqRel) { - shouldBeTrue(getModule()->features.hasRelaxedAtomics(), - curr, - "Acquire/release operations require relaxed atomics " - "[--enable-relaxed-atomics]"); + switch (curr->order) { + case MemoryOrder::AcqRel: { + shouldBeTrue(getModule()->features.hasRelaxedAtomics(), + curr, + "Acquire/release operations require relaxed atomics " + "[--enable-relaxed-atomics]"); + break; + } + case MemoryOrder::Unordered: + case MemoryOrder::SeqCst: + break; } if (curr->valueType == Type::v128) { shouldBeTrue(getModule()->features.hasSIMD(), @@ -1185,6 +1197,24 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operations require threads [--enable-threads]"); + + switch (curr->order) { + case MemoryOrder::AcqRel: { + shouldBeTrue(getModule()->features.hasRelaxedAtomics(), + curr, + "Acquire/release operations require relaxed atomics " + "[--enable-relaxed-atomics]"); + break; + } + // Unordered RMW should be impossible unless there's a bug in the code. + case MemoryOrder::Unordered: { + WASM_UNREACHABLE("Atomic RMW can't be unordered"); + break; + } + case MemoryOrder::SeqCst: + break; + } + validateMemBytes(curr->bytes, curr->type, curr); shouldBeEqualOrFirstIsUnreachable( curr->ptr->type, diff --git a/test/spec/relaxed-atomics.wast b/test/spec/relaxed-atomics.wast index 1527fbcae2a..9ad4dcfaf27 100644 --- a/test/spec/relaxed-atomics.wast +++ b/test/spec/relaxed-atomics.wast @@ -1,118 +1,50 @@ (module - (memory $0 23 256 shared) + (memory 1 1 shared) + (memory 1 1 shared) - (func $acqrel (result i32) - (i32.atomic.store acqrel (i32.const 0) (i32.const 0)) - (i32.atomic.load acqrel - (i32.const 1) - ) - ) - (func $seqcst (result i32) - (i32.atomic.store 0 seqcst (i32.const 0) (i32.const 0)) - ;; seqcst may be omitted for atomic loads, it's the default - (drop (i32.atomic.load seqcst - (i32.const 1) - )) - ;; allows memory index before memory ordering immediate - (i32.atomic.load 0 seqcst - (i32.const 1) - ) - ) -) + (func $no_ordering_without_memid + (drop (i32.atomic.load (i32.const 51))) + (i32.atomic.store (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add (i32.const 51) (i32.const 51))) + ) -(assert_malformed - (module quote - "(memory $0 23 256 shared)" - "(func $acqrel (result i32)" - " (i32.load acqrel" - " (i32.const 1)" - " ) " - ") " - ) - "Memory ordering can only be provided for atomic loads." -) + (func $acqrel_without_memid + (drop (i32.atomic.load acqrel (i32.const 51))) + (i32.atomic.store acqrel (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add acqrel (i32.const 51) (i32.const 51))) + ) -;; Parses acquire-release immediate -;; (module -;; (memory $0 23 256 shared) -;; (func $acqrel -;; (i32.atomic.store (i32.const 0) (i32.const 0)) -;; ) -;; ) -(module binary - "\00asm\01\00\00\00" - "\01\04\01\60\00\00\03\02\01\00\05\05\01\03\17\80\02" ;; other sections - "\0a\0d\01" ;; code section - "\0b\00" ;; func $acqrel - "\41\00\41\00" ;; (i32.const 0) (i32.const 0) - "\fe\17" ;; i32.atomic.store - "\22" ;; 2 | (1<<5): Alignment of 2 (32-bit store), with bit 5 set indicating that then next byte is a memory ordering - "\01" ;; acqrel ordering - "\00" ;; offset - "\0b" ;; end -) + (func $seqcst_without_memid + (drop (i32.atomic.load seqcst (i32.const 51))) + (i32.atomic.store seqcst (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add seqcst (i32.const 51) (i32.const 51))) + ) -;; Parses optional seq-cst immediate -(module binary - "\00asm\01\00\00\00" - "\01\04\01\60\00\00\03\02\01\00\05\05\01\03\17\80\02" ;; other sections - "\0a\0d\01" ;; code section - "\0b\00" ;; func $seqcst - "\41\00\41\00" ;; (i32.const 0) (i32.const 0) - "\fe\17" ;; i32.atomic.store - "\22" ;; 2 | (1<<5): Alignment of 2 (32-bit store), with bit 5 set indicating that then next byte is a memory ordering - "\00" ;; seqcst ordering - "\00" ;; offset - "\0b" ;; end -) + (func $no_ordering_with_memid + (drop (i32.atomic.load 1 (i32.const 51))) + (i32.atomic.store 1 (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add 1 (i32.const 51) (i32.const 51))) + ) -;; Parses optional seq-cst immediate with memory idx -(module binary - "\00asm\01\00\00\00" - "\01\04\01\60\00\00\03\02\01\00\05\05\01\03\17\80\02" ;; other sections - "\0a\0e\01" ;; code section - "\0c\00" ;; func $seqcst - "\41\00\41\00" ;; (i32.const 0) (i32.const 0) - "\fe\17" ;; i32.atomic.store - "\62" ;; 2 | (1<<5): Alignment of 2 (32-bit store), with bit 5 set indicating that then next byte is a memory ordering - "\00" ;; memory index - "\00" ;; seqcst ordering - "\00" ;; offset - "\0b" ;; end -) + (func $acqrel_with_memid + (drop (i32.atomic.load 1 acqrel (i32.const 51))) + (i32.atomic.store 1 acqrel (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add 1 acqrel (i32.const 51) (i32.const 51))) + ) -;; Parses acquire-release immediate -;; Equivalent to -;; (module -;; (memory $0 23 256 shared) -;; (func $load (result i32) -;; (i32.atomic.load acqrel -;; (i32.const 1) -;; ) -;; ) -;; ) -(module binary - "\00asm\01\00\00\00" ;; header + version - "\01\05\01\60\00\01\7f\03\02\01\00\05\05\01\03\17\80\02" ;; other sections - "\0a\0b\01" ;; code section - ;; func $load body - "\09\00" ;; size + decl count - "\41\01" ;; i32.const 1 - "\fe\10" ;; i32.atomic.load - "\22" ;; 2 | (1<<5): Alignment of 2 (32-bit load), with bit 5 set indicating that the next byte is a memory ordering - "\01" ;; acqrel ordering - "\00" ;; offset - "\0b" ;; end + (func $seqcst_with_memid + (drop (i32.atomic.load 1 seqcst (i32.const 51))) + (i32.atomic.store 1 seqcst (i32.const 51) (i32.const 51)) + (drop (i32.atomic.rmw.add 1 seqcst (i32.const 51) (i32.const 51))) + ) ) -;; parses acquire-release immediate after memory index (module binary "\00asm\01\00\00\00" ;; header + version "\01\05\01\60\00\01\7f\03\02\01\00\05\05\01\03\17\80\02" ;; other sections "\0a\0c\01" ;; code section - ;; func $load body - "\0a\00" ;; size + decl count - "\41\01" ;; i32.const 1 + "\0a\00" ;; func size + decl count + "\41\33" ;; i32.const 51 "\fe\10" ;; i32.atomic.load "\62" ;; 2 | (1<<5) | (1<<6): Alignment of 2 (32-bit load), with bit 5 set indicating that the next byte is a memory ordering "\00" ;; memory index @@ -121,27 +53,38 @@ "\0b" ;; end ) -;; Parses optional seqcst memory ordering for atomic loads -;; This isn't covered by round-trip tests because we omit it by default. -;; Equivalent to -;; (module -;; (memory $0 23 256 shared) -;; (func $load (result i32) -;; (i32.atomic.load seqcst -;; (i32.const 1) -;; ) -;; ) -;; ) (module binary - "\00asm\01\00\00\00" ;; header + version - "\01\05\01\60\00\01\7f\03\02\01\00\05\05\01\03\17\80\02" ;; other sections - "\0a\0b\01" ;; code section - ;; func $load body - "\09\00" ;; size + decl count - "\41\01" ;; i32.const 1 - "\fe\10" ;; i32.atomic.load - "\22" ;; 2 | (1<<5): Alignment of 2 (32-bit load), with bit 5 set indicating that the next byte is a memory ordering - "\00" ;; seqcst ordering - "\00" ;; offset - "\0b" ;; end + "\00\61\73\6d\01\00\00\00\01\04\01\60\00\00\03\07\06\00\00\00\00\00\00\05\07\02\03\01\01\03\01\01" + "\0a\b8\01\06" ;; code section + "\1a\00" ;; func + "\41\33\fe\10\02\00\1a" ;; (drop (i32.atomic.load (i32.const 51))) + "\41\33\41\33\fe\17\02\00" ;; (i32.atomic.store (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\02\00\1a\0b" ;; (drop (i32.atomic.rmw.add (i32.const 51) (i32.const 51))) + "\1d\00" ;; func + "\41\33\fe\10\22\00\00\1a" ;; (drop (i32.atomic.load seqcst (i32.const 51))) + "\41\33\41\33\fe\17\22\00\00" ;; (i32.atomic.store seqcst (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\22\00\00\1a\0b" ;; (drop (i32.atomic.rmw.add seqcst (i32.const 51) (i32.const 51))) + "\1d\00" ;; func + "\41\33\fe\10\22\01\00\1a" ;; (drop (i32.atomic.load acqrel (i32.const 51))) + "\41\33\41\33\fe\17\22\01\00" ;; (i32.atomic.store acqrel (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\22\11\00\1a\0b" ;; (drop (i32.atomic.rmw.add acqrel (i32.const 51) (i32.const 51))) + "\1d\00" ;; func + "\41\33\fe\10\42\01\00\1a" ;; (drop (i32.atomic.load 1 (i32.const 51))) + "\41\33\41\33\fe\17\42\01\00" ;; (i32.atomic.store 1 (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\42\01\00\1a\0b" ;; (drop (i32.atomic.rmw.add 1 (i32.const 51) (i32.const 51))) + "\20\00" ;; func + "\41\33\fe\10\62\01\00\00\1a" ;; (drop (i32.atomic.load 1 seqcst (i32.const 51))) + "\41\33\41\33\fe\17\62\01\00\00" ;; (i32.atomic.store 1 seqcst (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\62\01\00\00\1a\0b" ;; (drop (i32.atomic.rmw.add 1 seqcst (i32.const 51) (i32.const 51))) + "\20\00" ;; func + "\41\33\fe\10\62\01\01\00\1a" ;; (drop (i32.atomic.load 1 acqrel (i32.const 51))) + "\41\33\41\33\fe\17\62\01\01\00" ;; (i32.atomic.store 1 acqrel (i32.const 51) (i32.const 51)) + "\41\33\41\33\fe\1e\62\01\11\00\1a\0b" ;; (drop (i32.atomic.rmw.add 1 acqrel (i32.const 51) (i32.const 51))) ) + +(assert_invalid (module + (memory 1 1 shared) + + (func $i32load (drop (i32.load acqrel (i32.const 51)))) +) "Can't set memory ordering for non-atomic i32.load") + From d4a56c8bdf006cba71366cd7cade2c1513630f1d Mon Sep 17 00:00:00 2001 From: stevenfontanella Date: Fri, 23 Jan 2026 00:56:21 +0000 Subject: [PATCH 2/2] Change validator failure --- src/wasm/wasm-validator.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 6cf82bb43bc..f50a51f6f55 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1206,9 +1206,12 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { "[--enable-relaxed-atomics]"); break; } - // Unordered RMW should be impossible unless there's a bug in the code. + // Unordered RMW should be impossible unless there's a bug in the parser. case MemoryOrder::Unordered: { - WASM_UNREACHABLE("Atomic RMW can't be unordered"); + shouldBeUnequal(curr->order, + MemoryOrder::Unordered, + curr, + "Atomic RMW can't be unordered"); break; } case MemoryOrder::SeqCst: