https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/171742

This adds CIR support for C++ pointer-to-member types. This only adds support 
for the type. Using the type in a non-trivial way requires a new CIR constant 
attribute which will be added in a follow-up PR.

>From 91371563ebf1315bf481d8ea0c30b05afdbfd92e Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 9 Dec 2025 17:12:51 -0800
Subject: [PATCH] [CIR] Upstream support for C++ method pointers

This adds CIR support for C++ pointer-to-member types. This only adds
support for the type. Using the type requires a new CIR constant attribute
which will be added in a follow-up PR.
---
 .../include/clang/CIR/Dialect/IR/CIRTypes.td  | 30 +++++++++++++++-
 clang/lib/CIR/CodeGen/CIRGenTypes.cpp         |  4 +--
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 27 +++++++++++++++
 .../Transforms/TargetLowering/CIRCXXABI.h     |  6 ++++
 .../TargetLowering/LowerItaniumCXXABI.cpp     | 25 ++++++++++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  6 ++++
 .../CIR/CodeGen/pointer-to-member-func.cpp    | 34 +++++++++++++++++++
 7 files changed, 129 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/pointer-to-member-func.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 59b97f0c6d39a..2be2b0344fb9a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -542,6 +542,34 @@ def CIR_FuncType : CIR_Type<"Func", "func"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// MethodType
+//===----------------------------------------------------------------------===//
+
+def CIR_MethodType : CIR_Type<"Method", "method",
+    [DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]> {
+  let summary = "CIR type that represents C++ pointer-to-member-function type";
+  let description = [{
+    `cir.method` models the pointer-to-member-function type in C++. The layout
+    of this type is ABI-dependent.
+  }];
+
+  let parameters = (ins "cir::FuncType":$member_func_ty,
+                        "cir::RecordType":$class_ty);
+
+  let builders = [
+    TypeBuilderWithInferredContext<(ins
+      "cir::FuncType":$member_func_ty, "cir::RecordType":$class_ty
+    ), [{
+      return $_get(member_func_ty.getContext(), member_func_ty, class_ty);
+    }]>,
+  ];
+
+  let assemblyFormat = [{
+    `<` qualified($member_func_ty) `in` $class_ty `>`
+  }];
+}
+
 
//===----------------------------------------------------------------------===//
 // Void type
 
//===----------------------------------------------------------------------===//
@@ -723,7 +751,7 @@ def CIRRecordType : Type<
 def CIR_AnyType : AnyTypeOf<[
   CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_VectorType, CIR_IntType,
   CIR_AnyFloatType, CIR_PointerType, CIR_FuncType, CIR_RecordType,
-  CIR_ComplexType, CIR_VPtrType, CIR_DataMemberType
+  CIR_ComplexType, CIR_VPtrType, CIR_DataMemberType, CIR_MethodType
 ]>;
 
 #endif // CLANG_CIR_DIALECT_IR_CIRTYPES_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp 
b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 7f000ece8a494..5fb9fd633c634 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -491,8 +491,8 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
     if (mpt->isMemberDataPointer()) {
       resultType = cir::DataMemberType::get(memberTy, clsTy);
     } else {
-      assert(!cir::MissingFeatures::methodType());
-      cgm.errorNYI(SourceLocation(), "MethodType");
+      auto memberFuncTy = mlir::cast<cir::FuncType>(memberTy);
+      resultType = cir::MethodType::get(memberFuncTy, clsTy);
     }
     break;
   }
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 9a37a4f4e3996..ce59991376c29 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -734,6 +734,33 @@ 
FuncType::verify(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
   return mlir::success();
 }
 
+//===----------------------------------------------------------------------===//
+// MethodType Definitions
+//===----------------------------------------------------------------------===//
+
+static mlir::Type getMethodLayoutType(mlir::MLIRContext *ctx) {
+  // With Itanium ABI, member function pointers have the same layout as the
+  // following struct: struct { fnptr_t, ptrdiff_t }, where fnptr_t is a
+  // function pointer type.
+  // TODO: consider member function pointer layout in other ABIs
+  auto voidPtrTy = cir::PointerType::get(cir::VoidType::get(ctx));
+  mlir::Type fields[2]{voidPtrTy, voidPtrTy};
+  return cir::RecordType::get(ctx, fields, /*packed=*/false,
+                              /*padded=*/false, cir::RecordType::Struct);
+}
+
+llvm::TypeSize
+MethodType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
+                              mlir::DataLayoutEntryListRef params) const {
+  return dataLayout.getTypeSizeInBits(getMethodLayoutType(getContext()));
+}
+
+uint64_t
+MethodType::getABIAlignment(const mlir::DataLayout &dataLayout,
+                            mlir::DataLayoutEntryListRef params) const {
+  return dataLayout.getTypeSizeInBits(getMethodLayoutType(getContext()));
+}
+
 
//===----------------------------------------------------------------------===//
 // BoolType
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
index 003cd78eb3f26..a8f30034aee3a 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
@@ -39,6 +39,12 @@ class CIRCXXABI {
   lowerDataMemberType(cir::DataMemberType type,
                       const mlir::TypeConverter &typeConverter) const = 0;
 
+  /// Lower the given member function pointer type to its ABI type. The 
returned
+  /// type is also a CIR type.
+  virtual mlir::Type
+  lowerMethodType(cir::MethodType type,
+                  const mlir::TypeConverter &typeConverter) const = 0;
+
   /// Lower the given data member pointer constant to a constant of the ABI
   /// type. The returned constant is represented as an attribute as well.
   virtual mlir::TypedAttr
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 7089990343dc0..da45cfe8079d8 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -39,6 +39,10 @@ class LowerItaniumCXXABI : public CIRCXXABI {
   lowerDataMemberType(cir::DataMemberType type,
                       const mlir::TypeConverter &typeConverter) const override;
 
+  mlir::Type
+  lowerMethodType(cir::MethodType type,
+                  const mlir::TypeConverter &typeConverter) const override;
+
   mlir::TypedAttr lowerDataMemberConstant(
       cir::DataMemberAttr attr, const mlir::DataLayout &layout,
       const mlir::TypeConverter &typeConverter) const override;
@@ -66,6 +70,27 @@ mlir::Type LowerItaniumCXXABI::lowerDataMemberType(
   return getPtrDiffCIRTy(lm);
 }
 
+mlir::Type LowerItaniumCXXABI::lowerMethodType(
+    cir::MethodType type, const mlir::TypeConverter &typeConverter) const {
+  // Itanium C++ ABI 2.3.2:
+  //    In all representations, the basic ABI properties of member function
+  //    pointer types are those of the following class, where fnptr_t is the
+  //    appropriate function-pointer type for a member function of this type:
+  //
+  //    struct {
+  //      fnptr_t ptr;
+  //      ptrdiff_t adj;
+  //    };
+
+  cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
+
+  // Note that clang CodeGen emits struct{ptrdiff_t, ptrdiff_t} for member
+  // function pointers. Let's follow this approach.
+  return cir::RecordType::get(type.getContext(), {ptrdiffCIRTy, ptrdiffCIRTy},
+                              /*packed=*/false, /*padded=*/false,
+                              cir::RecordType::Struct);
+}
+
 mlir::TypedAttr LowerItaniumCXXABI::lowerDataMemberConstant(
     cir::DataMemberAttr attr, const mlir::DataLayout &layout,
     const mlir::TypeConverter &typeConverter) const {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index eeb886445ede4..735598fdabcbd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2882,6 +2882,12 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter 
&converter,
             lowerModule->getCXXABI().lowerDataMemberType(type, converter);
         return converter.convertType(abiType);
       });
+  converter.addConversion([&, lowerModule](cir::MethodType type) -> mlir::Type 
{
+    assert(lowerModule && "CXXABI is not available");
+    mlir::Type abiType =
+        lowerModule->getCXXABI().lowerMethodType(type, converter);
+    return converter.convertType(abiType);
+  });
   converter.addConversion([&](cir::ArrayType type) -> mlir::Type {
     mlir::Type ty =
         convertTypeForMemory(converter, dataLayout, type.getElementType());
diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp 
b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp
new file mode 100644
index 0000000000000..6c02dacc7a44d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct Foo {
+  void m1(int);
+  virtual void m2(int);
+  virtual void m3(int);
+};
+
+void unused_pointer_to_member_func(void (Foo::*func)(int)) {
+}
+
+// CIR: cir.func {{.*}} 
@_Z29unused_pointer_to_member_funcM3FooFviE(%[[ARG:.*]]: 
!cir.method<!cir.func<(!s32i)> in !rec_Foo> {{.*}})
+// CIR:   %[[FUNC:.*]] = cir.alloca !cir.method<!cir.func<(!s32i)> in 
!rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["func", init]
+
+// NOTE: The difference between LLVM and OGCG are due to the lack of calling 
convention handling in CIR.
+
+// LLVM: define {{.*}} void @_Z29unused_pointer_to_member_funcM3FooFviE({ i64, 
i64 } %[[ARG:.*]])
+// LLVM:   %[[FUNC:.*]] = alloca { i64, i64 }
+// LLVM:   store { i64, i64 } %[[ARG]], ptr %[[FUNC]]
+
+// OGCG: define {{.*}} void @_Z29unused_pointer_to_member_funcM3FooFviE(i64 
%[[FUNC_COERCE0:.*]], i64 %[[FUNC_COERCE1:.*]])
+// OGCG:   %[[FUNC:.*]] = alloca { i64, i64 }
+// OGCG:   %[[FUNC_ADDR:.*]] = alloca { i64, i64 }
+// OGCG:   %[[FUNC_0:.*]] = getelementptr inbounds nuw { i64, i64 }, ptr 
%[[FUNC]], i32 0, i32 0
+// OGCG:   store i64 %[[FUNC_COERCE0]], ptr %[[FUNC_0]]
+// OGCG:   %[[FUNC_1:.*]] = getelementptr inbounds nuw { i64, i64 }, ptr 
%[[FUNC]], i32 0, i32 1
+// OGCG:   store i64 %[[FUNC_COERCE1]], ptr %[[FUNC_1]]
+// OGCG:   %[[FUNC1:.*]] = load { i64, i64 }, ptr %[[FUNC]]
+// OGCG:   store { i64, i64 } %[[FUNC1]], ptr %[[FUNC_ADDR]]

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

Reply via email to