;; 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 --remove-unused-brs --all-features -S -o - | filecheck %s

(module
 ;; CHECK:      (type $vector (array (mut i32)))
 (type $vector (array (mut i32)))
 ;; CHECK:      (type $struct (struct (field (ref null $vector))))
 (type $struct (struct (field (ref null $vector))))
 ;; CHECK:      (type $2 (func (param i32)))

 ;; CHECK:      (type $3 (func (result (ref null $struct))))

 ;; CHECK:      (type $4 (func (result f64)))

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

 ;; CHECK:      (type $6 (func (param i32) (result funcref)))

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

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

 ;; CHECK:      (type $9 (func (param funcref) (result funcref)))

 ;; CHECK:      (type $10 (func (result funcref)))

 ;; CHECK:      (import "out" "log" (func $log (type $2) (param i32)))
 (import "out" "log" (func $log (param i32)))
 ;; CHECK:      (elem declare func $br_on_non_null $br_on_null $i32_=>_none $none_=>_i32)

 ;; CHECK:      (func $foo (type $3) (result (ref null $struct))
 ;; CHECK-NEXT:  (if (result (ref null $struct))
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (struct.new $struct
 ;; CHECK-NEXT:     (array.new_default $vector
 ;; CHECK-NEXT:      (i32.const 1)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (ref.null none)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $foo (result (ref null $struct))
  (if (result (ref null $struct))
   (i32.const 1)
   (then
    (struct.new $struct
     ;; regression test for computing the cost of an array.new_default, which
     ;; lacks the optional field "init"
     (array.new_default $vector
      (i32.const 1)
     )
    )
   )
   (else
    (ref.null $struct)
   )
  )
 )

 ;; CHECK:      (func $test-prefinalize (type $4) (result f64)
 ;; CHECK-NEXT:  (local $x i32)
 ;; CHECK-NEXT:  (loop $loop (result f64)
 ;; CHECK-NEXT:   (if (result f64)
 ;; CHECK-NEXT:    (local.get $x)
 ;; CHECK-NEXT:    (then
 ;; CHECK-NEXT:     (f64.const 0)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (else
 ;; CHECK-NEXT:     (block $block (result f64)
 ;; CHECK-NEXT:      (nop)
 ;; CHECK-NEXT:      (br_if $loop
 ;; CHECK-NEXT:       (i32.eqz
 ;; CHECK-NEXT:        (i32.const 0)
 ;; CHECK-NEXT:       )
 ;; CHECK-NEXT:      )
 ;; CHECK-NEXT:      (unreachable)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $test-prefinalize (result f64)
  (local $x i32)
  (loop $loop (result f64)
   (block $block (result f64)
    (drop
     (br_if $block
      (f64.const 0)
      (local.get $x)
     )
    )
    (if
     (i32.const 0)
     (then
      (unreachable)
     )
    )
    ;; this will be moved from $block into the if right before it. we must be
    ;; careful to properly finalize() things, as if we finalize the block too
    ;; early - before the if - then the block ends in a none type, which is
    ;; invalid.
    (br $loop)
   )
  )
 )

 ;; CHECK:      (func $none_=>_i32 (type $5) (result i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 (func $none_=>_i32 (result i32)
  (unreachable)
 )
 ;; CHECK:      (func $i32_=>_none (type $2) (param $0 i32)
 ;; CHECK-NEXT: )
 (func $i32_=>_none (param i32)
 )
 ;; CHECK:      (func $selectify (type $6) (param $x i32) (result funcref)
 ;; CHECK-NEXT:  (select (result (ref func))
 ;; CHECK-NEXT:   (ref.func $none_=>_i32)
 ;; CHECK-NEXT:   (ref.func $i32_=>_none)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $selectify (param $x i32) (result funcref)
  ;; this if has arms with different function types, for which funcref is the
  ;; LUB
  (if (result funcref)
   (local.get $x)
   (then
    (ref.func $none_=>_i32)
   )
   (else
    (ref.func $i32_=>_none)
   )
  )
 )

 ;; CHECK:      (func $br_on_null (type $7) (param $x funcref)
 ;; CHECK-NEXT:  (block $null
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (block
 ;; CHECK-NEXT:     (drop
 ;; CHECK-NEXT:      (ref.null nofunc)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (br $null)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (ref.func $br_on_null)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (br_on_null $null
 ;; CHECK-NEXT:     (local.get $x)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $br_on_null (param $x funcref)
  (block $null
   ;; A null reference to bottom is definitely null, and the br is always taken.
   (drop
    (br_on_null $null (ref.null nofunc))
   )
   ;; On the other hand, if we know the input is not null, the branch will never
   ;; be taken.
   (drop
    (br_on_null $null (ref.func $br_on_null))
   )
   ;; If we don't know whether the input is null, we can't optimize.
   (drop
    (br_on_null $null (local.get $x))
   )
  )
 )

 ;; CHECK:      (func $br_on_null-fallthrough (type $8)
 ;; CHECK-NEXT:  (local $x funcref)
 ;; CHECK-NEXT:  (block $null
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (block
 ;; CHECK-NEXT:     (drop
 ;; CHECK-NEXT:      (local.tee $x
 ;; CHECK-NEXT:       (ref.null nofunc)
 ;; CHECK-NEXT:      )
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (br $null)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (ref.as_non_null
 ;; CHECK-NEXT:     (local.tee $x
 ;; CHECK-NEXT:      (ref.func $br_on_null)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $br_on_null-fallthrough
  ;; This is the same as above, but now the necessary type information comes
  ;; from fallthrough values.
  (local $x funcref)
  (block $null
   ;; Definitely taken.
   (drop
    (br_on_null $null (local.tee $x (ref.null nofunc)))
   )
   ;; Definitely not taken. Optimizable, but still requires a cast for validity.
   (drop
    (br_on_null $null (local.tee $x (ref.func $br_on_null)))
   )
  )
 )

 ;; CHECK:      (func $br_on_non_null (type $9) (param $x funcref) (result funcref)
 ;; CHECK-NEXT:  (block $non-null (result (ref func))
 ;; CHECK-NEXT:   (br $non-null
 ;; CHECK-NEXT:    (ref.func $br_on_non_null)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (ref.null nofunc)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (br_on_non_null $non-null
 ;; CHECK-NEXT:    (local.get $x)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $br_on_non_null (param $x funcref) (result funcref)
  (block $non-null (result (ref func))
   ;; A non-null reference is not null, and the br is always taken.
   (br_on_non_null $non-null
    (ref.func $br_on_non_null)
   )
   ;; On the other hand, if we know the input is null, the branch will never be
   ;; taken.
   (br_on_non_null $non-null
    (ref.null nofunc)
   )
   ;; If we don't know whether the input is null, we can't optimize.
   (br_on_non_null $non-null
    (local.get $x)
   )
   (unreachable)
  )
 )

 ;; CHECK:      (func $br_on_non_null-fallthrough (type $10) (result funcref)
 ;; CHECK-NEXT:  (local $x funcref)
 ;; CHECK-NEXT:  (block $non-null (result (ref func))
 ;; CHECK-NEXT:   (br $non-null
 ;; CHECK-NEXT:    (ref.as_non_null
 ;; CHECK-NEXT:     (local.tee $x
 ;; CHECK-NEXT:      (ref.func $br_on_non_null)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (local.tee $x
 ;; CHECK-NEXT:     (ref.null nofunc)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $br_on_non_null-fallthrough (result funcref)
  ;; Same as above, but now using fallthrough values.
  (local $x funcref)
  (block $non-null (result (ref func))
   ;; Definitely taken. Requires cast.
   (br_on_non_null $non-null
    (local.tee $x (ref.func $br_on_non_null))
   )
   ;; Definitely not taken.
   (br_on_non_null $non-null
    (local.tee $x (ref.null nofunc))
   )
   (unreachable)
  )
 )
)
