r183721 - Rework IR emission for lifetime-extended temporaries. Instead of trying to walk

Manuel Klimek klimek at google.com
Tue Jun 11 05:41:48 PDT 2013


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/8a9ab0c1/attachment.html>


More information about the cfe-commits mailing list