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