diff options
Diffstat (limited to 'lib/CodeGen/CGException.cpp')
-rw-r--r-- | lib/CodeGen/CGException.cpp | 131 |
1 files changed, 80 insertions, 51 deletions
diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 6cb9599e25..91be321d31 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -112,11 +112,18 @@ static llvm::Constant *getUnexpectedFn(CodeGenFunction &CGF) { return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_call_unexpected"); } +llvm::Constant *CodeGenFunction::getUnwindResumeFn() { + const llvm::FunctionType *FTy = + llvm::FunctionType::get(VoidTy, Int8PtrTy, /*IsVarArgs=*/false); + + if (CGM.getLangOptions().SjLjExceptions) + return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume"); + return CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume"); +} + llvm::Constant *CodeGenFunction::getUnwindResumeOrRethrowFn() { - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), Int8PtrTy, - /*IsVarArgs=*/false); + llvm::FunctionType::get(VoidTy, Int8PtrTy, /*IsVarArgs=*/false); if (CGM.getLangOptions().SjLjExceptions) return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume_or_Rethrow"); @@ -563,47 +570,59 @@ llvm::BasicBlock *CodeGenFunction::getInvokeDestImpl() { return LP; } +// This code contains a hack to work around a design flaw in +// LLVM's EH IR which breaks semantics after inlining. This same +// hack is implemented in llvm-gcc. +// +// The LLVM EH abstraction is basically a thin veneer over the +// traditional GCC zero-cost design: for each range of instructions +// in the function, there is (at most) one "landing pad" with an +// associated chain of EH actions. A language-specific personality +// function interprets this chain of actions and (1) decides whether +// or not to resume execution at the landing pad and (2) if so, +// provides an integer indicating why it's stopping. In LLVM IR, +// the association of a landing pad with a range of instructions is +// achieved via an invoke instruction, the chain of actions becomes +// the arguments to the @llvm.eh.selector call, and the selector +// call returns the integer indicator. Other than the required +// presence of two intrinsic function calls in the landing pad, +// the IR exactly describes the layout of the output code. +// +// A principal advantage of this design is that it is completely +// language-agnostic; in theory, the LLVM optimizers can treat +// landing pads neutrally, and targets need only know how to lower +// the intrinsics to have a functioning exceptions system (assuming +// that platform exceptions follow something approximately like the +// GCC design). Unfortunately, landing pads cannot be combined in a +// language-agnostic way: given selectors A and B, there is no way +// to make a single landing pad which faithfully represents the +// semantics of propagating an exception first through A, then +// through B, without knowing how the personality will interpret the +// (lowered form of the) selectors. This means that inlining has no +// choice but to crudely chain invokes (i.e., to ignore invokes in +// the inlined function, but to turn all unwindable calls into +// invokes), which is only semantically valid if every unwind stops +// at every landing pad. +// +// Therefore, the invoke-inline hack is to guarantee that every +// landing pad has a catch-all. +enum CleanupHackLevel_t { + /// A level of hack that requires that all landing pads have + /// catch-alls. + CHL_MandatoryCatchall, + + /// A level of hack that requires that all landing pads handle + /// cleanups. + CHL_MandatoryCleanup, + + /// No hacks at all; ideal IR generation. + CHL_Ideal +}; +const CleanupHackLevel_t CleanupHackLevel = CHL_MandatoryCleanup; + llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { assert(EHStack.requiresLandingPad()); - // This function contains a hack to work around a design flaw in - // LLVM's EH IR which breaks semantics after inlining. This same - // hack is implemented in llvm-gcc. - // - // The LLVM EH abstraction is basically a thin veneer over the - // traditional GCC zero-cost design: for each range of instructions - // in the function, there is (at most) one "landing pad" with an - // associated chain of EH actions. A language-specific personality - // function interprets this chain of actions and (1) decides whether - // or not to resume execution at the landing pad and (2) if so, - // provides an integer indicating why it's stopping. In LLVM IR, - // the association of a landing pad with a range of instructions is - // achieved via an invoke instruction, the chain of actions becomes - // the arguments to the @llvm.eh.selector call, and the selector - // call returns the integer indicator. Other than the required - // presence of two intrinsic function calls in the landing pad, - // the IR exactly describes the layout of the output code. - // - // A principal advantage of this design is that it is completely - // language-agnostic; in theory, the LLVM optimizers can treat - // landing pads neutrally, and targets need only know how to lower - // the intrinsics to have a functioning exceptions system (assuming - // that platform exceptions follow something approximately like the - // GCC design). Unfortunately, landing pads cannot be combined in a - // language-agnostic way: given selectors A and B, there is no way - // to make a single landing pad which faithfully represents the - // semantics of propagating an exception first through A, then - // through B, without knowing how the personality will interpret the - // (lowered form of the) selectors. This means that inlining has no - // choice but to crudely chain invokes (i.e., to ignore invokes in - // the inlined function, but to turn all unwindable calls into - // invokes), which is only semantically valid if every unwind stops - // at every landing pad. - // - // Therefore, the invoke-inline hack is to guarantee that every - // landing pad has a catch-all. - const bool UseInvokeInlineHack = true; - for (EHScopeStack::iterator ir = EHStack.begin(); ; ) { assert(ir != EHStack.end() && "stack requiring landing pad is nothing but non-EH scopes?"); @@ -736,16 +755,23 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { EHSelector.append(EHFilters.begin(), EHFilters.end()); // Also check whether we need a cleanup. - if (UseInvokeInlineHack || HasEHCleanup) - EHSelector.push_back(UseInvokeInlineHack + if (CleanupHackLevel == CHL_MandatoryCatchall || HasEHCleanup) + EHSelector.push_back(CleanupHackLevel == CHL_MandatoryCatchall ? getCatchAllValue(*this) : getCleanupValue(*this)); // Otherwise, signal that we at least have cleanups. - } else if (UseInvokeInlineHack || HasEHCleanup) { - EHSelector.push_back(UseInvokeInlineHack + } else if (CleanupHackLevel == CHL_MandatoryCatchall || HasEHCleanup) { + EHSelector.push_back(CleanupHackLevel == CHL_MandatoryCatchall ? getCatchAllValue(*this) : getCleanupValue(*this)); + + // At the MandatoryCleanup hack level, we don't need to actually + // spuriously tell the unwinder that we have cleanups, but we do + // need to always be prepared to handle cleanups. + } else if (CleanupHackLevel == CHL_MandatoryCleanup) { + // Just don't decrement LastToEmitInLoop. + } else { assert(LastToEmitInLoop > 2); LastToEmitInLoop--; @@ -833,7 +859,7 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { // If there was a cleanup, we'll need to actually check whether we // landed here because the filter triggered. - if (UseInvokeInlineHack || HasEHCleanup) { + if (CleanupHackLevel != CHL_Ideal || HasEHCleanup) { llvm::BasicBlock *RethrowBB = createBasicBlock("cleanup"); llvm::BasicBlock *UnexpectedBB = createBasicBlock("ehspec.unexpected"); @@ -843,10 +869,11 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { Builder.CreateCondBr(FailsFilter, UnexpectedBB, RethrowBB); // The rethrow block is where we land if this was a cleanup. - // TODO: can this be _Unwind_Resume if the InvokeInlineHack is off? EmitBlock(RethrowBB); - Builder.CreateCall(getUnwindResumeOrRethrowFn(), - Builder.CreateLoad(getExceptionSlot())) + llvm::Constant *RethrowFn = + CleanupHackLevel == CHL_MandatoryCatchall ? getUnwindResumeOrRethrowFn() + : getUnwindResumeFn(); + Builder.CreateCall(RethrowFn, Builder.CreateLoad(getExceptionSlot())) ->setDoesNotReturn(); Builder.CreateUnreachable(); @@ -863,7 +890,7 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { Builder.CreateUnreachable(); // ...or a normal catch handler... - } else if (!UseInvokeInlineHack && !HasEHCleanup) { + } else if (CleanupHackLevel == CHL_Ideal && !HasEHCleanup) { llvm::Value *Type = EHSelector.back(); EmitBranchThroughEHCleanup(EHHandlers[Type]); @@ -1444,7 +1471,9 @@ CodeGenFunction::UnwindDest CodeGenFunction::getRethrowDest() { if (!RethrowName.empty()) RethrowFn = getCatchallRethrowFn(*this, RethrowName); else - RethrowFn = getUnwindResumeOrRethrowFn(); + RethrowFn = (CleanupHackLevel == CHL_MandatoryCatchall + ? getUnwindResumeOrRethrowFn() + : getUnwindResumeFn()); Builder.CreateCall(RethrowFn, Builder.CreateLoad(getExceptionSlot())) ->setDoesNotReturn(); |