Index: lib/CodeGen/CGCleanup.cpp =================================================================== --- lib/CodeGen/CGCleanup.cpp +++ lib/CodeGen/CGCleanup.cpp @@ -188,6 +188,7 @@ bool IsNormalCleanup = Kind & NormalCleanup; bool IsEHCleanup = Kind & EHCleanup; bool IsActive = !(Kind & InactiveCleanup); + bool IsLifetimeMarker = Kind & LifetimeMarker; EHCleanupScope *Scope = new (Buffer) EHCleanupScope(IsNormalCleanup, IsEHCleanup, @@ -200,6 +201,8 @@ InnermostNormalCleanup = stable_begin(); if (IsEHCleanup) InnermostEHScope = stable_begin(); + if (IsLifetimeMarker) + Scope->setLifetimeMarker(); return Scope->getCleanupBuffer(); } Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -521,19 +521,6 @@ CGF.EmitCall(FnInfo, CleanupFn, ReturnValueSlot(), Args); } }; - - /// A cleanup to call @llvm.lifetime.end. - class CallLifetimeEnd final : public EHScopeStack::Cleanup { - llvm::Value *Addr; - llvm::Value *Size; - public: - CallLifetimeEnd(Address addr, llvm::Value *size) - : Addr(addr.getPointer()), Size(size) {} - - void Emit(CodeGenFunction &CGF, Flags flags) override { - CGF.EmitLifetimeEnd(Size, Addr); - } - }; } // end anonymous namespace /// EmitAutoVarWithLifetime - Does the setup required for an automatic @@ -1283,6 +1270,14 @@ QualType type = D->getType(); if (type->isReferenceType()) { + // It is a lifetime-extension. Ensure that there is a RunCleanupsScope for + // the delayed CallLifetimeEnd in EmitMaterializeTemporaryExpr. + Optional Cleanup; + // Create this scope only when there is a MaterializeTemporaryExpr that is + // not guarded by an ExprWithCleanups. + if (dyn_cast(init)) + Cleanup.emplace(*this); + RValue rvalue = EmitReferenceBindingToExpr(init); if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast(D)); @@ -1387,13 +1382,10 @@ // Make sure we call @llvm.lifetime.end. This needs to happen // *last*, so the cleanup needs to be pushed *first*. - if (emission.useLifetimeMarkers()) { - EHStack.pushCleanup(NormalAndEHCleanup, + if (emission.useLifetimeMarkers()) + EHStack.pushCleanup(NormalEHLifetimeMarker, emission.getAllocatedAddress(), emission.getSizeForLifetimeMarkers()); - EHCleanupScope &cleanup = cast(*EHStack.begin()); - cleanup.setLifetimeMarker(); - } // Check the type for a cleanup. if (QualType::DestructionKind dtorKind = D.getType().isDestructedType()) Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenFunction.h" #include "CGCXXABI.h" #include "CGCall.h" +#include "CGCleanup.h" #include "CGDebugInfo.h" #include "CGObjCRuntime.h" #include "CGOpenMPRuntime.h" #include "CGRecordLayout.h" +#include "CodeGenFunction.h" #include "CodeGenModule.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -415,7 +416,19 @@ EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); } } else { + llvm::Value *Size = nullptr; + // Only handle lifetime extended objects. + // TODO: Handle normal temporaries. + if (M->getStorageDuration() == SD_Automatic) + Size = EmitLifetimeStart( + CGM.getDataLayout().getTypeAllocSize(Object.getElementType()), + Object.getPointer()); + EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); + + if (Size) + pushCleanupAfterFullExpr(NormalEHLifetimeMarker, Object, + Size); } pushTemporaryCleanup(*this, M, E, Object); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -300,6 +300,19 @@ llvm::Instruction *CurrentFuncletPad = nullptr; + class CallLifetimeEnd final : public EHScopeStack::Cleanup { + llvm::Value *Addr; + llvm::Value *Size; + + public: + CallLifetimeEnd(Address addr, llvm::Value *size) + : Addr(addr.getPointer()), Size(size) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitLifetimeEnd(Size, Addr); + } + }; + /// Header for data within LifetimeExtendedCleanupStack. struct LifetimeExtendedCleanupHeader { /// The size of the following cleanup object. Index: lib/CodeGen/EHScopeStack.h =================================================================== --- lib/CodeGen/EHScopeStack.h +++ lib/CodeGen/EHScopeStack.h @@ -89,7 +89,10 @@ InactiveCleanup = 0x4, InactiveEHCleanup = EHCleanup | InactiveCleanup, InactiveNormalCleanup = NormalCleanup | InactiveCleanup, - InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup + InactiveNormalAndEHCleanup = NormalAndEHCleanup | InactiveCleanup, + + LifetimeMarker = 0x8, + NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup, }; /// A stack of scopes which respond to exceptions, including cleanups Index: test/CodeGen/lifetime-extended-temporaries.cpp =================================================================== --- /dev/null +++ test/CodeGen/lifetime-extended-temporaries.cpp @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 %s -std=c++11 -O1 -DWITH_DTOR -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-DTOR %s +// RUN: %clang_cc1 %s -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck -check-prefix=CHECK-NO-DTOR %s + +struct A { + A(); +#ifdef WITH_DTOR + ~A(); +#endif + char a[1024]; + operator bool() const; +}; + +template +void Foo(T &&); + +template +void Bar(T &&); + +template +T Baz(); + +void Test1() { + // CHECK-DTOR-LABEL: Test1 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: } + + // CHECK-NO-DTOR-LABEL: Test1 + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-NO-DTOR: } + { + const A &a = A{}; + Foo(a); + } + { + const A &a = A{}; + Foo(a); + } +} + +void Test2() { + // CHECK-DTOR-LABEL: Test2 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]]) + // CHECK-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR2]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[VAR1]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]]) + // CHECK-DTOR: } + + // CHECK-NO-DTOR-LABEL: Test2 + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR1:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR1:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR2:[0-9]+]]) + // CHECK-NO-DTOR: call void @_ZN1AC1Ev(%struct.A* nonnull %[[VAR2:[^ ]+]]) + // CHECK-NO-DTOR: call void @_Z3FooIRK1AEvOT_ + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR2]]) + // CHECK-NO-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR1]]) + // CHECK-NO-DTOR: } + const A &a = A{}; + Foo(a); + const A &b = A{}; + Foo(b); +} + +void Test3() { + // CHECK-DTOR-LABEL: Test3 + // CHECK-DTOR: entry: + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: if.then: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: cleanup{{.*}}: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: cleanup{{.*}}: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: } + const A &a = A{}; + if (const A &b = A(a)) { + Foo(b); + return; + } + Bar(a); +} + +void Test4() { + // CHECK-DTOR-LABEL: Test4 + // CHECK-DTOR: entry: + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: for.cond.cleanup: + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: for.body: + // CHECK-DTOR: } + for (const A &a = A{}; a;) { + Foo(a); + } +} + +int Test5() { + // CHECK-DTOR-LABEL: Test5 + // CHECK-DTOR: call void @llvm.lifetime.start + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIRKiEvOT_ + // CHECK-DTOR: load + // CHECK-DTOR: call void @llvm.lifetime.end + // CHECK-DTOR: } + const int &a = Baz(); + Foo(a); + return a; +}