MLIR-AIE 1.4.5.dev
AIEHoistVectorTransferPointers.cpp
Go to the documentation of this file.
1//===- AIEHoistVectorTransferPointers.cpp -----------------------*- C++ -*-===//
2//
3// Copyright (C) 2025 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7//
8// This pass hoists vector transfer operations with IV-dependent pointers
9// out of scf.for loops by using iter_args to track pointer updates. This
10// optimization reduces address computation overhead in loops by maintaining
11// a running pointer offset rather than recomputing addresses each iteration.
12//
13//===----------------------------------------------------------------------===//
14
17
18#include "mlir/Dialect/Affine/IR/AffineOps.h"
19#include "mlir/Dialect/Arith/IR/Arith.h"
20#include "mlir/Dialect/Func/IR/FuncOps.h"
21#include "mlir/Dialect/MemRef/IR/MemRef.h"
22#include "mlir/Dialect/MemRef/Utils/MemRefUtils.h"
23#include "mlir/Dialect/SCF/IR/SCF.h"
24#include "mlir/Dialect/SCF/Utils/Utils.h"
25#include "mlir/Dialect/Utils/StaticValueUtils.h"
26#include "mlir/Dialect/Vector/IR/VectorOps.h"
27#include "mlir/IR/IRMapping.h"
28#include "mlir/IR/PatternMatch.h"
29#include "mlir/Interfaces/LoopLikeInterface.h"
30#include "mlir/Interfaces/SideEffectInterfaces.h"
31#include "mlir/Pass/Pass.h"
32#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
33
34namespace xilinx::AIE {
35#define GEN_PASS_DEF_AIEHOISTVECTORTRANSFERPOINTERS
36#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
37} // namespace xilinx::AIE
38
39#define DEBUG_TYPE "aie-hoist-vector-transfer-pointers"
40
41using namespace mlir;
42using namespace xilinx;
43using namespace xilinx::AIE;
44
45namespace {
46
47//===----------------------------------------------------------------------===//
48// Helper Functions
49//===----------------------------------------------------------------------===//
50
51/// The coefficient of `iv` in the linear expression `expr`, given the
52/// coefficient of each of the map's operands (dims first, then symbols).
53/// nullopt if `expr` is not linear in `iv`.
54static std::optional<int64_t> getExprCoefficient(AffineExpr expr,
55 ArrayRef<int64_t> operandCoefs,
56 unsigned numDims) {
57 if (auto dim = dyn_cast<AffineDimExpr>(expr))
58 return operandCoefs[dim.getPosition()];
59 if (auto sym = dyn_cast<AffineSymbolExpr>(expr))
60 return operandCoefs[numDims + sym.getPosition()];
61 if (isa<AffineConstantExpr>(expr))
62 return 0;
63 auto bin = dyn_cast<AffineBinaryOpExpr>(expr);
64 if (!bin)
65 return std::nullopt;
66 std::optional<int64_t> lhs =
67 getExprCoefficient(bin.getLHS(), operandCoefs, numDims);
68 std::optional<int64_t> rhs =
69 getExprCoefficient(bin.getRHS(), operandCoefs, numDims);
70 if (!lhs || !rhs)
71 return std::nullopt;
72 switch (bin.getKind()) {
73 case AffineExprKind::Add:
74 return *lhs + *rhs;
75 case AffineExprKind::Mul:
76 // In an affine expression one side of a multiplication is a constant.
77 if (auto c = dyn_cast<AffineConstantExpr>(bin.getRHS()))
78 return *lhs * c.getValue();
79 if (auto c = dyn_cast<AffineConstantExpr>(bin.getLHS()))
80 return *rhs * c.getValue();
81 return (*lhs == 0 && *rhs == 0) ? std::optional<int64_t>(0) : std::nullopt;
82 default:
83 // mod, floordiv and ceildiv are linear only if neither side depends on
84 // the IV.
85 return (*lhs == 0 && *rhs == 0) ? std::optional<int64_t>(0) : std::nullopt;
86 }
87}
88
89/// Writes `v` as `coef * iv + inv`, where `inv` does not change across
90/// iterations of `forOp` and can be recomputed before it, and returns `coef`.
91/// nullopt when `v` is not of that form: it reads an iter_arg, loads from
92/// memory, or depends on the IV non-linearly.
93static std::optional<int64_t>
94getIVCoefficient(Value v, scf::ForOp forOp,
95 DenseMap<Value, std::optional<int64_t>> &cache) {
96 if (v == forOp.getInductionVar())
97 return 1;
98 if (forOp.isDefinedOutsideOfLoop(v))
99 return 0;
100 auto it = cache.find(v);
101 if (it != cache.end())
102 return it->second;
103
104 std::optional<int64_t> result;
105 Operation *def = v.getDefiningOp();
106 // A block argument other than the IV (an iter_arg) changes every iteration.
107 if (def && isPure(def)) {
108 SmallVector<int64_t> coefs;
109 bool linearOperands = true;
110 for (Value operand : def->getOperands()) {
111 std::optional<int64_t> c = getIVCoefficient(operand, forOp, cache);
112 if (!c) {
113 linearOperands = false;
114 break;
115 }
116 coefs.push_back(*c);
117 }
118 if (linearOperands) {
119 bool invariant = llvm::all_of(coefs, [](int64_t c) { return c == 0; });
120 if (isa<arith::AddIOp>(def)) {
121 result = coefs[0] + coefs[1];
122 } else if (isa<arith::SubIOp>(def)) {
123 result = coefs[0] - coefs[1];
124 } else if (auto mul = dyn_cast<arith::MulIOp>(def)) {
125 if (std::optional<int64_t> c = getConstantIntValue(mul.getRhs()))
126 result = coefs[0] * *c;
127 else if (std::optional<int64_t> c = getConstantIntValue(mul.getLhs()))
128 result = coefs[1] * *c;
129 else if (invariant)
130 result = 0;
131 } else if (auto apply = dyn_cast<affine::AffineApplyOp>(def)) {
132 AffineMap map = apply.getAffineMap();
133 result = getExprCoefficient(map.getResult(0), coefs, map.getNumDims());
134 } else if (invariant) {
135 // Any other pure op over loop-invariant operands is loop-invariant.
136 result = 0;
137 }
138 }
139 }
140 cache[v] = result;
141 return result;
142}
143
144/// Recomputes `v` (of the form getIVCoefficient accepts) before `forOp` with
145/// the IV replaced by the loop's lower bound.
146static Value cloneAtLowerBound(Value v, scf::ForOp forOp, OpBuilder &builder,
147 IRMapping &mapping) {
148 if (v == forOp.getInductionVar())
149 return forOp.getLowerBound();
150 if (forOp.isDefinedOutsideOfLoop(v))
151 return v;
152 if (Value mapped = mapping.lookupOrNull(v))
153 return mapped;
154 Operation *def = v.getDefiningOp();
155 for (Value operand : def->getOperands())
156 mapping.map(operand, cloneAtLowerBound(operand, forOp, builder, mapping));
157 builder.clone(*def, mapping);
158 return mapping.lookup(v);
159}
160
161/// Get the total number of elements in a vector type
162static int64_t getVectorNumElements(VectorType vectorType) {
163 int64_t numElements = 1;
164 for (int64_t dim : vectorType.getShape()) {
165 numElements *= dim;
166 }
167 return numElements;
168}
169
170//===----------------------------------------------------------------------===//
171// HoistVectorTransferPointers Pattern
172//===----------------------------------------------------------------------===//
173
174/// Information about a vector transfer operation
175struct TransferOpInfo {
176 Operation *op;
177 Value base;
178 MemRefType memrefType;
179 VectorType vectorType;
180 SmallVector<Value> indices;
181 int64_t constantStride; // Total constant stride per iteration
182 bool hasIVDependentIndices;
183};
184
185/// Pattern to hoist vector transfer operations with IV-dependent pointers
186/// out of scf.for loops by using iter_args to track pointer updates
187struct HoistVectorTransferPointersPattern
188 : public OpRewritePattern<scf::ForOp> {
189 using OpRewritePattern<scf::ForOp>::OpRewritePattern;
190
191 LogicalResult matchAndRewrite(scf::ForOp forOp,
192 PatternRewriter &rewriter) const override {
193 Location loc = forOp.getLoc();
194
195 // Collect all vector transfer operations with IV-dependent indices
196 SmallVector<TransferOpInfo> transferOps;
197 DenseMap<Value, std::optional<int64_t>> ivCoefs;
198
199 for (Operation &op : forOp.getBody()->without_terminator()) {
200 Value base;
201 VectorType vectorType;
202 SmallVector<Value> indices;
203
204 if (auto readOp = dyn_cast<vector::TransferReadOp>(&op)) {
205 base = readOp.getBase();
206 vectorType = readOp.getVectorType();
207 indices.assign(readOp.getIndices().begin(), readOp.getIndices().end());
208 } else if (auto writeOp = dyn_cast<vector::TransferWriteOp>(&op)) {
209 base = writeOp.getBase();
210 vectorType = writeOp.getVectorType();
211 indices.assign(writeOp.getIndices().begin(),
212 writeOp.getIndices().end());
213 } else {
214 continue;
215 }
216
217 auto memrefType = dyn_cast<MemRefType>(base.getType());
218 if (!memrefType)
219 continue;
220
221 // The rewrite accesses the vector as one contiguous, unmasked, in-bounds
222 // run of the flattened buffer. Skip transfers that are not of that form.
223 auto xfer = cast<VectorTransferOpInterface>(op);
224 if (xfer.getMask() || !xfer.getPermutationMap().isMinorIdentity() ||
225 !llvm::all_of(xfer.getInBoundsValues(), [](bool b) { return b; }))
226 continue;
227 if (!memref::isStaticShapeAndContiguousRowMajor(memrefType))
228 continue;
229 // A vector<4x16> row block is contiguous only if each vector dim inside
230 // its outermost non-unit one spans its whole memref dim (so
231 // vector<1x1x8x8> of memref<4x8x8x8> is, vector<4x16> of memref<64x64>
232 // is not).
233 int64_t vecRank = vectorType.getRank();
234 int64_t memRank = memrefType.getRank();
235 if (vecRank > memRank)
236 continue;
237 int64_t outer = 0;
238 while (outer < vecRank - 1 && vectorType.getDimSize(outer) == 1)
239 ++outer;
240 bool contiguous = true;
241 for (int64_t i = outer + 1; i < vecRank; ++i)
242 if (vectorType.getDimSize(i) !=
243 memrefType.getDimSize(memRank - vecRank + i))
244 contiguous = false;
245 if (!contiguous)
246 continue;
247 // The flattened view is created before the loop.
248 if (!forOp.isDefinedOutsideOfLoop(base))
249 continue;
250
251 // Each index must be coef * iv + (loop-invariant); the pointer then
252 // advances by sum(coef * dimStride) * step elements per iteration.
253 std::optional<int64_t> step =
254 forOp.getConstantStep()
255 ? std::optional<int64_t>(forOp.getConstantStep()->getSExtValue())
256 : std::nullopt;
257 bool analyzable = true;
258 int64_t elementsPerIV = 0;
259 int64_t dimStride = 1;
260 for (int64_t d = memRank - 1; d >= 0; --d) {
261 std::optional<int64_t> coef =
262 getIVCoefficient(indices[d], forOp, ivCoefs);
263 if (!coef) {
264 analyzable = false;
265 break;
266 }
267 elementsPerIV += *coef * dimStride;
268 dimStride *= memrefType.getDimSize(d);
269 }
270 if (!analyzable)
271 continue;
272 bool hasIVDependentIndices = elementsPerIV != 0;
273 if (hasIVDependentIndices && !step)
274 continue;
275 int64_t constantStride =
276 hasIVDependentIndices ? elementsPerIV * *step : 0;
277
278 transferOps.push_back({&op, base, memrefType, vectorType, indices,
279 constantStride, hasIVDependentIndices});
280 }
281
282 // If there are no transfer ops, don't modify
283 if (transferOps.empty())
284 return failure();
285
286 // Prepare to add iter_args for each transfer operation with IV-dependent
287 // indices
288 SmallVector<Value> newInitArgs;
289 SmallVector<Value> flatMemrefs;
290
291 for (const auto &info : transferOps) {
292 if (!info.hasIVDependentIndices)
293 continue;
294
295 // Flatten the memref if needed
296 rewriter.setInsertionPoint(forOp);
297 Value flatMemref = info.base;
298 if (info.memrefType.getRank() > 1) {
299 int64_t totalSize = 1;
300 for (int64_t dim : info.memrefType.getShape()) {
301 if (dim == ShapedType::kDynamic)
302 return failure(); // Dynamic memref shapes not supported
303 totalSize *= dim;
304 }
305
306 // Preserve strided layout if present
307 MemRefType flatMemrefType;
308 if (auto stridedLayout = dyn_cast_or_null<StridedLayoutAttr>(
309 info.memrefType.getLayout())) {
310 // The collapsed stride is the innermost stride (last element)
311 int64_t collapsedStride = stridedLayout.getStrides().back();
312 int64_t offset = stridedLayout.getOffset();
313
314 auto newLayout = StridedLayoutAttr::get(rewriter.getContext(), offset,
315 {collapsedStride});
316 flatMemrefType =
317 MemRefType::get({totalSize}, info.memrefType.getElementType(),
318 newLayout, info.memrefType.getMemorySpace());
319 } else {
320 flatMemrefType =
321 MemRefType::get({totalSize}, info.memrefType.getElementType(),
322 AffineMap(), info.memrefType.getMemorySpace());
323 }
324
325 SmallVector<ReassociationIndices> reassociation;
326 ReassociationIndices allDims;
327 for (size_t i = 0; i < static_cast<size_t>(info.memrefType.getRank());
328 ++i) {
329 allDims.push_back(i);
330 }
331 reassociation.push_back(allDims);
332
333 flatMemref = memref::CollapseShapeOp::create(
334 rewriter, loc, flatMemrefType, info.base, reassociation);
335 }
336 flatMemrefs.push_back(flatMemref);
337
338 // Compute base pointer (with zeros for IV-dependent parts)
339 int64_t rank = info.memrefType.getRank();
340 AffineExpr linearExpr = rewriter.getAffineConstantExpr(0);
341 int64_t stride = 1;
342 for (int64_t i = rank - 1; i >= 0; --i) {
343 linearExpr = linearExpr + rewriter.getAffineDimExpr(i) * stride;
344 if (i > 0)
345 stride *= info.memrefType.getShape()[i];
346 }
347 auto linearMap = AffineMap::get(rank, 0, linearExpr);
348
349 // Initial pointer: every index evaluated at the lower bound.
350 SmallVector<Value> evaluatedIndices;
351 IRMapping indexMapping;
352 for (Value idx : info.indices)
353 evaluatedIndices.push_back(
354 cloneAtLowerBound(idx, forOp, rewriter, indexMapping));
355
356 Value basePointer = affine::AffineApplyOp::create(
357 rewriter, loc, linearMap, evaluatedIndices);
358
359 newInitArgs.push_back(basePointer);
360 }
361
362 // If there are no IV-dependent transfers, just process them to flatten
363 // vectors
364 if (newInitArgs.empty()) {
365 // Check if any transfer needs flattening (avoid infinite rewrites)
366 bool needsFlattening = false;
367 bool hasProcessableTransfers = false;
368 for (const auto &info : transferOps) {
369 // Skip if base is defined inside the loop (e.g., a subview)
370 // We can't hoist these
371 if (info.base.getDefiningOp() &&
372 forOp->isProperAncestor(info.base.getDefiningOp()))
373 continue;
374
375 hasProcessableTransfers = true;
376
377 // Check if this transfer has already been flattened
378 // (flattened transfers use 1D identity map)
379 if (auto readOp = dyn_cast<vector::TransferReadOp>(info.op)) {
380 if (readOp.getPermutationMap().getNumDims() != 1)
381 needsFlattening = true;
382 } else if (auto writeOp = dyn_cast<vector::TransferWriteOp>(info.op)) {
383 if (writeOp.getPermutationMap().getNumDims() != 1)
384 needsFlattening = true;
385 }
386 }
387
388 // If there are no processable transfers (all bases defined in loop)
389 // or nothing needs flattening, bail out
390 if (!hasProcessableTransfers || !needsFlattening)
391 return failure();
392
393 // First, create flattened memrefs outside the loop for bases not defined
394 // inside
395 DenseMap<Value, Value> baseFlatMemrefs;
396 rewriter.setInsertionPoint(forOp);
397 for (const auto &info : transferOps) {
398 if (baseFlatMemrefs.count(info.base))
399 continue;
400
401 // Skip if base is defined inside the loop (e.g., a subview)
402 if (info.base.getDefiningOp() &&
403 forOp->isProperAncestor(info.base.getDefiningOp()))
404 continue;
405
406 Value flatMemref = info.base;
407 if (info.memrefType.getRank() > 1) {
408 int64_t totalSize = 1;
409 for (int64_t dim : info.memrefType.getShape()) {
410 totalSize *= dim;
411 }
412
413 // Preserve strided layout if present
414 MemRefType flatMemrefType;
415 if (auto stridedLayout = dyn_cast_or_null<StridedLayoutAttr>(
416 info.memrefType.getLayout())) {
417 int64_t collapsedStride = stridedLayout.getStrides().back();
418 int64_t offset = stridedLayout.getOffset();
419
420 auto newLayout = StridedLayoutAttr::get(rewriter.getContext(),
421 offset, {collapsedStride});
422 flatMemrefType =
423 MemRefType::get({totalSize}, info.memrefType.getElementType(),
424 newLayout, info.memrefType.getMemorySpace());
425 } else {
426 flatMemrefType =
427 MemRefType::get({totalSize}, info.memrefType.getElementType(),
428 AffineMap(), info.memrefType.getMemorySpace());
429 }
430
431 SmallVector<ReassociationIndices> reassociation;
432 ReassociationIndices allDims;
433 for (size_t i = 0; i < static_cast<size_t>(info.memrefType.getRank());
434 ++i) {
435 allDims.push_back(i);
436 }
437 reassociation.push_back(allDims);
438 flatMemref = memref::CollapseShapeOp::create(
439 rewriter, loc, flatMemrefType, info.base, reassociation);
440 }
441 baseFlatMemrefs[info.base] = flatMemref;
442 }
443
444 // Process all transfers without using iter_args
445 bool madeChanges = false;
446 for (const auto &info : transferOps) {
447 // Skip if base is defined inside the loop
448 if (info.base.getDefiningOp() &&
449 forOp->isProperAncestor(info.base.getDefiningOp()))
450 continue;
451
452 // Skip if we don't have a flattened version
453 if (!baseFlatMemrefs.count(info.base))
454 continue;
455
456 rewriter.setInsertionPoint(info.op);
457
458 // Flatten vector type
459 int64_t numElements = getVectorNumElements(info.vectorType);
460 VectorType flatVectorType =
461 VectorType::get({numElements}, info.vectorType.getElementType());
462
463 // Get the flattened memref
464 Value flatMemref = baseFlatMemrefs[info.base];
465
466 // Compute pointer from indices
467 int64_t rank = info.memrefType.getRank();
468 AffineExpr linearExpr = rewriter.getAffineConstantExpr(0);
469 int64_t stride = 1;
470 for (int64_t i = rank - 1; i >= 0; --i) {
471 linearExpr = linearExpr + rewriter.getAffineDimExpr(i) * stride;
472 if (i > 0)
473 stride *= info.memrefType.getShape()[i];
474 }
475 auto linearMap = AffineMap::get(rank, 0, linearExpr);
476
477 Value currentPointer = affine::AffineApplyOp::create(
478 rewriter, loc, linearMap, info.indices);
479
480 // Transform the transfer operation
481 AffineMap identityMap1D = AffineMap::get(
482 1, 0, rewriter.getAffineDimExpr(0), rewriter.getContext());
483 auto inBoundsAttr = rewriter.getBoolArrayAttr({true});
484
485 if (auto readOp = dyn_cast<vector::TransferReadOp>(info.op)) {
486 Value flatRead = vector::TransferReadOp::create(
487 rewriter, loc, flatVectorType, flatMemref,
488 ValueRange{currentPointer}, AffineMapAttr::get(identityMap1D),
489 readOp.getPadding(),
490 /*mask=*/Value(), inBoundsAttr);
491 Value shapedRead = vector::ShapeCastOp::create(
492 rewriter, loc, info.vectorType, flatRead);
493 rewriter.replaceOp(readOp, shapedRead);
494 madeChanges = true;
495 } else if (auto writeOp = dyn_cast<vector::TransferWriteOp>(info.op)) {
496 Value flatValue = vector::ShapeCastOp::create(
497 rewriter, loc, flatVectorType, writeOp.getVector());
498 rewriter.replaceOpWithNewOp<vector::TransferWriteOp>(
499 writeOp, flatValue, flatMemref, ValueRange{currentPointer},
500 AffineMapAttr::get(identityMap1D), /*mask=*/Value(),
501 inBoundsAttr);
502 madeChanges = true;
503 }
504 }
505 return madeChanges ? success() : failure();
506 }
507
508 // Use replaceWithAdditionalYields to add pointer iter_args
509 auto yieldValuesFn =
510 [&](OpBuilder &b, Location yieldLoc,
511 ArrayRef<BlockArgument> newBbArgs) -> SmallVector<Value> {
512 SmallVector<Value> yieldValues;
513
514 // Process each transfer operation with IV-dependent indices
515 size_t iterArgIdx = 0;
516 for (const auto &info : transferOps) {
517 if (!info.hasIVDependentIndices)
518 continue;
519
520 BlockArgument ptrIterArg =
521 newBbArgs[newBbArgs.size() - newInitArgs.size() + iterArgIdx];
522 Value flatMemref = flatMemrefs[iterArgIdx];
523
524 // Flatten vector type
525 int64_t numElements = getVectorNumElements(info.vectorType);
526 VectorType flatVectorType =
527 VectorType::get({numElements}, info.vectorType.getElementType());
528
529 // Transform the transfer operation to use the iter_arg pointer
530 b.setInsertionPoint(info.op);
531
532 AffineMap identityMap1D =
533 AffineMap::get(1, 0, b.getAffineDimExpr(0), b.getContext());
534 auto inBoundsAttr = b.getBoolArrayAttr({true});
535
536 if (auto readOp = dyn_cast<vector::TransferReadOp>(info.op)) {
537 Value flatRead = vector::TransferReadOp::create(
538 b, loc, flatVectorType, flatMemref, ValueRange{ptrIterArg},
539 AffineMapAttr::get(identityMap1D), readOp.getPadding(),
540 /*mask=*/Value(), inBoundsAttr);
541 Value shapedRead =
542 vector::ShapeCastOp::create(b, loc, info.vectorType, flatRead);
543 rewriter.replaceOp(readOp, shapedRead);
544 } else if (auto writeOp = dyn_cast<vector::TransferWriteOp>(info.op)) {
545 Value flatValue = vector::ShapeCastOp::create(b, loc, flatVectorType,
546 writeOp.getVector());
547 rewriter.replaceOpWithNewOp<vector::TransferWriteOp>(
548 writeOp, flatValue, flatMemref, ValueRange{ptrIterArg},
549 AffineMapAttr::get(identityMap1D), /*mask=*/Value(),
550 inBoundsAttr);
551 }
552
553 // Compute next pointer value: current_ptr + constant_stride
554 Value strideConst =
555 arith::ConstantIndexOp::create(b, yieldLoc, info.constantStride);
556 Value nextPtr =
557 arith::AddIOp::create(b, yieldLoc, ptrIterArg, strideConst);
558 yieldValues.push_back(nextPtr);
559
560 iterArgIdx++;
561 }
562
563 return yieldValues;
564 };
565
566 // Create new loop with additional iter_args for pointers
567 FailureOr<LoopLikeOpInterface> newLoopResult =
568 cast<LoopLikeOpInterface>(forOp.getOperation())
569 .replaceWithAdditionalYields(
570 rewriter, newInitArgs, // new init operands (base pointers)
571 true, // replace uses in loop
572 yieldValuesFn);
573
574 if (failed(newLoopResult))
575 return failure();
576
577 return success();
578 }
579};
580
581//===----------------------------------------------------------------------===//
582// AIEHoistVectorTransferPointersPass
583//===----------------------------------------------------------------------===//
584
585struct AIEHoistVectorTransferPointersPass
586 : xilinx::AIE::impl::AIEHoistVectorTransferPointersBase<
587 AIEHoistVectorTransferPointersPass> {
588 void getDependentDialects(DialectRegistry &registry) const override {
589 registry.insert<affine::AffineDialect, arith::ArithDialect,
590 memref::MemRefDialect, scf::SCFDialect,
591 vector::VectorDialect>();
592 }
593
594 void runOnOperation() override {
595 ModuleOp moduleOp = getOperation();
596 MLIRContext *context = &getContext();
597
598 RewritePatternSet patterns(context);
599 patterns.add<HoistVectorTransferPointersPattern>(context);
600
601 // Apply patterns to the entire module - the pattern will only match scf.for
602 // ops within aie.core regions
603 if (failed(applyPatternsGreedily(moduleOp, std::move(patterns))))
604 signalPassFailure();
605 }
606};
607
608} // namespace
609
610std::unique_ptr<OperationPass<ModuleOp>>
612 return std::make_unique<AIEHoistVectorTransferPointersPass>();
613}
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIEHoistVectorTransferPointersPass()