From: John McCall Date: Tue, 13 Jul 2010 22:12:14 +0000 (+0000) Subject: Allow for the possibility that __cxa_end_catch might throw for a catch-all block X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8e3f86193995c47ee0d229e4336c3382410f09f5;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108287 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index d3949baf3a..5e91adc669 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -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(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(CatchType)) { + bool EndCatchMightThrow = cast(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); } diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 7293537302..2992867cbc 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -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 @@ -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(NormalAndEHCleanup, + EndCatchMightThrow, + ObjCTypes.getObjCEndCatchFn()); // Bind the catch parameter if it exists. if (const VarDecl *CatchParam = Handler.Variable) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index ce3453ae5e..90a5421c69 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -206,6 +206,22 @@ public: // Variadic templates would make this not terrible. + /// Push a lazily-created cleanup on the stack. + template + 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 + 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 void pushLazyCleanup(CleanupKind Kind, A0 a0, A1 a1) { diff --git a/test/CodeGenCXX/eh.cpp b/test/CodeGenCXX/eh.cpp index f8d7ef69e0..6d79c3e17b 100644 --- a/test/CodeGenCXX/eh.cpp +++ b/test/CodeGenCXX/eh.cpp @@ -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( + } +}