[llvm] r232664 - Use WinEHPrepare to outline SEH finally blocks
Reid Kleckner
reid at kleckner.net
Wed Mar 18 13:26:53 PDT 2015
Author: rnk
Date: Wed Mar 18 15:26:53 2015
New Revision: 232664
URL: http://llvm.org/viewvc/llvm-project?rev=232664&view=rev
Log:
Use WinEHPrepare to outline SEH finally blocks
No outlining is necessary for SEH catch blocks. Use the blockaddr of the
handler in place of the usual outlined function.
Reviewers: majnemer, andrew.w.kaylor
Differential Revision: http://reviews.llvm.org/D8370
Added:
llvm/trunk/test/CodeGen/WinEH/seh-simple.ll
Modified:
llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
Modified: llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/WinEHPrepare.cpp?rev=232664&r1=232663&r2=232664&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/WinEHPrepare.cpp (original)
+++ llvm/trunk/lib/CodeGen/WinEHPrepare.cpp Wed Mar 18 15:26:53 2015
@@ -27,6 +27,7 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
@@ -77,8 +78,8 @@ public:
}
private:
- bool prepareCPPEHHandlers(Function &F,
- SmallVectorImpl<LandingPadInst *> &LPads);
+ bool prepareExceptionHandlers(Function &F,
+ SmallVectorImpl<LandingPadInst *> &LPads);
bool outlineHandler(ActionHandler *Action, Function *SrcFn,
LandingPadInst *LPad, BasicBlock *StartBB,
FrameVarInfoMap &VarInfo);
@@ -88,6 +89,10 @@ private:
VisitedBlockSet &VisitedBlocks);
CleanupHandler *findCleanupHandler(BasicBlock *StartBB, BasicBlock *EndBB);
+ void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB);
+
+ // All fields are reset by runOnFunction.
+ EHPersonality Personality;
CatchHandlerMapTy CatchHandlerMap;
CleanupHandlerMapTy CleanupHandlerMap;
DenseMap<const LandingPadInst *, LandingPadMap> LPadMaps;
@@ -238,20 +243,22 @@ public:
class ActionHandler {
public:
ActionHandler(BasicBlock *BB, ActionType Type)
- : StartBB(BB), Type(Type), OutlinedFn(nullptr) {}
+ : StartBB(BB), Type(Type), HandlerBlockOrFunc(nullptr) {}
ActionType getType() const { return Type; }
BasicBlock *getStartBlock() const { return StartBB; }
- bool hasBeenOutlined() { return OutlinedFn != nullptr; }
+ bool hasBeenProcessed() { return HandlerBlockOrFunc != nullptr; }
- void setOutlinedFunction(Function *F) { OutlinedFn = F; }
- Function *getOutlinedFunction() { return OutlinedFn; }
+ void setHandlerBlockOrFunc(Constant *F) { HandlerBlockOrFunc = F; }
+ Constant *getHandlerBlockOrFunc() { return HandlerBlockOrFunc; }
private:
BasicBlock *StartBB;
ActionType Type;
- Function *OutlinedFn;
+
+ // Can be either a BlockAddress or a Function depending on the EH personality.
+ Constant *HandlerBlockOrFunc;
};
class CatchHandler : public ActionHandler {
@@ -326,6 +333,11 @@ FunctionPass *llvm::createWinEHPass(cons
return new WinEHPrepare(TM);
}
+// FIXME: Remove this once the backend can handle the prepared IR.
+static cl::opt<bool>
+SEHPrepare("sehprepare", cl::Hidden,
+ cl::desc("Prepare functions with SEH personalities"));
+
bool WinEHPrepare::runOnFunction(Function &Fn) {
SmallVector<LandingPadInst *, 4> LPads;
SmallVector<ResumeInst *, 4> Resumes;
@@ -341,27 +353,24 @@ bool WinEHPrepare::runOnFunction(Functio
return false;
// Classify the personality to see what kind of preparation we need.
- EHPersonality Pers = classifyEHPersonality(LPads.back()->getPersonalityFn());
+ Personality = classifyEHPersonality(LPads.back()->getPersonalityFn());
// Do nothing if this is not an MSVC personality.
- if (!isMSVCEHPersonality(Pers))
+ if (!isMSVCEHPersonality(Personality))
return false;
- // FIXME: This only returns true if the C++ EH handlers were outlined.
- // When that code is complete, it should always return whatever
- // prepareCPPEHHandlers returns.
- if (Pers == EHPersonality::MSVC_CXX && prepareCPPEHHandlers(Fn, LPads))
+ if (isAsynchronousEHPersonality(Personality) && !SEHPrepare) {
+ // Replace all resume instructions with unreachable.
+ // FIXME: Remove this once the backend can handle the prepared IR.
+ for (ResumeInst *Resume : Resumes) {
+ IRBuilder<>(Resume).CreateUnreachable();
+ Resume->eraseFromParent();
+ }
return true;
-
- // FIXME: SEH Cleanups are unimplemented. Replace them with unreachable.
- if (Resumes.empty())
- return false;
-
- for (ResumeInst *Resume : Resumes) {
- IRBuilder<>(Resume).CreateUnreachable();
- Resume->eraseFromParent();
}
+ // If there were any landing pads, prepareExceptionHandlers will make changes.
+ prepareExceptionHandlers(Fn, LPads);
return true;
}
@@ -371,7 +380,7 @@ bool WinEHPrepare::doFinalization(Module
void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {}
-bool WinEHPrepare::prepareCPPEHHandlers(
+bool WinEHPrepare::prepareExceptionHandlers(
Function &F, SmallVectorImpl<LandingPadInst *> &LPads) {
// These containers are used to re-map frame variables that are used in
// outlined catch and cleanup handlers. They will be populated as the
@@ -417,9 +426,21 @@ bool WinEHPrepare::prepareCPPEHHandlers(
mapLandingPadBlocks(LPad, Actions);
for (ActionHandler *Action : Actions) {
- if (Action->hasBeenOutlined())
+ if (Action->hasBeenProcessed())
continue;
BasicBlock *StartBB = Action->getStartBlock();
+
+ // SEH doesn't do any outlining for catches. Instead, pass the handler
+ // basic block addr to llvm.eh.actions and list the block as a return
+ // target.
+ if (isAsynchronousEHPersonality(Personality)) {
+ if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
+ processSEHCatchHandler(CatchAction, StartBB);
+ HandlersOutlined = true;
+ continue;
+ }
+ }
+
if (outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo)) {
HandlersOutlined = true;
}
@@ -440,6 +461,12 @@ bool WinEHPrepare::prepareCPPEHHandlers(
Invoke->setUnwindDest(NewLPadBB);
}
+ // Replace uses of the old lpad in phis with this block and delete the old
+ // block.
+ LPadBB->replaceSuccessorsPhiUsesWith(NewLPadBB);
+ LPadBB->getTerminator()->eraseFromParent();
+ new UnreachableInst(LPadBB->getContext(), LPadBB);
+
// Add a call to describe the actions for this landing pad.
std::vector<Value *> ActionArgs;
ActionArgs.push_back(NewLPad);
@@ -455,8 +482,8 @@ bool WinEHPrepare::prepareCPPEHHandlers(
} else {
ActionArgs.push_back(ConstantInt::get(Int32Type, 1));
}
- Constant *HandlerPtr =
- ConstantExpr::getBitCast(Action->getOutlinedFunction(), Int8PtrType);
+ Constant *HandlerPtr = ConstantExpr::getBitCast(
+ Action->getHandlerBlockOrFunc(), Int8PtrType);
ActionArgs.push_back(HandlerPtr);
}
CallInst *Recover =
@@ -656,11 +683,10 @@ bool WinEHPrepare::outlineHandler(Action
LandingPadMap &LPadMap = LPadMaps[LPad];
if (!LPadMap.isInitialized())
LPadMap.mapLandingPad(LPad);
- if (Action->getType() == Catch) {
- Constant *SelectorType = cast<CatchHandler>(Action)->getSelector();
- Director.reset(
- new WinEHCatchDirector(Handler, SelectorType, VarInfo, LPadMap));
- LPadMap.remapSelector(VMap, ConstantInt::get( Type::getInt32Ty(Context), 1));
+ if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
+ Constant *Sel = CatchAction->getSelector();
+ Director.reset(new WinEHCatchDirector(Handler, Sel, VarInfo, LPadMap));
+ LPadMap.remapSelector(VMap, ConstantInt::get(Type::getInt32Ty(Context), 1));
} else {
Director.reset(new WinEHCleanupDirector(Handler, VarInfo, LPadMap));
}
@@ -687,11 +713,38 @@ bool WinEHPrepare::outlineHandler(Action
CatchAction->setReturnTargets(CatchDirector->getReturnTargets());
}
- Action->setOutlinedFunction(Handler);
+ Action->setHandlerBlockOrFunc(Handler);
return true;
}
+/// This BB must end in a selector dispatch. All we need to do is pass the
+/// handler block to llvm.eh.actions and list it as a possible indirectbr
+/// target.
+void WinEHPrepare::processSEHCatchHandler(CatchHandler *CatchAction,
+ BasicBlock *StartBB) {
+ BasicBlock *HandlerBB;
+ BasicBlock *NextBB;
+ Constant *Selector;
+ bool Res = isSelectorDispatch(StartBB, HandlerBB, Selector, NextBB);
+ if (Res) {
+ // If this was EH dispatch, this must be a conditional branch to the handler
+ // block.
+ // FIXME: Handle instructions in the dispatch block. Currently we drop them,
+ // leading to crashes if some optimization hoists stuff here.
+ assert(CatchAction->getSelector() && HandlerBB &&
+ "expected catch EH dispatch");
+ } else {
+ // This must be a catch-all. Split the block after the landingpad.
+ assert(CatchAction->getSelector()->isNullValue() && "expected catch-all");
+ HandlerBB =
+ StartBB->splitBasicBlock(StartBB->getFirstInsertionPt(), "catch.all");
+ }
+ CatchAction->setHandlerBlockOrFunc(BlockAddress::get(HandlerBB));
+ TinyPtrVector<BasicBlock *> Targets(HandlerBB);
+ CatchAction->setReturnTargets(Targets);
+}
+
void LandingPadMap::mapLandingPad(const LandingPadInst *LPad) {
// Each instance of this class should only ever be used to map a single
// landing pad.
@@ -1115,12 +1168,18 @@ void WinEHPrepare::mapLandingPadBlocks(L
// The catch all must occur last.
assert(HandlersFound == NumClauses - 1);
- // See if there is any interesting code executed before the catch.
- if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
- // Add a cleanup entry to the list
- Actions.insertCleanupHandler(CleanupAction);
- DEBUG(dbgs() << " Found cleanup code in block "
- << CleanupAction->getStartBlock()->getName() << "\n");
+ // For C++ EH, check if there is any interesting cleanup code before we
+ // begin the catch. This is important because cleanups cannot rethrow
+ // exceptions but code called from catches can. For SEH, it isn't
+ // important if some finally code before a catch-all is executed out of
+ // line or after recovering from the exception.
+ if (Personality == EHPersonality::MSVC_CXX) {
+ if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
+ // Add a cleanup entry to the list
+ Actions.insertCleanupHandler(CleanupAction);
+ DEBUG(dbgs() << " Found cleanup code in block "
+ << CleanupAction->getStartBlock()->getName() << "\n");
+ }
}
// Add the catch handler to the action list.
@@ -1130,7 +1189,10 @@ void WinEHPrepare::mapLandingPadBlocks(L
Actions.insertCatchHandler(Action);
DEBUG(dbgs() << " Catch all handler at block " << BB->getName() << "\n");
++HandlersFound;
- continue;
+
+ // Once we reach a catch-all, don't expect to hit a resume instruction.
+ BB = nullptr;
+ break;
}
CatchHandler *CatchAction = findCatchHandler(BB, NextBB, VisitedBlocks);
@@ -1155,7 +1217,8 @@ void WinEHPrepare::mapLandingPadBlocks(L
BB = NextBB;
}
- // See if there is any interesting code executed before the resume.
+ // If we didn't wind up in a catch-all, see if there is any interesting code
+ // executed before the resume.
if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
// Add a cleanup entry to the list
Actions.insertCleanupHandler(CleanupAction);
@@ -1306,8 +1369,13 @@ CleanupHandler *WinEHPrepare::findCleanu
if (auto *Resume = dyn_cast<ResumeInst>(Terminator)) {
InsertValueInst *Insert1 = nullptr;
InsertValueInst *Insert2 = nullptr;
- if (!isa<PHINode>(Resume->getOperand(0))) {
- Insert2 = dyn_cast<InsertValueInst>(Resume->getOperand(0));
+ Value *ResumeVal = Resume->getOperand(0);
+ // If there is only one landingpad, we may use the lpad directly with no
+ // insertions.
+ if (isa<LandingPadInst>(ResumeVal))
+ return nullptr;
+ if (!isa<PHINode>(ResumeVal)) {
+ Insert2 = dyn_cast<InsertValueInst>(ResumeVal);
if (!Insert2)
return createCleanupHandler(CleanupHandlerMap, BB);
Insert1 = dyn_cast<InsertValueInst>(Insert2->getAggregateOperand());
Added: llvm/trunk/test/CodeGen/WinEH/seh-simple.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/seh-simple.ll?rev=232664&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/seh-simple.ll (added)
+++ llvm/trunk/test/CodeGen/WinEH/seh-simple.ll Wed Mar 18 15:26:53 2015
@@ -0,0 +1,138 @@
+; RUN: opt -S -winehprepare -sehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+declare void @cleanup()
+declare i32 @filt()
+declare void @might_crash()
+declare i32 @__C_specific_handler(...)
+declare i32 @llvm.eh.typeid.for(i8*)
+
+define i32 @simple_except_store() {
+entry:
+ %retval = alloca i32
+ store i32 0, i32* %retval
+ invoke void @might_crash()
+ to label %return unwind label %lpad
+
+lpad:
+ %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+ catch i32 ()* @filt
+ %sel = extractvalue { i8*, i32 } %ehvals, 1
+ %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+ %matches = icmp eq i32 %sel, %filt_sel
+ br i1 %matches, label %__except, label %eh.resume
+
+__except:
+ store i32 1, i32* %retval
+ br label %return
+
+return:
+ %r = load i32, i32* %retval
+ ret i32 %r
+
+eh.resume:
+ resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @simple_except_store()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@simple_except_store, %__except))
+; CHECK-NEXT: indirectbr {{.*}} [label %__except]
+
+define i32 @catch_all() {
+entry:
+ %retval = alloca i32
+ store i32 0, i32* %retval
+ invoke void @might_crash()
+ to label %return unwind label %lpad
+
+lpad:
+ %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+ catch i8* null
+ store i32 1, i32* %retval
+ br label %return
+
+return:
+ %r = load i32, i32* %retval
+ ret i32 %r
+}
+
+; CHECK-LABEL: define i32 @catch_all()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i8* null
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* null, i8* null, i8* blockaddress(@catch_all, %catch.all))
+; CHECK-NEXT: indirectbr {{.*}} [label %catch.all]
+;
+; CHECK: catch.all:
+; CHECK: store i32 1, i32* %retval
+
+
+define i32 @except_phi() {
+entry:
+ invoke void @might_crash()
+ to label %return unwind label %lpad
+
+lpad:
+ %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+ catch i32 ()* @filt
+ %sel = extractvalue { i8*, i32 } %ehvals, 1
+ %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+ %matches = icmp eq i32 %sel, %filt_sel
+ br i1 %matches, label %return, label %eh.resume
+
+return:
+ %r = phi i32 [0, %entry], [1, %lpad]
+ ret i32 %r
+
+eh.resume:
+ resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @except_phi()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions({{.*}}, i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@except_phi, %return))
+; CHECK-NEXT: indirectbr {{.*}} [label %return]
+;
+; CHECK: return:
+; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad1 ]
+; CHECK-NEXT: ret i32 %r
+
+define i32 @cleanup_and_except() {
+entry:
+ invoke void @might_crash()
+ to label %return unwind label %lpad
+
+lpad:
+ %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler
+ cleanup
+ catch i32 ()* @filt
+ call void @cleanup()
+ %sel = extractvalue { i8*, i32 } %ehvals, 1
+ %filt_sel = tail call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @filt to i8*))
+ %matches = icmp eq i32 %sel, %filt_sel
+ br i1 %matches, label %return, label %eh.resume
+
+return:
+ %r = phi i32 [0, %entry], [1, %lpad]
+ ret i32 %r
+
+eh.resume:
+ resume { i8*, i32 } %ehvals
+}
+
+; CHECK-LABEL: define i32 @cleanup_and_except()
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: catch i32 ()* @filt
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(
+; CHECK: i32 1, i8* bitcast (void (i8*, i8*)* @cleanup_and_except.cleanup to i8*),
+; CHECK: i32 0, i8* bitcast (i32 ()* @filt to i8*), i8* null, i8* blockaddress(@cleanup_and_except, %return))
+; CHECK-NEXT: indirectbr {{.*}} [label %return]
+;
+; CHECK: return:
+; CHECK-NEXT: %r = phi i32 [ 0, %entry ], [ 1, %lpad1 ]
+; CHECK-NEXT: ret i32 %r
More information about the llvm-commits
mailing list