blob: 728f60401de7a074e511e471d28dfc0984ec4195 [file]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --strip-refining-casts --traps-never-happen -all -S -o - | filecheck %s --check-prefix TNH
;; RUN: wasm-opt %s --strip-refining-casts -all -S -o - | filecheck %s --check-prefix NO_TNH
(module
;; TNH: (type $Super (sub (struct (field i32))))
;; NO_TNH: (type $Super (sub (struct (field i32))))
(type $Super (sub (struct (field i32))))
;; TNH: (type $Sub (sub $Super (struct (field i32) (field i64))))
;; NO_TNH: (type $Sub (sub $Super (struct (field i32) (field i64))))
(type $Sub (sub $Super (struct (field i32) (field i64))))
;; TNH: (type $Sig (func (param (ref $Super))))
;; TNH: (type $Container (sub (struct (field (mut (ref null $Super))))))
;; TNH: (type $Arr (sub (array (mut (ref null $Super)))))
;; TNH: (type $SubSub (sub $Sub (struct (field i32) (field i64) (field f32))))
;; NO_TNH: (type $Sig (func (param (ref $Super))))
;; NO_TNH: (type $Container (sub (struct (field (mut (ref null $Super))))))
;; NO_TNH: (type $Arr (sub (array (mut (ref null $Super)))))
;; NO_TNH: (type $SubSub (sub $Sub (struct (field i32) (field i64) (field f32))))
(type $SubSub (sub $Sub (struct (field i32) (field i64) (field f32))))
;; NO_TNH: (rec
;; NO_TNH-NEXT: (type $Described (sub (descriptor $Desc) (struct)))
;; NO_TNH: (type $Desc (sub (describes $Described) (struct)))
;; NO_TNH: (type $Sub2 (sub $Super (struct (field i32) (field f64))))
(type $Sub2 (sub $Super (struct (field i32) (field f64))))
(type $Container (sub (struct (field (mut (ref null $Super))))))
(type $Arr (sub (array (mut (ref null $Super)))))
(type $Sig (func (param (ref $Super))))
(rec
;; TNH: (rec
;; TNH-NEXT: (type $Described (sub (descriptor $Desc) (struct)))
(type $Described (sub (descriptor $Desc) (struct)))
;; TNH: (type $Desc (sub (describes $Described) (struct)))
(type $Desc (sub (describes $Described) (struct)))
)
(import "binaryen-intrinsics" "call.without.effects"
(func $cwe (param (ref $Super)) (param funcref)))
;; TNH: (global $g (mut (ref null $Super)) (ref.null none))
;; NO_TNH: (global $g (mut (ref null $Super)) (ref.null none))
(global $g (mut (ref null $Super)) (ref.null $Super))
;; TNH: (global $g-any (mut anyref) (ref.null none))
;; NO_TNH: (global $g-any (mut anyref) (ref.null none))
(global $g-any (mut anyref) (ref.null any))
;; TNH: (table $t 1 1 funcref)
;; NO_TNH: (table $t 1 1 funcref)
(table $t 1 1 funcref)
;; TNH: (func $callee (type $Sig) (param $s (ref $Super))
;; TNH-NEXT: (nop)
;; TNH-NEXT: )
;; NO_TNH: (func $callee (type $Sig) (param $s (ref $Super))
;; NO_TNH-NEXT: (nop)
;; NO_TNH-NEXT: )
(func $callee (param $s (ref $Super))
(nop)
)
;; TNH: (func $target-sub (type $9) (param $s (ref $Sub))
;; TNH-NEXT: (nop)
;; TNH-NEXT: )
;; NO_TNH: (func $target-sub (type $9) (param $s (ref $Sub))
;; NO_TNH-NEXT: (nop)
;; NO_TNH-NEXT: )
(func $target-sub (param $s (ref $Sub))
(nop)
)
;; TNH: (func $call (type $10) (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
;; TNH-NEXT: (call $callee
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (call $callee
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (call $callee
;; TNH-NEXT: (ref.as_non_null
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (call $callee
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (call $cwe
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (ref.func $target-sub)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $call (type $10) (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
;; NO_TNH-NEXT: (call $callee
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call $callee
;; NO_TNH-NEXT: (ref.cast (ref $SubSub)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call $callee
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call $callee
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call $cwe
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (ref.func $target-sub)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $call (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
;; The operand $x already satisfies the parameter type (ref $Super), so the
;; downcast to $Sub can be skipped in TNH mode.
(call $callee
(ref.cast (ref $Sub)
(local.get $x)
)
)
;; Chained casts where the innermost operand already satisfies the parameter
;; type are all stripped.
(call $callee
(ref.cast (ref $SubSub)
(ref.cast (ref $Sub)
(local.get $x)
)
)
)
;; Here $ns is nullable while the parameter requires (ref $Super), so the
;; outer cast to (ref $Sub) is stripped down to (ref.as_non_null $ns), which
;; is kept because $ns does not satisfy (ref $Super).
(call $callee
(ref.cast (ref $Sub)
(ref.as_non_null
(local.get $ns)
)
)
)
;; Here $any is not a subtype of (ref $Super), so the cast must be kept.
(call $callee
(ref.cast (ref $Sub)
(local.get $any)
)
)
;; Arguments of call.without.effects must match the parameters of the actual
;; target, not the intrinsic import, so casts on them must be kept.
(call $cwe
(ref.cast (ref $Sub)
(local.get $x)
)
(ref.func $target-sub)
)
)
;; TNH: (func $call-ref (type $11) (param $fn (ref $Sig)) (param $x (ref $Super)) (param $any anyref)
;; TNH-NEXT: (call_ref $Sig
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: (local.get $fn)
;; TNH-NEXT: )
;; TNH-NEXT: (call_ref $Sig
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $fn)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $call-ref (type $11) (param $fn (ref $Sig)) (param $x (ref $Super)) (param $any anyref)
;; NO_TNH-NEXT: (call_ref $Sig
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $fn)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call_ref $Sig
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $fn)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $call-ref (param $fn (ref $Sig)) (param $x (ref $Super)) (param $any anyref)
(call_ref $Sig
(ref.cast (ref $Sub)
(local.get $x)
)
(local.get $fn)
)
(call_ref $Sig
(ref.cast (ref $Sub)
(local.get $any)
)
(local.get $fn)
)
)
;; TNH: (func $call-indirect (type $12) (param $x (ref $Super)) (param $any anyref)
;; TNH-NEXT: (call_indirect $t (type $Sig)
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: (i32.const 0)
;; TNH-NEXT: )
;; TNH-NEXT: (call_indirect $t (type $Sig)
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: (i32.const 0)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $call-indirect (type $12) (param $x (ref $Super)) (param $any anyref)
;; NO_TNH-NEXT: (call_indirect $t (type $Sig)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (i32.const 0)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (call_indirect $t (type $Sig)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (i32.const 0)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $call-indirect (param $x (ref $Super)) (param $any anyref)
(call_indirect $t (type $Sig)
(ref.cast (ref $Sub)
(local.get $x)
)
(i32.const 0)
)
(call_indirect $t (type $Sig)
(ref.cast (ref $Sub)
(local.get $any)
)
(i32.const 0)
)
)
;; TNH: (func $return-cast (type $13) (param $x (ref $Super)) (result (ref $Super))
;; TNH-NEXT: (return
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $return-cast (type $13) (param $x (ref $Super)) (result (ref $Super))
;; NO_TNH-NEXT: (return
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $return-cast (param $x (ref $Super)) (result (ref $Super))
(return
(ref.cast (ref $Sub)
(local.get $x)
)
)
)
;; TNH: (func $return-keep (type $14) (param $any anyref) (result (ref $Super))
;; TNH-NEXT: (return
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $return-keep (type $14) (param $any anyref) (result (ref $Super))
;; NO_TNH-NEXT: (return
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $return-keep (param $any anyref) (result (ref $Super))
(return
(ref.cast (ref $Sub)
(local.get $any)
)
)
)
;; TNH: (func $local-and-global-set (type $15) (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref) (param $ext externref)
;; TNH-NEXT: (local $l (ref null $Super))
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (local.tee $l
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (global.set $g
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (global.set $g
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: (global.set $g-any
;; TNH-NEXT: (any.convert_extern
;; TNH-NEXT: (local.get $ext)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $local-and-global-set (type $15) (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref) (param $ext externref)
;; NO_TNH-NEXT: (local $l (ref null $Super))
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (local.tee $l
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (global.set $g
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (global.set $g
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (global.set $g-any
;; NO_TNH-NEXT: (any.convert_extern
;; NO_TNH-NEXT: (local.get $ext)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $local-and-global-set (param $x (ref $Super)) (param $ns (ref null $Super)) (param $any anyref) (param $ext externref)
(local $l (ref null $Super))
(local.set $l
(ref.cast (ref $Sub)
(local.get $x)
)
)
(local.set $l
(ref.as_non_null
(local.get $ns)
)
)
;; In Binaryen IR, local.tee has the type of the local, so its cast is also
;; removed.
(drop
(local.tee $l
(ref.cast (ref $Sub)
(local.get $x)
)
)
)
;; An unreachable cast is replaced with its unreachable operand.
(local.set $l
(ref.as_non_null
(unreachable)
)
)
;; Non-subtype operand: cast must be kept.
(local.set $l
(ref.cast (ref $Sub)
(local.get $any)
)
)
(global.set $g
(ref.cast (ref $Sub)
(local.get $x)
)
)
(global.set $g
(ref.as_non_null
(local.get $ns)
)
)
;; Non-RefAsNonNull operations like any.convert_extern must not be removed.
(global.set $g-any
(any.convert_extern
(local.get $ext)
)
)
)
;; TNH: (func $struct-and-array (type $16) (param $c (ref $Container)) (param $a (ref $Arr)) (param $s (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
;; TNH-NEXT: (struct.set $Container 0
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: )
;; TNH-NEXT: (struct.set $Container 0
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: (struct.set $Container 0
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (array.set $Arr
;; TNH-NEXT: (local.get $a)
;; TNH-NEXT: (i32.const 0)
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: )
;; TNH-NEXT: (array.set $Arr
;; TNH-NEXT: (local.get $a)
;; TNH-NEXT: (i32.const 0)
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (struct.new $Container
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (array.new $Arr
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: (i32.const 2)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (array.new_fixed $Arr 2
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $struct-and-array (type $16) (param $c (ref $Container)) (param $a (ref $Arr)) (param $s (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
;; NO_TNH-NEXT: (struct.set $Container 0
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (struct.set $Container 0
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (struct.set $Container 0
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (array.set $Arr
;; NO_TNH-NEXT: (local.get $a)
;; NO_TNH-NEXT: (i32.const 0)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (array.set $Arr
;; NO_TNH-NEXT: (local.get $a)
;; NO_TNH-NEXT: (i32.const 0)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (struct.new $Container
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (array.new $Arr
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (i32.const 2)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (array.new_fixed $Arr 2
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $struct-and-array (param $c (ref $Container)) (param $a (ref $Arr)) (param $s (ref $Super)) (param $ns (ref null $Super)) (param $any anyref)
(struct.set $Container 0
(local.get $c)
(ref.cast (ref $Sub)
(local.get $s)
)
)
(struct.set $Container 0
(local.get $c)
(ref.as_non_null
(local.get $ns)
)
)
(struct.set $Container 0
(local.get $c)
(ref.cast (ref $Sub)
(local.get $any)
)
)
(array.set $Arr
(local.get $a)
(i32.const 0)
(ref.cast (ref $Sub)
(local.get $s)
)
)
(array.set $Arr
(local.get $a)
(i32.const 0)
(ref.cast (ref $Sub)
(local.get $any)
)
)
(drop
(struct.new $Container
(ref.cast (ref $Sub)
(local.get $s)
)
)
)
(drop
(array.new $Arr
(ref.cast (ref $Sub)
(local.get $s)
)
(i32.const 2)
)
)
(drop
(array.new_fixed $Arr 2
(ref.cast (ref $Sub)
(local.get $s)
)
(ref.as_non_null
(local.get $ns)
)
)
)
)
;; TNH: (func $select (type $17) (param $cond i32) (param $a (ref $Super)) (param $b (ref $Super)) (param $any anyref) (result (ref $Super))
;; TNH-NEXT: (drop
;; TNH-NEXT: (select (result (ref $Super))
;; TNH-NEXT: (local.get $a)
;; TNH-NEXT: (local.get $b)
;; TNH-NEXT: (local.get $cond)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (select (result (ref $Super))
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $any)
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $b)
;; TNH-NEXT: (local.get $cond)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $select (type $17) (param $cond i32) (param $a (ref $Super)) (param $b (ref $Super)) (param $any anyref) (result (ref $Super))
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (select (result (ref $Super))
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $a)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (ref.cast (ref $Sub2)
;; NO_TNH-NEXT: (local.get $b)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $cond)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (select (result (ref $Super))
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $b)
;; NO_TNH-NEXT: (local.get $cond)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $select (param $cond i32) (param $a (ref $Super)) (param $b (ref $Super)) (param $any anyref) (result (ref $Super))
(drop
(select (result (ref $Super))
(ref.cast (ref $Sub)
(local.get $a)
)
(ref.cast (ref $Sub2)
(local.get $b)
)
(local.get $cond)
)
)
;; Here $any is not a subtype of (ref $Super), so its cast must be kept.
(select (result (ref $Super))
(ref.cast (ref $Sub)
(local.get $any)
)
(local.get $b)
(local.get $cond)
)
)
;; TNH: (func $ref-test (type $18) (param $s (ref $Super)) (param $ns (ref null $Super))
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref $SubSub)
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref $SubSub)
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref $SubSub)
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref $Super)
;; TNH-NEXT: (ref.as_non_null
;; TNH-NEXT: (local.get $ns)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref none)
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $s)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $ref-test (type $19) (param $s (ref $Super)) (param $ns (ref null $Super))
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref $SubSub)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref $SubSub)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref $SubSub)
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref $Super)
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $ns)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref none)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $s)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $ref-test (param $s (ref $Super)) (param $ns (ref null $Super))
;; Undecided ref.test: casts on the input do not affect the outcome and can
;; be removed.
(drop
(ref.test (ref $SubSub)
(ref.cast (ref $Sub)
(local.get $s)
)
)
)
(drop
(ref.test (ref $SubSub)
(ref.cast (ref $Sub)
(local.get $ns)
)
)
)
(drop
(ref.test (ref $SubSub)
(ref.as_non_null
(local.get $ns)
)
)
)
;; Decided ref.test (always 1 if non-trapping): the ref.as_non_null
;; determines the outcome, so it is left for OptimizeInstructions to
;; resolve.
(drop
(ref.test (ref $Super)
(ref.as_non_null
(local.get $ns)
)
)
)
;; Decided ref.test (always 0 if non-trapping): $Sub and $Sub2 are disjoint,
;; so the cast determines the outcome and is left for OptimizeInstructions.
(drop
(ref.test (ref $Sub2)
(ref.cast (ref $Sub)
(local.get $s)
)
)
)
)
;; TNH: (func $keep-descriptor-and-unreachable (type $19) (param $c i32) (param $x anyref) (param $d (ref $Desc))
;; TNH-NEXT: (local $l anyref)
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (local.set $l
;; TNH-NEXT: (ref.cast_desc_eq (ref $Described)
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: (block (result (ref $Desc))
;; TNH-NEXT: (call $callee
;; TNH-NEXT: (struct.new_default $Super)
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $d)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (block ;; (replaces unreachable StructNew we can't emit)
;; TNH-NEXT: (drop
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (block ;; (replaces unreachable ArrayNew we can't emit)
;; TNH-NEXT: (drop
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (i32.const 1)
;; TNH-NEXT: )
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (block ;; (replaces unreachable ArrayNewFixed we can't emit)
;; TNH-NEXT: (drop
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (select
;; TNH-NEXT: (ref.as_non_null
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (ref.test (ref $Super)
;; TNH-NEXT: (block ;; (replaces unreachable RefCast we can't emit)
;; TNH-NEXT: (drop
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: (unreachable)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; NO_TNH: (func $keep-descriptor-and-unreachable (type $20) (param $c i32) (param $x anyref) (param $d (ref $Desc))
;; NO_TNH-NEXT: (local $l anyref)
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.cast_desc_eq (ref $Described)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: (local.get $d)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.set $l
;; NO_TNH-NEXT: (ref.cast_desc_eq (ref $Described)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: (block (result (ref $Desc))
;; NO_TNH-NEXT: (call $callee
;; NO_TNH-NEXT: (struct.new_default $Super)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $d)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (block ;; (replaces unreachable StructNew we can't emit)
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (block ;; (replaces unreachable ArrayNew we can't emit)
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (i32.const 1)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (block ;; (replaces unreachable ArrayNewFixed we can't emit)
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (select
;; NO_TNH-NEXT: (ref.as_non_null
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.test (ref $Super)
;; NO_TNH-NEXT: (block ;; (replaces unreachable RefCast we can't emit)
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (unreachable)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $keep-descriptor-and-unreachable (param $c i32) (param $x anyref) (param $d (ref $Desc))
(local $l anyref)
;; Descriptor casts can be removed if the descriptor has no side effects.
(local.set $l
(ref.cast_desc_eq (ref $Described)
(local.get $x)
(local.get $d)
)
)
;; Descriptor casts must not be removed if the descriptor has side effects.
(local.set $l
(ref.cast_desc_eq (ref $Described)
(local.get $x)
(block (result (ref $Desc))
(call $callee
(struct.new_default $Super)
)
(local.get $d)
)
)
)
;; Unreachable expressions must be handled safely.
(drop
(struct.new $Container
(unreachable)
)
)
(drop
(array.new $Arr
(unreachable)
(i32.const 1)
)
)
(drop
(array.new_fixed $Arr 1
(unreachable)
)
)
(drop
(select (result anyref)
(ref.as_non_null
(local.get $x)
)
(unreachable)
(local.get $c)
)
)
(drop
(ref.test (ref $Super)
(ref.cast (ref any)
(unreachable)
)
)
)
)
;; TNH: (func $control-flow (type $20) (param $c i32) (param $x (ref $Super)) (param $sub (ref $Sub)) (result (ref $Super))
;; TNH-NEXT: (drop
;; TNH-NEXT: (if (result (ref $Super))
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: (then
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (else
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (try (result (ref $Super))
;; TNH-NEXT: (do
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (catch_all
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (block $b (result (ref $Super))
;; TNH-NEXT: (if
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: (then
;; TNH-NEXT: (br $b
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (br_if $b
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (drop
;; TNH-NEXT: (block $b-super (result (ref $Super))
;; TNH-NEXT: (block $b-sub (result (ref $Sub))
;; TNH-NEXT: (br_table $b-super $b-sub
;; TNH-NEXT: (ref.cast (ref $Sub)
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $c)
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: )
;; TNH-NEXT: (local.get $x)
;; TNH-NEXT: )
;; NO_TNH: (func $control-flow (type $21) (param $c i32) (param $x (ref $Super)) (param $sub (ref $Sub)) (result (ref $Super))
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (if (result (ref $Super))
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: (then
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (else
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (try (result (ref $Super))
;; NO_TNH-NEXT: (do
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (catch_all
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (block $b (result (ref $Super))
;; NO_TNH-NEXT: (if
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: (then
;; NO_TNH-NEXT: (br $b
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (br_if $b
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (block $b-super (result (ref $Super))
;; NO_TNH-NEXT: (block $b-sub (result (ref $Sub))
;; NO_TNH-NEXT: (br_table $b-super $b-sub
;; NO_TNH-NEXT: (ref.cast (ref $SubSub)
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (local.get $c)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: (ref.cast (ref $Sub)
;; NO_TNH-NEXT: (local.get $x)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
(func $control-flow (param $c i32) (param $x (ref $Super)) (param $sub (ref $Sub)) (result (ref $Super))
;; If and try-catch arms flowing into a supertype result can have their
;; casts stripped.
(drop
(if (result (ref $Super))
(local.get $c)
(then
(ref.cast (ref $Sub)
(local.get $x)
)
)
(else
(local.get $x)
)
)
)
(drop
(try (result (ref $Super))
(do
(ref.cast (ref $Sub)
(local.get $x)
)
)
(catch_all
(ref.cast (ref $Sub)
(local.get $x)
)
)
)
)
;; Unconditional br to a block with a supertype result strips its cast,
;; while br_if keeps its cast because the br_if's own result type in
;; Binaryen IR is the cast type (TODO: optimize through fallthrough
;; expressions like br_if).
(drop
(block $b (result (ref $Super))
(if
(local.get $c)
(then
(br $b
(ref.cast (ref $Sub)
(local.get $x)
)
)
)
)
(drop
(br_if $b
(ref.cast (ref $Sub)
(local.get $x)
)
(local.get $c)
)
)
(local.get $x)
)
)
;; br_table strips casts down to the greatest lower bound of all target
;; block types (here (ref $Sub)), keeping the cast to $Sub while stripping
;; the cast to $SubSub.
(drop
(block $b-super (result (ref $Super))
(block $b-sub (result (ref $Sub))
(br_table $b-super $b-sub
(ref.cast (ref $SubSub)
(ref.cast (ref $Sub)
(local.get $x)
)
)
(local.get $c)
)
)
)
)
;; Function body result slot strips its cast.
(ref.cast (ref $Sub)
(local.get $x)
)
)
)