Implement a new, much improved version of the cleanup hack. We just need

to be careful to emit landing pads that are always prepared to handle a
cleanup path.  This is correct mostly because of the fix to the LLVM
inliner, r132200.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132209 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
John McCall 2011-05-27 20:01:14 +00:00
Родитель f996e051d9
Коммит 0903421e36
10 изменённых файлов: 93 добавлений и 63 удалений

Просмотреть файл

@ -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();

Просмотреть файл

@ -1705,6 +1705,7 @@ public:
void EmitObjCAtThrowStmt(const ObjCAtThrowStmt &S);
void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S);
llvm::Constant *getUnwindResumeFn();
llvm::Constant *getUnwindResumeOrRethrowFn();
void EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
void ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);

Просмотреть файл

@ -7,5 +7,5 @@ void target() throw(int)
// CHECK: invoke void @_Z8externalv()
external();
}
// CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector({{.*}} i8* bitcast (i8** @_ZTIi to i8*), i8* null) nounwind
// CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector({{.*}} i8* bitcast (i8** @_ZTIi to i8*)) nounwind
// CHECK: call void @__cxa_call_unexpected

Просмотреть файл

@ -310,7 +310,7 @@ namespace test7 {
// CHECK: call i8* @llvm.eh.exception()
// CHECK: call void @__cxa_guard_abort(i32* @_ZGVZN5test74testEvE1x)
// CHECK: call void @_Unwind_Resume_or_Rethrow
// CHECK: call void @_Unwind_Resume(
}
}
@ -349,7 +349,7 @@ namespace test8 {
// CHECK: call i8* @llvm.eh.exception()
// CHECK: call void @__cxa_guard_abort(i32* @_ZGVZN5test84testEvE1x)
// CHECK: call void @_Unwind_Resume_or_Rethrow
// CHECK: call void @_Unwind_Resume(
}
}

Просмотреть файл

@ -334,7 +334,7 @@ namespace test7 {
// CHECK: ret void
// CHECK: call i8* @llvm.eh.exception(
// CHECK: call void @_ZdlPv({{.*}}) nounwind
// CHECK: call void @_Unwind_Resume_or_Rethrow
// CHECK: call void @_Unwind_Resume(
// Checked at top of file:
// @_ZN5test312_GLOBAL__N_11DD1Ev = alias internal {{.*}} @_ZN5test312_GLOBAL__N_11DD2Ev
@ -364,7 +364,7 @@ namespace test7 {
// CHECK: ret void
// CHECK: call i8* @llvm.eh.exception()
// CHECK: call void @_ZdlPv({{.*}}) nounwind
// CHECK: call void @_Unwind_Resume_or_Rethrow(
// CHECK: call void @_Unwind_Resume(
// CHECK: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD1Ev(
// CHECK: getelementptr inbounds i8* {{.*}}, i64 -8

Просмотреть файл

@ -190,7 +190,7 @@ namespace test9 {
// landing pad from first call to invoke
// CHECK: call i8* @llvm.eh.exception
// CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* {{.*}}, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* bitcast (i8** @_ZTIi to i8*), i8* null)
// CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* {{.*}}, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* bitcast (i8** @_ZTIi to i8*))
}
// __cxa_end_catch can throw for some kinds of caught exceptions.

Просмотреть файл

@ -95,7 +95,7 @@ X test2(bool B) {
// %invoke.cont17: rethrow block for %eh.cleanup.
// This really should be elsewhere in the function.
// CHECK-EH: call void @_Unwind_Resume_or_Rethrow
// CHECK-EH: call void @_Unwind_Resume(
// CHECK-EH-NEXT: unreachable
// %terminate.lpad: terminate landing pad.

Просмотреть файл

@ -24,6 +24,6 @@ void f() {
// CHECK: call i8* @llvm.eh.exception()
// CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector
// CHECK: call void @__cxa_guard_abort(i64* @_ZGVZ1fvE1x)
// CHECK: call void @_Unwind_Resume_or_Rethrow
// CHECK: call void @_Unwind_Resume(
// CHECK: unreachable
}

Просмотреть файл

@ -33,5 +33,5 @@ void test1() {
// CHECK: call i8* @llvm.eh.exception()
// CHECK: [[T1:%.*]] = bitcast [[N_T]]* [[N]] to i8*
// CHECK-NEXT: call void @_Block_object_dispose(i8* [[T1]], i32 8)
// CHECK: call void @_Unwind_Resume_or_Rethrow(
// CHECK: call void @_Unwind_Resume(
}

Просмотреть файл

@ -1,8 +1,8 @@
// RUN: %clang_cc1 -fobjc-nonfragile-abi -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck --check-prefix=DEFAULT_EH %s
// RUN: %clang_cc1 -fsjlj-exceptions -fobjc-nonfragile-abi -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck --check-prefix=SJLJ_EH %s
// DEFAULT_EH: declare void @_Unwind_Resume_or_Rethrow(i8*)
// SJLJ_EH: declare void @_Unwind_SjLj_Resume_or_Rethrow(i8*)
// DEFAULT_EH: declare void @_Unwind_Resume(i8*)
// SJLJ_EH: declare void @_Unwind_SjLj_Resume(i8*)
void f1(), f2();
void f0() {