diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index a40a76106a4..837713dceaa 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -289,10 +289,6 @@ class TranslateToFuzzReader { Expression* makeCallRef(Type type); Expression* makeLocalGet(Type type); Expression* makeLocalSet(Type type); - // Some globals are for internal use, and should not be modified by random - // fuzz code. - bool isValidGlobal(Name name); - Expression* makeGlobalGet(Type type); Expression* makeGlobalSet(Type type); Expression* makeTupleMake(Type type); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index ef7b3a5b72a..d9e0f6baf77 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -379,6 +379,24 @@ void TranslateToFuzzReader::setupGlobals() { } } } + + // Randomly assign some globals from initial content to be ignored for the + // fuzzer to use. Such globals will only be used from initial content. This is + // important to preserve some real-world patterns, like the "once" pattern in + // which a global is used in one function only. (If we randomly emitted gets + // and sets of such globals, we'd with very high probability end up breaking + // that pattern, and not fuzzing it at all.) + // + // Pick a percentage of initial globals to ignore later down when we decide + // which to allow uses from. + auto numInitialGlobals = wasm.globals.size(); + unsigned percentIgnoredInitialGlobals = 0; + if (numInitialGlobals) { + // Only generate this random number if it will be used. + percentIgnoredInitialGlobals = upTo(100); + } + + // Create new random globals. for (size_t index = upTo(MAX_GLOBALS); index > 0; --index) { auto type = getConcreteType(); auto* init = makeConst(type); @@ -394,12 +412,24 @@ void TranslateToFuzzReader::setupGlobals() { auto mutability = oneIn(2) ? Builder::Mutable : Builder::Immutable; auto global = builder.makeGlobal( Names::getValidGlobalName(wasm, "global$"), type, init, mutability); - globalsByType[type].push_back(global->name); - if (mutability == Builder::Mutable) { - mutableGlobalsByType[type].push_back(global->name); - } wasm.addGlobal(std::move(global)); } + + // Set up data structures for picking globals later for get/set operations. + for (Index i = 0; i < wasm.globals.size(); i++) { + auto& global = wasm.globals[i]; + + // Apply the chance for initial globals to be ignored, see above. + if (i < numInitialGlobals && upTo(100) < percentIgnoredInitialGlobals) { + continue; + } + + // This is a global we can use later, note it. + globalsByType[global->type].push_back(global->name); + if (global->mutable_) { + mutableGlobalsByType[global->type].push_back(global->name); + } + } } void TranslateToFuzzReader::setupTags() { @@ -1696,21 +1726,16 @@ Expression* TranslateToFuzzReader::makeLocalSet(Type type) { } } -bool TranslateToFuzzReader::isValidGlobal(Name name) { - return name != HANG_LIMIT_GLOBAL; -} - Expression* TranslateToFuzzReader::makeGlobalGet(Type type) { auto it = globalsByType.find(type); if (it == globalsByType.end() || it->second.empty()) { - return makeConst(type); - } - auto name = pick(it->second); - if (isValidGlobal(name)) { - return builder.makeGlobalGet(name, type); - } else { return makeTrivial(type); } + + auto name = pick(it->second); + // We don't want random fuzz code to use the hang limit global. + assert(name != HANG_LIMIT_GLOBAL); + return builder.makeGlobalGet(name, type); } Expression* TranslateToFuzzReader::makeGlobalSet(Type type) { @@ -1720,12 +1745,11 @@ Expression* TranslateToFuzzReader::makeGlobalSet(Type type) { if (it == mutableGlobalsByType.end() || it->second.empty()) { return makeTrivial(Type::none); } + auto name = pick(it->second); - if (isValidGlobal(name)) { - return builder.makeGlobalSet(name, make(type)); - } else { - return makeTrivial(Type::none); - } + // We don't want random fuzz code to use the hang limit global. + assert(name != HANG_LIMIT_GLOBAL); + return builder.makeGlobalSet(name, make(type)); } Expression* TranslateToFuzzReader::makeTupleMake(Type 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 6f507890946..9697da8bd15 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -8,48 +8,35 @@ total [table-data] : 1 [tables] : 1 [tags] : 2 - [total] : 643 - [vars] : 36 - ArrayCopy : 1 - ArrayFill : 1 - ArrayGet : 1 - ArrayLen : 5 - ArrayNew : 7 - ArraySet : 1 - 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 : 20 - Load : 20 - LocalGet : 75 - LocalSet : 48 - 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 : 3 - StructGet : 3 - StructNew : 7 - StructSet : 1 - Try : 3 - TupleExtract : 7 - TupleMake : 10 - Unary : 14 - Unreachable : 8 + [total] : 314 + [vars] : 38 + ArrayNew : 2 + ArrayNewFixed : 1 + AtomicFence : 1 + Binary : 58 + Block : 28 + Break : 6 + Call : 10 + Const : 72 + Drop : 3 + GlobalGet : 10 + GlobalSet : 10 + I31Get : 1 + If : 7 + Load : 18 + LocalGet : 36 + LocalSet : 21 + Loop : 1 + Nop : 2 + RefEq : 1 + RefFunc : 2 + RefI31 : 2 + RefNull : 1 + Return : 2 + Select : 1 + Store : 1 + StructGet : 1 + StructNew : 3 + TupleMake : 2 + Unary : 6 + Unreachable : 5