Skip to content

Commit 9e623c7

Browse files
authored
Generate Struct RMW operations in the fuzzer (#8535)
And allow fuzzing of initial contents that contain these operations.
1 parent 1a8150c commit 9e623c7

3 files changed

Lines changed: 73 additions & 18 deletions

File tree

scripts/test/fuzzing.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,6 @@
7070
# the fuzzer does not support imported memories
7171
'multi-memory-lowering-import.wast',
7272
'multi-memory-lowering-import-error.wast',
73-
# the fuzzer does not support struct RMW ops
74-
'gc-atomics.wast',
75-
'gc-atomics-null-refs.wast',
76-
'shared-structs.wast',
77-
'heap2local-rmw.wast',
78-
'optimize-instructions-struct-rmw.wast',
79-
'gto-removals-rmw.wast',
80-
'type-refining-rmw.wast',
81-
'type-ssa-exact-rmw.wast',
82-
'cfp-rmw.wast',
83-
'unsubtyping-cmpxchg.wast',
84-
'struct-atomic-threads.wast',
85-
'type-refining-gufa-rmw.wast',
86-
'struct-cmpxchg-shared-expected.wast',
87-
'precompute-gc-atomics-rmw.wast',
8873
# contains too many segments to run in a wasm VM
8974
'limit-segments_disable-bulk-memory.wast',
9075
# https://github.com/WebAssembly/binaryen/issues/7176

src/tools/fuzzing.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,8 @@ class TranslateToFuzzReader {
540540
bool maybeSignedGet(const Field& field);
541541

542542
Expression* makeStructGet(Type type);
543+
Expression* makeStructRMW(Type type);
544+
Expression* makeStructCmpxchg(Type type);
543545
Expression* makeStructSet(Type type);
544546
Expression* makeArrayGet(Type type);
545547
Expression* makeArraySet(Type type);

src/tools/fuzzing/fuzzing.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,6 +2598,12 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
25982598
if (typeStructFields.find(type) != typeStructFields.end()) {
25992599
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
26002600
&Self::makeStructGet);
2601+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2602+
FeatureSet::Atomics | FeatureSet::SharedEverything,
2603+
&Self::makeStructRMW);
2604+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
2605+
FeatureSet::Atomics | FeatureSet::SharedEverything,
2606+
&Self::makeStructCmpxchg);
26012607
}
26022608
if (typeArrays.find(type) != typeArrays.end()) {
26032609
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
@@ -5583,8 +5589,65 @@ Expression* TranslateToFuzzReader::makeStructGet(Type type) {
55835589
auto [structType, fieldIndex] = pick(structFields);
55845590
auto* ref = makeTrappingRefUse(structType);
55855591
auto signed_ = maybeSignedGet(structType.getStruct().fields[fieldIndex]);
5586-
return builder.makeStructGet(
5587-
fieldIndex, ref, MemoryOrder::Unordered, type, signed_);
5592+
auto order = MemoryOrder::Unordered;
5593+
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
5594+
oneIn(2)) {
5595+
order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5596+
}
5597+
return builder.makeStructGet(fieldIndex, ref, order, type, signed_);
5598+
}
5599+
5600+
Expression* TranslateToFuzzReader::makeStructRMW(Type type) {
5601+
bool isAny =
5602+
type.isRef() &&
5603+
HeapType(type.getHeapType().getTop()).isMaybeShared(HeapType::any);
5604+
if (type != Type::i32 && type != Type::i64 && !isAny) {
5605+
// Not a valid field type for an RMW operation.
5606+
return makeStructGet(type);
5607+
}
5608+
auto& structFields = typeStructFields[type];
5609+
assert(!structFields.empty());
5610+
auto [structType, fieldIndex] = pick(structFields);
5611+
const auto& field = structType.getStruct().fields[fieldIndex];
5612+
if (field.isPacked() || field.mutable_ == Immutable) {
5613+
// Cannot RMW a packed or immutable field.
5614+
return makeStructGet(type);
5615+
}
5616+
AtomicRMWOp op = RMWXchg;
5617+
if (type == Type::i32 || type == Type::i64) {
5618+
op = pick(RMWAdd, RMWSub, RMWAnd, RMWOr, RMWXor, RMWXchg);
5619+
}
5620+
auto* ref = makeTrappingRefUse(structType);
5621+
auto* value = make(type);
5622+
auto order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5623+
return builder.makeStructRMW(op, fieldIndex, ref, value, order);
5624+
}
5625+
5626+
Expression* TranslateToFuzzReader::makeStructCmpxchg(Type type) {
5627+
bool isShared = type.isRef() && type.getHeapType().isShared();
5628+
Type eq(HeapTypes::eq.getBasic(isShared ? Shared : Unshared), Nullable);
5629+
bool isEq = Type::isSubType(type, eq);
5630+
if (type != Type::i32 && type != Type::i64 && !isEq) {
5631+
// Not a valid field type for a cmpxchg operation.
5632+
return makeStructGet(type);
5633+
}
5634+
auto& structFields = typeStructFields[type];
5635+
assert(!structFields.empty());
5636+
auto [structType, fieldIndex] = pick(structFields);
5637+
const auto& field = structType.getStruct().fields[fieldIndex];
5638+
if (field.isPacked() || field.mutable_ == Immutable) {
5639+
// Cannot RMW a packed or immutable field.
5640+
return makeStructGet(type);
5641+
}
5642+
auto* ref = makeTrappingRefUse(structType);
5643+
// For reference fields, expected can be a subtype of eq. Only do use the
5644+
// extra flexibility occasionally because it makes the expected value less
5645+
// likely to be equal to the actual value.
5646+
auto* expected = make(isEq && oneIn(4) ? eq : type);
5647+
auto* replacement = make(type);
5648+
auto order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5649+
return builder.makeStructCmpxchg(
5650+
fieldIndex, ref, expected, replacement, order);
55885651
}
55895652

55905653
Expression* TranslateToFuzzReader::makeStructSet(Type type) {
@@ -5596,7 +5659,12 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) {
55965659
auto fieldType = structType.getStruct().fields[fieldIndex].type;
55975660
auto* ref = makeTrappingRefUse(structType);
55985661
auto* value = make(fieldType);
5599-
return builder.makeStructSet(fieldIndex, ref, value, MemoryOrder::Unordered);
5662+
auto order = MemoryOrder::Unordered;
5663+
if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() &&
5664+
oneIn(2)) {
5665+
order = oneIn(2) ? MemoryOrder::SeqCst : MemoryOrder::AcqRel;
5666+
}
5667+
return builder.makeStructSet(fieldIndex, ref, value, order);
56005668
}
56015669

56025670
// Make a bounds check for an array operation, given a ref + index. An optional

0 commit comments

Comments
 (0)