;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. ;; RUN: wasm-opt %s --rse -all -S -o - | filecheck %s (module ;; CHECK: (tag $e (param i32)) (tag $e (param i32)) ;; CHECK: (tag $e2) (tag $e2) ;; CHECK: (func $try1 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try1 (local $x i32) (try (do) (catch_all (local.set $x (i32.const 1)) ) ) ;; try will not throw. So this should NOT be dropped (local.set $x (i32.const 1)) ) ;; CHECK: (func $try2 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try2 (local $x i32) (try (do (throw $e (i32.const 0)) (local.set $x (i32.const 1)) ) (catch_all) ) ;; local.set is after 'throw' so it will not run. This should NOT be ;; dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $try3 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try3 (local $x i32) (try (do (throw $e (i32.const 0)) ) (catch_all (local.set $x (i32.const 1)) ) ) ;; try body will throw and catch_all contains the same local.set. This ;; should be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $foo (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $foo) ;; CHECK: (func $try4 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (call $foo) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try4 (local $x i32) (try (do (call $foo) (local.set $x (i32.const 1)) ) (catch_all) ) ;; (call $foo) may throw and the local.set may not run, so this should NOT ;; be dropped (local.set $x (i32.const 1)) ) ;; CHECK: (func $try5 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $foo) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try5 (local $x i32) (try (do (local.set $x (i32.const 1)) (call $foo) ) (catch_all) ) ;; Even if (call $foo) throws, local.set runs before it, so this should be ;; dropped (local.set $x (i32.const 1)) ) ;; CHECK: (func $nested-try1 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (rethrow $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $nested-try1 (local $x i32) (try (do (try $l0 (do (throw $e (i32.const 0)) ) (catch_all (rethrow $l0) ) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ;; The exception is caught by the inner catch_all and rethrown and again ;; caught by the outer catch_all, which runs the local.set. So this should ;; be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $nested-try2 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rethrow $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $nested-try2 (local $x i32) (try (do (try $l0 (do (throw $e (i32.const 0)) ) (catch_all (local.set $x (i32.const 1)) (rethrow $l0) ) ) ) (catch_all) ) ;; The exception is caught by the inner catch_all, which runs the local.set, ;; so this should be dropped (local.set $x (i32.const 1)) ) ;; CHECK: (func $nested-try3 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (rethrow $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $nested-try3 (local $x i32) (try (do (try $l0 (do (throw $e (i32.const 0)) ) (catch $e (drop (pop i32)) (local.set $x (i32.const 1)) (rethrow $l0) ) ) ) (catch_all) ) ;; Unlike nested-try2, the exception may not be caught by the inner catch, ;; so the local.set may not run. So this should NOT be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $nested-catch1 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e2 ;; CHECK-NEXT: (try $try0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e2 ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $nested-catch1 (local $x i32) (try (do (throw $e (i32.const 0)) ) (catch $e (drop (pop i32)) ) (catch $e2 (try (do (throw $e (i32.const 0)) ) (catch $e (drop (pop i32)) ) (catch $e2 (local.set $x (i32.const 1)) ) ) ) ) ;; This should NOT be dropped because the exception might not be caught by ;; the inner catches, and the local.set above us may not have run, and ;; other possible code paths do not even set the local. (local.set $x (i32.const 1)) ) ;; CHECK: (func $nested-catch2 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (try $try1 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $e ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $nested-catch2 (local $x i32) (try (do (throw $e (i32.const 0)) ) (catch $e (drop (pop i32)) (local.set $x (i32.const 1)) ) (catch_all (try (do (throw $e (i32.const 0)) ) (catch $e (drop (pop i32)) (local.set $x (i32.const 1)) ) (catch_all (local.set $x (i32.const 1)) ) ) ) ) ;; This should be dropped because the exception is guaranteed to be caught ;; by one of the catches and it will set the local to 1. (local.set $x (i32.const 1)) ) ;; CHECK: (func $catchless-try (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (call $foo) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $catchless-try (local $x i32) (try (do (call $foo) (local.set $x (i32.const 1)) ) ) ;; The only way we end up here is when (call $foo) does not throw, because ;; if (call $foo) throws, it will throw to the caller because it is within ;; a catchless try. In that case the local.set after (call $foo) would have ;; run before this, so this can be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $try-delegate0 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try2 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try-delegate0 (local $x i32) (try $l0 (do (try (do (try (do (throw $e (i32.const 0)) ) (delegate $l0) ) ) (catch_all) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ;; The innermost try has a delegate, which delegates to the outermost try's ;; catch_all, which has the same local.set. So this can be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $try-delegate1 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try3 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try-delegate1 (local $x i32) (try $l0 (do (try (do (try (do (throw $e (i32.const 0)) ) (delegate $l0) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ) (catch_all) ) ;; The middle try's catch_all has the same local.set, but it is skipped ;; because the innermost try-delegate delegates to the outer try while ;; skipping the middle try-catch_all. So this should NOT be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $try-delegate2 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try4 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try-delegate2 (local $x i32) (try $l0 (do (try (do (try (do (throw $e (i32.const 0)) ) (delegate 2) ;; delegate to caller ) ) (catch_all (local.set $x (i32.const 1)) ) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ;; The innermost try-delegate delegates to the caller, bypassing all ;; local.sets in the middle and the outermost try-catch_alls. So this should ;; NOT be dropped. (Instead this is unreachable, but that's DCE's work) (local.set $x (i32.const 1)) ) ;; CHECK: (func $try-delegate3 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $l1 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try5 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try6 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try-delegate3 (local $x i32) (try $l0 (do (try (do (try $l1 (do (try (do (try (do (throw $e (i32.const 0)) ) (delegate $l1) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ) (delegate $l0) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ) (catch_all) ) ;; The innermost try delegates to $l1, which in turn delegates to $l0, ;; skipping all local.sets in between. So this should NOT be dropped. (local.set $x (i32.const 1)) ) ;; CHECK: (func $try-delegate4 (type $0) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (try $l0 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $l1 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try7 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (try $try8 ;; CHECK-NEXT: (do ;; CHECK-NEXT: (throw $e ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (delegate $l0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $try-delegate4 (local $x i32) (try $l0 (do (try (do (try $l1 (do (try (do (try (do (throw $e (i32.const 0)) ) (delegate $l1) ) ) (catch_all) ) ) (delegate $l0) ) ) (catch_all) ) ) (catch_all (local.set $x (i32.const 1)) ) ) ;; The innermost try delegates to $l1, which in turn delgates to $l0, whose ;; catch_all runs the same local.set. So this can be dropped. (local.set $x (i32.const 1)) ) )