llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

We seem to have diverged significantly from classic compiler here, likely 
because the incubator missed these changes.  The result was the exmaple given 
in the test would crash because the 'this' argument wasn't set right.

This patch copy/pastes/lightly modifies classic codegen to get these lambdas 
done correctly.

---
Full diff: https://github.com/llvm/llvm-project/pull/225518.diff


3 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+31-7) 
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+12-15) 
- (added) clang/test/CIR/CodeGen/lambda-explicit-obj.cpp (+92) 


``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 664425b20577f9..ca3c7be4bdd96e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -914,17 +914,41 @@ static LValue emitCapturedFieldLValue(CIRGenFunction 
&cgf, const FieldDecl *fd,
 LValue CIRGenFunction::emitLValueForLambdaField(const FieldDecl *field,
                                                 mlir::Value thisValue) {
   bool hasExplicitObjectParameter = false;
-  const auto *methD = dyn_cast_if_present<CXXMethodDecl>(curCodeDecl);
+  const auto *md = dyn_cast_if_present<CXXMethodDecl>(curCodeDecl);
   LValue lambdaLV;
-  if (methD) {
-    hasExplicitObjectParameter = methD->isExplicitObjectMemberFunction();
-    assert(methD->getParent()->isLambda());
-    assert(methD->getParent() == field->getParent());
+  if (md) {
+    hasExplicitObjectParameter = md->isExplicitObjectMemberFunction();
+    assert(md->getParent()->isLambda());
+    assert(md->getParent() == field->getParent());
   }
+
   if (hasExplicitObjectParameter) {
-    cgm.errorNYI(field->getSourceRange(), "ExplicitObjectMemberFunction");
+    const VarDecl *d = cast<CXXMethodDecl>(curCodeDecl)->getParamDecl(0);
+    auto it = localDeclMap.find(d);
+    assert(it != localDeclMap.end() && "explicit parameter not loaded?");
+    Address addrOfExplicitObject = it->second;
+    if (d->getType()->isReferenceType())
+      lambdaLV = emitLoadOfReferenceLValue(addrOfExplicitObject,
+                                           getLoc(field->getSourceRange()),
+                                           d->getType(), 
AlignmentSource::Decl);
+    else
+      lambdaLV = makeAddrLValue(addrOfExplicitObject,
+                                d->getType().getNonReferenceType());
+
+    // Make sure we have an lvalue to the lambda itself and not a derived 
class.
+    auto *thisTy = d->getType().getNonReferenceType()->getAsCXXRecordDecl();
+    auto *lambdaTy = cast<CXXRecordDecl>(field->getParent());
+    if (thisTy != lambdaTy) {
+      const CXXCastPath &basePathArray = getContext().LambdaCastPaths.at(md);
+      Address base = getAddressOfBaseClass(
+          lambdaLV.getAddress(), thisTy,
+          llvm::make_range(basePathArray.begin(), basePathArray.end()),
+          /*nullCheckValue=*/false, SourceLocation());
+      CanQualType t = getContext().getCanonicalTagType(lambdaTy);
+      lambdaLV = makeAddrLValue(base, t);
+    }
   } else {
-    QualType lambdaTagType =
+    CanQualType lambdaTagType =
         getContext().getCanonicalTagType(field->getParent());
     lambdaLV = makeNaturalAlignAddrLValue(thisValue, lambdaTagType);
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 64572144b5d83b..a6d32eaed09d5f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -587,22 +587,19 @@ void CIRGenFunction::startFunction(GlobalDecl gd, 
QualType returnType,
     }
   }
 
-  // Only implicit-object member functions (without an explicit `this`
-  // parameter) receive an implicit `this` argument that the CXXABI prolog has
-  // to set up. C++23 explicit-object members (P0847R7) carry their object via 
a
-  // regular parameter and use the standard parameter prolog instead.
-  if (isa_and_nonnull<CXXMethodDecl>(d) &&
-      cast<CXXMethodDecl>(d)->isImplicitObjectMemberFunction()) {
-    cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);
-
-    const auto *md = cast<CXXMethodDecl>(d);
-    if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) 
{
-      // We're in a lambda.
-      auto fn = dyn_cast<cir::FuncOp>(curFn);
-      assert(fn && "lambda in non-function region");
+  if (const auto *md = dyn_cast_if_present<CXXMethodDecl>(d);
+      md && !md->isStatic()) {
+    bool isInLambda =
+        md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call;
+
+    if (md->isImplicitObjectMemberFunction())
+      cgm.getCXXABI().emitInstanceFunctionProlog(loc, *this);
+
+    if (isInLambda) {
+      // We're in a lambda; figure out the captures.
+      auto fn = cast<cir::FuncOp>(curFn);
       fn.setLambda(true);
 
-      // Figure out the captures.
       md->getParent()->getCaptureFields(lambdaCaptureFields,
                                         lambdaThisCaptureField);
       if (lambdaThisCaptureField) {
@@ -629,7 +626,7 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType 
returnType,
         if (fd->hasCapturedVLAType())
           cgm.errorNYI(loc, "lambda captured VLA type");
       }
-    } else {
+    } else if (md->isImplicitObjectMemberFunction()) {
       // Not in a lambda; just use 'this' from the method.
       // FIXME: Should we generate a new load for each use of 'this'? The fast
       // register allocator would be happier...
diff --git a/clang/test/CIR/CodeGen/lambda-explicit-obj.cpp 
b/clang/test/CIR/CodeGen/lambda-explicit-obj.cpp
new file mode 100644
index 00000000000000..18fd8821b20136
--- /dev/null
+++ b/clang/test/CIR/CodeGen/lambda-explicit-obj.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM,CIRONLY --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
+
+struct HasThis {
+  int m;
+  void func() {
+    auto capturesThis = [this](this auto) { (void)m; };
+    capturesThis(); }
+};
+
+void g(HasThis ht) { ht.func(); }
+// LLVM: %[[LAMBDA_TY:.*]] = type { ptr }
+
+// 'g' body'.
+// CIR-LABEL: cir.func {{.*}}@_Z1g7HasThis(
+// CIR: %[[HT_ALLOCA:.*]] = cir.alloca "ht" align(4) init : 
!cir.ptr<!rec_HasThis>
+// CIR: cir.call @_ZN7HasThis4funcEv(%[[HT_ALLOCA]]) : (!cir.ptr<!rec_HasThis> 
{{.*}}) -> ()
+
+// LLVM-LABEL: define {{.*}}@_Z1g7HasThis(
+// LLVM: %[[HT_ALLOCA:.*]] = alloca %struct.HasThis, align 4
+// LLVM: call void @_ZN7HasThis4funcEv(ptr {{.*}}%[[HT_ALLOCA]])
+
+// 'func' body.
+// CIR-LABEL: cir.func {{.*}}@_ZN7HasThis4funcEv(
+// CIR-SAME: %[[THIS:.*]]: !cir.ptr<!rec_HasThis>
+// CIR: %[[COERCE:.*]] = cir.alloca "coerce" align(8) : !cir.ptr<!rec_anon2E0>
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" align(8) init : 
!cir.ptr<!cir.ptr<!rec_HasThis>>
+// CIR: %[[CAPTURE_ALLOCA:.*]] = cir.alloca "capturesThis" align(8) init : 
!cir.ptr<!rec_anon2E0>
+// CIR: %[[TMP_ALLOCA:.*]] = cir.alloca "agg.tmp0" align(8) : 
!cir.ptr<!rec_anon2E0>
+// CIR: cir.store %[[THIS]], %[[THIS_ALLOCA]] : !cir.ptr<!rec_HasThis>, 
!cir.ptr<!cir.ptr<!rec_HasThis>>
+// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_HasThis>>, !cir.ptr<!rec_HasThis>
+// CIR: %[[CAPTURE_THIS_GET_MEM:.*]] = cir.get_member %[[CAPTURE_ALLOCA]][0] 
{name = "this"} : !cir.ptr<!rec_anon2E0> -> !cir.ptr<!cir.ptr<!rec_HasThis>>
+// CIR: cir.store align(8) %[[THIS_LOAD]], %[[CAPTURE_THIS_GET_MEM]] : 
!cir.ptr<!rec_HasThis>, !cir.ptr<!cir.ptr<!rec_HasThis>>
+// CIR: cir.copy %[[CAPTURE_ALLOCA]]{{.*}} to %[[TMP_ALLOCA]]{{.*}} : 
!cir.ptr<!rec_anon2E0>
+// CIR: %[[LOAD_TMP:.*]] = cir.load align(8) %[[TMP_ALLOCA]] : 
!cir.ptr<!rec_anon2E0>, !rec_anon2E0
+// CIR: cir.store %[[LOAD_TMP]], %[[COERCE]] : !rec_anon2E0, 
!cir.ptr<!rec_anon2E0>
+// CIR: %[[CAST_COERCE:.*]] = cir.cast bitcast %[[COERCE]] : 
!cir.ptr<!rec_anon2E0> -> !cir.ptr<!cir.ptr<!void>>
+// CIR: %[[LOAD_COERCE:.*]] = cir.load %[[CAST_COERCE]] : 
!cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
+// CIR: cir.call @_ZZN7HasThis4funcEvENHUlT_E_clIS1_EEDaS0_(%[[LOAD_COERCE]]) 
: (!cir.ptr<!void>) -> ()
+
+// LLVM-LABEL: define {{.*}}@_ZN7HasThis4funcEv
+// LLVM-SAME: (ptr {{.*}}%[[THIS:.*]])
+// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr, align 8
+// CIRONLY: %[[COERCE:.*]] = alloca %[[LAMBDA_TY]], align 8
+// CIRONLY: %[[THIS_ALLOCA:.*]] = alloca ptr, align 8
+// LLVM: %[[CAPTURE_ALLOCA:.*]] = alloca %[[LAMBDA_TY]], align 8
+// LLVM: %[[TMP_ALLOCA:.*]] = alloca %[[LAMBDA_TY]], align 8
+// LLVM: store ptr %[[THIS]], ptr %[[THIS_ALLOCA]], align 8
+// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]], align 8
+// LLVM: %[[CAPTURE_THIS_GET_MEM:.*]] = getelementptr inbounds nuw 
%[[LAMBDA_TY]], ptr %[[CAPTURE_ALLOCA]], i32 0, i32 0
+// LLVM: store ptr %[[THIS_LOAD]], ptr %[[CAPTURE_THIS_GET_MEM]], align 8
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}%[[TMP_ALLOCA]], ptr 
{{.*}}%[[CAPTURE_ALLOCA]], i64 8, i1 false)
+// CIRONLY: %[[LOAD_TMP:.*]] = load %[[LAMBDA_TY]], ptr %[[TMP:.*]], align 8
+// CIRONLY: store %[[LAMBDA_TY]] %[[LOAD_TMP]], ptr %[[COERCE]], align 8
+// OGCG: %[[COERCE:.*]] = getelementptr inbounds nuw %[[LAMBDA_TY]], ptr 
%[[TMP_ALLOCA]], i32 0, i32 0
+// LLVM: %[[LOAD_COERCE:.*]] = load ptr, ptr %[[COERCE]], align 8
+// LLVM: call void @_ZZN7HasThis4funcEvENHUlT_E_clIS1_EEDaS0_(ptr 
%[[LOAD_COERCE]])
+
+// Lambda Body:
+// CIR-LABEL: cir.func {{.*}}@_ZZN7HasThis4funcEvENHUlT_E_clIS1_EEDaS0_(
+// CIR-SAME: %[[LAMBDA_THIS:.*]]: !cir.ptr<!void>
+// CIR: %[[COERCE:.*]] = cir.alloca "coerce" align(8) : 
!cir.ptr<!cir.ptr<!void>>
+// CIR: cir.store %[[LAMBDA_THIS]], %[[COERCE]] : !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!void>>
+// CIR: %[[CAST_COERCE:.*]] = cir.cast bitcast %[[COERCE]] : 
!cir.ptr<!cir.ptr<!void>> -> !cir.ptr<!rec_anon2E0>
+// CIR: %[[LOAD_COERCE:.*]] = cir.load %[[CAST_COERCE]] : 
!cir.ptr<!rec_anon2E0>, !rec_anon2E0
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "" align(8) init : 
!cir.ptr<!rec_anon2E0>
+// CIR: cir.store %[[LOAD_COERCE]], %[[THIS_ALLOCA]] : !rec_anon2E0, 
!cir.ptr<!rec_anon2E0>
+// CIR: %[[GET_HAS_THIS:.*]] = cir.get_member %[[THIS_ALLOCA]][0] {name = 
"this"} : !cir.ptr<!rec_anon2E0> -> !cir.ptr<!cir.ptr<!rec_HasThis>>
+// CIR: %[[LOAD_HAS_THIS:.*]] = cir.load align(8) %[[GET_HAS_THIS]] : 
!cir.ptr<!cir.ptr<!rec_HasThis>>, !cir.ptr<!rec_HasThis>
+// CIR: cir.get_member %[[LOAD_HAS_THIS]][0] {name = "m"} : 
!cir.ptr<!rec_HasThis> -> !cir.ptr<!s32i>
+
+// LLVM-LABEL: define {{.*}}@_ZZN7HasThis4funcEvENHUlT_E_clIS1_EEDaS0_(
+// LLVM-SAME: ptr %[[LAMBDA_THIS:.*]])
+// CIRONLY: %[[COERCE:.*]] = alloca ptr, align 8
+// OGCG: %[[LAMBDA_ALLOCA:.*]] = alloca %[[LAMBDA_TY]]
+// OGCG: %[[COERCE:.*]] = getelementptr inbounds nuw %[[LAMBDA_TY]], ptr 
%[[LAMBDA_ALLOCA]], i32 0, i32 0
+
+// LLVM: store ptr %[[LAMBDA_THIS]], ptr %[[COERCE]], align 8
+
+// CIRONLY: %[[LOAD_COERCE:.*]] = load %[[LAMBDA_TY]], ptr %[[COERCE]], align 8
+// CIRONLY: %[[THIS_ALLOCA:.*]] = alloca %[[LAMBDA_TY]], align 8
+// CIRONLY: store %[[LAMBDA_TY]] %[[LOAD_COERCE]], ptr %[[THIS_ALLOCA]], align 
8
+// CIRONLY: %[[GET_HAS_THIS:.*]] = getelementptr inbounds nuw %[[LAMBDA_TY]], 
ptr %[[THIS_ALLOCA]], i32 0, i32 0
+
+// OGCG: %[[GET_HAS_THIS:.*]] = getelementptr inbounds nuw %[[LAMBDA_TY]], ptr 
%[[LAMBDA_ALLOCA]], i32 0, i32 0
+
+// LLVM: %[[LOAD_HAS_THIS:.*]] = load ptr, ptr %[[GET_HAS_THIS]], align 8
+// LLVM: getelementptr inbounds nuw %struct.HasThis, ptr %[[LOAD_HAS_THIS]], 
i32 0, i32 0

``````````

</details>


https://github.com/llvm/llvm-project/pull/225518
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to