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

Commit d5ef27a

Browse files
authored
Merge pull request #66 from arielb1/shimmir-pr29151
Fix inliner funclet unwind memoization
2 parents 33795db + 9791af5 commit d5ef27a

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)