https://github.com/AbdallahRashed updated https://github.com/llvm/llvm-project/pull/205641
>From 344578c20617f801691ba325bb99621c01be9f44 Mon Sep 17 00:00:00 2001 From: AbdallahRashed <[email protected]> Date: Sun, 31 May 2026 20:17:08 +0200 Subject: [PATCH] [CIR][CodeGen] Implement ConstExprEmitter array initialization from InitListExpr Implement the VisitInitListExpr array path in ConstExprEmitter, which handles constant array initialization from InitListExpr nodes directly. Previously this path returned null, falling back to non-constant initialization. Now arrays are emitted as proper CIR constant arrays via emitArrayConstant. This produces better output for string literal arrays in array initializers (compact string representation instead of per-character int arrays) and enables constant-folding of array globals that go through the ConstExprEmitter visitor. Remove the constEmitterArrayILE MissingFeatures flag. --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 55 +++++++++++++++++--- clang/test/CIR/CodeGen/agg-expr-lvalue.c | 2 +- clang/test/CIR/CodeGen/const-array-init.c | 48 +++++++++++++++++ 4 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 clang/test/CIR/CodeGen/const-array-init.c diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 9a1546fe14e65..c4acd6b6b9df9 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -205,7 +205,6 @@ struct MissingFeatures { static bool cleanupAfterErrorDiags() { return false; } static bool cleanupDeactivationScope() { return false; } static bool cleanupsToDeactivate() { return false; } - static bool constEmitterArrayILE() { return false; } static bool constEmitterVectorILE() { return false; } static bool constantFoldSwitchStatement() { return false; } static bool constructABIArgDirectExtend() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index c29b66ac2f8bc..c9d212770f6ab 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -1048,16 +1048,59 @@ class ConstExprEmitter return cgm.getBuilder().getZeroInitAttr(cgm.convertType(t)); } + mlir::Attribute emitArrayInitialization(InitListExpr *ile, QualType t) { + auto *cat = cgm.getASTContext().getAsConstantArrayType(ile->getType()); + assert(cat && "can't emit array init for non-constant-bound array"); + const unsigned numInitElements = ile->getNumInits(); + const unsigned numElements = cat->getZExtSize(); + + // Initialising an array requires us to automatically + // initialise any elements that have not been initialised explicitly. + unsigned numInitableElts = std::min(numInitElements, numElements); + + QualType eltType = cat->getElementType(); + + // Initialize remaining array elements. + mlir::TypedAttr fillC; + if (const Expr *filler = ile->getArrayFiller()) { + fillC = mlir::dyn_cast_or_null<mlir::TypedAttr>( + emitter.tryEmitPrivateForMemory(filler, eltType)); + if (!fillC) + return {}; + } + + // Copy initializer elements. + SmallVector<mlir::TypedAttr, 16> elts; + if (fillC && cgm.getBuilder().isNullValue(fillC)) + elts.reserve(numInitableElts + 1); + else + elts.reserve(numElements); + + mlir::Type commonElementType; + for (unsigned i = 0; i < numInitableElts; ++i) { + const Expr *init = ile->getInit(i); + mlir::Attribute c = emitter.tryEmitPrivateForMemory(init, eltType); + if (!c) + return {}; + auto typedC = mlir::cast<mlir::TypedAttr>(c); + if (i == 0) + commonElementType = typedC.getType(); + else if (typedC.getType() != commonElementType) + commonElementType = {}; + elts.push_back(typedC); + } + + mlir::Type desiredType = cgm.convertType(ile->getType()); + return emitArrayConstant(cgm, desiredType, commonElementType, numElements, + elts, fillC); + } + mlir::Attribute VisitInitListExpr(InitListExpr *ile, QualType t) { if (ile->isTransparent()) return Visit(ile->getInit(0), t); - if (ile->getType()->isArrayType()) { - // If we return null here, the non-constant initializer will take care of - // it, but we would prefer to handle it here. - assert(!cir::MissingFeatures::constEmitterArrayILE()); - return {}; - } + if (ile->getType()->isArrayType()) + return emitArrayInitialization(ile, t); if (ile->getType()->isRecordType()) { return ConstRecordBuilder::buildRecord(emitter, ile, t); diff --git a/clang/test/CIR/CodeGen/agg-expr-lvalue.c b/clang/test/CIR/CodeGen/agg-expr-lvalue.c index 51109d3082ab0..f230ed8c3b785 100644 --- a/clang/test/CIR/CodeGen/agg-expr-lvalue.c +++ b/clang/test/CIR/CodeGen/agg-expr-lvalue.c @@ -21,7 +21,7 @@ void test_member_in_array(void) { } // CIR-DAG: cir.global "private" constant cir_private @[[LINE_CONST:.*]] = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i}> : !rec_Point, #cir.const_record<{#cir.int<3> : !s32i, #cir.int<4> : !s32i}> : !rec_Point}> : !rec_Line -// CIR-DAG: cir.global "private" constant cir_private @[[MATRIX_CONST:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<104> : !s8i, #cir.int<101> : !s8i, #cir.int<108> : !s8i, #cir.int<108> : !s8i, #cir.int<111> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>, #cir.const_array<[#cir.int<119> : !s8i, #cir.int<111> : !s8i, #cir.int<114> : !s8i, #cir.int<108> : !s8i, #cir.int<100> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>]> +// CIR-DAG: cir.global "private" constant cir_private @[[MATRIX_CONST:.*]] = #cir.const_array<[#cir.const_array<"hello" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6>, #cir.const_array<"world" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6>]> // LLVM-DAG: @[[LINE_CONST:.*]] = private constant %struct.Line { %struct.Point { i32 1, i32 2 }, %struct.Point { i32 3, i32 4 } } // LLVM-DAG: @[[MATRIX_CONST:.*]] = private constant [2 x [6 x i8]] {{.*}} diff --git a/clang/test/CIR/CodeGen/const-array-init.c b/clang/test/CIR/CodeGen/const-array-init.c new file mode 100644 index 0000000000000..0a3f390899ce0 --- /dev/null +++ b/clang/test/CIR/CodeGen/const-array-init.c @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// Fully initialized array. +int arr1[5] = {1, 2, 3, 4, 5}; +// CIR: cir.global external @arr1 = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i]> : !cir.array<!s32i x 5> +// LLVM: @arr1 = global [5 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5] +// OGCG: @arr1 = global [5 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5] + +// Partial initialization with implicit zero fill (< 8 trailing zeros). +int arr2[7] = {1, 2, 3}; +// CIR: cir.global external @arr2 = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 7> +// LLVM: @arr2 = global [7 x i32] [i32 1, i32 2, i32 3, i32 0, i32 0, i32 0, i32 0] +// OGCG: @arr2 = global [7 x i32] [i32 1, i32 2, i32 3, i32 0, i32 0, i32 0, i32 0] + +// All-zero array from {0}. +int arr3[4] = {0}; +// CIR: cir.global external @arr3 = #cir.zero : !cir.array<!s32i x 4> +// LLVM: @arr3 = global [4 x i32] zeroinitializer +// OGCG: @arr3 = global [4 x i32] zeroinitializer + +// Nested 2D array. +int arr4[2][3] = {{1, 2, 3}, {4, 5, 6}}; +// CIR: cir.global external @arr4 = #cir.const_array<[#cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>, #cir.const_array<[#cir.int<4> : !s32i, #cir.int<5> : !s32i, #cir.int<6> : !s32i]> : !cir.array<!s32i x 3>]> : !cir.array<!cir.array<!s32i x 3> x 2> +// LLVM: @arr4 = global [2 x [3 x i32]] {{\[}}[3 x i32] [i32 1, i32 2, i32 3], [3 x i32] [i32 4, i32 5, i32 6]] +// OGCG: @arr4 = global [2 x [3 x i32]] {{\[}}[3 x i32] [i32 1, i32 2, i32 3], [3 x i32] [i32 4, i32 5, i32 6]] + +// Float array. +float arr5[3] = {1.0f, 2.0f, 3.0f}; +// CIR: cir.global external @arr5 = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.float, #cir.fp<2.000000e+00> : !cir.float, #cir.fp<3.000000e+00> : !cir.float]> : !cir.array<!cir.float x 3> +// LLVM: @arr5 = global [3 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00] +// OGCG: @arr5 = global [3 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00] + +// Large trailing zeros (>= 8) triggers struct packing in emitArrayConstant. +int arr6[20] = {1, 2, 3}; +// CIR: cir.global external @arr6 = #cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.zero : !cir.array<!s32i x 17>}> +// LLVM: @arr6 = global <{ i32, i32, i32, [17 x i32] }> <{ i32 1, i32 2, i32 3, [17 x i32] zeroinitializer }> +// OGCG: @arr6 = global <{ i32, i32, i32, [17 x i32] }> <{ i32 1, i32 2, i32 3, [17 x i32] zeroinitializer }> + +// Char array from string literal. +char str[] = "hello"; +// CIR: cir.global external @str = #cir.const_array<"hello" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> +// LLVM: @str = global [6 x i8] c"hello\00" +// OGCG: @str = global [6 x i8] c"hello\00" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
