diff options
author | Andy Kaylor <akaylor@nvidia.com> | 2025-05-19 14:42:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-19 14:42:50 -0700 |
commit | a0c515a9ef3be780e2dfd64d86de9e401b137b28 (patch) | |
tree | be738d9a2fccfd8a68b8b3d227b1458a24ca9c04 /clang/lib | |
parent | 0b4cfd19f237fbbffb6e49b82dd91ee65e1e43a2 (diff) | |
download | llvm-a0c515a9ef3be780e2dfd64d86de9e401b137b28.zip llvm-a0c515a9ef3be780e2dfd64d86de9e401b137b28.tar.gz llvm-a0c515a9ef3be780e2dfd64d86de9e401b137b28.tar.bz2 |
[CIR] Upstream support for C++ member function calls (#140290)
This change adds the support needed to handle a C++ member function
call, including arranging the function type with an argument added for
the 'this' parameter. It was necessary to introduce the class to handle
the CXXABI, but at this time no target-specific subclasses have been
added.
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp | 45 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 27 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp | 186 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCall.cpp | 102 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCall.h | 12 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 37 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 10 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 15 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 15 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 2 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.h | 22 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CMakeLists.txt | 2 |
12 files changed, 457 insertions, 18 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp new file mode 100644 index 0000000..e368ccd --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This provides an abstract class for C++ code generation. Concrete subclasses +// of this implement code generation for specific C++ ABIs. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenCXXABI.h" +#include "CIRGenFunction.h" + +#include "clang/AST/Decl.h" +#include "clang/AST/GlobalDecl.h" + +using namespace clang; +using namespace clang::CIRGen; + +CIRGenCXXABI::~CIRGenCXXABI() {} + +void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf, + FunctionArgList ¶ms) { + const auto *md = cast<CXXMethodDecl>(cgf.curGD.getDecl()); + + // FIXME: I'm not entirely sure I like using a fake decl just for code + // generation. Maybe we can come up with a better way? + auto *thisDecl = + ImplicitParamDecl::Create(cgm.getASTContext(), nullptr, md->getLocation(), + &cgm.getASTContext().Idents.get("this"), + md->getThisType(), ImplicitParamKind::CXXThis); + params.push_back(thisDecl); + + // Classic codegen save thisDecl in CodeGenFunction::CXXABIThisDecl, but it + // doesn't seem to be needed in CIRGen. + assert(!cir::MissingFeatures::cxxabiThisDecl()); + + // Classic codegen computes the alignment of thisDecl and saves it in + // CodeGenFunction::CXXABIThisAlignment, but it doesn't seem to be needed in + // CIRGen. + assert(!cir::MissingFeatures::cxxabiThisAlignment()); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 5279307..b5c33a2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H #define LLVM_CLANG_LIB_CIR_CIRGENCXXABI_H +#include "CIRGenCall.h" #include "CIRGenModule.h" #include "clang/AST/Mangle.h" @@ -31,9 +32,33 @@ public: // implemented. CIRGenCXXABI(CIRGenModule &cgm) : cgm(cgm), mangleContext(cgm.getASTContext().createMangleContext()) {} - ~CIRGenCXXABI(); + virtual ~CIRGenCXXABI(); public: + /// Get the type of the implicit "this" parameter used by a method. May return + /// zero if no specific type is applicable, e.g. if the ABI expects the "this" + /// parameter to point to some artificial offset in a complete object due to + /// vbases being reordered. + virtual const clang::CXXRecordDecl * + getThisArgumentTypeForMethod(const clang::CXXMethodDecl *md) { + return md->getParent(); + } + + /// Build a parameter variable suitable for 'this'. + void buildThisParam(CIRGenFunction &cgf, FunctionArgList ¶ms); + + /// Returns true if the given constructor or destructor is one of the kinds + /// that the ABI says returns 'this' (only applies when called non-virtually + /// for destructors). + /// + /// There currently is no way to indicate if a destructor returns 'this' when + /// called virtually, and CIR generation does not support this case. + virtual bool hasThisReturn(clang::GlobalDecl gd) const { return false; } + + virtual bool hasMostDerivedReturn(clang::GlobalDecl gd) const { + return false; + } + /// Gets the mangle context. clang::MangleContext &getMangleContext() { return *mangleContext; } }; diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp new file mode 100644 index 0000000..906c212 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp @@ -0,0 +1,186 @@ +//===--- CIRGenExprCXX.cpp - Emit CIR Code for C++ expressions ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code dealing with code generation of C++ expressions +// +//===----------------------------------------------------------------------===// + +#include "CIRGenCXXABI.h" +#include "CIRGenFunction.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +namespace { +struct MemberCallInfo { + RequiredArgs reqArgs; + // Number of prefix arguments for the call. Ignores the `this` pointer. + unsigned prefixSize; +}; +} // namespace + +static MemberCallInfo commonBuildCXXMemberOrOperatorCall( + CIRGenFunction &cgf, const CXXMethodDecl *md, mlir::Value thisPtr, + mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce, + CallArgList &args, CallArgList *rtlArgs) { + assert(ce == nullptr || isa<CXXMemberCallExpr>(ce) || + isa<CXXOperatorCallExpr>(ce)); + assert(md->isInstance() && + "Trying to emit a member or operator call expr on a static method!"); + + // Push the this ptr. + const CXXRecordDecl *rd = + cgf.cgm.getCXXABI().getThisArgumentTypeForMethod(md); + args.add(RValue::get(thisPtr), cgf.getTypes().deriveThisType(rd, md)); + + // If there is an implicit parameter (e.g. VTT), emit it. + if (implicitParam) { + args.add(RValue::get(implicitParam), implicitParamTy); + } + + const auto *fpt = md->getType()->castAs<FunctionProtoType>(); + RequiredArgs required = + RequiredArgs::getFromProtoWithExtraSlots(fpt, args.size()); + unsigned prefixSize = args.size() - 1; + + // Add the rest of the call args + if (rtlArgs) { + // Special case: if the caller emitted the arguments right-to-left already + // (prior to emitting the *this argument), we're done. This happens for + // assignment operators. + args.addFrom(*rtlArgs); + } else if (ce) { + // Special case: skip first argument of CXXOperatorCall (it is "this"). + unsigned argsToSkip = isa<CXXOperatorCallExpr>(ce) ? 1 : 0; + cgf.emitCallArgs(args, fpt, drop_begin(ce->arguments(), argsToSkip), + ce->getDirectCallee()); + } else { + assert( + fpt->getNumParams() == 0 && + "No CallExpr specified for function with non-zero number of arguments"); + } + + // return {required, prefixSize}; + return {required, prefixSize}; +} + +RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr( + const CallExpr *ce, const CXXMethodDecl *md, ReturnValueSlot returnValue, + bool hasQualifier, NestedNameSpecifier *qualifier, bool isArrow, + const Expr *base) { + assert(isa<CXXMemberCallExpr>(ce) || isa<CXXOperatorCallExpr>(ce)); + + if (md->isVirtual()) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: virtual call"); + return RValue::get(nullptr); + } + + bool trivialForCodegen = + md->isTrivial() || (md->isDefaulted() && md->getParent()->isUnion()); + bool trivialAssignment = + trivialForCodegen && + (md->isCopyAssignmentOperator() || md->isMoveAssignmentOperator()) && + !md->getParent()->mayInsertExtraPadding(); + (void)trivialAssignment; + + // C++17 demands that we evaluate the RHS of a (possibly-compound) assignment + // operator before the LHS. + CallArgList rtlArgStorage; + CallArgList *rtlArgs = nullptr; + if (auto *oce = dyn_cast<CXXOperatorCallExpr>(ce)) { + cgm.errorNYI(oce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: operator call"); + return RValue::get(nullptr); + } + + LValue thisPtr; + if (isArrow) { + LValueBaseInfo baseInfo; + assert(!cir::MissingFeatures::opTBAA()); + Address thisValue = emitPointerWithAlignment(base, &baseInfo); + thisPtr = makeAddrLValue(thisValue, base->getType(), baseInfo); + } else { + thisPtr = emitLValue(base); + } + + if (const CXXConstructorDecl *ctor = dyn_cast<CXXConstructorDecl>(md)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: constructor call"); + return RValue::get(nullptr); + } + + if (trivialForCodegen) { + if (isa<CXXDestructorDecl>(md)) + return RValue::get(nullptr); + + if (trivialAssignment) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: trivial assignment"); + return RValue::get(nullptr); + } else { + assert(md->getParent()->mayInsertExtraPadding() && + "unknown trivial member function"); + } + } + + // Compute the function type we're calling + const CXXMethodDecl *calleeDecl = md; + const CIRGenFunctionInfo *fInfo = nullptr; + if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: destructor call"); + return RValue::get(nullptr); + } else { + fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl); + } + + mlir::Type ty = cgm.getTypes().getFunctionType(*fInfo); + + assert(!cir::MissingFeatures::sanitizers()); + assert(!cir::MissingFeatures::emitTypeCheck()); + + if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: destructor call"); + return RValue::get(nullptr); + } + + assert(!cir::MissingFeatures::sanitizers()); + if (getLangOpts().AppleKext) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: AppleKext"); + return RValue::get(nullptr); + } + CIRGenCallee callee = + CIRGenCallee::forDirect(cgm.getAddrOfFunction(md, ty), GlobalDecl(md)); + + return emitCXXMemberOrOperatorCall( + calleeDecl, callee, returnValue, thisPtr.getPointer(), + /*ImplicitParam=*/nullptr, QualType(), ce, rtlArgs); +} + +RValue CIRGenFunction::emitCXXMemberOrOperatorCall( + const CXXMethodDecl *md, const CIRGenCallee &callee, + ReturnValueSlot returnValue, mlir::Value thisPtr, mlir::Value implicitParam, + QualType implicitParamTy, const CallExpr *ce, CallArgList *rtlArgs) { + const auto *fpt = md->getType()->castAs<FunctionProtoType>(); + CallArgList args; + MemberCallInfo callInfo = commonBuildCXXMemberOrOperatorCall( + *this, md, thisPtr, implicitParam, implicitParamTy, ce, args, rtlArgs); + auto &fnInfo = cgm.getTypes().arrangeCXXMethodCall( + args, fpt, callInfo.reqArgs, callInfo.prefixSize); + assert((ce || currSrcLoc) && "expected source location"); + mlir::Location loc = ce ? getLoc(ce->getExprLoc()) : *currSrcLoc; + assert(!cir::MissingFeatures::opCallMustTail()); + return emitCall(fnInfo, callee, returnValue, args, nullptr, loc); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 17bfa19..bf38515 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CIRGenCall.h" +#include "CIRGenCXXABI.h" #include "CIRGenFunction.h" #include "CIRGenFunctionInfo.h" #include "clang/CIR/MissingFeatures.h" @@ -76,11 +77,35 @@ static void appendParameterTypes(const CIRGenTypes &cgt, cgt.getCGModule().errorNYI("appendParameterTypes: hasExtParameterInfos"); } +/// Derives the 'this' type for CIRGen purposes, i.e. ignoring method CVR +/// qualification. Either or both of `rd` and `md` may be null. A null `rd` +/// indicates that there is no meaningful 'this' type, and a null `md` can occur +/// when calling a method pointer. +CanQualType CIRGenTypes::deriveThisType(const CXXRecordDecl *rd, + const CXXMethodDecl *md) { + QualType recTy; + if (rd) { + recTy = getASTContext().getTagDeclType(rd)->getCanonicalTypeInternal(); + } else { + // This can happen with the MS ABI. It shouldn't need anything more than + // setting recTy to VoidTy here, but we're flagging it for now because we + // don't have the full handling implemented. + cgm.errorNYI("deriveThisType: no record decl"); + recTy = getASTContext().VoidTy; + } + + if (md) + recTy = getASTContext().getAddrSpaceQualType( + recTy, md->getMethodQualifiers().getAddressSpace()); + return getASTContext().getPointerType(CanQualType::CreateUnsafe(recTy)); +} + /// Arrange the CIR function layout for a value of the given function type, on /// top of any implicit parameters already stored. static const CIRGenFunctionInfo & arrangeCIRFunctionInfo(CIRGenTypes &cgt, SmallVectorImpl<CanQualType> &prefix, CanQual<FunctionProtoType> ftp) { + assert(!cir::MissingFeatures::opCallFnInfoOpts()); RequiredArgs required = RequiredArgs::getFromProtoWithExtraSlots(ftp, prefix.size()); assert(!cir::MissingFeatures::opCallExtParameterInfo()); @@ -112,24 +137,88 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm, CanQualType retType = fnType->getReturnType() ->getCanonicalTypeUnqualified() .getUnqualifiedType(); + + assert(!cir::MissingFeatures::opCallFnInfoOpts()); return cgt.arrangeCIRFunctionInfo(retType, argTypes, required); } +/// Arrange a call to a C++ method, passing the given arguments. +/// +/// numPrefixArgs is the number of the ABI-specific prefix arguments we have. It +/// does not count `this`. +const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXMethodCall( + const CallArgList &args, const FunctionProtoType *proto, + RequiredArgs required, unsigned numPrefixArgs) { + assert(!cir::MissingFeatures::opCallExtParameterInfo()); + assert(numPrefixArgs + 1 <= args.size() && + "Emitting a call with less args than the required prefix?"); + + // FIXME: Kill copy. + llvm::SmallVector<CanQualType, 16> argTypes; + for (const CallArg &arg : args) + argTypes.push_back(astContext.getCanonicalParamType(arg.ty)); + + assert(!cir::MissingFeatures::opCallFnInfoOpts()); + return arrangeCIRFunctionInfo(proto->getReturnType() + ->getCanonicalTypeUnqualified() + .getUnqualifiedType(), + argTypes, required); +} + const CIRGenFunctionInfo & CIRGenTypes::arrangeFreeFunctionCall(const CallArgList &args, const FunctionType *fnType) { return arrangeFreeFunctionLikeCall(*this, cgm, args, fnType); } +/// Arrange the argument and result information for a declaration or definition +/// of the given C++ non-static member function. The member function must be an +/// ordinary function, i.e. not a constructor or destructor. +const CIRGenFunctionInfo & +CIRGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *md) { + assert(!isa<CXXConstructorDecl>(md) && "wrong method for constructors!"); + assert(!isa<CXXDestructorDecl>(md) && "wrong method for destructors!"); + + auto prototype = + md->getType()->getCanonicalTypeUnqualified().getAs<FunctionProtoType>(); + assert(!cir::MissingFeatures::cudaSupport()); + + if (md->isInstance()) { + // The abstract case is perfectly fine. + auto *thisType = theCXXABI.getThisArgumentTypeForMethod(md); + return arrangeCXXMethodType(thisType, prototype.getTypePtr(), md); + } + + return arrangeFreeFunctionType(prototype); +} + +/// Arrange the argument and result information for a call to an unknown C++ +/// non-static member function of the given abstract type. (A null RD means we +/// don't have any meaningful "this" argument type, so fall back to a generic +/// pointer type). The member fucntion must be an ordinary function, i.e. not a +/// constructor or destructor. +const CIRGenFunctionInfo & +CIRGenTypes::arrangeCXXMethodType(const CXXRecordDecl *rd, + const FunctionProtoType *ftp, + const CXXMethodDecl *md) { + llvm::SmallVector<CanQualType, 16> argTypes; + + // Add the 'this' pointer. + argTypes.push_back(deriveThisType(rd, md)); + + assert(!cir::MissingFeatures::opCallFnInfoOpts()); + return ::arrangeCIRFunctionInfo( + *this, argTypes, + ftp->getCanonicalTypeUnqualified().getAs<FunctionProtoType>()); +} + /// Arrange the argument and result information for the declaration or /// definition of the given function. const CIRGenFunctionInfo & CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) { - if (const auto *md = dyn_cast<CXXMethodDecl>(fd)) { - if (md->isInstance()) { - cgm.errorNYI("arrangeFunctionDeclaration: instance method"); - } - } + if (const auto *md = dyn_cast<CXXMethodDecl>(fd)) + if (md->isInstance()) + return arrangeCXXMethodDeclaration(md); CanQualType funcTy = fd->getType()->getCanonicalTypeUnqualified(); @@ -142,6 +231,7 @@ CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) { if (CanQual<FunctionNoProtoType> noProto = funcTy.getAs<FunctionNoProtoType>()) { assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo()); + assert(!cir::MissingFeatures::opCallFnInfoOpts()); return arrangeCIRFunctionInfo(noProto->getReturnType(), std::nullopt, RequiredArgs::All); } @@ -167,12 +257,14 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, const CIRGenFunctionInfo & CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionProtoType> fpt) { SmallVector<CanQualType, 16> argTypes; + assert(!cir::MissingFeatures::opCallFnInfoOpts()); return ::arrangeCIRFunctionInfo(*this, argTypes, fpt); } const CIRGenFunctionInfo & CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> fnpt) { CanQualType resultType = fnpt->getReturnType().getUnqualifiedType(); + assert(!cir::MissingFeatures::opCallFnInfoOpts()); return arrangeCIRFunctionInfo(resultType, {}, RequiredArgs(0)); } diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 2ba1676..d3c241c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -109,6 +109,18 @@ public: class CallArgList : public llvm::SmallVector<CallArg, 8> { public: void add(RValue rvalue, clang::QualType type) { emplace_back(rvalue, type); } + + /// Add all the arguments from another CallArgList to this one. After doing + /// this, the old CallArgList retains its list of arguments, but must not + /// be used to emit a call. + void addFrom(const CallArgList &other) { + insert(end(), other.begin(), other.end()); + // Classic codegen has handling for these here. We may not need it here for + // CIR, but if not we should implement equivalent handling in lowering. + assert(!cir::MissingFeatures::writebacks()); + assert(!cir::MissingFeatures::cleanupsToDeactivate()); + assert(!cir::MissingFeatures::stackBase()); + } }; /// Contains the address where the return value of a function can be stored, and diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 0386961..b8e644d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -909,7 +909,7 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy, cgm.getTypes().arrangeFreeFunctionCall(args, fnType); assert(!cir::MissingFeatures::opCallNoPrototypeFunc()); - assert(!cir::MissingFeatures::opCallChainCall()); + assert(!cir::MissingFeatures::opCallFnInfoOpts()); assert(!cir::MissingFeatures::hip()); assert(!cir::MissingFeatures::opCallMustTail()); @@ -945,10 +945,8 @@ RValue CIRGenFunction::emitCallExpr(const clang::CallExpr *e, ReturnValueSlot returnValue) { assert(!cir::MissingFeatures::objCBlocks()); - if (isa<CXXMemberCallExpr>(e)) { - cgm.errorNYI(e->getSourceRange(), "call to member function"); - return RValue::get(nullptr); - } + if (const auto *ce = dyn_cast<CXXMemberCallExpr>(e)) + return emitCXXMemberCallExpr(ce, returnValue); if (isa<CUDAKernelCallExpr>(e)) { cgm.errorNYI(e->getSourceRange(), "call to CUDA kernel"); @@ -1148,6 +1146,35 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty, return addr; } +// Note: this function also emit constructor calls to support a MSVC extensions +// allowing explicit constructor function call. +RValue CIRGenFunction::emitCXXMemberCallExpr(const CXXMemberCallExpr *ce, + ReturnValueSlot returnValue) { + const Expr *callee = ce->getCallee()->IgnoreParens(); + + if (isa<BinaryOperator>(callee)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberCallExpr: C++ binary operator"); + return RValue::get(nullptr); + } + + const auto *me = cast<MemberExpr>(callee); + const auto *md = cast<CXXMethodDecl>(me->getMemberDecl()); + + if (md->isStatic()) { + cgm.errorNYI(ce->getSourceRange(), "emitCXXMemberCallExpr: static method"); + return RValue::get(nullptr); + } + + bool hasQualifier = me->hasQualifier(); + NestedNameSpecifier *qualifier = hasQualifier ? me->getQualifier() : nullptr; + bool isArrow = me->isArrow(); + const Expr *base = me->getBase(); + + return emitCXXMemberOrOperatorMemberCallExpr( + ce, md, returnValue, hasQualifier, qualifier, isArrow, base); +} + RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) { // Emit the expression as an lvalue. LValue lv = emitLValue(e); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index a424f7a..6bfad71 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -12,6 +12,7 @@ #include "CIRGenFunction.h" +#include "CIRGenCXXABI.h" #include "CIRGenCall.h" #include "CIRGenValue.h" #include "mlir/IR/Location.h" @@ -481,8 +482,13 @@ clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, QualType retTy = fd->getReturnType(); const auto *md = dyn_cast<CXXMethodDecl>(fd); - if (md && md->isInstance()) - cgm.errorNYI(fd->getSourceRange(), "buildFunctionArgList: CXXMethodDecl"); + if (md && md->isInstance()) { + if (cgm.getCXXABI().hasThisReturn(gd)) + cgm.errorNYI(fd->getSourceRange(), "this return"); + else if (cgm.getCXXABI().hasMostDerivedReturn(gd)) + cgm.errorNYI(fd->getSourceRange(), "most derived return"); + cgm.getCXXABI().buildThisParam(*this, args); + } if (isa<CXXConstructorDecl>(fd)) cgm.errorNYI(fd->getSourceRange(), diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index f7670ed..dbcc6ad 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -534,6 +534,21 @@ public: mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s, llvm::ArrayRef<const Attr *> attrs); + RValue emitCXXMemberCallExpr(const clang::CXXMemberCallExpr *e, + ReturnValueSlot returnValue); + + RValue emitCXXMemberOrOperatorCall( + const clang::CXXMethodDecl *md, const CIRGenCallee &callee, + ReturnValueSlot returnValue, mlir::Value thisPtr, + mlir::Value implicitParam, clang::QualType implicitParamTy, + const clang::CallExpr *ce, CallArgList *rtlArgs); + + RValue emitCXXMemberOrOperatorMemberCallExpr( + const clang::CallExpr *ce, const clang::CXXMethodDecl *md, + ReturnValueSlot returnValue, bool hasQualifier, + clang::NestedNameSpecifier *qualifier, bool isArrow, + const clang::Expr *base); + mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); /// Emit an expression as an initializer for an object (variable, field, etc.) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index bd3aa37..e170498 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -61,7 +61,6 @@ CIRGenCXXABI *CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { return new CIRGenCXXABI(cgm); } } // namespace clang::CIRGen -CIRGenCXXABI::~CIRGenCXXABI() {} CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, clang::ASTContext &astContext, @@ -251,7 +250,6 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, const CIRGenFunctionInfo &fi = getTypes().arrangeGlobalDeclaration(gd); cir::FuncType funcType = getTypes().getFunctionType(fi); - cir::FuncOp funcOp = dyn_cast_if_present<cir::FuncOp>(op); if (!funcOp || funcOp.getFunctionType() != funcType) { funcOp = getAddrOfFunction(gd, funcType, /*ForVTable=*/false, @@ -539,8 +537,16 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, if (const auto *method = dyn_cast<CXXMethodDecl>(decl)) { // Make sure to emit the definition(s) before we emit the thunks. This is // necessary for the generation of certain thunks. - (void)method; - errorNYI(method->getSourceRange(), "member function"); + if (isa<CXXConstructorDecl>(method) || isa<CXXDestructorDecl>(method)) + errorNYI(method->getSourceRange(), "C++ ctor/dtor"); + else if (fd->isMultiVersion()) + errorNYI(method->getSourceRange(), "multiversion functions"); + else + emitGlobalFunctionDefinition(gd, op); + + if (method->isVirtual()) + errorNYI(method->getSourceRange(), "virtual member function"); + return; } @@ -770,6 +776,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { decl->getDeclKindName()); break; + case Decl::CXXMethod: case Decl::Function: { auto *fd = cast<FunctionDecl>(decl); // Consteval functions shouldn't be emitted. diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index dc88721..d49999e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -15,7 +15,7 @@ using namespace clang::CIRGen; CIRGenTypes::CIRGenTypes(CIRGenModule &genModule) : cgm(genModule), astContext(genModule.getASTContext()), - builder(cgm.getBuilder()), + builder(cgm.getBuilder()), theCXXABI(cgm.getCXXABI()), theABIInfo(cgm.getTargetCIRGenInfo().getABIInfo()) {} CIRGenTypes::~CIRGenTypes() { diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 53e79c3..aaad7f9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -17,6 +17,7 @@ #include "CIRGenFunctionInfo.h" #include "CIRGenRecordLayout.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/Type.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" @@ -38,6 +39,7 @@ namespace clang::CIRGen { class CallArgList; class CIRGenBuilderTy; +class CIRGenCXXABI; class CIRGenModule; /// This class organizes the cross-module state that is used while lowering @@ -46,6 +48,7 @@ class CIRGenTypes { CIRGenModule &cgm; clang::ASTContext &astContext; CIRGenBuilderTy &builder; + CIRGenCXXABI &theCXXABI; const ABIInfo &theABIInfo; @@ -81,6 +84,11 @@ public: bool isFuncTypeConvertible(const clang::FunctionType *ft); bool isFuncParamTypeConvertible(clang::QualType type); + /// Derives the 'this' type for CIRGen purposes, i.e. ignoring method CVR + /// qualification. + clang::CanQualType deriveThisType(const clang::CXXRecordDecl *rd, + const clang::CXXMethodDecl *md); + /// This map of clang::Type to mlir::Type (which includes CIR type) is a /// cache of types that have already been processed. using TypeCacheTy = llvm::DenseMap<const clang::Type *, mlir::Type>; @@ -152,6 +160,20 @@ public: bool isZeroInitializable(clang::QualType ty); bool isZeroInitializable(const RecordDecl *rd); + const CIRGenFunctionInfo & + arrangeCXXMethodCall(const CallArgList &args, + const clang::FunctionProtoType *type, + RequiredArgs required, unsigned numPrefixArgs); + + /// C++ methods have some special rules and also have implicit parameters. + const CIRGenFunctionInfo & + arrangeCXXMethodDeclaration(const clang::CXXMethodDecl *md); + + const CIRGenFunctionInfo & + arrangeCXXMethodType(const clang::CXXRecordDecl *rd, + const clang::FunctionProtoType *ftp, + const clang::CXXMethodDecl *md); + const CIRGenFunctionInfo &arrangeFreeFunctionCall(const CallArgList &args, const FunctionType *fnType); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 8f5796e..4b30558 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -10,6 +10,8 @@ add_clang_library(clangCIR CIRGenerator.cpp CIRGenBuilder.cpp CIRGenCall.cpp + CIRGenCXXABI.cpp + CIRGenCXXExpr.cpp CIRGenDecl.cpp CIRGenDeclOpenACC.cpp CIRGenExpr.cpp |