;; 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:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    ;; A minimal "once" function. It is so trivial we can remove its body.
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    ;; Call a once function more than once, in a way that we can optimize: the
    ;; first dominates the second. The second call will become a nop.
    (call $once)
    (call $once)
  )
)

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

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 100)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    ;; Add some more content in the function.
    (drop (i32.const 100))
  )

  ;; CHECK:      (func $caller-if-1 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller-if-1
    ;; Add more calls, and ones that are conditional.
    (if
      (i32.const 1)
      (then
        (block
          (call $once)
          (call $once)
          (call $once)
          (call $once)
        )
      )
    )
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller-if-2 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller-if-2
    ;; Call in both arms. As we only handle dominance, and not merges, the first
    ;; call after the if is *not* optimized.
    (if
      (i32.const 1)
      (then
        (call $once)
      )
      (else
        (block
          (call $once)
          (call $once)
        )
      )
    )
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller-loop-1 (type $0)
  ;; CHECK-NEXT:  (loop $loop
  ;; CHECK-NEXT:   (if
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (call $once)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (call $once)
  ;; CHECK-NEXT:   (nop)
  ;; CHECK-NEXT:   (br_if $loop
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller-loop-1
    ;; Add calls in a loop.
    (loop $loop
      (if
        (i32.const 1)
        (then
          (call $once)
        )
      )
      (call $once)
      (call $once)
      (br_if $loop (i32.const 1))
    )
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller-loop-2 (type $0)
  ;; CHECK-NEXT:  (loop $loop
  ;; CHECK-NEXT:   (if
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (call $once)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (br_if $loop
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller-loop-2
    ;; Add a single conditional call in a loop.
    (loop $loop
      (if
        (i32.const 1)
        (then
          (call $once)
        )
      )
      (br_if $loop (i32.const 1))
    )
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller-single (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller-single
    ;; A short function with a single call.
    (call $once)
  )

  ;; CHECK:      (func $caller-empty (type $0)
  ;; CHECK-NEXT: )
  (func $caller-empty
    ;; A tiny function with nothing at all, just to verify we do not crash on
    ;; such things.
  )
)

;; Corner case: Initial value is not zero. We can still optimize this here,
;; though in fact the function will never execute the payload call of foo(),
;; which in theory we could further optimize.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "foo" (func $foo (type $0)))
  (import "env" "foo" (func $foo))

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

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $foo)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $foo)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: function is not quite once, there is code before the if, so no
;; optimization will happen.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "foo" (func $foo (type $0)))
  (import "env" "foo" (func $foo))

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

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $foo)
  ;; CHECK-NEXT: )
  (func $once
    (nop)
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $foo)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: a nop after the if.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "foo" (func $foo (type $0)))
  (import "env" "foo" (func $foo))

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

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $foo)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (nop)
    (global.set $once (i32.const 1))
    (call $foo)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: The if has an else.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "foo" (func $foo (type $0)))
  (import "env" "foo" (func $foo))

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

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (call $foo)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $foo)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
      (else
        (call $foo)
      )
    )
    (global.set $once (i32.const 1))
    (call $foo)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: different global names in the get and set
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once1 (mut i32) (i32.const 0))
  (global $once1 (mut i32) (i32.const 0))
  ;; CHECK:      (global $once2 (mut i32) (i32.const 0))
  (global $once2 (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once2
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once1)
      (then
        (return)
      )
    )
    (global.set $once2 (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: The global is written a zero.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 0))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: The global is written a zero elsewhere.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
    (global.set $once (i32.const 0))
  )
)

;; Corner case: The global is written a non-zero value elsewhere. This is ok to
;; optimize, and in fact we can write a value different than 1 both there and
;; in the "once" function, and we can still optimize.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 42))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
    (global.set $once (i32.const 1337))
  )

  ;; CHECK:      (func $caller-2 (type $0)
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1337)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller-2
    ;; Reverse order of the above.
    (global.set $once (i32.const 1337))
    (call $once)
    (call $once)
  )
)

;; It is ok to call the "once" function inside itself - as that call appears
;; behind a set of the global, the call is redundant and we optimize it away.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $once)
  )
)

;; Corner case: Non-integer global, which we ignore.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut f64) (f64.const 0))
  (global $once (mut f64) (f64.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.trunc_f64_s
  ;; CHECK-NEXT:    (global.get $once)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (f64.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      ;; We must cast this to an integer for the wasm to validate.
      (i32.trunc_f64_s
        (global.get $once)
      )
      (then
        (return)
      )
    )
    (global.set $once (f64.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Non-constant initial value. This is fine, as the initial value
;; does not matter (if it is zero, then this is a "classic" "once" global; if
;; not then it will never be written to, and the "once" function will never run
;; at all, which is fine too)
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "glob" (global $import i32))
  (import "env" "glob" (global $import i32))

  ;; CHECK:      (global $once (mut i32) (global.get $import))
  (global $once (mut i32) (global.get $import))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Non-constant later value.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.eqz
  ;; CHECK-NEXT:    (i32.eqz
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.eqz (i32.eqz (i32.const 1))))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: "Once" function has a param.
(module
  ;; CHECK:      (type $0 (func (param i32)))

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

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0) (param $x i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once (param $x i32)
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $1)
  ;; CHECK-NEXT:  (call $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (call $once (i32.const 1))
    (call $once (i32.const 1))
  )
)

;; Corner case: "Once" function has a result.
(module
  ;; CHECK:      (type $0 (func (result i32)))

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

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0) (result i32)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (i32.const 2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 3)
  ;; CHECK-NEXT: )
  (func $once (result i32)
    (if
      (global.get $once)
      (then
        (return (i32.const 2))
      )
    )
    (global.set $once (i32.const 1))
    (i32.const 3)
  )

  ;; CHECK:      (func $caller (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $once)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $once)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (drop (call $once))
    (drop (call $once))
  )
)

;; Corner case: "Once" function body is not a block.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (loop $loop
  ;; CHECK-NEXT:   (if
  ;; CHECK-NEXT:    (global.get $once)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (return)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (global.set $once
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (loop $loop
      (if
        (global.get $once)
        (then
          (return)
        )
      )
      (global.set $once (i32.const 1))
    )
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Once body is too short.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Additional reads of the global.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
    (drop (global.get $once))
  )
)

;; Corner case: Additional reads of the global in the "once" func.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (drop (global.get $once))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Optimization opportunties in unreachable code (which we can
;; ignore, but should not error on).
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
    (unreachable)
    (call $once)
    (call $once)
  )
)

;; Add a very long chain of control flow.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (call $once)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (nop)
      )
      (else
        (nop)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (nop)
      )
      (else
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (nop)
      )
      (else
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (call $once)
    (if
      (i32.const 1)
      (then
        (call $once)
      )
    )
    (call $once)
    (call $once)
  )
)

;; A test with a try-catch. This verifies that we emit their contents properly
;; in reverse postorder and do not hit any assertions.
(module
  ;; CHECK:      (type $0 (func))

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

  ;; CHECK:      (global $once (mut i32) (i32.const 0))

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

  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $try-catch (type $0)
  ;; CHECK-NEXT:  (try $label$5
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (if
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (call $once)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (catch $tag
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (pop i32)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $try-catch
    (try $label$5
      (do
        (if
          (i32.const 1)
          (then
            (call $once)
          )
        )
      )
      (catch $tag
        (drop
          (pop i32)
        )
      )
    )
  )
)

(module
  ;; Test a module with more than one global that we can optimize, and more than
  ;; one that we cannot.

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

  ;; CHECK:      (global $once1 (mut i32) (i32.const 0))
  (global $once1 (mut i32) (i32.const 0))
  ;; CHECK:      (global $many1 (mut i32) (i32.const 0))
  (global $many1 (mut i32) (i32.const 0))
  ;; CHECK:      (global $once2 (mut i32) (i32.const 0))
  (global $once2 (mut i32) (i32.const 0))
  ;; CHECK:      (global $many2 (mut i32) (i32.const 0))
  (global $many2 (mut i32) (i32.const 0))

  ;; CHECK:      (func $once1 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once1
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (call $once2)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT: )
  (func $once1
    (if
      (global.get $once1)
      (then
        (return)
      )
    )
    (global.set $once1 (i32.const 1))
    (call $once1)
    (call $many1)
    (call $once2)
    (call $many2)
    (call $once1)
    (call $many1)
    (call $once2)
    (call $many2)
  )

  ;; CHECK:      (func $many1 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $many1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $many1
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (call $once1)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (call $once2)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $many1
    (if
      (global.get $many1)
      (then
        (return)
      )
    )
    (global.set $many1 (i32.const 0)) ;; prevent this global being "once"
    (call $many2)
    (call $once1)
    (call $many1)
    (call $once2)
    (call $many2)
    (call $once1)
    (call $many1)
    (call $once2)
  )

  ;; CHECK:      (func $once2 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once2)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once2
  ;; CHECK-NEXT:   (i32.const 2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (call $once1)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT: )
  (func $once2
    (if
      (global.get $once2)
      (then
        (return)
      )
    )
    (global.set $once2 (i32.const 2))
    (call $once2)
    (call $many2)
    (call $once1)
    (call $many1)
    (call $once2)
    (call $many2)
    (call $once1)
    (call $many1)
  )

  ;; CHECK:      (func $many2 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $many2)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $many1
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (call $once2)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (call $once1)
  ;; CHECK-NEXT:  (call $many1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $many2)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $many2
    (if
      (global.get $many2)
      (then
        (return)
      )
    )
    (global.set $many1 (i32.const 0))
    (call $many1)
    (call $once2)
    (call $many2)
    (call $once1)
    (call $many1)
    (call $once2)
    (call $many2)
    (call $once1)
  )
)

;; Test for propagation of information about called functions: if A->B->C->D
;; and D calls some "once" functions, then A can infer that it's call to B does
;; so.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $A (type $0)
  ;; CHECK-NEXT:  (call $B)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $A
    ;; We can infer that calling B calls C and then D, and D calls this "once"
    ;; function, so we can remove the call after it
    (call $B)
    (call $once)
  )

  ;; CHECK:      (func $B (type $0)
  ;; CHECK-NEXT:  (call $C)
  ;; CHECK-NEXT: )
  (func $B
    (call $C)
  )

  ;; CHECK:      (func $C (type $0)
  ;; CHECK-NEXT:  (call $D)
  ;; CHECK-NEXT: )
  (func $C
    (call $D)
  )

  ;; CHECK:      (func $D (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $D
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $bad-A (type $0)
  ;; CHECK-NEXT:  (call $bad-B)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $bad-A
    ;; Call a function that does *not* do anything useful. We should not remove
    ;; the second call here.
    (call $bad-B)
    (call $once)
  )

  ;; CHECK:      (func $bad-B (type $0)
  ;; CHECK-NEXT: )
  (func $bad-B
  )
)

;; Corner case: Imported mutable global. We cannot optimize it, since the
;; outside may read and write it.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "glob" (global $once (mut i32)))
  (import "env" "glob" (global $once (mut i32)))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Corner case: Exported mutable global. We cannot optimize it, since the
;; outside may read and write it.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (export "once-global" (global $once))
  (export "once-global" (global $once))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    (call $once)
    (call $once)
  )
)

;; Test calls of other "once" functions in "once" functions.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (global $once.1 (mut i32) (i32.const 0))
  (global $once.1 (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT: )
  (func $once
    ;; A minimal "once" function, which calls another. We can remove the first
    ;; two lines here (the early-exit logic).
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $once.1)
  )

  ;; CHECK:      (func $once.1 (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once.1
    ;; Another minimal "once" function. It has no payload and we can empty it
    ;; out.
    (if
      (global.get $once.1)
      (then
        (return)
      )
    )
    (global.set $once.1 (i32.const 1))
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    ;; Call a once function more than once. The second call will become a nop.
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller$1 (type $0)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller$1
    ;; Two calls to the second function. Again, the second becomes a nop.
    (call $once.1)
    (call $once.1)
  )

  ;; CHECK:      (func $caller$2 (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT: )
  (func $caller$2
    ;; A mix of calls. We can remove the second in principle, because we know
    ;; the first will call it, but we leave that for later optimizations
    ;; ($once turns into a call to $once.1 that inlining will remove, and then
    ;; we'll have two identical calls here).
    (call $once)
    (call $once.1)
  )

  ;; CHECK:      (func $caller$3 (type $0)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller$3
    ;; Reverse of the above; again we cannot optimize (as $once.1 does not call
    ;; $once).
    (call $once.1)
    (call $once)
  )

  ;; CHECK:      (func $caller$4 (type $0)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (call $caller$4)
  ;; CHECK-NEXT: )
  (func $caller$4
    ;; Here we cannot optimize, since the second function is not a "once"
    ;; function.
    (call $once.1)
    (call $caller$4)
  )
)

;; Test loops between "once" functions.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (global $once.1 (mut i32) (i32.const 0))
  (global $once.1 (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT: )
  (func $once
    ;; This "once" function calls another, and so we can remove the early-exit
    ;; logic.
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $once.1)
  )

  ;; CHECK:      (func $once.1 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once.1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once.1
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $once.1
    ;; This early-exit logic looks removable, since we call another "once"
    ;; function. However, we remove that function's early-exit logic, so we
    ;; cannot do so here (it would risk an infinite loop).
    (if
      (global.get $once.1)
      (then
        (return)
      )
    )
    (global.set $once.1 (i32.const 1))
    (call $once) ;; This call was added.
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    ;; The second call will become a nop.
    (call $once)
    (call $once)
  )

  ;; CHECK:      (func $caller$1 (type $0)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller$1
    ;; Again, the second becomes a nop.
    (call $once.1)
    (call $once.1)
  )

  ;; CHECK:      (func $caller$2 (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT: )
  (func $caller$2
    ;; The second could become a nop, but we leave that for later optimizations
    ;; (after inlining the call to $once becomes a call to $once.1).
    (call $once)
    (call $once.1)
  )

  ;; CHECK:      (func $caller$3 (type $0)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller$3
    ;; Reverse order, same result (no optimization).
    (call $once.1)
    (call $once)
  )
)

;; Test a dangerous triple loop.
(module
  ;; CHECK:      (type $0 (func))

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

  ;; CHECK:      (import "env" "foo" (func $import (type $1) (param i32)))
  (import "env" "foo" (func $import (param i32)))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (global $once.1 (mut i32) (i32.const 0))
  (global $once.1 (mut i32) (i32.const 0))

  ;; CHECK:      (global $once.2 (mut i32) (i32.const 0))
  (global $once.2 (mut i32) (i32.const 0))


  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (call $once.2)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    (call $once.1)
    ;; We cannot remove this second call. While $once.1 calls $once.2, we may
    ;; be in this situation: a call started at $once.1, which calls $once
    ;; (here) which then calls $once.1 which immediately exits (as the global
    ;; has been set for it), and then we call $once.2 from here, which calls
    ;; the other two that immediately exit as well, and then we call the import
    ;; from there with value 2. Then we call it from here with value 0, and
    ;; then we return to the caller, $once.1, which calls with 1, so we have
    ;; 2, 0, 1. If we remove the call here to $once.2 then the order would be
    ;; 0, 2, 1.
    ;;
    ;; The problem is that the setting of the global happens at the very start
    ;; of the once function, but that does not mean we have executed the body
    ;; yet, and without executing it, we cannot infer anything about other
    ;; globals.
    (call $once.2)
    (call $import
      (i32.const 0)
    )
  )

  ;; CHECK:      (func $once.1 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once.1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once.1
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once.2)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once.1
    (if
      (global.get $once.1)
      (then
        (return)
      )
    )
    (global.set $once.1 (i32.const 1))
    (call $once)
    ;; As above, by symmetry, we cannot remove this second call.
    (call $once.2)
    (call $import
      (i32.const 1)
    )
  )

  ;; CHECK:      (func $once.2 (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once.2)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once.2
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $once.1)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (i32.const 2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $once.2
    (if
      (global.get $once.2)
      (then
        (return)
      )
    )
    (global.set $once.2 (i32.const 1))
    (call $once)
    ;; As above, by symmetry, we cannot remove this second call.
    (call $once.1)
    (call $import
      (i32.const 2)
    )
  )
)

;; Test a self-loop.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))


  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    ;; A recursive call. This of course does not recurse infinitely since the
    ;; next call early exits, as the global is set, and for that reason we can
    ;; optimize this to a nop.
    (call $once)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    ;; The second call will become a nop.
    (call $once)
    (call $once)
  )
)

;; Calls from non-"once" functions to "once" functions.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $once
    ;; A minimal "once" function.
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
  )

  ;; CHECK:      (func $do-once (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $do-once
    ;; Call the once function.
    (call $once)
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $do-once)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $caller
    ;; The first proves the second is not needed, and can be nopped.
    (call $do-once)
    (call $once)
  )

  ;; CHECK:      (func $caller2 (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $do-once)
  ;; CHECK-NEXT: )
  (func $caller2
    ;; Reverse order of the above. We cannot optimize here: $do-once does
    ;; nothing aside from call $once, but all we know is that it is not a "once"
    ;; function itself, and we only remove calls to "once" functions.
    (call $once)
    (call $do-once)
  )
)

;; Calls from "once" functions to non-"once" functions.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $once (mut i32) (i32.const 0))
  (global $once (mut i32) (i32.const 0))

  ;; CHECK:      (func $once (type $0)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (global.get $once)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $once
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $other)
  ;; CHECK-NEXT: )
  (func $once
    ;; We should not remove this early-exit logic.
    (if
      (global.get $once)
      (then
        (return)
      )
    )
    (global.set $once (i32.const 1))
    ;; A call to a non-"once" function.
    (call $other)
  )

  ;; CHECK:      (func $other (type $0)
  ;; CHECK-NEXT: )
  (func $other
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $other)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT: )
  (func $caller
    ;; There is nothing to optimize here.
    (call $other)
    (call $once)
  )

  ;; CHECK:      (func $caller2 (type $0)
  ;; CHECK-NEXT:  (call $once)
  ;; CHECK-NEXT:  (call $other)
  ;; CHECK-NEXT: )
  (func $caller2
    ;; Reverse order of the above. Also nothing to do.
    (call $once)
    (call $other)
  )
)
