blob: 497af29ee691cab4671c201c36b114ea4a5c2b8c [file] [log] [blame] [edit]
;; 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)
)
)