[llvm] r257274 - [WinEH] Disallow cyclic unwinds
Joseph Tremoulet via llvm-commits
llvm-commits at lists.llvm.org
Sat Jan 9 20:31:06 PST 2016
Author: josepht
Date: Sat Jan 9 22:31:05 2016
New Revision: 257274
URL: http://llvm.org/viewvc/llvm-project?rev=257274&view=rev
Log:
[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
Modified:
llvm/trunk/docs/ExceptionHandling.rst
llvm/trunk/lib/IR/Verifier.cpp
llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll
llvm/trunk/test/Verifier/invalid-eh.ll
Modified: llvm/trunk/docs/ExceptionHandling.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/ExceptionHandling.rst?rev=257274&r1=257273&r2=257274&view=diff
==============================================================================
--- llvm/trunk/docs/ExceptionHandling.rst (original)
+++ llvm/trunk/docs/ExceptionHandling.rst Sat Jan 9 22:31:05 2016
@@ -835,3 +835,7 @@ unwind as ``nounwind``, it is legal to n
caller``\ " ``catchswitch`` within a funclet pad that has an unwind
destination other than caller; it is undefined behavior for such a ``call``
or ``catchswitch`` to unwind.
+
+Finally, the funclet pads' unwind destinations cannot form a cycle. This
+ensures that EH lowering can construct "try regions" with a tree-like
+structure, which funclet-based personalities may require.
Modified: llvm/trunk/lib/IR/Verifier.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Verifier.cpp?rev=257274&r1=257273&r2=257274&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Verifier.cpp (original)
+++ llvm/trunk/lib/IR/Verifier.cpp Sat Jan 9 22:31:05 2016
@@ -45,6 +45,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/Verifier.h"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -145,6 +146,11 @@ private:
OS << *C;
}
+ template <typename T> void Write(ArrayRef<T> Vs) {
+ for (const T &V : Vs)
+ Write(V);
+ }
+
template <typename T1, typename... Ts>
void WriteTs(const T1 &V1, const Ts &... Vs) {
Write(V1);
@@ -204,6 +210,10 @@ class Verifier : public InstVisitor<Veri
/// given function and the largest index passed to llvm.localrecover.
DenseMap<Function *, std::pair<unsigned, unsigned>> FrameEscapeInfo;
+ // Maps catchswitches and cleanuppads that unwind to siblings to the
+ // terminators that indicate the unwind, used to detect cycles therein.
+ MapVector<Instruction *, TerminatorInst *> SiblingFuncletInfo;
+
/// Cache of constants visited in search of ConstantExprs.
SmallPtrSet<const Constant *, 32> ConstantExprVisited;
@@ -245,9 +255,11 @@ public:
Broken = false;
// FIXME: We strip const here because the inst visitor strips const.
visit(const_cast<Function &>(F));
+ verifySiblingFuncletUnwinds();
InstsInThisBlock.clear();
LandingPadResultTy = nullptr;
SawFrameEscape = false;
+ SiblingFuncletInfo.clear();
return !Broken;
}
@@ -429,6 +441,7 @@ private:
void visitConstantExpr(const ConstantExpr *CE);
void VerifyStatepoint(ImmutableCallSite CS);
void verifyFrameRecoverIndices();
+ void verifySiblingFuncletUnwinds();
// Module-level debug info verification...
void verifyTypeRefs();
@@ -1697,6 +1710,59 @@ void Verifier::verifyFrameRecoverIndices
}
}
+static Instruction *getSuccPad(TerminatorInst *Terminator) {
+ BasicBlock *UnwindDest;
+ if (auto *II = dyn_cast<InvokeInst>(Terminator))
+ UnwindDest = II->getUnwindDest();
+ else if (auto *CSI = dyn_cast<CatchSwitchInst>(Terminator))
+ UnwindDest = CSI->getUnwindDest();
+ else
+ UnwindDest = cast<CleanupReturnInst>(Terminator)->getUnwindDest();
+ return UnwindDest->getFirstNonPHI();
+}
+
+void Verifier::verifySiblingFuncletUnwinds() {
+ SmallPtrSet<Instruction *, 8> Visited;
+ SmallPtrSet<Instruction *, 8> Active;
+ for (const auto &Pair : SiblingFuncletInfo) {
+ Instruction *PredPad = Pair.first;
+ if (Visited.count(PredPad))
+ continue;
+ Active.insert(PredPad);
+ TerminatorInst *Terminator = Pair.second;
+ do {
+ Instruction *SuccPad = getSuccPad(Terminator);
+ if (Active.count(SuccPad)) {
+ // Found a cycle; report error
+ Instruction *CyclePad = SuccPad;
+ SmallVector<Instruction *, 8> CycleNodes;
+ do {
+ CycleNodes.push_back(CyclePad);
+ TerminatorInst *CycleTerminator = SiblingFuncletInfo[CyclePad];
+ if (CycleTerminator != CyclePad)
+ CycleNodes.push_back(CycleTerminator);
+ CyclePad = getSuccPad(CycleTerminator);
+ } while (CyclePad != SuccPad);
+ Assert(false, "EH pads can't handle each other's exceptions",
+ ArrayRef<Instruction *>(CycleNodes));
+ }
+ // Don't re-walk a node we've already checked
+ if (!Visited.insert(SuccPad).second)
+ break;
+ // Walk to this successor if it has a map entry.
+ PredPad = SuccPad;
+ auto TermI = SiblingFuncletInfo.find(PredPad);
+ if (TermI == SiblingFuncletInfo.end())
+ break;
+ Terminator = TermI->second;
+ Active.insert(PredPad);
+ } while (true);
+ // Each node only has one successor, so we've walked all the active
+ // nodes' successors.
+ Active.clear();
+ }
+}
+
// visitFunction - Verify that a function is ok.
//
void Verifier::visitFunction(const Function &F) {
@@ -3150,6 +3216,10 @@ void Verifier::visitFuncletPadInst(Funcl
} else {
FirstUser = U;
FirstUnwindPad = UnwindPad;
+ // Record cleanup sibling unwinds for verifySiblingFuncletUnwinds
+ if (isa<CleanupPadInst>(&FPI) && !isa<ConstantTokenNone>(UnwindPad) &&
+ getParentPad(UnwindPad) == getParentPad(&FPI))
+ SiblingFuncletInfo[&FPI] = cast<TerminatorInst>(U);
}
}
// Make sure we visit all uses of FPI, but for nested pads stop as
@@ -3227,17 +3297,21 @@ void Verifier::visitCatchSwitchInst(Catc
"CatchSwitchInst not the first non-PHI instruction in the block.",
&CatchSwitch);
+ auto *ParentPad = CatchSwitch.getParentPad();
+ Assert(isa<ConstantTokenNone>(ParentPad) || isa<FuncletPadInst>(ParentPad),
+ "CatchSwitchInst has an invalid parent.", ParentPad);
+
if (BasicBlock *UnwindDest = CatchSwitch.getUnwindDest()) {
Instruction *I = UnwindDest->getFirstNonPHI();
Assert(I->isEHPad() && !isa<LandingPadInst>(I),
"CatchSwitchInst must unwind to an EH block which is not a "
"landingpad.",
&CatchSwitch);
- }
- auto *ParentPad = CatchSwitch.getParentPad();
- Assert(isa<ConstantTokenNone>(ParentPad) || isa<FuncletPadInst>(ParentPad),
- "CatchSwitchInst has an invalid parent.", ParentPad);
+ // Record catchswitch sibling unwinds for verifySiblingFuncletUnwinds
+ if (getParentPad(I) == ParentPad)
+ SiblingFuncletInfo[&CatchSwitch] = &CatchSwitch;
+ }
Assert(CatchSwitch.getNumHandlers() != 0,
"CatchSwitchInst cannot have empty handler list", &CatchSwitch);
Modified: llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll?rev=257274&r1=257273&r2=257274&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll (original)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-cloning.ll Sat Jan 9 22:31:05 2016
@@ -233,48 +233,6 @@ exit:
; CHECK-NEXT: br label %outer.ret
-define void @test9() personality i32 (...)* @__C_specific_handler {
-entry:
- invoke void @f()
- to label %invoke.cont unwind label %left
-invoke.cont:
- invoke void @f()
- to label %unreachable unwind label %right
-left:
- %cp.left = cleanuppad within none []
- call void @llvm.foo(i32 1)
- invoke void @f() [ "funclet"(token %cp.left) ]
- to label %unreachable unwind label %right
-right:
- %cp.right = cleanuppad within none []
- call void @llvm.foo(i32 2)
- invoke void @f() [ "funclet"(token %cp.right) ]
- to label %unreachable unwind label %left
-unreachable:
- unreachable
-}
-; This is an irreducible loop with two funclets that enter each other.
-; CHECK-LABEL: define void @test9(
-; CHECK: entry:
-; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]]
-; CHECK: invoke.cont:
-; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]]
-; CHECK: [[LEFT]]:
-; CHECK: call void @llvm.foo(i32 1)
-; CHECK: invoke void @f()
-; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT]]
-; CHECK: [[RIGHT]]:
-; CHECK: call void @llvm.foo(i32 2)
-; CHECK: invoke void @f()
-; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT]]
-; CHECK: [[UNREACHABLE_RIGHT]]:
-; CHECK: unreachable
-; CHECK: [[UNREACHABLE_LEFT]]:
-; CHECK: unreachable
-; CHECK: [[UNREACHABLE_ENTRY]]:
-; CHECK: unreachable
-
-
define void @test10() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
Modified: llvm/trunk/test/Verifier/invalid-eh.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Verifier/invalid-eh.ll?rev=257274&r1=257273&r2=257274&view=diff
==============================================================================
--- llvm/trunk/test/Verifier/invalid-eh.ll (original)
+++ llvm/trunk/test/Verifier/invalid-eh.ll Sat Jan 9 22:31:05 2016
@@ -15,6 +15,8 @@
; RUN: sed -e s/.T15:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK15 %s
; RUN: sed -e s/.T16:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK16 %s
; RUN: sed -e s/.T17:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK17 %s
+; RUN: sed -e s/.T18:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK18 %s
+; RUN: sed -e s/.T19:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK19 %s
declare void @g()
@@ -282,3 +284,58 @@ declare void @g()
;T17: cleanuppad within none []
;T17: unreachable
;T17: }
+
+;T18: define void @f() personality void ()* @g {
+;T18: entry:
+;T18: invoke void @g()
+;T18: to label %invoke.cont unwind label %left
+;T18: invoke.cont:
+;T18: invoke void @g()
+;T18: to label %unreachable unwind label %right
+;T18: left:
+;T18: %cp.left = cleanuppad within none []
+;T18: invoke void @g() [ "funclet"(token %cp.left) ]
+;T18: to label %unreachable unwind label %right
+;T18: right:
+;T18: %cp.right = cleanuppad within none []
+;T18: invoke void @g() [ "funclet"(token %cp.right) ]
+;T18: to label %unreachable unwind label %left
+;T18: ; CHECK18: EH pads can't handle each other's exceptions
+;T18: ; CHECK18-NEXT: %cp.left = cleanuppad within none []
+;T18: ; CHECK18-NEXT: invoke void @g() [ "funclet"(token %cp.left) ]
+;T18: ; CHECK18-NEXT: to label %unreachable unwind label %right
+;T18: ; CHECK18-NEXT: %cp.right = cleanuppad within none []
+;T18: ; CHECK18-NEXT: invoke void @g() [ "funclet"(token %cp.right) ]
+;T18: ; CHECK18-NEXT: to label %unreachable unwind label %left
+;T18: unreachable:
+;T18: unreachable
+;T18: }
+
+;T19: define void @f() personality void ()* @g {
+;T19: entry:
+;T19: ret void
+;T19: red:
+;T19: %redpad = cleanuppad within none []
+;T19: unreachable
+;T19: red.inner:
+;T19: %innerpad = cleanuppad within %redpad []
+;T19: invoke void @g() [ "funclet"(token %innerpad) ]
+;T19: to label %unreachable unwind label %green
+;T19: green:
+;T19: %greenswitch = catchswitch within none [label %catch] unwind label %blue
+;T19: catch:
+;T19: catchpad within %greenswitch [i32 42]
+;T19: unreachable
+;T19: blue:
+;T19: %bluepad = cleanuppad within none []
+;T19: cleanupret from %bluepad unwind label %red
+;T19: ; CHECK19: EH pads can't handle each other's exceptions
+;T19: ; CHECK19-NEXT: %redpad = cleanuppad within none []
+;T19: ; CHECK19-NEXT: invoke void @g() [ "funclet"(token %innerpad) ]
+;T19: ; CHECK19-NEXT: to label %unreachable unwind label %green
+;T19: ; CHECK19-NEXT: %greenswitch = catchswitch within none [label %catch] unwind label %blue
+;T19: ; CHECK19-NEXT: %bluepad = cleanuppad within none []
+;T19: ; CHECK19-NEXT: cleanupret from %bluepad unwind label %red
+;T19: unreachable:
+;T19: unreachable
+;T19: }
More information about the llvm-commits
mailing list