diff options
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 50 | ||||
-rw-r--r-- | test/lit/passes/remove-unused-module-elements-refs.wast | 63 |
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: ) |