Skip to content

Commit 4513317

Browse files
committed
[CIR] initial support for pointer-to-data-member type
This patch adds initial support for the pointer-to-data-member type. Specifically, this commit includes: - New ops, types, and attributes: - Add a new type !cir.member_ptr - Add a new attribute #cir.data_member_ptr - Add a new operation cir.get_indirect_member - CodeGen for pointer-to-data-member types and values - Lower C++ pointer-to-member type to !cir.member_ptr - Lower C++ expression &C::D to cir.const(#cir.data_member_ptr) - Lower C++ expression c.*p and c->*p to cir.get_indirect_member
1 parent b33de0c commit 4513317

File tree

14 files changed

+464
-8
lines changed

14 files changed

+464
-8
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,36 @@ def ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
224224
let hasCustomAssemblyFormat = 1;
225225
}
226226

227+
//===----------------------------------------------------------------------===//
228+
// DataMemberPtrAttr
229+
//===----------------------------------------------------------------------===//
230+
231+
def DataMemberPtrAttr : CIR_Attr<"DataMemberPtr", "data_member_ptr",
232+
[TypedAttrInterface]> {
233+
let summary = "Holds a constant data member pointer value";
234+
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
235+
"Type":$clsTy,
236+
"IntegerAttr":$memberIndex);
237+
let description = [{
238+
A data member pointer attribute is a literal attribute that represents a
239+
constant pointer-to-data-member value.
240+
}];
241+
let builders = [
242+
AttrBuilderWithInferredContext<(ins "Type":$type, "Type":$clsTy,
243+
"IntegerAttr":$memberIndex), [{
244+
return $_get(type.getContext(), type, clsTy, memberIndex);
245+
}]>,
246+
AttrBuilderWithInferredContext<(ins "Type":$type, "Type":$clsTy,
247+
"uint64_t":$memberIndex), [{
248+
return $_get(type.getContext(), type, clsTy,
249+
IntegerAttr::get(IndexType::get(type.getContext()), memberIndex));
250+
}]>,
251+
];
252+
253+
let hasCustomAssemblyFormat = 1;
254+
let genVerifyDecl = 1;
255+
}
256+
227257
//===----------------------------------------------------------------------===//
228258
// SignedOverflowBehaviorAttr
229259
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,35 @@ def GetMemberOp : CIR_Op<"get_member"> {
16721672
let hasVerifier = 1;
16731673
}
16741674

1675+
//===----------------------------------------------------------------------===//
1676+
// GetIndirectMemberOp
1677+
//===----------------------------------------------------------------------===//
1678+
1679+
def GetIndirectMemberOp : CIR_Op<"get_indirect_member"> {
1680+
let summary = "Get the address of a member of a struct";
1681+
let description = [{
1682+
The `cir.get_indirect_member` operation gets the address of a member from
1683+
the input record. The target member is given by a value of type
1684+
`!cir.member_ptr` (i.e. a pointer-to-data-member value).
1685+
1686+
It expects a pointer to the base record as well as the pointer to the target
1687+
member.
1688+
}];
1689+
1690+
let arguments = (ins
1691+
Arg<CIR_PointerType, "address of the struct object", [MemRead]>:$addr,
1692+
Arg<CIR_MemberPtrType, "pointer to the target member">:$member);
1693+
1694+
let results = (outs Res<CIR_PointerType, "">:$result);
1695+
1696+
let assemblyFormat = [{
1697+
$addr `[` $member `:` qualified(type($member)) `]` attr-dict
1698+
`:` qualified(type($addr)) `->` qualified(type($result))
1699+
}];
1700+
1701+
let hasVerifier = 1;
1702+
}
1703+
16751704
//===----------------------------------------------------------------------===//
16761705
// VecExtractOp
16771706
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIRTypes.td

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,36 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
111111
let hasCustomAssemblyFormat = 1;
112112
}
113113

114+
//===----------------------------------------------------------------------===//
115+
// MemberPtrType
116+
//===----------------------------------------------------------------------===//
117+
118+
def CIR_MemberPtrType : CIR_Type<"MemberPtr", "member_ptr",
119+
[DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]> {
120+
121+
let summary = "CIR pointer to data member type";
122+
let description = [{
123+
`cir.member_ptr` models the pointer-to-member type in C++.
124+
}];
125+
126+
let parameters = (ins "mlir::Type":$memberTy, "mlir::Type":$clsTy);
127+
128+
let assemblyFormat = [{
129+
`<` $memberTy `in` $clsTy `>`
130+
}];
131+
132+
let extraClassDeclaration = [{
133+
/// Determine whether this type represents a pointer-to-member-data type.
134+
bool isMemberDataPtr() const;
135+
136+
/// Determine whether this type represents a pointer-to-member-function
137+
/// type.
138+
bool isMemberFunctionPtr() const;
139+
}];
140+
141+
let genVerifyDecl = 1;
142+
}
143+
114144
//===----------------------------------------------------------------------===//
115145
// BoolType
116146
//
@@ -262,8 +292,8 @@ def CIR_StructType : Type<CPred<"$_self.isa<::mlir::cir::StructType>()">,
262292
"CIR struct type">;
263293

264294
def CIR_AnyType : AnyTypeOf<[
265-
CIR_IntType, CIR_PointerType, CIR_BoolType, CIR_ArrayType, CIR_VectorType,
266-
CIR_FuncType, CIR_VoidType, CIR_StructType, AnyFloat,
295+
CIR_IntType, CIR_PointerType, CIR_MemberPtrType, CIR_BoolType, CIR_ArrayType,
296+
CIR_VectorType, CIR_FuncType, CIR_VoidType, CIR_StructType, AnyFloat,
267297
]>;
268298

269299

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,3 +1476,63 @@ mlir::Value CIRGenFunction::getVTablePtr(SourceLocation Loc, Address This,
14761476

14771477
return VTable;
14781478
}
1479+
1480+
Address CIRGenFunction::buildCXXMemberDataPointerAddress(
1481+
const Expr *E, Address base, mlir::Value memberPtr,
1482+
const MemberPointerType *memberPtrType, LValueBaseInfo *baseInfo) {
1483+
auto memberPtrTy = memberPtr.getType().cast<mlir::cir::MemberPtrType>();
1484+
// TODO(cir): consider address space.
1485+
auto resultTy = builder.getPointerTo(memberPtrTy.getMemberTy());
1486+
1487+
auto op = builder.create<mlir::cir::GetIndirectMemberOp>(
1488+
getLoc(E->getSourceRange()), resultTy, base.getPointer(), memberPtr);
1489+
1490+
QualType memberType = memberPtrType->getPointeeType();
1491+
CharUnits memberAlign = CGM.getNaturalTypeAlignment(memberType, baseInfo);
1492+
memberAlign = CGM.getDynamicOffsetAlignment(
1493+
base.getAlignment(), memberPtrType->getClass()->getAsCXXRecordDecl(),
1494+
memberAlign);
1495+
1496+
return Address(op, convertTypeForMem(memberPtrType->getPointeeType()),
1497+
memberAlign);
1498+
}
1499+
1500+
clang::CharUnits
1501+
CIRGenModule::getDynamicOffsetAlignment(clang::CharUnits actualBaseAlign,
1502+
const clang::CXXRecordDecl *baseDecl,
1503+
clang::CharUnits expectedTargetAlign) {
1504+
// If the base is an incomplete type (which is, alas, possible with
1505+
// member pointers), be pessimistic.
1506+
if (!baseDecl->isCompleteDefinition())
1507+
return std::min(actualBaseAlign, expectedTargetAlign);
1508+
1509+
auto &baseLayout = getASTContext().getASTRecordLayout(baseDecl);
1510+
CharUnits expectedBaseAlign = baseLayout.getNonVirtualAlignment();
1511+
1512+
// If the class is properly aligned, assume the target offset is, too.
1513+
//
1514+
// This actually isn't necessarily the right thing to do --- if the
1515+
// class is a complete object, but it's only properly aligned for a
1516+
// base subobject, then the alignments of things relative to it are
1517+
// probably off as well. (Note that this requires the alignment of
1518+
// the target to be greater than the NV alignment of the derived
1519+
// class.)
1520+
//
1521+
// However, our approach to this kind of under-alignment can only
1522+
// ever be best effort; after all, we're never going to propagate
1523+
// alignments through variables or parameters. Note, in particular,
1524+
// that constructing a polymorphic type in an address that's less
1525+
// than pointer-aligned will generally trap in the constructor,
1526+
// unless we someday add some sort of attribute to change the
1527+
// assumed alignment of 'this'. So our goal here is pretty much
1528+
// just to allow the user to explicitly say that a pointer is
1529+
// under-aligned and then safely access its fields and vtables.
1530+
if (actualBaseAlign >= expectedBaseAlign) {
1531+
return expectedTargetAlign;
1532+
}
1533+
1534+
// Otherwise, we might be offset by an arbitrary multiple of the
1535+
// actual alignment. The correct adjustment is to take the min of
1536+
// the two alignments.
1537+
return std::min(actualBaseAlign, expectedTargetAlign);
1538+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,27 @@ LValue CIRGenFunction::buildDeclRefLValue(const DeclRefExpr *E) {
865865
llvm_unreachable("Unhandled DeclRefExpr");
866866
}
867867

868+
LValue
869+
CIRGenFunction::buildPointerToDataMemberBinaryExpr(const BinaryOperator *E) {
870+
auto baseAddr = Address::invalid();
871+
if (E->getOpcode() == BO_PtrMemD)
872+
baseAddr = buildLValue(E->getLHS()).getAddress();
873+
else if (E->getOpcode() == BO_PtrMemI)
874+
baseAddr = buildPointerWithAlignment(E->getLHS());
875+
else
876+
llvm_unreachable("unexpected binary operator opcode");
877+
878+
const auto *memberPtrTy = E->getRHS()->getType()->castAs<MemberPointerType>();
879+
880+
auto memberPtr = buildScalarExpr(E->getRHS());
881+
882+
LValueBaseInfo baseInfo;
883+
auto memberAddr = buildCXXMemberDataPointerAddress(E, baseAddr, memberPtr,
884+
memberPtrTy, &baseInfo);
885+
886+
return makeAddrLValue(memberAddr, memberPtrTy->getPointeeType(), baseInfo);
887+
}
888+
868889
LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) {
869890
// Comma expressions just emit their LHS then their RHS as an l-value.
870891
if (E->getOpcode() == BO_Comma) {
@@ -873,7 +894,7 @@ LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) {
873894
}
874895

875896
if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI)
876-
assert(0 && "not implemented");
897+
return buildPointerToDataMemberBinaryExpr(E);
877898

878899
assert(E->getOpcode() == BO_Assign && "unexpected binary l-value");
879900

clang/lib/CIR/CodeGen/CIRGenExprConst.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,20 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
15321532
return buildArrayConstant(CGM, Desired, CommonElementType, NumElements,
15331533
Elts, typedFiller);
15341534
}
1535+
case APValue::MemberPointer: {
1536+
const ValueDecl *memberDecl = Value.getMemberPointerDecl();
1537+
assert(!Value.isMemberPointerToDerivedMember() && "NYI");
1538+
1539+
auto cirTy =
1540+
CGM.getTypes().ConvertType(DestType).cast<mlir::cir::MemberPtrType>();
1541+
1542+
if (const auto *memberFuncDecl = dyn_cast<CXXMethodDecl>(memberDecl))
1543+
assert(0 && "not implemented");
1544+
1545+
const auto *fieldDecl = cast<FieldDecl>(memberDecl);
1546+
return mlir::cir::DataMemberPtrAttr::get(cirTy, cirTy.getClsTy(),
1547+
fieldDecl->getFieldIndex());
1548+
}
15351549
case APValue::LValue:
15361550
return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
15371551
case APValue::Struct:
@@ -1542,7 +1556,6 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
15421556
case APValue::ComplexFloat:
15431557
case APValue::Vector:
15441558
case APValue::AddrLabelDiff:
1545-
case APValue::MemberPointer:
15461559
assert(0 && "not implemented");
15471560
}
15481561
llvm_unreachable("Unknown APValue kind");

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
495495
}
496496

497497
mlir::Value VisitUnaryAddrOf(const UnaryOperator *E) {
498-
assert(!llvm::isa<MemberPointerType>(E->getType()) && "not implemented");
498+
if (llvm::isa<MemberPointerType>(E->getType())) {
499+
const DeclRefExpr *DRE = cast<DeclRefExpr>(E->getSubExpr());
500+
return CGF.buildMemberPtrConstant(DRE, E->getType());
501+
}
502+
499503
return CGF.buildLValue(E->getSubExpr()).getPointer();
500504
}
501505

@@ -647,8 +651,13 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
647651
return Visit(E->getRHS());
648652
}
649653

650-
mlir::Value VisitBinPtrMemD(const Expr *E) { llvm_unreachable("NYI"); }
651-
mlir::Value VisitBinPtrMemI(const Expr *E) { llvm_unreachable("NYI"); }
654+
mlir::Value VisitBinPtrMemD(const BinaryOperator *E) {
655+
return buildLoadOfLValue(E);
656+
}
657+
658+
mlir::Value VisitBinPtrMemI(const BinaryOperator *E) {
659+
return buildLoadOfLValue(E);
660+
}
652661

653662
mlir::Value VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
654663
llvm_unreachable("NYI");
@@ -2289,6 +2298,29 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
22892298
E->EvaluateKnownConstInt(CGF.getContext()));
22902299
}
22912300

2301+
mlir::Value CIRGenFunction::buildMemberPtrConstant(const clang::DeclRefExpr *E,
2302+
QualType Ty) {
2303+
auto loc = getLoc(E->getSourceRange());
2304+
2305+
const auto *memberPtrTy = cast<MemberPointerType>(Ty);
2306+
auto cirTy = getCIRType(Ty);
2307+
auto cirClsTy = getCIRType(QualType(memberPtrTy->getClass(), 0));
2308+
2309+
const auto *decl = E->getDecl();
2310+
2311+
// A member function pointer.
2312+
// Member function pointer is not supported yet.
2313+
if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl))
2314+
assert(0 && "not implemented");
2315+
2316+
// Otherwise, a member data pointer.
2317+
const auto *fieldDecl = cast<FieldDecl>(decl);
2318+
return builder.create<mlir::cir::ConstantOp>(
2319+
loc, cirTy,
2320+
mlir::cir::DataMemberPtrAttr::get(cirTy, cirClsTy,
2321+
fieldDecl->getFieldIndex()));
2322+
}
2323+
22922324
mlir::Value CIRGenFunction::buildCheckedInBoundsGEP(
22932325
mlir::Type ElemTy, mlir::Value Ptr, ArrayRef<mlir::Value> IdxList,
22942326
bool SignedIndices, bool IsSubtraction, SourceLocation Loc) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,13 @@ class CIRGenFunction : public CIRGenTypeCache {
746746

747747
LValue buildStmtExprLValue(const StmtExpr *E);
748748

749+
LValue buildPointerToDataMemberBinaryExpr(const BinaryOperator *E);
750+
751+
/// TODO: Add TBAAAccessInfo
752+
Address buildCXXMemberDataPointerAddress(
753+
const Expr *E, Address base, mlir::Value memberPtr,
754+
const MemberPointerType *memberPtrType, LValueBaseInfo *baseInfo);
755+
749756
/// Generate a call of the given function, expecting the given
750757
/// result type, and using the given argument list which specifies both the
751758
/// LLVM arguments and the types they were derived from.
@@ -1137,6 +1144,8 @@ class CIRGenFunction : public CIRGenTypeCache {
11371144
LValueBaseInfo *BaseInfo = nullptr,
11381145
KnownNonNull_t IsKnownNonNull = NotKnownNonNull);
11391146

1147+
mlir::Value buildMemberPtrConstant(const clang::DeclRefExpr *E, QualType Ty);
1148+
11401149
LValue
11411150
buildConditionalOperatorLValue(const AbstractConditionalOperator *expr);
11421151

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,12 @@ class CIRGenModule : public CIRGenTypeCache {
374374
LValueBaseInfo *BaseInfo = nullptr,
375375
bool forPointeeType = false);
376376

377+
/// TODO: Add TBAAAccessInfo
378+
clang::CharUnits
379+
getDynamicOffsetAlignment(clang::CharUnits actualBaseAlign,
380+
const clang::CXXRecordDecl *baseDecl,
381+
clang::CharUnits expectedTargetAlign);
382+
377383
mlir::cir::FuncOp getAddrOfCXXStructor(
378384
clang::GlobalDecl GD, const CIRGenFunctionInfo *FnInfo = nullptr,
379385
mlir::cir::FuncType FnType = nullptr, bool DontDefer = false,

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,11 @@ mlir::Type CIRGenTypes::ConvertType(QualType T) {
692692
}
693693

694694
case Type::MemberPointer: {
695-
assert(0 && "not implemented");
695+
const auto *MPT = cast<MemberPointerType>(Ty);
696+
auto memberTy = ConvertType(MPT->getPointeeType());
697+
auto clsTy = ConvertType(QualType(MPT->getClass(), 0));
698+
ResultType =
699+
mlir::cir::MemberPtrType::get(Builder.getContext(), memberTy, clsTy);
696700
break;
697701
}
698702

0 commit comments

Comments
 (0)