llvm.org GIT mirror llvm / c2d8241
[WinEH] Verify consistent funclet unwind exits Summary: A funclet EH pad may be exited by an unwind edge, which may be a cleanupret exiting its cleanuppad, an invoke exiting a funclet, or an unwind out of a nested funclet transitively exiting its parent. Funclet EH personalities require all such exceptional exits from a given funclet to have the same unwind destination, and EH preparation / state numbering / table generation implicitly depends on this. Formalize it as a rule of the IR in the LangRef and verifier. Reviewers: rnk, majnemer, andrew.w.kaylor Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D15962 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@257273 91177308-0d34-0410-b5e6-96231b3b80d8 Joseph Tremoulet 3 years ago
6 changed file(s) with 270 addition(s) and 34 deletion(s). Raw diff Collapse all Expand all
817817 or "none" if there is no such pad. This ensures that the stack of executing
818818 funclets at run-time always corresponds to some path in the funclet pad tree
819819 that the parent tokens encode.
820
821 All unwind edges which exit any given funclet pad (including ``cleanupret``
822 edges exiting their ``cleanuppad`` and ``catchswitch`` edges exiting their
823 ``catchswitch``) must share the same unwind destination. Similarly, any
824 funclet pad which may be exited by unwind to caller must not be exited by
825 any exception edges which unwind anywhere other than the caller. This
826 ensures that each funclet as a whole has only one unwind destination, which
827 EH tables for funclet personalities may require. Note that any unwind edge
828 which exits a ``catchpad`` also exits its parent ``catchswitch``, so this
829 implies that for any given ``catchswitch``, its unwind destination must also
830 be the unwind destination of any unwind edge that exits any of its constituent
831 ``catchpad``\s. Because ``catchswitch`` has no ``nounwind`` variant, and
832 because IR producers are not *required* to annotate calls which will not
833 unwind as ``nounwind``, it is legal to nest a ``call`` or an "``unwind to
834 caller``\ " ``catchswitch`` within a funclet pad that has an unwind
835 destination other than caller; it is undefined behavior for such a ``call``
836 or ``catchswitch`` to unwind.
86568656 it is undefined behavior to execute a :ref:`call ` or :ref:`invoke `
86578657 that does not carry an appropriate :ref:`"funclet" bundle `.
86588658
8659 It is undefined behavior for the ``cleanuppad`` to exit via an unwind edge which
8660 does not transitively unwind to the same destination as a constituent
8661 ``cleanupret``.
8662
86638659 Example:
86648660 """"""""
86658661
402402 void visitCatchPadInst(CatchPadInst &CPI);
403403 void visitCatchReturnInst(CatchReturnInst &CatchReturn);
404404 void visitCleanupPadInst(CleanupPadInst &CPI);
405 void visitFuncletPadInst(FuncletPadInst &FPI);
405406 void visitCatchSwitchInst(CatchSwitchInst &CatchSwitch);
406407 void visitCleanupReturnInst(CleanupReturnInst &CRI);
407408
30273028 Assert(BB->getFirstNonPHI() == &CPI,
30283029 "CatchPadInst not the first non-PHI instruction in the block.", &CPI);
30293030
3030 visitInstruction(CPI);
3031 visitFuncletPadInst(CPI);
30313032 }
30323033
30333034 void Verifier::visitCatchReturnInst(CatchReturnInst &CatchReturn) {
30573058 Assert(isa(ParentPad) || isa(ParentPad),
30583059 "CleanupPadInst has an invalid parent.", &CPI);
30593060
3061 visitFuncletPadInst(CPI);
3062 }
3063
3064 void Verifier::visitFuncletPadInst(FuncletPadInst &FPI) {
30603065 User *FirstUser = nullptr;
3061 BasicBlock *FirstUnwindDest = nullptr;
3062 for (User *U : CPI.users()) {
3063 BasicBlock *UnwindDest;
3064 if (CleanupReturnInst *CRI = dyn_cast(U)) {
3065 UnwindDest = CRI->getUnwindDest();
3066 } else if (isa(U) || isa(U)) {
3067 continue;
3068 } else if (CallSite(U)) {
3069 continue;
3070 } else {
3071 Assert(false, "bogus cleanuppad use", &CPI);
3072 }
3073
3074 if (!FirstUser) {
3075 FirstUser = U;
3076 FirstUnwindDest = UnwindDest;
3077 } else {
3078 Assert(
3079 UnwindDest == FirstUnwindDest,
3080 "cleanupret instructions from the same cleanuppad must have the same "
3081 "unwind destination",
3082 FirstUser, U);
3083 }
3084 }
3085
3086 visitInstruction(CPI);
3066 Value *FirstUnwindPad = nullptr;
3067 SmallVector Worklist({&FPI});
3068 while (!Worklist.empty()) {
3069 FuncletPadInst *CurrentPad = Worklist.pop_back_val();
3070 Value *UnresolvedAncestorPad = nullptr;
3071 for (User *U : CurrentPad->users()) {
3072 BasicBlock *UnwindDest;
3073 if (auto *CRI = dyn_cast(U)) {
3074 UnwindDest = CRI->getUnwindDest();
3075 } else if (auto *CSI = dyn_cast(U)) {
3076 // We allow catchswitch unwind to caller to nest
3077 // within an outer pad that unwinds somewhere else,
3078 // because catchswitch doesn't have a nounwind variant.
3079 // See e.g. SimplifyCFGOpt::SimplifyUnreachable.
3080 if (CSI->unwindsToCaller())
3081 continue;
3082 UnwindDest = CSI->getUnwindDest();
3083 } else if (auto *II = dyn_cast(U)) {
3084 UnwindDest = II->getUnwindDest();
3085 } else if (isa(U)) {
3086 // Calls which don't unwind may be found inside funclet
3087 // pads that unwind somewhere else. We don't *require*
3088 // such calls to be annotated nounwind.
3089 continue;
3090 } else if (auto *CPI = dyn_cast(U)) {
3091 // The unwind dest for a cleanup can only be found by
3092 // recursive search. Add it to the worklist, and we'll
3093 // search for its first use that determines where it unwinds.
3094 Worklist.push_back(CPI);
3095 continue;
3096 } else {
3097 Assert(isa(U), "Bogus funclet pad use", U);
3098 continue;
3099 }
3100
3101 Value *UnwindPad;
3102 bool ExitsFPI;
3103 if (UnwindDest) {
3104 UnwindPad = UnwindDest->getFirstNonPHI();
3105 Value *UnwindParent = getParentPad(UnwindPad);
3106 // Ignore unwind edges that don't exit CurrentPad.
3107 if (UnwindParent == CurrentPad)
3108 continue;
3109 // Determine whether the original funclet pad is exited,
3110 // and if we are scanning nested pads determine how many
3111 // of them are exited so we can stop searching their
3112 // children.
3113 Value *ExitedPad = CurrentPad;
3114 ExitsFPI = false;
3115 do {
3116 if (ExitedPad == &FPI) {
3117 ExitsFPI = true;
3118 // Now we can resolve any ancestors of CurrentPad up to
3119 // FPI, but not including FPI since we need to make sure
3120 // to check all direct users of FPI for consistency.
3121 UnresolvedAncestorPad = &FPI;
3122 break;
3123 }
3124 Value *ExitedParent = getParentPad(ExitedPad);
3125 if (ExitedParent == UnwindParent) {
3126 // ExitedPad is the ancestor-most pad which this unwind
3127 // edge exits, so we can resolve up to it, meaning that
3128 // ExitedParent is the first ancestor still unresolved.
3129 UnresolvedAncestorPad = ExitedParent;
3130 break;
3131 }
3132 ExitedPad = ExitedParent;
3133 } while (!isa(ExitedPad));
3134 } else {
3135 // Unwinding to caller exits all pads.
3136 UnwindPad = ConstantTokenNone::get(FPI.getContext());
3137 ExitsFPI = true;
3138 UnresolvedAncestorPad = &FPI;
3139 }
3140
3141 if (ExitsFPI) {
3142 // This unwind edge exits FPI. Make sure it agrees with other
3143 // such edges.
3144 if (FirstUser) {
3145 Assert(UnwindPad == FirstUnwindPad, "Unwind edges out of a funclet "
3146 "pad must have the same unwind "
3147 "dest",
3148 &FPI, U, FirstUser);
3149 } else {
3150 FirstUser = U;
3151 FirstUnwindPad = UnwindPad;
3152 }
3153 }
3154 // Make sure we visit all uses of FPI, but for nested pads stop as
3155 // soon as we know where they unwind to.
3156 if (CurrentPad != &FPI)
3157 break;
3158 }
3159 if (UnresolvedAncestorPad) {
3160 if (CurrentPad == UnresolvedAncestorPad) {
3161 // When CurrentPad is FPI itself, we don't mark it as resolved even if
3162 // we've found an unwind edge that exits it, because we need to verify
3163 // all direct uses of FPI.
3164 assert(CurrentPad == &FPI);
3165 continue;
3166 }
3167 // Pop off the worklist any nested pads that we've found an unwind
3168 // destination for. The pads on the worklist are the uncles,
3169 // great-uncles, etc. of CurrentPad. We've found an unwind destination
3170 // for all ancestors of CurrentPad up to but not including
3171 // UnresolvedAncestorPad.
3172 Value *ResolvedPad = CurrentPad;
3173 while (!Worklist.empty()) {
3174 Value *UnclePad = Worklist.back();
3175 Value *AncestorPad = getParentPad(UnclePad);
3176 // Walk ResolvedPad up the ancestor list until we either find the
3177 // uncle's parent or the last resolved ancestor.
3178 while (ResolvedPad != AncestorPad) {
3179 Value *ResolvedParent = getParentPad(ResolvedPad);
3180 if (ResolvedParent == UnresolvedAncestorPad) {
3181 break;
3182 }
3183 ResolvedPad = ResolvedParent;
3184 }
3185 // If the resolved ancestor search didn't find the uncle's parent,
3186 // then the uncle is not yet resolved.
3187 if (ResolvedPad != AncestorPad)
3188 break;
3189 // This uncle is resolved, so pop it from the worklist.
3190 Worklist.pop_back();
3191 }
3192 }
3193 }
3194
3195 if (FirstUnwindPad) {
3196 if (auto *CatchSwitch = dyn_cast(FPI.getParentPad())) {
3197 BasicBlock *SwitchUnwindDest = CatchSwitch->getUnwindDest();
3198 Value *SwitchUnwindPad;
3199 if (SwitchUnwindDest)
3200 SwitchUnwindPad = SwitchUnwindDest->getFirstNonPHI();
3201 else
3202 SwitchUnwindPad = ConstantTokenNone::get(FPI.getContext());
3203 Assert(SwitchUnwindPad == FirstUnwindPad,
3204 "Unwind edges out of a catch must have the same unwind dest as "
3205 "the parent catchswitch",
3206 &FPI, FirstUser, CatchSwitch);
3207 }
3208 }
3209
3210 visitInstruction(FPI);
30873211 }
30883212
30893213 void Verifier::visitCatchSwitchInst(CatchSwitchInst &CatchSwitch) {
3232
3333 shared:
3434 %x = call i32 @g()
35 invoke void @f() [ "funclet"(token %0) ]
35 invoke void @f()
3636 to label %shared.cont unwind label %inner
3737
3838 shared.cont:
7171
7272 shared:
7373 %x = call i32 @g()
74 invoke void @f() [ "funclet"(token %0) ]
74 invoke void @f()
7575 to label %shared.cont unwind label %inner
7676
7777 shared.cont:
4343 ; CHECK: catch:
4444 ; CHECK: store i32 2
4545 ; CHECK: invoke void @_CxxThrowException(
46 invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #1
46 invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) [ "funclet"(token %1) ]
4747 to label %unreachable unwind label %catch.dispatch.1
4848
4949 catch.dispatch.1: ; preds = %catch
1010 ; RUN: sed -e s/.T11:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK11 %s
1111 ; RUN: sed -e s/.T12:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK12 %s
1212 ; RUN: sed -e s/.T13:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK13 %s
13 ; RUN: sed -e s/.T14:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK14 %s
14 ; RUN: sed -e s/.T15:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK15 %s
15 ; RUN: sed -e s/.T16:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK16 %s
16 ; RUN: sed -e s/.T17:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK17 %s
1317
1418 declare void @g()
1519
182186 ;T13: unreachable
183187 ;T13: }
184188
189 ;T14: define void @f() personality void ()* @g {
190 ;T14: entry:
191 ;T14: ret void
192 ;T14: cleanup:
193 ;T14: %cp = cleanuppad within none []
194 ;T14: unreachable
195 ;T14: left:
196 ;T14: cleanupret from %cp unwind label %switch
197 ;T14: right:
198 ;T14: cleanupret from %cp unwind to caller
199 ;T14: ; CHECK14: Unwind edges out of a funclet pad must have the same unwind dest
200 ;T14: ; CHECK14-NEXT: %cp = cleanuppad within none []
201 ;T14: ; CHECK14-NEXT: cleanupret from %cp unwind label %switch
202 ;T14: ; CHECK14-NEXT: cleanupret from %cp unwind to caller
203 ;T14: switch:
204 ;T14: %cs = catchswitch within none [label %catch] unwind to caller
205 ;T14: catch:
206 ;T14: catchpad within %cs [i32 1]
207 ;T14: unreachable
208 ;T14: }
209
210 ;T15: define void @f() personality void ()* @g {
211 ;T15: entry:
212 ;T15: ret void
213 ;T15: switch:
214 ;T15: %cs = catchswitch within none [label %catch] unwind to caller
215 ;T15: catch:
216 ;T15: %catch.pad = catchpad within %cs [i32 1]
217 ;T15: invoke void @g() [ "funclet"(token %catch.pad) ]
218 ;T15: to label %unreachable unwind label %target1
219 ;T15: unreachable:
220 ;T15: unreachable
221 ;T15: target1:
222 ;T15: cleanuppad within none []
223 ;T15: unreachable
224 ;T15: target2:
225 ;T15: cleanuppad within none []
226 ;T15: unreachable
227 ;T15: nested.1:
228 ;T15: %nested.pad.1 = cleanuppad within %catch.pad []
229 ;T15: unreachable
230 ;T15: nested.2:
231 ;T15: %nested.pad.2 = cleanuppad within %nested.pad.1 []
232 ;T15: cleanupret from %nested.pad.2 unwind label %target2
233 ;T15: ; CHECK15: Unwind edges out of a funclet pad must have the same unwind dest
234 ;T15: ; CHECK15-NEXT: %catch.pad = catchpad within %cs [i32 1]
235 ;T15: ; CHECK15-NEXT: cleanupret from %nested.pad.2 unwind label %target2
236 ;T15: ; CHECK15-NEXT: invoke void @g() [ "funclet"(token %catch.pad) ]
237 ;T15: ; CHECK15-NEXT: to label %unreachable unwind label %target1
238 ;T15: }
239
240 ;T16: define void @f() personality void ()* @g {
241 ;T16: entry:
242 ;T16: ret void
243 ;T16: switch:
244 ;T16: %cs = catchswitch within none [label %catch] unwind to caller
245 ;T16: catch:
246 ;T16: %catch.pad = catchpad within %cs [i32 1]
247 ;T16: invoke void @g() [ "funclet"(token %catch.pad) ]
248 ;T16: to label %unreachable unwind label %target1
249 ;T16: ; CHECK16: Unwind edges out of a catch must have the same unwind dest as the parent catchswitch
250 ;T16: ; CHECK16-NEXT: %catch.pad = catchpad within %cs [i32 1]
251 ;T16: ; CHECK16-NEXT: invoke void @g() [ "funclet"(token %catch.pad) ]
252 ;T16: ; CHECK16-NEXT: to label %unreachable unwind label %target1
253 ;T16: ; CHECK16-NEXT: %cs = catchswitch within none [label %catch] unwind to caller
254 ;T16: unreachable:
255 ;T16: unreachable
256 ;T16: target1:
257 ;T16: cleanuppad within none []
258 ;T16: unreachable
259 ;T16: }
260
261 ;T17: define void @f() personality void ()* @g {
262 ;T17: entry:
263 ;T17: ret void
264 ;T17: switch:
265 ;T17: %cs = catchswitch within none [label %catch] unwind label %target1
266 ;T17: catch:
267 ;T17: %catch.pad = catchpad within %cs [i32 1]
268 ;T17: invoke void @g() [ "funclet"(token %catch.pad) ]
269 ;T17: to label %unreachable unwind label %target2
270 ;T17: ; CHECK17: Unwind edges out of a catch must have the same unwind dest as the parent catchswitch
271 ;T17: ; CHECK17-NEXT: %catch.pad = catchpad within %cs [i32 1]
272 ;T17: ; CHECK17-NEXT: invoke void @g() [ "funclet"(token %catch.pad) ]
273 ;T17: ; CHECK17-NEXT: to label %unreachable unwind label %target2
274 ;T17: ; CHECK17-NEXT: %cs = catchswitch within none [label %catch] unwind label %target1
275 ;T17: unreachable:
276 ;T17: unreachable
277 ;T17: target1:
278 ;T17: cleanuppad within none []
279 ;T17: unreachable
280 ;T17: target2:
281 ;T17: cleanuppad within none []
282 ;T17: unreachable
283 ;T17: }