diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index b2945a1074c..6f8c905201d 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -98,7 +98,8 @@ private: void mapLandingPadBlocks(LandingPadInst *LPad, LandingPadActions &Actions); CatchHandler *findCatchHandler(BasicBlock *BB, BasicBlock *&NextBB, VisitedBlockSet &VisitedBlocks); - CleanupHandler *findCleanupHandler(BasicBlock *StartBB, BasicBlock *EndBB); + void findCleanupHandlers(LandingPadActions &Actions, BasicBlock *StartBB, + BasicBlock *EndBB); void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB); @@ -402,6 +403,7 @@ bool WinEHPrepare::prepareExceptionHandlers( LandingPadActions Actions; mapLandingPadBlocks(LPad, Actions); + HandlersOutlined |= !Actions.actions().empty(); for (ActionHandler *Action : Actions) { if (Action->hasBeenProcessed()) continue; @@ -413,19 +415,12 @@ bool WinEHPrepare::prepareExceptionHandlers( if (isAsynchronousEHPersonality(Personality)) { if (auto *CatchAction = dyn_cast(Action)) { processSEHCatchHandler(CatchAction, StartBB); - HandlersOutlined = true; continue; } } - if (outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo)) { - HandlersOutlined = true; - } - } // End for each Action - - // FIXME: We need a guard against partially outlined functions. - if (!HandlersOutlined) - continue; + outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo); + } // Replace the landing pad with a new llvm.eh.action based landing pad. BasicBlock *NewLPadBB = BasicBlock::Create(Context, "lpad", &F, LPadBB); @@ -1340,13 +1335,7 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad, DEBUG(dbgs() << "Mapping landing pad: " << BB->getName() << "\n"); if (NumClauses == 0) { - // This landing pad contains only cleanup code. - CleanupHandler *Action = new CleanupHandler(BB); - CleanupHandlerMap[BB] = Action; - Actions.insertCleanupHandler(Action); - DEBUG(dbgs() << " Assuming cleanup code in block " << BB->getName() - << "\n"); - assert(LPad->isCleanup()); + findCleanupHandlers(Actions, BB, nullptr); return; } @@ -1366,14 +1355,8 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad, // 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"); - } - } + if (Personality == EHPersonality::MSVC_CXX) + findCleanupHandlers(Actions, BB, BB); // Add the catch handler to the action list. CatchHandler *Action = @@ -1390,13 +1373,7 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad, CatchHandler *CatchAction = findCatchHandler(BB, NextBB, VisitedBlocks); // See if there is any interesting code executed before the dispatch. - if (auto *CleanupAction = - findCleanupHandler(BB, CatchAction->getStartBlock())) { - // Add a cleanup entry to the list - Actions.insertCleanupHandler(CleanupAction); - DEBUG(dbgs() << " Found cleanup code in block " - << CleanupAction->getStartBlock()->getName() << "\n"); - } + findCleanupHandlers(Actions, BB, CatchAction->getStartBlock()); assert(CatchAction); ++HandlersFound; @@ -1412,12 +1389,7 @@ void WinEHPrepare::mapLandingPadBlocks(LandingPadInst *LPad, // 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); - DEBUG(dbgs() << " Found cleanup code in block " - << CleanupAction->getStartBlock()->getName() << "\n"); - } + findCleanupHandlers(Actions, BB, BB); // It's possible that some optimization moved code into a landingpad that // wasn't @@ -1477,20 +1449,56 @@ CatchHandler *WinEHPrepare::findCatchHandler(BasicBlock *BB, return nullptr; } -// These are helper functions to combine repeated code from findCleanupHandler. -static CleanupHandler * -createCleanupHandler(CleanupHandlerMapTy &CleanupHandlerMap, BasicBlock *BB) { +// These are helper functions to combine repeated code from findCleanupHandlers. +static void createCleanupHandler(LandingPadActions &Actions, + CleanupHandlerMapTy &CleanupHandlerMap, + BasicBlock *BB) { CleanupHandler *Action = new CleanupHandler(BB); CleanupHandlerMap[BB] = Action; - return Action; + Actions.insertCleanupHandler(Action); + DEBUG(dbgs() << " Found cleanup code in block " + << Action->getStartBlock()->getName() << "\n"); +} + +static bool isFrameAddressCall(Value *V) { + return match(V, m_Intrinsic(m_SpecificInt(0))); +} + +static CallSite matchOutlinedFinallyCall(BasicBlock *BB, + Instruction *MaybeCall) { + // Look for finally blocks that Clang has already outlined for us. + // %fp = call i8* @llvm.frameaddress(i32 0) + // call void @"fin$parent"(iN 1, i8* %fp) + if (isFrameAddressCall(MaybeCall) && MaybeCall != BB->getTerminator()) + MaybeCall = MaybeCall->getNextNode(); + CallSite FinallyCall(MaybeCall); + if (!FinallyCall || FinallyCall.arg_size() != 2) + return CallSite(); + if (!match(FinallyCall.getArgument(0), m_SpecificInt(1))) + return CallSite(); + if (!isFrameAddressCall(FinallyCall.getArgument(1))) + return CallSite(); + return FinallyCall; +} + +static BasicBlock *followSingleUnconditionalBranches(BasicBlock *BB) { + // Skip single ubr blocks. + while (BB->getFirstNonPHIOrDbg() == BB->getTerminator()) { + auto *Br = dyn_cast(BB->getTerminator()); + if (Br && Br->isUnconditional()) + BB = Br->getSuccessor(0); + else + return BB; + } + return BB; } // This function searches starting with the input block for the next block that // contains code that is not part of a catch handler and would not be eliminated // during handler outlining. // -CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, - BasicBlock *EndBB) { +void WinEHPrepare::findCleanupHandlers(LandingPadActions &Actions, + BasicBlock *StartBB, BasicBlock *EndBB) { // Here we will skip over the following: // // landing pad prolog: @@ -1507,6 +1515,7 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, // Anything other than an unconditional branch will kick us out of this loop // one way or another. while (BB) { + BB = followSingleUnconditionalBranches(BB); // If we've already scanned this block, don't scan it again. If it is // a cleanup block, there will be an action in the CleanupHandlerMap. // If we've scanned it and it is not a cleanup block, there will be a @@ -1515,7 +1524,12 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, // avoid creating a null entry for blocks we haven't scanned. if (CleanupHandlerMap.count(BB)) { if (auto *Action = CleanupHandlerMap[BB]) { - return cast(Action); + Actions.insertCleanupHandler(Action); + DEBUG(dbgs() << " Found cleanup code in block " + << Action->getStartBlock()->getName() << "\n"); + // FIXME: This cleanup might chain into another, and we need to discover + // that. + return; } else { // Here we handle the case where the cleanup handler map contains a // value for this block but the value is a nullptr. This means that @@ -1527,11 +1541,9 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, // would terminate the search for cleanup code, so the unconditional // branch is the only case for which we might need to continue // searching. - if (BB == EndBB) - return nullptr; - BasicBlock *SuccBB; - if (!match(BB->getTerminator(), m_UnconditionalBr(SuccBB))) - return nullptr; + BasicBlock *SuccBB = followSingleUnconditionalBranches(BB); + if (SuccBB == BB || SuccBB == EndBB) + return; BB = SuccBB; continue; } @@ -1564,14 +1576,14 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, // If there is only one landingpad, we may use the lpad directly with no // insertions. if (isa(ResumeVal)) - return nullptr; + return; if (!isa(ResumeVal)) { Insert2 = dyn_cast(ResumeVal); if (!Insert2) - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); Insert1 = dyn_cast(Insert2->getAggregateOperand()); if (!Insert1) - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); } for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end(); II != IE; ++II) { @@ -1582,10 +1594,10 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, continue; if (!Inst->hasOneUse() || (Inst->user_back() != Insert1 && Inst->user_back() != Insert2)) { - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); } } - return nullptr; + return; } BranchInst *Branch = dyn_cast(Terminator); @@ -1596,7 +1608,7 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, // br i1 %matches, label %catch14, label %eh.resume CmpInst *Compare = dyn_cast(Branch->getCondition()); if (!Compare || !Compare->isEquality()) - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end(); II != IE; ++II) { Instruction *Inst = II; @@ -1606,11 +1618,54 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, continue; if (match(Inst, m_Intrinsic())) continue; - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); } // The selector dispatch block should always terminate our search. assert(BB == EndBB); - return nullptr; + return; + } + + if (isAsynchronousEHPersonality(Personality)) { + // If this is a landingpad block, split the block at the first non-landing + // pad instruction. + Instruction *MaybeCall = BB->getFirstNonPHIOrDbg(); + if (LPadMap) { + while (MaybeCall != BB->getTerminator() && + LPadMap->isLandingPadSpecificInst(MaybeCall)) + MaybeCall = MaybeCall->getNextNode(); + } + + // Look for outlined finally calls. + if (CallSite FinallyCall = matchOutlinedFinallyCall(BB, MaybeCall)) { + Function *Fin = FinallyCall.getCalledFunction(); + assert(Fin && "outlined finally call should be direct"); + auto *Action = new CleanupHandler(BB); + Action->setHandlerBlockOrFunc(Fin); + Actions.insertCleanupHandler(Action); + CleanupHandlerMap[BB] = Action; + DEBUG(dbgs() << " Found frontend-outlined finally call to " + << Fin->getName() << " in block " + << Action->getStartBlock()->getName() << "\n"); + + // Split the block if there were more interesting instructions and look + // for finally calls in the normal successor block. + BasicBlock *SuccBB = BB; + if (FinallyCall.getInstruction() != BB->getTerminator() && + FinallyCall.getInstruction()->getNextNode() != BB->getTerminator()) { + SuccBB = BB->splitBasicBlock(FinallyCall.getInstruction()->getNextNode()); + } else { + if (FinallyCall.isInvoke()) { + SuccBB = cast(FinallyCall.getInstruction())->getNormalDest(); + } else { + SuccBB = BB->getUniqueSuccessor(); + assert(SuccBB && "splitOutlinedFinallyCalls didn't insert a branch"); + } + } + BB = SuccBB; + if (BB == EndBB) + return; + continue; + } } // Anything else is either a catch block or interesting cleanup code. @@ -1624,21 +1679,21 @@ CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB, continue; // If this is a catch block, there is no cleanup code to be found. if (match(Inst, m_Intrinsic())) - return nullptr; + return; // If this a nested landing pad, it may contain an endcatch call. if (match(Inst, m_Intrinsic())) - return nullptr; + return; // Anything else makes this interesting cleanup code. - return createCleanupHandler(CleanupHandlerMap, BB); + return createCleanupHandler(Actions, CleanupHandlerMap, BB); } // Only unconditional branches in empty blocks should get this far. assert(Branch && Branch->isUnconditional()); if (BB == EndBB) - return nullptr; + return; BB = Branch->getSuccessor(0); } - return nullptr; + return; } // This is a public function, declared in WinEHFuncInfo.h and is also diff --git a/test/CodeGen/WinEH/seh-outlined-finally.ll b/test/CodeGen/WinEH/seh-outlined-finally.ll new file mode 100644 index 00000000000..a2843bfdb93 --- /dev/null +++ b/test/CodeGen/WinEH/seh-outlined-finally.ll @@ -0,0 +1,155 @@ +; RUN: opt -S -winehprepare -sehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s + +; Test case based on this code: +; +; extern "C" int _abnormal_termination(); +; #pragma intrinsic(_abnormal_termination) +; extern "C" int printf(const char *, ...); +; extern "C" void may_crash() { +; *(volatile int *)0 = 42; +; } +; int main() { +; int myres = 0; +; __try { +; __try { +; may_crash(); +; } __finally { +; printf("inner finally %d\n", _abnormal_termination()); +; may_crash(); +; } +; } __finally { +; printf("outer finally %d\n", _abnormal_termination()); +; } +; } +; +; Note that if the inner finally crashes, the outer finally still runs. There +; is nothing like a std::terminate call in this situation. + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +@str_outer_finally = linkonce_odr unnamed_addr constant [18 x i8] c"outer finally %d\0A\00", align 1 +@str_inner_finally = linkonce_odr unnamed_addr constant [18 x i8] c"inner finally %d\0A\00", align 1 + +; Function Attrs: nounwind uwtable +define void @may_crash() #0 { +entry: + store volatile i32 42, i32* null, align 4 + ret void +} + +; Function Attrs: uwtable +define i32 @main() #1 { +entry: + %myres = alloca i32, align 4 + %exn.slot = alloca i8* + %ehselector.slot = alloca i32 + store i32 0, i32* %myres, align 4 + invoke void @may_crash() #4 + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + %0 = call i8* @llvm.frameaddress(i32 0) + invoke void @"\01?fin$1@0@main@@"(i1 zeroext false, i8* %0) #4 + to label %invoke.cont2 unwind label %lpad1 + +invoke.cont2: ; preds = %invoke.cont + %1 = call i8* @llvm.frameaddress(i32 0) + call void @"\01?fin$0@0@main@@"(i1 zeroext false, i8* %1) + ret i32 0 + +lpad: ; preds = %entry + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %3 = extractvalue { i8*, i32 } %2, 0 + store i8* %3, i8** %exn.slot + %4 = extractvalue { i8*, i32 } %2, 1 + store i32 %4, i32* %ehselector.slot + %5 = call i8* @llvm.frameaddress(i32 0) + invoke void @"\01?fin$1@0@main@@"(i1 zeroext true, i8* %5) #4 + to label %invoke.cont3 unwind label %lpad1 + +lpad1: ; preds = %lpad, %invoke.cont + %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + %7 = extractvalue { i8*, i32 } %6, 0 + store i8* %7, i8** %exn.slot + %8 = extractvalue { i8*, i32 } %6, 1 + store i32 %8, i32* %ehselector.slot + br label %ehcleanup + +invoke.cont3: ; preds = %lpad + br label %ehcleanup + +ehcleanup: ; preds = %invoke.cont3, %lpad1 + %9 = call i8* @llvm.frameaddress(i32 0) + call void @"\01?fin$0@0@main@@"(i1 zeroext true, i8* %9) + br label %eh.resume + +eh.resume: ; preds = %ehcleanup + %exn = load i8*, i8** %exn.slot + %sel = load i32, i32* %ehselector.slot + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0 + %lpad.val4 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val4 +} + +; CHECK-NOT: define internal void @ + +; CHECK-LABEL: define i32 @main() +; CHECK: invoke void @may_crash() +; +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$1@0@main@@", i32 0, void (i1, i8*)* @"\01?fin$0@0@main@@") +; CHECK-NEXT: indirectbr +; +; CHECK: landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$0@0@main@@") +; CHECK-NEXT: indirectbr + +; There should not be any *new* cleanup helpers, just the existing ones. +; CHECK-NOT: define internal void @ +; CHECK: define internal void @"\01?fin$0@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) +; CHECK-NOT: define internal void @ +; CHECK: define internal void @"\01?fin$1@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) +; CHECK-NOT: define internal void @ + +define internal void @"\01?fin$0@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 { +entry: + %frame_pointer.addr = alloca i8*, align 8 + %abnormal_termination.addr = alloca i8, align 1 + store i8* %frame_pointer, i8** %frame_pointer.addr, align 8 + %frombool = zext i1 %abnormal_termination to i8 + store i8 %frombool, i8* %abnormal_termination.addr, align 1 + %0 = zext i1 %abnormal_termination to i32 + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_outer_finally, i32 0, i32 0), i32 %0) + ret void +} + +declare i32 @printf(i8*, ...) #2 + +define internal void @"\01?fin$1@0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 { +entry: + %frame_pointer.addr = alloca i8*, align 8 + %abnormal_termination.addr = alloca i8, align 1 + store i8* %frame_pointer, i8** %frame_pointer.addr, align 8 + %frombool = zext i1 %abnormal_termination to i8 + store i8 %frombool, i8* %abnormal_termination.addr, align 1 + %0 = zext i1 %abnormal_termination to i32 + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_inner_finally, i32 0, i32 0), i32 %0) + call void @may_crash() + ret void +} + +declare i32 @__C_specific_handler(...) + +; Function Attrs: nounwind readnone +declare i8* @llvm.frameaddress(i32) #3 + +attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } +attributes #4 = { noinline }