llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> This upstreams support for pointer-to-member value representations of null and virtual member pointers and the lowering of virtual member pointers. --- Full diff: https://github.com/llvm/llvm-project/pull/176522.diff 11 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+4) - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+23-8) - (modified) clang/include/clang/CIR/MissingFeatures.h (-1) - (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+4) - (modified) clang/lib/CIR/CodeGen/CIRGenCXXABI.h (+3) - (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+2-3) - (modified) clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp (+22) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+3-6) - (modified) clang/lib/CIR/Dialect/IR/CIRAttrs.cpp (+28-1) - (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp (+23-1) - (modified) clang/test/CIR/CodeGen/pointer-to-member-func.cpp (+54) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 2aaae86240cf2..c35c42c8c506b 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -184,6 +184,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return cir::MethodAttr::get(ty, methodFuncSymbolRef); } + cir::MethodAttr getNullMethodAttr(cir::MethodType ty) { + return cir::MethodAttr::get(ty); + } + 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 2309811815f27..9447ae629b445 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -511,10 +511,13 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { 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 the member function is a virtual function, the `vtable_offset` parameter + gives the offset of the vtable entry corresponding to the virtual member + function. - If `symbol` is not present, the attribute represents a null pointer - constant. + `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. Examples: ``` @@ -522,7 +525,7 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { %0 = cir.const #cir.method<@_ZN1S2m1Ei> : !cir.method<!cir.func<(!s32i)> in !rec_S> - // Virtual method (not yet implemented) + // Virtual method %1 = cir.const #cir.method<vtable_offset = 8> : !cir.method<!cir.func<(!s32i)> in !rec_S> @@ -534,23 +537,35 @@ def CIR_MethodAttr : CIR_Attr<"Method", "method", [TypedAttrInterface]> { let parameters = (ins AttributeSelfTypeParameter< "", "cir::MethodType">:$type, OptionalParameter< - "std::optional<mlir::FlatSymbolRefAttr>">:$symbol); + "std::optional<mlir::FlatSymbolRefAttr>">:$symbol, + OptionalParameter< + "std::optional<uint64_t>">:$vtable_offset); let builders = [ AttrBuilderWithInferredContext<(ins "cir::MethodType":$type), [{ - return $_get(type.getContext(), type, std::nullopt); + return $_get(type.getContext(), type, std::nullopt, std::nullopt); }]>, AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, "mlir::FlatSymbolRefAttr":$symbol), [{ - return $_get(type.getContext(), type, symbol); + return $_get(type.getContext(), type, symbol, std::nullopt); + }]>, + AttrBuilderWithInferredContext<(ins "cir::MethodType":$type, + "uint64_t":$vtable_offset), [{ + return $_get(type.getContext(), type, std::nullopt, vtable_offset); }]>, ]; let hasCustomAssemblyFormat = 1; + let genVerifyDecl = 1; + let extraClassDeclaration = [{ bool isNull() const { - return !getSymbol().has_value(); + return !getSymbol().has_value() && !getVtableOffset().has_value(); + } + + bool isVirtual() const { + return getVtableOffset().has_value(); } }]; } diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 359d813171294..10ef749b51dd3 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -354,7 +354,6 @@ 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/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index ff492edf0b04e..0f43429803596 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -370,6 +370,10 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return cir::ConstantOp::create(*this, loc, getNullDataMemberAttr(ty)); } + cir::ConstantOp getNullMethodPtr(cir::MethodType ty, mlir::Location loc) { + return cir::ConstantOp::create(*this, loc, getNullMethodAttr(ty)); + } + // TODO: split this to createFPExt/createFPTrunc when we have dedicated cast // operations. mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) { diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 1d5fce3036bf4..27d48bfabeb38 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -61,6 +61,9 @@ class CIRGenCXXABI { cir::PointerType destCIRTy, bool isRefCast, Address src) = 0; + virtual cir::MethodAttr buildVirtualMethodAttr(cir::MethodType methodTy, + const CXXMethodDecl *md) = 0; + public: /// Similar to AddedStructorArgs, but only notes the number of additional /// arguments. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 4ce38f4e0a1f9..48ceb742a8787 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -2235,9 +2235,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { const MemberPointerType *mpt = ce->getType()->getAs<MemberPointerType>(); if (mpt->isMemberFunctionPointerType()) { - cgf.cgm.errorNYI(subExpr->getSourceRange(), - "CK_NullToMemberPointer: member function pointer"); - return {}; + auto Ty = mlir::cast<cir::MethodType>(cgf.convertType(destTy)); + return builder.getNullMethodPtr(Ty, cgf.getLoc(subExpr->getExprLoc())); } auto ty = mlir::cast<cir::DataMemberType>(cgf.convertType(destTy)); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index b749336df4068..26465a804f1e6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -156,6 +156,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { cir::PointerType destCIRTy, bool isRefCast, Address src) override; + cir::MethodAttr buildVirtualMethodAttr(cir::MethodType methodTy, + const CXXMethodDecl *md) override; + Address initializeArrayCookie(CIRGenFunction &cgf, Address newPtr, mlir::Value numElements, const CXXNewExpr *e, QualType elementType) override; @@ -2195,6 +2198,25 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf, isRefCast, castInfo); } +cir::MethodAttr +CIRGenItaniumCXXABI::buildVirtualMethodAttr(cir::MethodType methodTy, + const CXXMethodDecl *md) { + assert(md->isVirtual() && "only deal with virtual member functions"); + + uint64_t index = cgm.getItaniumVTableContext().getMethodVTableIndex(md); + uint64_t vtableOffset; + if (cgm.getItaniumVTableContext().isRelativeLayout()) { + // Multiply by 4-byte relative offsets. + vtableOffset = index * 4; + } else { + const ASTContext &astContext = cgm.getASTContext(); + CharUnits pointerWidth = astContext.toCharUnitsFromBits( + astContext.getTargetInfo().getPointerWidth(LangAS::Default)); + vtableOffset = index * pointerWidth.getQuantity(); + } + + return cir::MethodAttr::get(methodTy, vtableOffset); +} /// The Itanium ABI always places an offset to the complete object /// at entry -2 in the vtable. void CIRGenItaniumCXXABI::emitVirtualObjectDelete( diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 44fe9cbd96879..d223158f99e9e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1529,12 +1529,9 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) { // A member function pointer. 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 {}; - } + if (methodDecl->isVirtual()) + return cir::ConstantOp::create( + builder, loc, getCXXABI().buildVirtualMethodAttr(ty, methodDecl)); cir::FuncOp methodFuncOp = getAddrOfFunction(methodDecl); return cir::ConstantOp::create(builder, loc, diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 2f4240c385cab..43bd33759fba9 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -305,6 +305,18 @@ DataMemberAttr::verify(function_ref<InFlightDiagnostic()> emitError, // MethodAttr definitions //===----------------------------------------------------------------------===// +LogicalResult MethodAttr::verify(function_ref<InFlightDiagnostic()> emitError, + cir::MethodType type, + std::optional<FlatSymbolRefAttr> symbol, + std::optional<uint64_t> vtable_offset) { + if (symbol.has_value() && vtable_offset.has_value()) + return emitError() + << "at most one of symbol and vtable_offset can be present " + "in #cir.method"; + + return success(); +} + Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { auto ty = mlir::cast<cir::MethodType>(odsType); @@ -331,15 +343,30 @@ Attribute MethodAttr::parse(AsmParser &parser, Type odsType) { return get(ty, symbol); } - return {}; + // Parse a uint64 that represents the vtable offset. + std::uint64_t vtableOffset = 0; + if (parser.parseKeyword("vtable_offset")) + return {}; + if (parser.parseEqual()) + return {}; + if (parser.parseInteger(vtableOffset)) + return {}; + + if (parser.parseGreater()) + return {}; + + return get(ty, vtableOffset); } void MethodAttr::print(AsmPrinter &printer) const { auto symbol = getSymbol(); + auto vtableOffset = getVtableOffset(); printer << '<'; if (symbol.has_value()) { printer << *symbol; + } else if (vtableOffset.has_value()) { + printer << "vtable_offset = " << *vtableOffset; } else { printer << "null"; } diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index 94342f864fca6..a66d5f3cbb7a4 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -202,7 +202,29 @@ mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant( loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero})); } - assert(!cir::MissingFeatures::virtualMethodAttr()); + if (attr.isVirtual()) { + if (useARMMethodPtrABI) { + // ARM C++ ABI 3.2.1: + // This ABI specifies that adj contains twice the this + // adjustment, plus 1 if the member function is virtual. The + // least significant bit of adj then makes exactly the same + // discrimination as the least significant bit of ptr does for + // Itanium. + llvm_unreachable("ARM method ptr abi NYI"); + } + + // Itanium C++ ABI 2.3.2: + // + // In the standard representation, a member function pointer for a + // virtual function is represented with ptr set to 1 plus the function's + // v-table entry offset (in bytes), converted to a function pointer as if + // by reinterpret_cast<fnptr_t>(uintfnptr_t(1 + offset)), where + // uintfnptr_t is an unsigned integer of the same size as fnptr_t. + auto ptr = + cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value()); + return cir::ConstRecordAttr::get( + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero})); + } // Itanium C++ ABI 2.3.2: // diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp index ad081d0d06dbc..4dedee3e48c45 100644 --- a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp @@ -39,6 +39,60 @@ auto make_non_virtual() -> void (Foo::*)(int) { // OGCG: define {{.*}} { i64, i64 } @_Z16make_non_virtualv() // OGCG: ret { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } +auto make_virtual() -> void (Foo::*)(int) { + return &Foo::m3; +} + +// CIR-BEFORE: cir.func {{.*}} @_Z12make_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<vtable_offset = 8> : !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CIR-BEFORE: cir.store %[[METHOD_PTR]], %[[RETVAL]] +// CIR-BEFORE: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo> +// CIR-BEFORE: cir.return %[[RET]] : !cir.method<!cir.func<(!s32i)> in !rec_Foo> + +// CIR-AFTER: cir.func {{.*}} @_Z12make_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.int<9> : !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 + +// LLVM: define {{.*}} @_Z12make_virtualv() +// LLVM: %[[RETVAL:.*]] = alloca { i64, i64 } +// LLVM: store { i64, i64 } { i64 9, i64 0 }, ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] +// LLVM: ret { i64, i64 } %[[RET]] + +// OGCG: define {{.*}} @_Z12make_virtualv() +// OGCG: ret { i64, i64 } { i64 9, i64 0 } + +auto make_null() -> void (Foo::*)(int) { + return nullptr; +} + +// CIR-BEFORE: cir.func {{.*}} @_Z9make_nullv() -> !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<null> : !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: cir.func {{.*}} @_Z9make_nullv() -> !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.int<0> : !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 + +// LLVM: define {{.*}} @_Z9make_nullv() +// LLVM: %[[RETVAL:.*]] = alloca { i64, i64 } +// LLVM: store { i64, i64 } zeroinitializer, ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] +// LLVM: ret { i64, i64 } %[[RET]] + +// OGCG: define {{.*}} @_Z9make_nullv() +// OGCG: ret { i64, i64 } zeroinitializer + void call(Foo *obj, void (Foo::*func)(int), int arg) { (obj->*func)(arg); } `````````` </details> https://github.com/llvm/llvm-project/pull/176522 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
