;; 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 --local-cse -all -S -o - | filecheck %s

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

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

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

  ;; CHECK:      (global $mutable (mut i32) (i32.const 42))
  (global $mutable (mut i32) (i32.const 42))

  ;; CHECK:      (global $immutable i32 (i32.const 1337))
  (global $immutable i32 (i32.const 1337))

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

  ;; CHECK:      (func $potent (type $1) (param $0 i32) (result i32)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: )
  (func $potent (param i32) (result i32)
    ;; As above, but not marked as idempotent.
    (call $import)
    (i32.const 1337)
  )

  ;; CHECK:      (func $yes (type $0)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $0
  ;; CHECK-NEXT:    (call $idempotent
  ;; CHECK-NEXT:     (i32.const 10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $yes
    ;; We can optimize here.
    (drop
      (call $idempotent
        (i32.const 10)
      )
    )
    (drop
      (call $idempotent
        (i32.const 10)
      )
    )
  )

  ;; CHECK:      (func $no (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $potent
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $potent
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no
    ;; Without idempotency we cannot optimize.
    (drop
      (call $potent
        (i32.const 10)
      )
    )
    (drop
      (call $potent
        (i32.const 10)
      )
    )
  )

  ;; CHECK:      (func $different-input (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $different-input
    ;; We cannot optimize here.
    (drop
      (call $idempotent
        (i32.const 10)
      )
    )
    (drop
      (call $idempotent
        (i32.const 20)
      )
    )
  )

  ;; CHECK:      (func $idem-effects (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $idem-effects
    ;; An idempotent function still has effects on the first call. Those effects
    ;; can invalidate the global.get here.
    (drop
      (call $idempotent
        (global.get $mutable)
      )
    )
    (drop
      (call $idempotent
        (global.get $mutable)
      )
    )
  )

  ;; CHECK:      (func $idem-effects-immutable (type $0)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $0
  ;; CHECK-NEXT:    (call $idempotent
  ;; CHECK-NEXT:     (global.get $immutable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $idem-effects-immutable
    ;; But here we read an immutable value, so we can optimize.
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
  )

  ;; CHECK:      (func $idem-effects-interact (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.add
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $immutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.add
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $immutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $idem-effects-interact
    ;; An idempotent function still has effects on the first call. That
    ;; prevents optimizing the i32.add, as the first call might alter $mutable.
    (drop
      (i32.add
        (global.get $mutable)
        (global.get $mutable)
      )
    )
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
    (drop
      (i32.add
        (global.get $mutable)
        (global.get $mutable)
      )
    )
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
  )

  ;; CHECK:      (func $idem-effects-interact-2 (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $immutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.add
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $idempotent
  ;; CHECK-NEXT:    (global.get $immutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.add
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:    (global.get $mutable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $idem-effects-interact-2
    ;; As above, but interleaved differently. The first i32.add is now between
    ;; the two idempotent calls. Here we could optimize, but do not atm, as the
    ;; mutable reads invalidate the first call (only the reverse is true, but
    ;; our check is symmetrical atm), and the second call - which we fail to
    ;; optimize out - invalidates the later mutable reads. TODO
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
    (drop
      (i32.add
        (global.get $mutable)
        (global.get $mutable)
      )
    )
    (drop
      (call $idempotent
        (global.get $immutable)
      )
    )
    (drop
      (i32.add
        (global.get $mutable)
        (global.get $mutable)
      )
    )
  )
)
