diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 484822c351746..c0207393832f3 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -90,7 +90,6 @@ struct MissingFeatures { static bool opCallArgEvaluationOrder() { return false; } static bool opCallCallConv() { return false; } static bool opCallSideEffect() { return false; } - static bool opCallChainCall() { return false; } static bool opCallNoPrototypeFunc() { return false; } static bool opCallMustTail() { return false; } static bool opCallIndirect() { return false; } @@ -107,6 +106,13 @@ struct MissingFeatures { static bool opCallLandingPad() { return false; } static bool opCallContinueBlock() { return false; } + // FnInfoOpts -- This is used to track whether calls are chain calls or + // instance methods. Classic codegen uses chain call to track and extra free + // register for x86 and uses instance method as a condition for a thunk + // generation special case. It's not clear that we need either of these in + // pre-lowering CIR codegen. + static bool opCallFnInfoOpts() { return false; } + // ScopeOp handling static bool opScopeCleanupRegion() { return false; } @@ -189,6 +195,12 @@ struct MissingFeatures { static bool constEmitterArrayILE() { return false; } static bool constEmitterVectorILE() { return false; } static bool needsGlobalCtorDtor() { return false; } + static bool emitTypeCheck() { return false; } + static bool cxxabiThisDecl() { return false; } + static bool cxxabiThisAlignment() { return false; } + static bool writebacks() { return false; } + static bool cleanupsToDeactivate() { return false; } + static bool stackBase() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp new file mode 100644 index 0000000000000..e368ccd006bb8 --- /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(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 5279307e19613..b5c33a29442c7 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 @@ class CIRGenCXXABI { // 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 0000000000000..906c212f0fa8a --- /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(ce) || + isa(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(); + 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(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(ce) || isa(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(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(md)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberOrOperatorMemberCallExpr: constructor call"); + return RValue::get(nullptr); + } + + if (trivialForCodegen) { + if (isa(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(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(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(); + 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 17bfa19f9fd63..bf3851544a3a5 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 &prefix, CanQual 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 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(md) && "wrong method for constructors!"); + assert(!isa(md) && "wrong method for destructors!"); + + auto prototype = + md->getType()->getCanonicalTypeUnqualified().getAs(); + 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 argTypes; + + // Add the 'this' pointer. + argTypes.push_back(deriveThisType(rd, md)); + + assert(!cir::MissingFeatures::opCallFnInfoOpts()); + return ::arrangeCIRFunctionInfo( + *this, argTypes, + ftp->getCanonicalTypeUnqualified().getAs()); +} + /// 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(fd)) { - if (md->isInstance()) { - cgm.errorNYI("arrangeFunctionDeclaration: instance method"); - } - } + if (const auto *md = dyn_cast(fd)) + if (md->isInstance()) + return arrangeCXXMethodDeclaration(md); CanQualType funcTy = fd->getType()->getCanonicalTypeUnqualified(); @@ -142,6 +231,7 @@ CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) { if (CanQual noProto = funcTy.getAs()) { 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 fpt) { SmallVector argTypes; + assert(!cir::MissingFeatures::opCallFnInfoOpts()); return ::arrangeCIRFunctionInfo(*this, argTypes, fpt); } const CIRGenFunctionInfo & CIRGenTypes::arrangeFreeFunctionType(CanQual 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 2ba1676eb6b97..d3c241c27d048 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -109,6 +109,18 @@ struct CallArg { class CallArgList : public llvm::SmallVector { 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 038696182f6c8..b8e644d80d747 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(e)) { - cgm.errorNYI(e->getSourceRange(), "call to member function"); - return RValue::get(nullptr); - } + if (const auto *ce = dyn_cast(e)) + return emitCXXMemberCallExpr(ce, returnValue); if (isa(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(callee)) { + cgm.errorNYI(ce->getSourceRange(), + "emitCXXMemberCallExpr: C++ binary operator"); + return RValue::get(nullptr); + } + + const auto *me = cast(callee); + const auto *md = cast(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 a424f7abd05c0..6bfad71f241dc 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(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(fd)) cgm.errorNYI(fd->getSourceRange(), diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index f7670eda7ef87..dbcc6ad832b34 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -534,6 +534,21 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s, llvm::ArrayRef 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 bd3aa37a92af6..e170498b67548 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(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(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(method) || isa(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(decl); // Consteval functions shouldn't be emitted. diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index dc8872122995c..d49999e0bdb52 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 53e79c3641c40..aaad7f933c60d 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 @@ class CIRGenTypes { 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; @@ -152,6 +160,20 @@ class CIRGenTypes { 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 8f5796e59d3bb..4b305581876bc 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 diff --git a/clang/test/CIR/CodeGen/member-functions.cpp b/clang/test/CIR/CodeGen/member-functions.cpp new file mode 100644 index 0000000000000..3d7dc8c4184a1 --- /dev/null +++ b/clang/test/CIR/CodeGen/member-functions.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR + +struct C { + void f(); + void f2(int a, int b); +}; + +// CIR: !rec_C = !cir.record + +void C::f() {} + +// CIR: cir.func @_ZN1C1fEv(%[[THIS_ARG:.*]]: !cir.ptr +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> +// CIR: cir.return +// CIR: } + +void C::f2(int a, int b) {} + +// CIR: cir.func @_ZN1C2f2Eii(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}, %[[A_ARG:.*]]: !s32i {{.*}}, %[[B_ARG:.*]]: !s32i {{.*}}) { +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR-NEXT: %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR-NEXT: %[[B_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR-NEXT: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr, !cir.ptr> +// CIR-NEXT: cir.store %[[A_ARG]], %[[A_ADDR]] : !s32i, !cir.ptr loc(#loc12) +// CIR-NEXT: cir.store %[[B_ARG]], %[[B_ADDR]] : !s32i, !cir.ptr loc(#loc12) +// CIR-NEXT: cir.return +// CIR-NEXT: } + +void test1() { + C c; + c.f(); + c.f2(1, 2); +} + +// CIR: cir.func @_Z5test1v() { +// CIR-NEXT: %[[C_ADDR:.*]] = cir.alloca !rec_C, !cir.ptr, ["c"] +// CIR-NEXT: cir.call @_ZN1C1fEv(%[[C_ADDR]]) : (!cir.ptr) -> () +// CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR-NEXT: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i +// CIR-NEXT: cir.call @_ZN1C2f2Eii(%[[C_ADDR]], %[[ONE]], %[[TWO]]) : (!cir.ptr, !s32i, !s32i) -> () +// CIR-NEXT: cir.return +// CIR-NEXT: }