;; 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 --duplicate-function-elimination --all-features -S -o - | filecheck %s

;; We should not merge functions differing in semantics-altering annotations
;; like js.called and removable.if.unused.

;; One function has the js.called hint, so we do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (@binaryen.js.called)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.js.called)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Flipped, now other has it. We still do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (@binaryen.js.called)
 ;; CHECK-NEXT: (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.js.called)
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Both have it, so we do merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $a))

 ;; CHECK:      (@binaryen.js.called)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.js.called)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 (@binaryen.js.called)
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Just one function has removable.if.unused, so we do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (@binaryen.removable.if.unused)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.removable.if.unused)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Mix-and-match of hints, each with a different one. We do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (@binaryen.removable.if.unused)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.removable.if.unused)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (@binaryen.js.called)
 ;; CHECK-NEXT: (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.js.called)
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Identical on one hint, different on another, so we do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (@binaryen.removable.if.unused)
 ;; CHECK-NEXT: (@binaryen.js.called)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.removable.if.unused)
 (@binaryen.js.called)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (@binaryen.js.called)
 ;; CHECK-NEXT: (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.js.called)
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; Both hints are present, so we do merge. Note the order differs and does not
;; confuse us.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $a))

 ;; CHECK:      (@binaryen.removable.if.unused)
 ;; CHECK-NEXT: (@binaryen.js.called)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.removable.if.unused)
 (@binaryen.js.called)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 (@binaryen.js.called)
 (@binaryen.removable.if.unused)
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

;; One function has the idempotent hint, so we do not merge.
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (export "a" (func $a))

 ;; CHECK:      (export "b" (func $b))

 ;; CHECK:      (@binaryen.idempotent)
 ;; CHECK-NEXT: (func $a (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (@binaryen.idempotent)
 (func $a (export "a") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )

 ;; CHECK:      (func $b (type $0) (param $x i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $b (export "b") (param $x i32)
  (if
   (local.get $x)
   (then
    (unreachable)
   )
  )
 )
)

