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