: 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
}
/// 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;
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.
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
// 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());
}
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.
///
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.
}
}
+ // 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>()) {
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:
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)) {
EmitScalarInit(E, M->getExtendingDecl(), RefTempDst, false);
- pushTemporaryCleanup(*this, M, E, Object);
+ pushTemporaryCleanup(*this, M, E, Object, ObjectSize);
return RefTempDst;
}
}
}
- // 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.
} 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
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();
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
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);
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);
--- /dev/null
+// 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);
+}