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
