Skip to content

Commit a6da2c7

Browse files
Add relaxed atomics support for stores
1 parent b92a217 commit a6da2c7

File tree

10 files changed

+98
-36
lines changed

10 files changed

+98
-36
lines changed

src/binaryen-c.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,8 @@ BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module,
13681368
(Expression*)ptr,
13691369
(Expression*)value,
13701370
Type(type),
1371-
getMemoryName(module, memoryName)));
1371+
getMemoryName(module, memoryName),
1372+
MemoryOrder::SeqCst));
13721373
}
13731374
BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module,
13741375
BinaryenOp op,

src/parser/contexts.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,8 @@ struct NullInstrParserCtx {
565565
int,
566566
bool,
567567
MemoryIdxT*,
568-
MemargT) {
568+
MemargT,
569+
MemoryOrder) {
569570
return Ok{};
570571
}
571572
Result<> makeAtomicRMW(Index,
@@ -2255,12 +2256,13 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
22552256
int bytes,
22562257
bool isAtomic,
22572258
Name* mem,
2258-
Memarg memarg) {
2259+
Memarg memarg,
2260+
MemoryOrder order) {
22592261
auto m = getMemory(pos, mem);
22602262
CHECK_ERR(m);
22612263
if (isAtomic) {
2262-
return withLoc(pos,
2263-
irBuilder.makeAtomicStore(bytes, memarg.offset, type, *m));
2264+
return withLoc(
2265+
pos, irBuilder.makeAtomicStore(bytes, memarg.offset, type, *m, order));
22642266
}
22652267
return withLoc(
22662268
pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));

src/parser/parsers.h

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -850,17 +850,6 @@ Result<typename Ctx::MemTypeT> memtypeContinued(Ctx& ctx, Type addressType) {
850850
return ctx.makeMemType(addressType, *limits, shared);
851851
}
852852

853-
// memorder ::= '' | 'seqcst' | 'acqrel'
854-
template<typename Ctx> Result<MemoryOrder> memorder(Ctx& ctx) {
855-
if (ctx.in.takeKeyword("seqcst"sv)) {
856-
return MemoryOrder::SeqCst;
857-
}
858-
if (ctx.in.takeKeyword("acqrel"sv)) {
859-
return MemoryOrder::AcqRel;
860-
}
861-
return MemoryOrder::SeqCst;
862-
}
863-
864853
// memorder ::= 'seqcst' | 'acqrel'
865854
template<typename Ctx> MaybeResult<MemoryOrder> maybeMemOrder(Ctx& ctx) {
866855
if (ctx.in.takeKeyword("seqcst"sv)) {
@@ -873,6 +862,13 @@ template<typename Ctx> MaybeResult<MemoryOrder> maybeMemOrder(Ctx& ctx) {
873862
return {};
874863
}
875864

865+
// memorder ::= '' | 'seqcst' | 'acqrel'
866+
template<typename Ctx> Result<MemoryOrder> memorder(Ctx& ctx) {
867+
auto order = maybeMemOrder(ctx);
868+
CHECK_ERR(order);
869+
return order ? *order : MemoryOrder::SeqCst;
870+
}
871+
876872
// tabletype ::= (limits32 | 'i32' limits32 | 'i64' limit64) reftype
877873
template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx& ctx) {
878874
Type addressType = Type::i32;
@@ -1755,7 +1751,8 @@ Result<> makeLoad(Ctx& ctx,
17551751
CHECK_ERR(mem);
17561752

17571753
// We could only parse this when `isAtomic`, but this way gives a clearer
1758-
// error since `memIdx` can never be mistaken for a `memOrder`.
1754+
// error when a memorder is given for non-atomic operations
1755+
// since the next token can never be mistaken for a `memOrder`.
17591756
auto maybeOrder = maybeMemOrder(ctx);
17601757
CHECK_ERR(maybeOrder);
17611758

@@ -1787,10 +1784,26 @@ Result<> makeStore(Ctx& ctx,
17871784
bool isAtomic) {
17881785
auto mem = maybeMemidx(ctx);
17891786
CHECK_ERR(mem);
1787+
1788+
auto maybeOrder = maybeMemOrder(ctx);
1789+
CHECK_ERR(maybeOrder);
1790+
1791+
if (maybeOrder && !isAtomic) {
1792+
return Err{"Memory ordering can only be provided for atomic stores."};
1793+
}
1794+
17901795
auto arg = memarg(ctx, bytes);
17911796
CHECK_ERR(arg);
1792-
return ctx.makeStore(
1793-
pos, annotations, type, bytes, isAtomic, mem.getPtr(), *arg);
1797+
return ctx.makeStore(pos,
1798+
annotations,
1799+
type,
1800+
bytes,
1801+
isAtomic,
1802+
mem.getPtr(),
1803+
*arg,
1804+
maybeOrder ? *maybeOrder
1805+
: isAtomic ? MemoryOrder::SeqCst
1806+
: MemoryOrder::Unordered);
17941807
}
17951808

17961809
template<typename Ctx>

src/passes/Print.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ struct PrintExpressionContents
605605
}
606606
restoreNormalColor(o);
607607
printMemoryName(curr->memory, o, wasm);
608+
printMemoryOrder(curr->order, /*prependSpace=*/true, /*appendSpace=*/false);
608609
if (curr->offset) {
609610
o << " offset=" << curr->offset;
610611
}

src/tools/wasm-split/instrumenter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ void Instrumenter::instrumentFuncs() {
151151
builder.makeConstPtr(0, Type::i32),
152152
builder.makeConst(uint32_t(1)),
153153
Type::i32,
154-
memoryName),
154+
memoryName,
155+
MemoryOrder::SeqCst),
155156
func->body,
156157
func->body->type);
157158
++funcIdx;

src/wasm-builder.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,13 @@ class Builder {
451451
Expression* ptr,
452452
Expression* value,
453453
Type type,
454-
Name memory) {
454+
Name memory,
455+
MemoryOrder order) {
456+
assert(order != MemoryOrder::Unordered &&
457+
"Atomic stores can't be unordered");
458+
455459
Store* store = makeStore(bytes, offset, bytes, ptr, value, type, memory);
456-
store->order = MemoryOrder::SeqCst;
460+
store->order = order;
457461
return store;
458462
}
459463
AtomicRMW* makeAtomicRMW(AtomicRMWOp op,

src/wasm-ir-builder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
158158
unsigned bytes, Address offset, unsigned align, Type type, Name mem);
159159
Result<> makeAtomicLoad(
160160
unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order);
161-
Result<> makeAtomicStore(unsigned bytes, Address offset, Type type, Name mem);
161+
Result<> makeAtomicStore(unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order);
162162
Result<> makeAtomicRMW(
163163
AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem);
164164
Result<>

src/wasm/wasm-binary.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3685,37 +3685,44 @@ Result<> WasmBinaryReader::readInst() {
36853685
case BinaryConsts::I32AtomicStore8: {
36863686
auto [mem, align, offset, memoryOrder] =
36873687
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3688-
return builder.makeAtomicStore(1, offset, Type::i32, mem);
3688+
return builder.makeAtomicStore(
3689+
1, offset, Type::i32, mem, memoryOrder);
36893690
}
36903691
case BinaryConsts::I32AtomicStore16: {
36913692
auto [mem, align, offset, memoryOrder] =
36923693
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3693-
return builder.makeAtomicStore(2, offset, Type::i32, mem);
3694+
return builder.makeAtomicStore(
3695+
2, offset, Type::i32, mem, memoryOrder);
36943696
}
36953697
case BinaryConsts::I32AtomicStore: {
36963698
auto [mem, align, offset, memoryOrder] =
36973699
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3698-
return builder.makeAtomicStore(4, offset, Type::i32, mem);
3700+
return builder.makeAtomicStore(
3701+
4, offset, Type::i32, mem, memoryOrder);
36993702
}
37003703
case BinaryConsts::I64AtomicStore8: {
37013704
auto [mem, align, offset, memoryOrder] =
37023705
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3703-
return builder.makeAtomicStore(1, offset, Type::i64, mem);
3706+
return builder.makeAtomicStore(
3707+
1, offset, Type::i64, mem, memoryOrder);
37043708
}
37053709
case BinaryConsts::I64AtomicStore16: {
37063710
auto [mem, align, offset, memoryOrder] =
37073711
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3708-
return builder.makeAtomicStore(2, offset, Type::i64, mem);
3712+
return builder.makeAtomicStore(
3713+
2, offset, Type::i64, mem, memoryOrder);
37093714
}
37103715
case BinaryConsts::I64AtomicStore32: {
37113716
auto [mem, align, offset, memoryOrder] =
37123717
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3713-
return builder.makeAtomicStore(4, offset, Type::i64, mem);
3718+
return builder.makeAtomicStore(
3719+
4, offset, Type::i64, mem, memoryOrder);
37143720
}
37153721
case BinaryConsts::I64AtomicStore: {
37163722
auto [mem, align, offset, memoryOrder] =
37173723
getMemarg(/*isAtomic=*/true, /*isRMW=*/false);
3718-
return builder.makeAtomicStore(8, offset, Type::i64, mem);
3724+
return builder.makeAtomicStore(
3725+
8, offset, Type::i64, mem, memoryOrder);
37193726
}
37203727

37213728
#define RMW(op) \

src/wasm/wasm-ir-builder.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,15 +1492,14 @@ Result<> IRBuilder::makeAtomicLoad(
14921492
return Ok{};
14931493
}
14941494

1495-
Result<> IRBuilder::makeAtomicStore(unsigned bytes,
1496-
Address offset,
1497-
Type type,
1498-
Name mem) {
1495+
Result<> IRBuilder::makeAtomicStore(
1496+
unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order) {
14991497
Store curr;
15001498
curr.memory = mem;
15011499
curr.valueType = type;
15021500
CHECK_ERR(visitStore(&curr));
1503-
push(builder.makeAtomicStore(bytes, offset, curr.ptr, curr.value, type, mem));
1501+
push(builder.makeAtomicStore(
1502+
bytes, offset, curr.ptr, curr.value, type, mem, order));
15041503
return Ok{};
15051504
}
15061505

test/spec/relaxed-atomics.wast

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
(memory $0 23 256 shared)
33

44
(func $acqrel (result i32)
5+
(i32.atomic.store acqrel (i32.const 0) (i32.const 0))
56
(i32.atomic.load acqrel
67
(i32.const 1)
78
)
89
)
910
(func $seqcst (result i32)
10-
;; seqcst may be omitted for atomic loads, it's the default
11+
;; seqcst immediate may be omitted for atomic operations, it's the default
12+
(i32.atomic.store seqcst (i32.const 0) (i32.const 0))
1113
(i32.atomic.load seqcst
1214
(i32.const 1)
1315
)
@@ -26,6 +28,38 @@
2628
"Memory ordering can only be provided for atomic loads."
2729
)
2830

31+
;; Parses acquire-release immediate
32+
;; (module
33+
;; (memory $0 23 256 shared)
34+
;; (func $acqrel
35+
;; (i32.atomic.store (i32.const 0) (i32.const 0))
36+
;; )
37+
;; )
38+
(module binary
39+
"\00asm\01\00\00\00"
40+
"\01\04\01\60\00\00\03\02\01\00\05\05\01\03\17\80\02" ;; other sections
41+
"\0a\0d\01" ;; code section
42+
"\0b\00" ;; func $acqrel
43+
"\41\00\41\00" ;; (i32.const 0) (i32.const 0)
44+
"\fe\17" ;; i32.atomic.store
45+
"\22" ;; 2 | (1<<5): Alignment of 2 (32-bit store), with bit 5 set indicating that then next byte is a memory ordering
46+
"\01" ;; acqrel ordering
47+
"\00" ;; offset
48+
"\0b" ;; end
49+
)
50+
51+
(module binary
52+
"\00asm\01\00\00\00"
53+
"\01\04\01\60\00\00\03\02\01\00\05\05\01\03\17\80\02" ;; other sections
54+
"\0a\0d\01" ;; code section
55+
"\0b\00" ;; func $acqrel
56+
"\41\00\41\00" ;; (i32.const 0) (i32.const 0)
57+
"\fe\17" ;; i32.atomic.store
58+
"\22" ;; 2 | (1<<5): Alignment of 2 (32-bit store), with bit 5 set indicating that then next byte is a memory ordering
59+
"\00" ;; seqcst ordering
60+
"\00" ;; offset
61+
"\0b" ;; end
62+
)
2963

3064
;; Parses acquire-release immediate
3165
;; Equivalent to

0 commit comments

Comments
 (0)