Skip to content

Commit 5283ba0

Browse files
authored
GUFA: Don't misoptimize struct/array atomics (#8484)
For now, just write an unknown value of the proper type. Simplify ArrayStore similarly: remove special multibyte handling and just pass in an unknown value of the type. Fixes #8473
1 parent d3ca4a0 commit 5283ba0

3 files changed

Lines changed: 315 additions & 33 deletions

File tree

scripts/test/fuzzing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
'cfp-rmw.wast',
8383
'unsubtyping-cmpxchg.wast',
8484
'struct-atomic-threads.wast',
85+
'type-refining-gufa-rmw.wast',
8586
# contains too many segments to run in a wasm VM
8687
'limit-segments_disable-bulk-memory.wast',
8788
# https://github.com/WebAssembly/binaryen/issues/7176

src/ir/possible-contents.cpp

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,16 +1073,19 @@ struct InfoCollector
10731073
if (curr->ref->type == Type::unreachable) {
10741074
return;
10751075
}
1076-
// TODO: Model the modification part of the RMW in addition to the read and
1077-
// the write.
1076+
addChildParentLink(curr->ref, curr);
1077+
addChildParentLink(curr->value, curr);
1078+
// TODO: Model the output
10781079
addRoot(curr);
10791080
}
10801081
void visitStructCmpxchg(StructCmpxchg* curr) {
10811082
if (curr->ref->type == Type::unreachable) {
10821083
return;
10831084
}
1084-
// TODO: Model the modification part of the RMW in addition to the read and
1085-
// the write.
1085+
addChildParentLink(curr->ref, curr);
1086+
addChildParentLink(curr->expected, curr);
1087+
addChildParentLink(curr->replacement, curr);
1088+
// TODO: Model the output
10861089
addRoot(curr);
10871090
}
10881091
void visitStructWait(StructWait* curr) { addRoot(curr); }
@@ -1171,16 +1174,19 @@ struct InfoCollector
11711174
if (curr->ref->type == Type::unreachable) {
11721175
return;
11731176
}
1174-
// TODO: Model the modification part of the RMW in addition to the read and
1175-
// the write.
1177+
addChildParentLink(curr->ref, curr);
1178+
addChildParentLink(curr->value, curr);
1179+
// TODO: Model the output
11761180
addRoot(curr);
11771181
}
11781182
void visitArrayCmpxchg(ArrayCmpxchg* curr) {
11791183
if (curr->ref->type == Type::unreachable) {
11801184
return;
11811185
}
1182-
// TODO: Model the modification part of the RMW in addition to the read and
1183-
// the write.
1186+
addChildParentLink(curr->ref, curr);
1187+
addChildParentLink(curr->expected, curr);
1188+
addChildParentLink(curr->replacement, curr);
1189+
// TODO: Model the output
11841190
addRoot(curr);
11851191
}
11861192
void visitStringNew(StringNew* curr) {
@@ -2247,10 +2253,12 @@ struct Flower {
22472253
Expression* read);
22482254

22492255
// Similar to readFromData, but does a write for a struct.set or array.set.
2256+
void writeToData(Expression* ref, Expression* value, Index fieldIndex);
2257+
2258+
// A write with given contents.
22502259
void writeToData(Expression* ref,
2251-
Expression* value,
2252-
Index fieldIndex,
2253-
bool multibyte = false);
2260+
const PossibleContents& valueContents,
2261+
Index fieldIndex);
22542262

22552263
// We will need subtypes during the flow, so compute them once ahead of time.
22562264
std::unique_ptr<SubTypes> subTypes;
@@ -2839,6 +2847,18 @@ void Flower::flowAfterUpdate(LocationIndex locationIndex) {
28392847
// |child| is either the reference or the value child of a struct.set.
28402848
assert(set->ref == child || set->value == child);
28412849
writeToData(set->ref, set->value, set->index);
2850+
} else if (auto* set = parent->dynCast<StructRMW>()) {
2851+
assert(set->ref == child || set->value == child);
2852+
// TODO: model the stored value, depending on the actual operation.
2853+
writeToData(
2854+
set->ref, PossibleContents::fromType(set->value->type), set->index);
2855+
} else if (auto* set = parent->dynCast<StructCmpxchg>()) {
2856+
assert(set->ref == child || set->expected == child ||
2857+
set->replacement == child);
2858+
// TODO: model the stored value, depending on the actual operation.
2859+
writeToData(set->ref,
2860+
PossibleContents::fromType(set->replacement->type),
2861+
set->index);
28422862
} else if (auto* get = parent->dynCast<ArrayGet>()) {
28432863
assert(get->ref == child);
28442864
readFromData(get->ref->type, 0, contents, get);
@@ -2847,9 +2867,21 @@ void Flower::flowAfterUpdate(LocationIndex locationIndex) {
28472867
writeToData(set->ref, set->value, 0);
28482868
} else if (auto* store = parent->dynCast<ArrayStore>()) {
28492869
assert(store->ref == child || store->value == child);
2850-
writeToData(store->ref, store->value, 0, /*multibyte*/ true);
2870+
// TODO: model the stored value, and handle different but equal values in
2871+
// type, e.g. writing i16 0x1212 is the same as i8 0x12.
2872+
writeToData(
2873+
store->ref, PossibleContents::fromType(store->value->type), 0);
2874+
} else if (auto* set = parent->dynCast<ArrayRMW>()) {
2875+
assert(set->ref == child || set->value == child);
2876+
// TODO: model the stored value, depending on the actual operation.
2877+
writeToData(set->ref, PossibleContents::fromType(set->value->type), 0);
2878+
} else if (auto* set = parent->dynCast<ArrayCmpxchg>()) {
2879+
assert(set->ref == child || set->expected == child ||
2880+
set->replacement == child);
2881+
// TODO: model the stored value, depending on the actual operation.
2882+
writeToData(
2883+
set->ref, PossibleContents::fromType(set->replacement->type), 0);
28512884
} else if (auto* get = parent->dynCast<RefGetDesc>()) {
2852-
// Similar to struct.get.
28532885
assert(get->ref == child);
28542886
readFromData(
28552887
get->ref->type, DataLocation::DescriptorIndex, contents, get);
@@ -3214,23 +3246,7 @@ void Flower::readFromData(Type declaredType,
32143246
connectDuringFlow(coneReadLocation, ExpressionLocation{read, 0});
32153247
}
32163248

3217-
void Flower::writeToData(Expression* ref,
3218-
Expression* value,
3219-
Index fieldIndex,
3220-
bool multibyte) {
3221-
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
3222-
std::cout << " add special writes\n";
3223-
#endif
3224-
3225-
auto refContents = getContents(getIndex(ExpressionLocation{ref, 0}));
3226-
3227-
#ifndef NDEBUG
3228-
// We must not have anything in the reference that is invalid for the wasm
3229-
// type there.
3230-
auto maximalContents = PossibleContents::coneType(ref->type);
3231-
assert(PossibleContents::isSubContents(refContents, maximalContents));
3232-
#endif
3233-
3249+
void Flower::writeToData(Expression* ref, Expression* value, Index fieldIndex) {
32343250
// We could set up links here as we do for reads, but as we get to this code
32353251
// in any case, we can just flow the values forward directly. This avoids
32363252
// adding any links (edges) to the graph (and edges are what we want to avoid
@@ -3253,6 +3269,24 @@ void Flower::writeToData(Expression* ref,
32533269
// reference and value.)
32543270

32553271
auto valueContents = getContents(getIndex(ExpressionLocation{value, 0}));
3272+
writeToData(ref, valueContents, fieldIndex);
3273+
}
3274+
3275+
void Flower::writeToData(Expression* ref,
3276+
const PossibleContents& valueContents,
3277+
Index fieldIndex) {
3278+
#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2
3279+
std::cout << " add special writes\n";
3280+
#endif
3281+
3282+
auto refContents = getContents(getIndex(ExpressionLocation{ref, 0}));
3283+
3284+
#ifndef NDEBUG
3285+
// We must not have anything in the reference that is invalid for the wasm
3286+
// type there.
3287+
auto maximalContents = PossibleContents::coneType(ref->type);
3288+
assert(PossibleContents::isSubContents(refContents, maximalContents));
3289+
#endif
32563290

32573291
// See the related comment in readFromData() as to why these are the only
32583292
// things we need to check, and why the assertion afterwards contains the only
@@ -3265,9 +3299,6 @@ void Flower::writeToData(Expression* ref,
32653299
// As in readFromData, normalize to the proper cone.
32663300
auto cone = refContents.getCone();
32673301
auto normalizedDepth = getNormalizedConeDepth(cone.type, cone.depth);
3268-
if (multibyte) {
3269-
valueContents = PossibleContents::fromType(value->type);
3270-
}
32713302

32723303
subTypes->iterSubTypes(
32733304
cone.type.getHeapType(), normalizedDepth, [&](HeapType type, Index depth) {

0 commit comments

Comments
 (0)