Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR] initial support for pointer-to-data-member type #401

Merged
merged 3 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<size_t>">:$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<!s32i in !ty_22Point22>
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved

#null = #cir.data_member<null> : !cir.data_member<!s32i in !ty_22Point22>
```
}];

let genVerifyDecl = 1;

let assemblyFormat = [{
`<` ($memberIndex^):(`null`)? `>`
}];
}

//===----------------------------------------------------------------------===//
// SignedOverflowBehaviorAttr
//===----------------------------------------------------------------------===//
Expand Down
57 changes: 57 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,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).

bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
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<StructPtr, "address of the struct object", [MemRead]>:$addr,
Arg<CIR_DataMemberType, "pointer to the target member">:$member);

let results = (outs Res<CIR_PointerType, "">:$result);

let assemblyFormat = [{
$addr `[` $member `:` qualified(type($member)) `]` attr-dict
`:` qualified(type($addr)) `->` qualified(type($result))
}];

let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// VecInsertOp
//===----------------------------------------------------------------------===//
Expand Down
14 changes: 7 additions & 7 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -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_
36 changes: 34 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,28 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
let hasCustomAssemblyFormat = 1;
}

//===----------------------------------------------------------------------===//
// DataMemberType
//===----------------------------------------------------------------------===//

def CIR_DataMemberType : CIR_Type<"DataMember", "data_member",
[DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]> {

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
//
Expand Down Expand Up @@ -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<[
Expand Down Expand Up @@ -351,8 +382,9 @@ def CIR_StructType : Type<CPred<"$_self.isa<::mlir::cir::StructType>()">,
//===----------------------------------------------------------------------===//

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
29 changes: 29 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::cir::IntType>())
return mlir::cir::IntAttr::get(ty, 0);
Expand Down Expand Up @@ -551,6 +561,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
return create<mlir::cir::ConstantOp>(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<mlir::cir::ConstantOp>(loc, ty, getNullDataMemberAttr(ty));
}

// Creates constant null value for integral type ty.
mlir::cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) {
return create<mlir::cir::ConstantOp>(loc, ty, getZeroInitAttr(ty));
Expand Down Expand Up @@ -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<mlir::cir::DataMemberType>();

// TODO(cir): consider address space.
assert(!UnimplementedFeature::addressSpace());
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
auto resultTy = getPointerTo(memberPtrTy.getMemberTy());

return create<mlir::cir::GetRuntimeMemberOp>(loc, resultTy, objectPtr,
memberPtr);
}

mlir::Value createPtrIsNull(mlir::Value ptr) {
return createNot(createPtrToBoolCast(ptr));
}
Expand Down
58 changes: 58 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
sitio-couto marked this conversation as resolved.
Show resolved Hide resolved

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);
}
26 changes: 25 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MemberPointerType>();

auto memberPtr = buildScalarExpr(E->getRHS());

LValueBaseInfo baseInfo;
sitio-couto marked this conversation as resolved.
Show resolved Hide resolved
// 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) {
Expand All @@ -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");

Expand Down
36 changes: 35 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprConst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1760,6 +1760,21 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
return buildArrayConstant(CGM, Desired, CommonElementType, NumElements,
Elts, typedFiller);
}
case APValue::MemberPointer: {
sitio-couto marked this conversation as resolved.
Show resolved Hide resolved
assert(!UnimplementedFeature::cxxABI());

const ValueDecl *memberDecl = Value.getMemberPointerDecl();
assert(!Value.isMemberPointerToDerivedMember() && "NYI");

if (const auto *memberFuncDecl = dyn_cast<CXXMethodDecl>(memberDecl))
assert(0 && "not implemented");

auto cirTy =
CGM.getTypes().ConvertType(DestType).cast<mlir::cir::DataMemberType>();

const auto *fieldDecl = cast<FieldDecl>(memberDecl);
return builder.getDataMemberAttr(cirTy, fieldDecl->getFieldIndex());
}
case APValue::LValue:
return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
case APValue::Struct:
Expand All @@ -1770,7 +1785,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");
Expand Down Expand Up @@ -1799,6 +1813,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<DeclRefExpr>(E->getSubExpr())->getDecl();

// A member function pointer.
// Member function pointer is not supported yet.
if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl))
assert(0 && "not implemented");

auto ty = getCIRType(E->getType()).cast<mlir::cir::DataMemberType>();

// Otherwise, a member data pointer.
const auto *fieldDecl = cast<FieldDecl>(decl);
return builder.create<mlir::cir::ConstantOp>(
loc, ty, builder.getDataMemberAttr(ty, fieldDecl->getFieldIndex()));
}

mlir::Attribute ConstantEmitter::emitAbstract(const Expr *E,
QualType destType) {
auto state = pushAbstract();
Expand Down
Loading
Loading