Allow for the possibility that __cxa_end_catch might throw for a catch-all block

or a catch of a record type by value or reference.  Also convert this to a
lazy cleanup.

llvm-svn: 108287
This commit is contained in:
John McCall 2010-07-13 22:12:14 +00:00
parent 6c6c14a55c
commit 5c08ab956b
4 changed files with 116 additions and 24 deletions

View File

@ -1020,18 +1020,45 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() {
return LP;
}
namespace {
/// A cleanup to call __cxa_end_catch. In many cases, the caught
/// exception type lets us state definitively that the thrown exception
/// type does not have a destructor. In particular:
/// - Catch-alls tell us nothing, so we have to conservatively
/// assume that the thrown exception might have a destructor.
/// - Catches by reference behave according to their base types.
/// - Catches of non-record types will only trigger for exceptions
/// of non-record types, which never have destructors.
/// - Catches of record types can trigger for arbitrary subclasses
/// of the caught type, so we have to assume the actual thrown
/// exception type might have a throwing destructor, even if the
/// caught type's destructor is trivial or nothrow.
struct CallEndCatch : EHScopeStack::LazyCleanup {
CallEndCatch(bool MightThrow) : MightThrow(MightThrow) {}
bool MightThrow;
void Emit(CodeGenFunction &CGF, bool IsForEH) {
if (!MightThrow) {
CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow();
return;
}
CGF.EmitCallOrInvoke(getEndCatchFn(CGF), 0, 0);
}
};
}
/// Emits a call to __cxa_begin_catch and enters a cleanup to call
/// __cxa_end_catch.
static llvm::Value *CallBeginCatch(CodeGenFunction &CGF, llvm::Value *Exn) {
///
/// \param EndMightThrow - true if __cxa_end_catch might throw
static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
llvm::Value *Exn,
bool EndMightThrow) {
llvm::CallInst *Call = CGF.Builder.CreateCall(getBeginCatchFn(CGF), Exn);
Call->setDoesNotThrow();
{
CodeGenFunction::CleanupBlock EndCatchCleanup(CGF, NormalAndEHCleanup);
// __cxa_end_catch never throws, so this can just be a call.
CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow();
}
CGF.EHStack.pushLazyCleanup<CallEndCatch>(NormalAndEHCleanup, EndMightThrow);
return Call;
}
@ -1051,8 +1078,11 @@ static void InitCatchParam(CodeGenFunction &CGF,
// If we're catching by reference, we can just cast the object
// pointer to the appropriate pointer.
if (isa<ReferenceType>(CatchType)) {
bool EndCatchMightThrow = cast<ReferenceType>(CatchType)->getPointeeType()
->isRecordType();
// __cxa_begin_catch returns the adjusted object pointer.
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, EndCatchMightThrow);
llvm::Value *ExnCast =
CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref");
CGF.Builder.CreateStore(ExnCast, ParamAddr);
@ -1063,7 +1093,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
bool IsComplex = false;
if (!CGF.hasAggregateLLVMType(CatchType) ||
(IsComplex = CatchType->isAnyComplexType())) {
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, false);
// If the catch type is a pointer type, __cxa_begin_catch returns
// the pointer by value.
@ -1098,7 +1128,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
const llvm::Type *PtrTy = LLVMCatchTy->getPointerTo(0); // addrspace 0 ok
if (RD->hasTrivialCopyConstructor()) {
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn);
llvm::Value *AdjustedExn = CallBeginCatch(CGF, Exn, true);
llvm::Value *Cast = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy);
CGF.EmitAggregateCopy(ParamAddr, Cast, CatchType);
return;
@ -1131,7 +1161,7 @@ static void InitCatchParam(CodeGenFunction &CGF,
CGF.EHStack.popTerminate();
// Finally we can call __cxa_begin_catch.
CallBeginCatch(CGF, Exn);
CallBeginCatch(CGF, Exn, true);
}
/// Begins a catch statement by initializing the catch variable and
@ -1164,7 +1194,7 @@ static void BeginCatch(CodeGenFunction &CGF,
VarDecl *CatchParam = S->getExceptionDecl();
if (!CatchParam) {
llvm::Value *Exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot(), "exn");
CallBeginCatch(CGF, Exn);
CallBeginCatch(CGF, Exn, true);
return;
}
@ -1298,7 +1328,7 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body,
Builder.CreateLoad(ForEHVar, "finally.endcatch");
Builder.CreateCondBr(ShouldEndCatch, EndCatchBB, CleanupContBB);
EmitBlock(EndCatchBB);
Builder.CreateCall(EndCatchFn)->setDoesNotThrow();
EmitCallOrInvoke(EndCatchFn, 0, 0); // catch-all, so might throw
EmitBlock(CleanupContBB);
}

View File

@ -32,6 +32,7 @@
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetData.h"
#include <cstdio>
@ -5712,6 +5713,22 @@ namespace {
llvm::BasicBlock *Block;
llvm::Value *TypeInfo;
};
struct CallObjCEndCatch : EHScopeStack::LazyCleanup {
CallObjCEndCatch(bool MightThrow, llvm::Value *Fn) :
MightThrow(MightThrow), Fn(Fn) {}
bool MightThrow;
llvm::Value *Fn;
void Emit(CodeGenFunction &CGF, bool IsForEH) {
if (!MightThrow) {
CGF.Builder.CreateCall(Fn)->setDoesNotThrow();
return;
}
CGF.EmitCallOrInvoke(Fn, 0, 0);
}
};
}
void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
@ -5801,13 +5818,10 @@ void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
Exn->setDoesNotThrow();
// Add a cleanup to leave the catch.
{
CodeGenFunction::CleanupBlock EndCatchBlock(CGF, NormalAndEHCleanup);
// __objc_end_catch never throws.
CGF.Builder.CreateCall(ObjCTypes.getObjCEndCatchFn())
->setDoesNotThrow();
}
bool EndCatchMightThrow = (Handler.Variable == 0);
CGF.EHStack.pushLazyCleanup<CallObjCEndCatch>(NormalAndEHCleanup,
EndCatchMightThrow,
ObjCTypes.getObjCEndCatchFn());
// Bind the catch parameter if it exists.
if (const VarDecl *CatchParam = Handler.Variable) {

View File

@ -206,6 +206,22 @@ public:
// Variadic templates would make this not terrible.
/// Push a lazily-created cleanup on the stack.
template <class T>
void pushLazyCleanup(CleanupKind Kind) {
void *Buffer = pushLazyCleanup(Kind, sizeof(T));
LazyCleanup *Obj = new(Buffer) T();
(void) Obj;
}
/// Push a lazily-created cleanup on the stack.
template <class T, class A0>
void pushLazyCleanup(CleanupKind Kind, A0 a0) {
void *Buffer = pushLazyCleanup(Kind, sizeof(T));
LazyCleanup *Obj = new(Buffer) T(a0);
(void) Obj;
}
/// Push a lazily-created cleanup on the stack.
template <class T, class A0, class A1>
void pushLazyCleanup(CleanupKind Kind, A0 a0, A1 a1) {

View File

@ -181,11 +181,7 @@ namespace test8 {
// CHECK-NEXT: invoke void @_ZN5test81AC1ERKS0_(
// CHECK: call i8* @__cxa_begin_catch
// CHECK-NEXT: invoke void @_ZN5test81AD1Ev(
// CHECK: call void @__cxa_end_catch()
// CHECK-NEXT: load
// CHECK-NEXT: switch
// CHECK: ret void
}
}
@ -217,3 +213,39 @@ namespace test9 {
// 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)
}
// __cxa_end_catch can throw for some kinds of caught exceptions.
namespace test10 {
void opaque();
struct A { ~A(); };
struct B { int x; };
// CHECK: define void @_ZN6test103fooEv()
void foo() {
A a; // force a cleanup context
try {
// CHECK: invoke void @_ZN6test106opaqueEv()
opaque();
} catch (int i) {
// CHECK: call i8* @__cxa_begin_catch
// CHECK-NEXT: bitcast
// CHECK-NEXT: load i32*
// CHECK-NEXT: store i32
// CHECK-NEXT: call void @__cxa_end_catch() nounwind
} catch (B a) {
// CHECK: call i8* @__cxa_begin_catch
// CHECK-NEXT: bitcast
// CHECK-NEXT: bitcast
// CHECK-NEXT: bitcast
// CHECK-NEXT: call void @llvm.memcpy
// CHECK-NEXT: invoke void @__cxa_end_catch()
} catch (...) {
// CHECK: call i8* @__cxa_begin_catch
// CHECK-NEXT: invoke void @__cxa_end_catch()
}
// CHECK: call void @_ZN6test101AD1Ev(
}
}