diff options
author | Alon Zakai <azakai@google.com> | 2023-10-18 10:10:58 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-18 17:10:58 +0000 |
commit | 89c02aad305474aea1d413e110aadd68278a13d6 (patch) | |
tree | f1185f1ffaa2d6f6b3691b21d81bbecb24440169 /src/tools/fuzzing/fuzzing.cpp | |
parent | f2a556b1179fe4ee350728ad674bf723eb824768 (diff) | |
download | binaryen-89c02aad305474aea1d413e110aadd68278a13d6.tar.gz binaryen-89c02aad305474aea1d413e110aadd68278a13d6.tar.bz2 binaryen-89c02aad305474aea1d413e110aadd68278a13d6.zip |
Fuzzer: Allow non-nullable locals (#6019)
Remove the code that avoided such locals.
To avoid much new trapping, add logic to set a value to such locals if they have
accesses that are not dominated by a set already. Also in makeTrivial only
rarely emit a local.get of a non-nullable local (prefer a constant).
Diffstat (limited to 'src/tools/fuzzing/fuzzing.cpp')
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 39 |
1 files changed, 30 insertions, 9 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); |