[clang] [CIR] Upstream support for function-level variable decompositions (PR #151073)
Morris Hafner via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 31 07:45:44 PDT 2025
https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/151073
>From ebedb14a06f964ae6d75ffc2443ce5e44304e023 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Tue, 29 Jul 2025 04:23:46 +0200
Subject: [PATCH 1/4] [CIR] Upstream support for function-level variable
decompositions
This implements support for structured bindings on a function scope level. It does not add support for global structured bindings.
---
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 8 ++-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 5 ++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 7 ++-
.../CIR/CodeGen/variable-decomposition.cpp | 55 +++++++++++++++++++
5 files changed, 73 insertions(+), 3 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/variable-decomposition.cpp
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index ad3329d3be32e..adc7b5f239ea9 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -217,6 +217,7 @@ struct MissingFeatures {
static bool intrinsics() { return false; }
static bool isMemcpyEquivalentSpecialMember() { return false; }
static bool isTrivialCtorOrDtor() { return false; }
+ static bool lambdaCaptures() { return false; }
static bool lambdaFieldToName() { return false; }
static bool loopInfoStack() { return false; }
static bool lowerAggregateLoadStore() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 6527fb5697f7c..66a6555b21f1b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -608,11 +608,16 @@ void CIRGenFunction::emitDecl(const Decl &d) {
case Decl::UsingDirective: // using namespace X; [C++]
assert(!cir::MissingFeatures::generateDebugInfo());
return;
- case Decl::Var: {
+ case Decl::Var:
+ case Decl::Decomposition: {
const VarDecl &vd = cast<VarDecl>(d);
assert(vd.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
emitVarDecl(vd);
+ if (auto *dd = dyn_cast<DecompositionDecl>(&vd))
+ for (BindingDecl *b : dd->bindings())
+ if (VarDecl *hd = b->getHoldingVar())
+ emitVarDecl(*hd);
return;
}
case Decl::OpenACCDeclare:
@@ -632,7 +637,6 @@ void CIRGenFunction::emitDecl(const Decl &d) {
case Decl::ImplicitConceptSpecialization:
case Decl::TopLevelStmt:
case Decl::UsingPack:
- case Decl::Decomposition: // This could be moved to join Decl::Var
case Decl::OMPDeclareReduction:
case Decl::OMPDeclareMapper:
cgm.errorNYI(d.getSourceRange(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index c18498f80e99f..e1fc64428043b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -584,6 +584,11 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
return lv;
}
+ if (const auto *bd = dyn_cast<BindingDecl>(nd)) {
+ assert(!e->refersToEnclosingVariableOrCapture() && !cir::MissingFeatures::lambdaCaptures());
+ return emitLValue(bd->getBinding());
+ }
+
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
return LValue();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 01f365f64356c..8bacd0ebb0a72 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1308,8 +1308,13 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
break;
}
- case Decl::Var: {
+ case Decl::Var:
+ case Decl::Decomposition: {
auto *vd = cast<VarDecl>(decl);
+ if(isa<DecompositionDecl>(decl)) {
+ errorNYI(decl->getSourceRange(), "global variable decompositions");
+ break;
+ }
emitGlobal(vd);
break;
}
diff --git a/clang/test/CIR/CodeGen/variable-decomposition.cpp b/clang/test/CIR/CodeGen/variable-decomposition.cpp
new file mode 100644
index 0000000000000..b82e0c62424d4
--- /dev/null
+++ b/clang/test/CIR/CodeGen/variable-decomposition.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct some_struct {
+ int a;
+ float b;
+};
+
+float function() {
+ auto[a, b] = some_struct{1, 2.f};
+
+ return a + b;
+}
+
+// CIR-LABEL: cir.func dso_local @_Z8functionv() -> !cir.float
+// CIR: %[[RETVAL:.+]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["__retval"]
+// CIR: %[[STRUCT:.+]] = cir.alloca !rec_some_struct, !cir.ptr<!rec_some_struct>, [""]
+// CIR: %[[MEMBER_A:.+]] = cir.get_member %[[STRUCT]][0] {name = "a"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!s32i>
+// CIR: %[[LOAD_A:.+]] = cir.load align(4) %[[MEMBER_A]] : !cir.ptr<!s32i>, !s32i
+// CIR: %[[CAST_A:.+]] = cir.cast(int_to_float, %[[LOAD_A]] : !s32i), !cir.float
+// CIR: %[[MEMBER_B:.+]] = cir.get_member %[[STRUCT]][1] {name = "b"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!cir.float>
+// CIR: %[[LOAD_B:.+]] = cir.load align(4) %[[MEMBER_B]] : !cir.ptr<!cir.float>, !cir.float
+// CIR: %[[ADD:.+]] = cir.binop(add, %[[CAST_A]], %[[LOAD_B]]) : !cir.float
+// CIR: cir.store %[[ADD]], %[[RETVAL]] : !cir.float, !cir.ptr<!cir.float>
+// CIR: %[[RET:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!cir.float>, !cir.float
+// CIR: cir.return %[[RET]] : !cir.float
+
+// LLVM-LABEL: define dso_local float @_Z8functionv()
+// LLVM: %[[RETVAL:.+]] = alloca float, i64 1
+// LLVM: %[[STRUCT:.+]] = alloca %struct.some_struct, i64 1
+// LLVM: %[[GEP_A:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0
+// LLVM: %[[LOAD_A:.+]] = load i32, ptr %[[GEP_A]]
+// LLVM: %[[CAST_A:.+]] = sitofp i32 %[[LOAD_A]] to float
+// LLVM: %[[GEP_B:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1
+// LLVM: %[[LOAD_B:.+]] = load float, ptr %[[GEP_B]]
+// LLVM: %[[ADD:.+]] = fadd float %[[CAST_A]], %[[LOAD_B]]
+// LLVM: store float %[[ADD]], ptr %[[RETVAL]]
+// LLVM: %[[RET:.+]] = load float, ptr %[[RETVAL]]
+// LLVM: ret float %[[RET]]
+
+// OGCG: @__const._Z8functionv.{{.*}} = private unnamed_addr constant %struct.some_struct { i32 1, float 2.000000e+00 }
+// OGCG-LABEL: define dso_local noundef float @_Z8functionv()
+// OGCG: %[[STRUCT:.+]] = alloca %struct.some_struct
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[STRUCT]], ptr align 4 @__const._Z8functionv.{{.*}}, i64 8, i1 false)
+// OGCG: %[[GEP_A:.+]] = getelementptr inbounds nuw %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0
+// OGCG: %[[LOAD_A:.+]] = load i32, ptr %[[GEP_A]]
+// OGCG: %[[CAST_A:.+]] = sitofp i32 %[[LOAD_A]] to float
+// OGCG: %[[GEP_B:.+]] = getelementptr inbounds nuw %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1
+// OGCG: %[[LOAD_B:.+]] = load float, ptr %[[GEP_B]]
+// OGCG: %[[ADD:.+]] = fadd float %[[CAST_A]], %[[LOAD_B]]
+// OGCG: ret float %[[ADD]]
>From 681fdf547b90bdc487701c024ca14017ece4df64 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Tue, 29 Jul 2025 15:28:00 +0200
Subject: [PATCH 2/4] clang-format
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 3 ++-
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index e1fc64428043b..abeea6273253e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -585,7 +585,8 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
}
if (const auto *bd = dyn_cast<BindingDecl>(nd)) {
- assert(!e->refersToEnclosingVariableOrCapture() && !cir::MissingFeatures::lambdaCaptures());
+ assert(!e->refersToEnclosingVariableOrCapture() &&
+ !cir::MissingFeatures::lambdaCaptures());
return emitLValue(bd->getBinding());
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 8bacd0ebb0a72..d0f9fc3bd48d2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1311,7 +1311,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
case Decl::Var:
case Decl::Decomposition: {
auto *vd = cast<VarDecl>(decl);
- if(isa<DecompositionDecl>(decl)) {
+ if (isa<DecompositionDecl>(decl)) {
errorNYI(decl->getSourceRange(), "global variable decompositions");
break;
}
>From 36331c38562248ca95ce6b4b173c260b920a1f48 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Wed, 30 Jul 2025 17:56:41 +0200
Subject: [PATCH 3/4] * introduce maybeEmitDeferredVarDeclInit * reformat rest
---
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 16 +++++++++++-----
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 7 +++++--
clang/lib/CIR/CodeGen/CIRGenFunction.h | 4 +++-
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 6 +++---
.../test/CIR/CodeGen/variable-decomposition.cpp | 8 ++++----
5 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 66a6555b21f1b..6251308c75e78 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -520,7 +520,7 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
llvm_unreachable("bad evaluation kind");
}
-void CIRGenFunction::emitDecl(const Decl &d) {
+void CIRGenFunction::emitDecl(const Decl &d, bool evaluateConditionDecl) {
switch (d.getKind()) {
case Decl::BuiltinTemplate:
case Decl::TranslationUnit:
@@ -614,10 +614,8 @@ void CIRGenFunction::emitDecl(const Decl &d) {
assert(vd.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
emitVarDecl(vd);
- if (auto *dd = dyn_cast<DecompositionDecl>(&vd))
- for (BindingDecl *b : dd->bindings())
- if (VarDecl *hd = b->getHoldingVar())
- emitVarDecl(*hd);
+ if (evaluateConditionDecl)
+ maybeEmitDeferredVarDeclInit(&vd);
return;
}
case Decl::OpenACCDeclare:
@@ -801,3 +799,11 @@ void CIRGenFunction::emitAutoVarTypeCleanup(
assert(!cir::MissingFeatures::ehCleanupFlags());
ehStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
}
+
+void CIRGenFunction::maybeEmitDeferredVarDeclInit(const VarDecl *vd) {
+ if (auto *dd = dyn_cast_if_present<DecompositionDecl>(vd)) {
+ for (auto *b : dd->flat_bindings())
+ if (auto *hd = b->getHoldingVar())
+ emitVarDecl(*hd);
+ }
+}
\ No newline at end of file
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index abeea6273253e..a509ffa9be961 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -585,8 +585,11 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
}
if (const auto *bd = dyn_cast<BindingDecl>(nd)) {
- assert(!e->refersToEnclosingVariableOrCapture() &&
- !cir::MissingFeatures::lambdaCaptures());
+ if (e->refersToEnclosingVariableOrCapture()) {
+ assert(!cir::MissingFeatures::lambdaCaptures());
+ cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: lambda captures");
+ return LValue();
+ }
return emitLValue(bd->getBinding());
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 603f75078c519..f9c8636ab0220 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -870,6 +870,8 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
clang::QualType::DestructionKind dtorKind);
+ void maybeEmitDeferredVarDeclInit(const VarDecl *vd);
+
void emitBaseInitializer(mlir::Location loc, const CXXRecordDecl *classDecl,
CXXCtorInitializer *baseInit);
@@ -1059,7 +1061,7 @@ class CIRGenFunction : public CIRGenTypeCache {
void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
- void emitDecl(const clang::Decl &d);
+ void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false);
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 21bee3312eb0f..75da229e64c6c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -363,8 +363,8 @@ mlir::LogicalResult CIRGenFunction::emitIfStmt(const IfStmt &s) {
mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {
assert(builder.getInsertionBlock() && "expected valid insertion point");
- for (const Decl *I : s.decls())
- emitDecl(*I);
+ for (const Decl *i : s.decls())
+ emitDecl(*i, /*evaluateConditionDecl=*/true);
return mlir::success();
}
@@ -875,7 +875,7 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {
return mlir::failure();
if (s.getConditionVariable())
- emitDecl(*s.getConditionVariable());
+ emitDecl(*s.getConditionVariable(), /*evaluateConditionDecl=*/true);
mlir::Value condV = emitScalarExpr(s.getCond());
diff --git a/clang/test/CIR/CodeGen/variable-decomposition.cpp b/clang/test/CIR/CodeGen/variable-decomposition.cpp
index b82e0c62424d4..022d06a97e369 100644
--- a/clang/test/CIR/CodeGen/variable-decomposition.cpp
+++ b/clang/test/CIR/CodeGen/variable-decomposition.cpp
@@ -6,14 +6,14 @@
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
struct some_struct {
- int a;
- float b;
+ int a;
+ float b;
};
float function() {
- auto[a, b] = some_struct{1, 2.f};
+ auto[a, b] = some_struct{1, 2.f};
- return a + b;
+ return a + b;
}
// CIR-LABEL: cir.func dso_local @_Z8functionv() -> !cir.float
>From a161ab1e5ab9aee11069f5ec3261cf1e991d25de Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhafner at nvidia.com>
Date: Thu, 31 Jul 2025 16:43:04 +0200
Subject: [PATCH 4/4] Add missing newline
---
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 6251308c75e78..9e8eaa5afa1e6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -806,4 +806,4 @@ void CIRGenFunction::maybeEmitDeferredVarDeclInit(const VarDecl *vd) {
if (auto *hd = b->getHoldingVar())
emitVarDecl(*hd);
}
-}
\ No newline at end of file
+}
More information about the cfe-commits
mailing list