[clang] [CIR] Implement lowering for const-emitted global compound literals (PR #201152)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 2 09:46:02 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Erich Keane (erichkeane)
<details>
<summary>Changes</summary>
This came up in a test suite as a NYI, it is just emitting a constant-backing literal for an initializer. These are specific to C, as global compound literals have static storage duration in C. This patch, just like classic codgen, just creates a '.compoundliteral' object as backing for these variables, and lets us create references to them.
---
Full diff: https://github.com/llvm/llvm-project/pull/201152.diff
4 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h (+5-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+46-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+13)
- (added) clang/test/CIR/CodeGen/compound_literal.c (+67)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index 5280198524773..2591b53704a8d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -53,7 +53,7 @@ class ConstantEmitter {
/// block addresses or PredefinedExprs.
ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}
- ConstantEmitter(CIRGenModule &cgm, CIRGenFunction *cgf = nullptr)
+ ConstantEmitter(CIRGenModule &cgm, const CIRGenFunction *cgf = nullptr)
: cgm(cgm), cgf(cgf) {}
ConstantEmitter(const ConstantEmitter &other) = delete;
@@ -61,9 +61,13 @@ class ConstantEmitter {
~ConstantEmitter();
+ bool isInConstantContext() const { return inConstantContext; }
+ void setInConstantContext(bool value) { inConstantContext = value; }
+
/// Try to emit the initializer of the given declaration as an abstract
/// constant. If this succeeds, the emission must be finalized.
mlir::Attribute tryEmitForInitializer(const VarDecl &d);
+ mlir::Attribute tryEmitForInitializer(const Expr *e, QualType destType);
mlir::Attribute emitForInitializer(const APValue &value, QualType destType);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index c6346542b4b44..5208af44412a3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1439,10 +1439,48 @@ ConstantLValue ConstantLValueEmitter::VisitConstantExpr(const ConstantExpr *e) {
return {};
}
+static cir::GlobalViewAttr
+tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
+ const CompoundLiteralExpr *e) {
+ CIRGenModule &cgm = emitter.cgm;
+ CIRGenBuilderTy &builder = cgm.getBuilder();
+ CharUnits align = cgm.getASTContext().getTypeAlignInChars(e->getType());
+
+ if (cir::GlobalOp addr = cgm.getAddrOfConstantCompoundLiteralIfEmitted(e))
+ return builder.getGlobalViewAttr(addr);
+
+ assert(!cir::MissingFeatures::addressSpace());
+ mlir::Attribute c =
+ emitter.tryEmitForInitializer(e->getInitializer(), e->getType());
+ if (!c) {
+ assert(!e->isFileScope() &&
+ "file-scope compound literal did not have constant initializer!");
+ return {};
+ }
+
+ auto typedInit = mlir::cast<mlir::TypedAttr>(c);
+ bool isConstant = e->getType().isConstantStorage(cgm.getASTContext(),
+ /*ExcludeCtor=*/true,
+ /*ExcludeDtor=*/false);
+
+ std::string name = cgm.getUniqueGlobalName(".compoundliteral");
+ mlir::Location loc = cgm.getLoc(e->getSourceRange());
+ cir::GlobalOp gv =
+ cgm.createGlobalOp(loc, name, typedInit.getType(), isConstant);
+ gv.setLinkage(cir::GlobalLinkageKind::InternalLinkage);
+ gv.setAlignment(align.getAsAlign().value());
+ CIRGenModule::setInitializer(gv, c);
+
+ emitter.finalize(gv);
+ cgm.setAddrOfConstantCompoundLiteral(e, gv);
+ return builder.getGlobalViewAttr(gv);
+}
+
ConstantLValue
ConstantLValueEmitter::VisitCompoundLiteralExpr(const CompoundLiteralExpr *e) {
- cgm.errorNYI(e->getSourceRange(), "ConstantLValueEmitter: compound literal");
- return {};
+ ConstantEmitter compoundLiteralEmitter(cgm, emitter.cgf);
+ compoundLiteralEmitter.setInConstantContext(emitter.isInConstantContext());
+ return tryEmitGlobalCompoundLiteral(compoundLiteralEmitter, e);
}
ConstantLValue
@@ -1518,6 +1556,12 @@ mlir::Attribute ConstantEmitter::tryEmitForInitializer(const VarDecl &d) {
return markIfFailed(tryEmitPrivateForVarInit(d));
}
+mlir::Attribute ConstantEmitter::tryEmitForInitializer(const Expr *e,
+ QualType destType) {
+ initializeNonAbstract();
+ return markIfFailed(tryEmitPrivateForMemory(e, destType));
+}
+
mlir::Attribute ConstantEmitter::emitForInitializer(const APValue &value,
QualType destType) {
initializeNonAbstract();
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 38436fa0ea5db..70be3c43b3d88 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -447,6 +447,19 @@ class CIRGenModule : public CIRGenTypeCache {
llvm::DenseMap<mlir::Attribute, cir::GlobalOp> constantStringMap;
llvm::DenseMap<const UnnamedGlobalConstantDecl *, cir::GlobalOp>
unnamedGlobalConstantDeclMap;
+ llvm::DenseMap<const CompoundLiteralExpr *, cir::GlobalOp>
+ emittedCompoundLiterals;
+
+ cir::GlobalOp
+ getAddrOfConstantCompoundLiteralIfEmitted(const CompoundLiteralExpr *e) {
+ return emittedCompoundLiterals.lookup(e);
+ }
+ void setAddrOfConstantCompoundLiteral(const CompoundLiteralExpr *e,
+ cir::GlobalOp gv) {
+ bool ok = emittedCompoundLiterals.insert({e, gv}).second;
+ (void)ok;
+ assert(ok && "compound literal global already emitted");
+ }
/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
diff --git a/clang/test/CIR/CodeGen/compound_literal.c b/clang/test/CIR/CodeGen/compound_literal.c
new file mode 100644
index 0000000000000..d3d45f5af8e27
--- /dev/null
+++ b/clang/test/CIR/CodeGen/compound_literal.c
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
+
+int *p1 = (int[]){1, 2, 3};
+// CIR: cir.global "private" internal @".compoundliteral" = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3> {alignment = 4 : i64}
+// CIR: cir.global external @p1 = #cir.global_view<@".compoundliteral"> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @.compoundliteral = internal global [3 x i32] [i32 1, i32 2, i32 3], align 4
+// LLVM: @p1 = global ptr @.compoundliteral, align 8
+
+int *p2 = &(int){42};
+// CIR: cir.global "private" internal @".compoundliteral.1" = #cir.int<42> : !s32i {alignment = 4 : i64}
+// CIR: cir.global external @p2 = #cir.global_view<@".compoundliteral.1"> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @.compoundliteral.1 = internal global i32 42, align 4
+// LLVM: @p2 = global ptr @.compoundliteral.1, align 8
+
+struct S { int x, y; };
+struct S *p3 = &(struct S){5, 10};
+// CIR: cir.global "private" internal @".compoundliteral.2" = #cir.const_record<{#cir.int<5> : !s32i, #cir.int<10> : !s32i}> : !rec_S {alignment = 4 : i64}
+// CIR: cir.global external @p3 = #cir.global_view<@".compoundliteral.2"> : !cir.ptr<!rec_S> {alignment = 8 : i64}
+// LLVM: @.compoundliteral.2 = internal global %struct.S { i32 5, i32 10 }, align 4
+// LLVM: @p3 = global ptr @.compoundliteral.2, align 8
+
+int *p4[2] = { (int[]){1, 2}, (int[]){3, 4} };
+// CIR: cir.global "private" internal @".compoundliteral.3" = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i]> : !cir.array<!s32i x 2> {alignment = 4 : i64}
+// CIR: cir.global "private" internal @".compoundliteral.4" = #cir.const_array<[#cir.int<3> : !s32i, #cir.int<4> : !s32i]> : !cir.array<!s32i x 2> {alignment = 4 : i64}
+// CIR: cir.global external @p4 = #cir.const_array<[#cir.global_view<@".compoundliteral.3"> : !cir.ptr<!s32i>, #cir.global_view<@".compoundliteral.4"> : !cir.ptr<!s32i>]> : !cir.array<!cir.ptr<!s32i> x 2> {alignment = 16 : i64}
+// LLVM: @.compoundliteral.3 = internal global [2 x i32] [i32 1, i32 2], align 4
+// LLVM: @.compoundliteral.4 = internal global [2 x i32] [i32 3, i32 4], align 4
+// LLVM: @p4 = global [2 x ptr] [ptr @.compoundliteral.3, ptr @.compoundliteral.4], align 16
+
+struct W { int *p; };
+struct W p5 = { (int[]){10, 20} };
+// CIR: cir.global "private" internal @".compoundliteral.5" = #cir.const_array<[#cir.int<10> : !s32i, #cir.int<20> : !s32i]> : !cir.array<!s32i x 2> {alignment = 4 : i64}
+// CIR: cir.global external @p5 = #cir.const_record<{#cir.global_view<@".compoundliteral.5"> : !cir.ptr<!s32i>}> : !rec_W {alignment = 8 : i64}
+// LLVM: @.compoundliteral.5 = internal global [2 x i32] [i32 10, i32 20], align 4
+// LLVM: @p5 = global %struct.W { ptr @.compoundliteral.5 }, align 8
+
+const int *p6 = (const int[]){1, 2, 3};
+// CIR: cir.global "private" constant internal @".compoundliteral.6" = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3> {alignment = 4 : i64}
+// CIR: cir.global external @p6 = #cir.global_view<@".compoundliteral.6"> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @.compoundliteral.6 = internal constant [3 x i32] [i32 1, i32 2, i32 3], align 4
+// LLVM: @p6 = global ptr @.compoundliteral.6, align 8
+
+char *p7 = (char[]){"hi"};
+// CIR: cir.global "private" internal @".compoundliteral.7" = #cir.const_array<"hi" : !cir.array<!s8i x 2>, trailing_zeros> : !cir.array<!s8i x 3> {alignment = 1 : i64}
+// CIR: cir.global external @p7 = #cir.global_view<@".compoundliteral.7"> : !cir.ptr<!s8i> {alignment = 8 : i64}
+// LLVM: @.compoundliteral.7 = internal global [3 x i8] c"hi\00", align 1
+// LLVM: @p7 = global ptr @.compoundliteral.7, align 8
+
+int *p8 = &((int[]){10, 20, 30})[1];
+// CIR: cir.global "private" internal @".compoundliteral.8" = #cir.const_array<[#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.int<30> : !s32i]> : !cir.array<!s32i x 3> {alignment = 4 : i64}
+// CIR: cir.global external @p8 = #cir.global_view<@".compoundliteral.8", [1 : i32]> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @.compoundliteral.8 = internal global [3 x i32] [i32 10, i32 20, i32 30], align 4
+// LLVM: @p8 = global ptr getelementptr {{(inbounds nuw )?}}(i8, ptr @.compoundliteral.8, i64 4), align 8
+
+int x;
+int **p9 = (int*[]){&x, &x};
+// CIR: cir.global external @x
+// CIR: cir.global "private" internal @".compoundliteral.9" = #cir.const_array<[#cir.global_view<@x> : !cir.ptr<!s32i>, #cir.global_view<@x> : !cir.ptr<!s32i>]> : !cir.array<!cir.ptr<!s32i> x 2> {alignment = 8 : i64}
+// CIR: cir.global external @p9 = #cir.global_view<@".compoundliteral.9"> : !cir.ptr<!cir.ptr<!s32i>> {alignment = 8 : i64}
+// LLVM: @x = global i32 0, align 4
+// LLVM: @.compoundliteral.9 = internal global [2 x ptr] [ptr @x, ptr @x], align 8
+// LLVM: @p9 = global ptr @.compoundliteral.9, align 8
``````````
</details>
https://github.com/llvm/llvm-project/pull/201152
More information about the cfe-commits
mailing list