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. llvm-svn: 213576
This commit is contained in:
parent
0fb2f5d0f9
commit
17a83cf4b6
|
@ -468,22 +468,6 @@ namespace {
|
|||
CGF.EmitCall(FnInfo, CleanupFn, ReturnValueSlot(), Args);
|
||||
}
|
||||
};
|
||||
|
||||
/// A cleanup to call @llvm.lifetime.end.
|
||||
class CallLifetimeEnd : public EHScopeStack::Cleanup {
|
||||
llvm::Value *Addr;
|
||||
llvm::Value *Size;
|
||||
public:
|
||||
CallLifetimeEnd(llvm::Value *addr, llvm::Value *size)
|
||||
: 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// EmitAutoVarWithLifetime - Does the setup required for an automatic
|
||||
|
@ -802,10 +786,9 @@ 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) {
|
||||
bool CodeGenFunction::shouldUseLifetimeMarkers(unsigned Size) const {
|
||||
// For now, only in optimized builds.
|
||||
if (CGF.CGM.getCodeGenOpts().OptimizationLevel == 0)
|
||||
if (CGM.getCodeGenOpts().OptimizationLevel == 0)
|
||||
return false;
|
||||
|
||||
// Limit the size of marked objects to 32 bytes. We don't want to increase
|
||||
|
@ -815,7 +798,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.
|
||||
|
@ -825,6 +807,18 @@ void CodeGenFunction::EmitAutoVarDecl(const VarDecl &D) {
|
|||
EmitAutoVarCleanups(emission);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitLifetimeStart(llvm::Value *Size, llvm::Value *Addr) {
|
||||
llvm::Value *castAddr = Builder.CreateBitCast(Addr, Int8PtrTy);
|
||||
Builder.CreateCall2(CGM.getLLVMLifetimeStartFn(), Size, castAddr)
|
||||
->setDoesNotThrow();
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -920,13 +914,11 @@ 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)) {
|
||||
if (HaveInsertPoint() && shouldUseLifetimeMarkers(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();
|
||||
EmitLifetimeStart(sizeV, Alloc);
|
||||
} else {
|
||||
assert(!emission.useLifetimeMarkers());
|
||||
}
|
||||
|
|
|
@ -353,6 +353,17 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
|
|||
|
||||
// Create and initialize the reference temporary.
|
||||
llvm::Value *Object = createReferenceTemporary(*this, M, E);
|
||||
|
||||
uint64_t size =
|
||||
CGM.getDataLayout().getTypeStoreSize(ConvertTypeForMem(E->getType()));
|
||||
llvm::Value *sizeV = nullptr;
|
||||
llvm::AllocaInst *Alloca = dyn_cast<llvm::AllocaInst>(Object);
|
||||
bool useLifetimeMarkers = Alloca && shouldUseLifetimeMarkers(size);
|
||||
if (useLifetimeMarkers) {
|
||||
sizeV = llvm::ConstantInt::get(Int64Ty, size);
|
||||
EmitLifetimeStart(sizeV, Object);
|
||||
}
|
||||
|
||||
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,6 +374,20 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr(
|
|||
} else {
|
||||
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
|
||||
}
|
||||
|
||||
if (useLifetimeMarkers)
|
||||
switch (M->getStorageDuration()) {
|
||||
case SD_FullExpression:
|
||||
EHStack.pushCleanup<CallLifetimeEnd>(NormalAndEHCleanup, Object, sizeV);
|
||||
break;
|
||||
case SD_Automatic:
|
||||
pushCleanupAfterFullExpr<CallLifetimeEnd>(NormalAndEHCleanup, Object,
|
||||
sizeV);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected storage duration for Lifetime markers");
|
||||
}
|
||||
|
||||
pushTemporaryCleanup(*this, M, E, Object);
|
||||
|
||||
// Perform derived-to-base casts and/or field accesses, to get from the
|
||||
|
|
|
@ -436,6 +436,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();
|
||||
|
@ -990,6 +1007,23 @@ private:
|
|||
void EmitOpenCLKernelMetadata(const FunctionDecl *FD,
|
||||
llvm::Function *Fn);
|
||||
|
||||
/// Should we use the LLVM lifetime intrinsics for a local variable of the
|
||||
/// given size in bytes ?
|
||||
bool shouldUseLifetimeMarkers(unsigned Size) const;
|
||||
|
||||
/// A cleanup to call @llvm.lifetime.end.
|
||||
class CallLifetimeEnd : public EHScopeStack::Cleanup {
|
||||
llvm::Value *Addr;
|
||||
llvm::Value *Size;
|
||||
public:
|
||||
CallLifetimeEnd(llvm::Value *addr, llvm::Value *size)
|
||||
: Addr(addr), Size(size) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
CGF.EmitLifetimeEnd(Size, Addr);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext=false);
|
||||
~CodeGenFunction();
|
||||
|
@ -1673,6 +1707,9 @@ public:
|
|||
void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType,
|
||||
llvm::Value *Ptr);
|
||||
|
||||
void EmitLifetimeStart(llvm::Value *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,132 @@
|
|||
// 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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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
|
||||
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]])
|
||||
useX(X());
|
||||
useY(Y());
|
||||
}
|
||||
|
||||
struct L {
|
||||
L(int);
|
||||
~L();
|
||||
char t[33];
|
||||
};
|
||||
|
||||
// Check the lifetime-extended case
|
||||
// CHECK-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);
|
||||
}
|
Loading…
Reference in New Issue