Teach function-try-blocks on constructors and destructors to implicitly

rethrow.  Fixes rdar://problem/7696603

llvm-svn: 107757
This commit is contained in:
John McCall 2010-07-07 06:56:46 +00:00
parent 3956106543
commit b609d3f5f9
4 changed files with 51 additions and 18 deletions

View File

@ -606,11 +606,9 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
// Enter the function-try-block before the constructor prologue if
// applicable.
CXXTryStmtInfo TryInfo;
bool IsTryBody = (Body && isa<CXXTryStmt>(Body));
if (IsTryBody)
TryInfo = EnterCXXTryStmt(*cast<CXXTryStmt>(Body));
EnterCXXTryStmt(*cast<CXXTryStmt>(Body), true);
EHScopeStack::stable_iterator CleanupDepth = EHStack.stable_begin();
@ -631,7 +629,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
PopCleanupBlocks(CleanupDepth);
if (IsTryBody)
ExitCXXTryStmt(*cast<CXXTryStmt>(Body), TryInfo);
ExitCXXTryStmt(*cast<CXXTryStmt>(Body), true);
}
/// EmitCtorPrologue - This routine generates necessary code to initialize
@ -671,11 +669,10 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// anything else --- unless we're in a deleting destructor, in which
// case we're just going to call the complete destructor and then
// call operator delete() on the way out.
CXXTryStmtInfo TryInfo;
bool isTryBody = (DtorType != Dtor_Deleting &&
Body && isa<CXXTryStmt>(Body));
if (isTryBody)
TryInfo = EnterCXXTryStmt(*cast<CXXTryStmt>(Body));
EnterCXXTryStmt(*cast<CXXTryStmt>(Body), true);
// Emit the destructor epilogue now. If this is a complete
// destructor with a function-try-block, perform the base epilogue
@ -742,7 +739,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// Exit the try if applicable.
if (isTryBody)
ExitCXXTryStmt(*cast<CXXTryStmt>(Body), TryInfo);
ExitCXXTryStmt(*cast<CXXTryStmt>(Body), true);
}
/// EmitDtorEpilogue - Emit all code that comes at the end of class's

View File

@ -563,13 +563,12 @@ void CodeGenFunction::EmitEndEHSpec(const Decl *D) {
}
void CodeGenFunction::EmitCXXTryStmt(const CXXTryStmt &S) {
CXXTryStmtInfo Info = EnterCXXTryStmt(S);
EnterCXXTryStmt(S);
EmitStmt(S.getTryBlock());
ExitCXXTryStmt(S, Info);
ExitCXXTryStmt(S);
}
CodeGenFunction::CXXTryStmtInfo
CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S) {
void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
unsigned NumHandlers = S.getNumHandlers();
EHCatchScope *CatchScope = EHStack.pushCatch(NumHandlers);
@ -593,8 +592,6 @@ CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S) {
CatchScope->setCatchAllHandler(I, Handler);
}
}
return CXXTryStmtInfo();
}
/// Check whether this is a non-EH scope, i.e. a scope which doesn't
@ -1103,8 +1100,7 @@ static void BeginCatch(CodeGenFunction &CGF,
CGF.EmitLocalBlockVarDecl(*CatchParam, &InitCatchParam);
}
void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S,
CXXTryStmtInfo TryInfo) {
void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
unsigned NumHandlers = S.getNumHandlers();
EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin());
assert(CatchScope.getNumHandlers() == NumHandlers);
@ -1123,6 +1119,12 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S,
if (HaveInsertPoint())
Builder.CreateBr(ContBB);
// Determine if we need an implicit rethrow for all these catch handlers.
bool ImplicitRethrow = false;
if (IsFnTryBlock)
ImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) ||
isa<CXXConstructorDecl>(CurCodeDecl);
for (unsigned I = 0; I != NumHandlers; ++I) {
llvm::BasicBlock *CatchBlock = Handlers[I].Block;
EmitBlock(CatchBlock);
@ -1137,6 +1139,14 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S,
// Initialize the catch variable and set up the cleanups.
BeginCatch(*this, C);
// If there's an implicit rethrow, push a normal "cleanup" to call
// _cxa_rethrow. This needs to happen before _cxa_end_catch is
// called.
if (ImplicitRethrow) {
CleanupBlock Rethrow(*this, NormalCleanup);
EmitCallOrInvoke(getReThrowFn(*this), 0, 0);
}
// Perform the body of the catch.
EmitStmt(C->getHandlerBlock());

View File

@ -1102,9 +1102,8 @@ public:
void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S);
llvm::Constant *getUnwindResumeOrRethrowFn();
struct CXXTryStmtInfo {};
CXXTryStmtInfo EnterCXXTryStmt(const CXXTryStmt &S);
void ExitCXXTryStmt(const CXXTryStmt &S, CXXTryStmtInfo Info);
void EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
void ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
void EmitCXXTryStmt(const CXXTryStmt &S);

View File

@ -193,3 +193,30 @@ namespace test8 {
}
}
}
// Constructor function-try-block must rethrow on fallthrough.
// rdar://problem/7696603
namespace test9 {
void opaque();
struct A { A(); };
// CHECK: define void @_ZN5test91AC1Ev
// CHECK: call void @_ZN5test91AC2Ev
// CHECK-NEXT: ret void
// CHECK: define void @_ZN5test91AC2Ev(
A::A() try {
// CHECK: invoke void @_ZN5test96opaqueEv()
opaque();
} catch (int x) {
// CHECK: call i8* @__cxa_begin_catch
// CHECK: invoke void @_ZN5test96opaqueEv()
// CHECK: invoke void @__cxa_rethrow()
opaque();
}
// 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)
}