diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 0b1316fc4012..e78baa37bccd 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -838,45 +838,6 @@ def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { }]; } -//===----------------------------------------------------------------------===// -// RecordLayoutAttr -//===----------------------------------------------------------------------===// - -// Used to decouple layout information from the record type. RecordType's -// uses this attribute to cache that information. - -def RecordLayoutAttr : CIR_Attr<"RecordLayout", "record_layout"> { - let summary = "ABI specific information about a record layout"; - let description = [{ - Holds layout information often queried by !cir.record users - during lowering passes and optimizations. - }]; - - let parameters = (ins "unsigned":$size, - "unsigned":$alignment, - "bool":$padded, - "mlir::Type":$largest_member, - "mlir::ArrayAttr":$offsets); - - let builders = [ - AttrBuilderWithInferredContext<(ins "unsigned":$size, - "unsigned":$alignment, - "bool":$padded, - "mlir::Type":$largest_member, - "mlir::ArrayAttr":$offsets), [{ - return $_get(largest_member.getContext(), size, alignment, padded, - largest_member, offsets); - }]>, - ]; - - let genVerifyDecl = 1; - let assemblyFormat = [{ - `<` - struct($size, $alignment, $padded, $largest_member, $offsets) - `>` - }]; -} - //===----------------------------------------------------------------------===// // DynamicCastInfoAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index 194fb5360ee3..a00f172c1cc1 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -743,13 +743,12 @@ def CIR_RecordType : CIR_Type<"Record", "record", bool isLayoutIdentical(const RecordType &other); - // Utilities for lazily computing and cacheing data layout info. - // FIXME: currently opaque because there's a cycle if CIRTypes.types include - // from CIRAttrs.h. The implementation operates in terms of RecordLayoutAttr - // instead. + // Utilities for computing data layout info private: - mutable mlir::Attribute layoutInfo; - void computeSizeAndAlignment(const mlir::DataLayout &dataLayout) const; + unsigned computeStructSize(const mlir::DataLayout &dataLayout) const; + unsigned computeUnionSize(const mlir::DataLayout &dataLayout) const; + uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const; + uint64_t computeUnionAlignment(const mlir::DataLayout &dataLayout) const; public: }]; diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 19da8f32519c..88d5bebd8e7e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -170,18 +170,6 @@ LogicalResult ConstRecordAttr::verify( return success(); } -LogicalResult RecordLayoutAttr::verify( - ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, unsigned size, - unsigned alignment, bool padded, mlir::Type largest_member, - mlir::ArrayAttr offsets) { - if (not std::all_of(offsets.begin(), offsets.end(), [](mlir::Attribute attr) { - return mlir::isa(attr); - })) { - return emitError() << "all index values must be integers"; - } - return success(); -} - //===----------------------------------------------------------------------===// // LangAttr definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 4955edfde4de..125c9fe99f73 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -112,9 +112,34 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const { /// /// Recurses into union members never returning a union as the largest member. Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { - if (!layoutInfo) - computeSizeAndAlignment(dataLayout); - return mlir::cast(layoutInfo).getLargestMember(); + assert(isUnion() && "getLargetMember called for non-union record"); + + // This is a similar algorithm to LLVM's StructLayout. + unsigned numElements = getNumElements(); + auto members = getMembers(); + mlir::Type largestMember; + unsigned largestMemberSize = 0; + + // Ignore the last member if this is a padded union. + if (getPadded()) + --numElements; + + for (unsigned i = 0, e = numElements; i != e; ++i) { + auto ty = members[i]; + + // Found a nested union: recurse into it to fetch its largest member. + if (!largestMember || + dataLayout.getTypeABIAlignment(ty) > + dataLayout.getTypeABIAlignment(largestMember) || + (dataLayout.getTypeABIAlignment(ty) == + dataLayout.getTypeABIAlignment(largestMember) && + dataLayout.getTypeSize(ty) > largestMemberSize)) { + largestMember = ty; + largestMemberSize = dataLayout.getTypeSize(largestMember); + } + } + + return largestMember; } Type RecordType::parse(mlir::AsmParser &parser) { @@ -385,117 +410,137 @@ cir::VectorType::getABIAlignment(const ::mlir::DataLayout &dataLayout, return llvm::NextPowerOf2(dataLayout.getTypeSizeInBits(*this)); } +// TODO(cir): Implement a way to cache the datalayout info calculated below. + llvm::TypeSize -RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout, - ::mlir::DataLayoutEntryListRef params) const { - if (!layoutInfo) - computeSizeAndAlignment(dataLayout); - return llvm::TypeSize::getFixed( - mlir::cast(layoutInfo).getSize() * 8); +RecordType::getTypeSizeInBits(const mlir::DataLayout &dataLayout, + mlir::DataLayoutEntryListRef params) const { + if (isUnion()) + return llvm::TypeSize::getFixed(computeUnionSize(dataLayout) * 8); + + return llvm::TypeSize::getFixed(computeStructSize(dataLayout) * 8); } uint64_t RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout, ::mlir::DataLayoutEntryListRef params) const { - if (!layoutInfo) - computeSizeAndAlignment(dataLayout); - return mlir::cast(layoutInfo).getAlignment(); -} + // Packed structures always have an ABI alignment of 1. + if (getPacked()) + return 1; -uint64_t RecordType::getElementOffset(const ::mlir::DataLayout &dataLayout, - unsigned idx) const { - assert(idx < getMembers().size() && "access not valid"); - if (!layoutInfo) - computeSizeAndAlignment(dataLayout); - auto offsets = mlir::cast(layoutInfo).getOffsets(); - auto intAttr = mlir::cast(offsets[idx]); - return intAttr.getInt(); + if (isUnion()) + return computeUnionAlignment(dataLayout); + return computeStructAlignment(dataLayout); } -void RecordType::computeSizeAndAlignment( - const ::mlir::DataLayout &dataLayout) const { +unsigned +RecordType::computeUnionSize(const mlir::DataLayout &dataLayout) const { assert(isComplete() && "Cannot get layout of incomplete records"); - // Do not recompute. - if (layoutInfo) - return; + assert(isUnion() && "computeUnionSize called for non-union record"); // This is a similar algorithm to LLVM's StructLayout. unsigned recordSize = 0; llvm::Align recordAlignment{1}; - bool isPadded = false; unsigned numElements = getNumElements(); auto members = getMembers(); - mlir::Type largestMember; unsigned largestMemberSize = 0; - llvm::SmallVector memberOffsets; - bool dontCountLastElt = isUnion() && getPadded(); - if (dontCountLastElt) - numElements--; + auto largestMember = getLargestMember(dataLayout); + recordSize = dataLayout.getTypeSize(largestMember); - // Loop over each of the elements, placing them in memory. - memberOffsets.reserve(numElements); + // If the union is padded, add the padding to the size. + if (getPadded()) { + auto ty = getMembers()[numElements - 1]; + recordSize += dataLayout.getTypeSize(ty); + } - for (unsigned i = 0, e = numElements; i != e; ++i) { - auto ty = members[i]; + return recordSize; +} - // Found a nested union: recurse into it to fetch its largest member. - if (!largestMember || - dataLayout.getTypeABIAlignment(ty) > - dataLayout.getTypeABIAlignment(largestMember) || - (dataLayout.getTypeABIAlignment(ty) == - dataLayout.getTypeABIAlignment(largestMember) && - dataLayout.getTypeSize(ty) > largestMemberSize)) { - largestMember = ty; - largestMemberSize = dataLayout.getTypeSize(largestMember); - } +unsigned +RecordType::computeStructSize(const mlir::DataLayout &dataLayout) const { + assert(isComplete() && "Cannot get layout of incomplete records"); + + // This is a similar algorithm to LLVM's StructLayout. + unsigned recordSize = 0; + uint64_t recordAlignment = 1; + + // We can't use a range-based for loop here because we might be ignoring the + // last element. + for (mlir::Type ty : getMembers()) { + // This assumes that we're calculating size based on the ABI alignment, not + // the preferred alignment for each type. + const uint64_t tyAlign = + (getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty)); + + // Add padding to the struct size to align it to the abi alignment of the + // element type before than adding the size of the element. + recordSize = llvm::alignTo(recordSize, tyAlign); + recordSize += dataLayout.getTypeSize(ty); + + // The alignment requirement of a struct is equal to the strictest alignment + // requirement of its elements. + recordAlignment = std::max(tyAlign, recordAlignment); + } + + // At the end, add padding to the struct to satisfy its own alignment + // requirement. Otherwise structs inside of arrays would be misaligned. + recordSize = llvm::alignTo(recordSize, recordAlignment); + return recordSize; +} + +// We also compute the alignment as part of computeStructSize, but this is more +// efficient. Ideally, we'd like to compute both at once and cache the result, +// but that's implemented yet. +// TODO(CIR): Implement a way to cache the result. +uint64_t +RecordType::computeStructAlignment(const mlir::DataLayout &dataLayout) const { + assert(isComplete() && "Cannot get layout of incomplete records"); + + // This is a similar algorithm to LLVM's StructLayout. + uint64_t recordAlignment = 1; + for (mlir::Type ty : getMembers()) + recordAlignment = + std::max(dataLayout.getTypeABIAlignment(ty), recordAlignment); + + return recordAlignment; +} + +uint64_t +RecordType::computeUnionAlignment(const mlir::DataLayout &dataLayout) const { + auto largestMember = getLargestMember(dataLayout); + return dataLayout.getTypeABIAlignment(largestMember); +} + +uint64_t RecordType::getElementOffset(const ::mlir::DataLayout &dataLayout, + unsigned idx) const { + assert(idx < getMembers().size() && "access not valid"); + + // All union elements are at offset zero. + if (isUnion() || idx == 0) + return 0; + + assert(isComplete() && "Cannot get layout of incomplete records"); + assert(idx < getNumElements()); + auto members = getMembers(); + + unsigned offset = 0; + + for (unsigned i = 0, e = idx; i != e; ++i) { + auto ty = members[i]; // This matches LLVM since it uses the ABI instead of preferred alignment. const llvm::Align tyAlign = llvm::Align(getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty)); // Add padding if necessary to align the data element properly. - if (!llvm::isAligned(tyAlign, recordSize)) { - isPadded = true; - recordSize = llvm::alignTo(recordSize, tyAlign); - } - - // Keep track of maximum alignment constraint. - recordAlignment = std::max(tyAlign, recordAlignment); - - // Record size up to each element is the element offset. - memberOffsets.push_back(mlir::IntegerAttr::get( - mlir::IntegerType::get(getContext(), 32), isUnion() ? 0 : recordSize)); + offset = llvm::alignTo(offset, tyAlign); // Consume space for this data item - recordSize += dataLayout.getTypeSize(ty); - } - - // For unions, the size and aligment is that of the largest element. - if (isUnion()) { - recordSize = largestMemberSize; - if (getPadded()) { - memberOffsets.push_back(mlir::IntegerAttr::get( - mlir::IntegerType::get(getContext(), 32), recordSize)); - auto ty = getMembers()[numElements]; - recordSize += dataLayout.getTypeSize(ty); - isPadded = true; - } else { - isPadded = false; - } - } else { - // Add padding to the end of the record so that it could be put in an array - // and all array elements would be aligned correctly. - if (!llvm::isAligned(recordAlignment, recordSize)) { - isPadded = true; - recordSize = llvm::alignTo(recordSize, recordAlignment); - } + offset += dataLayout.getTypeSize(ty); } - auto offsets = mlir::ArrayAttr::get(getContext(), memberOffsets); - layoutInfo = cir::RecordLayoutAttr::get(getContext(), recordSize, - recordAlignment.value(), isPadded, - largestMember, offsets); + return offset; } //===----------------------------------------------------------------------===//