Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ 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 @@ } 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,9 @@ VisitedBlockSet &VisitedBlocks); CleanupHandler *findCleanupHandler(BasicBlock *StartBB, BasicBlock *EndBB); + void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB); + + EHPersonality Personality; CatchHandlerMapTy CatchHandlerMap; CleanupHandlerMapTy CleanupHandlerMap; DenseMap LPadMaps; @@ -238,20 +242,22 @@ 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 +332,11 @@ 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 +352,24 @@ 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 +379,7 @@ 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 +425,21 @@ 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 +460,12 @@ 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 +481,8 @@ } 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 +682,10 @@ 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 +712,27 @@ 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); + (void)Res; + assert(Res && HandlerBB && "mis-identified catch block?"); + 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. @@ -1306,8 +1347,13 @@ 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()); Index: test/CodeGen/WinEH/seh-simple.ll =================================================================== --- /dev/null +++ test/CodeGen/WinEH/seh-simple.ll @@ -0,0 +1,110 @@ +; RUN: opt -S -winehprepare -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 @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