Skip to content

Commit 0b9e8a0

Browse files
authored
[CIR][CIRGen] Support Lambda capturing this object (#1213)
The PR should help us to get rid of NYI `NYI UNREACHABLE executed at clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp:899` [Relevant OG code here](https://github.com/llvm/clangir/blob/7fb608d4d1b72c25a1739a1bd66c9024208819cb/clang/lib/CodeGen/CGExpr.cpp#L4767): I put `HasExplicitObjectParameter` support as a missing feature, which is a new C++23 feature.
1 parent fcd1b68 commit 0b9e8a0

File tree

5 files changed

+173
-20
lines changed

5 files changed

+173
-20
lines changed

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -943,11 +943,35 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &CGF, const Expr *E,
943943
return LV;
944944
}
945945

946-
static LValue emitCapturedFieldLValue(CIRGenFunction &CGF, const FieldDecl *FD,
947-
mlir::Value ThisValue) {
948-
QualType TagType = CGF.getContext().getTagDeclType(FD->getParent());
949-
LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType);
950-
return CGF.emitLValueForField(LV, FD);
946+
static LValue emitCapturedFieldLValue(CIRGenFunction &cgf, const FieldDecl *fd,
947+
mlir::Value thisValue) {
948+
return cgf.emitLValueForLambdaField(fd, thisValue);
949+
}
950+
951+
/// Given that we are currently emitting a lambda, emit an l-value for
952+
/// one of its members.
953+
///
954+
LValue CIRGenFunction::emitLValueForLambdaField(const FieldDecl *field,
955+
mlir::Value thisValue) {
956+
bool hasExplicitObjectParameter = false;
957+
const auto *methD = dyn_cast_if_present<CXXMethodDecl>(CurCodeDecl);
958+
LValue lambdaLV;
959+
if (methD) {
960+
hasExplicitObjectParameter = methD->isExplicitObjectMemberFunction();
961+
assert(methD->getParent()->isLambda());
962+
assert(methD->getParent() == field->getParent());
963+
}
964+
if (hasExplicitObjectParameter) {
965+
llvm_unreachable("ExplicitObjectMemberFunction NYI");
966+
} else {
967+
QualType lambdaTagType = getContext().getTagDeclType(field->getParent());
968+
lambdaLV = MakeNaturalAlignAddrLValue(thisValue, lambdaTagType);
969+
}
970+
return emitLValueForField(lambdaLV, field);
971+
}
972+
973+
LValue CIRGenFunction::emitLValueForLambdaField(const FieldDecl *field) {
974+
return emitLValueForLambdaField(field, CXXABIThisValue);
951975
}
952976

953977
static LValue emitFunctionDeclLValue(CIRGenFunction &CGF, const Expr *E,

clang/lib/CIR/CodeGen/CIRGenExprAgg.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,8 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) {
928928
ValueDecl *v = capture.getCapturedVar();
929929
fieldName = v->getName();
930930
CGF.getCIRGenModule().LambdaFieldToName[*CurField] = fieldName;
931+
} else if (capture.capturesThis()) {
932+
CGF.getCIRGenModule().LambdaFieldToName[*CurField] = "this";
931933
} else {
932934
llvm_unreachable("NYI");
933935
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,24 @@ void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
12791279
MD->getParent()->getCaptureFields(LambdaCaptureFields,
12801280
LambdaThisCaptureField);
12811281
if (LambdaThisCaptureField) {
1282-
llvm_unreachable("NYI");
1282+
// If the lambda captures the object referred to by '*this' - either by
1283+
// value or by reference, make sure CXXThisValue points to the correct
1284+
// object.
1285+
1286+
// Get the lvalue for the field (which is a copy of the enclosing object
1287+
// or contains the address of the enclosing object).
1288+
LValue thisFieldLValue =
1289+
emitLValueForLambdaField(LambdaThisCaptureField);
1290+
if (!LambdaThisCaptureField->getType()->isPointerType()) {
1291+
// If the enclosing object was captured by value, just use its
1292+
// address. Sign this pointer.
1293+
CXXThisValue = thisFieldLValue.getPointer();
1294+
} else {
1295+
// Load the lvalue pointed to by the field, since '*this' was captured
1296+
// by reference.
1297+
CXXThisValue = emitLoadOfLValue(thisFieldLValue, SourceLocation())
1298+
.getScalarVal();
1299+
}
12831300
}
12841301
for (auto *FD : MD->getParent()->fields()) {
12851302
if (FD->hasCapturedVLAType()) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1679,8 +1679,11 @@ class CIRGenFunction : public CIRGenTypeCache {
16791679
void initializeVTablePointer(mlir::Location loc, const VPtr &Vptr);
16801680

16811681
AggValueSlot::Overlap_t getOverlapForFieldInit(const FieldDecl *FD);
1682-
LValue emitLValueForField(LValue Base, const clang::FieldDecl *Field);
1682+
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);
16831683
LValue emitLValueForBitField(LValue base, const FieldDecl *field);
1684+
LValue emitLValueForLambdaField(const FieldDecl *field);
1685+
LValue emitLValueForLambdaField(const FieldDecl *field,
1686+
mlir::Value thisValue);
16841687

16851688
/// Like emitLValueForField, excpet that if the Field is a reference, this
16861689
/// will return the address of the reference and not the address of the value

clang/test/CIR/CodeGen/lambda.cpp

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ void fn() {
99
a();
1010
}
1111

12+
// CHECK-DAG: !ty_A = !cir.struct<struct "A" {!s32i}>
1213
// CHECK: !ty_anon2E0_ = !cir.struct<class "anon.0" {!u8i}>
14+
// CHECK-DAG: !ty_anon2E7_ = !cir.struct<class "anon.7" {!ty_A}>
15+
// CHECK-DAG: !ty_anon2E8_ = !cir.struct<class "anon.8" {!cir.ptr<!ty_A>}>
1316
// CHECK-DAG: module
1417

1518
// CHECK: cir.func lambda internal private @_ZZ2fnvENK3$_0clEv{{.*}}) extra
@@ -18,9 +21,8 @@ void fn() {
1821
// CHECK-NEXT: %0 = cir.alloca !ty_anon2E0_, !cir.ptr<!ty_anon2E0_>, ["a"]
1922
// CHECK: cir.call @_ZZ2fnvENK3$_0clEv
2023

21-
// LLVM: {{.*}}void @"_ZZ2fnvENK3$_0clEv"(ptr [[THIS:%.*]])
22-
// FIXME: argument attributes should be emmitted, and lambda's alignment
23-
// COM: LLVM: {{.*}} @"_ZZ2fnvENK3$_0clEv"(ptr noundef nonnull align 1 dereferenceable(1) [[THIS:%.*]]){{%.*}} align 2 {
24+
// LLVM-LABEL: _ZZ2fnvENK3$_0clEv
25+
// LLVM-SAME: (ptr [[THIS:%.*]])
2426
// LLVM: [[THIS_ADDR:%.*]] = alloca ptr, i64 1, align 8
2527
// LLVM: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
2628
// LLVM: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
@@ -53,9 +55,10 @@ void l0() {
5355
// CHECK: %8 = cir.load %7 : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
5456
// CHECK: cir.store %6, %8 : !s32i, !cir.ptr<!s32i>
5557

56-
// CHECK: cir.func @_Z2l0v()
58+
// CHECK-LABEL: _Z2l0v
5759

58-
// LLVM: {{.* }}void @"_ZZ2l0vENK3$_0clEv"(ptr [[THIS:%.*]])
60+
// LLVM-LABEL: _ZZ2l0vENK3$_0clEv
61+
// LLVM-SAME: (ptr [[THIS:%.*]])
5962
// LLVM: [[THIS_ADDR:%.*]] = alloca ptr, i64 1, align 8
6063
// LLVM: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
6164
// LLVM: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8
@@ -91,7 +94,7 @@ auto g() {
9194
};
9295
}
9396

94-
// CHECK: cir.func @_Z1gv() -> !ty_anon2E3_
97+
// CHECK-LABEL: @_Z1gv()
9598
// CHECK: %0 = cir.alloca !ty_anon2E3_, !cir.ptr<!ty_anon2E3_>, ["__retval"] {alignment = 8 : i64}
9699
// CHECK: %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
97100
// CHECK: %2 = cir.const #cir.int<12> : !s32i
@@ -120,7 +123,7 @@ auto g2() {
120123
}
121124

122125
// Should be same as above because of NRVO
123-
// CHECK: cir.func @_Z2g2v() -> !ty_anon2E4_
126+
// CHECK-LABEL: @_Z2g2v()
124127
// CHECK-NEXT: %0 = cir.alloca !ty_anon2E4_, !cir.ptr<!ty_anon2E4_>, ["__retval", init] {alignment = 8 : i64}
125128
// CHECK-NEXT: %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
126129
// CHECK-NEXT: %2 = cir.const #cir.int<12> : !s32i
@@ -143,7 +146,7 @@ int f() {
143146
return g2()();
144147
}
145148

146-
// CHECK: cir.func @_Z1fv() -> !s32i
149+
// CHECK-LABEL: @_Z1fv()
147150
// CHECK-NEXT: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
148151
// CHECK-NEXT: cir.scope {
149152
// CHECK-NEXT: %2 = cir.alloca !ty_anon2E4_, !cir.ptr<!ty_anon2E4_>, ["ref.tmp0"] {alignment = 8 : i64}
@@ -156,7 +159,8 @@ int f() {
156159
// CHECK-NEXT: cir.return %1 : !s32i
157160
// CHECK-NEXT: }
158161

159-
// LLVM: {{.*}}i32 @"_ZZ2g2vENK3$_0clEv"(ptr [[THIS:%.*]])
162+
// LLVM-LABEL: _ZZ2g2vENK3$_0clEv
163+
// LLVM-SAME: (ptr [[THIS:%.*]])
160164
// LLVM: [[THIS_ADDR:%.*]] = alloca ptr, i64 1, align 8
161165
// LLVM: [[I_SAVE:%.*]] = alloca i32, i64 1, align 4
162166
// LLVM: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8
@@ -201,7 +205,7 @@ int g3() {
201205
// lambda operator int (*)(int const&)()
202206
// CHECK: cir.func internal private @_ZZ2g3vENK3$_0cvPFiRKiEEv
203207

204-
// CHECK: cir.func @_Z2g3v() -> !s32i
208+
// CHECK-LABEL: @_Z2g3v()
205209
// CHECK: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
206210
// CHECK: %1 = cir.alloca !cir.ptr<!cir.func<!s32i (!cir.ptr<!s32i>)>>, !cir.ptr<!cir.ptr<!cir.func<!s32i (!cir.ptr<!s32i>)>>>, ["fn", init] {alignment = 8 : i64}
207211
// CHECK: %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["task", init] {alignment = 4 : i64}
@@ -230,12 +234,14 @@ int g3() {
230234
// CHECK: }
231235

232236
// lambda operator()
237+
// LLVM-LABEL: _ZZ2g3vENK3$_0clERKi
233238
// FIXME: argument attributes should be emitted
234-
// COM: LLVM: define internal noundef i32 @"_ZZ2g3vENK3$_0clERKi"(ptr noundef nonnull align 1 dereferenceable(1) {{%.*}}, ptr noundef nonnull align 4 dereferenceable(4){{%.*}}) #0 align 2
235-
// LLVM: {{.*}}i32 @"_ZZ2g3vENK3$_0clERKi"(ptr {{%.*}}, ptr {{%.*}})
239+
// COM: LLVM-SAME: (ptr noundef nonnull align 1 dereferenceable(1) {{%.*}},
240+
// COM: LLVM-SAME: ptr noundef nonnull align 4 dereferenceable(4){{%.*}}) #0 align 2
236241

237242
// lambda __invoke()
238-
// LLVM: {{.*}}i32 @"_ZZ2g3vEN3$_08__invokeERKi"(ptr [[i:%.*]])
243+
// LLVM-LABEL: _ZZ2g3vEN3$_08__invokeERKi
244+
// LLVM-SAME: (ptr [[i:%.*]])
239245
// LLVM: [[i_addr:%.*]] = alloca ptr, i64 1, align 8
240246
// LLVM: [[ret_val:%.*]] = alloca i32, i64 1, align 4
241247
// LLVM: [[unused_capture:%.*]] = alloca %class.anon.5, i64 1, align 1
@@ -285,3 +291,104 @@ int g3() {
285291
// LLVM: store i32 [[tmp2]], ptr [[ret_val]], align 4
286292
// LLVM: [[tmp3:%.*]] = load i32, ptr [[ret_val]], align 4
287293
// LLVM: ret i32 [[tmp3]]
294+
295+
struct A {
296+
int a = 111;
297+
int foo() { return [*this] { return a; }(); }
298+
int bar() { return [this] { return a; }(); }
299+
};
300+
// A's default ctor
301+
// CHECK-LABEL: _ZN1AC1Ev
302+
303+
// lambda operator() in foo()
304+
// CHECK-LABEL: _ZZN1A3fooEvENKUlvE_clEv
305+
// CHECK-SAME: ([[ARG:%.*]]: !cir.ptr<!ty_anon2E7_>
306+
// CHECK: [[ARG_ADDR:%.*]] = cir.alloca !cir.ptr<!ty_anon2E7_>, !cir.ptr<!cir.ptr<!ty_anon2E7_>>, ["this", init] {alignment = 8 : i64}
307+
// CHECK: [[RETVAL_ADDR:%.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
308+
// CHECK: cir.store [[ARG]], [[ARG_ADDR]] : !cir.ptr<!ty_anon2E7_>, !cir.ptr<!cir.ptr<!ty_anon2E7_>>
309+
// CHECK: [[CLS_ANNO7:%.*]] = cir.load [[ARG_ADDR]] : !cir.ptr<!cir.ptr<!ty_anon2E7_>>, !cir.ptr<!ty_anon2E7_>
310+
// CHECK: [[STRUCT_A:%.*]] = cir.get_member [[CLS_ANNO7]][0] {name = "this"} : !cir.ptr<!ty_anon2E7_> -> !cir.ptr<!ty_A>
311+
// CHECK: [[a:%.*]] = cir.get_member [[STRUCT_A]][0] {name = "a"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i> loc(#loc70)
312+
// CHECK: [[TMP0:%.*]] = cir.load [[a]] : !cir.ptr<!s32i>, !s32i
313+
// CHECK: cir.store [[TMP0]], [[RETVAL_ADDR]] : !s32i, !cir.ptr<!s32i>
314+
// CHECK: [[RET_VAL:%.*]] = cir.load [[RETVAL_ADDR]] : !cir.ptr<!s32i>,
315+
// CHECK: cir.return [[RET_VAL]] : !s32i
316+
317+
// LLVM-LABEL: @_ZZN1A3fooEvENKUlvE_clEv
318+
// LLVM-SAME: (ptr [[ARG:%.*]])
319+
// LLVM: [[ARG_ADDR:%.*]] = alloca ptr, i64 1, align 8
320+
// LLVM: [[RET:%.*]] = alloca i32, i64 1, align 4
321+
// LLVM: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8
322+
// LLVM: [[CLS_ANNO7:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8
323+
// LLVM: [[STRUCT_A:%.*]] = getelementptr %class.anon.7, ptr [[CLS_ANNO7]], i32 0, i32 0
324+
// LLVM: [[a:%.*]] = getelementptr %struct.A, ptr [[STRUCT_A]], i32 0, i32 0
325+
// LLVM: [[TMP0:%.*]] = load i32, ptr [[a]], align 4
326+
// LLVM: store i32 [[TMP0]], ptr [[RET]], align 4
327+
// LLVM: [[TMP1:%.*]] = load i32, ptr [[RET]], align 4
328+
// LLVM: ret i32 [[TMP1]]
329+
330+
// A::foo()
331+
// CHECK-LABEL: @_ZN1A3fooEv
332+
// CHECK: [[THIS_ARG:%.*]] = cir.alloca !ty_anon2E7_, !cir.ptr<!ty_anon2E7_>, ["ref.tmp0"] {alignment = 4 : i64}
333+
// CHECK: cir.call @_ZZN1A3fooEvENKUlvE_clEv([[THIS_ARG]]) : (!cir.ptr<!ty_anon2E7_>) -> !s32i
334+
335+
// LLVM-LABEL: _ZN1A3fooEv
336+
// LLVM: [[this_in_foo:%.*]] = alloca %class.anon.7, i64 1, align 4
337+
// LLVM: call i32 @_ZZN1A3fooEvENKUlvE_clEv(ptr [[this_in_foo]])
338+
339+
// lambda operator() in bar()
340+
// CHECK-LABEL: _ZZN1A3barEvENKUlvE_clEv
341+
// CHECK-SAME: ([[ARG2:%.*]]: !cir.ptr<!ty_anon2E8_>
342+
// CHECK: [[ARG2_ADDR:%.*]] = cir.alloca !cir.ptr<!ty_anon2E8_>, !cir.ptr<!cir.ptr<!ty_anon2E8_>>, ["this", init] {alignment = 8 : i64}
343+
// CHECK: [[RETVAL_ADDR:%.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
344+
// CHECK: cir.store [[ARG2]], [[ARG2_ADDR]] : !cir.ptr<!ty_anon2E8_>, !cir.ptr<!cir.ptr<!ty_anon2E8_>>
345+
// CHECK: [[CLS_ANNO8:%.*]] = cir.load [[ARG2_ADDR]] : !cir.ptr<!cir.ptr<!ty_anon2E8_>>, !cir.ptr<!ty_anon2E8_>
346+
// CHECK: [[STRUCT_A_PTR:%.*]] = cir.get_member [[CLS_ANNO8]][0] {name = "this"} : !cir.ptr<!ty_anon2E8_> -> !cir.ptr<!cir.ptr<!ty_A>>
347+
// CHECK: [[STRUCT_A:%.*]] = cir.load [[STRUCT_A_PTR]] : !cir.ptr<!cir.ptr<!ty_A>>, !cir.ptr<!ty_A>
348+
// CHECK: [[a:%.*]] = cir.get_member [[STRUCT_A]][0] {name = "a"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i> loc(#loc70)
349+
// CHECK: [[TMP0:%.*]] = cir.load [[a]] : !cir.ptr<!s32i>, !s32i
350+
// CHECK: cir.store [[TMP0]], [[RETVAL_ADDR]] : !s32i, !cir.ptr<!s32i>
351+
// CHECK: [[RET_VAL:%.*]] = cir.load [[RETVAL_ADDR]] : !cir.ptr<!s32i>
352+
// CHECK: cir.return [[RET_VAL]] : !s32i
353+
354+
// LLVM-LABEL: _ZZN1A3barEvENKUlvE_clEv
355+
// LLVM-SAME: (ptr [[ARG2:%.*]])
356+
// LLVM: [[ARG2_ADDR:%.*]] = alloca ptr, i64 1, align 8
357+
// LLVM: [[RET:%.*]] = alloca i32, i64 1, align 4
358+
// LLVM: store ptr [[ARG2]], ptr [[ARG2_ADDR]], align 8
359+
// LLVM: [[CLS_ANNO8:%.*]] = load ptr, ptr [[ARG2_ADDR]], align 8
360+
// LLVM: [[STRUCT_A_PTR:%.*]] = getelementptr %class.anon.8, ptr [[CLS_ANNO8]], i32 0, i32 0
361+
// LLVM: [[STRUCT_A:%.*]] = load ptr, ptr [[STRUCT_A_PTR]], align 8
362+
// LLVM: [[a:%.*]] = getelementptr %struct.A, ptr [[STRUCT_A]], i32
363+
// LLVM: [[TMP0:%.*]] = load i32, ptr [[a]], align 4
364+
// LLVM: store i32 [[TMP0]], ptr [[RET]], align 4
365+
// LLVM: [[TMP1:%.*]] = load i32, ptr [[RET]], align 4
366+
// LLVM: ret i32 [[TMP1]]
367+
368+
// A::bar()
369+
// CHECK-LABEL: _ZN1A3barEv
370+
// CHECK: [[THIS_ARG:%.*]] = cir.alloca !ty_anon2E8_, !cir.ptr<!ty_anon2E8_>, ["ref.tmp0"] {alignment = 8 : i64}
371+
// CHECK: cir.call @_ZZN1A3barEvENKUlvE_clEv([[THIS_ARG]])
372+
373+
// LLVM-LABEL: _ZN1A3barEv
374+
// LLVM: [[this_in_bar:%.*]] = alloca %class.anon.8, i64 1, align 8
375+
// LLVM: call i32 @_ZZN1A3barEvENKUlvE_clEv(ptr [[this_in_bar]])
376+
377+
int test_lambda_this1(){
378+
struct A clsA;
379+
int x = clsA.foo();
380+
int y = clsA.bar();
381+
return x+y;
382+
}
383+
384+
// CHECK-LABEL: test_lambda_this1
385+
// Construct A
386+
// CHECK: cir.call @_ZN1AC1Ev([[A_THIS:%.*]]) : (!cir.ptr<!ty_A>) -> ()
387+
// CHECK: cir.call @_ZN1A3fooEv([[A_THIS]]) : (!cir.ptr<!ty_A>) -> !s32i
388+
// CHECK: cir.call @_ZN1A3barEv([[A_THIS]]) : (!cir.ptr<!ty_A>) -> !s32i
389+
390+
// LLVM-LABEL: test_lambda_this1
391+
// LLVM: [[A_THIS:%.*]] = alloca %struct.A, i64 1, align 4
392+
// LLVM: call void @_ZN1AC1Ev(ptr [[A_THIS]])
393+
// LLVM: call i32 @_ZN1A3fooEv(ptr [[A_THIS]])
394+
// LLVM: call i32 @_ZN1A3barEv(ptr [[A_THIS]])

0 commit comments

Comments
 (0)