Skip to content

Commit 0848a27

Browse files
authored
Add EH support for CFGWalker (#2597)
This adds EH instruction support for `CFGWalker`. This also implements `call` instruction handling within a try-catch; every call can possibly throw and unwind to the innermost catch block. This adds tests for RedundantSetElimination pass, which uses `CFGWalker`.
1 parent 987bbca commit 0848a27

File tree

3 files changed

+288
-0
lines changed

3 files changed

+288
-0
lines changed

src/cfg/cfg-traversal.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,15 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> {
6464
BasicBlock* currBasicBlock;
6565
// a block or loop => its branches
6666
std::map<Expression*, std::vector<BasicBlock*>> branches;
67+
// stack of the last blocks of if conditions + the last blocks of if true
68+
// bodies
6769
std::vector<BasicBlock*> ifStack;
70+
// stack of the first blocks of loops
6871
std::vector<BasicBlock*> loopStack;
72+
// stack of the last blocks of try bodies
73+
std::vector<BasicBlock*> tryStack;
74+
// stack of the first blocks of catch bodies
75+
std::vector<BasicBlock*> catchStack;
6976

7077
void startBasicBlock() {
7178
currBasicBlock = ((SubType*)this)->makeBasicBlock();
@@ -198,6 +205,59 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> {
198205
self->startUnreachableBlock();
199206
}
200207

208+
static void doEndCall(SubType* self, Expression** currp) {
209+
// Every call can possibly throw, but we don't end the current basic block
210+
// unless the call is within a try-catch, because the CFG will have too many
211+
// blocks that way, and if an exception is thrown, the function will be
212+
// exited anyway.
213+
if (!self->catchStack.empty()) {
214+
auto* last = self->currBasicBlock;
215+
self->startBasicBlock();
216+
self->link(last, self->currBasicBlock); // exception not thrown
217+
self->link(last, self->catchStack.back()); // exception thrown
218+
}
219+
}
220+
221+
static void doStartTry(SubType* self, Expression** currp) {
222+
auto* last = self->currBasicBlock;
223+
self->startBasicBlock(); // catch body's first block
224+
self->catchStack.push_back(self->currBasicBlock);
225+
self->currBasicBlock = last; // reset to the current block
226+
}
227+
228+
static void doStartCatch(SubType* self, Expression** currp) {
229+
self->tryStack.push_back(self->currBasicBlock); // last block of try body
230+
self->currBasicBlock = self->catchStack.back();
231+
self->catchStack.pop_back();
232+
}
233+
234+
static void doEndTry(SubType* self, Expression** currp) {
235+
auto* last = self->currBasicBlock;
236+
self->startBasicBlock(); // continuation block after try-catch
237+
// catch body's last block -> continuation block
238+
self->link(last, self->currBasicBlock);
239+
// try body's last block -> continuation block
240+
self->link(self->tryStack.back(), self->currBasicBlock);
241+
self->tryStack.pop_back();
242+
}
243+
244+
static void doEndThrow(SubType* self, Expression** currp) {
245+
// We unwind to the innermost catch, if any
246+
if (!self->catchStack.empty()) {
247+
self->link(self->currBasicBlock, self->catchStack.back());
248+
}
249+
self->startUnreachableBlock();
250+
}
251+
252+
static void doEndBrOnExn(SubType* self, Expression** currp) {
253+
auto* curr = (*currp)->cast<BrOnExn>();
254+
self->branches[self->findBreakTarget(curr->name)].push_back(
255+
self->currBasicBlock); // branch to the target
256+
auto* last = self->currBasicBlock;
257+
self->startBasicBlock();
258+
self->link(last, self->currBasicBlock); // we might fall through
259+
}
260+
201261
static void scan(SubType* self, Expression** currp) {
202262
Expression* curr = *currp;
203263

@@ -238,6 +298,28 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> {
238298
self->pushTask(SubType::doStartUnreachableBlock, currp);
239299
break;
240300
}
301+
case Expression::Id::CallId:
302+
case Expression::Id::CallIndirectId: {
303+
self->pushTask(SubType::doEndCall, currp);
304+
break;
305+
}
306+
case Expression::Id::TryId: {
307+
self->pushTask(SubType::doEndTry, currp);
308+
self->pushTask(SubType::scan, &curr->cast<Try>()->catchBody);
309+
self->pushTask(SubType::doStartCatch, currp);
310+
self->pushTask(SubType::scan, &curr->cast<Try>()->body);
311+
self->pushTask(SubType::doStartTry, currp);
312+
return; // don't do anything else
313+
}
314+
case Expression::Id::ThrowId:
315+
case Expression::Id::RethrowId: {
316+
self->pushTask(SubType::doEndThrow, currp);
317+
break;
318+
}
319+
case Expression::Id::BrOnExnId: {
320+
self->pushTask(SubType::doEndBrOnExn, currp);
321+
break;
322+
}
241323
default: {}
242324
}
243325

@@ -263,6 +345,8 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> {
263345
assert(branches.size() == 0);
264346
assert(ifStack.size() == 0);
265347
assert(loopStack.size() == 0);
348+
assert(tryStack.size() == 0);
349+
assert(catchStack.size() == 0);
266350
}
267351

268352
std::unordered_set<BasicBlock*> findLiveBlocks() {

test/passes/rse.txt renamed to test/passes/rse_all-features.txt

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
(type $i32_=>_none (func (param i32)))
44
(type $i32_i32_=>_none (func (param i32 i32)))
55
(type $i32_f64_=>_none (func (param i32 f64)))
6+
(event $e (attr 0) (param i32))
67
(func $basic (; 0 ;) (param $x i32) (param $y f64)
78
(local $a f32)
89
(local $b i64)
@@ -459,4 +460,128 @@
459460
)
460461
)
461462
)
463+
(func $try1 (; 19 ;)
464+
(local $x i32)
465+
(try
466+
(nop)
467+
(catch
468+
(drop
469+
(exnref.pop)
470+
)
471+
(local.set $x
472+
(i32.const 1)
473+
)
474+
)
475+
)
476+
(local.set $x
477+
(i32.const 1)
478+
)
479+
)
480+
(func $try2 (; 20 ;)
481+
(local $x i32)
482+
(try
483+
(block $block
484+
(throw $e
485+
(i32.const 0)
486+
)
487+
(local.set $x
488+
(i32.const 1)
489+
)
490+
)
491+
(catch
492+
(drop
493+
(exnref.pop)
494+
)
495+
)
496+
)
497+
(local.set $x
498+
(i32.const 1)
499+
)
500+
)
501+
(func $try3 (; 21 ;)
502+
(local $x i32)
503+
(try
504+
(throw $e
505+
(i32.const 0)
506+
)
507+
(catch
508+
(drop
509+
(exnref.pop)
510+
)
511+
(local.set $x
512+
(i32.const 1)
513+
)
514+
)
515+
)
516+
(drop
517+
(i32.const 1)
518+
)
519+
)
520+
(func $foo (; 22 ;)
521+
(nop)
522+
)
523+
(func $try4 (; 23 ;)
524+
(local $x i32)
525+
(try
526+
(block $block
527+
(call $foo)
528+
(local.set $x
529+
(i32.const 1)
530+
)
531+
)
532+
(catch
533+
(drop
534+
(exnref.pop)
535+
)
536+
)
537+
)
538+
(local.set $x
539+
(i32.const 1)
540+
)
541+
)
542+
(func $try5 (; 24 ;)
543+
(local $x i32)
544+
(try
545+
(block $block
546+
(local.set $x
547+
(i32.const 1)
548+
)
549+
(call $foo)
550+
)
551+
(catch
552+
(drop
553+
(exnref.pop)
554+
)
555+
)
556+
)
557+
(drop
558+
(i32.const 1)
559+
)
560+
)
561+
(func $nested-try (; 25 ;)
562+
(local $x i32)
563+
(try
564+
(try
565+
(throw $e
566+
(i32.const 0)
567+
)
568+
(catch
569+
(rethrow
570+
(exnref.pop)
571+
)
572+
)
573+
)
574+
(catch
575+
(drop
576+
(exnref.pop)
577+
)
578+
(local.set $x
579+
(i32.const 1)
580+
)
581+
)
582+
)
583+
(drop
584+
(i32.const 1)
585+
)
586+
)
462587
)

test/passes/rse.wast renamed to test/passes/rse_all-features.wast

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,5 +277,84 @@
277277
)
278278
)
279279
)
280+
281+
(event $e (attr 0) (param i32))
282+
(func $try1
283+
(local $x i32)
284+
(try
285+
(catch
286+
(drop (exnref.pop))
287+
(local.set $x (i32.const 1))
288+
)
289+
)
290+
(local.set $x (i32.const 1)) ;; should NOT be dropped
291+
)
292+
(func $try2
293+
(local $x i32)
294+
(try
295+
(block
296+
(throw $e (i32.const 0))
297+
(local.set $x (i32.const 1))
298+
)
299+
(catch
300+
(drop (exnref.pop))
301+
)
302+
)
303+
(local.set $x (i32.const 1)) ;; should NOT be dropped
304+
)
305+
(func $try3
306+
(local $x i32)
307+
(try
308+
(throw $e (i32.const 0))
309+
(catch
310+
(drop (exnref.pop))
311+
(local.set $x (i32.const 1))
312+
)
313+
)
314+
(local.set $x (i32.const 1)) ;; should be dropped
315+
)
316+
(func $foo)
317+
(func $try4
318+
(local $x i32)
319+
(try
320+
(block
321+
(call $foo)
322+
(local.set $x (i32.const 1))
323+
)
324+
(catch
325+
(drop (exnref.pop))
326+
)
327+
)
328+
(local.set $x (i32.const 1)) ;; should NOT be dropped
329+
)
330+
(func $try5
331+
(local $x i32)
332+
(try
333+
(block
334+
(local.set $x (i32.const 1))
335+
(call $foo)
336+
)
337+
(catch
338+
(drop (exnref.pop))
339+
)
340+
)
341+
(local.set $x (i32.const 1)) ;; should be dropped
342+
)
343+
(func $nested-try
344+
(local $x i32)
345+
(try
346+
(try
347+
(throw $e (i32.const 0))
348+
(catch
349+
(rethrow (exnref.pop))
350+
)
351+
)
352+
(catch
353+
(drop (exnref.pop))
354+
(local.set $x (i32.const 1))
355+
)
356+
)
357+
(local.set $x (i32.const 1)) ;; should be dropped
358+
)
280359
)
281360

0 commit comments

Comments
 (0)