From: Richard Smith Date: Wed, 23 May 2018 23:41:38 +0000 (+0000) Subject: Use zeroinitializer for (trailing zero portion of) large array initializers X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=38a4b05cf0ab12d3aec5a7ee8f2ce1f8ad7886e5;p=clang Use zeroinitializer for (trailing zero portion of) large array initializers more reliably. This re-commits r333044 with a fix for PR37560. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@333141 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 7b076ea3e6..b26ff3d20e 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -635,6 +635,60 @@ static ConstantAddress tryEmitGlobalCompoundLiteral(CodeGenModule &CGM, return ConstantAddress(GV, Align); } +static llvm::Constant * +EmitArrayConstant(CodeGenModule &CGM, const ConstantArrayType *DestType, + 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( + CGM.getTypes().ConvertType(QualType(DestType, 0))); + } + + // Add a zeroinitializer array filler if we have lots of trailing zeroes. + unsigned TrailingZeroes = ArrayBound - NonzeroLength; + if (TrailingZeroes >= 8) { + assert(Elements.size() >= NonzeroLength && + "missing initializer for non-zero element"); + Elements.resize(NonzeroLength + 1); + auto *FillerType = + CommonElementType + ? CommonElementType + : CGM.getTypes().ConvertType(DestType->getElementType()); + FillerType = llvm::ArrayType::get(FillerType, TrailingZeroes); + Elements.back() = llvm::ConstantAggregateZero::get(FillerType); + CommonElementType = nullptr; + } else if (Elements.size() != ArrayBound) { + // Otherwise pad to the right size with the filler if necessary. + Elements.resize(ArrayBound, Filler); + if (Filler->getType() != CommonElementType) + CommonElementType = nullptr; + } + + // If all elements have the same type, just emit an array constant. + if (CommonElementType) + return llvm::ConstantArray::get( + llvm::ArrayType::get(CommonElementType, ArrayBound), Elements); + + // 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(CGM.getLLVMContext(), 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 @@ -832,68 +886,47 @@ public: } llvm::Constant *EmitArrayInitialization(InitListExpr *ILE, QualType T) { - llvm::ArrayType *AType = - cast(ConvertType(ILE->getType())); - llvm::Type *ElemTy = AType->getElementType(); + auto *CAT = CGM.getContext().getAsConstantArrayType(ILE->getType()); + assert(CAT && "can't emit array init for non-constant-bound array"); unsigned NumInitElements = ILE->getNumInits(); - unsigned NumElements = AType->getNumElements(); + unsigned NumElements = CAT->getSize().getZExtValue(); // Initialising an array requires us to automatically // initialise any elements that have not been initialised explicitly unsigned NumInitableElts = std::min(NumInitElements, NumElements); - QualType EltType = CGM.getContext().getAsArrayType(T)->getElementType(); + QualType EltType = CAT->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(CGM, CAT, CommonElementType, NumElements, Elts, + fillC); } llvm::Constant *EmitRecordInitialization(InitListExpr *ILE, QualType T) { @@ -1889,40 +1922,31 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, case APValue::Union: return ConstStructBuilder::BuildStruct(*this, Value, DestType); case APValue::Array: { - const ArrayType *CAT = CGM.getContext().getAsArrayType(DestType); + const ConstantArrayType *CAT = + CGM.getContext().getAsConstantArrayType(DestType); unsigned NumElements = Value.getArraySize(); unsigned NumInitElts = Value.getArrayInitializedElts(); // Emit array filler, if there is one. llvm::Constant *Filler = nullptr; - if (Value.hasArrayFiller()) + if (Value.hasArrayFiller()) { Filler = tryEmitAbstractForMemory(Value.getArrayFiller(), CAT->getElementType()); - - // 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); + if (!Filler) + return nullptr; } + // Emit initializer elements. 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); + + llvm::Type *CommonElementType = nullptr; + 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 +1956,8 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, 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(CGM, CAT, CommonElementType, NumElements, Elts, + Filler); } case APValue::MemberPointer: return CGM.getCXXABI().EmitMemberPointer(Value, DestType); diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index e64a0d66cb..dc9e29858d 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -751,6 +751,9 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, 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()); diff --git a/test/CodeGen/init.c b/test/CodeGen/init.c index 9924662cdd..8403320ec6 100644 --- a/test/CodeGen/init.c +++ b/test/CodeGen/init.c @@ -72,6 +72,16 @@ struct a7 { 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. diff --git a/test/CodeGenCXX/cxx11-initializer-aggregate.cpp b/test/CodeGenCXX/cxx11-initializer-aggregate.cpp index 8bf35966f5..325607f69f 100644 --- a/test/CodeGenCXX/cxx11-initializer-aggregate.cpp +++ b/test/CodeGenCXX/cxx11-initializer-aggregate.cpp @@ -11,6 +11,42 @@ namespace NonAggregateCopyInAggregateInit { // PR32044 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}}; +} + +namespace PR37560 { + union U { + char x; + int a; + }; + // FIXME: [dcl.init]p2, the padding bits of the union object should be + // initialized to 0, not undef, which would allow us to collapse the tail + // of these arrays to zeroinitializer. + // CHECK-DAG: @_ZN7PR375601cE = global <{ { i8, [3 x i8] } }> <{ { i8, [3 x i8] } { i8 0, [3 x i8] undef } }> + U c[1] = {}; + // CHECK-DAG: @_ZN7PR375601dE = global {{.*}} <{ { i8, [3 x i8] } { i8 97, [3 x i8] undef }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 98, [3 x i8] undef }, { i8, [3 x i8] } { i8 0, [3 x i8] undef }, + U d[16] = {'a', {.a = 123}, 'b'}; + // CHECK-DAG: @_ZN7PR375601eE = global {{.*}} <{ %"{{[^"]*}}" { i32 123 }, %"{{[^"]*}}" { i32 456 }, { i8, [3 x i8] } { i8 0, [3 x i8] undef }, + U e[16] = {{.a = 123}, {.a = 456}}; + + union V { + int a; + char x; + }; + // CHECK-DAG: @_ZN7PR375601fE = global [1 x %"{{[^"]*}}"] zeroinitializer + V f[1] = {}; + // CHECK-DAG: @_ZN7PR375601gE = global {{.*}} <{ { i8, [3 x i8] } { i8 97, [3 x i8] undef }, %"{{[^"]*}}" { i32 123 }, { i8, [3 x i8] } { i8 98, [3 x i8] undef }, [13 x %"{{[^"]*}}"] zeroinitializer }> + V g[16] = {{.x = 'a'}, {.a = 123}, {.x = 'b'}}; + // CHECK-DAG: @_ZN7PR375601hE = global {{.*}} <{ %"{{[^"]*}}" { i32 123 }, %"{{[^"]*}}" { i32 456 }, [14 x %"{{[^"]*}}"] zeroinitializer }> + V h[16] = {{.a = 123}, {.a = 456}}; + // CHECK-DAG: @_ZN7PR375601iE = global [4 x %"{{[^"]*}}"] [%"{{[^"]*}}" { i32 123 }, %"{{[^"]*}}" { i32 456 }, %"{{[^"]*}}" zeroinitializer, %"{{[^"]*}}" zeroinitializer] + V i[4] = {{.a = 123}, {.a = 456}}; +} + // CHECK-LABEL: define {{.*}}@_Z3fn1i( int fn1(int x) { // CHECK: %[[INITLIST:.*]] = alloca %struct.A @@ -51,3 +87,35 @@ namespace NonTrivialInit { // 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 +} diff --git a/test/SemaCXX/aggregate-initialization.cpp b/test/SemaCXX/aggregate-initialization.cpp index 514473f9bc..0dfb61333e 100644 --- a/test/SemaCXX/aggregate-initialization.cpp +++ b/test/SemaCXX/aggregate-initialization.cpp @@ -180,3 +180,9 @@ namespace IdiomaticStdArrayInitDoesNotWarn { #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}}; +}