[clang] [CIR] Implement LValue InitListExpr and FunctionalCastExpr lowering (PR #192298)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 05:58:50 PDT 2026


https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/192298

>From 91f3f54d515d53c6e2bce745b1596f7544bd66d2 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 15 Apr 2026 07:01:15 -0700
Subject: [PATCH 1/2] [CIR] Implement LValue InitListExpr and
 FunctionalCastExpr lowering

First, this fixes the InitListExpr lowering.  This copies the same
implementation from classic-codegen, and adds some tests (this is
    otherwise untested in classic codegen?).

Second, while I was writing tests for the above, I accidented into the
FunctionalCastExpr, which just calls emitCastLValue, so this fixes that
as well.
---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp    | 16 ++++-
 clang/lib/CIR/CodeGen/CIRGenFunction.h      |  1 +
 clang/test/CIR/CodeGen/init-list-lvalue.cpp | 74 +++++++++++++++++++++
 3 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CIR/CodeGen/init-list-lvalue.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index fcfbeb809371e..0dfa6104d97ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1010,6 +1010,17 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
   return retTy;
 }
 
+LValue CIRGenFunction::emitInitListLValue(const InitListExpr *e) {
+  // Initializing an aggregate temporary in C++11: T{...}.
+  if (!e->isGLValue()) {
+    return emitAggExprToLValue(e);
+  }
+
+  // An lvalue initializer list must be initializing a reference.
+  assert(e->isTransparent() && "non-transparent glvalue init list");
+  return emitLValue(e->getInit(0));
+}
+
 /// Emit code to compute a designator that specifies the location
 /// of the expression.
 /// FIXME: document this function better.
@@ -1084,7 +1095,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
   case Expr::CXXDynamicCastExprClass:
   case Expr::CXXReinterpretCastExprClass:
   case Expr::CXXConstCastExprClass:
-    // TODO(cir): The above list is missing CXXFunctionalCastExprClass,
+  case Expr::CXXFunctionalCastExprClass:
+    // TODO(cir): The above list is missing
     // CXXAddrSpaceCastExprClass, and ObjCBridgedCastExprClass.
     return emitCastLValue(cast<CastExpr>(e));
   case Expr::MaterializeTemporaryExprClass:
@@ -1095,6 +1107,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
     return emitLValue(cast<ChooseExpr>(e)->getChosenSubExpr());
   case Expr::SubstNonTypeTemplateParmExprClass:
     return emitLValue(cast<SubstNonTypeTemplateParmExpr>(e)->getReplacement());
+  case Expr::InitListExprClass:
+    return emitInitListLValue(cast<InitListExpr>(e));
   }
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 4f0f33a933e01..dbe67efa41d1c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1447,6 +1447,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::Value emitArrayLength(const clang::ArrayType *arrayType,
                               QualType &baseType, Address &addr);
   LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
+  LValue emitInitListLValue(const InitListExpr *e);
 
   LValue emitExtVectorElementExpr(const ExtVectorElementExpr *e);
 
diff --git a/clang/test/CIR/CodeGen/init-list-lvalue.cpp b/clang/test/CIR/CodeGen/init-list-lvalue.cpp
new file mode 100644
index 0000000000000..fd4df4558318b
--- /dev/null
+++ b/clang/test/CIR/CodeGen/init-list-lvalue.cpp
@@ -0,0 +1,74 @@
+// 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 -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 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+
+struct Struct {
+  int i;
+  const char *c;
+};
+
+void test1(int i) {
+  // CIR: cir.func {{.*}}@_Z5test1i(%[[I_ARG:.*]]: {{.*}})
+  // LLVM: define {{.*}}void @_Z5test1i(i32 {{.*}}%[[I_ARG:.*]])
+  int &refI = {i};
+  // CIR: %[[I_ALLOCA:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
+  // CIR: %[[REFI_ALLOCA:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["refI", init, const]
+  // CIR: cir.store %[[I_ARG]], %[[I_ALLOCA]] : !s32i, !cir.ptr<!s32i>
+  // CIR: cir.store {{.*}}%[[I_ALLOCA]], %[[REFI_ALLOCA]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+  // LLVM: %[[I_ALLOCA:.*]] = alloca i32
+  // LLVM: %[[REFI_ALLOCA:.*]] = alloca ptr
+  // LLVM: store i32 %[[I_ARG]], ptr %[[I_ALLOCA]]
+  // LLVM: store ptr %[[I_ALLOCA]], ptr %[[REFI_ALLOCA]]
+}
+
+void test2() {
+  // CIR-LABEL: cir.func {{.*}}@_Z5test2v()
+  // LLVM-LABEL: define {{.*}}void @_Z5test2v()
+  Struct s {1, "asdf"};
+  Struct &refS = {s};
+  // CIR: %[[S_ALLOCA:.*]] = cir.alloca !rec_Struct, !cir.ptr<!rec_Struct>, ["s", init]
+  // CIR: %[[REFS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>, ["refS", init, const]
+  // CIR: %[[GET_S_INIT:.*]] = cir.get_global @__const._Z5test2v.s : !cir.ptr<!rec_Struct>
+  // CIR: cir.copy %[[GET_S_INIT]] to %[[S_ALLOCA]] : !cir.ptr<!rec_Struct> loc(#loc33)
+  // CIR: cir.store {{.*}}%[[S_ALLOCA]], %[[REFS_ALLOCA]] : !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>
+  // LLVM: %[[S_ALLOCA:.*]] = alloca %struct.Struct
+  // LLVM: %[[REFS_ALLOCA:.*]] = alloca ptr
+  // LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}%[[S_ALLOCA]], ptr {{.*}}@__const._Z5test2v.s, i64 16, i1 false)
+  // LLVM: store ptr %[[S_ALLOCA]], ptr %[[REFS_ALLOCA]]
+}
+
+// Note: In addition to testing init-list-lvalue, this also tests
+// FunctionalCastExpr.
+void test3(Struct &s) {
+  // CIR: cir.func {{.*}}@_Z5test3R6Struct(%[[S_ARG:.*]]: !cir.ptr<!rec_Struct> {{.*}})
+  // LLVM: define dso_local void @_Z5test3R6Struct(ptr{{.*}}%[[S_ARG:.*]])
+  using refSTy = Struct &;
+  Struct &refS = refSTy{s};
+  // CIR: %[[S_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>, ["s", init, const]
+  // CIR: %[[REFS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>, ["refS", init, const]
+  // CIR: cir.store %[[S_ARG]], %[[S_ALLOCA]] : !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>
+  // CIR: %[[S_LOAD:.*]] = cir.load %[[S_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Struct>>, !cir.ptr<!rec_Struct>
+  // CIR: cir.store {{.*}}%[[S_LOAD]], %[[REFS_ALLOCA]] : !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>
+  // LLVM: %[[S_ALLOCA:.*]] = alloca ptr
+  // LLVM: %[[REFS_ALLOCA:.*]] = alloca ptr
+  // LLVM: store ptr %[[S_ARG]], ptr %[[S_ALLOCA]]
+  // LLVM: %[[S_LOAD:.*]] = load ptr, ptr %[[S_ALLOCA]]
+  // LLVM: store ptr %[[S_LOAD]], ptr %[[REFS_ALLOCA]]
+}
+
+void test4() {
+  // CIR-LABEL: cir.func {{.*}}@_Z5test4v()
+  // LLVM-LABEL: define {{.*}}void @_Z5test4v()
+  Struct s;
+  auto& [sb1, sb2] {s};
+
+  // CIR: %[[S_ALLOCA:.*]] = cir.alloca !rec_Struct, !cir.ptr<!rec_Struct>, ["s"] {alignment = 8 : i64}
+  // CIR: %[[SB_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>, ["", init, const]
+  // CIR: cir.store {{.*}}%[[S_ALLOCA]], %[[SB_ALLOCA]] : !cir.ptr<!rec_Struct>, !cir.ptr<!cir.ptr<!rec_Struct>>
+  // LLVM: %[[S_ALLOCA:.*]] = alloca %struct.Struct
+  // LLVM: %[[SB_ALLOCA:.*]] = alloca ptr
+  // LLVM: store ptr %[[S_ALLOCA]], ptr %[[SB_ALLOCA]]
+}

>From bee3c6279bcb4520c2681f4010cd40758e89293c Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 15 Apr 2026 12:14:01 -0700
Subject: [PATCH 2/2] Remove unnecessary parens

---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0dfa6104d97ef..47d90581e8a53 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1012,9 +1012,8 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
 
 LValue CIRGenFunction::emitInitListLValue(const InitListExpr *e) {
   // Initializing an aggregate temporary in C++11: T{...}.
-  if (!e->isGLValue()) {
+  if (!e->isGLValue())
     return emitAggExprToLValue(e);
-  }
 
   // An lvalue initializer list must be initializing a reference.
   assert(e->isTransparent() && "non-transparent glvalue init list");



More information about the cfe-commits mailing list