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 assert((mlir::isa<cir::SingleType, cir::DoubleType>(ty)) &&
62 "only float and double supported");
63
64 if (ty.isF32() || mlir::isa<cir::SingleType>(ty))
65 return mlir::APFloat(0.f);
66
67 if (ty.isF64() || mlir::isa<cir::DoubleType>(ty))
68 return mlir::APFloat(0.0);
69
70 llvm_unreachable("NYI");
71}
72
73/// \param attr the ConstArrayAttr to convert
74/// \param values the output parameter, the values array to fill
75/// \param currentDims the shpae of tensor we're going to convert to
76/// \param dimIndex the current dimension we're processing
77/// \param currentIndex the current index in the values array
78template <typename AttrTy, typename StorageTy>
80 cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values,
81 const llvm::SmallVectorImpl<int64_t> &currentDims, int64_t dimIndex,
82 int64_t currentIndex) {
83 if (auto stringAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
84 if (auto arrayType = mlir::dyn_cast<cir::ArrayType>(attr.getType())) {
85 for (auto element : stringAttr) {
86 auto intAttr = cir::IntAttr::get(arrayType.getElementType(), element);
87 values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue();
88 }
89 // Remaining slots are trailing zeros; values was zero-initialized.
90 currentIndex += attr.getTrailingZerosNum();
91 return;
92 }
93 }
94
95 dimIndex++;
96 std::size_t elementsSizeInCurrentDim = 1;
97 for (std::size_t i = dimIndex; i < currentDims.size(); i++)
98 elementsSizeInCurrentDim *= currentDims[i];
99
100 auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts());
101 for (auto eltAttr : arrayAttr) {
102 if constexpr (std::is_same_v<StorageTy, mlir::APInt>) {
103 if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(eltAttr)) {
104 values[currentIndex++] =
105 llvm::APInt(1, static_cast<uint64_t>(boolAttr.getValue()));
106 continue;
107 }
108 }
109 if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) {
110 values[currentIndex++] = valueAttr.getValue();
111 continue;
112 }
113
114 if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) {
115 convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims,
116 dimIndex, currentIndex);
117 currentIndex += elementsSizeInCurrentDim;
118 continue;
119 }
120
121 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(eltAttr)) {
122 currentIndex += elementsSizeInCurrentDim;
123 continue;
124 }
125
126 llvm_unreachable("unknown element in ConstArrayAttr");
127 }
128}
129
130template <typename AttrTy, typename StorageTy>
131mlir::DenseElementsAttr convertToDenseElementsAttr(
132 cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims,
133 mlir::Type elementType, mlir::Type convertedElementType) {
134 unsigned vectorSize = 1;
135 for (auto dim : dims)
136 vectorSize *= dim;
138 vectorSize, getZeroInitFromType<StorageTy>(elementType));
139 convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims, /*currentDim=*/0,
140 /*initialIndex=*/0);
141 return mlir::DenseElementsAttr::get(
142 mlir::RankedTensorType::get(dims, convertedElementType),
143 llvm::ArrayRef(values));
144}
145
146/// Return true when \p gv can be lowered to a \c FlatSymbolRefAttr leaf without
147/// addrspacecast or bitcast (mirrors \c CIRAttrToValue::visitCirAttr).
148static bool globalViewMatchesPointerLeaf(cir::GlobalViewAttr gv,
149 mlir::ModuleOp moduleOp,
150 const mlir::TypeConverter *converter) {
151 if (gv.getIndices() || mlir::isa<cir::IntType, cir::VPtrType>(gv.getType()))
152 return false;
153
154 auto ptrTy = mlir::dyn_cast<cir::PointerType>(gv.getType());
155 if (!ptrTy)
156 return false;
157
158 unsigned sourceAddrSpace = 0;
159 mlir::Type sourceType;
160 auto sourceSymbol =
161 mlir::SymbolTable::lookupSymbolIn(moduleOp, gv.getSymbol());
162 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
163 sourceType = llvmSymbol.getType();
164 sourceAddrSpace = llvmSymbol.getAddrSpace();
165 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(sourceSymbol)) {
166 sourceType = converter->convertType(cirSymbol.getSymType());
167 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
168 cirSymbol.getAddrSpaceAttr()))
169 sourceAddrSpace = targetAS.getValue();
170 } else {
171 // cir.func and other symbols not yet lowered to globals cannot be used as
172 // bulk constant leaves; those cases keep the insertvalue fallback.
173 return false;
174 }
175
176 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
177 if (llvmDstTy.getAddressSpace() != sourceAddrSpace)
178 return false;
179
180 mlir::Type llvmEltTy = converter->convertType(ptrTy.getPointee());
181 if (llvmEltTy == sourceType)
182 return true;
183 if (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(sourceType))
184 return llvmEltTy == arrTy.getElementType();
185 return false;
186}
187
188/// Lower a single pointer-element of a \c cir.const_array to an LLVM-dialect
189/// constant leaf suitable for a bulk \c llvm.mlir.constant. Only handles
190/// address-of-global without indices and null pointers; indexed global views
191/// must use the per-element \c llvm.insertvalue fallback.
192static std::optional<mlir::Attribute>
193lowerPointerElementAttr(mlir::Attribute elt, mlir::MLIRContext *ctx,
194 mlir::ModuleOp moduleOp,
195 const mlir::TypeConverter *converter) {
196 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(elt)) {
197 if (!moduleOp || !globalViewMatchesPointerLeaf(gv, moduleOp, converter))
198 return std::nullopt;
199 return gv.getSymbol();
200 }
201 if (auto nullPtr = mlir::dyn_cast<cir::ConstPtrAttr>(elt)) {
202 if (nullPtr.isNullValue())
203 return mlir::LLVM::ZeroAttr::get(ctx);
204 return std::nullopt;
205 }
206 return std::nullopt;
207}
208
209static bool containsPoison(mlir::Attribute attr) {
210 if (mlir::isa<cir::PoisonAttr>(attr))
211 return true;
212 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(attr))
213 return llvm::any_of(elts, containsPoison);
214 if (auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
215 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
216 return false;
217 if (auto elts = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts()))
218 return llvm::any_of(elts, containsPoison);
219 }
220 return false;
221}
222
223std::optional<mlir::Attribute>
224lowerConstArrayAttr(cir::ConstArrayAttr constArr,
225 const mlir::TypeConverter *converter,
226 mlir::ModuleOp moduleOp) {
227 // Ensure ConstArrayAttr has a type.
228 const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr);
229
230 // Ensure ConstArrayAttr type is a ArrayType.
231 const auto cirArrayType = mlir::cast<cir::ArrayType>(typedConstArr.getType());
232
233 // Is a ConstArrayAttr with an cir::ArrayType: fetch element type.
234 mlir::Type type = cirArrayType;
235 auto dims = llvm::SmallVector<int64_t, 2>{};
236 while (auto arrayType = mlir::dyn_cast<cir::ArrayType>(type)) {
237 dims.push_back(arrayType.getSize());
238 type = arrayType.getElementType();
239 }
240
241 if (containsPoison(constArr))
242 return std::nullopt;
243
244 if (mlir::isa<mlir::StringAttr>(constArr.getElts()))
246 converter->convertType(type));
247 if (mlir::isa<cir::IntType>(type))
249 constArr, dims, type, converter->convertType(type));
250
251 if (mlir::isa<cir::BoolType>(type))
253 constArr, dims, type, converter->convertType(type));
254
255 if (mlir::isa<cir::FPTypeInterface>(type))
257 constArr, dims, type, converter->convertType(type));
258
259 if (mlir::isa<cir::PointerType>(type)) {
260 // FIXME: Pointer arrays with trailing_zeros (null-sentinel tables) fall
261 // through to the insertvalue path for now.
262 if (constArr.getTrailingZerosNum() > 0)
263 return std::nullopt;
264 auto eltsArr = mlir::dyn_cast<mlir::ArrayAttr>(constArr.getElts());
265 if (!eltsArr)
266 return std::nullopt;
268 lowered.reserve(eltsArr.size());
269 mlir::MLIRContext *ctx = constArr.getContext();
270 for (mlir::Attribute elt : eltsArr) {
271 std::optional<mlir::Attribute> llvmElt =
272 lowerPointerElementAttr(elt, ctx, moduleOp, converter);
273 if (!llvmElt)
274 return std::nullopt;
275 lowered.push_back(*llvmElt);
276 }
277 return mlir::ArrayAttr::get(ctx, lowered);
278 }
279
280 return std::nullopt;
281}
282
283mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc,
284 mlir::Type typ, const llvm::APInt &val) {
285 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
286}
287
288mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ,
289 unsigned val) {
290 return mlir::LLVM::ConstantOp::create(bld, loc, typ, val);
291}
292
293mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
294 if (!rhs)
295 return lhs;
296 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
297 return mlir::LLVM::ShlOp::create(bld, lhs.getLoc(), lhs, rhsVal);
298}
299
300mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
301 if (!rhs)
302 return lhs;
303 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
304 return mlir::LLVM::AShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
305}
306
307mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs,
308 const llvm::APInt &rhs) {
309 mlir::Value rhsVal = getConstAPInt(bld, lhs.getLoc(), lhs.getType(), rhs);
310 return mlir::LLVM::AndOp::create(bld, lhs.getLoc(), lhs, rhsVal);
311}
312
313mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs) {
314 if (!rhs)
315 return lhs;
316 mlir::Value rhsVal = getConst(bld, lhs.getLoc(), lhs.getType(), rhs);
317 return mlir::LLVM::LShrOp::create(bld, lhs.getLoc(), lhs, rhsVal);
318}
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)
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 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)