;; 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 --directize                                                 --all-features -S -o - | filecheck %s --check-prefix=CHECK
;; RUN: foreach %s %t wasm-opt --directize --pass-arg=directize-initial-contents-immutable --all-features -S -o - | filecheck %s --check-prefix=IMMUT

(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 ;; CHECK:      (table $t64 i64 5 5 funcref)

 ;; CHECK:      (elem $elem (table $0) (i32.const 1) func $foo)
 ;; IMMUT:      (table $t64 i64 5 5 funcref)

 ;; IMMUT:      (elem $elem (table $0) (i32.const 1) func $foo)
 (elem $elem (i32.const 1) $foo)

 (table $t64 i64 5 5 funcref)

 ;; CHECK:      (elem $elem64 (table $t64) (i64.const 1) func $foo)
 ;; IMMUT:      (elem $elem64 (table $t64) (i64.const 1) func $foo)
 (elem $elem64 (table $t64) (i64.const 1) funcref (ref.func $foo))

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  ;; helper function
  (unreachable)
 )

 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call $foo
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

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

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (type $1 (func (param i32) (result i32)))

 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 ;; CHECK:      (table $1 5 5 funcref)
 ;; IMMUT:      (table $1 5 5 funcref)
 (table $1 5 5 funcref)
 (elem (table $0) (i32.const 1) func $dummy)
 (elem (table $1) (i32.const 1) func $f)
 ;; CHECK:      (elem $0 (table $0) (i32.const 1) func $dummy)

 ;; CHECK:      (elem $1 (table $1) (i32.const 1) func $f)

 ;; CHECK:      (func $dummy (type $1) (param $0 i32) (result i32)
 ;; CHECK-NEXT:  (local.get $0)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (table $0) (i32.const 1) func $dummy)

 ;; IMMUT:      (elem $1 (table $1) (i32.const 1) func $f)

 ;; IMMUT:      (func $dummy (type $1) (param $0 i32) (result i32)
 ;; IMMUT-NEXT:  (local.get $0)
 ;; IMMUT-NEXT: )
 (func $dummy (param i32) (result i32)
  (local.get 0)
 )
 ;; CHECK:      (func $f (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $f (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $f (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $g (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call $f
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $g (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $f
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $g (param $x i32) (param $y i32)
  (call_indirect $1 (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

;; at table edges
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 ;; CHECK:      (table $1 5 5 funcref)
 ;; IMMUT:      (table $1 5 5 funcref)
 (table $1 5 5 funcref)
 (elem (table $1) (i32.const 4) func $foo)
 ;; CHECK:      (elem $0 (table $1) (i32.const 4) func $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (table $1) (i32.const 4) func $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call $foo
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect $1 (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 4)
  )
 )
)

(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 0) $foo)
 ;; CHECK:      (elem $0 (i32.const 0) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 0) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call $foo
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 0)
  )
 )
)

(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 ;; CHECK:      (table $1 5 5 funcref)
 ;; IMMUT:      (table $1 5 5 funcref)
 (table $1 5 5 funcref)
 (elem (i32.const 0) $foo $foo $foo $foo $foo)
 (elem (table $1) (i32.const 0) func $foo $foo $foo $foo $foo)
 ;; CHECK:      (elem $0 (table $0) (i32.const 0) func $foo $foo $foo $foo $foo)

 ;; CHECK:      (elem $1 (table $1) (i32.const 0) func $foo $foo $foo $foo $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (table $0) (i32.const 0) func $foo $foo $foo $foo $foo)

 ;; IMMUT:      (elem $1 (table $1) (i32.const 0) func $foo $foo $foo $foo $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call $foo
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect $1 (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 2)
  )
 )
)

;; imported table. only optimizable in the immutable case.
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (import "env" "table" (table $table 5 5 funcref))
 ;; IMMUT:      (import "env" "table" (table $table 5 5 funcref))
 (import "env" "table" (table $table 5 5 funcref))
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )

 ;; CHECK:      (func $out-of-bounds (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 999)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $out-of-bounds (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call_indirect $table (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (i32.const 999)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $out-of-bounds (param $x i32) (param $y i32)
  ;; The index here, 999, is out of bounds. We can't optimize that even in the
  ;; immutable case, since we only assume the initial contents in the table are
  ;; immutable, and so something might be written to offset 999 later.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 999)
  )
 )
)

;; exported table. only optimizable in the immutable case.
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (export "tab" (table $0))
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (export "tab" (table $0))
 (export "tab" (table $0))
 (elem (i32.const 1) $foo)
 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

;; non-constant table offset
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 (global $g (import "env" "g") i32)

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

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (import "env" "g" (global $g i32))

 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)

 (elem (global.get $g) $foo)
 ;; CHECK:      (elem $0 (global.get $g) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (global.get $g) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (i32.const 1)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 (global $g (import "env" "g") i32)

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

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (import "env" "g" (global $g i32))

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

 (elem (table $1) (global.get $g) func $foo)
 ;; CHECK:      (elem $0 (table $1) (global.get $g) func $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (table $1) (global.get $g) func $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $1 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (call_indirect $1 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (i32.const 1)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect $1 (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

;; non-constant call index
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (type $1 (func (param i32 i32 i32)))

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (type $1 (func (param i32 i32 i32)))

 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $1) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (local.get $z)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $1) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (local.get $z)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32) (param $z i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (local.get $z)
  )
 )
)

;; bad index
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $y
 ;; CHECK-NEXT:    (local.get $y)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (drop
 ;; IMMUT-NEXT:   (local.tee $y
 ;; IMMUT-NEXT:    (local.get $y)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   ;; Use a local.tee to show that an operand with side effects is kept/dropped.
   (local.tee $y
    (local.get $y)
   )
   (i32.const 5)
  )
 )
)

;; missing index
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 2)
  )
 )
)

;; bad type
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $0 (func (param i32)))

 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $0) (param $0 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $0) (param $0 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

;; no table
(module
 ;; CHECK:      (type $0 (func (param i32)))

 ;; CHECK:      (func $foo (type $0) (param $0 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (type $0 (func (param i32)))

 ;; IMMUT:      (func $foo (type $0) (param $0 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32)
  (unreachable)
 )
)

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

 ;; CHECK:      (table $0 8 8 funcref)
 ;; IMMUT:      (type $0 (func))

 ;; IMMUT:      (table $0 8 8 funcref)
 (table $0 8 8 funcref)
 ;; CHECK:      (func $0 (type $0)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $0 (type $0)
 ;; IMMUT-NEXT:  (nop)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $0
  (block ;; the type of this block will change
   (nop)
   (call_indirect (type 0)
    (i32.const 15)
   )
  )
 )
)

(module ;; indirect tail call
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))
 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo)
 ;; CHECK:      (elem $0 (i32.const 1) $foo)

 ;; CHECK:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo)

 ;; IMMUT:      (func $foo (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (return_call $foo
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (return_call $foo
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  (return_call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
 )
)

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

 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $0 (func (param i32 i32 i32)))

 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))

 (type $none (func))

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

 ;; CHECK:      (table $0 5 5 funcref)
 ;; IMMUT:      (type $2 (func (param i32)))

 ;; IMMUT:      (table $0 5 5 funcref)
 (table $0 5 5 funcref)
 (elem (i32.const 1) $foo1 $foo2)
 ;; CHECK:      (elem $0 (i32.const 1) $foo1 $foo2)

 ;; CHECK:      (func $foo1 (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo1 $foo2)

 ;; IMMUT:      (func $foo1 (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo1 (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $foo2 (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $foo2 (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo2 (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $select (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (local $3 i32)
 ;; CHECK-NEXT:  (local $4 i32)
 ;; CHECK-NEXT:  (local.set $3
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (local.set $4
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $z)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (call $foo1
 ;; CHECK-NEXT:     (local.get $3)
 ;; CHECK-NEXT:     (local.get $4)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (call $foo2
 ;; CHECK-NEXT:     (local.get $3)
 ;; CHECK-NEXT:     (local.get $4)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (local $3 i32)
 ;; IMMUT-NEXT:  (local $4 i32)
 ;; IMMUT-NEXT:  (local.set $3
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (local.set $4
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (if
 ;; IMMUT-NEXT:   (local.get $z)
 ;; IMMUT-NEXT:   (then
 ;; IMMUT-NEXT:    (call $foo1
 ;; IMMUT-NEXT:     (local.get $3)
 ;; IMMUT-NEXT:     (local.get $4)
 ;; IMMUT-NEXT:    )
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:   (else
 ;; IMMUT-NEXT:    (call $foo2
 ;; IMMUT-NEXT:     (local.get $3)
 ;; IMMUT-NEXT:     (local.get $4)
 ;; IMMUT-NEXT:    )
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select (param $x i32) (param $y i32) (param $z i32)
  ;; Test we can optimize a call_indirect whose index is a select between two
  ;; constants. We can emit an if and two direct calls for that.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (i32.const 1)
    (i32.const 2)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-bad-1 (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (local.get $z)
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:    (local.get $z)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-bad-1 (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (local.get $z)
 ;; IMMUT-NEXT:    (i32.const 2)
 ;; IMMUT-NEXT:    (local.get $z)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-bad-1 (param $x i32) (param $y i32) (param $z i32)
  ;; As above but one select arm is not constant.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (local.get $z)
    (i32.const 2)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-bad-2 (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:    (local.get $z)
 ;; CHECK-NEXT:    (local.get $z)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-bad-2 (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (i32.const 2)
 ;; IMMUT-NEXT:    (local.get $z)
 ;; IMMUT-NEXT:    (local.get $z)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-bad-2 (param $x i32) (param $y i32) (param $z i32)
  ;; As above but the other select arm is not constant.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (i32.const 2)
    (local.get $z)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-out-of-range (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (local $3 i32)
 ;; CHECK-NEXT:  (local $4 i32)
 ;; CHECK-NEXT:  (local.set $3
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (local.set $4
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $z)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (call $foo2
 ;; CHECK-NEXT:     (local.get $3)
 ;; CHECK-NEXT:     (local.get $4)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-out-of-range (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (local $3 i32)
 ;; IMMUT-NEXT:  (local $4 i32)
 ;; IMMUT-NEXT:  (local.set $3
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (local.set $4
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (if
 ;; IMMUT-NEXT:   (local.get $z)
 ;; IMMUT-NEXT:   (then
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:   (else
 ;; IMMUT-NEXT:    (call $foo2
 ;; IMMUT-NEXT:     (local.get $3)
 ;; IMMUT-NEXT:     (local.get $4)
 ;; IMMUT-NEXT:    )
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-out-of-range (param $x i32) (param $y i32) (param $z i32)
  ;; Both are constants, but one is out of range for the table, and there is no
  ;; valid function to call there; emit an unreachable.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (i32.const 99999)
    (i32.const 2)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-both-out-of-range (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (local $3 i32)
 ;; CHECK-NEXT:  (local $4 i32)
 ;; CHECK-NEXT:  (local.set $3
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (local.set $4
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $z)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-both-out-of-range (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (local $3 i32)
 ;; IMMUT-NEXT:  (local $4 i32)
 ;; IMMUT-NEXT:  (local.set $3
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (local.set $4
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (if
 ;; IMMUT-NEXT:   (local.get $z)
 ;; IMMUT-NEXT:   (then
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:   (else
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-both-out-of-range (param $x i32) (param $y i32) (param $z i32)
  ;; Both are constants, and both are out of range for the table.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (i32.const 99999)
    (i32.const -1)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-unreachable-operand (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:    (local.get $z)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-unreachable-operand (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:    (i32.const 2)
 ;; IMMUT-NEXT:    (local.get $z)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-unreachable-operand (param $x i32) (param $y i32) (param $z i32)
  ;; One operand is unreachable.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (unreachable)
    (i32.const 2)
    (local.get $z)
   )
  )
 )
 ;; CHECK:      (func $select-unreachable-condition (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (i32.const 1)
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-unreachable-condition (type $0) (param $x i32) (param $y i32) (param $z i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (i32.const 1)
 ;; IMMUT-NEXT:    (i32.const 2)
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-unreachable-condition (param $x i32) (param $y i32) (param $z i32)
  ;; The condition is unreachable. We should not even create any vars here, and
  ;; just not do anything.
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (select
    (i32.const 1)
    (i32.const 2)
    (unreachable)
   )
  )
 )
 ;; CHECK:      (func $select-bad-type (type $2) (param $z i32)
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $z)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-bad-type (type $2) (param $z i32)
 ;; IMMUT-NEXT:  (if
 ;; IMMUT-NEXT:   (local.get $z)
 ;; IMMUT-NEXT:   (then
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:   (else
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-bad-type (param $z i32)
  ;; The type here is $none, which does not match the functions at indexes 1 and
  ;; 2, so we know they will trap and can emit unreachables.
  (call_indirect (type $none)
   (select
    (i32.const 1)
    (i32.const 2)
    (local.get $z)
   )
  )
 )
)

(module
 ;; CHECK:      (type $F (func (param (ref func))))

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

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

 ;; CHECK:      (table $0 15 15 funcref)
 ;; IMMUT:      (type $F (func (param (ref func))))

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

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

 ;; IMMUT:      (table $0 15 15 funcref)
 (table $0 15 15 funcref)
 (type $F (func (param (ref func))))
 (elem (i32.const 10) $foo-ref $foo-ref)

 ;; CHECK:      (elem $0 (i32.const 10) $foo-ref $foo-ref)

 ;; CHECK:      (elem declare func $select-non-nullable)

 ;; CHECK:      (func $foo-ref (type $F) (param $0 (ref func))
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 10) $foo-ref $foo-ref)

 ;; IMMUT:      (elem declare func $select-non-nullable)

 ;; IMMUT:      (func $foo-ref (type $F) (param $0 (ref func))
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo-ref (param (ref func))
  ;; helper function
  (unreachable)
 )

 ;; CHECK:      (func $select-non-nullable (type $1) (param $x i32)
 ;; CHECK-NEXT:  (local $1 (ref (exact $1)))
 ;; CHECK-NEXT:  (local.set $1
 ;; CHECK-NEXT:   (ref.func $select-non-nullable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (if
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (call $foo-ref
 ;; CHECK-NEXT:     (local.get $1)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (call $foo-ref
 ;; CHECK-NEXT:     (local.get $1)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-non-nullable (type $1) (param $x i32)
 ;; IMMUT-NEXT:  (local $1 (ref (exact $1)))
 ;; IMMUT-NEXT:  (local.set $1
 ;; IMMUT-NEXT:   (ref.func $select-non-nullable)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (if
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (then
 ;; IMMUT-NEXT:    (call $foo-ref
 ;; IMMUT-NEXT:     (local.get $1)
 ;; IMMUT-NEXT:    )
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:   (else
 ;; IMMUT-NEXT:    (call $foo-ref
 ;; IMMUT-NEXT:     (local.get $1)
 ;; IMMUT-NEXT:    )
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-non-nullable (param $x i32)
  ;; Test we can handle a non-nullable value when optimizing a select, during
  ;; which we place values in locals. The local can remain non-nullable since it
  ;; dominates the uses.
  (call_indirect (type $F)
   (ref.func $select-non-nullable)
   (select
    (i32.const 10)
    (i32.const 11)
    (local.get $x)
   )
  )
 )

 ;; CHECK:      (func $select-non-nullable-unreachable-condition (type $2)
 ;; CHECK-NEXT:  (call_indirect $0 (type $F)
 ;; CHECK-NEXT:   (ref.func $select-non-nullable)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:    (i32.const 11)
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-non-nullable-unreachable-condition (type $2)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $F)
 ;; IMMUT-NEXT:   (ref.func $select-non-nullable)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (i32.const 10)
 ;; IMMUT-NEXT:    (i32.const 11)
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-non-nullable-unreachable-condition
  ;; Test we ignore an unreachable condition and don't make any changes at all
  ;; to the code (in particular, we shouldn't add any vars).
  (call_indirect (type $F)
   (ref.func $select-non-nullable)
   (select
    (i32.const 10)
    (i32.const 11)
    (unreachable)
   )
  )
 )

 ;; CHECK:      (func $select-non-nullable-unreachable-arm (type $2)
 ;; CHECK-NEXT:  (call_indirect $0 (type $F)
 ;; CHECK-NEXT:   (ref.func $select-non-nullable)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (f32.const 3.141590118408203)
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:    (i32.const 1)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-non-nullable-unreachable-arm (type $2)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $F)
 ;; IMMUT-NEXT:   (ref.func $select-non-nullable)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (f32.const 3.141590118408203)
 ;; IMMUT-NEXT:    (unreachable)
 ;; IMMUT-NEXT:    (i32.const 1)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-non-nullable-unreachable-arm
  ;; Test we ignore an unreachable arm and don't make any changes at all
  ;; to the code (in particular, we shouldn't add any vars).
  (call_indirect (type $F)
   (ref.func $select-non-nullable)
   (select
    ;; Note how the type here is not even an i32, so we must not even try to
    ;; look into the select's values at all - the select is unreachable and we
    ;; should give up on optimizing.
    (f32.const 3.14159)
    (unreachable)
    (i32.const 1)
   )
  )
 )

 ;; CHECK:      (func $select-non-nullable-unreachable-arg (type $1) (param $x i32)
 ;; CHECK-NEXT:  (call_indirect $0 (type $F)
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:    (i32.const 11)
 ;; CHECK-NEXT:    (local.get $x)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $select-non-nullable-unreachable-arg (type $1) (param $x i32)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $F)
 ;; IMMUT-NEXT:   (unreachable)
 ;; IMMUT-NEXT:   (select
 ;; IMMUT-NEXT:    (i32.const 10)
 ;; IMMUT-NEXT:    (i32.const 11)
 ;; IMMUT-NEXT:    (local.get $x)
 ;; IMMUT-NEXT:   )
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $select-non-nullable-unreachable-arg (param $x i32)
  ;; Test we ignore an unreachable argument and don't make any changes at all
  ;; to the code (in particular, we shouldn't add any vars).
  (call_indirect (type $F)
   (unreachable)
   (select
    (i32.const 10)
    (i32.const 11)
    (local.get $x)
   )
  )
 )
)

;; A table.set prevents optimization.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 ;; CHECK:      (table $has-set 5 5 funcref)
 ;; IMMUT:      (table $has-set 5 5 funcref)
 (table $has-set 5 5 funcref)

 ;; CHECK:      (table $no-set 5 5 funcref)
 ;; IMMUT:      (table $no-set 5 5 funcref)
 (table $no-set 5 5 funcref)

 ;; CHECK:      (elem $0 (table $has-set) (i32.const 1) func $foo)
 ;; IMMUT:      (elem $0 (table $has-set) (i32.const 1) func $foo)
 (elem $0 (table $has-set) (i32.const 1) func $foo)

 ;; CHECK:      (elem $1 (table $no-set) (i32.const 1) func $foo)
 ;; IMMUT:      (elem $1 (table $no-set) (i32.const 1) func $foo)
 (elem $1 (table $no-set) (i32.const 1) func $foo)

 ;; CHECK:      (func $foo (type $v)
 ;; CHECK-NEXT:  (table.set $has-set
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:   (ref.func $foo)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $foo (type $v)
 ;; IMMUT-NEXT:  (table.set $has-set
 ;; IMMUT-NEXT:   (i32.const 1)
 ;; IMMUT-NEXT:   (ref.func $foo)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $foo
  ;; Technically this set writes the same value as is already there, but the
  ;; analysis will give up on optimizing when it sees any set to a table.
  (table.set $has-set
   (i32.const 1)
   (ref.func $foo)
  )
 )

 ;; CHECK:      (func $bar (type $v)
 ;; CHECK-NEXT:  (call_indirect $has-set (type $v)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call $foo)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $v)
 ;; IMMUT-NEXT:  (call $foo)
 ;; IMMUT-NEXT:  (call $foo)
 ;; IMMUT-NEXT: )
 (func $bar
  ;; We can't optimize this one, but we can the one after it. (But we can
  ;; optimize both in the immutable case.)
  (call_indirect $has-set (type $v)
   (i32.const 1)
  )
  (call_indirect $no-set (type $v)
   (i32.const 1)
  )
 )
)

;; An imported table with a non-contiguous range in the initial contents.
(module
 ;; CHECK:      (type $ii (func (param i32 i32)))
 ;; IMMUT:      (type $ii (func (param i32 i32)))
 (type $ii (func (param i32 i32)))

 ;; CHECK:      (import "env" "table" (table $table 5 5 funcref))
 ;; IMMUT:      (import "env" "table" (table $table 5 5 funcref))
 (import "env" "table" (table $table 5 5 funcref))
 (elem (i32.const 1) $foo1)
 (elem (i32.const 3) $foo2)

 ;; CHECK:      (elem $0 (i32.const 1) $foo1)

 ;; CHECK:      (elem $1 (i32.const 3) $foo2)

 ;; CHECK:      (func $foo1 (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (elem $0 (i32.const 1) $foo1)

 ;; IMMUT:      (elem $1 (i32.const 3) $foo2)

 ;; IMMUT:      (func $foo1 (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo1 (param i32) (param i32)
  (unreachable)
 )
 ;; CHECK:      (func $foo2 (type $ii) (param $0 i32) (param $1 i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $foo2 (type $ii) (param $0 i32) (param $1 i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $foo2 (param i32) (param i32)
  (unreachable)
 )

 ;; CHECK:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 2)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 3)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call_indirect $table (type $ii)
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:   (local.get $y)
 ;; CHECK-NEXT:   (i32.const 4)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $bar (type $ii) (param $x i32) (param $y i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT:  (call $foo1
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT:  (call $foo2
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT:  (call_indirect $table (type $ii)
 ;; IMMUT-NEXT:   (local.get $x)
 ;; IMMUT-NEXT:   (local.get $y)
 ;; IMMUT-NEXT:   (i32.const 4)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $bar (param $x i32) (param $y i32)
  ;; When assuming the initial contents are immutable, we can optimize some
  ;; of these cases. 0 and 2 are offsets that are known to contain a null, so
  ;; they will trap, and 1 and 3 contain known contents we can do a direct call
  ;; to. 4 is out of bounds so we cannot optimize there. (And in all of these,
  ;; we cannot optimize anything in the non-immutable case, since the table is
  ;; imported.)
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 0)
  )
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 1)
  )
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 2)
  )
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 3)
  )
  (call_indirect (type $ii)
   (local.get $x)
   (local.get $y)
   (i32.const 4)
  )
 )
)

;; A table.fill prevents optimization.
(module
 ;; CHECK:      (type $i32 (func (result i32)))
 ;; IMMUT:      (type $i32 (func (result i32)))
 (type $i32 (func (result i32)))

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

 ;; CHECK:      (table $table 111 funcref)
 ;; IMMUT:      (type $1 (func))

 ;; IMMUT:      (table $table 111 funcref)
 (table $table 111 funcref)
 (elem (i32.const 0) $func-A)

 ;; CHECK:      (elem $0 (i32.const 0) $func-A)

 ;; CHECK:      (elem declare func $func-B)

 ;; CHECK:      (export "a" (func $fill))
 ;; IMMUT:      (elem $0 (i32.const 0) $func-A)

 ;; IMMUT:      (elem declare func $func-B)

 ;; IMMUT:      (export "a" (func $fill))
 (export "a" (func $fill))
 ;; CHECK:      (export "b" (func $call))
 ;; IMMUT:      (export "b" (func $call))
 (export "b" (func $call))

 ;; CHECK:      (func $func-A (type $i32) (result i32)
 ;; CHECK-NEXT:  (i32.const 0)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $func-A (type $i32) (result i32)
 ;; IMMUT-NEXT:  (i32.const 0)
 ;; IMMUT-NEXT: )
 (func $func-A (result i32)
  (i32.const 0)
 )

 ;; CHECK:      (func $func-B (type $i32) (result i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $func-B (type $i32) (result i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $func-B (result i32)
  (unreachable)
 )

 ;; CHECK:      (func $fill (type $1)
 ;; CHECK-NEXT:  (table.fill $table
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (ref.func $func-B)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $fill (type $1)
 ;; IMMUT-NEXT:  (table.fill $table
 ;; IMMUT-NEXT:   (i32.const 0)
 ;; IMMUT-NEXT:   (ref.func $func-B)
 ;; IMMUT-NEXT:   (i32.const 1)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $fill
  (table.fill $table
   (i32.const 0)
   (ref.func $func-B)
   (i32.const 1)
  )
 )

 ;; CHECK:      (func $call (type $1)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call_indirect $table (type $i32)
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $call (type $1)
 ;; IMMUT-NEXT:  (drop
 ;; IMMUT-NEXT:   (call $func-A)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $call
  (drop
   ;; This cannot be turned into a direct call due to the table.fill, unless we
   ;; assume initial contents are immutable.
   (call_indirect (type $i32)
    (i32.const 0)
   )
  )
 )
)

;; A table.init prevents optimization.
(module
 ;; CHECK:      (type $i32 (func (result i32)))
 ;; IMMUT:      (type $i32 (func (result i32)))
 (type $i32 (func (result i32)))

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

 ;; CHECK:      (table $table 111 funcref)
 ;; IMMUT:      (type $1 (func))

 ;; IMMUT:      (table $table 111 funcref)
 (table $table 111 funcref)
 (elem (i32.const 0) $func-A)

 ;; CHECK:      (elem $0 (i32.const 0) $func-A)

 ;; CHECK:      (elem $elem func $func-B)
 ;; IMMUT:      (elem $0 (i32.const 0) $func-A)

 ;; IMMUT:      (elem $elem func $func-B)
 (elem $elem $func-B)

 ;; CHECK:      (export "a" (func $init))
 ;; IMMUT:      (export "a" (func $init))
 (export "a" (func $init))
 ;; CHECK:      (export "b" (func $call))
 ;; IMMUT:      (export "b" (func $call))
 (export "b" (func $call))

 ;; CHECK:      (func $func-A (type $i32) (result i32)
 ;; CHECK-NEXT:  (i32.const 0)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $func-A (type $i32) (result i32)
 ;; IMMUT-NEXT:  (i32.const 0)
 ;; IMMUT-NEXT: )
 (func $func-A (result i32)
  (i32.const 0)
 )

 ;; CHECK:      (func $func-B (type $i32) (result i32)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $func-B (type $i32) (result i32)
 ;; IMMUT-NEXT:  (unreachable)
 ;; IMMUT-NEXT: )
 (func $func-B (result i32)
  (unreachable)
 )

 ;; CHECK:      (func $init (type $1)
 ;; CHECK-NEXT:  (table.init $table $elem
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $init (type $1)
 ;; IMMUT-NEXT:  (table.init $table $elem
 ;; IMMUT-NEXT:   (i32.const 0)
 ;; IMMUT-NEXT:   (i32.const 0)
 ;; IMMUT-NEXT:   (i32.const 1)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $init
  (table.init $table $elem
   (i32.const 0)
   (i32.const 0)
   (i32.const 1)
  )
 )

 ;; CHECK:      (func $call (type $1)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call_indirect $table (type $i32)
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (func $call (type $1)
 ;; IMMUT-NEXT:  (drop
 ;; IMMUT-NEXT:   (call $func-A)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $call
  (drop
   ;; This cannot be turned into a direct call due to the table.init, unless we
   ;; assume initial contents are immutable.
   (call_indirect (type $i32)
    (i32.const 0)
   )
  )
 )
)

;; The elem's offset is way out of bounds, which we should not error on, and do
;; nothing otherwise.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 (table 10 10 funcref)

 (elem (i32.const -1) $0)

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

 ;; CHECK:      (elem $0 (i32.const -1) $0)

 ;; CHECK:      (func $0 (type $v)
 ;; CHECK-NEXT:  (call_indirect $0 (type $v)
 ;; CHECK-NEXT:   (i32.const -1)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (table $0 10 10 funcref)

 ;; IMMUT:      (elem $0 (i32.const -1) $0)

 ;; IMMUT:      (func $0 (type $v)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $v)
 ;; IMMUT-NEXT:   (i32.const -1)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $0
  (call_indirect (type $v)
   (i32.const -1)
  )
 )
)

;; Another elem offset that is way out of bounds.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 (table 10 10 funcref)

 (elem (i32.const -2) $0)

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

 ;; CHECK:      (elem $0 (i32.const -2) $0)

 ;; CHECK:      (func $0 (type $v)
 ;; CHECK-NEXT:  (call_indirect $0 (type $v)
 ;; CHECK-NEXT:   (i32.const -2)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (table $0 10 10 funcref)

 ;; IMMUT:      (elem $0 (i32.const -2) $0)

 ;; IMMUT:      (func $0 (type $v)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $v)
 ;; IMMUT-NEXT:   (i32.const -2)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $0
  (call_indirect (type $v)
   (i32.const -2)
  )
 )
)

;; The elem is just out of bounds due to its offset.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 (table 10 10 funcref)

 (elem (i32.const 10) $0)

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

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

 ;; CHECK:      (func $0 (type $v)
 ;; CHECK-NEXT:  (call_indirect $0 (type $v)
 ;; CHECK-NEXT:   (i32.const 10)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (table $0 10 10 funcref)

 ;; IMMUT:      (elem $0 (i32.const 10) $0)

 ;; IMMUT:      (func $0 (type $v)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $v)
 ;; IMMUT-NEXT:   (i32.const 10)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $0
  (call_indirect (type $v)
   (i32.const 10)
  )
 )
)

;; The elem is just out of bounds due to its length.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 (table 10 10 funcref)

 (elem (i32.const 9) $0 $0)

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

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

 ;; CHECK:      (func $0 (type $v)
 ;; CHECK-NEXT:  (call_indirect $0 (type $v)
 ;; CHECK-NEXT:   (i32.const 9)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; IMMUT:      (table $0 10 10 funcref)

 ;; IMMUT:      (elem $0 (i32.const 9) $0 $0)

 ;; IMMUT:      (func $0 (type $v)
 ;; IMMUT-NEXT:  (call_indirect $0 (type $v)
 ;; IMMUT-NEXT:   (i32.const 9)
 ;; IMMUT-NEXT:  )
 ;; IMMUT-NEXT: )
 (func $0
  (call_indirect (type $v)
   ;; We could in theory optimize this, as the out of bounds part is after us,
   ;; but the wasm traps anyhow, so leave it alone.
   (i32.const 9)
  )
 )
)

;; The elem is ok, and we can optimize.
(module
 ;; CHECK:      (type $v (func))
 ;; IMMUT:      (type $v (func))
 (type $v (func))

 (table 10 10 funcref)

 (elem (i32.const 9) $0)

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

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

 ;; CHECK:      (func $0 (type $v)
 ;; CHECK-NEXT:  (call $0)
 ;; CHECK-NEXT: )
 ;; IMMUT:      (table $0 10 10 funcref)

 ;; IMMUT:      (elem $0 (i32.const 9) $0)

 ;; IMMUT:      (func $0 (type $v)
 ;; IMMUT-NEXT:  (call $0)
 ;; IMMUT-NEXT: )
 (func $0
  (call_indirect (type $v)
   (i32.const 9)
  )
 )
)
