diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 4949a03908dc..7ca5f1060f3f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -264,6 +264,40 @@ def ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> { let hasCustomAssemblyFormat = 1; } +//===----------------------------------------------------------------------===// +// DataMemberAttr +//===----------------------------------------------------------------------===// + +def DataMemberAttr : CIR_Attr<"DataMember", "data_member", + [TypedAttrInterface]> { + let summary = "Holds a constant data member pointer value"; + let parameters = (ins AttributeSelfTypeParameter< + "", "mlir::cir::DataMemberType">:$type, + OptionalParameter< + "std::optional">:$memberIndex); + let description = [{ + A data member attribute is a literal attribute that represents a constant + pointer-to-data-member value. + + The `memberIndex` parameter represents the index of the pointed-to member + within its containing struct. It is an optional parameter; lack of this + parameter indicates a null pointer-to-data-member value. + + Example: + ``` + #ptr = #cir.data_member<1> : !cir.data_member + + #null = #cir.data_member : !cir.data_member + ``` + }]; + + let genVerifyDecl = 1; + + let assemblyFormat = [{ + `<` ($memberIndex^):(`null`)? `>` + }]; +} + //===----------------------------------------------------------------------===// // SignedOverflowBehaviorAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index ae77df0bbe4b..36623b1a9171 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -1770,6 +1770,63 @@ def GetMemberOp : CIR_Op<"get_member"> { let hasVerifier = 1; } +//===----------------------------------------------------------------------===// +// GetRuntimeMemberOp +//===----------------------------------------------------------------------===// + +def GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> { + let summary = "Get the address of a member of a struct"; + let description = [{ + The `cir.get_runtime_member` operation gets the address of a member from + the input record. The target member is given by a value of type + `!cir.data_member` (i.e. a pointer-to-data-member value). + + This operation differs from `cir.get_member` in when the target member can + be determined. For the `cir.get_member` operation, the target member is + specified as a constant index so the member it returns access to is known + when the operation is constructed. For the `cir.get_runtime_member` + operation, the target member is given through a pointer-to-data-member + value which is unknown until the program being compiled is executed. In + other words, `cir.get_member` represents a normal member access through the + `.` operator in C/C++: + + ```cpp + struct Foo { int x; }; + Foo f; + (void)f.x; // cir.get_member + ``` + + And `cir.get_runtime_member` represents a member access through the `.*` or + the `->*` operator in C++: + + ```cpp + struct Foo { int x; } + Foo f; + Foo *p; + int Foo::*member; + + (void)f.*member; // cir.get_runtime_member + (void)f->*member; // cir.get_runtime_member + ``` + + This operation expects a pointer to the base record as well as the pointer + to the target member. + }]; + + let arguments = (ins + Arg:$addr, + Arg:$member); + + let results = (outs Res:$result); + + let assemblyFormat = [{ + $addr `[` $member `:` qualified(type($member)) `]` attr-dict + `:` qualified(type($addr)) `->` qualified(type($result)) + }]; + + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // VecInsertOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h index 512b9db01dfe..651790031dba 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h @@ -20,13 +20,6 @@ #include "clang/CIR/Interfaces/ASTAttrInterfaces.h" -//===----------------------------------------------------------------------===// -// CIR Dialect Tablegen'd Types -//===----------------------------------------------------------------------===// - -#define GET_TYPEDEF_CLASSES -#include "clang/CIR/Dialect/IR/CIROpsTypes.h.inc" - //===----------------------------------------------------------------------===// // CIR StructType // @@ -184,4 +177,11 @@ class StructType } // namespace cir } // namespace mlir +//===----------------------------------------------------------------------===// +// CIR Dialect Tablegen'd Types +//===----------------------------------------------------------------------===// + +#define GET_TYPEDEF_CLASSES +#include "clang/CIR/Dialect/IR/CIROpsTypes.h.inc" + #endif // MLIR_DIALECT_CIR_IR_CIRTYPES_H_ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index 442ce90cc54a..1501cd1122ea 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -145,6 +145,28 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr", let hasCustomAssemblyFormat = 1; } +//===----------------------------------------------------------------------===// +// DataMemberType +//===----------------------------------------------------------------------===// + +def CIR_DataMemberType : CIR_Type<"DataMember", "data_member", + [DeclareTypeInterfaceMethods]> { + + let summary = "CIR type that represents pointer-to-data-member type in C++"; + let description = [{ + `cir.member_ptr` models the pointer-to-data-member type in C++. Values of + this type are essentially offsets of the pointed-to member within one of + its containing struct. + }]; + + let parameters = (ins "mlir::Type":$memberTy, + "mlir::cir::StructType":$clsTy); + + let assemblyFormat = [{ + `<` $memberTy `in` $clsTy `>` + }]; +} + //===----------------------------------------------------------------------===// // BoolType // @@ -309,6 +331,15 @@ def VoidPtr : Type< "mlir::cir::VoidType::get($_builder.getContext()))"> { } +// Pointer to struct +def StructPtr : Type< + And<[ + CPred<"$_self.isa<::mlir::cir::PointerType>()">, + CPred<"$_self.cast<::mlir::cir::PointerType>()" + ".getPointee().isa<::mlir::cir::StructType>()">, + ]>, "!cir.struct*"> { +} + // Pointers to exception info def ExceptionInfoPtr : Type< And<[ @@ -351,8 +382,9 @@ def CIR_StructType : Type()">, //===----------------------------------------------------------------------===// def CIR_AnyType : AnyTypeOf<[ - CIR_IntType, CIR_PointerType, CIR_BoolType, CIR_ArrayType, CIR_VectorType, - CIR_FuncType, CIR_VoidType, CIR_StructType, CIR_ExceptionInfo, CIR_AnyFloat, + CIR_IntType, CIR_PointerType, CIR_DataMemberType, CIR_BoolType, CIR_ArrayType, + CIR_VectorType, CIR_FuncType, CIR_VoidType, CIR_StructType, CIR_ExceptionInfo, + CIR_AnyFloat, ]>; #endif // MLIR_CIR_DIALECT_CIR_TYPES diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index c585ac70cffb..bfdd07aaef9d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -221,6 +221,16 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { return mlir::cir::TypeInfoAttr::get(anonStruct.getType(), fieldsAttr); } + mlir::cir::DataMemberAttr getDataMemberAttr(mlir::cir::DataMemberType ty, + size_t memberIndex) { + return mlir::cir::DataMemberAttr::get(getContext(), ty, memberIndex); + } + + mlir::cir::DataMemberAttr + getNullDataMemberAttr(mlir::cir::DataMemberType ty) { + return mlir::cir::DataMemberAttr::get(getContext(), ty, std::nullopt); + } + mlir::TypedAttr getZeroInitAttr(mlir::Type ty) { if (ty.isa()) return mlir::cir::IntAttr::get(ty, 0); @@ -551,6 +561,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { return create(loc, ty, getConstPtrAttr(ty, 0)); } + /// Create constant nullptr for pointer-to-data-member type ty. + mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty, + mlir::Location loc) { + return create(loc, ty, getNullDataMemberAttr(ty)); + } + // Creates constant null value for integral type ty. mlir::cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) { return create(loc, ty, getZeroInitAttr(ty)); @@ -866,6 +882,19 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { } } + mlir::cir::GetRuntimeMemberOp createGetIndirectMember(mlir::Location loc, + mlir::Value objectPtr, + mlir::Value memberPtr) { + auto memberPtrTy = memberPtr.getType().cast(); + + // TODO(cir): consider address space. + assert(!UnimplementedFeature::addressSpace()); + auto resultTy = getPointerTo(memberPtrTy.getMemberTy()); + + return create(loc, resultTy, objectPtr, + memberPtr); + } + mlir::Value createPtrIsNull(mlir::Value ptr) { return createNot(createPtrToBoolCast(ptr)); } diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 4f9e06b668be..12fbbe69038a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -1475,3 +1475,61 @@ mlir::Value CIRGenFunction::getVTablePtr(mlir::Location Loc, Address This, return VTable; } + +Address CIRGenFunction::buildCXXMemberDataPointerAddress( + const Expr *E, Address base, mlir::Value memberPtr, + const MemberPointerType *memberPtrType, LValueBaseInfo *baseInfo) { + assert(!UnimplementedFeature::cxxABI()); + + auto op = builder.createGetIndirectMember(getLoc(E->getSourceRange()), + base.getPointer(), memberPtr); + + QualType memberType = memberPtrType->getPointeeType(); + CharUnits memberAlign = CGM.getNaturalTypeAlignment(memberType, baseInfo); + memberAlign = CGM.getDynamicOffsetAlignment( + base.getAlignment(), memberPtrType->getClass()->getAsCXXRecordDecl(), + memberAlign); + + return Address(op, convertTypeForMem(memberPtrType->getPointeeType()), + memberAlign); +} + +clang::CharUnits +CIRGenModule::getDynamicOffsetAlignment(clang::CharUnits actualBaseAlign, + const clang::CXXRecordDecl *baseDecl, + clang::CharUnits expectedTargetAlign) { + // If the base is an incomplete type (which is, alas, possible with + // member pointers), be pessimistic. + if (!baseDecl->isCompleteDefinition()) + return std::min(actualBaseAlign, expectedTargetAlign); + + auto &baseLayout = getASTContext().getASTRecordLayout(baseDecl); + CharUnits expectedBaseAlign = baseLayout.getNonVirtualAlignment(); + + // If the class is properly aligned, assume the target offset is, too. + // + // This actually isn't necessarily the right thing to do --- if the + // class is a complete object, but it's only properly aligned for a + // base subobject, then the alignments of things relative to it are + // probably off as well. (Note that this requires the alignment of + // the target to be greater than the NV alignment of the derived + // class.) + // + // However, our approach to this kind of under-alignment can only + // ever be best effort; after all, we're never going to propagate + // alignments through variables or parameters. Note, in particular, + // that constructing a polymorphic type in an address that's less + // than pointer-aligned will generally trap in the constructor, + // unless we someday add some sort of attribute to change the + // assumed alignment of 'this'. So our goal here is pretty much + // just to allow the user to explicitly say that a pointer is + // under-aligned and then safely access its fields and vtables. + if (actualBaseAlign >= expectedBaseAlign) { + return expectedTargetAlign; + } + + // Otherwise, we might be offset by an arbitrary multiple of the + // actual alignment. The correct adjustment is to take the min of + // the two alignments. + return std::min(actualBaseAlign, expectedTargetAlign); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 5a6ba2696f1d..8996216a6914 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -891,6 +891,30 @@ LValue CIRGenFunction::buildDeclRefLValue(const DeclRefExpr *E) { llvm_unreachable("Unhandled DeclRefExpr"); } +LValue +CIRGenFunction::buildPointerToDataMemberBinaryExpr(const BinaryOperator *E) { + assert((E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) && + "unexpected binary operator opcode"); + + auto baseAddr = Address::invalid(); + if (E->getOpcode() == BO_PtrMemD) + baseAddr = buildLValue(E->getLHS()).getAddress(); + else + baseAddr = buildPointerWithAlignment(E->getLHS()); + + const auto *memberPtrTy = E->getRHS()->getType()->castAs(); + + auto memberPtr = buildScalarExpr(E->getRHS()); + + LValueBaseInfo baseInfo; + // TODO(cir): add TBAA + assert(!UnimplementedFeature::tbaa()); + auto memberAddr = buildCXXMemberDataPointerAddress(E, baseAddr, memberPtr, + memberPtrTy, &baseInfo); + + return makeAddrLValue(memberAddr, memberPtrTy->getPointeeType(), baseInfo); +} + LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) { // Comma expressions just emit their LHS then their RHS as an l-value. if (E->getOpcode() == BO_Comma) { @@ -899,7 +923,7 @@ LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) { } if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) - assert(0 && "not implemented"); + return buildPointerToDataMemberBinaryExpr(E); assert(E->getOpcode() == BO_Assign && "unexpected binary l-value"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp index 59551e6d4bdc..8a5f61c60efd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp @@ -1763,6 +1763,21 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value, return buildArrayConstant(CGM, Desired, CommonElementType, NumElements, Elts, typedFiller); } + case APValue::MemberPointer: { + assert(!UnimplementedFeature::cxxABI()); + + const ValueDecl *memberDecl = Value.getMemberPointerDecl(); + assert(!Value.isMemberPointerToDerivedMember() && "NYI"); + + if (const auto *memberFuncDecl = dyn_cast(memberDecl)) + assert(0 && "not implemented"); + + auto cirTy = + CGM.getTypes().ConvertType(DestType).cast(); + + const auto *fieldDecl = cast(memberDecl); + return builder.getDataMemberAttr(cirTy, fieldDecl->getFieldIndex()); + } case APValue::LValue: return ConstantLValueEmitter(*this, Value, DestType).tryEmit(); case APValue::Struct: @@ -1773,7 +1788,6 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value, case APValue::ComplexFloat: case APValue::Vector: case APValue::AddrLabelDiff: - case APValue::MemberPointer: assert(0 && "not implemented"); } llvm_unreachable("Unknown APValue kind"); @@ -1802,6 +1816,26 @@ mlir::Value CIRGenModule::buildNullConstant(QualType T, mlir::Location loc) { return {}; } +mlir::Value CIRGenModule::buildMemberPointerConstant(const UnaryOperator *E) { + assert(!UnimplementedFeature::cxxABI()); + + auto loc = getLoc(E->getSourceRange()); + + const auto *decl = cast(E->getSubExpr())->getDecl(); + + // A member function pointer. + // Member function pointer is not supported yet. + if (const auto *methodDecl = dyn_cast(decl)) + assert(0 && "not implemented"); + + auto ty = getCIRType(E->getType()).cast(); + + // Otherwise, a member data pointer. + const auto *fieldDecl = cast(decl); + return builder.create( + loc, ty, builder.getDataMemberAttr(ty, fieldDecl->getFieldIndex())); +} + mlir::Attribute ConstantEmitter::emitAbstract(const Expr *E, QualType destType) { auto state = pushAbstract(); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 9e4e16e8bcea..95844e1bb686 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -498,7 +498,9 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitUnaryAddrOf(const UnaryOperator *E) { - assert(!llvm::isa(E->getType()) && "not implemented"); + if (llvm::isa(E->getType())) + return CGF.CGM.buildMemberPointerConstant(E); + return CGF.buildLValue(E->getSubExpr()).getPointer(); } @@ -653,8 +655,13 @@ class ScalarExprEmitter : public StmtVisitor { return Visit(E->getRHS()); } - mlir::Value VisitBinPtrMemD(const Expr *E) { llvm_unreachable("NYI"); } - mlir::Value VisitBinPtrMemI(const Expr *E) { llvm_unreachable("NYI"); } + mlir::Value VisitBinPtrMemD(const BinaryOperator *E) { + return buildLoadOfLValue(E); + } + + mlir::Value VisitBinPtrMemI(const BinaryOperator *E) { + return buildLoadOfLValue(E); + } mlir::Value VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) { llvm_unreachable("NYI"); @@ -1447,8 +1454,18 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { mlir::cir::ConstPtrAttr::get(Builder.getContext(), Ty, 0)); } - case CK_NullToMemberPointer: - llvm_unreachable("NYI"); + case CK_NullToMemberPointer: { + if (MustVisitNullValue(E)) + CGF.buildIgnoredExpr(E); + + assert(!UnimplementedFeature::cxxABI()); + + const MemberPointerType *MPT = CE->getType()->getAs(); + assert(!MPT->isMemberFunctionPointerType() && "NYI"); + + auto Ty = CGF.getCIRType(DestTy).cast(); + return Builder.getNullDataMemberPtr(Ty, CGF.getLoc(E->getExprLoc())); + } case CK_ReinterpretMemberPointer: llvm_unreachable("NYI"); case CK_BaseToDerivedMemberPointer: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index b66b7daf0aef..c1a2c1843730 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -787,6 +787,13 @@ class CIRGenFunction : public CIRGenTypeCache { LValue buildStmtExprLValue(const StmtExpr *E); + LValue buildPointerToDataMemberBinaryExpr(const BinaryOperator *E); + + /// TODO: Add TBAAAccessInfo + Address buildCXXMemberDataPointerAddress( + const Expr *E, Address base, mlir::Value memberPtr, + const MemberPointerType *memberPtrType, LValueBaseInfo *baseInfo); + /// Generate a call of the given function, expecting the given /// result type, and using the given argument list which specifies both the /// LLVM arguments and the types they were derived from. diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index ff18a16784f7..cba0b9fa1d10 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -384,6 +384,12 @@ class CIRGenModule : public CIRGenTypeCache { LValueBaseInfo *BaseInfo = nullptr, bool forPointeeType = false); + /// TODO: Add TBAAAccessInfo + clang::CharUnits + getDynamicOffsetAlignment(clang::CharUnits actualBaseAlign, + const clang::CXXRecordDecl *baseDecl, + clang::CharUnits expectedTargetAlign); + mlir::cir::FuncOp getAddrOfCXXStructor( clang::GlobalDecl GD, const CIRGenFunctionInfo *FnInfo = nullptr, mlir::cir::FuncType FnType = nullptr, bool DontDefer = false, @@ -511,6 +517,8 @@ class CIRGenModule : public CIRGenTypeCache { /// null constant. mlir::Value buildNullConstant(QualType T, mlir::Location loc); + mlir::Value buildMemberPointerConstant(const UnaryOperator *E); + llvm::StringRef getMangledName(clang::GlobalDecl GD); void buildTentativeDefinition(const VarDecl *D); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 6b774f14746e..966558f170ea 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -700,7 +700,14 @@ mlir::Type CIRGenTypes::ConvertType(QualType T) { } case Type::MemberPointer: { - assert(0 && "not implemented"); + const auto *MPT = cast(Ty); + assert(MPT->isMemberDataPointer() && "ptr-to-member-function is NYI"); + + auto memberTy = ConvertType(MPT->getPointeeType()); + auto clsTy = + ConvertType(QualType(MPT->getClass(), 0)).cast(); + ResultType = + mlir::cir::DataMemberType::get(Builder.getContext(), memberTy, clsTy); break; } diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index e10852d202e6..5b952e40d83e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -358,6 +358,43 @@ LogicalResult cir::FPAttr::verify(function_ref emitError, return success(); } +//===----------------------------------------------------------------------===// +// DataMemberAttr definitions +//===----------------------------------------------------------------------===// + +LogicalResult +DataMemberAttr::verify(function_ref emitError, + mlir::cir::DataMemberType ty, + std::optional memberIndex) { + if (!memberIndex.has_value()) { + // DataMemberAttr without a given index represents a null value. + return success(); + } + + auto clsStructTy = ty.getClsTy(); + if (clsStructTy.isIncomplete()) { + emitError() << "incomplete 'cir.struct' cannot be used to build a non-null " + "data member pointer"; + return failure(); + } + + auto memberIndexValue = memberIndex.value(); + if (memberIndexValue >= clsStructTy.getNumElements()) { + emitError() + << "member index of a #cir.data_member attribute is out of range"; + return failure(); + } + + auto memberTy = clsStructTy.getMembers()[memberIndexValue]; + if (memberTy != ty.getMemberTy()) { + emitError() << "member type of a #cir.data_member attribute must match the " + "attribute type"; + return failure(); + } + + return success(); +} + //===----------------------------------------------------------------------===// // CIR Dialect //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 22c29360d783..1f51087b472e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -292,6 +292,12 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, return op->emitOpError("nullptr expects pointer type"); } + if (attrType.isa()) { + // More detailed type verifications are already done in + // DataMemberAttr::verify. Don't need to repeat here. + return success(); + } + if (attrType.isa()) { if (opType.isa<::mlir::cir::StructType, ::mlir::cir::ArrayType>()) return success(); @@ -2555,6 +2561,28 @@ LogicalResult GetMemberOp::verify() { return mlir::success(); } +//===----------------------------------------------------------------------===// +// GetRuntimeMemberOp Definitions +//===----------------------------------------------------------------------===// + +LogicalResult GetRuntimeMemberOp::verify() { + auto recordTy = + getAddr().getType().cast().getPointee().cast(); + auto memberPtrTy = getMember().getType(); + + if (recordTy != memberPtrTy.getClsTy()) { + emitError() << "record type does not match the member pointer type"; + return mlir::failure(); + } + + if (getType().getPointee() != memberPtrTy.getMemberTy()) { + emitError() << "result type does not match the member pointer type"; + return mlir::failure(); + } + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 5ddafd66231d..d99ab72cdcc5 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -403,6 +403,27 @@ uint64_t PointerType::getPreferredAlignment( return 8; } +llvm::TypeSize +DataMemberType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + // FIXME: consider size differences under different ABIs + return llvm::TypeSize::getFixed(64); +} + +uint64_t +DataMemberType::getABIAlignment(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + // FIXME: consider alignment differences under different ABIs + return 8; +} + +uint64_t DataMemberType::getPreferredAlignment( + const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + // FIXME: consider alignment differences under different ABIs + return 8; +} + llvm::TypeSize ArrayType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member.cpp b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp new file mode 100644 index 000000000000..9ffa714e4d70 --- /dev/null +++ b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -Wno-unused-value -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +struct Point { + int x; + int y; + int z; +}; +// CHECK-DAG: !ty_22Point22 = !cir.struct + +int Point::*pt_member = &Point::x; +// CHECK: cir.global external @pt_member = #cir.data_member<0> : !cir.data_member + +auto test1() -> int Point::* { + return &Point::y; +} +// CHECK: cir.func @_Z5test1v() -> !cir.data_member +// CHECK: %{{.+}} = cir.const(#cir.data_member<1> : !cir.data_member) : !cir.data_member +// CHECK: } + +int test2(const Point &pt, int Point::*member) { + return pt.*member; +} +// CHECK: cir.func @_Z5test2RK5PointMS_i +// CHECK: %{{.+}} = cir.get_runtime_member %{{.+}}[%{{.+}} : !cir.data_member] : !cir.ptr -> !cir.ptr +// CHECK: } + +int test3(const Point *pt, int Point::*member) { + return pt->*member; +} +// CHECK: cir.func @_Z5test3PK5PointMS_i +// CHECK: %{{.+}} = cir.get_runtime_member %{{.+}}[%{{.+}} : !cir.data_member] : !cir.ptr -> !cir.ptr +// CHECK: } + +auto test4(int Incomplete::*member) -> int Incomplete::* { + return member; +} +// CHECK: cir.func @_Z5test4M10Incompletei(%arg0: !cir.data_member loc({{.+}})) -> !cir.data_member + +int test5(Incomplete *ic, int Incomplete::*member) { + return ic->*member; +} +// CHECK: cir.func @_Z5test5P10IncompleteMS_i +// CHECK: %{{.+}} = cir.get_runtime_member %{{.+}}[%{{.+}} : !cir.data_member] : !cir.ptr -> !cir.ptr +// CHECK: } + +auto test_null() -> int Point::* { + return nullptr; +} +// CHECK: cir.func @_Z9test_nullv +// CHECK: %{{.+}} = cir.const(#cir.data_member : !cir.data_member) : !cir.data_member +// CHECK: } + +auto test_null_incomplete() -> int Incomplete::* { + return nullptr; +} +// CHECK: cir.func @_Z20test_null_incompletev +// CHECK: %{{.+}} = cir.const(#cir.data_member : !cir.data_member) : !cir.data_member +// CHECK: } diff --git a/clang/test/CIR/IR/data-member-ptr.cir b/clang/test/CIR/IR/data-member-ptr.cir new file mode 100644 index 000000000000..6370877291a4 --- /dev/null +++ b/clang/test/CIR/IR/data-member-ptr.cir @@ -0,0 +1,46 @@ +// RUN: cir-opt %s | cir-opt | FileCheck %s + +!s32i = !cir.int +!ty_22Foo22 = !cir.struct + +#global_ptr = #cir.data_member<0> : !cir.data_member + +module { + cir.func @null_member() { + %0 = cir.const(#cir.data_member : !cir.data_member) : !cir.data_member + cir.return + } + + cir.func @get_runtime_member(%arg0: !cir.ptr) { + %0 = cir.const(#cir.data_member<0> : !cir.data_member) : !cir.data_member + %1 = cir.get_runtime_member %arg0[%0 : !cir.data_member] : !cir.ptr -> !cir.ptr + cir.return + } + + cir.func @get_global_member(%arg0: !cir.ptr) { + %0 = cir.const(#global_ptr) : !cir.data_member + %1 = cir.get_runtime_member %arg0[%0 : !cir.data_member] : !cir.ptr -> !cir.ptr + cir.return + } +} + +// CHECK: module { + +// CHECK-NEXT: cir.func @null_member() { +// CHECK-NEXT: %0 = cir.const(#cir.data_member : !cir.data_member) : !cir.data_member +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +// CHECK-NEXT: cir.func @get_runtime_member(%arg0: !cir.ptr) { +// CHECK-NEXT: %0 = cir.const(#cir.data_member<0> : !cir.data_member) : !cir.data_member +// CHECK-NEXT: %1 = cir.get_runtime_member %arg0[%0 : !cir.data_member] : !cir.ptr -> !cir.ptr +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +// CHECK-NEXT: cir.func @get_global_member(%arg0: !cir.ptr) { +// CHECK-NEXT: %0 = cir.const(#cir.data_member<0> : !cir.data_member) : !cir.data_member +// CHECK-NEXT: %1 = cir.get_runtime_member %arg0[%0 : !cir.data_member] : !cir.ptr -> !cir.ptr +// CHECK-NEXT: cir.return +// CHECK-NEXT: } + +// CHECK: } diff --git a/clang/test/CIR/IR/invalid.cir b/clang/test/CIR/IR/invalid.cir index 3b5e5f83a14e..986353e83447 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -763,4 +763,74 @@ cir.func @const_type_mismatch() -> () { // expected-error@+1 {{'cir.const' op result type ('!cir.int') does not match value type ('!cir.int')}} %2 = cir.const(#cir.int<0> : !s8i) : !u8i cir.return -} \ No newline at end of file +} + +// ----- + +!u16i = !cir.int + +// expected-error@+1 {{invalid kind of type specified}} +#invalid_type = #cir.data_member<0> : !u16i + +// ----- + +!u16i = !cir.int +!u32i = !cir.int +!struct1 = !cir.struct + +// expected-error@+1 {{member type of a #cir.data_member attribute must match the attribute type}} +#invalid_member_ty = #cir.data_member<0> : !cir.data_member + +// ----- + +!u16i = !cir.int +!u32i = !cir.int +!struct1 = !cir.struct + +module { + cir.func @invalid_base_type(%arg0 : !cir.data_member) { + %0 = cir.alloca !u32i, cir.ptr , ["tmp"] {alignment = 4 : i64} + // expected-error@+1 {{'cir.get_runtime_member' op operand #0 must be !cir.struct*}} + %1 = cir.get_runtime_member %0[%arg0 : !cir.data_member] : !cir.ptr -> !cir.ptr + cir.return + } +} + +// ----- + +!u16i = !cir.int +!u32i = !cir.int +!struct1 = !cir.struct +!struct2 = !cir.struct + +module { + cir.func @invalid_base_type(%arg0 : !cir.data_member) { + %0 = cir.alloca !struct2, cir.ptr , ["tmp"] {alignment = 4 : i64} + // expected-error@+1 {{record type does not match the member pointer type}} + %1 = cir.get_runtime_member %0[%arg0 : !cir.data_member] : !cir.ptr -> !cir.ptr + cir.return + } +} + +// ----- + +!u16i = !cir.int +!u32i = !cir.int +!struct1 = !cir.struct + +module { + cir.func @invalid_base_type(%arg0 : !cir.data_member) { + %0 = cir.alloca !struct1, cir.ptr , ["tmp"] {alignment = 4 : i64} + // expected-error@+1 {{result type does not match the member pointer type}} + %1 = cir.get_runtime_member %0[%arg0 : !cir.data_member] : !cir.ptr -> !cir.ptr + cir.return + } +} + +// ----- + +!u16i = !cir.int +!incomplete_struct = !cir.struct + +// expected-error@+1 {{incomplete 'cir.struct' cannot be used to build a non-null data member pointer}} +#incomplete_cls_member = #cir.data_member<0> : !cir.data_member