;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; RUN: foreach %s %t wasm-opt -all --inlining -S -o - | filecheck %s

;; Test that we inline functions with unreachable bodies. This is important to
;; propagate the trap to the caller (where it might lead to DCE).

(module
  (func $trap
    (unreachable)
  )
  ;; CHECK:      (type $0 (func))

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

  ;; CHECK:      (func $call-trap (type $0)
  ;; CHECK-NEXT:  (block $__inlined_func$trap
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $call-trap
    ;; In this case the call had type none, but the inlined code is unreachable,
    ;; so we'll add a br to the new block to keep the type as none (the br is
    ;; not actually reached, and other opts will remove it).
    (call $trap)
  )

  (func $trap-result (result i32)
    ;; As above, but now there is a declared result.
    (unreachable)
  )

  ;; CHECK:      (func $call-trap-result (type $1) (result i32)
  ;; CHECK-NEXT:  (block $__inlined_func$trap-result$1
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $call-trap-result (result i32)
    (call $trap-result)
  )

  (func $contents-then-trap
    ;; Add some contents in addition to the trap.
    (nop)
    (drop
      (i32.const 1337)
    )
    (nop)
    (unreachable)
  )
  ;; CHECK:      (func $call-contents-then-trap (type $0)
  ;; CHECK-NEXT:  (block $__inlined_func$contents-then-trap$2
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (i32.const 1337)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $call-contents-then-trap
    (call $contents-then-trap)
  )
)

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

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

  ;; CHECK:      (import "env" "imported" (func $imported (type $1) (param i32) (result i32)))
  (import "env" "imported" (func $imported (param i32) (result i32)))

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $__inlined_func$callee
  ;; CHECK-NEXT:    (call $imported
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (drop
      (call $callee)
    )
  )

  ;; After inlining, this return_call will turn into a call, but should still be
  ;; unreachable. Validation will fail if it is not.
  (func $callee (result i32)
    (return_call $imported
      (unreachable)
    )
  )

  ;; CHECK:      (func $caller-2 (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $__inlined_func$callee-2$1
  ;; CHECK-NEXT:    (block
  ;; CHECK-NEXT:     (block $__return_call
  ;; CHECK-NEXT:      (block
  ;; CHECK-NEXT:       (try
  ;; CHECK-NEXT:        (do
  ;; CHECK-NEXT:         (unreachable)
  ;; CHECK-NEXT:         (br $__return_call)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (call $imported
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller-2
    (drop
      (call $callee-2)
    )
  )

  ;; Same as above, but with a return_call with a try block
  (func $callee-2 (result i32)
    (try
      (do
        (return_call $imported
          (unreachable)
        )
      )
    )
  )
)

(module
  ;; CHECK:      (type $A (func))
  (type $A (func))

  (func $0
    (nop)
    (call_ref $A
      (ref.null nofunc) ;; In Binaryen IR this makes the call_ref unreachable.
    )
  )

  ;; CHECK:      (func $1 (type $A)
  ;; CHECK-NEXT:  (block $__inlined_func$0
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (nop)
  ;; 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:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $1 (type $A)
    ;; After inlining, this function body will become unreachable.
    (call $0)
  )
)

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

  ;; CHECK:      (func $0 (type $0) (result f64)
  ;; CHECK-NEXT:  (block $block
  ;; CHECK-NEXT:   (br_if $block
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (return
  ;; CHECK-NEXT:   (block $__inlined_func$1
  ;; CHECK-NEXT:    (block $block0
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $0 (result f64)
    (block $block
      (br_if $block
        (i32.const 0)
      )
    )
    (return
      ;; The inlined function has the same label, $block. We should not be
      ;; confused by that when we inline the unreachable code (an error can
      ;; occur if we mix up the two blocks or think they are identical; to avoid
      ;; that we should fix up the duplicate labels before doing anything that
      ;; depends on valid label names, like refinalization).
      (call $1)
    )
  )

  (func $1 (result f64)
    (block $block
      (unreachable)
    )
  )
)
