llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang 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 664425b20577f..ca3c7be4bdd96 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 64572144b5d83..a6d32eaed09d5 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 0000000000000..18fd8821b2013 --- /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
