diff options
Diffstat (limited to 'src/ir')
-rw-r--r-- | src/ir/import-utils.h | 14 | ||||
-rw-r--r-- | src/ir/module-splitting.cpp | 129 | ||||
-rw-r--r-- | src/ir/module-utils.h | 48 | ||||
-rw-r--r-- | src/ir/names.h | 4 |
4 files changed, 138 insertions, 57 deletions
diff --git a/src/ir/import-utils.h b/src/ir/import-utils.h index 3f3d27f1b..e4a656379 100644 --- a/src/ir/import-utils.h +++ b/src/ir/import-utils.h @@ -29,6 +29,7 @@ struct ImportInfo { std::vector<Global*> importedGlobals; std::vector<Function*> importedFunctions; + std::vector<Table*> importedTables; std::vector<Event*> importedEvents; ImportInfo(Module& wasm) : wasm(wasm) { @@ -42,6 +43,11 @@ struct ImportInfo { importedFunctions.push_back(import.get()); } } + for (auto& import : wasm.tables) { + if (import->imported()) { + importedTables.push_back(import.get()); + } + } for (auto& import : wasm.events) { if (import->imported()) { importedEvents.push_back(import.get()); @@ -80,12 +86,14 @@ struct ImportInfo { Index getNumImportedFunctions() { return importedFunctions.size(); } + Index getNumImportedTables() { return importedTables.size(); } + Index getNumImportedEvents() { return importedEvents.size(); } Index getNumImports() { return getNumImportedGlobals() + getNumImportedFunctions() + getNumImportedEvents() + (wasm.memory.imported() ? 1 : 0) + - (wasm.table.imported() ? 1 : 0); + getNumImportedTables(); } Index getNumDefinedGlobals() { @@ -96,6 +104,10 @@ struct ImportInfo { return wasm.functions.size() - getNumImportedFunctions(); } + Index getNumDefinedTables() { + return wasm.tables.size() - getNumImportedTables(); + } + Index getNumDefinedEvents() { return wasm.events.size() - getNumImportedEvents(); } diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 1f33b44ac..111271063 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -99,23 +99,27 @@ namespace ModuleSplitting { namespace { -template<class F> void forEachElement(Table& table, F f) { - for (auto& segment : table.segments) { - Name base = ""; - Index offset = 0; - if (auto* c = segment.offset->dynCast<Const>()) { - offset = c->value.geti32(); - } else if (auto* g = segment.offset->dynCast<GlobalGet>()) { - base = g->name; - } - for (size_t i = 0; i < segment.data.size(); ++i) { - f(base, offset + i, segment.data[i]); +template<class F> void forEachElement(Module& module, F f) { + for (auto& table : module.tables) { + for (auto& segment : table->segments) { + Name base = ""; + Index offset = 0; + if (auto* c = segment.offset->dynCast<Const>()) { + offset = c->value.geti32(); + } else if (auto* g = segment.offset->dynCast<GlobalGet>()) { + base = g->name; + } + for (size_t i = 0; i < segment.data.size(); ++i) { + f(table->name, base, offset + i, segment.data[i]); + } } } } struct TableSlotManager { struct Slot { + Name tableName; + // If `global` is empty, then this slot is at a statically known index. Name global; Index index = 0; @@ -124,13 +128,15 @@ struct TableSlotManager { Expression* makeExpr(Module& module); }; Module& module; - Table& table; + Table* activeTable = nullptr; Table::Segment* activeSegment = nullptr; Slot activeBase; std::map<Name, Slot> funcIndices; TableSlotManager(Module& module); + Table* makeTable(); + // Returns the table index for `func`, allocating a new index if necessary. Slot getSlot(Name func); void addSlot(Name func, Slot slot); @@ -153,41 +159,54 @@ void TableSlotManager::addSlot(Name func, Slot slot) { assert(it.second && "Function already has multiple table slots"); } -TableSlotManager::TableSlotManager(Module& module) - : module(module), table(module.table) { +TableSlotManager::TableSlotManager(Module& module) : module(module) { + if (module.tables.empty()) { + return; + } + activeTable = module.tables.front().get(); // If there is exactly one table segment and that segment has a non-constant // offset, append new items to the end of that segment. In all other cases, // append new items at constant offsets after all existing items at constant // offsets. - if (table.segments.size() == 1 && !table.segments[0].offset->is<Const>()) { - assert(table.segments[0].offset->is<GlobalGet>() && + if (activeTable->segments.size() == 1 && + !activeTable->segments[0].offset->is<Const>()) { + assert(activeTable->segments[0].offset->is<GlobalGet>() && "Unexpected initializer instruction"); - activeSegment = &table.segments[0]; - activeBase = {table.segments[0].offset->cast<GlobalGet>()->name, 0}; + activeSegment = &activeTable->segments[0]; + activeBase = {activeTable->name, + activeTable->segments[0].offset->cast<GlobalGet>()->name, + 0}; } else { // Finds the segment with the highest occupied table slot so that new items // can be inserted contiguously at the end of it without accidentally // overwriting any other items. TODO: be more clever about filling gaps in // the table, if that is ever useful. Index maxIndex = 0; - for (auto& segment : table.segments) { + for (auto& segment : activeTable->segments) { assert(segment.offset->is<Const>() && "Unexpected non-const segment offset with multiple segments"); Index segmentBase = segment.offset->cast<Const>()->value.geti32(); if (segmentBase + segment.data.size() >= maxIndex) { maxIndex = segmentBase + segment.data.size(); activeSegment = &segment; - activeBase = {"", segmentBase}; + activeBase = {activeTable->name, "", segmentBase}; } } } + // Initialize funcIndices with the functions already in the table. - forEachElement(table, [&](Name base, Index offset, Name func) { - addSlot(func, {base, offset}); + forEachElement(module, [&](Name table, Name base, Index offset, Name func) { + addSlot(func, {table, base, offset}); }); } +Table* TableSlotManager::makeTable() { + module.addTable(Builder::makeTable(Name::fromInt(0))); + + return module.tables.front().get(); +} + TableSlotManager::Slot TableSlotManager::getSlot(Name func) { auto slotIt = funcIndices.find(func); if (slotIt != funcIndices.end()) { @@ -196,21 +215,26 @@ TableSlotManager::Slot TableSlotManager::getSlot(Name func) { // If there are no segments yet, allocate one. if (activeSegment == nullptr) { - table.exists = true; - assert(table.segments.size() == 0); - table.segments.emplace_back(Builder(module).makeConst(int32_t(0))); - activeSegment = &table.segments.back(); + if (activeTable == nullptr) { + activeTable = makeTable(); + activeBase = {activeTable->name, "", 0}; + } + + assert(activeTable->segments.size() == 0); + activeTable->segments.emplace_back(Builder(module).makeConst(int32_t(0))); + activeSegment = &activeTable->segments.back(); } - Slot newSlot = {activeBase.global, + Slot newSlot = {activeBase.tableName, + activeBase.global, activeBase.index + Index(activeSegment->data.size())}; activeSegment->data.push_back(func); addSlot(func, newSlot); - if (table.initial <= newSlot.index) { - table.initial = newSlot.index + 1; + if (activeTable->initial <= newSlot.index) { + activeTable->initial = newSlot.index + 1; } - if (table.max <= newSlot.index) { - table.max = newSlot.index + 1; + if (activeTable->max <= newSlot.index) { + activeTable->max = newSlot.index + 1; } return newSlot; } @@ -358,8 +382,8 @@ void ModuleSplitter::thunkExportedSecondaryFunctions() { for (size_t i = 0, size = func->sig.params.size(); i < size; ++i) { args.push_back(builder.makeLocalGet(i, func->sig.params[i])); } - func->body = - builder.makeCallIndirect(tableSlot.makeExpr(primary), args, func->sig); + func->body = builder.makeCallIndirect( + tableSlot.tableName, tableSlot.makeExpr(primary), args, func->sig); primary.addFunction(std::move(func)); } } @@ -376,8 +400,10 @@ void ModuleSplitter::indirectCallsToSecondaryFunctions() { if (!parent.secondaryFuncs.count(curr->target)) { return; } + auto tableSlot = parent.tableManager.getSlot(curr->target); replaceCurrent(builder.makeCallIndirect( - parent.tableManager.getSlot(curr->target).makeExpr(parent.primary), + tableSlot.tableName, + tableSlot.makeExpr(parent.primary), curr->operands, parent.secondary.getFunction(curr->target)->sig, curr->isReturn)); @@ -425,11 +451,15 @@ void ModuleSplitter::exportImportCalledPrimaryFunctions() { } void ModuleSplitter::setupTablePatching() { + if (!tableManager.activeTable) { + return; + } + std::map<Index, Name> replacedElems; // Replace table references to secondary functions with an imported // placeholder that encodes the table index in its name: // `importNamespace`.`index`. - forEachElement(primary.table, [&](Name, Index index, Name& elem) { + forEachElement(primary, [&](Name, Name, Index index, Name& elem) { if (secondaryFuncs.count(elem)) { replacedElems[index] = elem; auto* secondaryFunc = secondary.getFunction(elem); @@ -451,10 +481,14 @@ void ModuleSplitter::setupTablePatching() { return; } + auto secondaryTable = + ModuleUtils::copyTableWithoutSegments(tableManager.activeTable, secondary); + if (tableManager.activeBase.global.size()) { - assert(primary.table.segments.size() == 1 && + assert(tableManager.activeTable->segments.size() == 1 && "Unexpected number of segments with non-const base"); - assert(secondary.table.segments.size() == 0); + assert(secondary.tables.size() == 1 && + secondary.tables.front()->segments.empty()); // Since addition is not currently allowed in initializer expressions, we // need to start the new secondary segment where the primary segment starts. // The secondary segment will contain the same primary functions as the @@ -463,7 +497,8 @@ void ModuleSplitter::setupTablePatching() { // to be imported into the second module. TODO: use better strategies here, // such as using ref.func in the start function or standardizing addition in // initializer expressions. - const Table::Segment& primarySeg = primary.table.segments.front(); + const Table::Segment& primarySeg = + tableManager.activeTable->segments.front(); std::vector<Name> secondaryElems; secondaryElems.reserve(primarySeg.data.size()); @@ -484,7 +519,7 @@ void ModuleSplitter::setupTablePatching() { } auto offset = ExpressionManipulator::copy(primarySeg.offset, secondary); - secondary.table.segments.emplace_back(offset, secondaryElems); + secondaryTable->segments.emplace_back(offset, secondaryElems); return; } @@ -494,7 +529,7 @@ void ModuleSplitter::setupTablePatching() { std::vector<Name> currData; auto finishSegment = [&]() { auto* offset = Builder(secondary).makeConst(int32_t(currBase)); - secondary.table.segments.emplace_back(offset, currData); + secondaryTable->segments.emplace_back(offset, currData); }; for (auto curr = replacedElems.begin(); curr != replacedElems.end(); ++curr) { if (curr->first != currBase + currData.size()) { @@ -551,12 +586,14 @@ void ModuleSplitter::shareImportableItems() { primary.memory, secondary.memory, "memory", ExternalKind::Memory); } - if (primary.table.exists) { - secondary.table.exists = true; - secondary.table.initial = primary.table.initial; - secondary.table.max = primary.table.max; - makeImportExport( - primary.table, secondary.table, "table", ExternalKind::Table); + for (auto& table : primary.tables) { + auto secondaryTable = secondary.getTableOrNull(table->name); + if (!secondaryTable) { + secondaryTable = + ModuleUtils::copyTableWithoutSegments(table.get(), secondary); + } + + makeImportExport(*table, *secondaryTable, "table", ExternalKind::Table); } for (auto& global : primary.globals) { diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index f33d41266..29ebcc2b5 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -70,6 +70,29 @@ inline Event* copyEvent(Event* event, Module& out) { return ret; } +inline Table* copyTableWithoutSegments(Table* table, Module& out) { + auto ret = std::make_unique<Table>(); + ret->name = table->name; + ret->module = table->module; + ret->base = table->base; + + ret->initial = table->initial; + ret->max = table->max; + + return out.addTable(std::move(ret)); +} + +inline Table* copyTable(Table* table, Module& out) { + auto ret = copyTableWithoutSegments(table, out); + + for (auto segment : table->segments) { + segment.offset = ExpressionManipulator::copy(segment.offset, out); + ret->segments.push_back(segment); + } + + return ret; +} + inline void copyModule(const Module& in, Module& out) { // we use names throughout, not raw pointers, so simple copying is fine // for everything *but* expressions @@ -85,9 +108,8 @@ inline void copyModule(const Module& in, Module& out) { for (auto& curr : in.events) { copyEvent(curr.get(), out); } - out.table = in.table; - for (auto& segment : out.table.segments) { - segment.offset = ExpressionManipulator::copy(segment.offset, out); + for (auto& curr : in.tables) { + copyTable(curr.get(), out); } out.memory = in.memory; for (auto& segment : out.memory.segments) { @@ -126,9 +148,11 @@ template<typename T> inline void renameFunctions(Module& wasm, T& map) { } }; maybeUpdate(wasm.start); - for (auto& segment : wasm.table.segments) { - for (auto& name : segment.data) { - maybeUpdate(name); + for (auto& table : wasm.tables) { + for (auto& segment : table->segments) { + for (auto& name : segment.data) { + maybeUpdate(name); + } } } for (auto& exp : wasm.exports) { @@ -169,14 +193,18 @@ template<typename T> inline void iterDefinedMemories(Module& wasm, T visitor) { } template<typename T> inline void iterImportedTables(Module& wasm, T visitor) { - if (wasm.table.exists && wasm.table.imported()) { - visitor(&wasm.table); + for (auto& import : wasm.tables) { + if (import->imported()) { + visitor(import.get()); + } } } template<typename T> inline void iterDefinedTables(Module& wasm, T visitor) { - if (wasm.table.exists && !wasm.table.imported()) { - visitor(&wasm.table); + for (auto& import : wasm.tables) { + if (!import->imported()) { + visitor(import.get()); + } } } diff --git a/src/ir/names.h b/src/ir/names.h index 99dd3cab7..6fbb987f5 100644 --- a/src/ir/names.h +++ b/src/ir/names.h @@ -76,6 +76,10 @@ inline Name getValidFunctionName(Module& module, Name root) { return getValidName( module, root, [&](Name test) { return !module.getFunctionOrNull(test); }); } +inline Name getValidTableName(Module& module, Name root) { + return getValidName( + module, root, [&](Name test) { return !module.getTableOrNull(test); }); +} inline Name getValidEventName(Module& module, Name root) { return getValidName( module, root, [&](Name test) { return !module.getEventOrNull(test); }); |