diff options
author | Thomas Lively <tlively@google.com> | 2024-04-22 19:59:24 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-23 02:59:24 +0000 |
commit | e50a2ba8e85c274880898ef7c5d6efc186efcc17 (patch) | |
tree | f44d25a51e902abaf8b69ac53682f448c717a490 /src/ir/module-splitting.cpp | |
parent | 7b7832d65ce28702bbac1b44b6de854029c1e0b1 (diff) | |
download | binaryen-e50a2ba8e85c274880898ef7c5d6efc186efcc17.tar.gz binaryen-e50a2ba8e85c274880898ef7c5d6efc186efcc17.tar.bz2 binaryen-e50a2ba8e85c274880898ef7c5d6efc186efcc17.zip |
[wasm-split] Do not split out functions referring to segments (#6517)
Since data and elem segments cannot be imported or exported, there is no way to
access them from the secondary module, so functions that need to refer to them
cannot be split out.
Fixes #6512.
Diffstat (limited to 'src/ir/module-splitting.cpp')
-rw-r--r-- | src/ir/module-splitting.cpp | 66 |
1 files changed, 62 insertions, 4 deletions
diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 8ef0e7b8c..27b6a0902 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -282,7 +282,7 @@ struct ModuleSplitter { // Initialization helpers static std::unique_ptr<Module> initSecondary(const Module& primary); static std::pair<std::set<Name>, std::set<Name>> - classifyFunctions(const Module& primary, const Config& config); + classifyFunctions(Module& primary, const Config& config); static std::map<Name, Name> initExportedPrimaryFuncs(const Module& primary); // Other helpers @@ -343,7 +343,64 @@ std::unique_ptr<Module> ModuleSplitter::initSecondary(const Module& primary) { } std::pair<std::set<Name>, std::set<Name>> -ModuleSplitter::classifyFunctions(const Module& primary, const Config& config) { +ModuleSplitter::classifyFunctions(Module& primary, const Config& config) { + // Find functions that refer to data or element segments. These functions must + // remain in the primary module because segments cannot be exported to be + // accessed from the secondary module. + // + // TODO: Investigate other options, such as moving the segments to the + // secondary module or replacing the segment-using instructions in the + // secondary module with calls to imports. + ModuleUtils::ParallelFunctionAnalysis<std::vector<Name>> + segmentReferrerCollector( + primary, [&](Function* func, std::vector<Name>& segmentReferrers) { + if (func->imported()) { + return; + } + + struct SegmentReferrerCollector + : PostWalker<SegmentReferrerCollector, + UnifiedExpressionVisitor<SegmentReferrerCollector>> { + bool hasSegmentReference = false; + + void visitExpression(Expression* curr) { + +#define DELEGATE_ID curr->_id + +#define DELEGATE_START(id) [[maybe_unused]] auto* cast = curr->cast<id>(); +#define DELEGATE_GET_FIELD(id, field) cast->field +#define DELEGATE_FIELD_TYPE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_CHILD(id, field) +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) + +#define DELEGATE_FIELD_NAME_KIND(id, field, kind) \ + if (kind == ModuleItemKind::DataSegment || \ + kind == ModuleItemKind::ElementSegment) { \ + hasSegmentReference = true; \ + } + +#include "wasm-delegations-fields.def" + } + }; + SegmentReferrerCollector collector; + collector.walkFunction(func); + if (collector.hasSegmentReference) { + segmentReferrers.push_back(func->name); + } + }); + + std::unordered_set<Name> segmentReferrers; + for (auto& [_, referrers] : segmentReferrerCollector.map) { + segmentReferrers.insert(referrers.begin(), referrers.end()); + } + std::set<Name> primaryFuncs, secondaryFuncs; for (auto& func : primary.functions) { // In JSPI mode exported functions cannot be moved to the secondary @@ -351,14 +408,15 @@ ModuleSplitter::classifyFunctions(const Module& primary, const Config& config) { // wrapper. Exported JSPI functions can still benefit from splitting though // since only the JSPI wrapper stub will remain in the primary module. if (func->imported() || config.primaryFuncs.count(func->name) || - (config.jspi && ExportUtils::isExported(primary, *func))) { + (config.jspi && ExportUtils::isExported(primary, *func)) || + segmentReferrers.count(func->name)) { primaryFuncs.insert(func->name); } else { assert(func->name != primary.start && "The start function must be kept"); secondaryFuncs.insert(func->name); } } - return std::make_pair(primaryFuncs, secondaryFuncs); + return std::make_pair(std::move(primaryFuncs), std::move(secondaryFuncs)); } std::map<Name, Name> |