Skip to content
This repository was archived by the owner on Feb 5, 2019. It is now read-only.

Commit 9791af5

Browse files
JosephTremouletAriel Ben-Yehuda
authored andcommitted
Fix inliner funclet unwind memoization
Summary: The inliner may need to determine where a given funclet unwinds to, and this determination may depend on other funclets throughout the funclet tree. The code that performs this walk in getUnwindDestToken memoizes results to avoid redundant computations. In the case that a funclet's unwind destination is derived from its ancestor, there's code to walk back down the tree from the ancestor updating the memo map of its descendants to record the unwind destination. This change fixes that code to account for the case that some descendant has a different unwind destination, which can happen if that unwind dest is a descendant of the EHPad being queried and thus didn't determine its unwind destination. Also update test inline-funclets.ll, which is supposed to cover such scenarios, to include a case that fails an assertion without this fix but passes with it. Fixes PR29151. Reviewers: majnemer Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D24117 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280610 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 859fb26 commit 9791af5

File tree

2 files changed

+303
-11
lines changed

2 files changed

+303
-11
lines changed

lib/Transforms/Utils/InlineFunction.cpp

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "llvm/Transforms/Utils/Cloning.h"
1616
#include "llvm/ADT/SetVector.h"
17+
#include "llvm/ADT/SmallPtrSet.h"
1718
#include "llvm/ADT/SmallSet.h"
1819
#include "llvm/ADT/SmallVector.h"
1920
#include "llvm/ADT/StringExtras.h"
@@ -228,7 +229,7 @@ static Value *getUnwindDestTokenHelper(Instruction *EHPad,
228229
Instruction *ChildPad = cast<Instruction>(Child);
229230
auto Memo = MemoMap.find(ChildPad);
230231
if (Memo == MemoMap.end()) {
231-
// Haven't figure out this child pad yet; queue it.
232+
// Haven't figured out this child pad yet; queue it.
232233
Worklist.push_back(ChildPad);
233234
continue;
234235
}
@@ -366,6 +367,10 @@ static Value *getUnwindDestToken(Instruction *EHPad,
366367
// search up the chain to try to find a funclet with information. Put
367368
// null entries in the memo map to avoid re-processing as we go up.
368369
MemoMap[EHPad] = nullptr;
370+
#ifndef NDEBUG
371+
SmallPtrSet<Instruction *, 4> TempMemos;
372+
TempMemos.insert(EHPad);
373+
#endif
369374
Instruction *LastUselessPad = EHPad;
370375
Value *AncestorToken;
371376
for (AncestorToken = getParentPad(EHPad);
@@ -374,6 +379,13 @@ static Value *getUnwindDestToken(Instruction *EHPad,
374379
// Skip over catchpads since they just follow their catchswitches.
375380
if (isa<CatchPadInst>(AncestorPad))
376381
continue;
382+
// If the MemoMap had an entry mapping AncestorPad to nullptr, since we
383+
// haven't yet called getUnwindDestTokenHelper for AncestorPad in this
384+
// call to getUnwindDestToken, that would mean that AncestorPad had no
385+
// information in itself, its descendants, or its ancestors. If that
386+
// were the case, then we should also have recorded the lack of information
387+
// for the descendant that we're coming from. So assert that we don't
388+
// find a null entry in the MemoMap for AncestorPad.
377389
assert(!MemoMap.count(AncestorPad) || MemoMap[AncestorPad]);
378390
auto AncestorMemo = MemoMap.find(AncestorPad);
379391
if (AncestorMemo == MemoMap.end()) {
@@ -384,25 +396,85 @@ static Value *getUnwindDestToken(Instruction *EHPad,
384396
if (UnwindDestToken)
385397
break;
386398
LastUselessPad = AncestorPad;
399+
MemoMap[LastUselessPad] = nullptr;
400+
#ifndef NDEBUG
401+
TempMemos.insert(LastUselessPad);
402+
#endif
387403
}
388404

389-
// Since the whole tree under LastUselessPad has no information, it all must
390-
// match UnwindDestToken; record that to avoid repeating the search.
405+
// We know that getUnwindDestTokenHelper was called on LastUselessPad and
406+
// returned nullptr (and likewise for EHPad and any of its ancestors up to
407+
// LastUselessPad), so LastUselessPad has no information from below. Since
408+
// getUnwindDestTokenHelper must investigate all downward paths through
409+
// no-information nodes to prove that a node has no information like this,
410+
// and since any time it finds information it records it in the MemoMap for
411+
// not just the immediately-containing funclet but also any ancestors also
412+
// exited, it must be the case that, walking downward from LastUselessPad,
413+
// visiting just those nodes which have not been mapped to an unwind dest
414+
// by getUnwindDestTokenHelper (the nullptr TempMemos notwithstanding, since
415+
// they are just used to keep getUnwindDestTokenHelper from repeating work),
416+
// any node visited must have been exhaustively searched with no information
417+
// for it found.
391418
SmallVector<Instruction *, 8> Worklist(1, LastUselessPad);
392419
while (!Worklist.empty()) {
393420
Instruction *UselessPad = Worklist.pop_back_val();
394-
assert(!MemoMap.count(UselessPad) || MemoMap[UselessPad] == nullptr);
421+
auto Memo = MemoMap.find(UselessPad);
422+
if (Memo != MemoMap.end() && Memo->second) {
423+
// Here the name 'UselessPad' is a bit of a misnomer, because we've found
424+
// that it is a funclet that does have information about unwinding to
425+
// a particular destination; its parent was a useless pad.
426+
// Since its parent has no information, the unwind edge must not escape
427+
// the parent, and must target a sibling of this pad. This local unwind
428+
// gives us no information about EHPad. Leave it and the subtree rooted
429+
// at it alone.
430+
assert(getParentPad(Memo->second) == getParentPad(UselessPad));
431+
continue;
432+
}
433+
// We know we don't have information for UselesPad. If it has an entry in
434+
// the MemoMap (mapping it to nullptr), it must be one of the TempMemos
435+
// added on this invocation of getUnwindDestToken; if a previous invocation
436+
// recorded nullptr, it would have had to prove that the ancestors of
437+
// UselessPad, which include LastUselessPad, had no information, and that
438+
// in turn would have required proving that the descendants of
439+
// LastUselesPad, which include EHPad, have no information about
440+
// LastUselessPad, which would imply that EHPad was mapped to nullptr in
441+
// the MemoMap on that invocation, which isn't the case if we got here.
442+
assert(!MemoMap.count(UselessPad) || TempMemos.count(UselessPad));
443+
// Assert as we enumerate users that 'UselessPad' doesn't have any unwind
444+
// information that we'd be contradicting by making a map entry for it
445+
// (which is something that getUnwindDestTokenHelper must have proved for
446+
// us to get here). Just assert on is direct users here; the checks in
447+
// this downward walk at its descendants will verify that they don't have
448+
// any unwind edges that exit 'UselessPad' either (i.e. they either have no
449+
// unwind edges or unwind to a sibling).
395450
MemoMap[UselessPad] = UnwindDestToken;
396451
if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UselessPad)) {
397-
for (BasicBlock *HandlerBlock : CatchSwitch->handlers())
398-
for (User *U : HandlerBlock->getFirstNonPHI()->users())
452+
assert(CatchSwitch->getUnwindDest() == nullptr && "Expected useless pad");
453+
for (BasicBlock *HandlerBlock : CatchSwitch->handlers()) {
454+
auto *CatchPad = HandlerBlock->getFirstNonPHI();
455+
for (User *U : CatchPad->users()) {
456+
assert(
457+
(!isa<InvokeInst>(U) ||
458+
(getParentPad(
459+
cast<InvokeInst>(U)->getUnwindDest()->getFirstNonPHI()) ==
460+
CatchPad)) &&
461+
"Expected useless pad");
399462
if (isa<CatchSwitchInst>(U) || isa<CleanupPadInst>(U))
400463
Worklist.push_back(cast<Instruction>(U));
464+
}
465+
}
401466
} else {
402467
assert(isa<CleanupPadInst>(UselessPad));
403-
for (User *U : UselessPad->users())
468+
for (User *U : UselessPad->users()) {
469+
assert(!isa<CleanupReturnInst>(U) && "Expected useless pad");
470+
assert((!isa<InvokeInst>(U) ||
471+
(getParentPad(
472+
cast<InvokeInst>(U)->getUnwindDest()->getFirstNonPHI()) ==
473+
UselessPad)) &&
474+
"Expected useless pad");
404475
if (isa<CatchSwitchInst>(U) || isa<CleanupPadInst>(U))
405476
Worklist.push_back(cast<Instruction>(U));
477+
}
406478
}
407479
}
408480

test/Transforms/Inline/inline-funclets.ll

Lines changed: 224 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,20 +409,240 @@ exit:
409409
ret void
410410
}
411411

412+
;;; Test with funclets that don't have information for themselves, but have
413+
;;; descendants which unwind to other descendants (left.left unwinds to
414+
;;; left.right, and right unwinds to far_right). Make sure that these local
415+
;;; unwinds don't trip up processing of the ancestor nodes (left and root) that
416+
;;; ultimately have no information.
417+
;;; CHECK-LABEL: define void @test6(
418+
define void @test6() personality void()* @ProcessCLRException {
419+
entry:
420+
; CHECK-NEXT: entry:
421+
invoke void @test6_inlinee()
422+
to label %exit unwind label %cleanup
423+
cleanup:
424+
%pad = cleanuppad within none []
425+
call void @g() [ "funclet"(token %pad) ]
426+
cleanupret from %pad unwind to caller
427+
exit:
428+
ret void
429+
}
430+
431+
define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
432+
entry:
433+
invoke void @g()
434+
to label %exit unwind label %root
435+
; CHECK-NEXT: invoke void @g()
436+
; CHECK-NEXT: unwind label %[[root:.+]]
437+
root:
438+
%root.pad = cleanuppad within none []
439+
invoke void @g() [ "funclet"(token %root.pad) ]
440+
to label %root.cont unwind label %left
441+
; CHECK: [[root]]:
442+
; CHECK-NEXT: %[[root_pad:.+]] = cleanuppad within none []
443+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
444+
; CHECK-NEXT: to label %[[root_cont:.+]] unwind label %[[left:.+]]
445+
446+
left:
447+
%left.cs = catchswitch within %root.pad [label %left.catch] unwind to caller
448+
; CHECK: [[left]]:
449+
; CHECK-NEXT: %[[left_cs:.+]] = catchswitch within %[[root_pad]] [label %[[left_catch:.+]]] unwind label %cleanup
450+
451+
left.catch:
452+
%left.cp = catchpad within %left.cs []
453+
call void @g() [ "funclet"(token %left.cp) ]
454+
invoke void @g() [ "funclet"(token %left.cp) ]
455+
to label %unreach unwind label %left.left
456+
; CHECK: [[left_catch:.+]]:
457+
; CHECK-NEXT: %[[left_cp:.+]] = catchpad within %[[left_cs]] []
458+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
459+
; CHECK-NEXT: to label %[[lc_cont:.+]] unwind label %cleanup
460+
; CHECK: [[lc_cont]]:
461+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
462+
; CHECK-NEXT: to label %[[unreach:.+]] unwind label %[[left_left:.+]]
463+
464+
left.left:
465+
%ll.pad = cleanuppad within %left.cp []
466+
cleanupret from %ll.pad unwind label %left.right
467+
; CHECK: [[left_left]]:
468+
; CHECK-NEXT: %[[ll_pad:.+]] = cleanuppad within %[[left_cp]] []
469+
; CHECK-NEXT: cleanupret from %[[ll_pad]] unwind label %[[left_right:.+]]
470+
471+
left.right:
472+
%lr.pad = cleanuppad within %left.cp []
473+
unreachable
474+
; CHECK: [[left_right]]:
475+
; CHECK-NEXT: %[[lr_pad:.+]] = cleanuppad within %[[left_cp]] []
476+
; CHECK-NEXT: unreachable
477+
478+
root.cont:
479+
call void @g() [ "funclet"(token %root.pad) ]
480+
invoke void @g() [ "funclet"(token %root.pad) ]
481+
to label %unreach unwind label %right
482+
; CHECK: [[root_cont]]:
483+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
484+
; CHECK-NEXT: to label %[[root_cont_cont:.+]] unwind label %cleanup
485+
; CHECK: [[root_cont_cont]]:
486+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
487+
; CHECK-NEXT: to label %[[unreach]] unwind label %[[right:.+]]
488+
489+
right:
490+
%right.pad = cleanuppad within %root.pad []
491+
invoke void @g() [ "funclet"(token %right.pad) ]
492+
to label %unreach unwind label %right.child
493+
; CHECK: [[right]]:
494+
; CHECK-NEXT: %[[right_pad:.+]] = cleanuppad within %[[root_pad]] []
495+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_pad]]) ]
496+
; CHECK-NEXT: to label %[[unreach]] unwind label %[[right_child:.+]]
497+
498+
right.child:
499+
%rc.pad = cleanuppad within %right.pad []
500+
invoke void @g() [ "funclet"(token %rc.pad) ]
501+
to label %unreach unwind label %far_right
502+
; CHECK: [[right_child]]:
503+
; CHECK-NEXT: %[[rc_pad:.+]] = cleanuppad within %[[right_pad]] []
504+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[rc_pad]]) ]
505+
; CHECK-NEXT: to label %[[unreach]] unwind label %[[far_right:.+]]
506+
507+
far_right:
508+
%fr.cs = catchswitch within %root.pad [label %fr.catch] unwind to caller
509+
; CHECK: [[far_right]]:
510+
; CHECK-NEXT: %[[fr_cs:.+]] = catchswitch within %[[root_pad]] [label %[[fr_catch:.+]]] unwind label %cleanup
511+
512+
fr.catch:
513+
%fr.cp = catchpad within %fr.cs []
514+
unreachable
515+
; CHECK: [[fr_catch]]:
516+
; CHECK-NEXT: %[[fr_cp:.+]] = catchpad within %[[fr_cs]] []
517+
; CHECK-NEXT: unreachable
518+
519+
unreach:
520+
unreachable
521+
; CHECK: [[unreach]]:
522+
; CHECK-NEXT: unreachable
523+
524+
exit:
525+
ret void
526+
}
527+
528+
529+
;;; Test with a no-info funclet (right) which has a cousin (left.left) that
530+
;;; unwinds to another cousin (left.right); make sure we don't trip over this
531+
;;; when propagating unwind destination info to "right".
532+
;;; CHECK-LABEL: define void @test7(
533+
define void @test7() personality void()* @ProcessCLRException {
534+
entry:
535+
; CHECK-NEXT: entry:
536+
invoke void @test7_inlinee()
537+
to label %exit unwind label %cleanup
538+
cleanup:
539+
%pad = cleanuppad within none []
540+
call void @g() [ "funclet"(token %pad) ]
541+
cleanupret from %pad unwind to caller
542+
exit:
543+
ret void
544+
}
545+
546+
define void @test7_inlinee() alwaysinline personality void ()* @ProcessCLRException {
547+
entry:
548+
invoke void @g()
549+
to label %exit unwind label %root
550+
; CHECK-NEXT: invoke void @g()
551+
; CHECK-NEXT: unwind label %[[root:.+]]
552+
553+
root:
554+
%root.cp = cleanuppad within none []
555+
invoke void @g() [ "funclet"(token %root.cp) ]
556+
to label %root.cont unwind label %child
557+
; CHECK: [[root]]:
558+
; CHECK-NEXT: %[[root_cp:.+]] = cleanuppad within none []
559+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_cp]]) ]
560+
; CHECK-NEXT: to label %[[root_cont:.+]] unwind label %[[child:.+]]
561+
562+
root.cont:
563+
cleanupret from %root.cp unwind to caller
564+
; CHECK: [[root_cont]]:
565+
; CHECK-NEXT: cleanupret from %[[root_cp]] unwind label %cleanup
566+
567+
child:
568+
%child.cp = cleanuppad within %root.cp []
569+
invoke void @g() [ "funclet"(token %child.cp) ]
570+
to label %child.cont unwind label %left
571+
; CHECK: [[child]]:
572+
; CHECK-NEXT: %[[child_cp:.+]] = cleanuppad within %[[root_cp]] []
573+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
574+
; CHECK-NEXT: to label %[[child_cont:.+]] unwind label %[[left:.+]]
575+
576+
left:
577+
%left.cp = cleanuppad within %child.cp []
578+
invoke void @g() [ "funclet"(token %left.cp) ]
579+
to label %left.cont unwind label %left.left
580+
; CHECK: [[left]]:
581+
; CHECK-NEXT: %[[left_cp:.+]] = cleanuppad within %[[child_cp]] []
582+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
583+
; CHECK-NEXT: to label %[[left_cont:.+]] unwind label %[[left_left:.+]]
584+
585+
left.left:
586+
%ll.cp = cleanuppad within %left.cp []
587+
cleanupret from %ll.cp unwind label %left.right
588+
; CHECK: [[left_left]]:
589+
; CHECK-NEXT: %[[ll_cp:.+]] = cleanuppad within %[[left_cp]] []
590+
; CHECK-NEXT: cleanupret from %[[ll_cp]] unwind label %[[left_right:.+]]
591+
592+
left.cont:
593+
invoke void @g() [ "funclet"(token %left.cp) ]
594+
to label %unreach unwind label %left.right
595+
; CHECK: [[left_cont]]:
596+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
597+
; CHECK-NEXT: to label %[[unreach:.+]] unwind label %[[left_right]]
598+
599+
left.right:
600+
%lr.cp = cleanuppad within %left.cp []
601+
unreachable
602+
; CHECK: [[left_right]]:
603+
; CHECK-NEXT: %[[lr_cp:.+]] = cleanuppad within %[[left_cp]] []
604+
; CHECK-NEXT: unreachable
605+
606+
child.cont:
607+
invoke void @g() [ "funclet"(token %child.cp) ]
608+
to label %unreach unwind label %right
609+
; CHECK: [[child_cont]]:
610+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
611+
; CHECK-NEXT: to label %[[unreach]] unwind label %[[right:.+]]
612+
613+
right:
614+
%right.cp = cleanuppad within %child.cp []
615+
call void @g() [ "funclet"(token %right.cp) ]
616+
unreachable
617+
; CHECK: [[right]]:
618+
; CHECK-NEXT: %[[right_cp:.+]] = cleanuppad within %[[child_cp]]
619+
; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_cp]]) ]
620+
; CHECK-NEXT: to label %[[right_cont:.+]] unwind label %cleanup
621+
; CHECK: [[right_cont]]:
622+
; CHECK-NEXT: unreachable
623+
624+
unreach:
625+
unreachable
626+
; CHECK: [[unreach]]:
627+
; CHECK-NEXT: unreachable
628+
629+
exit:
630+
ret void
631+
}
412632

413633
declare void @ProcessCLRException()
414634

415635
; Make sure the logic doesn't get tripped up when the inlined invoke is
416636
; itself within a funclet in the caller.
417-
; CHECK-LABEL: define void @test6(
418-
define void @test6() personality void ()* @ProcessCLRException {
637+
; CHECK-LABEL: define void @test8(
638+
define void @test8() personality void ()* @ProcessCLRException {
419639
entry:
420640
invoke void @g()
421641
to label %exit unwind label %callsite_parent
422642
callsite_parent:
423643
%callsite_parent.pad = cleanuppad within none []
424644
; CHECK: %callsite_parent.pad = cleanuppad within none
425-
invoke void @test6_inlinee() [ "funclet"(token %callsite_parent.pad) ]
645+
invoke void @test8_inlinee() [ "funclet"(token %callsite_parent.pad) ]
426646
to label %ret unwind label %cleanup
427647
ret:
428648
cleanupret from %callsite_parent.pad unwind label %cleanup
@@ -434,7 +654,7 @@ exit:
434654
ret void
435655
}
436656

437-
define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
657+
define void @test8_inlinee() alwaysinline personality void ()* @ProcessCLRException {
438658
entry:
439659
invoke void @g()
440660
to label %exit unwind label %inlinee_cleanup

0 commit comments

Comments
 (0)