diff options
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 39 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 83 |
2 files changed, 71 insertions, 51 deletions
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index bdaf71e83..ef7b3a5b7 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -16,6 +16,7 @@ #include "tools/fuzzing.h" #include "ir/gc-type-utils.h" +#include "ir/local-structural-dominance.h" #include "ir/module-utils.h" #include "ir/subtypes.h" #include "ir/type-updating.h" @@ -559,15 +560,37 @@ void TranslateToFuzzReader::addHashMemorySupport() { } TranslateToFuzzReader::FunctionCreationContext::~FunctionCreationContext() { + // We must ensure non-nullable locals validate. Later down we'll run + // TypeUpdating::handleNonDefaultableLocals which will make them validate by + // turning them nullable + add ref.as_non_null to fix up types. That has the + // downside of making them trap at runtime, however, and also we lose the non- + // nullability in the type, so we prefer to do a manual fixup that avoids a + // trap, which we do by writing a non-nullable value into the local at the + // function entry. + // TODO: We could be more precise and use a LocalGraph here, at the cost of + // doing more work. + LocalStructuralDominance info( + func, parent.wasm, LocalStructuralDominance::NonNullableOnly); + for (auto index : info.nonDominatingIndices) { + // Do not always do this, but with high probability, to reduce the amount of + // traps. + if (!parent.oneIn(5)) { + auto* value = parent.makeTrivial(func->getLocalType(index)); + func->body = parent.builder.makeSequence( + parent.builder.makeLocalSet(index, value), func->body); + } + } + + // Then, to handle remaining cases we did not just fix up, do the general + // fixup to ensure we validate. + TypeUpdating::handleNonDefaultableLocals(func, parent.wasm); + if (HANG_LIMIT > 0) { parent.addHangLimitChecks(func); } assert(breakableStack.empty()); assert(hangStack.empty()); parent.funcContext = nullptr; - - // We must ensure non-nullable locals validate. - TypeUpdating::handleNonDefaultableLocals(func, parent.wasm); } Expression* TranslateToFuzzReader::makeHangLimitCheck() { @@ -621,11 +644,6 @@ Function* TranslateToFuzzReader::addFunction() { Index numVars = upToSquared(MAX_VARS); for (Index i = 0; i < numVars; i++) { auto type = getConcreteType(); - if (!type.isDefaultable()) { - // We can't use a nondefaultable type as a var, as those must be - // initialized to some default value. - continue; - } funcContext->typeLocals[type].push_back(params.size() + func->vars.size()); func->vars.push_back(type); } @@ -1251,7 +1269,10 @@ Expression* TranslateToFuzzReader::makeTrivial(Type type) { } nester(*this); if (type.isConcrete()) { - if (oneIn(2) && funcContext) { + // If we have a function context, use a local half the time. Use a local + // less often if the local is non-nullable, however, as then we might be + // using it before it was set, which would trap. + if (funcContext && oneIn(type.isNonNullable() ? 10 : 2)) { return makeLocalGet(type); } else { return makeConst(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 3cd4228e9..6f5078909 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,56 +1,55 @@ total - [exports] : 5 - [funcs] : 8 + [exports] : 3 + [funcs] : 6 [globals] : 1 [imports] : 5 [memories] : 1 [memory-data] : 20 - [table-data] : 6 + [table-data] : 1 [tables] : 1 [tags] : 2 - [total] : 807 - [vars] : 27 - ArrayLen : 1 + [total] : 643 + [vars] : 36 + ArrayCopy : 1 + ArrayFill : 1 + ArrayGet : 1 + ArrayLen : 5 ArrayNew : 7 - ArrayNewFixed : 3 ArraySet : 1 - AtomicCmpxchg : 1 - AtomicFence : 1 - Binary : 89 - Block : 102 - Break : 18 - Call : 21 - CallRef : 1 - Const : 154 - Drop : 5 - GlobalGet : 38 - GlobalSet : 38 + AtomicNotify : 1 + AtomicRMW : 1 + Binary : 84 + Block : 58 + Break : 9 + Call : 22 + CallRef : 2 + Const : 144 + Drop : 2 + GlobalGet : 16 + GlobalSet : 16 I31Get : 2 - If : 27 - Load : 19 - LocalGet : 62 + If : 20 + Load : 20 + LocalGet : 75 LocalSet : 48 - Loop : 12 - MemoryFill : 1 - Nop : 10 - Pop : 5 - RefAs : 19 - RefCast : 3 - RefEq : 2 - RefFunc : 7 - RefI31 : 4 - RefIsNull : 2 - RefNull : 14 - RefTest : 2 - Return : 2 + Loop : 4 + MemoryInit : 1 + Nop : 4 + Pop : 4 + RefAs : 4 + RefFunc : 6 + RefI31 : 3 + RefNull : 11 + RefTest : 1 + Return : 7 SIMDExtract : 1 Select : 5 - Store : 4 - StructGet : 6 - StructNew : 9 + Store : 3 + StructGet : 3 + StructNew : 7 StructSet : 1 - Try : 5 - TupleExtract : 2 - TupleMake : 2 - Unary : 32 - Unreachable : 19 + Try : 3 + TupleExtract : 7 + TupleMake : 10 + Unary : 14 + Unreachable : 8 |