forked from OSchip/llvm-project
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:
parent
6c6c14a55c
commit
5c08ab956b
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue