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
llvm-svn: 280610
diff --git a/llvm/test/Transforms/Inline/inline-funclets.ll b/llvm/test/Transforms/Inline/inline-funclets.ll
index 362e03d..2cc2153 100644
--- a/llvm/test/Transforms/Inline/inline-funclets.ll
+++ b/llvm/test/Transforms/Inline/inline-funclets.ll
@@ -409,20 +409,240 @@
ret void
}
+;;; Test with funclets that don't have information for themselves, but have
+;;; descendants which unwind to other descendants (left.left unwinds to
+;;; left.right, and right unwinds to far_right). Make sure that these local
+;;; unwinds don't trip up processing of the ancestor nodes (left and root) that
+;;; ultimately have no information.
+;;; CHECK-LABEL: define void @test6(
+define void @test6() personality void()* @ProcessCLRException {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test6_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
+entry:
+ invoke void @g()
+ to label %exit unwind label %root
+ ; CHECK-NEXT: invoke void @g()
+ ; CHECK-NEXT: unwind label %[[root:.+]]
+root:
+ %root.pad = cleanuppad within none []
+ invoke void @g() [ "funclet"(token %root.pad) ]
+ to label %root.cont unwind label %left
+; CHECK: [[root]]:
+; CHECK-NEXT: %[[root_pad:.+]] = cleanuppad within none []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
+; CHECK-NEXT: to label %[[root_cont:.+]] unwind label %[[left:.+]]
+
+left:
+ %left.cs = catchswitch within %root.pad [label %left.catch] unwind to caller
+; CHECK: [[left]]:
+; CHECK-NEXT: %[[left_cs:.+]] = catchswitch within %[[root_pad]] [label %[[left_catch:.+]]] unwind label %cleanup
+
+left.catch:
+ %left.cp = catchpad within %left.cs []
+ call void @g() [ "funclet"(token %left.cp) ]
+ invoke void @g() [ "funclet"(token %left.cp) ]
+ to label %unreach unwind label %left.left
+; CHECK: [[left_catch:.+]]:
+; CHECK-NEXT: %[[left_cp:.+]] = catchpad within %[[left_cs]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
+; CHECK-NEXT: to label %[[lc_cont:.+]] unwind label %cleanup
+; CHECK: [[lc_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
+; CHECK-NEXT: to label %[[unreach:.+]] unwind label %[[left_left:.+]]
+
+left.left:
+ %ll.pad = cleanuppad within %left.cp []
+ cleanupret from %ll.pad unwind label %left.right
+; CHECK: [[left_left]]:
+; CHECK-NEXT: %[[ll_pad:.+]] = cleanuppad within %[[left_cp]] []
+; CHECK-NEXT: cleanupret from %[[ll_pad]] unwind label %[[left_right:.+]]
+
+left.right:
+ %lr.pad = cleanuppad within %left.cp []
+ unreachable
+; CHECK: [[left_right]]:
+; CHECK-NEXT: %[[lr_pad:.+]] = cleanuppad within %[[left_cp]] []
+; CHECK-NEXT: unreachable
+
+root.cont:
+ call void @g() [ "funclet"(token %root.pad) ]
+ invoke void @g() [ "funclet"(token %root.pad) ]
+ to label %unreach unwind label %right
+; CHECK: [[root_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
+; CHECK-NEXT: to label %[[root_cont_cont:.+]] unwind label %cleanup
+; CHECK: [[root_cont_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
+; CHECK-NEXT: to label %[[unreach]] unwind label %[[right:.+]]
+
+right:
+ %right.pad = cleanuppad within %root.pad []
+ invoke void @g() [ "funclet"(token %right.pad) ]
+ to label %unreach unwind label %right.child
+; CHECK: [[right]]:
+; CHECK-NEXT: %[[right_pad:.+]] = cleanuppad within %[[root_pad]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_pad]]) ]
+; CHECK-NEXT: to label %[[unreach]] unwind label %[[right_child:.+]]
+
+right.child:
+ %rc.pad = cleanuppad within %right.pad []
+ invoke void @g() [ "funclet"(token %rc.pad) ]
+ to label %unreach unwind label %far_right
+; CHECK: [[right_child]]:
+; CHECK-NEXT: %[[rc_pad:.+]] = cleanuppad within %[[right_pad]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[rc_pad]]) ]
+; CHECK-NEXT: to label %[[unreach]] unwind label %[[far_right:.+]]
+
+far_right:
+ %fr.cs = catchswitch within %root.pad [label %fr.catch] unwind to caller
+; CHECK: [[far_right]]:
+; CHECK-NEXT: %[[fr_cs:.+]] = catchswitch within %[[root_pad]] [label %[[fr_catch:.+]]] unwind label %cleanup
+
+fr.catch:
+ %fr.cp = catchpad within %fr.cs []
+ unreachable
+; CHECK: [[fr_catch]]:
+; CHECK-NEXT: %[[fr_cp:.+]] = catchpad within %[[fr_cs]] []
+; CHECK-NEXT: unreachable
+
+unreach:
+ unreachable
+; CHECK: [[unreach]]:
+; CHECK-NEXT: unreachable
+
+exit:
+ ret void
+}
+
+
+;;; Test with a no-info funclet (right) which has a cousin (left.left) that
+;;; unwinds to another cousin (left.right); make sure we don't trip over this
+;;; when propagating unwind destination info to "right".
+;;; CHECK-LABEL: define void @test7(
+define void @test7() personality void()* @ProcessCLRException {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test7_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test7_inlinee() alwaysinline personality void ()* @ProcessCLRException {
+entry:
+ invoke void @g()
+ to label %exit unwind label %root
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[root:.+]]
+
+root:
+ %root.cp = cleanuppad within none []
+ invoke void @g() [ "funclet"(token %root.cp) ]
+ to label %root.cont unwind label %child
+; CHECK: [[root]]:
+; CHECK-NEXT: %[[root_cp:.+]] = cleanuppad within none []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_cp]]) ]
+; CHECK-NEXT: to label %[[root_cont:.+]] unwind label %[[child:.+]]
+
+root.cont:
+ cleanupret from %root.cp unwind to caller
+; CHECK: [[root_cont]]:
+; CHECK-NEXT: cleanupret from %[[root_cp]] unwind label %cleanup
+
+child:
+ %child.cp = cleanuppad within %root.cp []
+ invoke void @g() [ "funclet"(token %child.cp) ]
+ to label %child.cont unwind label %left
+; CHECK: [[child]]:
+; CHECK-NEXT: %[[child_cp:.+]] = cleanuppad within %[[root_cp]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
+; CHECK-NEXT: to label %[[child_cont:.+]] unwind label %[[left:.+]]
+
+left:
+ %left.cp = cleanuppad within %child.cp []
+ invoke void @g() [ "funclet"(token %left.cp) ]
+ to label %left.cont unwind label %left.left
+; CHECK: [[left]]:
+; CHECK-NEXT: %[[left_cp:.+]] = cleanuppad within %[[child_cp]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
+; CHECK-NEXT: to label %[[left_cont:.+]] unwind label %[[left_left:.+]]
+
+left.left:
+ %ll.cp = cleanuppad within %left.cp []
+ cleanupret from %ll.cp unwind label %left.right
+; CHECK: [[left_left]]:
+; CHECK-NEXT: %[[ll_cp:.+]] = cleanuppad within %[[left_cp]] []
+; CHECK-NEXT: cleanupret from %[[ll_cp]] unwind label %[[left_right:.+]]
+
+left.cont:
+ invoke void @g() [ "funclet"(token %left.cp) ]
+ to label %unreach unwind label %left.right
+; CHECK: [[left_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
+; CHECK-NEXT: to label %[[unreach:.+]] unwind label %[[left_right]]
+
+left.right:
+ %lr.cp = cleanuppad within %left.cp []
+ unreachable
+; CHECK: [[left_right]]:
+; CHECK-NEXT: %[[lr_cp:.+]] = cleanuppad within %[[left_cp]] []
+; CHECK-NEXT: unreachable
+
+child.cont:
+ invoke void @g() [ "funclet"(token %child.cp) ]
+ to label %unreach unwind label %right
+; CHECK: [[child_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
+; CHECK-NEXT: to label %[[unreach]] unwind label %[[right:.+]]
+
+right:
+ %right.cp = cleanuppad within %child.cp []
+ call void @g() [ "funclet"(token %right.cp) ]
+ unreachable
+; CHECK: [[right]]:
+; CHECK-NEXT: %[[right_cp:.+]] = cleanuppad within %[[child_cp]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_cp]]) ]
+; CHECK-NEXT: to label %[[right_cont:.+]] unwind label %cleanup
+; CHECK: [[right_cont]]:
+; CHECK-NEXT: unreachable
+
+unreach:
+ unreachable
+; CHECK: [[unreach]]:
+; CHECK-NEXT: unreachable
+
+exit:
+ ret void
+}
declare void @ProcessCLRException()
; Make sure the logic doesn't get tripped up when the inlined invoke is
; itself within a funclet in the caller.
-; CHECK-LABEL: define void @test6(
-define void @test6() personality void ()* @ProcessCLRException {
+; CHECK-LABEL: define void @test8(
+define void @test8() personality void ()* @ProcessCLRException {
entry:
invoke void @g()
to label %exit unwind label %callsite_parent
callsite_parent:
%callsite_parent.pad = cleanuppad within none []
; CHECK: %callsite_parent.pad = cleanuppad within none
- invoke void @test6_inlinee() [ "funclet"(token %callsite_parent.pad) ]
+ invoke void @test8_inlinee() [ "funclet"(token %callsite_parent.pad) ]
to label %ret unwind label %cleanup
ret:
cleanupret from %callsite_parent.pad unwind label %cleanup
@@ -434,7 +654,7 @@
ret void
}
-define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
+define void @test8_inlinee() alwaysinline personality void ()* @ProcessCLRException {
entry:
invoke void @g()
to label %exit unwind label %inlinee_cleanup