|
|
|
|
@@ -112,11 +112,18 @@ static llvm::Constant *getUnexpectedFn(CodeGenFunction &CGF) {
|
|
|
|
|
return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_call_unexpected");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
llvm::Constant *CodeGenFunction::getUnwindResumeOrRethrowFn() {
|
|
|
|
|
const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext());
|
|
|
|
|
llvm::Constant *CodeGenFunction::getUnwindResumeFn() {
|
|
|
|
|
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");
|
|
|
|
|
return CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
llvm::Constant *CodeGenFunction::getUnwindResumeOrRethrowFn() {
|
|
|
|
|
const llvm::FunctionType *FTy =
|
|
|
|
|
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();
|
|
|
|
|
|