blob: ca492d4d0ff1af723e0724c8dc1ac2ef2d8680ba [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --remove-unused-brs -S -o - | filecheck %s
(module
;; CHECK: (tag $e (type $0))
(tag $e)
;; CHECK: (tag $f (type $0))
(tag $f)
;; CHECK: (tag $g (type $0))
(tag $g)
;; CHECK: (func $throw-caught-all (type $0)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_all $catch)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-all
(block $catch
(try_table (catch_all $catch)
;; This throw can be a br. After that, it can also be removed, as we
;; flow to that block anyhow.
(throw $e)
)
)
)
;; CHECK: (func $throw-caught-all-no-flow (type $0)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_all $catch)
;; CHECK-NEXT: (br $catch)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-all-no-flow
(block $catch
(try_table (catch_all $catch)
(throw $e)
)
;; Block the flow, so that after the throw is optimized to a br, the br
;; remains.
(unreachable)
)
)
;; CHECK: (func $throw-caught-all-more (type $2) (param $x i32)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_all $catch)
;; CHECK-NEXT: (br_if $catch
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-all-more (param $x i32)
(block $catch
(try_table (catch_all $catch)
;; Look into nested children. After we turn the throw into a br, it also
;; fuses with the if into a br_if.
(if
(local.get $x)
(then
(throw $e)
)
)
)
)
)
;; CHECK: (func $throw-caught-precise (type $0)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch $e $catch)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-precise
(block $catch
;; We can still optimize here, even though we replaced the catch_all with
;; a precise tag, because the tag matches.
(try_table (catch $e $catch)
(throw $e)
)
)
)
;; CHECK: (func $throw-caught-precise-later (type $0)
;; CHECK-NEXT: (block $fail
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch $f $fail) (catch $e $catch)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $throw-caught-precise-later)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-precise-later
(block $fail
(block $catch
;; We can still optimize here, by looking through the tags til we find
;; ours.
(try_table (catch $f $fail) (catch $e $catch)
(throw $e)
)
)
;; Add an effect here, so the two blocks are not mergeable.
(call $throw-caught-precise-later)
)
)
;; CHECK: (func $throw-caught-all-later (type $0)
;; CHECK-NEXT: (block $fail
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch $f $fail) (catch_all $catch)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $throw-caught-precise-later)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-all-later
(block $fail
(block $catch
;; We can still optimize here, by looking through the tags til we find
;; the catch_all
(try_table (catch $f $fail) (catch_all $catch)
(throw $e)
)
)
;; Add an effect here, so the two blocks are not mergeable.
(call $throw-caught-precise-later)
)
)
;; CHECK: (func $throw-caught-fail (type $0)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch $f $catch)
;; CHECK-NEXT: (throw $e)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-fail
(block $catch
;; The tag does *not* match.
(try_table (catch $f $catch)
(throw $e)
)
)
)
;; CHECK: (func $throw-caught-outer (type $0)
;; CHECK-NEXT: (block $fail
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch $e $catch)
;; CHECK-NEXT: (try_table (catch $f $fail)
;; CHECK-NEXT: (br $catch)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $throw-caught-precise-later)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-outer
(block $fail
(block $catch
(try_table (catch $e $catch)
(try_table (catch $f $fail)
;; This throw can be a br, thanks to the outer try.
(throw $e)
)
)
;; Block the flow, so that the br above remains.
(unreachable)
)
;; Add an effect here, so the two blocks are not mergeable.
(call $throw-caught-precise-later)
)
)
;; CHECK: (func $throw-catch-all-ref (type $1) (result exnref)
;; CHECK-NEXT: (block $catch (result exnref)
;; CHECK-NEXT: (try_table (catch_all_ref $catch)
;; CHECK-NEXT: (throw $e)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-catch-all-ref (result exnref)
(block $catch (result exnref)
(try_table (catch_all_ref $catch)
;; This is caught with a ref, so we do not optimize.
(throw $e)
)
)
)
;; CHECK: (func $throw-caught-ref (type $1) (result exnref)
;; CHECK-NEXT: (block $catch (result exnref)
;; CHECK-NEXT: (try_table (catch_ref $e $catch)
;; CHECK-NEXT: (throw $e)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-ref (result exnref)
(block $catch (result exnref)
(try_table (catch_ref $e $catch)
;; As above, but without catch_all.
(throw $e)
)
)
)
;; CHECK: (func $throw-caught-ref-later-all (type $1) (result exnref)
;; CHECK-NEXT: (block $outer (result exnref)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_ref $e $outer) (catch_all $catch)
;; CHECK-NEXT: (throw $e)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-ref-later-all (result exnref)
(block $outer (result exnref)
(block $catch
(try_table (catch_ref $e $outer) (catch_all $catch)
;; This is caught with a ref, before we reach the catch all, so we do
;; not optimize.
(throw $e)
)
(unreachable)
)
(unreachable)
)
)
;; CHECK: (func $throw-multi (type $0)
;; CHECK-NEXT: (block $outer
;; CHECK-NEXT: (block $middle
;; CHECK-NEXT: (block $inner
;; CHECK-NEXT: (try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
;; CHECK-NEXT: (br $outer)
;; CHECK-NEXT: (br $middle)
;; CHECK-NEXT: (br $inner)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $throw-caught-precise-later)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $throw-caught-precise-later)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-multi
(block $outer
(block $middle
(block $inner
(try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
;; Multiple throws, optimizable in different ways.
(throw $e)
(throw $f)
(throw $g)
;; Prevent the br we optimize to at the end from getting optimized
;; out.
(unreachable)
)
)
;; Add an effect here, so the two blocks are not mergeable.
(call $throw-caught-precise-later)
)
(call $throw-caught-precise-later)
)
)
;; CHECK: (func $throw-mixed (type $0)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_all $catch)
;; CHECK-NEXT: (try
;; CHECK-NEXT: (do
;; CHECK-NEXT: (throw $e)
;; CHECK-NEXT: )
;; CHECK-NEXT: (catch_all
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-mixed
;; When we see mixed Trys and TryTables, we do not optimize (we would need
;; to analyze if the Trys catch the exceptions and not the TryTables, but
;; we don't bother to handle this odd case of mixing the old and new
;; styles of code).
(block $catch
(try_table (catch_all $catch)
(try
(do
;; This throw is caught by the Try, not the TryTable.
(throw $e)
)
(catch_all
(unreachable)
)
)
)
)
)
;; CHECK: (func $threading (type $0)
;; CHECK-NEXT: (block $outer
;; CHECK-NEXT: (block $middle
;; CHECK-NEXT: (block $inner
;; CHECK-NEXT: (try_table (catch $e $outer) (catch $f $outer) (catch_all $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $threading
(block $outer
(block $middle
(block $inner
;; All the branch targets here will turn into "outer", see below.
(try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
)
)
;; Jumping to inner is the same as middle, as there is nothing
;; between them.
)
;; Jumping to middle is the same as outer, as we jump there anyhow.
(br $outer)
)
)
;; CHECK: (func $threading-2 (type $0)
;; CHECK-NEXT: (block $outer
;; CHECK-NEXT: (block $middle
;; CHECK-NEXT: (block $inner
;; CHECK-NEXT: (try_table (catch $e $outer) (catch $f $middle) (catch_all $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $threading-2
(block $outer
(block $middle
(block $inner
(try_table (catch $e $outer) (catch $f $middle) (catch_all $inner)
;; Only inner will turn into outer.
)
)
;; Skip over middle, so jumps to inner go to outer.
(br $outer)
)
;; Stop execution between middle and outer. We should still optimize
;; inner to outer.
(unreachable)
)
)
)
(module
;; CHECK: (import "a" "b" (func $effect (type $2) (result i32)))
(import "a" "b" (func $effect (result i32)))
;; CHECK: (tag $e (type $1) (param i32))
(tag $e (param i32))
;; CHECK: (tag $multi (type $3) (param i32 f64))
(tag $multi (param i32 f64))
;; CHECK: (func $throw-caught-all (type $1) (param $x i32)
;; CHECK-NEXT: (block $catch
;; CHECK-NEXT: (try_table (catch_all $catch)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $effect)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $catch)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-caught-all (param $x i32)
(block $catch
(try_table (catch_all $catch)
;; This throw can be a br. The call must be kept in a drop.
(throw $e
(call $effect)
)
)
)
)
;; CHECK: (func $throw-br-contents (type $2) (result i32)
;; CHECK-NEXT: (block $catch (result i32)
;; CHECK-NEXT: (try_table (result i32) (catch $e $catch)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-br-contents (result i32)
(block $catch (result i32)
(try_table (catch $e $catch)
;; This throw is not caught by catch_all as above, so the value must be
;; sent as a value on the br we optimize it to. That br can also be
;; optimized away by letting the value flow out.
(throw $e
(i32.const 42)
)
)
)
)
;; CHECK: (func $throw-br-contents-multi (type $0) (result i32 f64)
;; CHECK-NEXT: (block $catch (type $0) (result i32 f64)
;; CHECK-NEXT: (try_table (type $0) (result i32 f64) (catch $multi $catch)
;; CHECK-NEXT: (tuple.make 2
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $throw-br-contents-multi (result i32 f64)
;; As above, but now with a multivalue tag.
(block $catch (result i32 f64)
(try_table (catch $multi $catch)
(throw $multi
(i32.const 42)
(f64.const 3.14159)
)
)
)
)
;; CHECK: (func $no-flow-through-throw (type $4)
;; CHECK-NEXT: (block $label
;; CHECK-NEXT: (try_table (catch_all $label)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (if (result i32)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $label)
;; CHECK-NEXT: )
;; CHECK-NEXT: (else
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $label)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $no-flow-through-throw
;; The throw here can turn into a break. While doing so, we must clear all
;; the currently-flowing things, namely the br in the if arm. If we do not
;; do so then it will try to flow out through the drop that we add for the
;; throw's value, which is impossible.
(block $label
(try_table (catch_all $label)
(throw $e
(if (result i32)
(i32.const 0)
(then
(br $label)
)
(else
(i32.const 42)
)
)
)
)
)
)
)