@@ -130,9 +130,55 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
130
130
}
131
131
132
132
void visitBlock (Block* curr) {
133
+ auto & list = curr->list ;
134
+
135
+ // If traps are assumed to never happen, we can remove code on paths that
136
+ // must reach a trap:
137
+ //
138
+ // (block
139
+ // (i32.store ..)
140
+ // (br_if ..) ;; execution branches here, so the first store remains
141
+ // (i32.store ..) ;; this store can be removed
142
+ // (unreachable);
143
+ // )
144
+ //
145
+ // For this to be useful we need to have at least 2 elements: something to
146
+ // remove, and an unreachable.
147
+ if (getPassOptions ().trapsNeverHappen && list.size () >= 2 ) {
148
+ // Go backwards. When we find a trap, mark the things before it as heading
149
+ // to a trap.
150
+ auto headingToTrap = false ;
151
+ for (int i = list.size () - 1 ; i >= 0 ; i--) {
152
+ if (list[i]->is <Unreachable>()) {
153
+ headingToTrap = true ;
154
+ continue ;
155
+ }
156
+
157
+ if (!headingToTrap) {
158
+ continue ;
159
+ }
160
+
161
+ // Check if we may no longer be heading to a trap. Two situations count
162
+ // here: Control flow might branch, or we might call (since a call might
163
+ // reach an import; see notes on that in pass.h:trapsNeverHappen).
164
+ //
165
+ // We also cannot remove a pop as it is necessary for structural
166
+ // reasons.
167
+ EffectAnalyzer effects (getPassOptions (), *getModule (), list[i]);
168
+ if (effects.transfersControlFlow () || effects.calls ||
169
+ effects.danglingPop ) {
170
+ headingToTrap = false ;
171
+ continue ;
172
+ }
173
+
174
+ // This code can be removed! Turn it into a nop, and leave it for the
175
+ // code lower down to finish cleaning up.
176
+ ExpressionManipulator::nop (list[i]);
177
+ }
178
+ }
179
+
133
180
// compress out nops and other dead code
134
181
int skip = 0 ;
135
- auto & list = curr->list ;
136
182
size_t size = list.size ();
137
183
for (size_t z = 0 ; z < size; z++) {
138
184
auto * child = list[z];
@@ -210,6 +256,38 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
210
256
return ;
211
257
}
212
258
// from here on, we can assume the condition executed
259
+
260
+ // In trapsNeverHappen mode, a definitely-trapping arm can be assumed to not
261
+ // happen. Such conditional code can be assumed to never be reached in this
262
+ // mode.
263
+ //
264
+ // Ignore the case of an unreachable if, such as having both arms be
265
+ // unreachable. In that case we'd need to fix up the IR to avoid changing
266
+ // the type; leave that for DCE to simplify first. After checking that
267
+ // curr->type != unreachable, we can assume that only one of the arms is
268
+ // unreachable (at most).
269
+ if (getPassOptions ().trapsNeverHappen && curr->type != Type::unreachable) {
270
+ auto optimizeArm = [&](Expression* arm, Expression* otherArm) {
271
+ if (!arm->is <Unreachable>()) {
272
+ return false ;
273
+ }
274
+ Builder builder (*getModule ());
275
+ Expression* rep = builder.makeDrop (curr->condition );
276
+ if (otherArm) {
277
+ rep = builder.makeSequence (rep, otherArm);
278
+ }
279
+ replaceCurrent (rep);
280
+ return true ;
281
+ };
282
+
283
+ // As mentioned above, do not try to optimize both arms; leave that case
284
+ // for DCE.
285
+ if (optimizeArm (curr->ifTrue , curr->ifFalse ) ||
286
+ (curr->ifFalse && optimizeArm (curr->ifFalse , curr->ifTrue ))) {
287
+ return ;
288
+ }
289
+ }
290
+
213
291
if (curr->ifFalse ) {
214
292
if (curr->ifFalse ->is <Nop>()) {
215
293
curr->ifFalse = nullptr ;
0 commit comments