Skip to content

Commit ad723a1

Browse files
authored
[EH][GC] Fix nested pop after removing ref.cast (#4407)
`ref.cast` can be statically removed when the ref's type is a subtype of the intended RTT type and either of `--ignore-implicit-traps` or `--traps-never-happen` is given: https://github.com/WebAssembly/binaryen/blob/083ab9842ec3d4ca278c95e1a33112ae7cd4d9e5/src/passes/OptimizeInstructions.cpp#L1603-L1624 Some more context: #4097 (comment) But this can create a block in which a `pop` is nested, which makes the `catch` invalid. The test in this PR is the same as the example given by @kripken in #4237. This calls the fixup function `EHUtils::handleBlockNestedPops` at the end of the pass to fix this. Also, because this pass creates a lot of blocks in other patterns, I think it is possible there can be other patterns to cause this kind of `pop` nesting.
1 parent 3352e23 commit ad723a1

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

src/passes/OptimizeInstructions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <ir/bits.h>
2727
#include <ir/cost.h>
2828
#include <ir/effects.h>
29+
#include <ir/eh-utils.h>
2930
#include <ir/find_all.h>
3031
#include <ir/gc-type-utils.h>
3132
#include <ir/iteration.h>
@@ -229,6 +230,9 @@ struct OptimizeInstructions
229230
// Some patterns create locals (like when we use getResultOfFirst), which we
230231
// may need to fix up.
231232
TypeUpdating::handleNonDefaultableLocals(func, *getModule());
233+
// Some patterns create blocks that can interfere 'catch' and 'pop', nesting
234+
// the 'pop' into a block making it invalid.
235+
EHUtils::handleBlockNestedPops(func, *getModule());
232236
}
233237

234238
// Set to true when one of the visitors makes a change (either replacing the
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
;; RUN: wasm-opt %s --ignore-implicit-traps --optimize-instructions -all -S -o - \
3+
;; RUN: | filecheck %s
4+
5+
(module
6+
;; CHECK: (type $struct.A (struct (field i32)))
7+
(type $struct.A (struct i32))
8+
;; CHECK: (global $g-struct.A (rtt $struct.A) (rtt.canon $struct.A))
9+
10+
;; CHECK: (tag $e (param (ref null $struct.A)))
11+
(tag $e (param (ref null $struct.A)))
12+
(global $g-struct.A (rtt $struct.A) (rtt.canon $struct.A))
13+
14+
;; CHECK: (func $ref-cast-statically-removed
15+
;; CHECK-NEXT: (local $0 (ref null $struct.A))
16+
;; CHECK-NEXT: (local $1 (ref null $struct.A))
17+
;; CHECK-NEXT: (try $try
18+
;; CHECK-NEXT: (do
19+
;; CHECK-NEXT: (nop)
20+
;; CHECK-NEXT: )
21+
;; CHECK-NEXT: (catch $e
22+
;; CHECK-NEXT: (local.set $1
23+
;; CHECK-NEXT: (pop (ref null $struct.A))
24+
;; CHECK-NEXT: )
25+
;; CHECK-NEXT: (throw $e
26+
;; CHECK-NEXT: (block (result (ref null $struct.A))
27+
;; CHECK-NEXT: (local.set $0
28+
;; CHECK-NEXT: (local.get $1)
29+
;; CHECK-NEXT: )
30+
;; CHECK-NEXT: (drop
31+
;; CHECK-NEXT: (global.get $g-struct.A)
32+
;; CHECK-NEXT: )
33+
;; CHECK-NEXT: (local.get $0)
34+
;; CHECK-NEXT: )
35+
;; CHECK-NEXT: )
36+
;; CHECK-NEXT: )
37+
;; CHECK-NEXT: )
38+
;; CHECK-NEXT: )
39+
(func $ref-cast-statically-removed
40+
(try
41+
(do)
42+
(catch $e
43+
(throw $e
44+
;; Because --ignore-implicit-traps is given, this ref.cast is assumed
45+
;; not to throw so this ref.cast can be statically removed. But that
46+
;; creates a block around this, making 'pop' nested into it, which is
47+
;; invalid. We fix this up at the end up OptimizeInstruction,
48+
;; assigning the 'pop' to a local at the start of this 'catch' body
49+
;; and later using 'local.get' to get it.
50+
(ref.cast
51+
(pop (ref null $struct.A))
52+
(global.get $g-struct.A)
53+
)
54+
)
55+
)
56+
)
57+
)
58+
)

0 commit comments

Comments
 (0)