From 01a1af4fe44c6bfcf1120091edb5dc541c3a8953 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 18 Mar 2015 20:26:53 +0000 Subject: [PATCH] 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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@232664 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/WinEHPrepare.cpp | 150 ++++++++++++++++++++++--------- test/CodeGen/WinEH/seh-simple.ll | 138 ++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 41 deletions(-) create mode 100644 test/CodeGen/WinEH/seh-simple.ll diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index f68189af399..383aaf6613f 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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 &LPads); + bool prepareExceptionHandlers(Function &F, + SmallVectorImpl &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 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 +SEHPrepare("sehprepare", cl::Hidden, + cl::desc("Prepare functions with SEH personalities")); + bool WinEHPrepare::runOnFunction(Function &Fn) { SmallVector LPads; SmallVector 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 &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(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 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(Action)->getSelector(); - Director.reset( - new WinEHCatchDirector(Handler, SelectorType, VarInfo, LPadMap)); - LPadMap.remapSelector(VMap, ConstantInt::get( Type::getInt32Ty(Context), 1)); + if (auto *CatchAction = dyn_cast(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 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(Terminator)) { InsertValueInst *Insert1 = nullptr; InsertValueInst *Insert2 = nullptr; - if (!isa(Resume->getOperand(0))) { - Insert2 = dyn_cast(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(ResumeVal)) + return nullptr; + if (!isa(ResumeVal)) { + Insert2 = dyn_cast(ResumeVal); if (!Insert2) return createCleanupHandler(CleanupHandlerMap, BB); Insert1 = dyn_cast(Insert2->getAggregateOperand()); diff --git a/test/CodeGen/WinEH/seh-simple.ll b/test/CodeGen/WinEH/seh-simple.ll new file mode 100644 index 00000000000..152dfa36ae3 --- /dev/null +++ b/test/CodeGen/WinEH/seh-simple.ll @@ -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 -- 2.34.1