diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 44 |
1 files changed, 39 insertions, 5 deletions
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 2466d6252..686a95032 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -23,6 +23,7 @@ #include <memory> #include "ir/element-utils.h" +#include "ir/intrinsics.h" #include "ir/module-utils.h" #include "ir/utils.h" #include "pass.h" @@ -62,8 +63,8 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { // // TODO: We assume a closed world in the GC space atm, but eventually should // have a flag for that, and when the world is not closed we'd need to - // check for RefFuncs that flow out to exports. - std::unordered_map<HeapType, std::vector<Name>> uncalledRefFuncMap; + // check for RefFuncs that flow out to exports or imports + std::unordered_map<HeapType, std::unordered_set<Name>> uncalledRefFuncMap; ReachabilityAnalyzer(Module* module, const std::vector<ModuleElement>& roots) : module(module) { @@ -124,7 +125,29 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { void visitCall(Call* curr) { maybeAdd(ModuleElement(ModuleElementKind::Function, curr->target)); + + if (Intrinsics(*module).isCallWithoutEffects(curr)) { + // A call-without-effects receives a function reference and calls it, the + // same as a CallRef. When we have a flag for non-closed-world, we should + // handle this automatically by the reference flowing out to an import, + // which is what binaryen intrinsics look like. For now, to support use + // cases of a closed world but that also use this intrinsic, handle the + // intrinsic specifically here. + auto* target = curr->operands.back(); + if (auto* refFunc = target->dynCast<RefFunc>()) { + // We can see exactly where this goes. + Call call(module->allocator); + call.target = refFunc->func; + visitCall(&call); + } else { + // All we can see is the type, so do a CallRef of that. + CallRef callRef(module->allocator); + callRef.target = target; + visitCallRef(&callRef); + } + } } + void visitCallIndirect(CallIndirect* curr) { maybeAddTable(curr->table); } void visitCallRef(CallRef* curr) { @@ -186,7 +209,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { maybeAdd(ModuleElement(ModuleElementKind::Function, curr->func)); } else { // We've never seen a CallRef for this, but might see one later. - uncalledRefFuncMap[type].push_back(curr->func); + uncalledRefFuncMap[type].insert(curr->func); } } void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); } @@ -277,24 +300,35 @@ struct RemoveUnusedModuleElements : public Pass { for (auto target : targets) { uncalledRefFuncs.insert(target); } + + // We cannot have a type in both this map and calledSignatures. + assert(analyzer.calledSignatures.count(type) == 0); + } + +#ifndef NDEBUG + for (auto type : analyzer.calledSignatures) { + assert(analyzer.uncalledRefFuncMap.count(type) == 0); } +#endif // Remove unreachable elements. module->removeFunctions([&](Function* curr) { if (analyzer.reachable.count( ModuleElement(ModuleElementKind::Function, curr->name))) { + // This is reached. return false; } if (uncalledRefFuncs.count(curr->name)) { - // See comment above on uncalledRefFuncs. + // This is not reached, but has a reference. See comment above on + // uncalledRefFuncs. if (!curr->imported()) { curr->body = Builder(*module).makeUnreachable(); } return false; } - // The function is not reached and has no references; remove it. + // The function is not reached and has no reference; remove it. return true; }); module->removeGlobals([&](Global* curr) { |