Index: cfe/trunk/lib/CodeGen/CGCleanup.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCleanup.cpp +++ cfe/trunk/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: cfe/trunk/lib/CodeGen/CGDecl.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGDecl.cpp +++ cfe/trunk/lib/CodeGen/CGDecl.cpp @@ -529,19 +529,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 @@ -1406,13 +1393,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: cfe/trunk/lib/CodeGen/CGExpr.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExpr.cpp +++ cfe/trunk/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" @@ -423,6 +424,23 @@ EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); } } else { + switch (M->getStorageDuration()) { + case SD_Automatic: + case SD_FullExpression: + if (auto *Size = EmitLifetimeStart( + CGM.getDataLayout().getTypeAllocSize(Object.getElementType()), + Object.getPointer())) { + if (M->getStorageDuration() == SD_Automatic) + pushCleanupAfterFullExpr(NormalEHLifetimeMarker, + Object, Size); + else + pushFullExprCleanup(NormalEHLifetimeMarker, Object, + Size); + } + break; + default: + break; + } EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); } pushTemporaryCleanup(*this, M, E, Object); Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -302,6 +302,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: cfe/trunk/lib/CodeGen/EHScopeStack.h =================================================================== --- cfe/trunk/lib/CodeGen/EHScopeStack.h +++ cfe/trunk/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: cfe/trunk/test/CodeGen/temporary-lifetime-exceptions.cpp =================================================================== --- cfe/trunk/test/CodeGen/temporary-lifetime-exceptions.cpp +++ cfe/trunk/test/CodeGen/temporary-lifetime-exceptions.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 %s -fexceptions -fcxx-exceptions -std=c++11 -O1 -triple x86_64 -emit-llvm -o - | FileCheck %s + +// lifetime.end should be invoked even if the destructor doesn't run due to an +// exception thrown from previous ctor call. + +struct A { A(); ~A(); }; +A Baz(const A&); + +void Test1() { + // CHECK-LABEL: @_Z5Test1v( + // CHECK: getelementptr + // CHECK-NEXT: call void @llvm.lifetime.start(i64 1, i8* [[TMP:[^ ]+]]) + // CHECK-NEXT: getelementptr + // CHECK-NEXT: call void @llvm.lifetime.start(i64 1, i8* [[TMP1:[^ ]+]]) + + // Normal exit + // CHECK: call void @llvm.lifetime.end(i64 1, i8* [[TMP1]]) + // CHECK-NEXT: call void @llvm.lifetime.end(i64 1, i8* [[TMP]]) + + // Exception exit + // CHECK: call void @llvm.lifetime.end(i64 1, i8* [[TMP1]]) + // CHECK-NEXT: call void @llvm.lifetime.end(i64 1, i8* [[TMP]]) + Baz(Baz(A())); +} Index: cfe/trunk/test/CodeGen/temporary-lifetime.cpp =================================================================== --- cfe/trunk/test/CodeGen/temporary-lifetime.cpp +++ cfe/trunk/test/CodeGen/temporary-lifetime.cpp @@ -0,0 +1,165 @@ +// 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; +} + +void Test6() { + // CHECK-DTOR-LABEL: Test6 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIiEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]]) + // CHECK-DTOR: call void @llvm.lifetime.start(i64 {{[0-9]+}}, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call i32 @_Z3BazIiET_v() + // CHECK-DTOR: store + // CHECK-DTOR: call void @_Z3FooIiEvOT_ + // CHECK-DTOR: call void @llvm.lifetime.end(i64 {{[0-9]+}}, i8* %[[ADDR]]) + // CHECK-DTOR: } + Foo(Baz()); + Foo(Baz()); +} + +void Test7() { + // CHECK-DTOR-LABEL: Test7 + // CHECK-DTOR: call void @llvm.lifetime.start(i64 1024, i8* %[[ADDR:[0-9]+]]) + // CHECK-DTOR: call void @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]]) + // 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 @_Z3BazI1AET_v({{.*}} %[[SLOT:[^ ]+]]) + // CHECK-DTOR: call void @_Z3FooI1AEvOT_({{.*}} %[[SLOT]]) + // CHECK-DTOR: call void @_ZN1AD1Ev(%struct.A* nonnull %[[SLOT]]) + // CHECK-DTOR: call void @llvm.lifetime.end(i64 1024, i8* %[[ADDR]]) + // CHECK-DTOR: } + Foo(Baz()); + Foo(Baz()); +} Index: cfe/trunk/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ cfe/trunk/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 %s -// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-LIFETIME %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O0 %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-O3 -check-prefix WIN32-LIFETIME %s struct A { A(); @@ -95,40 +95,78 @@ return (cond ? TakesTwo((TakeRef(A()), A()), (TakeRef(A()), A())) : CouldThrow()); } -// WIN32-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { -// WIN32: alloca i1 -// WIN32: %[[arg1_cond:.*]] = alloca i1 +// WIN32-O0-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { +// WIN32-O0: alloca i1 +// WIN32-O0: %[[arg1_cond:.*]] = alloca i1 // Start all four cleanups as deactivated. -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: store i1 false -// WIN32: br i1 +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: store i1 false +// WIN32-O0: br i1 // True condition. -// WIN32: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z" -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true, i1* %[[arg1_cond]] -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: invoke void @"\01?TakeRef@@YAXABUA@@@Z" -// WIN32: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" -// WIN32: store i1 true -// WIN32: store i1 false, i1* %[[arg1_cond]] -// WIN32: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" +// WIN32-O0: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true, i1* %[[arg1_cond]] +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O0: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O0: store i1 true +// WIN32-O0: store i1 false, i1* %[[arg1_cond]] +// WIN32-O0: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" // False condition. -// WIN32: invoke i32 @"\01?CouldThrow@@YAHXZ"() +// WIN32-O0: invoke i32 @"\01?CouldThrow@@YAHXZ"() // Two normal cleanups for TakeRef args. -// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) -// WIN32-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" -// WIN32: ret i32 +// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O0-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" +// WIN32-O0: ret i32 // // Somewhere in the landing pad soup, we conditionally destroy arg1. -// WIN32: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] -// WIN32: br i1 %[[isactive]] -// WIN32: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) -// WIN32: } +// WIN32-O0: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] +// WIN32-O0: br i1 %[[isactive]] +// WIN32-O0: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O0: } + +// WIN32-O3-LABEL: define i32 @"\01?HasConditionalDeactivatedCleanups@@YAH_N@Z"{{.*}} { +// WIN32-O3: alloca i1 +// WIN32-O3: alloca i1 +// WIN32-O3: %[[arg1_cond:.*]] = alloca i1 +// Start all four cleanups as deactivated. +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: store i1 false +// WIN32-O3: br i1 +// True condition. +// WIN32-O3: call x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true, i1* %[[arg1_cond]] +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: invoke void @"\01?TakeRef@@YAXABUA@@@Z" +// WIN32-O3: invoke x86_thiscallcc %struct.A* @"\01??0A@@QAE@XZ" +// WIN32-O3: store i1 true +// WIN32-O3: store i1 false, i1* %[[arg1_cond]] +// WIN32-O3: invoke i32 @"\01?TakesTwo@@YAHUA@@0@Z" +// False condition. +// WIN32-O3: invoke i32 @"\01?CouldThrow@@YAHXZ"() +// Two normal cleanups for TakeRef args. +// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O3-NOT: invoke x86_thiscallcc void @"\01??1A@@QAE@XZ" +// WIN32-O3: ret i32 +// +// Somewhere in the landing pad soup, we conditionally destroy arg1. +// WIN32-O3: %[[isactive:.*]] = load i1, i1* %[[arg1_cond]] +// WIN32-O3: br i1 %[[isactive]] +// WIN32-O3: call x86_thiscallcc void @"\01??1A@@QAE@XZ"({{.*}}) +// WIN32-O3: } namespace crash_on_partial_destroy { struct A {