Skip to content

Commit b92a217

Browse files
Acquire/release memory ordering for loads
1 parent 158ee9b commit b92a217

19 files changed

+6970
-3207
lines changed

src/binaryen-c.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,8 @@ BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module,
13511351
offset,
13521352
(Expression*)ptr,
13531353
Type(type),
1354-
getMemoryName(module, memoryName)));
1354+
getMemoryName(module, memoryName),
1355+
MemoryOrder::SeqCst));
13551356
}
13561357
BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module,
13571358
uint32_t bytes,

src/parser/contexts.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,8 @@ struct NullInstrParserCtx {
555555
int,
556556
bool,
557557
MemoryIdxT*,
558-
MemargT) {
558+
MemargT,
559+
MemoryOrder) {
559560
return Ok{};
560561
}
561562
Result<> makeStore(Index,
@@ -2235,12 +2236,13 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
22352236
int bytes,
22362237
bool isAtomic,
22372238
Name* mem,
2238-
Memarg memarg) {
2239+
Memarg memarg,
2240+
MemoryOrder order) {
22392241
auto m = getMemory(pos, mem);
22402242
CHECK_ERR(m);
22412243
if (isAtomic) {
2242-
return withLoc(pos,
2243-
irBuilder.makeAtomicLoad(bytes, memarg.offset, type, *m));
2244+
return withLoc(
2245+
pos, irBuilder.makeAtomicLoad(bytes, memarg.offset, type, *m, order));
22442246
}
22452247
return withLoc(pos,
22462248
irBuilder.makeLoad(

src/parser/parsers.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx&);
4949
template<typename Ctx>
5050
Result<typename Ctx::MemTypeT> memtypeContinued(Ctx&, Type addressType);
5151
template<typename Ctx> Result<MemoryOrder> memorder(Ctx&);
52+
template<typename Ctx> MaybeResult<MemoryOrder> maybeMemOrder(Ctx&);
5253
template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx&);
5354
template<typename Ctx>
5455
Result<typename Ctx::TableTypeT> tabletypeContinued(Ctx&, Type addressType);
@@ -860,6 +861,18 @@ template<typename Ctx> Result<MemoryOrder> memorder(Ctx& ctx) {
860861
return MemoryOrder::SeqCst;
861862
}
862863

864+
// memorder ::= 'seqcst' | 'acqrel'
865+
template<typename Ctx> MaybeResult<MemoryOrder> maybeMemOrder(Ctx& ctx) {
866+
if (ctx.in.takeKeyword("seqcst"sv)) {
867+
return MemoryOrder::SeqCst;
868+
}
869+
if (ctx.in.takeKeyword("acqrel"sv)) {
870+
return MemoryOrder::AcqRel;
871+
}
872+
873+
return {};
874+
}
875+
863876
// tabletype ::= (limits32 | 'i32' limits32 | 'i64' limit64) reftype
864877
template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx& ctx) {
865878
Type addressType = Type::i32;
@@ -1737,12 +1750,32 @@ Result<> makeLoad(Ctx& ctx,
17371750
bool signed_,
17381751
int bytes,
17391752
bool isAtomic) {
1753+
17401754
auto mem = maybeMemidx(ctx);
17411755
CHECK_ERR(mem);
1756+
1757+
// We could only parse this when `isAtomic`, but this way gives a clearer
1758+
// error since `memIdx` can never be mistaken for a `memOrder`.
1759+
auto maybeOrder = maybeMemOrder(ctx);
1760+
CHECK_ERR(maybeOrder);
1761+
1762+
if (maybeOrder && !isAtomic) {
1763+
return Err{"Memory ordering can only be provided for atomic loads."};
1764+
}
1765+
17421766
auto arg = memarg(ctx, bytes);
17431767
CHECK_ERR(arg);
1744-
return ctx.makeLoad(
1745-
pos, annotations, type, signed_, bytes, isAtomic, mem.getPtr(), *arg);
1768+
return ctx.makeLoad(pos,
1769+
annotations,
1770+
type,
1771+
signed_,
1772+
bytes,
1773+
isAtomic,
1774+
mem.getPtr(),
1775+
*arg,
1776+
maybeOrder ? *maybeOrder
1777+
: isAtomic ? MemoryOrder::SeqCst
1778+
: MemoryOrder::Unordered);
17461779
}
17471780

17481781
template<typename Ctx>

src/passes/Print.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ struct PrintExpressionContents
574574
}
575575
restoreNormalColor(o);
576576
printMemoryName(curr->memory, o, wasm);
577+
printMemoryOrder(curr->order, /*prependSpace=*/true, /*appendSpace=*/false);
577578
if (curr->offset) {
578579
o << " offset=" << curr->offset;
579580
}
@@ -2323,7 +2324,9 @@ struct PrintExpressionContents
23232324
o << index;
23242325
}
23252326
}
2326-
void printMemoryOrder(MemoryOrder order) {
2327+
2328+
void
2329+
printMemoryOrder(MemoryOrder order, bool prependSpace, bool appendSpace) {
23272330
switch (order) {
23282331
// Unordered should have a different base instruction, so there is nothing
23292332
// to print. We could be explicit and print seqcst, but we choose not to
@@ -2332,10 +2335,15 @@ struct PrintExpressionContents
23322335
case MemoryOrder::SeqCst:
23332336
break;
23342337
case MemoryOrder::AcqRel:
2335-
o << "acqrel ";
2338+
o << (prependSpace ? " " : "") << "acqrel" << (appendSpace ? " " : "");
23362339
break;
23372340
}
23382341
}
2342+
2343+
void printMemoryOrder(MemoryOrder order) {
2344+
printMemoryOrder(order, /*prependSpace=*/false, /*appendSpace=*/true);
2345+
}
2346+
23392347
void visitStructGet(StructGet* curr) {
23402348
auto heapType = curr->ref->type.getHeapType();
23412349
const auto& field = heapType.getStruct().fields[curr->index];

src/passes/SafeHeap.cpp

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "ir/import-utils.h"
2727
#include "ir/load-utils.h"
2828
#include "pass.h"
29+
#include "support/string.h"
2930
#include "wasm-builder.h"
3031
#include "wasm.h"
3132

@@ -37,18 +38,28 @@ static const Name SEGFAULT_IMPORT("segfault");
3738
static const Name ALIGNFAULT_IMPORT("alignfault");
3839

3940
static Name getLoadName(Load* curr) {
40-
std::string ret = "SAFE_HEAP_LOAD_";
41-
ret += curr->type.toString();
42-
ret += "_" + std::to_string(curr->bytes) + "_";
41+
std::vector<std::string> parts{curr->type.toString(),
42+
std::to_string(curr->bytes)};
4343
if (LoadUtils::isSignRelevant(curr) && !curr->signed_) {
44-
ret += "U_";
44+
parts.push_back("U");
4545
}
46-
if (curr->isAtomic()) {
47-
ret += "A";
48-
} else {
49-
ret += std::to_string(curr->align);
46+
47+
switch (curr->order) {
48+
case MemoryOrder::Unordered: {
49+
parts.push_back(std::to_string(curr->align));
50+
break;
51+
}
52+
case MemoryOrder::SeqCst: {
53+
parts.push_back("SC");
54+
break;
55+
}
56+
case MemoryOrder::AcqRel: {
57+
parts.push_back("AR");
58+
break;
59+
}
5060
}
51-
return ret;
61+
62+
return "SAFE_HEAP_LOAD_" + String::join(parts, "_");
5263
}
5364

5465
static Name getStoreName(Store* curr) {
@@ -232,10 +243,11 @@ struct SafeHeap : public Pass {
232243
if (align > bytes) {
233244
continue;
234245
}
235-
for (auto isAtomic : {true, false}) {
236-
load.order =
237-
isAtomic ? MemoryOrder::SeqCst : MemoryOrder::Unordered;
238-
if (isAtomic &&
246+
for (auto memoryOrder : {MemoryOrder::Unordered,
247+
MemoryOrder::AcqRel,
248+
MemoryOrder::SeqCst}) {
249+
load.order = memoryOrder;
250+
if (load.isAtomic() &&
239251
!isPossibleAtomicOperation(
240252
align, bytes, module->memories[0]->shared, type)) {
241253
continue;

src/support/string.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ class Split : public std::vector<std::string> {
5858
}
5959
};
6060

61+
template<typename StringLike,
62+
typename = std::enable_if_t<
63+
std::is_convertible_v<StringLike, std::string_view>>>
64+
std::string join(const std::vector<StringLike>& strs, std::string_view sep) {
65+
if (strs.empty()) {
66+
return "";
67+
}
68+
69+
std::string ret = std::string(strs[0]);
70+
for (size_t i = 1; i < strs.size(); i++) {
71+
ret.append(sep);
72+
ret.append(strs[i]);
73+
}
74+
75+
return ret;
76+
}
77+
6178
// Handles bracketing in a list initially split by ",", but the list may
6279
// contain nested ","s. For example,
6380
// void foo(int, double)

src/tools/wasm-split/instrumenter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,12 @@ void Instrumenter::addProfileExport(size_t numFuncs) {
287287
getAddr(),
288288
builder.makeBinary(
289289
MulInt32, getFuncIdx(), builder.makeConst(uint32_t(4)))),
290-
builder.makeAtomicLoad(
291-
1, 0, getFuncIdx(), Type::i32, loadMemoryName),
290+
builder.makeAtomicLoad(1,
291+
0,
292+
getFuncIdx(),
293+
Type::i32,
294+
loadMemoryName,
295+
MemoryOrder::SeqCst),
292296
Type::i32,
293297
wasm->memories[0]->name),
294298
builder.makeLocalSet(

src/wasm-binary.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,8 +1728,10 @@ class WasmBinaryReader {
17281728
size_t inlineHintsLen = 0;
17291729
void readInlineHints(size_t payloadLen);
17301730

1731-
Index readMemoryAccess(Address& alignment, Address& offset);
1732-
std::tuple<Name, Address, Address> getMemarg();
1731+
std::tuple<Address, Address, Index, MemoryOrder>
1732+
readMemoryAccess(bool isAtomic, bool isRMW);
1733+
std::tuple<Name, Address, Address, MemoryOrder> getMemarg(bool isAtomic,
1734+
bool isRMW);
17331735
MemoryOrder getMemoryOrder(bool isRMW = false);
17341736

17351737
[[noreturn]] void throwError(std::string text) {

src/wasm-builder.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,10 +384,17 @@ class Builder {
384384
ret->finalize();
385385
return ret;
386386
}
387-
Load* makeAtomicLoad(
388-
unsigned bytes, Address offset, Expression* ptr, Type type, Name memory) {
387+
Load* makeAtomicLoad(unsigned bytes,
388+
Address offset,
389+
Expression* ptr,
390+
Type type,
391+
Name memory,
392+
MemoryOrder order) {
393+
assert(order != MemoryOrder::Unordered &&
394+
"Atomic loads can't be unordered");
395+
389396
Load* load = makeLoad(bytes, false, offset, bytes, ptr, type, memory);
390-
load->order = MemoryOrder::SeqCst;
397+
load->order = order;
391398
return load;
392399
}
393400
AtomicWait* makeAtomicWait(Expression* ptr,

src/wasm-ir-builder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
156156
Name mem);
157157
Result<> makeStore(
158158
unsigned bytes, Address offset, unsigned align, Type type, Name mem);
159-
Result<> makeAtomicLoad(unsigned bytes, Address offset, Type type, Name mem);
159+
Result<> makeAtomicLoad(
160+
unsigned bytes, Address offset, Type type, Name mem, MemoryOrder order);
160161
Result<> makeAtomicStore(unsigned bytes, Address offset, Type type, Name mem);
161162
Result<> makeAtomicRMW(
162163
AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem);

0 commit comments

Comments
 (0)