Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -635,6 +635,52 @@ return ConstantAddress(GV, Align); } +static llvm::Constant * +EmitArrayConstant(llvm::ArrayType *PreferredArrayType, + llvm::Type *CommonElementType, unsigned ArrayBound, + SmallVectorImpl &Elements, + llvm::Constant *Filler) { + // Figure out how long the initial prefix of non-zero elements is. + unsigned NonzeroLength = ArrayBound; + if (Elements.size() < NonzeroLength && Filler->isNullValue()) + NonzeroLength = Elements.size(); + if (NonzeroLength == Elements.size()) { + while (NonzeroLength > 0 && Elements[NonzeroLength - 1]->isNullValue()) + --NonzeroLength; + } + + if (NonzeroLength == 0) + return llvm::ConstantAggregateZero::get(PreferredArrayType); + + // If there's not many trailing zero elements, just emit an array + // constant. + if (NonzeroLength + 8 >= ArrayBound && CommonElementType) { + Elements.resize(ArrayBound, Filler); + return llvm::ConstantArray::get( + llvm::ArrayType::get(CommonElementType, ArrayBound), Elements); + } + + // Add a zeroinitializer array filler if we have trailing zeroes. + if (unsigned TrailingZeroes = ArrayBound - NonzeroLength) { + assert(Elements.size() >= NonzeroLength && + "missing initializer for non-zero element"); + Elements.resize(NonzeroLength + 1); + auto *FillerType = PreferredArrayType->getElementType(); + if (TrailingZeroes > 1) + FillerType = llvm::ArrayType::get(FillerType, TrailingZeroes); + Elements.back() = llvm::ConstantAggregateZero::get(FillerType); + } + + // We have mixed types. Use a packed struct. + llvm::SmallVector Types; + Types.reserve(Elements.size()); + for (llvm::Constant *Elt : Elements) + Types.push_back(Elt->getType()); + llvm::StructType *SType = + llvm::StructType::get(PreferredArrayType->getContext(), Types, true); + return llvm::ConstantStruct::get(SType, Elements); +} + /// This class only needs to handle two cases: /// 1) Literals (this is used by APValue emission to emit literals). /// 2) Arrays, structs and unions (outside C++11 mode, we don't currently @@ -834,7 +880,6 @@ llvm::Constant *EmitArrayInitialization(InitListExpr *ILE, QualType T) { llvm::ArrayType *AType = cast(ConvertType(ILE->getType())); - llvm::Type *ElemTy = AType->getElementType(); unsigned NumInitElements = ILE->getNumInits(); unsigned NumElements = AType->getNumElements(); @@ -845,55 +890,35 @@ QualType EltType = CGM.getContext().getAsArrayType(T)->getElementType(); // Initialize remaining array elements. - llvm::Constant *fillC; - if (Expr *filler = ILE->getArrayFiller()) + llvm::Constant *fillC = nullptr; + if (Expr *filler = ILE->getArrayFiller()) { fillC = Emitter.tryEmitAbstractForMemory(filler, EltType); - else - fillC = Emitter.emitNullForMemory(EltType); - if (!fillC) - return nullptr; - - // Try to use a ConstantAggregateZero if we can. - if (fillC->isNullValue() && !NumInitableElts) - return llvm::ConstantAggregateZero::get(AType); + if (!fillC) + return nullptr; + } // Copy initializer elements. SmallVector Elts; - Elts.reserve(std::max(NumInitableElts, NumElements)); + if (fillC && fillC->isNullValue()) + Elts.reserve(NumInitableElts + 1); + else + Elts.reserve(NumElements); - bool RewriteType = false; - bool AllNullValues = true; + llvm::Type *CommonElementType = nullptr; for (unsigned i = 0; i < NumInitableElts; ++i) { Expr *Init = ILE->getInit(i); llvm::Constant *C = Emitter.tryEmitPrivateForMemory(Init, EltType); if (!C) return nullptr; - RewriteType |= (C->getType() != ElemTy); + if (i == 0) + CommonElementType = C->getType(); + else if (C->getType() != CommonElementType) + CommonElementType = nullptr; Elts.push_back(C); - if (AllNullValues && !C->isNullValue()) - AllNullValues = false; } - // If all initializer elements are "zero," then avoid storing NumElements - // instances of the zero representation. - if (AllNullValues) - return llvm::ConstantAggregateZero::get(AType); - - RewriteType |= (fillC->getType() != ElemTy); - Elts.resize(NumElements, fillC); - - if (RewriteType) { - // FIXME: Try to avoid packing the array - std::vector Types; - Types.reserve(Elts.size()); - for (unsigned i = 0, e = Elts.size(); i < e; ++i) - Types.push_back(Elts[i]->getType()); - llvm::StructType *SType = llvm::StructType::get(AType->getContext(), - Types, true); - return llvm::ConstantStruct::get(SType, Elts); - } - - return llvm::ConstantArray::get(AType, Elts); + return EmitArrayConstant(AType, CommonElementType, NumElements, Elts, + fillC); } llvm::Constant *EmitRecordInitialization(InitListExpr *ILE, QualType T) { @@ -1895,34 +1920,28 @@ // Emit array filler, if there is one. llvm::Constant *Filler = nullptr; - if (Value.hasArrayFiller()) + if (Value.hasArrayFiller()) { Filler = tryEmitAbstractForMemory(Value.getArrayFiller(), CAT->getElementType()); + if (!Filler) + return nullptr; + } // Emit initializer elements. llvm::Type *CommonElementType = CGM.getTypes().ConvertType(CAT->getElementType()); - - // Try to use a ConstantAggregateZero if we can. - if (Filler && Filler->isNullValue() && !NumInitElts) { - llvm::ArrayType *AType = - llvm::ArrayType::get(CommonElementType, NumElements); - return llvm::ConstantAggregateZero::get(AType); - } + llvm::ArrayType *PreferredArrayType = + llvm::ArrayType::get(CommonElementType, NumElements); SmallVector Elts; - Elts.reserve(NumElements); - for (unsigned I = 0; I < NumElements; ++I) { - llvm::Constant *C = Filler; - if (I < NumInitElts) { - C = tryEmitPrivateForMemory(Value.getArrayInitializedElt(I), - CAT->getElementType()); - } else if (!Filler) { - assert(Value.hasArrayFiller() && - "Missing filler for implicit elements of initializer"); - C = tryEmitPrivateForMemory(Value.getArrayFiller(), - CAT->getElementType()); - } + if (Filler && Filler->isNullValue()) + Elts.reserve(NumInitElts + 1); + else + Elts.reserve(NumElements); + + for (unsigned I = 0; I < NumInitElts; ++I) { + llvm::Constant *C = tryEmitPrivateForMemory( + Value.getArrayInitializedElt(I), CAT->getElementType()); if (!C) return nullptr; if (I == 0) @@ -1932,20 +1951,8 @@ Elts.push_back(C); } - if (!CommonElementType) { - // FIXME: Try to avoid packing the array - std::vector Types; - Types.reserve(NumElements); - for (unsigned i = 0, e = Elts.size(); i < e; ++i) - Types.push_back(Elts[i]->getType()); - llvm::StructType *SType = - llvm::StructType::get(CGM.getLLVMContext(), Types, true); - return llvm::ConstantStruct::get(SType, Elts); - } - - llvm::ArrayType *AType = - llvm::ArrayType::get(CommonElementType, NumElements); - return llvm::ConstantArray::get(AType, Elts); + return EmitArrayConstant(PreferredArrayType, CommonElementType, NumElements, + Elts, Filler); } case APValue::MemberPointer: return CGM.getCXXABI().EmitMemberPointer(Value, DestType); Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -751,6 +751,9 @@ ElementEntity.getKind() == InitializedEntity::EK_VectorElement) ElementEntity.setElementIndex(Init); + if (Init >= NumInits && ILE->hasArrayFiller()) + return; + Expr *InitExpr = (Init < NumInits ? ILE->getInit(Init) : nullptr); if (!InitExpr && Init < NumInits && ILE->hasArrayFiller()) ILE->setInit(Init, ILE->getArrayFiller()); Index: test/CodeGen/init.c =================================================================== --- test/CodeGen/init.c +++ test/CodeGen/init.c @@ -72,6 +72,16 @@ struct a7 test7 = { .b = 0, .v = "bar" }; +// CHECK-DAG: @huge_array = global {{.*}} <{ i32 1, i32 0, i32 2, i32 0, i32 3, [999999995 x i32] zeroinitializer }> +int huge_array[1000000000] = {1, 0, 2, 0, 3, 0, 0, 0}; + +// CHECK-DAG: @huge_struct = global {{.*}} { i32 1, <{ i32, [999999999 x i32] }> <{ i32 2, [999999999 x i32] zeroinitializer }> } +struct Huge { + int a; + int arr[1000 * 1000 * 1000]; +} huge_struct = {1, {2, 0, 0, 0}}; + + // PR279 comment #3 char test8(int X) { char str[100000] = "abc"; // tail should be memset. Index: test/CodeGenCXX/cxx11-initializer-aggregate.cpp =================================================================== --- test/CodeGenCXX/cxx11-initializer-aggregate.cpp +++ test/CodeGenCXX/cxx11-initializer-aggregate.cpp @@ -11,6 +11,13 @@ struct C { A &&p; } c{{1}}; } +namespace NearlyZeroInit { + // CHECK-DAG: @_ZN14NearlyZeroInit1aE = global {{.*}} <{ i32 1, i32 2, i32 3, [120 x i32] zeroinitializer }> + int a[123] = {1, 2, 3}; + // CHECK-DAG: @_ZN14NearlyZeroInit1bE = global {{.*}} { i32 1, <{ i32, [2147483647 x i32] }> <{ i32 2, [2147483647 x i32] zeroinitializer }> } + struct B { int n; int arr[1024 * 1024 * 1024 * 2u]; } b = {1, {2}}; +} + // CHECK-LABEL: define {{.*}}@_Z3fn1i( int fn1(int x) { // CHECK: %[[INITLIST:.*]] = alloca %struct.A @@ -51,3 +58,35 @@ // meaningful. B b[30] = {}; } + +namespace ZeroInit { + enum { Zero, One }; + constexpr int zero() { return 0; } + constexpr int *null() { return nullptr; } + struct Filler { + int x; + Filler(); + }; + struct S1 { + int x; + }; + + // These declarations, if implemented elementwise, require huge + // amout of memory and compiler time. + unsigned char data_1[1024 * 1024 * 1024 * 2u] = { 0 }; + unsigned char data_2[1024 * 1024 * 1024 * 2u] = { Zero }; + unsigned char data_3[1024][1024][1024] = {{{0}}}; + unsigned char data_4[1024 * 1024 * 1024 * 2u] = { zero() }; + int *data_5[1024 * 1024 * 512] = { nullptr }; + int *data_6[1024 * 1024 * 512] = { null() }; + struct S1 data_7[1024 * 1024 * 512] = {{0}}; + char data_8[1000 * 1000 * 1000] = {}; + int (&&data_9)[1000 * 1000 * 1000] = {0}; + unsigned char data_10[1024 * 1024 * 1024 * 2u] = { 1 }; + unsigned char data_11[1024 * 1024 * 1024 * 2u] = { One }; + unsigned char data_12[1024][1024][1024] = {{{1}}}; + + // This variable must be initialized elementwise. + Filler data_e1[1024] = {}; + // CHECK: getelementptr inbounds {{.*}} @_ZN8ZeroInit7data_e1E +} Index: test/SemaCXX/aggregate-initialization.cpp =================================================================== --- test/SemaCXX/aggregate-initialization.cpp +++ test/SemaCXX/aggregate-initialization.cpp @@ -180,3 +180,9 @@ #pragma clang diagnostic pop } + +namespace HugeArraysUseArrayFiller { + // All we're checking here is that initialization completes in a reasonable + // amount of time. + struct A { int n; int arr[1000 * 1000 * 1000]; } a = {1, {2}}; +}