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"
35#define GEN_PASS_DEF_AIEHOISTVECTORTRANSFERPOINTERS
36#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
39#define DEBUG_TYPE "aie-hoist-vector-transfer-pointers"
54static std::optional<int64_t> getExprCoefficient(AffineExpr expr,
55 ArrayRef<int64_t> operandCoefs,
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))
63 auto bin = dyn_cast<AffineBinaryOpExpr>(expr);
66 std::optional<int64_t> lhs =
67 getExprCoefficient(bin.getLHS(), operandCoefs, numDims);
68 std::optional<int64_t> rhs =
69 getExprCoefficient(bin.getRHS(), operandCoefs, numDims);
72 switch (bin.getKind()) {
73 case AffineExprKind::Add:
75 case AffineExprKind::Mul:
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;
85 return (*lhs == 0 && *rhs == 0) ? std::optional<int64_t>(0) : std::nullopt;
93static std::optional<int64_t>
94getIVCoefficient(Value v, scf::ForOp forOp,
95 DenseMap<Value, std::optional<int64_t>> &cache) {
96 if (v == forOp.getInductionVar())
98 if (forOp.isDefinedOutsideOfLoop(v))
100 auto it = cache.find(v);
101 if (it != cache.end())
104 std::optional<int64_t> result;
105 Operation *def = v.getDefiningOp();
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);
113 linearOperands =
false;
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;
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) {
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))
152 if (Value mapped = mapping.lookupOrNull(v))
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);
162static int64_t getVectorNumElements(VectorType vectorType) {
163 int64_t numElements = 1;
164 for (int64_t dim : vectorType.getShape()) {
175struct TransferOpInfo {
178 MemRefType memrefType;
179 VectorType vectorType;
180 SmallVector<Value> indices;
181 int64_t constantStride;
182 bool hasIVDependentIndices;
187struct HoistVectorTransferPointersPattern
191 LogicalResult matchAndRewrite(scf::ForOp forOp,
192 PatternRewriter &rewriter)
const override {
193 Location loc = forOp.getLoc();
196 SmallVector<TransferOpInfo> transferOps;
197 DenseMap<Value, std::optional<int64_t>> ivCoefs;
199 for (Operation &op : forOp.getBody()->without_terminator()) {
201 VectorType vectorType;
202 SmallVector<Value> indices;
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());
217 auto memrefType = dyn_cast<MemRefType>(base.getType());
223 auto xfer = cast<VectorTransferOpInterface>(op);
224 if (xfer.getMask() || !xfer.getPermutationMap().isMinorIdentity() ||
225 !llvm::all_of(xfer.getInBoundsValues(), [](
bool b) { return b; }))
227 if (!memref::isStaticShapeAndContiguousRowMajor(memrefType))
233 int64_t vecRank = vectorType.getRank();
234 int64_t memRank = memrefType.getRank();
235 if (vecRank > memRank)
238 while (outer < vecRank - 1 && vectorType.getDimSize(outer) == 1)
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))
248 if (!forOp.isDefinedOutsideOfLoop(base))
253 std::optional<int64_t> step =
254 forOp.getConstantStep()
255 ? std::optional<int64_t>(forOp.getConstantStep()->getSExtValue())
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);
267 elementsPerIV += *coef * dimStride;
268 dimStride *= memrefType.getDimSize(d);
272 bool hasIVDependentIndices = elementsPerIV != 0;
273 if (hasIVDependentIndices && !step)
275 int64_t constantStride =
276 hasIVDependentIndices ? elementsPerIV * *step : 0;
278 transferOps.push_back({&op, base, memrefType, vectorType, indices,
279 constantStride, hasIVDependentIndices});
283 if (transferOps.empty())
288 SmallVector<Value> newInitArgs;
289 SmallVector<Value> flatMemrefs;
291 for (
const auto &info : transferOps) {
292 if (!info.hasIVDependentIndices)
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)
307 MemRefType flatMemrefType;
308 if (
auto stridedLayout = dyn_cast_or_null<StridedLayoutAttr>(
309 info.memrefType.getLayout())) {
311 int64_t collapsedStride = stridedLayout.getStrides().back();
312 int64_t offset = stridedLayout.getOffset();
314 auto newLayout = StridedLayoutAttr::get(rewriter.getContext(), offset,
317 MemRefType::get({totalSize}, info.memrefType.getElementType(),
318 newLayout, info.memrefType.getMemorySpace());
321 MemRefType::get({totalSize}, info.memrefType.getElementType(),
322 AffineMap(), info.memrefType.getMemorySpace());
325 SmallVector<ReassociationIndices> reassociation;
326 ReassociationIndices allDims;
327 for (
size_t i = 0; i < static_cast<size_t>(info.memrefType.getRank());
329 allDims.push_back(i);
331 reassociation.push_back(allDims);
333 flatMemref = memref::CollapseShapeOp::create(
334 rewriter, loc, flatMemrefType, info.base, reassociation);
336 flatMemrefs.push_back(flatMemref);
339 int64_t rank = info.memrefType.getRank();
340 AffineExpr linearExpr = rewriter.getAffineConstantExpr(0);
342 for (int64_t i = rank - 1; i >= 0; --i) {
343 linearExpr = linearExpr + rewriter.getAffineDimExpr(i) * stride;
345 stride *= info.memrefType.getShape()[i];
347 auto linearMap = AffineMap::get(rank, 0, linearExpr);
350 SmallVector<Value> evaluatedIndices;
351 IRMapping indexMapping;
352 for (Value idx : info.indices)
353 evaluatedIndices.push_back(
354 cloneAtLowerBound(idx, forOp, rewriter, indexMapping));
356 Value basePointer = affine::AffineApplyOp::create(
357 rewriter, loc, linearMap, evaluatedIndices);
359 newInitArgs.push_back(basePointer);
364 if (newInitArgs.empty()) {
366 bool needsFlattening =
false;
367 bool hasProcessableTransfers =
false;
368 for (
const auto &info : transferOps) {
371 if (info.base.getDefiningOp() &&
372 forOp->isProperAncestor(info.base.getDefiningOp()))
375 hasProcessableTransfers =
true;
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;
390 if (!hasProcessableTransfers || !needsFlattening)
395 DenseMap<Value, Value> baseFlatMemrefs;
396 rewriter.setInsertionPoint(forOp);
397 for (
const auto &info : transferOps) {
398 if (baseFlatMemrefs.count(info.base))
402 if (info.base.getDefiningOp() &&
403 forOp->isProperAncestor(info.base.getDefiningOp()))
406 Value flatMemref = info.base;
407 if (info.memrefType.getRank() > 1) {
408 int64_t totalSize = 1;
409 for (int64_t dim : info.memrefType.getShape()) {
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();
420 auto newLayout = StridedLayoutAttr::get(rewriter.getContext(),
421 offset, {collapsedStride});
423 MemRefType::get({totalSize}, info.memrefType.getElementType(),
424 newLayout, info.memrefType.getMemorySpace());
427 MemRefType::get({totalSize}, info.memrefType.getElementType(),
428 AffineMap(), info.memrefType.getMemorySpace());
431 SmallVector<ReassociationIndices> reassociation;
432 ReassociationIndices allDims;
433 for (
size_t i = 0; i < static_cast<size_t>(info.memrefType.getRank());
435 allDims.push_back(i);
437 reassociation.push_back(allDims);
438 flatMemref = memref::CollapseShapeOp::create(
439 rewriter, loc, flatMemrefType, info.base, reassociation);
441 baseFlatMemrefs[info.base] = flatMemref;
445 bool madeChanges =
false;
446 for (
const auto &info : transferOps) {
448 if (info.base.getDefiningOp() &&
449 forOp->isProperAncestor(info.base.getDefiningOp()))
453 if (!baseFlatMemrefs.count(info.base))
456 rewriter.setInsertionPoint(info.op);
459 int64_t numElements = getVectorNumElements(info.vectorType);
460 VectorType flatVectorType =
461 VectorType::get({numElements}, info.vectorType.getElementType());
464 Value flatMemref = baseFlatMemrefs[info.base];
467 int64_t rank = info.memrefType.getRank();
468 AffineExpr linearExpr = rewriter.getAffineConstantExpr(0);
470 for (int64_t i = rank - 1; i >= 0; --i) {
471 linearExpr = linearExpr + rewriter.getAffineDimExpr(i) * stride;
473 stride *= info.memrefType.getShape()[i];
475 auto linearMap = AffineMap::get(rank, 0, linearExpr);
477 Value currentPointer = affine::AffineApplyOp::create(
478 rewriter, loc, linearMap, info.indices);
481 AffineMap identityMap1D = AffineMap::get(
482 1, 0, rewriter.getAffineDimExpr(0), rewriter.getContext());
483 auto inBoundsAttr = rewriter.getBoolArrayAttr({
true});
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),
490 Value(), inBoundsAttr);
491 Value shapedRead = vector::ShapeCastOp::create(
492 rewriter, loc, info.vectorType, flatRead);
493 rewriter.replaceOp(readOp, shapedRead);
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), Value(),
505 return madeChanges ? success() : failure();
510 [&](OpBuilder &b, Location yieldLoc,
511 ArrayRef<BlockArgument> newBbArgs) -> SmallVector<Value> {
512 SmallVector<Value> yieldValues;
515 size_t iterArgIdx = 0;
516 for (
const auto &info : transferOps) {
517 if (!info.hasIVDependentIndices)
520 BlockArgument ptrIterArg =
521 newBbArgs[newBbArgs.size() - newInitArgs.size() + iterArgIdx];
522 Value flatMemref = flatMemrefs[iterArgIdx];
525 int64_t numElements = getVectorNumElements(info.vectorType);
526 VectorType flatVectorType =
527 VectorType::get({numElements}, info.vectorType.getElementType());
530 b.setInsertionPoint(info.op);
532 AffineMap identityMap1D =
533 AffineMap::get(1, 0, b.getAffineDimExpr(0), b.getContext());
534 auto inBoundsAttr = b.getBoolArrayAttr({
true});
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 Value(), inBoundsAttr);
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), Value(),
555 arith::ConstantIndexOp::create(b, yieldLoc, info.constantStride);
557 arith::AddIOp::create(b, yieldLoc, ptrIterArg, strideConst);
558 yieldValues.push_back(nextPtr);
567 FailureOr<LoopLikeOpInterface> newLoopResult =
568 cast<LoopLikeOpInterface>(forOp.getOperation())
569 .replaceWithAdditionalYields(
570 rewriter, newInitArgs,
574 if (failed(newLoopResult))
585struct AIEHoistVectorTransferPointersPass
586 : xilinx::AIE::impl::AIEHoistVectorTransferPointersBase<
587 AIEHoistVectorTransferPointersPass> {
588 void getDependentDialects(DialectRegistry ®istry)
const override {
589 registry.insert<affine::AffineDialect, arith::ArithDialect,
590 memref::MemRefDialect, scf::SCFDialect,
591 vector::VectorDialect>();
594 void runOnOperation()
override {
595 ModuleOp moduleOp = getOperation();
596 MLIRContext *context = &getContext();
598 RewritePatternSet patterns(context);
599 patterns.add<HoistVectorTransferPointersPattern>(context);
603 if (failed(applyPatternsGreedily(moduleOp, std::move(patterns))))
610std::unique_ptr<OperationPass<ModuleOp>>
612 return std::make_unique<AIEHoistVectorTransferPointersPass>();
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIEHoistVectorTransferPointersPass()