llvm.org GIT mirror llvm / 12b6cd2
[WinEH] Disallow cyclic unwinds Summary: Funclet-based EH personalities/tables likely can't handle these, and they can't be generated at source, so make them officially illegal in IR as well. Reviewers: andrew.w.kaylor, rnk, majnemer Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D15963 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@257274 91177308-0d34-0410-b5e6-96231b3b80d8 Joseph Tremoulet 4 years ago
4 changed file(s) with 140 addition(s) and 47 deletion(s). Raw diff Collapse all Expand all
834834 caller``\ " ``catchswitch`` within a funclet pad that has an unwind
835835 destination other than caller; it is undefined behavior for such a ``call``
836836 or ``catchswitch`` to unwind.
837
838 Finally, the funclet pads' unwind destinations cannot form a cycle. This
839 ensures that EH lowering can construct "try regions" with a tree-like
840 structure, which funclet-based personalities may require.
4444 //===----------------------------------------------------------------------===//
4545
4646 #include "llvm/IR/Verifier.h"
47 #include "llvm/ADT/MapVector.h"
4748 #include "llvm/ADT/STLExtras.h"
4849 #include "llvm/ADT/SetVector.h"
4950 #include "llvm/ADT/SmallPtrSet.h"
144145 OS << *C;
145146 }
146147
148 template void Write(ArrayRef Vs) {
149 for (const T &V : Vs)
150 Write(V);
151 }
152
147153 template
148154 void WriteTs(const T1 &V1, const Ts &... Vs) {
149155 Write(V1);
203209 /// given function and the largest index passed to llvm.localrecover.
204210 DenseMap> FrameEscapeInfo;
205211
212 // Maps catchswitches and cleanuppads that unwind to siblings to the
213 // terminators that indicate the unwind, used to detect cycles therein.
214 MapVector SiblingFuncletInfo;
215
206216 /// Cache of constants visited in search of ConstantExprs.
207217 SmallPtrSet ConstantExprVisited;
208218
244254 Broken = false;
245255 // FIXME: We strip const here because the inst visitor strips const.
246256 visit(const_cast(F));
257 verifySiblingFuncletUnwinds();
247258 InstsInThisBlock.clear();
248259 LandingPadResultTy = nullptr;
249260 SawFrameEscape = false;
261 SiblingFuncletInfo.clear();
250262
251263 return !Broken;
252264 }
428440 void visitConstantExpr(const ConstantExpr *CE);
429441 void VerifyStatepoint(ImmutableCallSite CS);
430442 void verifyFrameRecoverIndices();
443 void verifySiblingFuncletUnwinds();
431444
432445 // Module-level debug info verification...
433446 void verifyTypeRefs();
16931706 "number of arguments passed ot llvm.localescape in the parent "
16941707 "function",
16951708 F);
1709 }
1710 }
1711
1712 static Instruction *getSuccPad(TerminatorInst *Terminator) {
1713 BasicBlock *UnwindDest;
1714 if (auto *II = dyn_cast(Terminator))
1715 UnwindDest = II->getUnwindDest();
1716 else if (auto *CSI = dyn_cast(Terminator))
1717 UnwindDest = CSI->getUnwindDest();
1718 else
1719 UnwindDest = cast(Terminator)->getUnwindDest();
1720 return UnwindDest->getFirstNonPHI();
1721 }
1722
1723 void Verifier::verifySiblingFuncletUnwinds() {
1724 SmallPtrSet Visited;
1725 SmallPtrSet Active;
1726 for (const auto &Pair : SiblingFuncletInfo) {
1727 Instruction *PredPad = Pair.first;
1728 if (Visited.count(PredPad))
1729 continue;
1730 Active.insert(PredPad);
1731 TerminatorInst *Terminator = Pair.second;
1732 do {
1733 Instruction *SuccPad = getSuccPad(Terminator);
1734 if (Active.count(SuccPad)) {
1735 // Found a cycle; report error
1736 Instruction *CyclePad = SuccPad;
1737 SmallVector CycleNodes;
1738 do {
1739 CycleNodes.push_back(CyclePad);
1740 TerminatorInst *CycleTerminator = SiblingFuncletInfo[CyclePad];
1741 if (CycleTerminator != CyclePad)
1742 CycleNodes.push_back(CycleTerminator);
1743 CyclePad = getSuccPad(CycleTerminator);
1744 } while (CyclePad != SuccPad);
1745 Assert(false, "EH pads can't handle each other's exceptions",
1746 ArrayRef(CycleNodes));
1747 }
1748 // Don't re-walk a node we've already checked
1749 if (!Visited.insert(SuccPad).second)
1750 break;
1751 // Walk to this successor if it has a map entry.
1752 PredPad = SuccPad;
1753 auto TermI = SiblingFuncletInfo.find(PredPad);
1754 if (TermI == SiblingFuncletInfo.end())
1755 break;
1756 Terminator = TermI->second;
1757 Active.insert(PredPad);
1758 } while (true);
1759 // Each node only has one successor, so we've walked all the active
1760 // nodes' successors.
1761 Active.clear();
16961762 }
16971763 }
16981764
31493215 } else {
31503216 FirstUser = U;
31513217 FirstUnwindPad = UnwindPad;
3218 // Record cleanup sibling unwinds for verifySiblingFuncletUnwinds
3219 if (isa(&FPI) && !isa(UnwindPad) &&
3220 getParentPad(UnwindPad) == getParentPad(&FPI))
3221 SiblingFuncletInfo[&FPI] = cast(U);
31523222 }
31533223 }
31543224 // Make sure we visit all uses of FPI, but for nested pads stop as
32263296 "CatchSwitchInst not the first non-PHI instruction in the block.",
32273297 &CatchSwitch);
32283298
3299 auto *ParentPad = CatchSwitch.getParentPad();
3300 Assert(isa(ParentPad) || isa(ParentPad),
3301 "CatchSwitchInst has an invalid parent.", ParentPad);
3302
32293303 if (BasicBlock *UnwindDest = CatchSwitch.getUnwindDest()) {
32303304 Instruction *I = UnwindDest->getFirstNonPHI();
32313305 Assert(I->isEHPad() && !isa(I),
32323306 "CatchSwitchInst must unwind to an EH block which is not a "
32333307 "landingpad.",
32343308 &CatchSwitch);
3235 }
3236
3237 auto *ParentPad = CatchSwitch.getParentPad();
3238 Assert(isa(ParentPad) || isa(ParentPad),
3239 "CatchSwitchInst has an invalid parent.", ParentPad);
3309
3310 // Record catchswitch sibling unwinds for verifySiblingFuncletUnwinds
3311 if (getParentPad(I) == ParentPad)
3312 SiblingFuncletInfo[&CatchSwitch] = &CatchSwitch;
3313 }
32403314
32413315 Assert(CatchSwitch.getNumHandlers() != 0,
32423316 "CatchSwitchInst cannot have empty handler list", &CatchSwitch);
232232 ; CHECK-NEXT: br label %outer.ret
233233
234234
235 define void @test9() personality i32 (...)* @__C_specific_handler {
236 entry:
237 invoke void @f()
238 to label %invoke.cont unwind label %left
239 invoke.cont:
240 invoke void @f()
241 to label %unreachable unwind label %right
242 left:
243 %cp.left = cleanuppad within none []
244 call void @llvm.foo(i32 1)
245 invoke void @f() [ "funclet"(token %cp.left) ]
246 to label %unreachable unwind label %right
247 right:
248 %cp.right = cleanuppad within none []
249 call void @llvm.foo(i32 2)
250 invoke void @f() [ "funclet"(token %cp.right) ]
251 to label %unreachable unwind label %left
252 unreachable:
253 unreachable
254 }
255 ; This is an irreducible loop with two funclets that enter each other.
256 ; CHECK-LABEL: define void @test9(
257 ; CHECK: entry:
258 ; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]]
259 ; CHECK: invoke.cont:
260 ; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]]
261 ; CHECK: [[LEFT]]:
262 ; CHECK: call void @llvm.foo(i32 1)
263 ; CHECK: invoke void @f()
264 ; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT]]
265 ; CHECK: [[RIGHT]]:
266 ; CHECK: call void @llvm.foo(i32 2)
267 ; CHECK: invoke void @f()
268 ; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT]]
269 ; CHECK: [[UNREACHABLE_RIGHT]]:
270 ; CHECK: unreachable
271 ; CHECK: [[UNREACHABLE_LEFT]]:
272 ; CHECK: unreachable
273 ; CHECK: [[UNREACHABLE_ENTRY]]:
274 ; CHECK: unreachable
275
276
277235 define void @test10() personality i32 (...)* @__CxxFrameHandler3 {
278236 entry:
279237 invoke void @f()
1414 ; RUN: sed -e s/.T15:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK15 %s
1515 ; RUN: sed -e s/.T16:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK16 %s
1616 ; RUN: sed -e s/.T17:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK17 %s
17 ; RUN: sed -e s/.T18:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK18 %s
18 ; RUN: sed -e s/.T19:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK19 %s
1719
1820 declare void @g()
1921
281283 ;T17: cleanuppad within none []
282284 ;T17: unreachable
283285 ;T17: }
286
287 ;T18: define void @f() personality void ()* @g {
288 ;T18: entry:
289 ;T18: invoke void @g()
290 ;T18: to label %invoke.cont unwind label %left
291 ;T18: invoke.cont:
292 ;T18: invoke void @g()
293 ;T18: to label %unreachable unwind label %right
294 ;T18: left:
295 ;T18: %cp.left = cleanuppad within none []
296 ;T18: invoke void @g() [ "funclet"(token %cp.left) ]
297 ;T18: to label %unreachable unwind label %right
298 ;T18: right:
299 ;T18: %cp.right = cleanuppad within none []
300 ;T18: invoke void @g() [ "funclet"(token %cp.right) ]
301 ;T18: to label %unreachable unwind label %left
302 ;T18: ; CHECK18: EH pads can't handle each other's exceptions
303 ;T18: ; CHECK18-NEXT: %cp.left = cleanuppad within none []
304 ;T18: ; CHECK18-NEXT: invoke void @g() [ "funclet"(token %cp.left) ]
305 ;T18: ; CHECK18-NEXT: to label %unreachable unwind label %right
306 ;T18: ; CHECK18-NEXT: %cp.right = cleanuppad within none []
307 ;T18: ; CHECK18-NEXT: invoke void @g() [ "funclet"(token %cp.right) ]
308 ;T18: ; CHECK18-NEXT: to label %unreachable unwind label %left
309 ;T18: unreachable:
310 ;T18: unreachable
311 ;T18: }
312
313 ;T19: define void @f() personality void ()* @g {
314 ;T19: entry:
315 ;T19: ret void
316 ;T19: red:
317 ;T19: %redpad = cleanuppad within none []
318 ;T19: unreachable
319 ;T19: red.inner:
320 ;T19: %innerpad = cleanuppad within %redpad []
321 ;T19: invoke void @g() [ "funclet"(token %innerpad) ]
322 ;T19: to label %unreachable unwind label %green
323 ;T19: green:
324 ;T19: %greenswitch = catchswitch within none [label %catch] unwind label %blue
325 ;T19: catch:
326 ;T19: catchpad within %greenswitch [i32 42]
327 ;T19: unreachable
328 ;T19: blue:
329 ;T19: %bluepad = cleanuppad within none []
330 ;T19: cleanupret from %bluepad unwind label %red
331 ;T19: ; CHECK19: EH pads can't handle each other's exceptions
332 ;T19: ; CHECK19-NEXT: %redpad = cleanuppad within none []
333 ;T19: ; CHECK19-NEXT: invoke void @g() [ "funclet"(token %innerpad) ]
334 ;T19: ; CHECK19-NEXT: to label %unreachable unwind label %green
335 ;T19: ; CHECK19-NEXT: %greenswitch = catchswitch within none [label %catch] unwind label %blue
336 ;T19: ; CHECK19-NEXT: %bluepad = cleanuppad within none []
337 ;T19: ; CHECK19-NEXT: cleanupret from %bluepad unwind label %red
338 ;T19: unreachable:
339 ;T19: unreachable
340 ;T19: }