summaryrefslogtreecommitdiff
path: root/src/ir
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir')
-rw-r--r--src/ir/import-utils.h14
-rw-r--r--src/ir/module-splitting.cpp129
-rw-r--r--src/ir/module-utils.h48
-rw-r--r--src/ir/names.h4
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); });