blob: 6292e0b29639318c6215ec4a61a5dd99d5c2f0c0 [file]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --closed-world --generate-global-effects --simplify-locals -S -o - | filecheck %s
;; Tests for aggregating effects from indirect calls in GlobalEffects when
;; --closed-world is true. Continued from global-effects-closed-world.wast.
;; Test that effects are aggregated from both $indirect-type-super and
;; $indirect-type-sub for indirect calls to $indirect-type-super.
(module
;; CHECK: (type $indirect-type-super (sub (func (param i32))))
(type $indirect-type-super (sub (func (param i32))))
;; CHECK: (type $indirect-type-sub (sub $indirect-type-super (func (param i32))))
(type $indirect-type-sub (sub $indirect-type-super (func (param i32))))
;; CHECK: (type $2 (func (param (ref $indirect-type-super))))
;; CHECK: (type $3 (func (param (ref $indirect-type-sub))))
;; CHECK: (global $g1 (mut i32) (i32.const 0))
(global $g1 (mut i32) (i32.const 0))
;; CHECK: (global $g2 (mut i32) (i32.const 0))
(global $g2 (mut i32) (i32.const 0))
;; CHECK: (global $g3 (mut i32) (i32.const 0))
(global $g3 (mut i32) (i32.const 0))
;; CHECK: (export "impl1" (func $impl1))
;; CHECK: (export "impl2" (func $impl2))
;; CHECK: (func $impl1 (type $indirect-type-super) (param $i32 i32)
;; CHECK-NEXT: (global.set $g1
;; CHECK-NEXT: (local.get $i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impl1 (export "impl1") (type $indirect-type-super) (param $i32 i32)
(global.set $g1 (local.get $i32))
)
;; CHECK: (func $impl2 (type $indirect-type-sub) (param $i32 i32)
;; CHECK-NEXT: (global.set $g2
;; CHECK-NEXT: (local.get $i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impl2 (export "impl2") (type $indirect-type-sub) (param $i32 i32)
(global.set $g2 (local.get $i32))
)
;; CHECK: (func $merges-effects-from-super-and-sub (type $2) (param $ref (ref $indirect-type-super))
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (local $z i32)
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $y
;; CHECK-NEXT: (global.get $g2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (call_ref $indirect-type-super
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (global.get $g3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $merges-effects-from-super-and-sub (param $ref (ref $indirect-type-super))
(local $x i32)
(local $y i32)
(local $z i32)
(local.set $x (global.get $g1))
(local.set $y (global.get $g2))
(local.set $z (global.get $g3))
;; This acts as a barrier for $x and $y, but not $z because
;; $ref may write to $g1 (via $impl1) or $g2 (via $impl2) but not $g3.
;; $z is optimized out and $x and $y are left alone.
(call_ref $indirect-type-super (i32.const 1) (local.get $ref))
(drop (local.get $x))
(drop (local.get $y))
(drop (local.get $z))
)
;; CHECK: (func $merges-effects-from-sub-only (type $3) (param $ref (ref $indirect-type-sub))
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (local $z i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (local.set $y
;; CHECK-NEXT: (global.get $g2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (call_ref $indirect-type-sub
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (global.get $g3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $merges-effects-from-sub-only (param $ref (ref $indirect-type-sub))
(local $x i32)
(local $y i32)
(local $z i32)
(local.set $x (global.get $g1))
(local.set $y (global.get $g2))
(local.set $z (global.get $g3))
;; Similar to above but here it's impossible to reach $impl1
;; (the supertype), so $x can safely be optimized out.
(call_ref $indirect-type-sub (i32.const 1) (local.get $ref))
(drop (local.get $x))
(drop (local.get $y))
(drop (local.get $z))
)
)
;; Test different ways of referencing functions to ensure that they're included
;; in indirect effects analysis. A function is considered referenced if it's:
;; - exported
;; - referenced in a ref.func
;; - contained in an `elem` segment
(module
;; CHECK: (type $indirect-type (func (param i32)))
(type $indirect-type (func (param i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (param (ref $indirect-type))))
;; CHECK: (global $g1 (mut i32) (i32.const 0))
(global $g1 (mut i32) (i32.const 0))
;; CHECK: (global $g2 (mut i32) (i32.const 0))
(global $g2 (mut i32) (i32.const 0))
;; CHECK: (global $g3 (mut i32) (i32.const 0))
(global $g3 (mut i32) (i32.const 0))
;; CHECK: (global $g4 (mut i32) (i32.const 0))
(global $g4 (mut i32) (i32.const 0))
(table 1 1 funcref)
;; CHECK: (table $0 1 1 funcref)
;; CHECK: (elem $0 (i32.const 0) $f3)
;; CHECK: (elem declare func $f2)
;; CHECK: (export "f1" (func $f1))
;; CHECK: (func $f1 (type $indirect-type) (param $i32 i32)
;; CHECK-NEXT: (global.set $g1
;; CHECK-NEXT: (local.get $i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $f1 (export "f1") (type $indirect-type) (param $i32 i32)
(global.set $g1 (local.get $i32))
)
;; CHECK: (func $f2 (type $indirect-type) (param $i32 i32)
;; CHECK-NEXT: (global.set $g2
;; CHECK-NEXT: (local.get $i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $f2 (type $indirect-type) (param $i32 i32)
(global.set $g2 (local.get $i32))
)
;; CHECK: (func $reference-f2 (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $f2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $reference-f2
(drop (ref.func $f2))
)
;; CHECK: (func $f3 (type $indirect-type) (param $i32 i32)
;; CHECK-NEXT: (global.set $g3
;; CHECK-NEXT: (local.get $i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $f3 (type $indirect-type) (param $i32 i32)
(global.set $g3 (local.get $i32))
)
(elem (i32.const 0) $f3)
;; CHECK: (func $merges-multiple-effects (type $2) (param $ref (ref $indirect-type))
;; CHECK-NEXT: (local $l1 i32)
;; CHECK-NEXT: (local $l2 i32)
;; CHECK-NEXT: (local $l3 i32)
;; CHECK-NEXT: (local $l4 i32)
;; CHECK-NEXT: (local.set $l1
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $l2
;; CHECK-NEXT: (global.get $g2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $l3
;; CHECK-NEXT: (global.get $g3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (call_ref $indirect-type
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $l1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $l2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $l3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (global.get $g4)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $merges-multiple-effects (param $ref (ref $indirect-type))
(local $l1 i32)
(local $l2 i32)
(local $l3 i32)
(local $l4 i32)
(local.set $l1 (global.get $g1))
(local.set $l2 (global.get $g2))
(local.set $l3 (global.get $g3))
(local.set $l4 (global.get $g4))
;; This acts as a barrier for $l1, $l2, and $l3 but not $l4.
;; $ref may write to $g1 via $f1, or $g2 via $f2, $g3 via $f3 but not $g4.
;; $l4 is optimized out and the others are left alone.
(call_ref $indirect-type (i32.const 1) (local.get $ref))
(drop (local.get $l1))
(drop (local.get $l2))
(drop (local.get $l3))
(drop (local.get $l4))
)
)
;; Test that a reference that may contain an import has all possible effects.
(module
;; CHECK: (type $indirect-type (func (param i32)))
(type $indirect-type (func (param i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (param (ref $indirect-type))))
;; CHECK: (import "" "" (func $imported-func (type $indirect-type) (param i32)))
(import "" "" (func $imported-func (type $indirect-type)))
;; CHECK: (global $g1 (mut i32) (i32.const 0))
(global $g1 (mut i32) (i32.const 0))
;; CHECK: (global $g2 (mut i32) (i32.const 0))
(global $g2 (mut i32) (i32.const 0))
;; CHECK: (elem declare func $imported-func)
;; CHECK: (func $reference-imported-func (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $imported-func)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $reference-imported-func
(drop (ref.func $imported-func))
)
;; CHECK: (func $merges-multiple-effects (type $2) (param $ref (ref $indirect-type))
;; CHECK-NEXT: (local $l1 i32)
;; CHECK-NEXT: (local $l2 i32)
;; CHECK-NEXT: (local.set $l1
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $l2
;; CHECK-NEXT: (global.get $g2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call_ref $indirect-type
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $l1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $l2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $merges-multiple-effects (param $ref (ref $indirect-type))
(local $l1 i32)
(local $l2 i32)
(local.set $l1 (global.get $g1))
(local.set $l2 (global.get $g2))
;; This can flow to an import, so we assume that $g1 and $g2 could be
;; mutated, and nothing can be optimized.
;; TODO: Since $g1 and $g2 are not imported or exported, it's impossible for
;; an import to mutate them and we should be able to optimize anyway.
(call_ref $indirect-type (i32.const 1) (local.get $ref))
(drop (local.get $l1))
(drop (local.get $l2))
)
)