blob: 145ae1ff032a84ea55fb3f71ec73bdd544092bd3 [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up.
;; RUN: foreach %s %t wasm-opt --dae --all-features -S -o - | filecheck %s
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $1 (func (param i32)))
;; CHECK: (type $2 (func (result i32)))
;; CHECK: (type $3 (func (result f64)))
;; CHECK: (type $4 (func (param f64)))
;; CHECK: (import "a" "b" (func $get-i32 (type $2) (result i32)))
(import "a" "b" (func $get-i32 (result i32)))
;; CHECK: (import "a" "c" (func $get-f64 (type $3) (result f64)))
(import "a" "c" (func $get-f64 (result f64)))
;; CHECK: (table $0 2 2 funcref)
;; CHECK: (elem $0 (i32.const 0) $a9 $c8)
;; CHECK: (export "a8" (func $a8))
(export "a8" (func $a8))
(table 2 2 funcref)
(elem (i32.const 0) $a9 $c8)
;; CHECK: (func $a (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a (param $x i32))
;; CHECK: (func $b (type $0)
;; CHECK-NEXT: (call $a)
;; CHECK-NEXT: )
(func $b
(call $a (i32.const 1)) ;; best case scenario
)
;; CHECK: (func $a1 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $a1 (param $x i32)
(unreachable)
)
;; CHECK: (func $b1 (type $0)
;; CHECK-NEXT: (call $a1)
;; CHECK-NEXT: )
(func $b1
(call $a1 (i32.const 2)) ;; same value in both, so works
)
;; CHECK: (func $b11 (type $0)
;; CHECK-NEXT: (call $a1)
;; CHECK-NEXT: )
(func $b11
(call $a1 (i32.const 2))
)
;; CHECK: (func $a2 (type $1) (param $x i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a2 (param $x i32)
(drop (local.get $x))
)
;; CHECK: (func $b2 (type $0)
;; CHECK-NEXT: (call $a2
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b2
(call $a2 (i32.const 3)) ;; different value!
)
;; CHECK: (func $b22 (type $0)
;; CHECK-NEXT: (call $a2
;; CHECK-NEXT: (i32.const 4)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b22
(call $a2 (i32.const 4))
)
;; CHECK: (func $a3 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a3 (param $x i32)
(drop (i32.const -1)) ;; diff value, but at least unused, so no need to send
)
;; CHECK: (func $b3 (type $0)
;; CHECK-NEXT: (call $a3)
;; CHECK-NEXT: )
(func $b3
(call $a3 (i32.const 3))
)
;; CHECK: (func $b33 (type $0)
;; CHECK-NEXT: (call $a3)
;; CHECK-NEXT: )
(func $b33
(call $a3 (i32.const 4))
)
;; CHECK: (func $a4 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a4 (param $x i32)
;; This function is called with one constant and one unreachable. We can
;; remove the param despite the unreachable's effects.
)
;; CHECK: (func $b4 (type $0)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $b4
;; This call will vanish entirely, because the unreachable child executes
;; first (so we cannot see here that we removed the parameter from $a4, but
;; that can be confirmed in $a4 itself).
(call $a4 (unreachable))
)
;; CHECK: (func $b43 (type $0)
;; CHECK-NEXT: (call $a4)
;; CHECK-NEXT: )
(func $b43
;; We will remove the parameter here.
(call $a4 (i32.const 4))
)
;; CHECK: (func $a5 (type $0)
;; CHECK-NEXT: (local $0 f64)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a5 (param $x i32) (param $y f64) ;; optimize two
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $b5 (type $0)
;; CHECK-NEXT: (call $a5)
;; CHECK-NEXT: )
(func $b5
(call $a5 (i32.const 1) (f64.const 3.14159))
)
;; CHECK: (func $a6 (type $1) (param $0 i32)
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a6 (param $x i32) (param $y f64) ;; optimize just one
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $b6 (type $0)
;; CHECK-NEXT: (call $a6
;; CHECK-NEXT: (call $get-i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b6
(call $a6 (call $get-i32) (f64.const 3.14159))
)
;; CHECK: (func $a7 (type $4) (param $0 f64)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a7 (param $x i32) (param $y f64) ;; optimize just the other one
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $b7 (type $0)
;; CHECK-NEXT: (call $a7
;; CHECK-NEXT: (call $get-f64)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b7
(call $a7 (i32.const 1) (call $get-f64))
)
;; CHECK: (func $a8 (type $1) (param $x i32)
;; CHECK-NEXT: )
(func $a8 (param $x i32)) ;; exported, do not optimize
;; CHECK: (func $b8 (type $0)
;; CHECK-NEXT: (call $a8
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b8
(call $a8 (i32.const 1))
)
;; CHECK: (func $a9 (type $1) (param $x i32)
;; CHECK-NEXT: )
(func $a9 (param $x i32)) ;; tabled, do not optimize
;; CHECK: (func $b9 (type $0)
;; CHECK-NEXT: (call $a9
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b9
(call $a9 (i32.const 1))
)
;; CHECK: (func $a10 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (call $a10)
;; CHECK-NEXT: (call $a10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a10 (param $x i32) ;; recursion
(call $a10 (i32.const 1))
(call $a10 (i32.const 1))
)
;; CHECK: (func $a11 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (call $a11)
;; CHECK-NEXT: (call $a11)
;; CHECK-NEXT: )
(func $a11 (param $x i32) ;; partially successful recursion
(call $a11 (i32.const 1))
(call $a11 (i32.const 2))
)
;; CHECK: (func $a12 (type $1) (param $x i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $a12
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $a12
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $a12 (param $x i32) ;; unsuccessful recursion
(drop (local.get $x))
(call $a12 (i32.const 1))
(call $a12 (i32.const 2))
)
;; return values
;; CHECK: (func $c1 (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (call $c2)
;; CHECK-NEXT: (call $c3)
;; CHECK-NEXT: (call $c3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $c4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (call $c4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $c5
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $c6)
;; CHECK-NEXT: (call $c7)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $c8)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $c1
(local $x i32)
(drop (call $c2))
(drop (call $c3))
(drop (call $c3))
(drop (call $c4))
(local.set $x (call $c4))
(drop (call $c5 (unreachable)))
(drop (call $c6))
(drop (call $c7))
(drop (call $c8))
)
;; CHECK: (func $c2 (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $c2 (result i32)
(i32.const 1)
)
;; CHECK: (func $c3 (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $c3 (result i32)
(i32.const 2)
)
;; CHECK: (func $c4 (type $2) (result i32)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
(func $c4 (result i32)
(i32.const 3)
)
;; CHECK: (func $c5 (type $1) (param $x i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $c5 (param $x i32) (result i32)
(local.get $x)
)
;; CHECK: (func $c6 (type $0)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $c6 (result i32)
(unreachable)
)
;; CHECK: (func $c7 (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 4)
;; CHECK-NEXT: )
;; CHECK-NEXT: (return)
;; CHECK-NEXT: )
(func $c7 (result i32)
(return (i32.const 4))
)
;; CHECK: (func $c8 (type $2) (result i32)
;; CHECK-NEXT: (i32.const 5)
;; CHECK-NEXT: )
(func $c8 (result i32)
(i32.const 5)
)
)
(module ;; both operations at once: remove params and return value
;; CHECK: (type $0 (func))
;; CHECK: (export "a" (func $a))
;; CHECK: (func $a (type $0)
;; CHECK-NEXT: (call $b)
;; CHECK-NEXT: )
(func $a (export "a")
(drop
(call $b
(i32.const 1)
)
)
)
;; CHECK: (func $b (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $b (param $x i32) (result i32)
(local.get $x)
)
)
(module ;; tail calls inhibit dropped result removal
;; CHECK: (type $0 (func (param i32) (result i32)))
;; CHECK: (type $1 (func (result i32)))
;; CHECK: (func $foo (type $0) (param $x i32) (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (return_call $bar)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
(func $foo (param $x i32) (result i32)
(drop
(return_call $bar
(i32.const 0)
)
)
(i32.const 42)
)
;; CHECK: (func $bar (type $1) (result i32)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 7)
;; CHECK-NEXT: )
(func $bar (param $x i32) (result i32)
(i32.const 7)
)
)
(module ;; indirect tail calls inhibit dropped result removal
;; CHECK: (type $T (func (result i32)))
(type $T (func (result i32)))
(table 1 1 funcref)
;; CHECK: (type $1 (func))
;; CHECK: (table $0 1 1 funcref)
;; CHECK: (func $foo (type $T) (result i32)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (return_call_indirect $0 (type $T)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $foo (param $x i32) (result i32)
(drop
(return_call_indirect (type $T)
(i32.const 0)
)
)
)
;; CHECK: (func $bar (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $foo)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bar
(drop
(call $foo
(i32.const 42)
)
)
)
)
(module
;; CHECK: (type $0 (func (param funcref i32 f64) (result i64)))
;; CHECK: (type $1 (func (param f32) (result funcref)))
;; CHECK: (elem declare func $0)
;; CHECK: (export "export" (func $export))
;; CHECK: (func $0 (type $0) (param $0 funcref) (param $1 i32) (param $2 f64) (result i64)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $0 (param $0 funcref) (param $1 i32) (param $2 f64) (result i64)
(nop)
(unreachable)
)
;; CHECK: (func $export (type $1) (param $0 f32) (result funcref)
;; CHECK-NEXT: (ref.func $0)
;; CHECK-NEXT: )
(func $export (export "export") (param $0 f32) (result funcref)
;; a ref.func should prevent us from changing the type of a function, as it
;; may escape
(ref.func $0)
)
)
(module
;; CHECK: (type $i64 (func (param i64)))
(type $i64 (func (param i64)))
;; CHECK: (type $1 (func))
;; CHECK: (global $global$0 (ref $i64) (ref.func $0))
(global $global$0 (ref $i64) (ref.func $0))
;; CHECK: (export "even" (func $1))
(export "even" (func $1))
;; the argument to this function cannot be removed due to the ref.func of it
;; in a global
;; CHECK: (func $0 (type $i64) (param $0 i64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $0 (param $0 i64)
(unreachable)
)
;; CHECK: (func $1 (type $1)
;; CHECK-NEXT: (call_ref $i64
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: (global.get $global$0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $1
(call_ref $i64
(i64.const 0)
(global.get $global$0)
)
)
;; CHECK: (func $2 (type $1)
;; CHECK-NEXT: (call $0
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $2
(call $0
(i64.const 0)
)
)
)
(module
;; a removable non-nullable parameter
;; CHECK: (type $0 (func))
;; CHECK: (func $0 (type $0)
;; CHECK-NEXT: (local $0 i31ref)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $0 (param $x i31ref)
(nop)
)
;; CHECK: (func $1 (type $0)
;; CHECK-NEXT: (call $0)
;; CHECK-NEXT: )
(func $1
(call $0
(ref.i31 (i32.const 0))
)
)
)
;; Arguments that read an immutable global can be optimized, as that is a
;; constant value.
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $1 (func (param i32)))
;; CHECK: (type $2 (func (param i32 i32)))
;; CHECK: (global $immut i32 (i32.const 42))
(global $immut i32 (i32.const 42))
;; CHECK: (global $immut2 i32 (i32.const 43))
(global $immut2 i32 (i32.const 43))
;; CHECK: (global $mut (mut i32) (i32.const 1337))
(global $mut (mut i32) (i32.const 1337))
;; CHECK: (func $foo (type $1) (param $0 i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (global.get $immut)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $foo (param $x i32) (param $y i32)
;; "Use" the params to avoid other optimizations kicking in.
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $foo-caller (type $0)
;; CHECK-NEXT: (global.set $mut
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $foo
;; CHECK-NEXT: (global.get $mut)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.set $mut
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $foo
;; CHECK-NEXT: (global.get $mut)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $foo-caller
;; Note how the mutable param has a different value in each call, which shows
;; the reason that we cannot optimize in this case. But we can optimize the
;; immutable param.
(global.set $mut (i32.const 1))
(call $foo
(global.get $immut)
(global.get $mut)
)
(global.set $mut (i32.const 2))
(call $foo
(global.get $immut)
(global.get $mut)
)
)
;; CHECK: (func $bar (type $2) (param $x i32) (param $y i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bar (param $x i32) (param $y i32)
(drop (local.get $x))
(drop (local.get $y))
)
;; CHECK: (func $bar-caller (type $0)
;; CHECK-NEXT: (global.set $mut
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $bar
;; CHECK-NEXT: (global.get $immut)
;; CHECK-NEXT: (global.get $immut)
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.set $mut
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $bar
;; CHECK-NEXT: (global.get $mut)
;; CHECK-NEXT: (global.get $immut2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bar-caller
;; Corner cases of mixing mutable with immutable and mixing two immutables.
(global.set $mut (i32.const 1))
(call $bar
(global.get $immut)
(global.get $immut)
)
(global.set $mut (i32.const 2))
(call $bar
(global.get $mut)
(global.get $immut2)
)
)
)
(module
;; CHECK: (type $0 (func))
;; CHECK: (func $0 (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (return)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (return)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $0 (param $0 i32) (result i32)
;; The returns here are nested in each other, and one is a recursive call to
;; this function itself, which makes this a corner case we might emit invalid
;; code for. We end up removing the parameter, and then the call vanishes as
;; it was unreachable; we also remove the return as well as it is dropped in
;; the other caller, below.
(return
(drop
(call $0
(return
(i32.const 1)
)
)
)
)
)
;; CHECK: (func $other-call (type $0)
;; CHECK-NEXT: (call $0)
;; CHECK-NEXT: )
(func $other-call
(drop
(call $0
(i32.const 1)
)
)
)
)
(module
;; CHECK: (type $A (func (result (ref $A))))
(type $A (func (result (ref $A))))
;; CHECK: (type $1 (func))
;; CHECK: (func $no-caller (type $A) (result (ref $A))
;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null nofunc)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $no-caller (type $A) (result (ref $A))
;; This return_call is to a bottom type, which we should ignore and not error
;; on. There is nothing to optimize here (other passes will turn this call
;; into an unreachable). In particular we should not be confused by the fact
;; that this expression itself is unreachable (as a return call).
(return_call_ref $A
(ref.null nofunc)
)
)
;; CHECK: (func $caller (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $no-caller)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $caller
(drop
(call $no-caller)
)
)
)
(module
;; CHECK: (type $0 (func (param f64) (result i32)))
;; CHECK: (func $target (type $0) (param $0 f64) (result i32)
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local $3 i32)
;; CHECK-NEXT: (local $4 i32)
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (call $target
;; CHECK-NEXT: (f64.const 1.1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (call $target
;; CHECK-NEXT: (f64.const 4.4)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $target
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $target (param $a i32) (param $b f64) (param $c i32) (result i32)
;; Test removing a parameter despite calls having interesting non-unreachable
;; effects. This also tests recursion of such calls. We can remove all the i32
;; parameters here.
(call $target
(call $target
(i32.const 0)
(f64.const 1.1)
(i32.const 2)
)
(local.get $b)
(call $target
(i32.const 3)
(f64.const 4.4)
(i32.const 5)
)
)
)
)
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $v128 (func (result v128)))
(type $v128 (func (result v128)))
;; CHECK: (type $2 (func (result f32)))
;; CHECK: (table $0 10 funcref)
(table $0 10 funcref)
;; CHECK: (func $caller-effects (type $0)
;; CHECK-NEXT: (local $0 v128)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (call_indirect $0 (type $v128)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $target)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $caller-effects
(drop
(call $target
(i64.const 0)
;; We'd like to remove this unused parameter, but it has effects, so we'll
;; move it to a local first.
(call_indirect $0 (type $v128)
(i32.const 0)
)
(i64.const 0)
)
)
)
;; CHECK: (func $target (type $2) (result f32)
;; CHECK-NEXT: (local $0 i64)
;; CHECK-NEXT: (local $1 i64)
;; CHECK-NEXT: (local $2 v128)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $target (param $0 i64) (param $1 v128) (param $2 i64) (result f32)
;; All parameters here should vanish.
(unreachable)
)
)
(module
;; CHECK: (type $0 (func (param i32 i64)))
;; CHECK: (type $1 (func (param i64 i64)))
;; CHECK: (func $caller-later-br (type $0) (param $x i32) (param $y i64)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (block $block
;; CHECK-NEXT: (block
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (return)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $block)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $target
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $caller-later-br (param $x i32) (param $y i64)
(block $block
(drop
(call $target
(i64.const 0)
;; We'd like to remove this unused parameter, and we can do so by moving
;; it to a local, but we need to be careful: the br right after us must be
;; kept around, as it is the only thing that makes the outer block have
;; type none and not unreachable.
(block (result i32)
(if
(local.get $x)
(then
(return)
)
)
(i32.const 42)
)
;; We'll move this around, but won't remove it, as explained above.
(br $block)
)
)
)
;; Another call, to show the effect of removing the i32 parameter (also, if
;; no calls remain after removing the unreachable one before us, then the pass
;; would stop before removing parameters in $target - we don't remove params
;; from functions that look dead).
(drop
(call $target
(local.get $y)
(local.get $x)
(local.get $y)
)
)
)
;; CHECK: (func $target (type $1) (param $0 i64) (param $1 i64)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $target (param $0 i64) (param $1 i32) (param $2 i64) (result f32)
;; The i32 parameter should vanish.
(drop
(local.get $0)
)
(drop
(local.get $2)
)
(unreachable)
)
)
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $1 (func (param i32)))
;; CHECK: (func $target (type $0)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $target (param $0 i32)
;; The parameter here is unused: there is a get, but it is unreachable. We can
;; remove the parameter here, and in the caller below.
(unreachable)
(drop
(local.get $0)
)
)
;; CHECK: (func $caller (type $1) (param $x i32)
;; CHECK-NEXT: (call $target)
;; CHECK-NEXT: )
(func $caller (param $x i32)
(call $target
(local.get $x)
)
)
)