Files
clang-p2996/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
Andy Kaylor 80c19b3b1d [CIR] Upstream initial support for complete record types (#135844)
This adds basic support for populating record types. In order to keep
the change small, everything non-essential was deferred to a later
change set. Only non-recursive structures are handled. Structures
padding is not yet implemented. Bitfields are not supported. No attempt
is made to handle ABI requirements for passing structure arguments.
2025-04-16 10:33:09 -07:00

224 lines
7.6 KiB
C++

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This contains code to compute the layout of a record.
//
//===----------------------------------------------------------------------===//
#include "CIRGenBuilder.h"
#include "CIRGenModule.h"
#include "CIRGenTypes.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "llvm/Support/Casting.h"
#include <memory>
using namespace llvm;
using namespace clang;
using namespace clang::CIRGen;
namespace {
/// The CIRRecordLowering is responsible for lowering an ASTRecordLayout to an
/// mlir::Type. Some of the lowering is straightforward, some is not.
// TODO: Detail some of the complexities and weirdnesses?
// (See CGRecordLayoutBuilder.cpp)
struct CIRRecordLowering final {
// MemberInfo is a helper structure that contains information about a record
// member. In addition to the standard member types, there exists a sentinel
// member type that ensures correct rounding.
struct MemberInfo final {
CharUnits offset;
enum class InfoKind { Field } kind;
mlir::Type data;
union {
const FieldDecl *fieldDecl;
// CXXRecordDecl will be used here when base types are supported.
};
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
const FieldDecl *fieldDecl = nullptr)
: offset(offset), kind(kind), data(data), fieldDecl(fieldDecl) {};
// MemberInfos are sorted so we define a < operator.
bool operator<(const MemberInfo &other) const {
return offset < other.offset;
}
};
// The constructor.
CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl,
bool isPacked);
void lower();
void accumulateFields();
CharUnits bitsToCharUnits(uint64_t bitOffset) {
return astContext.toCharUnitsFromBits(bitOffset);
}
CharUnits getSize(mlir::Type Ty) {
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
return CharUnits::One();
}
CharUnits getAlignment(mlir::Type Ty) {
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
return CharUnits::One();
}
mlir::Type getStorageType(const FieldDecl *fieldDecl) {
mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType());
if (fieldDecl->isBitField()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"getStorageType for bitfields");
}
return type;
}
uint64_t getFieldBitOffset(const FieldDecl *fieldDecl) {
return astRecordLayout.getFieldOffset(fieldDecl->getFieldIndex());
}
/// Fills out the structures that are ultimately consumed.
void fillOutputFields();
CIRGenTypes &cirGenTypes;
CIRGenBuilderTy &builder;
const ASTContext &astContext;
const RecordDecl *recordDecl;
const ASTRecordLayout &astRecordLayout;
// Helpful intermediate data-structures
std::vector<MemberInfo> members;
// Output fields, consumed by CIRGenTypes::computeRecordLayout
llvm::SmallVector<mlir::Type, 16> fieldTypes;
llvm::DenseMap<const FieldDecl *, unsigned> fields;
LLVM_PREFERRED_TYPE(bool)
unsigned zeroInitializable : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned packed : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned padded : 1;
private:
CIRRecordLowering(const CIRRecordLowering &) = delete;
void operator=(const CIRRecordLowering &) = delete;
}; // CIRRecordLowering
} // namespace
CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
const RecordDecl *recordDecl,
bool isPacked)
: cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()),
astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
astRecordLayout(
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
zeroInitializable(true), packed(isPacked), padded(false) {}
void CIRRecordLowering::lower() {
if (recordDecl->isUnion()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"lower: union");
return;
}
if (isa<CXXRecordDecl>(recordDecl)) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"lower: class");
return;
}
assert(!cir::MissingFeatures::cxxSupport());
accumulateFields();
llvm::stable_sort(members);
// TODO: implement clipTailPadding once bitfields are implemented
assert(!cir::MissingFeatures::bitfields());
// TODO: implemented packed records
assert(!cir::MissingFeatures::packedRecords());
// TODO: implement padding
assert(!cir::MissingFeatures::recordPadding());
// TODO: support zeroInit
assert(!cir::MissingFeatures::recordZeroInit());
fillOutputFields();
}
void CIRRecordLowering::fillOutputFields() {
for (const MemberInfo &member : members) {
if (member.data)
fieldTypes.push_back(member.data);
if (member.kind == MemberInfo::InfoKind::Field) {
if (member.fieldDecl)
fields[member.fieldDecl->getCanonicalDecl()] = fieldTypes.size() - 1;
// A field without storage must be a bitfield.
assert(!cir::MissingFeatures::bitfields());
}
assert(!cir::MissingFeatures::cxxSupport());
}
}
void CIRRecordLowering::accumulateFields() {
for (const FieldDecl *field : recordDecl->fields()) {
if (field->isBitField()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulate bitfields");
++field;
} else if (!field->isZeroSize(astContext)) {
members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(field)),
MemberInfo::InfoKind::Field,
getStorageType(field), field));
++field;
} else {
// TODO(cir): do we want to do anything special about zero size members?
assert(!cir::MissingFeatures::zeroSizeRecordMembers());
++field;
}
}
}
std::unique_ptr<CIRGenRecordLayout>
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
CIRRecordLowering lowering(*this, rd, /*packed=*/false);
assert(ty->isIncomplete() && "recomputing record layout?");
lowering.lower();
// If we're in C++, compute the base subobject type.
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
!rd->hasAttr<FinalAttr>()) {
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl");
}
// Fill in the record *after* computing the base type. Filling in the body
// signifies that the type is no longer opaque and record layout is complete,
// but we may need to recursively layout rd while laying D out as a base type.
assert(!cir::MissingFeatures::astRecordDeclAttr());
ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded);
auto rl = std::make_unique<CIRGenRecordLayout>(ty ? *ty : cir::RecordType());
assert(!cir::MissingFeatures::recordZeroInit());
assert(!cir::MissingFeatures::cxxSupport());
assert(!cir::MissingFeatures::bitfields());
// Add all the field numbers.
rl->fieldInfo.swap(lowering.fields);
// Dump the layout, if requested.
if (getASTContext().getLangOpts().DumpRecordLayouts) {
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: dump layout");
}
// TODO: implement verification
return rl;
}