From 17a83cf4b6dbba6ab6e96608ecf77c8c87019f3f Mon Sep 17 00:00:00 2001 From: "Arnaud A. de Grandmaison" Date: Mon, 21 Jul 2014 18:54:21 +0000 Subject: [PATCH] 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 --- clang/lib/CodeGen/CGDecl.cpp | 40 +++---- clang/lib/CodeGen/CGExpr.cpp | 25 ++++ clang/lib/CodeGen/CodeGenFunction.h | 37 ++++++ clang/test/CodeGenCXX/unnamed-object-lifetime.cpp | 132 ++++++++++++++++++++++ 4 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 clang/test/CodeGenCXX/unnamed-object-lifetime.cpp diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 91f8041..9c8882d 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/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/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 512b323..2c9c2f1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/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(Object); + bool useLifetimeMarkers = Alloca && shouldUseLifetimeMarkers(size); + if (useLifetimeMarkers) { + sizeV = llvm::ConstantInt::get(Int64Ty, size); + EmitLifetimeStart(sizeV, Object); + } + if (auto *Var = dyn_cast(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(NormalAndEHCleanup, Object, sizeV); + break; + case SD_Automatic: + pushCleanupAfterFullExpr(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/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 59cc30d..cd668e0 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/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 + 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/clang/test/CodeGenCXX/unnamed-object-lifetime.cpp b/clang/test/CodeGenCXX/unnamed-object-lifetime.cpp new file mode 100644 index 0000000..c1422bc --- /dev/null +++ b/clang/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); +} -- 2.7.4