Skip to content

Commit e298f16

Browse files
authored
[CIR] Upstream support for record packing and padding (#136036)
This change adds support for packing and padding record types in ClangIR and introduces some infrastructure needed for this computation. Although union support has not been upstreamed yet, there is no good way to report unions as NYI in the layout computation, so the code added here includes layout computation for unions. Unions will be added soon.
1 parent b2ba531 commit e298f16

File tree

7 files changed

+254
-25
lines changed

7 files changed

+254
-25
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// Provides an LLVM-like API wrapper to DLTI and MLIR layout queries. This
9+
// makes it easier to port some of LLVM codegen layout logic to CIR.
10+
//===----------------------------------------------------------------------===//
11+
12+
#ifndef CLANG_CIR_DIALECT_IR_CIRDATALAYOUT_H
13+
#define CLANG_CIR_DIALECT_IR_CIRDATALAYOUT_H
14+
15+
#include "mlir/IR/BuiltinOps.h"
16+
17+
namespace cir {
18+
19+
// TODO(cir): This might be replaced by a CIRDataLayout interface which can
20+
// provide the same functionalities.
21+
class CIRDataLayout {
22+
// This is starting with the minimum functionality needed for code that is
23+
// being upstreamed. Additional methods and members will be added as needed.
24+
public:
25+
mlir::DataLayout layout;
26+
27+
/// Constructs a DataLayout the module's data layout attribute.
28+
CIRDataLayout(mlir::ModuleOp modOp) : layout{modOp} {}
29+
};
30+
31+
} // namespace cir
32+
33+
#endif // CLANG_CIR_DIALECT_IR_CIRDATALAYOUT_H

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,11 @@ def CIR_RecordType : CIR_Type<"Record", "record",
502502

503503
void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
504504
bool isPadded);
505+
506+
private:
507+
unsigned computeStructSize(const mlir::DataLayout &dataLayout) const;
508+
uint64_t computeStructAlignment(const mlir::DataLayout &dataLayout) const;
509+
public:
505510
}];
506511

507512
let hasCustomAssemblyFormat = 1;

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,10 @@ struct MissingFeatures {
102102
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
103103

104104
// RecordType
105-
static bool recordTypeLayoutInfo() { return false; }
106105
static bool recursiveRecordLayout() { return false; }
107106
static bool skippedLayout() { return false; }
108107
static bool astRecordDeclAttr() { return false; }
109108
static bool cxxSupport() { return false; }
110-
static bool packedRecords() { return false; }
111-
static bool recordPadding() { return false; }
112109
static bool recordZeroInit() { return false; }
113110
static bool zeroSizeRecordMembers() { return false; }
114111

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/DeclCXX.h"
2020
#include "clang/AST/RecordLayout.h"
2121
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
22+
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
2223
#include "llvm/Support/Casting.h"
2324

2425
#include <memory>
@@ -57,21 +58,52 @@ struct CIRRecordLowering final {
5758
CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl,
5859
bool isPacked);
5960

61+
/// Constructs a MemberInfo instance from an offset and mlir::Type.
62+
MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) {
63+
return MemberInfo(offset, MemberInfo::InfoKind::Field, data);
64+
}
65+
6066
void lower();
6167

68+
/// Determines if we need a packed llvm struct.
69+
void determinePacked();
70+
/// Inserts padding everywhere it's needed.
71+
void insertPadding();
72+
6273
void accumulateFields();
6374

6475
CharUnits bitsToCharUnits(uint64_t bitOffset) {
6576
return astContext.toCharUnitsFromBits(bitOffset);
6677
}
6778

6879
CharUnits getSize(mlir::Type Ty) {
69-
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
70-
return CharUnits::One();
80+
return CharUnits::fromQuantity(dataLayout.layout.getTypeSize(Ty));
7181
}
7282
CharUnits getAlignment(mlir::Type Ty) {
73-
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
74-
return CharUnits::One();
83+
return CharUnits::fromQuantity(dataLayout.layout.getTypeABIAlignment(Ty));
84+
}
85+
86+
/// Wraps cir::IntType with some implicit arguments.
87+
mlir::Type getUIntNType(uint64_t numBits) {
88+
unsigned alignedBits = llvm::PowerOf2Ceil(numBits);
89+
alignedBits = std::max(8u, alignedBits);
90+
return cir::IntType::get(&cirGenTypes.getMLIRContext(), alignedBits,
91+
/*isSigned=*/false);
92+
}
93+
94+
mlir::Type getCharType() {
95+
return cir::IntType::get(&cirGenTypes.getMLIRContext(),
96+
astContext.getCharWidth(),
97+
/*isSigned=*/false);
98+
}
99+
100+
mlir::Type getByteArrayType(CharUnits numberOfChars) {
101+
assert(!numberOfChars.isZero() && "Empty byte arrays aren't allowed.");
102+
mlir::Type type = getCharType();
103+
return numberOfChars == CharUnits::One()
104+
? type
105+
: cir::ArrayType::get(type.getContext(), type,
106+
numberOfChars.getQuantity());
75107
}
76108

77109
mlir::Type getStorageType(const FieldDecl *fieldDecl) {
@@ -100,6 +132,7 @@ struct CIRRecordLowering final {
100132
// Output fields, consumed by CIRGenTypes::computeRecordLayout
101133
llvm::SmallVector<mlir::Type, 16> fieldTypes;
102134
llvm::DenseMap<const FieldDecl *, unsigned> fields;
135+
cir::CIRDataLayout dataLayout;
103136

104137
LLVM_PREFERRED_TYPE(bool)
105138
unsigned zeroInitializable : 1;
@@ -121,6 +154,7 @@ CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
121154
astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
122155
astRecordLayout(
123156
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
157+
dataLayout(cirGenTypes.getCGModule().getModule()),
124158
zeroInitializable(true), packed(isPacked), padded(false) {}
125159

126160
void CIRRecordLowering::lower() {
@@ -138,18 +172,20 @@ void CIRRecordLowering::lower() {
138172

139173
assert(!cir::MissingFeatures::cxxSupport());
140174

175+
CharUnits size = astRecordLayout.getSize();
176+
141177
accumulateFields();
142178

143179
llvm::stable_sort(members);
144180
// TODO: implement clipTailPadding once bitfields are implemented
145181
assert(!cir::MissingFeatures::bitfields());
146-
// TODO: implemented packed records
147-
assert(!cir::MissingFeatures::packedRecords());
148-
// TODO: implement padding
149-
assert(!cir::MissingFeatures::recordPadding());
150-
// TODO: support zeroInit
151182
assert(!cir::MissingFeatures::recordZeroInit());
152183

184+
members.push_back(makeStorageInfo(size, getUIntNType(8)));
185+
determinePacked();
186+
insertPadding();
187+
members.pop_back();
188+
153189
fillOutputFields();
154190
}
155191

@@ -186,6 +222,56 @@ void CIRRecordLowering::accumulateFields() {
186222
}
187223
}
188224

225+
void CIRRecordLowering::determinePacked() {
226+
if (packed)
227+
return;
228+
CharUnits alignment = CharUnits::One();
229+
230+
// TODO(cir): handle non-virtual base types
231+
assert(!cir::MissingFeatures::cxxSupport());
232+
233+
for (const MemberInfo &member : members) {
234+
if (!member.data)
235+
continue;
236+
// If any member falls at an offset that it not a multiple of its alignment,
237+
// then the entire record must be packed.
238+
if (member.offset % getAlignment(member.data))
239+
packed = true;
240+
alignment = std::max(alignment, getAlignment(member.data));
241+
}
242+
// If the size of the record (the capstone's offset) is not a multiple of the
243+
// record's alignment, it must be packed.
244+
if (members.back().offset % alignment)
245+
packed = true;
246+
// Update the alignment of the sentinel.
247+
if (!packed)
248+
members.back().data = getUIntNType(astContext.toBits(alignment));
249+
}
250+
251+
void CIRRecordLowering::insertPadding() {
252+
std::vector<std::pair<CharUnits, CharUnits>> padding;
253+
CharUnits size = CharUnits::Zero();
254+
for (const MemberInfo &member : members) {
255+
if (!member.data)
256+
continue;
257+
CharUnits offset = member.offset;
258+
assert(offset >= size);
259+
// Insert padding if we need to.
260+
if (offset !=
261+
size.alignTo(packed ? CharUnits::One() : getAlignment(member.data)))
262+
padding.push_back(std::make_pair(size, offset - size));
263+
size = offset + getSize(member.data);
264+
}
265+
if (padding.empty())
266+
return;
267+
padded = true;
268+
// Add the padding to the Members list and sort it.
269+
for (const std::pair<CharUnits, CharUnits> &paddingPair : padding)
270+
members.push_back(makeStorageInfo(paddingPair.first,
271+
getByteArrayType(paddingPair.second)));
272+
llvm::stable_sort(members);
273+
}
274+
189275
std::unique_ptr<CIRGenRecordLayout>
190276
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
191277
CIRRecordLowering lowering(*this, rd, /*packed=*/false);

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

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ void RecordType::print(mlir::AsmPrinter &printer) const {
177177
// Type not yet printed: continue printing the entire record.
178178
printer << ' ';
179179

180+
if (getPacked())
181+
printer << "packed ";
182+
183+
if (getPadded())
184+
printer << "padded ";
185+
180186
if (isIncomplete()) {
181187
printer << "incomplete";
182188
} else {
@@ -210,6 +216,10 @@ mlir::StringAttr RecordType::getName() const { return getImpl()->name; }
210216

211217
bool RecordType::getIncomplete() const { return getImpl()->incomplete; }
212218

219+
bool RecordType::getPacked() const { return getImpl()->packed; }
220+
221+
bool RecordType::getPadded() const { return getImpl()->padded; }
222+
213223
cir::RecordType::RecordKind RecordType::getKind() const {
214224
return getImpl()->kind;
215225
}
@@ -225,17 +235,78 @@ void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) {
225235
//===----------------------------------------------------------------------===//
226236

227237
llvm::TypeSize
228-
RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
229-
::mlir::DataLayoutEntryListRef params) const {
230-
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
231-
return llvm::TypeSize::getFixed(8);
238+
RecordType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
239+
mlir::DataLayoutEntryListRef params) const {
240+
if (isUnion()) {
241+
// TODO(CIR): Implement union layout.
242+
return llvm::TypeSize::getFixed(8);
243+
}
244+
245+
unsigned recordSize = computeStructSize(dataLayout);
246+
return llvm::TypeSize::getFixed(recordSize * 8);
232247
}
233248

234249
uint64_t
235250
RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
236251
::mlir::DataLayoutEntryListRef params) const {
237-
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
238-
return 4;
252+
if (isUnion()) {
253+
// TODO(CIR): Implement union layout.
254+
return 8;
255+
}
256+
257+
// Packed structures always have an ABI alignment of 1.
258+
if (getPacked())
259+
return 1;
260+
return computeStructAlignment(dataLayout);
261+
}
262+
263+
unsigned
264+
RecordType::computeStructSize(const mlir::DataLayout &dataLayout) const {
265+
assert(isComplete() && "Cannot get layout of incomplete records");
266+
267+
// This is a similar algorithm to LLVM's StructLayout.
268+
unsigned recordSize = 0;
269+
uint64_t recordAlignment = 1;
270+
271+
// We can't use a range-based for loop here because we might be ignoring the
272+
// last element.
273+
for (mlir::Type ty : getMembers()) {
274+
// This assumes that we're calculating size based on the ABI alignment, not
275+
// the preferred alignment for each type.
276+
const uint64_t tyAlign =
277+
(getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty));
278+
279+
// Add padding to the struct size to align it to the abi alignment of the
280+
// element type before than adding the size of the element.
281+
recordSize = llvm::alignTo(recordSize, tyAlign);
282+
recordSize += dataLayout.getTypeSize(ty);
283+
284+
// The alignment requirement of a struct is equal to the strictest alignment
285+
// requirement of its elements.
286+
recordAlignment = std::max(tyAlign, recordAlignment);
287+
}
288+
289+
// At the end, add padding to the struct to satisfy its own alignment
290+
// requirement. Otherwise structs inside of arrays would be misaligned.
291+
recordSize = llvm::alignTo(recordSize, recordAlignment);
292+
return recordSize;
293+
}
294+
295+
// We also compute the alignment as part of computeStructSize, but this is more
296+
// efficient. Ideally, we'd like to compute both at once and cache the result,
297+
// but that's implemented yet.
298+
// TODO(CIR): Implement a way to cache the result.
299+
uint64_t
300+
RecordType::computeStructAlignment(const mlir::DataLayout &dataLayout) const {
301+
assert(isComplete() && "Cannot get layout of incomplete records");
302+
303+
// This is a similar algorithm to LLVM's StructLayout.
304+
uint64_t recordAlignment = 1;
305+
for (mlir::Type ty : getMembers())
306+
recordAlignment =
307+
std::max(dataLayout.getTypeABIAlignment(ty), recordAlignment);
308+
309+
return recordAlignment;
239310
}
240311

241312
//===----------------------------------------------------------------------===//

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,12 +1356,11 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
13561356
if (type.getName()) {
13571357
llvmStruct = mlir::LLVM::LLVMStructType::getIdentified(
13581358
type.getContext(), type.getPrefixedName());
1359-
assert(!cir::MissingFeatures::packedRecords());
1360-
if (llvmStruct.setBody(llvmMembers, /*isPacked=*/true).failed())
1359+
if (llvmStruct.setBody(llvmMembers, type.getPacked()).failed())
13611360
llvm_unreachable("Failed to set body of record");
13621361
} else { // Record has no name: lower as literal record.
13631362
llvmStruct = mlir::LLVM::LLVMStructType::getLiteral(
1364-
type.getContext(), llvmMembers, /*isPacked=*/true);
1363+
type.getContext(), llvmMembers, type.getPacked());
13651364
}
13661365

13671366
return llvmStruct;

0 commit comments

Comments
 (0)