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

;; RUN: wasm-opt %s --remove-unused-brs -all -S -o - \
;; RUN:   | filecheck %s
;; RUN: wasm-opt %s --remove-unused-brs --pass-arg=remove-unused-brs-never-unconditionalize -all -S -o - \
;; RUN:   | filecheck %s --check-prefix=NO_UN

;; Verify that the "never-unconditionalize" flag is respected: when set, we do
;; not run code unconditionally that previously might not have run. This is
;; important as the branch hint in un-executed code may be right or wrong, which
;; can confuse the fuzzer.

(module
  ;; CHECK:      (func $selectify (type $0) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:   (@metadata.code.branch_hint "\01")
  ;; CHECK-NEXT:   (if (result i32)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (block $out (result i32)
  ;; CHECK-NEXT:      (nop)
  ;; CHECK-NEXT:      (i32.const 20)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; NO_UN:      (func $selectify (type $0) (param $x i32) (param $y i32) (result i32)
  ;; NO_UN-NEXT:  (if (result i32)
  ;; NO_UN-NEXT:   (local.get $x)
  ;; NO_UN-NEXT:   (then
  ;; NO_UN-NEXT:    (local.get $y)
  ;; NO_UN-NEXT:   )
  ;; NO_UN-NEXT:   (else
  ;; NO_UN-NEXT:    (@metadata.code.branch_hint "\01")
  ;; NO_UN-NEXT:    (if (result i32)
  ;; NO_UN-NEXT:     (local.get $y)
  ;; NO_UN-NEXT:     (then
  ;; NO_UN-NEXT:      (i32.const 10)
  ;; NO_UN-NEXT:     )
  ;; NO_UN-NEXT:     (else
  ;; NO_UN-NEXT:      (block $out (result i32)
  ;; NO_UN-NEXT:       (nop)
  ;; NO_UN-NEXT:       (i32.const 20)
  ;; NO_UN-NEXT:      )
  ;; NO_UN-NEXT:     )
  ;; NO_UN-NEXT:    )
  ;; NO_UN-NEXT:   )
  ;; NO_UN-NEXT:  )
  ;; NO_UN-NEXT: )
  (func $selectify (param $x i32) (param $y i32) (result i32)
    ;; This if can be a select, but the nested if's branch hint will then
    ;; always execute, which we should avoid when the flag is passed.
    (if (result i32)
      (local.get $x)
      (then
        (local.get $y)
      )
      (else
        (block $out (result i32)
          (@metadata.code.branch_hint "\01")
          (if
            (local.get $y)
            (then
              (br $out
                (i32.const 10)
              )
            )
          )
          (i32.const 20)
        )
      )
    )
  )

  ;; CHECK:      (func $if-select (type $1) (param $x i32) (param $y i32)
  ;; CHECK-NEXT:  (block $out
  ;; CHECK-NEXT:   (br_if $out
  ;; CHECK-NEXT:    (select
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; NO_UN:      (func $if-select (type $1) (param $x i32) (param $y i32)
  ;; NO_UN-NEXT:  (if
  ;; NO_UN-NEXT:   (local.get $x)
  ;; NO_UN-NEXT:   (then
  ;; NO_UN-NEXT:    (block $out
  ;; NO_UN-NEXT:     (br_if $out
  ;; NO_UN-NEXT:      (local.get $y)
  ;; NO_UN-NEXT:     )
  ;; NO_UN-NEXT:    )
  ;; NO_UN-NEXT:   )
  ;; NO_UN-NEXT:  )
  ;; NO_UN-NEXT: )
  (func $if-select (param $x i32) (param $y i32)
    ;; The br_if can be combined with the if using a select, but not when the
    ;; flag is passed.
    (block $out
      (if
        (local.get $x)
        (then
          (br_if $out
            (local.get $y)
          )
        )
      )
    )
  )

  ;; CHECK:      (func $if-select-2 (type $1) (param $x i32) (param $y i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; NO_UN:      (func $if-select-2 (type $1) (param $x i32) (param $y i32)
  ;; NO_UN-NEXT:  (if
  ;; NO_UN-NEXT:   (local.get $x)
  ;; NO_UN-NEXT:   (then
  ;; NO_UN-NEXT:    (if
  ;; NO_UN-NEXT:     (local.get $y)
  ;; NO_UN-NEXT:     (then
  ;; NO_UN-NEXT:      (nop)
  ;; NO_UN-NEXT:     )
  ;; NO_UN-NEXT:    )
  ;; NO_UN-NEXT:   )
  ;; NO_UN-NEXT:  )
  ;; NO_UN-NEXT: )
  (func $if-select-2 (param $x i32) (param $y i32)
    ;; The if conditions can be combined into one if with a select, but not when
    ;; the flag is passed.
    (if
      (local.get $x)
      (then
        (if
          (local.get $y)
          (then
            (nop)
          )
        )
      )
    )
  )

  ;; CHECK:      (func $nothing (type $2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  ;; NO_UN:      (func $nothing (type $2)
  ;; NO_UN-NEXT:  (nop)
  ;; NO_UN-NEXT: )
  (func $nothing
    ;; Helper for below.
    (nop)
  )

  ;; CHECK:      (func $restructure-br_if-value-effectful (type $0) (param $x i32) (param $y i32) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (call $nothing)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (block $x (result i32)
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; NO_UN:      (func $restructure-br_if-value-effectful (type $0) (param $x i32) (param $y i32) (result i32)
  ;; NO_UN-NEXT:  (block $x (result i32)
  ;; NO_UN-NEXT:   (drop
  ;; NO_UN-NEXT:    (br_if $x
  ;; NO_UN-NEXT:     (block (result i32)
  ;; NO_UN-NEXT:      (call $nothing)
  ;; NO_UN-NEXT:      (local.get $y)
  ;; NO_UN-NEXT:     )
  ;; NO_UN-NEXT:     (local.get $x)
  ;; NO_UN-NEXT:    )
  ;; NO_UN-NEXT:   )
  ;; NO_UN-NEXT:   (i32.const 0)
  ;; NO_UN-NEXT:  )
  ;; NO_UN-NEXT: )
  (func $restructure-br_if-value-effectful (param $x i32) (param $y i32) (result i32)
    ;; We can restructure this to a select, but not when the flag is passed.
    (block $x (result i32)
      (drop
        (br_if $x
          (block (result i32)
            (call $nothing)
            (local.get $y)
          )
          (local.get $x)
        )
      )
      (i32.const 0)
    )
  )
)
