MLIR-AIE 1.4.5.dev
DmaDecomposition.cpp
Go to the documentation of this file.
1//===- DmaDecomposition.cpp -------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
9
14
15#include "mlir/Dialect/Utils/StaticValueUtils.h"
16#include "mlir/IR/Diagnostics.h"
17#include "mlir/IR/Operation.h"
18#include "llvm/ADT/STLExtras.h"
19
20#include <algorithm>
21
22using namespace mlir;
23using namespace xilinx;
24using namespace xilinx::AIEX;
25
26namespace {
27
28/// The longest iteration dimension (d3) a BD holds: its wrap field is biased
29/// by one.
30int64_t maxIterations(const AIE::AIETargetModel &tm, int col, int row) {
31 return 1LL << tm.getDmaBdIterBits(col, row);
32}
33
34int64_t maxLegalInputSizeForDim(const AIE::AIETargetModel &tm, int col, int row,
35 unsigned dim, int64_t stride,
36 uint64_t elemWidth, uint32_t gran) {
37 uint32_t wrapBits = tm.getDmaBdWrapBits(col, row);
38 if (wrapBits == 0)
39 return 0;
40
41 if (dim == 0) {
42 int64_t maxHw = (1LL << wrapBits) - 1;
43 int64_t maxInput = maxHw * (int64_t)gran / (int64_t)elemWidth;
44 int64_t divisor = bdGranuleDivisor(elemWidth, gran);
45 if (divisor > 1)
46 maxInput = (maxInput / divisor) * divisor;
47 return maxInput;
48 }
49 if (dim == 3)
50 return stride == 0 ? tm.getMaxRepeatCount() + 1
51 : maxIterations(tm, col, row);
52 return (1LL << wrapBits) - 1;
53}
54
55/// Whether dimension `d`'s stride fits the BD's step field. The hardware
56/// stride is in address granules, biased by one as the field encodes it
57/// (getHardwareStridesWraps); the iteration slot (d == 3) shares the field
58/// width and additionally admits a zero stride, a re-read.
59bool strideFitsStepField(const AIE::AIETargetModel &tm, Operation *forOp,
60 BaseMemRefType bufType, int col, int row,
61 const NdDmaPattern &pattern, unsigned d) {
62 SmallVector<int64_t, kNdDmaDims> hwSizes(kNdDmaDims);
63 SmallVector<int64_t, kNdDmaDims> hwStrides(kNdDmaDims);
64 getHardwareStridesWraps(tm, forOp, bufType, pattern.sizes, pattern.strides,
65 hwSizes, hwStrides);
66 uint32_t stepBits = tm.getDmaBdStepBits(col, row);
67 return hwStrides[d] <= (1LL << stepBits) - 1;
68}
69
70/// Enumerate divisors b of n in descending order (largest inner factor first).
71void divisorsDescending(int64_t n, SmallVectorImpl<int64_t> &out) {
72 out.clear();
73 if (n <= 1)
74 return;
75 SmallVector<int64_t, 32> small;
76 for (int64_t b = 2; b * b <= n; ++b) {
77 if (n % b != 0)
78 continue;
79 out.push_back(n / b);
80 if (b != n / b)
81 small.push_back(b);
82 }
83 llvm::sort(out);
84 std::reverse(out.begin(), out.end());
85 out.append(small.rbegin(), small.rend());
86}
87
88/// Fold each dimension into the next-inner one where the pair walks memory
89/// contiguously (stride[d+1] == size[d] * stride[d]) and the merged size still
90/// fits the inner slot's wrap. The element order is unchanged; the freed
91/// outermost slot becomes a unit dimension.
92NdDmaPattern mergeContiguousDims(const AIE::AIETargetModel &tm, int col,
93 int row, uint64_t elemWidth, uint32_t gran,
94 const NdDmaPattern &pattern) {
95 NdDmaPattern merged = pattern;
96 unsigned d = 0;
97 while (d + 1 < kNdDmaDims) {
98 int64_t n = merged.sizes[d] * merged.sizes[d + 1];
99 if (merged.sizes[d] <= 1 || merged.sizes[d + 1] <= 1 ||
100 merged.strides[d] <= 0 ||
101 merged.strides[d + 1] != merged.sizes[d] * merged.strides[d] ||
102 n > maxLegalInputSizeForDim(tm, col, row, d, merged.strides[d],
103 elemWidth, gran)) {
104 ++d;
105 continue;
106 }
107 merged.offsets[d] += merged.offsets[d + 1] * merged.sizes[d];
108 merged.sizes[d] = n;
109 for (unsigned i = d + 1; i + 1 < kNdDmaDims; ++i) {
110 merged.sizes[i] = merged.sizes[i + 1];
111 merged.strides[i] = merged.strides[i + 1];
112 merged.offsets[i] = merged.offsets[i + 1];
113 }
114 merged.sizes[kNdDmaDims - 1] = 1;
115 merged.strides[kNdDmaDims - 1] = 0;
116 merged.offsets[kNdDmaDims - 1] = 0;
117 }
118 return merged;
119}
120
121/// Split dim d (size N = a*b, stride s) into an inner dim (b, s) kept at
122/// position d and an outer dim (a, b*s) inserted at position d+1, shifting the
123/// higher dims outward. The factored pair stays adjacent so the sub-traversal
124/// of dim d is contiguous and its place in the overall nesting is unchanged
125/// => element order is preserved. The outermost slot must be free and carry no
126/// base offset. Returns every such factoring, largest inner factor first.
127SmallVector<NdDmaPattern> factorings(const NdDmaPattern &pattern,
128 uint64_t elemWidth, uint32_t gran) {
129 SmallVector<NdDmaPattern> out;
130 bool outerSlotHasOffset = pattern.offsets[3] != 0 && pattern.strides[3] != 0;
131 if (pattern.sizes[3] != 1 || outerSlotHasOffset)
132 return out;
133 for (unsigned d = 0; d < 3; ++d) {
134 int64_t n = pattern.sizes[d];
135 if (n <= 1)
136 continue;
137 int64_t s = pattern.strides[d];
138
139 SmallVector<int64_t, 32> divisors;
140 divisorsDescending(n, divisors);
141 for (int64_t b : divisors) {
142 int64_t a = n / b;
143 if (a <= 1 || b <= 1)
144 continue;
145
146 // Both factors must remain granule-realizable on the innermost dim.
147 if (d == 0 && (!isConstMultipleOfGranule(b, elemWidth, gran)))
148 continue;
149
150 NdDmaPattern factored = pattern;
151 // Shift dims (d+1 .. 2) outward to (d+2 .. 3).
152 for (unsigned i = 3; i > d + 1; --i) {
153 factored.sizes[i] = pattern.sizes[i - 1];
154 factored.strides[i] = pattern.strides[i - 1];
155 factored.offsets[i] = pattern.offsets[i - 1];
156 }
157 factored.sizes[d] = b; // inner factor
158 factored.strides[d] = s; // inner keeps original stride/offset
159 factored.sizes[d + 1] = a; // outer factor
160 factored.strides[d + 1] = b * s; // outer stride
161 factored.offsets[d + 1] = 0;
162 out.push_back(std::move(factored));
163 }
164 }
165 return out;
166}
167
168/// The first legal pattern factoring alone reaches, without slicing.
169std::optional<NdDmaPattern>
170factorToLegal(Operation *forOp, BaseMemRefType bufType,
171 const AIE::AIETargetModel &tm, int col, int row,
172 const NdDmaPattern &pattern, uint64_t elemWidth, uint32_t gran) {
173 if (patternPassesVerification(forOp, bufType, tm, col, row, pattern))
174 return pattern;
175 for (const NdDmaPattern &factored : factorings(pattern, elemWidth, gran))
176 if (std::optional<NdDmaPattern> legal = factorToLegal(
177 forOp, bufType, tm, col, row, factored, elemWidth, gran))
178 return legal;
179 return std::nullopt;
180}
181
182FailureOr<SmallVector<NdDmaPattern>>
183decomposeRecursive(Operation *forOp, BaseMemRefType bufType,
184 const AIE::AIETargetModel &tm, int col, int row,
185 const NdDmaPattern &pattern) {
186 if (patternPassesVerification(forOp, bufType, tm, col, row, pattern))
187 return SmallVector<NdDmaPattern>{pattern};
188
189 DataLayout dataLayout = DataLayout::closest(forOp);
190 uint64_t elemWidth = dataLayout.getTypeSizeInBits(bufType.getElementType());
191 uint32_t gran = tm.getAddressGenGranularity();
192
193 // The hardware BD emits elements in lexicographic order of the loop indices
194 // with d0 the innermost (fastest) and d3 the outermost (slowest) dimension.
195 // A decomposition is only correct if the concatenation of the sub-transfers'
196 // emitted element sequences is IDENTICAL (same order, not just same set) to
197 // the original. Both transformations below are order-preserving by
198 // construction.
199
200 // Outermost active dimension (highest index with size > 1).
201 int outermost = -1;
202 for (int i = 3; i >= 0; --i)
203 if (pattern.sizes[i] > 1) {
204 outermost = i;
205 break;
206 }
207 if (outermost < 0)
208 return failure();
209
210 // (1) Order-preserving dimension factoring. A single legal pattern is
211 // preferred over the first factoring that only succeeds by slicing.
212 bool outerSlotHasOffset = pattern.offsets[3] != 0 && pattern.strides[3] != 0;
213 if (std::optional<NdDmaPattern> legal =
214 factorToLegal(forOp, bufType, tm, col, row, pattern, elemWidth, gran))
215 return SmallVector<NdDmaPattern>{*legal};
216 for (const NdDmaPattern &factored : factorings(pattern, elemWidth, gran)) {
217 auto sub = decomposeRecursive(forOp, bufType, tm, col, row, factored);
218 if (succeeded(sub))
219 return sub;
220 }
221
222 // (2) Order-preserving slicing: only the OUTERMOST active dimension may be
223 // split into contiguous index ranges emitted in order. Slicing an inner
224 // dimension would interleave the outer iterations and reorder the emitted
225 // element stream, so it is not allowed.
226 //
227 // The chunk is the largest wrap the slot holds, or a single index when the
228 // dimension's stride is past the step field: a stride the BD cannot
229 // encode is carried in each slice's offset instead, which has no such
230 // limit (a column-major weight whose column-block stride is the whole
231 // matrix's height, for one). Same bytes, same order, one descriptor per
232 // index of that dimension.
233 {
234 unsigned d = static_cast<unsigned>(outermost);
235 int64_t n = pattern.sizes[d];
236 int64_t chunkSize = maxLegalInputSizeForDim(
237 tm, col, row, d, pattern.strides[d], elemWidth, gran);
238 // An offset-bearing singleton cannot be reused by factoring; slice instead.
239 if (pattern.sizes[3] == 1 && outerSlotHasOffset && n <= chunkSize)
240 chunkSize = 1;
241 bool oversizedStride =
242 !strideFitsStepField(tm, forOp, bufType, col, row, pattern, d);
243 // Peel outer dimensions until an oversized inner stride can be folded.
244 for (unsigned i = 0; i <= d; ++i)
245 if (pattern.sizes[i] > 1 && pattern.strides[i] > 0 &&
246 !strideFitsStepField(tm, forOp, bufType, col, row, pattern, i)) {
247 chunkSize = 1;
248 break;
249 }
250 // It fits, so what is illegal is inside it and could not be factored
251 // away: slice it into single indices, each slice's outermost dimension
252 // then being the next one in.
253 if (chunkSize >= n)
254 chunkSize = 1;
255 if (chunkSize > 0 && chunkSize < n) {
256 int64_t numChunks = (n + chunkSize - 1) / chunkSize;
257 if (numChunks > kMaxNdDmaPieces)
258 return failure();
259 SmallVector<NdDmaPattern> combined;
260 for (int64_t i = 0; i < numChunks; ++i) {
261 NdDmaPattern slice = pattern;
262 slice.sizes[d] = std::min(chunkSize, n - i * chunkSize);
263 slice.offsets[d] = pattern.offsets[d] + i * chunkSize;
264 if (slice.sizes[d] == 1 && oversizedStride) {
265 int64_t stride = bdGranuleDivisor(elemWidth, gran);
266 if (pattern.strides[d] % stride != 0)
267 return failure();
268 slice.offsets[d] *= pattern.strides[d] / stride;
269 slice.strides[d] = stride; // keep the singleton granule-aligned
270 }
271
272 auto sub = decomposeRecursive(forOp, bufType, tm, col, row, slice);
273 // failed() above already guards this deref; the checker just doesn't
274 // associate FailureOr's failed()/succeeded() with its optional base.
275 if (failed(sub))
276 return failure();
277 SmallVector<NdDmaPattern> &subPatterns =
278 *sub; // NOLINT(bugprone-unchecked-optional-access)
279 combined.append(subPatterns.begin(), subPatterns.end());
280 if (static_cast<int64_t>(combined.size()) > kMaxNdDmaPieces)
281 return failure();
282 }
283 return combined;
284 }
285 }
286
287 return failure();
288}
289
290// The kNdDmaDims-dimension patterns a longer pattern reduces to, in order (see
291// decomposeNdDmaPattern). Every dimension past d2 is an iteration dimension:
292// one execution per index of them all, outermost slowest.
293FailureOr<SmallVector<NdDmaPattern>>
294reduceIterationDims(const NdDmaPattern &pattern) {
295 NdDmaPattern piece;
296 piece.baseOffset = pattern.baseOffset;
297 for (unsigned d = 0; d < 3; ++d) {
298 piece.offsets.push_back(pattern.offsets[d]);
299 piece.sizes.push_back(pattern.sizes[d]);
300 piece.strides.push_back(pattern.strides[d]);
301 }
302 // The iteration dimensions, innermost first, with a dimension that continues
303 // the one inside it merged into it and unit ones dropped. Their offsets move
304 // into the base, since merging and peeling both move the dimensions.
305 SmallVector<int64_t> sizes, strides;
306 for (unsigned d = 3; d < pattern.sizes.size(); ++d) {
307 piece.baseOffset += pattern.offsets[d] * pattern.strides[d];
308 if (pattern.sizes[d] == 1)
309 continue;
310 if (!sizes.empty() && pattern.strides[d] == sizes.back() * strides.back()) {
311 sizes.back() *= pattern.sizes[d];
312 continue;
313 }
314 sizes.push_back(pattern.sizes[d]);
315 strides.push_back(pattern.strides[d]);
316 }
317 piece.offsets.push_back(0);
318 piece.sizes.push_back(sizes.empty() ? 1 : sizes.front());
319 piece.strides.push_back(strides.empty() ? 0 : strides.front());
320
321 int64_t count = 1;
322 for (int64_t size : ArrayRef(sizes).drop_front()) {
323 if (size > kMaxNdDmaPieces / count)
324 return failure();
325 count *= size;
326 }
327
328 // Peel the rest, one piece per index, working outward so that each outer
329 // dimension repeats all the pieces inside it.
330 SmallVector<NdDmaPattern> pieces{piece};
331 for (unsigned i = 1; i < sizes.size(); ++i) {
332 SmallVector<NdDmaPattern> outer;
333 outer.reserve(pieces.size() * sizes[i]);
334 for (int64_t index = 0; index < sizes[i]; ++index)
335 for (NdDmaPattern inner : pieces) {
336 inner.baseOffset += index * strides[i];
337 outer.push_back(std::move(inner));
338 }
339 pieces = std::move(outer);
340 }
341 return pieces;
342}
343
344// A contiguous pattern is lowered as a plain length, so only its iteration
345// dimension has a limit left to exceed.
346bool contiguousAndFits(const AIE::AIETargetModel &tm, int col, int row,
347 const NdDmaPattern &pattern) {
348 return isContiguousTransfer(pattern.sizes, pattern.strides) &&
349 pattern.sizes[3] <= maxIterations(tm, col, row);
350}
351
352// A contiguous pattern whose iteration dimension is too long, as consecutive
353// runs of it, each at most `chunk` long.
354FailureOr<SmallVector<NdDmaPattern>>
355sliceIterations(const NdDmaPattern &pattern, int64_t chunk) {
356 if ((pattern.sizes[3] + chunk - 1) / chunk > kMaxNdDmaPieces)
357 return failure();
358 SmallVector<NdDmaPattern> slices;
359 for (int64_t first = 0; first < pattern.sizes[3]; first += chunk) {
360 NdDmaPattern slice = pattern;
361 slice.sizes[3] = std::min(chunk, pattern.sizes[3] - first);
362 slice.offsets[3] = pattern.offsets[3] + first;
363 slices.push_back(std::move(slice));
364 }
365 return slices;
366}
367
368// The 4-dimensional patterns a 4-dimensional one that is neither legal nor
369// contiguous and short enough lowers to.
370FailureOr<SmallVector<NdDmaPattern>> decompose4d(Operation *forOp,
371 BaseMemRefType bufType,
372 const AIE::AIETargetModel &tm,
373 int col, int row,
374 const NdDmaPattern &pattern) {
375 if (isContiguousTransfer(pattern.sizes, pattern.strides))
376 return sliceIterations(pattern, maxIterations(tm, col, row));
377 return decomposeRecursive(forOp, bufType, tm, col, row, pattern);
378}
379
380// Drops the dimensions of size one past d0 while there are more than a BD
381// holds, innermost first. Such a dimension moves nothing, but where it sits
382// between d0 and d2 it keeps an iteration dimension out of the BD, which then
383// has to be peeled.
384void squeezeUnitDims(NdDmaPattern &pattern) {
385 for (unsigned d = 1;
386 d < pattern.sizes.size() && pattern.sizes.size() > kNdDmaDims;) {
387 if (pattern.sizes[d] != 1) {
388 ++d;
389 continue;
390 }
391 pattern.baseOffset += pattern.offsets[d] * pattern.strides[d];
392 pattern.offsets.erase(pattern.offsets.begin() + d);
393 pattern.sizes.erase(pattern.sizes.begin() + d);
394 pattern.strides.erase(pattern.strides.begin() + d);
395 }
396}
397
398// `op`'s pattern, innermost first and padded to kNdDmaDims, when it has no
399// padding and a constant offset, length, sizes and strides.
400std::optional<NdDmaPattern> constantPattern(AIE::DMABDOp op) {
401 if (op.getPadDimensions().has_value() ||
402 (op.getOffset() && !op.getConstantOffset()) ||
403 (op.getLen() && !op.getConstantLen()))
404 return std::nullopt;
405 std::optional<SmallVector<int64_t>> sizes =
406 getConstantIntValues(op.getMixedSizes());
407 std::optional<SmallVector<int64_t>> strides =
408 getConstantIntValues(op.getMixedStrides());
409 if (!sizes || !strides || sizes->empty())
410 return std::nullopt;
411 NdDmaPattern pattern;
412 pattern.sizes.assign(sizes->rbegin(), sizes->rend());
413 pattern.strides.assign(strides->rbegin(), strides->rend());
414 pattern.sizes.resize(std::max<size_t>(pattern.sizes.size(), kNdDmaDims), 1);
415 pattern.strides.resize(pattern.sizes.size(), 0);
416 pattern.offsets.assign(pattern.sizes.size(), 0);
417 return pattern;
418}
419
420} // namespace
421
422bool AIEX::patternPassesVerification(Operation *forOp,
423 BaseMemRefType referencedBufType,
424 const AIE::AIETargetModel &tm, int tileCol,
425 int tileRow, const NdDmaPattern &pattern,
426 bool skipTransformationChecks) {
427 if (pattern.sizes.size() != kNdDmaDims)
428 return false;
429 SmallVector<int64_t, kNdDmaDims> hwSizes(kNdDmaDims);
430 SmallVector<int64_t, kNdDmaDims> hwStrides(kNdDmaDims);
431 getHardwareStridesWraps(tm, forOp, referencedBufType, pattern.sizes,
432 pattern.strides, hwSizes, hwStrides);
433
434 // Location-only: an op diagnostic prints the whole enclosing sequence.
435 ScopedDiagnosticHandler handler(forOp->getContext(),
436 [](Diagnostic &) { return success(); });
437 return succeeded(verifyStridesWraps(
438 forOp, [&] { return mlir::emitError(forOp->getLoc()); },
439 referencedBufType, tileCol, tileRow, pattern.sizes, pattern.strides,
440 hwSizes, hwStrides, skipTransformationChecks));
441}
442
443bool AIEX::isDecomposableNdDmaPattern(Operation *forOp,
444 BaseMemRefType referencedBufType,
445 const AIE::AIETargetModel &targetModel,
446 int tileCol, int tileRow,
447 ArrayRef<int64_t> offsetsInnermostFirst,
448 ArrayRef<int64_t> sizesInnermostFirst,
449 ArrayRef<int64_t> stridesInnermostFirst) {
450 if (offsetsInnermostFirst.size() != kNdDmaDims ||
451 sizesInnermostFirst.size() != kNdDmaDims ||
452 stridesInnermostFirst.size() != kNdDmaDims)
453 return false;
454
455 if (isContiguousTransfer(sizesInnermostFirst, stridesInnermostFirst))
456 return false;
457
458 NdDmaPattern pattern;
459 pattern.offsets.assign(offsetsInnermostFirst.begin(),
460 offsetsInnermostFirst.end());
461 pattern.sizes.assign(sizesInnermostFirst.begin(), sizesInnermostFirst.end());
462 pattern.strides.assign(stridesInnermostFirst.begin(),
463 stridesInnermostFirst.end());
464
465 if (patternPassesVerification(forOp, referencedBufType, targetModel, tileCol,
466 tileRow, pattern))
467 return false;
468
469 auto decomposed = decomposeNdDmaPattern(forOp, referencedBufType, pattern,
470 targetModel, tileCol, tileRow);
471 // failed() above already guards this deref; the checker just doesn't
472 // associate FailureOr's failed()/succeeded() with its optional base.
473 if (failed(decomposed))
474 return false;
475 SmallVector<NdDmaPattern> &bds =
476 *decomposed; // NOLINT(bugprone-unchecked-optional-access)
477 return !bds.empty();
478}
479
480FailureOr<SmallVector<NdDmaPattern>>
481AIEX::decomposeNdDmaPattern(Operation *forOp, BaseMemRefType referencedBufType,
482 const NdDmaPattern &pattern,
483 const AIE::AIETargetModel &targetModel, int tileCol,
484 int tileRow) {
485 if (pattern.offsets.size() != pattern.sizes.size() ||
486 pattern.strides.size() != pattern.sizes.size() ||
487 pattern.sizes.size() < kNdDmaDims)
488 return failure();
489
490 if (pattern.sizes.size() > kNdDmaDims) {
491 NdDmaPattern squeezed = pattern;
492 squeezeUnitDims(squeezed);
493 FailureOr<SmallVector<NdDmaPattern>> pieces =
494 squeezed.sizes.size() > kNdDmaDims
495 ? reduceIterationDims(squeezed)
496 : SmallVector<NdDmaPattern>{squeezed};
497 if (failed(pieces))
498 return failure();
499 SmallVector<NdDmaPattern> result;
500 for (const NdDmaPattern &piece :
501 *pieces) { // NOLINT(bugprone-unchecked-optional-access)
502 // A piece already legal, or contiguous and so left to the lowering as a
503 // plain length, is kept as it is.
504 if (contiguousAndFits(targetModel, tileCol, tileRow, piece) ||
505 patternPassesVerification(forOp, referencedBufType, targetModel,
506 tileCol, tileRow, piece)) {
507 result.push_back(piece);
508 continue;
509 }
510 auto sub = decompose4d(forOp, referencedBufType, targetModel, tileCol,
511 tileRow, piece);
512 if (failed(sub))
513 return failure();
514 SmallVector<NdDmaPattern> &subPatterns =
515 *sub; // NOLINT(bugprone-unchecked-optional-access)
516 result.append(subPatterns.begin(), subPatterns.end());
517 if (static_cast<int64_t>(result.size()) > kMaxNdDmaPieces)
518 return failure();
519 }
520 return result;
521 }
522
523 if (contiguousAndFits(targetModel, tileCol, tileRow, pattern))
524 return failure();
525
526 if (patternPassesVerification(forOp, referencedBufType, targetModel, tileCol,
527 tileRow, pattern))
528 return failure();
529
530 DataLayout dataLayout = DataLayout::closest(forOp);
531 NdDmaPattern merged = mergeContiguousDims(
532 targetModel, tileCol, tileRow,
533 dataLayout.getTypeSizeInBits(referencedBufType.getElementType()),
534 targetModel.getAddressGenGranularity(), pattern);
535 if (patternPassesVerification(forOp, referencedBufType, targetModel, tileCol,
536 tileRow, merged))
537 return SmallVector<NdDmaPattern>{merged};
538
539 return decompose4d(forOp, referencedBufType, targetModel, tileCol, tileRow,
540 pattern);
541}
542
543FailureOr<TaskBdDecomposition>
544AIEX::decomposeTaskBdPattern(AIE::DMABDOp bd,
545 const AIE::AIETargetModel &targetModel,
546 int tileCol, int tileRow) {
547 std::optional<NdDmaPattern> pattern = constantPattern(bd);
548 if (!pattern)
549 return failure();
550 auto bufferType = cast<BaseMemRefType>(bd.getBuffer().getType());
551 // See contiguousAndFits for why only d3 can make one too long.
552 auto lowerable = [&](const NdDmaPattern &p) {
553 if (p.sizes.size() != kNdDmaDims)
554 return false;
555 if (isContiguousTransfer(p.sizes, p.strides))
556 return p.sizes[3] <= maxIterations(targetModel, tileCol, tileRow);
557 return patternPassesVerification(bd, bufferType, targetModel, tileCol,
558 tileRow, p);
559 };
560
562 plan.pass = *pattern;
563 if (lowerable(plan.pass)) {
564 plan.legal = true;
565 return plan;
566 }
567
568 // Outer dimensions that only re-read the same data repeat, as the task's
569 // repeat count does, so they go and the pieces restart instead.
570 for (unsigned d = plan.pass.sizes.size();
571 d-- > 3 && (plan.pass.sizes[d] == 1 || plan.pass.strides[d] == 0);)
572 plan.pass.sizes[d] = 1;
573 if (lowerable(plan.pass)) {
574 plan.pieces = {plan.pass};
575 return plan;
576 }
577 FailureOr<SmallVector<NdDmaPattern>> pieces = decomposeNdDmaPattern(
578 bd, bufferType, plan.pass, targetModel, tileCol, tileRow);
579 if (succeeded(pieces))
580 plan.pieces =
581 std::move(*pieces); // NOLINT(bugprone-unchecked-optional-access)
582 return plan;
583}
virtual uint32_t getDmaBdWrapBits(AIETileType tileType) const =0
Return the bit width of the wrap (size) field in a DMA buffer descriptor for the given tile type.
virtual uint32_t getDmaBdIterBits(AIETileType tileType) const =0
Return the bit width of the iteration (repeat) wrap/stride fields in a DMA buffer descriptor for the ...
virtual uint32_t getDmaBdStepBits(AIETileType tileType) const =0
Return the bit width of the step (stride) field in a DMA buffer descriptor for the given tile type.
virtual uint32_t getMaxRepeatCount() const =0
Return the largest DMA task repeat count (unsupported = 0).
virtual uint32_t getAddressGenGranularity() const =0
Return the data bus width of the device.
bool isContiguousTransfer(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
int64_t bdGranuleDivisor(uint64_t elemWidth, uint32_t addressGranularity)
Definition BdLowering.h:58
mlir::LogicalResult verifyStridesWraps(mlir::Operation *forOp, llvm::function_ref< mlir::InFlightDiagnostic()> emitError, mlir::BaseMemRefType referencedBufType, int tileCol, int tileRow, llvm::SmallVector< int64_t, 4 > inputSizes, llvm::SmallVector< int64_t, 4 > inputStrides, llvm::SmallVector< int64_t, 4 > hardwareSizes, llvm::SmallVector< int64_t, 4 > hardwareStrides, bool skipTransformationChecks=false)
bool isDecomposableNdDmaPattern(mlir::Operation *forOp, mlir::BaseMemRefType referencedBufType, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow, llvm::ArrayRef< int64_t > offsetsInnermostFirst, llvm::ArrayRef< int64_t > sizesInnermostFirst, llvm::ArrayRef< int64_t > stridesInnermostFirst)
True when the pattern is fully constant, non-contiguous, currently illegal, and decomposeNdDmaPattern...
mlir::FailureOr< llvm::SmallVector< NdDmaPattern > > decomposeNdDmaPattern(mlir::Operation *forOp, mlir::BaseMemRefType referencedBufType, const NdDmaPattern &pattern, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow)
Decompose an illegal pattern into one or more legal sub-patterns that move the same data.
void getHardwareStridesWraps(const AIE::AIETargetModel &targetModel, mlir::Operation *op, mlir::BaseMemRefType referencedBufType, llvm::SmallVector< int64_t, 4 > inputSizes, llvm::SmallVector< int64_t, 4 > inputStrides, llvm::SmallVector< int64_t, 4 > &sizes, llvm::SmallVector< int64_t, 4 > &strides)
mlir::FailureOr< TaskBdDecomposition > decomposeTaskBdPattern(xilinx::AIE::DMABDOp bd, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow)
Plan the split of bd, a runtime task's descriptor, on tile (tileCol, tileRow).
bool isConstMultipleOfGranule(int64_t value, uint64_t elemWidth, uint32_t addressGranularity)
Definition BdLowering.h:65
bool patternPassesVerification(mlir::Operation *forOp, mlir::BaseMemRefType referencedBufType, const xilinx::AIE::AIETargetModel &tm, int tileCol, int tileRow, const NdDmaPattern &pattern, bool skipTransformationChecks=false)
Returns true when the pattern passes verifyStridesWraps for the given tile and memref element type.
Innermost-first ND access pattern (d0..d3 / repeat), matching the convention used by verifyStridesWra...
llvm::SmallVector< int64_t, kNdDmaDims > sizes
int64_t baseOffset
Elements added to the address the offsets give.
llvm::SmallVector< int64_t, kNdDmaDims > offsets
llvm::SmallVector< int64_t, kNdDmaDims > strides
How aie-decompose-large-dma-bd splits a runtime task's descriptor.
bool legal
The descriptor lowers as it is.
llvm::SmallVector< NdDmaPattern > pieces
The legal patterns pass splits into; empty when no split fits.
NdDmaPattern pass
One pass over the descriptor, less the outer dimensions that only re-read the same data.