;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --once-reduction -all -S -o - | filecheck %s

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

  ;; CHECK:      (import "a" "b" (func $import (type $0)))
  (import "a" "b" (func $import))

  ;; CHECK:      (@binaryen.idempotent)
  ;; CHECK-NEXT: (func $idempotent (type $0)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT: )
  (@binaryen.idempotent)
  (func $idempotent
    ;; This function has side effects, but is marked idempotent.
    (call $import)
  )

  ;; CHECK:      (func $potent (type $0)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT: )
  (func $potent
    ;; As above, but not marked as idempotent.
    (call $import)
  )

  ;; CHECK:      (func $basics (type $0)
  ;; CHECK-NEXT:  (call $idempotent)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $potent)
  ;; CHECK-NEXT:  (call $potent)
  ;; CHECK-NEXT: )
  (func $basics
    ;; We can optimize here, using idempotency. The second call goes away.
    (call $idempotent)
    (call $idempotent)

    ;; We can't optimize here.
    (call $potent)
    (call $potent)
  )

  ;; CHECK:      (@binaryen.idempotent)
  ;; CHECK-NEXT: (func $idempotent2 (type $0)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT: )
  (@binaryen.idempotent)
  (func $idempotent2
    ;; A second idempotent function.
    (call $import)
  )

  ;; CHECK:      (func $mix (type $0)
  ;; CHECK-NEXT:  (call $idempotent)
  ;; CHECK-NEXT:  (call $idempotent2)
  ;; CHECK-NEXT: )
  (func $mix
    ;; We can't optimize here: these are idempotent, but different.
    (call $idempotent)
    (call $idempotent2)
  )

  ;; CHECK:      (func $mix2 (type $0)
  ;; CHECK-NEXT:  (call $idempotent2)
  ;; CHECK-NEXT:  (call $idempotent)
  ;; CHECK-NEXT: )
  (func $mix2
    ;; As above, reverse order. We can't optimize here.
    (call $idempotent2)
    (call $idempotent)
  )

  ;; CHECK:      (func $mix3 (type $0)
  ;; CHECK-NEXT:  (call $idempotent)
  ;; CHECK-NEXT:  (call $idempotent2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $mix3
    (call $idempotent)
    (call $idempotent2)

    ;; We can remove both of these second calls.
    (call $idempotent)
    (call $idempotent2)

    ;; These as well, which are reordered.
    (call $idempotent2)
    (call $idempotent)
  )
)

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

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

  ;; CHECK:      (import "a" "b" (func $import (type $0)))
  (import "a" "b" (func $import))

  ;; CHECK:      (@binaryen.idempotent)
  ;; CHECK-NEXT: (func $idempotent (type $1) (param $x i32)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT: )
  (@binaryen.idempotent)
  (func $idempotent (param $x i32)
    ;; As above, but now with a param.
    (call $import)
  )

  ;; CHECK:      (func $basics (type $0)
  ;; CHECK-NEXT:  (call $idempotent
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $idempotent
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $basics
    ;; We could optimize here in theory, but the pass does not handle params
    ;; yet. It could compare them for equality, check generativity, etc.
    (call $idempotent (i32.const 10))
    (call $idempotent (i32.const 10))
  )
)

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

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

  ;; CHECK:      (import "a" "b" (func $import (type $0)))
  (import "a" "b" (func $import))

  ;; CHECK:      (@binaryen.idempotent)
  ;; CHECK-NEXT: (func $idempotent (type $1) (result i32)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (@binaryen.idempotent)
  (func $idempotent (result i32)
    ;; As above, but now with a result.
    (call $import)
    (i32.const 42)
  )

  ;; CHECK:      (func $basics (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $basics
    ;; We could optimize here in theory, but the pass does not handle results
    ;; yet.
    (drop
      (call $idempotent)
    )
    (drop
      (call $idempotent)
    )
  )
)
