[clang] [CIR] Implement handling for CXXConstructLValue expressions (PR #203402)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 12 09:48:44 PDT 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/203402
>From b43575ab8f1e26bb26808e38463b540bca79446e Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Mon, 8 Jun 2026 16:20:58 -0700
Subject: [PATCH 1/2] [CIR] Implement handling for CXXConstructLValue
expressions
This implements the handling to emit an l-value for CXXConstructExpr
and CXXTemporaryObjectExpr expressions. This is a simple copy from
the equivalent code in classic codegen and uses existing CIR code for
most of the actual work.
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 8 ++
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 4 +-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 +
.../test/CIR/CodeGen/cxx-construct-lvalue.cpp | 73 +++++++++++++++++++
4 files changed, 83 insertions(+), 3 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index e110f6e5d21a2..beaedd853f57b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2145,6 +2145,14 @@ CIRGenFunction::emitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *e) {
return makeAddrLValue(slot.getAddress(), e->getType(), AlignmentSource::Decl);
}
+LValue CIRGenFunction::emitCXXConstructLValue(const CXXConstructExpr *e) {
+ assert(e->getType()->getAsCXXRecordDecl()->hasTrivialDestructor() &&
+ "binding l-value to type which needs a temporary");
+ AggValueSlot slot = createAggTemp(e->getType(), getLoc(e->getSourceRange()));
+ emitCXXConstructExpr(e, slot);
+ return makeAddrLValue(slot.getAddress(), e->getType(), AlignmentSource::Decl);
+}
+
LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
// Comma expressions just emit their LHS then their RHS as an l-value.
if (e->getOpcode() == BO_Comma) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 21b2640b36071..4b020c96964a7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1220,9 +1220,7 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
return emitInitListLValue(cast<InitListExpr>(e));
case Expr::CXXTemporaryObjectExprClass:
case Expr::CXXConstructExprClass:
- getCIRGenModule().errorNYI(e->getSourceRange(),
- "emitLValue: CXXConstructExpr");
- return LValue();
+ return emitCXXConstructLValue(cast<CXXConstructExpr>(e));
case Expr::CXXBindTemporaryExprClass:
return emitCXXBindTemporaryLValue(cast<CXXBindTemporaryExpr>(e));
case Expr::CXXUuidofExprClass:
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 35112d1dd7b68..317151c8d61c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1755,6 +1755,7 @@ class CIRGenFunction : public CIRGenTypeCache {
LValue emitCallExprLValue(const clang::CallExpr *e);
LValue emitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *e);
+ LValue emitCXXConstructLValue(const CXXConstructExpr *e);
CIRGenCallee emitCallee(const clang::Expr *e);
template <typename T>
diff --git a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
new file mode 100644
index 0000000000000..0ca10c15e16b9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++03 -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 -std=c++03 -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 -std=c++03 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+
+// A multi-argument constructor call written with explicit type syntax produces
+// a CXXTemporaryObjectExpr. Using it as the base of a member access reaches
+// emitLValue with that expression class.
+struct Pt {
+ Pt(int a, int b);
+ int v;
+};
+
+int tempObj(int i) { return Pt(i, i).v; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z7tempObji(%arg0: !s32i
+// CIR: %[[I:.*]] = cir.alloca "i"
+// CIR: %[[RET:.*]] = cir.alloca "__retval"
+// CIR: %[[TMP:.*]] = cir.alloca "tmp"
+// CIR: cir.store %arg0, %[[I]]
+// CIR: %[[A:.*]] = cir.load align(4) %[[I]]
+// CIR: %[[B:.*]] = cir.load align(4) %[[I]]
+// CIR: cir.call @_ZN2PtC1Eii(%[[TMP]], %[[A]], %[[B]])
+// CIR: %[[V:.*]] = cir.get_member %[[TMP]][0] {name = "v"} : !cir.ptr<!rec_Pt> -> !cir.ptr<!s32i>
+// CIR: %[[VAL:.*]] = cir.load align(4) %[[V]]
+// CIR: cir.store %[[VAL]], %[[RET]]
+// CIR: %[[RES:.*]] = cir.load %[[RET]]
+// CIR: cir.return %[[RES]]
+
+// LLVM-LABEL: define {{.*}}i32 @_Z7tempObji
+// LLVM: %[[I_ADDR:.*]] = alloca i32
+// LLVM: %[[TMP:.*]] = alloca %struct.Pt
+// LLVM: store i32 %{{.*}}, ptr %[[I_ADDR]]
+// LLVM: %[[A:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM: %[[B:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM: call void @_ZN2PtC1Eii(ptr {{.*}} %[[TMP]], i32 {{.*}} %[[A]], i32 {{.*}} %[[B]])
+// LLVM: %[[V:.*]] = getelementptr inbounds nuw %struct.Pt, ptr %[[TMP]], i32 0, i32 0
+// LLVM: %[[VAL:.*]] = load i32, ptr %[[V]]
+
+// A single-argument constructor call performs a constructor conversion, so the
+// base of the member access is a CXXFunctionalCastExpr whose subexpression is a
+// CXXConstructExpr. emitCastLValue forwards to the subexpression, reaching
+// emitLValue with the CXXConstructExpr class.
+struct Conv {
+ Conv(int a);
+ int y;
+};
+
+int construct(int i) { return Conv(i).y; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z9constructi(%arg0: !s32i
+// CIR: %[[I:.*]] = cir.alloca "i"
+// CIR: %[[RET:.*]] = cir.alloca "__retval"
+// CIR: %[[TMP:.*]] = cir.alloca "tmp"
+// CIR: cir.store %arg0, %[[I]]
+// CIR: %[[A:.*]] = cir.load align(4) %[[I]]
+// CIR: cir.call @_ZN4ConvC1Ei(%[[TMP]], %[[A]])
+// CIR: %[[Y:.*]] = cir.get_member %[[TMP]][0] {name = "y"} : !cir.ptr<!rec_Conv> -> !cir.ptr<!s32i>
+// CIR: %[[VAL:.*]] = cir.load align(4) %[[Y]]
+// CIR: cir.store %[[VAL]], %[[RET]]
+// CIR: %[[RES:.*]] = cir.load %[[RET]]
+// CIR: cir.return %[[RES]]
+
+// LLVM-LABEL: define {{.*}}i32 @_Z9constructi
+// LLVM: %[[I_ADDR:.*]] = alloca i32
+// LLVM: %[[TMP:.*]] = alloca %struct.Conv
+// LLVM: store i32 %{{.*}}, ptr %[[I_ADDR]]
+// LLVM: %[[A:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM: call void @_ZN4ConvC1Ei(ptr {{.*}} %[[TMP]], i32 {{.*}} %[[A]])
+// LLVM: %[[Y:.*]] = getelementptr inbounds nuw %struct.Conv, ptr %[[TMP]], i32 0, i32 0
+// LLVM: %[[VAL:.*]] = load i32, ptr %[[Y]]
>From 90ef516f5fe1c2325e60c6094c40312678bda271 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 12 Jun 2026 09:32:05 -0700
Subject: [PATCH 2/2] Address review feedback
---
clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp | 7 -------
1 file changed, 7 deletions(-)
diff --git a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
index 0ca10c15e16b9..51d8be2b417aa 100644
--- a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
+++ b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
@@ -5,9 +5,6 @@
// RUN: %clang_cc1 -std=c++03 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
-// A multi-argument constructor call written with explicit type syntax produces
-// a CXXTemporaryObjectExpr. Using it as the base of a member access reaches
-// emitLValue with that expression class.
struct Pt {
Pt(int a, int b);
int v;
@@ -39,10 +36,6 @@ int tempObj(int i) { return Pt(i, i).v; }
// LLVM: %[[V:.*]] = getelementptr inbounds nuw %struct.Pt, ptr %[[TMP]], i32 0, i32 0
// LLVM: %[[VAL:.*]] = load i32, ptr %[[V]]
-// A single-argument constructor call performs a constructor conversion, so the
-// base of the member access is a CXXFunctionalCastExpr whose subexpression is a
-// CXXConstructExpr. emitCastLValue forwards to the subexpression, reaching
-// emitLValue with the CXXConstructExpr class.
struct Conv {
Conv(int a);
int y;
More information about the cfe-commits
mailing list