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 | // |
9 | // This contains code to compute the layout of a record. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "CIRGenBuilder.h" |
14 | #include "CIRGenModule.h" |
15 | #include "CIRGenTypes.h" |
16 | |
17 | #include "clang/AST/ASTContext.h" |
18 | #include "clang/AST/Decl.h" |
19 | #include "clang/AST/DeclCXX.h" |
20 | #include "clang/AST/RecordLayout.h" |
21 | #include "clang/CIR/Dialect/IR/CIRAttrs.h" |
22 | #include "clang/CIR/Dialect/IR/CIRDataLayout.h" |
23 | #include "llvm/Support/Casting.h" |
24 | |
25 | #include <memory> |
26 | |
27 | using namespace llvm; |
28 | using namespace clang; |
29 | using namespace clang::CIRGen; |
30 | |
31 | namespace { |
32 | /// The CIRRecordLowering is responsible for lowering an ASTRecordLayout to an |
33 | /// mlir::Type. Some of the lowering is straightforward, some is not. |
34 | // TODO: Detail some of the complexities and weirdnesses? |
35 | // (See CGRecordLayoutBuilder.cpp) |
36 | struct CIRRecordLowering final { |
37 | |
38 | // MemberInfo is a helper structure that contains information about a record |
39 | // member. In addition to the standard member types, there exists a sentinel |
40 | // member type that ensures correct rounding. |
41 | struct MemberInfo final { |
42 | CharUnits offset; |
43 | enum class InfoKind { Field, Base } kind; |
44 | mlir::Type data; |
45 | union { |
46 | const FieldDecl *fieldDecl; |
47 | const CXXRecordDecl *cxxRecordDecl; |
48 | }; |
49 | MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data, |
50 | const FieldDecl *fieldDecl = nullptr) |
51 | : offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {} |
52 | MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data, |
53 | const CXXRecordDecl *rd) |
54 | : offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {} |
55 | // MemberInfos are sorted so we define a < operator. |
56 | bool operator<(const MemberInfo &other) const { |
57 | return offset < other.offset; |
58 | } |
59 | }; |
60 | // The constructor. |
61 | CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl, |
62 | bool packed); |
63 | |
64 | /// Constructs a MemberInfo instance from an offset and mlir::Type. |
65 | MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) { |
66 | return MemberInfo(offset, MemberInfo::InfoKind::Field, data); |
67 | } |
68 | |
69 | void lower(); |
70 | void lowerUnion(); |
71 | |
72 | /// Determines if we need a packed llvm struct. |
73 | void determinePacked(); |
74 | /// Inserts padding everywhere it's needed. |
75 | void insertPadding(); |
76 | |
77 | void accumulateBases(const CXXRecordDecl *cxxRecordDecl); |
78 | void accumulateVPtrs(); |
79 | void accumulateFields(); |
80 | |
81 | CharUnits bitsToCharUnits(uint64_t bitOffset) { |
82 | return astContext.toCharUnitsFromBits(BitSize: bitOffset); |
83 | } |
84 | |
85 | void calculateZeroInit(); |
86 | |
87 | CharUnits getSize(mlir::Type Ty) { |
88 | return CharUnits::fromQuantity(dataLayout.layout.getTypeSize(Ty)); |
89 | } |
90 | CharUnits getAlignment(mlir::Type Ty) { |
91 | return CharUnits::fromQuantity(dataLayout.layout.getTypeABIAlignment(Ty)); |
92 | } |
93 | |
94 | bool isZeroInitializable(const FieldDecl *fd) { |
95 | return cirGenTypes.isZeroInitializable(fd->getType()); |
96 | } |
97 | bool isZeroInitializable(const RecordDecl *rd) { |
98 | return cirGenTypes.isZeroInitializable(rd); |
99 | } |
100 | |
101 | /// Wraps cir::IntType with some implicit arguments. |
102 | mlir::Type getUIntNType(uint64_t numBits) { |
103 | unsigned alignedBits = llvm::PowerOf2Ceil(A: numBits); |
104 | alignedBits = std::max(a: 8u, b: alignedBits); |
105 | return cir::IntType::get(&cirGenTypes.getMLIRContext(), alignedBits, |
106 | /*isSigned=*/false); |
107 | } |
108 | |
109 | mlir::Type getCharType() { |
110 | return cir::IntType::get(&cirGenTypes.getMLIRContext(), |
111 | astContext.getCharWidth(), |
112 | /*isSigned=*/false); |
113 | } |
114 | |
115 | mlir::Type getByteArrayType(CharUnits numberOfChars) { |
116 | assert(!numberOfChars.isZero() && "Empty byte arrays aren't allowed." ); |
117 | mlir::Type type = getCharType(); |
118 | return numberOfChars == CharUnits::One() |
119 | ? type |
120 | : cir::ArrayType::get(type, numberOfChars.getQuantity()); |
121 | } |
122 | |
123 | // Gets the CIR BaseSubobject type from a CXXRecordDecl. |
124 | mlir::Type getStorageType(const CXXRecordDecl *RD) { |
125 | return cirGenTypes.getCIRGenRecordLayout(RD).getBaseSubobjectCIRType(); |
126 | } |
127 | |
128 | mlir::Type getStorageType(const FieldDecl *fieldDecl) { |
129 | mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType()); |
130 | if (fieldDecl->isBitField()) { |
131 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
132 | "getStorageType for bitfields" ); |
133 | } |
134 | return type; |
135 | } |
136 | |
137 | uint64_t getFieldBitOffset(const FieldDecl *fieldDecl) { |
138 | return astRecordLayout.getFieldOffset(FieldNo: fieldDecl->getFieldIndex()); |
139 | } |
140 | |
141 | /// Fills out the structures that are ultimately consumed. |
142 | void fillOutputFields(); |
143 | |
144 | void appendPaddingBytes(CharUnits size) { |
145 | if (!size.isZero()) { |
146 | fieldTypes.push_back(getByteArrayType(size)); |
147 | padded = true; |
148 | } |
149 | } |
150 | |
151 | CIRGenTypes &cirGenTypes; |
152 | CIRGenBuilderTy &builder; |
153 | const ASTContext &astContext; |
154 | const RecordDecl *recordDecl; |
155 | const ASTRecordLayout &astRecordLayout; |
156 | // Helpful intermediate data-structures |
157 | std::vector<MemberInfo> members; |
158 | // Output fields, consumed by CIRGenTypes::computeRecordLayout |
159 | llvm::SmallVector<mlir::Type, 16> fieldTypes; |
160 | llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap; |
161 | llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases; |
162 | cir::CIRDataLayout dataLayout; |
163 | |
164 | LLVM_PREFERRED_TYPE(bool) |
165 | unsigned zeroInitializable : 1; |
166 | LLVM_PREFERRED_TYPE(bool) |
167 | unsigned zeroInitializableAsBase : 1; |
168 | LLVM_PREFERRED_TYPE(bool) |
169 | unsigned packed : 1; |
170 | LLVM_PREFERRED_TYPE(bool) |
171 | unsigned padded : 1; |
172 | |
173 | private: |
174 | CIRRecordLowering(const CIRRecordLowering &) = delete; |
175 | void operator=(const CIRRecordLowering &) = delete; |
176 | }; // CIRRecordLowering |
177 | } // namespace |
178 | |
179 | CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes, |
180 | const RecordDecl *recordDecl, bool packed) |
181 | : cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()), |
182 | astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl), |
183 | astRecordLayout( |
184 | cirGenTypes.getASTContext().getASTRecordLayout(D: recordDecl)), |
185 | dataLayout(cirGenTypes.getCGModule().getModule()), |
186 | zeroInitializable(true), zeroInitializableAsBase(true), packed(packed), |
187 | padded(false) {} |
188 | |
189 | void CIRRecordLowering::lower() { |
190 | if (recordDecl->isUnion()) { |
191 | lowerUnion(); |
192 | assert(!cir::MissingFeatures::bitfields()); |
193 | return; |
194 | } |
195 | |
196 | assert(!cir::MissingFeatures::recordLayoutVirtualBases()); |
197 | CharUnits size = astRecordLayout.getSize(); |
198 | |
199 | accumulateFields(); |
200 | |
201 | if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(Val: recordDecl)) { |
202 | accumulateVPtrs(); |
203 | accumulateBases(cxxRecordDecl); |
204 | if (members.empty()) { |
205 | appendPaddingBytes(size); |
206 | assert(!cir::MissingFeatures::bitfields()); |
207 | return; |
208 | } |
209 | assert(!cir::MissingFeatures::recordLayoutVirtualBases()); |
210 | } |
211 | |
212 | llvm::stable_sort(Range&: members); |
213 | // TODO: implement clipTailPadding once bitfields are implemented |
214 | assert(!cir::MissingFeatures::bitfields()); |
215 | assert(!cir::MissingFeatures::recordZeroInit()); |
216 | |
217 | members.push_back(makeStorageInfo(size, getUIntNType(8))); |
218 | determinePacked(); |
219 | insertPadding(); |
220 | members.pop_back(); |
221 | |
222 | calculateZeroInit(); |
223 | fillOutputFields(); |
224 | } |
225 | |
226 | void CIRRecordLowering::fillOutputFields() { |
227 | for (const MemberInfo &member : members) { |
228 | if (member.data) |
229 | fieldTypes.push_back(member.data); |
230 | if (member.kind == MemberInfo::InfoKind::Field) { |
231 | if (member.fieldDecl) |
232 | fieldIdxMap[member.fieldDecl->getCanonicalDecl()] = |
233 | fieldTypes.size() - 1; |
234 | // A field without storage must be a bitfield. |
235 | assert(!cir::MissingFeatures::bitfields()); |
236 | } else if (member.kind == MemberInfo::InfoKind::Base) { |
237 | nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1; |
238 | } |
239 | assert(!cir::MissingFeatures::recordLayoutVirtualBases()); |
240 | } |
241 | } |
242 | |
243 | void CIRRecordLowering::accumulateFields() { |
244 | for (const FieldDecl *field : recordDecl->fields()) { |
245 | if (field->isBitField()) { |
246 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
247 | "accumulate bitfields" ); |
248 | ++field; |
249 | } else if (!field->isZeroSize(Ctx: astContext)) { |
250 | members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(field)), |
251 | MemberInfo::InfoKind::Field, |
252 | getStorageType(field), field)); |
253 | ++field; |
254 | } else { |
255 | // TODO(cir): do we want to do anything special about zero size members? |
256 | assert(!cir::MissingFeatures::zeroSizeRecordMembers()); |
257 | ++field; |
258 | } |
259 | } |
260 | } |
261 | |
262 | void CIRRecordLowering::calculateZeroInit() { |
263 | for (const MemberInfo &member : members) { |
264 | if (member.kind == MemberInfo::InfoKind::Field) { |
265 | if (!member.fieldDecl || isZeroInitializable(fd: member.fieldDecl)) |
266 | continue; |
267 | zeroInitializable = zeroInitializableAsBase = false; |
268 | return; |
269 | } else if (member.kind == MemberInfo::InfoKind::Base) { |
270 | if (isZeroInitializable(member.cxxRecordDecl)) |
271 | continue; |
272 | zeroInitializable = false; |
273 | if (member.kind == MemberInfo::InfoKind::Base) |
274 | zeroInitializableAsBase = false; |
275 | } |
276 | assert(!cir::MissingFeatures::recordLayoutVirtualBases()); |
277 | } |
278 | } |
279 | |
280 | void CIRRecordLowering::determinePacked() { |
281 | if (packed) |
282 | return; |
283 | CharUnits alignment = CharUnits::One(); |
284 | |
285 | // TODO(cir): handle non-virtual base types |
286 | assert(!cir::MissingFeatures::cxxSupport()); |
287 | |
288 | for (const MemberInfo &member : members) { |
289 | if (!member.data) |
290 | continue; |
291 | // If any member falls at an offset that it not a multiple of its alignment, |
292 | // then the entire record must be packed. |
293 | if (member.offset % getAlignment(Ty: member.data)) |
294 | packed = true; |
295 | alignment = std::max(alignment, getAlignment(Ty: member.data)); |
296 | } |
297 | // If the size of the record (the capstone's offset) is not a multiple of the |
298 | // record's alignment, it must be packed. |
299 | if (members.back().offset % alignment) |
300 | packed = true; |
301 | // Update the alignment of the sentinel. |
302 | if (!packed) |
303 | members.back().data = getUIntNType(astContext.toBits(alignment)); |
304 | } |
305 | |
306 | void CIRRecordLowering::insertPadding() { |
307 | std::vector<std::pair<CharUnits, CharUnits>> padding; |
308 | CharUnits size = CharUnits::Zero(); |
309 | for (const MemberInfo &member : members) { |
310 | if (!member.data) |
311 | continue; |
312 | CharUnits offset = member.offset; |
313 | assert(offset >= size); |
314 | // Insert padding if we need to. |
315 | if (offset != |
316 | size.alignTo(Align: packed ? CharUnits::One() : getAlignment(Ty: member.data))) |
317 | padding.push_back(x: std::make_pair(x&: size, y: offset - size)); |
318 | size = offset + getSize(Ty: member.data); |
319 | } |
320 | if (padding.empty()) |
321 | return; |
322 | padded = true; |
323 | // Add the padding to the Members list and sort it. |
324 | for (const std::pair<CharUnits, CharUnits> &paddingPair : padding) |
325 | members.push_back(makeStorageInfo(paddingPair.first, |
326 | getByteArrayType(paddingPair.second))); |
327 | llvm::stable_sort(Range&: members); |
328 | } |
329 | |
330 | std::unique_ptr<CIRGenRecordLayout> |
331 | CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { |
332 | CIRRecordLowering lowering(*this, rd, /*packed=*/false); |
333 | assert(ty->isIncomplete() && "recomputing record layout?" ); |
334 | lowering.lower(); |
335 | |
336 | // If we're in C++, compute the base subobject type. |
337 | cir::RecordType baseTy; |
338 | if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() && |
339 | !rd->hasAttr<FinalAttr>()) { |
340 | baseTy = *ty; |
341 | if (lowering.astRecordLayout.getNonVirtualSize() != |
342 | lowering.astRecordLayout.getSize()) { |
343 | CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed); |
344 | baseLowering.lower(); |
345 | std::string baseIdentifier = getRecordTypeName(rd, suffix: ".base" ); |
346 | baseTy = |
347 | builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier, |
348 | baseLowering.packed, baseLowering.padded); |
349 | // TODO(cir): add something like addRecordTypeName |
350 | |
351 | // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work |
352 | // on both of them with the same index. |
353 | assert(lowering.packed == baseLowering.packed && |
354 | "Non-virtual and complete types must agree on packedness" ); |
355 | } |
356 | } |
357 | |
358 | if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() && |
359 | !rd->hasAttr<FinalAttr>()) { |
360 | if (lowering.astRecordLayout.getNonVirtualSize() != |
361 | lowering.astRecordLayout.getSize()) { |
362 | cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl" ); |
363 | } |
364 | } |
365 | |
366 | // Fill in the record *after* computing the base type. Filling in the body |
367 | // signifies that the type is no longer opaque and record layout is complete, |
368 | // but we may need to recursively layout rd while laying D out as a base type. |
369 | assert(!cir::MissingFeatures::astRecordDeclAttr()); |
370 | ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded); |
371 | |
372 | auto rl = std::make_unique<CIRGenRecordLayout>( |
373 | ty ? *ty : cir::RecordType{}, baseTy ? baseTy : cir::RecordType{}, |
374 | (bool)lowering.zeroInitializable, (bool)lowering.zeroInitializableAsBase); |
375 | |
376 | assert(!cir::MissingFeatures::recordZeroInit()); |
377 | |
378 | rl->nonVirtualBases.swap(lowering.nonVirtualBases); |
379 | |
380 | assert(!cir::MissingFeatures::cxxSupport()); |
381 | assert(!cir::MissingFeatures::bitfields()); |
382 | |
383 | // Add all the field numbers. |
384 | rl->fieldIdxMap.swap(lowering.fieldIdxMap); |
385 | |
386 | // Dump the layout, if requested. |
387 | if (getASTContext().getLangOpts().DumpRecordLayouts) { |
388 | cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: dump layout" ); |
389 | } |
390 | |
391 | // TODO: implement verification |
392 | return rl; |
393 | } |
394 | |
395 | void CIRRecordLowering::lowerUnion() { |
396 | CharUnits layoutSize = astRecordLayout.getSize(); |
397 | mlir::Type storageType = nullptr; |
398 | bool seenNamedMember = false; |
399 | |
400 | // Iterate through the fields setting bitFieldInfo and the Fields array. Also |
401 | // locate the "most appropriate" storage type. |
402 | for (const FieldDecl *field : recordDecl->fields()) { |
403 | mlir::Type fieldType; |
404 | if (field->isBitField()) |
405 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
406 | "bitfields in lowerUnion" ); |
407 | else |
408 | fieldType = getStorageType(field); |
409 | |
410 | // This maps a field to its index. For unions, the index is always 0. |
411 | fieldIdxMap[field->getCanonicalDecl()] = 0; |
412 | |
413 | // Compute zero-initializable status. |
414 | // This union might not be zero initialized: it may contain a pointer to |
415 | // data member which might have some exotic initialization sequence. |
416 | // If this is the case, then we ought not to try and come up with a "better" |
417 | // type, it might not be very easy to come up with a Constant which |
418 | // correctly initializes it. |
419 | if (!seenNamedMember) { |
420 | seenNamedMember = field->getIdentifier(); |
421 | if (!seenNamedMember) |
422 | if (const RecordDecl *fieldRD = field->getType()->getAsRecordDecl()) |
423 | seenNamedMember = fieldRD->findFirstNamedDataMember(); |
424 | if (seenNamedMember && !isZeroInitializable(fd: field)) { |
425 | zeroInitializable = zeroInitializableAsBase = false; |
426 | storageType = fieldType; |
427 | } |
428 | } |
429 | |
430 | // Because our union isn't zero initializable, we won't be getting a better |
431 | // storage type. |
432 | if (!zeroInitializable) |
433 | continue; |
434 | |
435 | // Conditionally update our storage type if we've got a new "better" one. |
436 | if (!storageType || getAlignment(Ty: fieldType) > getAlignment(Ty: storageType) || |
437 | (getAlignment(Ty: fieldType) == getAlignment(Ty: storageType) && |
438 | getSize(Ty: fieldType) > getSize(Ty: storageType))) |
439 | storageType = fieldType; |
440 | |
441 | // NOTE(cir): Track all union member's types, not just the largest one. It |
442 | // allows for proper type-checking and retain more info for analisys. |
443 | fieldTypes.push_back(fieldType); |
444 | } |
445 | |
446 | if (!storageType) |
447 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
448 | "No-storage Union NYI" ); |
449 | |
450 | if (layoutSize < getSize(Ty: storageType)) |
451 | storageType = getByteArrayType(layoutSize); |
452 | else |
453 | appendPaddingBytes(size: layoutSize - getSize(Ty: storageType)); |
454 | |
455 | // Set packed if we need it. |
456 | if (layoutSize % getAlignment(Ty: storageType)) |
457 | packed = true; |
458 | } |
459 | |
460 | void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) { |
461 | // If we've got a primary virtual base, we need to add it with the bases. |
462 | if (astRecordLayout.isPrimaryBaseVirtual()) { |
463 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
464 | "accumulateBases: primary virtual base" ); |
465 | } |
466 | |
467 | // Accumulate the non-virtual bases. |
468 | for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) { |
469 | if (base.isVirtual()) { |
470 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
471 | "accumulateBases: virtual base" ); |
472 | continue; |
473 | } |
474 | // Bases can be zero-sized even if not technically empty if they |
475 | // contain only a trailing array member. |
476 | const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl(); |
477 | if (!baseDecl->isEmpty() && |
478 | !astContext.getASTRecordLayout(baseDecl).getNonVirtualSize().isZero()) { |
479 | members.push_back(MemberInfo(astRecordLayout.getBaseClassOffset(baseDecl), |
480 | MemberInfo::InfoKind::Base, |
481 | getStorageType(baseDecl), baseDecl)); |
482 | } |
483 | } |
484 | } |
485 | |
486 | void CIRRecordLowering::accumulateVPtrs() { |
487 | if (astRecordLayout.hasOwnVFPtr()) |
488 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
489 | "accumulateVPtrs: hasOwnVFPtr" ); |
490 | if (astRecordLayout.hasOwnVBPtr()) |
491 | cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), |
492 | "accumulateVPtrs: hasOwnVBPtr" ); |
493 | } |
494 | |