;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. ;; RUN: foreach %s %t wasm-opt --remove-unused-brs --all-features -S -o - | filecheck %s (module ;; CHECK: (type $vector (array (mut i32))) (type $vector (array (mut i32))) ;; CHECK: (type $struct (struct (field (ref null $vector)))) (type $struct (struct (field (ref null $vector)))) ;; CHECK: (type $2 (func (param i32))) ;; CHECK: (type $3 (func (result (ref null $struct)))) ;; CHECK: (type $4 (func (result f64))) ;; CHECK: (type $5 (func (result i32))) ;; CHECK: (type $6 (func (param i32) (result funcref))) ;; CHECK: (type $7 (func (param funcref))) ;; CHECK: (type $8 (func)) ;; CHECK: (type $9 (func (param funcref) (result funcref))) ;; CHECK: (type $10 (func (result funcref))) ;; CHECK: (import "out" "log" (func $log (type $2) (param i32))) (import "out" "log" (func $log (param i32))) ;; CHECK: (elem declare func $br_on_non_null $br_on_null $i32_=>_none $none_=>_i32) ;; CHECK: (func $foo (type $3) (result (ref null $struct)) ;; CHECK-NEXT: (if (result (ref null $struct)) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (struct.new $struct ;; CHECK-NEXT: (array.new_default $vector ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $foo (result (ref null $struct)) (if (result (ref null $struct)) (i32.const 1) (then (struct.new $struct ;; regression test for computing the cost of an array.new_default, which ;; lacks the optional field "init" (array.new_default $vector (i32.const 1) ) ) ) (else (ref.null $struct) ) ) ) ;; CHECK: (func $test-prefinalize (type $4) (result f64) ;; CHECK-NEXT: (loop $loop (result f64) ;; CHECK-NEXT: (if (result f64) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (f64.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else ;; CHECK-NEXT: (block $block (result f64) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (br_if $loop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test-prefinalize (result f64) (loop $loop (result f64) (block $block (result f64) (drop (br_if $block (f64.const 0) (i32.const 1) ) ) (if (i32.const 0) (then (unreachable) ) ) ;; this will be moved from $block into the if right before it. we must be ;; careful to properly finalize() things, as if we finalize the block too ;; early - before the if - then the block ends in a none type, which is ;; invalid. (br $loop) ) ) ) ;; CHECK: (func $none_=>_i32 (type $5) (result i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $none_=>_i32 (result i32) (unreachable) ) ;; CHECK: (func $i32_=>_none (type $2) (param $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $i32_=>_none (param i32) ) ;; CHECK: (func $selectify (type $6) (param $x i32) (result funcref) ;; CHECK-NEXT: (select (result funcref) ;; CHECK-NEXT: (ref.func $none_=>_i32) ;; CHECK-NEXT: (ref.func $i32_=>_none) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $selectify (param $x i32) (result funcref) ;; this if has arms with different function types, for which funcref is the ;; LUB (if (result funcref) (local.get $x) (then (ref.func $none_=>_i32) ) (else (ref.func $i32_=>_none) ) ) ) ;; CHECK: (func $br_on_null (type $7) (param $x funcref) ;; CHECK-NEXT: (block $null ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.func $br_on_null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (br_on_null $null ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $br_on_null (param $x funcref) (block $null ;; A null reference to bottom is definitely null, and the br is always taken. (drop (br_on_null $null (ref.null nofunc)) ) ;; On the other hand, if we know the input is not null, the branch will never ;; be taken. (drop (br_on_null $null (ref.func $br_on_null)) ) ;; If we don't know whether the input is null, we can't optimize. (drop (br_on_null $null (local.get $x)) ) ) ) ;; CHECK: (func $br_on_null-fallthrough (type $8) ;; CHECK-NEXT: (local $x funcref) ;; CHECK-NEXT: (block $null ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (ref.func $br_on_null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $br_on_null-fallthrough ;; This is the same as above, but now the necessary type information comes ;; from fallthrough values. (local $x funcref) (block $null ;; Definitely taken. (drop (br_on_null $null (local.tee $x (ref.null nofunc))) ) ;; Definitely not taken. Optimizable, but still requires a cast for validity. (drop (br_on_null $null (local.tee $x (ref.func $br_on_null))) ) ) ) ;; CHECK: (func $br_on_non_null (type $9) (param $x funcref) (result funcref) ;; CHECK-NEXT: (block $non-null (result (ref func)) ;; CHECK-NEXT: (br $non-null ;; CHECK-NEXT: (ref.func $br_on_non_null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br_on_non_null $non-null ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $br_on_non_null (param $x funcref) (result funcref) (block $non-null (result (ref func)) ;; A non-null reference is not null, and the br is always taken. (br_on_non_null $non-null (ref.func $br_on_non_null) ) ;; On the other hand, if we know the input is null, the branch will never be ;; taken. (br_on_non_null $non-null (ref.null nofunc) ) ;; If we don't know whether the input is null, we can't optimize. (br_on_non_null $non-null (local.get $x) ) (unreachable) ) ) ;; CHECK: (func $br_on_non_null-fallthrough (type $10) (result funcref) ;; CHECK-NEXT: (local $x funcref) ;; CHECK-NEXT: (block $non-null (result (ref func)) ;; CHECK-NEXT: (br $non-null ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (ref.func $br_on_non_null) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (ref.null nofunc) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $br_on_non_null-fallthrough (result funcref) ;; Same as above, but now using fallthrough values. (local $x funcref) (block $non-null (result (ref func)) ;; Definitely taken. Requires cast. (br_on_non_null $non-null (local.tee $x (ref.func $br_on_non_null)) ) ;; Definitely not taken. (br_on_non_null $non-null (local.tee $x (ref.null nofunc)) ) (unreachable) ) ) )