;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; Test that we do not error on trying to serialize continuations (which cannot
;; be serialized). When we do not pass --kept-exports, we can at least remove
;; that "test" export, since we don't need to serialize anything when it doesn't
;; stick around.

;; RUN: foreach %s %t wasm-ctor-eval --ctors=test --kept-exports=test -all --ignore-external-input -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-ctor-eval --ctors=test                     -all --ignore-external-input -S -o - | filecheck %s --check-prefix=NOKEEP

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


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

 ;; CHECK:      (type $3 (func (result (ref $cont))))

 ;; CHECK:      (global $global (mut i32) (i32.const 1))
 ;; NOKEEP:      (type $0 (func (result i32)))

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

 (export "test" (func $test))

 ;; CHECK:      (elem declare func $func)

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

 ;; CHECK:      (export "test" (func $test_3))

 ;; CHECK:      (func $func (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 (func $func
 )

 (func $test (result (ref $cont))
  ;; An effect before the cont.new. We can precompute it and remove it from
  ;; here, applying the 1 to the global.
  (global.set $global
   (i32.const 1)
  )
  (cont.new $cont
   (ref.func $func)
  )
 )

 ;; CHECK:      (func $read (type $2) (result i32)
 ;; CHECK-NEXT:  (global.get $global)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (export "read" (func $read))

 ;; NOKEEP:      (func $read (type $0) (result i32)
 ;; NOKEEP-NEXT:  (global.get $global)
 ;; NOKEEP-NEXT: )
 (func $read (export "read") (result i32)
  (global.get $global)
 )
)

;; CHECK:      (func $test_3 (type $3) (result (ref $cont))
;; CHECK-NEXT:  (cont.new $cont
;; CHECK-NEXT:   (ref.func $func)
;; CHECK-NEXT:  )
;; CHECK-NEXT: )
(module
 ;; As above, but there is another use of $test.

 ;; CHECK:      (type $func (func))
 ;; NOKEEP:      (type $func (func))
 (type $func (func))
 ;; CHECK:      (type $cont (cont $func))
 ;; NOKEEP:      (type $cont (cont $func))
 (type $cont (cont $func))

 ;; CHECK:      (type $2 (func (result (ref $cont))))

 ;; CHECK:      (elem declare func $func)

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

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

 ;; CHECK:      (func $func (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (type $2 (func (result (ref $cont))))

 ;; NOKEEP:      (elem declare func $func)

 ;; NOKEEP:      (export "export" (func $export))

 ;; NOKEEP:      (func $func (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $func
 )

 ;; CHECK:      (func $test (type $2) (result (ref $cont))
 ;; CHECK-NEXT:  (cont.new $cont
 ;; CHECK-NEXT:   (ref.func $func)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $test (type $2) (result (ref $cont))
 ;; NOKEEP-NEXT:  (cont.new $cont
 ;; NOKEEP-NEXT:   (ref.func $func)
 ;; NOKEEP-NEXT:  )
 ;; NOKEEP-NEXT: )
 (func $test (result (ref $cont))
  (cont.new $cont
   (ref.func $func)
  )
 )

 ;; CHECK:      (func $export (type $func)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $test)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $export (type $func)
 ;; NOKEEP-NEXT:  (drop
 ;; NOKEEP-NEXT:   (call $test)
 ;; NOKEEP-NEXT:  )
 ;; NOKEEP-NEXT: )
 (func $export (export "export")
  ;; Call $test internally. This keeps $test alive in NOKEEP as well (but not
  ;; the export "test").
  (drop
   (call $test)
  )
 )
)

;; As the original testcase, but the serialization problem happens in a local.
(module
 ;; CHECK:      (type $func (func))
 ;; NOKEEP:      (type $func (func))
 (type $func (func))
 ;; CHECK:      (type $cont (cont $func))
 ;; NOKEEP:      (type $cont (cont $func))
 (type $cont (cont $func))

 ;; CHECK:      (elem declare func $func)

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

 ;; CHECK:      (export "test" (func $test))
 ;; NOKEEP:      (elem declare func $func)

 ;; NOKEEP:      (export "export" (func $export))

 ;; NOKEEP:      (export "test" (func $test))
 (export "test" (func $test))

 ;; CHECK:      (func $func (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $func (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $func
 )

 ;; CHECK:      (func $test (type $func)
 ;; CHECK-NEXT:  (local $cont (ref $cont))
 ;; CHECK-NEXT:  (local.set $cont
 ;; CHECK-NEXT:   (cont.new $cont
 ;; CHECK-NEXT:    (ref.func $func)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $test (type $func)
 ;; NOKEEP-NEXT:  (local $cont (ref $cont))
 ;; NOKEEP-NEXT:  (local.set $cont
 ;; NOKEEP-NEXT:   (cont.new $cont
 ;; NOKEEP-NEXT:    (ref.func $func)
 ;; NOKEEP-NEXT:   )
 ;; NOKEEP-NEXT:  )
 ;; NOKEEP-NEXT:  (unreachable)
 ;; NOKEEP-NEXT: )
 (func $test
  (local $cont (ref $cont))
  (local.set $cont
   (cont.new $cont
    (ref.func $func)
   )
  )
  ;; An effect, so the function cannot be fully precomputed - we precompute up
  ;; to here, and try to stash in locals, but fail.
  (unreachable)
 )

 ;; CHECK:      (func $export (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $export (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $export (export "export")
  ;; Keep the testcase from being trivial in the NOKEEP case.
 )
)

;; As the original testcase, but the serialization problem happens in a
;; return_call param.
(module
 ;; CHECK:      (type $func (func))
 ;; NOKEEP:      (type $func (func))
 (type $func (func))
 ;; CHECK:      (type $1 (func (param contref)))

 ;; CHECK:      (type $cont (cont $func))
 ;; NOKEEP:      (type $1 (func (param contref)))

 ;; NOKEEP:      (type $cont (cont $func))
 (type $cont (cont $func))

 ;; CHECK:      (elem declare func $func)

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

 ;; CHECK:      (export "test" (func $test))
 ;; NOKEEP:      (elem declare func $func)

 ;; NOKEEP:      (export "export" (func $export))

 ;; NOKEEP:      (export "test" (func $test))
 (export "test" (func $test))

 ;; CHECK:      (func $func (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $func (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $func
 )

 ;; CHECK:      (func $test (type $1) (param $cont contref)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (ref.is_null
 ;; CHECK-NEXT:    (local.get $cont)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (return_call $test
 ;; CHECK-NEXT:     (cont.new $cont
 ;; CHECK-NEXT:      (ref.func $func)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $test (type $1) (param $cont contref)
 ;; NOKEEP-NEXT:  (if
 ;; NOKEEP-NEXT:   (ref.is_null
 ;; NOKEEP-NEXT:    (local.get $cont)
 ;; NOKEEP-NEXT:   )
 ;; NOKEEP-NEXT:   (then
 ;; NOKEEP-NEXT:    (return_call $test
 ;; NOKEEP-NEXT:     (cont.new $cont
 ;; NOKEEP-NEXT:      (ref.func $func)
 ;; NOKEEP-NEXT:     )
 ;; NOKEEP-NEXT:    )
 ;; NOKEEP-NEXT:   )
 ;; NOKEEP-NEXT:  )
 ;; NOKEEP-NEXT:  (unreachable)
 ;; NOKEEP-NEXT: )
 (func $test (param $cont (ref null cont))
  (if
   (ref.is_null
    (local.get $cont)
   )
   (then
    (return_call $test
     (cont.new $cont
      (ref.func $func)
     )
    )
   )
  )
  (unreachable)
 )

 ;; CHECK:      (func $export (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (func $export (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $export (export "export")
 )
)

;; As the original testcase, but the problem happens in a nested GC value.
(module
 ;; CHECK:      (type $func (func))
 ;; NOKEEP:      (type $func (func))
 (type $func (func))
 ;; CHECK:      (type $cont (cont $func))
 (type $cont (cont $func))

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

 ;; CHECK:      (type $struct (struct (field $cont (ref $cont))))
 (type $struct (struct (field $cont (ref $cont))))

 ;; CHECK:      (elem declare func $func)

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

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

 ;; CHECK:      (func $func (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 (func $func
 )

 ;; CHECK:      (func $test (type $2) (result anyref)
 ;; CHECK-NEXT:  (struct.new $struct
 ;; CHECK-NEXT:   (cont.new $cont
 ;; CHECK-NEXT:    (ref.func $func)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $test (result anyref)
  (struct.new $struct
   (cont.new $cont
    (ref.func $func)
   )
  )
 )

 ;; CHECK:      (func $export (type $func)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 ;; NOKEEP:      (export "export" (func $export))

 ;; NOKEEP:      (func $export (type $func)
 ;; NOKEEP-NEXT:  (nop)
 ;; NOKEEP-NEXT: )
 (func $export (export "export")
 )
)
