return CGF.CGM.CreateRuntimeFunction(FTy, "_ZSt9terminatev");
}
-// CopyObject - Utility to copy an object. Calls copy constructor as necessary.
-// DestPtr is casted to the right type.
-static void CopyObject(CodeGenFunction &CGF, const Expr *E,
- llvm::Value *DestPtr, llvm::Value *ExceptionPtrPtr) {
- QualType ObjectType = E->getType();
-
- // Store the throw exception in the exception object.
- if (!CGF.hasAggregateLLVMType(ObjectType)) {
- llvm::Value *Value = CGF.EmitScalarExpr(E);
- const llvm::Type *ValuePtrTy = Value->getType()->getPointerTo();
-
- CGF.Builder.CreateStore(Value,
- CGF.Builder.CreateBitCast(DestPtr, ValuePtrTy));
- } else {
- const llvm::Type *Ty = CGF.ConvertType(ObjectType)->getPointerTo();
- const CXXRecordDecl *RD =
- cast<CXXRecordDecl>(ObjectType->getAs<RecordType>()->getDecl());
-
- llvm::Value *This = CGF.Builder.CreateBitCast(DestPtr, Ty);
- if (RD->hasTrivialCopyConstructor()) {
- CGF.EmitAggExpr(E, This, false);
- } else if (CXXConstructorDecl *CopyCtor
- = RD->getCopyConstructor(CGF.getContext(), 0)) {
- llvm::Value *CondPtr = 0;
- if (CGF.Exceptions) {
- CodeGenFunction::EHCleanupBlock Cleanup(CGF);
- llvm::Constant *FreeExceptionFn = getFreeExceptionFn(CGF);
-
- llvm::BasicBlock *CondBlock = CGF.createBasicBlock("cond.free");
- llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
- CondPtr = CGF.CreateTempAlloca(llvm::Type::getInt1Ty(CGF.getLLVMContext()),
- "doEHfree");
-
- CGF.Builder.CreateCondBr(CGF.Builder.CreateLoad(CondPtr),
- CondBlock, Cont);
- CGF.EmitBlock(CondBlock);
-
- // Load the exception pointer.
- llvm::Value *ExceptionPtr = CGF.Builder.CreateLoad(ExceptionPtrPtr);
- CGF.Builder.CreateCall(FreeExceptionFn, ExceptionPtr);
-
- CGF.EmitBlock(Cont);
- }
-
- if (CondPtr)
- CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(CGF.getLLVMContext()),
- CondPtr);
-
- llvm::Value *Src = CGF.EmitLValue(E).getAddress();
-
- if (CondPtr)
- CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(CGF.getLLVMContext()),
- CondPtr);
-
- llvm::BasicBlock *TerminateHandler = CGF.getTerminateHandler();
- llvm::BasicBlock *PrevLandingPad = CGF.getInvokeDest();
- CGF.setInvokeDest(TerminateHandler);
-
- // Stolen from EmitClassAggrMemberwiseCopy
- llvm::Value *Callee = CGF.CGM.GetAddrOfCXXConstructor(CopyCtor,
- Ctor_Complete);
- CallArgList CallArgs;
- CallArgs.push_back(std::make_pair(RValue::get(This),
- CopyCtor->getThisType(CGF.getContext())));
-
- // Push the Src ptr.
- CallArgs.push_back(std::make_pair(RValue::get(Src),
- CopyCtor->getParamDecl(0)->getType()));
- const FunctionProtoType *FPT
- = CopyCtor->getType()->getAs<FunctionProtoType>();
- CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(CallArgs, FPT),
- Callee, ReturnValueSlot(), CallArgs, CopyCtor);
- CGF.setInvokeDest(PrevLandingPad);
- } else
- llvm_unreachable("uncopyable object");
+// Emits an exception expression into the given location. This
+// differs from EmitAnyExprToMem only in that, if a final copy-ctor
+// call is required, an exception within that copy ctor causes
+// std::terminate to be invoked.
+static void EmitAnyExprToExn(CodeGenFunction &CGF, const Expr *E,
+ llvm::Value *ExnLoc) {
+ // We want to release the allocated exception object if this
+ // expression throws. We do this by pushing an EH-only cleanup
+ // block which, furthermore, deactivates itself after the expression
+ // is complete.
+ llvm::AllocaInst *ShouldFreeVar =
+ CGF.CreateTempAlloca(llvm::Type::getInt1Ty(CGF.getLLVMContext()),
+ "should-free-exnobj.var");
+ CGF.InitTempAlloca(ShouldFreeVar,
+ llvm::ConstantInt::getFalse(CGF.getLLVMContext()));
+
+ // A variable holding the exception pointer. This is necessary
+ // because the throw expression does not necessarily dominate the
+ // cleanup, for example if it appears in a conditional expression.
+ llvm::AllocaInst *ExnLocVar =
+ CGF.CreateTempAlloca(ExnLoc->getType(), "exnobj.var");
+
+ llvm::BasicBlock *SavedInvokeDest = CGF.getInvokeDest();
+ {
+ CodeGenFunction::EHCleanupBlock Cleanup(CGF);
+ llvm::BasicBlock *FreeBB = CGF.createBasicBlock("free-exnobj");
+ llvm::BasicBlock *DoneBB = CGF.createBasicBlock("free-exnobj.done");
+
+ llvm::Value *ShouldFree = CGF.Builder.CreateLoad(ShouldFreeVar,
+ "should-free-exnobj");
+ CGF.Builder.CreateCondBr(ShouldFree, FreeBB, DoneBB);
+ CGF.EmitBlock(FreeBB);
+ llvm::Value *ExnLocLocal = CGF.Builder.CreateLoad(ExnLocVar, "exnobj");
+ CGF.Builder.CreateCall(getFreeExceptionFn(CGF), ExnLocLocal);
+ CGF.EmitBlock(DoneBB);
}
+ llvm::BasicBlock *Cleanup = CGF.getInvokeDest();
+
+ CGF.Builder.CreateStore(ExnLoc, ExnLocVar);
+ CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(CGF.getLLVMContext()),
+ ShouldFreeVar);
+
+ // __cxa_allocate_exception returns a void*; we need to cast this
+ // to the appropriate type for the object.
+ const llvm::Type *Ty = CGF.ConvertType(E->getType())->getPointerTo();
+ llvm::Value *TypedExnLoc = CGF.Builder.CreateBitCast(ExnLoc, Ty);
+
+ // FIXME: this isn't quite right! If there's a final unelided call
+ // to a copy constructor, then according to [except.terminate]p1 we
+ // must call std::terminate() if that constructor throws, because
+ // technically that copy occurs after the exception expression is
+ // evaluated but before the exception is caught. But the best way
+ // to handle that is to teach EmitAggExpr to do the final copy
+ // differently if it can't be elided.
+ CGF.EmitAnyExprToMem(E, TypedExnLoc, /*Volatile*/ false);
+
+ CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(CGF.getLLVMContext()),
+ ShouldFreeVar);
+
+ // Pop the cleanup block if it's still the top of the cleanup stack.
+ // Otherwise, temporaries have been created and our cleanup will get
+ // properly removed in time.
+ // TODO: this is not very resilient.
+ if (CGF.getInvokeDest() == Cleanup)
+ CGF.setInvokeDest(SavedInvokeDest);
}
// CopyObject - Utility to copy an object. Calls copy constructor as necessary.
llvm::ConstantInt::get(SizeTy, TypeSize),
"exception");
- llvm::Value *ExceptionPtrPtr =
- CreateTempAlloca(ExceptionPtr->getType(), "exception.ptr");
- Builder.CreateStore(ExceptionPtr, ExceptionPtrPtr);
-
-
- CopyObject(*this, E->getSubExpr(), ExceptionPtr, ExceptionPtrPtr);
+ EmitAnyExprToExn(*this, E->getSubExpr(), ExceptionPtr);
// Now throw the exception.
const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext());
llvm::Constant *TypeInfo = CGM.GetAddrOfRTTIDescriptor(ThrowType);
- llvm::Constant *Dtor = llvm::Constant::getNullValue(Int8PtrTy);
+
+ // The address of the destructor. If the exception type has a
+ // trivial destructor (or isn't a record), we just pass null.
+ llvm::Constant *Dtor = 0;
+ if (const RecordType *RecordTy = ThrowType->getAs<RecordType>()) {
+ CXXRecordDecl *Record = cast<CXXRecordDecl>(RecordTy->getDecl());
+ if (!Record->hasTrivialDestructor()) {
+ CXXDestructorDecl *DtorD = Record->getDestructor(getContext());
+ Dtor = CGM.GetAddrOfCXXDestructor(DtorD, Dtor_Complete);
+ Dtor = llvm::ConstantExpr::getBitCast(Dtor, Int8PtrTy);
+ }
+ }
+ if (!Dtor) Dtor = llvm::Constant::getNullValue(Int8PtrTy);
if (getInvokeDest()) {
llvm::BasicBlock *Cont = createBasicBlock("invoke.cont");
return new llvm::AllocaInst(Ty, 0, Name, AllocaInsertPt);
}
+void CodeGenFunction::InitTempAlloca(llvm::AllocaInst *Var,
+ llvm::Value *Init) {
+ llvm::StoreInst *Store = new llvm::StoreInst(Init, Var);
+ llvm::BasicBlock *Block = AllocaInsertPt->getParent();
+ Block->getInstList().insertAfter(&*AllocaInsertPt, Store);
+}
+
llvm::Value *CodeGenFunction::CreateIRTemp(QualType Ty,
const llvm::Twine &Name) {
llvm::AllocaInst *Alloc = CreateTempAlloca(ConvertType(Ty), Name);
"Pushed the same temporary twice; AST is likely wrong");
llvm::BasicBlock *DtorBlock = createBasicBlock("temp.dtor");
- llvm::Value *CondPtr = 0;
+ llvm::AllocaInst *CondPtr = 0;
// Check if temporaries need to be conditional. If so, we'll create a
// condition boolean, initialize it to 0 and
// Initialize it to false. This initialization takes place right after
// the alloca insert point.
- llvm::StoreInst *SI =
- new llvm::StoreInst(llvm::ConstantInt::getFalse(VMContext), CondPtr);
- llvm::BasicBlock *Block = AllocaInsertPt->getParent();
- Block->getInstList().insertAfter((llvm::Instruction *)AllocaInsertPt, SI);
+ InitTempAlloca(CondPtr, llvm::ConstantInt::getFalse(VMContext));
// Now set it to true.
Builder.CreateStore(llvm::ConstantInt::getTrue(VMContext), CondPtr);
llvm::AllocaInst *CreateTempAlloca(const llvm::Type *Ty,
const llvm::Twine &Name = "tmp");
+ /// InitTempAlloca - Provide an initial value for the given alloca.
+ void InitTempAlloca(llvm::AllocaInst *Alloca, llvm::Value *Value);
+
/// CreateIRTemp - Create a temporary IR object of the given type, with
/// appropriate alignment. This routine should only be used when an temporary
/// value needs to be stored into an alloca (for example, to avoid explicit
// If the type of the exception would be an incomplete type or a pointer
// to an incomplete type other than (cv) void the program is ill-formed.
QualType Ty = E->getType();
- int isPointer = 0;
+ bool isPointer = false;
if (const PointerType* Ptr = Ty->getAs<PointerType>()) {
Ty = Ptr->getPointeeType();
- isPointer = 1;
+ isPointer = true;
}
if (!isPointer || !Ty->isVoidType()) {
if (RequireCompleteType(ThrowLoc, Ty,
PDiag(diag::err_throw_abstract_type)
<< E->getSourceRange()))
return true;
-
- // FIXME: This is just a hack to mark the copy constructor referenced.
- // This should go away when the next FIXME is fixed.
- const RecordType *RT = Ty->getAs<RecordType>();
- if (!RT)
- return false;
-
- const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
- if (RD->hasTrivialCopyConstructor())
- return false;
- CXXConstructorDecl *CopyCtor = RD->getCopyConstructor(Context, 0);
- MarkDeclarationReferenced(ThrowLoc, CopyCtor);
}
- // FIXME: Construct a temporary here.
+ // Initialize the exception result. This implicitly weeds out
+ // abstract types or types with inaccessible copy constructors.
+ InitializedEntity Entity =
+ InitializedEntity::InitializeException(ThrowLoc, E->getType());
+ OwningExprResult Res = PerformCopyInitialization(Entity,
+ SourceLocation(),
+ Owned(E));
+ if (Res.isInvalid())
+ return true;
+ E = Res.takeAs<Expr>();
return false;
}
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++0x -emit-llvm %s -o %t.ll
+// RUN: %clang_cc1 -fexceptions -triple x86_64-apple-darwin -std=c++0x -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s
struct test1_D {
throw d1;
}
-// CHECK: define void @_Z5test1v() nounwind {
-// CHECK: %{{exception.ptr|1}} = alloca i8*
-// CHECK-NEXT: %{{exception|2}} = call i8* @__cxa_allocate_exception(i64 8)
-// CHECK-NEXT: store i8* %{{exception|2}}, i8** %{{exception.ptr|1}}
-// CHECK-NEXT: %{{0|3}} = bitcast i8* %{{exception|2}} to %struct.test1_D*
-// CHECK-NEXT: %{{tmp|4}} = bitcast %struct.test1_D* %{{0|3}} to i8*
-// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{tmp|4}}, i8* bitcast (%struct.test1_D* @d1 to i8*), i64 8, i32 8, i1 false)
-// CHECK-NEXT: call void @__cxa_throw(i8* %{{exception|2}}, i8* bitcast (%0* @_ZTI7test1_D to i8*), i8* null) noreturn
+// CHECK: define void @_Z5test1v()
+// CHECK: [[FREEVAR:%.*]] = alloca i1
+// CHECK-NEXT: [[EXNOBJVAR:%.*]] = alloca i8*
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 8)
+// CHECK-NEXT: store i8* [[EXNOBJ]], i8** [[EXNOBJVAR]]
+// CHECK-NEXT: store i1 true, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[DSTAR:%[^*]*\*]]
+// CHECK-NEXT: [[EXN2:%.*]] = bitcast [[DSTAR]] [[EXN]] to i8*
+// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[EXN2]], i8* bitcast ([[DSTAR]] @d1 to i8*), i64 8, i32 8, i1 false)
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%0* @_ZTI7test1_D to i8*), i8* null) noreturn
// CHECK-NEXT: unreachable
throw d2;
}
-// CHECK: define void @_Z5test2v() nounwind {
-// CHECK: %{{exception.ptr|1}} = alloca i8*
-// CHECK-NEXT: %{{exception|2}} = call i8* @__cxa_allocate_exception(i64 16)
-// CHECK-NEXT: store i8* %{{exception|2}}, i8** %{{\1}}
-// CHECK-NEXT: %{{0|3}} = bitcast i8* %{{exception|2}} to %struct.test2_D*
-// CHECK: invoke void @_ZN7test2_DC1ERKS_(%struct.test2_D* %{{0|3}}, %struct.test2_D* @d2)
-// CHECK-NEXT: to label %{{invoke.cont|8}} unwind label %{{terminate.handler|4}}
-// CHECK: call void @__cxa_throw(i8* %{{exception|2}}, i8* bitcast (%{{0|3}}* @_ZTI7test2_D to i8*), i8* null) noreturn
+// CHECK: define void @_Z5test2v()
+// CHECK: [[FREEVAR:%.*]] = alloca i1
+// CHECK-NEXT: [[EXNOBJVAR:%.*]] = alloca i8*
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 16)
+// CHECK-NEXT: store i8* [[EXNOBJ]], i8** [[EXNOBJVAR]]
+// CHECK-NEXT: store i1 true, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[DSTAR:%[^*]*\*]]
+// CHECK-NEXT: invoke void @_ZN7test2_DC1ERKS_([[DSTAR]] [[EXN]], [[DSTAR]] @d2)
+// CHECK-NEXT: to label %[[CONT:.*]] unwind label %{{.*}}
+// CHECK: [[CONT]]:
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%{{.*}}* @_ZTI7test2_D to i8*), i8* null) noreturn
// CHECK-NEXT: unreachable
throw (volatile test3_D *)0;
}
-// CHECK: define void @_Z5test3v() nounwind {
-// CHECK: %{{exception.ptr|1}} = alloca i8*
-// CHECK-NEXT: %{{exception|2}} = call i8* @__cxa_allocate_exception(i64 8)
-// CHECK-NEXT: store i8* %{{exception|2}}, i8** %{{exception.ptr|1}}
-// CHECK-NEXT: %{{0|3}} = bitcast i8* %{{exception|2}} to %struct.test3_D**
-// CHECK-NEXT: store %struct.test3_D* null, %struct.test3_D**
-// CHECK-NEXT: call void @__cxa_throw(i8* %{{exception|2}}, i8* bitcast (%1* @_ZTIPV7test3_D to i8*), i8* null) noreturn
-// CHECK-NEXT: unreachable
+// CHECK: define void @_Z5test3v()
+// CHECK: [[FREEVAR:%.*]] = alloca i1
+// CHECK-NEXT: [[EXNOBJVAR:%.*]] = alloca i8*
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 8)
+// CHECK-NEXT: store i8* [[EXNOBJ]], i8** [[EXNOBJVAR]]
+// CHECK-NEXT: store i1 true, i1* [[FREEVAR]]
+// CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[DSS:%[^*]*\*]]*
+// CHECK-NEXT: store [[DSS]] null, [[DSS]]* [[EXN]]
+// CHECK-NEXT: store i1 false, i1* [[FREEVAR]]
+// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%1* @_ZTIPV7test3_D to i8*), i8* null) noreturn
+// CHECK-NEXT: unreachable
void test4() {
throw;
}
-// CHECK: define void @_Z5test4v() nounwind {
+// CHECK: define void @_Z5test4v()
// CHECK: call void @__cxa_rethrow() noreturn
// CHECK-NEXT: unreachable
+
+
+// rdar://problem/7696549
+namespace test5 {
+ struct A {
+ A();
+ A(const A&);
+ ~A();
+ };
+
+ void test() {
+ try { throw A(); } catch (A &x) {}
+ }
+// CHECK: define void @_ZN5test54testEv()
+// CHECK: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 1)
+// CHECK: [[EXNCAST:%.*]] = bitcast i8* [[EXNOBJ]] to [[A:%[^*]*]]*
+// CHECK-NEXT: invoke void @_ZN5test51AC1Ev([[A]]* [[EXNCAST]])
+// CHECK: invoke void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({{%.*}}* @_ZTIN5test51AE to i8*), i8* bitcast (void ([[A]]*)* @_ZN5test51AD1Ev to i8*)) noreturn
+// CHECK-NEXT: to label {{%.*}} unwind label %[[HANDLER:[^ ]*]]
+// CHECK: [[HANDLER]]:
+// CHECK: {{%.*}} = call i32 @llvm.eh.typeid.for(i8* bitcast ({{%.*}}* @_ZTIN5test51AE to i8*))
+}