diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/module-utils.h | 26 | ||||
-rw-r--r-- | src/tools/wasm-metadce.cpp | 223 | ||||
-rw-r--r-- | src/wasm.h | 7 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 47 |
4 files changed, 162 insertions, 141 deletions
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index a1ecfdf84..d33a47673 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -218,6 +218,32 @@ template<typename T> inline void iterImportable(Module& wasm, T visitor) { } } +// Iterates over all module items. The visitor provided should have signature +// void(ModuleItemKind, Named*). +template<typename T> inline void iterModuleItems(Module& wasm, T visitor) { + for (auto& curr : wasm.functions) { + visitor(ModuleItemKind::Function, curr.get()); + } + for (auto& curr : wasm.tables) { + visitor(ModuleItemKind::Table, curr.get()); + } + for (auto& curr : wasm.memories) { + visitor(ModuleItemKind::Memory, curr.get()); + } + for (auto& curr : wasm.globals) { + visitor(ModuleItemKind::Global, curr.get()); + } + for (auto& curr : wasm.tags) { + visitor(ModuleItemKind::Tag, curr.get()); + } + for (auto& curr : wasm.dataSegments) { + visitor(ModuleItemKind::DataSegment, curr.get()); + } + for (auto& curr : wasm.elementSegments) { + visitor(ModuleItemKind::ElementSegment, curr.get()); + } +} + // Helper class for performing an operation on all the functions in the module, // in parallel, with an Info object for each one that can contain results of // some computation that the operation performs. diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index 73d7c0691..c88a9fd3d 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -54,16 +54,12 @@ struct MetaDCEGraph { std::unordered_map<Name, DCENode> nodes; std::unordered_set<Name> roots; - // export exported name => DCE name std::unordered_map<Name, Name> exportToDCENode; - std::unordered_map<Name, Name> functionToDCENode; // function name => DCE name - std::unordered_map<Name, Name> globalToDCENode; // global name => DCE name - std::unordered_map<Name, Name> tagToDCENode; // tag name => DCE name - std::unordered_map<Name, Name> DCENodeToExport; // reverse maps - std::unordered_map<Name, Name> DCENodeToFunction; - std::unordered_map<Name, Name> DCENodeToGlobal; - std::unordered_map<Name, Name> DCENodeToTag; + using KindName = std::pair<ModuleItemKind, Name>; + + // Kind and exported name => DCE name + std::unordered_map<KindName, Name> itemToDCENode; // imports are not mapped 1:1 to DCE nodes in the wasm, since env.X might // be imported twice, for example. So we don't map a DCE node to an Import, @@ -85,18 +81,8 @@ struct MetaDCEGraph { return std::string(module.str) + " (*) " + std::string(base.str); } - 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); - } - - ImportId getTagImportId(Name name) { - auto* imp = wasm.getTag(name); + ImportId getImportId(ModuleItemKind kind, Name name) { + auto* imp = wasm.getImport(kind, name); return getImportId(imp->module, imp->base); } @@ -107,6 +93,15 @@ struct MetaDCEGraph { MetaDCEGraph(Module& wasm) : wasm(wasm) {} + std::unordered_map<ModuleItemKind, std::string> kindPrefixes = { + {ModuleItemKind::Function, "func"}, + {ModuleItemKind::Table, "table"}, + {ModuleItemKind::Memory, "memory"}, + {ModuleItemKind::Global, "global"}, + {ModuleItemKind::Tag, "tag"}, + {ModuleItemKind::DataSegment, "dseg"}, + {ModuleItemKind::ElementSegment, "eseg"}}; + // populate the graph with info from the wasm, integrating with // potentially-existing nodes for imports and exports that the graph may // already contain. @@ -114,72 +109,28 @@ 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.) - ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) { - auto dceName = getName("func", func->name.toString()); - DCENodeToFunction[dceName] = func->name; - functionToDCENode[func->name] = dceName; - nodes[dceName] = DCENode(dceName); - }); - ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { - auto dceName = getName("global", global->name.toString()); - DCENodeToGlobal[dceName] = global->name; - globalToDCENode[global->name] = dceName; - nodes[dceName] = DCENode(dceName); - }); - ModuleUtils::iterDefinedTags(wasm, [&](Tag* tag) { - auto dceName = getName("tag", tag->name.toString()); - DCENodeToTag[dceName] = tag->name; - tagToDCENode[tag->name] = dceName; - nodes[dceName] = DCENode(dceName); - }); - // only process function, global, and tag 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.toString()); - 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.toString()); - importIdToDCENode[id] = dceName; - } - }); - ModuleUtils::iterImportedTags(wasm, [&](Tag* import) { - auto id = getImportId(import->module, import->base); - if (importIdToDCENode.find(id) == importIdToDCENode.end()) { - auto dceName = getName("importId", import->name.toString()); - importIdToDCENode[id] = dceName; + ModuleUtils::iterModuleItems(wasm, [&](ModuleItemKind kind, Named* item) { + if (auto* import = wasm.getImportOrNull(kind, item->name)) { + auto id = getImportId(import->module, import->base); + if (importIdToDCENode.find(id) == importIdToDCENode.end()) { + auto dceName = getName("importId", import->name.toString()); + importIdToDCENode[id] = dceName; + } + return; } + auto dceName = getName(kindPrefixes[kind], item->name.toString()); + itemToDCENode[{kind, item->name}] = dceName; + nodes[dceName] = DCENode(dceName); }); for (auto& exp : wasm.exports) { if (exportToDCENode.find(exp->name) == exportToDCENode.end()) { auto dceName = getName("export", exp->name.toString()); - DCENodeToExport[dceName] = exp->name; exportToDCENode[exp->name] = dceName; nodes[dceName] = DCENode(dceName); } // we can also link the export to the thing being exported auto& node = nodes[exportToDCENode[exp->name]]; - if (exp->kind == ExternalKind::Function) { - node.reaches.push_back(getFunctionDCEName(exp->value)); - } else if (exp->kind == ExternalKind::Global) { - if (!wasm.getGlobal(exp->value)->imported()) { - node.reaches.push_back(globalToDCENode[exp->value]); - } else { - node.reaches.push_back( - importIdToDCENode[getGlobalImportId(exp->value)]); - } - } else if (exp->kind == ExternalKind::Tag) { - if (!wasm.getTag(exp->value)->imported()) { - node.reaches.push_back(tagToDCENode[exp->value]); - } else { - node.reaches.push_back(importIdToDCENode[getTagImportId(exp->value)]); - } - } + node.reaches.push_back(getDCEName(ModuleItemKind(exp->kind), exp->value)); } // Add initializer dependencies // if we provide a parent DCE name, that is who can reach what we see @@ -193,7 +144,7 @@ struct MetaDCEGraph { void visitRefFunc(RefFunc* curr) { assert(!parentDceName.isNull()); parent->nodes[parentDceName].reaches.push_back( - parent->getFunctionDCEName(curr->func)); + parent->getDCEName(ModuleItemKind::Function, curr->func)); } private: @@ -204,10 +155,11 @@ struct MetaDCEGraph { Name dceName; if (!getModule()->getGlobal(name)->imported()) { // its a defined global - dceName = parent->globalToDCENode[name]; + dceName = parent->itemToDCENode[{ModuleItemKind::Global, name}]; } else { // it's an import. - dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)]; + dceName = parent->importIdToDCENode[parent->getImportId( + ModuleItemKind::Global, name)]; } if (parentDceName.isNull()) { parent->roots.insert(dceName); @@ -217,26 +169,33 @@ struct MetaDCEGraph { } }; ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { - InitScanner scanner(this, globalToDCENode[global->name]); + InitScanner scanner( + this, itemToDCENode[{ModuleItemKind::Global, global->name}]); scanner.setModule(&wasm); scanner.walk(global->init); }); - // we can't remove segments, so root what they need + // We can't remove active segments, so root them and what they use. + // TODO: treat them as in a cycle with their parent memory/table InitScanner rooter(this, Name()); rooter.setModule(&wasm); ModuleUtils::iterActiveElementSegments(wasm, [&](ElementSegment* segment) { // TODO: currently, all functions in the table are roots, but we // should add an option to refine that ElementUtils::iterElementSegmentFunctionNames( - segment, - [&](Name name, Index) { roots.insert(getFunctionDCEName(name)); }); + segment, [&](Name name, Index) { + roots.insert(getDCEName(ModuleItemKind::Function, name)); + }); + rooter.walk(segment->offset); + roots.insert(getDCEName(ModuleItemKind::ElementSegment, segment->name)); + }); + ModuleUtils::iterActiveDataSegments(wasm, [&](DataSegment* segment) { rooter.walk(segment->offset); + roots.insert(getDCEName(ModuleItemKind::DataSegment, segment->name)); }); - ModuleUtils::iterActiveDataSegments( - wasm, [&](DataSegment* segment) { rooter.walk(segment->offset); }); // A parallel scanner for function bodies - struct Scanner : public WalkerPass<PostWalker<Scanner>> { + struct Scanner : public WalkerPass< + PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>>> { bool isFunctionParallel() override { return true; } Scanner(MetaDCEGraph* parent) : parent(parent) {} @@ -245,50 +204,46 @@ struct MetaDCEGraph { return std::make_unique<Scanner>(parent); } - void visitCall(Call* curr) { handleFunction(curr->target); } - void visitRefFunc(RefFunc* curr) { handleFunction(curr->func); } - void visitGlobalGet(GlobalGet* curr) { handleGlobal(curr->name); } - void visitGlobalSet(GlobalSet* curr) { handleGlobal(curr->name); } - void visitThrow(Throw* curr) { handleTag(curr->tag); } - void visitTry(Try* curr) { - for (auto tag : curr->catchTags) { - handleTag(tag); - } + void visitExpression(Expression* curr) { +#define DELEGATE_ID curr->_id + +#define DELEGATE_START(id) [[maybe_unused]] auto* cast = curr->cast<id>(); + +#define DELEGATE_GET_FIELD(id, field) cast->field + +#define DELEGATE_FIELD_TYPE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_CHILD(id, field) +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_INT_ARRAY(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_NAME_VECTOR(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) + +#define DELEGATE_FIELD_NAME_KIND(id, field, kind) \ + if (cast->field.is()) { \ + handle(kind, cast->field); \ + } + +#include "wasm-delegations-fields.def" } private: MetaDCEGraph* parent; - void handleFunction(Name name) { - parent->nodes[parent->functionToDCENode[getFunction()->name]] - .reaches.push_back(parent->getFunctionDCEName(name)); - } - - void handleGlobal(Name name) { - if (!getFunction()) { - return; // non-function stuff (initializers) are handled separately - } - Name dceName; - if (!getModule()->getGlobal(name)->imported()) { - // its a global - dceName = parent->globalToDCENode[name]; - } else { - // it's an import. - dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)]; - } - parent->nodes[parent->functionToDCENode[getFunction()->name]] - .reaches.push_back(dceName); + void handle(ModuleItemKind kind, Name name) { + getCurrentFunctionDCENode().reaches.push_back( + parent->getDCEName(kind, name)); } - void handleTag(Name name) { - Name dceName; - if (!getModule()->getTag(name)->imported()) { - dceName = parent->tagToDCENode[name]; - } else { - dceName = parent->importIdToDCENode[parent->getTagImportId(name)]; - } - parent->nodes[parent->functionToDCENode[getFunction()->name]] - .reaches.push_back(dceName); + DCENode& getCurrentFunctionDCENode() { + return parent->nodes[parent->itemToDCENode[{ModuleItemKind::Function, + getFunction()->name}]]; } }; @@ -296,11 +251,11 @@ struct MetaDCEGraph { Scanner(this).run(&runner, &wasm); } - Name getFunctionDCEName(Name name) { - if (!wasm.getFunction(name)->imported()) { - return functionToDCENode[name]; + Name getDCEName(ModuleItemKind kind, Name name) { + if (wasm.getImportOrNull(kind, name)) { + return importIdToDCENode[getImportId(kind, name)]; } else { - return importIdToDCENode[getFunctionImportId(name)]; + return itemToDCENode[{kind, name}]; } } @@ -394,19 +349,6 @@ public: if (importMap.find(name) != importMap.end()) { std::cout << " is import " << importMap[name] << '\n'; } - if (DCENodeToExport.find(name) != DCENodeToExport.end()) { - std::cout << " is export " << DCENodeToExport[name] << ", " - << wasm.getExport(DCENodeToExport[name])->value << '\n'; - } - if (DCENodeToFunction.find(name) != DCENodeToFunction.end()) { - std::cout << " is function " << DCENodeToFunction[name] << '\n'; - } - if (DCENodeToGlobal.find(name) != DCENodeToGlobal.end()) { - std::cout << " is global " << DCENodeToGlobal[name] << '\n'; - } - if (DCENodeToTag.find(name) != DCENodeToTag.end()) { - std::cout << " is tag " << DCENodeToTag[name] << '\n'; - } for (auto target : node.reaches) { std::cout << " reaches: " << target << '\n'; } @@ -606,7 +548,6 @@ int main(int argc, const char* argv[]) { "for the form"; } graph.exportToDCENode[exp->getIString()] = node.name; - graph.DCENodeToExport[node.name] = exp->getIString(); } if (ref->has(IMPORT)) { json::Ref imp = ref[IMPORT]; diff --git a/src/wasm.h b/src/wasm.h index 47cc047bb..a7a2e0b1e 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2310,6 +2310,13 @@ public: Global* getGlobalOrNull(Name name); Tag* getTagOrNull(Name name); + // get* methods that are generic over the kind, that is, items are identified + // by their kind and their name. Otherwise, they are similar to the above + // get* methods. These return items that can be imports. + // TODO: Add methods for things that cannot be imports (segments). + Importable* getImport(ModuleItemKind kind, Name name); + Importable* getImportOrNull(ModuleItemKind kind, Name name); + Export* addExport(Export* curr); Function* addFunction(Function* curr); Global* addGlobal(Global* curr); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 24479c1cf..8ff2dc978 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1482,6 +1482,53 @@ Tag* Module::getTagOrNull(Name name) { return getModuleElementOrNull(tagsMap, name); } +Importable* Module::getImport(ModuleItemKind kind, Name name) { + switch (kind) { + case ModuleItemKind::Function: + return getFunction(name); + case ModuleItemKind::Table: + return getTable(name); + case ModuleItemKind::Memory: + return getMemory(name); + case ModuleItemKind::Global: + return getGlobal(name); + case ModuleItemKind::Tag: + return getTag(name); + case ModuleItemKind::DataSegment: + case ModuleItemKind::ElementSegment: + case ModuleItemKind::Invalid: + WASM_UNREACHABLE("invalid kind"); + } + + WASM_UNREACHABLE("unexpected kind"); +} + +Importable* Module::getImportOrNull(ModuleItemKind kind, Name name) { + auto doReturn = [](Importable* importable) { + return importable->imported() ? importable : nullptr; + }; + + switch (kind) { + case ModuleItemKind::Function: + return doReturn(getFunctionOrNull(name)); + case ModuleItemKind::Table: + return doReturn(getTableOrNull(name)); + case ModuleItemKind::Memory: + return doReturn(getMemoryOrNull(name)); + case ModuleItemKind::Global: + return doReturn(getGlobalOrNull(name)); + case ModuleItemKind::Tag: + return doReturn(getTagOrNull(name)); + case ModuleItemKind::DataSegment: + case ModuleItemKind::ElementSegment: + return nullptr; + case ModuleItemKind::Invalid: + WASM_UNREACHABLE("invalid kind"); + } + + WASM_UNREACHABLE("unexpected kind"); +} + // TODO(@warchant): refactor all usages to use variant with unique_ptr template<typename Vector, typename Map, typename Elem> Elem* addModuleElement(Vector& v, Map& m, Elem* curr, std::string funcName) { |