blob: 074558d95ae59246c40886bc29b5c32f130e1a06 [file] [log] [blame] [edit]
;; 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 $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 $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)
)
)
)