From: Douglas Gregor Date: Wed, 21 Jul 2010 01:10:17 +0000 (+0000) Subject: Implement zero-initialization for array new when there is an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=59174c0633fb5cde41735cfbff5744bdf837e8d9;p=clang Implement zero-initialization for array new when there is an initializer of (). Make sure to use a simple memset() when we can, or fall back to generating a loop when a simple memset will not suffice. Fixes , a regression due to my work in r107857. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108977 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index c50fe90f8a..799dd644a2 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -873,19 +873,24 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD, /// 'D' is the default constructor for elements of the array, 'ArrayTy' is the /// array type and 'ArrayPtr' points to the beginning fo the array. /// It is assumed that all relevant checks have been made by the caller. +/// +/// \param ZeroInitialization True if each element should be zero-initialized +/// before it is constructed. void CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, - const ConstantArrayType *ArrayTy, - llvm::Value *ArrayPtr, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd) { + const ConstantArrayType *ArrayTy, + llvm::Value *ArrayPtr, + CallExpr::const_arg_iterator ArgBeg, + CallExpr::const_arg_iterator ArgEnd, + bool ZeroInitialization) { const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); llvm::Value * NumElements = llvm::ConstantInt::get(SizeTy, getContext().getConstantArrayElementCount(ArrayTy)); - EmitCXXAggrConstructorCall(D, NumElements, ArrayPtr, ArgBeg, ArgEnd); + EmitCXXAggrConstructorCall(D, NumElements, ArrayPtr, ArgBeg, ArgEnd, + ZeroInitialization); } void @@ -893,7 +898,8 @@ CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, llvm::Value *NumElements, llvm::Value *ArrayPtr, CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd) { + CallExpr::const_arg_iterator ArgEnd, + bool ZeroInitialization) { const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); // Create a temporary for the loop index and initialize it with 0. @@ -924,6 +930,11 @@ CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, llvm::Value *Address = Builder.CreateInBoundsGEP(ArrayPtr, Counter, "arrayidx"); + // Zero initialize the storage, if requested. + if (ZeroInitialization) + EmitNullInitialization(Address, + getContext().getTypeDeclType(D->getParent())); + // C++ [class.temporary]p4: // There are two contexts in which temporaries are destroyed at a different // point than the end of the full-expression. The first context is when a diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index f419382989..eb984d3cbb 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -423,13 +423,16 @@ static CharUnits CalculateCookiePadding(ASTContext &Ctx, const CXXNewExpr *E) { static llvm::Value *EmitCXXNewAllocSize(ASTContext &Context, CodeGenFunction &CGF, const CXXNewExpr *E, - llvm::Value *&NumElements) { + llvm::Value *&NumElements, + llvm::Value *&SizeWithoutCookie) { QualType Type = E->getAllocatedType(); CharUnits TypeSize = CGF.getContext().getTypeSizeInChars(Type); const llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); - if (!E->isArray()) - return llvm::ConstantInt::get(SizeTy, TypeSize.getQuantity()); + if (!E->isArray()) { + SizeWithoutCookie = llvm::ConstantInt::get(SizeTy, TypeSize.getQuantity()); + return SizeWithoutCookie; + } // Emit the array size expression. NumElements = CGF.EmitScalarExpr(E->getArraySize()); @@ -488,6 +491,7 @@ static llvm::Value *EmitCXXNewAllocSize(ASTContext &Context, PN->addIncoming(llvm::Constant::getAllOnesValue(V->getType()), OverflowBB); Size = PN; } + SizeWithoutCookie = Size; // Add the cookie padding if necessary. CharUnits CookiePadding = CalculateCookiePadding(CGF.getContext(), E); @@ -571,18 +575,60 @@ CodeGenFunction::EmitNewArrayInitializer(const CXXNewExpr *E, EmitBlock(AfterFor, true); } +static void EmitZeroMemSet(CodeGenFunction &CGF, QualType T, + llvm::Value *NewPtr, llvm::Value *Size) { + llvm::LLVMContext &VMContext = CGF.CGM.getLLVMContext(); + const llvm::Type *BP = llvm::Type::getInt8PtrTy(VMContext); + if (NewPtr->getType() != BP) + NewPtr = CGF.Builder.CreateBitCast(NewPtr, BP, "tmp"); + + CGF.Builder.CreateCall5(CGF.CGM.getMemSetFn(BP, CGF.IntPtrTy), NewPtr, + llvm::Constant::getNullValue(llvm::Type::getInt8Ty(VMContext)), + Size, + llvm::ConstantInt::get(CGF.Int32Ty, + CGF.getContext().getTypeAlign(T)/8), + llvm::ConstantInt::get(llvm::Type::getInt1Ty(VMContext), + 0)); +} + static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E, llvm::Value *NewPtr, - llvm::Value *NumElements) { + llvm::Value *NumElements, + llvm::Value *AllocSizeWithoutCookie) { if (E->isArray()) { if (CXXConstructorDecl *Ctor = E->getConstructor()) { - if (!Ctor->getParent()->hasTrivialConstructor()) - CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr, - E->constructor_arg_begin(), - E->constructor_arg_end()); + bool RequiresZeroInitialization = false; + if (Ctor->getParent()->hasTrivialConstructor()) { + // If new expression did not specify value-initialization, then there + // is no initialization. + if (!E->hasInitializer() || Ctor->getParent()->isEmpty()) + return; + + if (!CGF.CGM.getTypes().ContainsPointerToDataMember( + E->getAllocatedType())) { + // Optimization: since zero initialization will just set the memory + // to all zeroes, generate a single memset to do it in one shot. + EmitZeroMemSet(CGF, E->getAllocatedType(), NewPtr, + AllocSizeWithoutCookie); + return; + } + + RequiresZeroInitialization = true; + } + + CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr, + E->constructor_arg_begin(), + E->constructor_arg_end(), + RequiresZeroInitialization); return; - } - else { + } else if (E->getNumConstructorArgs() == 1 && + isa(E->getConstructorArg(0))) { + // Optimization: since zero initialization will just set the memory + // to all zeroes, generate a single memset to do it in one shot. + EmitZeroMemSet(CGF, E->getAllocatedType(), NewPtr, + AllocSizeWithoutCookie); + return; + } else { CGF.EmitNewArrayInitializer(E, NewPtr, NumElements); return; } @@ -621,8 +667,10 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { QualType SizeTy = getContext().getSizeType(); llvm::Value *NumElements = 0; + llvm::Value *AllocSizeWithoutCookie = 0; llvm::Value *AllocSize = EmitCXXNewAllocSize(getContext(), - *this, E, NumElements); + *this, E, NumElements, + AllocSizeWithoutCookie); NewArgs.push_back(std::make_pair(RValue::get(AllocSize), SizeTy)); @@ -714,12 +762,12 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { NewPtr = Builder.CreateBitCast(NewPtr, ConvertType(getContext().getPointerType(AllocType))); - EmitNewInitializer(*this, E, NewPtr, NumElements); + EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); NewPtr = Builder.CreateBitCast(NewPtr, ConvertType(E->getType())); } else { NewPtr = Builder.CreateBitCast(NewPtr, ConvertType(E->getType())); - EmitNewInitializer(*this, E, NewPtr, NumElements); + EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); } if (NullCheckResult) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index d335e2b4f8..3e31803753 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1053,13 +1053,15 @@ public: const ConstantArrayType *ArrayTy, llvm::Value *ArrayPtr, CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd); + CallExpr::const_arg_iterator ArgEnd, + bool ZeroInitialization = false); void EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, llvm::Value *NumElements, llvm::Value *ArrayPtr, CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd); + CallExpr::const_arg_iterator ArgEnd, + bool ZeroInitialization = false); void EmitCXXAggrDestructorCall(const CXXDestructorDecl *D, const ArrayType *Array, diff --git a/test/CodeGenCXX/new.cpp b/test/CodeGenCXX/new.cpp index 885158f8a0..3f22e89f2a 100644 --- a/test/CodeGenCXX/new.cpp +++ b/test/CodeGenCXX/new.cpp @@ -90,19 +90,53 @@ A* t10() { return new(1, 2, 3.45, 100) A; } +// CHECK: define void @_Z3t11i struct B { int a; }; -void t11() { +struct Bmemptr { int Bmemptr::* memptr; int a; }; + +void t11(int n) { // CHECK: call noalias i8* @_Znwm // CHECK: call void @llvm.memset.p0i8.i64( B* b = new B(); + + // CHECK: call noalias i8* @_Znam + // CHECK: {{call void.*llvm.memset.p0i8.i64.*i8 0, i64 %}} + B *b2 = new B[n](); + + // CHECK: call noalias i8* @_Znam + // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64 + // CHECK: br + Bmemptr *b_memptr = new Bmemptr[n](); + + // CHECK: ret void } struct Empty { }; // We don't need to initialize an empty class. +// CHECK: define void @_Z3t12v void t12() { - // CHECK: define void @_Z3t12v - // CHECK-NOT: br label - // CHECK: ret void + // CHECK: call noalias i8* @_Znam + // CHECK-NOT: br (void)new Empty[10]; + + // CHECK: call noalias i8* @_Znam + // CHECK-NOT: br + (void)new Empty[10](); + + // CHECK: ret void +} + +// Zero-initialization +// CHECK: define void @_Z3t13i +void t13(int n) { + // CHECK: call noalias i8* @_Znwm + // CHECK: store i32 0, i32* + (void)new int(); + + // CHECK: call noalias i8* @_Znam + // CHECK: {{call void.*llvm.memset.p0i8.i64.*i8 0, i64 %}} + (void)new int[n](); + + // CHECK-NEXT: ret void }