forked from OSchip/llvm-project
Emit lifetime.start / lifetime.end markers for unnamed temporary objects.
This will give more information to the optimizers so that they can reuse stack slots and reduce stack usage. llvm-svn: 218865
This commit is contained in:
parent
4ae7f2e839
commit
42d314d1ba
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue