summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp50
-rw-r--r--test/lit/passes/remove-unused-module-elements-refs.wast63
2 files changed, 87 insertions, 26 deletions
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 95318acd2..43236bf84 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -232,11 +232,11 @@ struct Analyzer {
// All the RefFuncs we've seen, grouped by heap type. When we see a CallRef of
// one of the types here, we know all the RefFuncs corresponding to it are
- // used. This is the reverse side of calledSignatures: for a function to
- // be used via a reference, we need the combination of a RefFunc of it as
- // well as a CallRef of that, and we may see them in any order. (Or, if the
- // RefFunc is in a table, we need a CallIndirect, which is handled in the
- // table logic.)
+ // potentially used (and also those of subtypes). This is the reverse side of
+ // calledSignatures: for a function to be used via a reference, we need the
+ // combination of a RefFunc of it as well as a CallRef of that, and we may see
+ // them in any order. (Or, if the RefFunc is in a table, we need a
+ // CallIndirect, which is handled in the table logic.)
//
// After we see a call for a type, we can clear out the entry here for it, as
// we'll have that type in calledSignatures, and so this contains only
@@ -326,24 +326,38 @@ struct Analyzer {
return worked;
}
+ std::optional<SubTypes> subTypes;
+
void useCallRefType(HeapType type) {
- // Call all the functions of that signature. We can then forget about
- // them, as this signature will be marked as called.
- auto iter = uncalledRefFuncMap.find(type);
- if (iter != uncalledRefFuncMap.end()) {
- // We must not have a type in both calledSignatures and
- // uncalledRefFuncMap: once it is called, we do not track RefFuncs for
- // it any more.
- assert(calledSignatures.count(type) == 0);
+ if (type.isBasic()) {
+ // Nothing to do for something like a bottom type; attempts to call such a
+ // type will trap at runtime.
+ return;
+ }
- for (Name target : iter->second) {
- use(ModuleElement(ModuleElementKind::Function, target));
+ if (!subTypes) {
+ subTypes = SubTypes(*module);
+ }
+
+ // Call all the functions of that signature, and subtypes. We can then
+ // forget about them, as those signatures will be marked as called.
+ for (auto subType : subTypes->getAllSubTypes(type)) {
+ auto iter = uncalledRefFuncMap.find(subType);
+ if (iter != uncalledRefFuncMap.end()) {
+ // We must not have a type in both calledSignatures and
+ // uncalledRefFuncMap: once it is called, we do not track RefFuncs for
+ // it any more.
+ assert(calledSignatures.count(subType) == 0);
+
+ for (Name target : iter->second) {
+ use(ModuleElement(ModuleElementKind::Function, target));
+ }
+
+ uncalledRefFuncMap.erase(iter);
}
- uncalledRefFuncMap.erase(iter);
+ calledSignatures.insert(subType);
}
-
- calledSignatures.insert(type);
}
void useRefFunc(Name func) {
diff --git a/test/lit/passes/remove-unused-module-elements-refs.wast b/test/lit/passes/remove-unused-module-elements-refs.wast
index 7d430b021..2bbba0cad 100644
--- a/test/lit/passes/remove-unused-module-elements-refs.wast
+++ b/test/lit/passes/remove-unused-module-elements-refs.wast
@@ -7,24 +7,38 @@
;; if no calls exist to the right type, the function is not reached.
(module
- ;; CHECK: (type $A (func))
- ;; OPEN_WORLD: (type $A (func))
- (type $A (func))
+ ;; CHECK: (type $A-super (func))
+ ;; OPEN_WORLD: (type $A-super (func))
+ (type $A-super (func))
+
+ ;; CHECK: (type $A (func_subtype $A-super))
+ ;; OPEN_WORLD: (type $A (func_subtype $A-super))
+ (type $A (func_subtype $A-super))
+
+ ;; CHECK: (type $A-sub (func_subtype $A))
+ ;; OPEN_WORLD: (type $A-sub (func_subtype $A))
+ (type $A-sub (func_subtype $A))
;; CHECK: (type $B (func))
;; OPEN_WORLD: (type $B (func))
(type $B (func))
- ;; CHECK: (elem declare func $target-A $target-B)
+ ;; CHECK: (elem declare func $target-A $target-A-sub $target-A-super $target-B)
;; CHECK: (export "foo" (func $foo))
- ;; CHECK: (func $foo (type $A)
+ ;; CHECK: (func $foo (type $A-super)
;; CHECK-NEXT: (local $A (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $target-A)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.func $target-A-sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.func $target-A-super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $target-B)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call_ref $A
@@ -37,16 +51,22 @@
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
- ;; OPEN_WORLD: (elem declare func $target-A $target-B)
+ ;; OPEN_WORLD: (elem declare func $target-A $target-A-sub $target-A-super $target-B)
;; OPEN_WORLD: (export "foo" (func $foo))
- ;; OPEN_WORLD: (func $foo (type $A)
+ ;; OPEN_WORLD: (func $foo (type $A-super)
;; OPEN_WORLD-NEXT: (local $A (ref null $A))
;; OPEN_WORLD-NEXT: (drop
;; OPEN_WORLD-NEXT: (ref.func $target-A)
;; OPEN_WORLD-NEXT: )
;; OPEN_WORLD-NEXT: (drop
+ ;; OPEN_WORLD-NEXT: (ref.func $target-A-sub)
+ ;; OPEN_WORLD-NEXT: )
+ ;; OPEN_WORLD-NEXT: (drop
+ ;; OPEN_WORLD-NEXT: (ref.func $target-A-super)
+ ;; OPEN_WORLD-NEXT: )
+ ;; OPEN_WORLD-NEXT: (drop
;; OPEN_WORLD-NEXT: (ref.func $target-B)
;; OPEN_WORLD-NEXT: )
;; OPEN_WORLD-NEXT: (call_ref $A
@@ -61,11 +81,17 @@
;; OPEN_WORLD-NEXT: )
(func $foo (export "foo")
(local $A (ref null $A))
- ;; This export has two RefFuncs, and one CallRef.
+ ;; This export has some RefFuncs, and one CallRef.
(drop
(ref.func $target-A)
)
(drop
+ (ref.func $target-A-sub)
+ )
+ (drop
+ (ref.func $target-A-super)
+ )
+ (drop
(ref.func $target-B)
)
(call_ref $A
@@ -94,6 +120,27 @@
;; no RefFunc.
)
+ ;; CHECK: (func $target-A-sub (type $A-sub)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; OPEN_WORLD: (func $target-A-sub (type $A-sub)
+ ;; OPEN_WORLD-NEXT: (nop)
+ ;; OPEN_WORLD-NEXT: )
+ (func $target-A-sub (type $A-sub)
+ ;; This function is reachable because we have a CallRef of a supertype ($A).
+ )
+
+ ;; CHECK: (func $target-A-super (type $A-super)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; OPEN_WORLD: (func $target-A-super (type $A-super)
+ ;; OPEN_WORLD-NEXT: (nop)
+ ;; OPEN_WORLD-NEXT: )
+ (func $target-A-super (type $A-super)
+ ;; This function is not reachable. We have a CallRef of a subtype ($A), but
+ ;; that is not enough.
+ )
+
;; CHECK: (func $target-B (type $B)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )