;; 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 $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) ) ) ) ) ;; 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) ) ) )