Skip to content

Commit d8c6b2a

Browse files
authored
Add a "relaxed" memory order (#8984)
The memory order is not the C++ relaxed memory order, but rather the slightly stronger load-store ordered memory order, which prevents out-of-thin-air values by forbidding earlier atomic loads from being reordered past later atomic stores. This makes it impossible to construct the self-justifying causality cycle that gives rise to out-of-thin-air values. The name of the memory order in both the implementation and the text format is likely to change in the future to become more descriptive.
1 parent 86a7c4c commit d8c6b2a

22 files changed

Lines changed: 2143 additions & 702 deletions

src/binaryen-c.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,9 @@ BinaryenExternalKind BinaryenExternalTag(void) {
430430
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderUnordered(void) {
431431
return static_cast<BinaryenMemoryOrder>(MemoryOrder::Unordered);
432432
}
433+
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderRelaxed(void) {
434+
return static_cast<BinaryenMemoryOrder>(MemoryOrder::Relaxed);
435+
}
433436
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderAcqRel(void) {
434437
return static_cast<BinaryenMemoryOrder>(MemoryOrder::AcqRel);
435438
}

src/binaryen-c.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ typedef uint8_t BinaryenMemoryOrder;
211211

212212
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderUnordered(void);
213213

214+
// Relaxed atomic memory operation.
215+
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderRelaxed(void);
216+
214217
// Acquire/Release atomic memory operation; acquire for loads, release for
215218
// stores.
216219
BINARYEN_API BinaryenMemoryOrder BinaryenMemoryOrderAcqRel(void);

src/ir/effects.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ class EffectAnalyzer {
364364
if (danglingPop) {
365365
return true;
366366
}
367+
// Relaxed (or stronger) loads cannot be reordered after relaxed (or
368+
// stronger) stores.
369+
if (readOrder >= MemoryOrder::Relaxed &&
370+
other.writeOrder >= MemoryOrder::Relaxed) {
371+
return true;
372+
}
367373
// Shared location accesses cannot be reordered after (but may be able to be
368374
// reordered before) release stores.
369375
if (other.writeOrder >= MemoryOrder::AcqRel &&

src/js/binaryen.js-post.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ function initializeConstants() {
162162
// MemoryOrder for atomic operations
163163
Module['MemoryOrder'] = {};
164164
[ 'Unordered',
165-
'SeqCst',
166-
'AcqRel'
165+
'Relaxed',
166+
'AcqRel',
167+
'SeqCst'
167168
].forEach(name => {
168169
Module['MemoryOrder'][name.toLowerCase()] = Module['_BinaryenMemoryOrder' + name]()
169170
});

src/parser/parsers.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -900,19 +900,22 @@ Result<typename Ctx::MemTypeT> memtypeContinued(Ctx& ctx, Type addressType) {
900900
return ctx.makeMemType(addressType, *limits, shared, pageSizeLog2);
901901
}
902902

903-
// memorder ::= 'seqcst' | 'acqrel'
903+
// memorder ::= 'seqcst' | 'acqrel' | 'relaxed'
904904
template<typename Ctx> MaybeResult<MemoryOrder> maybeMemOrder(Ctx& ctx) {
905905
if (ctx.in.takeKeyword("seqcst"sv)) {
906906
return MemoryOrder::SeqCst;
907907
}
908908
if (ctx.in.takeKeyword("acqrel"sv)) {
909909
return MemoryOrder::AcqRel;
910910
}
911+
if (ctx.in.takeKeyword("relaxed"sv)) {
912+
return MemoryOrder::Relaxed;
913+
}
911914

912915
return {};
913916
}
914917

915-
// memorder ::= '' | 'seqcst' | 'acqrel'
918+
// memorder ::= '' | 'seqcst' | 'acqrel' | 'relaxed'
916919
template<typename Ctx> Result<MemoryOrder> memorder(Ctx& ctx) {
917920
auto order = maybeMemOrder(ctx);
918921
CHECK_ERR(order);

src/passes/OptimizeInstructions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,7 @@ struct OptimizeInstructions
19131913
trapOnNull(curr, curr->ref);
19141914
// Relax acquire loads of unshared fields to unordered because they cannot
19151915
// synchronize with other threads.
1916+
// TODO: Relax all other memory orderings as well.
19161917
if (curr->order == MemoryOrder::AcqRel && curr->ref->type.isRef() &&
19171918
!curr->ref->type.getHeapType().isShared()) {
19181919
curr->order = MemoryOrder::Unordered;
@@ -1936,6 +1937,7 @@ struct OptimizeInstructions
19361937

19371938
// Relax release stores of unshared fields to unordered because they cannot
19381939
// synchronize with other threads.
1940+
// TODO: Relax all other memory orderings as well.
19391941
if (curr->order == MemoryOrder::AcqRel && curr->ref->type.isRef() &&
19401942
!curr->ref->type.getHeapType().isShared()) {
19411943
curr->order = MemoryOrder::Unordered;

src/passes/Precompute.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,10 @@ class PrecomputingExpressionRunner
158158
case MemoryOrder::SeqCst:
159159
// This can never be precomputed away because it synchronizes with other
160160
// threads.
161+
// TODO: Unify with other memory orderings below.
161162
return Flow(NONCONSTANT_FLOW);
162163
case MemoryOrder::AcqRel:
164+
case MemoryOrder::Relaxed:
163165
// This synchronizes only with writes to the same data, so it can still
164166
// be precomputed if the data is not shared with other threads.
165167
if (curr->ref->type.getHeapType().isShared()) {
@@ -201,8 +203,10 @@ class PrecomputingExpressionRunner
201203
case MemoryOrder::SeqCst:
202204
// This can never be precomputed away because it synchronizes with other
203205
// threads.
206+
// TODO: Unify with other memory orderings below.
204207
return Flow(NONCONSTANT_FLOW);
205208
case MemoryOrder::AcqRel:
209+
case MemoryOrder::Relaxed:
206210
// This synchronizes only with writes to the same data, so it can still
207211
// be precomputed if the data is not shared with other threads.
208212
if (curr->ref->type.getHeapType().isShared()) {

src/passes/Print.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,9 @@ struct PrintExpressionContents
23292329
case MemoryOrder::AcqRel:
23302330
o << " acqrel";
23312331
break;
2332+
case MemoryOrder::Relaxed:
2333+
o << " relaxed";
2334+
break;
23322335
}
23332336
}
23342337

@@ -4042,6 +4045,9 @@ std::ostream& operator<<(std::ostream& os, wasm::MemoryOrder mo) {
40424045
case wasm::MemoryOrder::Unordered:
40434046
os << "Unordered";
40444047
break;
4048+
case wasm::MemoryOrder::Relaxed:
4049+
os << "Relaxed";
4050+
break;
40454051
case wasm::MemoryOrder::SeqCst:
40464052
os << "SeqCst";
40474053
break;

src/passes/SafeHeap.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,18 @@ static Name getLoadName(Load* curr) {
4949
parts.push_back(std::to_string(curr->align));
5050
break;
5151
}
52-
case MemoryOrder::SeqCst: {
53-
parts.push_back("SC");
52+
case MemoryOrder::Relaxed: {
53+
parts.push_back("RLX");
5454
break;
5555
}
5656
case MemoryOrder::AcqRel: {
5757
parts.push_back("AR");
5858
break;
5959
}
60+
case MemoryOrder::SeqCst: {
61+
parts.push_back("SC");
62+
break;
63+
}
6064
}
6165

6266
return "SAFE_HEAP_LOAD_" + String::join(parts, "_");
@@ -70,12 +74,17 @@ static Name getStoreName(Store* curr) {
7074
parts.push_back(std::to_string(curr->align));
7175
break;
7276
}
73-
case MemoryOrder::SeqCst: {
74-
parts.push_back("SC");
77+
case MemoryOrder::Relaxed: {
78+
parts.push_back("RLX");
7579
break;
7680
}
7781
case MemoryOrder::AcqRel: {
7882
parts.push_back("AR");
83+
break;
84+
}
85+
case MemoryOrder::SeqCst: {
86+
parts.push_back("SC");
87+
break;
7988
}
8089
}
8190
return "SAFE_HEAP_STORE_" + String::join(parts, "_");
@@ -229,13 +238,14 @@ struct SafeHeap : public Pass {
229238
void addGlobals(Module* module, FeatureSet features) {
230239
// load funcs
231240
Load load;
232-
std::vector<MemoryOrder> memoryOrdersToGenerate(
233-
features.hasAcquireReleaseAtomics()
234-
? std::initializer_list<MemoryOrder>{MemoryOrder::Unordered,
235-
MemoryOrder::AcqRel,
236-
MemoryOrder::SeqCst}
237-
: std::initializer_list<MemoryOrder>{MemoryOrder::Unordered,
238-
MemoryOrder::SeqCst});
241+
std::vector<MemoryOrder> memoryOrdersToGenerate{MemoryOrder::Unordered,
242+
MemoryOrder::SeqCst};
243+
if (features.hasRelaxedAtomics()) {
244+
memoryOrdersToGenerate.push_back(MemoryOrder::Relaxed);
245+
}
246+
if (features.hasAcquireReleaseAtomics()) {
247+
memoryOrdersToGenerate.push_back(MemoryOrder::AcqRel);
248+
}
239249
for (Type type : {Type::i32, Type::i64, Type::f32, Type::f64, Type::v128}) {
240250
if (type == Type::v128 && !features.hasSIMD()) {
241251
continue;

src/wasm-binary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,7 @@ enum ASTNodes {
12351235

12361236
OrderSeqCst = 0x0,
12371237
OrderAcqRel = 0x1,
1238+
OrderRelaxed = 0x2,
12381239
StructAtomicGet = 0x5c,
12391240
StructAtomicGetS = 0x5d,
12401241
StructAtomicGetU = 0x5e,

0 commit comments

Comments
 (0)