Skip to content

Commit 155d0b5

Browse files
authored
Atomic wait/wake fixes (#1383)
* fix wait and wake binary format support, they have alignments and offsets * don't emit unreachable parts of atomic operations, for simplicity and to avoid special handling * don't emit atomic waits by default in the fuzzer, they hang in native vm support
1 parent 02729a1 commit 155d0b5

File tree

9 files changed

+61
-17
lines changed

9 files changed

+61
-17
lines changed

src/binaryen-c.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, BinaryenIn
809809
return static_cast<Expression*>(ret);
810810
}
811811
BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef timeout, BinaryenType expectedType) {
812-
auto* ret = Builder(*((Module*)module)).makeAtomicWait((Expression*)ptr, (Expression*)expected, (Expression*)timeout, WasmType(expectedType));
812+
auto* ret = Builder(*((Module*)module)).makeAtomicWait((Expression*)ptr, (Expression*)expected, (Expression*)timeout, WasmType(expectedType), 0);
813813

814814
if (tracing) {
815815
auto id = noteExpression(ret);
@@ -819,7 +819,7 @@ BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, BinaryenExpre
819819
return static_cast<Expression*>(ret);
820820
}
821821
BinaryenExpressionRef BinaryenAtomicWake(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef wakeCount) {
822-
auto* ret = Builder(*((Module*)module)).makeAtomicWake((Expression*)ptr, (Expression*)wakeCount);
822+
auto* ret = Builder(*((Module*)module)).makeAtomicWake((Expression*)ptr, (Expression*)wakeCount, 0);
823823

824824
if (tracing) {
825825
auto id = noteExpression(ret);

src/ir/ExpressionAnalyzer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,15 @@ uint32_t ExpressionAnalyzer::hash(Expression* curr) {
492492
break;
493493
}
494494
case Expression::Id::AtomicWaitId: {
495+
HASH(AtomicWait, offset);
495496
HASH(AtomicWait, expectedType);
496497
PUSH(AtomicWait, ptr);
497498
PUSH(AtomicWait, expected);
498499
PUSH(AtomicWait, timeout);
499500
break;
500501
}
501502
case Expression::Id::AtomicWakeId: {
503+
HASH(AtomicWake, offset);
502504
PUSH(AtomicWake, ptr);
503505
PUSH(AtomicWake, wakeCount);
504506
break;

src/ir/ExpressionManipulator.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom
116116
curr->type);
117117
}
118118
Expression* visitAtomicWait(AtomicWait* curr) {
119-
return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->expectedType);
119+
return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->expectedType, curr->offset);
120120
}
121121
Expression* visitAtomicWake(AtomicWake* curr) {
122-
return builder.makeAtomicWake(copy(curr->ptr), copy(curr->wakeCount));
122+
return builder.makeAtomicWake(copy(curr->ptr), copy(curr->wakeCount), curr->offset);
123123
}
124124
Expression* visitConst(Const *curr) {
125125
return builder.makeConst(curr->value);

src/passes/Print.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
411411
o << '(' ;
412412
prepareColor(o);
413413
o << printWasmType(curr->expectedType) << ".wait";
414+
if (curr->offset) {
415+
o << " offset=" << curr->offset;
416+
}
414417
restoreNormalColor(o);
415418
incIndent();
416419
printFullLine(curr->ptr);
@@ -420,6 +423,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
420423
}
421424
void visitAtomicWake(AtomicWake* curr) {
422425
printOpening(o, "wake");
426+
if (curr->offset) {
427+
o << " offset=" << curr->offset;
428+
}
423429
incIndent();
424430
printFullLine(curr->ptr);
425431
printFullLine(curr->wakeCount);

src/tools/fuzzing.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ class TranslateToFuzzReader {
174174
// Whether to emit atomics
175175
static const bool ATOMICS = true;
176176

177+
// Whether to emit atomic waits (which in single-threaded mode, may hang...)
178+
static const bool ATOMIC_WAITS = false;
179+
177180
// after we finish the input, we start going through it again, but xoring
178181
// so it's not identical
179182
int xorFactor = 0;
@@ -1242,16 +1245,16 @@ class TranslateToFuzzReader {
12421245
if (!ATOMICS || (type != i32 && type != i64)) return makeTrivial(type);
12431246
wasm.memory.shared = true;
12441247
if (type == i32 && oneIn(2)) {
1245-
if (oneIn(2)) {
1248+
if (ATOMIC_WAITS && oneIn(2)) {
12461249
auto* ptr = makePointer();
12471250
auto expectedType = pick(i32, i64);
12481251
auto* expected = make(expectedType);
12491252
auto* timeout = make(i64);
1250-
return builder.makeAtomicWait(ptr, expected, timeout, expectedType);
1253+
return builder.makeAtomicWait(ptr, expected, timeout, expectedType, logify(get()));
12511254
} else {
12521255
auto* ptr = makePointer();
12531256
auto* count = make(i32);
1254-
return builder.makeAtomicWake(ptr, count);
1257+
return builder.makeAtomicWake(ptr, count, logify(get()));
12551258
}
12561259
}
12571260
Index bytes;

src/wasm-binary.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ class WasmBinaryBuilder {
959959
void visitSetLocal(SetLocal *curr, uint8_t code);
960960
void visitGetGlobal(GetGlobal *curr);
961961
void visitSetGlobal(SetGlobal *curr);
962-
void readMemoryAccess(Address& alignment, size_t bytes, Address& offset);
962+
void readMemoryAccess(Address& alignment, Address& offset);
963963
bool maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic);
964964
bool maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic);
965965
bool maybeVisitAtomicRMW(Expression*& out, uint8_t code);

src/wasm-builder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,19 @@ class Builder {
240240
load->isAtomic = true;
241241
return load;
242242
}
243-
AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType expectedType) {
243+
AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType expectedType, Address offset) {
244244
auto* wait = allocator.alloc<AtomicWait>();
245+
wait->offset = offset;
245246
wait->ptr = ptr;
246247
wait->expected = expected;
247248
wait->timeout = timeout;
248249
wait->expectedType = expectedType;
249250
wait->finalize();
250251
return wait;
251252
}
252-
AtomicWake* makeAtomicWake(Expression* ptr, Expression* wakeCount) {
253+
AtomicWake* makeAtomicWake(Expression* ptr, Expression* wakeCount, Address offset) {
253254
auto* wake = allocator.alloc<AtomicWake>();
255+
wake->offset = offset;
254256
wake->ptr = ptr;
255257
wake->wakeCount = wakeCount;
256258
wake->finalize();

src/wasm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ class AtomicWait : public SpecificExpression<Expression::AtomicWaitId> {
474474
AtomicWait() = default;
475475
AtomicWait(MixedArena& allocator) : AtomicWait() {}
476476

477+
Address offset;
477478
Expression* ptr;
478479
Expression* expected;
479480
Expression* timeout;
@@ -487,6 +488,7 @@ class AtomicWake : public SpecificExpression<Expression::AtomicWakeId> {
487488
AtomicWake() = default;
488489
AtomicWake(MixedArena& allocator) : AtomicWake() {}
489490

491+
Address offset;
490492
Expression* ptr;
491493
Expression* wakeCount;
492494

src/wasm/wasm-binary.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,10 @@ void WasmBinaryWriter::visitStore(Store *curr) {
954954
void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) {
955955
if (debug) std::cerr << "zz node: AtomicRMW" << std::endl;
956956
recurse(curr->ptr);
957+
// stop if the rest isn't reachable anyhow
958+
if (curr->ptr->type == unreachable) return;
957959
recurse(curr->value);
960+
if (curr->value->type == unreachable) return;
958961

959962
if (curr->type == unreachable) {
960963
// don't even emit it; we don't know the right type
@@ -1005,8 +1008,12 @@ void WasmBinaryWriter::visitAtomicRMW(AtomicRMW *curr) {
10051008
void WasmBinaryWriter::visitAtomicCmpxchg(AtomicCmpxchg *curr) {
10061009
if (debug) std::cerr << "zz node: AtomicCmpxchg" << std::endl;
10071010
recurse(curr->ptr);
1011+
// stop if the rest isn't reachable anyhow
1012+
if (curr->ptr->type == unreachable) return;
10081013
recurse(curr->expected);
1014+
if (curr->expected->type == unreachable) return;
10091015
recurse(curr->replacement);
1016+
if (curr->replacement->type == unreachable) return;
10101017

10111018
if (curr->type == unreachable) {
10121019
// don't even emit it; we don't know the right type
@@ -1041,23 +1048,39 @@ void WasmBinaryWriter::visitAtomicCmpxchg(AtomicCmpxchg *curr) {
10411048
void WasmBinaryWriter::visitAtomicWait(AtomicWait *curr) {
10421049
if (debug) std::cerr << "zz node: AtomicWait" << std::endl;
10431050
recurse(curr->ptr);
1051+
// stop if the rest isn't reachable anyhow
1052+
if (curr->ptr->type == unreachable) return;
10441053
recurse(curr->expected);
1054+
if (curr->expected->type == unreachable) return;
10451055
recurse(curr->timeout);
1056+
if (curr->timeout->type == unreachable) return;
10461057

10471058
o << int8_t(BinaryConsts::AtomicPrefix);
10481059
switch (curr->expectedType) {
1049-
case i32: o << int8_t(BinaryConsts::I32AtomicWait); break;
1050-
case i64: o << int8_t(BinaryConsts::I64AtomicWait); break;
1060+
case i32: {
1061+
o << int8_t(BinaryConsts::I32AtomicWait);
1062+
emitMemoryAccess(4, 4, 0);
1063+
break;
1064+
}
1065+
case i64: {
1066+
o << int8_t(BinaryConsts::I64AtomicWait);
1067+
emitMemoryAccess(8, 8, 0);
1068+
break;
1069+
}
10511070
default: WASM_UNREACHABLE();
10521071
}
10531072
}
10541073

10551074
void WasmBinaryWriter::visitAtomicWake(AtomicWake *curr) {
10561075
if (debug) std::cerr << "zz node: AtomicWake" << std::endl;
10571076
recurse(curr->ptr);
1077+
// stop if the rest isn't reachable anyhow
1078+
if (curr->ptr->type == unreachable) return;
10581079
recurse(curr->wakeCount);
1080+
if (curr->wakeCount->type == unreachable) return;
10591081

10601082
o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicWake);
1083+
emitMemoryAccess(4, 4, 0);
10611084
}
10621085

10631086
void WasmBinaryWriter::visitConst(Const *curr) {
@@ -2555,7 +2578,7 @@ void WasmBinaryBuilder::visitSetGlobal(SetGlobal *curr) {
25552578
curr->finalize();
25562579
}
25572580

2558-
void WasmBinaryBuilder::readMemoryAccess(Address& alignment, size_t bytes, Address& offset) {
2581+
void WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) {
25592582
auto rawAlignment = getU32LEB();
25602583
if (rawAlignment > 4) throw ParseException("Alignment must be of a reasonable size");
25612584
alignment = Pow2(rawAlignment);
@@ -2599,7 +2622,7 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAt
25992622
}
26002623

26012624
curr->isAtomic = isAtomic;
2602-
readMemoryAccess(curr->align, curr->bytes, curr->offset);
2625+
readMemoryAccess(curr->align, curr->offset);
26032626
curr->ptr = popNonVoidExpression();
26042627
curr->finalize();
26052628
out = curr;
@@ -2636,7 +2659,7 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA
26362659

26372660
curr->isAtomic = isAtomic;
26382661
if (debug) std::cerr << "zz node: Store" << std::endl;
2639-
readMemoryAccess(curr->align, curr->bytes, curr->offset);
2662+
readMemoryAccess(curr->align, curr->offset);
26402663
curr->value = popNonVoidExpression();
26412664
curr->ptr = popNonVoidExpression();
26422665
curr->finalize();
@@ -2679,7 +2702,7 @@ bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) {
26792702

26802703
if (debug) std::cerr << "zz node: AtomicRMW" << std::endl;
26812704
Address readAlign;
2682-
readMemoryAccess(readAlign, curr->bytes, curr->offset);
2705+
readMemoryAccess(readAlign, curr->offset);
26832706
if (readAlign != curr->bytes) throw ParseException("Align of AtomicRMW must match size");
26842707
curr->value = popNonVoidExpression();
26852708
curr->ptr = popNonVoidExpression();
@@ -2710,7 +2733,7 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code)
27102733

27112734
if (debug) std::cerr << "zz node: AtomicCmpxchg" << std::endl;
27122735
Address readAlign;
2713-
readMemoryAccess(readAlign, curr->bytes, curr->offset);
2736+
readMemoryAccess(readAlign, curr->offset);
27142737
if (readAlign != curr->bytes) throw ParseException("Align of AtomicCpxchg must match size");
27152738
curr->replacement = popNonVoidExpression();
27162739
curr->expected = popNonVoidExpression();
@@ -2734,6 +2757,9 @@ bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) {
27342757
curr->timeout = popNonVoidExpression();
27352758
curr->expected = popNonVoidExpression();
27362759
curr->ptr = popNonVoidExpression();
2760+
Address readAlign;
2761+
readMemoryAccess(readAlign, curr->offset);
2762+
if (readAlign != getWasmTypeSize(curr->expectedType)) throw ParseException("Align of AtomicWait must match size");
27372763
curr->finalize();
27382764
out = curr;
27392765
return true;
@@ -2747,6 +2773,9 @@ bool WasmBinaryBuilder::maybeVisitAtomicWake(Expression*& out, uint8_t code) {
27472773
curr->type = i32;
27482774
curr->wakeCount = popNonVoidExpression();
27492775
curr->ptr = popNonVoidExpression();
2776+
Address readAlign;
2777+
readMemoryAccess(readAlign, curr->offset);
2778+
if (readAlign != getWasmTypeSize(curr->type)) throw ParseException("Align of AtomicWake must match size");
27502779
curr->finalize();
27512780
out = curr;
27522781
return true;

0 commit comments

Comments
 (0)