summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaud A. de Grandmaison <arnaud.degrandmaison@arm.com>2014-07-21 18:54:21 +0000
committerArnaud A. de Grandmaison <arnaud.degrandmaison@arm.com>2014-07-21 18:54:21 +0000
commitdfd60139839e1cfe0063be2261034e8e7888bd90 (patch)
tree179397e1071a589ff1ecd2c29f39aad734c03aff
parent30974231cab08ddc43e78dfe224009f8555d0ec3 (diff)
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213576 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CGDecl.cpp40
-rw-r--r--lib/CodeGen/CGExpr.cpp25
-rw-r--r--lib/CodeGen/CodeGenFunction.h37
-rw-r--r--test/CodeGenCXX/unnamed-object-lifetime.cpp132
4 files changed, 210 insertions, 24 deletions
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 91f8041930..9c8882decc 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -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());
}
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 512b323ba1..2c9c2f130e 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -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
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 59cc30da42..cd668e05af 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -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);
diff --git a/test/CodeGenCXX/unnamed-object-lifetime.cpp b/test/CodeGenCXX/unnamed-object-lifetime.cpp
new file mode 100644
index 0000000000..c1422bc507
--- /dev/null
+++ b/test/CodeGenCXX/unnamed-object-lifetime.cpp
@@ -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);
+}