| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| ;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --nominal -all -S -o - | filecheck %s |
| |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| ;; CHECK: (type $ref?|$A|_=>_none (func_subtype (param (ref null $A)) func)) |
| |
| ;; CHECK: (type $B (func_subtype func)) |
| (type $B (func)) |
| |
| ;; CHECK: (elem declare func $target-A $target-B) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $ref?|$A|_=>_none) (param $A (ref null $A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-B) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $A |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") (param $A (ref null $A)) |
| ;; This export has two RefFuncs, and one CallRef. |
| (drop |
| (ref.func $target-A) |
| ) |
| (drop |
| (ref.func $target-B) |
| ) |
| (call_ref |
| (local.get $A) |
| ) |
| ;; Verify that we do not crash on an unreachable call_ref, which has no |
| ;; heap type for us to analyze. |
| (call_ref |
| (unreachable) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-A (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-A (type $A) |
| ;; This function is reachable from the export "foo": there is a RefFunc and |
| ;; a CallRef for it there. |
| ) |
| |
| (func $target-A-noref (type $A) |
| ;; This function is not reachable. We have a CallRef of the right type, but |
| ;; no RefFunc. |
| ) |
| |
| ;; CHECK: (func $target-B (type $B) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| (func $target-B (type $B) |
| ;; This function is not reachable. We have a RefFunc in "foo" but no |
| ;; suitable CallRef. |
| ;; |
| ;; Note that we cannot remove the function, as the RefFunc must refer to |
| ;; something in order to validate. But we can clear out the body of this |
| ;; function with an unreachable. |
| ) |
| ) |
| |
| ;; As above, but reverse the order inside $foo, so we see the CallRef first. |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| (type $B (func)) |
| |
| ;; CHECK: (elem declare func $target-A) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $A) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null nofunc) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") |
| (call_ref |
| (ref.null $A) |
| ) |
| (drop |
| (ref.func $target-A) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-A (type $A) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| (func $target-A (type $A) |
| ;; This function is reachable. |
| ) |
| |
| (func $target-A-noref (type $A) |
| ;; This function is not reachable. |
| ) |
| ) |
| |
| ;; As above, but interleave CallRefs with RefFuncs. |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| (type $B (func)) |
| |
| ;; CHECK: (type $ref?|$A|_=>_none (func_subtype (param (ref null $A)) func)) |
| |
| ;; CHECK: (elem declare func $target-A-1 $target-A-2) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $ref?|$A|_=>_none) (param $A (ref null $A)) |
| ;; CHECK-NEXT: (call_ref $A |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A-1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $A |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") (param $A (ref null $A)) |
| (call_ref |
| (local.get $A) |
| ) |
| (drop |
| (ref.func $target-A-1) |
| ) |
| (call_ref |
| (local.get $A) |
| ) |
| (drop |
| (ref.func $target-A-2) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-A-1 (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-A-1 (type $A) |
| ;; This function is reachable. |
| ) |
| |
| ;; CHECK: (func $target-A-2 (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-A-2 (type $A) |
| ;; This function is reachable. |
| ) |
| |
| (func $target-A-3 (type $A) |
| ;; This function is not reachable. |
| ) |
| ) |
| |
| ;; As above, with the order reversed inside $foo. The results should be the |
| ;; same. |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| (type $B (func)) |
| |
| ;; CHECK: (type $ref?|$A|_=>_none (func_subtype (param (ref null $A)) func)) |
| |
| ;; CHECK: (elem declare func $target-A-1 $target-A-2) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $ref?|$A|_=>_none) (param $A (ref null $A)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A-1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $A |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-A-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call_ref $A |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") (param $A (ref null $A)) |
| (drop |
| (ref.func $target-A-1) |
| ) |
| (call_ref |
| (local.get $A) |
| ) |
| (drop |
| (ref.func $target-A-2) |
| ) |
| (call_ref |
| (local.get $A) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-A-1 (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-A-1 (type $A) |
| ;; This function is reachable. |
| ) |
| |
| ;; CHECK: (func $target-A-2 (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-A-2 (type $A) |
| ;; This function is reachable. |
| ) |
| |
| (func $target-A-3 (type $A) |
| ;; This function is not reachable. |
| ) |
| ) |
| |
| ;; The call.without.effects intrinsic does a call to the reference given to it, |
| ;; but for now other imports do not (until we add a flag for closed-world). |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| |
| ;; CHECK: (type $funcref_=>_none (func_subtype (param funcref) func)) |
| |
| ;; CHECK: (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (param funcref))) |
| (import "binaryen-intrinsics" "call.without.effects" |
| (func $call-without-effects (param funcref))) |
| |
| ;; CHECK: (import "other" "import" (func $other-import (param funcref))) |
| (import "other" "import" |
| (func $other-import (param funcref))) |
| |
| ;; CHECK: (elem declare func $target-drop $target-keep) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $A) |
| ;; CHECK-NEXT: (call $call-without-effects |
| ;; CHECK-NEXT: (ref.func $target-keep) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $other-import |
| ;; CHECK-NEXT: (ref.func $target-drop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") |
| ;; Calling the intrinsic with a reference is considered a call of the |
| ;; reference, so we will not remove $target-keep. |
| (call $call-without-effects |
| (ref.func $target-keep) |
| ) |
| ;; The other import is not enough to keep $target-drop alive. |
| (call $other-import |
| (ref.func $target-drop) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-keep (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-keep (type $A) |
| ) |
| |
| ;; CHECK: (func $target-drop (type $A) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| (func $target-drop (type $A) |
| ) |
| ) |
| |
| ;; As above, but now the call to the intrinsic does not let us see the exact |
| ;; function being called. |
| (module |
| ;; CHECK: (type $A (func_subtype func)) |
| (type $A (func)) |
| |
| ;; CHECK: (type $funcref_=>_none (func_subtype (param funcref) func)) |
| |
| ;; CHECK: (type $ref?|$A|_=>_none (func_subtype (param (ref null $A)) func)) |
| |
| ;; CHECK: (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (param funcref))) |
| (import "binaryen-intrinsics" "call.without.effects" |
| (func $call-without-effects (param funcref))) |
| |
| ;; CHECK: (import "other" "import" (func $other-import (param funcref))) |
| (import "other" "import" |
| (func $other-import (param funcref))) |
| |
| ;; CHECK: (elem declare func $target-keep $target-keep-2) |
| |
| ;; CHECK: (export "foo" (func $foo)) |
| |
| ;; CHECK: (func $foo (type $ref?|$A|_=>_none) (param $A (ref null $A)) |
| ;; CHECK-NEXT: (call $call-without-effects |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $target-keep) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $other-import |
| ;; CHECK-NEXT: (ref.func $target-keep-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $foo (export "foo") (param $A (ref null $A)) |
| ;; Call the intrinsic without a RefFunc. All we infer here is the type, |
| ;; which means we must assume anything with type $A (and a reference) can be |
| ;; called, which will keep alive both $target-keep and $target-keep-2 |
| (call $call-without-effects |
| (local.get $A) |
| ) |
| (drop |
| (ref.func $target-keep) |
| ) |
| (call $other-import |
| (ref.func $target-keep-2) |
| ) |
| ) |
| |
| ;; CHECK: (func $target-keep (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-keep (type $A) |
| ) |
| |
| ;; CHECK: (func $target-keep-2 (type $A) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $target-keep-2 (type $A) |
| ) |
| ) |