Skip to content

Commit 0fe26e7

Browse files
authored
[Wasm GC] Optimize static casts in br_on_cast* (#4520)
We were missing this particular case, which we can in fact handle when the cast is static.
1 parent 11ada63 commit 0fe26e7

File tree

2 files changed

+116
-3
lines changed

2 files changed

+116
-3
lines changed

src/ir/gc-type-utils.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,19 @@ inline EvaluationResult evaluateKindCheck(Expression* curr) {
5252
// We don't check nullability here.
5353
case BrOnNull:
5454
case BrOnNonNull:
55-
// Casts can only be known at runtime using RTTs.
56-
case BrOnCast:
5755
case BrOnCastFail:
56+
flip = true;
57+
[[fallthrough]];
58+
case BrOnCast:
59+
if (!br->rtt) {
60+
// This is a static cast check, which we may be able to resolve at
61+
// compile time. Note that the type must be non-nullable for us to
62+
// succeed at that inference, as otherwise a null can make us fail.
63+
if (Type::isSubType(br->ref->type,
64+
Type(br->intendedType, NonNullable))) {
65+
return flip ? Failure : Success;
66+
}
67+
}
5868
return Unknown;
5969
case BrOnNonFunc:
6070
flip = true;

test/lit/passes/remove-unused-brs-gc.wast

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,108 @@
111111
(unreachable)
112112
)
113113
)
114-
)
115114

115+
;; CHECK: (func $br_on_cast_static (result (ref $struct))
116+
;; CHECK-NEXT: (local $temp (ref null $struct))
117+
;; CHECK-NEXT: (block $block (result (ref $struct))
118+
;; CHECK-NEXT: (drop
119+
;; CHECK-NEXT: (br $block
120+
;; CHECK-NEXT: (struct.new_default $struct)
121+
;; CHECK-NEXT: )
122+
;; CHECK-NEXT: )
123+
;; CHECK-NEXT: (unreachable)
124+
;; CHECK-NEXT: )
125+
;; CHECK-NEXT: )
126+
(func $br_on_cast_static (result (ref $struct))
127+
(local $temp (ref null $struct))
128+
(block $block (result (ref $struct))
129+
(drop
130+
;; This static cast can be computed at compile time: it will definitely be
131+
;; taken, so we can turn it into a normal br.
132+
(br_on_cast_static $block $struct
133+
(struct.new $struct)
134+
)
135+
)
136+
(unreachable)
137+
)
138+
)
139+
140+
;; CHECK: (func $br_on_cast_static_no (result (ref $struct))
141+
;; CHECK-NEXT: (local $temp (ref null $struct))
142+
;; CHECK-NEXT: (block $block (result (ref $struct))
143+
;; CHECK-NEXT: (drop
144+
;; CHECK-NEXT: (br_on_cast_static $block $struct
145+
;; CHECK-NEXT: (ref.null $struct)
146+
;; CHECK-NEXT: )
147+
;; CHECK-NEXT: )
148+
;; CHECK-NEXT: (unreachable)
149+
;; CHECK-NEXT: )
150+
;; CHECK-NEXT: )
151+
(func $br_on_cast_static_no (result (ref $struct))
152+
(local $temp (ref null $struct))
153+
(block $block (result (ref $struct))
154+
(drop
155+
(br_on_cast_static $block $struct
156+
;; As above, but now the type is nullable, so we cannot infer anything.
157+
(ref.null $struct)
158+
)
159+
)
160+
(unreachable)
161+
)
162+
)
163+
164+
;; CHECK: (func $br_on_cast_fail_static (result (ref $struct))
165+
;; CHECK-NEXT: (local $temp (ref null $struct))
166+
;; CHECK-NEXT: (block $block
167+
;; CHECK-NEXT: (drop
168+
;; CHECK-NEXT: (struct.new_default $struct)
169+
;; CHECK-NEXT: )
170+
;; CHECK-NEXT: (unreachable)
171+
;; CHECK-NEXT: )
172+
;; CHECK-NEXT: )
173+
(func $br_on_cast_fail_static (result (ref $struct))
174+
(local $temp (ref null $struct))
175+
(block $block (result (ref $struct))
176+
(drop
177+
;; As $br_on_cast_static, but this checks for a failing cast, so we know it will
178+
;; *not* be taken.
179+
(br_on_cast_static_fail $block $struct
180+
(struct.new $struct)
181+
)
182+
)
183+
(unreachable)
184+
)
185+
)
186+
187+
;; CHECK: (func $br_on_cast_dynamic (result (ref $struct))
188+
;; CHECK-NEXT: (local $temp (ref null $struct))
189+
;; CHECK-NEXT: (block $block (result (ref $struct))
190+
;; CHECK-NEXT: (drop
191+
;; CHECK-NEXT: (br_on_cast $block
192+
;; CHECK-NEXT: (struct.new_default_with_rtt $struct
193+
;; CHECK-NEXT: (rtt.canon $struct)
194+
;; CHECK-NEXT: )
195+
;; CHECK-NEXT: (rtt.canon $struct)
196+
;; CHECK-NEXT: )
197+
;; CHECK-NEXT: )
198+
;; CHECK-NEXT: (unreachable)
199+
;; CHECK-NEXT: )
200+
;; CHECK-NEXT: )
201+
(func $br_on_cast_dynamic (result (ref $struct))
202+
(local $temp (ref null $struct))
203+
(block $block (result (ref $struct))
204+
(drop
205+
;; This dynamic cast happens to be optimizable since we see both sides use
206+
;; rtt.canon, but we do not inspect things that closely, and leave such
207+
;; dynamic casts to runtime.
208+
(br_on_cast $block
209+
(struct.new_with_rtt $struct
210+
(rtt.canon $struct)
211+
)
212+
(rtt.canon $struct)
213+
)
214+
)
215+
(unreachable)
216+
)
217+
)
218+
)

0 commit comments

Comments
 (0)