diff options
Diffstat (limited to 'src/ir/module-splitting.cpp')
-rw-r--r-- | src/ir/module-splitting.cpp | 129 |
1 files changed, 83 insertions, 46 deletions
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) { |