Emit lifetime.start / lifetime.end markers for unnamed temporary objects.
authorArnaud A. de Grandmaison <arnaud.degrandmaison@arm.com>
Thu, 2 Oct 2014 12:19:51 +0000 (12:19 +0000)
committerArnaud A. de Grandmaison <arnaud.degrandmaison@arm.com>
Thu, 2 Oct 2014 12:19:51 +0000 (12:19 +0000)
This will give more information to the optimizers so that they can reuse stack slots
and reduce stack usage.

llvm-svn: 218865

clang/lib/CodeGen/CGCleanup.cpp
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/test/CodeGenCXX/unnamed-object-lifetime.cpp [new file with mode: 0644]

index ed9f96d..911734a 100644 (file)
@@ -387,14 +387,9 @@ void CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old) {
   }
 }
 
-/// Pops cleanup blocks until the given savepoint is reached, then add the
-/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
+/// Move our deferred cleanups onto the EH stack.
 void
-CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old,
-                                  size_t OldLifetimeExtendedSize) {
-  PopCleanupBlocks(Old);
-
-  // Move our deferred cleanups onto the EH stack.
+CodeGenFunction::MoveDeferedCleanups(size_t OldLifetimeExtendedSize) {
   for (size_t I = OldLifetimeExtendedSize,
               E = LifetimeExtendedCleanupStack.size(); I != E; /**/) {
     // Alignment should be guaranteed by the vptrs in the individual cleanups.
@@ -414,6 +409,17 @@ CodeGenFunction::PopCleanupBlocks(EHScopeStack::stable_iterator Old,
   LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize);
 }
 
+/// 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.
+  MoveDeferedCleanups(OldLifetimeExtendedSize);
+}
+
 static llvm::BasicBlock *CreateNormalEntry(CodeGenFunction &CGF,
                                            EHCleanupScope &Scope) {
   assert(Scope.isNormalCleanup());
index a9c2da8..7f6b296 100644 (file)
@@ -476,12 +476,10 @@ namespace {
       : Addr(addr), Size(size) {}
 
     void Emit(CodeGenFunction &CGF, Flags flags) override {
-      llvm::Value *castAddr = CGF.Builder.CreateBitCast(Addr, CGF.Int8PtrTy);
-      CGF.Builder.CreateCall2(CGF.CGM.getLLVMLifetimeEndFn(),
-                              Size, castAddr)
-        ->setDoesNotThrow();
+      CGF.EmitLifetimeEnd(Size, Addr);
     }
   };
+
 }
 
 /// EmitAutoVarWithLifetime - Does the setup required for an automatic
@@ -800,8 +798,7 @@ static bool shouldUseMemSetPlusStoresToInitialize(llvm::Constant *Init,
 }
 
 /// Should we use the LLVM lifetime intrinsics for the given local variable?
-static bool shouldUseLifetimeMarkers(CodeGenFunction &CGF, const VarDecl &D,
-                                     unsigned Size) {
+static bool shouldUseLifetimeMarkers(CodeGenFunction &CGF, uint64_t Size) {
   // For now, only in optimized builds.
   if (CGF.CGM.getCodeGenOpts().OptimizationLevel == 0)
     return false;
@@ -813,7 +810,6 @@ static bool shouldUseLifetimeMarkers(CodeGenFunction &CGF, const VarDecl &D,
   return Size > SizeThreshold;
 }
 
-
 /// EmitAutoVarDecl - Emit code and set up an entry in LocalDeclMap for a
 /// variable declaration with auto, register, or no storage class specifier.
 /// These turn into simple stack objects, or GlobalValues depending on target.
@@ -823,6 +819,27 @@ void CodeGenFunction::EmitAutoVarDecl(const VarDecl &D) {
   EmitAutoVarCleanups(emission);
 }
 
+/// Emit a lifetime.begin marker if some criteria are satisfied.
+/// \return a pointer to the temporary size Value if a marker was emitted, null
+/// otherwise
+llvm::Value *CodeGenFunction::EmitLifetimeStart(uint64_t Size,
+                                                llvm::Value *Addr) {
+  if (!shouldUseLifetimeMarkers(*this, Size))
+    return nullptr;
+
+  llvm::Value *SizeV = llvm::ConstantInt::get(Int64Ty, Size);
+  llvm::Value *CastAddr = Builder.CreateBitCast(Addr, Int8PtrTy);
+  Builder.CreateCall2(CGM.getLLVMLifetimeStartFn(), SizeV, CastAddr)
+      ->setDoesNotThrow();
+  return SizeV;
+}
+
+void CodeGenFunction::EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr) {
+  llvm::Value *CastAddr = Builder.CreateBitCast(Addr, Int8PtrTy);
+  Builder.CreateCall2(CGM.getLLVMLifetimeEndFn(), Size, CastAddr)
+      ->setDoesNotThrow();
+}
+
 /// EmitAutoVarAlloca - Emit the alloca and debug information for a
 /// local variable.  Does not emit initialization or destruction.
 CodeGenFunction::AutoVarEmission
@@ -918,13 +935,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
       // Emit a lifetime intrinsic if meaningful.  There's no point
       // in doing this if we don't have a valid insertion point (?).
       uint64_t size = CGM.getDataLayout().getTypeAllocSize(LTy);
-      if (HaveInsertPoint() && shouldUseLifetimeMarkers(*this, D, size)) {
-        llvm::Value *sizeV = llvm::ConstantInt::get(Int64Ty, size);
-
-        emission.SizeForLifetimeMarkers = sizeV;
-        llvm::Value *castAddr = Builder.CreateBitCast(Alloc, Int8PtrTy);
-        Builder.CreateCall2(CGM.getLLVMLifetimeStartFn(), sizeV, castAddr)
-          ->setDoesNotThrow();
+      if (HaveInsertPoint() && EmitLifetimeStart(size, Alloc)) {
+        emission.SizeForLifetimeMarkers = llvm::ConstantInt::get(Int64Ty, size);
       } else {
         assert(!emission.useLifetimeMarkers());
       }
@@ -1366,6 +1378,32 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(
       cleanupKind, addr, type, destroyer, useEHCleanupForArray);
 }
 
+void
+CodeGenFunction::pushLifetimeEndMarker(StorageDuration SD,
+                                       llvm::Value *ReferenceTemporary,
+                                       llvm::Value *SizeForLifeTimeMarkers) {
+  // SizeForLifeTimeMarkers is null in case no corresponding
+  // @llvm.lifetime.start was emitted: there is nothing to do then.
+  if (!SizeForLifeTimeMarkers)
+    return;
+
+  switch (SD) {
+  case SD_FullExpression:
+    pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ReferenceTemporary,
+                                         SizeForLifeTimeMarkers);
+    return;
+  case SD_Automatic:
+    EHStack.pushCleanup<CallLifetimeEnd>(static_cast<CleanupKind>(EHCleanup),
+                                         ReferenceTemporary,
+                                         SizeForLifeTimeMarkers);
+    pushCleanupAfterFullExpr<CallLifetimeEnd>(
+        NormalAndEHCleanup, ReferenceTemporary, SizeForLifeTimeMarkers);
+    return;
+  default:
+    llvm_unreachable("unexpected storage duration for Lifetime markers");
+  }
+}
+
 /// emitDestroy - Immediately perform the destruction of the given
 /// object.
 ///
index 4fe3754..7b26009 100644 (file)
@@ -173,9 +173,10 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E,
   llvm_unreachable("bad evaluation kind");
 }
 
-static void
-pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
-                     const Expr *E, llvm::Value *ReferenceTemporary) {
+static void pushTemporaryCleanup(CodeGenFunction &CGF,
+                                 const MaterializeTemporaryExpr *M,
+                                 const Expr *E, llvm::Value *ReferenceTemporary,
+                                 llvm::Value *SizeForLifeTimeMarkers) {
   // 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.
@@ -242,6 +243,10 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
     }
   }
 
+  // Call @llvm.lifetime.end marker for the temporary.
+  CGF.pushLifetimeEndMarker(M->getStorageDuration(), ReferenceTemporary,
+                            SizeForLifeTimeMarkers);
+
   CXXDestructorDecl *ReferenceTemporaryDtor = nullptr;
   if (const RecordType *RT =
           E->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
@@ -296,11 +301,18 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M,
 
 static llvm::Value *
 createReferenceTemporary(CodeGenFunction &CGF,
-                         const MaterializeTemporaryExpr *M, const Expr *Inner) {
+                         const MaterializeTemporaryExpr *M, const Expr *Inner,
+                         llvm::Value *&SizeForLifeTimeMarkers) {
+  SizeForLifeTimeMarkers = nullptr;
   switch (M->getStorageDuration()) {
   case SD_FullExpression:
-  case SD_Automatic:
-    return CGF.CreateMemTemp(Inner->getType(), "ref.tmp");
+  case SD_Automatic: {
+    llvm::Value *RefTemp = CGF.CreateMemTemp(Inner->getType(), "ref.tmp");
+    uint64_t TempSize = CGF.CGM.getDataLayout().getTypeStoreSize(
+        CGF.ConvertTypeForMem(Inner->getType()));
+    SizeForLifeTimeMarkers = CGF.EmitLifetimeStart(TempSize, RefTemp);
+    return RefTemp;
+  }
 
   case SD_Thread:
   case SD_Static:
@@ -321,7 +333,8 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
       M->getType().getObjCLifetime() != Qualifiers::OCL_None &&
       M->getType().getObjCLifetime() != Qualifiers::OCL_ExplicitNone) {
     // FIXME: Fold this into the general case below.
-    llvm::Value *Object = createReferenceTemporary(*this, M, E);
+    llvm::Value *ObjectSize;
+    llvm::Value *Object = createReferenceTemporary(*this, M, E, ObjectSize);
     LValue RefTempDst = MakeAddrLValue(Object, M->getType());
 
     if (auto *Var = dyn_cast<llvm::GlobalVariable>(Object)) {
@@ -333,7 +346,7 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
 
     EmitScalarInit(E, M->getExtendingDecl(), RefTempDst, false);
 
-    pushTemporaryCleanup(*this, M, E, Object);
+    pushTemporaryCleanup(*this, M, E, Object, ObjectSize);
     return RefTempDst;
   }
 
@@ -351,8 +364,10 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
     }
   }
 
-  // Create and initialize the reference temporary.
-  llvm::Value *Object = createReferenceTemporary(*this, M, E);
+  // Create and initialize the reference temporary and get the temporary size
+  llvm::Value *ObjectSize;
+  llvm::Value *Object = createReferenceTemporary(*this, M, E, ObjectSize);
+
   if (auto *Var = dyn_cast<llvm::GlobalVariable>(Object)) {
     // If the temporary is a global and has a constant initializer, we may
     // have already initialized it.
@@ -363,7 +378,8 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
   } else {
     EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
   }
-  pushTemporaryCleanup(*this, M, E, Object);
+
+  pushTemporaryCleanup(*this, M, E, Object, ObjectSize);
 
   // Perform derived-to-base casts and/or field accesses, to get from the
   // temporary object we created (and, potentially, for which we extended
index 9f35918..9361771 100644 (file)
@@ -229,6 +229,11 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
       DI->EmitLocation(Builder, EndLoc);
   }
 
+  // Some top level lifetime extended variables may still need
+  // to have their cleanups called.
+  if (!LifetimeExtendedCleanupStack.empty())
+    MoveDeferedCleanups(0);
+
   // Pop any cleanups that might have been associated with the
   // parameters.  Do this in whatever block we're currently in; it's
   // important to do this before we enter the return block or return
index 2709a36..a4171a0 100644 (file)
@@ -444,6 +444,23 @@ public:
     new (Buffer + sizeof(Header)) T(a0, a1, a2, a3);
   }
 
+  /// \brief Queue a cleanup to be pushed after finishing the current
+  /// full-expression.
+  template <class T, class A0, class A1>
+  void pushCleanupAfterFullExpr(CleanupKind Kind, A0 a0, A1 a1) {
+    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);
+  }
+
   /// Set up the last cleaup that was pushed as a conditional
   /// full-expression cleanup.
   void initFullExprCleanup();
@@ -596,6 +613,10 @@ public:
   void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize,
                         size_t OldLifetimeExtendedStackSize);
 
+  /// \brief Moves deferred cleanups from lifetime-extended variables from
+  /// the given position on top of the stack
+  void MoveDeferedCleanups(size_t OldLifetimeExtendedSize);
+
   void ResolveBranchFixups(llvm::BasicBlock *Target);
 
   /// The given basic block lies in the current EH scope, but may be a
@@ -1112,6 +1133,9 @@ public:
   void pushLifetimeExtendedDestroy(CleanupKind kind, llvm::Value *addr,
                                    QualType type, Destroyer *destroyer,
                                    bool useEHCleanupForArray);
+  void pushLifetimeEndMarker(StorageDuration SD,
+                             llvm::Value *ReferenceTemporary,
+                             llvm::Value *SizeForLifeTimeMarkers);
   void pushStackRestore(CleanupKind kind, llvm::Value *SPMem);
   void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,
                    bool useEHCleanupForArray);
@@ -1715,6 +1739,9 @@ public:
   void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType,
                         llvm::Value *Ptr);
 
+  llvm::Value *EmitLifetimeStart(uint64_t Size, llvm::Value *Addr);
+  void EmitLifetimeEnd(llvm::Value *Size, llvm::Value *Addr);
+
   llvm::Value *EmitCXXNewExpr(const CXXNewExpr *E);
   void EmitCXXDeleteExpr(const CXXDeleteExpr *E);
 
diff --git a/clang/test/CodeGenCXX/unnamed-object-lifetime.cpp b/clang/test/CodeGenCXX/unnamed-object-lifetime.cpp
new file mode 100644 (file)
index 0000000..22a64bc
--- /dev/null
@@ -0,0 +1,290 @@
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -O1 -fcxx-exceptions -fexceptions -o - %s | FileCheck --check-prefix=CHECK-EH %s
+
+// Test lifetime marker generation for unnamed temporary objects.
+
+struct X {
+  X();
+  ~X();
+  char t[33]; // make the class big enough so that lifetime markers get inserted
+};
+
+extern void useX(const X &);
+
+// CHECK-LABEL: define void @_Z6simplev
+// CHECK-EH-LABEL: define void @_Z6simplev
+void simple() {
+  // CHECK: [[ALLOCA:%.*]] = alloca %struct.X
+  // CHECK: [[PTR:%.*]] = getelementptr inbounds %struct.X* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[PTR]])
+  // CHECK-NEXT: call void @_ZN1XC1Ev
+  // CHECK-NEXT: call void @_Z4useXRK1X
+  // CHECK-NEXT: call void @_ZN1XD1Ev
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  //
+  // CHECK-EH: [[ALLOCA:%.*]] = alloca %struct.X
+  // CHECK-EH: [[PTR:%.*]] = getelementptr inbounds %struct.X* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[PTR]])
+  // CHECK-EH-NEXT: call void @_ZN1XC1Ev
+  // CHECK-EH: invoke void @_Z4useXRK1X
+  // CHECK-EH: invoke void @_ZN1XD1Ev
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  useX(X());
+}
+
+// Same as above, but with a sub-scope
+// CHECK-LABEL: define void @_Z6simpleb
+// CHECK-EH-LABEL: define void @_Z6simpleb
+void simple(bool b) {
+  // CHECK: [[ALLOCA:%.*]] = alloca %struct.X
+  // CHECK: br i1 %b
+  // CHECK: [[PTR:%.*]] = getelementptr inbounds %struct.X* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[PTR]])
+  // CHECK-NEXT: call void @_ZN1XC1Ev
+  // CHECK-NEXT: call void @_Z4useXRK1X
+  // CHECK-NEXT: call void @_ZN1XD1Ev
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  //
+  // CHECK-EH: [[ALLOCA:%.*]] = alloca %struct.X
+  // CHECK-EH: br i1 %b
+  // CHECK-EH: [[PTR:%.*]] = getelementptr inbounds %struct.X* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[PTR]])
+  // CHECK-EH-NEXT: call void @_ZN1XC1Ev
+  // CHECK-EH: invoke void @_Z4useXRK1X
+  // CHECK-EH: invoke void @_ZN1XD1Ev
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[PTR]])
+  if (b) {
+    useX(X());
+  }
+}
+
+struct Y {
+  Y(){}
+  ~Y(){}
+  char t[34]; // make the class big enough so that lifetime markers get inserted
+};
+
+extern void useY(const Y &);
+
+// Check lifetime markers are inserted, despite Y's trivial constructor & destructor
+// CHECK-LABEL: define void @_Z7trivialv
+// CHECK-EH-LABEL: define void @_Z7trivialv
+void trivial() {
+  // CHECK: [[ALLOCA:%.*]] = alloca %struct.Y
+  // CHECK: [[PTR:%.*]] = getelementptr inbounds %struct.Y* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 34, i8* [[PTR]])
+  // CHECK-NEXT: call void @_Z4useYRK1Y
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  //
+  // CHECK-EH: [[ALLOCA:%.*]] = alloca %struct.Y
+  // CHECK-EH: [[PTR:%.*]] = getelementptr inbounds %struct.Y* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 34, i8* [[PTR]])
+  // CHECK-EH-NEXT: invoke void @_Z4useYRK1Y
+  // CHECK-EH: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  useY(Y());
+}
+
+// Same as above, but with a sub-scope
+// CHECK-LABEL: define void @_Z7trivialb
+// CHECK-EH-LABEL: define void @_Z7trivialb
+void trivial(bool b) {
+  // CHECK: [[ALLOCA:%.*]] = alloca %struct.Y
+  // CHECK: br i1 %b
+  // CHECK: [[PTR:%.*]] = getelementptr inbounds %struct.Y* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 34, i8* [[PTR]])
+  // CHECK-NEXT: call void @_Z4useYRK1Y
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  //
+  // CHECK-EH: [[ALLOCA:%.*]] = alloca %struct.Y
+  // CHECK-EH: br i1 %b
+  // CHECK-EH: [[PTR:%.*]] = getelementptr inbounds %struct.Y* [[ALLOCA]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 34, i8* [[PTR]])
+  // CHECK-EH-NEXT: invoke void @_Z4useYRK1Y
+  // CHECK-EH: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 34, i8* [[PTR]])
+  if (b) {
+    useY(Y());
+  }
+}
+
+struct Z {
+  Z();
+  ~Z();
+  char t;
+};
+
+extern void useZ(const Z &);
+
+// Check lifetime markers are not inserted if the unnamed object is too small
+// CHECK-LABEL: define void @_Z8tooSmallv
+// CHECK-EH-LABEL: define void @_Z8tooSmallv
+void tooSmall() {
+  // CHECK-NOT: call void @llvm.lifetime.start
+  // CHECK: call void @_Z4useZRK1Z
+  // CHECK-NOT: call void @llvm.lifetime.end
+  // CHECK: ret
+  //
+  // CHECK-EH-NOT: call void @llvm.lifetime.start
+  // CHECK-EH: invoke void @_Z4useZRK1Z
+  // CHECK-EH-NOT: call void @llvm.lifetime.end
+  // CHECK-EH: ret
+  useZ(Z());
+}
+
+// Check the lifetime are inserted at the right place in their respective scope
+// CHECK-LABEL: define void @_Z6scopesv
+// CHECK-EH-LABEL: define void @_Z6scopesv
+void scopes() {
+  // CHECK: alloca %struct
+  // CHECK: alloca %struct
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[X:%.*]])
+  // CHECK: call void @llvm.lifetime.end(i64 33, i8* [[X]])
+  // CHECK: call void @llvm.lifetime.start(i64 34, i8* [[Y:%.*]])
+  // CHECK: call void @llvm.lifetime.end(i64 34, i8* [[Y]])
+  //
+  // CHECK-EH: alloca %struct
+  // CHECK-EH: alloca %struct
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[X:%.*]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[X]])
+  // CHECK-EH: call void @llvm.lifetime.start(i64 34, i8* [[Y:%.*]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 34, i8* [[Y]])
+  useX(X());
+  useY(Y());
+}
+
+struct L {
+  L(int);
+  ~L();
+  char t[33];
+};
+
+// Check the lifetime-extended case, with a non trivial destructor
+// and a top level scope
+// CHECK-LABEL: define void @_Z16extendedLifetimev
+// CHECK-EH-LABEL: define void @_Z16extendedLifetimev
+void extendedLifetime() {
+  extern void useL(const L&);
+
+  // CHECK: [[A:%.*]] = alloca %struct.L
+  // CHECK: [[P:%.*]] = getelementptr inbounds %struct.L* [[A]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK: call void @_ZN1LC1Ei(%struct.L* [[A]], i32 2)
+  // CHECK-NOT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK: call void @_Z4useLRK1L(%struct.L* dereferenceable(33) [[A]])
+  // CHECK: call void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  //
+  // CHECK-EH: [[A:%.*]] = alloca %struct.L
+  // CHECK-EH: [[P:%.*]] = getelementptr inbounds %struct.L* [[A]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK-EH: call void @_ZN1LC1Ei(%struct.L* [[A]], i32 2)
+  // CHECK-EH-NOT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH: invoke void @_Z4useLRK1L(%struct.L* dereferenceable(33) [[A]])
+  // CHECK-EH: invoke void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH: invoke void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  const L &l = 2;
+  useL(l);
+}
+
+// Check the lifetime-extended case, with a non trivial destructor in a
+// sub-scope
+// CHECK-LABEL: define void @_Z16extendedLifetimeb
+// CHECK-EH-LABEL: define void @_Z16extendedLifetimeb
+void extendedLifetime(bool b) {
+  extern void useL(const L&);
+
+  // CHECK: [[A:%.*]] = alloca %struct.L
+  // CHECK: br i1 %b
+  // CHECK: [[P:%.*]] = getelementptr inbounds %struct.L* [[A]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK: call void @_ZN1LC1Ei(%struct.L* [[A]], i32 2)
+  // CHECK-NOT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK: call void @_Z4useLRK1L(%struct.L* dereferenceable(33) [[A]])
+  // CHECK: call void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-NEXT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  //
+  // CHECK-EH: [[A:%.*]] = alloca %struct.L
+  // CHECK-EH: br i1 %b
+  // CHECK-EH: [[P:%.*]] = getelementptr inbounds %struct.L* [[A]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK-EH: call void @_ZN1LC1Ei(%struct.L* [[A]], i32 2)
+  // CHECK-EH-NOT: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH: invoke void @_Z4useLRK1L(%struct.L* dereferenceable(33) [[A]])
+  // CHECK-EH: invoke void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-EH: invoke void @_ZN1LD1Ev(%struct.L* [[A]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  if (b) {
+    const L &l = 2;
+    useL(l);
+  }
+}
+
+struct T {
+  T();
+  T(int);
+  char t[33];
+};
+
+// Check the lifetime-extended case, with a trivial destructor,
+// in a sub-scope
+// CHECK-LABEL: define void @_Z37extendedLifetimeWithTrivialDestructorb
+// CHECK-EH-LABEL: define void @_Z37extendedLifetimeWithTrivialDestructorb
+void extendedLifetimeWithTrivialDestructor(bool b) {
+  extern void useT(const T &);
+
+  // CHECK: [[A:%.*]] = alloca %struct.T
+  // CHECK: br i1 %b
+  // CHECK: [[P:%.*]] = getelementptr inbounds %struct.T* [[A]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK: call void @_ZN1TC1Ei(%struct.T* [[A]], i32 2)
+  // CHECK: call void @_Z4useTRK1T(%struct.T* dereferenceable(33) [[A]])
+  // CHECK: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK: br label
+  //
+  // CHECK-EH: [[A:%.*]] = alloca %struct.T
+  // CHECK-EH: br i1 %b
+  // CHECK-EH: [[P:%.*]] = getelementptr inbounds %struct.T* [[A]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK-EH: call void @_ZN1TC1Ei(%struct.T* [[A]], i32 2)
+  // CHECK-EH: invoke void @_Z4useTRK1T(%struct.T* dereferenceable(33) [[A]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH-NEXT: resume
+  if (b) {
+    const T &t = 2;
+    useT(t);
+  }
+}
+
+// Check the lifetime-extended case, with a trivial destructor and a top level
+// scope
+// CHECK-LABEL: define void @_Z37extendedLifetimeWithTrivialDestructorv
+// CHECK-EH-LABEL: define void @_Z37extendedLifetimeWithTrivialDestructorv
+void extendedLifetimeWithTrivialDestructor() {
+  extern void useT(const T &);
+
+  // CHECK: [[A:%.*]] = alloca %struct.T
+  // CHECK: [[P:%.*]] = getelementptr inbounds %struct.T* [[A]], i32 0, i32 0, i32 0
+  // CHECK: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK: call void @_ZN1TC1Ei(%struct.T* [[A]], i32 3)
+  // CHECK: call void @_Z4useTRK1T(%struct.T* dereferenceable(33) [[A]])
+  // CHECK: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-NEXT: ret
+  //
+  // CHECK-EH: [[A:%.*]] = alloca %struct.T
+  // CHECK-EH: [[P:%.*]] = getelementptr inbounds %struct.T* [[A]], i32 0, i32 0, i32 0
+  // CHECK-EH: call void @llvm.lifetime.start(i64 33, i8* [[P]])
+  // CHECK-EH: call void @_ZN1TC1Ei(%struct.T* [[A]], i32 3)
+  // CHECK-EH: invoke void @_Z4useTRK1T(%struct.T* dereferenceable(33) [[A]])
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH-NEXT: ret
+  // CHECK-EH: call void @llvm.lifetime.end(i64 33, i8* [[P]])
+  // CHECK-EH-NEXT: resume
+  const T &t = 3;
+  useT(t);
+}