From 09371421966c31075afa7022f9596e6aecdfeeeb Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Sun, 23 Nov 2025 17:00:57 -0800 Subject: [PATCH] Update [ghstack-poisoned] --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 24 +++++- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 15 ++++ clang/lib/CIR/CodeGen/CIRGenExprConst.cpp | 12 +-- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 86 +++++++++++++++++++ clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 31 ++++++- .../TargetLowering/ItaniumCXXABI.cpp | 5 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 12 +++ clang/test/CIR/CodeGen/member-ptr-init.cpp | 35 ++++++++ 8 files changed, 202 insertions(+), 18 deletions(-) create mode 100644 clang/test/CIR/CodeGen/member-ptr-init.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index c3281e30506c..08c2a180f4c9 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -696,6 +696,10 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { gives the offset of the vtable entry corresponding to the virtual member function. + The `adjustment` parameter specifies the this-pointer adjustment required + for the member function pointer, in bytes. This is needed for multiple + inheritance and virtual base classes. + `symbol` and `vtable_offset` cannot be present at the same time. If both of `symbol` and `vtable_offset` are not present, the attribute represents a null pointer constant. @@ -706,19 +710,31 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { OptionalParameter< "std::optional">:$symbol, OptionalParameter< - "std::optional">:$vtable_offset); + "std::optional">:$vtable_offset, + DefaultValuedParameter< + "int64_t", "0">:$adjustment); let builders = [ AttrBuilderWithInferredContext<(ins "cir::MethodType":$type), [{ - return $_get(type.getContext(), type, std::nullopt, std::nullopt); + return $_get(type.getContext(), type, std::nullopt, std::nullopt, 0); }]>, AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, "mlir::FlatSymbolRefAttr":$symbol), [{ - return $_get(type.getContext(), type, symbol, std::nullopt); + return $_get(type.getContext(), type, symbol, std::nullopt, 0); + }]>, + AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, + "mlir::FlatSymbolRefAttr":$symbol, + "int64_t":$adjustment), [{ + return $_get(type.getContext(), type, symbol, std::nullopt, adjustment); }]>, AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, "uint64_t":$vtable_offset), [{ - return $_get(type.getContext(), type, std::nullopt, vtable_offset); + return $_get(type.getContext(), type, std::nullopt, vtable_offset, 0); + }]>, + AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, + "uint64_t":$vtable_offset, + "int64_t":$adjustment), [{ + return $_get(type.getContext(), type, std::nullopt, vtable_offset, adjustment); }]>, ]; diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index cc08eca83354..e395fda5002d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -411,6 +411,21 @@ class CIRGenCXXABI { virtual cir::MethodAttr buildVirtualMethodAttr(cir::MethodType MethodTy, const CXXMethodDecl *MD) = 0; + /// Emit a member pointer constant from an APValue. + virtual mlir::TypedAttr emitMemberPointer(const APValue &memberPointer, + QualType mpType) = 0; + + /// Build a member function pointer constant with the given method and + /// adjustment. + virtual mlir::TypedAttr + buildMemberFunctionPointer(cir::MethodType methodTy, + const CXXMethodDecl *methodDecl, + CharUnits thisAdjustment) = 0; + + /// Build a member data pointer constant with the given field offset. + virtual mlir::TypedAttr buildMemberDataPointer(const MemberPointerType *mpt, + CharUnits offset) = 0; + /**************************** Array cookies ******************************/ /// Returns the extra size required in order to store the array diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp index 0e5a403968f2..31b1596b793d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp @@ -2033,16 +2033,10 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value, case APValue::MemberPointer: { assert(!cir::MissingFeatures::cxxABI()); - const ValueDecl *memberDecl = Value.getMemberPointerDecl(); - assert(!Value.isMemberPointerToDerivedMember() && "NYI"); + if (Value.isMemberPointerToDerivedMember()) + llvm_unreachable("NYI: derived-to-base member pointer conversions"); - if (isa(memberDecl)) - assert(0 && "not implemented"); - - auto cirTy = mlir::cast(CGM.convertType(DestType)); - - const auto *fieldDecl = cast(memberDecl); - return builder.getDataMemberAttr(cirTy, fieldDecl->getFieldIndex()); + return CGM.getCXXABI().emitMemberPointer(Value, DestType); } case APValue::LValue: return ConstantLValueEmitter(*this, Value, DestType).tryEmit(); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index ecc6b71a2390..72632b7f0c04 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -362,6 +362,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { cir::MethodAttr buildVirtualMethodAttr(cir::MethodType MethodTy, const CXXMethodDecl *MD) override; + mlir::TypedAttr emitMemberPointer(const APValue &memberPointer, + QualType mpType) override; + + mlir::TypedAttr buildMemberFunctionPointer(cir::MethodType methodTy, + const CXXMethodDecl *methodDecl, + CharUnits thisAdjustment) override; + + mlir::TypedAttr buildMemberDataPointer(const MemberPointerType *mpt, + CharUnits offset) override; + Address initializeArrayCookie(CIRGenFunction &CGF, Address NewPtr, mlir::Value NumElements, const CXXNewExpr *E, QualType ElementType) override; @@ -2987,6 +2997,82 @@ CIRGenItaniumCXXABI::buildVirtualMethodAttr(cir::MethodType MethodTy, return cir::MethodAttr::get(MethodTy, VTableOffset); } +mlir::TypedAttr +CIRGenItaniumCXXABI::buildMemberFunctionPointer(cir::MethodType methodTy, + const CXXMethodDecl *methodDecl, + CharUnits thisAdjustment) { + assert(methodDecl->isInstance() && "Member function must not be static!"); + + // Get the function pointer (or index if this is a virtual function). + if (methodDecl->isVirtual()) { + uint64_t index = + CGM.getItaniumVTableContext().getMethodVTableIndex(methodDecl); + uint64_t vTableOffset; + if (CGM.getItaniumVTableContext().isRelativeLayout()) { + // Multiply by 4-byte relative offsets. + vTableOffset = index * 4; + } else { + const ASTContext &astContext = getContext(); + CharUnits pointerWidth = astContext.toCharUnitsFromBits( + astContext.getTargetInfo().getPointerWidth(LangAS::Default)); + vTableOffset = index * pointerWidth.getQuantity(); + } + + // Itanium C++ ABI 2.3: + // For a virtual function, [the pointer field] is 1 plus the + // virtual table offset (in bytes) of the function, + // represented as a ptrdiff_t. + return cir::MethodAttr::get(methodTy, vTableOffset, + thisAdjustment.getQuantity()); + } + + // For non-virtual functions, get the function symbol. + auto methodFuncOp = CGM.GetAddrOfFunction(methodDecl); + auto methodFuncSymbolRef = mlir::FlatSymbolRefAttr::get(methodFuncOp); + return cir::MethodAttr::get(methodTy, methodFuncSymbolRef, + thisAdjustment.getQuantity()); +} + +mlir::TypedAttr +CIRGenItaniumCXXABI::buildMemberDataPointer(const MemberPointerType *mpt, + CharUnits offset) { + // This method is not currently used for APValue emission. + // Data member pointers are handled directly in emitMemberPointer. + llvm_unreachable("NYI: buildMemberDataPointer"); +} + +mlir::TypedAttr +CIRGenItaniumCXXABI::emitMemberPointer(const APValue &memberPointer, + QualType mpType) { + const auto *mpt = mpType->castAs(); + const ValueDecl *mpd = memberPointer.getMemberPointerDecl(); + + if (!mpd) { + // Null member pointer. + if (mpt->isMemberDataPointer()) { + auto ty = mlir::cast(CGM.convertType(mpType)); + return cir::DataMemberAttr::get(ty); + } + // Null member function pointer. + auto ty = mlir::cast(CGM.convertType(mpType)); + return cir::MethodAttr::get(ty); + } + + CharUnits thisAdjustment = + getContext().getMemberPointerPathAdjustment(memberPointer); + + if (const auto *md = dyn_cast(mpd)) { + auto ty = mlir::cast(CGM.convertType(mpType)); + return buildMemberFunctionPointer(ty, md, thisAdjustment); + } + + // Data member pointer. + auto &builder = CGM.getBuilder(); + auto ty = mlir::cast(CGM.convertType(mpType)); + const auto *fieldDecl = cast(mpd); + return builder.getDataMemberAttr(ty, fieldDecl->getFieldIndex()); +} + /// The Itanium ABI requires non-zero initialization only for data /// member pointers, for which '0' is a valid offset. bool CIRGenItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) { diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index af9483d47a0d..bfed5794cac8 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -431,7 +431,8 @@ DataMemberAttr::verify(function_ref emitError, LogicalResult MethodAttr::verify(function_ref emitError, cir::MethodType type, std::optional symbol, - std::optional vtable_offset) { + std::optional vtable_offset, + int64_t adjustment) { if (symbol.has_value() && vtable_offset.has_value()) return emitError() << "at most one of symbol and vtable_offset can be present " @@ -460,9 +461,18 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { if (parseSymbolRefResult.has_value()) { if (parseSymbolRefResult.value().failed()) return {}; + + // Try to parse optional adjustment. + int64_t adjustment = 0; + if (parser.parseOptionalComma().succeeded()) { + if (parser.parseKeyword("adjustment") || parser.parseEqual() || + parser.parseInteger(adjustment)) + return {}; + } + if (parser.parseGreater()) return {}; - return get(ty, symbol); + return get(ty, symbol, adjustment); } // Parse a uint64 that represents the vtable offset. @@ -474,21 +484,36 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { if (parser.parseInteger(vtableOffset)) return {}; + // Try to parse optional adjustment. + int64_t adjustment = 0; + if (parser.parseOptionalComma().succeeded()) { + if (parser.parseKeyword("adjustment") || parser.parseEqual() || + parser.parseInteger(adjustment)) + return {}; + } + if (parser.parseGreater()) return {}; - return get(ty, vtableOffset); + return get(ty, vtableOffset, adjustment); } void MethodAttr::print(AsmPrinter &printer) const { auto symbol = getSymbol(); auto vtableOffset = getVtableOffset(); + auto adjustment = getAdjustment(); printer << '<'; if (symbol.has_value()) { printer << *symbol; + if (adjustment != 0) { + printer << ", adjustment = " << adjustment; + } } else if (vtableOffset.has_value()) { printer << "vtable_offset = " << *vtableOffset; + if (adjustment != 0) { + printer << ", adjustment = " << adjustment; + } } else { printer << "null"; } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp index 8b333d38cc92..ee61576fcec4 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/ItaniumCXXABI.cpp @@ -212,6 +212,7 @@ mlir::TypedAttr ItaniumCXXABI::lowerMethodConstant( lowerMethodType(attr.getType(), typeConverter)); auto zero = cir::IntAttr::get(ptrdiffCIRTy, 0); + auto adj = cir::IntAttr::get(ptrdiffCIRTy, attr.getAdjustment()); // Itanium C++ ABI 2.3.2: // In all representations, the basic ABI properties of member function @@ -257,7 +258,7 @@ mlir::TypedAttr ItaniumCXXABI::lowerMethodConstant( auto ptr = cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value()); return cir::ConstRecordAttr::get( - loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero})); + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, adj})); } // Itanium C++ ABI 2.3.2: @@ -267,7 +268,7 @@ mlir::TypedAttr ItaniumCXXABI::lowerMethodConstant( // 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})); + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, adj})); } mlir::Operation *ItaniumCXXABI::lowerGetRuntimeMember( diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index ebe7877b303f..6ef0f0181f00 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2730,6 +2730,18 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::lowerInitializer( return lowerInitializer(rewriter, op, abiLlvmType, init, useInitializerRegion); } + if (auto methodAttr = mlir::dyn_cast(init)) { + assert(lowerMod && "lower module is not available"); + mlir::DataLayout layout(op->getParentOfType()); + mlir::TypedAttr abiValue = lowerMod->getCXXABI().lowerMethodConstant( + methodAttr, layout, *typeConverter); + init = abiValue; + auto abiLlvmType = convertTypeForMemory(*getTypeConverter(), dataLayout, + abiValue.getType()); + // Recursively lower the CIR attribute produced by the C++ ABI. + return lowerInitializer(rewriter, op, abiLlvmType, init, + useInitializerRegion); + } op.emitError() << "unsupported initializer '" << init << "'"; return mlir::failure(); diff --git a/clang/test/CIR/CodeGen/member-ptr-init.cpp b/clang/test/CIR/CodeGen/member-ptr-init.cpp new file mode 100644 index 000000000000..82b6e6d52dd4 --- /dev/null +++ b/clang/test/CIR/CodeGen/member-ptr-init.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ogcg.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ogcg.ll %s + +// Test APValue emission for member function pointers with CIR, LLVM lowering, +// and comparison to original CodeGen. + +struct S { + void foo(); + virtual void bar(); +}; + +// Test 1: Non-virtual member function pointer +// CIR: cir.global external @pmf1 = #cir.method<@_ZN1S3fooEv> +// LLVM: @pmf1 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN1S3fooEv to i64), i64 0 } +// OGCG: @pmf1 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN1S3fooEv to i64), i64 0 } +extern void (S::*pmf1)(); +void (S::*pmf1)() = &S::foo; + +// Test 2: Virtual member function pointer +// CIR: cir.global external @pmf2 = #cir.method +// LLVM: @pmf2 = global { i64, i64 } { i64 {{[0-9]+}}, i64 0 } +// OGCG: @pmf2 = global { i64, i64 } { i64 {{[0-9]+}}, i64 0 } +extern void (S::*pmf2)(); +void (S::*pmf2)() = &S::bar; + +// Test 3: Null member function pointer +// CIR: cir.global external @pmf3 = #cir.method +// LLVM: @pmf3 = global { i64, i64 } zeroinitializer +// OGCG: @pmf3 = global { i64, i64 } zeroinitializer +extern void (S::*pmf3)(); +void (S::*pmf3)() = nullptr;