clang 24.0.0git
LoweringHelpers.cpp
Go to the documentation of this file.
1//====- LoweringHelpers.cpp - Lowering helper functions -------------------===//
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 file contains helper functions for lowering from CIR to LLVM or MLIR.
10//
11//===----------------------------------------------------------------------===//
12
14#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
15#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
16#include "mlir/IR/BuiltinTypes.h"
17#include "mlir/IR/SymbolTable.h"
18#include "mlir/Interfaces/DataLayoutInterfaces.h"
20
21namespace {
22/// A _BitInt(N) whose padded storage integer iM has a larger alloc size than
23/// its M/8 store size is laid out by clang as a byte array, not a plain
24/// integer (e.g. _BitInt(129) -> i192 with alloc size 32 != store size 24).
25/// That "split" storage form is not yet implemented; lowerings must detect
26/// it and report errorNYI rather than emit the wrong-sized integer.
27bool isSplitStorageBitInt(cir::IntType ty, const mlir::DataLayout &dataLayout) {
28 if (!ty.isBitInt())
29 return false;
30 unsigned storageBits = ty.getStorageTypeWidth(dataLayout);
31 auto storageTy = mlir::IntegerType::get(ty.getContext(), storageBits);
32 uint64_t storeSize = storageBits / 8;
33 uint64_t allocSize =
34 llvm::alignTo(storeSize, dataLayout.getTypeABIAlignment(storageTy));
35 return allocSize != storeSize;
36}
37} // namespace
38
39mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter,
40 mlir::DataLayout const &dataLayout,
41 mlir::Type type) {
42 // TODO(cir): Handle other types similarly to clang's codegen
43 // convertTypeForMemory
44 if (mlir::isa<cir::BoolType>(type)) {
45 return mlir::IntegerType::get(type.getContext(),
46 dataLayout.getTypeSizeInBits(type));
47 }
48
49 if (auto vecTy = mlir::dyn_cast<cir::VectorType>(type)) {
50 if (mlir::isa<cir::BoolType>(vecTy.getElementType())) {
52 // Pad to at least one byte.
53 uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8);
54 return mlir::IntegerType::get(type.getContext(), bytePadded);
55 }
56 }
57
58 // _BitInt(N) keeps its literal width as a value but is stored in a padded
59 // integer iM in memory, the same way bool is i1 as a value and i8 in
60 // memory. The byte-array storage form for wide split widths is not
61 // implemented; a null return signals that, and op lowerings turn it into
62 // errorNYI.
63 if (auto intTy = mlir::dyn_cast<cir::IntType>(type);
64 intTy && intTy.isBitInt()) {
65 if (isSplitStorageBitInt(intTy, dataLayout))
66 return {};
67 return mlir::IntegerType::get(type.getContext(),
68 intTy.getStorageTypeWidth(dataLayout));
69 }
70
71 return converter.convertType(type);
72}
73
74static unsigned getIntOrBoolBitWidth(mlir::Type ty) {
75 if (auto intTy = mlir::dyn_cast<cir::IntType>(ty))
76 return intTy.getWidth();
77 assert(mlir::isa<cir::BoolType>(ty) &&
78 "expected CIR integer or bool element type");
79 return 1;
80}
81
82mlir::DenseElementsAttr
83convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr,
84 mlir::Type type) {
85 const auto stringAttr = mlir::cast<mlir::StringAttr>(attr.getElts());
86 const auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());
87 const unsigned totalSize = arrayTy.getSize();
88 const unsigned trailingZeros = attr.getTrailingZerosNum();
89 assert(stringAttr.size() + trailingZeros == totalSize &&
90 "string const_array size must match explicit elements plus "
91 "trailing_zeros");
92
93 const unsigned bitWidth = getIntOrBoolBitWidth(arrayTy.getElementType());
95 values.reserve(totalSize);
96
97 // String bytes are raw values; interpret each as an unsigned byte so a
98 // high-bit char (>= 0x80) does not sign-extend to a value that overflows
99 // the element bit width when constructing the APInt.
100 for (const char element : stringAttr)
101 values.emplace_back(bitWidth, static_cast<unsigned char>(element));
102
103 values.insert(values.end(), trailingZeros, mlir::APInt::getZero(bitWidth));
104
105 return mlir::DenseElementsAttr::get(
106 mlir::RankedTensorType::get({totalSize}, type), llvm::ArrayRef(values));
107}
108
109template <> mlir::APInt getZeroInitFromType(mlir::Type ty) {
110 if (mlir::isa<cir::BoolType>(ty))
111 return mlir::APInt::getZero(1);
112 const auto intTy = mlir::cast<cir::IntType>(ty);
113 return mlir::APInt::getZero(intTy.getWidth());
114}
115
116template <> mlir::APFloat getZeroInitFromType(mlir::Type ty) {
117 auto fpTy = mlir::cast<cir::FPTypeInterface>(ty);
118 return mlir::APFloat::getZero(fpTy.getFloatSemantics());
119}
120
121/// \param attr the ConstArrayAttr to convert
122/// \param values the output parameter, the values array to fill
123/// \param currentDims the shpae of tensor we're going to convert to
124/// \param dimIndex the current dimension we're processing
125/// \param currentIndex the current index in the values array
126template <typename AttrTy, typename StorageTy>
128 cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values,
129 const llvm::SmallVectorImpl<int64_t> &currentDims, int64_t dimIndex,
130 int64_t currentIndex) {
131 if (auto stringAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
132 if (auto arrayType = mlir::dyn_cast<cir::ArrayType>(attr.getType())) {
133 for (auto element : stringAttr) {
134 auto intAttr = cir::IntAttr::get(arrayType.getElementType(), element);
135 values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue();
136 }
137 // Remaining slots are trailing zeros; values was zero-initialized.
138 currentIndex += attr.getTrailingZerosNum();
139 return true;
140 }
141 }
142
143 dimIndex++;
144 std::size_t elementsSizeInCurrentDim = 1;
145 for (std::size_t i = dimIndex; i < currentDims.size(); i++)
146 elementsSizeInCurrentDim *= currentDims[i];
147
148 auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts());
149 for (auto eltAttr : arrayAttr) {
150 if constexpr (std::is_same_v<StorageTy, mlir::APInt>) {
151 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(eltAttr)) {
152 values[currentIndex++] =
153 llvm::APInt(1, static_cast<uint64_t>(boolAttr.getValue()));
154 continue;
155 }
156 }
157 if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) {
158 values[currentIndex++] = valueAttr.getValue();
159 continue;
160 }
161
162 if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) {
163 convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims,
164 dimIndex, currentIndex);
165 currentIndex += elementsSizeInCurrentDim;
166 continue;
167 }
168
169 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(eltAttr)) {
170 currentIndex += elementsSizeInCurrentDim;
171 continue;
172 }
173
174 // A global view can be the result of pointer conversions, so we can't
175 // represent them as an APInt/APFloat. So give up if we see one.
176 if (auto global = mlir::dyn_cast<cir::GlobalViewAttr>(eltAttr))
177 return false;
178
179 llvm_unreachable("unknown element in ConstArrayAttr");
180 }
181 return true;
182}
183
184template <typename AttrTy, typename StorageTy>
185std::optional<mlir::DenseElementsAttr> convertToDenseElementsAttr(
186 cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims,
187 mlir::Type elementType, mlir::Type convertedElementType) {
188 unsigned vectorSize = 1;
189 for (auto dim : dims)
190 vectorSize *= dim;
192 vectorSize, getZeroInitFromType<StorageTy>(elementType));
193
194 if (!convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims,
195 /*currentDim=*/0,
196 /*initialIndex=*/0))
197 return std::nullopt;
198
199 return mlir::DenseElementsAttr::get(
200 mlir::RankedTensorType::get(dims, convertedElementType),
201 llvm::ArrayRef(values));
202}
203
204/// Return true when \p gv can be lowered to a \c FlatSymbolRefAttr leaf without
205/// addrspacecast or bitcast (mirrors \c CIRAttrToValue::visitCirAttr).
206static bool
207globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, mlir::ModuleOp moduleOp,
208 mlir::SymbolTableCollection &symbolTables,
209 const mlir::TypeConverter *converter) {
210 if (gv.getIndices() || mlir::isa<cir::IntType, cir::VPtrType>(gv.getType()))
211 return false;
212
213 auto ptrTy = mlir::dyn_cast<cir::PointerType>(gv.getType());
214 if (!ptrTy)
215 return false;
216
217 unsigned sourceAddrSpace = 0;
218 mlir::Type sourceType;
219 auto sourceSymbol = symbolTables.lookupSymbolIn(moduleOp, gv.getSymbol());
220 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
221 sourceType = llvmSymbol.getType();
222 sourceAddrSpace = llvmSymbol.getAddrSpace();
223 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(sourceSymbol)) {
224 sourceType = converter->convertType(cirSymbol.getSymType());
225 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
226 cirSymbol.getAddrSpaceAttr()))
227 sourceAddrSpace = targetAS.getValue();
228 } else {
229 // cir.func and other symbols not yet lowered to globals cannot be used as
230 // bulk constant leaves; those cases keep the insertvalue fallback.
231 return false;
232 }
233
234 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
235 if (llvmDstTy.getAddressSpace() != sourceAddrSpace)
236 return false;
237
238 mlir::Type llvmEltTy = converter->convertType(ptrTy.getPointee());
239 if (llvmEltTy == sourceType)
240 return true;
241 if (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(sourceType))
242 return llvmEltTy == arrTy.getElementType();
243 return false;
244}
245
246/// Lower a single pointer-element of a \c cir.const_array to an LLVM-dialect
247/// constant leaf suitable for a bulk \c llvm.mlir.constant. Only handles
248/// address-of-global without indices and null pointers; indexed global views
249/// must use the per-element \c llvm.insertvalue fallback.
250static std::optional<mlir::Attribute>
251lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx,
252 mlir::ModuleOp moduleOp,
253 mlir::SymbolTableCollection &symbolTables,
254 const mlir::TypeConverter *converter) {
255 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(elt)) {
256 if (!moduleOp ||
257 !globalViewMatchesPointerLeaf(gv, moduleOp, symbolTables, converter))
258 return std::nullopt;
259 return gv.getSymbol();
260 }
261 if (auto nullPtr = mlir::dyn_cast<cir::ConstPtrAttr>(elt)) {
262 if (nullPtr.isNullValue())
263 return mlir::LLVM::ZeroAttr::get(ctx);
264 return std::nullopt;
265 }
266 return std::nullopt;
267}
268
269static bool containsPoison(mlir::Attribute attr) {
270 if (mlir::isa<cir::PoisonAttr>(attr))
271 return true;
272 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
273 return llvm::any_of(elts, containsPoison);
274 if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
275 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
276 return false;
277 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
278 return llvm::any_of(elts, containsPoison);
279 }
280 return false;
281}
282
283/// Block-address attributes (address-of-label and label differences) are
284/// lowered to relocation expressions that cannot be materialized as part of a
285/// dense/aggregate constant attribute; they require the per-element
286/// insertvalue region lowering. Return true if \p attr contains any such
287/// element.
288static bool containsBlockAddress(mlir::Attribute attr) {
289 if (mlir::isa<cir::BlockAddrInfoAttr, cir::BlockAddrDiffAttr>(attr))
290 return true;
291 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
292 return llvm::any_of(elts, containsBlockAddress);
293 if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
294 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
295 return false;
296 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
297 return llvm::any_of(elts, containsBlockAddress);
298 }
299 return false;
300}
301
302static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
303 mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
304 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp);
305
306std::optional<mlir::Attribute> lowerConstArrayAttr(
307 cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables,
308 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
309 // Ensure ConstArrayAttr has a type.
310 const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr);
311
312 // Ensure ConstArrayAttr type is a ArrayType.
313 const auto cirArrayType = mlir::cast<cir::ArrayType>(typedConstArr.getType());
314
315 // Is a ConstArrayAttr with an cir::ArrayType: fetch element type.
316 mlir::Type type = cirArrayType;
317 auto dims = llvm::SmallVector<int64_t, 2>{};
318 while (auto arrayType = mlir::dyn_cast<cir::ArrayType>(type)) {
319 dims.push_back(arrayType.getSize());
320 type = arrayType.getElementType();
321 }
322
323 if (containsPoison(constArr))
324 return std::nullopt;
325
326 // Block-address initializers cannot be represented as a dense/aggregate
327 // constant attribute; fall back to the per-element insertvalue lowering.
328 if (containsBlockAddress(constArr))
329 return std::nullopt;
330
331 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
333 converter->convertType(type));
334 if (mlir::isa<cir::IntType>(type)) {
335 // A _BitInt array element is stored in a padded integer in memory; the
336 // dense-attribute path here cannot express that widening, so fall back to
337 // the insertvalue region, which widens each element via emitToMemory.
338 if (mlir::cast<cir::IntType>(type).isBitInt())
339 return std::nullopt;
341 constArr, dims, type, converter->convertType(type));
342 }
343
344 if (mlir::isa<cir::BoolType>(type))
346 constArr, dims, type, converter->convertType(type));
347
348 if (mlir::isa<cir::FPTypeInterface>(type))
350 constArr, dims, type, converter->convertType(type));
351
352 if (mlir::isa<cir::PointerType>(type)) {
353 // FIXME: Pointer arrays with trailing_zeros (null-sentinel tables) fall
354 // through to the insertvalue path for now.
355 if (constArr.getTrailingZerosNum() > 0)
356 return std::nullopt;
357 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
358 if (!eltsArr)
359 return std::nullopt;
361 lowered.reserve(eltsArr.size());
362 mlir::MLIRContext *ctx = constArr.getContext();
363 for (mlir::Attribute elt : eltsArr) {
364 std::optional<mlir::Attribute> llvmElt =
365 lowerPointerElementAttr(elt, ctx, moduleOp, symbolTables, converter);
366 if (!llvmElt)
367 return std::nullopt;
368 lowered.push_back(*llvmElt);
369 }
370 return mlir::ArrayAttr::get(ctx, lowered);
371 }
372
373 if (mlir::isa<cir::RecordType>(type)) {
374 // A record type is just an array of the element values.
375 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
376 if (!eltsArr)
377 return std::nullopt;
378
380 loweredElts.reserve(cirArrayType.getSize());
381
382 for (mlir::Attribute elt : eltsArr) {
383 std::optional<mlir::Attribute> llvmElt =
384 lowerConstRecordMemberAttr(elt, symbolTables, converter, moduleOp);
385 if (!llvmElt)
386 return std::nullopt;
387 loweredElts.push_back(*llvmElt);
388 }
389
390 // Remaining elts are either going to be padding (and should be undef), or
391 // trailing-zeros. We can't really tell the difference as CIR lowering
392 // doesn't differentiate anyway, so just zero-fill them.
393 while (loweredElts.size() < cirArrayType.getSize())
394 loweredElts.push_back(mlir::LLVM::ZeroAttr::get(constArr.getContext()));
395 return mlir::ArrayAttr::get(constArr.getContext(), loweredElts);
396 }
397
398 return std::nullopt;
399}
400
401/// Lower a constant attribute that initializes a single member of a record (or
402/// a leaf of a nested aggregate) to an LLVM-dialect attribute that can be
403/// attached directly to an \c llvm.mlir.global, avoiding an insertvalue
404/// initializer region. Returns \c std::nullopt when the attribute cannot be
405/// represented as a single constant attribute (e.g. an indexed
406/// \c GlobalViewAttr), in which case the caller falls back to the region-based
407/// lowering.
408static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
409 mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
410 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
411 mlir::MLIRContext *ctx = attr.getContext();
412
413 if (auto arrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(attr))
414 return lowerConstArrayAttr(arrayAttr, symbolTables, converter, moduleOp);
415
416 if (auto recordAttr = mlir::dyn_cast<cir::ConstRecordAttr>(attr))
417 return lowerConstRecordAttr(recordAttr, symbolTables, converter, moduleOp);
418
419 if (mlir::isa<cir::ZeroAttr>(attr))
420 return mlir::LLVM::ZeroAttr::get(ctx);
421
422 if (mlir::isa<cir::UndefAttr>(attr))
423 return mlir::LLVM::UndefAttr::get(ctx);
424
425 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(attr)) {
426 // A _BitInt member is stored in a padded integer in memory; defer to the
427 // insertvalue region (CIRAttrToValue), which performs the extension.
428 if (mlir::cast<cir::IntType>(intAttr.getType()).isBitInt())
429 return std::nullopt;
430 return mlir::IntegerAttr::get(converter->convertType(intAttr.getType()),
431 intAttr.getValue());
432 }
433
434 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr))
435 return mlir::IntegerAttr::get(converter->convertType(boolAttr.getType()),
436 boolAttr.getValue() ? 1 : 0);
437
438 if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr))
439 return mlir::FloatAttr::get(converter->convertType(fpAttr.getType()),
440 fpAttr.getValue());
441
442 // Null pointers and simple address-of-global references can be represented
443 // as constant attributes; anything more complex uses the region fallback.
444 return lowerPointerElementAttr(attr, ctx, moduleOp, symbolTables, converter);
445}
446
447// Figure out if we want mark the new struct 'packed' if it isn't already. IF
448// it is already, we have to keep that behavior. We pack it with logic similar
449// to classic codegen, though will end up missing cases, since we don't want to
450// change the type other than the FAM.
451// We can do so if:
452// 1- Packing it won't change any of the field offsets.
453// 2- the non-padded struct would add padding beyond the
454// flexible array member. We don't pack if the flexible array member manages
455// to not cause trailing padding.
456static bool shouldPackFAMStruct(const mlir::DataLayout &dataLayout,
458 uint64_t maxAlign = 1;
459 uint64_t totalSize = 0;
460 for (mlir::Type member : members) {
461 uint64_t align = dataLayout.getTypeABIAlignment(member);
462 maxAlign = std::max(maxAlign, align);
463 uint64_t size = dataLayout.getTypeSize(member).getFixedValue();
464
465 if (llvm::alignTo(totalSize, align) != totalSize)
466 return false;
467
468 totalSize += size;
469 }
470 return llvm::alignTo(totalSize, maxAlign) != totalSize;
471}
472
473// CIR supports flexible-array-members in its struct types. That is, a
474// zero-length array as the last element, which can be initialized with an
475// arbitrary number of elements. A ConstRecordAttr can be created with one of
476// these, and our verifier allows it. However, the LLVM implementation does NOT
477// permit this. So we have to replace this type in LLVM with special struct for
478// this value.
479//
480// Additionally, the struct itself could contain a struct with a FAM or a union
481// that needed adjustment, so it recurses to check those. If no such type has
482// been found/no adjustment needed, this returns the type unchanged.
483//
484// Additionally, a union having an active member of not-the-largest
485// alignment can cause the need for a small padding array. We also capture the
486// original indices of the fields that had this padding prepended, so the
487// lowerConstRecordAttr can later put in a 'zero' init there.
489 mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord,
490 const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout,
491 llvm::SmallVectorImpl<unsigned> &paddingAddedIndexes) {
492 assert(paddingAddedIndexes.empty() &&
493 "Not for accumulation, just single depth");
495 constRecord.getMembers().getValue();
496 llvm::SmallVector<mlir::Type> origBody{structTy.getBody()};
498 bool packed = structTy.isPacked();
499 uint64_t curOffset = 0;
500 bool changed = false;
501
502 // Recursively adjust each member. A member that is itself a union (or a
503 // struct containing one) lowers to a type that differs from its declared
504 // field type, and this struct has to adopt that adjusted type so the
505 // enclosing insertvalue chain type-checks.
506 for (auto [idx, member] : llvm::enumerate(initMembers)) {
507 if (idx >= origBody.size())
508 break;
509 mlir::Type adjusted =
510 adjustGlobalTypeForInit(origBody[idx], member, converter, dataLayout);
511 unsigned adjustedAlign = dataLayout.getTypeABIAlignment(adjusted);
512
513 if (adjusted != origBody[idx]) {
514 // We're always going to 'change' the layout if it has changed, but we
515 // need to see if there is new 'padding' that won't happen automatically
516 // here based on alignment.
517 unsigned origAlign = dataLayout.getTypeABIAlignment(origBody[idx]);
518
519 uint64_t origOffset =
520 packed ? curOffset : llvm::alignTo(curOffset, origAlign);
521 uint64_t adjustedOffset =
522 packed ? curOffset : llvm::alignTo(curOffset, adjustedAlign);
523
524 if (adjustedOffset != origOffset) {
525 // If the offset would change, we have to insert padding to make up for
526 // it. This should only happen since alignment will decrease with
527 // unions, so we should be able to assume adjusted-offset < origOffset?
528 assert(adjustedOffset < origOffset);
529 // Rather than just pad the difference between the offsets, we have to
530 // fill in since the end of the last field, else we leave room thanks to
531 // alignment between this field and the padding.
532 uint64_t difference = origOffset - curOffset;
533 newBody.push_back(mlir::LLVM::LLVMArrayType::get(
534 mlir::IntegerType::get(structTy.getContext(), 8), difference));
535 paddingAddedIndexes.push_back(idx);
536 curOffset = origOffset;
537 }
538 changed = true;
539 }
540 newBody.push_back(adjusted);
541
542 if (!packed)
543 curOffset = llvm::alignTo(curOffset, adjustedAlign);
544 curOffset += dataLayout.getTypeSize(adjusted).getFixedValue();
545 }
546
547 // CIR supports flexible-array-members in its struct types. That is, a
548 // zero-length array as the last element, which can be initialized with an
549 // arbitrary number of elements. A ConstRecordAttr can be created with one of
550 // these, and our verifier allows it. However, the LLVM implementation does
551 // NOT permit this, so we widen that trailing member to the initializer's
552 // array type (packing the struct if that changes the layout).
553 bool widenedFAM = false;
554 if (auto fam =
555 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(structTy.getBody().back());
556 fam && fam.getNumElements() == 0) {
557 mlir::Type lastInitType =
558 mlir::cast<mlir::TypedAttr>(initMembers.back()).getType();
559 if (mlir::cast<cir::ArrayType>(lastInitType).getSize() != 0) {
560 newBody.back() =
561 adjustGlobalTypeForInit(converter.convertType(lastInitType),
562 initMembers.back(), converter, dataLayout);
563 packed = packed || shouldPackFAMStruct(dataLayout, newBody);
564 widenedFAM = true;
565 changed = true;
566 }
567 }
568
569 if (!changed)
570 return structTy;
571
572 // We've likely reduced the alignment, so make sure we put padding 'behind'
573 // it. We can skip this in the FAM case, since a Flexible array member is not
574 // allowed to be initialized unless it is the 'last' element. So it doesn't
575 // need to be padded out.
576 if (!widenedFAM) {
577 uint64_t declaredSize = dataLayout.getTypeSize(structTy).getFixedValue();
578 assert(curOffset <= declaredSize && "body bigger than type?");
579 if (curOffset < declaredSize)
580 newBody.push_back(mlir::LLVM::LLVMArrayType::get(
581 mlir::IntegerType::get(structTy.getContext(), 8),
582 declaredSize - curOffset));
583 }
584
585 return mlir::LLVM::LLVMStructType::getLiteral(structTy.getContext(), newBody,
586 packed);
587}
588
589// A union constant only initializes its active member, but a union is lowered
590// to its most-aligned member (its 'storage' type), which need not be the
591// initialized one -- e.g. `union { char buf[16]; long cap; }` stores as `long`
592// (higher alignment) but may be initialized through `buf`. So the storage type
593// (and thus structTy) generally can't hold the active member's value. Rebuild
594// an anonymous struct `{ <active member>, [pad x i8] }` that holds the active
595// member followed by enough byte padding to span the union's full allocated
596// size, mirroring classic codegen.
598 mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord,
599 const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout) {
600
601 auto unionTy = mlir::cast<cir::UnionType>(constRecord.getType());
602
603 // Unions can only initialize one field, so this has to be sizeof-one.
604 assert(constRecord.getMembers().size() == 1);
605 mlir::Attribute member = constRecord.getMembers()[0];
606 mlir::Type memberTy = convertTypeForMemory(
607 converter, dataLayout, mlir::cast<mlir::TypedAttr>(member).getType());
608
609 // The active member may itself need adjusting (e.g. it is a nested union, or
610 // a struct containing one), so recurse before using its type below.
611 memberTy = adjustGlobalTypeForInit(memberTy, member, converter, dataLayout);
612
613 // The converted union type is { storage, [padding] }, where storage is the
614 // union's most-aligned member. When the active member IS that storage type,
615 // the converted type already describes this initializer exactly (the pad we
616 // would compute equals the union's declared pad), so there is nothing to do.
617 if (memberTy == structTy.getBody().front())
618 return structTy;
619
620 uint64_t unionSize = dataLayout.getTypeSize(unionTy).getFixedValue();
621 uint64_t initSize = dataLayout.getTypeSize(memberTy).getFixedValue();
622 assert(initSize <= unionSize && "union initializer larger than the union");
623
625 newBody.push_back(memberTy);
626
627 // Fill the rest of the union's allocated size with byte padding.
628 if (initSize < unionSize)
629 newBody.push_back(mlir::LLVM::LLVMArrayType::get(
630 mlir::IntegerType::get(structTy.getContext(), 8),
631 unionSize - initSize));
632
633 return mlir::LLVM::LLVMStructType::getLiteral(structTy.getContext(), newBody,
634 unionTy.getPacked());
635}
636
637// Unions in an array can cause individual elements to be of different types.
638// This function adjusts the array type and replaces it with a struct type if
639// necessary, OR leaves it as a 'new' array type if necessary.
641 mlir::LLVM::LLVMArrayType arrayTy, cir::ConstArrayAttr arrayInit,
642 const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout) {
643 auto elts = mlir::dyn_cast<mlir::ArrayAttr>(arrayInit.getElts());
644
645 if (!elts)
646 return arrayTy;
647
648 mlir::Type origEltTy = arrayTy.getElementType();
649 llvm::SmallVector<mlir::Type> adjustedElts(arrayTy.getNumElements(),
650 origEltTy);
651 bool changed = false;
652
653 for (auto [idx, elt] : llvm::enumerate(elts)) {
654 mlir::Type adjusted =
655 adjustGlobalTypeForInit(origEltTy, elt, converter, dataLayout);
656
657 if (idx >= arrayTy.getNumElements()) {
658 adjustedElts.push_back(adjusted);
659 changed = true;
660 } else if (adjusted != origEltTy) {
661 adjustedElts[idx] = adjusted;
662 changed = true;
663 }
664 }
665
666 if (!changed)
667 return arrayTy;
668
669 if (llvm::all_equal(adjustedElts))
670 return mlir::LLVM::LLVMArrayType::get(adjustedElts.front(),
671 adjustedElts.size());
672
673 // Packed, because arrays shouldn't be subject to padding.
674 return mlir::LLVM::LLVMStructType::getLiteral(
675 arrayTy.getContext(), adjustedElts, /*isPacked=*/true);
676}
677
678// Apply various adjustments required for struct/union types.
679mlir::Type
680adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init,
681 const mlir::TypeConverter &converter,
682 const mlir::DataLayout &dataLayout,
683 llvm::SmallVectorImpl<unsigned> &paddingAddedIndexes) {
684 if (auto arrayInit = mlir::dyn_cast_if_present<cir::ConstArrayAttr>(init)) {
685 auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(llvmType);
686 if (!arrayTy)
687 return llvmType;
688 return adjustGlobalArrayTypeForInit(arrayTy, arrayInit, converter,
689 dataLayout);
690 }
691
692 // Conversions for both only happen if we have a record init.
693 auto constRecord = mlir::dyn_cast_if_present<cir::ConstRecordAttr>(init);
694 if (!constRecord)
695 return llvmType;
696
697 // If this isn't of struct-type, or doesn't have any members, there is nothing
698 // to do.
699 auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(llvmType);
700 if (!structTy || structTy.getBody().empty())
701 return llvmType;
702
703 // Structs can have a flexible array member, adjust that.
704 if (mlir::isa<cir::StructType>(constRecord.getType()))
705 return adjustGlobalStructTypeForInit(structTy, constRecord, converter,
706 dataLayout, paddingAddedIndexes);
707 if (mlir::isa<cir::UnionType>(constRecord.getType()))
708 return adjustGlobalUnionTypeForInit(structTy, constRecord, converter,
709 dataLayout);
710 return llvmType;
711}
712
713mlir::Type adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init,
714 const mlir::TypeConverter &converter,
715 const mlir::DataLayout &dataLayout) {
716 llvm::SmallVector<unsigned> ignoredAddedIndexes;
717 return adjustGlobalTypeForInit(llvmType, init, converter, dataLayout,
718 ignoredAddedIndexes);
719}
720
721std::optional<mlir::Attribute> lowerConstRecordAttr(
722 cir::ConstRecordAttr constRecord, mlir::SymbolTableCollection &symbolTables,
723 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
724
725 // Build one constant attribute per record member. The LLVM dialect global
726 // translation accepts an ArrayAttr (one element per struct field) and emits
727 // an llvm::ConstantStruct, so the whole initializer can be a single
728 // attribute on the global instead of an insertvalue region.
729 mlir::ArrayAttr memberAttrs = constRecord.getMembers();
731 loweredMembers.reserve(memberAttrs.size());
732 for (mlir::Attribute member : memberAttrs) {
733 std::optional<mlir::Attribute> lowered =
734 lowerConstRecordMemberAttr(member, symbolTables, converter, moduleOp);
735 if (!lowered)
736 return std::nullopt;
737 loweredMembers.push_back(*lowered);
738 }
739
740 // The lowered LLVM type may have more fields than the CIR record has members
741 // for a few reasons:
742 // 1- a union lowers to { active-member, [pad x i8]).
743 // 2- A struct that contains such a union can have its alignment changed too,
744 // so it needs tail padding to fill that in.
745 // 3- A struct containing a union whose initializer doesn't use the
746 // highest-aligned field will have to prepend a bit of padding, such as struct
747 // { i32, union { i64, i32 } }. Typically the union gets lowered to a struct
748 // { i64 } (as i64 has the greatest alignment), but if the init causes it to
749 // be the i32(or any such smaller field) we have to prepend it with padding:
750 // struct { i32, [4 x i8], struct { i32 }}
751 // instead of (with no init):
752 // struct { i32, struct { i64 }}
753 llvm::SmallVector<unsigned> paddingAddedIndexes;
754 mlir::Type adjustedTy = adjustGlobalTypeForInit(
755 converter->convertType(constRecord.getType()), constRecord, *converter,
756 mlir::DataLayout(moduleOp), paddingAddedIndexes);
757
758 // This handles #3 from above. adjustGlobalTypeForInit ensures the
759 // indexes are in increasing order, so we can insert 'backwards' without
760 // causing problems.
761 for (unsigned paddedElt : llvm::reverse(paddingAddedIndexes))
762 loweredMembers.insert(loweredMembers.begin() + paddedElt,
763 mlir::LLVM::ZeroAttr::get(constRecord.getContext()));
764
765 // Any remaining difference will be the union/struct padding case. We don't
766 // have a great handle/way to tell when to zero-vs-undef init, so always
767 // zero init, as it is always safe to do so.
768 if (auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(adjustedTy))
769 while (loweredMembers.size() < structTy.getBody().size())
770 loweredMembers.push_back(
771 mlir::LLVM::ZeroAttr::get(constRecord.getContext()));
772
773 return mlir::ArrayAttr::get(constRecord.getContext(), loweredMembers);
774}
775
776mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc,
777 mlir::Type typ, const llvm::APInt &val) {
778 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
779}
780
781mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ,
782 unsigned val) {
783 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
784}
785
786mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
787 if (!rhs)
788 return lhs;
789 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
790 return mlir::LLVM::ShlOp::create(bld, lhs.getLoc(), lhs, rhsVal);
791}
792
793mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
794 if (!rhs)
795 return lhs;
796 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
797 return mlir::LLVM::AShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
798}
799
800mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs,
801 const llvm::APInt &rhs) {
802 mlir::Value rhsVal = getConstAPInt(bld, lhs.getLoc(), lhs.getType(), rhs);
803 return mlir::LLVM::AndOp::create(bld, lhs.getLoc(), lhs, rhsVal);
804}
805
806mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
807 if (!rhs)
808 return lhs;
809 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
810 return mlir::LLVM::LShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
811}
TokenType getType() const
Returns the token's type, e.g.
mlir::DenseElementsAttr convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, mlir::Type type)
mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter, mlir::DataLayout const &dataLayout, mlir::Type type)
static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, mlir::ModuleOp moduleOp, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter)
Return true when gv can be lowered to a FlatSymbolRefAttr leaf without addrspacecast or bitcast (mirr...
mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ, unsigned val)
static mlir::Type adjustGlobalStructTypeForInit(mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout, llvm::SmallVectorImpl< unsigned > &paddingAddedIndexes)
mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ, const llvm::APInt &val)
std::optional< mlir::DenseElementsAttr > convertToDenseElementsAttr(cir::ConstArrayAttr attr, const llvm::SmallVectorImpl< int64_t > &dims, mlir::Type elementType, mlir::Type convertedElementType)
std::optional< mlir::Attribute > lowerConstArrayAttr(cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp)
mlir::Type adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout, llvm::SmallVectorImpl< unsigned > &paddingAddedIndexes)
static mlir::Type adjustGlobalArrayTypeForInit(mlir::LLVM::LLVMArrayType arrayTy, cir::ConstArrayAttr arrayInit, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout)
static bool containsPoison(mlir::Attribute attr)
static std::optional< mlir::Attribute > lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx, mlir::ModuleOp moduleOp, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter)
Lower a single pointer-element of a cir.const_array to an LLVM-dialect constant leaf suitable for a b...
mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
std::optional< mlir::Attribute > lowerConstRecordAttr(cir::ConstRecordAttr constRecord, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp)
static std::optional< mlir::Attribute > lowerConstRecordMemberAttr(mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp)
Lower a constant attribute that initializes a single member of a record (or a leaf of a nested aggreg...
mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs, const llvm::APInt &rhs)
bool convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr, llvm::SmallVectorImpl< StorageTy > &values, const llvm::SmallVectorImpl< int64_t > &currentDims, int64_t dimIndex, int64_t currentIndex)
static bool shouldPackFAMStruct(const mlir::DataLayout &dataLayout, llvm::ArrayRef< mlir::Type > members)
static bool containsBlockAddress(mlir::Attribute attr)
Block-address attributes (address-of-label and label differences) are lowered to relocation expressio...
static mlir::Type adjustGlobalUnionTypeForInit(mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout)
mlir::APInt getZeroInitFromType(mlir::Type ty)
static unsigned getIntOrBoolBitWidth(mlir::Type ty)
StorageTy getZeroInitFromType(mlir::Type ty)
void convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr, llvm::SmallVectorImpl< StorageTy > &values)