summaryrefslogtreecommitdiff
path: root/src/ir/module-splitting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/module-splitting.cpp')
-rw-r--r--src/ir/module-splitting.cpp129
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) {