https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/174640
>From 07fa05c82b4a77bd4a3196268a28d1a0cf34e432 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Thu, 11 Dec 2025 10:48:09 -0800 Subject: [PATCH 1/4] [CIR] Upstream CIR method attribute handling This adds code for generating cir.method attributes and lowering them to LLVM IR to implement support the C++ method pointer variables. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 ++ .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 43 +++++++++++++++++ clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 15 ++++-- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 44 +++++++++++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 +- .../CIR/Dialect/Transforms/CXXABILowering.cpp | 9 ++++ .../Transforms/TargetLowering/CIRCXXABI.h | 6 +++ .../TargetLowering/LowerItaniumCXXABI.cpp | 48 +++++++++++++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 15 +++--- .../CIR/CodeGen/pointer-to-member-func.cpp | 41 ++++++++-------- 11 files changed, 200 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index cc28941aaa079..eadf3dd6ee0f0 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -179,6 +179,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return getPointerTo(cir::VoidType::get(getContext()), as); } + cir::MethodAttr getMethodAttr(cir::MethodType ty, cir::FuncOp methodFuncOp) { + auto methodFuncSymbolRef = mlir::FlatSymbolRefAttr::get(methodFuncOp); + return cir::MethodAttr::get(ty, methodFuncSymbolRef); + } + cir::BoolAttr getCIRBoolAttr(bool state) { return cir::BoolAttr::get(getContext(), state); } diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index c0279a0b20670..74ca19fb329b3 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -498,6 +498,49 @@ def CIR_DataMemberAttr : CIR_Attr<"DataMember", "data_member", [ }]; } +//===----------------------------------------------------------------------===// +// MethodAttr +//===----------------------------------------------------------------------===// + +def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { + let summary = "Holds a constant pointer-to-member-function value"; + let description = [{ + A method attribute is a literal attribute that represents a constant + pointer-to-member-function value. + + If the member function is a non-virtual function, the `symbol` parameter + gives the global symbol for the non-virtual member function. + + Virtual function handling is not yet implemented. + + If `symbol` is not present, the attribute represents a null pointer + constant. + }]; + + let parameters = (ins AttributeSelfTypeParameter< + "", "cir::MethodType">:$type, + OptionalParameter< + "std::optional<mlir::FlatSymbolRefAttr>">:$symbol); + + let builders = [ + AttrBuilderWithInferredContext<(ins "cir::MethodType":$type), [{ + return $_get(type.getContext(), type, std::nullopt); + }]>, + AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, + "mlir::FlatSymbolRefAttr":$symbol), [{ + return $_get(type.getContext(), type, symbol); + }]>, + ]; + + let hasCustomAssemblyFormat = 1; + + let extraClassDeclaration = [{ + bool isNull() const { + return !getSymbol().has_value(); + } + }]; +} + //===----------------------------------------------------------------------===// // GlobalViewAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 51c8de3c98a23..9336fafea04cd 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -347,6 +347,7 @@ struct MissingFeatures { static bool useEHCleanupForArray() { return false; } static bool vaArgABILowering() { return false; } static bool vectorConstants() { return false; } + static bool virtualMethodAttr() { return false; } static bool vlas() { return false; } static bool vtableInitialization() { return false; } static bool vtableEmitMetadata() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 42df6304628dc..a995a4ceb601d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1527,9 +1527,18 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) { const auto *decl = cast<DeclRefExpr>(e->getSubExpr())->getDecl(); // A member function pointer. - if (isa<CXXMethodDecl>(decl)) { - errorNYI(e->getSourceRange(), "emitMemberPointerConstant: method pointer"); - return {}; + if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl)) { + auto ty = mlir::cast<cir::MethodType>(convertType(e->getType())); + if (methodDecl->isVirtual()) { + assert(!cir::MissingFeatures::virtualMethodAttr()); + errorNYI(e->getSourceRange(), + "emitMemberPointerConstant: virtual method pointer"); + return {}; + } + + auto methodFuncOp = getAddrOfFunction(methodDecl); + return cir::ConstantOp::create(builder, loc, + builder.getMethodAttr(ty, methodFuncOp)); } // Otherwise, a member data pointer. diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 59d7765198f9e..c69335ccf7960 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -301,6 +301,50 @@ DataMemberAttr::verify(function_ref<InFlightDiagnostic()> emitError, return success(); } +//===----------------------------------------------------------------------===// +// MethodAttr definitions +//===----------------------------------------------------------------------===// + +Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { + auto ty = mlir::cast<cir::MethodType>(odsType); + + if (parser.parseLess().failed()) + return {}; + + // Try to parse the null pointer constant. + if (parser.parseOptionalKeyword("null").succeeded()) { + if (parser.parseGreater()) + return {}; + return get(ty); + } + + // Try to parse a flat symbol ref for a pointer to non-virtual member + // function. + FlatSymbolRefAttr symbol; + auto parseSymbolRefResult = parser.parseOptionalAttribute(symbol); + if (parseSymbolRefResult.has_value()) { + if (parseSymbolRefResult.value().failed()) + return {}; + if (parser.parseGreater()) + return {}; + return get(ty, symbol); + } + + return {}; +} + +void MethodAttr::print(AsmPrinter &printer) const { + auto symbol = getSymbol(); + + printer << '<'; + if (symbol.has_value()) { + printer << *symbol; + } else { + printer << "null"; + } + printer << '>'; +} + //===----------------------------------------------------------------------===// // CIR ConstArrayAttr //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index e836c3050e4ee..6c4607abb40e7 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -358,9 +358,9 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, return success(); } - if (isa<cir::DataMemberAttr>(attrType)) { + if (isa<cir::DataMemberAttr, cir::MethodAttr>(attrType)) { // More detailed type verifications are already done in - // DataMemberAttr::verify. Don't need to repeat here. + // DataMemberAttr::verify or MethodAttr::verify. Don't need to repeat here. return success(); } diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index 99addf8d0adb4..145f8574893f4 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -143,6 +143,15 @@ mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite( return mlir::success(); } + if (mlir::isa<cir::MethodType>(op.getType())) { + auto method = mlir::cast<cir::MethodAttr>(op.getValue()); + mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>()); + mlir::TypedAttr abiValue = lowerModule->getCXXABI().lowerMethodConstant( + method, layout, *getTypeConverter()); + rewriter.replaceOpWithNewOp<ConstantOp>(op, abiValue); + return mlir::success(); + } + llvm_unreachable("constant operand is not an CXXABI-dependent type"); } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h index d554a64e4bf63..0dedfa7221f5f 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h @@ -53,6 +53,12 @@ class CIRCXXABI { const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const = 0; + /// Lower the given member function pointer constant to a constant of the ABI + /// type. The returned constant is represented as an attribute as well. + virtual mlir::TypedAttr + lowerMethodConstant(cir::MethodAttr attr, const mlir::DataLayout &layout, + const mlir::TypeConverter &typeConverter) const = 0; + /// Lower the given cir.get_runtime_member op to a sequence of more /// "primitive" CIR operations that act on the ABI types. virtual mlir::Operation * diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index 5abe58489e96f..88e7aa7b1b3df 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -47,6 +47,10 @@ class LowerItaniumCXXABI : public CIRCXXABI { cir::DataMemberAttr attr, const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const override; + mlir::TypedAttr + lowerMethodConstant(cir::MethodAttr attr, const mlir::DataLayout &layout, + const mlir::TypeConverter &typeConverter) const override; + mlir::Operation * lowerGetRuntimeMember(cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy, mlir::Value loweredAddr, mlir::Value loweredMember, @@ -129,6 +133,50 @@ mlir::TypedAttr LowerItaniumCXXABI::lowerDataMemberConstant( return cir::IntAttr::get(abiTy, memberOffset); } +mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant( + cir::MethodAttr attr, const mlir::DataLayout &layout, + const mlir::TypeConverter &typeConverter) const { + cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm); + auto loweredMethodTy = mlir::cast<cir::RecordType>( + lowerMethodType(attr.getType(), typeConverter)); + + auto zero = cir::IntAttr::get(ptrdiffCIRTy, 0); + + // 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; + // }; + + if (attr.isNull()) { + // Itanium C++ ABI 2.3.2: + // + // In the standard representation, a null member function pointer is + // represented with ptr set to a null pointer. The value of adj is + // unspecified for null member function pointers. + // + // clang CodeGen emits struct{null, null} for null member function pointers. + // Let's do the same here. + return cir::ConstRecordAttr::get( + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero})); + } + + assert(!cir::MissingFeatures::virtualMethodAttr()); + + // Itanium C++ ABI 2.3.2: + // + // A member function pointer for a non-virtual member function is + // represented with ptr set to a pointer to the function, using the base + // ABI's representation of function pointers. + auto ptr = cir::GlobalViewAttr::get(ptrdiffCIRTy, attr.getSymbol().value()); + return cir::ConstRecordAttr::get( + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero})); +} + mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember( cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy, mlir::Value loweredAddr, mlir::Value loweredMember, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 60143a734d37a..857ac751ca539 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -578,11 +578,11 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) { mlir::LLVM::GEPNoWrapFlags::none); } - // The incubator has handling here for the attribute having integer type, but - // the only test case I could find that reaches it is a direct CIR-to-LLVM IR - // lowering with no clear indication of how the CIR might have been generated. - // We'll hit the unreachable below if this happens. - assert(!cir::MissingFeatures::globalViewIntLowering()); + if (auto intTy = mlir::dyn_cast<cir::IntType>(globalAttr.getType())) { + auto llvmDstTy = converter->convertType(globalAttr.getType()); + return mlir::LLVM::PtrToIntOp::create(rewriter, parentOp->getLoc(), + llvmDstTy, addrOp); + } if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(globalAttr.getType())) { mlir::Type llvmEltTy = @@ -1802,7 +1802,10 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( } else if (mlir::isa<cir::IntType>(op.getType())) { // Lower GlobalViewAttr to llvm.mlir.addressof + llvm.mlir.ptrtoint if (auto ga = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) { - // See the comment in visitCirAttr for why this isn't implemented. + // We can have a global view with an integer type in the case of method + // pointers, but the lowering of those doesn't go through this path. + // They are handled in the visitCirAttr. This is left as an error until + // we have a test case that reaches it. assert(!cir::MissingFeatures::globalViewIntLowering()); op.emitError() << "global view with integer type"; return mlir::failure(); diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp index e3fe05dfa34fa..47c5871e72290 100644 --- a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp @@ -12,28 +12,29 @@ struct Foo { virtual void m3(int); }; -void unused_pointer_to_member_func(void (Foo::*func)(int)) { +auto make_non_virtual() -> void (Foo::*)(int) { + return &Foo::m1; } -// CIR-BEFORE: cir.func {{.*}} @_Z29unused_pointer_to_member_funcM3FooFviE(%[[ARG:.*]]: !cir.method<!cir.func<(!s32i)> in !rec_Foo>) -// CIR-BEFORE: %[[FUNC:.*]] = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["func", init] +// CIR-BEFORE: cir.func {{.*}} @_Z16make_non_virtualv() -> !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CIR-BEFORE: %[[RETVAL:.*]] = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["__retval"] +// CIR-BEFORE: %[[METHOD_PTR:.*]] = cir.const #cir.method<@_ZN3Foo2m1Ei> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CIR-BEFORE: cir.store %[[METHOD_PTR]], %[[RETVAL]] +// CIR-BEFORE: %[[RET:.*]] = cir.load %[[RETVAL]] +// CIR-BEFORE: cir.return %[[RET]] : !cir.method<!cir.func<(!s32i)> in !rec_Foo> -// CIR-AFTER: !rec_anon_struct = !cir.record<struct {!s64i, !s64i}> -// CIR-AFTER: cir.func {{.*}} @_Z29unused_pointer_to_member_funcM3FooFviE(%[[ARG:.*]]: !rec_anon_struct {{.*}}) -// CIR-AFTER %[[FUNC:.*]] = cir.alloca !rec_anon_struct, !cir.ptr<!rec_anon_struct>, ["func", init] +// CIR-AFTER: cir.func {{.*}} @_Z16make_non_virtualv() -> !rec_anon_struct { +// CIR-AFTER: %[[RETVAL:.*]] = cir.alloca !rec_anon_struct, !cir.ptr<!rec_anon_struct>, ["__retval"] +// CIR-AFTER: %[[METHOD_PTR:.*]] = cir.const #cir.const_record<{#cir.global_view<@_ZN3Foo2m1Ei> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// CIR-AFTER: cir.store %[[METHOD_PTR]], %[[RETVAL]] +// CIR-AFTER: %[[RET:.*]] = cir.load %[[RETVAL]] +// CIR-AFTER: cir.return %[[RET]] : !rec_anon_struct -// NOTE: The difference between LLVM and OGCG are due to the lack of calling convention handling in CIR. +// LLVM: define {{.*}} { i64, i64 } @_Z16make_non_virtualv() +// LLVM: %[[RETVAL:.*]] = alloca { i64, i64 } +// LLVM: store { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 }, ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] +// LLVM: ret { i64, i64 } %[[RET]] -// 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]] +// OGCG: define {{.*}} { i64, i64 } @_Z16make_non_virtualv() +// OGCG: ret { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } >From fe2bad54f822f55d7a3584981e14fb32416ee08e Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Wed, 7 Jan 2026 09:12:46 -0800 Subject: [PATCH 2/4] Address review feedback --- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 2 +- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 7 ++++--- .../Transforms/TargetLowering/LowerItaniumCXXABI.cpp | 4 ++++ clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index a995a4ceb601d..fcd3f105807ca 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1536,7 +1536,7 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) { return {}; } - auto methodFuncOp = getAddrOfFunction(methodDecl); + cir::FuncOp methodFuncOp = getAddrOfFunction(methodDecl); return cir::ConstantOp::create(builder, loc, builder.getMethodAttr(ty, methodFuncOp)); } diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index c69335ccf7960..2f4240c385cab 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -313,7 +313,7 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { // Try to parse the null pointer constant. if (parser.parseOptionalKeyword("null").succeeded()) { - if (parser.parseGreater()) + if (parser.parseGreater().failed()) return {}; return get(ty); } @@ -321,11 +321,12 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { // Try to parse a flat symbol ref for a pointer to non-virtual member // function. FlatSymbolRefAttr symbol; - auto parseSymbolRefResult = parser.parseOptionalAttribute(symbol); + mlir::OptionalParseResult parseSymbolRefResult = + parser.parseOptionalAttribute(symbol); if (parseSymbolRefResult.has_value()) { if (parseSymbolRefResult.value().failed()) return {}; - if (parser.parseGreater()) + if (parser.parseGreater().failed()) return {}; return get(ty, symbol); } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index 88e7aa7b1b3df..d944fa3294684 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -137,6 +137,10 @@ mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant( cir::MethodAttr attr, const mlir::DataLayout &layout, const mlir::TypeConverter &typeConverter) const { cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm); + + // lowerMethodType returns the CIR type used to represent the method pointer + // in an ABI-specific way. That's why lowerMethodType returns cir::RecordType + // here. auto loweredMethodTy = mlir::cast<cir::RecordType>( lowerMethodType(attr.getType(), typeConverter)); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 857ac751ca539..a4082fc9d3d29 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -579,7 +579,7 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) { } if (auto intTy = mlir::dyn_cast<cir::IntType>(globalAttr.getType())) { - auto llvmDstTy = converter->convertType(globalAttr.getType()); + mlir::Type llvmDstTy = converter->convertType(globalAttr.getType()); return mlir::LLVM::PtrToIntOp::create(rewriter, parentOp->getLoc(), llvmDstTy, addrOp); } >From d9b21e6bab0a1c1480c79d43d2018e0a0a082636 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 12 Jan 2026 16:15:05 -0800 Subject: [PATCH 3/4] Add examples --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 74ca19fb329b3..2309811815f27 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -515,6 +515,20 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { If `symbol` is not present, the attribute represents a null pointer constant. + + Examples: + ``` + // Non-virtual method + %0 = cir.const #cir.method<@_ZN1S2m1Ei> : + !cir.method<!cir.func<(!s32i)> in !rec_S> + + // Virtual method (not yet implemented) + %1 = cir.const #cir.method<vtable_offset = 8> : + !cir.method<!cir.func<(!s32i)> in !rec_S> + + // Null method pointer + %0 = cir.const #cir.method<null> : + !cir.method<!cir.func<(!s32i)> in !rec_S> }]; let parameters = (ins AttributeSelfTypeParameter< >From 1dc8cd6fa2c216d8e5ccc74a35e74d4c345330d6 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 13 Jan 2026 12:02:56 -0800 Subject: [PATCH 4/4] Address review feedback --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 4 ++ clang/test/CIR/IR/method-attr.cir | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 clang/test/CIR/IR/method-attr.cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index a4082fc9d3d29..ad1de401e08bf 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -578,6 +578,10 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) { mlir::LLVM::GEPNoWrapFlags::none); } + // We can have a global view with an integer type in the case of method + // pointers. With the Itanium ABI, the #cir.method attribute is lowered to a + // #cir.global_view with a pointer-sized integer representing the address of + // the method. if (auto intTy = mlir::dyn_cast<cir::IntType>(globalAttr.getType())) { mlir::Type llvmDstTy = converter->convertType(globalAttr.getType()); return mlir::LLVM::PtrToIntOp::create(rewriter, parentOp->getLoc(), diff --git a/clang/test/CIR/IR/method-attr.cir b/clang/test/CIR/IR/method-attr.cir new file mode 100644 index 0000000000000..1ab3f65f205b7 --- /dev/null +++ b/clang/test/CIR/IR/method-attr.cir @@ -0,0 +1,41 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!void = !cir.void + +!rec_Foo = !cir.record<struct "Foo" {!cir.vptr}> +!s32i = !cir.int<s, 32> +module { + cir.func private @_ZN3Foo2m1Ei(!s32i) + cir.func no_inline dso_local @_Z16make_non_virtualv() -> !cir.method<!cir.func<(!s32i)> in !rec_Foo> { + %0 = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["__retval"] {alignment = 8 : i64} + %1 = cir.const #cir.method<@_ZN3Foo2m1Ei> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> + cir.store %1, %0 : !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>> + %2 = cir.load %0 : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo> + cir.return %2 : !cir.method<!cir.func<(!s32i)> in !rec_Foo> + } + +// CHECK: cir.func no_inline dso_local @_Z16make_non_virtualv() -> !cir.method<!cir.func<(!s32i)> in !rec_Foo> { +// CHECK: %0 = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["__retval"] {alignment = 8 : i64} +// CHECK: %1 = cir.const #cir.method<@_ZN3Foo2m1Ei> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: cir.store %1, %0 : !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>> +// CHECK: %2 = cir.load %0 : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: cir.return %2 : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: } + + cir.func no_inline dso_local @_Z9make_nullv() -> !cir.method<!cir.func<(!s32i)> in !rec_Foo> { + %0 = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["__retval"] {alignment = 8 : i64} + %1 = cir.const #cir.method<null> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> + cir.store %1, %0 : !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>> + %2 = cir.load %0 : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo> + cir.return %2 : !cir.method<!cir.func<(!s32i)> in !rec_Foo> + } + +// CHECK: cir.func no_inline dso_local @_Z9make_nullv() -> !cir.method<!cir.func<(!s32i)> in !rec_Foo> { +// CHECK: %0 = cir.alloca !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, ["__retval"] {alignment = 8 : i64} +// CHECK: %1 = cir.const #cir.method<null> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: cir.store %1, %0 : !cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>> +// CHECK: %2 = cir.load %0 : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: cir.return %2 : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CHECK: } + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
