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

;; TODO: Analyze and optimize stack switching instructions.

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $k (cont $f))

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

    ;; CHECK:       (type $f (func))
    (type $f (func (param i32)))
    (type $k (cont $f))

    (type $f-unused (func (param i32)))
    (type $k-unused (cont $f-unused))

  )

  ;; CHECK:      (elem declare func $f)

  ;; CHECK:      (func $f (type $f)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $f (type $f) (param i32)
    ;; This function is used in a continuation, but not in a way that it ever
    ;; receives parameters, so it can be optimized.
    (nop)
  )

  ;; CHECK:      (func $cont-new (type $1)
  ;; CHECK-NEXT:  (local $k (ref null $k))
  ;; CHECK-NEXT:  (local.set $k
  ;; CHECK-NEXT:   (block (result (ref null $k))
  ;; CHECK-NEXT:    (local.get $k)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (cont.new $k
  ;; CHECK-NEXT:    (ref.func $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cont-new
    ;; Having a local that uses a continuation type, and even moving values into
    ;; and out of that local, is not enough to inhibit optimization.
    (local $k (ref null $k))
    (local.set $k
      (block (result (ref null $k))
        (local.get $k)
      )
    )
    ;; A cont.new is not enough to inhibit optimizations, either.
    (drop
      (cont.new $k
        (ref.func $f)
      )
    )
  )
)

(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $k-sent (cont $f-sent))

  ;; CHECK:       (type $k (cont $f))

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

  ;; CHECK:       (type $f-sent (func))

  ;; CHECK:       (type $f (func (param i32)))
  (type $f (func (param i32)))
  (type $k (cont $f))

  (type $f-sent (func (param i64)))
  (type $k-sent (cont $f-sent))

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

  ;; CHECK:      (type $6 (func (param i32 (ref $k))))

  ;; CHECK:      (elem declare func $f $f-sent)

  ;; CHECK:      (tag $e (type $5))
  (tag $e)

  ;; CHECK:      (func $f (type $f) (param $0 i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f (type $f) (param i32)
    ;; This referenced function will not be optimized.
    (drop
      (ref.func $f)
    )
  )

  ;; CHECK:      (func $unreferenced (type $2)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $unreferenced (type $f) (param i32)
    ;; This unreferenced function with the same type can still be optimized.
    (nop)
  )

  ;; CHECK:      (func $f-sent (type $f-sent)
  ;; CHECK-NEXT:  (local $0 i64)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f-sent)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f-sent (type $f-sent) (param i64)
    ;; This can be optimized.
    (drop
      (ref.func $f-sent)
    )
  )

  ;; CHECK:      (func $resume (type $6) (param $x i32) (param $k (ref $k))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result (ref null $k-sent))
  ;; CHECK-NEXT:    (resume $k (on $e $l)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:     (local.get $k)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $resume (param $x i32) (param $k (ref $k))
    ;; resume inhibits optimizations for the resumed continuation type, but not
    ;; the continuation types it sends.
    (drop
      (block $l (result (ref null $k-sent))
        (resume $k (on $e $l)
          (local.get $x)
          (local.get $k)
        )
        (unreachable)
      )
    )
  )
)

(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $k-sent (cont $f-sent))

  ;; CHECK:       (type $k (cont $f))

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

  ;; CHECK:       (type $f-sent (func))

  ;; CHECK:       (type $f (func (param i32)))
  (type $f (func (param i32)))
  (type $k (cont $f))

  (type $f-sent (func (param i64)))
  (type $k-sent (cont $f-sent))

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

  ;; CHECK:      (type $6 (func (param (ref $k))))

  ;; CHECK:      (elem declare func $f $f-sent)

  ;; CHECK:      (tag $e (type $5))
  (tag $e)

  ;; CHECK:      (func $f (type $f) (param $0 i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f (type $f) (param i32)
    ;; This referenced function will not be optimized.
    (drop
      (ref.func $f)
    )
  )

  ;; CHECK:      (func $f-sent (type $f-sent)
  ;; CHECK-NEXT:  (local $0 i64)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f-sent)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f-sent (type $f-sent) (param i64)
    ;; This can be optimized.
    (drop
      (ref.func $f-sent)
    )
  )

  ;; CHECK:      (func $resume-throw (type $6) (param $k (ref $k))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result (ref null $k-sent))
  ;; CHECK-NEXT:    (resume_throw $k $e (on $e $l)
  ;; CHECK-NEXT:     (local.get $k)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $resume-throw (param $k (ref $k))
    ;; resume_throw inhibits optimizations for the resumed continuation type,
    ;; but not the continuation types it sends.
    (drop
      (block $l (result (ref null $k-sent))
        (resume_throw $k $e (on $e $l)
          (local.get $k)
        )
        (unreachable)
      )
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $k-ret (cont $f-ret))

    ;; CHECK:       (type $k (cont $f))

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

    ;; CHECK:       (type $f-ret (func (param i64 (ref null $k))))
    (type $f-ret (func (param i64 (ref null $k))))
    (type $k-ret (cont $f-ret))
    ;; CHECK:       (type $f (func (param i32 (ref null $k-ret))))
    (type $f (func (param i32 (ref null $k-ret))))
    (type $k (cont $f))
  )

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

  ;; CHECK:      (type $6 (func (param i32 (ref $k))))

  ;; CHECK:      (elem declare func $f $f-ret)

  ;; CHECK:      (tag $e (type $5))
  (tag $e)

  ;; CHECK:      (func $f (type $f) (param $0 i32) (param $1 (ref null $k-ret))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f (type $f) (param i32) (param (ref null $k-ret))
    ;; This referenced function will not be optimized.
    (drop
      (ref.func $f)
    )
  )

  ;; CHECK:      (func $f-ret (type $f-ret) (param $0 i64) (param $1 (ref null $k))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f-ret)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f-ret (type $f-ret) (param i64) (param (ref null $k))
    ;; This cannot be optimized either.
    (drop
      (ref.func $f-ret)
    )
  )

  ;; CHECK:      (func $switch (type $6) (param $x i32) (param $k (ref $k))
  ;; CHECK-NEXT:  (tuple.drop 2
  ;; CHECK-NEXT:   (switch $k $e
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (local.get $k)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block ;; (replaces unreachable StackSwitch we can't emit)
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $switch (param $x i32) (param $k (ref $k))
    ;; switch inhibits optimizations for both the target and return continuation
    ;; types.
    (tuple.drop 2
      (switch $k $e
        (local.get $x)
        (local.get $k)
      )
    )
    ;; Do not get confused when the continuation is unreachable.
    (switch $k $e
      (local.get $x)
      (unreachable)
    )
  )
)

(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $k2 (cont $f2))

  ;; CHECK:       (type $k1 (cont $f1))

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

  ;; CHECK:       (type $f2 (func (param i64)))

  ;; CHECK:       (type $f1 (func (param i32 i64)))
  (type $f1 (func (param i32 i64)))
  (type $k1 (cont $f1))
  (type $f2 (func (param i64)))
  (type $k2 (cont $f2))
  ;; CHECK:       (type $5 (func))

  ;; CHECK:      (type $6 (func (param i32 (ref $k1))))

  ;; CHECK:      (elem declare func $f1 $f2)

  ;; CHECK:      (tag $e (type $5))
  (tag $e)

  ;; CHECK:      (func $f1 (type $f1) (param $0 i32) (param $1 i64)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f1 (type $f1) (param i32) (param i64)
    ;; This referenced function will not be optimized.
    (drop
      (ref.func $f1)
    )
  )

  ;; CHECK:      (func $f2 (type $f2) (param $0 i64)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.func $f2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $f2 (type $f2) (param i64)
    ;; This will not be optimized either.
    (drop
      (ref.func $f2)
    )
  )

  ;; CHECK:      (func $cont-bind (type $6) (param $x i32) (param $k1 (ref $k1))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (cont.bind $k1 $k2
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (local.get $k1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cont-bind (param $x i32) (param $k1 (ref $k1))
    ;; cont.bind inhibits optimizations for both the input and output types.
    (drop
      (cont.bind $k1 $k2
        (local.get $x)
        (local.get $k1)
      )
    )
  )
)
