MLIR-AIE 1.4.5.dev
AIEVecUtils.h
Go to the documentation of this file.
1//===- AIEVecUtils.h - AIE Vector Utility Operations ------------*- C++ -*-===//
2//
3// Copyright (C) 2022 Xilinx, Inc.
4// Copyright (C) 2022-2025 Advanced Micro Devices, Inc.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// Utility functions for AIE vectorization
9//===----------------------------------------------------------------------===//
10
11#ifndef AIE_DIALECT_AIEVEC_AIEVECUTILS_H
12#define AIE_DIALECT_AIEVEC_AIEVECUTILS_H
13
18
19#include "mlir/Dialect/Affine/IR/AffineOps.h"
20#include "mlir/Dialect/Arith/IR/Arith.h"
21#include "mlir/Dialect/Vector/IR/VectorOps.h"
22
23#include <cassert>
24#include <numeric>
25
26namespace xilinx::aievec {
27
28// For input val, return its value in hex. Since we currently support each
29// offset value to be only 4 bits, the val must be < 16
30inline char getHexValue(int val) {
31 assert(val >= 0 && val < 16);
32 if (val <= 9)
33 return '0' + val;
34 return 'A' + (val - 10);
35}
36
37// Return true if the number is a power of 2
38inline bool isPowerOfTwo(int32_t n) { return (n & (n - 1)) == 0; }
39
40// Create a vector type, given the lanes and underlying element type
41inline mlir::VectorType createVectorType(unsigned lanes,
42 mlir::Type elementType) {
43 llvm::SmallVector<int64_t, 4> vecShape = {lanes};
44 return mlir::VectorType::get(vecShape, elementType);
45}
46
47// Return the size (in bits) of the underlying element type of the vector
48inline int32_t getElementSizeInBits(mlir::VectorType type) {
49 return llvm::cast<mlir::ShapedType>(type).getElementTypeBitWidth();
50}
51
52// Return the number of lanes along the vectorized dimension for the vector
53// type. For a multidimensional vector, return the innermost dimension size
54inline unsigned getVectorLaneSize(mlir::VectorType type) {
55 assert(type.getRank() > 0 && "Cannot handle rank-0 vectors");
56 auto vShape = type.getShape();
57 assert(llvm::all_of(vShape, [](int64_t dim) { return dim > 0; }) &&
58 "Vector dimensions cannot be dynamic");
59 return std::accumulate(vShape.begin(), vShape.end(), 1,
60 std::multiplies<int64_t>());
61}
62
63// For a 1D vector, return its size in bits. For an nD vector, return the size
64// of the innerost dimension in bits.
65inline int32_t getVectorSizeInBits(mlir::VectorType type) {
66 int32_t veclen = getVectorLaneSize(type) * getElementSizeInBits(type);
67 assert(veclen >= 128 && "AIE vector size should be greater than 128 bits");
68 return veclen;
69}
70
71// Return true if this is an operation defined in AIE dialect
72inline bool isAIEOp(mlir::Operation *op) {
73 return llvm::isa<aievec::AIEVecDialect, aievec::aie1::AIEVecAIE1Dialect>(
74 op->getDialect());
75}
76
77// Determine the output type for a vector operation based on whether
78// it operates on integer or floating point data.
79inline mlir::VectorType getVectorOpDestType(mlir::VectorType type, bool AIE2) {
80 mlir::Type stype = type.getElementType();
81
82 if (auto itype = llvm::dyn_cast<mlir::IntegerType>(stype)) {
83 // Integer vector types are sized for the appropriate accumulators
84 assert(itype.getWidth() <= 64);
85 unsigned width;
86 if (AIE2)
87 width = itype.getWidth() <= 16 ? 32 : 64;
88 else
89 width = itype.getWidth() <= 16 ? 48 : 80;
90
91 mlir::Type ctype = mlir::IntegerType::get(itype.getContext(), width);
92 return mlir::VectorType::get(type.getShape(), ctype);
93 }
94
95 if (auto ftype = llvm::dyn_cast<mlir::FloatType>(stype)) {
96 if (AIE2 && ftype.getWidth() == 16)
97 return mlir::VectorType::get(type.getShape(),
98 mlir::Float32Type::get(ftype.getContext()));
99
100 // Floating point vector types for aie1 are returned as is since the
101 // floating point operations write back to registers and not accumulators
102 return type;
103 }
104
105 llvm::report_fatal_error("Unsupported destination type");
106}
107
108// Linearize the exprVec as a strided access, but do not simplify
109inline mlir::AffineExpr
110flattenedStridedExpr(llvm::ArrayRef<int64_t> sizes,
111 llvm::ArrayRef<mlir::AffineExpr> exprs,
112 mlir::MLIRContext *context) {
113 // Expect non-empty sizes and exprs
114 if (sizes.empty() || exprs.empty())
115 return nullptr;
116
117 if (is_contained(sizes, 0))
118 return getAffineConstantExpr(0, context);
119
120 auto maps = mlir::AffineMap::inferFromExprList(exprs, context);
121 if (maps.empty())
122 return nullptr;
123
124 unsigned nSymbols = maps[0].getNumSymbols();
125
126 mlir::AffineExpr expr;
127 bool dynamicPoisonBit = false;
128 int64_t runningSize = 1;
129 for (auto en : zip(reverse(exprs), reverse(sizes))) {
130 int64_t size = std::get<1>(en);
131 if (size == 0)
132 continue;
133
134 mlir::AffineExpr dimExpr = std::get<0>(en);
135 mlir::AffineExpr stride = dynamicPoisonBit
136 ? getAffineSymbolExpr(nSymbols++, context)
137 : getAffineConstantExpr(runningSize, context);
138 expr = expr ? expr + dimExpr * stride : dimExpr * stride;
139 if (size > 0) {
140 runningSize *= size;
141 if (runningSize <= 0)
142 return nullptr;
143 } else
144 dynamicPoisonBit = true;
145 }
146 return expr;
147}
148
149// Construct a linearized affine expression for the upd op.
150inline mlir::AffineExpr constructLinearizedAffineExprForUPDOp(UPDOp updOp) {
151 auto memRefType = llvm::cast<mlir::MemRefType>(updOp.getSource().getType());
152 mlir::MLIRContext *context = memRefType.getContext();
153
154 llvm::SmallVector<mlir::AffineExpr, 8> exprVec;
155 llvm::SmallDenseMap<mlir::Value, mlir::AffineExpr, 8> indexToExprDimMap;
156 for (auto idxAndValue : llvm::enumerate(updOp.getIndices())) {
157 auto value = idxAndValue.value();
158 if (auto apOf = value.getDefiningOp<mlir::affine::AffineApplyOp>()) {
159 mlir::AffineMap map = apOf.getAffineMap();
160 // Cannot create linearized mlir::AffineExpr for complicated index.
161 if (map.getNumResults() != 1)
162 return nullptr;
163
164 llvm::SmallVector<mlir::AffineExpr, 4> indexExprs;
165
166 for (auto index : apOf.getMapOperands())
167 if (auto cIdx = index.getDefiningOp<mlir::arith::ConstantOp>()) {
168 auto idxVal =
169 llvm::cast<mlir::IntegerAttr>(cIdx.getValue()).getValue();
170 unsigned idx = idxVal.getSExtValue();
171 indexExprs.push_back(getAffineConstantExpr(idx, context));
172 } else {
173 if (!indexToExprDimMap.count(index))
174 indexToExprDimMap[index] =
175 getAffineDimExpr(indexToExprDimMap.size(), context);
176 indexExprs.push_back(indexToExprDimMap[index]);
177 }
178
179 exprVec.push_back(map.getResult(0).replaceDims(indexExprs));
180 } else if (auto cOp = value.getDefiningOp<mlir::arith::ConstantOp>()) {
181 auto idxVal = llvm::cast<mlir::IntegerAttr>(cOp.getValue()).getValue();
182 unsigned idx = idxVal.getSExtValue();
183 exprVec.push_back(getAffineConstantExpr(idx, context));
184 } else {
185 if (!indexToExprDimMap.count(value))
186 indexToExprDimMap[value] =
187 getAffineDimExpr(indexToExprDimMap.size(), context);
188 exprVec.push_back(indexToExprDimMap[value]);
189 }
190 }
191
192 if (exprVec.empty())
193 return nullptr;
194
195 auto ret = flattenedStridedExpr(memRefType.getShape(), exprVec,
196 memRefType.getContext());
197 return ret;
198}
199
200// From a linearized affine expression, compute the base and the constant
201// offset. If the access is A[i][j+2] for an N*N array A, the linearized
202// expression will be A[i*N+j+2]. The base in this case will be (i*N+j), and the
203// offset will be 2.
204inline std::pair<mlir::AffineExpr, int32_t>
205extractBaseAndOffset(mlir::AffineExpr expr) {
206 mlir::AffineExpr base = expr;
207 int32_t offset = 0;
208
209 if (auto constExpr = llvm::dyn_cast<mlir::AffineConstantExpr>(expr)) {
210 base = nullptr;
211 offset += constExpr.getValue();
212 } else if (auto binopExpr = llvm::dyn_cast<mlir::AffineBinaryOpExpr>(expr)) {
213 if (binopExpr.getKind() == mlir::AffineExprKind::Add) {
214 mlir::AffineExpr lhs = binopExpr.getLHS(), rhs = binopExpr.getRHS();
215 if (auto constExpr = llvm::dyn_cast<mlir::AffineConstantExpr>(lhs)) {
216 base = rhs;
217 offset += constExpr.getValue();
218 }
219 if (auto constExpr = llvm::dyn_cast<mlir::AffineConstantExpr>(rhs)) {
220 base = base == rhs ? nullptr : lhs;
221 offset += constExpr.getValue();
222 }
223 }
224 }
225 return std::make_pair(base, offset);
226}
227
228// MLIR-AIE auto-vectorization to CPP flow currently doesn't support to
229// implicitly broadcast a dynamic dimension of size `1`. Hence, we assume that
230// dynamic dimensions are not with size '1' that can be interpreted to various
231// broadcasting scenarios. We let lowerings assume this on a per-scope basis if
232// the tosa.no_implicit_broadcast_of_dynamic_sizes attribute presents on any
233// parent of the block.
234inline bool isAssumingNoImplicitBroadcastOfDynamicSizes(mlir::Block *block) {
235 for (mlir::Operation *parentOp = block->getParentOp(); parentOp;
236 parentOp = parentOp->getParentOp())
237 if (parentOp->hasAttr("tosa.no_implicit_broadcast_of_dynamic_sizes"))
238 return true;
239 return false;
240}
241
242// Helper that uses the block from an OpBuilder for determining whether we
243// are assuming no implict broadcast of dynamic sizes
244inline bool
246 return isAssumingNoImplicitBroadcastOfDynamicSizes(builder.getBlock());
247}
248
249} // namespace xilinx::aievec
250// end namespace xilinx
251
252#endif // AIE_DIALECT_AIEVEC_AIEVECUTILS_H
bool isPowerOfTwo(int32_t n)
Definition AIEVecUtils.h:38
int32_t getVectorSizeInBits(mlir::VectorType type)
Definition AIEVecUtils.h:65
unsigned getVectorLaneSize(mlir::VectorType type)
Definition AIEVecUtils.h:54
bool isAssumingNoImplicitBroadcastOfDynamicSizes(mlir::Block *block)
char getHexValue(int val)
Definition AIEVecUtils.h:30
std::pair< mlir::AffineExpr, int32_t > extractBaseAndOffset(mlir::AffineExpr expr)
mlir::AffineExpr constructLinearizedAffineExprForUPDOp(UPDOp updOp)
mlir::VectorType createVectorType(unsigned lanes, mlir::Type elementType)
Definition AIEVecUtils.h:41
int32_t getElementSizeInBits(mlir::VectorType type)
Definition AIEVecUtils.h:48
mlir::VectorType getVectorOpDestType(mlir::VectorType type, bool AIE2)
Definition AIEVecUtils.h:79
bool isAIEOp(mlir::Operation *op)
Definition AIEVecUtils.h:72
mlir::AffineExpr flattenedStridedExpr(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< mlir::AffineExpr > exprs, mlir::MLIRContext *context)