;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --vacuum -all -S -o - | filecheck %s

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

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects-fj (type $3) (param f32 funcref) (result i64)))
  (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects-fj (param f32) (param funcref) (result i64)))

  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects-ref (type $4) (param funcref) (result (ref any))))
  (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects-ref (param funcref) (result (ref any))))

  ;; CHECK:      (func $used (type $1) (result i32)
  ;; CHECK-NEXT:  (local $i32 i32)
  ;; CHECK-NEXT:  (local.set $i32
  ;; CHECK-NEXT:   (call $call.without.effects
  ;; CHECK-NEXT:    (ref.func $i)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $i32)
  ;; CHECK-NEXT: )
  (func $used (result i32)
    (local $i32 i32)
    ;; The result is used (by the local.set), so we cannot do anything here.
    (local.set $i32
      (call $call.without.effects (ref.func $i))
    )
    (local.get $i32)
  )

  ;; CHECK:      (func $unused (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $unused
    ;; The result is unused, so we can remove the call.
    (drop
      (call $call.without.effects (ref.func $i))
    )
  )

  ;; CHECK:      (func $unused-fj (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $unused-fj
    ;; As above, but with an extra float param and a different result type.
    (drop
      (call $call.without.effects-fj (f32.const 2.71828) (ref.func $fj))
    )
  )

  ;; CHECK:      (func $unused-fj-side-effects (type $5) (result f32)
  ;; CHECK-NEXT:  (local $f32 f32)
  ;; CHECK-NEXT:  (local.set $f32
  ;; CHECK-NEXT:   (f32.const 2.718280076980591)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $f32)
  ;; CHECK-NEXT: )
  (func $unused-fj-side-effects (result f32)
    (local $f32 f32)
    ;; As above, but side effects in the param. We must keep the params around
    ;; and drop them.
    (drop
      (call $call.without.effects-fj
        (local.tee $f32
          (f32.const 2.71828)
        )
        (ref.func $fj)
      )
    )
    (local.get $f32)
  )

  ;; CHECK:      (func $unused-unreachable (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $call.without.effects-fj
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:    (ref.func $fj)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $unused-unreachable
    ;; An unused result, but the call is unreachable and so we ignore it (and
    ;; leave it for DCE).
    (drop
      (call $call.without.effects-fj (unreachable) (ref.func $fj))
    )
  )

  ;; CHECK:      (func $used-fallthrough (type $0)
  ;; CHECK-NEXT:  (local $i32 i32)
  ;; CHECK-NEXT:  (local.set $i32
  ;; CHECK-NEXT:   (if (result i32)
  ;; CHECK-NEXT:    (block $condition (result i32)
  ;; CHECK-NEXT:     (call $nop)
  ;; CHECK-NEXT:     (call $call.without.effects
  ;; CHECK-NEXT:      (ref.func $i)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (block $ifTrue (result i32)
  ;; CHECK-NEXT:      (call $nop)
  ;; CHECK-NEXT:      (call $call.without.effects
  ;; CHECK-NEXT:       (ref.func $i)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (block $ifFalse (result i32)
  ;; CHECK-NEXT:      (call $nop)
  ;; CHECK-NEXT:      (call $call.without.effects
  ;; CHECK-NEXT:       (ref.func $i)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $used-fallthrough
    (local $i32 i32)
    (local.set $i32
      (if (result i32)
        ;; The block falls through a value that is used as the if condition.
        (block $condition (result i32)
          ;; Add a call to $nop so that the blocks are not optimized away.
          (call $nop)
          (call $call.without.effects (ref.func $i))
        )
        ;; The arms fall through their blocks and also through the if, and end
        ;; up used by the set.
        (then
          (block $ifTrue (result i32)
            (call $nop)
            (call $call.without.effects (ref.func $i))
          )
        )
        (else
          (block $ifFalse (result i32)
            (call $nop)
            (call $call.without.effects (ref.func $i))
          )
        )
      )
    )
  )

  ;; CHECK:      (func $unused-fallthrough (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (if (result i32)
  ;; CHECK-NEXT:    (block $condition (result i32)
  ;; CHECK-NEXT:     (call $nop)
  ;; CHECK-NEXT:     (call $call.without.effects
  ;; CHECK-NEXT:      (ref.func $i)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (block $ifTrue (result i32)
  ;; CHECK-NEXT:      (call $nop)
  ;; CHECK-NEXT:      (i32.const 0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (block $ifFalse (result i32)
  ;; CHECK-NEXT:      (call $nop)
  ;; CHECK-NEXT:      (i32.const 0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $unused-fallthrough
    (drop
      (if (result i32)
        (block $condition (result i32)
          (call $nop)
          (call $call.without.effects (ref.func $i))
        )
        ;; As above, but now there is a drop outside the if, so the arms are
        ;; unused and we can optimize them.
        (then
          (block $ifTrue (result i32)
            (call $nop)
            (call $call.without.effects (ref.func $i))
          )
        )
        (else
          (block $ifFalse (result i32)
            (call $nop)
            (call $call.without.effects (ref.func $i))
          )
        )
      )
    )
  )

  ;; CHECK:      (func $unused-fallthrough-bad-type (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (if (result (ref any))
  ;; CHECK-NEXT:    (call $i)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (call $call.without.effects-ref
  ;; CHECK-NEXT:      (ref.func $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (call $call.without.effects-ref
  ;; CHECK-NEXT:      (ref.func $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $unused-fallthrough-bad-type
    (drop
      (if (result (ref any))
        (call $i)
        ;; As above, but the type of these unused values prevents us from
        ;; optimizing as we cannot create a "zero" for them.
        (then
          (call $call.without.effects-ref (ref.func $ref))
        )
        (else
          (call $call.without.effects-ref (ref.func $ref))
        )
      )
    )
  )

  ;; CHECK:      (func $i (type $1) (result i32)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $i (result i32)
    ;; Helper function for the above.
    (unreachable)
  )

  ;; CHECK:      (func $fj (type $6) (param $0 f32) (result i64)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $fj (param f32) (result i64)
    ;; Helper function for the above.
    (unreachable)
  )

  ;; CHECK:      (func $ref (type $7) (result (ref any))
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $ref (result (ref any))
    ;; Helper function for the above.
    (unreachable)
  )


  ;; CHECK:      (func $nop (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $nop
    ;; Helper function for the above.
    (nop)
  )
)
