Skip to content

Commit caefb33

Browse files
authored
Atomic struct RMW instructions (#7194)
Add `StructRMW` and `StructCmpxchg` expression classes with binary and text printing and parsing as well as validation.
1 parent b6eacd7 commit caefb33

29 files changed

+962
-54
lines changed

scripts/gen-s-parser.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,13 @@
622622
("struct.atomic.get_u", "makeAtomicStructGet(false)"),
623623
("struct.set", "makeStructSet()"),
624624
("struct.atomic.set", "makeAtomicStructSet()"),
625+
("struct.atomic.rmw.add", "makeStructRMW(RMWAdd)"),
626+
("struct.atomic.rmw.sub", "makeStructRMW(RMWSub)"),
627+
("struct.atomic.rmw.and", "makeStructRMW(RMWAnd)"),
628+
("struct.atomic.rmw.or", "makeStructRMW(RMWOr)"),
629+
("struct.atomic.rmw.xor", "makeStructRMW(RMWXor)"),
630+
("struct.atomic.rmw.xchg", "makeStructRMW(RMWXchg)"),
631+
("struct.atomic.rmw.cmpxchg", "makeStructCmpxchg()"),
625632
("array.new", "makeArrayNew(false)"),
626633
("array.new_default", "makeArrayNew(true)"),
627634
("array.new_data", "makeArrayNewData()"),

src/gen-s-parser.inc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5043,6 +5043,63 @@ switch (buf[0]) {
50435043
default: goto parse_error;
50445044
}
50455045
}
5046+
case 'r': {
5047+
switch (buf[18]) {
5048+
case 'a': {
5049+
switch (buf[19]) {
5050+
case 'd':
5051+
if (op == "struct.atomic.rmw.add"sv) {
5052+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWAdd));
5053+
return Ok{};
5054+
}
5055+
goto parse_error;
5056+
case 'n':
5057+
if (op == "struct.atomic.rmw.and"sv) {
5058+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWAnd));
5059+
return Ok{};
5060+
}
5061+
goto parse_error;
5062+
default: goto parse_error;
5063+
}
5064+
}
5065+
case 'c':
5066+
if (op == "struct.atomic.rmw.cmpxchg"sv) {
5067+
CHECK_ERR(makeStructCmpxchg(ctx, pos, annotations));
5068+
return Ok{};
5069+
}
5070+
goto parse_error;
5071+
case 'o':
5072+
if (op == "struct.atomic.rmw.or"sv) {
5073+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWOr));
5074+
return Ok{};
5075+
}
5076+
goto parse_error;
5077+
case 's':
5078+
if (op == "struct.atomic.rmw.sub"sv) {
5079+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWSub));
5080+
return Ok{};
5081+
}
5082+
goto parse_error;
5083+
case 'x': {
5084+
switch (buf[19]) {
5085+
case 'c':
5086+
if (op == "struct.atomic.rmw.xchg"sv) {
5087+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWXchg));
5088+
return Ok{};
5089+
}
5090+
goto parse_error;
5091+
case 'o':
5092+
if (op == "struct.atomic.rmw.xor"sv) {
5093+
CHECK_ERR(makeStructRMW(ctx, pos, annotations, RMWXor));
5094+
return Ok{};
5095+
}
5096+
goto parse_error;
5097+
default: goto parse_error;
5098+
}
5099+
}
5100+
default: goto parse_error;
5101+
}
5102+
}
50465103
case 's':
50475104
if (op == "struct.atomic.set"sv) {
50485105
CHECK_ERR(makeAtomicStructSet(ctx, pos, annotations));

src/ir/ReFinalize.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ void ReFinalize::visitBrOn(BrOn* curr) {
158158
void ReFinalize::visitStructNew(StructNew* curr) { curr->finalize(); }
159159
void ReFinalize::visitStructGet(StructGet* curr) { curr->finalize(); }
160160
void ReFinalize::visitStructSet(StructSet* curr) { curr->finalize(); }
161+
void ReFinalize::visitStructRMW(StructRMW* curr) { curr->finalize(); }
162+
void ReFinalize::visitStructCmpxchg(StructCmpxchg* curr) { curr->finalize(); }
161163
void ReFinalize::visitArrayNew(ArrayNew* curr) { curr->finalize(); }
162164
void ReFinalize::visitArrayNewData(ArrayNewData* curr) { curr->finalize(); }
163165
void ReFinalize::visitArrayNewElem(ArrayNewElem* curr) { curr->finalize(); }

src/ir/child-typer.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,29 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
891891
note(&curr->value, fields[curr->index].type);
892892
}
893893

894+
void visitStructRMW(StructRMW* curr,
895+
std::optional<HeapType> ht = std::nullopt) {
896+
if (!ht) {
897+
ht = curr->ref->type.getHeapType();
898+
}
899+
const auto& fields = ht->getStruct().fields;
900+
assert(curr->index < fields.size());
901+
note(&curr->ref, Type(*ht, Nullable));
902+
note(&curr->value, fields[curr->index].type);
903+
}
904+
905+
void visitStructCmpxchg(StructCmpxchg* curr,
906+
std::optional<HeapType> ht = std::nullopt) {
907+
if (!ht) {
908+
ht = curr->ref->type.getHeapType();
909+
}
910+
const auto& fields = ht->getStruct().fields;
911+
assert(curr->index < fields.size());
912+
note(&curr->ref, Type(*ht, Nullable));
913+
note(&curr->expected, fields[curr->index].type);
914+
note(&curr->replacement, fields[curr->index].type);
915+
}
916+
894917
void visitArrayNew(ArrayNew* curr) {
895918
if (!curr->isWithDefault()) {
896919
note(&curr->init, curr->type.getHeapType().getArray().element.type);

src/ir/cost.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,14 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
687687
CostType visitStructSet(StructSet* curr) {
688688
return 2 + nullCheckCost(curr->ref) + visit(curr->ref) + visit(curr->value);
689689
}
690+
CostType visitStructRMW(StructRMW* curr) {
691+
return AtomicCost + nullCheckCost(curr->ref) + visit(curr->ref) +
692+
visit(curr->value);
693+
}
694+
CostType visitStructCmpxchg(StructCmpxchg* curr) {
695+
return AtomicCost + nullCheckCost(curr->ref) + visit(curr->ref) +
696+
visit(curr->expected) + visit(curr->replacement);
697+
}
690698
CostType visitArrayNew(ArrayNew* curr) {
691699
return 4 + visit(curr->size) + maybeVisit(curr->init);
692700
}

src/ir/effects.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,32 @@ class EffectAnalyzer {
899899
parent.isAtomic = true;
900900
}
901901
}
902+
void visitStructRMW(StructRMW* curr) {
903+
if (curr->ref->type.isNull()) {
904+
parent.trap = true;
905+
return;
906+
}
907+
parent.readsMutableStruct = true;
908+
parent.writesStruct = true;
909+
if (curr->ref->type.isNullable()) {
910+
parent.implicitTrap = true;
911+
}
912+
assert(curr->order != MemoryOrder::Unordered);
913+
parent.isAtomic = true;
914+
}
915+
void visitStructCmpxchg(StructCmpxchg* curr) {
916+
if (curr->ref->type.isNull()) {
917+
parent.trap = true;
918+
return;
919+
}
920+
parent.readsMutableStruct = true;
921+
parent.writesStruct = true;
922+
if (curr->ref->type.isNullable()) {
923+
parent.implicitTrap = true;
924+
}
925+
assert(curr->order != MemoryOrder::Unordered);
926+
parent.isAtomic = true;
927+
}
902928
void visitArrayNew(ArrayNew* curr) {}
903929
void visitArrayNewData(ArrayNewData* curr) {
904930
// Traps on out of bounds access to segments or access to dropped

src/ir/possible-contents.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,22 @@ struct InfoCollector
10161016
addChildParentLink(curr->ref, curr);
10171017
addChildParentLink(curr->value, curr);
10181018
}
1019+
void visitStructRMW(StructRMW* curr) {
1020+
if (curr->ref->type == Type::unreachable) {
1021+
return;
1022+
}
1023+
// TODO: Model the modification part of the RMW in addition to the read and
1024+
// the write.
1025+
addRoot(curr);
1026+
}
1027+
void visitStructCmpxchg(StructCmpxchg* curr) {
1028+
if (curr->ref->type == Type::unreachable) {
1029+
return;
1030+
}
1031+
// TODO: Model the modification part of the RMW in addition to the read and
1032+
// the write.
1033+
addRoot(curr);
1034+
}
10191035
// Array operations access the array's location, parallel to how structs work.
10201036
void visitArrayGet(ArrayGet* curr) {
10211037
if (!isRelevant(curr->ref)) {
@@ -1552,6 +1568,10 @@ void TNHOracle::scan(Function* func,
15521568

15531569
void visitStructGet(StructGet* curr) { notePossibleTrap(curr->ref); }
15541570
void visitStructSet(StructSet* curr) { notePossibleTrap(curr->ref); }
1571+
void visitStructRMW(StructRMW* curr) { notePossibleTrap(curr->ref); }
1572+
void visitStructCmpxchg(StructCmpxchg* curr) {
1573+
notePossibleTrap(curr->ref);
1574+
}
15551575
void visitArrayGet(ArrayGet* curr) { notePossibleTrap(curr->ref); }
15561576
void visitArraySet(ArraySet* curr) { notePossibleTrap(curr->ref); }
15571577
void visitArrayLen(ArrayLen* curr) { notePossibleTrap(curr->ref); }
@@ -2354,7 +2374,8 @@ bool Flower::updateContents(LocationIndex locationIndex,
23542374
// we must only combine the filtered contents (e.g. if 0xff arrives which
23552375
// as a signed read is truly 0xffffffff then we cannot first combine the
23562376
// existing 0xffffffff with the new 0xff, as they are different, and the
2357-
// result will no longer be a constant).
2377+
// result will no longer be a constant). There is no need to filter atomic
2378+
// RMW operations here because they always do unsigned reads.
23582379
filterPackedDataReads(newContents, *exprLoc);
23592380
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
23602381
std::cout << " pre-filtered packed read contents:\n";

src/ir/subtype-exprs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,21 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
324324
const auto& fields = curr->ref->type.getHeapType().getStruct().fields;
325325
self()->noteSubtype(curr->value, fields[curr->index].type);
326326
}
327+
void visitStructRMW(StructRMW* curr) {
328+
if (!curr->ref->type.isStruct()) {
329+
return;
330+
}
331+
const auto& fields = curr->ref->type.getHeapType().getStruct().fields;
332+
self()->noteSubtype(curr->value, fields[curr->index].type);
333+
}
334+
void visitStructCmpxchg(StructCmpxchg* curr) {
335+
if (!curr->ref->type.isStruct()) {
336+
return;
337+
}
338+
const auto& fields = curr->ref->type.getHeapType().getStruct().fields;
339+
self()->noteSubtype(curr->expected, fields[curr->index].type);
340+
self()->noteSubtype(curr->replacement, fields[curr->index].type);
341+
}
327342
void visitArrayNew(ArrayNew* curr) {
328343
if (!curr->type.isArray() || curr->isWithDefault()) {
329344
return;

src/parser/contexts.h

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,20 @@ struct NullInstrParserCtx {
749749
return Ok{};
750750
}
751751
template<typename HeapTypeT>
752+
Result<> makeStructRMW(Index,
753+
const std::vector<Annotation>&,
754+
AtomicRMWOp,
755+
HeapTypeT,
756+
FieldIdxT,
757+
MemoryOrder) {
758+
return Ok{};
759+
}
760+
template<typename HeapTypeT>
761+
Result<> makeStructCmpxchg(
762+
Index, const std::vector<Annotation>&, HeapTypeT, FieldIdxT, MemoryOrder) {
763+
return Ok{};
764+
}
765+
template<typename HeapTypeT>
752766
Result<> makeArrayNew(Index, const std::vector<Annotation>&, HeapTypeT) {
753767
return Ok{};
754768
}
@@ -2453,18 +2467,35 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
24532467
HeapType type,
24542468
Index field,
24552469
bool signed_,
2456-
MemoryOrder order = MemoryOrder::Unordered) {
2470+
MemoryOrder order) {
24572471
return withLoc(pos, irBuilder.makeStructGet(type, field, signed_, order));
24582472
}
24592473

24602474
Result<> makeStructSet(Index pos,
24612475
const std::vector<Annotation>& annotations,
24622476
HeapType type,
24632477
Index field,
2464-
MemoryOrder order = MemoryOrder::Unordered) {
2478+
MemoryOrder order) {
24652479
return withLoc(pos, irBuilder.makeStructSet(type, field, order));
24662480
}
24672481

2482+
Result<> makeStructRMW(Index pos,
2483+
const std::vector<Annotation>& annotations,
2484+
AtomicRMWOp op,
2485+
HeapType type,
2486+
Index field,
2487+
MemoryOrder order) {
2488+
return withLoc(pos, irBuilder.makeStructRMW(op, type, field, order));
2489+
}
2490+
2491+
Result<> makeStructCmpxchg(Index pos,
2492+
const std::vector<Annotation>& annotations,
2493+
HeapType type,
2494+
Index field,
2495+
MemoryOrder order) {
2496+
return withLoc(pos, irBuilder.makeStructCmpxchg(type, field, order));
2497+
}
2498+
24682499
Result<> makeArrayNew(Index pos,
24692500
const std::vector<Annotation>& annotations,
24702501
HeapType type) {

src/parser/parsers.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ Result<> makeStructSet(Ctx&, Index, const std::vector<Annotation>&);
256256
template<typename Ctx>
257257
Result<> makeAtomicStructSet(Ctx&, Index, const std::vector<Annotation>&);
258258
template<typename Ctx>
259+
Result<> makeStructRMW(AtomicRMWOp, Index, const std::vector<Annotation>&);
260+
template<typename Ctx>
261+
Result<> makeStructCmpxchg(Index, const std::vector<Annotation>&);
262+
template<typename Ctx>
259263
Result<>
260264
makeArrayNew(Ctx&, Index, const std::vector<Annotation>&, bool default_);
261265
template<typename Ctx>
@@ -2282,6 +2286,43 @@ Result<> makeAtomicStructSet(Ctx& ctx,
22822286
return ctx.makeStructSet(pos, annotations, *type, *field, *order);
22832287
}
22842288

2289+
template<typename Ctx>
2290+
Result<> makeStructRMW(Ctx& ctx,
2291+
Index pos,
2292+
const std::vector<Annotation>& annotations,
2293+
AtomicRMWOp op) {
2294+
auto order1 = memorder(ctx);
2295+
CHECK_ERR(order1);
2296+
auto order2 = memorder(ctx);
2297+
CHECK_ERR(order2);
2298+
if (*order1 != *order2) {
2299+
return ctx.in.err(pos, "struct.atomic.rmw memory orders must be identical");
2300+
}
2301+
auto type = typeidx(ctx);
2302+
CHECK_ERR(type);
2303+
auto field = fieldidx(ctx, *type);
2304+
CHECK_ERR(field);
2305+
return ctx.makeStructRMW(pos, annotations, op, *type, *field, *order1);
2306+
}
2307+
2308+
template<typename Ctx>
2309+
Result<> makeStructCmpxchg(Ctx& ctx,
2310+
Index pos,
2311+
const std::vector<Annotation>& annotations) {
2312+
auto order1 = memorder(ctx);
2313+
CHECK_ERR(order1);
2314+
auto order2 = memorder(ctx);
2315+
CHECK_ERR(order2);
2316+
if (*order1 != *order2) {
2317+
return ctx.in.err(pos, "struct.atomic.rmw memory orders must be identical");
2318+
}
2319+
auto type = typeidx(ctx);
2320+
CHECK_ERR(type);
2321+
auto field = fieldidx(ctx, *type);
2322+
CHECK_ERR(field);
2323+
return ctx.makeStructCmpxchg(pos, annotations, *type, *field, *order1);
2324+
}
2325+
22852326
template<typename Ctx>
22862327
Result<> makeArrayNew(Ctx& ctx,
22872328
Index pos,

0 commit comments

Comments
 (0)