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