summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp44
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) {