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.data_member that models the ptr-to-data-member in C++
  - Add a new attribute #cir.data_member that represents a constant ptr-to-data-member value
  - Add a new operation cir.get_runtime_member that dereferences a ptr-to-data-member value
- CodeGen for pointer-to-data-member types and values
  • Loading branch information
Lancern committed Feb 14, 2024
1 parent a2b7b65 commit 7bfd595
Show file tree
Hide file tree
Showing 16 changed files with 622 additions and 19 deletions.
42 changes: 42 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,48 @@ 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<"">:$type,
"Type":$clsTy,
"IntegerAttr":$memberIndex);
let description = [{
A data member 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));
}]>,
AttrBuilderWithInferredContext<(ins "Type":$type), [{
mlir::Type clsTy = type.cast<mlir::cir::DataMemberType>().getClsTy();
return $_get(type.getContext(), type, clsTy,
IntegerAttr::get(IndexType::get(type.getContext()), -1));
}]>,
];

let extraClassDeclaration = [{
/// Determine whether the data member pointer is a null pointer value.
bool isNullValue() const {
return getMemberIndex().getValue().isNegative();
}
}];

let hasCustomAssemblyFormat = 1;
let genVerifyDecl = 1;
}

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

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_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 Expand Up @@ -2809,9 +2838,9 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> {
let description = [{
The `cir.asm` operation represents C/C++ asm inline.

CIR constraints strings follow barelly the same rules that are established
for the C level assembler constraints with several differences caused by
clang::AsmStmt processing.
CIR constraints strings follow barelly the same rules that are established
for the C level assembler constraints with several differences caused by
clang::AsmStmt processing.

Thus, numbers that appears in the constraint string may also refer to:
- the output variable index referenced by the input operands.
Expand All @@ -2823,11 +2852,11 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> {
__asm__("bar $42 %[val]" : [val] "=r" (x), "+&r"(x));
__asm__("baz $42 %[val]" : [val] "=r" (x), "+&r"(x) : "[val]"(y));
```

```mlir
cir.asm(x86_att, {"foo" ""})
cir.asm(x86_att, {"bar $$42 $0" "=r,=&r,1"})
cir.asm(x86_att, {"baz $$42 $0" "=r,=&r,0,1"})
cir.asm(x86_att, {"bar $$42 $0" "=r,=&r,1"})
cir.asm(x86_att, {"baz $$42 $0" "=r,=&r,0,1"})
```
}];

Expand All @@ -2836,12 +2865,12 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> {
let arguments = (
ins StrAttr:$asm_string,
StrAttr:$constraints,
AsmFlavor:$asm_flavor);
AsmFlavor:$asm_flavor);

let assemblyFormat = [{
`(`$asm_flavor`,` `{` $asm_string $constraints `}` `)` attr-dict
`(`$asm_flavor`,` `{` $asm_string $constraints `}` `)` attr-dict
`:` type($res)
}];
}];
}

//===----------------------------------------------------------------------===//
Expand Down
28 changes: 26 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,29 @@ 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::Type":$clsTy);

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

let genVerifyDecl = 1;
}

//===----------------------------------------------------------------------===//
// BoolType
//
Expand Down Expand Up @@ -317,8 +340,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, AnyFloat,
CIR_IntType, CIR_PointerType, CIR_DataMemberType, CIR_BoolType, CIR_ArrayType,
CIR_VectorType, CIR_FuncType, CIR_VoidType, CIR_StructType, CIR_ExceptionInfo,
AnyFloat,
]>;

#endif // MLIR_CIR_DIALECT_CIR_TYPES
23 changes: 23 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,
unsigned memberIndex) {
return mlir::cir::DataMemberAttr::get(ty, ty.getClsTy(), memberIndex);
}

mlir::cir::DataMemberAttr
getNullDataMemberAttr(mlir::cir::DataMemberType ty) {
return mlir::cir::DataMemberAttr::get(ty);
}

mlir::TypedAttr getZeroInitAttr(mlir::Type ty) {
if (ty.isa<mlir::cir::IntType>())
return mlir::cir::IntAttr::get(ty, 0);
Expand Down Expand Up @@ -865,6 +875,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());
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);

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;
// 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 @@ -1758,6 +1758,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<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 @@ -1768,7 +1783,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 @@ -1797,6 +1811,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

0 comments on commit 7bfd595

Please sign in to comment.