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