diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 75e3a2a9a4e..f4400e39ec9 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -293,6 +293,7 @@ class TranslateToFuzzReader { Expression* buildIf(const struct ThreeArgs& args, Type type); Expression* makeIf(Type type); Expression* makeTry(Type type); + Expression* makeTryTable(Type type); Expression* makeBreak(Type type); Expression* makeCall(Type type); Expression* makeCallIndirect(Type type); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index a8b8f7855ef..abc6a63d530 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -1366,6 +1366,7 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) { &Self::makeCall, &Self::makeCallIndirect) .add(FeatureSet::ExceptionHandling, &Self::makeTry) + .add(FeatureSet::ExceptionHandling, &Self::makeTryTable) .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef); } if (type.isSingle()) { @@ -1451,6 +1452,8 @@ Expression* TranslateToFuzzReader::_makenone() { &Self::makeGlobalSet) .add(FeatureSet::BulkMemory, &Self::makeBulkMemory) .add(FeatureSet::Atomics, &Self::makeAtomic) + .add(FeatureSet::ExceptionHandling, &Self::makeTry) + .add(FeatureSet::ExceptionHandling, &Self::makeTryTable) .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef) .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeStructSet) .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet) @@ -1688,6 +1691,71 @@ Expression* TranslateToFuzzReader::makeTry(Type type) { return builder.makeTry(body, catchTags, catchBodies); } +Expression* TranslateToFuzzReader::makeTryTable(Type type) { + auto* body = make(type); + + if (funcContext->breakableStack.empty()) { + // Nothing to break to, emit a trivial TryTable. + // TODO: Perhaps generate a block wrapping us? + return builder.makeTryTable(body, {}, {}, {}); + } + + if (wasm.tags.empty()) { + addTag(); + } + + // Add catches of specific tags, and possibly a catch_all at the end. We use + // the last iteration of the loop for that. + std::vector catchTags; + std::vector catchDests; + std::vector catchRefs; + auto numCatches = upTo(MAX_TRY_CATCHES); + for (Index i = 0; i <= numCatches; i++) { + Name tagName; + Type tagType; + if (i < numCatches) { + // Look for a specific tag. + auto& tag = pick(wasm.tags); + tagName = tag->name; + tagType = tag->sig.params; + } else { + // Add a catch_all at the end, some of the time (but all of the time if we + // have nothing else). + if (!catchTags.empty() && oneIn(2)) { + break; + } + tagType = Type::none; + } + + // We need to find a proper target to break to, which means a target that + // has the type of the tag, or the tag + an exnref at the end. + std::vector vec(tagType.begin(), tagType.end()); + // Use a non-nullable exnref here, and then the subtyping check below will + // also accept a target that is nullable. + vec.push_back(Type(HeapType::exn, NonNullable)); + auto tagTypeWithExn = Type(vec); + int tries = TRIES; + while (tries-- > 0) { + auto* target = pick(funcContext->breakableStack); + auto dest = getTargetName(target); + auto valueType = getTargetType(target); + auto subOfTagType = Type::isSubType(tagType, valueType); + auto subOfTagTypeWithExn = Type::isSubType(tagTypeWithExn, valueType); + if (subOfTagType || subOfTagTypeWithExn) { + catchTags.push_back(tagName); + catchDests.push_back(dest); + catchRefs.push_back(subOfTagTypeWithExn); + break; + } + } + // TODO: Perhaps generate a block wrapping us, if we fail to find a target? + // TODO: It takes a bit of luck to find a target with an exnref - perhaps + // generate those? + } + + return builder.makeTryTable(body, catchTags, catchDests, catchRefs); +} + Expression* TranslateToFuzzReader::makeBreak(Type type) { if (funcContext->breakableStack.empty()) { return makeTrivial(type); 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 07afaa7ebf4..e58a508309c 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,53 +1,53 @@ Metrics total - [exports] : 5 - [funcs] : 9 + [exports] : 3 + [funcs] : 5 [globals] : 26 [imports] : 5 [memories] : 1 [memory-data] : 20 - [table-data] : 3 + [table-data] : 0 [tables] : 1 [tags] : 2 - [total] : 669 - [vars] : 27 - ArrayNew : 16 - ArrayNewFixed : 3 + [total] : 499 + [vars] : 20 + ArrayNew : 14 + ArrayNewFixed : 2 AtomicCmpxchg : 1 - AtomicFence : 1 - Binary : 75 - Block : 70 - Break : 7 - Call : 26 - CallRef : 1 - Const : 143 - Drop : 3 - GlobalGet : 37 - GlobalSet : 27 + AtomicNotify : 1 + AtomicRMW : 1 + Binary : 69 + Block : 42 + Break : 8 + Call : 6 + Const : 126 + Drop : 2 + GlobalGet : 27 + GlobalSet : 16 I31Get : 1 - If : 20 - Load : 21 - LocalGet : 55 - LocalSet : 40 - Loop : 6 - Nop : 5 - Pop : 5 + If : 10 + Load : 18 + LocalGet : 43 + LocalSet : 22 + Loop : 5 + Nop : 3 + Pop : 3 RefAs : 2 - RefEq : 2 - RefFunc : 5 - RefI31 : 2 - RefNull : 11 - RefTest : 2 - Return : 6 - Select : 2 - StringConst : 6 - StringEq : 1 - StringMeasure : 1 - StringWTF16Get : 1 - StructNew : 17 + RefFunc : 2 + RefI31 : 1 + RefNull : 8 + RefTest : 1 + Return : 1 + Select : 1 + Store : 3 + StringConst : 9 + StringEncode : 1 + StringEq : 3 + StructNew : 12 StructSet : 1 - Try : 4 - TupleExtract : 3 - TupleMake : 5 - Unary : 20 - Unreachable : 15 + Try : 3 + TryTable : 2 + TupleExtract : 1 + TupleMake : 4 + Unary : 13 + Unreachable : 11