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"
35 unsigned dim, int64_t stride,
36 uint64_t elemWidth, uint32_t gran) {
42 int64_t maxHw = (1LL << wrapBits) - 1;
43 int64_t maxInput = maxHw * (int64_t)gran / (int64_t)elemWidth;
46 maxInput = (maxInput / divisor) * divisor;
51 : maxIterations(tm, col, row);
52 return (1LL << wrapBits) - 1;
60 BaseMemRefType bufType,
int col,
int row,
62 SmallVector<int64_t, kNdDmaDims> hwSizes(kNdDmaDims);
63 SmallVector<int64_t, kNdDmaDims> hwStrides(kNdDmaDims);
67 return hwStrides[d] <= (1LL << stepBits) - 1;
71void divisorsDescending(int64_t n, SmallVectorImpl<int64_t> &out) {
75 SmallVector<int64_t, 32> small;
76 for (int64_t b = 2; b * b <= n; ++b) {
84 std::reverse(out.begin(), out.end());
85 out.append(small.rbegin(), small.rend());
93 int row, uint64_t elemWidth, uint32_t gran,
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 ||
102 n > maxLegalInputSizeForDim(tm, col, row, d, merged.
strides[d],
109 for (
unsigned i = d + 1; i + 1 < kNdDmaDims; ++i) {
114 merged.
sizes[kNdDmaDims - 1] = 1;
115 merged.
strides[kNdDmaDims - 1] = 0;
116 merged.
offsets[kNdDmaDims - 1] = 0;
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)
133 for (
unsigned d = 0; d < 3; ++d) {
134 int64_t n = pattern.
sizes[d];
137 int64_t s = pattern.
strides[d];
139 SmallVector<int64_t, 32> divisors;
140 divisorsDescending(n, divisors);
141 for (int64_t b : divisors) {
143 if (a <= 1 || b <= 1)
152 for (
unsigned i = 3; i > d + 1; --i) {
157 factored.
sizes[d] = b;
159 factored.
sizes[d + 1] = a;
160 factored.
strides[d + 1] = b * s;
162 out.push_back(std::move(factored));
169std::optional<NdDmaPattern>
170factorToLegal(Operation *forOp, BaseMemRefType bufType,
172 const NdDmaPattern &pattern, uint64_t elemWidth, uint32_t gran) {
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))
182FailureOr<SmallVector<NdDmaPattern>>
183decomposeRecursive(Operation *forOp, BaseMemRefType bufType,
187 return SmallVector<NdDmaPattern>{pattern};
189 DataLayout dataLayout = DataLayout::closest(forOp);
190 uint64_t elemWidth = dataLayout.getTypeSizeInBits(bufType.getElementType());
202 for (
int i = 3; i >= 0; --i)
203 if (pattern.
sizes[i] > 1) {
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);
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);
239 if (pattern.
sizes[3] == 1 && outerSlotHasOffset && n <= chunkSize)
241 bool oversizedStride =
242 !strideFitsStepField(tm, forOp, bufType, col, row, pattern, d);
244 for (
unsigned i = 0; i <= d; ++i)
246 !strideFitsStepField(tm, forOp, bufType, col, row, pattern, i)) {
255 if (chunkSize > 0 && chunkSize < n) {
256 int64_t numChunks = (n + chunkSize - 1) / chunkSize;
257 if (numChunks > kMaxNdDmaPieces)
259 SmallVector<NdDmaPattern> combined;
260 for (int64_t i = 0; i < numChunks; ++i) {
262 slice.
sizes[d] = std::min(chunkSize, n - i * chunkSize);
264 if (slice.
sizes[d] == 1 && oversizedStride) {
266 if (pattern.
strides[d] % stride != 0)
272 auto sub = decomposeRecursive(forOp, bufType, tm, col, row, slice);
277 SmallVector<NdDmaPattern> &subPatterns =
279 combined.append(subPatterns.begin(), subPatterns.end());
280 if (
static_cast<int64_t
>(combined.size()) > kMaxNdDmaPieces)
293FailureOr<SmallVector<NdDmaPattern>>
297 for (
unsigned d = 0; d < 3; ++d) {
305 SmallVector<int64_t> sizes, strides;
306 for (
unsigned d = 3; d < pattern.
sizes.size(); ++d) {
308 if (pattern.
sizes[d] == 1)
310 if (!sizes.empty() && pattern.
strides[d] == sizes.back() * strides.back()) {
311 sizes.back() *= pattern.
sizes[d];
314 sizes.push_back(pattern.
sizes[d]);
315 strides.push_back(pattern.
strides[d]);
318 piece.
sizes.push_back(sizes.empty() ? 1 : sizes.front());
319 piece.
strides.push_back(strides.empty() ? 0 : strides.front());
322 for (int64_t size : ArrayRef(sizes).drop_front()) {
323 if (size > kMaxNdDmaPieces / count)
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)
336 inner.baseOffset += index * strides[i];
337 outer.push_back(std::move(inner));
339 pieces = std::move(outer);
349 pattern.
sizes[3] <= maxIterations(tm, col, row);
354FailureOr<SmallVector<NdDmaPattern>>
355sliceIterations(
const NdDmaPattern &pattern, int64_t chunk) {
356 if ((pattern.
sizes[3] + chunk - 1) / chunk > kMaxNdDmaPieces)
358 SmallVector<NdDmaPattern> slices;
359 for (int64_t first = 0; first < pattern.
sizes[3]; first += chunk) {
361 slice.
sizes[3] = std::min(chunk, pattern.
sizes[3] - first);
363 slices.push_back(std::move(slice));
370FailureOr<SmallVector<NdDmaPattern>> decompose4d(Operation *forOp,
371 BaseMemRefType bufType,
376 return sliceIterations(pattern, maxIterations(tm, col, row));
377 return decomposeRecursive(forOp, bufType, tm, col, row, pattern);
386 d < pattern.
sizes.size() && pattern.
sizes.size() > kNdDmaDims;) {
387 if (pattern.
sizes[d] != 1) {
393 pattern.
sizes.erase(pattern.
sizes.begin() + d);
400std::optional<NdDmaPattern> constantPattern(AIE::DMABDOp op) {
401 if (op.getPadDimensions().has_value() ||
402 (op.getOffset() && !op.getConstantOffset()) ||
403 (op.getLen() && !op.getConstantLen()))
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())
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);
423 BaseMemRefType referencedBufType,
426 bool skipTransformationChecks) {
427 if (pattern.
sizes.size() != kNdDmaDims)
429 SmallVector<int64_t, kNdDmaDims> hwSizes(kNdDmaDims);
430 SmallVector<int64_t, kNdDmaDims> hwStrides(kNdDmaDims);
432 pattern.
strides, hwSizes, hwStrides);
435 ScopedDiagnosticHandler handler(forOp->getContext(),
436 [](Diagnostic &) { return success(); });
438 forOp, [&] {
return mlir::emitError(forOp->getLoc()); },
439 referencedBufType, tileCol, tileRow, pattern.
sizes, pattern.
strides,
440 hwSizes, hwStrides, skipTransformationChecks));
444 BaseMemRefType referencedBufType,
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)
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());
470 targetModel, tileCol, tileRow);
473 if (failed(decomposed))
475 SmallVector<NdDmaPattern> &bds =
480FailureOr<SmallVector<NdDmaPattern>>
487 pattern.
sizes.size() < kNdDmaDims)
490 if (pattern.
sizes.size() > kNdDmaDims) {
492 squeezeUnitDims(squeezed);
493 FailureOr<SmallVector<NdDmaPattern>> pieces =
494 squeezed.
sizes.size() > kNdDmaDims
495 ? reduceIterationDims(squeezed)
499 SmallVector<NdDmaPattern> result;
504 if (contiguousAndFits(targetModel, tileCol, tileRow, piece) ||
506 tileCol, tileRow, piece)) {
507 result.push_back(piece);
510 auto sub = decompose4d(forOp, referencedBufType, targetModel, tileCol,
514 SmallVector<NdDmaPattern> &subPatterns =
516 result.append(subPatterns.begin(), subPatterns.end());
517 if (
static_cast<int64_t
>(result.size()) > kMaxNdDmaPieces)
523 if (contiguousAndFits(targetModel, tileCol, tileRow, pattern))
530 DataLayout dataLayout = DataLayout::closest(forOp);
532 targetModel, tileCol, tileRow,
533 dataLayout.getTypeSizeInBits(referencedBufType.getElementType()),
537 return SmallVector<NdDmaPattern>{merged};
539 return decompose4d(forOp, referencedBufType, targetModel, tileCol, tileRow,
543FailureOr<TaskBdDecomposition>
546 int tileCol,
int tileRow) {
547 std::optional<NdDmaPattern> pattern = constantPattern(bd);
550 auto bufferType = cast<BaseMemRefType>(bd.getBuffer().getType());
553 if (p.sizes.size() != kNdDmaDims)
556 return p.sizes[3] <= maxIterations(targetModel, tileCol, tileRow);
562 plan.
pass = *pattern;
563 if (lowerable(plan.
pass)) {
570 for (
unsigned d = plan.
pass.
sizes.size();
573 if (lowerable(plan.
pass)) {
578 bd, bufferType, plan.
pass, targetModel, tileCol, tileRow);
579 if (succeeded(pieces))
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)
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)
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.