summaryrefslogtreecommitdiff
path: root/test/lit
diff options
context:
space:
mode:
authorSébastien Doeraene <sjrdoeraene@gmail.com>2024-08-21 00:43:25 +0200
committerGitHub <noreply@github.com>2024-08-20 15:43:25 -0700
commit340ad71810484c279b1a36a9a7e458c9b18855b9 (patch)
tree4167b08dea6f5ffcdb975d90eb6f3c7925f628e0 /test/lit
parent2c9c74d8b64e1776c6c374af8631995b0be606f1 (diff)
downloadbinaryen-340ad71810484c279b1a36a9a7e458c9b18855b9.tar.gz
binaryen-340ad71810484c279b1a36a9a7e458c9b18855b9.tar.bz2
binaryen-340ad71810484c279b1a36a9a7e458c9b18855b9.zip
[Exceptions] Finish interpreter + optimizer support for try_table. (#6814)
* Add interpreter support for exnref values. * Fix optimization passes to support try_table. * Enable the interpreter (but not in V8, see code) on exceptions.
Diffstat (limited to 'test/lit')
-rw-r--r--test/lit/basic/reference-types.wast228
-rw-r--r--test/lit/exec/eh-gc.wast27
-rw-r--r--test/lit/exec/eh-print.wast (renamed from test/lit/exec/eh-legacy-print.wast)0
-rw-r--r--test/lit/exec/eh.wast45
-rw-r--r--test/lit/merge/renamings.wat48
-rw-r--r--test/lit/merge/renamings.wat.second16
-rw-r--r--test/lit/passes/coalesce-locals-eh-legacy.wast6
-rw-r--r--test/lit/passes/coalesce-locals-eh.wast84
-rw-r--r--test/lit/passes/code-folding-eh-legacy.wast5
-rw-r--r--test/lit/passes/code-folding-eh.wast296
-rw-r--r--test/lit/passes/code-pushing-eh-legacy.wast139
-rw-r--r--test/lit/passes/code-pushing-eh.wast300
-rw-r--r--test/lit/passes/dce-eh-legacy.wast26
-rw-r--r--test/lit/passes/dce-eh.wast145
-rw-r--r--test/lit/passes/global-effects-eh-legacy.wast507
-rw-r--r--test/lit/passes/global-effects.wast157
-rw-r--r--test/lit/passes/gufa-eh.wast60
-rw-r--r--test/lit/passes/local-subtyping.wast52
-rw-r--r--test/lit/passes/simplify-locals-eh-legacy.wast105
-rw-r--r--test/lit/passes/simplify-locals-eh.wast226
-rw-r--r--test/lit/passes/vacuum-eh-legacy.wast3
-rw-r--r--test/lit/passes/vacuum-eh.wast235
22 files changed, 2341 insertions, 369 deletions
diff --git a/test/lit/basic/reference-types.wast b/test/lit/basic/reference-types.wast
index 7f9ba4219..335939573 100644
--- a/test/lit/basic/reference-types.wast
+++ b/test/lit/basic/reference-types.wast
@@ -642,6 +642,62 @@
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: )
;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $tryend (result eqref)
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $catch (result i32)
+ ;; CHECK-TEXT-NEXT: (br $tryend
+ ;; CHECK-TEXT-NEXT: (try_table (result eqref) (catch $e-i32 $catch)
+ ;; CHECK-TEXT-NEXT: (local.get $local_eqref)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (ref.null none)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $tryend0 (result funcref)
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $catch0 (result i32)
+ ;; CHECK-TEXT-NEXT: (br $tryend0
+ ;; CHECK-TEXT-NEXT: (try_table (result funcref) (catch $e-i32 $catch0)
+ ;; CHECK-TEXT-NEXT: (ref.func $foo)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (ref.null nofunc)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $tryend1 (result anyref)
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $catch1 (result i32)
+ ;; CHECK-TEXT-NEXT: (br $tryend1
+ ;; CHECK-TEXT-NEXT: (try_table (result anyref) (catch $e-i32 $catch1)
+ ;; CHECK-TEXT-NEXT: (local.get $local_eqref)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (ref.null none)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $tryend2 (result anyref)
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (block $catch2 (result i32)
+ ;; CHECK-TEXT-NEXT: (br $tryend2
+ ;; CHECK-TEXT-NEXT: (try_table (result anyref) (catch $e-i32 $catch2)
+ ;; CHECK-TEXT-NEXT: (ref.null none)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (local.get $local_eqref)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (drop
;; CHECK-TEXT-NEXT: (select (result eqref)
;; CHECK-TEXT-NEXT: (local.get $local_eqref)
;; CHECK-TEXT-NEXT: (ref.null none)
@@ -1189,6 +1245,62 @@
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$50 (result eqref)
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$51 (result i32)
+ ;; CHECK-BIN-NEXT: (br $label$50
+ ;; CHECK-BIN-NEXT: (try_table (result eqref) (catch $e-i32 $label$51)
+ ;; CHECK-BIN-NEXT: (local.get $local_eqref)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (ref.null none)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$53 (result funcref)
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$54 (result i32)
+ ;; CHECK-BIN-NEXT: (br $label$53
+ ;; CHECK-BIN-NEXT: (try_table (result funcref) (catch $e-i32 $label$54)
+ ;; CHECK-BIN-NEXT: (ref.func $foo)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (ref.null nofunc)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$56 (result anyref)
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$57 (result i32)
+ ;; CHECK-BIN-NEXT: (br $label$56
+ ;; CHECK-BIN-NEXT: (try_table (result anyref) (catch $e-i32 $label$57)
+ ;; CHECK-BIN-NEXT: (local.get $local_eqref)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (ref.null none)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$59 (result anyref)
+ ;; CHECK-BIN-NEXT: (drop
+ ;; CHECK-BIN-NEXT: (block $label$60 (result i32)
+ ;; CHECK-BIN-NEXT: (br $label$59
+ ;; CHECK-BIN-NEXT: (try_table (result anyref) (catch $e-i32 $label$60)
+ ;; CHECK-BIN-NEXT: (ref.null none)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (local.get $local_eqref)
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: )
+ ;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (select (result eqref)
;; CHECK-BIN-NEXT: (local.get $local_eqref)
;; CHECK-BIN-NEXT: (ref.null none)
@@ -1597,6 +1709,66 @@
)
)
+ ;; Test try_table return type
+ (drop
+ (block $tryend (result eqref)
+ (drop
+ (block $catch (result i32)
+ (br $tryend
+ (try_table (result eqref) (catch $e-i32 $catch)
+ (local.get $local_eqref)
+ )
+ )
+ )
+ )
+ (ref.null eq)
+ )
+ )
+ (drop
+ (block $tryend (result funcref)
+ (drop
+ (block $catch (result i32)
+ (br $tryend
+ (try_table (result funcref) (catch $e-i32 $catch)
+ (ref.func $foo)
+ )
+ )
+ )
+ )
+ (ref.null func)
+ )
+ )
+
+ ;; Test subtype relationship for try_table return type
+ (drop
+ (block $tryend (result anyref)
+ (drop
+ (block $catch (result i32)
+ (br $tryend
+ (try_table (result anyref) (catch $e-i32 $catch)
+ (local.get $local_eqref)
+ )
+ )
+ )
+ )
+ (ref.null any)
+ )
+ )
+ (drop
+ (block $tryend (result anyref)
+ (drop
+ (block $catch (result i32)
+ (br $tryend
+ (try_table (result anyref) (catch $e-i32 $catch)
+ (ref.null eq)
+ )
+ )
+ )
+ )
+ (local.get $local_eqref)
+ )
+ )
+
;; Test typed select
(drop
(select (result eqref)
@@ -2416,6 +2588,62 @@
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$50 (result eqref)
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$51 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$50
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (result eqref) (catch $tag$0 $label$51)
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$53 (result funcref)
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$54 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$53
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (result funcref) (catch $tag$0 $label$54)
+;; CHECK-BIN-NODEBUG-NEXT: (ref.func $3)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null nofunc)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$56 (result anyref)
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$57 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$56
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (result anyref) (catch $tag$0 $label$57)
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$59 (result anyref)
+;; CHECK-BIN-NODEBUG-NEXT: (drop
+;; CHECK-BIN-NODEBUG-NEXT: (block $label$60 (result i32)
+;; CHECK-BIN-NODEBUG-NEXT: (br $label$59
+;; CHECK-BIN-NODEBUG-NEXT: (try_table (result anyref) (catch $tag$0 $label$60)
+;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: )
+;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (select (result eqref)
;; CHECK-BIN-NODEBUG-NEXT: (local.get $0)
;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
diff --git a/test/lit/exec/eh-gc.wast b/test/lit/exec/eh-gc.wast
new file mode 100644
index 000000000..6f97f44f8
--- /dev/null
+++ b/test/lit/exec/eh-gc.wast
@@ -0,0 +1,27 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited.
+
+;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s
+
+(module
+ (tag $tag (param externref))
+
+ ;; CHECK: [fuzz-exec] calling catch-null
+ (func $catch-null (export "catch-null")
+ (block $tryend
+ ;; The actual resulting value type is more refined than externref (it is a
+ ;; bottom type) which we should not error on.
+ (drop
+ (block $catch (result externref)
+ (try_table (catch $tag $catch)
+ (throw $tag
+ (ref.null noextern)
+ )
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+)
+;; CHECK: [fuzz-exec] calling catch-null
+;; CHECK-NEXT: [fuzz-exec] comparing catch-null
diff --git a/test/lit/exec/eh-legacy-print.wast b/test/lit/exec/eh-print.wast
index f50164631..f50164631 100644
--- a/test/lit/exec/eh-legacy-print.wast
+++ b/test/lit/exec/eh-print.wast
diff --git a/test/lit/exec/eh.wast b/test/lit/exec/eh.wast
new file mode 100644
index 000000000..45215b048
--- /dev/null
+++ b/test/lit/exec/eh.wast
@@ -0,0 +1,45 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited.
+
+;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s
+
+(module
+ (tag $e-i32 (param i32))
+
+ ;; CHECK: [fuzz-exec] calling throw
+ ;; CHECK-NEXT: [exception thrown: e-i32 1]
+ (func $throw (export "throw")
+ (throw $e-i32 (i32.const 1))
+ )
+
+ ;; CHECK: [fuzz-exec] calling try_table-catch
+ (func $try_table-catch (export "try_table-catch")
+ (block $tryend
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $e-i32 $catch)
+ (throw $e-i32 (i32.const 2))
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: [fuzz-exec] calling catchless-try_table
+ ;; CHECK-NEXT: [exception thrown: e-i32 3]
+ (func $catchless-try_table (export "catchless-try_table")
+ (try_table
+ (throw $e-i32 (i32.const 3))
+ )
+ )
+)
+;; CHECK: [fuzz-exec] calling throw
+;; CHECK-NEXT: [exception thrown: e-i32 1]
+
+;; CHECK: [fuzz-exec] calling try_table-catch
+
+;; CHECK: [fuzz-exec] calling catchless-try_table
+;; CHECK-NEXT: [exception thrown: e-i32 3]
+;; CHECK-NEXT: [fuzz-exec] comparing catchless-try_table
+;; CHECK-NEXT: [fuzz-exec] comparing throw
+;; CHECK-NEXT: [fuzz-exec] comparing try_table-catch
diff --git a/test/lit/merge/renamings.wat b/test/lit/merge/renamings.wat
index c6a22542a..9c54f3514 100644
--- a/test/lit/merge/renamings.wat
+++ b/test/lit/merge/renamings.wat
@@ -160,6 +160,22 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $foo $catch)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch0 (result i64)
+ ;; CHECK-NEXT: (try_table (catch $bar $catch0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i64.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.load $foo
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
@@ -220,6 +236,22 @@
)
)
)
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $foo $catch)
+ (nop)
+ )
+ (i32.const 0)
+ )
+ )
+ (drop
+ (block $catch (result i64)
+ (try_table (catch $bar $catch)
+ (nop)
+ )
+ (i64.const 0)
+ )
+ )
;; Memories
(drop
@@ -310,6 +342,22 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (block $catch (result f32)
+;; CHECK-NEXT: (try_table (catch $foo_2 $catch)
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (f32.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
+;; CHECK-NEXT: (block $catch0 (result f64)
+;; CHECK-NEXT: (try_table (catch $other $catch0)
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (f64.const 0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.load $foo_2
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
diff --git a/test/lit/merge/renamings.wat.second b/test/lit/merge/renamings.wat.second
index 25d3d5e81..c17b00cc5 100644
--- a/test/lit/merge/renamings.wat.second
+++ b/test/lit/merge/renamings.wat.second
@@ -70,6 +70,22 @@
)
)
)
+ (drop
+ (block $catch (result f32)
+ (try_table (catch $foo $catch)
+ (nop)
+ )
+ (f32.const 0.0)
+ )
+ )
+ (drop
+ (block $catch (result f64)
+ (try_table (catch $other $catch)
+ (nop)
+ )
+ (f64.const 0.0)
+ )
+ )
;; Memories
(drop
diff --git a/test/lit/passes/coalesce-locals-eh-legacy.wast b/test/lit/passes/coalesce-locals-eh-legacy.wast
index 63b1445dd..9091fdcb9 100644
--- a/test/lit/passes/coalesce-locals-eh-legacy.wast
+++ b/test/lit/passes/coalesce-locals-eh-legacy.wast
@@ -3,8 +3,10 @@
(module
;; CHECK: (tag $e)
+ (tag $e)
;; CHECK: (tag $any (param (ref any)))
+ (tag $any (param (ref any)))
;; CHECK: (func $bar (type $2) (result i32)
;; CHECK-NEXT: (i32.const 1984)
@@ -13,10 +15,6 @@
(i32.const 1984)
)
- (tag $e)
-
- (tag $any (param (ref any)))
-
;; CHECK: (func $bug-cfg-traversal (type $3) (param $0 i32) (result i32)
;; CHECK-NEXT: (try
;; CHECK-NEXT: (do
diff --git a/test/lit/passes/coalesce-locals-eh.wast b/test/lit/passes/coalesce-locals-eh.wast
new file mode 100644
index 000000000..90458f32f
--- /dev/null
+++ b/test/lit/passes/coalesce-locals-eh.wast
@@ -0,0 +1,84 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --coalesce-locals -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (tag $e)
+ (tag $e)
+
+ ;; CHECK: (tag $any (param (ref any)))
+ (tag $any (param (ref any)))
+
+ ;; CHECK: (func $bar (type $2) (result i32)
+ ;; CHECK-NEXT: (i32.const 1984)
+ ;; CHECK-NEXT: )
+ (func $bar (result i32)
+ (i32.const 1984)
+ )
+
+ ;; CHECK: (func $bug-cfg-traversal (type $3) (param $0 i32) (result i32)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (call $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ (func $bug-cfg-traversal (param $0 i32) (result i32)
+ (local $x i32)
+ ;; This is a regression test case for a bug in cfg-traversal for EH.
+ ;; See https://github.com/WebAssembly/binaryen/pull/3594
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (local.set $x
+ ;; the call may or may not throw, so we may reach the get of $x
+ (call $bar)
+ )
+ )
+ (br $tryend)
+ )
+ (unreachable)
+ )
+ (local.get $x)
+ )
+
+ ;; CHECK: (func $0 (type $0)
+ ;; CHECK-NEXT: (local $0 anyref)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block (result anyref)
+ ;; CHECK-NEXT: (block $catch (result (ref any))
+ ;; CHECK-NEXT: (try_table (catch $any $catch)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $0
+ (local $0 (ref null any))
+ (block $tryend
+ (drop
+ ;; There is a difference between the type of the value here and the type
+ ;; of the local, due to the local being nullable. We should not error on
+ ;; that as we replace the tee with a drop (as it has no gets).
+ (local.tee $0
+ (block $catch (result (ref any))
+ (try_table (catch $any $catch)
+ (nop)
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/code-folding-eh-legacy.wast b/test/lit/passes/code-folding-eh-legacy.wast
index 05cd8db8b..852ec126a 100644
--- a/test/lit/passes/code-folding-eh-legacy.wast
+++ b/test/lit/passes/code-folding-eh-legacy.wast
@@ -96,6 +96,7 @@
(func $try-call-optimize-terminating-tails-success (result i32)
(try
(do
+ ;; Expressions that cannot throw can be taken out of 'try' scope.
(drop (i32.const 1))
(drop (i32.const 1))
(return (i32.const 0))
@@ -243,6 +244,9 @@
(drop (i32.const 1))
(drop (i32.const 1))
(drop (i32.const 1))
+ ;; return_call executes the call after returning from this function.
+ ;; This try cannot catch exceptions it throws, so we can fold it out of
+ ;; the try.
(return_call $foo-i32)
)
(catch_all
@@ -281,6 +285,7 @@
(block $x
(try
(do
+ ;; Expressions that cannot throw can be taken out of 'try' scope.
(drop (i32.const 1))
(drop (i32.const 1))
(drop (i32.const 1))
diff --git a/test/lit/passes/code-folding-eh.wast b/test/lit/passes/code-folding-eh.wast
new file mode 100644
index 000000000..5a7cd68c7
--- /dev/null
+++ b/test/lit/passes/code-folding-eh.wast
@@ -0,0 +1,296 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --remove-unused-names --code-folding -all -S -o - \
+;; RUN: | filecheck %s
+
+(module
+ ;; CHECK: (tag $e-i32 (param i32))
+ (tag $e-i32 (param i32))
+
+ ;; CHECK: (func $try_table-call-optimize-terminating-tails-success (type $0) (result i32)
+ ;; CHECK-NEXT: (block $folding-inner0
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (br $folding-inner0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $folding-inner0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-terminating-tails-success (result i32)
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; Expressions that cannot throw can be taken out of 'try' scope.
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (return (i32.const 0))
+ )
+ (br $tryend)
+ )
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (return (i32.const 0))
+ )
+ (i32.const 0)
+ )
+
+
+ ;; CHECK: (func $foo (type $1)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo)
+
+ ;; CHECK: (func $try_table-call-optimize-terminating-tails (type $0) (result i32)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-terminating-tails (result i32)
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; Expressions that can throw should NOT be taken out of 'try' scope.
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (return (i32.const 0))
+ )
+ (br $tryend)
+ )
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (return (i32.const 0))
+ )
+ (i32.const 0)
+ )
+
+ ;; CHECK: (func $foo-i32 (type $0) (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $foo-i32 (result i32)
+ (i32.const 0)
+ )
+
+ ;; CHECK: (func $try_table-call-optimize-terminating-tails-call-return (type $0) (result i32)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (call $foo-i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (call $foo-i32)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-terminating-tails-call-return (result i32)
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ ;; Cannot be folded out of the try because it might throw.
+ (return (call $foo-i32))
+ )
+ (br $tryend)
+ )
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (return (call $foo-i32))
+ )
+ (i32.const 0)
+ )
+
+ ;; CHECK: (func $try_table-call-optimize-terminating-tails-return-call (type $0) (result i32)
+ ;; CHECK-NEXT: (block $folding-inner0
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (block (result i32)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (br $folding-inner0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $folding-inner0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return_call $foo-i32)
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-terminating-tails-return-call (result i32)
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ ;; return_call executes the call after returning from this function.
+ ;; This try_table cannot catch exceptions it throws, so we can fold it
+ ;; out of the try_table.
+ (return_call $foo-i32)
+ )
+ (br $tryend)
+ )
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (return_call $foo-i32)
+ )
+ (i32.const 0)
+ )
+
+ ;; CHECK: (func $try_table-call-optimize-expression-tails-success (type $1)
+ ;; CHECK-NEXT: (block $x
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (br $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-expression-tails-success
+ (block $x
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; Expressions that cannot throw can be taken out of 'try_table'
+ ;; scope.
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (br $x)
+ )
+ (br $tryend)
+ )
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (drop (i32.const 1))
+ (br $x)
+ )
+ (unreachable)
+ )
+ )
+
+ ;; CHECK: (func $try_table-call-optimize-expression-tails (type $1)
+ ;; CHECK-NEXT: (block $x
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (br $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: (br $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-call-optimize-expression-tails
+ (block $x
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; Expressions that can throw should NOT be taken out of 'try' scope.
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (br $x)
+ )
+ (br $tryend)
+ )
+ (call $foo)
+ (call $foo)
+ (call $foo)
+ (br $x)
+ )
+ (unreachable)
+ )
+ )
+)
diff --git a/test/lit/passes/code-pushing-eh-legacy.wast b/test/lit/passes/code-pushing-eh-legacy.wast
index 8fc0d423d..9511d244a 100644
--- a/test/lit/passes/code-pushing-eh-legacy.wast
+++ b/test/lit/passes/code-pushing-eh-legacy.wast
@@ -7,69 +7,6 @@
;; CHECK: (tag $e (param i32))
(tag $e (param i32))
- ;; CHECK: (func $cannot-push-past-call (type $0)
- ;; CHECK-NEXT: (local $x i32)
- ;; CHECK-NEXT: (block $out
- ;; CHECK-NEXT: (local.set $x
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (call $cannot-push-past-call)
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (br_if $out
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $cannot-push-past-call
- (local $x i32)
- (block $out
- ;; This local.set cannot be pushed down, because the call below can throw
- (local.set $x (i32.const 1))
- (call $cannot-push-past-call)
- (drop (i32.const 1))
- (br_if $out (i32.const 2))
- (drop (local.get $x))
- )
- )
-
- ;; CHECK: (func $cannot-push-past-throw (type $0)
- ;; CHECK-NEXT: (local $x i32)
- ;; CHECK-NEXT: (block $out
- ;; CHECK-NEXT: (local.set $x
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (throw $e
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (br_if $out
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $cannot-push-past-throw
- (local $x i32)
- (block $out
- ;; This local.set cannot be pushed down, because there is 'throw' below.
- ;; This pass only pushes past conditional control flow atm.
- (local.set $x (i32.const 1))
- (throw $e (i32.const 0))
- (drop (i32.const 1))
- (br_if $out (i32.const 2))
- (drop (local.get $x))
- )
- )
-
;; CHECK: (func $can-push-past-try (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
@@ -323,80 +260,4 @@
(drop (local.get $x))
)
)
-
- ;; CHECK: (func $can-push-past-conditional-throw (type $1) (param $param i32)
- ;; CHECK-NEXT: (local $x i32)
- ;; CHECK-NEXT: (block $block
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (local.get $param)
- ;; CHECK-NEXT: (then
- ;; CHECK-NEXT: (throw $e
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (local.set $x
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $can-push-past-conditional-throw (param $param i32)
- (local $x i32)
- (block $block
- ;; We can push past an if containing a throw. The if is conditional
- ;; control flow, which is what we look for in this optimization, and a
- ;; throw is like a break - it will jump out of the current block - so we
- ;; can push the set past it, as the set is only needed in this block.
- (local.set $x (i32.const 1))
- (if
- (local.get $param)
- (then
- (throw $e (i32.const 0))
- )
- )
- (drop (local.get $x))
- )
- )
-
- ;; CHECK: (func $cannot-push-past-conditional-throw-extra-use (type $1) (param $param i32)
- ;; CHECK-NEXT: (local $x i32)
- ;; CHECK-NEXT: (block $block
- ;; CHECK-NEXT: (local.set $x
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (local.get $param)
- ;; CHECK-NEXT: (then
- ;; CHECK-NEXT: (throw $e
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $cannot-push-past-conditional-throw-extra-use (param $param i32)
- (local $x i32)
- ;; As above, but now there is another local.get outside of the block. That
- ;; means the local.set cannot be pushed to a place it might not execute.
- (block $block
- (local.set $x (i32.const 1))
- (if
- (local.get $param)
- (then
- (throw $e (i32.const 0))
- )
- )
- (drop (local.get $x))
- )
- (drop (local.get $x))
- )
)
diff --git a/test/lit/passes/code-pushing-eh.wast b/test/lit/passes/code-pushing-eh.wast
new file mode 100644
index 000000000..ee2798c46
--- /dev/null
+++ b/test/lit/passes/code-pushing-eh.wast
@@ -0,0 +1,300 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --code-pushing -all -S -o - | filecheck %s
+
+;; The tests in this file test EffectAnalyzer, which is used by CodePushing.
+
+(module
+ ;; CHECK: (tag $e (param i32))
+ (tag $e (param i32))
+
+ ;; CHECK: (func $cannot-push-past-call (type $0)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $out
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $cannot-push-past-call)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br_if $out
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-push-past-call
+ (local $x i32)
+ (block $out
+ ;; This local.set cannot be pushed down, because the call below can throw.
+ (local.set $x (i32.const 1))
+ (call $cannot-push-past-call)
+ (drop (i32.const 1))
+ (br_if $out (i32.const 2))
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $cannot-push-past-throw (type $0)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $out
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br_if $out
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-push-past-throw
+ (local $x i32)
+ (block $out
+ ;; This local.set cannot be pushed down, because there is 'throw' below.
+ ;; This pass only pushes past conditional control flow atm.
+ (local.set $x (i32.const 1))
+ (throw $e (i32.const 0))
+ (drop (i32.const 1))
+ (br_if $out (i32.const 2))
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $can-push-past-try_table (type $0)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $out
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br_if $out
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $can-push-past-try_table
+ (local $x i32)
+ (block $out
+ ;; This local.set can be pushed down, because the 'throw' below is going
+ ;; to be caught by the inner catch_all.
+ (local.set $x (i32.const 1))
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (throw $e (i32.const 0))
+ )
+ (br $tryend)
+ )
+ )
+ (drop (i32.const 1))
+ (br_if $out (i32.const 2))
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $foo (type $0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo)
+
+ ;; CHECK: (func $cannot-push-past-try_table (type $0)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $out
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e $catch)
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br_if $out
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-push-past-try_table
+ (local $x i32)
+ (block $out
+ ;; This local.set cannot be pushed down, because the exception thrown by
+ ;; 'call $foo' below may not be caught by 'catch $e'.
+ (local.set $x (i32.const 1))
+ (block $tryend
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $e $catch)
+ (call $foo)
+ )
+ (br $tryend)
+ )
+ )
+ )
+ (drop (i32.const 1))
+ (br_if $out (i32.const 2))
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $cannot-push-past-throw_ref-within-catch (type $0)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $out
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (throw_ref
+ ;; CHECK-NEXT: (block $catch (result exnref)
+ ;; CHECK-NEXT: (try_table (catch_all_ref $catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br_if $out
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-push-past-throw_ref-within-catch
+ (local $x i32)
+ (block $out
+ ;; This local.set cannot be pushed down, because there is 'throw_ref'
+ ;; within the catch handler.
+ (local.set $x (i32.const 1))
+ (block $tryend
+ (throw_ref
+ (block $catch (result exnref)
+ (try_table (catch_all_ref $catch)
+ (throw $e (i32.const 0))
+ )
+ (br $tryend)
+ )
+ )
+ )
+ (drop (i32.const 1))
+ (br_if $out (i32.const 2))
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $can-push-past-conditional-throw (type $1) (param $param i32)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $block
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $param)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $can-push-past-conditional-throw (param $param i32)
+ (local $x i32)
+ (block $block
+ ;; We can push past an if containing a throw. The if is conditional
+ ;; control flow, which is what we look for in this optimization, and a
+ ;; throw is like a break - it will jump out of the current block - so we
+ ;; can push the set past it, as the set is only needed in this block.
+ (local.set $x (i32.const 1))
+ (if
+ (local.get $param)
+ (then
+ (throw $e (i32.const 0))
+ )
+ )
+ (drop (local.get $x))
+ )
+ )
+
+ ;; CHECK: (func $cannot-push-past-conditional-throw-extra-use (type $1) (param $param i32)
+ ;; CHECK-NEXT: (local $x i32)
+ ;; CHECK-NEXT: (block $block
+ ;; CHECK-NEXT: (local.set $x
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $param)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cannot-push-past-conditional-throw-extra-use (param $param i32)
+ (local $x i32)
+ ;; As above, but now there is another local.get outside of the block. That
+ ;; means the local.set cannot be pushed to a place it might not execute.
+ (block $block
+ (local.set $x (i32.const 1))
+ (if
+ (local.get $param)
+ (then
+ (throw $e (i32.const 0))
+ )
+ )
+ (drop (local.get $x))
+ )
+ (drop (local.get $x))
+ )
+)
diff --git a/test/lit/passes/dce-eh-legacy.wast b/test/lit/passes/dce-eh-legacy.wast
index 120ec4e11..ef6d569d6 100644
--- a/test/lit/passes/dce-eh-legacy.wast
+++ b/test/lit/passes/dce-eh-legacy.wast
@@ -79,32 +79,6 @@
(call $foo) ;; should be dce'd
)
- ;; CHECK: (func $throw (type $0)
- ;; CHECK-NEXT: (block $label$0
- ;; CHECK-NEXT: (block $label$1
- ;; CHECK-NEXT: (throw $e)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $throw
- ;; All these wrapping expressions before 'throw' will be dce'd
- (drop
- (block $label$0 (result externref)
- (if
- (i32.clz
- (block $label$1 (result i32)
- (throw $e)
- )
- )
- (then
- (nop)
- )
- )
- (ref.null extern)
- )
- )
- )
-
;; CHECK: (func $rethrow (type $0)
;; CHECK-NEXT: (try $l0
;; CHECK-NEXT: (do
diff --git a/test/lit/passes/dce-eh.wast b/test/lit/passes/dce-eh.wast
new file mode 100644
index 000000000..413a278d0
--- /dev/null
+++ b/test/lit/passes/dce-eh.wast
@@ -0,0 +1,145 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --dce -all -S -o - | filecheck %s
+
+;; If either try_table body or any of catch handler is reachable, the whole
+;; try_table construct is reachable.
+(module
+ ;; CHECK: (tag $e)
+ (tag $e)
+
+ ;; CHECK: (tag $e-i32 (param i32))
+ (tag $e-i32 (param i32))
+
+ ;; CHECK: (func $foo (type $0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $foo)
+
+ ;; CHECK: (func $try_table_unreachable (type $0)
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: )
+ (func $try_table_unreachable
+ (block $catch
+ (try_table (catch_all $catch)
+ (unreachable)
+ )
+ )
+ (call $foo) ;; shouldn't be dce'd
+ )
+
+ ;; CHECK: (func $catch_unreachable (type $0)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call $foo)
+ ;; CHECK-NEXT: )
+ (func $catch_unreachable
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (br $tryend)
+ )
+ )
+ (unreachable)
+ )
+ (call $foo) ;; shouldn't be dce'd
+ )
+
+ ;; CHECK: (func $both_unreachable (type $0)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $both_unreachable
+ (block $tryend
+ (block $catch
+ (try_table (catch_all $catch)
+ (unreachable)
+ )
+ )
+ (unreachable)
+ )
+ (call $foo) ;; should be dce'd
+ )
+
+ ;; CHECK: (func $throw (type $0)
+ ;; CHECK-NEXT: (block $label$0
+ ;; CHECK-NEXT: (block $label$1
+ ;; CHECK-NEXT: (throw $e)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $throw
+ ;; All these wrapping expressions before 'throw' will be dce'd.
+ (drop
+ (block $label$0 (result externref)
+ (if
+ (i32.clz
+ (block $label$1 (result i32)
+ (throw $e)
+ )
+ )
+ (then
+ (nop)
+ )
+ )
+ (ref.null extern)
+ )
+ )
+ )
+
+ ;; CHECK: (func $throw_ref (type $0)
+ ;; CHECK-NEXT: (local $ex exnref)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (local.set $ex
+ ;; CHECK-NEXT: (block $catch (result exnref)
+ ;; CHECK-NEXT: (try_table (catch_all_ref $catch)
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (throw_ref
+ ;; CHECK-NEXT: (local.get $ex)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $throw_ref
+ (local $ex exnref)
+ (block $tryend
+ (local.set $ex
+ (block $catch (result exnref)
+ (try_table (catch_all_ref $catch)
+ (br $tryend)
+ )
+ )
+ )
+ (drop
+ ;; This i32.add will be dce'd.
+ (i32.add
+ (i32.const 0)
+ (throw_ref (local.get $ex))
+ )
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/global-effects-eh-legacy.wast b/test/lit/passes/global-effects-eh-legacy.wast
new file mode 100644
index 000000000..7390f996d
--- /dev/null
+++ b/test/lit/passes/global-effects-eh-legacy.wast
@@ -0,0 +1,507 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; Run without global effects, and run with, and also run with but discard them
+;; first (to check that discard works; that should be the same as without).
+
+;; RUN: foreach %s %t wasm-opt -all --vacuum -S -o - | filecheck %s --check-prefix WITHOUT
+;; RUN: foreach %s %t wasm-opt -all --generate-global-effects --vacuum -S -o - | filecheck %s --check-prefix INCLUDE
+;; RUN: foreach %s %t wasm-opt -all --generate-global-effects --discard-global-effects --vacuum -S -o - | filecheck %s --check-prefix WITHOUT
+
+(module
+
+ ;; WITHOUT: (type $void (func))
+ ;; INCLUDE: (type $void (func))
+ (type $void (func))
+
+ ;; WITHOUT: (type $1 (func (result i32)))
+
+ ;; WITHOUT: (type $2 (func (param i32)))
+
+ ;; WITHOUT: (import "a" "b" (func $import (type $void)))
+ ;; INCLUDE: (type $1 (func (result i32)))
+
+ ;; INCLUDE: (type $2 (func (param i32)))
+
+ ;; INCLUDE: (import "a" "b" (func $import (type $void)))
+ (import "a" "b" (func $import))
+
+ ;; WITHOUT: (table $t 0 funcref)
+ ;; INCLUDE: (table $t 0 funcref)
+ (table $t 0 funcref)
+
+ ;; WITHOUT: (elem declare func $throw)
+
+ ;; WITHOUT: (tag $tag)
+ ;; INCLUDE: (elem declare func $throw)
+
+ ;; INCLUDE: (tag $tag)
+ (tag $tag)
+
+ ;; WITHOUT: (func $main (type $void)
+ ;; WITHOUT-NEXT: (call $nop)
+ ;; WITHOUT-NEXT: (call $unreachable)
+ ;; WITHOUT-NEXT: (call $call-nop)
+ ;; WITHOUT-NEXT: (call $call-unreachable)
+ ;; WITHOUT-NEXT: (drop
+ ;; WITHOUT-NEXT: (call $unimportant-effects)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (call $throw)
+ ;; WITHOUT-NEXT: (call $throw-and-import)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $main (type $void)
+ ;; INCLUDE-NEXT: (call $unreachable)
+ ;; INCLUDE-NEXT: (call $call-unreachable)
+ ;; INCLUDE-NEXT: (call $throw)
+ ;; INCLUDE-NEXT: (call $throw-and-import)
+ ;; INCLUDE-NEXT: )
+ (func $main
+ ;; Calling a function with no effects can be optimized away in INCLUDE (but
+ ;; not WITHOUT or DISCARD, where the global effect info is not available).
+ (call $nop)
+ ;; Calling a function with effects cannot.
+ (call $unreachable)
+ ;; Calling something that calls something with no effects can be optimized
+ ;; away, since we compute transitive effects
+ (call $call-nop)
+ ;; Calling something that calls something with effects cannot.
+ (call $call-unreachable)
+ ;; Calling something that only has unimportant effects can be optimized
+ ;; (see below for details).
+ (drop
+ (call $unimportant-effects)
+ )
+ ;; A throwing function cannot be removed.
+ (call $throw)
+ ;; A function that throws and calls an import definitely cannot be removed.
+ (call $throw-and-import)
+ )
+
+ ;; WITHOUT: (func $cycle (type $void)
+ ;; WITHOUT-NEXT: (call $cycle)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $cycle (type $void)
+ ;; INCLUDE-NEXT: (call $cycle)
+ ;; INCLUDE-NEXT: )
+ (func $cycle
+ ;; Calling a function with no effects in a cycle cannot be optimized out -
+ ;; this must keep hanging forever.
+ (call $cycle)
+ )
+
+ ;; WITHOUT: (func $cycle-1 (type $void)
+ ;; WITHOUT-NEXT: (call $cycle-2)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $cycle-1 (type $void)
+ ;; INCLUDE-NEXT: (call $cycle-2)
+ ;; INCLUDE-NEXT: )
+ (func $cycle-1
+ ;; $cycle-1 and -2 form a cycle together, in which no call can be removed.
+ (call $cycle-2)
+ )
+
+ ;; WITHOUT: (func $cycle-2 (type $void)
+ ;; WITHOUT-NEXT: (call $cycle-1)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $cycle-2 (type $void)
+ ;; INCLUDE-NEXT: (call $cycle-1)
+ ;; INCLUDE-NEXT: )
+ (func $cycle-2
+ (call $cycle-1)
+ )
+
+ ;; WITHOUT: (func $nop (type $void)
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $nop (type $void)
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ (func $nop
+ (nop)
+ )
+
+ ;; WITHOUT: (func $unreachable (type $void)
+ ;; WITHOUT-NEXT: (unreachable)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $unreachable (type $void)
+ ;; INCLUDE-NEXT: (unreachable)
+ ;; INCLUDE-NEXT: )
+ (func $unreachable
+ (unreachable)
+ )
+
+ ;; WITHOUT: (func $call-nop (type $void)
+ ;; WITHOUT-NEXT: (call $nop)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-nop (type $void)
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ (func $call-nop
+ ;; This call to a nop can be optimized out, as above, in INCLUDE.
+ (call $nop)
+ )
+
+ ;; WITHOUT: (func $call-unreachable (type $void)
+ ;; WITHOUT-NEXT: (call $unreachable)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-unreachable (type $void)
+ ;; INCLUDE-NEXT: (call $unreachable)
+ ;; INCLUDE-NEXT: )
+ (func $call-unreachable
+ (call $unreachable)
+ )
+
+ ;; WITHOUT: (func $unimportant-effects (type $1) (result i32)
+ ;; WITHOUT-NEXT: (local $x i32)
+ ;; WITHOUT-NEXT: (local.set $x
+ ;; WITHOUT-NEXT: (i32.const 100)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (return
+ ;; WITHOUT-NEXT: (local.get $x)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $unimportant-effects (type $1) (result i32)
+ ;; INCLUDE-NEXT: (local $x i32)
+ ;; INCLUDE-NEXT: (local.set $x
+ ;; INCLUDE-NEXT: (i32.const 100)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (return
+ ;; INCLUDE-NEXT: (local.get $x)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ (func $unimportant-effects (result i32)
+ (local $x i32)
+ ;; Operations on locals should not prevent optimization, as when we return
+ ;; from the function they no longer matter.
+ (local.set $x
+ (i32.const 100)
+ )
+ ;; A return is an effect that no longer matters once we exit the function.
+ (return
+ (local.get $x)
+ )
+ )
+
+ ;; WITHOUT: (func $call-throw-and-catch (type $void)
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $throw)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $throw-and-import)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-throw-and-catch (type $void)
+ ;; INCLUDE-NEXT: (try
+ ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (call $throw-and-import)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (catch_all
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ (func $call-throw-and-catch
+ (try
+ (do
+ ;; This call cannot be optimized out, as the target throws. However, the
+ ;; entire try-catch can be, since the call's only effect is to throw,
+ ;; and the catch_all catches that.
+ (call $throw)
+ )
+ (catch_all)
+ )
+ (try
+ (do
+ ;; This call both throws and calls an import, and cannot be removed.
+ (call $throw-and-import)
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $return-call-throw-and-catch (type $void)
+ ;; WITHOUT-NEXT: (return_call $throw)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $return-call-throw-and-catch (type $void)
+ ;; INCLUDE-NEXT: (return_call $throw)
+ ;; INCLUDE-NEXT: )
+ (func $return-call-throw-and-catch
+ (try
+ (do
+ ;; This call cannot be optimized out, as the target throws. However, the
+ ;; surrounding try-catch can be removed even without global effects
+ ;; because the throw from the return_call is never observed by this
+ ;; try-catch.
+ (return_call $throw)
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $return-call-indirect-throw-and-catch (type $void)
+ ;; WITHOUT-NEXT: (return_call_indirect $t (type $void)
+ ;; WITHOUT-NEXT: (i32.const 0)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $return-call-indirect-throw-and-catch (type $void)
+ ;; INCLUDE-NEXT: (return_call_indirect $t (type $void)
+ ;; INCLUDE-NEXT: (i32.const 0)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ (func $return-call-indirect-throw-and-catch
+ (try
+ (do
+ ;; This call cannot be optimized out, as the target may throw. However,
+ ;; the surrounding try-catch can be removed even without global effects
+ ;; because the throw from the return_call is never observed by this
+ ;; try-catch.
+ (return_call_indirect
+ (i32.const 0)
+ )
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $return-call-ref-throw-and-catch (type $void)
+ ;; WITHOUT-NEXT: (return_call_ref $void
+ ;; WITHOUT-NEXT: (ref.func $throw)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $return-call-ref-throw-and-catch (type $void)
+ ;; INCLUDE-NEXT: (return_call_ref $void
+ ;; INCLUDE-NEXT: (ref.func $throw)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ (func $return-call-ref-throw-and-catch
+ (try
+ (do
+ ;; This call cannot be optimized out, as the target may throw. However,
+ ;; the surrounding try-catch can be removed even without global effects
+ ;; because the throw from the return_call is never observed by this
+ ;; try-catch.
+ (return_call_ref $void
+ (ref.func $throw)
+ )
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $call-return-call-throw-and-catch (type $void)
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $return-call-throw-and-catch)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (call $return-call-throw-and-catch)
+ ;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch)
+ ;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-return-call-throw-and-catch (type $void)
+ ;; INCLUDE-NEXT: (try
+ ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (catch_all
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (try
+ ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (catch_all
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (call $return-call-throw-and-catch)
+ ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
+ ;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
+ ;; INCLUDE-NEXT: )
+ (func $call-return-call-throw-and-catch
+ (try
+ (do
+ ;; Even though the body of the previous function is a try-catch_all, the
+ ;; function still throws because of its return_call, so this cannot be
+ ;; optimized out, but once again the entire try-catch can be.
+ (call $return-call-throw-and-catch)
+ )
+ (catch_all)
+ )
+ (try
+ (do
+ ;; This would be the same, except since it performs an indirect call, we
+ ;; conservatively assume it could have any effect, so we can't optimize.
+ (call $return-call-indirect-throw-and-catch)
+ )
+ (catch_all)
+ )
+ (try
+ (do
+ ;; Same here.
+ (call $return-call-ref-throw-and-catch)
+ )
+ (catch_all)
+ )
+
+ ;; These cannot be optimized out at all.
+ (call $return-call-throw-and-catch)
+ (call $return-call-indirect-throw-and-catch)
+ (call $return-call-ref-throw-and-catch)
+ )
+
+ ;; WITHOUT: (func $call-unreachable-and-catch (type $void)
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (call $unreachable)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-unreachable-and-catch (type $void)
+ ;; INCLUDE-NEXT: (call $unreachable)
+ ;; INCLUDE-NEXT: )
+ (func $call-unreachable-and-catch
+ (try
+ (do
+ ;; This call has a non-throw effect. We can optimize away the try-catch
+ ;; (since no exception can be thrown anyhow), but we must leave the
+ ;; call.
+ (call $unreachable)
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $call-throw-or-unreachable-and-catch (type $2) (param $x i32)
+ ;; WITHOUT-NEXT: (try
+ ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (if
+ ;; WITHOUT-NEXT: (local.get $x)
+ ;; WITHOUT-NEXT: (then
+ ;; WITHOUT-NEXT: (call $throw)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (else
+ ;; WITHOUT-NEXT: (call $unreachable)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: (catch_all
+ ;; WITHOUT-NEXT: (nop)
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $call-throw-or-unreachable-and-catch (type $2) (param $x i32)
+ ;; INCLUDE-NEXT: (try
+ ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (if
+ ;; INCLUDE-NEXT: (local.get $x)
+ ;; INCLUDE-NEXT: (then
+ ;; INCLUDE-NEXT: (call $throw)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (else
+ ;; INCLUDE-NEXT: (call $unreachable)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (catch_all
+ ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: )
+ (func $call-throw-or-unreachable-and-catch (param $x i32)
+ ;; This try-catch-all's body will either call a throw or an unreachable.
+ ;; Since we have both possible effects, we cannot optimize anything here.
+ (try
+ (do
+ (if
+ (local.get $x)
+ (then
+ (call $throw)
+ )
+ (else
+ (call $unreachable)
+ )
+ )
+ )
+ (catch_all)
+ )
+ )
+
+ ;; WITHOUT: (func $throw (type $void)
+ ;; WITHOUT-NEXT: (throw $tag)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $throw (type $void)
+ ;; INCLUDE-NEXT: (throw $tag)
+ ;; INCLUDE-NEXT: )
+ (func $throw
+ (throw $tag)
+ )
+
+ ;; WITHOUT: (func $throw-and-import (type $void)
+ ;; WITHOUT-NEXT: (throw $tag)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $throw-and-import (type $void)
+ ;; INCLUDE-NEXT: (throw $tag)
+ ;; INCLUDE-NEXT: )
+ (func $throw-and-import
+ (if
+ (i32.const 1)
+ (then
+ (throw $tag)
+ )
+ (else
+ (call $import)
+ )
+ )
+ )
+
+ ;; WITHOUT: (func $cycle-with-unknown-call (type $void)
+ ;; WITHOUT-NEXT: (call $cycle-with-unknown-call)
+ ;; WITHOUT-NEXT: (call $import)
+ ;; WITHOUT-NEXT: )
+ ;; INCLUDE: (func $cycle-with-unknown-call (type $void)
+ ;; INCLUDE-NEXT: (call $cycle-with-unknown-call)
+ ;; INCLUDE-NEXT: (call $import)
+ ;; INCLUDE-NEXT: )
+ (func $cycle-with-unknown-call
+ ;; This function can not only call itself recursively, but also calls an
+ ;; import. We should not remove anything here, and not error during the
+ ;; analysis (this guards against a bug where the import would make us toss
+ ;; away the effects object, and the infinite loop makes us set a property on
+ ;; that object, so it must check the object still exists).
+ (call $cycle-with-unknown-call)
+ (call $import)
+ )
+)
diff --git a/test/lit/passes/global-effects.wast b/test/lit/passes/global-effects.wast
index 7390f996d..c3c6d2073 100644
--- a/test/lit/passes/global-effects.wast
+++ b/test/lit/passes/global-effects.wast
@@ -182,49 +182,44 @@
)
;; WITHOUT: (func $call-throw-and-catch (type $void)
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend)
;; WITHOUT-NEXT: (call $throw)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend0
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend0)
;; WITHOUT-NEXT: (call $throw-and-import)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; INCLUDE: (func $call-throw-and-catch (type $void)
- ;; INCLUDE-NEXT: (try
- ;; INCLUDE-NEXT: (do
- ;; INCLUDE-NEXT: (call $throw-and-import)
+ ;; INCLUDE-NEXT: (block $tryend
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend)
+ ;; INCLUDE-NEXT: (call $throw)
;; INCLUDE-NEXT: )
- ;; INCLUDE-NEXT: (catch_all
- ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (block $tryend0
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend0)
+ ;; INCLUDE-NEXT: (call $throw-and-import)
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
(func $call-throw-and-catch
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call cannot be optimized out, as the target throws. However, the
- ;; entire try-catch can be, since the call's only effect is to throw,
- ;; and the catch_all catches that.
+ ;; entire try_table could be, since the call's only effect is to throw,
+ ;; and the catch_all catches that. We do this for `try` but not yet for
+ ;; `try_table`.
(call $throw)
)
- (catch_all)
)
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call both throws and calls an import, and cannot be removed.
(call $throw-and-import)
)
- (catch_all)
)
)
@@ -235,15 +230,14 @@
;; INCLUDE-NEXT: (return_call $throw)
;; INCLUDE-NEXT: )
(func $return-call-throw-and-catch
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call cannot be optimized out, as the target throws. However, the
- ;; surrounding try-catch can be removed even without global effects
+ ;; surrounding try_table can be removed even without global effects
;; because the throw from the return_call is never observed by this
- ;; try-catch.
+ ;; try_table.
(return_call $throw)
)
- (catch_all)
)
)
@@ -258,17 +252,16 @@
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
(func $return-call-indirect-throw-and-catch
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call cannot be optimized out, as the target may throw. However,
- ;; the surrounding try-catch can be removed even without global effects
+ ;; the surrounding try_table can be removed even without global effects
;; because the throw from the return_call is never observed by this
;; try-catch.
(return_call_indirect
(i32.const 0)
)
)
- (catch_all)
)
)
@@ -283,94 +276,81 @@
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
(func $return-call-ref-throw-and-catch
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call cannot be optimized out, as the target may throw. However,
- ;; the surrounding try-catch can be removed even without global effects
+ ;; the surrounding try_table can be removed even without global effects
;; because the throw from the return_call is never observed by this
;; try-catch.
(return_call_ref $void
(ref.func $throw)
)
)
- (catch_all)
)
)
;; WITHOUT: (func $call-return-call-throw-and-catch (type $void)
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend)
;; WITHOUT-NEXT: (call $return-call-throw-and-catch)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend0
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend0)
;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend1
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend1)
;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: (call $return-call-throw-and-catch)
;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch)
;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch)
;; WITHOUT-NEXT: )
;; INCLUDE: (func $call-return-call-throw-and-catch (type $void)
- ;; INCLUDE-NEXT: (try
- ;; INCLUDE-NEXT: (do
- ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
+ ;; INCLUDE-NEXT: (block $tryend
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend)
+ ;; INCLUDE-NEXT: (call $return-call-throw-and-catch)
;; INCLUDE-NEXT: )
- ;; INCLUDE-NEXT: (catch_all
- ;; INCLUDE-NEXT: (nop)
+ ;; INCLUDE-NEXT: )
+ ;; INCLUDE-NEXT: (block $tryend0
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend0)
+ ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
- ;; INCLUDE-NEXT: (try
- ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (block $tryend1
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend1)
;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
;; INCLUDE-NEXT: )
- ;; INCLUDE-NEXT: (catch_all
- ;; INCLUDE-NEXT: (nop)
- ;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: (call $return-call-throw-and-catch)
;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch)
;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch)
;; INCLUDE-NEXT: )
(func $call-return-call-throw-and-catch
- (try
- (do
- ;; Even though the body of the previous function is a try-catch_all, the
+ (block $tryend
+ (try_table (catch_all $tryend)
+ ;; Even though the body of the previous function has a catch_all, the
;; function still throws because of its return_call, so this cannot be
- ;; optimized out, but once again the entire try-catch can be.
+ ;; optimized out, but once again the entire try_table could be. Again,
+ ;; this is something we do for `try` for not yet for `try_table`.
(call $return-call-throw-and-catch)
)
- (catch_all)
)
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This would be the same, except since it performs an indirect call, we
;; conservatively assume it could have any effect, so we can't optimize.
(call $return-call-indirect-throw-and-catch)
)
- (catch_all)
)
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; Same here.
(call $return-call-ref-throw-and-catch)
)
- (catch_all)
)
;; These cannot be optimized out at all.
@@ -380,33 +360,29 @@
)
;; WITHOUT: (func $call-unreachable-and-catch (type $void)
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend)
;; WITHOUT-NEXT: (call $unreachable)
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; INCLUDE: (func $call-unreachable-and-catch (type $void)
;; INCLUDE-NEXT: (call $unreachable)
;; INCLUDE-NEXT: )
(func $call-unreachable-and-catch
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
;; This call has a non-throw effect. We can optimize away the try-catch
;; (since no exception can be thrown anyhow), but we must leave the
;; call.
(call $unreachable)
)
- (catch_all)
)
)
;; WITHOUT: (func $call-throw-or-unreachable-and-catch (type $2) (param $x i32)
- ;; WITHOUT-NEXT: (try
- ;; WITHOUT-NEXT: (do
+ ;; WITHOUT-NEXT: (block $tryend
+ ;; WITHOUT-NEXT: (try_table (catch_all $tryend)
;; WITHOUT-NEXT: (if
;; WITHOUT-NEXT: (local.get $x)
;; WITHOUT-NEXT: (then
@@ -417,14 +393,11 @@
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
- ;; WITHOUT-NEXT: (catch_all
- ;; WITHOUT-NEXT: (nop)
- ;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; WITHOUT-NEXT: )
;; INCLUDE: (func $call-throw-or-unreachable-and-catch (type $2) (param $x i32)
- ;; INCLUDE-NEXT: (try
- ;; INCLUDE-NEXT: (do
+ ;; INCLUDE-NEXT: (block $tryend
+ ;; INCLUDE-NEXT: (try_table (catch_all $tryend)
;; INCLUDE-NEXT: (if
;; INCLUDE-NEXT: (local.get $x)
;; INCLUDE-NEXT: (then
@@ -435,16 +408,13 @@
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
- ;; INCLUDE-NEXT: (catch_all
- ;; INCLUDE-NEXT: (nop)
- ;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
;; INCLUDE-NEXT: )
(func $call-throw-or-unreachable-and-catch (param $x i32)
- ;; This try-catch-all's body will either call a throw or an unreachable.
+ ;; This try_table's body will either call a throw or an unreachable.
;; Since we have both possible effects, we cannot optimize anything here.
- (try
- (do
+ (block $tryend
+ (try_table (catch_all $tryend)
(if
(local.get $x)
(then
@@ -455,7 +425,6 @@
)
)
)
- (catch_all)
)
)
diff --git a/test/lit/passes/gufa-eh.wast b/test/lit/passes/gufa-eh.wast
new file mode 100644
index 000000000..79265a318
--- /dev/null
+++ b/test/lit/passes/gufa-eh.wast
@@ -0,0 +1,60 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt -all --gufa -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $0 (func (param i32)))
+
+ ;; CHECK: (type $1 (func (result i32)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (tag $e (param i32))
+ (tag $e (param i32))
+
+ ;; CHECK: (func $try_table-target-block-is-not-unreachable (type $1) (result i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e $catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $try_table-target-block-is-not-unreachable (result i32)
+ ;; Ensure that try_table connects caught tags with their branch targets.
+ (block $catch (result i32)
+ (try_table (catch $e $catch)
+ (throw $e (i32.const 0))
+ )
+ )
+ )
+
+ ;; CHECK: (func $try_table-materializes-exnref (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result exnref)
+ ;; CHECK-NEXT: (try_table (catch_all_ref $catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-materializes-exnref
+ ;; Ensure that catch_all_ref materializes a non-null exnref value. If we do
+ ;; not connect a non-null exnref value to the branch target, GUFA will think
+ ;; no value can possibly get out of that block, and will insert an
+ ;; unreachable instruction after the block.
+ (drop
+ (block $catch (result exnref)
+ (try_table (catch_all_ref $catch)
+ (throw $e (i32.const 0))
+ )
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast
index 582c24f23..2985102e1 100644
--- a/test/lit/passes/local-subtyping.wast
+++ b/test/lit/passes/local-subtyping.wast
@@ -24,6 +24,9 @@
;; CHECK: (import "out" "i64" (func $i64 (type $6) (result i64)))
(import "out" "i64" (func $i64 (result i64)))
+ ;; CHECK: (tag $e-anyref (param anyref))
+ (tag $e-anyref (param anyref))
+
;; Refinalization can find a more specific type, where the declared type was
;; not the optimal LUB.
;; CHECK: (func $refinalize (type $2) (param $x i32)
@@ -80,7 +83,7 @@
;; A simple case where a local has a single assignment that we can use as a
;; more specific type. A similar thing with a parameter, however, is not a
;; thing we can optimize. Also, ignore a local with zero assignments.
- ;; CHECK: (func $simple-local-but-not-param (type $7) (param $x funcref)
+ ;; CHECK: (func $simple-local-but-not-param (type $8) (param $x funcref)
;; CHECK-NEXT: (local $y (ref $1))
;; CHECK-NEXT: (local $unused funcref)
;; CHECK-NEXT: (local.set $x
@@ -101,7 +104,7 @@
)
)
- ;; CHECK: (func $locals-with-multiple-assignments (type $8) (param $struct structref)
+ ;; CHECK: (func $locals-with-multiple-assignments (type $9) (param $struct structref)
;; CHECK-NEXT: (local $x eqref)
;; CHECK-NEXT: (local $y (ref i31))
;; CHECK-NEXT: (local $z structref)
@@ -568,4 +571,49 @@
(local.get $x)
)
)
+
+ ;; CHECK: (func $try_table-catch-result (type $0)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result anyref)
+ ;; CHECK-NEXT: (try_table (catch $e-anyref $catch)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-catch-result
+ (drop
+ ;; Must not be refined to (result nullref).
+ (block $catch (result anyref)
+ (try_table (catch $e-anyref $catch)
+ (nop)
+ )
+ (ref.null none)
+ )
+ )
+ )
+
+ ;; CHECK: (func $try_table-ref (type $0)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result exnref)
+ ;; CHECK-NEXT: (try_table (catch_all_ref $catch)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null noexn)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try_table-ref
+ (drop
+ ;; Must not be refined to nullexnref.
+ ;; An exnref comes from the catch_all_ref.
+ (block $catch (result exnref)
+ (try_table (catch_all_ref $catch)
+ (nop)
+ )
+ (ref.null exn)
+ )
+ )
+ )
)
diff --git a/test/lit/passes/simplify-locals-eh-legacy.wast b/test/lit/passes/simplify-locals-eh-legacy.wast
index 7b48707d4..d7fb75776 100644
--- a/test/lit/passes/simplify-locals-eh-legacy.wast
+++ b/test/lit/passes/simplify-locals-eh-legacy.wast
@@ -4,7 +4,7 @@
(module
;; CHECK: (tag $e-i32 (param i32))
(tag $e-i32 (param i32))
- ;; CHECK: (func $foo (type $2) (param $0 i32) (param $1 i32)
+ ;; CHECK: (func $foo (type $3) (param $0 i32) (param $1 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $foo (param i32 i32))
@@ -83,7 +83,7 @@
)
)
- ;; CHECK: (func $bar (type $3) (result i32)
+ ;; CHECK: (func $bar (type $1) (result i32)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
(func $bar (result i32) (i32.const 3))
@@ -152,7 +152,7 @@
)
)
- ;; CHECK: (func $return-call-can-be-sinked-into-try (type $3) (result i32)
+ ;; CHECK: (func $return-call-can-be-sinked-into-try (type $1) (result i32)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (try (result i32)
@@ -202,103 +202,4 @@
)
)
)
-
- ;; CHECK: (func $equivalent-set-removal-call (type $1) (param $0 i32)
- ;; CHECK-NEXT: (local $1 i32)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (call $equivalent-set-removal-call
- ;; CHECK-NEXT: (i32.const 2)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $equivalent-set-removal-call (param $0 i32)
- (local $1 i32)
- (local.set $1 (local.get $0))
- (drop (local.get $0))
- (drop (local.get $1))
- ;; Even with EH enabled we can look past the call and optimize the final 1
- ;; to a 0, since they contain the same (and while the call might branch,
- ;; such a branch does not cause a problem here, as if we branch we just
- ;; don't reach the change later down).
- (call $equivalent-set-removal-call
- (i32.const 2)
- )
- (drop (local.get $0))
- (drop (local.get $1))
- )
-
- ;; CHECK: (func $equivalent-set-removal-if (type $2) (param $p i32) (param $0 i32)
- ;; CHECK-NEXT: (local $1 i32)
- ;; CHECK-NEXT: (nop)
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (local.set $1
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (if
- ;; CHECK-NEXT: (local.get $p)
- ;; CHECK-NEXT: (then
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (else
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $0)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- (func $equivalent-set-removal-if (param $p i32) (param $0 i32)
- (local $1 i32)
- (local.set $1 (local.get $0))
- (drop (local.get $0))
- ;; This local.get of 1 can be of 0.
- (drop (local.get $1))
- (if
- (local.get $p)
- (then
- (block
- ;; We also optimize in this block, which is adjacent to the code before
- ;; us. It is valid to optimize the 1 to a 0 here, as it is dominated by
- ;; the code earlier.
- (drop (local.get $0))
- (drop (local.get $1))
- )
- )
- (else
- (block
- ;; We could also optimize here, but atm just look at code adjacent to
- ;; its dominator. TODO
- (drop (local.get $0))
- (drop (local.get $1))
- )
- )
- )
- ;; As in the else, this could be optimized. TODO
- (drop (local.get $0))
- (drop (local.get $1))
- )
)
diff --git a/test/lit/passes/simplify-locals-eh.wast b/test/lit/passes/simplify-locals-eh.wast
new file mode 100644
index 000000000..f455bd4e5
--- /dev/null
+++ b/test/lit/passes/simplify-locals-eh.wast
@@ -0,0 +1,226 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --simplify-locals -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (tag $e-i32 (param i32))
+ (tag $e-i32 (param i32))
+
+ ;; CHECK: (func $bar (type $1) (result i32)
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ (func $bar (result i32) (i32.const 3))
+
+ ;; CHECK: (func $call-cannot-be-sinked-into-try_table (type $2)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (call $bar)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e-i32 $catch)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-cannot-be-sinked-into-try_table (local $0 i32)
+ (drop
+ ;; This local.tee should NOT be sinked into 'try_table' below, because it
+ ;; may throw
+ (local.tee $0 (call $bar))
+ )
+ (block $tryend
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $e-i32 $catch)
+ (drop (local.get $0))
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $non-call-can-be-sinked-into-try_table (type $2)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (block $tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e-i32 $catch)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $non-call-can-be-sinked-into-try_table (local $0 i32)
+ (drop
+ ;; This local.tee can be sinked into 'try_table' below, because it cannot
+ ;; throw
+ (local.tee $0 (i32.const 3))
+ )
+ (block $tryend
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $e-i32 $catch)
+ (drop (local.get $0))
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $return-call-can-be-sinked-into-try_table (type $1) (result i32)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (block $tryend (result i32)
+ ;; CHECK-NEXT: (try_table (result i32) (catch $e-i32 $tryend)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (if (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (return_call $return-call-can-be-sinked-into-try_table)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-call-can-be-sinked-into-try_table (result i32)
+ (local $0 i32)
+ (drop
+ ;; This cannot throw either, so it can be sunk. Wrap the return_call in an
+ ;; if so the whole expression does not return unconditionally.
+ (local.tee $0
+ (if (result i32)
+ (i32.const 0)
+ (then
+ (return_call $return-call-can-be-sinked-into-try_table)
+ )
+ (else
+ (i32.const 1)
+ )
+ )
+ )
+ )
+ (block $tryend (result i32)
+ (try_table (result i32) (catch $e-i32 $tryend)
+ (drop (local.get $0))
+ (i32.const 0)
+ )
+ )
+ )
+
+ ;; CHECK: (func $equivalent-set-removal-call (type $0) (param $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (call $equivalent-set-removal-call
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $equivalent-set-removal-call (param $0 i32)
+ (local $1 i32)
+ (local.set $1 (local.get $0))
+ (drop (local.get $0))
+ (drop (local.get $1))
+ ;; Even with EH enabled we can look past the call and optimize the final 1
+ ;; to a 0, since they contain the same (and while the call might branch,
+ ;; such a branch does not cause a problem here, as if we branch we just
+ ;; don't reach the change later down).
+ (call $equivalent-set-removal-call
+ (i32.const 2)
+ )
+ (drop (local.get $0))
+ (drop (local.get $1))
+ )
+
+ ;; CHECK: (func $equivalent-set-removal-if (type $3) (param $p i32) (param $0 i32)
+ ;; CHECK-NEXT: (local $1 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $1
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $p)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.get $1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $equivalent-set-removal-if (param $p i32) (param $0 i32)
+ (local $1 i32)
+ (local.set $1 (local.get $0))
+ (drop (local.get $0))
+ ;; This local.get of 1 can be of 0.
+ (drop (local.get $1))
+ (if
+ (local.get $p)
+ (then
+ (block
+ ;; We also optimize in this block, which is adjacent to the code before
+ ;; us. It is valid to optimize the 1 to a 0 here, as it is dominated by
+ ;; the code earlier.
+ (drop (local.get $0))
+ (drop (local.get $1))
+ )
+ )
+ (else
+ (block
+ ;; We could also optimize here, but atm just look at code adjacent to
+ ;; its dominator. TODO
+ (drop (local.get $0))
+ (drop (local.get $1))
+ )
+ )
+ )
+ ;; As in the else, this could be optimized. TODO
+ (drop (local.get $0))
+ (drop (local.get $1))
+ )
+)
diff --git a/test/lit/passes/vacuum-eh-legacy.wast b/test/lit/passes/vacuum-eh-legacy.wast
index 125475e26..0eb53bb56 100644
--- a/test/lit/passes/vacuum-eh-legacy.wast
+++ b/test/lit/passes/vacuum-eh-legacy.wast
@@ -6,14 +6,13 @@
(type $void (func))
;; CHECK: (table $t 0 funcref)
+ (table $t 0 funcref)
;; CHECK: (tag $e (param i32))
(tag $e (param i32))
;; CHECK: (tag $e2 (param i32))
(tag $e2 (param i32))
- (table $t 0 funcref)
-
;; CHECK: (func $try-test (type $void)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
diff --git a/test/lit/passes/vacuum-eh.wast b/test/lit/passes/vacuum-eh.wast
new file mode 100644
index 000000000..ec62f3c58
--- /dev/null
+++ b/test/lit/passes/vacuum-eh.wast
@@ -0,0 +1,235 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+;; RUN: wasm-opt %s --vacuum -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $void (func))
+ (type $void (func))
+
+ ;; CHECK: (table $t 0 funcref)
+ (table $t 0 funcref)
+
+ ;; CHECK: (tag $e (param i32))
+ (tag $e (param i32))
+ ;; CHECK: (tag $e2 (param i32))
+ (tag $e2 (param i32))
+
+ ;; CHECK: (func $try_table-test (type $void)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $try_table-test
+ ;; When try_table body does not throw, the try_table can be replaced with
+ ;; its body
+ (block $tryend
+ (drop
+ (block $catch (result i32)
+ (try_table (catch $e $catch)
+ (drop (i32.const 0))
+ )
+ (br $tryend)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $inner-try_table-catch_all-test (type $2) (result i32)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (block $inner-catch
+ ;; CHECK-NEXT: (try_table (catch_all $inner-catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: )
+ (func $inner-try_table-catch_all-test (result i32)
+ (local $0 i32)
+ ;; The exception thrown in the inner try_table is caught by the inner
+ ;; catch_all, so the outer try_table body does not throw and the outer
+ ;; try_table can be removed
+ (block $outer-tryend
+ (drop
+ (block $outer-catch (result i32)
+ (try_table (catch $e $outer-catch)
+ (block $inner-catch
+ (try_table (catch_all $inner-catch)
+ (throw $e (i32.const 0))
+ )
+ (unreachable)
+ )
+ (return (i32.const 1))
+ )
+ (br $outer-tryend)
+ )
+ )
+ )
+ (i32.const 2)
+ )
+
+ ;; CHECK: (func $inner-try_table-catch-test (type $void)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (block $outer-tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $outer-catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e $outer-catch)
+ ;; CHECK-NEXT: (block $inner-tryend
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $inner-catch (result i32)
+ ;; CHECK-NEXT: (try_table (catch $e $inner-catch)
+ ;; CHECK-NEXT: (throw $e2
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $0
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (br $outer-tryend)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $inner-try_table-catch-test (local $0 i32)
+ ;; The exception thrown in the inner try_table will not be caught by the
+ ;; inner catch, so the outer try_table cannot be removed.
+ (block $outer-tryend
+ (drop
+ (block $outer-catch (result i32)
+ (try_table (catch $e $outer-catch)
+ (block $inner-tryend
+ (drop
+ (block $inner-catch (result i32)
+ (try_table (catch $e $inner-catch)
+ (throw $e2 (i32.const 0))
+ )
+ (br $inner-tryend)
+ )
+ )
+ (local.set $0 (i32.const 1))
+ )
+ )
+ (br $outer-tryend)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $trivial-catch-all-of-throw (type $void)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (block $catch
+ ;; CHECK-NEXT: (try_table (catch_all $catch)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block $catch0
+ ;; CHECK-NEXT: (try_table (catch_all $catch0)
+ ;; CHECK-NEXT: (if
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (then
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (else
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $trivial-catch-all-of-throw (local $0 i32)
+ ;; This try_table's body throws, but the catch_all catches it, so the entire
+ ;; try_table could be optimized out. We do this for `try` but not yet for
+ ;; `try_table`.
+ (block $catch
+ (try_table (catch_all $catch)
+ (throw $e (i32.const 0))
+ )
+ )
+ ;; Here we also have a possible trap, so we can't do it.
+ (block $catch
+ (try_table (catch_all $catch)
+ (if
+ (local.get $0)
+ (then
+ (throw $e (i32.const 0))
+ )
+ (else
+ (unreachable)
+ )
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $throw (type $void)
+ ;; CHECK-NEXT: (throw $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $throw
+ ;; Helper for the tail call tests below.
+ (throw $e
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $return-call-catch (type $void)
+ ;; CHECK-NEXT: (return_call $throw)
+ ;; CHECK-NEXT: )
+ (func $return-call-catch
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; This returns before it throws, so we can optimize out the surrounding
+ ;; try_table.
+ (return_call $throw)
+ )
+ )
+ )
+
+ ;; CHECK: (func $return-call-indirect-catch (type $void)
+ ;; CHECK-NEXT: (return_call_indirect $t (type $void)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-call-indirect-catch
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; This returns before it throws, so we can optimize out the surrounding
+ ;; try_table.
+ (return_call_indirect
+ (i32.const 0)
+ )
+ )
+ )
+ )
+
+ ;; CHECK: (func $return-call-ref-catch (type $void)
+ ;; CHECK-NEXT: (return_call_ref $void
+ ;; CHECK-NEXT: (ref.func $throw)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-call-ref-catch
+ (block $catch
+ (try_table (catch_all $catch)
+ ;; This returns before it throws, so we can optimize out the surrounding
+ ;; try_table.
+ (return_call_ref $void
+ (ref.func $throw)
+ )
+ )
+ )
+ )
+)