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

;; RUN: wasm-opt %s --optimize-instructions -all -S -o - | filecheck %s

;; Also verify that the "never-fold-or-reorder" flag is respected: when set, we
;; do not fold code together (important, as we keep one of the branch hints, and
;; it may be wrong, which can confuse the fuzzer), and we never reorder (which
;; can move a branch hint to execute before a trap, which can also cause the
;; fuzzer to alert).

;; RUN: wasm-opt %s --optimize-instructions -all --pass-arg=optimize-instructions-never-fold-or-reorder -S -o - \
;; RUN:   | filecheck %s --check-prefix=NO_FO

(module
 ;; CHECK:      (func $conditionals (type $1) (param $x i32) (result i32)
 ;; CHECK-NEXT:  (@metadata.code.branch_hint "\01")
 ;; CHECK-NEXT:  (if (result i32)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (i32.const 1337)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (i32.const 42)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_FO:      (func $conditionals (type $1) (param $x i32) (result i32)
 ;; NO_FO-NEXT:  (@metadata.code.branch_hint "\01")
 ;; NO_FO-NEXT:  (if (result i32)
 ;; NO_FO-NEXT:   (local.get $x)
 ;; NO_FO-NEXT:   (then
 ;; NO_FO-NEXT:    (i32.const 1337)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:   (else
 ;; NO_FO-NEXT:    (i32.const 42)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT: )
 (func $conditionals (param $x i32) (result i32)
  ;; When we flip the if, the hint should flip too.
  (@metadata.code.branch_hint "\00")
  (if (result i32)
   (i32.eqz
    (local.get $x)
   )
   (then
    (i32.const 42)
   )
   (else
    (i32.const 1337)
   )
  )
 )

 ;; CHECK:      (func $still-fold (type $0) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (@metadata.code.branch_hint "\00")
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_FO:      (func $still-fold (type $0) (param $x i32) (param $y i32)
 ;; NO_FO-NEXT:  (if
 ;; NO_FO-NEXT:   (local.get $x)
 ;; NO_FO-NEXT:   (then
 ;; NO_FO-NEXT:    (@metadata.code.branch_hint "\00")
 ;; NO_FO-NEXT:    (if
 ;; NO_FO-NEXT:     (local.get $y)
 ;; NO_FO-NEXT:     (then
 ;; NO_FO-NEXT:      (unreachable)
 ;; NO_FO-NEXT:     )
 ;; NO_FO-NEXT:    )
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:   (else
 ;; NO_FO-NEXT:    (@metadata.code.branch_hint "\01")
 ;; NO_FO-NEXT:    (if
 ;; NO_FO-NEXT:     (local.get $y)
 ;; NO_FO-NEXT:     (then
 ;; NO_FO-NEXT:      (unreachable)
 ;; NO_FO-NEXT:     )
 ;; NO_FO-NEXT:    )
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT: )
 (func $still-fold (param $x i32) (param $y i32)
  ;; We fold if arms even if metadata differs (like LLVM). We do not fold if the
  ;; flag was passed, however.
  (if
   (local.get $x)
   (then
    (@metadata.code.branch_hint "\00")
    (if
     (local.get $y)
     (then
      (unreachable)
     )
    )
   )
   (else
    (@metadata.code.branch_hint "\01")
    (if
     (local.get $y)
     (then
      (unreachable)
     )
    )
   )
  )
 )

 ;; CHECK:      (func $yes-fold (type $0) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (@metadata.code.branch_hint "\01")
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_FO:      (func $yes-fold (type $0) (param $x i32) (param $y i32)
 ;; NO_FO-NEXT:  (if
 ;; NO_FO-NEXT:   (local.get $x)
 ;; NO_FO-NEXT:   (then
 ;; NO_FO-NEXT:    (@metadata.code.branch_hint "\01")
 ;; NO_FO-NEXT:    (if
 ;; NO_FO-NEXT:     (local.get $y)
 ;; NO_FO-NEXT:     (then
 ;; NO_FO-NEXT:      (unreachable)
 ;; NO_FO-NEXT:     )
 ;; NO_FO-NEXT:    )
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:   (else
 ;; NO_FO-NEXT:    (@metadata.code.branch_hint "\01")
 ;; NO_FO-NEXT:    (if
 ;; NO_FO-NEXT:     (local.get $y)
 ;; NO_FO-NEXT:     (then
 ;; NO_FO-NEXT:      (unreachable)
 ;; NO_FO-NEXT:     )
 ;; NO_FO-NEXT:    )
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT: )
 (func $yes-fold (param $x i32) (param $y i32)
  ;; Now the hints match, so we definitely fold (without the flag).
  (if
   (local.get $x)
   (then
    (@metadata.code.branch_hint "\01")
    (if
     (local.get $y)
     (then
      (unreachable)
     )
    )
   )
   (else
    (@metadata.code.branch_hint "\01")
    (if
     (local.get $y)
     (then
      (unreachable)
     )
    )
   )
  )
 )

 ;; CHECK:      (func $always-fold-select (type $2) (param $x i32) (param $y i32) (result i32)
 ;; CHECK-NEXT:  (@metadata.code.branch_hint "\00")
 ;; CHECK-NEXT:  (if (result i32)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (i32.const 20)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_FO:      (func $always-fold-select (type $2) (param $x i32) (param $y i32) (result i32)
 ;; NO_FO-NEXT:  (@metadata.code.branch_hint "\00")
 ;; NO_FO-NEXT:  (if (result i32)
 ;; NO_FO-NEXT:   (local.get $x)
 ;; NO_FO-NEXT:   (then
 ;; NO_FO-NEXT:    (i32.const 10)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:   (else
 ;; NO_FO-NEXT:    (i32.const 20)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT: )
 (func $always-fold-select (param $x i32) (param $y i32) (result i32)
  ;; A select with different metadata is still foldable: the code was executed
  ;; anyhow, so it's fine if we execute just one of the two (we pick the first,
  ;; arbitrarily). We do so even with the flag.
  (select
   (@metadata.code.branch_hint "\00")
   (if (result i32)
    (local.get $x)
    (then
     (i32.const 10)
    )
    (else
     (i32.const 20)
    )
   )
   (@metadata.code.branch_hint "\01")
   (if (result i32)
    (local.get $x)
    (then
     (i32.const 10)
    )
    (else
     (i32.const 20)
    )
   )
   (local.get $y)
  )
 )

 ;; CHECK:      (func $ordering (type $3) (param $x i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (i32.add
 ;; CHECK-NEXT:    (local.get $x)
 ;; CHECK-NEXT:    (i32.const 42)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (i32.add
 ;; CHECK-NEXT:    (local.get $x)
 ;; CHECK-NEXT:    (i32.const 42)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NO_FO:      (func $ordering (type $3) (param $x i32)
 ;; NO_FO-NEXT:  (drop
 ;; NO_FO-NEXT:   (i32.add
 ;; NO_FO-NEXT:    (local.get $x)
 ;; NO_FO-NEXT:    (i32.const 42)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT:  (drop
 ;; NO_FO-NEXT:   (i32.add
 ;; NO_FO-NEXT:    (i32.const 42)
 ;; NO_FO-NEXT:    (local.get $x)
 ;; NO_FO-NEXT:   )
 ;; NO_FO-NEXT:  )
 ;; NO_FO-NEXT: )
 (func $ordering (param $x i32)
  ;; Normally we canonicalize the sides of a binary like this (so after the
  ;; pass, both the below expressions would be identical), but we refrain from
  ;; doing so with the flag.
  (drop
   (i32.add
    (local.get $x)
    (i32.const 42)
   )
  )
  (drop
   (i32.add
    (i32.const 42)
    (local.get $x)
   )
  )
 )
)
