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 <rdar://problem/8212208>, a regression due to my work
in r107857.

llvm-svn: 108977
This commit is contained in:
Douglas Gregor 2010-07-21 01:10:17 +00:00
parent a0e7f0c1b1
commit 05fc5be32f
4 changed files with 120 additions and 25 deletions

View File

@ -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) {
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

View File

@ -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;
}
else {
RequiresZeroInitialization = true;
}
CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr,
E->constructor_arg_begin(),
E->constructor_arg_end(),
RequiresZeroInitialization);
return;
} else if (E->getNumConstructorArgs() == 1 &&
isa<ImplicitValueInitExpr>(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) {

View File

@ -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,

View File

@ -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
}