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