Skip to content

Commit 390cf25

Browse files
authored
[CIR] Replace RecordType data layout calculations (#1569)
We have been using RecordLayoutAttr to "cache" data layout information calculated for records. Unfortunately, it wasn't actually caching the information, and because each call was calculating more information than it needed, it was doing extra work. This replaces the previous implementation with a set of functions that compute only the information needed. Ideally, we would like to have a mechanism to properly cache this information, but until such a mechanism is implemented, these new functions should be a small step forward.
1 parent 0bae1fd commit 390cf25

File tree

4 files changed

+133
-140
lines changed

4 files changed

+133
-140
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

-39
Original file line numberDiff line numberDiff line change
@@ -838,45 +838,6 @@ def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
838838
}];
839839
}
840840

841-
//===----------------------------------------------------------------------===//
842-
// RecordLayoutAttr
843-
//===----------------------------------------------------------------------===//
844-
845-
// Used to decouple layout information from the record type. RecordType's
846-
// uses this attribute to cache that information.
847-
848-
def RecordLayoutAttr : CIR_Attr<"RecordLayout", "record_layout"> {
849-
let summary = "ABI specific information about a record layout";
850-
let description = [{
851-
Holds layout information often queried by !cir.record users
852-
during lowering passes and optimizations.
853-
}];
854-
855-
let parameters = (ins "unsigned":$size,
856-
"unsigned":$alignment,
857-
"bool":$padded,
858-
"mlir::Type":$largest_member,
859-
"mlir::ArrayAttr":$offsets);
860-
861-
let builders = [
862-
AttrBuilderWithInferredContext<(ins "unsigned":$size,
863-
"unsigned":$alignment,
864-
"bool":$padded,
865-
"mlir::Type":$largest_member,
866-
"mlir::ArrayAttr":$offsets), [{
867-
return $_get(largest_member.getContext(), size, alignment, padded,
868-
largest_member, offsets);
869-
}]>,
870-
];
871-
872-
let genVerifyDecl = 1;
873-
let assemblyFormat = [{
874-
`<`
875-
struct($size, $alignment, $padded, $largest_member, $offsets)
876-
`>`
877-
}];
878-
}
879-
880841
//===----------------------------------------------------------------------===//
881842
// DynamicCastInfoAttr
882843
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIRTypes.td

+5-6
Original file line numberDiff line numberDiff line change
@@ -781,13 +781,12 @@ def CIR_RecordType : CIR_Type<"Record", "record",
781781

782782
bool isLayoutIdentical(const RecordType &other);
783783

784-
// Utilities for lazily computing and cacheing data layout info.
785-
// FIXME: currently opaque because there's a cycle if CIRTypes.types include
786-
// from CIRAttrs.h. The implementation operates in terms of RecordLayoutAttr
787-
// instead.
784+
// Utilities for computing data layout info
788785
private:
789-
mutable mlir::Attribute layoutInfo;
790-
void computeSizeAndAlignment(const mlir::DataLayout &dataLayout) const;
786+
unsigned computeStructSize(const mlir::DataLayout &dataLayout) const;
787+
unsigned computeUnionSize(const mlir::DataLayout &dataLayout) const;
788+
uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const;
789+
uint64_t computeUnionAlignment(const mlir::DataLayout &dataLayout) const;
791790
public:
792791
}];
793792

clang/lib/CIR/Dialect/IR/CIRAttrs.cpp

-12
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,6 @@ LogicalResult ConstRecordAttr::verify(
170170
return success();
171171
}
172172

173-
LogicalResult RecordLayoutAttr::verify(
174-
::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, unsigned size,
175-
unsigned alignment, bool padded, mlir::Type largest_member,
176-
mlir::ArrayAttr offsets) {
177-
if (not std::all_of(offsets.begin(), offsets.end(), [](mlir::Attribute attr) {
178-
return mlir::isa<mlir::IntegerAttr>(attr);
179-
})) {
180-
return emitError() << "all index values must be integers";
181-
}
182-
return success();
183-
}
184-
185173
//===----------------------------------------------------------------------===//
186174
// LangAttr definitions
187175
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRTypes.cpp

+128-83
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,34 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
112112
///
113113
/// Recurses into union members never returning a union as the largest member.
114114
Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const {
115-
if (!layoutInfo)
116-
computeSizeAndAlignment(dataLayout);
117-
return mlir::cast<cir::RecordLayoutAttr>(layoutInfo).getLargestMember();
115+
assert(isUnion() && "getLargetMember called for non-union record");
116+
117+
// This is a similar algorithm to LLVM's StructLayout.
118+
unsigned numElements = getNumElements();
119+
auto members = getMembers();
120+
mlir::Type largestMember;
121+
unsigned largestMemberSize = 0;
122+
123+
// Ignore the last member if this is a padded union.
124+
if (getPadded())
125+
--numElements;
126+
127+
for (unsigned i = 0, e = numElements; i != e; ++i) {
128+
auto ty = members[i];
129+
130+
// Found a nested union: recurse into it to fetch its largest member.
131+
if (!largestMember ||
132+
dataLayout.getTypeABIAlignment(ty) >
133+
dataLayout.getTypeABIAlignment(largestMember) ||
134+
(dataLayout.getTypeABIAlignment(ty) ==
135+
dataLayout.getTypeABIAlignment(largestMember) &&
136+
dataLayout.getTypeSize(ty) > largestMemberSize)) {
137+
largestMember = ty;
138+
largestMemberSize = dataLayout.getTypeSize(largestMember);
139+
}
140+
}
141+
142+
return largestMember;
118143
}
119144

120145
Type RecordType::parse(mlir::AsmParser &parser) {
@@ -385,117 +410,137 @@ cir::VectorType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
385410
return llvm::NextPowerOf2(dataLayout.getTypeSizeInBits(*this));
386411
}
387412

413+
// TODO(cir): Implement a way to cache the datalayout info calculated below.
414+
388415
llvm::TypeSize
389-
RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
390-
::mlir::DataLayoutEntryListRef params) const {
391-
if (!layoutInfo)
392-
computeSizeAndAlignment(dataLayout);
393-
return llvm::TypeSize::getFixed(
394-
mlir::cast<cir::RecordLayoutAttr>(layoutInfo).getSize() * 8);
416+
RecordType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
417+
mlir::DataLayoutEntryListRef params) const {
418+
if (isUnion())
419+
return llvm::TypeSize::getFixed(computeUnionSize(dataLayout) * 8);
420+
421+
return llvm::TypeSize::getFixed(computeStructSize(dataLayout) * 8);
395422
}
396423

397424
uint64_t
398425
RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
399426
::mlir::DataLayoutEntryListRef params) const {
400-
if (!layoutInfo)
401-
computeSizeAndAlignment(dataLayout);
402-
return mlir::cast<cir::RecordLayoutAttr>(layoutInfo).getAlignment();
403-
}
427+
// Packed structures always have an ABI alignment of 1.
428+
if (getPacked())
429+
return 1;
404430

405-
uint64_t RecordType::getElementOffset(const ::mlir::DataLayout &dataLayout,
406-
unsigned idx) const {
407-
assert(idx < getMembers().size() && "access not valid");
408-
if (!layoutInfo)
409-
computeSizeAndAlignment(dataLayout);
410-
auto offsets = mlir::cast<cir::RecordLayoutAttr>(layoutInfo).getOffsets();
411-
auto intAttr = mlir::cast<mlir::IntegerAttr>(offsets[idx]);
412-
return intAttr.getInt();
431+
if (isUnion())
432+
return computeUnionAlignment(dataLayout);
433+
return computeStructAlignment(dataLayout);
413434
}
414435

415-
void RecordType::computeSizeAndAlignment(
416-
const ::mlir::DataLayout &dataLayout) const {
436+
unsigned
437+
RecordType::computeUnionSize(const mlir::DataLayout &dataLayout) const {
417438
assert(isComplete() && "Cannot get layout of incomplete records");
418-
// Do not recompute.
419-
if (layoutInfo)
420-
return;
439+
assert(isUnion() && "computeUnionSize called for non-union record");
421440

422441
// This is a similar algorithm to LLVM's StructLayout.
423442
unsigned recordSize = 0;
424443
llvm::Align recordAlignment{1};
425-
bool isPadded = false;
426444
unsigned numElements = getNumElements();
427445
auto members = getMembers();
428-
mlir::Type largestMember;
429446
unsigned largestMemberSize = 0;
430-
llvm::SmallVector<mlir::Attribute, 4> memberOffsets;
431447

432-
bool dontCountLastElt = isUnion() && getPadded();
433-
if (dontCountLastElt)
434-
numElements--;
448+
auto largestMember = getLargestMember(dataLayout);
449+
recordSize = dataLayout.getTypeSize(largestMember);
435450

436-
// Loop over each of the elements, placing them in memory.
437-
memberOffsets.reserve(numElements);
451+
// If the union is padded, add the padding to the size.
452+
if (getPadded()) {
453+
auto ty = getMembers()[numElements - 1];
454+
recordSize += dataLayout.getTypeSize(ty);
455+
}
438456

439-
for (unsigned i = 0, e = numElements; i != e; ++i) {
440-
auto ty = members[i];
457+
return recordSize;
458+
}
441459

442-
// Found a nested union: recurse into it to fetch its largest member.
443-
if (!largestMember ||
444-
dataLayout.getTypeABIAlignment(ty) >
445-
dataLayout.getTypeABIAlignment(largestMember) ||
446-
(dataLayout.getTypeABIAlignment(ty) ==
447-
dataLayout.getTypeABIAlignment(largestMember) &&
448-
dataLayout.getTypeSize(ty) > largestMemberSize)) {
449-
largestMember = ty;
450-
largestMemberSize = dataLayout.getTypeSize(largestMember);
451-
}
460+
unsigned
461+
RecordType::computeStructSize(const mlir::DataLayout &dataLayout) const {
462+
assert(isComplete() && "Cannot get layout of incomplete records");
463+
464+
// This is a similar algorithm to LLVM's StructLayout.
465+
unsigned recordSize = 0;
466+
uint64_t recordAlignment = 1;
467+
468+
// We can't use a range-based for loop here because we might be ignoring the
469+
// last element.
470+
for (mlir::Type ty : getMembers()) {
471+
// This assumes that we're calculating size based on the ABI alignment, not
472+
// the preferred alignment for each type.
473+
const uint64_t tyAlign =
474+
(getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty));
475+
476+
// Add padding to the struct size to align it to the abi alignment of the
477+
// element type before than adding the size of the element.
478+
recordSize = llvm::alignTo(recordSize, tyAlign);
479+
recordSize += dataLayout.getTypeSize(ty);
480+
481+
// The alignment requirement of a struct is equal to the strictest alignment
482+
// requirement of its elements.
483+
recordAlignment = std::max(tyAlign, recordAlignment);
484+
}
485+
486+
// At the end, add padding to the struct to satisfy its own alignment
487+
// requirement. Otherwise structs inside of arrays would be misaligned.
488+
recordSize = llvm::alignTo(recordSize, recordAlignment);
489+
return recordSize;
490+
}
491+
492+
// We also compute the alignment as part of computeStructSize, but this is more
493+
// efficient. Ideally, we'd like to compute both at once and cache the result,
494+
// but that's implemented yet.
495+
// TODO(CIR): Implement a way to cache the result.
496+
uint64_t
497+
RecordType::computeStructAlignment(const mlir::DataLayout &dataLayout) const {
498+
assert(isComplete() && "Cannot get layout of incomplete records");
499+
500+
// This is a similar algorithm to LLVM's StructLayout.
501+
uint64_t recordAlignment = 1;
502+
for (mlir::Type ty : getMembers())
503+
recordAlignment =
504+
std::max(dataLayout.getTypeABIAlignment(ty), recordAlignment);
505+
506+
return recordAlignment;
507+
}
508+
509+
uint64_t
510+
RecordType::computeUnionAlignment(const mlir::DataLayout &dataLayout) const {
511+
auto largestMember = getLargestMember(dataLayout);
512+
return dataLayout.getTypeABIAlignment(largestMember);
513+
}
514+
515+
uint64_t RecordType::getElementOffset(const ::mlir::DataLayout &dataLayout,
516+
unsigned idx) const {
517+
assert(idx < getMembers().size() && "access not valid");
518+
519+
// All union elements are at offset zero.
520+
if (isUnion() || idx == 0)
521+
return 0;
522+
523+
assert(isComplete() && "Cannot get layout of incomplete records");
524+
assert(idx < getNumElements());
525+
auto members = getMembers();
526+
527+
unsigned offset = 0;
528+
529+
for (unsigned i = 0, e = idx; i != e; ++i) {
530+
auto ty = members[i];
452531

453532
// This matches LLVM since it uses the ABI instead of preferred alignment.
454533
const llvm::Align tyAlign =
455534
llvm::Align(getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty));
456535

457536
// Add padding if necessary to align the data element properly.
458-
if (!llvm::isAligned(tyAlign, recordSize)) {
459-
isPadded = true;
460-
recordSize = llvm::alignTo(recordSize, tyAlign);
461-
}
462-
463-
// Keep track of maximum alignment constraint.
464-
recordAlignment = std::max(tyAlign, recordAlignment);
465-
466-
// Record size up to each element is the element offset.
467-
memberOffsets.push_back(mlir::IntegerAttr::get(
468-
mlir::IntegerType::get(getContext(), 32), isUnion() ? 0 : recordSize));
537+
offset = llvm::alignTo(offset, tyAlign);
469538

470539
// Consume space for this data item
471-
recordSize += dataLayout.getTypeSize(ty);
472-
}
473-
474-
// For unions, the size and aligment is that of the largest element.
475-
if (isUnion()) {
476-
recordSize = largestMemberSize;
477-
if (getPadded()) {
478-
memberOffsets.push_back(mlir::IntegerAttr::get(
479-
mlir::IntegerType::get(getContext(), 32), recordSize));
480-
auto ty = getMembers()[numElements];
481-
recordSize += dataLayout.getTypeSize(ty);
482-
isPadded = true;
483-
} else {
484-
isPadded = false;
485-
}
486-
} else {
487-
// Add padding to the end of the record so that it could be put in an array
488-
// and all array elements would be aligned correctly.
489-
if (!llvm::isAligned(recordAlignment, recordSize)) {
490-
isPadded = true;
491-
recordSize = llvm::alignTo(recordSize, recordAlignment);
492-
}
540+
offset += dataLayout.getTypeSize(ty);
493541
}
494542

495-
auto offsets = mlir::ArrayAttr::get(getContext(), memberOffsets);
496-
layoutInfo = cir::RecordLayoutAttr::get(getContext(), recordSize,
497-
recordAlignment.value(), isPadded,
498-
largestMember, offsets);
543+
return offset;
499544
}
500545

501546
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)