diff options
Diffstat (limited to 'src/ir')
-rw-r--r-- | src/ir/ReFinalize.cpp | 3 | ||||
-rw-r--r-- | src/ir/module-splitting.cpp | 109 | ||||
-rw-r--r-- | src/ir/module-utils.h | 65 | ||||
-rw-r--r-- | src/ir/table-utils.cpp | 8 | ||||
-rw-r--r-- | src/ir/table-utils.h | 61 | ||||
-rw-r--r-- | src/ir/utils.h | 2 |
6 files changed, 152 insertions, 96 deletions
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index de4184596..a0166381b 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -178,6 +178,9 @@ void ReFinalize::visitFunction(Function* curr) { void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } +void ReFinalize::visitElementSegment(ElementSegment* curr) { + WASM_UNREACHABLE("unimp"); +} void ReFinalize::visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitEvent(Event* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 7215629e4..d4d03991e 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -87,20 +87,18 @@ namespace ModuleSplitting { namespace { 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]); - } + ModuleUtils::iterActiveElementSegments(module, [&](ElementSegment* segment) { + 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(segment->table, base, offset + i, segment->data[i]); + } + }); } struct TableSlotManager { @@ -116,9 +114,10 @@ struct TableSlotManager { }; Module& module; Table* activeTable = nullptr; - Table::Segment* activeSegment = nullptr; + ElementSegment* activeSegment = nullptr; Slot activeBase; std::map<Name, Slot> funcIndices; + std::vector<ElementSegment*> activeTableSegments; TableSlotManager(Module& module); @@ -148,22 +147,29 @@ void TableSlotManager::addSlot(Name func, Slot slot) { } TableSlotManager::TableSlotManager(Module& module) : module(module) { + // TODO: Reject or handle passive element segments + if (module.tables.empty()) { return; } activeTable = module.tables.front().get(); + ModuleUtils::iterTableSegments( + module, activeTable->name, [&](ElementSegment* segment) { + activeTableSegments.push_back(segment); + }); + // 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 (activeTable->segments.size() == 1 && - !activeTable->segments[0].offset->is<Const>()) { - assert(activeTable->segments[0].offset->is<GlobalGet>() && + if (activeTableSegments.size() == 1 && + !activeTableSegments[0]->offset->is<Const>()) { + assert(activeTableSegments[0]->offset->is<GlobalGet>() && "Unexpected initializer instruction"); - activeSegment = &activeTable->segments[0]; + activeSegment = activeTableSegments[0]; activeBase = {activeTable->name, - activeTable->segments[0].offset->cast<GlobalGet>()->name, + activeTableSegments[0]->offset->cast<GlobalGet>()->name, 0}; } else { // Finds the segment with the highest occupied table slot so that new items @@ -171,13 +177,13 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { // 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 : activeTable->segments) { - assert(segment.offset->is<Const>() && + for (auto& segment : activeTableSegments) { + 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; + Index segmentBase = segment->offset->cast<Const>()->value.geti32(); + if (segmentBase + segment->data.size() >= maxIndex) { + maxIndex = segmentBase + segment->data.size(); + activeSegment = segment; activeBase = {activeTable->name, "", segmentBase}; } } @@ -190,9 +196,7 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { } Table* TableSlotManager::makeTable() { - module.addTable(Builder::makeTable(Name::fromInt(0))); - - return module.tables.front().get(); + return module.addTable(Builder::makeTable(Name::fromInt(0))); } TableSlotManager::Slot TableSlotManager::getSlot(Name func) { @@ -208,9 +212,16 @@ TableSlotManager::Slot TableSlotManager::getSlot(Name func) { activeBase = {activeTable->name, "", 0}; } - assert(activeTable->segments.size() == 0); - activeTable->segments.emplace_back(Builder(module).makeConst(int32_t(0))); - activeSegment = &activeTable->segments.back(); + assert(std::all_of(module.elementSegments.begin(), + module.elementSegments.end(), + [&](std::unique_ptr<ElementSegment>& segment) { + return segment->table != activeTable->name; + })); + auto segment = std::make_unique<ElementSegment>( + activeTable->name, Builder(module).makeConst(int32_t(0))); + segment->setName(Name::fromInt(0), false); + activeSegment = segment.get(); + module.addElementSegment(std::move(segment)); } Slot newSlot = {activeBase.tableName, @@ -470,13 +481,12 @@ void ModuleSplitter::setupTablePatching() { } auto secondaryTable = - ModuleUtils::copyTableWithoutSegments(tableManager.activeTable, secondary); + ModuleUtils::copyTable(tableManager.activeTable, secondary); if (tableManager.activeBase.global.size()) { - assert(tableManager.activeTable->segments.size() == 1 && + assert(tableManager.activeTableSegments.size() == 1 && "Unexpected number of segments with non-const base"); - assert(secondary.tables.size() == 1 && - secondary.tables.front()->segments.empty()); + assert(secondary.tables.size() == 1 && secondary.elementSegments.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 @@ -485,29 +495,31 @@ 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 = - tableManager.activeTable->segments.front(); + const ElementSegment* primarySeg = tableManager.activeTableSegments.front(); std::vector<Name> secondaryElems; - secondaryElems.reserve(primarySeg.data.size()); + secondaryElems.reserve(primarySeg->data.size()); // Copy functions from the primary segment to the secondary segment, // replacing placeholders and creating new exports and imports as necessary. auto replacement = replacedElems.begin(); for (Index i = 0; - i < primarySeg.data.size() && replacement != replacedElems.end(); + i < primarySeg->data.size() && replacement != replacedElems.end(); ++i) { if (replacement->first == i) { - // primarySeg.data[i] is a placeholder, so use the secondary function. + // primarySeg->data[i] is a placeholder, so use the secondary function. secondaryElems.push_back(replacement->second); ++replacement; } else { - exportImportFunction(primarySeg.data[i]); - secondaryElems.push_back(primarySeg.data[i]); + exportImportFunction(primarySeg->data[i]); + secondaryElems.push_back(primarySeg->data[i]); } } - auto offset = ExpressionManipulator::copy(primarySeg.offset, secondary); - secondaryTable->segments.emplace_back(offset, secondaryElems); + auto offset = ExpressionManipulator::copy(primarySeg->offset, secondary); + auto secondaryElem = std::make_unique<ElementSegment>( + secondaryTable->name, offset, secondaryElems); + secondaryElem->setName(primarySeg->name, primarySeg->hasExplicitName); + secondary.addElementSegment(std::move(secondaryElem)); return; } @@ -517,7 +529,11 @@ void ModuleSplitter::setupTablePatching() { std::vector<Name> currData; auto finishSegment = [&]() { auto* offset = Builder(secondary).makeConst(int32_t(currBase)); - secondaryTable->segments.emplace_back(offset, currData); + auto secondaryElem = + std::make_unique<ElementSegment>(secondaryTable->name, offset, currData); + secondaryElem->setName(Name::fromInt(secondary.elementSegments.size()), + false); + secondary.addElementSegment(std::move(secondaryElem)); }; for (auto curr = replacedElems.begin(); curr != replacedElems.end(); ++curr) { if (curr->first != currBase + currData.size()) { @@ -577,8 +593,7 @@ void ModuleSplitter::shareImportableItems() { for (auto& table : primary.tables) { auto secondaryTable = secondary.getTableOrNull(table->name); if (!secondaryTable) { - secondaryTable = - ModuleUtils::copyTableWithoutSegments(table.get(), secondary); + secondaryTable = ModuleUtils::copyTable(table.get(), secondary); } makeImportExport(*table, *secondaryTable, "table", ExternalKind::Table); diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 371239e19..22ed89b37 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -70,7 +70,25 @@ inline Event* copyEvent(Event* event, Module& out) { return ret; } -inline Table* copyTableWithoutSegments(Table* table, Module& out) { +inline ElementSegment* copyElementSegment(const ElementSegment* segment, + Module& out) { + auto copy = [&](std::unique_ptr<ElementSegment>&& ret) { + ret->name = segment->name; + ret->hasExplicitName = segment->hasExplicitName; + ret->data = segment->data; + + return out.addElementSegment(std::move(ret)); + }; + + if (segment->table.isNull()) { + return copy(std::make_unique<ElementSegment>()); + } else { + auto offset = ExpressionManipulator::copy(segment->offset, out); + return copy(std::make_unique<ElementSegment>(segment->table, offset)); + } +} + +inline Table* copyTable(Table* table, Module& out) { auto ret = std::make_unique<Table>(); ret->name = table->name; ret->module = table->module; @@ -82,17 +100,6 @@ inline Table* copyTableWithoutSegments(Table* table, Module& out) { 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 @@ -108,9 +115,13 @@ inline void copyModule(const Module& in, Module& out) { for (auto& curr : in.events) { copyEvent(curr.get(), out); } + for (auto& curr : in.elementSegments) { + copyElementSegment(curr.get(), out); + } for (auto& curr : in.tables) { copyTable(curr.get(), out); } + out.memory = in.memory; for (auto& segment : out.memory.segments) { segment.offset = ExpressionManipulator::copy(segment.offset, out); @@ -148,11 +159,9 @@ template<typename T> inline void renameFunctions(Module& wasm, T& map) { } }; maybeUpdate(wasm.start); - for (auto& table : wasm.tables) { - for (auto& segment : table->segments) { - for (auto& name : segment.data) { - maybeUpdate(name); - } + for (auto& segment : wasm.elementSegments) { + for (auto& name : segment->data) { + maybeUpdate(name); } } for (auto& exp : wasm.exports) { @@ -208,6 +217,28 @@ template<typename T> inline void iterDefinedTables(Module& wasm, T visitor) { } } +template<typename T> +inline void iterTableSegments(Module& wasm, Name table, T visitor) { + // Just a precaution so that we don't iterate over passive elem segments by + // accident + assert(table.is() && "Table name must not be null"); + + for (auto& segment : wasm.elementSegments) { + if (segment->table == table) { + visitor(segment.get()); + } + } +} + +template<typename T> +inline void iterActiveElementSegments(Module& wasm, T visitor) { + for (auto& segment : wasm.elementSegments) { + if (segment->table.is()) { + visitor(segment.get()); + } + } +} + template<typename T> inline void iterImportedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (import->imported()) { diff --git a/src/ir/table-utils.cpp b/src/ir/table-utils.cpp index ef89e50f3..639f8fbe6 100644 --- a/src/ir/table-utils.cpp +++ b/src/ir/table-utils.cpp @@ -31,11 +31,9 @@ std::set<Name> getFunctionsNeedingElemDeclare(Module& wasm) { // Find all the names in the tables. std::unordered_set<Name> tableNames; - for (auto& table : wasm.tables) { - for (auto& segment : table->segments) { - for (auto name : segment.data) { - tableNames.insert(name); - } + for (auto& segment : wasm.elementSegments) { + for (auto name : segment->data) { + tableNames.insert(name); } } diff --git a/src/ir/table-utils.h b/src/ir/table-utils.h index 80ffc0c06..e90b0ca72 100644 --- a/src/ir/table-utils.h +++ b/src/ir/table-utils.h @@ -18,6 +18,7 @@ #define wasm_ir_table_h #include "ir/literal-utils.h" +#include "ir/module-utils.h" #include "wasm-traversal.h" #include "wasm.h" @@ -29,32 +30,38 @@ struct FlatTable { std::vector<Name> names; bool valid; - FlatTable(Table& table) { + FlatTable(Module& wasm, Table& table) { valid = true; - for (auto& segment : table.segments) { - auto offset = segment.offset; - if (!offset->is<Const>()) { - // TODO: handle some non-constant segments - valid = false; - return; - } - Index start = offset->cast<Const>()->value.geti32(); - Index end = start + segment.data.size(); - if (end > names.size()) { - names.resize(end); - } - for (Index i = 0; i < segment.data.size(); i++) { - names[start + i] = segment.data[i]; - } - } + ModuleUtils::iterTableSegments( + wasm, table.name, [&](ElementSegment* segment) { + auto offset = segment->offset; + if (!offset->is<Const>()) { + // TODO: handle some non-constant segments + valid = false; + return; + } + Index start = offset->cast<Const>()->value.geti32(); + Index end = start + segment->data.size(); + if (end > names.size()) { + names.resize(end); + } + for (Index i = 0; i < segment->data.size(); i++) { + names[start + i] = segment->data[i]; + } + }); } }; -inline Table::Segment& getSingletonSegment(Table& table, Module& wasm) { - if (table.segments.size() != 1) { +inline ElementSegment* getSingletonSegment(Table& table, Module& wasm) { + std::vector<ElementSegment*> tableSegments; + ModuleUtils::iterTableSegments( + wasm, table.name, [&](ElementSegment* segment) { + tableSegments.push_back(segment); + }); + if (tableSegments.size() != 1) { Fatal() << "Table doesn't have a singleton segment."; } - return table.segments[0]; + return tableSegments[0]; } // Appends a name to the table. This assumes the table has 0 or 1 segments, @@ -65,10 +72,10 @@ inline Table::Segment& getSingletonSegment(Table& table, Module& wasm) { // module has a single table segment, and that the dylink section indicates // we can validly append to that segment, see the check below. inline Index append(Table& table, Name name, Module& wasm) { - auto& segment = getSingletonSegment(table, wasm); - auto tableIndex = segment.data.size(); + auto* segment = getSingletonSegment(table, wasm); + auto tableIndex = segment->data.size(); if (wasm.dylinkSection) { - if (segment.data.size() != wasm.dylinkSection->tableSize) { + if (segment->data.size() != wasm.dylinkSection->tableSize) { Fatal() << "Appending to the table in a module with a dylink section " "that has tableSize which indicates it wants to reserve more " "table space than the actual table elements in the module. " @@ -77,7 +84,7 @@ inline Index append(Table& table, Name name, Module& wasm) { } wasm.dylinkSection->tableSize++; } - segment.data.push_back(name); + segment->data.push_back(name); table.initial = table.initial + 1; return tableIndex; } @@ -85,9 +92,9 @@ inline Index append(Table& table, Name name, Module& wasm) { // Checks if a function is already in the table. Returns that index if so, // otherwise appends it. inline Index getOrAppend(Table& table, Name name, Module& wasm) { - auto& segment = getSingletonSegment(table, wasm); - for (Index i = 0; i < segment.data.size(); i++) { - if (segment.data[i] == name) { + auto segment = getSingletonSegment(table, wasm); + for (Index i = 0; i < segment->data.size(); i++) { + if (segment->data[i] == name) { return i; } } diff --git a/src/ir/utils.h b/src/ir/utils.h index 424298bb3..f06a68fff 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -121,6 +121,7 @@ struct ReFinalize void visitExport(Export* curr); void visitGlobal(Global* curr); void visitTable(Table* curr); + void visitElementSegment(ElementSegment* curr); void visitMemory(Memory* curr); void visitEvent(Event* curr); void visitModule(Module* curr); @@ -144,6 +145,7 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> { void visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } void visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } + void visitElementSegment(ElementSegment* curr) { WASM_UNREACHABLE("unimp"); } void visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } void visitEvent(Event* curr) { WASM_UNREACHABLE("unimp"); } void visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } |