diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 736920829fe..c6085511690 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -220,6 +220,9 @@ class TranslateToFuzzReader { // All struct fields that are mutable. std::vector mutableStructFields; + // All struct fields that can be waited on. + std::vector structWaitFields; + // All arrays that are mutable. std::vector mutableArrays; @@ -560,6 +563,8 @@ class TranslateToFuzzReader { Expression* makeStructRMW(Type type); Expression* makeStructCmpxchg(Type type); Expression* makeStructSet(Type type); + Expression* makeStructWait(Type type); + Expression* makeWaitqueueNotify(Type type); Expression* makeArrayGet(Type type); Expression* makeArraySet(Type type); Expression* makeArrayRMW(Type type); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 65444bc6355..cd812914919 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -560,12 +560,20 @@ void TranslateToFuzzReader::setupHeapTypes() { interestingHeapSubTypes[struct_].push_back(type); interestingHeapSubTypes[eq].push_back(type); interestingHeapSubTypes[any].push_back(type); - // Note the mutable fields. - auto& fields = type.getStruct().fields; + // Note the mutable fields and fields that can be waited on. + const auto& fields = type.getStruct().fields; for (Index i = 0; i < fields.size(); i++) { if (fields[i].mutable_) { mutableStructFields.push_back(StructField{type, i}); } + if (!fields[i].isPacked()) { + auto fieldType = fields[i].type; + if (fieldType == Type::i32 || fieldType == Type::i64 || + Type::isSubType( + fieldType, Type(HeapTypes::eq.getBasic(Shared), Nullable))) { + structWaitFields.push_back(StructField{type, i}); + } + } } break; } @@ -1709,6 +1717,18 @@ void TranslateToFuzzReader::processFunctions() { } } + if (!ATOMIC_WAITS) { + for (auto& func : wasm.functions) { + if (!func->imported()) { + for (auto* wait : FindAll(func->body).list) { + if (wait->timeout->type == Type::i64) { + wait->timeout = builder.makeConst(int64_t(0)); + } + } + } + } + } + // Also fix up closed world, if we need to. We must do this at the end, so // nothing can break the closed world assumptions after. if (worldMode == WorldMode::Closed) { @@ -1858,6 +1878,13 @@ void TranslateToFuzzReader::addHangLimitChecks(Function* func) { AndInt32, arrayNew->size, builder.makeConst(int32_t(1024 - 1))); } } + if (!ATOMIC_WAITS) { + for (auto* wait : FindAll(func->body).list) { + if (wait->timeout->type == Type::i64) { + wait->timeout = builder.makeConst(int64_t(0)); + } + } + } } void TranslateToFuzzReader::recombine(Function* func) { @@ -2393,6 +2420,14 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) { } fixer(wasm, *this); fixer.walk(func->body); + if (!ATOMIC_WAITS) { + for (auto* wait : FindAll(func->body).list) { + if (wait->timeout->type == Type::i64) { + wait->timeout = builder.makeConst(int64_t(0)); + } + } + } + // Refinalize at the end, after labels are all fixed up. ReFinalize().walkFunctionInModule(func, &wasm); } @@ -2838,6 +2873,11 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { &Self::makeStringEq, &Self::makeStringMeasure, &Self::makeStringGet); + options.add(FeatureSet::ReferenceTypes | FeatureSet::SharedEverything, + &Self::makeWaitqueueNotify); + options.add(FeatureSet::ReferenceTypes | FeatureSet::GC | + FeatureSet::SharedEverything, + &Self::makeStructWait); } if (type == Type::i64) { options.add(FeatureSet::WideArithmetic | FeatureSet::Multivalue, @@ -4364,7 +4404,8 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) { case HeapType::noext: case HeapType::nofunc: case HeapType::nocont: - case HeapType::noexn: { + case HeapType::noexn: + case HeapType::nowaitqueue: { auto null = builder.makeRefNull(heapType.getBasic(share)); if (!type.isNullable()) { return builder.makeRefAs(RefAsNonNull, null); @@ -4372,9 +4413,11 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) { return null; } - case HeapType::waitqueue: - case HeapType::nowaitqueue: { - WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer"); + case HeapType::waitqueue: { + if (type.isNullable() && oneIn(2)) { + return builder.makeRefNull(HeapTypes::sharedWaitqueue.getBasic(share)); + } + return builder.makeWaitqueueNew(); } } WASM_UNREACHABLE("invalid basic ref type"); @@ -6017,8 +6060,11 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) { return makeTrivial(type); } auto [structType, fieldIndex] = pick(mutableStructFields); - auto fieldType = structType.getStruct().fields[fieldIndex].type; auto* ref = makeTrappingRefUse(structType); + auto fieldType = structType.getStruct().fields[fieldIndex].type; + if (ref->type.isStruct()) { + fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type; + } auto* value = make(fieldType); auto order = MemoryOrder::Unordered; if (wasm.features.hasAtomics() && wasm.features.hasSharedEverything() && @@ -6028,6 +6074,35 @@ Expression* TranslateToFuzzReader::makeStructSet(Type type) { return builder.makeStructSet(fieldIndex, ref, value, order); } +Expression* TranslateToFuzzReader::makeStructWait(Type type) { + assert(type == Type::i32); + if (structWaitFields.empty()) { + return makeTrivial(type); + } + auto [structType, fieldIndex] = pick(structWaitFields); + auto* ref = makeTrappingRefUse(structType); + auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable)); + auto fieldType = structType.getStruct().fields[fieldIndex].type; + if (ref->type.isStruct()) { + fieldType = ref->type.getHeapType().getStruct().fields[fieldIndex].type; + } + auto* expected = make(fieldType); + Expression* timeout = nullptr; + if (ATOMIC_WAITS && oneIn(2)) { + timeout = make(Type::i64); + } else { + timeout = builder.makeConst(int64_t(0)); + } + return builder.makeStructWait(fieldIndex, ref, waitqueue, expected, timeout); +} + +Expression* TranslateToFuzzReader::makeWaitqueueNotify(Type type) { + assert(type == Type::i32); + auto* waitqueue = make(Type(HeapTypes::sharedWaitqueue, Nullable)); + auto* count = make(Type::i32); + return builder.makeWaitqueueNotify(waitqueue, count); +} + // Make a bounds check for an array operation, given a ref + index. An optional // additional length parameter can be provided, which is added to the index if // so (that is useful for something like array.fill, which operations on not a @@ -6662,11 +6737,11 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) { case HeapType::nofunc: case HeapType::nocont: case HeapType::noexn: + case HeapType::nowaitqueue: break; case HeapType::waitqueue: - case HeapType::nowaitqueue: { - WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer"); - } + return pick(HeapTypes::sharedWaitqueue, HeapTypes::sharedNowaitqueue) + .getBasic(share); } } // Look for an interesting subtype. diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index ee9ce65df3c..3509b867e01 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -344,6 +344,9 @@ struct HeapTypeGeneratorImpl { if (features.hasStackSwitching() && share == Unshared) { bottoms.push_back(HeapType::nocont); } + if (features.hasSharedEverything() && share == Shared) { + bottoms.push_back(HeapType::nowaitqueue); + } return rand.pick(bottoms).getBasic(share); } @@ -366,6 +369,9 @@ struct HeapTypeGeneratorImpl { if (features.hasExceptionHandling() && share == Unshared) { options.push_back(HeapType::exn); } + if (features.hasSharedEverything() && share == Shared) { + options.push_back(HeapType::waitqueue); + } auto ht = rand.pick(options); return ht.getBasic(share); } @@ -691,11 +697,13 @@ struct HeapTypeGeneratorImpl { case HeapType::nofunc: case HeapType::nocont: case HeapType::noexn: + case HeapType::nowaitqueue: return type; case HeapType::waitqueue: - case HeapType::nowaitqueue: { - WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer"); - } + if (rand.oneIn(2)) { + return HeapTypes::sharedNowaitqueue.getBasic(share); + } + return type; } WASM_UNREACHABLE("unexpected type"); } @@ -743,6 +751,7 @@ struct HeapTypeGeneratorImpl { case HeapType::exn: case HeapType::cont: case HeapType::any: + case HeapType::waitqueue: break; case HeapType::eq: candidates.push_back(HeapTypes::any.getBasic(share)); @@ -768,10 +777,9 @@ struct HeapTypeGeneratorImpl { case HeapType::noexn: candidates.push_back(HeapTypes::exn.getBasic(share)); break; - case HeapType::waitqueue: - case HeapType::nowaitqueue: { - WASM_UNREACHABLE("waitqueue is unimplemented in the fuzzer"); - } + case HeapType::nowaitqueue: + candidates.push_back(HeapTypes::sharedWaitqueue.getBasic(share)); + break; } assert(!candidates.empty()); return rand.pick(candidates); diff --git a/test/lit/fuzz-types.test b/test/lit/fuzz-types.test index ffcac612584..af396f42dec 100644 --- a/test/lit/fuzz-types.test +++ b/test/lit/fuzz-types.test @@ -1,6 +1,6 @@ ;; RUN: wasm-fuzz-types -v --seed=3 | filecheck %s -;; CHECK: Running with seed 3 +;; CHECK: Running with seed 3 ;; CHECK-NEXT: Built 20 types: ;; CHECK-NEXT: (rec ;; CHECK-NEXT: (type $0 (sub (shared (func (param i64 f64 exnref (ref null $0)) (result (ref cont)))))) diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index 79bac424b20..05fa0f63405 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,92 +1,95 @@ Metrics total - [exports] : 41 - [funcs] : 114 - [globals] : 13 - [imports] : 12 + [exports] : 125 + [funcs] : 235 + [globals] : 22 + [imports] : 15 [memories] : 1 [memory-data] : 31 - [table-data] : 33 + [table-data] : 71 [tables] : 2 [tags] : 2 - [total] : 89280 - [vars] : 3822 - ArrayCmpxchg : 14 - ArrayCopy : 35 - ArrayFill : 24 - ArrayGet : 332 - ArrayLen : 485 - ArrayNew : 1170 - ArrayNewFixed : 259 - ArrayRMW : 19 - ArraySet : 67 - AtomicCmpxchg : 21 - AtomicFence : 55 - AtomicNotify : 20 - AtomicRMW : 31 - Binary : 3205 - Block : 5335 - BrOn : 206 - Break : 820 - Call : 831 - CallIndirect : 72 - CallRef : 221 - Const : 11991 - ContNew : 59 - DataDrop : 14 - Drop : 432 - GlobalGet : 4707 - GlobalSet : 1594 - I31Get : 42 - If : 2005 - Load : 260 - LocalGet : 10609 - LocalSet : 3430 - Loop : 678 - MemoryCopy : 13 - MemoryFill : 23 - MemoryInit : 13 - Nop : 549 - Pop : 251 - RefAs : 8721 - RefCast : 506 - RefEq : 209 - RefFunc : 1855 - RefGetDesc : 43 - RefI31 : 549 - RefIsNull : 65 - RefNull : 10369 - RefTest : 45 - Return : 334 - SIMDExtract : 123 - SIMDLoad : 2 - SIMDReplace : 1 - SIMDShuffle : 3 - SIMDTernary : 3 - Select : 251 - Store : 122 - StringConst : 316 - StringEncode : 51 - StringEq : 56 - StringMeasure : 51 + [total] : 81404 + [vars] : 4717 + ArrayCmpxchg : 10 + ArrayCopy : 22 + ArrayFill : 34 + ArrayGet : 393 + ArrayLen : 478 + ArrayNew : 1620 + ArrayNewFixed : 455 + ArrayRMW : 12 + ArraySet : 46 + AtomicCmpxchg : 34 + AtomicFence : 50 + AtomicNotify : 26 + AtomicRMW : 24 + Binary : 3769 + Block : 5960 + BrOn : 237 + Break : 870 + Call : 958 + CallIndirect : 172 + CallRef : 206 + Const : 12924 + ContNew : 127 + DataDrop : 21 + Drop : 480 + GlobalGet : 4068 + GlobalSet : 1900 + I31Get : 60 + If : 2183 + Load : 252 + LocalGet : 6655 + LocalSet : 3455 + Loop : 711 + MemoryCopy : 15 + MemoryFill : 11 + MemoryInit : 24 + Nop : 551 + Pop : 276 + RefAs : 6130 + RefCast : 606 + RefEq : 239 + RefFunc : 1346 + RefGetDesc : 59 + RefI31 : 537 + RefIsNull : 59 + RefNull : 8237 + RefTest : 48 + Return : 301 + SIMDExtract : 109 + SIMDReplace : 3 + SIMDShift : 1 + SIMDShuffle : 1 + SIMDTernary : 1 + Select : 250 + Store : 105 + StringConcat : 1 + StringConst : 322 + StringEncode : 55 + StringEq : 55 + StringMeasure : 60 StringNew : 6 - StringSliceWTF : 2 - StringWTF16Get : 55 - StructCmpxchg : 33 - StructGet : 464 - StructNew : 11370 - StructRMW : 36 - StructSet : 70 - Switch : 4 - TableGet : 3 - TableSet : 50 - Throw : 50 + StringWTF16Get : 50 + StructCmpxchg : 48 + StructGet : 391 + StructNew : 8620 + StructRMW : 41 + StructSet : 59 + StructWait : 57 + Switch : 5 + TableGet : 1 + TableSet : 61 + Throw : 141 ThrowRef : 7 - Try : 340 - TryTable : 365 - TupleExtract : 215 - TupleMake : 199 - Unary : 1587 - Unreachable : 826 - WideIntAddSub : 9 - WideIntMul : 22 + Try : 351 + TryTable : 448 + TupleExtract : 200 + TupleMake : 221 + Unary : 1752 + Unreachable : 971 + WaitqueueNew : 263 + WaitqueueNotify: 66 + WideIntAddSub : 12 + WideIntMul : 19