;; 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 --remove-unused-module-elements --all-features -S -o - | filecheck %s

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

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

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

  ;; CHECK:      (memory $0 0)

  ;; CHECK:      (table $0 1 1 funcref)

  ;; CHECK:      (elem $0 (i32.const 0) $called_indirect)

  ;; CHECK:      (export "memory" (memory $0))

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

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

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

  ;; CHECK:      (start $start)
  (start $start)
  (type $0 (func))
  (type $0-dupe (func))
  (type $1 (func (param i32)))
  (type $1-dupe (func (param i32)))
  (type $2 (func (param i32) (result i32)))
  (type $2-dupe (func (param i32) (result i32)))
  (type $2-thrupe (func (param i32) (result i32)))
  (export "memory" (memory $0))
  (export "exported" (func $exported))
  (export "other1" (func $other1))
  (export "other2" (func $other2))
  (table 1 1 funcref)
  (elem (i32.const 0) $called_indirect)
  ;; CHECK:      (func $start (type $0)
  ;; CHECK-NEXT:  (call $called0)
  ;; CHECK-NEXT: )
  (func $start (type $0)
    (call $called0)
  )
  ;; CHECK:      (func $called0 (type $0)
  ;; CHECK-NEXT:  (call $called1)
  ;; CHECK-NEXT: )
  (func $called0 (type $0)
    (call $called1)
  )
  ;; CHECK:      (func $called1 (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $called1 (type $0)
    (nop)
  )
  ;; CHECK:      (func $called_indirect (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $called_indirect (type $0)
    (nop)
  )
  ;; CHECK:      (func $exported (type $0)
  ;; CHECK-NEXT:  (call $called2)
  ;; CHECK-NEXT: )
  (func $exported (type $0-dupe)
    (call $called2)
  )
  ;; CHECK:      (func $called2 (type $0)
  ;; CHECK-NEXT:  (call $called2)
  ;; CHECK-NEXT:  (call $called3)
  ;; CHECK-NEXT: )
  (func $called2 (type $0-dupe)
    (call $called2)
    (call $called3)
  )
  ;; CHECK:      (func $called3 (type $0)
  ;; CHECK-NEXT:  (call $called4)
  ;; CHECK-NEXT: )
  (func $called3 (type $0-dupe)
    (call $called4)
  )
  ;; CHECK:      (func $called4 (type $0)
  ;; CHECK-NEXT:  (call $called3)
  ;; CHECK-NEXT: )
  (func $called4 (type $0-dupe)
    (call $called3)
  )
  (func $remove0 (type $0-dupe)
    (call $remove1)
  )
  (func $remove1 (type $0-dupe)
    (nop)
  )
  (func $remove2 (type $0-dupe)
    (call $remove2)
  )
  (func $remove3 (type $0)
    (call $remove4)
  )
  (func $remove4 (type $0)
    (call $remove3)
  )
  ;; CHECK:      (func $other1 (type $1) (param $0 i32)
  ;; CHECK-NEXT:  (call_indirect $0 (type $0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $1)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $0 (type $1)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call_indirect $0 (type $2)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call_indirect $0 (type $2)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call_indirect $0 (type $2)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $other1 (type $1) (param i32)
    (call_indirect (type $0) (i32.const 0))
    (call_indirect (type $0) (i32.const 0))
    (call_indirect (type $0-dupe) (i32.const 0))
    (call_indirect (type $0-dupe) (i32.const 0))
    (call_indirect (type $1) (i32.const 0) (i32.const 0))
    (call_indirect (type $1-dupe) (i32.const 0) (i32.const 0))
    (drop (call_indirect (type $2) (i32.const 0) (i32.const 0)))
    (drop (call_indirect (type $2-dupe) (i32.const 0) (i32.const 0)))
    (drop (call_indirect (type $2-thrupe) (i32.const 0) (i32.const 0)))
  )
  ;; CHECK:      (func $other2 (type $1) (param $0 i32)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $other2 (type $1-dupe) (param i32)
    (unreachable)
  )
)
(module ;; remove the table and memory
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 0 funcref))
)
(module ;; remove all tables and the memory
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 0 funcref))
  (import "env" "table2" (table $1 2 2 funcref))
  (elem (table $1) (offset (i32.const 0)) func)
  (elem (table $1) (offset (i32.const 1)) func)
)
(module ;; remove the first table and memory, but not the second one
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 0 funcref))
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "table2" (table $1 1 1 funcref))
  (import "env" "table2" (table $1 1 1 funcref))
  (elem (table $1) (offset (i32.const 0)) func)
  (elem (table $1) (offset (i32.const 0)) func $f)
  ;; CHECK:      (elem $1 (i32.const 0) $f)

  ;; CHECK:      (func $f (type $0)
  ;; CHECK-NEXT: )
  (func $f)
)
(module ;; also when not imported
  (memory 256)
  (table 1 funcref)
)
(module ;; also with multiple tables
  (memory 256)
  (table $0 1 funcref)
  (table $1 1 funcref)
  (elem (table $1) (i32.const 0) func)
)
(module ;; but not when exported
  ;; CHECK:      (import "env" "memory" (memory $0 256))
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 1 funcref))
  (export "mem" (memory 0))
  (export "tab" (table 0))
)
;; CHECK:      (import "env" "table" (table $timport$0 1 funcref))

;; CHECK:      (export "mem" (memory $0))

;; CHECK:      (export "tab" (table $timport$0))
(module ;; and not when there are segments
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "memory" (memory $0 256))
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 1 funcref))
  (data (i32.const 1) "hello, world!")
  (elem (i32.const 0) $waka)
  ;; CHECK:      (import "env" "table" (table $timport$0 1 funcref))

  ;; CHECK:      (data $0 (i32.const 1) "hello, world!")

  ;; CHECK:      (elem $0 (i32.const 0) $waka)

  ;; CHECK:      (func $waka (type $0)
  ;; CHECK-NEXT: )
  (func $waka)
)
(module ;; and not when used
  ;; CHECK:      (type $0 (func))
  (type $0 (func))
  ;; CHECK:      (import "env" "memory" (memory $0 256))
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 0 funcref))
  ;; CHECK:      (import "env" "table" (table $timport$0 0 funcref))

  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.load
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_indirect $timport$0 (type $0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user
    (drop (i32.load (i32.const 0)))
    (call_indirect (type $0) (i32.const 0))
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (memory $0 23 256 shared)
  (memory $0 23 256 shared)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (i32.store
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user
    (i32.store (i32.const 0) (i32.const 0))
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (memory $0 23 256 shared)
  (memory $0 23 256 shared)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.atomic.rmw.add
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user (result i32)
    (i32.atomic.rmw.add (i32.const 0) (i32.const 0))
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (memory $0 23 256 shared)
  (memory $0 23 256 shared)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.atomic.rmw8.cmpxchg_u
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user (result i32)
    (i32.atomic.rmw8.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0))
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (memory $0 23 256 shared)
  (memory $0 23 256 shared)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (local $1 i64)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (memory.atomic.wait32
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user
    (local $0 i32)
    (local $1 i64)
    (drop
     (memory.atomic.wait32
      (local.get $0)
      (local.get $0)
      (local.get $1)
     )
    )
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (memory $0 23 256 shared)
  (memory $0 23 256 shared)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0) (result i32)
  ;; CHECK-NEXT:  (memory.atomic.notify
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user (result i32)
    (memory.atomic.notify (i32.const 0) (i32.const 0))
  )
)
(module ;; atomic.fence and data.drop do not use a memory, so should not keep the memory alive.
  (memory $0 1 1 shared)
  (data "")
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (data $0 "")

  ;; CHECK:      (export "fake-user" (func $user))
  (export "fake-user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (atomic.fence)
  ;; CHECK-NEXT:  (data.drop $0)
  ;; CHECK-NEXT: )
  (func $user
    (atomic.fence)
    (data.drop 0)
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "mem" (memory $0 256))
  (import "env" "mem" (memory $0 256))
  ;; CHECK:      (memory $1 23 256)
  (memory $1 23 256)
  (memory $unused 1 1)

  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (memory.grow $0
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (memory.grow $1
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user
    (drop (memory.grow $0 (i32.const 0)))
    (drop (memory.grow $1 (i32.const 0)))
  )
)
(module ;; more use checks
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (memory $0 23 256)
  (memory $0 23 256)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0) (result i32)
  ;; CHECK-NEXT:  (memory.size)
  ;; CHECK-NEXT: )
  (func $user (result i32)
    (memory.size)
  )
)
(module ;; memory.copy should keep both memories alive
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (memory $0 1 1)
  (memory $0 1 1)
  ;; CHECK:      (memory $1 1 1)
  (memory $1 1 1)
  (memory $unused 1 1)
  ;; CHECK:      (export "user" (func $user))
  (export "user" (func $user))
  ;; CHECK:      (func $user (type $0)
  ;; CHECK-NEXT:  (memory.copy $0 $1
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $user
    (memory.copy $0 $1
      (i32.const 0)
      (i32.const 0)
      (i32.const 0)
    )
  )
)
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "memory" (memory $0 256))
  (import "env" "memory" (memory $0 256))
  (import "env" "table" (table 0 funcref))
  ;; CHECK:      (import "env" "table" (table $timport$0 0 funcref))

  ;; CHECK:      (import "env" "memoryBase" (global $memoryBase i32))
  (import "env" "memoryBase" (global $memoryBase i32)) ;; used in init
  ;; CHECK:      (import "env" "tableBase" (global $tableBase i32))
  (import "env" "tableBase" (global $tableBase i32)) ;; used in init
  (data (global.get $memoryBase) "hello, world!")
  (elem (global.get $tableBase) $waka)
  ;; CHECK:      (data $0 (global.get $memoryBase) "hello, world!")

  ;; CHECK:      (elem $0 (global.get $tableBase) $waka)

  ;; CHECK:      (func $waka (type $0)
  ;; CHECK-NEXT: )
  (func $waka) ;; used in table
)
(module ;; one is exported, and one->two->int global, whose init->imported
  ;; CHECK:      (type $0 (func (result i32)))

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

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

  ;; CHECK:      (import "env" "imported" (global $imported i32))
  (import "env" "imported" (global $imported i32))
  (import "env" "forgetme" (global $forgetme i32))
  ;; CHECK:      (import "env" "_puts" (func $_puts (type $2) (param i32) (result i32)))
  (import "env" "_puts" (func $_puts (param i32) (result i32)))
  (import "env" "forget_puts" (func $forget_puts (param i32) (result i32)))
  ;; CHECK:      (global $int (mut i32) (global.get $imported))
  (global $int (mut i32) (global.get $imported))
  ;; CHECK:      (global $set (mut i32) (i32.const 100))
  (global $set (mut i32) (i32.const 100))
  (global $forglobal.get (mut i32) (i32.const 500))
  ;; CHECK:      (global $exp_glob i32 (i32.const 600))
  (global $exp_glob i32 (i32.const 600))
  ;; CHECK:      (export "one" (func $one))
  (export "one" (func $one))
  ;; CHECK:      (export "three" (func $three))
  (export "three" (func $three))
  ;; CHECK:      (export "exp_glob" (global $exp_glob))
  (export "exp_glob" (global $exp_glob))
  (start $starter)
  ;; CHECK:      (func $one (type $0) (result i32)
  ;; CHECK-NEXT:  (call $two)
  ;; CHECK-NEXT: )
  (func $one (result i32)
    (call $two)
  )
  ;; CHECK:      (func $two (type $0) (result i32)
  ;; CHECK-NEXT:  (global.get $int)
  ;; CHECK-NEXT: )
  (func $two (result i32)
    (global.get $int)
  )
  ;; CHECK:      (func $three (type $1)
  ;; CHECK-NEXT:  (call $four)
  ;; CHECK-NEXT: )
  (func $three
    (call $four)
  )
  ;; CHECK:      (func $four (type $1)
  ;; CHECK-NEXT:  (global.set $set
  ;; CHECK-NEXT:   (i32.const 200)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $_puts
  ;; CHECK-NEXT:    (i32.const 300)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $four
    (global.set $set (i32.const 200))
    (drop (call $_puts (i32.const 300)))
  )
  (func $forget_implemented
    (nop)
  )
  (func $starter
    (nop)
  )
)
(module ;; empty start being removed
  (start $starter)
  (func $starter
    (nop)
  )
)
(module ;; non-empty start being kept
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (start $starter)
  (start $starter)
  ;; CHECK:      (func $starter (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $starter
    (drop (i32.const 0))
  )
)
(module ;; imported start cannot be removed
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (import "env" "start" (func $start (type $0)))
  (import "env" "start" (func $start))
  ;; CHECK:      (start $start)
  (start $start)
)
(module ;; the function and the table can be removed
 (type $0 (func (param f64) (result f64)))
 (table 6 6 funcref)
 (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
  (if (result f64)
   (f64.eq
    (f64.const 1)
    (f64.const 1)
   )
   (then
    (f64.const 1)
   )
   (else
    (f64.const 0)
   )
  )
 )
)
(module ;; the function uses the table, but all are removeable
 (type $0 (func (param f64) (result f64)))
 (table 6 6 funcref)
 (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
  (if (result f64)
   (f64.eq
    (f64.const 1)
    (f64.const 1)
   )
   (then
    (call_indirect (type $0) (f64.const 1) (i32.const 0))
   )
   (else
    (f64.const 0)
   )
  )
 )
)
(module
 ;; We import two tables and have an active segment that writes to one of them.
 ;; We must keep that table and the segment, but we can remove the other table.
 ;; CHECK:      (type $0 (func (param f64) (result f64)))
 (type $0 (func (param f64) (result f64)))

 ;; CHECK:      (import "env" "written" (table $written 6 6 funcref))
 (import "env" "written" (table $written 6 6 funcref))

 (import "env" "unwritten" (table $unwritten 6 6 funcref))

 (table $defined-unused 6 6 funcref)

 ;; CHECK:      (table $defined-used 6 6 funcref)
 (table $defined-used 6 6 funcref)

 ;; CHECK:      (elem $active1 (table $written) (i32.const 0) func $0)
 (elem $active1 (table $written) (i32.const 0) func $0)

 ;; This empty active segment doesn't keep the unwritten table alive.
 (elem $active2 (table $unwritten) (i32.const 0) func)

 (elem $active3 (table $defined-unused) (i32.const 0) func $0)

 ;; CHECK:      (elem $active4 (table $defined-used) (i32.const 0) func $0)
 (elem $active4 (table $defined-used) (i32.const 0) func $0)

 (elem $active5 (table $defined-used) (i32.const 0) func)
 ;; CHECK:      (func $0 (type $0) (param $var$0 f64) (result f64)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (table.get $defined-used
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (if (result f64)
 ;; CHECK-NEXT:   (f64.eq
 ;; CHECK-NEXT:    (f64.const 1)
 ;; CHECK-NEXT:    (f64.const 1)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (f64.const 1)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (f64.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64)
  (drop
   (table.get $defined-used
    (i32.const 0)
   )
  )
  (if (result f64)
   (f64.eq
    (f64.const 1)
    (f64.const 1)
   )
   (then
    (f64.const 1)
   )
   (else
    (f64.const 0)
   )
  )
 )
)
(module
 ;; The same thing works for memories with active segments.
 ;; CHECK:      (type $0 (func))

 ;; CHECK:      (import "env" "written" (memory $written 1 1))
 (import "env" "written" (memory $written 1 1))

 (import "env" "unwritten" (memory $unwritten 1 1))

 (memory $defined-unused 1 1)

 ;; CHECK:      (memory $defined-used 1 1)
 (memory $defined-used 1 1)

 ;; CHECK:      (data $active1 (i32.const 0) "foobar")
 (data $active1 (memory $written) (i32.const 0) "foobar")

 (data $active2 (memory $unwritten) (i32.const 0) "")

 (data $active3 (memory $defined-unused) (i32.const 0) "hello")

 ;; CHECK:      (data $active4 (memory $defined-used) (i32.const 0) "hello")
 (data $active4 (memory $defined-used) (i32.const 0) "hello")

 (data $active5 (memory $defined-used) (i32.const 0) "")

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

 ;; CHECK:      (func $user (type $0)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (i32.load $defined-used
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $user (export "user")
  (drop
   (i32.load $defined-used
    (i32.const 0)
   )
  )
 )
)
(module
 ;; Nothing should break if the unused segments precede the used segments.
 ;; CHECK:      (type $0 (func))

 ;; CHECK:      (type $array (array funcref))
 (type $array (array funcref))

 (memory $mem 1 1)
 (table $tab 1 1 funcref)

 (data $unused "")
 (elem $unused func)

 ;; CHECK:      (data $used "")
 (data $used "")
 ;; CHECK:      (elem $used func)
 (elem $used func)

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

 ;; CHECK:      (func $user (type $0)
 ;; CHECK-NEXT:  (data.drop $used)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (array.new_elem $array $used
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $user (export "user")
  (data.drop 1)
  (drop
   (array.new_elem $array 1
    (i32.const 0)
    (i32.const 0)
   )
  )
 )
)
;; SIMD operations can keep memories alive
(module
 ;; CHECK:      (type $0 (func))

 ;; CHECK:      (memory $A 1 1)
 (memory $A 1 1)
 ;; CHECK:      (memory $B 1 1)
 (memory $B 1 1)
 (memory $C-unused 1 1)

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

 ;; CHECK:      (func $func (type $0)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (v128.load64_splat $A
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (v128.load16_lane $B 0
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:    (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $func (export "func")
  (drop
   (v128.load64_splat $A
    (i32.const 0)
   )
  )
  (drop
   (v128.load16_lane $B 0
    (i32.const 0)
    (v128.const i32x4 0 0 0 0)
   )
  )
 )
)

(module
  ;; When we export a function that calls another, we can export the called
  ;; function, skipping the one in the middle. The exports of $middle and
  ;; $other-middle can be changed to their targets here.

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

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

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

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

  ;; CHECK:      (export "export-other-middle" (func $other-middle))
  (export "export-other-middle" (func $other-middle))

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

  ;; CHECK:      (func $middle (type $0)
  ;; CHECK-NEXT:  (call $import)
  ;; CHECK-NEXT: )
  (func $middle
    (call $import)
  )

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

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

;; As above, but we do not do that optimization when it would change the
;; exported type.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (func))
    (type $A (func))
    ;; CHECK:       (type $B (func))
    (type $B (func))
  )

  ;; CHECK:      (import "a" "a" (func $import-A (type $A)))
  (import "a" "a" (func $import-A (type $A)))
  ;; CHECK:      (import "b" "b" (func $import-B (type $B)))
  (import "b" "b" (func $import-B (type $B)))

  ;; CHECK:      (export "export-import-A" (func $import-A))
  (export "export-import-A" (func $import-A))

  ;; CHECK:      (export "export-import-B" (func $import-B))
  (export "export-import-B" (func $import-B))

  ;; CHECK:      (export "export-middle-A-A" (func $middle-A-A))
  (export "export-middle-A-A" (func $middle-A-A))

  ;; CHECK:      (export "export-middle-A-B" (func $middle-A-B))
  (export "export-middle-A-B" (func $middle-A-B))

  ;; CHECK:      (export "export-middle-B-A" (func $middle-B-A))
  (export "export-middle-B-A" (func $middle-B-A))

  ;; CHECK:      (export "export-middle-B-B" (func $middle-B-B))
  (export "export-middle-B-B" (func $middle-B-B))

  ;; CHECK:      (func $middle-A-A (type $A)
  ;; CHECK-NEXT:  (call $import-A)
  ;; CHECK-NEXT: )
  (func $middle-A-A (type $A)
    (call $import-A)
  )

  ;; CHECK:      (func $middle-A-B (type $A)
  ;; CHECK-NEXT:  (call $import-B)
  ;; CHECK-NEXT: )
  (func $middle-A-B (type $A)
    (call $import-B)
  )

  ;; CHECK:      (func $middle-B-A (type $B)
  ;; CHECK-NEXT:  (call $import-A)
  ;; CHECK-NEXT: )
  (func $middle-B-A (type $B)
    (call $import-A)
  )

  ;; CHECK:      (func $middle-B-B (type $B)
  ;; CHECK-NEXT:  (call $import-B)
  ;; CHECK-NEXT: )
  (func $middle-B-B (type $B)
    (call $import-B)
  )
)

;; As above, but checking for parameters: It's ok to pass values through, but
;; not to do anything else.
(module
  ;; CHECK:      (type $0 (func (param i32)))

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

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

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

  ;; CHECK:      (export "export-middle-local" (func $middle-local))
  (export "export-middle-local" (func $middle-local))

  ;; CHECK:      (export "export-middle-other" (func $middle-other))
  (export "export-middle-other" (func $middle-other))

  ;; CHECK:      (export "export-middle-noncall" (func $middle-noncall))
  (export "export-middle-noncall" (func $middle-noncall))

  ;; CHECK:      (func $middle (type $0) (param $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle (param $x i32)
    ;; This extra local is not a problem.
    (local $y i32)
    (call $import
      (local.get $x)
    )
  )

  ;; CHECK:      (func $middle-local (type $0) (param $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle-local (param $x i32)
    (local $y i32)
    (call $import
      ;; Now we get the local instead of the param, so we cannot optimize.
      (local.get $y)
    )
  )

  ;; CHECK:      (func $middle-other (type $0) (param $x i32)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle-other (param $x i32)
    (call $import
      ;; Something other than local.get, so we cannot optimize.
      (i32.const 1)
    )
  )

  ;; CHECK:      (func $middle-noncall (type $0) (param $x i32)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle-noncall (param $x i32)
    ;; Not even a call here.
    (drop
      (i32.const 1)
    )
  )
)

;; Function with two parameters: we can only optimize when the arguments are in
;; the right order.
;;
;; Also test with a return value.
(module
  ;; CHECK:      (type $0 (func (param i32 i32) (result f64)))

  ;; CHECK:      (import "a" "b" (func $import (type $0) (param i32 i32) (result f64)))
  (import "a" "b" (func $import (param i32) (param i32) (result f64)))

  ;; CHECK:      (export "export-middle-right" (func $middle-right))
  (export "export-middle-right" (func $middle-right))

  ;; CHECK:      (export "export-middle-wrong" (func $middle-wrong))
  (export "export-middle-wrong" (func $middle-wrong))

  ;; CHECK:      (func $middle-right (type $0) (param $x i32) (param $y i32) (result f64)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle-right (param $x i32) (param $y i32) (result f64)
    (call $import
      (local.get $x)
      (local.get $y)
    )
  )

  ;; CHECK:      (func $middle-wrong (type $0) (param $x i32) (param $y i32) (result f64)
  ;; CHECK-NEXT:  (call $import
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $middle-wrong (param $x i32) (param $y i32) (result f64)
    ;; The local.gets are reversed here, so we cannot optimize.
    (call $import
      (local.get $y)
      (local.get $x)
    )
  )
)

(module
  ;; As above, but with a return_call. We can optimize it like a call.

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

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

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

  ;; CHECK:      (func $middle (type $0)
  ;; CHECK-NEXT:  (return_call $import)
  ;; CHECK-NEXT: )
  (func $middle
    (return_call $import)
  )
)