r183721 - Rework IR emission for lifetime-extended temporaries. Instead of trying to walk
Richard Smith
richard at metafoo.co.uk
Tue Jun 11 12:15:41 PDT 2013
Thanks, I see the problem. Reverted in r183776.
On Tue, Jun 11, 2013 at 5:41 AM, Manuel Klimek <klimek at google.com> wrote:
> This change caused a regression with range-based for loops:
> $ cat t2.cc
> #include <map>
> #include <string>
>
> struct E {
> std::string x;
> };
>
> int main() {
> std::map<int, E> m;
> m[42].x = "grrrrrr";
>
> for (const std::pair<int, E>& e : m) {
> }
> }
>
> Valgrind finds:
> ==25512== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
> ==25512== at 0x4C2B1C7: operator new(unsigned long) (in
> /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==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)
> ==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)
> ==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)
> ==25512== by 0x400D8A: main (in /tmp/t)
>
> This does not leak before the change. Replacing the pair type so there is
> no temporary fixes the issue.
>
>
> On Tue, Jun 11, 2013 at 4:41 AM, Richard Smith <richard-llvm at metafoo.co.uk
> > wrote:
>
>> Author: rsmith
>> Date: Mon Jun 10 21:41:00 2013
>> New Revision: 183721
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=183721&view=rev
>> Log:
>> 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.h
>> 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGCleanup.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGCleanup.cpp Mon Jun 10 21:41:00 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGCleanup.h (original)
>> +++ cfe/trunk/lib/CodeGen/CGCleanup.h Mon Jun 10 21:41:00 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGDecl.cpp Mon Jun 10 21:41:00 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGExpr.cpp Mon Jun 10 21:41:00 2013
>> @@ -171,128 +171,186 @@ 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");
>> + }
>> +}
>>
>> +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 +360,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 +422,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.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
>> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Jun 10 21:41:00 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/SemaInit.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Sema/SemaInit.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaInit.cpp Mon Jun 10 21:41:00 2013
>> @@ -5214,6 +5214,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
>>
>> 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp (original)
>> +++ cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp Mon Jun 10 21:41:00
>> 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=183721&r1=183720&r2=183721&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/temporaries.cpp (original)
>> +++ cfe/trunk/test/CodeGenCXX/temporaries.cpp Mon Jun 10 21:41:00 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,91 @@ 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 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]])
>> + }
>> +}
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130611/6689e0a5/attachment.html>
More information about the cfe-commits
mailing list