Skip to content

Commit

Permalink
[CIR] initial support for pointer-to-data-member type
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Lancern committed Jan 14, 2024
1 parent b33de0c commit 4513317
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 8 deletions.
30 changes: 30 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,36 @@ def ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
let hasCustomAssemblyFormat = 1;
}

//===----------------------------------------------------------------------===//
// DataMemberPtrAttr
//===----------------------------------------------------------------------===//

def DataMemberPtrAttr : CIR_Attr<"DataMemberPtr", "data_member_ptr",
[TypedAttrInterface]> {
let summary = "Holds a constant data member pointer value";
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
"Type":$clsTy,
"IntegerAttr":$memberIndex);
let description = [{
A data member pointer attribute is a literal attribute that represents a
constant pointer-to-data-member value.
}];
let builders = [
AttrBuilderWithInferredContext<(ins "Type":$type, "Type":$clsTy,
"IntegerAttr":$memberIndex), [{
return $_get(type.getContext(), type, clsTy, memberIndex);
}]>,
AttrBuilderWithInferredContext<(ins "Type":$type, "Type":$clsTy,
"uint64_t":$memberIndex), [{
return $_get(type.getContext(), type, clsTy,
IntegerAttr::get(IndexType::get(type.getContext()), memberIndex));
}]>,
];

let hasCustomAssemblyFormat = 1;
let genVerifyDecl = 1;
}

//===----------------------------------------------------------------------===//
// SignedOverflowBehaviorAttr
//===----------------------------------------------------------------------===//
Expand Down
29 changes: 29 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,35 @@ def GetMemberOp : CIR_Op<"get_member"> {
let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// GetIndirectMemberOp
//===----------------------------------------------------------------------===//

def GetIndirectMemberOp : CIR_Op<"get_indirect_member"> {
let summary = "Get the address of a member of a struct";
let description = [{
The `cir.get_indirect_member` operation gets the address of a member from
the input record. The target member is given by a value of type
`!cir.member_ptr` (i.e. a pointer-to-data-member value).

It expects a pointer to the base record as well as the pointer to the target
member.
}];

let arguments = (ins
Arg<CIR_PointerType, "address of the struct object", [MemRead]>:$addr,
Arg<CIR_MemberPtrType, "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;
}

//===----------------------------------------------------------------------===//
// VecExtractOp
//===----------------------------------------------------------------------===//
Expand Down
34 changes: 32 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,36 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr",
let hasCustomAssemblyFormat = 1;
}

//===----------------------------------------------------------------------===//
// MemberPtrType
//===----------------------------------------------------------------------===//

def CIR_MemberPtrType : CIR_Type<"MemberPtr", "member_ptr",
[DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]> {

let summary = "CIR pointer to data member type";
let description = [{
`cir.member_ptr` models the pointer-to-member type in C++.
}];

let parameters = (ins "mlir::Type":$memberTy, "mlir::Type":$clsTy);

let assemblyFormat = [{
`<` $memberTy `in` $clsTy `>`
}];

let extraClassDeclaration = [{
/// Determine whether this type represents a pointer-to-member-data type.
bool isMemberDataPtr() const;

/// Determine whether this type represents a pointer-to-member-function
/// type.
bool isMemberFunctionPtr() const;
}];

let genVerifyDecl = 1;
}

//===----------------------------------------------------------------------===//
// BoolType
//
Expand Down Expand Up @@ -262,8 +292,8 @@ def CIR_StructType : Type<CPred<"$_self.isa<::mlir::cir::StructType>()">,
"CIR struct type">;

def CIR_AnyType : AnyTypeOf<[
CIR_IntType, CIR_PointerType, CIR_BoolType, CIR_ArrayType, CIR_VectorType,
CIR_FuncType, CIR_VoidType, CIR_StructType, AnyFloat,
CIR_IntType, CIR_PointerType, CIR_MemberPtrType, CIR_BoolType, CIR_ArrayType,
CIR_VectorType, CIR_FuncType, CIR_VoidType, CIR_StructType, AnyFloat,
]>;


Expand Down
60 changes: 60 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,3 +1476,63 @@ mlir::Value CIRGenFunction::getVTablePtr(SourceLocation Loc, Address This,

return VTable;
}

Address CIRGenFunction::buildCXXMemberDataPointerAddress(
const Expr *E, Address base, mlir::Value memberPtr,
const MemberPointerType *memberPtrType, LValueBaseInfo *baseInfo) {
auto memberPtrTy = memberPtr.getType().cast<mlir::cir::MemberPtrType>();
// TODO(cir): consider address space.
auto resultTy = builder.getPointerTo(memberPtrTy.getMemberTy());

auto op = builder.create<mlir::cir::GetIndirectMemberOp>(
getLoc(E->getSourceRange()), resultTy, 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);
}
23 changes: 22 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,27 @@ LValue CIRGenFunction::buildDeclRefLValue(const DeclRefExpr *E) {
llvm_unreachable("Unhandled DeclRefExpr");
}

LValue
CIRGenFunction::buildPointerToDataMemberBinaryExpr(const BinaryOperator *E) {
auto baseAddr = Address::invalid();
if (E->getOpcode() == BO_PtrMemD)
baseAddr = buildLValue(E->getLHS()).getAddress();
else if (E->getOpcode() == BO_PtrMemI)
baseAddr = buildPointerWithAlignment(E->getLHS());
else
llvm_unreachable("unexpected binary operator opcode");

const auto *memberPtrTy = E->getRHS()->getType()->castAs<MemberPointerType>();

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

LValueBaseInfo baseInfo;
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 @@ -873,7 +894,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
15 changes: 14 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprConst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,20 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &Value,
return buildArrayConstant(CGM, Desired, CommonElementType, NumElements,
Elts, typedFiller);
}
case APValue::MemberPointer: {
const ValueDecl *memberDecl = Value.getMemberPointerDecl();
assert(!Value.isMemberPointerToDerivedMember() && "NYI");

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

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

const auto *fieldDecl = cast<FieldDecl>(memberDecl);
return mlir::cir::DataMemberPtrAttr::get(cirTy, cirTy.getClsTy(),
fieldDecl->getFieldIndex());
}
case APValue::LValue:
return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
case APValue::Struct:
Expand All @@ -1542,7 +1556,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
38 changes: 35 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
}

mlir::Value VisitUnaryAddrOf(const UnaryOperator *E) {
assert(!llvm::isa<MemberPointerType>(E->getType()) && "not implemented");
if (llvm::isa<MemberPointerType>(E->getType())) {
const DeclRefExpr *DRE = cast<DeclRefExpr>(E->getSubExpr());
return CGF.buildMemberPtrConstant(DRE, E->getType());
}

return CGF.buildLValue(E->getSubExpr()).getPointer();
}

Expand Down Expand Up @@ -647,8 +651,13 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
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");
Expand Down Expand Up @@ -2289,6 +2298,29 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
E->EvaluateKnownConstInt(CGF.getContext()));
}

mlir::Value CIRGenFunction::buildMemberPtrConstant(const clang::DeclRefExpr *E,
QualType Ty) {
auto loc = getLoc(E->getSourceRange());

const auto *memberPtrTy = cast<MemberPointerType>(Ty);
auto cirTy = getCIRType(Ty);
auto cirClsTy = getCIRType(QualType(memberPtrTy->getClass(), 0));

const auto *decl = E->getDecl();

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

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

mlir::Value CIRGenFunction::buildCheckedInBoundsGEP(
mlir::Type ElemTy, mlir::Value Ptr, ArrayRef<mlir::Value> IdxList,
bool SignedIndices, bool IsSubtraction, SourceLocation Loc) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,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.
Expand Down Expand Up @@ -1137,6 +1144,8 @@ class CIRGenFunction : public CIRGenTypeCache {
LValueBaseInfo *BaseInfo = nullptr,
KnownNonNull_t IsKnownNonNull = NotKnownNonNull);

mlir::Value buildMemberPtrConstant(const clang::DeclRefExpr *E, QualType Ty);

LValue
buildConditionalOperatorLValue(const AbstractConditionalOperator *expr);

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,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,
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,11 @@ mlir::Type CIRGenTypes::ConvertType(QualType T) {
}

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

Expand Down
Loading

0 comments on commit 4513317

Please sign in to comment.