Skip to content

Commit 8a5d363

Browse files
authored
[CIR] Upstream support for function-level variable decompositions (#151073)
This implements support for structured bindings on a function scope level. It does not add support for global structured bindings.
1 parent 99d70e0 commit 8a5d363

File tree

7 files changed

+90
-8
lines changed

7 files changed

+90
-8
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ struct MissingFeatures {
217217
static bool intrinsics() { return false; }
218218
static bool isMemcpyEquivalentSpecialMember() { return false; }
219219
static bool isTrivialCtorOrDtor() { return false; }
220+
static bool lambdaCaptures() { return false; }
220221
static bool lambdaFieldToName() { return false; }
221222
static bool loopInfoStack() { return false; }
222223
static bool lowerAggregateLoadStore() { return false; }

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
520520
llvm_unreachable("bad evaluation kind");
521521
}
522522

523-
void CIRGenFunction::emitDecl(const Decl &d) {
523+
void CIRGenFunction::emitDecl(const Decl &d, bool evaluateConditionDecl) {
524524
switch (d.getKind()) {
525525
case Decl::BuiltinTemplate:
526526
case Decl::TranslationUnit:
@@ -608,11 +608,14 @@ void CIRGenFunction::emitDecl(const Decl &d) {
608608
case Decl::UsingDirective: // using namespace X; [C++]
609609
assert(!cir::MissingFeatures::generateDebugInfo());
610610
return;
611-
case Decl::Var: {
611+
case Decl::Var:
612+
case Decl::Decomposition: {
612613
const VarDecl &vd = cast<VarDecl>(d);
613614
assert(vd.isLocalVarDecl() &&
614615
"Should not see file-scope variables inside a function!");
615616
emitVarDecl(vd);
617+
if (evaluateConditionDecl)
618+
maybeEmitDeferredVarDeclInit(&vd);
616619
return;
617620
}
618621
case Decl::OpenACCDeclare:
@@ -632,7 +635,6 @@ void CIRGenFunction::emitDecl(const Decl &d) {
632635
case Decl::ImplicitConceptSpecialization:
633636
case Decl::TopLevelStmt:
634637
case Decl::UsingPack:
635-
case Decl::Decomposition: // This could be moved to join Decl::Var
636638
case Decl::OMPDeclareReduction:
637639
case Decl::OMPDeclareMapper:
638640
cgm.errorNYI(d.getSourceRange(),
@@ -797,3 +799,11 @@ void CIRGenFunction::emitAutoVarTypeCleanup(
797799
assert(!cir::MissingFeatures::ehCleanupFlags());
798800
ehStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer);
799801
}
802+
803+
void CIRGenFunction::maybeEmitDeferredVarDeclInit(const VarDecl *vd) {
804+
if (auto *dd = dyn_cast_if_present<DecompositionDecl>(vd)) {
805+
for (auto *b : dd->flat_bindings())
806+
if (auto *hd = b->getHoldingVar())
807+
emitVarDecl(*hd);
808+
}
809+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,15 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
584584
return lv;
585585
}
586586

587+
if (const auto *bd = dyn_cast<BindingDecl>(nd)) {
588+
if (e->refersToEnclosingVariableOrCapture()) {
589+
assert(!cir::MissingFeatures::lambdaCaptures());
590+
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: lambda captures");
591+
return LValue();
592+
}
593+
return emitLValue(bd->getBinding());
594+
}
595+
587596
cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
588597
return LValue();
589598
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,8 @@ class CIRGenFunction : public CIRGenTypeCache {
870870
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
871871
clang::QualType::DestructionKind dtorKind);
872872

873+
void maybeEmitDeferredVarDeclInit(const VarDecl *vd);
874+
873875
void emitBaseInitializer(mlir::Location loc, const CXXRecordDecl *classDecl,
874876
CXXCtorInitializer *baseInit);
875877

@@ -1059,7 +1061,7 @@ class CIRGenFunction : public CIRGenTypeCache {
10591061

10601062
void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
10611063

1062-
void emitDecl(const clang::Decl &d);
1064+
void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false);
10631065
mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
10641066
LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
10651067

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1308,8 +1308,13 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
13081308
break;
13091309
}
13101310

1311-
case Decl::Var: {
1311+
case Decl::Var:
1312+
case Decl::Decomposition: {
13121313
auto *vd = cast<VarDecl>(decl);
1314+
if (isa<DecompositionDecl>(decl)) {
1315+
errorNYI(decl->getSourceRange(), "global variable decompositions");
1316+
break;
1317+
}
13131318
emitGlobal(vd);
13141319
break;
13151320
}

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,8 @@ mlir::LogicalResult CIRGenFunction::emitIfStmt(const IfStmt &s) {
363363
mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {
364364
assert(builder.getInsertionBlock() && "expected valid insertion point");
365365

366-
for (const Decl *I : s.decls())
367-
emitDecl(*I);
366+
for (const Decl *i : s.decls())
367+
emitDecl(*i, /*evaluateConditionDecl=*/true);
368368

369369
return mlir::success();
370370
}
@@ -875,7 +875,7 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {
875875
return mlir::failure();
876876

877877
if (s.getConditionVariable())
878-
emitDecl(*s.getConditionVariable());
878+
emitDecl(*s.getConditionVariable(), /*evaluateConditionDecl=*/true);
879879

880880
mlir::Value condV = emitScalarExpr(s.getCond());
881881

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
5+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-pc-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
7+
8+
struct some_struct {
9+
int a;
10+
float b;
11+
};
12+
13+
float function() {
14+
auto[a, b] = some_struct{1, 2.f};
15+
16+
return a + b;
17+
}
18+
19+
// CIR-LABEL: cir.func dso_local @_Z8functionv() -> !cir.float
20+
// CIR: %[[RETVAL:.+]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["__retval"]
21+
// CIR: %[[STRUCT:.+]] = cir.alloca !rec_some_struct, !cir.ptr<!rec_some_struct>, [""]
22+
// CIR: %[[MEMBER_A:.+]] = cir.get_member %[[STRUCT]][0] {name = "a"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!s32i>
23+
// CIR: %[[LOAD_A:.+]] = cir.load align(4) %[[MEMBER_A]] : !cir.ptr<!s32i>, !s32i
24+
// CIR: %[[CAST_A:.+]] = cir.cast(int_to_float, %[[LOAD_A]] : !s32i), !cir.float
25+
// CIR: %[[MEMBER_B:.+]] = cir.get_member %[[STRUCT]][1] {name = "b"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!cir.float>
26+
// CIR: %[[LOAD_B:.+]] = cir.load align(4) %[[MEMBER_B]] : !cir.ptr<!cir.float>, !cir.float
27+
// CIR: %[[ADD:.+]] = cir.binop(add, %[[CAST_A]], %[[LOAD_B]]) : !cir.float
28+
// CIR: cir.store %[[ADD]], %[[RETVAL]] : !cir.float, !cir.ptr<!cir.float>
29+
// CIR: %[[RET:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!cir.float>, !cir.float
30+
// CIR: cir.return %[[RET]] : !cir.float
31+
32+
// LLVM-LABEL: define dso_local float @_Z8functionv()
33+
// LLVM: %[[RETVAL:.+]] = alloca float, i64 1
34+
// LLVM: %[[STRUCT:.+]] = alloca %struct.some_struct, i64 1
35+
// LLVM: %[[GEP_A:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0
36+
// LLVM: %[[LOAD_A:.+]] = load i32, ptr %[[GEP_A]]
37+
// LLVM: %[[CAST_A:.+]] = sitofp i32 %[[LOAD_A]] to float
38+
// LLVM: %[[GEP_B:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1
39+
// LLVM: %[[LOAD_B:.+]] = load float, ptr %[[GEP_B]]
40+
// LLVM: %[[ADD:.+]] = fadd float %[[CAST_A]], %[[LOAD_B]]
41+
// LLVM: store float %[[ADD]], ptr %[[RETVAL]]
42+
// LLVM: %[[RET:.+]] = load float, ptr %[[RETVAL]]
43+
// LLVM: ret float %[[RET]]
44+
45+
// OGCG: @__const._Z8functionv.{{.*}} = private unnamed_addr constant %struct.some_struct { i32 1, float 2.000000e+00 }
46+
// OGCG-LABEL: define dso_local noundef float @_Z8functionv()
47+
// OGCG: %[[STRUCT:.+]] = alloca %struct.some_struct
48+
// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[STRUCT]], ptr align 4 @__const._Z8functionv.{{.*}}, i64 8, i1 false)
49+
// OGCG: %[[GEP_A:.+]] = getelementptr inbounds nuw %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0
50+
// OGCG: %[[LOAD_A:.+]] = load i32, ptr %[[GEP_A]]
51+
// OGCG: %[[CAST_A:.+]] = sitofp i32 %[[LOAD_A]] to float
52+
// OGCG: %[[GEP_B:.+]] = getelementptr inbounds nuw %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1
53+
// OGCG: %[[LOAD_B:.+]] = load float, ptr %[[GEP_B]]
54+
// OGCG: %[[ADD:.+]] = fadd float %[[CAST_A]], %[[LOAD_B]]
55+
// OGCG: ret float %[[ADD]]

0 commit comments

Comments
 (0)