[clang] [CIR] Implement aggregate-expr-init with a const-expr lowering (PR #190631)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 6 09:44:25 PDT 2026
https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/190631
An aggregate initialized by a ConstantExpr happens when you have a consteval constructor. This patch implements this lowering.
There is a bit of a difference in decisions between classic-codegen and cir lowering, where classic-codegen will do init-via-GEP, whereas CIR does either a struct-store, or a memcpy, otherwise these seem identical.
There IS a branch (if emitting a constant-expr fails) that appears dead to the best of my knowledge, but since classic-codegen does it, I left the version there. It is at most a pessimisation of the constant-emit (just emitting the expression), so it shouldn't cause problems.
>From d48f8ad7eeee38802a943532991e7d528bc8747e Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Mon, 6 Apr 2026 09:41:50 -0700
Subject: [PATCH] [CIR] Implement aggregate-expr-init with a const-expr
lowering
An aggregate initialized by a ConstantExpr happens when you have a
consteval constructor. This patch implements this lowering.
There is a bit of a difference in decisions between classic-codegen and
cir lowering, where classic-codegen will do init-via-GEP, whereas CIR
does either a struct-store, or a memcpy, otherwise these seem identical.
There IS a branch (if emitting a constant-expr fails) that appears dead
to the best of my knowledge, but since classic-codegen does it, I left
the version there. It is at most a pessimisation of the constant-emit
(just emitting the expression), so it shouldn't cause problems.
---
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 15 ++++++-
clang/test/CIR/CodeGen/agg-init-constexpr.cpp | 39 +++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/agg-init-constexpr.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 8e63a64b3e664..169a26ca3d375 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "CIRGenBuilder.h"
+#include "CIRGenConstantEmitter.h"
#include "CIRGenFunction.h"
#include "CIRGenValue.h"
#include "mlir/IR/Builders.h"
@@ -303,7 +304,19 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
"AggExprEmitter: VisitSubstNonTypeTemplateParmExpr");
}
void VisitConstantExpr(ConstantExpr *e) {
- cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitConstantExpr");
+ ensureDest(cgf.getLoc(e->getSourceRange()), e->getType());
+
+ if (mlir::Attribute result = ConstantEmitter(cgf).tryEmitConstantExpr(e)) {
+ mlir::Value resultVal = cgf.getBuilder().getConstant(
+ cgf.getLoc(e->getSourceRange()), mlir::cast<mlir::TypedAttr>(result));
+ LValue destLVal = cgf.makeAddrLValue(dest.getAddress(), e->getType());
+ cgf.emitStoreThroughLValue(RValue::get(resultVal), destLVal);
+ return;
+ }
+
+ // It isn't clear that it is possible to get to here, but this branch is
+ // present in classic codegen, so we leave it here too.
+ return Visit(e->getSubExpr());
}
void VisitMemberExpr(MemberExpr *e) { emitAggLoadOfLValue(e); }
void VisitUnaryDeref(UnaryOperator *e) { emitAggLoadOfLValue(e); }
diff --git a/clang/test/CIR/CodeGen/agg-init-constexpr.cpp b/clang/test/CIR/CodeGen/agg-init-constexpr.cpp
new file mode 100644
index 0000000000000..8cef9ef33bda5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/agg-init-constexpr.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -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 -std=c++20 -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 -std=c++20 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct HasVal {
+ int x = 5;
+};
+struct WithCtor {
+ consteval WithCtor(int a, long b) : x(a*a), y(a*b) {}
+ int x;
+ long y;
+ HasVal a;
+};
+
+extern "C" void construct() {
+ WithCtor c(2,5);
+}
+
+// CIR-LABEL: construct()
+// CIR-NEXT: %[[WC_ALLOCA:.*]] = cir.alloca !rec_WithCtor
+// CIR-NEXT: %[[CONST_VAL:.*]] = cir.const #cir.const_record<{#cir.int<4> : !s32i, #cir.int<10> : !s64i, #cir.const_record<{#cir.int<5> : !s32i}> : !rec_HasVal}>
+// CIR-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[WC_ALLOCA]]
+// CIR-NEXT: cir.store{{.*}}%[[CONST_VAL]], %[[BITCAST]]
+
+// LLVM-LABEL: construct()
+// LLVM-NEXT: %[[WC_ALLOCA:.*]] = alloca %struct.WithCtor
+// LLVM-NEXT: store { i32, i64, %struct.HasVal } { i32 4, i64 10, %struct.HasVal { i32 5 } }, ptr %[[WC_ALLOCA]]
+
+// OGCG-LABEL: construct()
+// OGCG: %[[WC_ALLOCA:.*]] = alloca %struct.WithCtor
+// OGCG-NEXT: %[[X_GEP:.*]] = getelementptr{{.*}} { i32, i64, %struct.HasVal }, ptr %[[WC_ALLOCA]], i32 0, i32 0
+// OGCG-NEXT: store i32 4, ptr %[[X_GEP]]
+// OGCG-NEXT: %[[Y_GEP:.*]] = getelementptr{{.*}} { i32, i64, %struct.HasVal }, ptr %[[WC_ALLOCA]], i32 0, i32 1
+// OGCG-NEXT: store i64 10, ptr %[[Y_GEP]]
+// OGCG-NEXT: %[[A_GEP:.*]] = getelementptr{{.*}} { i32, i64, %struct.HasVal }, ptr %[[WC_ALLOCA]], i32 0, i32 2
+// OGCG-NEXT: store %struct.HasVal { i32 5 }, ptr %[[A_GEP]]
More information about the cfe-commits
mailing list