Skip to content

Commit 4277476

Browse files
Lancernlanza
authored andcommitted
[CIR] initial support for pointer-to-data-member type (llvm#401)
This patch adds initial support for the pointer-to-data-member type. Specifically, this commit includes: - New ops, types, and attributes: - CodeGen for pointer-to-data-member types and values - Lower C++ pointer-to-member type - Lower C++ expression `&C::D` - Lower C++ expression `c.*p` and `c->*p` This patch only includes an initial support. The following stuff related to pointer-to-member types are not supported yet: - Pointer to member function; - Conversion from `T Base::*` to `T Derived::*`; - LLVMIR lowering.
1 parent 6a4b4a3 commit 4277476

18 files changed

+589
-18
lines changed

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

+34
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,40 @@ def ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
264264
let hasCustomAssemblyFormat = 1;
265265
}
266266

267+
//===----------------------------------------------------------------------===//
268+
// DataMemberAttr
269+
//===----------------------------------------------------------------------===//
270+
271+
def DataMemberAttr : CIR_Attr<"DataMember", "data_member",
272+
[TypedAttrInterface]> {
273+
let summary = "Holds a constant data member pointer value";
274+
let parameters = (ins AttributeSelfTypeParameter<
275+
"", "mlir::cir::DataMemberType">:$type,
276+
OptionalParameter<
277+
"std::optional<size_t>">:$memberIndex);
278+
let description = [{
279+
A data member attribute is a literal attribute that represents a constant
280+
pointer-to-data-member value.
281+
282+
The `memberIndex` parameter represents the index of the pointed-to member
283+
within its containing struct. It is an optional parameter; lack of this
284+
parameter indicates a null pointer-to-data-member value.
285+
286+
Example:
287+
```
288+
#ptr = #cir.data_member<1> : !cir.data_member<!s32i in !ty_22Point22>
289+
290+
#null = #cir.data_member<null> : !cir.data_member<!s32i in !ty_22Point22>
291+
```
292+
}];
293+
294+
let genVerifyDecl = 1;
295+
296+
let assemblyFormat = [{
297+
`<` ($memberIndex^):(`null`)? `>`
298+
}];
299+
}
300+
267301
//===----------------------------------------------------------------------===//
268302
// SignedOverflowBehaviorAttr
269303
//===----------------------------------------------------------------------===//

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

+57
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,63 @@ def GetMemberOp : CIR_Op<"get_member"> {
17701770
let hasVerifier = 1;
17711771
}
17721772

1773+
//===----------------------------------------------------------------------===//
1774+
// GetRuntimeMemberOp
1775+
//===----------------------------------------------------------------------===//
1776+
1777+
def GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> {
1778+
let summary = "Get the address of a member of a struct";
1779+
let description = [{
1780+
The `cir.get_runtime_member` operation gets the address of a member from
1781+
the input record. The target member is given by a value of type
1782+
`!cir.data_member` (i.e. a pointer-to-data-member value).
1783+
1784+
This operation differs from `cir.get_member` in when the target member can
1785+
be determined. For the `cir.get_member` operation, the target member is
1786+
specified as a constant index so the member it returns access to is known
1787+
when the operation is constructed. For the `cir.get_runtime_member`
1788+
operation, the target member is given through a pointer-to-data-member
1789+
value which is unknown until the program being compiled is executed. In
1790+
other words, `cir.get_member` represents a normal member access through the
1791+
`.` operator in C/C++:
1792+
1793+
```cpp
1794+
struct Foo { int x; };
1795+
Foo f;
1796+
(void)f.x; // cir.get_member
1797+
```
1798+
1799+
And `cir.get_runtime_member` represents a member access through the `.*` or
1800+
the `->*` operator in C++:
1801+
1802+
```cpp
1803+
struct Foo { int x; }
1804+
Foo f;
1805+
Foo *p;
1806+
int Foo::*member;
1807+
1808+
(void)f.*member; // cir.get_runtime_member
1809+
(void)f->*member; // cir.get_runtime_member
1810+
```
1811+
1812+
This operation expects a pointer to the base record as well as the pointer
1813+
to the target member.
1814+
}];
1815+
1816+
let arguments = (ins
1817+
Arg<StructPtr, "address of the struct object", [MemRead]>:$addr,
1818+
Arg<CIR_DataMemberType, "pointer to the target member">:$member);
1819+
1820+
let results = (outs Res<CIR_PointerType, "">:$result);
1821+
1822+
let assemblyFormat = [{
1823+
$addr `[` $member `:` qualified(type($member)) `]` attr-dict
1824+
`:` qualified(type($addr)) `->` qualified(type($result))
1825+
}];
1826+
1827+
let hasVerifier = 1;
1828+
}
1829+
17731830
//===----------------------------------------------------------------------===//
17741831
// VecInsertOp
17751832
//===----------------------------------------------------------------------===//

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@
2020

2121
#include "clang/CIR/Interfaces/ASTAttrInterfaces.h"
2222

23-
//===----------------------------------------------------------------------===//
24-
// CIR Dialect Tablegen'd Types
25-
//===----------------------------------------------------------------------===//
26-
27-
#define GET_TYPEDEF_CLASSES
28-
#include "clang/CIR/Dialect/IR/CIROpsTypes.h.inc"
29-
3023
//===----------------------------------------------------------------------===//
3124
// CIR StructType
3225
//
@@ -184,4 +177,11 @@ class StructType
184177
} // namespace cir
185178
} // namespace mlir
186179

180+
//===----------------------------------------------------------------------===//
181+
// CIR Dialect Tablegen'd Types
182+
//===----------------------------------------------------------------------===//
183+
184+
#define GET_TYPEDEF_CLASSES
185+
#include "clang/CIR/Dialect/IR/CIROpsTypes.h.inc"
186+
187187
#endif // MLIR_DIALECT_CIR_IR_CIRTYPES_H_

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

+34-2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,28 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
145145
let hasCustomAssemblyFormat = 1;
146146
}
147147

148+
//===----------------------------------------------------------------------===//
149+
// DataMemberType
150+
//===----------------------------------------------------------------------===//
151+
152+
def CIR_DataMemberType : CIR_Type<"DataMember", "data_member",
153+
[DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]> {
154+
155+
let summary = "CIR type that represents pointer-to-data-member type in C++";
156+
let description = [{
157+
`cir.member_ptr` models the pointer-to-data-member type in C++. Values of
158+
this type are essentially offsets of the pointed-to member within one of
159+
its containing struct.
160+
}];
161+
162+
let parameters = (ins "mlir::Type":$memberTy,
163+
"mlir::cir::StructType":$clsTy);
164+
165+
let assemblyFormat = [{
166+
`<` $memberTy `in` $clsTy `>`
167+
}];
168+
}
169+
148170
//===----------------------------------------------------------------------===//
149171
// BoolType
150172
//
@@ -309,6 +331,15 @@ def VoidPtr : Type<
309331
"mlir::cir::VoidType::get($_builder.getContext()))"> {
310332
}
311333

334+
// Pointer to struct
335+
def StructPtr : Type<
336+
And<[
337+
CPred<"$_self.isa<::mlir::cir::PointerType>()">,
338+
CPred<"$_self.cast<::mlir::cir::PointerType>()"
339+
".getPointee().isa<::mlir::cir::StructType>()">,
340+
]>, "!cir.struct*"> {
341+
}
342+
312343
// Pointers to exception info
313344
def ExceptionInfoPtr : Type<
314345
And<[
@@ -351,8 +382,9 @@ def CIR_StructType : Type<CPred<"$_self.isa<::mlir::cir::StructType>()">,
351382
//===----------------------------------------------------------------------===//
352383

353384
def CIR_AnyType : AnyTypeOf<[
354-
CIR_IntType, CIR_PointerType, CIR_BoolType, CIR_ArrayType, CIR_VectorType,
355-
CIR_FuncType, CIR_VoidType, CIR_StructType, CIR_ExceptionInfo, CIR_AnyFloat,
385+
CIR_IntType, CIR_PointerType, CIR_DataMemberType, CIR_BoolType, CIR_ArrayType,
386+
CIR_VectorType, CIR_FuncType, CIR_VoidType, CIR_StructType, CIR_ExceptionInfo,
387+
CIR_AnyFloat,
356388
]>;
357389

358390
#endif // MLIR_CIR_DIALECT_CIR_TYPES

clang/lib/CIR/CodeGen/CIRGenBuilder.h

+29
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
221221
return mlir::cir::TypeInfoAttr::get(anonStruct.getType(), fieldsAttr);
222222
}
223223

224+
mlir::cir::DataMemberAttr getDataMemberAttr(mlir::cir::DataMemberType ty,
225+
size_t memberIndex) {
226+
return mlir::cir::DataMemberAttr::get(getContext(), ty, memberIndex);
227+
}
228+
229+
mlir::cir::DataMemberAttr
230+
getNullDataMemberAttr(mlir::cir::DataMemberType ty) {
231+
return mlir::cir::DataMemberAttr::get(getContext(), ty, std::nullopt);
232+
}
233+
224234
mlir::TypedAttr getZeroInitAttr(mlir::Type ty) {
225235
if (ty.isa<mlir::cir::IntType>())
226236
return mlir::cir::IntAttr::get(ty, 0);
@@ -551,6 +561,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
551561
return create<mlir::cir::ConstantOp>(loc, ty, getConstPtrAttr(ty, 0));
552562
}
553563

564+
/// Create constant nullptr for pointer-to-data-member type ty.
565+
mlir::cir::ConstantOp getNullDataMemberPtr(mlir::cir::DataMemberType ty,
566+
mlir::Location loc) {
567+
return create<mlir::cir::ConstantOp>(loc, ty, getNullDataMemberAttr(ty));
568+
}
569+
554570
// Creates constant null value for integral type ty.
555571
mlir::cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) {
556572
return create<mlir::cir::ConstantOp>(loc, ty, getZeroInitAttr(ty));
@@ -866,6 +882,19 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
866882
}
867883
}
868884

885+
mlir::cir::GetRuntimeMemberOp createGetIndirectMember(mlir::Location loc,
886+
mlir::Value objectPtr,
887+
mlir::Value memberPtr) {
888+
auto memberPtrTy = memberPtr.getType().cast<mlir::cir::DataMemberType>();
889+
890+
// TODO(cir): consider address space.
891+
assert(!UnimplementedFeature::addressSpace());
892+
auto resultTy = getPointerTo(memberPtrTy.getMemberTy());
893+
894+
return create<mlir::cir::GetRuntimeMemberOp>(loc, resultTy, objectPtr,
895+
memberPtr);
896+
}
897+
869898
mlir::Value createPtrIsNull(mlir::Value ptr) {
870899
return createNot(createPtrToBoolCast(ptr));
871900
}

clang/lib/CIR/CodeGen/CIRGenClass.cpp

+58
Original file line numberDiff line numberDiff line change
@@ -1475,3 +1475,61 @@ mlir::Value CIRGenFunction::getVTablePtr(mlir::Location Loc, Address This,
14751475

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

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+25-1
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,30 @@ LValue CIRGenFunction::buildDeclRefLValue(const DeclRefExpr *E) {
891891
llvm_unreachable("Unhandled DeclRefExpr");
892892
}
893893

894+
LValue
895+
CIRGenFunction::buildPointerToDataMemberBinaryExpr(const BinaryOperator *E) {
896+
assert((E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) &&
897+
"unexpected binary operator opcode");
898+
899+
auto baseAddr = Address::invalid();
900+
if (E->getOpcode() == BO_PtrMemD)
901+
baseAddr = buildLValue(E->getLHS()).getAddress();
902+
else
903+
baseAddr = buildPointerWithAlignment(E->getLHS());
904+
905+
const auto *memberPtrTy = E->getRHS()->getType()->castAs<MemberPointerType>();
906+
907+
auto memberPtr = buildScalarExpr(E->getRHS());
908+
909+
LValueBaseInfo baseInfo;
910+
// TODO(cir): add TBAA
911+
assert(!UnimplementedFeature::tbaa());
912+
auto memberAddr = buildCXXMemberDataPointerAddress(E, baseAddr, memberPtr,
913+
memberPtrTy, &baseInfo);
914+
915+
return makeAddrLValue(memberAddr, memberPtrTy->getPointeeType(), baseInfo);
916+
}
917+
894918
LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) {
895919
// Comma expressions just emit their LHS then their RHS as an l-value.
896920
if (E->getOpcode() == BO_Comma) {
@@ -899,7 +923,7 @@ LValue CIRGenFunction::buildBinaryOperatorLValue(const BinaryOperator *E) {
899923
}
900924

901925
if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI)
902-
assert(0 && "not implemented");
926+
return buildPointerToDataMemberBinaryExpr(E);
903927

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

clang/lib/CIR/CodeGen/CIRGenExprConst.cpp

+35-1
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,21 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
17611761
return buildArrayConstant(CGM, Desired, CommonElementType, NumElements,
17621762
Elts, typedFiller);
17631763
}
1764+
case APValue::MemberPointer: {
1765+
assert(!UnimplementedFeature::cxxABI());
1766+
1767+
const ValueDecl *memberDecl = Value.getMemberPointerDecl();
1768+
assert(!Value.isMemberPointerToDerivedMember() && "NYI");
1769+
1770+
if (const auto *memberFuncDecl = dyn_cast<CXXMethodDecl>(memberDecl))
1771+
assert(0 && "not implemented");
1772+
1773+
auto cirTy =
1774+
CGM.getTypes().ConvertType(DestType).cast<mlir::cir::DataMemberType>();
1775+
1776+
const auto *fieldDecl = cast<FieldDecl>(memberDecl);
1777+
return builder.getDataMemberAttr(cirTy, fieldDecl->getFieldIndex());
1778+
}
17641779
case APValue::LValue:
17651780
return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
17661781
case APValue::Struct:
@@ -1771,7 +1786,6 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
17711786
case APValue::ComplexFloat:
17721787
case APValue::Vector:
17731788
case APValue::AddrLabelDiff:
1774-
case APValue::MemberPointer:
17751789
assert(0 && "not implemented");
17761790
}
17771791
llvm_unreachable("Unknown APValue kind");
@@ -1800,6 +1814,26 @@ mlir::Value CIRGenModule::buildNullConstant(QualType T, mlir::Location loc) {
18001814
return {};
18011815
}
18021816

1817+
mlir::Value CIRGenModule::buildMemberPointerConstant(const UnaryOperator *E) {
1818+
assert(!UnimplementedFeature::cxxABI());
1819+
1820+
auto loc = getLoc(E->getSourceRange());
1821+
1822+
const auto *decl = cast<DeclRefExpr>(E->getSubExpr())->getDecl();
1823+
1824+
// A member function pointer.
1825+
// Member function pointer is not supported yet.
1826+
if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl))
1827+
assert(0 && "not implemented");
1828+
1829+
auto ty = getCIRType(E->getType()).cast<mlir::cir::DataMemberType>();
1830+
1831+
// Otherwise, a member data pointer.
1832+
const auto *fieldDecl = cast<FieldDecl>(decl);
1833+
return builder.create<mlir::cir::ConstantOp>(
1834+
loc, ty, builder.getDataMemberAttr(ty, fieldDecl->getFieldIndex()));
1835+
}
1836+
18031837
mlir::Attribute ConstantEmitter::emitAbstract(const Expr *E,
18041838
QualType destType) {
18051839
auto state = pushAbstract();

0 commit comments

Comments
 (0)