blob: 7a620193e0149b0988ed74e1c323cdef446e45fd [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --remove-unused-brs -all -S -o - \
;; RUN: | filecheck %s
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct )))
(type $struct (sub (struct)))
;; CHECK: (type $struct2 (struct ))
(type $struct2 (struct))
;; CHECK: (type $substruct (sub $struct (struct )))
(type $substruct (sub $struct (struct)))
)
;; CHECK: (func $br_on-if (type $7) (param $0 (ref struct))
;; CHECK-NEXT: (block $label
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select (result (ref struct))
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on-if (param $0 (ref struct))
(block $label
(drop
;; This br is never taken, as the input is non-nullable, so we can remove
;; it. When we do so, we replace it with the if. We should not rescan that
;; if, which has already been walked, as that would hit an assertion.
;;
(br_on_null $label
;; This if can also be turned into a select, separately from the above
;; (that is not specifically intended to be tested here).
(if (result (ref struct))
(i32.const 0)
(local.get $0)
(local.get $0)
)
)
)
)
)
;; CHECK: (func $br_on_cast (type $4) (result (ref $struct))
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (block $block (result (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (br_on_non_null $block
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast $block (ref $struct) (ref $substruct)
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast (result (ref $struct))
(local $struct (ref null $struct))
(block $block (result (ref $struct))
(drop
;; This cast can be computed at compile time: it will definitely be taken,
;; so we can turn it into a normal br.
(br_on_cast $block anyref (ref $struct)
(struct.new $struct)
)
)
(drop
;; This cast can be partially computed at compile time, but we still need to
;; do a null check.
(br_on_cast $block anyref (ref $struct)
(local.get $struct)
)
)
(drop
;; This cast cannot be optimized at all.
(br_on_cast $block anyref (ref $substruct)
(struct.new $struct)
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast-fallthrough (type $4) (result (ref $struct))
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (block $block (result (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (ref.cast (ref $struct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (br_on_non_null $block
;; CHECK-NEXT: (ref.cast (ref null $struct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast $block anyref (ref $substruct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast-fallthrough (result (ref $struct))
;; Same as above, but now the type information comes from fallthrough values.
(local $struct (ref null $struct))
(local $any anyref)
(block $block (result (ref $struct))
(drop
;; Definitely taken, but will need a cast for validity.
(br_on_cast $block anyref (ref $struct)
(local.tee $any (struct.new $struct))
)
)
(drop
;; Needs a null check and cast for validity.
(br_on_cast $block anyref (ref $struct)
(local.tee $any (local.get $struct))
)
)
(drop
;; This cannot be optimized, but at least it still doesn't need an
;; additional cast.
(br_on_cast $block anyref (ref $substruct)
(local.tee $any (struct.new $struct))
)
)
(unreachable)
)
)
;; CHECK: (func $nested_br_on_cast (type $8) (result i31ref)
;; CHECK-NEXT: (block $label$1 (result (ref i31))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $label$1
;; CHECK-NEXT: (ref.i31
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $nested_br_on_cast (result i31ref)
(block $label$1 (result i31ref)
(drop
;; The inner br_on_cast will become a direct br since the type proves it
;; is in fact i31. That then becomes unreachable, and the parent must
;; handle that properly (do nothing without hitting an assertion).
(br_on_cast $label$1 (ref any) (ref i31)
(br_on_cast $label$1 (ref any) (ref i31)
(ref.i31 (i32.const 0))
)
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_unrelated (type $5) (result (ref null $struct))
;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
;; CHECK-NEXT: (block $block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast $block (ref null $struct2) nullref
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_unrelated (result (ref null $struct))
(local $nullable-struct2 (ref null $struct2))
(block $block (result (ref null $struct))
(drop
;; This cast can be computed at compile time: it will definitely fail, so we
;; can remove it.
(br_on_cast $block anyref (ref $struct)
(struct.new $struct2)
)
)
(drop
;; We can still remove it even if the cast allows nulls.
(br_on_cast $block anyref (ref null $struct)
(struct.new $struct2)
)
)
(drop
;; Or if the cast does not allow nulls and the value is nullable.
(br_on_cast $block anyref (ref $struct)
(local.get $nullable-struct2)
)
)
(drop
;; But if both are nullable, then the cast will succeed only if the value is
;; null, so we can partially optimize.
;; TODO: Optimize this.
(br_on_cast $block anyref (ref null $struct)
(local.get $nullable-struct2)
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_unrelated-fallthrough (type $5) (result (ref null $struct))
;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
;; CHECK-NEXT: (block $block (result nullref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast $block anyref nullref
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_unrelated-fallthrough (result (ref null $struct))
;; Same as above, but now all the type information comes from fallthrough values.
(local $any anyref)
(local $nullable-struct2 (ref null $struct2))
(block $block (result (ref null $struct))
(drop
;; Definitely not taken.
(br_on_cast $block anyref (ref $struct)
(local.tee $any (struct.new $struct2))
)
)
(drop
;; Still not taken.
(br_on_cast $block anyref (ref null $struct)
(local.tee $any (struct.new $struct2))
)
)
(drop
;; Also not taken.
(br_on_cast $block anyref (ref $struct)
(local.tee $any (local.get $nullable-struct2))
)
)
(drop
;; Taken only if null.
;; TODO: Optimize this.
(br_on_cast $block anyref (ref null $struct)
(local.tee $any (local.get $nullable-struct2))
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_fail (type $3) (result anyref)
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (block $block (result (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast_fail $block (ref null $struct) (ref $struct)
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast_fail $block (ref $struct) (ref $substruct)
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_fail (result anyref)
(local $struct (ref null $struct))
(block $block (result anyref)
(drop
;; This cast can be computed at compile time: it will definitely succeed so
;; the branch will not be taken.
(br_on_cast_fail $block anyref (ref $struct)
(struct.new $struct)
)
)
(drop
;; This cast can be partially computed at compile time, but we still need to
;; do a null check.
;; TODO: optimize this.
(br_on_cast_fail $block anyref (ref $struct)
(local.get $struct)
)
)
(drop
;; This cast cannot be optimized at all.
(br_on_cast_fail $block anyref (ref $substruct)
(struct.new $struct)
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_fail-fallthrough (type $3) (result anyref)
;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (block $block (result anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast (ref $struct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast_fail $block anyref (ref $struct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br_on_cast_fail $block anyref (ref $substruct)
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_fail-fallthrough (result anyref)
;; Same as above, but now the type information comes from fallthrough values.
(local $any anyref)
(local $struct (ref null $struct))
(block $block (result anyref)
(drop
;; This cast will succeed. We will need a cast for validity.
(br_on_cast_fail $block anyref (ref $struct)
(local.tee $any (struct.new $struct))
)
)
(drop
;; We will still need a null check.
;; TODO: optimize this.
(br_on_cast_fail $block anyref (ref $struct)
(local.tee $any (local.get $struct))
)
)
(drop
;; This cast cannot be optimized at all.
(br_on_cast_fail $block anyref (ref $substruct)
(local.tee $any (struct.new $struct))
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_fail_unrelated (type $3) (result anyref)
;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
;; CHECK-NEXT: (block $block (result (ref null $struct2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (br_on_non_null $block
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_fail_unrelated (result anyref)
(local $nullable-struct2 (ref null $struct2))
(block $block (result anyref)
(drop
;; This cast can be computed at compile time: it will definitely fail, so we
;; can replace it with an unconditional br.
(br_on_cast_fail $block anyref (ref $struct)
(struct.new $struct2)
)
)
(drop
;; We can still replace it even if the cast allows nulls.
(br_on_cast_fail $block anyref (ref null $struct)
(struct.new $struct2)
)
)
(drop
;; Or if the cast does not allow nulls and the value is nullable.
(br_on_cast_fail $block anyref (ref $struct)
(local.get $nullable-struct2)
)
)
(drop
;; But if both are nullable, then we can only partially optimize because we
;; still have to do a null check.
(br_on_cast_fail $block anyref (ref null $struct)
(local.get $nullable-struct2)
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast_fail_unrelated-fallthrough (type $3) (result anyref)
;; CHECK-NEXT: (local $any anyref)
;; CHECK-NEXT: (local $nullable-struct2 (ref null $struct2))
;; CHECK-NEXT: (block $block (result anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (struct.new_default $struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (br_on_non_null $block
;; CHECK-NEXT: (local.tee $any
;; CHECK-NEXT: (local.get $nullable-struct2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_fail_unrelated-fallthrough (result anyref)
;; Same as above, but now type information comes from fallthrough values.
(local $any anyref)
(local $nullable-struct2 (ref null $struct2))
(block $block (result anyref)
(drop
;; Will definitely take the branch.
(br_on_cast_fail $block anyref (ref $struct)
(local.tee $any (struct.new $struct2))
)
)
(drop
;; Ditto.
(br_on_cast_fail $block anyref (ref null $struct)
(local.tee $any (struct.new $struct2))
)
)
(drop
;; Ditto.
(br_on_cast_fail $block anyref (ref $struct)
(local.tee $any (local.get $nullable-struct2))
)
)
(drop
;; Still has to do a null check.
(br_on_cast_fail $block anyref (ref null $struct)
(local.tee $any (local.get $nullable-struct2))
)
)
(unreachable)
)
)
;; CHECK: (func $br_on_cast-unreachable (type $6) (param $i31ref i31ref) (result anyref)
;; CHECK-NEXT: (block $block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (ref.cast (ref none)
;; CHECK-NEXT: (block (result i31ref)
;; CHECK-NEXT: (local.get $i31ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (ref.cast (ref none)
;; CHECK-NEXT: (block (result i31ref)
;; CHECK-NEXT: (local.get $i31ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast-unreachable (param $i31ref i31ref) (result anyref)
;; Optimize out br_on_cast* where the input is uninhabitable.
(block $block (result anyref)
(drop
(br_on_cast $block anyref (ref i31)
(block (result anyref)
(ref.cast (ref struct)
(block (result anyref)
(local.get $i31ref)
)
)
)
)
)
(br_on_cast_fail $block anyref (ref i31)
(block (result anyref)
(ref.cast (ref struct)
(block (result anyref)
(local.get $i31ref)
)
)
)
)
)
)
;; CHECK: (func $fallthrough-unreachable (type $6) (param $0 i31ref) (result anyref)
;; CHECK-NEXT: (block $outer
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast (ref none)
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $fallthrough-unreachable (param $0 i31ref) (result anyref)
(block $outer (result (ref none))
(drop
;; This should not crash due to the new unreachable below.
(br_on_cast $outer (ref none) (ref none)
(ref.cast (ref none)
;; This will be optimized to a drop + unreachable.
(br_on_cast $outer (ref none) (ref none)
(ref.cast (ref none)
(local.get $0)
)
)
)
)
)
(unreachable)
)
)
;; CHECK: (func $casts-are-costly (type $9) (param $x i32)
;; CHECK-NEXT: (local $struct (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (ref.test (ref none)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (if (result nullref)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (ref.cast nullref
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (if (result (ref null $struct))
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (block $something (result (ref null $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (br_on_non_null $something
;; CHECK-NEXT: (local.get $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (select (result nullref)
;; CHECK-NEXT: (block (result nullref)
;; CHECK-NEXT: (block $nothing
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $nothing)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $casts-are-costly (param $x i32)
;; We never turn an if into a select if an arm has a cast of any kind, as
;; those things involve branches internally, so we'd be adding more than we
;; save.
(local $struct (ref null $struct))
(drop
(if (result i32)
(local.get $x)
(ref.test (ref $struct)
(ref.null any)
)
(i32.const 0)
)
)
(drop
(if (result anyref)
(local.get $x)
(ref.null any)
(ref.cast (ref null $struct)
(ref.null any)
)
)
)
;; We do not selectify here because the amount of work in the if is
;; significant (there is a cast and a branch).
(drop
(if (result anyref)
(local.get $x)
(block (result anyref)
(block $something (result anyref)
(drop
(br_on_cast $something anyref (ref $struct)
(local.get $struct)
)
)
(ref.null any)
)
)
(ref.null any)
)
)
;; However, null checks are fairly fast, and we will emit a select here.
(drop
(if (result anyref)
(local.get $x)
(block (result anyref)
(block $nothing
(drop
(br_on_null $nothing
(ref.null $struct)
)
)
)
(ref.null any)
)
(ref.null any)
)
)
)
)