r183859 - Reapply r183721, reverted in r183776, with a fix for a bug in the former (we
Richard Smith
richard-llvm at metafoo.co.uk
Wed Jun 12 13:42:33 PDT 2013
Author: rsmith
Date: Wed Jun 12 15:42:33 2013
New Revision: 183859
URL: http://llvm.org/viewvc/llvm-project?rev=183859&view=rev
Log:
Reapply r183721, reverted in r183776, with a fix for a bug in the former (we
were lacking ExprWithCleanups nodes in some cases where the new approach to
lifetime extension needed them).
Original commit message:
Rework IR emission for lifetime-extended temporaries. Instead of trying to walk
into the expression and dig out a single lifetime-extended entity and manually
pull its cleanup outside the expression, instead keep a list of the cleanups
which we'll need to emit when we get to the end of the full-expression. Also
emit those cleanups early, as EH-only cleanups, to cover the case that the
full-expression does not terminate normally. This allows IR generation to
properly model temporary lifetime when multiple temporaries are extended by the
same declaration.
We have a pre-existing bug where an exception thrown from a temporary's
destructor does not clean up lifetime-extended temporaries created in the same
expression and extended to automatic storage duration; that is not fixed by
this patch.
Modified:
cfe/trunk/lib/CodeGen/CGCleanup.cpp
cfe/trunk/lib/CodeGen/CGCleanup.h
cfe/trunk/lib/CodeGen/CGDecl.cpp
cfe/trunk/lib/CodeGen/CGExpr.cpp
cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
cfe/trunk/lib/CodeGen/CodeGenFunction.h
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaInit.cpp
cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp
cfe/trunk/test/CodeGenCXX/temporaries.cpp
Modified: cfe/trunk/lib/CodeGen/CGCleanup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCleanup.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCleanup.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCleanup.cpp Wed Jun 12 15:42:33 2013
@@ -387,6 +387,33 @@ void CodeGenFunction::PopCleanupBlocks(E
}
}
+/// Pops cleanup blocks until the given savepoint is reached, then add the
+/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
+void
+CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old,
+ size_t OldLifetimeExtendedSize) {
+ PopCleanupBlocks(Old);
+
+ // Move our deferred cleanups onto the EH stack.
+ for (size_t I = OldLifetimeExtendedSize,
+ E = LifetimeExtendedCleanupStack.size(); I != E; /**/) {
+ // Alignment should be guaranteed by the vptrs in the individual cleanups.
+ assert((I % llvm::alignOf<LifetimeExtendedCleanupHeader>() == 0) &&
+ "misaligned cleanup stack entry");
+
+ LifetimeExtendedCleanupHeader &Header =
+ reinterpret_cast<LifetimeExtendedCleanupHeader&>(
+ LifetimeExtendedCleanupStack[I]);
+ I += sizeof(Header);
+
+ EHStack.pushCopyOfCleanup(Header.getKind(),
+ &LifetimeExtendedCleanupStack[I],
+ Header.getSize());
+ I += Header.getSize();
+ }
+ LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize);
+}
+
static llvm::BasicBlock *CreateNormalEntry(CodeGenFunction &CGF,
EHCleanupScope &Scope) {
assert(Scope.isNormalCleanup());
Modified: cfe/trunk/lib/CodeGen/CGCleanup.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCleanup.h?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCleanup.h (original)
+++ cfe/trunk/lib/CodeGen/CGCleanup.h Wed Jun 12 15:42:33 2013
@@ -374,6 +374,11 @@ public:
return new (Buffer) T(N, a0, a1, a2);
}
+ void pushCopyOfCleanup(CleanupKind Kind, const void *Cleanup, size_t Size) {
+ void *Buffer = pushCleanup(Kind, Size);
+ std::memcpy(Buffer, Cleanup, Size);
+ }
+
/// Pops a cleanup scope off the stack. This is private to CGCleanup.cpp.
void popCleanup();
Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Wed Jun 12 15:42:33 2013
@@ -1340,6 +1340,26 @@ void CodeGenFunction::pushDestroy(Cleanu
destroyer, useEHCleanupForArray);
}
+void CodeGenFunction::pushLifetimeExtendedDestroy(
+ CleanupKind cleanupKind, llvm::Value *addr, QualType type,
+ Destroyer *destroyer, bool useEHCleanupForArray) {
+ assert(!isInConditionalBranch() &&
+ "performing lifetime extension from within conditional");
+
+ // Push an EH-only cleanup for the object now.
+ // FIXME: When popping normal cleanups, we need to keep this EH cleanup
+ // around in case a temporary's destructor throws an exception.
+ if (cleanupKind & EHCleanup)
+ EHStack.pushCleanup<DestroyObject>(
+ static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), addr, type,
+ destroyer, useEHCleanupForArray);
+
+ // Remember that we need to push a full cleanup for the object at the
+ // end of the full-expression.
+ pushCleanupAfterFullExpr<DestroyObject>(
+ cleanupKind, addr, type, destroyer, useEHCleanupForArray);
+}
+
/// emitDestroy - Immediately perform the destruction of the given
/// object.
///
Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Wed Jun 12 15:42:33 2013
@@ -171,128 +171,187 @@ void CodeGenFunction::EmitAnyExprToMem(c
llvm_unreachable("bad evaluation kind");
}
-static llvm::Value *
-CreateReferenceTemporary(CodeGenFunction &CGF, QualType Type,
- const NamedDecl *InitializedDecl) {
- if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl)) {
- if (VD->hasGlobalStorage()) {
- SmallString<256> Name;
- llvm::raw_svector_ostream Out(Name);
- CGF.CGM.getCXXABI().getMangleContext().mangleReferenceTemporary(VD, Out);
- Out.flush();
-
- llvm::Type *RefTempTy = CGF.ConvertTypeForMem(Type);
-
- // Create the reference temporary.
- llvm::GlobalVariable *RefTemp =
- new llvm::GlobalVariable(CGF.CGM.getModule(),
- RefTempTy, /*isConstant=*/false,
- llvm::GlobalValue::InternalLinkage,
- llvm::Constant::getNullValue(RefTempTy),
- Name.str());
- // If we're binding to a thread_local variable, the temporary is also
- // thread local.
- if (VD->getTLSKind())
- CGF.CGM.setTLSMode(RefTemp, *VD);
- return RefTemp;
+static void
+pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
+ const Expr *E, llvm::Value *ReferenceTemporary) {
+ // Objective-C++ ARC:
+ // If we are binding a reference to a temporary that has ownership, we
+ // need to perform retain/release operations on the temporary.
+ //
+ // FIXME: This should be looking at E, not M.
+ if (CGF.getLangOpts().ObjCAutoRefCount &&
+ M->getType()->isObjCLifetimeType()) {
+ QualType ObjCARCReferenceLifetimeType = M->getType();
+ switch (Qualifiers::ObjCLifetime Lifetime =
+ ObjCARCReferenceLifetimeType.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ // Carry on to normal cleanup handling.
+ break;
+
+ case Qualifiers::OCL_Autoreleasing:
+ // Nothing to do; cleaned up by an autorelease pool.
+ return;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ switch (StorageDuration Duration = M->getStorageDuration()) {
+ case SD_Static:
+ // Note: we intentionally do not register a cleanup to release
+ // the object on program termination.
+ return;
+
+ case SD_Thread:
+ // FIXME: We should probably register a cleanup in this case.
+ return;
+
+ case SD_Automatic:
+ case SD_FullExpression:
+ assert(!ObjCARCReferenceLifetimeType->isArrayType());
+ CodeGenFunction::Destroyer *Destroy;
+ CleanupKind CleanupKind;
+ if (Lifetime == Qualifiers::OCL_Strong) {
+ const ValueDecl *VD = M->getExtendingDecl();
+ bool Precise =
+ VD && isa<VarDecl>(VD) && VD->hasAttr<ObjCPreciseLifetimeAttr>();
+ CleanupKind = CGF.getARCCleanupKind();
+ Destroy = Precise ? &CodeGenFunction::destroyARCStrongPrecise
+ : &CodeGenFunction::destroyARCStrongImprecise;
+ } else {
+ // __weak objects always get EH cleanups; otherwise, exceptions
+ // could cause really nasty crashes instead of mere leaks.
+ CleanupKind = NormalAndEHCleanup;
+ Destroy = &CodeGenFunction::destroyARCWeak;
+ }
+ if (Duration == SD_FullExpression)
+ CGF.pushDestroy(CleanupKind, ReferenceTemporary,
+ ObjCARCReferenceLifetimeType, *Destroy,
+ CleanupKind & EHCleanup);
+ else
+ CGF.pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary,
+ ObjCARCReferenceLifetimeType,
+ *Destroy, CleanupKind & EHCleanup);
+ return;
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary cannot have dynamic storage duration");
+ }
+ llvm_unreachable("unknown storage duration");
+ }
+ }
+
+ if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) {
+ if (ILE->initializesStdInitializerList()) {
+ // FIXME: This is wrong if the temporary has static or thread storage
+ // duration.
+ CGF.EmitStdInitializerListCleanup(ReferenceTemporary, ILE);
+ return;
+ }
+ }
+
+ CXXDestructorDecl *ReferenceTemporaryDtor = 0;
+ if (const RecordType *RT =
+ E->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
+ // Get the destructor for the reference temporary.
+ CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(RT->getDecl());
+ if (!ClassDecl->hasTrivialDestructor())
+ ReferenceTemporaryDtor = ClassDecl->getDestructor();
+ }
+
+ if (!ReferenceTemporaryDtor)
+ return;
+
+ // Call the destructor for the temporary.
+ switch (M->getStorageDuration()) {
+ case SD_Static:
+ case SD_Thread: {
+ llvm::Constant *CleanupFn;
+ llvm::Constant *CleanupArg;
+ if (E->getType()->isArrayType()) {
+ CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper(
+ cast<llvm::Constant>(ReferenceTemporary), E->getType(),
+ CodeGenFunction::destroyCXXObject, CGF.getLangOpts().Exceptions);
+ CleanupArg = llvm::Constant::getNullValue(CGF.Int8PtrTy);
+ } else {
+ CleanupFn =
+ CGF.CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);
+ CleanupArg = cast<llvm::Constant>(ReferenceTemporary);
}
+ CGF.CGM.getCXXABI().registerGlobalDtor(
+ CGF, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg);
+ break;
}
- return CGF.CreateMemTemp(Type, "ref.tmp");
+ case SD_FullExpression:
+ CGF.pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
+ CodeGenFunction::destroyCXXObject,
+ CGF.getLangOpts().Exceptions);
+ break;
+
+ case SD_Automatic:
+ CGF.pushLifetimeExtendedDestroy(NormalAndEHCleanup,
+ ReferenceTemporary, E->getType(),
+ CodeGenFunction::destroyCXXObject,
+ CGF.getLangOpts().Exceptions);
+ break;
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary cannot have dynamic storage duration");
+ }
}
static llvm::Value *
-EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
- llvm::Value *&ReferenceTemporary,
- const CXXDestructorDecl *&ReferenceTemporaryDtor,
- const InitListExpr *&ReferenceInitializerList,
- QualType &ObjCARCReferenceLifetimeType,
- const NamedDecl *InitializedDecl) {
- const MaterializeTemporaryExpr *M = NULL;
- E = E->findMaterializedTemporary(M);
- // Objective-C++ ARC:
- // If we are binding a reference to a temporary that has ownership, we
- // need to perform retain/release operations on the temporary.
- if (M && CGF.getLangOpts().ObjCAutoRefCount &&
- M->getType()->isObjCLifetimeType() &&
- (M->getType().getObjCLifetime() == Qualifiers::OCL_Strong ||
- M->getType().getObjCLifetime() == Qualifiers::OCL_Weak ||
- M->getType().getObjCLifetime() == Qualifiers::OCL_Autoreleasing))
- ObjCARCReferenceLifetimeType = M->getType();
+createReferenceTemporary(CodeGenFunction &CGF,
+ const MaterializeTemporaryExpr *M, const Expr *Inner) {
+ switch (M->getStorageDuration()) {
+ case SD_FullExpression:
+ case SD_Automatic:
+ return CGF.CreateMemTemp(Inner->getType(), "ref.tmp");
+
+ case SD_Thread:
+ case SD_Static:
+ return CGF.CGM.GetAddrOfGlobalTemporary(M, Inner);
+
+ case SD_Dynamic:
+ llvm_unreachable("temporary can't have dynamic storage duration");
+ }
+ llvm_unreachable("unknown storage duration");
+}
+static llvm::Value *
+emitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
+ const NamedDecl *InitializedDecl) {
if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E)) {
CGF.enterFullExpression(EWC);
CodeGenFunction::RunCleanupsScope Scope(CGF);
-
- return EmitExprForReferenceBinding(CGF, EWC->getSubExpr(),
- ReferenceTemporary,
- ReferenceTemporaryDtor,
- ReferenceInitializerList,
- ObjCARCReferenceLifetimeType,
- InitializedDecl);
+ return emitExprForReferenceBinding(CGF, EWC->getSubExpr(), InitializedDecl);
}
+ const MaterializeTemporaryExpr *M = 0;
+ E = E->findMaterializedTemporary(M);
+
if (E->isGLValue()) {
// Emit the expression as an lvalue.
LValue LV = CGF.EmitLValue(E);
assert(LV.isSimple());
return LV.getAddress();
}
-
- if (!ObjCARCReferenceLifetimeType.isNull()) {
- ReferenceTemporary = CreateReferenceTemporary(CGF,
- ObjCARCReferenceLifetimeType,
- InitializedDecl);
-
-
- LValue RefTempDst = CGF.MakeAddrLValue(ReferenceTemporary,
- ObjCARCReferenceLifetimeType);
+
+ assert(M && "prvalue reference initializer but not a materialized temporary");
+
+ if (CGF.getLangOpts().ObjCAutoRefCount &&
+ M->getType()->isObjCLifetimeType() &&
+ M->getType().getObjCLifetime() != Qualifiers::OCL_None &&
+ M->getType().getObjCLifetime() != Qualifiers::OCL_ExplicitNone) {
+ // FIXME: Fold this into the general case below.
+ llvm::Value *Object = createReferenceTemporary(CGF, M, E);
+ LValue RefTempDst = CGF.MakeAddrLValue(Object, M->getType());
CGF.EmitScalarInit(E, dyn_cast_or_null<ValueDecl>(InitializedDecl),
RefTempDst, false);
-
- bool ExtendsLifeOfTemporary = false;
- if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(InitializedDecl)) {
- if (Var->extendsLifetimeOfTemporary())
- ExtendsLifeOfTemporary = true;
- } else if (InitializedDecl && isa<FieldDecl>(InitializedDecl)) {
- ExtendsLifeOfTemporary = true;
- }
-
- if (!ExtendsLifeOfTemporary) {
- // Since the lifetime of this temporary isn't going to be extended,
- // we need to clean it up ourselves at the end of the full expression.
- switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- case Qualifiers::OCL_ExplicitNone:
- case Qualifiers::OCL_Autoreleasing:
- break;
-
- case Qualifiers::OCL_Strong: {
- assert(!ObjCARCReferenceLifetimeType->isArrayType());
- CleanupKind cleanupKind = CGF.getARCCleanupKind();
- CGF.pushDestroy(cleanupKind,
- ReferenceTemporary,
- ObjCARCReferenceLifetimeType,
- CodeGenFunction::destroyARCStrongImprecise,
- cleanupKind & EHCleanup);
- break;
- }
-
- case Qualifiers::OCL_Weak:
- assert(!ObjCARCReferenceLifetimeType->isArrayType());
- CGF.pushDestroy(NormalAndEHCleanup,
- ReferenceTemporary,
- ObjCARCReferenceLifetimeType,
- CodeGenFunction::destroyARCWeak,
- /*useEHCleanupForArray*/ true);
- break;
- }
-
- ObjCARCReferenceLifetimeType = QualType();
- }
-
- return ReferenceTemporary;
+
+ pushTemporaryCleanup(CGF, M, E, Object);
+ return Object;
}
SmallVector<const Expr *, 2> CommaLHSs;
@@ -302,112 +361,59 @@ EmitExprForReferenceBinding(CodeGenFunct
for (unsigned I = 0, N = CommaLHSs.size(); I != N; ++I)
CGF.EmitIgnoredExpr(CommaLHSs[I]);
- if (const OpaqueValueExpr *opaque = dyn_cast<OpaqueValueExpr>(E))
- if (opaque->getType()->isRecordType())
+ if (const OpaqueValueExpr *opaque = dyn_cast<OpaqueValueExpr>(E)) {
+ if (opaque->getType()->isRecordType()) {
+ assert(Adjustments.empty());
return CGF.EmitOpaqueValueLValue(opaque).getAddress();
-
- // Create a reference temporary if necessary.
- AggValueSlot AggSlot = AggValueSlot::ignored();
- if (CGF.hasAggregateEvaluationKind(E->getType())) {
- ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(),
- InitializedDecl);
- CharUnits Alignment = CGF.getContext().getTypeAlignInChars(E->getType());
- AggValueSlot::IsDestructed_t isDestructed
- = AggValueSlot::IsDestructed_t(InitializedDecl != 0);
- AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Alignment,
- Qualifiers(), isDestructed,
- AggValueSlot::DoesNotNeedGCBarriers,
- AggValueSlot::IsNotAliased);
- }
-
- if (InitializedDecl) {
- if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) {
- if (ILE->initializesStdInitializerList()) {
- ReferenceInitializerList = ILE;
- }
- }
- else if (const RecordType *RT =
- E->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()){
- // Get the destructor for the reference temporary.
- CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(RT->getDecl());
- if (!ClassDecl->hasTrivialDestructor())
- ReferenceTemporaryDtor = ClassDecl->getDestructor();
}
}
- RValue RV = CGF.EmitAnyExpr(E, AggSlot);
-
- // FIXME: This is wrong. We need to register the destructor for the temporary
- // now, *before* we perform the adjustments, because in the case of a
- // pointer-to-member adjustment, the adjustment might throw.
-
- // Check if need to perform derived-to-base casts and/or field accesses, to
- // get from the temporary object we created (and, potentially, for which we
- // extended the lifetime) to the subobject we're binding the reference to.
- if (!Adjustments.empty()) {
- llvm::Value *Object = RV.getAggregateAddr();
- for (unsigned I = Adjustments.size(); I != 0; --I) {
- SubobjectAdjustment &Adjustment = Adjustments[I-1];
- switch (Adjustment.Kind) {
- case SubobjectAdjustment::DerivedToBaseAdjustment:
- Object =
- CGF.GetAddressOfBaseClass(Object,
- Adjustment.DerivedToBase.DerivedClass,
- Adjustment.DerivedToBase.BasePath->path_begin(),
- Adjustment.DerivedToBase.BasePath->path_end(),
- /*NullCheckValue=*/false);
- break;
-
- case SubobjectAdjustment::FieldAdjustment: {
- LValue LV = CGF.MakeAddrLValue(Object, E->getType());
- LV = CGF.EmitLValueForField(LV, Adjustment.Field);
- assert(LV.isSimple() &&
- "materialized temporary field is not a simple lvalue");
- Object = LV.getAddress();
- break;
- }
+ // Create and initialize the reference temporary.
+ llvm::Value *Object = createReferenceTemporary(CGF, M, E);
+ CGF.EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
+ pushTemporaryCleanup(CGF, M, E, Object);
+
+ // Perform derived-to-base casts and/or field accesses, to get from the
+ // temporary object we created (and, potentially, for which we extended
+ // the lifetime) to the subobject we're binding the reference to.
+ for (unsigned I = Adjustments.size(); I != 0; --I) {
+ SubobjectAdjustment &Adjustment = Adjustments[I-1];
+ switch (Adjustment.Kind) {
+ case SubobjectAdjustment::DerivedToBaseAdjustment:
+ Object =
+ CGF.GetAddressOfBaseClass(Object,
+ Adjustment.DerivedToBase.DerivedClass,
+ Adjustment.DerivedToBase.BasePath->path_begin(),
+ Adjustment.DerivedToBase.BasePath->path_end(),
+ /*NullCheckValue=*/false);
+ break;
- case SubobjectAdjustment::MemberPointerAdjustment: {
- llvm::Value *Ptr = CGF.EmitScalarExpr(Adjustment.Ptr.RHS);
- Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress(
- CGF, Object, Ptr, Adjustment.Ptr.MPT);
- break;
- }
- }
+ case SubobjectAdjustment::FieldAdjustment: {
+ LValue LV = CGF.MakeAddrLValue(Object, E->getType());
+ LV = CGF.EmitLValueForField(LV, Adjustment.Field);
+ assert(LV.isSimple() &&
+ "materialized temporary field is not a simple lvalue");
+ Object = LV.getAddress();
+ break;
}
- return Object;
+ case SubobjectAdjustment::MemberPointerAdjustment: {
+ llvm::Value *Ptr = CGF.EmitScalarExpr(Adjustment.Ptr.RHS);
+ Object = CGF.CGM.getCXXABI().EmitMemberDataPointerAddress(
+ CGF, Object, Ptr, Adjustment.Ptr.MPT);
+ break;
+ }
+ }
}
- if (RV.isAggregate())
- return RV.getAggregateAddr();
-
- // Create a temporary variable that we can bind the reference to.
- ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(),
- InitializedDecl);
-
-
- LValue tempLV = CGF.MakeNaturalAlignAddrLValue(ReferenceTemporary,
- E->getType());
- if (RV.isScalar())
- CGF.EmitStoreOfScalar(RV.getScalarVal(), tempLV, /*init*/ true);
- else
- CGF.EmitStoreOfComplex(RV.getComplexVal(), tempLV, /*init*/ true);
- return ReferenceTemporary;
+ return Object;
}
RValue
CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E,
const NamedDecl *InitializedDecl) {
- llvm::Value *ReferenceTemporary = 0;
- const CXXDestructorDecl *ReferenceTemporaryDtor = 0;
- const InitListExpr *ReferenceInitializerList = 0;
- QualType ObjCARCReferenceLifetimeType;
- llvm::Value *Value = EmitExprForReferenceBinding(*this, E, ReferenceTemporary,
- ReferenceTemporaryDtor,
- ReferenceInitializerList,
- ObjCARCReferenceLifetimeType,
- InitializedDecl);
+ llvm::Value *Value = emitExprForReferenceBinding(*this, E, InitializedDecl);
+
if (SanitizePerformTypeCheck && !E->getType()->isFunctionType()) {
// C++11 [dcl.ref]p5 (as amended by core issue 453):
// If a glvalue to which a reference is directly bound designates neither
@@ -417,80 +423,7 @@ CodeGenFunction::EmitReferenceBindingToE
QualType Ty = E->getType();
EmitTypeCheck(TCK_ReferenceBinding, E->getExprLoc(), Value, Ty);
}
- if (!ReferenceTemporaryDtor && !ReferenceInitializerList &&
- ObjCARCReferenceLifetimeType.isNull())
- return RValue::get(Value);
-
- // Make sure to call the destructor for the reference temporary.
- const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl);
- if (VD && VD->hasGlobalStorage()) {
- if (ReferenceTemporaryDtor) {
- llvm::Constant *CleanupFn;
- llvm::Constant *CleanupArg;
- if (E->getType()->isArrayType()) {
- CleanupFn = CodeGenFunction(CGM).generateDestroyHelper(
- cast<llvm::Constant>(ReferenceTemporary), E->getType(),
- destroyCXXObject, getLangOpts().Exceptions);
- CleanupArg = llvm::Constant::getNullValue(Int8PtrTy);
- } else {
- CleanupFn =
- CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);
- CleanupArg = cast<llvm::Constant>(ReferenceTemporary);
- }
- CGM.getCXXABI().registerGlobalDtor(*this, *VD, CleanupFn, CleanupArg);
- } else if (ReferenceInitializerList) {
- // FIXME: This is wrong. We need to register a global destructor to clean
- // up the initializer_list object, rather than adding it as a local
- // cleanup.
- EmitStdInitializerListCleanup(ReferenceTemporary,
- ReferenceInitializerList);
- } else {
- assert(!ObjCARCReferenceLifetimeType.isNull() && !VD->getTLSKind());
- // Note: We intentionally do not register a global "destructor" to
- // release the object.
- }
-
- return RValue::get(Value);
- }
- if (ReferenceTemporaryDtor) {
- if (E->getType()->isArrayType())
- pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(),
- destroyCXXObject, getLangOpts().Exceptions);
- else
- PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary);
- } else if (ReferenceInitializerList) {
- EmitStdInitializerListCleanup(ReferenceTemporary,
- ReferenceInitializerList);
- } else {
- switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- llvm_unreachable(
- "Not a reference temporary that needs to be deallocated");
- case Qualifiers::OCL_ExplicitNone:
- case Qualifiers::OCL_Autoreleasing:
- // Nothing to do.
- break;
-
- case Qualifiers::OCL_Strong: {
- bool precise = VD && VD->hasAttr<ObjCPreciseLifetimeAttr>();
- CleanupKind cleanupKind = getARCCleanupKind();
- pushDestroy(cleanupKind, ReferenceTemporary, ObjCARCReferenceLifetimeType,
- precise ? destroyARCStrongPrecise : destroyARCStrongImprecise,
- cleanupKind & EHCleanup);
- break;
- }
-
- case Qualifiers::OCL_Weak: {
- // __weak objects always get EH cleanups; otherwise, exceptions
- // could cause really nasty crashes instead of mere leaks.
- pushDestroy(NormalAndEHCleanup, ReferenceTemporary,
- ObjCARCReferenceLifetimeType, destroyARCWeak, true);
- break;
- }
- }
- }
-
return RValue::get(Value);
}
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Wed Jun 12 15:42:33 2013
@@ -65,6 +65,8 @@ CodeGenFunction::CodeGenFunction(CodeGen
}
CodeGenFunction::~CodeGenFunction() {
+ assert(LifetimeExtendedCleanupStack.empty() && "failed to emit a cleanup");
+
// If there are any unclaimed block infos, go ahead and destroy them
// now. This can happen if IR-gen gets clever and skips evaluating
// something.
Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Wed Jun 12 15:42:33 2013
@@ -241,6 +241,18 @@ public:
llvm::DenseMap<const VarDecl *, llvm::Value *> NRVOFlags;
EHScopeStack EHStack;
+ llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack;
+
+ /// Header for data within LifetimeExtendedCleanupStack.
+ struct LifetimeExtendedCleanupHeader {
+ /// The size of the following cleanup object.
+ size_t Size : 29;
+ /// The kind of cleanup to push: a value from the CleanupKind enumeration.
+ unsigned Kind : 3;
+
+ size_t getSize() const { return Size; }
+ CleanupKind getKind() const { return static_cast<CleanupKind>(Kind); }
+ };
/// i32s containing the indexes of the cleanup destinations.
llvm::AllocaInst *NormalCleanupDest;
@@ -376,6 +388,23 @@ public:
initFullExprCleanup();
}
+ /// \brief Queue a cleanup to be pushed after finishing the current
+ /// full-expression.
+ template <class T, class A0, class A1, class A2, class A3>
+ void pushCleanupAfterFullExpr(CleanupKind Kind, A0 a0, A1 a1, A2 a2, A3 a3) {
+ assert(!isInConditionalBranch() && "can't defer conditional cleanup");
+
+ LifetimeExtendedCleanupHeader Header = { sizeof(T), Kind };
+
+ size_t OldSize = LifetimeExtendedCleanupStack.size();
+ LifetimeExtendedCleanupStack.resize(
+ LifetimeExtendedCleanupStack.size() + sizeof(Header) + Header.Size);
+
+ char *Buffer = &LifetimeExtendedCleanupStack[OldSize];
+ new (Buffer) LifetimeExtendedCleanupHeader(Header);
+ new (Buffer + sizeof(Header)) T(a0, a1, a2, a3);
+ }
+
/// Set up the last cleaup that was pushed as a conditional
/// full-expression cleanup.
void initFullExprCleanup();
@@ -421,6 +450,7 @@ public:
/// will be executed once the scope is exited.
class RunCleanupsScope {
EHScopeStack::stable_iterator CleanupStackDepth;
+ size_t LifetimeExtendedCleanupStackSize;
bool OldDidCallStackSave;
protected:
bool PerformCleanup;
@@ -438,6 +468,8 @@ public:
: PerformCleanup(true), CGF(CGF)
{
CleanupStackDepth = CGF.EHStack.stable_begin();
+ LifetimeExtendedCleanupStackSize =
+ CGF.LifetimeExtendedCleanupStack.size();
OldDidCallStackSave = CGF.DidCallStackSave;
CGF.DidCallStackSave = false;
}
@@ -447,7 +479,8 @@ public:
~RunCleanupsScope() {
if (PerformCleanup) {
CGF.DidCallStackSave = OldDidCallStackSave;
- CGF.PopCleanupBlocks(CleanupStackDepth);
+ CGF.PopCleanupBlocks(CleanupStackDepth,
+ LifetimeExtendedCleanupStackSize);
}
}
@@ -461,7 +494,8 @@ public:
void ForceCleanup() {
assert(PerformCleanup && "Already forced cleanup");
CGF.DidCallStackSave = OldDidCallStackSave;
- CGF.PopCleanupBlocks(CleanupStackDepth);
+ CGF.PopCleanupBlocks(CleanupStackDepth,
+ LifetimeExtendedCleanupStackSize);
PerformCleanup = false;
}
};
@@ -513,10 +547,16 @@ public:
};
- /// PopCleanupBlocks - Takes the old cleanup stack size and emits
- /// the cleanup blocks that have been added.
+ /// \brief Takes the old cleanup stack size and emits the cleanup blocks
+ /// that have been added.
void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize);
+ /// \brief Takes the old cleanup stack size and emits the cleanup blocks
+ /// that have been added, then adds all lifetime-extended cleanups from
+ /// the given position to the stack.
+ void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize,
+ size_t OldLifetimeExtendedStackSize);
+
void ResolveBranchFixups(llvm::BasicBlock *Target);
/// The given basic block lies in the current EH scope, but may be a
@@ -988,6 +1028,9 @@ public:
llvm::Value *addr, QualType type);
void pushDestroy(CleanupKind kind, llvm::Value *addr, QualType type,
Destroyer *destroyer, bool useEHCleanupForArray);
+ void pushLifetimeExtendedDestroy(CleanupKind kind, llvm::Value *addr,
+ QualType type, Destroyer *destroyer,
+ bool useEHCleanupForArray);
void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,
bool useEHCleanupForArray);
llvm::Function *generateDestroyHelper(llvm::Constant *addr,
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Jun 12 15:42:33 2013
@@ -2579,6 +2579,8 @@ Sema::BuildMemberInitializer(ValueDecl *
if (MemberInit.isInvalid())
return true;
+ CheckForDanglingReferenceOrPointer(*this, Member, MemberInit.get(), IdLoc);
+
// C++11 [class.base.init]p7:
// The initialization of each base and member constitutes a
// full-expression.
@@ -2587,7 +2589,6 @@ Sema::BuildMemberInitializer(ValueDecl *
return true;
Init = MemberInit.get();
- CheckForDanglingReferenceOrPointer(*this, Member, Init, IdLoc);
}
if (DirectMember) {
Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Wed Jun 12 15:42:33 2013
@@ -5272,6 +5272,9 @@ static void performLifetimeExtension(Exp
Init = const_cast<Expr *>(
Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments));
+ if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init))
+ Init = BTE->getSubExpr();
+
if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
if (ILE->initializesStdInitializerList() || ILE->getType()->isArrayType()) {
// FIXME: If this is an InitListExpr which creates a std::initializer_list
@@ -5567,16 +5570,21 @@ InitializationSequence::Perform(Sema &S,
performLifetimeExtension(CurInit.get(), ExtendingDecl);
// Materialize the temporary into memory.
- CurInit = new (S.Context) MaterializeTemporaryExpr(
+ MaterializeTemporaryExpr *MTE = new (S.Context) MaterializeTemporaryExpr(
Entity.getType().getNonReferenceType(), CurInit.get(),
Entity.getType()->isLValueReferenceType(), ExtendingDecl);
// If we're binding to an Objective-C object that has lifetime, we
- // need cleanups.
- if (S.getLangOpts().ObjCAutoRefCount &&
- CurInit.get()->getType()->isObjCLifetimeType())
+ // need cleanups. Likewise if we're extending this temporary to automatic
+ // storage duration -- we need to register its cleanup during the
+ // full-expression's cleanups.
+ if ((S.getLangOpts().ObjCAutoRefCount &&
+ MTE->getType()->isObjCLifetimeType()) ||
+ (MTE->getStorageDuration() == SD_Automatic &&
+ MTE->getType().isDestructedType()))
S.ExprNeedsCleanups = true;
-
+
+ CurInit = S.Owned(MTE);
break;
}
Modified: cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp Wed Jun 12 15:42:33 2013
@@ -35,7 +35,7 @@ int e = V<int>::m;
// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global
// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0
-// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global
+// CHECK: @_ZGRZ8tls_dtorvE1u = private thread_local global
// CHECK: @_ZGVN1VIiE1mE = weak_odr thread_local global i64 0
Modified: cfe/trunk/test/CodeGenCXX/temporaries.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/temporaries.cpp?rev=183859&r1=183858&r2=183859&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/temporaries.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/temporaries.cpp Wed Jun 12 15:42:33 2013
@@ -584,11 +584,9 @@ namespace BindToSubobject {
// CHECK: call void @_ZN15BindToSubobject1fEv()
// CHECK: call void @_ZN15BindToSubobject1gEv()
// CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1cE)
- // FIXME: This is wrong. We should emit the call to __cxa_atexit prior to
- // calling h(), in case h() throws.
+ // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1AD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1cE to i8*), i8* @__dso_handle)
// CHECK: call {{.*}} @_ZN15BindToSubobject1hE
// CHECK: getelementptr
- // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1AD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1cE to i8*), i8* @__dso_handle)
// CHECK: store i32* {{.*}}, i32** @_ZN15BindToSubobject1cE, align 8
int &&c = (f(), (g(), A().*h()));
@@ -598,9 +596,9 @@ namespace BindToSubobject {
};
// CHECK: call void @_ZN15BindToSubobject1BC1Ev({{.*}} @_ZGRN15BindToSubobject1dE)
+ // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1BD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1dE to i8*), i8* @__dso_handle)
// CHECK: call {{.*}} @_ZN15BindToSubobject1hE
// CHECK: getelementptr {{.*}} getelementptr
- // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1BD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1dE to i8*), i8* @__dso_handle)
// CHECK: store i32* {{.*}}, i32** @_ZN15BindToSubobject1dE, align 8
int &&d = (B().a).*h();
}
@@ -611,7 +609,7 @@ namespace Bitfield {
// Do not lifetime extend the S() temporary here.
// CHECK: alloca
// CHECK: call {{.*}}memset
- // CHECK: store i32 {{.*}}, i32* @_ZGRN8Bitfield1rE, align 4
+ // CHECK: store i32 {{.*}}, i32* @_ZGRN8Bitfield1rE
// CHECK: call void @_ZN8Bitfield1SD1
// CHECK: store i32* @_ZGRN8Bitfield1rE, i32** @_ZN8Bitfield1rE, align 8
int &&r = S().a;
@@ -626,15 +624,120 @@ namespace Vector {
};
// CHECK: alloca
// CHECK: extractelement
- // CHECK: store i32 {{.*}}, i32* @_ZGRN6Vector1rE,
+ // CHECK: store i32 {{.*}}, i32* @_ZGRN6Vector1rE
// CHECK: store i32* @_ZGRN6Vector1rE, i32** @_ZN6Vector1rE,
int &&r = S().v[1];
// CHECK: alloca
// CHECK: extractelement
- // CHECK: store i32 {{.*}}, i32* @_ZGRN6Vector1sE,
+ // CHECK: store i32 {{.*}}, i32* @_ZGRN6Vector1sE
// CHECK: store i32* @_ZGRN6Vector1sE, i32** @_ZN6Vector1sE,
int &&s = S().w[1];
// FIXME PR16204: The following code leads to an assertion in Sema.
//int &&s = S().w.y;
}
+
+namespace ImplicitTemporaryCleanup {
+ struct A { A(int); ~A(); };
+ void g();
+
+ // CHECK: define void @_ZN24ImplicitTemporaryCleanup1fEv(
+ void f() {
+ // CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1AC1Ei(
+ A &&a = 0;
+
+ // CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1gEv(
+ g();
+
+ // CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1AD1Ev(
+ }
+}
+
+namespace MultipleExtension {
+ struct A { A(); ~A(); };
+ struct B { B(); ~B(); };
+ struct C { C(); ~C(); };
+ struct D { D(); ~D(); int n; C c; };
+ struct E { const A &a; B b; const C &c; ~E(); };
+
+ E &&e1 = { A(), B(), D().c };
+
+ // CHECK: call void @_ZN17MultipleExtension1AC1Ev({{.*}} @[[TEMPA:_ZGRN17MultipleExtension2e1E.*]])
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1AD1Ev {{.*}} @[[TEMPA]]
+ // CHECK: store {{.*}} @[[TEMPA]], {{.*}} getelementptr inbounds ({{.*}} @[[TEMPE:_ZGRN17MultipleExtension2e1E.*]], i32 0, i32 0)
+
+ // CHECK: call void @_ZN17MultipleExtension1BC1Ev({{.*}} getelementptr inbounds ({{.*}} @[[TEMPE]], i32 0, i32 1))
+
+ // CHECK: call void @_ZN17MultipleExtension1DC1Ev({{.*}} @[[TEMPD:_ZGRN17MultipleExtension2e1E.*]])
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1DD1Ev {{.*}} @[[TEMPD]]
+ // CHECK: store {{.*}} @[[TEMPD]], {{.*}} getelementptr inbounds ({{.*}} @[[TEMPE]], i32 0, i32 2)
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1ED1Ev {{.*}} @[[TEMPE]]
+ // CHECK: store {{.*}} @[[TEMPE]], %"struct.MultipleExtension::E"** @_ZN17MultipleExtension2e1E, align 8
+
+ E e2 = { A(), B(), D().c };
+
+ // CHECK: call void @_ZN17MultipleExtension1AC1Ev({{.*}} @[[TEMPA:_ZGRN17MultipleExtension2e2E.*]])
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1AD1Ev {{.*}} @[[TEMPA]]
+ // CHECK: store {{.*}} @[[TEMPA]], {{.*}} getelementptr inbounds ({{.*}} @[[E:_ZN17MultipleExtension2e2E]], i32 0, i32 0)
+
+ // CHECK: call void @_ZN17MultipleExtension1BC1Ev({{.*}} getelementptr inbounds ({{.*}} @[[E]], i32 0, i32 1))
+
+ // CHECK: call void @_ZN17MultipleExtension1DC1Ev({{.*}} @[[TEMPD:_ZGRN17MultipleExtension2e2E.*]])
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1DD1Ev {{.*}} @[[TEMPD]]
+ // CHECK: store {{.*}} @[[TEMPD]], {{.*}} getelementptr inbounds ({{.*}} @[[E]], i32 0, i32 2)
+ // CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1ED1Ev {{.*}} @[[E]]
+
+
+ void g();
+ // CHECK: define void @[[NS:_ZN17MultipleExtension]]1fEv(
+ void f() {
+ E &&e1 = { A(), B(), D().c };
+ // CHECK: %[[TEMPE1_A:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1:.*]], i32 0, i32 0
+ // CHECK: call void @[[NS]]1AC1Ev({{.*}} %[[TEMPA1:.*]])
+ // CHECK: store {{.*}} %[[TEMPA1]], {{.*}} %[[TEMPE1_A]]
+ // CHECK: %[[TEMPE1_B:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1]], i32 0, i32 1
+ // CHECK: call void @[[NS]]1BC1Ev({{.*}} %[[TEMPE1_B]])
+ // CHECK: %[[TEMPE1_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1]], i32 0, i32 2
+ // CHECK: call void @[[NS]]1DC1Ev({{.*}} %[[TEMPD1:.*]])
+ // CHECK: %[[TEMPD1_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPD1]], i32 0, i32 1
+ // CHECK: store {{.*}} %[[TEMPD1_C]], {{.*}} %[[TEMPE1_C]]
+ // CHECK: store {{.*}} %[[TEMPE1]], {{.*}} %[[E1:.*]]
+
+ g();
+ // CHECK: call void @[[NS]]1gEv()
+
+ E e2 = { A(), B(), D().c };
+ // CHECK: %[[TEMPE2_A:.*]] = getelementptr inbounds {{.*}} %[[E2:.*]], i32 0, i32 0
+ // CHECK: call void @[[NS]]1AC1Ev({{.*}} %[[TEMPA2:.*]])
+ // CHECK: store {{.*}} %[[TEMPA2]], {{.*}} %[[TEMPE2_A]]
+ // CHECK: %[[TEMPE2_B:.*]] = getelementptr inbounds {{.*}} %[[E2]], i32 0, i32 1
+ // CHECK: call void @[[NS]]1BC1Ev({{.*}} %[[TEMPE2_B]])
+ // CHECK: %[[TEMPE2_C:.*]] = getelementptr inbounds {{.*}} %[[E2]], i32 0, i32 2
+ // CHECK: call void @[[NS]]1DC1Ev({{.*}} %[[TEMPD2:.*]])
+ // CHECK: %[[TEMPD2_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPD2]], i32 0, i32 1
+ // CHECK: store {{.*}} %[[TEMPD2_C]], {{.*}}* %[[TEMPE2_C]]
+
+ g();
+ // CHECK: call void @[[NS]]1gEv()
+
+ // CHECK: call void @[[NS]]1ED1Ev({{.*}} %[[E2]])
+ // CHECK: call void @[[NS]]1DD1Ev({{.*}} %[[TEMPD2]])
+ // CHECK: call void @[[NS]]1AD1Ev({{.*}} %[[TEMPA2]])
+ // CHECK: call void @[[NS]]1ED1Ev({{.*}} %[[TEMPE1]])
+ // CHECK: call void @[[NS]]1DD1Ev({{.*}} %[[TEMPD1]])
+ // CHECK: call void @[[NS]]1AD1Ev({{.*}} %[[TEMPA1]])
+ }
+}
+
+namespace Ctor {
+ struct A { A(); ~A(); };
+ void f();
+ struct B {
+ A &&a;
+ B() : a{} { f(); }
+ } b;
+ // CHECK: define {{.*}}void @_ZN4Ctor1BC1Ev(
+ // CHECK: call void @_ZN4Ctor1AC1Ev(
+ // CHECK: call void @_ZN4Ctor1fEv(
+ // CHECK: call void @_ZN4Ctor1AD1Ev(
+}
More information about the cfe-commits
mailing list