clang 23.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/SymbolTable.h"
17
18static unsigned getIntOrBoolBitWidth(mlir::Type ty) {
19 if (auto intTy = mlir::dyn_cast<cir::IntType>(ty))
20 return intTy.getWidth();
21 assert(mlir::isa<cir::BoolType>(ty) &&
22 "expected CIR integer or bool element type");
23 return 1;
24}
25
26mlir::DenseElementsAttr
27convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr,
28 mlir::Type type) {
29 const auto stringAttr = mlir::cast<mlir::StringAttr>(attr.getElts());
30 const auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType());
31 const unsigned totalSize = arrayTy.getSize();
32 const unsigned trailingZeros = attr.getTrailingZerosNum();
33 assert(stringAttr.size() + trailingZeros == totalSize &&
34 "string const_array size must match explicit elements plus "
35 "trailing_zeros");
36
37 const unsigned bitWidth = getIntOrBoolBitWidth(arrayTy.getElementType());
39 values.reserve(totalSize);
40
41 // String bytes are raw values; interpret each as an unsigned byte so a
42 // high-bit char (>= 0x80) does not sign-extend to a value that overflows
43 // the element bit width when constructing the APInt.
44 for (const char element : stringAttr)
45 values.emplace_back(bitWidth, static_cast<unsigned char>(element));
46
47 values.insert(values.end(), trailingZeros, mlir::APInt::getZero(bitWidth));
48
49 return mlir::DenseElementsAttr::get(
50 mlir::RankedTensorType::get({totalSize}, type), llvm::ArrayRef(values));
51}
52
53template <> mlir::APInt getZeroInitFromType(mlir::Type ty) {
54 if (mlir::isa<cir::BoolType>(ty))
55 return mlir::APInt::getZero(1);
56 const auto intTy = mlir::cast<cir::IntType>(ty);
57 return mlir::APInt::getZero(intTy.getWidth());
58}
59
60template <> mlir::APFloat getZeroInitFromType(mlir::Type ty) {
61 auto fpTy = mlir::cast<cir::FPTypeInterface>(ty);
62 return mlir::APFloat::getZero(fpTy.getFloatSemantics());
63}
64
65/// \param attr the ConstArrayAttr to convert
66/// \param values the output parameter, the values array to fill
67/// \param currentDims the shpae of tensor we're going to convert to
68/// \param dimIndex the current dimension we're processing
69/// \param currentIndex the current index in the values array
70template <typename AttrTy, typename StorageTy>
72 cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values,
73 const llvm::SmallVectorImpl<int64_t> &currentDims, int64_t dimIndex,
74 int64_t currentIndex) {
75 if (auto stringAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
76 if (auto arrayType = mlir::dyn_cast<cir::ArrayType>(attr.getType())) {
77 for (auto element : stringAttr) {
78 auto intAttr = cir::IntAttr::get(arrayType.getElementType(), element);
79 values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue();
80 }
81 // Remaining slots are trailing zeros; values was zero-initialized.
82 currentIndex += attr.getTrailingZerosNum();
83 return;
84 }
85 }
86
87 dimIndex++;
88 std::size_t elementsSizeInCurrentDim = 1;
89 for (std::size_t i = dimIndex; i < currentDims.size(); i++)
90 elementsSizeInCurrentDim *= currentDims[i];
91
92 auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts());
93 for (auto eltAttr : arrayAttr) {
94 if constexpr (std::is_same_v<StorageTy, mlir::APInt>) {
95 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(eltAttr)) {
96 values[currentIndex++] =
97 llvm::APInt(1, static_cast<uint64_t>(boolAttr.getValue()));
98 continue;
99 }
100 }
101 if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) {
102 values[currentIndex++] = valueAttr.getValue();
103 continue;
104 }
105
106 if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) {
107 convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims,
108 dimIndex, currentIndex);
109 currentIndex += elementsSizeInCurrentDim;
110 continue;
111 }
112
113 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(eltAttr)) {
114 currentIndex += elementsSizeInCurrentDim;
115 continue;
116 }
117
118 llvm_unreachable("unknown element in ConstArrayAttr");
119 }
120}
121
122template <typename AttrTy, typename StorageTy>
123mlir::DenseElementsAttr convertToDenseElementsAttr(
124 cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims,
125 mlir::Type elementType, mlir::Type convertedElementType) {
126 unsigned vectorSize = 1;
127 for (auto dim : dims)
128 vectorSize *= dim;
130 vectorSize, getZeroInitFromType<StorageTy>(elementType));
131 convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims, /*currentDim=*/0,
132 /*initialIndex=*/0);
133 return mlir::DenseElementsAttr::get(
134 mlir::RankedTensorType::get(dims, convertedElementType),
135 llvm::ArrayRef(values));
136}
137
138/// Return true when \p gv can be lowered to a \c FlatSymbolRefAttr leaf without
139/// addrspacecast or bitcast (mirrors \c CIRAttrToValue::visitCirAttr).
140static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv,
141 mlir::ModuleOp moduleOp,
142 const mlir::TypeConverter *converter) {
143 if (gv.getIndices() || mlir::isa<cir::IntType, cir::VPtrType>(gv.getType()))
144 return false;
145
146 auto ptrTy = mlir::dyn_cast<cir::PointerType>(gv.getType());
147 if (!ptrTy)
148 return false;
149
150 unsigned sourceAddrSpace = 0;
151 mlir::Type sourceType;
152 auto sourceSymbol =
153 mlir::SymbolTable::lookupSymbolIn(moduleOp, gv.getSymbol());
154 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
155 sourceType = llvmSymbol.getType();
156 sourceAddrSpace = llvmSymbol.getAddrSpace();
157 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(sourceSymbol)) {
158 sourceType = converter->convertType(cirSymbol.getSymType());
159 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
160 cirSymbol.getAddrSpaceAttr()))
161 sourceAddrSpace = targetAS.getValue();
162 } else {
163 // cir.func and other symbols not yet lowered to globals cannot be used as
164 // bulk constant leaves; those cases keep the insertvalue fallback.
165 return false;
166 }
167
168 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
169 if (llvmDstTy.getAddressSpace() != sourceAddrSpace)
170 return false;
171
172 mlir::Type llvmEltTy = converter->convertType(ptrTy.getPointee());
173 if (llvmEltTy == sourceType)
174 return true;
175 if (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(sourceType))
176 return llvmEltTy == arrTy.getElementType();
177 return false;
178}
179
180/// Lower a single pointer-element of a \c cir.const_array to an LLVM-dialect
181/// constant leaf suitable for a bulk \c llvm.mlir.constant. Only handles
182/// address-of-global without indices and null pointers; indexed global views
183/// must use the per-element \c llvm.insertvalue fallback.
184static std::optional<mlir::Attribute>
185lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx,
186 mlir::ModuleOp moduleOp,
187 const mlir::TypeConverter *converter) {
188 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(elt)) {
189 if (!moduleOp || !globalViewMatchesPointerLeaf(gv, moduleOp, converter))
190 return std::nullopt;
191 return gv.getSymbol();
192 }
193 if (auto nullPtr = mlir::dyn_cast<cir::ConstPtrAttr>(elt)) {
194 if (nullPtr.isNullValue())
195 return mlir::LLVM::ZeroAttr::get(ctx);
196 return std::nullopt;
197 }
198 return std::nullopt;
199}
200
201static bool containsPoison(mlir::Attribute attr) {
202 if (mlir::isa<cir::PoisonAttr>(attr))
203 return true;
204 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
205 return llvm::any_of(elts, containsPoison);
206 if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
207 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
208 return false;
209 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
210 return llvm::any_of(elts, containsPoison);
211 }
212 return false;
213}
214
215std::optional<mlir::Attribute>
216lowerConstArrayAttr(cir::ConstArrayAttr constArr,
217 const mlir::TypeConverter *converter,
218 mlir::ModuleOp moduleOp) {
219 // Ensure ConstArrayAttr has a type.
220 const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr);
221
222 // Ensure ConstArrayAttr type is a ArrayType.
223 const auto cirArrayType = mlir::cast<cir::ArrayType>(typedConstArr.getType());
224
225 // Is a ConstArrayAttr with an cir::ArrayType: fetch element type.
226 mlir::Type type = cirArrayType;
227 auto dims = llvm::SmallVector<int64_t, 2>{};
228 while (auto arrayType = mlir::dyn_cast<cir::ArrayType>(type)) {
229 dims.push_back(arrayType.getSize());
230 type = arrayType.getElementType();
231 }
232
233 if (containsPoison(constArr))
234 return std::nullopt;
235
236 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
238 converter->convertType(type));
239 if (mlir::isa<cir::IntType>(type))
241 constArr, dims, type, converter->convertType(type));
242
243 if (mlir::isa<cir::BoolType>(type))
245 constArr, dims, type, converter->convertType(type));
246
247 if (mlir::isa<cir::FPTypeInterface>(type))
249 constArr, dims, type, converter->convertType(type));
250
251 if (mlir::isa<cir::PointerType>(type)) {
252 // FIXME: Pointer arrays with trailing_zeros (null-sentinel tables) fall
253 // through to the insertvalue path for now.
254 if (constArr.getTrailingZerosNum() > 0)
255 return std::nullopt;
256 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
257 if (!eltsArr)
258 return std::nullopt;
260 lowered.reserve(eltsArr.size());
261 mlir::MLIRContext *ctx = constArr.getContext();
262 for (mlir::Attribute elt : eltsArr) {
263 std::optional<mlir::Attribute> llvmElt =
264 lowerPointerElementAttr(elt, ctx, moduleOp, converter);
265 if (!llvmElt)
266 return std::nullopt;
267 lowered.push_back(*llvmElt);
268 }
269 return mlir::ArrayAttr::get(ctx, lowered);
270 }
271
272 return std::nullopt;
273}
274
275/// Lower a constant attribute that initializes a single member of a record (or
276/// a leaf of a nested aggregate) to an LLVM-dialect attribute that can be
277/// attached directly to an \c llvm.mlir.global, avoiding an insertvalue
278/// initializer region. Returns \c std::nullopt when the attribute cannot be
279/// represented as a single constant attribute (e.g. an indexed
280/// \c GlobalViewAttr), in which case the caller falls back to the region-based
281/// lowering.
282static std::optional<mlir::Attribute>
283lowerConstRecordMemberAttr(mlir::Attribute attr,
284 const mlir::TypeConverter *converter,
285 mlir::ModuleOp moduleOp) {
286 mlir::MLIRContext *ctx = attr.getContext();
287
288 if (auto arrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(attr))
289 return lowerConstArrayAttr(arrayAttr, converter, moduleOp);
290
291 if (auto recordAttr = mlir::dyn_cast<cir::ConstRecordAttr>(attr))
292 return lowerConstRecordAttr(recordAttr, converter, moduleOp);
293
294 if (mlir::isa<cir::ZeroAttr>(attr))
295 return mlir::LLVM::ZeroAttr::get(ctx);
296
297 if (mlir::isa<cir::UndefAttr>(attr))
298 return mlir::LLVM::UndefAttr::get(ctx);
299
300 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(attr))
301 return mlir::IntegerAttr::get(converter->convertType(intAttr.getType()),
302 intAttr.getValue());
303
304 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr))
305 return mlir::IntegerAttr::get(converter->convertType(boolAttr.getType()),
306 boolAttr.getValue() ? 1 : 0);
307
308 if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr))
309 return mlir::FloatAttr::get(converter->convertType(fpAttr.getType()),
310 fpAttr.getValue());
311
312 // Null pointers and simple address-of-global references can be represented
313 // as constant attributes; anything more complex uses the region fallback.
314 return lowerPointerElementAttr(attr, ctx, moduleOp, converter);
315}
316
317std::optional<mlir::Attribute>
318lowerConstRecordAttr(cir::ConstRecordAttr constRecord,
319 const mlir::TypeConverter *converter,
320 mlir::ModuleOp moduleOp) {
321 // Build one constant attribute per record member. The LLVM dialect global
322 // translation accepts an ArrayAttr (one element per struct field) and emits
323 // an llvm::ConstantStruct, so the whole initializer can be a single
324 // attribute on the global instead of an insertvalue region.
325 mlir::ArrayAttr memberAttrs = constRecord.getMembers();
327 loweredMembers.reserve(memberAttrs.size());
328 for (mlir::Attribute member : memberAttrs) {
329 std::optional<mlir::Attribute> lowered =
330 lowerConstRecordMemberAttr(member, converter, moduleOp);
331 if (!lowered)
332 return std::nullopt;
333 loweredMembers.push_back(*lowered);
334 }
335
336 // For a union, the CIR representation is a single field. However, if the
337 // union has padding, we would have added that as an LLVM field, so make sure
338 // we have a corresponding undef for that spot.
339 if (auto unionTy = mlir::dyn_cast<cir::UnionType>(constRecord.getType());
340 unionTy && unionTy.getPadded()) {
341 assert(loweredMembers.size() == 1);
342 loweredMembers.push_back(
343 mlir::LLVM::UndefAttr::get(constRecord.getContext()));
344 }
345
346 return mlir::ArrayAttr::get(constRecord.getContext(), loweredMembers);
347}
348
349mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc,
350 mlir::Type typ, const llvm::APInt &val) {
351 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
352}
353
354mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ,
355 unsigned val) {
356 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
357}
358
359mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
360 if (!rhs)
361 return lhs;
362 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
363 return mlir::LLVM::ShlOp::create(bld, lhs.getLoc(), lhs, rhsVal);
364}
365
366mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
367 if (!rhs)
368 return lhs;
369 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
370 return mlir::LLVM::AShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
371}
372
373mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs,
374 const llvm::APInt &rhs) {
375 mlir::Value rhsVal = getConstAPInt(bld, lhs.getLoc(), lhs.getType(), rhs);
376 return mlir::LLVM::AndOp::create(bld, lhs.getLoc(), lhs, rhsVal);
377}
378
379mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
380 if (!rhs)
381 return lhs;
382 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
383 return mlir::LLVM::LShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
384}
mlir::DenseElementsAttr convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, mlir::Type type)
std::optional< mlir::Attribute > lowerConstArrayAttr(cir::ConstArrayAttr constArr, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp)
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 > lowerConstRecordAttr(cir::ConstRecordAttr constRecord, 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)
static bool containsPoison(mlir::Attribute attr)
static std::optional< mlir::Attribute > lowerConstRecordMemberAttr(mlir::Attribute attr, 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...
static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv, mlir::ModuleOp moduleOp, const mlir::TypeConverter *converter)
Return true when gv can be lowered to a FlatSymbolRefAttr leaf without addrspacecast or bitcast (mirr...
static std::optional< mlir::Attribute > lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx, mlir::ModuleOp moduleOp, 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)
mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs, const llvm::APInt &rhs)
mlir::DenseElementsAttr convertToDenseElementsAttr(cir::ConstArrayAttr attr, const llvm::SmallVectorImpl< int64_t > &dims, mlir::Type elementType, mlir::Type convertedElementType)
mlir::APInt getZeroInitFromType(mlir::Type ty)
static unsigned getIntOrBoolBitWidth(mlir::Type ty)
StorageTy getZeroInitFromType(mlir::Type ty)