https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/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.

>From e1e91e88d2857b3a245c09f5c2afbfe37bc66063 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Fri, 2 May 2025 16:58:13 -0700
Subject: [PATCH] [CIR] Upstream support for C++ member function calls

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.
---
 clang/include/clang/CIR/MissingFeatures.h   |  11 +-
 clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp      |  45 +++++
 clang/lib/CIR/CodeGen/CIRGenCXXABI.h        |  27 ++-
 clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp     | 186 ++++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenCall.cpp        |  97 +++++++++-
 clang/lib/CIR/CodeGen/CIRGenCall.h          |   8 +
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp        |  37 +++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp    |  10 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h      |  15 ++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp      |  15 +-
 clang/lib/CIR/CodeGen/CIRGenTypes.cpp       |   2 +-
 clang/lib/CIR/CodeGen/CIRGenTypes.h         |  22 +++
 clang/lib/CIR/CodeGen/CMakeLists.txt        |   2 +
 clang/test/CIR/CodeGen/member-functions.cpp |  44 +++++
 14 files changed, 502 insertions(+), 19 deletions(-)
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
 create mode 100644 clang/test/CIR/CodeGen/member-functions.cpp

diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 484822c351746..a4324053ba476 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,9 @@ 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; }
 
   // 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..fcd45768656ff
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp
@@ -0,0 +1,45 @@
+//===----- CirGenCXXABI.cpp - Interface to C++ ABIs 
-----------------------===//
+//
+// 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 &params) {
+  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 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 &params);
+
+  /// 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<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 17bfa19f9fd63..7590153410747 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,30 @@ 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
+    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 +132,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 +226,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 +252,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 2ba1676eb6b97..5156f78e562ad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -109,6 +109,14 @@ struct CallArg {
 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());
+    // TODO: Writebacks, CleanupsToDeactivate, 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<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 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<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 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<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 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<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 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<const clang::Type *, mlir::Type>;
@@ -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<struct "C" padded {!u8i}>
+
+void C::f() {}
+
+// CIR: cir.func @_ZN1C1fEv(%[[THIS_ARG:.*]]: !cir.ptr<!rec_C>
+// CIR:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, 
!cir.ptr<!cir.ptr<!rec_C>>, ["this", init]
+// CIR:   cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr<!rec_C>, 
!cir.ptr<!cir.ptr<!rec_C>>
+// CIR:   cir.return
+// CIR: }
+
+void C::f2(int a, int b) {}
+
+// CIR:      cir.func @_ZN1C2f2Eii(%[[THIS_ARG:.*]]: !cir.ptr<!rec_C> {{.*}}, 
%[[A_ARG:.*]]: !s32i {{.*}}, %[[B_ARG:.*]]: !s32i {{.*}}) {
+// CIR-NEXT:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_C>, 
!cir.ptr<!cir.ptr<!rec_C>>, ["this", init]
+// CIR-NEXT:   %[[A_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
+// CIR-NEXT:   %[[B_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
+// CIR-NEXT:   cir.store %[[THIS_ARG]], %[[THIS_ADDR]] : !cir.ptr<!rec_C>, 
!cir.ptr<!cir.ptr<!rec_C>>
+// CIR-NEXT:   cir.store %[[A_ARG]], %[[A_ADDR]] : !s32i, !cir.ptr<!s32i> 
loc(#loc12)
+// CIR-NEXT:   cir.store %[[B_ARG]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i> 
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<!rec_C>, ["c"]
+// CIR-NEXT:   cir.call @_ZN1C1fEv(%[[C_ADDR]]) : (!cir.ptr<!rec_C>) -> ()
+// 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<!rec_C>, !s32i, !s32i) -> ()
+// CIR-NEXT:   cir.return
+// CIR-NEXT: }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to