From 06a67e2c6faee4645f43e786e292b5eafcb9b70f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 3 Jun 2014 06:58:52 +0000 Subject: [PATCH] When emitting a multidimensional array new, emit the initializers for the trailing elements as a single loop, rather than sometimes emitting a nest of several loops. This fixes a bug where CodeGen would sometimes try to emit an expression with the wrong type for the element being initialized. Plus various other minor cleanups to the IR produced for array new initialization. llvm-svn: 210079 --- clang/lib/CodeGen/CGExprCXX.cpp | 307 ++++++++++++--------- clang/lib/CodeGen/CodeGenFunction.h | 3 +- .../CodeGenCXX/cxx11-initializer-array-new.cpp | 87 ++++-- clang/test/CodeGenCXX/new-array-init.cpp | 19 +- 4 files changed, 260 insertions(+), 156 deletions(-) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 548cd48..c6995d8 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -749,207 +749,242 @@ static void StoreAnyExprIntoOneUnit(CodeGenFunction &CGF, const Expr *Init, } void -CodeGenFunction::EmitNewArrayInitializer(const CXXNewExpr *E, - QualType elementType, - llvm::Value *beginPtr, - llvm::Value *numElements) { +CodeGenFunction::EmitNewArrayInitializer(const CXXNewExpr *E, + QualType ElementType, + llvm::Value *BeginPtr, + llvm::Value *NumElements, + llvm::Value *AllocSizeWithoutCookie) { + // If we have a type with trivial initialization and no initializer, + // there's nothing to do. if (!E->hasInitializer()) - return; // We have a POD type. + return; - llvm::Value *explicitPtr = beginPtr; - // Find the end of the array, hoisted out of the loop. - llvm::Value *endPtr = - Builder.CreateInBoundsGEP(beginPtr, numElements, "array.end"); + llvm::Value *CurPtr = BeginPtr; - unsigned initializerElements = 0; + unsigned InitListElements = 0; const Expr *Init = E->getInitializer(); - llvm::AllocaInst *endOfInit = nullptr; - QualType::DestructionKind dtorKind = elementType.isDestructedType(); - EHScopeStack::stable_iterator cleanup; - llvm::Instruction *cleanupDominator = nullptr; + llvm::AllocaInst *EndOfInit = nullptr; + QualType::DestructionKind DtorKind = ElementType.isDestructedType(); + EHScopeStack::stable_iterator Cleanup; + llvm::Instruction *CleanupDominator = nullptr; // If the initializer is an initializer list, first do the explicit elements. if (const InitListExpr *ILE = dyn_cast(Init)) { - initializerElements = ILE->getNumInits(); + InitListElements = ILE->getNumInits(); // If this is a multi-dimensional array new, we will initialize multiple // elements with each init list element. QualType AllocType = E->getAllocatedType(); if (const ConstantArrayType *CAT = dyn_cast_or_null( AllocType->getAsArrayTypeUnsafe())) { - unsigned AS = explicitPtr->getType()->getPointerAddressSpace(); + unsigned AS = CurPtr->getType()->getPointerAddressSpace(); llvm::Type *AllocPtrTy = ConvertTypeForMem(AllocType)->getPointerTo(AS); - explicitPtr = Builder.CreateBitCast(explicitPtr, AllocPtrTy); - initializerElements *= getContext().getConstantArrayElementCount(CAT); + CurPtr = Builder.CreateBitCast(CurPtr, AllocPtrTy); + InitListElements *= getContext().getConstantArrayElementCount(CAT); } - // Enter a partial-destruction cleanup if necessary. - if (needsEHCleanup(dtorKind)) { - // In principle we could tell the cleanup where we are more + // Enter a partial-destruction Cleanup if necessary. + if (needsEHCleanup(DtorKind)) { + // In principle we could tell the Cleanup where we are more // directly, but the control flow can get so varied here that it // would actually be quite complex. Therefore we go through an // alloca. - endOfInit = CreateTempAlloca(beginPtr->getType(), "array.endOfInit"); - cleanupDominator = Builder.CreateStore(beginPtr, endOfInit); - pushIrregularPartialArrayCleanup(beginPtr, endOfInit, elementType, - getDestroyer(dtorKind)); - cleanup = EHStack.stable_begin(); + EndOfInit = CreateTempAlloca(BeginPtr->getType(), "array.init.end"); + CleanupDominator = Builder.CreateStore(BeginPtr, EndOfInit); + pushIrregularPartialArrayCleanup(BeginPtr, EndOfInit, ElementType, + getDestroyer(DtorKind)); + Cleanup = EHStack.stable_begin(); } for (unsigned i = 0, e = ILE->getNumInits(); i != e; ++i) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. - if (endOfInit) Builder.CreateStore(explicitPtr, endOfInit); + if (EndOfInit) + Builder.CreateStore(Builder.CreateBitCast(CurPtr, BeginPtr->getType()), + EndOfInit); + // FIXME: If the last initializer is an incomplete initializer list for + // an array, and we have an array filler, we can fold together the two + // initialization loops. StoreAnyExprIntoOneUnit(*this, ILE->getInit(i), - ILE->getInit(i)->getType(), explicitPtr); - explicitPtr = Builder.CreateConstGEP1_32(explicitPtr, 1, - "array.exp.next"); + ILE->getInit(i)->getType(), CurPtr); + CurPtr = Builder.CreateConstInBoundsGEP1_32(CurPtr, 1, "array.exp.next"); } // The remaining elements are filled with the array filler expression. Init = ILE->getArrayFiller(); - explicitPtr = Builder.CreateBitCast(explicitPtr, beginPtr->getType()); + // Extract the initializer for the individual array elements by pulling + // out the array filler from all the nested initializer lists. This avoids + // generating a nested loop for the initialization. + while (Init && Init->getType()->isConstantArrayType()) { + auto *SubILE = dyn_cast(Init); + if (!SubILE) + break; + assert(SubILE->getNumInits() == 0 && "explicit inits in array filler?"); + Init = SubILE->getArrayFiller(); + } + + // Switch back to initializing one base element at a time. + CurPtr = Builder.CreateBitCast(CurPtr, BeginPtr->getType()); } - llvm::ConstantInt *constNum = dyn_cast(numElements); + // Attempt to perform zero-initialization using memset. + auto TryMemsetInitialization = [&]() -> bool { + // FIXME: If the type is a pointer-to-data-member under the Itanium ABI, + // we can initialize with a memset to -1. + if (!CGM.getTypes().isZeroInitializable(ElementType)) + return false; + + // Optimization: since zero initialization will just set the memory + // to all zeroes, generate a single memset to do it in one shot. + + // Subtract out the size of any elements we've already initialized. + auto *RemainingSize = AllocSizeWithoutCookie; + if (InitListElements) { + // We know this can't overflow; we check this when doing the allocation. + auto *InitializedSize = llvm::ConstantInt::get( + RemainingSize->getType(), + getContext().getTypeSizeInChars(ElementType).getQuantity() * + InitListElements); + RemainingSize = Builder.CreateSub(RemainingSize, InitializedSize); + } + + // Create the memset. + CharUnits Alignment = getContext().getTypeAlignInChars(ElementType); + Builder.CreateMemSet(CurPtr, Builder.getInt8(0), RemainingSize, + Alignment.getQuantity(), false); + return true; + }; + + // If this is a constructor call, try to optimize it out, and failing that + // emit a single loop to initialize all remaining elements. + if (const CXXConstructExpr *CCE = dyn_cast_or_null(Init)){ + CXXConstructorDecl *Ctor = CCE->getConstructor(); + if (Ctor->isTrivial()) { + // If new expression did not specify value-initialization, then there + // is no initialization. + if (!CCE->requiresZeroInitialization() || Ctor->getParent()->isEmpty()) + return; + + if (TryMemsetInitialization()) + return; + } + + // Store the new Cleanup position for irregular Cleanups. + // + // FIXME: Share this cleanup with the constructor call emission rather than + // having it create a cleanup of its own. + if (EndOfInit) Builder.CreateStore(CurPtr, EndOfInit); + + // Emit a constructor call loop to initialize the remaining elements. + if (InitListElements) + NumElements = Builder.CreateSub( + NumElements, + llvm::ConstantInt::get(NumElements->getType(), InitListElements)); + EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, + CCE->arg_begin(), CCE->arg_end(), + CCE->requiresZeroInitialization()); + return; + } + + // If this is value-initialization, we can usually use memset. + ImplicitValueInitExpr IVIE(ElementType); + if (Init && isa(Init)) { + if (TryMemsetInitialization()) + return; + + // Switch to an ImplicitValueInitExpr for the element type. This handles + // only one case: multidimensional array new of pointers to members. In + // all other cases, we already have an initializer for the array element. + Init = &IVIE; + } + + // At this point we should have found an initializer for the individual + // elements of the array. + assert(getContext().hasSameUnqualifiedType(ElementType, Init->getType()) && + "got wrong type of element to initialize"); + + llvm::ConstantInt *ConstNum = dyn_cast(NumElements); // If all elements have already been initialized, skip the whole loop. - if (constNum && constNum->getZExtValue() <= initializerElements) { - // If there was a cleanup, deactivate it. - if (cleanupDominator) - DeactivateCleanupBlock(cleanup, cleanupDominator); + if (ConstNum && ConstNum->getZExtValue() <= InitListElements) { + // If there was a Cleanup, deactivate it. + if (CleanupDominator) + DeactivateCleanupBlock(Cleanup, CleanupDominator); return; } - // Create the continuation block. - llvm::BasicBlock *contBB = createBasicBlock("new.loop.end"); + // Create the loop blocks. + llvm::BasicBlock *EntryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *LoopBB = createBasicBlock("new.loop"); + llvm::BasicBlock *ContBB = createBasicBlock("new.loop.end"); + + // Find the end of the array, hoisted out of the loop. + llvm::Value *EndPtr = + Builder.CreateInBoundsGEP(BeginPtr, NumElements, "array.end"); // If the number of elements isn't constant, we have to now check if there is // anything left to initialize. - if (!constNum) { - llvm::BasicBlock *nonEmptyBB = createBasicBlock("new.loop.nonempty"); - llvm::Value *isEmpty = Builder.CreateICmpEQ(explicitPtr, endPtr, + if (!ConstNum) { + llvm::Value *IsEmpty = Builder.CreateICmpEQ(CurPtr, EndPtr, "array.isempty"); - Builder.CreateCondBr(isEmpty, contBB, nonEmptyBB); - EmitBlock(nonEmptyBB); + Builder.CreateCondBr(IsEmpty, ContBB, LoopBB); } // Enter the loop. - llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); - llvm::BasicBlock *loopBB = createBasicBlock("new.loop"); - - EmitBlock(loopBB); + EmitBlock(LoopBB); // Set up the current-element phi. - llvm::PHINode *curPtr = - Builder.CreatePHI(explicitPtr->getType(), 2, "array.cur"); - curPtr->addIncoming(explicitPtr, entryBB); - - // Store the new cleanup position for irregular cleanups. - if (endOfInit) Builder.CreateStore(curPtr, endOfInit); - - // Enter a partial-destruction cleanup if necessary. - if (!cleanupDominator && needsEHCleanup(dtorKind)) { - pushRegularPartialArrayCleanup(beginPtr, curPtr, elementType, - getDestroyer(dtorKind)); - cleanup = EHStack.stable_begin(); - cleanupDominator = Builder.CreateUnreachable(); + llvm::PHINode *CurPtrPhi = + Builder.CreatePHI(CurPtr->getType(), 2, "array.cur"); + CurPtrPhi->addIncoming(CurPtr, EntryBB); + CurPtr = CurPtrPhi; + + // Store the new Cleanup position for irregular Cleanups. + if (EndOfInit) Builder.CreateStore(CurPtr, EndOfInit); + + // Enter a partial-destruction Cleanup if necessary. + if (!CleanupDominator && needsEHCleanup(DtorKind)) { + pushRegularPartialArrayCleanup(BeginPtr, CurPtr, ElementType, + getDestroyer(DtorKind)); + Cleanup = EHStack.stable_begin(); + CleanupDominator = Builder.CreateUnreachable(); } // Emit the initializer into this element. - StoreAnyExprIntoOneUnit(*this, Init, E->getAllocatedType(), curPtr); + StoreAnyExprIntoOneUnit(*this, Init, Init->getType(), CurPtr); - // Leave the cleanup if we entered one. - if (cleanupDominator) { - DeactivateCleanupBlock(cleanup, cleanupDominator); - cleanupDominator->eraseFromParent(); + // Leave the Cleanup if we entered one. + if (CleanupDominator) { + DeactivateCleanupBlock(Cleanup, CleanupDominator); + CleanupDominator->eraseFromParent(); } - // FIXME: The code below intends to initialize the individual array base - // elements, one at a time - but when dealing with multi-dimensional arrays - - // the pointer arithmetic can get confused - so the fix below entails casting - // to the allocated type to ensure that we get the pointer arithmetic right. - // It seems like the right approach here, it to really initialize the - // individual array base elements one at a time since it'll generate less - // code. I think the problem is that the wrong type is being passed into - // StoreAnyExprIntoOneUnit, but directly fixing that doesn't really work, - // because the Init expression has the wrong type at this point. - // So... this is ok for a quick fix, but we can and should do a lot better - // here long-term. - // Advance to the next element by adjusting the pointer type as necessary. - // For new int[10][20][30], alloc type is int[20][30], base type is 'int'. - QualType AllocType = E->getAllocatedType(); - llvm::Type *AllocPtrTy = ConvertTypeForMem(AllocType)->getPointerTo( - curPtr->getType()->getPointerAddressSpace()); - llvm::Value *curPtrAllocTy = Builder.CreateBitCast(curPtr, AllocPtrTy); - llvm::Value *nextPtrAllocTy = - Builder.CreateConstGEP1_32(curPtrAllocTy, 1, "array.next"); - // Cast it back to the base type so that we can compare it to the endPtr. - llvm::Value *nextPtr = - Builder.CreateBitCast(nextPtrAllocTy, endPtr->getType()); + llvm::Value *NextPtr = + Builder.CreateConstInBoundsGEP1_32(CurPtr, 1, "array.next"); + // Check whether we've gotten to the end of the array and, if so, // exit the loop. - llvm::Value *isEnd = Builder.CreateICmpEQ(nextPtr, endPtr, "array.atend"); - Builder.CreateCondBr(isEnd, contBB, loopBB); - curPtr->addIncoming(nextPtr, Builder.GetInsertBlock()); + llvm::Value *IsEnd = Builder.CreateICmpEQ(NextPtr, EndPtr, "array.atend"); + Builder.CreateCondBr(IsEnd, ContBB, LoopBB); + CurPtrPhi->addIncoming(NextPtr, Builder.GetInsertBlock()); - EmitBlock(contBB); + EmitBlock(ContBB); } -static void EmitZeroMemSet(CodeGenFunction &CGF, QualType T, - llvm::Value *NewPtr, llvm::Value *Size) { - CGF.EmitCastToVoidPtr(NewPtr); - CharUnits Alignment = CGF.getContext().getTypeAlignInChars(T); - CGF.Builder.CreateMemSet(NewPtr, CGF.Builder.getInt8(0), Size, - Alignment.getQuantity(), false); -} - static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E, QualType ElementType, llvm::Value *NewPtr, llvm::Value *NumElements, llvm::Value *AllocSizeWithoutCookie) { - const Expr *Init = E->getInitializer(); - if (E->isArray()) { - if (const CXXConstructExpr *CCE = dyn_cast_or_null(Init)){ - CXXConstructorDecl *Ctor = CCE->getConstructor(); - if (Ctor->isTrivial()) { - // If new expression did not specify value-initialization, then there - // is no initialization. - if (!CCE->requiresZeroInitialization() || Ctor->getParent()->isEmpty()) - return; - - if (CGF.CGM.getTypes().isZeroInitializable(ElementType)) { - // Optimization: since zero initialization will just set the memory - // to all zeroes, generate a single memset to do it in one shot. - EmitZeroMemSet(CGF, ElementType, NewPtr, AllocSizeWithoutCookie); - return; - } - } - - CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr, - CCE->arg_begin(), CCE->arg_end(), - CCE->requiresZeroInitialization()); - return; - } else if (Init && isa(Init) && - CGF.CGM.getTypes().isZeroInitializable(ElementType)) { - // Optimization: since zero initialization will just set the memory - // to all zeroes, generate a single memset to do it in one shot. - EmitZeroMemSet(CGF, ElementType, NewPtr, AllocSizeWithoutCookie); - return; - } - CGF.EmitNewArrayInitializer(E, ElementType, NewPtr, NumElements); - return; - } - - if (!Init) - return; - - StoreAnyExprIntoOneUnit(CGF, Init, E->getAllocatedType(), NewPtr); + if (E->isArray()) + CGF.EmitNewArrayInitializer(E, ElementType, NewPtr, NumElements, + AllocSizeWithoutCookie); + else if (const Expr *Init = E->getInitializer()) + StoreAnyExprIntoOneUnit(CGF, Init, E->getAllocatedType(), NewPtr); } /// Emit a call to an operator new or operator delete function, as implicitly diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 750bec8b..d1e63a7 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1647,7 +1647,8 @@ public: llvm::Value *This); void EmitNewArrayInitializer(const CXXNewExpr *E, QualType elementType, - llvm::Value *NewPtr, llvm::Value *NumElements); + llvm::Value *NewPtr, llvm::Value *NumElements, + llvm::Value *AllocSizeWithoutCookie); void EmitCXXTemporary(const CXXTemporary *Temporary, QualType TempType, llvm::Value *Ptr); diff --git a/clang/test/CodeGenCXX/cxx11-initializer-array-new.cpp b/clang/test/CodeGenCXX/cxx11-initializer-array-new.cpp index 8de88dc..2393939 100644 --- a/clang/test/CodeGenCXX/cxx11-initializer-array-new.cpp +++ b/clang/test/CodeGenCXX/cxx11-initializer-array-new.cpp @@ -28,7 +28,7 @@ void *p = new S[2][3]{ { 1, 2, 3 }, { 4, 5, 6 } }; // // { 4, 5, 6 } // -// CHECK: %[[S_1:.*]] = getelementptr [3 x %[[S]]]* %[[S_0]], i32 1 +// CHECK: %[[S_1:.*]] = getelementptr inbounds [3 x %[[S]]]* %[[S_0]], i32 1 // // CHECK: %[[S_1_0:.*]] = getelementptr inbounds [3 x %[[S]]]* %[[S_1]], i64 0, i64 0 // CHECK: call void @_ZN1SC1Ei(%[[S]]* %[[S_1_0]], i32 4) @@ -56,7 +56,6 @@ void *q = new S[n][3]{ { 1, 2, 3 }, { 4, 5, 6 } }; // CHECK: store i64 %[[ELTS]], i64* %[[COOKIE]] // CHECK: %[[START_AS_i8:.*]] = getelementptr inbounds i8* %[[ALLOC]], i64 8 // CHECK: %[[START_AS_S:.*]] = bitcast i8* %[[START_AS_i8]] to %[[S]]* -// CHECK: %[[END_AS_S:.*]] = getelementptr inbounds %[[S]]* %[[START_AS_S]], i64 %[[ELTS]] // // Explicit initializers: // @@ -73,7 +72,7 @@ void *q = new S[n][3]{ { 1, 2, 3 }, { 4, 5, 6 } }; // // { 4, 5, 6 } // -// CHECK: %[[S_1:.*]] = getelementptr [3 x %[[S]]]* %[[S_0]], i32 1 +// CHECK: %[[S_1:.*]] = getelementptr inbounds [3 x %[[S]]]* %[[S_0]], i32 1 // // CHECK: %[[S_1_0:.*]] = getelementptr inbounds [3 x %[[S]]]* %[[S_1]], i64 0, i64 0 // CHECK: call void @_ZN1SC1Ei(%[[S]]* %[[S_1_0]], i32 4) @@ -82,26 +81,80 @@ void *q = new S[n][3]{ { 1, 2, 3 }, { 4, 5, 6 } }; // CHECK: %[[S_1_2:.*]] = getelementptr inbounds %[[S]]* %[[S_1_1]], i64 1 // CHECK: call void @_ZN1SC1Ei(%[[S]]* %[[S_1_2]], i32 6) // -// CHECK: %[[S_2:.*]] = getelementptr [3 x %[[S]]]* %[[S_1]], i32 1 +// And the rest. +// +// CHECK: %[[S_2:.*]] = getelementptr inbounds [3 x %[[S]]]* %[[S_1]], i32 1 // CHECK: %[[S_2_AS_S:.*]] = bitcast [3 x %[[S]]]* %[[S_2]] to %[[S]]* -// CHECK: icmp eq %[[S]]* %[[S_2_AS_S]], %[[END_AS_S]] -// CHECK: br i1 // -// S[n-2][3] initialization loop: +// CHECK: %[[REST:.*]] = sub i64 %[[ELTS]], 6 +// CHECK: icmp eq i64 %[[REST]], 0 +// CHECK: br i1 // -// CHECK: %[[END_INNER:.*]] = getelementptr inbounds %[[S]]* %{{.*}}, i64 3 +// CHECK: %[[END:.*]] = getelementptr inbounds %[[S]]* %[[S_2_AS_S]], i64 %[[REST]] // CHECK: br label // -// S[3] initialization loop: +// CHECK: %[[CUR:.*]] = phi %[[S]]* [ %[[S_2_AS_S]], {{.*}} ], [ %[[NEXT:.*]], {{.*}} ] +// CHECK: call void @_ZN1SC1Ev(%[[S]]* %[[CUR]]) +// CHECK: %[[NEXT]] = getelementptr inbounds %[[S]]* %[[CUR]], i64 1 +// CHECK: icmp eq %[[S]]* %[[NEXT]], %[[END]] +// CHECK: br i1 // -// CHECK: call void @_ZN1SC1Ev(%[[S]]* -// CHECK: %[[NEXT_INNER:.*]] = getelementptr inbounds %[[S]]* %{{.*}}, i64 1 -// CHECK: icmp eq %[[S]]* %[[NEXT_INNER]], %[[END_INNER]] -// CHECK: br i1 +// CHECK: } + +struct T { int a; }; +void *r = new T[n][3]{ { 1, 2, 3 }, { 4, 5, 6 } }; + +// CHECK-LABEL: define // -// CHECK: %[[NEXT_OUTER:.*]] = getelementptr [3 x %[[S]]]* %{{.*}}, i32 1 -// CHECK: %[[NEXT_OUTER_AS_S:.*]] = bitcast [3 x %[[S]]]* %[[NEXT_OUTER]] to %[[S]]* -// CHECK: icmp eq %[[S]]* %[[NEXT_OUTER_AS_S]], %[[END_AS_S]] -// CHECK: br i1 +// CHECK: load i32* @n +// CHECK: call {{.*}} @llvm.umul.with.overflow.i64(i64 %[[N:.*]], i64 12) +// CHECK: %[[ELTS:.*]] = mul i64 %[[N]], 3 +// +// No cookie. +// CHECK-NOT: @llvm.uadd.with.overflow +// +// CHECK: %[[ALLOC:.*]] = call noalias i8* @_Znam(i64 %{{.*}}) +// +// CHECK: %[[START_AS_T:.*]] = bitcast i8* %[[ALLOC]] to %[[T:.*]]* +// +// Explicit initializers: +// +// { 1, 2, 3 } +// +// CHECK: %[[T_0:.*]] = bitcast %[[T]]* %[[START_AS_T]] to [3 x %[[T]]]* +// +// CHECK: %[[T_0_0:.*]] = getelementptr inbounds [3 x %[[T]]]* %[[T_0]], i64 0, i64 0 +// CHECK: %[[T_0_0_0:.*]] = getelementptr inbounds %[[T]]* %[[T_0_0]], i32 0, i32 0 +// CHECK: store i32 1, i32* %[[T_0_0_0]] +// CHECK: %[[T_0_1:.*]] = getelementptr inbounds %[[T]]* %[[T_0_0]], i64 1 +// CHECK: %[[T_0_1_0:.*]] = getelementptr inbounds %[[T]]* %[[T_0_1]], i32 0, i32 0 +// CHECK: store i32 2, i32* %[[T_0_1_0]] +// CHECK: %[[T_0_2:.*]] = getelementptr inbounds %[[T]]* %[[T_0_1]], i64 1 +// CHECK: %[[T_0_2_0:.*]] = getelementptr inbounds %[[T]]* %[[T_0_2]], i32 0, i32 0 +// CHECK: store i32 3, i32* %[[T_0_2_0]] +// +// { 4, 5, 6 } +// +// CHECK: %[[T_1:.*]] = getelementptr inbounds [3 x %[[T]]]* %[[T_0]], i32 1 +// +// CHECK: %[[T_1_0:.*]] = getelementptr inbounds [3 x %[[T]]]* %[[T_1]], i64 0, i64 0 +// CHECK: %[[T_1_0_0:.*]] = getelementptr inbounds %[[T]]* %[[T_1_0]], i32 0, i32 0 +// CHECK: store i32 4, i32* %[[T_1_0_0]] +// CHECK: %[[T_1_1:.*]] = getelementptr inbounds %[[T]]* %[[T_1_0]], i64 1 +// CHECK: %[[T_1_1_0:.*]] = getelementptr inbounds %[[T]]* %[[T_1_1]], i32 0, i32 0 +// CHECK: store i32 5, i32* %[[T_1_1_0]] +// CHECK: %[[T_1_2:.*]] = getelementptr inbounds %[[T]]* %[[T_1_1]], i64 1 +// CHECK: %[[T_1_2_0:.*]] = getelementptr inbounds %[[T]]* %[[T_1_2]], i32 0, i32 0 +// CHECK: store i32 6, i32* %[[T_1_2_0]] +// +// And the rest gets memset to 0. +// +// CHECK: %[[T_2:.*]] = getelementptr inbounds [3 x %[[T]]]* %[[T_1]], i32 1 +// CHECK: %[[T_2_AS_T:.*]] = bitcast [3 x %[[T]]]* %[[T_2]] to %[[T]]* +// +// CHECK: %[[SIZE:.*]] = sub i64 %{{.*}}, 24 +// CHECK: %[[REST:.*]] = bitcast %[[T]]* %[[T_2_AS_T]] to i8* +// CHECK: call void @llvm.memset.p0i8.i64(i8* %[[REST]], i8 0, i64 %[[SIZE]], i32 4, i1 false) // // CHECK: } + diff --git a/clang/test/CodeGenCXX/new-array-init.cpp b/clang/test/CodeGenCXX/new-array-init.cpp index 0e925c0a..65123ea 100644 --- a/clang/test/CodeGenCXX/new-array-init.cpp +++ b/clang/test/CodeGenCXX/new-array-init.cpp @@ -6,8 +6,8 @@ void fn(int n) { // CHECK: store i32 1 // CHECK: store i32 2 // CHECK: store i32 3 - // CHECK: icmp eq i32* - // CHECK-NEXT: br i1 + // CHECK: sub {{.*}}, 12 + // CHECK: call void @llvm.memset new int[n] { 1, 2, 3 }; } @@ -31,3 +31,18 @@ void const_sufficient() { new int[4] { 1, 2, 3 }; // CHECK: ret void } + +// CHECK-LABEL: define void @_Z22check_array_value_initv +void check_array_value_init() { + struct S; + new (int S::*[3][4][5]) (); + + // CHECK: call noalias i8* @_Zna{{.}}(i{{32 240|64 480}}) + // CHECK: getelementptr inbounds i{{32|64}}* {{.*}}, i{{32|64}} 60 + + // CHECK: phi + // CHECK: store i{{32|64}} -1, + // CHECK: getelementptr inbounds i{{32|64}}* {{.*}}, i{{32|64}} 1 + // CHECK: icmp eq + // CHECK: br i1 +} -- 2.7.4