diff options
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/execution-results.h | 3 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 37 | ||||
-rw-r--r-- | src/tools/wasm-merge.cpp | 168 | ||||
-rw-r--r-- | src/tools/wasm-metadce.cpp | 87 | ||||
-rw-r--r-- | src/tools/wasm-shell.cpp | 27 |
5 files changed, 176 insertions, 146 deletions
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 1e8fba4ff..ca4b819d9 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -20,6 +20,7 @@ #include "wasm.h" #include "shell-interface.h" +#include "ir/import-utils.h" namespace wasm { @@ -32,7 +33,7 @@ struct ExecutionResults { // get results of execution void get(Module& wasm) { - if (wasm.imports.size() > 0) { + if (ImportInfo(wasm).getNumImports() > 0) { std::cout << "[fuzz-exec] imports, so quitting\n"; return; } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index e11454b04..4272c5dba 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -36,6 +36,7 @@ #include "ir/global-utils.h" #include "ir/import-utils.h" #include "ir/literal-utils.h" +#include "ir/module-utils.h" using namespace wasm; @@ -124,23 +125,23 @@ public: EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface) : ModuleInstanceBase(wasm, externalInterface) { // if any global in the module has a non-const constructor, it is using a global import, // which we don't have, and is illegal to use - for (auto& global : wasm.globals) { + ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { if (!global->init->is<Const>()) { // some constants are ok to use if (auto* get = global->init->dynCast<GetGlobal>()) { auto name = get->name; - auto* import = wasm.getImport(name); + auto* import = wasm.getGlobal(name); if (import->module == Name("env") && ( import->base == Name("STACKTOP") || // stack constants are special, we handle them import->base == Name("STACK_MAX") )) { - continue; // this is fine + return; // this is fine } } // this global is dangerously initialized by an import, so if it is used, we must fail globals.addDangerous(global->name); } - } + }); } std::vector<char> stack; @@ -173,34 +174,33 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { void importGlobals(EvallingGlobalManager& globals, Module& wasm_) override { // fill usable values for stack imports, and globals initialized to them - if (auto* stackTop = ImportUtils::getImport(wasm_, "env", "STACKTOP")) { + ImportInfo imports(wasm_); + if (auto* stackTop = imports.getImportedGlobal("env", "STACKTOP")) { globals[stackTop->name] = Literal(int32_t(STACK_START)); if (auto* stackTop = GlobalUtils::getGlobalInitializedToImport(wasm_, "env", "STACKTOP")) { globals[stackTop->name] = Literal(int32_t(STACK_START)); } } - if (auto* stackMax = ImportUtils::getImport(wasm_, "env", "STACK_MAX")) { + if (auto* stackMax = imports.getImportedGlobal("env", "STACK_MAX")) { globals[stackMax->name] = Literal(int32_t(STACK_START)); if (auto* stackMax = GlobalUtils::getGlobalInitializedToImport(wasm_, "env", "STACK_MAX")) { globals[stackMax->name] = Literal(int32_t(STACK_START)); } } // fill in fake values for everything else, which is dangerous to use - for (auto& global : wasm_.globals) { - if (globals.find(global->name) == globals.end()) { - globals[global->name] = LiteralUtils::makeLiteralZero(global->type); + ModuleUtils::iterDefinedGlobals(wasm_, [&](Global* defined) { + if (globals.find(defined->name) == globals.end()) { + globals[defined->name] = LiteralUtils::makeLiteralZero(defined->type); } - } - for (auto& import : wasm_.imports) { - if (import->kind == ExternalKind::Global) { - if (globals.find(import->name) == globals.end()) { - globals[import->name] = LiteralUtils::makeLiteralZero(import->globalType); - } + }); + ModuleUtils::iterImportedGlobals(wasm_, [&](Global* import) { + if (globals.find(import->name) == globals.end()) { + globals[import->name] = LiteralUtils::makeLiteralZero(import->type); } - } + }); } - Literal callImport(Import *import, LiteralList& arguments) override { + Literal callImport(Function* import, LiteralList& arguments) override { std::string extra; if (import->module == "env" && import->base == "___cxa_atexit") { extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls to atexit are not emitted"; @@ -227,7 +227,8 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { if (start <= index && index < end) { auto name = segment.data[index - start]; // if this is one of our functions, we can call it; if it was imported, fail - if (wasm->getFunctionOrNull(name)) { + auto* func = wasm->getFunction(name); + if (!func->imported()) { return instance.callFunctionInternal(name, arguments); } else { throw FailToEvalException(std::string("callTable on imported function: ") + name.str); diff --git a/src/tools/wasm-merge.cpp b/src/tools/wasm-merge.cpp index e8910860e..70611e269 100644 --- a/src/tools/wasm-merge.cpp +++ b/src/tools/wasm-merge.cpp @@ -34,20 +34,10 @@ #include "wasm-binary.h" #include "wasm-builder.h" #include "wasm-validator.h" +#include "ir/module-utils.h" using namespace wasm; -// Calls note() on every import that has form "env".(base) -static void findImportsByBase(Module& wasm, Name base, std::function<void (Name)> note) { - for (auto& curr : wasm.imports) { - if (curr->module == ENV) { - if (curr->base == base) { - note(curr->name); - } - } - } -} - // Ensure a memory or table is of at least a size template<typename T> static void ensureSize(T& what, Index size) { @@ -119,31 +109,33 @@ struct Mergeable { } void findImports() { - findImportsByBase(wasm, MEMORY_BASE, [&](Name name) { - memoryBaseGlobals.insert(name); + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + if (import->module == ENV && import->base == MEMORY_BASE) { + memoryBaseGlobals.insert(import->name); + } }); if (memoryBaseGlobals.size() == 0) { // add one - auto* import = new Import; + auto* import = new Global; import->name = MEMORY_BASE; import->module = ENV; import->base = MEMORY_BASE; - import->kind = ExternalKind::Global; - import->globalType = i32; - wasm.addImport(import); + import->type = i32; + wasm.addGlobal(import); memoryBaseGlobals.insert(import->name); } - findImportsByBase(wasm, TABLE_BASE, [&](Name name) { - tableBaseGlobals.insert(name); + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + if (import->module == ENV && import->base == TABLE_BASE) { + tableBaseGlobals.insert(import->name); + } }); if (tableBaseGlobals.size() == 0) { - auto* import = new Import; + auto* import = new Global; import->name = TABLE_BASE; import->module = ENV; import->base = TABLE_BASE; - import->kind = ExternalKind::Global; - import->globalType = i32; - wasm.addImport(import); + import->type = i32; + wasm.addGlobal(import); tableBaseGlobals.insert(import->name); } } @@ -244,7 +236,7 @@ struct Mergeable { struct OutputMergeable : public PostWalker<OutputMergeable, Visitor<OutputMergeable>>, public Mergeable { OutputMergeable(Module& wasm) : Mergeable(wasm) {} - void visitCallImport(CallImport* curr) { + void visitCall(Call* curr) { auto iter = implementedFunctionImports.find(curr->target); if (iter != implementedFunctionImports.end()) { // this import is now in the module - call it @@ -264,10 +256,10 @@ struct OutputMergeable : public PostWalker<OutputMergeable, Visitor<OutputMergea void visitModule(Module* curr) { // remove imports that are being implemented for (auto& pair : implementedFunctionImports) { - curr->removeImport(pair.first); + curr->removeFunction(pair.first); } for (auto& pair : implementedGlobalImports) { - curr->removeImport(pair.first); + curr->removeGlobal(pair.first); } } }; @@ -288,11 +280,6 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp std::map<Name, Name> gNames; // globals void visitCall(Call* curr) { - curr->target = fNames[curr->target]; - assert(curr->target.is()); - } - - void visitCallImport(CallImport* curr) { auto iter = implementedFunctionImports.find(curr->target); if (iter != implementedFunctionImports.end()) { // this import is now in the module - call it @@ -333,29 +320,38 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp void merge() { // find function imports in us that are implemented in the output // TODO make maps, avoid N^2 - for (auto& imp : wasm.imports) { + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { // per wasm dynamic library rules, we expect to see exports on 'env' - if ((imp->kind == ExternalKind::Function || imp->kind == ExternalKind::Global) && imp->module == ENV) { + if (import->module == ENV) { // seek an export on the other side that matches for (auto& exp : outputMergeable.wasm.exports) { - if (exp->kind == imp->kind && exp->name == imp->base) { + if (exp->name == import->base) { // fits! - if (imp->kind == ExternalKind::Function) { - implementedFunctionImports[imp->name] = exp->value; - } else { - implementedGlobalImports[imp->name] = exp->value; - } + implementedFunctionImports[import->name] = exp->value; break; } } } - } + }); + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + // per wasm dynamic library rules, we expect to see exports on 'env' + if (import->module == ENV) { + // seek an export on the other side that matches + for (auto& exp : outputMergeable.wasm.exports) { + if (exp->name == import->base) { + // fits! + implementedGlobalImports[import->name] = exp->value; + break; + } + } + } + }); // remove the unneeded ones for (auto& pair : implementedFunctionImports) { - wasm.removeImport(pair.first); + wasm.removeFunction(pair.first); } for (auto& pair : implementedGlobalImports) { - wasm.removeImport(pair.first); + wasm.removeGlobal(pair.first); } // find new names @@ -364,27 +360,26 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp return outputMergeable.wasm.getFunctionTypeOrNull(name); }); } - for (auto& curr : wasm.imports) { - if (curr->kind == ExternalKind::Function) { - curr->name = fNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { - return !!outputMergeable.wasm.getImportOrNull(name) || !!outputMergeable.wasm.getFunctionOrNull(name); - }); - } else if (curr->kind == ExternalKind::Global) { - curr->name = gNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { - return !!outputMergeable.wasm.getImportOrNull(name) || !!outputMergeable.wasm.getGlobalOrNull(name); - }); - } - } - for (auto& curr : wasm.functions) { + ModuleUtils::iterImportedFunctions(wasm, [&](Function* curr) { + curr->name = fNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { + return !!outputMergeable.wasm.getFunctionOrNull(name); + }); + }); + ModuleUtils::iterImportedGlobals(wasm, [&](Global* curr) { + curr->name = gNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { + return !!outputMergeable.wasm.getGlobalOrNull(name); + }); + }); + ModuleUtils::iterDefinedFunctions(wasm, [&](Function* curr) { curr->name = fNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { return outputMergeable.wasm.getFunctionOrNull(name); }); - } - for (auto& curr : wasm.globals) { + }); + ModuleUtils::iterDefinedGlobals(wasm, [&](Global* curr) { curr->name = gNames[curr->name] = getNonColliding(curr->name, [&](Name name) -> bool { return outputMergeable.wasm.getGlobalOrNull(name); }); - } + }); // update global names in input { @@ -403,20 +398,26 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp } // find function imports in output that are implemented in the input - for (auto& imp : outputMergeable.wasm.imports) { - if ((imp->kind == ExternalKind::Function || imp->kind == ExternalKind::Global) && imp->module == ENV) { + ModuleUtils::iterImportedFunctions(outputMergeable.wasm, [&](Function* import) { + if (import->module == ENV) { for (auto& exp : wasm.exports) { - if (exp->kind == imp->kind && exp->name == imp->base) { - if (imp->kind == ExternalKind::Function) { - outputMergeable.implementedFunctionImports[imp->name] = fNames[exp->value]; - } else { - outputMergeable.implementedGlobalImports[imp->name] = gNames[exp->value]; - } + if (exp->name == import->base) { + outputMergeable.implementedFunctionImports[import->name] = fNames[exp->value]; break; } } } - } + }); + ModuleUtils::iterImportedGlobals(outputMergeable.wasm, [&](Global* import) { + if (import->module == ENV) { + for (auto& exp : wasm.exports) { + if (exp->name == import->base) { + outputMergeable.implementedGlobalImports[import->name] = gNames[exp->value]; + break; + } + } + } + }); // update the output before bringing anything in. avoid doing so when possible, as in the // common case the output module is very large. @@ -448,16 +449,19 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp for (auto& curr : wasm.functionTypes) { outputMergeable.wasm.addFunctionType(curr.release()); } - for (auto& curr : wasm.imports) { - if (curr->kind == ExternalKind::Memory || curr->kind == ExternalKind::Table) { - continue; // wasm has just 1 of each, they must match + for (auto& curr : wasm.globals) { + if (curr->imported()) { + outputMergeable.wasm.addGlobal(curr.release()); } - // update and add - if (curr->functionType.is()) { - curr->functionType = ftNames[curr->functionType]; - assert(curr->functionType.is()); + } + for (auto& curr : wasm.functions) { + if (curr->imported()) { + if (curr->type.is()) { + curr->type = ftNames[curr->type]; + assert(curr->type.is()); + } + outputMergeable.wasm.addFunction(curr.release()); } - outputMergeable.wasm.addImport(curr.release()); } for (auto& curr : wasm.exports) { if (curr->kind == ExternalKind::Memory || curr->kind == ExternalKind::Table) { @@ -477,13 +481,21 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp } } } + // Copy over the remaining non-imports (we have already transferred + // the imports, and they are nullptrs). for (auto& curr : wasm.functions) { - curr->type = ftNames[curr->type]; - assert(curr->type.is()); - outputMergeable.wasm.addFunction(curr.release()); + if (curr) { + assert(!curr->imported()); + curr->type = ftNames[curr->type]; + assert(curr->type.is()); + outputMergeable.wasm.addFunction(curr.release()); + } } for (auto& curr : wasm.globals) { - outputMergeable.wasm.addGlobal(curr.release()); + if (curr) { + assert(!curr->imported()); + outputMergeable.wasm.addGlobal(curr.release()); + } } } diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index 4f90ee612..5caf8fea7 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -33,7 +33,7 @@ #include "support/colors.h" #include "wasm-io.h" #include "wasm-builder.h" -#include "ir/import-utils.h" +#include "ir/module-utils.h" using namespace wasm; @@ -69,8 +69,13 @@ struct MetaDCEGraph { return std::string(module.str) + " (*) " + std::string(base.str); } - ImportId getImportId(Name name) { - auto* imp = wasm.getImport(name); + ImportId getFunctionImportId(Name name) { + auto* imp = wasm.getFunction(name); + return getImportId(imp->module, imp->base); + } + + ImportId getGlobalImportId(Name name) { + auto* imp = wasm.getGlobal(name); return getImportId(imp->module, imp->base); } @@ -86,28 +91,33 @@ struct MetaDCEGraph { // Add an entry for everything we might need ahead of time, so parallel work // does not alter parent state, just adds to things pointed by it, independently // (each thread will add for one function, etc.) - for (auto& func : wasm.functions) { + ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) { auto dceName = getName("func", func->name.str); DCENodeToFunction[dceName] = func->name; functionToDCENode[func->name] = dceName; nodes[dceName] = DCENode(dceName); - } - for (auto& global : wasm.globals) { + }); + ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { auto dceName = getName("global", global->name.str); DCENodeToGlobal[dceName] = global->name; globalToDCENode[global->name] = dceName; nodes[dceName] = DCENode(dceName); - } - for (auto& imp : wasm.imports) { - // only process function and global imports - the table and memory are always there - if (imp->kind == ExternalKind::Function || imp->kind == ExternalKind::Global) { - auto id = getImportId(imp->module, imp->base); - if (importIdToDCENode.find(id) == importIdToDCENode.end()) { - auto dceName = getName("importId", imp->name.str); - importIdToDCENode[id] = dceName; - } + }); + // only process function and global imports - the table and memory are always there + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { + auto id = getImportId(import->module, import->base); + if (importIdToDCENode.find(id) == importIdToDCENode.end()) { + auto dceName = getName("importId", import->name.str); + importIdToDCENode[id] = dceName; } - } + }); + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + auto id = getImportId(import->module, import->base); + if (importIdToDCENode.find(id) == importIdToDCENode.end()) { + auto dceName = getName("importId", import->name.str); + importIdToDCENode[id] = dceName; + } + }); for (auto& exp : wasm.exports) { if (exportToDCENode.find(exp->name) == exportToDCENode.end()) { auto dceName = getName("export", exp->name.str); @@ -118,16 +128,16 @@ struct MetaDCEGraph { // we can also link the export to the thing being exported auto& node = nodes[exportToDCENode[exp->name]]; if (exp->kind == ExternalKind::Function) { - if (wasm.getFunctionOrNull(exp->value)) { + if (!wasm.getFunction(exp->value)->imported()) { node.reaches.push_back(functionToDCENode[exp->value]); } else { - node.reaches.push_back(importIdToDCENode[getImportId(exp->value)]); + node.reaches.push_back(importIdToDCENode[getFunctionImportId(exp->value)]); } } else if (exp->kind == ExternalKind::Global) { - if (wasm.getGlobalOrNull(exp->value)) { + if (!wasm.getGlobal(exp->value)->imported()) { node.reaches.push_back(globalToDCENode[exp->value]); } else { - node.reaches.push_back(importIdToDCENode[getImportId(exp->value)]); + node.reaches.push_back(importIdToDCENode[getGlobalImportId(exp->value)]); } } } @@ -150,12 +160,12 @@ struct MetaDCEGraph { void handleGlobal(Name name) { Name dceName; - if (getModule()->getGlobalOrNull(name)) { - // its a global + if (!getModule()->getGlobal(name)->imported()) { + // its a defined global dceName = parent->globalToDCENode[name]; } else { // it's an import. - dceName = parent->importIdToDCENode[parent->getImportId(name)]; + dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)]; } if (parentDceName.isNull()) { parent->roots.insert(parentDceName); @@ -164,11 +174,11 @@ struct MetaDCEGraph { } } }; - for (auto& global : wasm.globals) { + ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { InitScanner scanner(this, globalToDCENode[global->name]); scanner.setModule(&wasm); scanner.walk(global->init); - } + }); // we can't remove segments, so root what they need InitScanner rooter(this, Name()); rooter.setModule(&wasm); @@ -176,10 +186,10 @@ struct MetaDCEGraph { // TODO: currently, all functions in the table are roots, but we // should add an option to refine that for (auto& name : segment.data) { - if (wasm.getFunctionOrNull(name)) { + if (!wasm.getFunction(name)->imported()) { roots.insert(functionToDCENode[name]); } else { - roots.insert(importIdToDCENode[getImportId(name)]); + roots.insert(importIdToDCENode[getFunctionImportId(name)]); } } rooter.walk(segment.offset); @@ -199,15 +209,16 @@ struct MetaDCEGraph { } void visitCall(Call* curr) { - parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( - parent->functionToDCENode[curr->target] - ); - } - void visitCallImport(CallImport* curr) { - assert(parent->functionToDCENode.count(getFunction()->name) > 0); - parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( - parent->importIdToDCENode[parent->getImportId(curr->target)] - ); + if (!getModule()->getFunction(curr->target)->imported()) { + parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( + parent->functionToDCENode[curr->target] + ); + } else { + assert(parent->functionToDCENode.count(getFunction()->name) > 0); + parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( + parent->importIdToDCENode[parent->getFunctionImportId(curr->target)] + ); + } } void visitGetGlobal(GetGlobal* curr) { handleGlobal(curr->name); @@ -222,12 +233,12 @@ struct MetaDCEGraph { void handleGlobal(Name name) { if (!getFunction()) return; // non-function stuff (initializers) are handled separately Name dceName; - if (getModule()->getGlobalOrNull(name)) { + if (!getModule()->getGlobal(name)->imported()) { // its a global dceName = parent->globalToDCENode[name]; } else { // it's an import. - dceName = parent->importIdToDCENode[parent->getImportId(name)]; + dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)]; } parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back(dceName); } diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp index 2a59be167..9ad1fa799 100644 --- a/src/tools/wasm-shell.cpp +++ b/src/tools/wasm-shell.cpp @@ -146,24 +146,29 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, } if (!invalid && id == ASSERT_UNLINKABLE) { // validate "instantiating" the mdoule - for (auto& import : wasm.imports) { + auto reportUnknownImport = [&](Importable* import) { + std::cerr << "unknown import: " << import->module << '.' << import->base << '\n'; + invalid = true; + }; + ModuleUtils::iterImportedGlobals(wasm, reportUnknownImport); + ModuleUtils::iterImportedFunctions(wasm, [&](Importable* import) { if (import->module == SPECTEST && import->base == PRINT) { - if (import->kind != ExternalKind::Function) { - std::cerr << "spectest.print should be a function, but is " << int32_t(import->kind) << '\n'; - invalid = true; - break; - } + // We can handle it. } else { - std::cerr << "unknown import: " << import->module << '.' << import->base << '\n'; - invalid = true; - break; + reportUnknownImport(import); } + }); + if (wasm.memory.imported()) { + reportUnknownImport(&wasm.memory); + } + if (wasm.table.imported()) { + reportUnknownImport(&wasm.table); } for (auto& segment : wasm.table.segments) { for (auto name : segment.data) { // spec tests consider it illegal to use spectest.print in a table - if (auto* import = wasm.getImportOrNull(name)) { - if (import->module == SPECTEST && import->base == PRINT) { + if (auto* import = wasm.getFunction(name)) { + if (import->imported() && import->module == SPECTEST && import->base == PRINT) { std::cerr << "cannot put spectest.print in table\n"; invalid = true; } |