diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 548cd488900a042d739b03c1ff73d7ae3c1ba184..c6995d8a1b29316c06ebff58f19093cb658a171b 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/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<InitListExpr>(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<ConstantArrayType>( 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<InitListExpr>(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<llvm::ConstantInt>(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<CXXConstructExpr>(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<ImplicitValueInitExpr>(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<llvm::ConstantInt>(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<CXXConstructExpr>(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<ImplicitValueInitExpr>(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/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 750bec8b07fbb62fc820d36dddf6c6e88d6638c7..d1e63a710bcf970247c991c7967acfe1416478f8 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/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/test/CodeGenCXX/cxx11-initializer-array-new.cpp b/test/CodeGenCXX/cxx11-initializer-array-new.cpp index 8de88dcefb02236c5538d65b65dd46e27e6389f6..2393939b031d6fcfb1fca8023d82de0ac19ddda6 100644 --- a/test/CodeGenCXX/cxx11-initializer-array-new.cpp +++ b/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/test/CodeGenCXX/new-array-init.cpp b/test/CodeGenCXX/new-array-init.cpp index 0e925c0a67ebfb322aad7b63aa5d17699c3f0150..65123ea7feefe8592fdc2f7ca1e3e72b6a8c7af4 100644 --- a/test/CodeGenCXX/new-array-init.cpp +++ b/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 +}