diff options
author | Reid Kleckner <reid@kleckner.net> | 2015-04-09 20:37:24 +0000 |
---|---|---|
committer | Reid Kleckner <reid@kleckner.net> | 2015-04-09 20:37:24 +0000 |
commit | 0dbecf2b7816353329f58c91157670cd3e1e7b77 (patch) | |
tree | 6ddd5da585e66e28a8f6726c7c02d09d47307269 /clang/lib/CodeGen/CGException.cpp | |
parent | 589015b1e3d220a0a0060ec115541a66c0d25fc7 (diff) | |
download | bcm5719-llvm-0dbecf2b7816353329f58c91157670cd3e1e7b77.tar.gz bcm5719-llvm-0dbecf2b7816353329f58c91157670cd3e1e7b77.zip |
[SEH] Outline finally blocks using the new variable capture support
WinEHPrepare was going to have to pattern match the control flow merge
and split that the old lowering used, and that wasn't really feasible.
Now we can teach WinEHPrepare to pattern match this, which is much
simpler:
%fp = call i8* @llvm.frameaddress(i32 0)
call void @func(iN [01], i8* %fp)
This prototype happens to match the prototype used by the Win64 SEH
personality function, so this is really simple.
llvm-svn: 234532
Diffstat (limited to 'clang/lib/CodeGen/CGException.cpp')
-rw-r--r-- | clang/lib/CodeGen/CGException.cpp | 261 |
1 files changed, 146 insertions, 115 deletions
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index cc16053ac7f..3c624f96d2c 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/CallSite.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace CodeGen; @@ -408,13 +409,6 @@ llvm::Value *CodeGenFunction::getSelectorFromSlot() { return Builder.CreateLoad(getEHSelectorSlot(), "sel"); } -llvm::Value *CodeGenFunction::getAbnormalTerminationSlot() { - if (!AbnormalTerminationSlot) - AbnormalTerminationSlot = - CreateTempAlloca(Int8Ty, "abnormal.termination.slot"); - return AbnormalTerminationSlot; -} - void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint) { if (const Expr *SubExpr = E->getSubExpr()) { @@ -1287,8 +1281,7 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { return; } - SEHFinallyInfo FI; - EnterSEHTryStmt(S, FI); + EnterSEHTryStmt(S); { JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave"); @@ -1301,42 +1294,36 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) { else delete TryExit.getBlock(); } - ExitSEHTryStmt(S, FI); + ExitSEHTryStmt(S); } namespace { -struct PerformSEHFinally : EHScopeStack::Cleanup { - CodeGenFunction::SEHFinallyInfo *FI; - PerformSEHFinally(CodeGenFunction::SEHFinallyInfo *FI) : FI(FI) {} +struct PerformSEHFinally : EHScopeStack::Cleanup { + llvm::Function *OutlinedFinally; + PerformSEHFinally(llvm::Function *OutlinedFinally) + : OutlinedFinally(OutlinedFinally) {} void Emit(CodeGenFunction &CGF, Flags F) override { - // Cleanups are emitted at most twice: once for normal control flow and once - // for exception control flow. Branch into the finally block, and remember - // the continuation block so we can branch out later. - if (!FI->FinallyBB) { - FI->FinallyBB = CGF.createBasicBlock("__finally"); - FI->FinallyBB->insertInto(CGF.CurFn); - FI->FinallyBB->moveAfter(CGF.Builder.GetInsertBlock()); - } - - // Set the termination status and branch in. - CGF.Builder.CreateStore( - llvm::ConstantInt::get(CGF.Int8Ty, F.isForEHCleanup()), - CGF.getAbnormalTerminationSlot()); - CGF.Builder.CreateBr(FI->FinallyBB); - - // Create a continuation block for normal or exceptional control. - if (F.isForEHCleanup()) { - assert(!FI->ResumeBB && "double emission for EH"); - FI->ResumeBB = CGF.createBasicBlock("__finally.resume"); - CGF.EmitBlock(FI->ResumeBB); - } else { - assert(F.isForNormalCleanup() && !FI->ContBB && "double normal emission"); - FI->ContBB = CGF.createBasicBlock("__finally.cont"); - CGF.EmitBlock(FI->ContBB); - // Try to keep source order. - FI->ContBB->moveAfter(FI->FinallyBB); - } + ASTContext &Context = CGF.getContext(); + QualType ArgTys[2] = {Context.BoolTy, Context.VoidPtrTy}; + FunctionProtoType::ExtProtoInfo EPI; + const auto *FTP = cast<FunctionType>( + Context.getFunctionType(Context.VoidTy, ArgTys, EPI)); + + CallArgList Args; + llvm::Value *IsForEH = + llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup()); + Args.add(RValue::get(IsForEH), ArgTys[0]); + + CodeGenModule &CGM = CGF.CGM; + llvm::Value *Zero = llvm::ConstantInt::get(CGM.Int32Ty, 0); + llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress); + llvm::Value *FP = CGF.Builder.CreateCall(FrameAddr, Zero); + Args.add(RValue::get(FP), ArgTys[1]); + + const CGFunctionInfo &FnInfo = + CGM.getTypes().arrangeFreeFunctionCall(Args, FTP, /*chainCall=*/false); + CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args); } }; } @@ -1354,7 +1341,8 @@ struct CaptureFinder : ConstStmtVisitor<CaptureFinder> { // See if this is a capture, then recurse. ConstStmtVisitor<CaptureFinder>::Visit(S); for (const Stmt *Child : S->children()) - Visit(Child); + if (Child) + Visit(Child); } void VisitDeclRefExpr(const DeclRefExpr *E) { @@ -1403,12 +1391,16 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF, CGM.ErrorUnsupported(VD, "VLA captured by SEH"); continue; } - assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) && "captured non-local variable"); - llvm::Value *ParentVar = ParentCGF.LocalDeclMap[VD]; - assert(ParentVar && "capture was not a local decl"); + // If this decl hasn't been declared yet, it will be declared in the + // OutlinedStmt. + auto I = ParentCGF.LocalDeclMap.find(VD); + if (I == ParentCGF.LocalDeclMap.end()) + continue; + llvm::Value *ParentVar = I->second; + llvm::CallInst *RecoverCall = nullptr; CGBuilderTy Builder(AllocaInsertPt); if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) { @@ -1445,39 +1437,17 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF, } } -/// Create a stub filter function that will ultimately hold the code of the -/// filter expression. The EH preparation passes in LLVM will outline the code -/// from the main function body into this stub. -llvm::Function * -CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, - const SEHExceptStmt &Except) { - const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; +/// Arrange a function prototype that can be called by Windows exception +/// handling personalities. On Win64, the prototype looks like: +/// RetTy func(void *EHPtrs, void *ParentFP); +void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF, + StringRef Name, QualType RetTy, + FunctionArgList &Args, + const Stmt *OutlinedStmt) { llvm::Function *ParentFn = ParentCGF.CurFn; - - Expr *FilterExpr = Except.getFilterExpr(); - - // Get the mangled function name. - SmallString<128> Name; - { - llvm::raw_svector_ostream OS(Name); - const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl); - assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); - CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS); - } - - // Arrange a function with the declaration: - // int filt(EXCEPTION_POINTERS *exception_pointers, void *frame_pointer) - QualType RetTy = getContext().IntTy; - FunctionArgList Args; - SEHPointersDecl = ImplicitParamDecl::Create( - getContext(), nullptr, FilterExpr->getLocStart(), - &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy); - Args.push_back(SEHPointersDecl); - Args.push_back(ImplicitParamDecl::Create( - getContext(), nullptr, FilterExpr->getLocStart(), - &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false); + llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo); llvm::Function *Fn = llvm::Function::Create(FnTy, ParentFn->getLinkage(), Name.str(), &CGM.getModule()); @@ -1485,7 +1455,6 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, if (llvm::Comdat *C = ParentFn->getComdat()) { Fn->setComdat(C); } else if (ParentFn->hasWeakLinkage() || ParentFn->hasLinkOnceLinkage()) { - // FIXME: Unreachable with Rafael's changes? llvm::Comdat *C = CGM.getModule().getOrInsertComdat(ParentFn->getName()); ParentFn->setComdat(C); Fn->setComdat(C); @@ -1493,14 +1462,55 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, Fn->setLinkage(llvm::GlobalValue::InternalLinkage); } + IsOutlinedSEHHelper = true; + StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args, - FilterExpr->getLocStart(), FilterExpr->getLocStart()); + OutlinedStmt->getLocStart(), OutlinedStmt->getLocStart()); - EmitSEHExceptionCodeSave(); + CGM.SetLLVMFunctionAttributes(nullptr, FnInfo, CurFn); auto AI = Fn->arg_begin(); ++AI; - EmitCapturedLocals(ParentCGF, FilterExpr, &*AI); + EmitCapturedLocals(ParentCGF, OutlinedStmt, &*AI); +} + +/// Create a stub filter function that will ultimately hold the code of the +/// filter expression. The EH preparation passes in LLVM will outline the code +/// from the main function body into this stub. +llvm::Function * +CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, + const SEHExceptStmt &Except) { + const Expr *FilterExpr = Except.getFilterExpr(); + SourceLocation StartLoc = FilterExpr->getLocStart(); + + SEHPointersDecl = ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy); + FunctionArgList Args; + Args.push_back(SEHPointersDecl); + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); + + // Get the mangled function name. + SmallString<128> Name; + { + llvm::raw_svector_ostream OS(Name); + const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; + const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl); + assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); + CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS); + } + + startOutlinedSEHHelper(ParentCGF, Name, getContext().IntTy, Args, FilterExpr); + + // Mark finally block calls as nounwind and noinline to make LLVM's job a + // little easier. + // FIXME: Remove these restrictions in the future. + CurFn->addFnAttr(llvm::Attribute::NoUnwind); + CurFn->addFnAttr(llvm::Attribute::NoInline); + + EmitSEHExceptionCodeSave(); // Emit the original filter expression, convert to i32, and return. llvm::Value *R = EmitScalarExpr(FilterExpr); @@ -1510,7 +1520,42 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, FinishFunction(FilterExpr->getLocEnd()); - return Fn; + return CurFn; +} + +llvm::Function * +CodeGenFunction::GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF, + const SEHFinallyStmt &Finally) { + const Stmt *FinallyBlock = Finally.getBlock(); + SourceLocation StartLoc = FinallyBlock->getLocStart(); + + FunctionArgList Args; + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("abnormal_termination"), getContext().BoolTy)); + Args.push_back(ImplicitParamDecl::Create( + getContext(), nullptr, StartLoc, + &getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy)); + + // Get the mangled function name. + SmallString<128> Name; + { + llvm::raw_svector_ostream OS(Name); + const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; + const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl); + assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH"); + CGM.getCXXABI().getMangleContext().mangleSEHFinallyBlock(Parent, OS); + } + + startOutlinedSEHHelper(ParentCGF, Name, getContext().VoidTy, Args, + FinallyBlock); + + // Emit the original filter expression, convert to i32, and return. + EmitStmt(FinallyBlock); + + FinishFunction(FinallyBlock->getLocEnd()); + + return CurFn; } void CodeGenFunction::EmitSEHExceptionCodeSave() { @@ -1554,21 +1599,24 @@ llvm::Value *CodeGenFunction::EmitSEHExceptionCode() { } llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() { - // Load from the abnormal termination slot. It will be uninitialized outside - // of __finally blocks, which we should warn or error on. - llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot()); - return Builder.CreateZExt(IsEH, Int32Ty); + // Abnormal termination is just the first parameter to the outlined finally + // helper. + auto AI = CurFn->arg_begin(); + return Builder.CreateZExt(&*AI, Int32Ty); } -void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { - if (S.getFinallyHandler()) { +void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) { + CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); + if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) { // Push a cleanup for __finally blocks. - EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, &FI); + llvm::Function *FinallyFunc = + HelperCGF.GenerateSEHFinallyFunction(*this, *Finally); + EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc); return; } // Otherwise, we must have an __except block. - SEHExceptStmt *Except = S.getExceptHandler(); + const SEHExceptStmt *Except = S.getExceptHandler(); assert(Except); EHCatchScope *CatchScope = EHStack.pushCatch(1); @@ -1583,40 +1631,17 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { // In general, we have to emit an outlined filter function. Use the function // in place of the RTTI typeinfo global that C++ EH uses. - CodeGenFunction FilterCGF(CGM, /*suppressNewContext=*/true); llvm::Function *FilterFunc = - FilterCGF.GenerateSEHFilterFunction(*this, *Except); + HelperCGF.GenerateSEHFilterFunction(*this, *Except); llvm::Constant *OpaqueFunc = llvm::ConstantExpr::getBitCast(FilterFunc, Int8PtrTy); CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except")); } -void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { +void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) { // Just pop the cleanup if it's a __finally block. - if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) { + if (S.getFinallyHandler()) { PopCleanupBlock(); - assert(FI.ContBB && "did not emit normal cleanup"); - - // Emit the code into FinallyBB. - CGBuilderTy::InsertPoint SavedIP = Builder.saveIP(); - Builder.SetInsertPoint(FI.FinallyBB); - EmitStmt(Finally->getBlock()); - - if (HaveInsertPoint()) { - if (FI.ResumeBB) { - llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot(), - "abnormal.termination"); - IsEH = Builder.CreateICmpEQ(IsEH, llvm::ConstantInt::get(Int8Ty, 0)); - Builder.CreateCondBr(IsEH, FI.ContBB, FI.ResumeBB); - } else { - // There was nothing exceptional in the try body, so we only have normal - // control flow. - Builder.CreateBr(FI.ContBB); - } - } - - Builder.restoreIP(SavedIP); - return; } @@ -1666,7 +1691,13 @@ void CodeGenFunction::EmitSEHLeaveStmt(const SEHLeaveStmt &S) { if (HaveInsertPoint()) EmitStopPoint(&S); - assert(!SEHTryEpilogueStack.empty() && - "sema should have rejected this __leave"); + // This must be a __leave from a __finally block, which we warn on and is UB. + // Just emit unreachable. + if (!isSEHTryScope()) { + Builder.CreateUnreachable(); + Builder.ClearInsertionPoint(); + return; + } + EmitBranchThroughCleanup(*SEHTryEpilogueStack.back()); } |