| ;; 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) |
| (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) |
| ;; 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 $i32_=>_i32 (func (param i32) (result i32))) |
| |
| ;; CHECK: (table $0 5 5 funcref) |
| ;; IMMUT: (type $i32_=>_i32 (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 $i32_=>_i32) (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 $i32_=>_i32) (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))) |
| ;; 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) |
| (global $g (import "env" "g") i32) |
| (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))) |
| ;; 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) |
| (global $g (import "env" "g") i32) |
| (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 $i32_i32_i32_=>_none (func (param i32 i32 i32))) |
| |
| ;; CHECK: (table $0 5 5 funcref) |
| ;; IMMUT: (type $i32_i32_i32_=>_none (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_=>_none (func (param i32))) |
| |
| ;; CHECK: (type $ii (func (param i32 i32))) |
| ;; IMMUT: (type $i32_=>_none (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 $i32_=>_none) (param $0 i32) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (elem $0 (i32.const 1) $foo) |
| |
| ;; IMMUT: (func $foo (type $i32_=>_none) (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 $i32_=>_none (func (param i32))) |
| |
| ;; CHECK: (func $foo (type $i32_=>_none) (param $0 i32) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (type $i32_=>_none (func (param i32))) |
| |
| ;; IMMUT: (func $foo (type $i32_=>_none) (param $0 i32) |
| ;; IMMUT-NEXT: (unreachable) |
| ;; IMMUT-NEXT: ) |
| (func $foo (param i32) |
| (unreachable) |
| ) |
| ) |
| |
| ;; change types |
| (module |
| (type (func)) |
| ;; CHECK: (type $none_=>_none (func)) |
| |
| ;; CHECK: (table $0 8 8 funcref) |
| ;; IMMUT: (type $none_=>_none (func)) |
| |
| ;; IMMUT: (table $0 8 8 funcref) |
| (table $0 8 8 funcref) |
| ;; CHECK: (func $0 (type $none_=>_none) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $0 (type $none_=>_none) |
| ;; 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 $i32_i32_i32_=>_none (func (param i32 i32 i32))) |
| |
| ;; CHECK: (type $ii (func (param i32 i32))) |
| ;; IMMUT: (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) |
| |
| ;; IMMUT: (type $ii (func (param i32 i32))) |
| (type $ii (func (param i32 i32))) |
| |
| (type $none (func)) |
| |
| ;; CHECK: (type $i32_=>_none (func (param i32))) |
| |
| ;; CHECK: (table $0 5 5 funcref) |
| ;; IMMUT: (type $i32_=>_none (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 $i32_i32_i32_=>_none) (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: (call $foo1 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $foo2 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $select (type $i32_i32_i32_=>_none) (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: (call $foo1 |
| ;; IMMUT-NEXT: (local.get $3) |
| ;; IMMUT-NEXT: (local.get $4) |
| ;; IMMUT-NEXT: ) |
| ;; IMMUT-NEXT: (call $foo2 |
| ;; IMMUT-NEXT: (local.get $3) |
| ;; IMMUT-NEXT: (local.get $4) |
| ;; 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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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: (unreachable) |
| ;; CHECK-NEXT: (call $foo2 |
| ;; CHECK-NEXT: (local.get $3) |
| ;; CHECK-NEXT: (local.get $4) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $select-out-of-range (type $i32_i32_i32_=>_none) (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: (unreachable) |
| ;; IMMUT-NEXT: (call $foo2 |
| ;; IMMUT-NEXT: (local.get $3) |
| ;; IMMUT-NEXT: (local.get $4) |
| ;; 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 $i32_i32_i32_=>_none) (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: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $select-both-out-of-range (type $i32_i32_i32_=>_none) (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: (unreachable) |
| ;; IMMUT-NEXT: (unreachable) |
| ;; 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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_i32_i32_=>_none) (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 $i32_=>_none) (param $z i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $select-bad-type (type $i32_=>_none) (param $z i32) |
| ;; IMMUT-NEXT: (if |
| ;; IMMUT-NEXT: (local.get $z) |
| ;; IMMUT-NEXT: (unreachable) |
| ;; IMMUT-NEXT: (unreachable) |
| ;; 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 $i32_=>_none (func (param i32))) |
| |
| ;; CHECK: (type $none_=>_none (func)) |
| |
| ;; CHECK: (table $0 15 15 funcref) |
| ;; IMMUT: (type $F (func (param (ref func)))) |
| |
| ;; IMMUT: (type $i32_=>_none (func (param i32))) |
| |
| ;; IMMUT: (type $none_=>_none (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 $i32_=>_none) (param $x i32) |
| ;; CHECK-NEXT: (local $1 (ref $i32_=>_none)) |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (ref.func $select-non-nullable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (call $foo-ref |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $foo-ref |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; IMMUT: (func $select-non-nullable (type $i32_=>_none) (param $x i32) |
| ;; IMMUT-NEXT: (local $1 (ref $i32_=>_none)) |
| ;; IMMUT-NEXT: (local.set $1 |
| ;; IMMUT-NEXT: (ref.func $select-non-nullable) |
| ;; IMMUT-NEXT: ) |
| ;; IMMUT-NEXT: (if |
| ;; IMMUT-NEXT: (local.get $x) |
| ;; IMMUT-NEXT: (call $foo-ref |
| ;; IMMUT-NEXT: (local.get $1) |
| ;; IMMUT-NEXT: ) |
| ;; IMMUT-NEXT: (call $foo-ref |
| ;; IMMUT-NEXT: (local.get $1) |
| ;; 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 $none_=>_none) |
| ;; 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 $none_=>_none) |
| ;; 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 $none_=>_none) |
| ;; 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 $none_=>_none) |
| ;; 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 $i32_=>_none) (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 $i32_=>_none) (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) $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) $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) |
| ) |
| ) |
| ) |