blob: 774a72d054fe19dea16c8ad21efa2f8353cb854a [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --closed-world --generate-global-effects --vacuum -S -o - | filecheck %s
;; Tests for aggregating effects from indirect calls in GlobalEffects when
;; --closed-world is true. Some more complicated tests are in
;; global-effects-closed-world-simplify-locals.wast.
(module
(table 1 1 funcref)
;; CHECK: (type $nopType (func (param i32)))
(type $nopType (func (param i32)))
;; CHECK: (func $nop (type $nopType) (param $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $nop (export "nop") (type $nopType)
(nop)
)
;; CHECK: (func $calls-nop-via-ref (type $1) (param $ref (ref $nopType))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $calls-nop-via-ref (param $ref (ref $nopType))
;; This can only possibly be a nop in closed-world.
;; The equivalent for call_indirect is tested in
;; test/lit/passes/global-effects-closed-world-tnh.wast.
(call_ref $nopType (i32.const 1) (local.get $ref))
)
;; CHECK: (func $calls-nop-via-nullable-ref (type $2) (param $ref (ref null $nopType))
;; CHECK-NEXT: (call_ref $nopType
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-nop-via-nullable-ref (param $ref (ref null $nopType))
(call_ref $nopType (i32.const 1) (local.get $ref))
)
;; CHECK: (func $call-indirect-nop (type $3)
;; CHECK-NEXT: (call_indirect $0 (type $nopType)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $call-indirect-nop
;; The call body is guaranteed to have no effects, however the call_indirect
;; itself may trap if the index is out of bounds or if the function we
;; lookup doesn't match $nopType. The call can't be optimized out.
;; This could be optimized out if --traps-never-happens or
;; --ignore-implicit-traps (in fewer cases) is set. See
;; global-effects-closed-world-tnh.wast and
;; global-effects-closed-world-ignore-implicit-traps.wast.
(call_indirect (type $nopType) (i32.const 1) (i32.const 0))
)
)
(module
(table 1 1 funcref)
;; CHECK: (type $maybe-has-effects (func (param i32)))
(type $maybe-has-effects (func (param i32)))
;; CHECK: (func $unreachable (type $maybe-has-effects) (param $0 i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $unreachable (export "unreachable") (type $maybe-has-effects) (param i32)
(unreachable)
)
;; CHECK: (func $nop (type $maybe-has-effects) (param $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $nop (export "nop") (type $maybe-has-effects) (param i32)
(nop)
)
;; CHECK: (func $calls-effectful-function-via-ref (type $1) (param $ref (ref $maybe-has-effects))
;; CHECK-NEXT: (call_ref $maybe-has-effects
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-effectful-function-via-ref (param $ref (ref $maybe-has-effects))
;; This may be a nop or it may trap depending on the ref
;; We don't know so don't optimize it out.
(call_ref $maybe-has-effects (i32.const 1) (local.get $ref))
)
;; CHECK: (func $call-indirect-effectful-function (type $2)
;; CHECK-NEXT: (call_indirect $0 (type $maybe-has-effects)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $call-indirect-effectful-function
;; As above, this may trap or be a nop depending on what's in the table.
;; In addition, this may trap due to a type mismatch of index out of bounds.
;; Since we don't know any of these things, don't optimize the call out.
(call_indirect (type $maybe-has-effects) (i32.const 1) (i32.const 1))
)
)
(module
;; CHECK: (type $uninhabited (func (param i32)))
(type $uninhabited (func (param i32)))
;; CHECK: (func $calls-uninhabited (type $1) (param $ref (ref $uninhabited))
;; CHECK-NEXT: (call_ref $uninhabited
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-uninhabited (param $ref (ref $uninhabited))
;; There's no function with this type, so it's impossible to create a ref to
;; call this function with. If this code is reached, it must trap.
;; TODO: Optimize this to (unreachable) by creating a 'must trap' effect.
(call_ref $uninhabited (i32.const 1) (local.get $ref))
)
;; CHECK: (func $calls-nullable-uninhabited (type $2) (param $ref (ref null $uninhabited))
;; CHECK-NEXT: (call_ref $uninhabited
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-nullable-uninhabited (param $ref (ref null $uninhabited))
;; This must be null, so it's guaranteed to trap and can't be optimized out.
;; TODO: Optimize this to (unreachable) by creating a 'must trap' effect.
(call_ref $uninhabited (i32.const 1) (local.get $ref))
)
)
(module
;; CHECK: (type $super (sub (func)))
(type $super (sub (func)))
;; Subtype
;; CHECK: (type $sub (sub $super (func)))
(type $sub (sub $super (func)))
;; CHECK: (func $nop-with-supertype (type $super)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $nop-with-supertype (export "nop-with-supertype") (type $super)
)
;; CHECK: (func $effectful-with-subtype (type $sub)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $effectful-with-subtype (export "effectful-with-subtype") (type $sub)
(unreachable)
)
;; CHECK: (func $calls-ref-with-supertype (type $2) (param $func (ref $super))
;; CHECK-NEXT: (call_ref $super
;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-ref-with-supertype (param $func (ref $super))
;; Check that we account for subtyping correctly.
;; $super has no effects (i.e. the union of all effects of functions with
;; this type is empty). However, $sub does have effects, and we can call_ref
;; with that subtype, so we need to include the unreachable effect and we
;; can't optimize out this call.
(call_ref $super (local.get $func))
)
;; CHECK: (func $calls-ref-with-exact-supertype (type $3) (param $func (ref (exact $super)))
;; CHECK-NEXT: (call_ref $super
;; CHECK-NEXT: (local.get $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-ref-with-exact-supertype (param $func (ref (exact $super)))
;; Same as above but this time our reference is the exact supertype
;; so we know not to aggregate effects from the subtype.
;; TODO: this case doesn't optimize today. Add exact ref support in the pass.
(call_ref $super (local.get $func))
)
)
(module
;; CHECK: (type $only-has-effects-in-not-addressable-function (func (param i32)))
(type $only-has-effects-in-not-addressable-function (func (param i32)))
;; CHECK: (func $nop (type $only-has-effects-in-not-addressable-function) (param $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $nop (export "nop") (type $only-has-effects-in-not-addressable-function) (param i32)
)
;; CHECK: (func $has-effects-but-not-exported (type $only-has-effects-in-not-addressable-function) (param $0 i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $has-effects-but-not-exported (type $only-has-effects-in-not-addressable-function) (param i32)
(unreachable)
)
;; CHECK: (func $calls-type-with-effects-but-not-addressable (type $1) (param $ref (ref $only-has-effects-in-not-addressable-function))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $calls-type-with-effects-but-not-addressable (param $ref (ref $only-has-effects-in-not-addressable-function))
;; The type $has-effects-but-not-exported doesn't have an address because
;; it's not exported and it's never the target of a ref.func.
;; So the call_ref's only potential target is $nop which has no effects.
(call_ref $only-has-effects-in-not-addressable-function (i32.const 1) (local.get $ref))
)
)
(module
;; CHECK: (type $unreachable-via-direct-call (func (param i32)))
(type $unreachable-via-direct-call (func (param i32)))
;; CHECK: (elem declare func $calls-unreachable)
;; CHECK: (func $unreachable (type $0)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $unreachable
(unreachable)
)
;; CHECK: (func $calls-unreachable (type $unreachable-via-direct-call) (param $0 i32)
;; CHECK-NEXT: (call $unreachable)
;; CHECK-NEXT: )
(func $calls-unreachable (export "calls-unreachable") (param i32)
(call $unreachable)
)
;; CHECK: (func $calls-unreachable-via-ref-and-direct-call-transtively (type $0)
;; CHECK-NEXT: (call_ref $unreachable-via-direct-call
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (ref.func $calls-unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-unreachable-via-ref-and-direct-call-transtively
(call_ref $unreachable-via-direct-call (i32.const 0) (ref.func $calls-unreachable))
)
;; CHECK: (func $f (type $0)
;; CHECK-NEXT: (call $calls-unreachable-via-ref-and-direct-call-transtively)
;; CHECK-NEXT: )
(func $f
;; Test that we can analyze longer call chains containing both indirect and
;; direct calls. In this case the call chain hits an unreachable via an
;; indirect call, then direct call, so we can't optimize this out.
(call $calls-unreachable-via-ref-and-direct-call-transtively)
)
)
(module
;; CHECK: (type $t (func (param i32)))
(type $t (func (param i32)))
;; (import "" "" (func $imported-func (type $t)))
;; CHECK: (import "" "" (func $imported-func (type $t) (param i32)))
(import "" "" (func $imported-func (type $t)))
(elem declare $imported-func)
;; CHECK: (func $nop (type $t) (param $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $nop (param i32)
)
;; CHECK: (func $indirect-calls (type $1) (param $ref (ref $t))
;; CHECK-NEXT: (call_ref $t
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $indirect-calls (param $ref (ref $t))
;; This might end up calling an imported function,
;; so we don't know anything about effects here
(call_ref $t (i32.const 1) (local.get $ref))
)
)
(module
(type $t (func (param i32)))
;; CHECK: (func $calls-unreachable (type $0)
;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $calls-unreachable (export "calls-unreachable")
;; $t looks like it has no effects, but unreachable is passed in,
;; so preserve the trap.
(call_ref $t (unreachable))
)
)