diff options
author | Alon Zakai <azakai@google.com> | 2024-02-20 16:49:37 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-20 16:49:37 -0800 |
commit | 1441bcbb33a5354432daf1edc4bc3a72d0bd7687 (patch) | |
tree | e14d7aee926edad11d315ac4ebbc0988243abad7 /src/passes/LegalizeJSInterface.cpp | |
parent | 0ecea77e9fd3dd42b5cd269ce64b6072691cfb52 (diff) | |
download | binaryen-1441bcbb33a5354432daf1edc4bc3a72d0bd7687.tar.gz binaryen-1441bcbb33a5354432daf1edc4bc3a72d0bd7687.tar.bz2 binaryen-1441bcbb33a5354432daf1edc4bc3a72d0bd7687.zip |
Fuzzer: Add a pass to prune illegal imports and exports for JS (#6312)
We already have passes to legalize i64 imports and exports, which the fuzzer will
run so that we can run wasm files in JS VMs. SIMD and multivalue also pose a
problem as they trap on the boundary. In principle we could legalize them as well,
but that is substantial effort, so instead just prune them: given a wasm module,
remove any imports or exports that use SIMD or multivalue (or anything else that
is not legal for JS).
Running this in the fuzzer will allow us to not skip running v8 on any testcase we
enable SIMD and multivalue for.
(Multivalue is allowed in newer VMs, so that part of this PR could be removed
eventually.)
Also remove the limitation on running v8 with multimemory (v8 now supports
that).
Diffstat (limited to 'src/passes/LegalizeJSInterface.cpp')
-rw-r--r-- | src/passes/LegalizeJSInterface.cpp | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/src/passes/LegalizeJSInterface.cpp b/src/passes/LegalizeJSInterface.cpp index 3ca6b7095..0082ba7ab 100644 --- a/src/passes/LegalizeJSInterface.cpp +++ b/src/passes/LegalizeJSInterface.cpp @@ -29,6 +29,12 @@ // across modules, we still want to legalize dynCalls so JS can call into the // tables even to a signature that is not legal. // +// Another variation also "prunes" imports and exports that we cannot yet +// legalize, like exports and imports with SIMD or multivalue. Until we write +// the logic to legalize them, removing those imports/exports still allows us to +// fuzz all the legal imports/exports. (Note that multivalue is supported in +// exports in newer VMs - node 16+ - so that part is only needed for older VMs.) +// #include "asmjs/shared-constants.h" #include "ir/element-utils.h" @@ -43,6 +49,8 @@ namespace wasm { +namespace { + // These are aliases for getTempRet0/setTempRet0 which emscripten defines in // compiler-rt and exports under these names. static Name GET_TEMP_RET_EXPORT("__get_temp_ret"); @@ -358,10 +366,88 @@ private: } }; +struct LegalizeAndPruneJSInterface : public LegalizeJSInterface { + // Legalize fully (true) and add pruning on top. + LegalizeAndPruneJSInterface() : LegalizeJSInterface(true) {} + + void run(Module* module) override { + LegalizeJSInterface::run(module); + + prune(module); + } + + void prune(Module* module) { + // For each function name, the exported id it is exported with. For + // example, + // + // (func $foo (export "bar") + // + // Would have exportedFunctions["foo"] = "bar"; + std::unordered_map<Name, Name> exportedFunctions; + for (auto& exp : module->exports) { + if (exp->kind == ExternalKind::Function) { + exportedFunctions[exp->value] = exp->name; + } + } + + for (auto& func : module->functions) { + // If the function is neither exported nor imported, no problem. + auto imported = func->imported(); + auto exported = exportedFunctions.count(func->name); + if (!imported && !exported) { + continue; + } + + // The params are allowed to be multivalue, but not the results. Otherwise + // look for SIMD. + auto sig = func->type.getSignature(); + auto illegal = isIllegal(sig.results); + illegal = + illegal || std::any_of(sig.params.begin(), + sig.params.end(), + [&](const Type& t) { return isIllegal(t); }); + if (!illegal) { + continue; + } + + // Prune an import by implementing it in a trivial manner. + if (imported) { + func->module = func->base = Name(); + + Builder builder(*module); + if (sig.results == Type::none) { + func->body = builder.makeNop(); + } else { + func->body = + builder.makeConstantExpression(Literal::makeZeros(sig.results)); + } + } + + // Prune an export by just removing it. + if (exported) { + module->removeExport(exportedFunctions[func->name]); + } + } + + // TODO: globals etc. + } + + bool isIllegal(Type type) { + auto features = type.getFeatures(); + return features.hasSIMD() || features.hasMultivalue(); + } +}; + +} // anonymous namespace + Pass* createLegalizeJSInterfacePass() { return new LegalizeJSInterface(true); } Pass* createLegalizeJSInterfaceMinimallyPass() { return new LegalizeJSInterface(false); } +Pass* createLegalizeAndPruneJSInterfacePass() { + return new LegalizeAndPruneJSInterface(); +} + } // namespace wasm |