[clang] [CIR][CodeGen] Support DesignatedInitUpdateExpr in constant emission (PR #194238)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 13:06:20 PDT 2026
https://github.com/AbdallahRashed updated https://github.com/llvm/llvm-project/pull/194238
>From 7aa125fdba002b6a52fd59bc09d48a9b3f267690 Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Sun, 26 Apr 2026 17:04:55 +0200
Subject: [PATCH] [CIR][CodeGen] Support DesignatedInitUpdateExpr in constant
emission
---
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 94 ++++++++++++++++++-
.../test/CIR/CodeGen/designated-init-update.c | 36 +++++++
3 files changed, 126 insertions(+), 5 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/designated-init-update.c
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 634d4dd83b3f8..fc87150b98eef 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -230,6 +230,7 @@ struct MissingFeatures {
static bool cleanupDeactivationScope() { return false; }
static bool cleanupWithPreservedValues() { return false; }
static bool cleanupsToDeactivate() { return false; }
+ static bool constEmitterAbstractForMemory() { return false; }
static bool constEmitterAggILE() { return false; }
static bool constEmitterArrayILE() { return false; }
static bool constEmitterVectorILE() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index a8b24fb686088..9616b970da2d4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -626,6 +626,11 @@ bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout,
return true;
}
+static bool emitDesignatedInitUpdater(ConstantEmitter &emitter,
+ ConstantAggregateBuilder &constant,
+ CharUnits offset, QualType type,
+ const InitListExpr *updater);
+
bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
RecordDecl *rd = ile->getType()->castAsRecordDecl();
const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
@@ -679,8 +684,19 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
// a new constant to emit independently.
if (allowOverwrite &&
(field->getType()->isArrayType() || field->getType()->isRecordType())) {
- cgm.errorNYI(field->getSourceRange(), "designated init lists");
- return false;
+ if (auto *subILE = dyn_cast<InitListExpr>(init)) {
+ CharUnits fieldOffset = cgm.getASTContext().toCharUnitsFromBits(
+ layout.getFieldOffset(index));
+ if (!emitDesignatedInitUpdater(emitter, builder,
+ startOffset + fieldOffset,
+ field->getType(), subILE))
+ return false;
+ // If we split apart the field's value, try to collapse it down to a
+ // single value now.
+ builder.condense(startOffset + fieldOffset,
+ cgm.getTypes().convertTypeForMem(field->getType()));
+ continue;
+ }
}
mlir::Attribute eltInitAttr =
@@ -868,6 +884,65 @@ bool ConstRecordBuilder::updateRecord(ConstantEmitter &emitter,
.build(updater, /*allowOverwrite*/ true);
}
+static bool emitDesignatedInitUpdater(ConstantEmitter &emitter,
+ ConstantAggregateBuilder &constant,
+ CharUnits offset, QualType type,
+ const InitListExpr *updater) {
+ if (type->isRecordType())
+ return ConstRecordBuilder::updateRecord(
+ emitter, constant, offset, const_cast<InitListExpr *>(updater));
+
+ auto *cat = emitter.cgm.getASTContext().getAsConstantArrayType(type);
+ if (!cat)
+ return false;
+ QualType elemType = cat->getElementType();
+ CharUnits elemSize = emitter.cgm.getASTContext().getTypeSizeInChars(elemType);
+ mlir::Type elemTy = emitter.cgm.getTypes().convertTypeForMem(elemType);
+
+ mlir::TypedAttr fillC;
+ if (const Expr *filler = updater->getArrayFiller()) {
+ if (!isa<NoInitExpr>(filler)) {
+ // Classic codegen uses tryEmitAbstractForMemory here to track abstract
+ // state for pointer authentication. We should use that when implemented.
+ assert(!cir::MissingFeatures::constEmitterAbstractForMemory());
+ mlir::Attribute result =
+ emitter.tryEmitPrivateForMemory(filler, elemType);
+ if (!result)
+ return false;
+ fillC = mlir::cast<mlir::TypedAttr>(result);
+ }
+ }
+
+ unsigned numElementsToUpdate =
+ fillC ? cat->getZExtSize() : updater->getNumInits();
+ for (unsigned i = 0; i != numElementsToUpdate; ++i, offset += elemSize) {
+ const Expr *init = nullptr;
+ if (i < updater->getNumInits())
+ init = updater->getInit(i);
+
+ if (!init && fillC) {
+ if (!constant.add(fillC, offset, true))
+ return false;
+ } else if (!init || isa<NoInitExpr>(init)) {
+ continue;
+ } else if (const auto *childILE = dyn_cast<InitListExpr>(init)) {
+ if (!emitDesignatedInitUpdater(emitter, constant, offset, elemType,
+ childILE))
+ return false;
+ // Attempt to reduce the array element to a single constant if necessary.
+ constant.condense(offset, elemTy);
+ } else {
+ mlir::Attribute val = emitter.tryEmitPrivateForMemory(init, elemType);
+ if (!val)
+ return false;
+ if (!constant.add(mlir::cast<mlir::TypedAttr>(val), offset, true))
+ return false;
+ }
+ }
+
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// ConstExprEmitter
//===----------------------------------------------------------------------===//
@@ -1074,9 +1149,18 @@ class ConstExprEmitter
if (!c)
return {};
- cgm.errorNYI(e->getBeginLoc(),
- "ConstExprEmitter::VisitDesignatedInitUpdateExpr");
- return {};
+ ConstantAggregateBuilder constant(cgm);
+ constant.add(mlir::cast<mlir::TypedAttr>(c), CharUnits::Zero(), false);
+
+ if (!emitDesignatedInitUpdater(emitter, constant, CharUnits::Zero(),
+ destType, e->getUpdater()))
+ return {};
+
+ mlir::Type valTy = cgm.convertType(destType);
+ bool hasFlexibleArray = false;
+ if (const auto *rd = destType->getAsRecordDecl())
+ hasFlexibleArray = rd->hasFlexibleArrayMember();
+ return constant.build(valTy, hasFlexibleArray);
}
mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) {
diff --git a/clang/test/CIR/CodeGen/designated-init-update.c b/clang/test/CIR/CodeGen/designated-init-update.c
new file mode 100644
index 0000000000000..6c75d2abe1710
--- /dev/null
+++ b/clang/test/CIR/CodeGen/designated-init-update.c
@@ -0,0 +1,36 @@
+// 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
+
+struct S {
+ int a, b, c;
+};
+
+// Basic designated init update: start from {1, 2, 3}, override .b = 20
+struct S g1 = (struct S){1, 2, 3, .b = 20};
+
+// CIR: cir.global external @g1 = #cir.const_record<{#cir.int<1> : !s32i, #cir.int<20> : !s32i, #cir.int<3> : !s32i}> : !rec_S
+// LLVM: @g1 = global %struct.S { i32 1, i32 20, i32 3 }
+// OGCG: @g1 = global %struct.S { i32 1, i32 20, i32 3 }
+
+// Multiple field overrides
+struct S g2 = (struct S){10, 20, 30, .a = 100, .c = 300};
+
+// CIR: cir.global external @g2 = #cir.const_record<{#cir.int<100> : !s32i, #cir.int<20> : !s32i, #cir.int<300> : !s32i}> : !rec_S
+// LLVM: @g2 = global %struct.S { i32 100, i32 20, i32 300 }
+// OGCG: @g2 = global %struct.S { i32 100, i32 20, i32 300 }
+
+// Nested struct with designated init update
+struct Outer {
+ struct S inner;
+ int x;
+};
+
+struct Outer g3 = (struct Outer){{1, 2, 3}, 4, .inner.b = 50};
+
+// CIR: cir.global external @g3 = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<50> : !s32i, #cir.int<3> : !s32i}> : !rec_S, #cir.int<4> : !s32i}> : !rec_Outer
+// LLVM: @g3 = global %struct.Outer { %struct.S { i32 1, i32 50, i32 3 }, i32 4 }
+// OGCG: @g3 = global %struct.Outer { %struct.S { i32 1, i32 50, i32 3 }, i32 4 }
More information about the cfe-commits
mailing list