;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --gufa --closed-world -S -o - | filecheck %s

;; Closed-world is applied here to avoid treating all ref.funcs as callable
;; from outside (and this is the more important mode to test on).

(module
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $2 (func (param i32 i32) (result i32)))

  ;; CHECK:      (type $3 (func (param i32) (result i32)))

  ;; CHECK:      (import "a" "b" (func $import (type $0) (result i32)))
  (import "a" "b" (func $import (result i32)))


  ;; CHECK:      (export "param-no" (func $param-no))

  ;; CHECK:      (func $never-called (type $3) (param $param i32) (result i32)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $never-called (param $param i32) (result i32)
    ;; This function is never called, so no content is possible in $param, and
    ;; we know this must be unreachable code that can be removed (replaced with
    ;; an unreachable).
    (local.get $param)
  )

  ;; CHECK:      (func $foo (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: )
  (func $foo (result i32)
    (i32.const 1)
  )

  ;; CHECK:      (func $bar (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (call $foo)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $import)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $bar
    ;; Both arms of the select have identical values, 1. Inlining +
    ;; OptimizeInstructions could of course discover that in this case, but
    ;; GUFA can do so even without inlining. As a result the select will be
    ;; dropped (due to the call which may have effects, we keep it), and at the
    ;; end we emit the constant 1 for the value.
    (drop
      (select
        (call $foo)
        (i32.const 1)
        (call $import)
      )
    )
  )

  ;; CHECK:      (func $baz (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (call $foo)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.eqz
  ;; CHECK-NEXT:     (i32.eqz
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (call $import)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $baz
    (drop
      (select
        (call $foo)
        ;; As above, but replace 1 with eqz(eqz(1)).This pass assumes any eqz
        ;; etc is a new value, and so here we do not optimize the select (we do
        ;; still optimize the call's result, though).
        (i32.eqz
          (i32.eqz
            (i32.const 1)
          )
        )
        (call $import)
      )
    )
  )

  ;; CHECK:      (func $return (type $0) (result i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: )
  (func $return (result i32)
    ;; Helper function that returns one result in a return and flows another
    ;; out. There is nothing to optimize in this function, but see the caller
    ;; below.
    (if
      (i32.const 0)
      (then
        (return
          (i32.const 1)
        )
      )
    )
    (i32.const 2)
  )

  ;; CHECK:      (func $call-return (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $return)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $call-return
    ;; The called function has two possible return values, so we cannot optimize
    ;; anything here.
    (drop
      (call $return)
    )
  )

  ;; CHECK:      (func $return-same (type $0) (result i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: )
  (func $return-same (result i32)
    ;; Helper function that returns the same result in a return as it flows out.
    ;; This is the same as above, but now the values are identical, and the
    ;; function must return 1. There is nothing to optimize in this function,
    ;; but see the caller below.
    (if
      (i32.const 0)
      (then
        (return
          (i32.const 1)
        )
      )
    )
    (i32.const 1)
  )

  ;; CHECK:      (func $call-return-same (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $return-same)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $call-return-same
    ;; Unlike in $call-return, now we can optimize here.
    (drop
      (call $return-same)
    )
  )

  ;; CHECK:      (func $local-no (type $0) (result i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (call $import)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $x)
  ;; CHECK-NEXT: )
  (func $local-no (result i32)
    (local $x i32)
    (if
      (call $import)
      (then
        (local.set $x
          (i32.const 1)
        )
      )
    )
    ;; $x has two possible values, 1 and the default 0, so we cannot optimize
    ;; anything here.
    (local.get $x)
  )

  ;; CHECK:      (func $local-yes (type $0) (result i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (call $import)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $local-yes (result i32)
    (local $x i32)
    (if
      (call $import)
      (then
        (local.set $x
          ;; As above, but now we set 0 here. We can optimize the local.get to 0
          ;; in this case.
          (i32.const 0)
        )
      )
    )
    (local.get $x)
  )

  ;; CHECK:      (func $param-no (type $3) (param $param i32) (result i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $param)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $param
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $param)
  ;; CHECK-NEXT: )
  (func $param-no (export "param-no") (param $param i32) (result i32)
    (if
      (local.get $param)
      (then
        (local.set $param
          (i32.const 1)
        )
      )
    )
    ;; $x has two possible values, the incoming param value and 1, so we cannot
    ;; optimize, since the function is exported - anything on the outside could
    ;; call it with values we are not aware of during the optimization.
    (local.get $param)
  )

  ;; CHECK:      (func $param-yes (type $3) (param $param i32) (result i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $param
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: )
  (func $param-yes (param $param i32) (result i32)
    ;; As above, but now the function is not exported. That means it has no
    ;; callers, so the first local.get can contain nothing, and will become an
    ;; unreachable. The other local.get later down can only contain the
    ;; local.set in the if, so we'll optimize it to 1.
    (if
      (local.get $param)
      (then
        (local.set $param
          (i32.const 1)
        )
      )
    )
    (local.get $param)
  )

  ;; CHECK:      (func $cycle (type $2) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $cycle
  ;; CHECK-NEXT:      (i32.const 42)
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (call $cycle
  ;; CHECK-NEXT:        (i32.const 42)
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 42)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $cycle (param $x i32) (param $y i32) (result i32)
    ;; Return 42, or else the result from a recursive call. The only possible
    ;; value is 42, which we can optimize to.
    ;; (Nothing else calls $cycle, so this is dead code in actuality, but this
    ;; pass leaves such things to other passes.)
    ;; Note that the first call passes constants for $x and $y which lets us
    ;; optimize them too (as we see no other contents arrive to them).
    (drop
      (call $cycle
        (i32.const 42)
        (i32.const 1)
      )
    )
    (select
      (i32.const 42)
      (call $cycle
        (local.get $x)
        (local.get $y)
      )
      (local.get $y)
    )
  )

  ;; CHECK:      (func $cycle-2 (type $2) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $cycle-2
  ;; CHECK-NEXT:      (i32.const 42)
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (call $cycle-2
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 42)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $cycle-2 (param $x i32) (param $y i32) (result i32)
    (drop
      (call $cycle-2
        (i32.const 42)
        (i32.const 1)
      )
    )
    ;; As above, but flip one $x and $y on the first and last local.gets. We
    ;; can see that $y must contain 1, and we cannot infer a value for $x (it
    ;; is sent both 42 and $y which is 1). Even without $x, however, we can see
    ;; the value leaving the select is 42, which means the call returns 42.
    (select
      (i32.const 42)
      (call $cycle-2
        (local.get $y)
        (local.get $y)
      )
      (local.get $x)
    )
  )

  ;; CHECK:      (func $cycle-3 (type $2) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $cycle-3
  ;; CHECK-NEXT:      (i32.const 1337)
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (call $cycle-3
  ;; CHECK-NEXT:        (i32.eqz
  ;; CHECK-NEXT:         (local.get $x)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 42)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $cycle-3 (param $x i32) (param $y i32) (result i32)
    ;; Even adding a caller with a different value for $x does not prevent us
    ;; from optimizing here.
    (drop
      (call $cycle-3
        (i32.const 1337)
        (i32.const 1)
      )
    )
    ;; As $cycle, but add an i32.eqz on $x. We can still optimize this, as
    ;; while the eqz is a new value arriving in $x, we do not actually return
    ;; $x, and again the only possible value flowing in the graph is 42.
    (select
      (i32.const 42)
      (call $cycle-3
        (i32.eqz
          (local.get $x)
        )
        (local.get $y)
      )
      (local.get $y)
    )
  )

  ;; CHECK:      (func $cycle-4 (type $2) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $cycle-4
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:   (call $cycle-4
  ;; CHECK-NEXT:    (i32.eqz
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cycle-4 (param $x i32) (param $y i32) (result i32)
    (drop
      (call $cycle-4
        (i32.const 1337)
        (i32.const 1)
      )
    )
    (select
      ;; As above, but we have no constant here, but $x. We may now return $x or
      ;; $eqz of $x, which means we cannot infer the result of the call. (But we
      ;; can still infer the value of $y, which is 1.)
      (local.get $x)
      (call $cycle-4
        (i32.eqz
          (local.get $x)
        )
        (local.get $y)
      )
      (local.get $y)
    )
  )

  ;; CHECK:      (func $cycle-5 (type $2) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $cycle-5
  ;; CHECK-NEXT:      (i32.const 1337)
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (call $cycle-5
  ;; CHECK-NEXT:        (i32.const 1337)
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 1337)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: )
  (func $cycle-5 (param $x i32) (param $y i32) (result i32)
    (drop
      (call $cycle-5
        (i32.const 1337)
        (i32.const 1)
      )
    )
    (select
      (local.get $x)
      (call $cycle-5
        ;; As above, but now we return $x in both cases, so we can optimize, and
        ;; infer the result is the 1337 which is passed in the earlier call.
        (local.get $x)
        (local.get $y)
      )
      (local.get $y)
    )
  )

  ;; CHECK:      (func $blocks (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block $named (result i32)
  ;; CHECK-NEXT:      (if
  ;; CHECK-NEXT:       (i32.const 0)
  ;; CHECK-NEXT:       (then
  ;; CHECK-NEXT:        (br $named
  ;; CHECK-NEXT:         (i32.const 1)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $named0 (result i32)
  ;; CHECK-NEXT:    (if
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (br $named0
  ;; CHECK-NEXT:       (i32.const 2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $blocks
    ;; A block with a branch to it, which we can infer a constant for.
    (drop
      (block $named (result i32)
        (if
          (i32.const 0)
          (then
            (br $named
              (i32.const 1)
            )
          )
        )
        (i32.const 1)
      )
    )
    ;; As above, but the two values reaching the block do not agree, so we
    ;; should not optimize.
    (drop
      (block $named (result i32)
        (if
          (i32.const 0)
          (then
            (br $named
              (i32.const 2) ;; this changed
            )
          )
        )
        (i32.const 1)
      )
    )
  )
)

(module
  ;; CHECK:      (type $i (func (param i32)))
  (type $i (func (param i32)))

  (table 10 funcref)
  (elem (i32.const 0) funcref
    (ref.func $reffed)
  )

  (export "table" (table 0))

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (table $0 10 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $reffed)

  ;; CHECK:      (export "table" (table $0))

  ;; CHECK:      (func $reffed (type $i) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $reffed (param $x i32)
    ;; This function is in the table, and the table is exported, so we cannot
    ;; see all callers, and cannot infer the value here.
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $do-calls (type $1)
  ;; CHECK-NEXT:  (call $reffed
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $do-calls
    (call $reffed
      (i32.const 42)
    )
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
  )
)

;; As above, but the table is not exported. We have a direct and an indirect
;; call with the same value, so we can optimize inside $reffed.
(module
  ;; CHECK:      (type $i (func (param i32)))
  (type $i (func (param i32)))

  (table 10 funcref)
  (elem (i32.const 0) funcref
    (ref.func $reffed)
  )

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (table $0 10 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $reffed)

  ;; CHECK:      (func $reffed (type $i) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $reffed (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $do-calls (type $1)
  ;; CHECK-NEXT:  (call $reffed
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $do-calls
    (call $reffed
      (i32.const 42)
    )
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
  )
)

;; As above but the only calls are indirect.
(module
  ;; CHECK:      (type $i (func (param i32)))
  (type $i (func (param i32)))

  (table 10 funcref)
  (elem (i32.const 0) funcref
    (ref.func $reffed)
  )

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (table $0 10 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $reffed)

  ;; CHECK:      (func $reffed (type $i) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $reffed (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $do-calls (type $1)
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $do-calls
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
  )
)

;; As above but the indirect calls have different parameters, so we do not
;; optimize.
(module
  ;; CHECK:      (type $i (func (param i32)))
  (type $i (func (param i32)))

  (table 10 funcref)
  (elem (i32.const 0) funcref
    (ref.func $reffed)
  )

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (table $0 10 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $reffed)

  ;; CHECK:      (func $reffed (type $i) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $reffed (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $do-calls (type $1)
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 1337)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $do-calls
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
    (call_indirect (type $i)
      (i32.const 1337)
      (i32.const 0)
    )
  )
)

;; As above but the second call is of another function type, so it does not
;; prevent us from optimizing even though it has a different value.
(module
  ;; CHECK:      (type $i (func (param i32)))
  (type $i (func (param i32)))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (type $f (func (param f32)))
  (type $f (func (param f32)))

  (table 10 funcref)
  (elem (i32.const 0) funcref
    (ref.func $reffed)
  )

  ;; CHECK:      (table $0 10 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $reffed)

  ;; CHECK:      (func $reffed (type $i) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $reffed (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $do-calls (type $1)
  ;; CHECK-NEXT:  (call_indirect $0 (type $i)
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $f)
  ;; CHECK-NEXT:   (f32.const 1337)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $do-calls
    (call_indirect (type $i)
      (i32.const 42)
      (i32.const 0)
    )
    (call_indirect (type $f)
      (f32.const 1337)
      (i32.const 0)
    )
  )
)

(module
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (func $const (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $const (result i32)
    ;; Return a const to the caller below.
    (i32.const 42)
  )

  ;; CHECK:      (func $retcall (type $0) (result i32)
  ;; CHECK-NEXT:  (return_call $const)
  ;; CHECK-NEXT: )
  (func $retcall (result i32)
    ;; Do a return call. This tests that we pass its value out as a result.
    (return_call $const)
  )

  ;; CHECK:      (func $caller (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $retcall)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    ;; Call the return caller. We can optimize this value to 42.
    (drop
      (call $retcall)
    )
  )
)

;; Imports have unknown values.
(module
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (import "a" "b" (func $import (type $0) (result i32)))
  (import "a" "b" (func $import (result i32)))

  ;; CHECK:      (func $internal (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $internal (result i32)
    (i32.const 42)
  )

  ;; CHECK:      (func $calls (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $import)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (call $internal)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $calls
    (drop
      (call $import)
    )
    ;; For comparison, we can optimize this call to an internal function.
    (drop
      (call $internal)
    )
  )
)

;; Test for nontrivial code in a global init. We need to process such code
;; normally and not hit any internal asserts (nothing can be optimized here).
(module
  ;; CHECK:      (global $global$0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 10)
  ;; CHECK-NEXT:  (i32.const 20)
  ;; CHECK-NEXT: ))
  (global $global$0 i32
    (i32.add
      (i32.const 10)
      (i32.const 20)
    )
  )
)

;; 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 (param i32)))
  (type $A (func (param i32)))

  (import "binaryen-intrinsics" "call.without.effects"
    (func $call-without-effects (param i32 funcref)))

  (import "other" "import"
    (func $other-import (param funcref)))

  ;; CHECK:      (type $1 (func (param i32 funcref)))

  ;; CHECK:      (type $2 (func (param funcref)))

  ;; CHECK:      (type $3 (func))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param i32 funcref)))

  ;; CHECK:      (import "other" "import" (func $other-import (type $2) (param funcref)))

  ;; CHECK:      (elem declare func $target-drop $target-keep)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $3)
  ;; CHECK-NEXT:  (call $call-without-effects
  ;; CHECK-NEXT:   (i32.const 1)
  ;; 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 $target-keep's code is reachable. We should leave it alone,
    ;; but we can put an unreachable in $target-drop.
    (call $call-without-effects
     (i32.const 1)
     (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) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $target-keep (type $A) (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $target-drop (type $A) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $target-drop (type $A) (param $x i32)
    (drop
      (local.get $x)
    )
  )
)

;; As above, but now the call to the intrinsic does not let us see the exact
;; function being called.
(module
  ;; CHECK:      (type $A (func (param i32)))
  (type $A (func (param i32)))

  (import "binaryen-intrinsics" "call.without.effects"
    (func $call-without-effects (param i32 funcref)))

  (import "other" "import"
    (func $other-import (param funcref)))

  ;; CHECK:      (type $1 (func (param i32 funcref)))

  ;; CHECK:      (type $2 (func (param funcref)))

  ;; CHECK:      (type $3 (func (param (ref null $A))))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call-without-effects (type $1) (param i32 funcref)))

  ;; CHECK:      (import "other" "import" (func $other-import (type $2) (param funcref)))

  ;; CHECK:      (elem declare func $target-keep $target-keep-2)

  ;; CHECK:      (export "foo" (func $foo))

  ;; CHECK:      (func $foo (type $3) (param $A (ref null $A))
  ;; CHECK-NEXT:  (call $call-without-effects
  ;; CHECK-NEXT:   (i32.const 1)
  ;; 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 the bodies of both $target-keep and
    ;; $target-keep-2 - no unreachables will be placed in either one.
    (call $call-without-effects
      (i32.const 1)
      (local.get $A)
    )
    (drop
      (ref.func $target-keep)
    )
    (call $other-import
      (ref.func $target-keep-2)
    )
  )

  ;; CHECK:      (func $target-keep (type $A) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $target-keep (type $A) (param $x i32)
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $target-keep-2 (type $A) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $target-keep-2 (type $A) (param $x i32)
    (drop
      (local.get $x)
    )
  )
)

;; Exported mutable globals can contain any value, as the outside can write to
;; them.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $exported-mutable (mut i32) (i32.const 42))
  (global $exported-mutable (mut i32) (i32.const 42))
  ;; CHECK:      (global $exported-immutable i32 (i32.const 42))
  (global $exported-immutable i32 (i32.const 42))
  ;; CHECK:      (global $mutable (mut i32) (i32.const 42))
  (global $mutable (mut i32) (i32.const 42))
  ;; CHECK:      (global $immutable i32 (i32.const 42))
  (global $immutable i32 (i32.const 42))

  ;; CHECK:      (export "exported-mutable" (global $exported-mutable))
  (export "exported-mutable" (global $exported-mutable))
  ;; CHECK:      (export "exported-immutable" (global $exported-immutable))
  (export "exported-immutable" (global $exported-immutable))

  ;; CHECK:      (func $test (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $exported-mutable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    ;; This should not be optimized to a constant.
    (drop
      (global.get $exported-mutable)
    )
    ;; All the rest can be optimized.
    (drop
      (global.get $exported-immutable)
    )
    (drop
      (global.get $mutable)
    )
    (drop
      (global.get $immutable)
    )
  )
)

;; Imported functions can be inferred.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "" "" (func $imported (type $0)))
  (import "" "" (func $imported))

  ;; CHECK:      (elem declare func $defined $imported)

  ;; CHECK:      (func $defined (type $0)
  ;; CHECK-NEXT:  (local $temp funcref)
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (ref.func $imported)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $imported)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (ref.func $defined)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $defined)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $defined
    (local $temp funcref)
    (local.set $temp
      (ref.func $imported)
    )
    ;; This will become a ref.func $imported.
    (drop
      (local.get $temp)
    )

    ;; Test a defined function too.
    (local.set $temp
      (ref.func $defined)
    )
    ;; This will become a ref.func $defined.
    (drop
      (local.get $temp)
    )
  )
)

;; We cannot know the types of imported functions, so we should not be able to
;; optimize this exact cast.
(module
  ;; CHECK:      (type $func (sub (func)))
  (type $func (sub (func)))
  (type $sub (sub $func (func)))
  ;; CHECK:      (type $1 (func (result i32)))

  ;; CHECK:      (import "" "" (func $f (type $func)))
  (import "" "" (func $f (type $func)))
  ;; CHECK:      (elem declare func $f)

  ;; CHECK:      (export "test" (func $test))

  ;; CHECK:      (func $test (type $1) (result i32)
  ;; CHECK-NEXT:  (ref.test (ref (exact $func))
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (export "test") (result i32)
    (ref.test (ref (exact $func))
      (ref.func $f)
    )
  )
)

;; As above, but now the cast is to a subtype. We should not be able to optimize
;; this either.
(module
  ;; CHECK:      (type $func (sub (func)))
  (type $func (sub (func)))
  ;; CHECK:      (type $1 (func (result i32)))

  ;; CHECK:      (type $sub (sub $func (func)))
  (type $sub (sub $func (func)))
  ;; CHECK:      (import "" "" (func $f (type $func)))
  (import "" "" (func $f (type $func)))
  ;; CHECK:      (elem declare func $f)

  ;; CHECK:      (export "test" (func $test))

  ;; CHECK:      (func $test (type $1) (result i32)
  ;; CHECK-NEXT:  (ref.test (ref $sub)
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (export "test") (result i32)
    (ref.test (ref $sub)
      (ref.func $f)
    )
  )
)

;; This is another exact cast, but now the imported type is final. TODO: Use
;; finality in this pass, as we could optimize here.
(module
  ;; CHECK:      (type $func (func))
  (type $func (sub final (func)))
  ;; CHECK:      (type $1 (func (result i32)))

  ;; CHECK:      (import "" "" (func $f (type $func)))
  (import "" "" (func $f (type $func)))
  ;; CHECK:      (elem declare func $f)

  ;; CHECK:      (export "test" (func $test))

  ;; CHECK:      (func $test (type $1) (result i32)
  ;; CHECK-NEXT:  (ref.test (ref (exact $func))
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (export "test") (result i32)
    (ref.test (ref (exact $func))
      (ref.func $f)
    )
  )
)

;; Now we use a ref.cast instead of a ref.test with the exact cast to the final
;; type. We cannot optimize even though we know the cast will succeed because
;; the Wasm type of the function reference is inexact.
(module
  ;; CHECK:      (type $func (func))
  (type $func (sub final (func)))
  ;; CHECK:      (type $1 (func (result funcref)))

  ;; CHECK:      (import "" "" (func $f (type $func)))
  (import "" "" (func $f (type $func)))
  ;; CHECK:      (elem declare func $f)

  ;; CHECK:      (export "test" (func $test))

  ;; CHECK:      (func $test (type $1) (result funcref)
  ;; CHECK-NEXT:  (ref.cast (ref (exact $func))
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (export "test") (result funcref)
    (ref.cast (ref (exact $func))
      (ref.func $f)
    )
  )
)

