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