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

llvm-svn: 232664
This commit is contained in:
Reid Kleckner 2015-03-18 20:26:53 +00:00
parent 5a28f4361d
commit 0f9e27a371
2 changed files with 247 additions and 41 deletions

View File

@ -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(const TargetMachine *TM) {
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(Function &Fn) {
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 &M) {
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(ActionHandler *Action, Function *SrcFn,
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(ActionHandler *Action, Function *SrcFn,
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(LandingPadInst *LPad,
// 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(LandingPadInst *LPad,
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(LandingPadInst *LPad,
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::findCleanupHandler(BasicBlock *StartBB,
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());

View File

@ -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