Skip to content
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
6 changes: 6 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::StoreOp>(loc, val, dst);
}

cir::GetMemberOp createGetMember(mlir::Location loc, mlir::Type resultTy,
mlir::Value base, llvm::StringRef name,
unsigned index) {
return create<cir::GetMemberOp>(loc, resultTy, base, name, index);
}

mlir::Value createDummyValue(mlir::Location loc, mlir::Type type,
clang::CharUnits alignment) {
auto addr = createAlloca(loc, getPointerTo(type), type, {},
Expand Down
64 changes: 64 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,70 @@ def GetGlobalOp : CIR_Op<"get_global",
let hasVerifier = 0;
}

//===----------------------------------------------------------------------===//
// GetMemberOp
//===----------------------------------------------------------------------===//

def GetMemberOp : CIR_Op<"get_member"> {
let summary = "Get the address of a member of a record";
let description = [{
The `cir.get_member` operation gets the address of a particular named
member from the input record.

It expects a pointer to the base record as well as the name of the member
and its field index.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this do for members of inherited types? is that encoded via the record? Or are they 'flattened' and the index is consistent?

Does it seem sensible to have examples of inheritance?

One thing that might be interesting if we don't encode it, is sometimes casts between types are legal if they have a 'common initial sequence' depending on language mode and use... so I wonder if encoding that (which, debatably, can change the common-initial-sequence rules) is worth-while some-day. (This last part is lamenting/thinking, not necessariy immediately actionable).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or are they 'flattened' and the index is consistent?

This one. For a bit of extra context: whenever something more complex is going on CIRGen takes care of it such that the final getmember is reliable.

One thing that might be interesting if we don't encode it is sometimes casts between types are legal if they have a 'common initial sequence' depending on language mode and use

Indeed interesting. If this turns out to help CIRGen / optimization we should be all in for it in the future. Any compelling use case in mind?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bunch of 'legal' vs 'UB' things with common-initial sequence, particularly in C that might be interesting to opt based on, but I don't have any good ideas.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha, I usually try to keep a log of these ideas, I'll ad this one. If anything else come up let me know.


Example:
```mlir
// Suppose we have a record with multiple members.
!s32i = !cir.int<s, 32>
!s8i = !cir.int<s, 8>
!ty_B = !cir.record<"struct.B" {!s32i, !s8i}>

// Get the address of the member at index 1.
%1 = cir.get_member %0[1] {name = "i"} : (!cir.ptr<!ty_B>) -> !cir.ptr<!s8i>
```
}];

let arguments = (ins
Arg<CIR_PointerType, "the address to load from", [MemRead]>:$addr,
StrAttr:$name,
IndexAttr:$index_attr);

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

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

let builders = [
OpBuilder<(ins "mlir::Type":$type,
"mlir::Value":$value,
"llvm::StringRef":$name,
"unsigned":$index),
[{
mlir::APInt fieldIdx(64, index);
build($_builder, $_state, type, value, name, fieldIdx);
}]>
];

let extraClassDeclaration = [{
/// Return the index of the record member being accessed.
uint64_t getIndex() { return getIndexAttr().getZExtValue(); }

/// Return the record type pointed by the base pointer.
cir::PointerType getAddrTy() { return getAddr().getType(); }

/// Return the result type.
cir::PointerType getResultTy() {
return mlir::cast<cir::PointerType>(getResult().getType());
}
}];

let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ def CIR_RecordType : CIR_Type<"Record", "record",
void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
bool isPadded);

uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
unsigned idx) const;

private:
unsigned computeStructSize(const mlir::DataLayout &dataLayout) const;
uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ struct MissingFeatures {
static bool emitCheckedInBoundsGEP() { return false; }
static bool preservedAccessIndexRegion() { return false; }
static bool bitfields() { return false; }
static bool typeChecks() { return false; }
static bool lambdaFieldToName() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
134 changes: 134 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,38 @@ using namespace clang;
using namespace clang::CIRGen;
using namespace cir;

/// Get the address of a zero-sized field within a record. The resulting address
/// doesn't necessarily have the right type.
Address CIRGenFunction::emitAddrOfFieldStorage(Address base,
const FieldDecl *field,
llvm::StringRef fieldName,
unsigned fieldIndex) {
if (field->isZeroSize(getContext())) {
cgm.errorNYI(field->getSourceRange(),
"emitAddrOfFieldStorage: zero-sized field");
return Address::invalid();
}

mlir::Location loc = getLoc(field->getLocation());

mlir::Type fieldType = convertType(field->getType());
auto fieldPtr = cir::PointerType::get(builder.getContext(), fieldType);
// For most cases fieldName is the same as field->getName() but for lambdas,
// which do not currently carry the name, so it can be passed down from the
// CaptureStmt.
cir::GetMemberOp memberAddr = builder.createGetMember(
loc, fieldPtr, base.getPointer(), fieldName, fieldIndex);

// Retrieve layout information, compute alignment and return the final
// address.
const RecordDecl *rec = field->getParent();
const CIRGenRecordLayout &layout = cgm.getTypes().getCIRGenRecordLayout(rec);
unsigned idx = layout.getCIRFieldNo(field);
CharUnits offset = CharUnits::fromQuantity(
layout.getCIRType().getElementOffset(cgm.getDataLayout().layout, idx));
return Address(memberAddr, base.getAlignment().alignmentAtOffset(offset));
}

/// Given an expression of pointer type, try to
/// derive a more accurate bound on the alignment of the pointer.
Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
Expand Down Expand Up @@ -264,6 +296,66 @@ mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
return {};
}

LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) {
LValueBaseInfo baseInfo = base.getBaseInfo();

if (field->isBitField()) {
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: bitfield");
return LValue();
}

QualType fieldType = field->getType();
const RecordDecl *rec = field->getParent();
AlignmentSource baseAlignSource = baseInfo.getAlignmentSource();
LValueBaseInfo fieldBaseInfo(getFieldAlignmentSource(baseAlignSource));
assert(!cir::MissingFeatures::opTBAA());

Address addr = base.getAddress();
if (auto *classDef = dyn_cast<CXXRecordDecl>(rec)) {
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: C++ class");
return LValue();
}

unsigned recordCVR = base.getVRQualifiers();
if (rec->isUnion()) {
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: union");
return LValue();
}

assert(!cir::MissingFeatures::preservedAccessIndexRegion());
llvm::StringRef fieldName = field->getName();
const CIRGenRecordLayout &layout =
cgm.getTypes().getCIRGenRecordLayout(field->getParent());
unsigned fieldIndex = layout.getCIRFieldNo(field);

assert(!cir::MissingFeatures::lambdaFieldToName());

addr = emitAddrOfFieldStorage(addr, field, fieldName, fieldIndex);

// If this is a reference field, load the reference right now.
if (fieldType->isReferenceType()) {
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: reference type");
return LValue();
}

if (field->hasAttr<AnnotateAttr>()) {
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: AnnotateAttr");
return LValue();
}

LValue lv = makeAddrLValue(addr, fieldType, fieldBaseInfo);
lv.getQuals().addCVRQualifiers(recordCVR);

// __weak attribute on a field is ignored.
if (lv.getQuals().getObjCGCAttr() == Qualifiers::Weak) {
cgm.errorNYI(field->getSourceRange(),
"emitLValueForField: __weak attribute");
return LValue();
}

return lv;
}

mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
// Bool has a different representation in memory than in registers,
// but in ClangIR, it is simply represented as a cir.bool value.
Expand Down Expand Up @@ -608,6 +700,48 @@ CIRGenFunction::emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e) {
return lv;
}

LValue CIRGenFunction::emitMemberExpr(const MemberExpr *e) {
if (auto *vd = dyn_cast<VarDecl>(e->getMemberDecl())) {
cgm.errorNYI(e->getSourceRange(), "emitMemberExpr: VarDecl");
return LValue();
}

Expr *baseExpr = e->getBase();
// If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar.
LValue baseLV;
if (e->isArrow()) {
LValueBaseInfo baseInfo;
assert(!cir::MissingFeatures::opTBAA());
Address addr = emitPointerWithAlignment(baseExpr, &baseInfo);
QualType ptrTy = baseExpr->getType()->getPointeeType();
assert(!cir::MissingFeatures::typeChecks());
baseLV = makeAddrLValue(addr, ptrTy, baseInfo);
} else {
assert(!cir::MissingFeatures::typeChecks());
baseLV = emitLValue(baseExpr);
}

const NamedDecl *nd = e->getMemberDecl();
if (auto *field = dyn_cast<FieldDecl>(nd)) {
LValue lv = emitLValueForField(baseLV, field);
assert(!cir::MissingFeatures::setObjCGCLValueClass());
if (getLangOpts().OpenMP) {
// If the member was explicitly marked as nontemporal, mark it as
// nontemporal. If the base lvalue is marked as nontemporal, mark access
// to children as nontemporal too.
cgm.errorNYI(e->getSourceRange(), "emitMemberExpr: OpenMP");
}
return lv;
}

if (const auto *fd = dyn_cast<FunctionDecl>(nd)) {
cgm.errorNYI(e->getSourceRange(), "emitMemberExpr: FunctionDecl");
return LValue();
}

llvm_unreachable("Unhandled member declaration!");
}

LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
// Comma expressions just emit their LHS then their RHS as an l-value.
if (e->getOpcode() == BO_Comma) {
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return emitLoadOfLValue(e);
}

mlir::Value VisitMemberExpr(MemberExpr *e);

mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) {
return VisitCastExpr(e);
}
Expand Down Expand Up @@ -1520,6 +1522,19 @@ mlir::Value ScalarExprEmitter::VisitCallExpr(const CallExpr *e) {
return v;
}

mlir::Value ScalarExprEmitter::VisitMemberExpr(MemberExpr *e) {
// TODO(cir): The classic codegen calls tryEmitAsConstant() here. Folding
// constants sound like work for MLIR optimizers, but we'll keep an assertion
// for now.
assert(!cir::MissingFeatures::tryEmitAsConstant());
Expr::EvalResult result;
if (e->EvaluateAsInt(result, cgf.getContext(), Expr::SE_AllowSideEffects)) {
cgf.cgm.errorNYI(e->getSourceRange(), "Constant interger member expr");
// Fall through to emit this as a non-constant access.
}
return emitLoadOfLValue(e);
}

mlir::Value CIRGenFunction::emitScalarConversion(mlir::Value src,
QualType srcTy, QualType dstTy,
SourceLocation loc) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
return emitArraySubscriptExpr(cast<ArraySubscriptExpr>(e));
case Expr::UnaryOperatorClass:
return emitUnaryOpLValue(cast<UnaryOperator>(e));
case Expr::MemberExprClass:
return emitMemberExpr(cast<MemberExpr>(e));
case Expr::BinaryOperatorClass:
return emitBinaryOperatorLValue(cast<BinaryOperator>(e));
case Expr::ParenExprClass:
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,10 @@ class CIRGenFunction : public CIRGenTypeCache {
clang::CharUnits alignment);

public:
Address emitAddrOfFieldStorage(Address base, const FieldDecl *field,
llvm::StringRef fieldName,
unsigned fieldIndex);

mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
mlir::Location loc, clang::CharUnits alignment,
bool insertIntoFnEntryBlock,
Expand Down Expand Up @@ -551,6 +555,9 @@ class CIRGenFunction : public CIRGenTypeCache {
/// of the expression.
/// FIXME: document this function better.
LValue emitLValue(const clang::Expr *e);
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);

LValue emitMemberExpr(const MemberExpr *e);

/// Given an expression with a pointer type, emit the value and compute our
/// best estimate of the alignment of the pointee.
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "CIRGenValue.h"

#include "clang/AST/CharUnits.h"
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"

#include "TargetInfo.h"
Expand Down Expand Up @@ -95,6 +96,12 @@ class CIRGenModule : public CIRGenTypeCache {
const clang::LangOptions &getLangOpts() const { return langOpts; }
mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); }

const cir::CIRDataLayout getDataLayout() const {
// FIXME(cir): instead of creating a CIRDataLayout every time, set it as an
// attribute for the CIRModule class.
return cir::CIRDataLayout(theModule);
}

/// -------
/// Handling globals
/// -------
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ class CIRGenRecordLayout {
cir::RecordType getCIRType() const { return completeObjectType; }

/// Return cir::RecordType element number that corresponds to the field FD.
unsigned getCIRFieldNo(const clang::FieldDecl *FD) const {
FD = FD->getCanonicalDecl();
assert(fieldInfo.count(FD) && "Invalid field for record!");
return fieldInfo.lookup(FD);
unsigned getCIRFieldNo(const clang::FieldDecl *fd) const {
fd = fd->getCanonicalDecl();
assert(fieldInfo.count(fd) && "Invalid field for record!");
return fieldInfo.lookup(fd);
}
};

Expand Down
21 changes: 21 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,27 @@ mlir::Type CIRGenTypes::convertTypeForMem(clang::QualType qualType,
return convertedType;
}

/// Return record layout info for the given record decl.
const CIRGenRecordLayout &
CIRGenTypes::getCIRGenRecordLayout(const RecordDecl *rd) {
const auto *key = astContext.getTagDeclType(rd).getTypePtr();

// If we have already computed the layout, return it.
auto it = cirGenRecordLayouts.find(key);
if (it != cirGenRecordLayouts.end())
return *it->second;

// Compute the type information.
convertRecordDeclType(rd);

// Now try again.
it = cirGenRecordLayouts.find(key);

assert(it != cirGenRecordLayouts.end() &&
"Unable to find record layout information for type");
return *it->second;
}

bool CIRGenTypes::isZeroInitializable(clang::QualType t) {
if (t->getAs<PointerType>())
return astContext.getTargetNullPointerValue(t) == 0;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class CIRGenTypes {
std::string getRecordTypeName(const clang::RecordDecl *,
llvm::StringRef suffix);

const CIRGenRecordLayout &getCIRGenRecordLayout(const clang::RecordDecl *rd);

/// Convert type T into an mlir::Type. This differs from convertType in that
/// it is used to convert to the memory representation for a type. For
/// example, the scalar representation for bool is i1, but the memory
Expand Down
Loading
Loading