MLIR-AIE 1.4.5.dev
FoldMulAddChainToConvOp.cpp
Go to the documentation of this file.
1//===--FoldMulAddChainToConvOp.cpp - Fold Mul Add Chain To AIEVec Conv Op--===//
2//
3// Copyright (C) 2022 Xilinx, Inc.
4// Copyright (C) 2022-2026 Advanced Micro Devices, Inc.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This is the implementation of the folding pass from mul add chain
9// to AIEVec convolution operations, compatible with the AIE2 architecture.
10//===----------------------------------------------------------------------===//
11
13
18#include "mlir/Analysis/SliceAnalysis.h"
19#include "mlir/Dialect/Arith/IR/Arith.h"
20#include "mlir/Dialect/UB/IR/UBOps.h"
21#include "mlir/IR/PatternMatch.h"
22#include "mlir/Transforms/DialectConversion.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/Support/Debug.h"
25
26#define DEBUG_TYPE "fold-mul-add-chain-to-conv"
27
28using namespace mlir;
29using namespace arith;
30using namespace vector;
31using namespace xilinx;
32using namespace xilinx::aievec;
33
34namespace xilinx::aievec {
35#define GEN_PASS_DEF_AIEVECCONVANALYSIS
36#include "aie/Dialect/AIEVec/Analysis/Passes.h.inc"
37} // namespace xilinx::aievec
38
39/// This analysis builds the longest possible chain of MAC operations whose
40/// operands are a vector that may or may not be shifted, and a broadcast.
41/// That is, these MACs represent `vector x scalar` ops, and are candidates to
42/// be grouped and replaced by mul_conv/fma_conv ops in AIE2.
43//
44// We build this chain recursively, climbing up the
46 static AnalysisManager *am;
47
48 struct ConvMac {
49 // If there's a non-accumulating convolution upchain,
50 // store it here temorarily.
51 std::unique_ptr<ConvMac> topOfChainMulConv;
52 // Accumulator value, if there is one.
53 Value acc;
54 // Left-hand side (non-broadcasting) source value
55 Value lhs;
56 // Left-hand side (broadcasting) source value
57 Value rhs;
58 // Amount that lhs is shifted
59 uint8_t shift;
60 // Element in rhs that is broadcasted
61 uint8_t bcastIdx;
62 ConvMac(Value lhs, Value rhs, uint8_t shift, uint8_t bcastIdx)
63 : topOfChainMulConv(nullptr), acc(nullptr), lhs(lhs), rhs(rhs),
65 };
66
68 // Group start index within the chain
69 uint64_t fromIdx;
70 // Index in chain after group last MAC
71 uint64_t toIdx;
72 // Initial position of the signal to be convolved
73 int64_t signalShift;
74 // Initial position of the convolution filter
75 int64_t bcastShift;
76 // Distance between elements in the filter
77 int64_t bcastDist; // Must be 1 or 2
78 };
79
80 using ConvMacChain = SmallVector<std::unique_ptr<ConvMac>, 8>;
81 using ConvMacChainGroupList = SmallVector<ConvMacChainGroup, 8>;
82
83 std::unique_ptr<ConvMacChain> convMacChain;
85
86 /// Sort the chain of MACs by sources. When two MACs share the same sources,
87 /// sort them by the broadcast index. If they don't, sort them by the order
88 /// of the ops in the code. This function should be called after the chain
89 /// is completed, and before operating on the groups of MACs. After sorting,
90 /// MACs that can be fused into single convolution ops will be contiguous in
91 /// the chain.
92 void sortChain() {
93 if ((*convMacChain)[0]->acc) {
94 std::sort(convMacChain->begin(), convMacChain->end(),
95 [](const auto &a, const auto &b) {
96 if (a->lhs == b->lhs) {
97 if (a->rhs == b->rhs)
98 return a->bcastIdx < b->bcastIdx;
99 return a->rhs.getDefiningOp()->isBeforeInBlock(
100 b->rhs.getDefiningOp());
101 }
102 // We should probably sort by lhs load address, if it exists
103 // XXX: We assume all MACs in the same block. If they're not,
104 // XXX: this will assert.
105 return a->lhs.getDefiningOp()->isBeforeInBlock(
106 b->lhs.getDefiningOp());
107 });
108 } else {
109 // If the top of the chain is not an accumulation, bring up all related
110 // convolution MACs and sort the rest by lhs.
111 auto firstLhs = (*convMacChain)[0]->lhs;
112 std::sort(convMacChain->begin(), convMacChain->end(),
113 [&firstLhs](const auto &a, const auto &b) {
114 if (a->lhs == b->lhs) {
115 if (a->rhs == b->rhs)
116 return a->bcastIdx < b->bcastIdx;
117 return a->rhs.getDefiningOp()->isBeforeInBlock(
118 b->rhs.getDefiningOp());
119 }
120 if (a->lhs == firstLhs)
121 return true;
122 if (b->lhs == firstLhs)
123 return false;
124 return a->lhs.getDefiningOp()->isBeforeInBlock(
125 b->lhs.getDefiningOp());
126 });
127 // Float the empty accumulator to the top.
128 if ((*convMacChain)[0]->acc)
129 for (auto &convMac : *convMacChain)
130 if (!convMac->acc) {
131 std::swap((*convMacChain)[0]->acc, convMac->acc);
132 break;
133 }
134 }
135 }
136
137 // Return the list of convolution mac ops in the chain as pairs of indices
138 // indicating the position within the chain where a group starts and the
139 // position where it ends: [start, end). If they have not been precomputed
140 // yet, this method will generate them.
142 // If there's no group or it's been computed already, return stored list.
143 if (!groupsInChain.empty() || !convMacChain || convMacChain->empty())
144 return groupsInChain;
145
146 uint64_t grpStartIdx = 0;
147 uint64_t grpCurIdx = 0;
148 Value curLhs = (*convMacChain)[0]->lhs;
149 Value curRhs = (*convMacChain)[0]->rhs;
150 for (const auto &convMac : *convMacChain) {
151 if (grpCurIdx > grpStartIdx) {
152 if (curLhs != convMac->lhs || curRhs != convMac->rhs) {
153 groupsInChain.push_back({grpStartIdx, grpCurIdx,
154 getGroupSignalShift(grpStartIdx, grpCurIdx),
155 getGroupBcastShift(grpStartIdx, grpCurIdx),
156 getGroupBcastDist(grpStartIdx, grpCurIdx)});
157 grpStartIdx = grpCurIdx;
158 curLhs = convMac->lhs;
159 curRhs = convMac->rhs;
160 }
161 }
162 grpCurIdx++;
163 }
164 if (grpStartIdx < grpCurIdx)
165 groupsInChain.push_back({grpStartIdx, grpCurIdx,
166 getGroupSignalShift(grpStartIdx, grpCurIdx),
167 getGroupBcastShift(grpStartIdx, grpCurIdx),
168 getGroupBcastDist(grpStartIdx, grpCurIdx)});
169 return groupsInChain;
170 }
171
172 // Return the signal shift for the group in the MAC chain in [fromIdx, toIdx)
173 // the top. This method verifies that the elements of the signal are
174 // contiguously accessed. If they do not, or the specified group doesn't
175 // exist, this function returns -1.
176 int64_t getGroupSignalShift(uint64_t fromIdx, uint64_t toIdx) {
177 if (fromIdx >= toIdx || toIdx > convMacChain->size())
178 return -1;
179 if (toIdx == fromIdx + 1)
180 return static_cast<int64_t>((*convMacChain)[fromIdx]->shift);
181 for (uint64_t i = fromIdx; i < toIdx - 1; i++)
182 if ((static_cast<int64_t>((*convMacChain)[i + 1]->shift) -
183 static_cast<int64_t>((*convMacChain)[i]->shift)) != 1)
184 return -1;
185 return static_cast<int64_t>((*convMacChain)[fromIdx]->shift);
186 }
187
188 // Return the shift in value of the first broadcasted element in the i-th
189 // group. If there is no chain, or the i-th group does not exist,
190 // returns -1.
191 int64_t getGroupBcastShift(uint64_t fromIdx, uint64_t toIdx) {
192 if (fromIdx >= toIdx || toIdx > convMacChain->size())
193 return -1;
194 return static_cast<int64_t>((*convMacChain)[fromIdx]->bcastIdx);
195 }
196
197 // Returns the broadcast distance between elements within the group. If the
198 // distance is not constant and equal to 1 or 2, it returns -1.
199 int64_t getGroupBcastDist(uint64_t fromIdx, uint64_t toIdx) {
200 if (fromIdx >= toIdx || toIdx > convMacChain->size())
201 return -1;
202 if (toIdx == fromIdx + 1)
203 return 1;
204 int64_t bcastDist =
205 static_cast<int64_t>((*convMacChain)[fromIdx + 1]->bcastIdx) -
206 static_cast<int64_t>((*convMacChain)[fromIdx]->bcastIdx);
207 if (bcastDist != 1 && bcastDist != 2)
208 return -1;
209 for (uint64_t i = fromIdx + 1; i < toIdx - 1; i++)
210 if ((static_cast<int64_t>((*convMacChain)[i + 1]->bcastIdx) -
211 static_cast<int64_t>((*convMacChain)[i]->bcastIdx)) != bcastDist)
212 return -1;
213 return bcastDist;
214 }
215
217 const auto &groups = getGroupsInChain();
218 if (groups.empty())
219 return false;
220 return llvm::all_of(groups, [](const auto &group) {
221 return group.signalShift != -1 && group.bcastShift != -1 &&
222 group.bcastDist != -1;
223 });
224 }
225
226 std::unique_ptr<ConvMac> getConvMacFromMulOp(arith::MulIOp mulOp) {
227 auto *mulOpLhsDefOp = mulOp.getLhs().getDefiningOp();
228 auto *mulOpRhsDefOp = mulOp.getRhs().getDefiningOp();
229 if (!mulOpLhsDefOp || !mulOpRhsDefOp)
230 return nullptr;
231
232 Value convMacRhs = nullptr;
233 uint8_t convMacBcastIdx = 0;
234
235 auto getConvMacRhs = [&](Operation *mulOpOperand) -> bool {
236 SetVector<Operation *> opBwdSlices;
237 auto opFilter = [](Operation *op) {
238 return isa<aievec::BroadcastOp>(op) || isa<aievec::ExtOp>(op) ||
239 isa<aievec::ConcatOp>(op);
240 };
241 BackwardSliceOptions backwardSliceOptions;
242 backwardSliceOptions.filter = opFilter;
243
244 (void)getBackwardSlice(mulOpOperand, &opBwdSlices, backwardSliceOptions);
245 opBwdSlices.insert(mulOpOperand);
246
247 LLVM_DEBUG(llvm::dbgs() << "opBwdSlices = [\n");
248 for ([[maybe_unused]] auto *op : opBwdSlices) {
249 LLVM_DEBUG(llvm::dbgs() << *op << "\n");
250 }
251 LLVM_DEBUG(llvm::dbgs() << "]\n");
252
253 if (opBwdSlices.size() == 1) {
254 if (auto bcastOp = dyn_cast<aievec::BroadcastOp>(opBwdSlices[0])) {
255 convMacRhs = bcastOp.getSource();
256 convMacBcastIdx = bcastOp.getIdx();
257 return true;
258 }
259 } else if (opBwdSlices.size() >= 3) {
260 auto sliceSz = opBwdSlices.size();
261 if ((isa<aievec::ExtOp>(opBwdSlices[sliceSz - 3]) &&
262 isa<aievec::BroadcastOp>(opBwdSlices[sliceSz - 2]) &&
263 isa<aievec::ConcatOp>(opBwdSlices[sliceSz - 1])) ||
264 (isa<aievec::ConcatOp>(opBwdSlices[sliceSz - 3]) &&
265 isa<aievec::BroadcastOp>(opBwdSlices[sliceSz - 2]) &&
266 isa<aievec::ExtOp>(opBwdSlices[sliceSz - 1]))) {
267 convMacRhs = opBwdSlices[sliceSz - 3]->getOperand(0);
268 convMacBcastIdx =
269 cast<aievec::BroadcastOp>(opBwdSlices[sliceSz - 2]).getIdx();
270 return true;
271 }
272 }
273
274 return false;
275 };
276
277 // Obtain the broadcast operation feeding into the MulIOp
278 if (!getConvMacRhs(mulOpRhsDefOp)) {
279 if (getConvMacRhs(mulOpLhsDefOp)) {
280 std::swap(mulOpLhsDefOp, mulOpRhsDefOp);
281 }
282 }
283 if (!convMacRhs)
284 return nullptr;
285
286 // Obtain the ext or ext->shift op feeding into the MulIOp
287 aievec::ExtOp extOp;
288 aievec::ShiftOp shiftOp;
289 shiftOp = dyn_cast<aievec::ShiftOp>(mulOpLhsDefOp);
290 if (shiftOp)
291 extOp = shiftOp.getLhs().getDefiningOp<aievec::ExtOp>();
292 else
293 extOp = dyn_cast<aievec::ExtOp>(mulOpLhsDefOp);
294
295 // The ExtOp may be absent; bail out of the fold in that case.
296 if (!extOp)
297 return nullptr;
298
299 Value convMacLhs = extOp.getSource();
300 uint8_t shift = 0;
301 if (shiftOp) {
302 auto shiftConstDefOp =
303 shiftOp.getShift().getDefiningOp<arith::ConstantOp>();
304 if (shiftConstDefOp) {
305 auto shiftAttr = cast<IntegerAttr>(shiftConstDefOp.getValue());
306 auto vType = cast<VectorType>(mulOp.getResult().getType());
307 shift = 8 * shiftAttr.getInt() / getElementSizeInBits(vType);
308 }
309 }
310
311 return std::make_unique<ConvMac>(convMacLhs, convMacRhs, shift,
312 convMacBcastIdx);
313 }
314
315 std::unique_ptr<ConvMac> getConvMacFromAddOp(arith::AddIOp addOp) {
316 // Make sure at least one of them is a multiplication, and the other one
317 // is the accumulator coming form upchain.
318 auto mulOp = addOp.getLhs().getDefiningOp<arith::MulIOp>();
319 Value acc = addOp.getRhs();
320 if (!mulOp) {
321 mulOp = addOp.getRhs().getDefiningOp<arith::MulIOp>();
322 acc = addOp.getLhs();
323 }
324 if (!mulOp)
325 return nullptr;
326
327 // Get the parameters of the convolution from the operands of the MulIOp
328 auto convMac = getConvMacFromMulOp(mulOp);
329 if (!convMac)
330 return nullptr;
331
332 // If both sides are MulIOp, we might be at the top of the chain
333 auto upChainAccMulOp = acc.getDefiningOp<arith::MulIOp>();
334 if (upChainAccMulOp) {
335 auto convMac2 = getConvMacFromMulOp(upChainAccMulOp);
336 // Pre-sort the top two MACs so that an undefined accumulator ends up
337 // at the top of the chain.
338 if (convMac2 && convMac->lhs == convMac2->lhs &&
339 convMac->rhs == convMac2->rhs) {
340 if (convMac->bcastIdx < convMac2->bcastIdx &&
341 convMac->shift < convMac2->shift) {
342 convMac2->topOfChainMulConv = std::move(convMac);
343 convMac2->acc = acc;
344 return convMac2;
345 }
346 if (convMac->bcastIdx > convMac2->bcastIdx &&
347 convMac->shift > convMac2->shift) {
348 convMac->topOfChainMulConv = std::move(convMac2);
349 convMac->acc = acc;
350 return convMac;
351 }
352 // WARNING: In this situation, the chain is ambiguous and
353 // picking one WARNING: option over the other may result in a
354 // successful WARNING: and/or better replacement. Here, we are
355 // assuming that WARNING: is going to be either one or the
356 // other, or it won't WARNING: matter.
357 } else {
358 convMac->topOfChainMulConv = std::move(convMac2);
359 }
360 }
361 convMac->acc = acc;
362 return convMac;
363 }
364
365 LongestConvMACChainAnalysis(arith::AddIOp addOp) {
366 std::unique_ptr<ConvMac> macConvChainElem = getConvMacFromAddOp(addOp);
367 if (!macConvChainElem)
368 return;
369
370 if (macConvChainElem->acc) {
371 auto upChainAddOp = macConvChainElem->acc.getDefiningOp<arith::AddIOp>();
372 if (upChainAddOp) {
373 auto &upChainChainAnalysis =
374 am->getChildAnalysis<LongestConvMACChainAnalysis>(upChainAddOp);
375 if (upChainChainAnalysis.convMacChain) {
376 convMacChain = std::move(upChainChainAnalysis.convMacChain);
377 convMacChain->push_back(std::move(macConvChainElem));
378 return;
379 }
380 }
381 }
382 assert(!convMacChain && "Convolution MAC chain unexpectedly not empty");
383 convMacChain = std::make_unique<ConvMacChain>();
384 if (macConvChainElem->topOfChainMulConv)
385 convMacChain->push_back(std::move(macConvChainElem->topOfChainMulConv));
386 convMacChain->push_back(std::move(macConvChainElem));
387 }
388};
389// The analysis manager is not directly accessible from within an analysis, but
390// it is needed to build the MAC chain recursively, so it is held here.
391AnalysisManager *LongestConvMACChainAnalysis::am = nullptr;
392
393// This conversion pattern folds a MAC chain into mul_conv and mac_conv
394// ops. We can handle the mul MAC with a random order.
396 : public OpConversionPattern<arith::AddIOp> {
397 using OpConversionPattern<arith::AddIOp>::OpConversionPattern;
398
399 FoldMulAddChainToConvOpPattern(MLIRContext *context, AnalysisManager &am,
400 unsigned shiftParam = 0)
401 : OpConversionPattern<arith::AddIOp>(context), am(am),
403
404 LogicalResult
405 matchAndRewrite(arith::AddIOp srcOp, OpAdaptor adaptor,
406 ConversionPatternRewriter &rewriter) const override {
407 auto &convMacChainAnalysis =
408 am.getChildAnalysis<LongestConvMACChainAnalysis>(srcOp);
409 auto &convMacChain = convMacChainAnalysis.convMacChain;
410 if (!convMacChain)
411 return failure();
412
413 auto loc = srcOp.getLoc();
414 VectorType vecTy = cast<VectorType>(srcOp.getResult().getType());
415 unsigned elemWidth = cast<IntegerType>(vecTy.getElementType()).getWidth();
416 unsigned accWidth = elemWidth <= 8 ? 32 : 64;
417 int32_t M = elemWidth == 8 ? 32 : 16;
418 int32_t N = elemWidth == 8 ? 8 : 4;
419
420 Type wideElemTy = IntegerType::get(getContext(), accWidth);
421 Type accVecTy = VectorType::get(vecTy.getShape(), wideElemTy);
422
423 const auto &groups = convMacChainAnalysis.getGroupsInChain();
424 Value grpAcc = (*convMacChain)[groups[0].fromIdx]->acc;
425 if (grpAcc)
426 grpAcc = aievec::UPSOp::create(rewriter, srcOp.getLoc(), accVecTy, grpAcc,
427 /*shift=*/0)
428 .getResult();
429 for (const auto &group : groups) {
430 Value grpLhs = (*convMacChain)[group.fromIdx]->lhs;
431 Value grpRhs = (*convMacChain)[group.fromIdx]->rhs;
432 auto filterVecTy = cast<VectorType>(grpRhs.getType());
433 auto signalVecTy = cast<VectorType>(grpLhs.getType());
434 // Sort out the vector used as filter
435 // If the length of the filter is half that of the signal, concatenate
436 // the filter with itself.
437 if (2 * filterVecTy.getShape()[0] == signalVecTy.getShape()[0])
438 grpRhs =
439 aievec::ConcatOp::create(rewriter, loc, signalVecTy,
440 SmallVector<Value, 2>({grpRhs, grpRhs}))
441 .getResult();
442 // If the filter has duplicate elements, pack them.
443 if (group.bcastDist == 2)
444 // NOTE: This shuffle mode works for `vector<64xi8>`
445 grpRhs = aievec::ShuffleOp::create(rewriter, loc, signalVecTy, grpRhs,
446 grpRhs, ShuffleMode::T8_64X2_LO)
447 .getResult();
448 // If the first element of the filter to be used is not 0, shift the
449 // filter to align the first element to the beginning.
450 if (group.bcastShift) {
451 int32_t shiftBytes =
452 group.bcastShift * getElementSizeInBits(filterVecTy) >>
453 (3 + group.bcastDist - 1);
454 auto shiftBytesCst =
455 arith::ConstantOp::create(rewriter, loc,
456 rewriter.getI32IntegerAttr(shiftBytes))
457 .getResult();
458 grpRhs =
459 aievec::ShiftOp::create(rewriter, grpRhs.getDefiningOp()->getLoc(),
460 signalVecTy, grpRhs, grpRhs, shiftBytesCst)
461 .getResult();
462 }
463 // Sort out the vector used as signal
464 // If the signal to be convolved doesn't start at element 0, shift the
465 // signal to align the first element to the beginning.
466 if (group.signalShift) {
467 int32_t shiftBytes =
468 group.signalShift * getElementSizeInBits(signalVecTy) >> 3;
469 auto shiftBytesCst =
470 arith::ConstantOp::create(rewriter, loc,
471 rewriter.getI32IntegerAttr(shiftBytes))
472 .getResult();
473 grpLhs = aievec::ShiftOp::create(rewriter, loc, signalVecTy, grpLhs,
474 grpLhs, shiftBytesCst)
475 .getResult();
476 }
477 // Generate a convolution operation for the group
478 // If there is no upchain accumulator, use a mul_conv; use a mac_conv
479 // otherwise.
480 if (!grpAcc)
481 grpAcc = aievec::MulConvOp::create(rewriter, srcOp.getLoc(), accVecTy,
482 grpLhs, grpRhs, M, N)
483 .getResult();
484 else
485 grpAcc = aievec::FMAConvOp::create(rewriter, srcOp.getLoc(), accVecTy,
486 grpLhs, grpRhs, grpAcc, M, N, false)
487 .getResult();
488 }
489
490 auto shiftParamOp = arith::ConstantOp::create(
491 rewriter, srcOp.getLoc(), rewriter.getI32IntegerAttr(shiftParam));
492 rewriter.replaceOpWithNewOp<aievec::SRSOp>(srcOp, vecTy, grpAcc,
493 shiftParamOp.getResult());
494 return success();
495 }
496
497 AnalysisManager &am;
498 unsigned shiftParam;
499};
500
501namespace xilinx::aievec {
502
504 AnalysisManager &am) {
506 target.addLegalDialect<AIEVecDialect>();
507 target.addLegalDialect<arith::ArithDialect>();
508 target.addLegalDialect<ub::UBDialect>();
509 target.addDynamicallyLegalOp<arith::AddIOp>([&am](arith::AddIOp op) {
510 auto &convAnalysis = am.getChildAnalysis<LongestConvMACChainAnalysis>(op);
511 return !convAnalysis.canChainBeReplacedWithConvOps();
512 });
513}
514
515void populateAIEVecConvOpTransformationPatterns(RewritePatternSet &patterns,
516 AnalysisManager &am,
517 unsigned shiftParam) {
518 patterns.add<FoldMulAddChainToConvOpPattern>(patterns.getContext(), am,
519 shiftParam);
520}
521
523 : public xilinx::aievec::impl::AIEVecConvAnalysisBase<AIEVecConvAnalysis> {
528
529 void runOnOperation() override {
530 markAllAnalysesPreserved();
531 AnalysisManager am = getAnalysisManager();
533 Operation *op = getOperation();
534
535 // Compute all the chains
536 op->walk([&](arith::AddIOp addOp) {
537 if (isa<VectorType>(addOp.getResult().getType()))
538 am.getChildAnalysis<LongestConvMACChainAnalysis>(addOp);
539 });
540
541 // Sort the chains, ready to split by group
542 op->walk([&](arith::AddIOp addOp) {
543 if (isa<VectorType>(addOp.getResult().getType())) {
544 auto &analysis =
545 am.getChildAnalysis<LongestConvMACChainAnalysis>(addOp);
546 if (analysis.convMacChain)
547 analysis.sortChain();
548 }
549 });
550
551 if (printResult) {
552 op->walk([&](arith::AddIOp addOp) {
553 if (isa<VectorType>(addOp.getResult().getType())) {
554 auto &macChainAnalysis =
555 am.getChildAnalysis<LongestConvMACChainAnalysis>(addOp);
556 if (macChainAnalysis.canChainBeReplacedWithConvOps()) {
557 addOp.print(llvm::outs());
558 llvm::outs() << " is at the end of a convolution MAC Chain:\n";
559 listChain(macChainAnalysis.convMacChain,
560 macChainAnalysis.getGroupsInChain());
561 }
562 }
563 });
564 }
565 }
566
567 void listChain(const std::unique_ptr<ConvMacChain> &chain,
568 const ConvMacChainGroupList &groups) const {
569 uint64_t gIdx = 0;
570 for (const auto &group : groups) {
571 llvm::outs() << "-------------- GROUP " << std::to_string(gIdx)
572 << " --------------\n";
573 llvm::outs() << " Signal Shift: " << std::to_string(group.signalShift)
574 << " Kernel Shift: " << std::to_string(group.bcastShift)
575 << " Kernel Duplication: "
576 << std::to_string(group.bcastDist) << "\n";
577 for (uint64_t i = group.fromIdx; i < group.toIdx; i++) {
578 auto shift = (*chain)[i]->shift;
579 auto bcastIdx = (*chain)[i]->bcastIdx;
580 auto *lhsOp = (*chain)[i]->lhs.getDefiningOp();
581 auto *rhsOp = (*chain)[i]->rhs.getDefiningOp();
582 if (!(*chain)[i]->acc)
583 llvm::outs() << " [mul_conv]\n";
584 llvm::outs() << " [Shift: " << std::to_string(shift) << "]: ";
585 lhsOp->print(llvm::outs());
586 llvm::outs() << "\n [Bcast: " << std::to_string(bcastIdx) << "]: ";
587 rhsOp->print(llvm::outs());
588 llvm::outs() << "\n";
589 }
590 gIdx++;
591 }
592 llvm::outs() << "-------------------------------------\n";
593 }
594};
595
596std::unique_ptr<Pass> createAIEVecConvolutionAnalysisPass() {
597 return std::make_unique<AIEVecConvAnalysis>();
598}
599
600} // namespace xilinx::aievec
std::unique_ptr< mlir::Pass > createAIEVecConvolutionAnalysisPass()
int32_t getElementSizeInBits(mlir::VectorType type)
Definition AIEVecUtils.h:48
void configureAIEVecConvOpTransformationLegalizations(ConversionTarget &target, AnalysisManager &am)
void populateAIEVecConvOpTransformationPatterns(RewritePatternSet &patterns, AnalysisManager &am, unsigned shiftParam)
LogicalResult matchAndRewrite(arith::AddIOp srcOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
FoldMulAddChainToConvOpPattern(MLIRContext *context, AnalysisManager &am, unsigned shiftParam=0)
ConvMac(Value lhs, Value rhs, uint8_t shift, uint8_t bcastIdx)
This analysis builds the longest possible chain of MAC operations whose operands are a vector that ma...
const ConvMacChainGroupList & getGroupsInChain()
SmallVector< ConvMacChainGroup, 8 > ConvMacChainGroupList
void sortChain()
Sort the chain of MACs by sources.
int64_t getGroupBcastShift(uint64_t fromIdx, uint64_t toIdx)
SmallVector< std::unique_ptr< ConvMac >, 8 > ConvMacChain
std::unique_ptr< ConvMacChain > convMacChain
LongestConvMACChainAnalysis(arith::AddIOp addOp)
std::unique_ptr< ConvMac > getConvMacFromAddOp(arith::AddIOp addOp)
int64_t getGroupSignalShift(uint64_t fromIdx, uint64_t toIdx)
std::unique_ptr< ConvMac > getConvMacFromMulOp(arith::MulIOp mulOp)
int64_t getGroupBcastDist(uint64_t fromIdx, uint64_t toIdx)
LongestConvMACChainAnalysis::ConvMacChain ConvMacChain
void listChain(const std::unique_ptr< ConvMacChain > &chain, const ConvMacChainGroupList &groups) const
LongestConvMACChainAnalysis::ConvMacChainGroupList ConvMacChainGroupList