[clang] [CIR] Upstream support for C++ method pointers (PR #171742)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 10 15:51:41 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Andy Kaylor (andykaylor)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/171742.diff
7 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+29-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+2-2)
- (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+27)
- (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h (+6)
- (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp (+25)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+6)
- (added) clang/test/CIR/CodeGen/pointer-to-member-func.cpp (+34)
``````````diff
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]]
``````````
</details>
https://github.com/llvm/llvm-project/pull/171742
More information about the cfe-commits
mailing list