Skip to content

[CIR] Upstream support for function-level variable decompositions #151073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
16 changes: 13 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -608,11 +608,14 @@ 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 (evaluateConditionDecl)
maybeEmitDeferredVarDeclInit(&vd);
return;
}
case Decl::OpenACCDeclare:
Expand All @@ -632,7 +635,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(),
Expand Down Expand Up @@ -797,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);
}
}
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,15 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
return lv;
}

if (const auto *bd = dyn_cast<BindingDecl>(nd)) {
if (e->refersToEnclosingVariableOrCapture()) {
assert(!cir::MissingFeatures::lambdaCaptures());
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: lambda captures");
return LValue();
}
return emitLValue(bd->getBinding());
}

cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
return LValue();
}
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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());

Expand Down
55 changes: 55 additions & 0 deletions clang/test/CIR/CodeGen/variable-decomposition.cpp
Original file line number Diff line number Diff line change
@@ -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]]