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"
19
20static unsigned getIntOrBoolBitWidth(mlir::Type ty) {
21 if (auto intTy = mlir::dyn_cast<cir::IntType>(ty))
22 return intTy.getWidth();
23 assert(mlir::isa<cir::BoolType>(ty) &&
24 "expected CIR integer or bool element type");
25 return 1;
26}
27
28mlir::DenseElementsAttr
29convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr,
30 mlir::Type type) {
31 const auto stringAttr = mlir::cast<mlir::StringAttr>(attr.getElts());
32 const auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());
33 const unsigned totalSize = arrayTy.getSize();
34 const unsigned trailingZeros = attr.getTrailingZerosNum();
35 assert(stringAttr.size() + trailingZeros == totalSize &&
36 "string const_array size must match explicit elements plus "
37 "trailing_zeros");
38
39 const unsigned bitWidth = getIntOrBoolBitWidth(arrayTy.getElementType());
41 values.reserve(totalSize);
42
43 // String bytes are raw values; interpret each as an unsigned byte so a
44 // high-bit char (>= 0x80) does not sign-extend to a value that overflows
45 // the element bit width when constructing the APInt.
46 for (const char element : stringAttr)
47 values.emplace_back(bitWidth, static_cast<unsigned char>(element));
48
49 values.insert(values.end(), trailingZeros, mlir::APInt::getZero(bitWidth));
50
51 return mlir::DenseElementsAttr::get(
52 mlir::RankedTensorType::get({totalSize}, type), llvm::ArrayRef(values));
53}
54
55template <> mlir::APInt getZeroInitFromType(mlir::Type ty) {
56 if (mlir::isa<cir::BoolType>(ty))
57 return mlir::APInt::getZero(1);
58 const auto intTy = mlir::cast<cir::IntType>(ty);
59 return mlir::APInt::getZero(intTy.getWidth());
60}
61
62template <> mlir::APFloat getZeroInitFromType(mlir::Type ty) {
63 auto fpTy = mlir::cast<cir::FPTypeInterface>(ty);
64 return mlir::APFloat::getZero(fpTy.getFloatSemantics());
65}
66
67/// \param attr the ConstArrayAttr to convert
68/// \param values the output parameter, the values array to fill
69/// \param currentDims the shpae of tensor we're going to convert to
70/// \param dimIndex the current dimension we're processing
71/// \param currentIndex the current index in the values array
72template <typename AttrTy, typename StorageTy>
74 cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values,
75 const llvm::SmallVectorImpl<int64_t> &currentDims, int64_t dimIndex,
76 int64_t currentIndex) {
77 if (auto stringAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
78 if (auto arrayType = mlir::dyn_cast<cir::ArrayType>(attr.getType())) {
79 for (auto element : stringAttr) {
80 auto intAttr = cir::IntAttr::get(arrayType.getElementType(), element);
81 values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue();
82 }
83 // Remaining slots are trailing zeros; values was zero-initialized.
84 currentIndex += attr.getTrailingZerosNum();
85 return;
86 }
87 }
88
89 dimIndex++;
90 std::size_t elementsSizeInCurrentDim = 1;
91 for (std::size_t i = dimIndex; i < currentDims.size(); i++)
92 elementsSizeInCurrentDim *= currentDims[i];
93
94 auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts());
95 for (auto eltAttr : arrayAttr) {
96 if constexpr (std::is_same_v<StorageTy, mlir::APInt>) {
97 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(eltAttr)) {
98 values[currentIndex++] =
99 llvm::APInt(1, static_cast<uint64_t>(boolAttr.getValue()));
100 continue;
101 }
102 }
103 if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) {
104 values[currentIndex++] = valueAttr.getValue();
105 continue;
106 }
107
108 if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) {
109 convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims,
110 dimIndex, currentIndex);
111 currentIndex += elementsSizeInCurrentDim;
112 continue;
113 }
114
115 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(eltAttr)) {
116 currentIndex += elementsSizeInCurrentDim;
117 continue;
118 }
119
120 llvm_unreachable("unknown element in ConstArrayAttr");
121 }
122}
123
124template <typename AttrTy, typename StorageTy>
125mlir::DenseElementsAttr convertToDenseElementsAttr(
126 cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims,
127 mlir::Type elementType, mlir::Type convertedElementType) {
128 unsigned vectorSize = 1;
129 for (auto dim : dims)
130 vectorSize *= dim;
132 vectorSize, getZeroInitFromType<StorageTy>(elementType));
133 convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims, /*currentDim=*/0,
134 /*initialIndex=*/0);
135 return mlir::DenseElementsAttr::get(
136 mlir::RankedTensorType::get(dims, convertedElementType),
137 llvm::ArrayRef(values));
138}
139
140/// Return true when \p gv can be lowered to a \c FlatSymbolRefAttr leaf without
141/// addrspacecast or bitcast (mirrors \c CIRAttrToValue::visitCirAttr).
142static bool
143globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, mlir::ModuleOp moduleOp,
144 mlir::SymbolTableCollection &symbolTables,
145 const mlir::TypeConverter *converter) {
146 if (gv.getIndices() || mlir::isa<cir::IntType, cir::VPtrType>(gv.getType()))
147 return false;
148
149 auto ptrTy = mlir::dyn_cast<cir::PointerType>(gv.getType());
150 if (!ptrTy)
151 return false;
152
153 unsigned sourceAddrSpace = 0;
154 mlir::Type sourceType;
155 auto sourceSymbol = symbolTables.lookupSymbolIn(moduleOp, gv.getSymbol());
156 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
157 sourceType = llvmSymbol.getType();
158 sourceAddrSpace = llvmSymbol.getAddrSpace();
159 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(sourceSymbol)) {
160 sourceType = converter->convertType(cirSymbol.getSymType());
161 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
162 cirSymbol.getAddrSpaceAttr()))
163 sourceAddrSpace = targetAS.getValue();
164 } else {
165 // cir.func and other symbols not yet lowered to globals cannot be used as
166 // bulk constant leaves; those cases keep the insertvalue fallback.
167 return false;
168 }
169
170 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
171 if (llvmDstTy.getAddressSpace() != sourceAddrSpace)
172 return false;
173
174 mlir::Type llvmEltTy = converter->convertType(ptrTy.getPointee());
175 if (llvmEltTy == sourceType)
176 return true;
177 if (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(sourceType))
178 return llvmEltTy == arrTy.getElementType();
179 return false;
180}
181
182/// Lower a single pointer-element of a \c cir.const_array to an LLVM-dialect
183/// constant leaf suitable for a bulk \c llvm.mlir.constant. Only handles
184/// address-of-global without indices and null pointers; indexed global views
185/// must use the per-element \c llvm.insertvalue fallback.
186static std::optional<mlir::Attribute>
187lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx,
188 mlir::ModuleOp moduleOp,
189 mlir::SymbolTableCollection &symbolTables,
190 const mlir::TypeConverter *converter) {
191 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(elt)) {
192 if (!moduleOp ||
193 !globalViewMatchesPointerLeaf(gv, moduleOp, symbolTables, converter))
194 return std::nullopt;
195 return gv.getSymbol();
196 }
197 if (auto nullPtr = mlir::dyn_cast<cir::ConstPtrAttr>(elt)) {
198 if (nullPtr.isNullValue())
199 return mlir::LLVM::ZeroAttr::get(ctx);
200 return std::nullopt;
201 }
202 return std::nullopt;
203}
204
205static bool containsPoison(mlir::Attribute attr) {
206 if (mlir::isa<cir::PoisonAttr>(attr))
207 return true;
208 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
209 return llvm::any_of(elts, containsPoison);
210 if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
211 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
212 return false;
213 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
214 return llvm::any_of(elts, containsPoison);
215 }
216 return false;
217}
218
219static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
220 mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
221 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp);
222
223std::optional<mlir::Attribute> lowerConstArrayAttr(
224 cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables,
225 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
226 // Ensure ConstArrayAttr has a type.
227 const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr);
228
229 // Ensure ConstArrayAttr type is a ArrayType.
230 const auto cirArrayType = mlir::cast<cir::ArrayType>(typedConstArr.getType());
231
232 // Is a ConstArrayAttr with an cir::ArrayType: fetch element type.
233 mlir::Type type = cirArrayType;
234 auto dims = llvm::SmallVector<int64_t, 2>{};
235 while (auto arrayType = mlir::dyn_cast<cir::ArrayType>(type)) {
236 dims.push_back(arrayType.getSize());
237 type = arrayType.getElementType();
238 }
239
240 if (containsPoison(constArr))
241 return std::nullopt;
242
243 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
245 converter->convertType(type));
246 if (mlir::isa<cir::IntType>(type)) {
247 // A _BitInt array element is stored in a padded integer in memory; the
248 // dense-attribute path here cannot express that widening, so fall back to
249 // the insertvalue region, which widens each element via emitToMemory.
250 if (mlir::cast<cir::IntType>(type).isBitInt())
251 return std::nullopt;
253 constArr, dims, type, converter->convertType(type));
254 }
255
256 if (mlir::isa<cir::BoolType>(type))
258 constArr, dims, type, converter->convertType(type));
259
260 if (mlir::isa<cir::FPTypeInterface>(type))
262 constArr, dims, type, converter->convertType(type));
263
264 if (mlir::isa<cir::PointerType>(type)) {
265 // FIXME: Pointer arrays with trailing_zeros (null-sentinel tables) fall
266 // through to the insertvalue path for now.
267 if (constArr.getTrailingZerosNum() > 0)
268 return std::nullopt;
269 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
270 if (!eltsArr)
271 return std::nullopt;
273 lowered.reserve(eltsArr.size());
274 mlir::MLIRContext *ctx = constArr.getContext();
275 for (mlir::Attribute elt : eltsArr) {
276 std::optional<mlir::Attribute> llvmElt =
277 lowerPointerElementAttr(elt, ctx, moduleOp, symbolTables, converter);
278 if (!llvmElt)
279 return std::nullopt;
280 lowered.push_back(*llvmElt);
281 }
282 return mlir::ArrayAttr::get(ctx, lowered);
283 }
284
285 if (mlir::isa<cir::RecordType>(type)) {
286 // A record type is just an array of the element values.
287 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
288 if (!eltsArr)
289 return std::nullopt;
290
292 loweredElts.reserve(cirArrayType.getSize());
293
294 for (mlir::Attribute elt : eltsArr) {
295 std::optional<mlir::Attribute> llvmElt =
296 lowerConstRecordMemberAttr(elt, symbolTables, converter, moduleOp);
297 if (!llvmElt)
298 return std::nullopt;
299 loweredElts.push_back(*llvmElt);
300 }
301
302 // Remaining elts are either going to be padding (and should be undef), or
303 // trailing-zeros. We can't really tell the difference as CIR lowering
304 // doesn't differentiate anyway, so just zero-fill them.
305 while (loweredElts.size() < cirArrayType.getSize())
306 loweredElts.push_back(mlir::LLVM::ZeroAttr::get(constArr.getContext()));
307 return mlir::ArrayAttr::get(constArr.getContext(), loweredElts);
308 }
309
310 return std::nullopt;
311}
312
313/// Lower a constant attribute that initializes a single member of a record (or
314/// a leaf of a nested aggregate) to an LLVM-dialect attribute that can be
315/// attached directly to an \c llvm.mlir.global, avoiding an insertvalue
316/// initializer region. Returns \c std::nullopt when the attribute cannot be
317/// represented as a single constant attribute (e.g. an indexed
318/// \c GlobalViewAttr), in which case the caller falls back to the region-based
319/// lowering.
320static std::optional<mlir::Attribute> lowerConstRecordMemberAttr(
321 mlir::Attribute attr, mlir::SymbolTableCollection &symbolTables,
322 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
323 mlir::MLIRContext *ctx = attr.getContext();
324
325 if (auto arrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(attr))
326 return lowerConstArrayAttr(arrayAttr, symbolTables, converter, moduleOp);
327
328 if (auto recordAttr = mlir::dyn_cast<cir::ConstRecordAttr>(attr))
329 return lowerConstRecordAttr(recordAttr, symbolTables, converter, moduleOp);
330
331 if (mlir::isa<cir::ZeroAttr>(attr))
332 return mlir::LLVM::ZeroAttr::get(ctx);
333
334 if (mlir::isa<cir::UndefAttr>(attr))
335 return mlir::LLVM::UndefAttr::get(ctx);
336
337 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(attr)) {
338 // A _BitInt member is stored in a padded integer in memory; defer to the
339 // insertvalue region (CIRAttrToValue), which performs the extension.
340 if (mlir::cast<cir::IntType>(intAttr.getType()).isBitInt())
341 return std::nullopt;
342 return mlir::IntegerAttr::get(converter->convertType(intAttr.getType()),
343 intAttr.getValue());
344 }
345
346 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr))
347 return mlir::IntegerAttr::get(converter->convertType(boolAttr.getType()),
348 boolAttr.getValue() ? 1 : 0);
349
350 if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr))
351 return mlir::FloatAttr::get(converter->convertType(fpAttr.getType()),
352 fpAttr.getValue());
353
354 // Null pointers and simple address-of-global references can be represented
355 // as constant attributes; anything more complex uses the region fallback.
356 return lowerPointerElementAttr(attr, ctx, moduleOp, symbolTables, converter);
357}
358
359// Figure out if we want mark the new struct 'packed' if it isn't already. IF
360// it is already, we have to keep that behavior. We pack it with logic similar
361// to classic codegen, though will end up missing cases, since we don't want to
362// change the type other than the FAM.
363// We can do so if:
364// 1- Packing it won't change any of the field offsets.
365// 2- the non-padded struct would add padding beyond the
366// flexible array member. We don't pack if the flexible array member manages
367// to not cause trailing padding.
368static bool shouldPackFAMStruct(const mlir::DataLayout &dataLayout,
370 uint64_t maxAlign = 1;
371 uint64_t totalSize = 0;
372 for (mlir::Type member : members) {
373 uint64_t align = dataLayout.getTypeABIAlignment(member);
374 maxAlign = std::max(maxAlign, align);
375 uint64_t size = dataLayout.getTypeSize(member).getFixedValue();
376
377 if (llvm::alignTo(totalSize, align) != totalSize)
378 return false;
379
380 totalSize += size;
381 }
382 return llvm::alignTo(totalSize, maxAlign) != totalSize;
383}
384
385// CIR supports flexible-array-members in its struct types. That is, a
386// zero-length array as the last element, which can be initialized with an
387// arbitrary number of elements. A ConstRecordAttr can be created with one of
388// these, and our verifier allows it. However, the LLVM implementation does NOT
389// permit this. So we have to replace this type in LLVM with special struct for
390// this value.
391//
392// Additionally, the struct itself could contain a struct with a FAM or a union
393// that needed adjustment, so it recurses to check those. If no such type has
394// been found/no adjustment needed, this returns the type unchanged.
396 mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord,
397 const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout) {
398
400 constRecord.getMembers().getValue();
401 llvm::SmallVector<mlir::Type> newBody{structTy.getBody()};
402 bool changed = false;
403
404 // Recursively adjust each member. A member that is itself a union (or a
405 // struct containing one) lowers to a type that differs from its declared
406 // field type, and this struct has to adopt that adjusted type so the
407 // enclosing insertvalue chain type-checks.
408 for (auto [idx, member] : llvm::enumerate(initMembers)) {
409 if (idx >= newBody.size())
410 break;
411 mlir::Type adjusted =
412 adjustGlobalTypeForInit(newBody[idx], member, converter, dataLayout);
413 if (adjusted != newBody[idx]) {
414 newBody[idx] = adjusted;
415 changed = true;
416 }
417 }
418
419 // CIR supports flexible-array-members in its struct types. That is, a
420 // zero-length array as the last element, which can be initialized with an
421 // arbitrary number of elements. A ConstRecordAttr can be created with one of
422 // these, and our verifier allows it. However, the LLVM implementation does
423 // NOT permit this, so we widen that trailing member to the initializer's
424 // array type (packing the struct if that changes the layout).
425 bool packed = structTy.isPacked();
426 if (auto fam =
427 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(structTy.getBody().back());
428 fam && fam.getNumElements() == 0) {
429 mlir::Type lastInitType =
430 mlir::cast<mlir::TypedAttr>(initMembers.back()).getType();
431 if (mlir::cast<cir::ArrayType>(lastInitType).getSize() != 0) {
432 newBody.back() = converter.convertType(lastInitType);
433 packed = packed || shouldPackFAMStruct(dataLayout, newBody);
434 changed = true;
435 }
436 }
437
438 if (!changed)
439 return structTy;
440
441 return mlir::LLVM::LLVMStructType::getLiteral(structTy.getContext(), newBody,
442 packed);
443}
444
445// A union constant only initializes its active member, but a union is lowered
446// to its most-aligned member (its 'storage' type), which need not be the
447// initialized one -- e.g. `union { char buf[16]; long cap; }` stores as `long`
448// (higher alignment) but may be initialized through `buf`. So the storage type
449// (and thus structTy) generally can't hold the active member's value. Rebuild
450// an anonymous struct `{ <active member>, [pad x i8] }` that holds the active
451// member followed by enough byte padding to span the union's full allocated
452// size, mirroring classic codegen.
454 mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord,
455 const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout) {
456
457 auto unionTy = mlir::cast<cir::UnionType>(constRecord.getType());
458
459 // Unions can only initialize one field, so this has to be sizeof-one.
460 assert(constRecord.getMembers().size() == 1);
461 mlir::Attribute member = constRecord.getMembers()[0];
462 mlir::Type memberTy =
463 converter.convertType(mlir::cast<mlir::TypedAttr>(member).getType());
464
465 // The active member may itself need adjusting (e.g. it is a nested union, or
466 // a struct containing one), so recurse before using its type below.
467 memberTy = adjustGlobalTypeForInit(memberTy, member, converter, dataLayout);
468
469 // The converted union type is { storage, [padding] }, where storage is the
470 // union's most-aligned member. When the active member IS that storage type,
471 // the converted type already describes this initializer exactly (the pad we
472 // would compute equals the union's declared pad), so there is nothing to do.
473 if (memberTy == structTy.getBody().front())
474 return structTy;
475
476 uint64_t unionSize = dataLayout.getTypeSize(unionTy).getFixedValue();
477 uint64_t initSize = dataLayout.getTypeSize(memberTy).getFixedValue();
478 assert(initSize <= unionSize && "union initializer larger than the union");
479
481 newBody.push_back(memberTy);
482
483 // Fill the rest of the union's allocated size with byte padding.
484 if (initSize < unionSize)
485 newBody.push_back(mlir::LLVM::LLVMArrayType::get(
486 mlir::IntegerType::get(structTy.getContext(), 8),
487 unionSize - initSize));
488
489 return mlir::LLVM::LLVMStructType::getLiteral(structTy.getContext(), newBody,
490 unionTy.getPacked());
491}
492
493// Apply various adjustments required for struct/union types.
494mlir::Type adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init,
495 const mlir::TypeConverter &converter,
496 const mlir::DataLayout &dataLayout) {
497 // Conversions for both only happen if we have a record init.
498 auto constRecord = mlir::dyn_cast_if_present<cir::ConstRecordAttr>(init);
499 if (!constRecord)
500 return llvmType;
501
502 // If this isn't of struct-type, or doesn't have any members, there is nothing
503 // to do.
504 auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(llvmType);
505 if (!structTy || structTy.getBody().empty())
506 return llvmType;
507
508 // Structs can have a flexible array member, adjust that.
509 if (mlir::isa<cir::StructType>(constRecord.getType()))
510 return adjustGlobalStructTypeForInit(structTy, constRecord, converter,
511 dataLayout);
512 if (mlir::isa<cir::UnionType>(constRecord.getType()))
513 return adjustGlobalUnionTypeForInit(structTy, constRecord, converter,
514 dataLayout);
515 return llvmType;
516}
517
518std::optional<mlir::Attribute> lowerConstRecordAttr(
519 cir::ConstRecordAttr constRecord, mlir::SymbolTableCollection &symbolTables,
520 const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp) {
521 // Build one constant attribute per record member. The LLVM dialect global
522 // translation accepts an ArrayAttr (one element per struct field) and emits
523 // an llvm::ConstantStruct, so the whole initializer can be a single
524 // attribute on the global instead of an insertvalue region.
525 mlir::ArrayAttr memberAttrs = constRecord.getMembers();
527 loweredMembers.reserve(memberAttrs.size());
528 for (mlir::Attribute member : memberAttrs) {
529 std::optional<mlir::Attribute> lowered =
530 lowerConstRecordMemberAttr(member, symbolTables, converter, moduleOp);
531 if (!lowered)
532 return std::nullopt;
533 loweredMembers.push_back(*lowered);
534 }
535
536 // The lowered LLVM type may have more fields than the CIR record has members
537 // -- e.g. a union lowers to { active-member, [pad x i8] } (see
538 // adjustGlobalTypeForInit, the single source of truth for the shape). Fill
539 // any such synthesized (padding) fields with undef so this ArrayAttr has
540 // exactly one entry per LLVM field, matching the type the global is declared
541 // with.
542 mlir::Type adjustedTy = adjustGlobalTypeForInit(
543 converter->convertType(constRecord.getType()), constRecord, *converter,
544 mlir::DataLayout(moduleOp));
545 if (auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(adjustedTy))
546 while (loweredMembers.size() < structTy.getBody().size())
547 loweredMembers.push_back(
548 mlir::LLVM::UndefAttr::get(constRecord.getContext()));
549
550 return mlir::ArrayAttr::get(constRecord.getContext(), loweredMembers);
551}
552
553mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc,
554 mlir::Type typ, const llvm::APInt &val) {
555 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
556}
557
558mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ,
559 unsigned val) {
560 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
561}
562
563mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
564 if (!rhs)
565 return lhs;
566 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
567 return mlir::LLVM::ShlOp::create(bld, lhs.getLoc(), lhs, rhsVal);
568}
569
570mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
571 if (!rhs)
572 return lhs;
573 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
574 return mlir::LLVM::AShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
575}
576
577mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs,
578 const llvm::APInt &rhs) {
579 mlir::Value rhsVal = getConstAPInt(bld, lhs.getLoc(), lhs.getType(), rhs);
580 return mlir::LLVM::AndOp::create(bld, lhs.getLoc(), lhs, rhsVal);
581}
582
583mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
584 if (!rhs)
585 return lhs;
586 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
587 return mlir::LLVM::LShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
588}
TokenType getType() const
Returns the token's type, e.g.
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)
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)
mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ, const llvm::APInt &val)
std::optional< mlir::Attribute > lowerConstArrayAttr(cir::ConstArrayAttr constArr, mlir::SymbolTableCollection &symbolTables, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp)
void convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr, llvm::SmallVectorImpl< StorageTy > &values, const llvm::SmallVectorImpl< int64_t > &currentDims, int64_t dimIndex, int64_t currentIndex)
mlir::Type adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout)
Adjust llvmType (the converted type of init) to the concrete LLVM type a global constant initialized ...
static mlir::Type adjustGlobalStructTypeForInit(mlir::LLVM::LLVMStructType structTy, cir::ConstRecordAttr constRecord, 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)
static bool shouldPackFAMStruct(const mlir::DataLayout &dataLayout, llvm::ArrayRef< mlir::Type > members)
mlir::DenseElementsAttr convertToDenseElementsAttr(cir::ConstArrayAttr attr, const llvm::SmallVectorImpl< int64_t > &dims, mlir::Type elementType, mlir::Type convertedElementType)
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)