13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/Dialect/Func/IR/FuncOps.h"
15#include "mlir/IR/DialectImplementation.h"
16#include "mlir/IR/Matchers.h"
17#include "mlir/IR/Operation.h"
18#include "mlir/IR/RegionGraphTraits.h"
19#include "mlir/IR/SymbolTable.h"
20#include "mlir/IR/TypeUtilities.h"
21#include "mlir/Interfaces/DataLayoutInterfaces.h"
22#include "mlir/Interfaces/FoldInterfaces.h"
23#include "mlir/Transforms/InliningUtils.h"
24#include "llvm/ADT/SCCIterator.h"
25#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/DenseMap.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/CheckedArithmetic.h"
30#include "llvm/Support/TypeSize.h"
39#include "aie/Dialect/AIEX/IR/AIEXDialect.cpp.inc"
41#include "aie/Dialect/AIEX/IR/AIEXEnums.cpp.inc"
43#include "aie/Dialect/AIEX/IR/AIEXInterfaces.cpp.inc"
45#define GET_TYPEDEF_CLASSES
46#include "aie/Dialect/AIEX/IR/AIEXTypes.cpp.inc"
48#define GET_ATTRDEF_CLASSES
49#include "aie/Dialect/AIEX/IR/AIEXAttrDefs.cpp.inc"
54void AIEXDialect::initialize() {
57#include "aie/Dialect/AIEX/IR/AIEX.cpp.inc"
60#define GET_TYPEDEF_LIST
61#include "aie/Dialect/AIEX/IR/AIEXTypes.cpp.inc"
64#define GET_ATTRDEF_LIST
65#include "aie/Dialect/AIEX/IR/AIEXAttrDefs.cpp.inc"
75#include "aie/Dialect/AIEX/IR/AIEX.cpp.inc"
112 mlir::BaseMemRefType referencedBufType,
113 llvm::SmallVector<int64_t, 4> inputSizes,
114 llvm::SmallVector<int64_t, 4> inputStrides,
115 llvm::SmallVector<int64_t, 4> &sizes,
116 llvm::SmallVector<int64_t, 4> &strides) {
117 assert(inputSizes.size() == inputStrides.size());
118 assert(sizes.size() == 4);
119 assert(strides.size() == 4);
121 DataLayout dataLayout = DataLayout::closest(op);
123 dataLayout.getTypeSizeInBits(referencedBufType.getElementType());
127 llvm::fill(sizes, 0);
128 llvm::fill(strides, 0);
130 if (inputSizes[0] == 0) {
137 int64_t inS[4] = {inputSizes[0], inputSizes[1], inputSizes[2], inputSizes[3]};
138 int64_t inT[4] = {inputStrides[0], inputStrides[1], inputStrides[2],
140 int64_t outS[4], outT[4];
143 for (
int i = 0; i < 4; i++) {
145 strides[i] = outT[i];
150 mlir::Operation *forOp,
151 llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
152 mlir::BaseMemRefType referencedBufType,
int tileCol,
int tileRow,
153 llvm::SmallVector<int64_t, 4> inputSizes,
154 llvm::SmallVector<int64_t, 4> inputStrides,
155 llvm::SmallVector<int64_t, 4> hardwareSizes,
156 llvm::SmallVector<int64_t, 4> hardwareStrides,
157 bool skipTransformationChecks) {
159 auto addressGranularity = targetModel.getAddressGenGranularity();
160 DataLayout dataLayout = DataLayout::closest(forOp);
162 dataLayout.getTypeSizeInBits(referencedBufType.getElementType());
165 if (!targetModel.isCoreTile(tileCol, tileRow) &&
166 !targetModel.isMemTile(tileCol, tileRow) &&
167 !targetModel.isShimNOCTile(tileCol, tileRow))
168 return emitError() <<
"Unsupported tile type at (" << tileCol <<
", "
169 << tileRow <<
") Must be ShimNOC, Mem or Core.";
171 uint32_t wrap_bits = targetModel.getDmaBdWrapBits(tileCol, tileRow);
172 uint32_t step_bits = targetModel.getDmaBdStepBits(tileCol, tileRow);
174 for (
int i = 0; i < 4; i++) {
175 if (inputSizes[i] <= 0) {
176 return emitError() <<
"Size " << i <<
" must be a positive integer.";
181 std::stringstream msg;
182 msg <<
"Transfer sizes must be multiples of " << (addressGranularity / 8)
183 <<
" bytes. " << inputSizes[0] <<
" elements at " << (elemWidth / 8)
184 <<
" bytes each equal " << (inputSizes[0] * elemWidth / 8)
185 <<
" bytes, which is not divisible by " << (addressGranularity / 8)
187 return emitError() << msg.str();
190 for (
int i = 0; i < 3; i++) {
191 if (inputSizes[i] > 1 && inputStrides[i] < 1) {
195 return emitError() <<
"Stride " << i <<
" must be a positive integer.";
200 if (inputSizes[3] > 1 && inputStrides[3] < 0) {
201 return emitError() <<
"Stride 3 must be a non-negative integer.";
204 for (
int i = 0; i < 4; i++) {
207 if (i == 0 && inputStrides[i] == 1)
210 addressGranularity)) {
211 std::stringstream msg;
212 msg <<
"Stride " << i <<
" is " << inputStrides[i] <<
" elements * "
213 << (elemWidth / 8) <<
" bytes = " << (inputStrides[i] * elemWidth / 8)
214 <<
" bytes, which is not divisible by " << (addressGranularity / 8)
216 return emitError() << msg.str();
221 if (elemWidth != addressGranularity && inputSizes[0] > 1 &&
222 inputStrides[0] != 1)
223 return emitError() <<
"Stride 0 is " << inputStrides[0]
224 <<
" elements, but must be 1 for " << (elemWidth / 8)
225 <<
"-byte elements: the DMA moves whole "
226 << (addressGranularity / 8) <<
"-byte words.";
228 auto outOfRange = [&](
const char *what,
int dim, int64_t written,
229 int64_t encoded, int64_t lo, int64_t hi) {
230 return emitError() << what <<
" " << dim <<
" is " << written
231 <<
" (encoded as " << encoded <<
"), which exceeds the ["
232 << lo <<
":" << hi <<
"] range.";
235 int64_t maxWrap = (1 << wrap_bits) - 1;
236 int64_t maxStep = 1 << step_bits;
238 for (
int dim : {0, 1}) {
239 if (!skipTransformationChecks && hardwareSizes[dim] > maxWrap)
240 return outOfRange(
"Size", dim, inputSizes[dim], hardwareSizes[dim], 0,
245 bool pureRepeat = inputStrides[3] == 0;
247 pureRepeat ? targetModel.getMaxRepeatCount() + 1
248 : 1LL << targetModel.getDmaBdIterBits(tileCol, tileRow);
249 if (inputSizes[3] > maxCount)
250 return emitError() << (pureRepeat ?
"repeat count " :
"iteration count ")
251 << inputSizes[3] <<
" exceeds the [1:" << maxCount
253 for (
int dim : {0, 1, 2}) {
254 if (hardwareStrides[dim] > maxStep - 1)
255 return outOfRange(
"Stride", dim, inputStrides[dim], hardwareStrides[dim],
260 if (hardwareStrides[3] > maxStep - 1 && hardwareSizes[3] > 0)
261 return outOfRange(
"Stride", 3, inputStrides[3], hardwareStrides[3], 1,
271LogicalResult AIEX::UseTokenOp::verify() {
272 auto *parentOp = (*this)->getParentOp();
273 if (isa<func::FuncOp>(parentOp) || isa<AIE::CoreOp>(parentOp) ||
274 isa<AIE::MemOp>(parentOp) || isa<AIE::ShimDMAOp>(parentOp))
283LogicalResult AIEX::MulticastOp::verify() {
284 Region &body = getPorts();
285 assert(getOperation()->getNumRegions());
286 assert(!body.empty());
287 for (
auto &ops : body.front())
288 if (!isa<MultiDestOp, AIE::EndOp>(ops))
289 return ops.emitOpError(
"cannot be contained in a Multicast op");
298LogicalResult AIEX::BroadcastPacketOp::verify() {
299 Region &body = getPorts();
300 assert(getOperation()->getNumRegions());
301 assert(!body.empty());
302 for (
auto &ops : body.front())
303 if (!isa<BPIDOp, AIE::EndOp>(ops))
304 return ops.emitOpError(
"cannot be contained in a BroadcastPacket op");
315uint32_t AIEX::NpuDmaMemcpyNdOp::getAxcacheOrDefault() {
319int64_t AIEX::NpuDmaMemcpyNdOp::getOffsetInBytes() {
320 llvm::SmallVector<int64_t, 4> offsets =
321 llvm::map_to_vector(llvm::reverse(getMixedOffsets()), [](OpFoldResult s) {
322 return getConstantIntValue(s).value();
324 auto strides = llvm::to_vector<4>(llvm::reverse(getMixedStrides()));
326 size_t R = offsets.size();
327 size_t el_bit_width = getElementTypeBitwidth();
328 assert(el_bit_width % 8 == 0 &&
329 "Expected Memref element bitwidth to be multiple of 8.");
330 size_t S = el_bit_width / 8;
335 for (
size_t i = 0; i < R; i++) {
338 auto strideConst = getConstantIntValue(strides[i]);
339 assert(strideConst &&
340 "verifier requires a stride paired with a non-zero offset to be "
342 offset += offsets[i] * (*strideConst) * S;
351 llvm::ArrayRef<int64_t> strides) {
352 return sizes[1] == 1 && sizes[2] == 1 && strides[0] == 1 && strides[1] == 0 &&
363 llvm::ArrayRef<int64_t> strides) {
366 if (sizes[1] > 1 && strides[1] != sizes[0])
368 if (sizes[2] > 1 && strides[2] != sizes[0] * sizes[1])
374 int row, llvm::ArrayRef<int64_t> sizes,
375 llvm::ArrayRef<int64_t> strides) {
385bool AIEX::NpuDmaMemcpyNdOp::isLinearTransferWithoutTransformation() {
386 llvm::SmallVector<int64_t, 4> inputSizes =
387 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
388 return getConstantIntValue(s).value();
390 llvm::SmallVector<int64_t, 4> inputStrides =
391 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
392 return getConstantIntValue(s).value();
420struct LinearizeContiguousTransfer
421 :
public mlir::OpRewritePattern<AIEX::NpuDmaMemcpyNdOp> {
422 using OpRewritePattern::OpRewritePattern;
425 matchAndRewrite(AIEX::NpuDmaMemcpyNdOp op,
426 mlir::PatternRewriter &rewriter)
const override {
428 if (!llvm::all_of(op.getMixedSizes(), [](mlir::OpFoldResult s) {
429 return mlir::getConstantIntValue(s).has_value();
431 return mlir::failure();
432 if (!llvm::all_of(op.getMixedStrides(), [](mlir::OpFoldResult s) {
433 return mlir::getConstantIntValue(s).has_value();
435 return mlir::failure();
436 if (!llvm::all_of(op.getMixedOffsets(), [](mlir::OpFoldResult s) {
437 return mlir::getConstantIntValue(s).has_value();
439 return mlir::failure();
442 if (op.isLinearTransferWithoutTransformation())
443 return mlir::failure();
447 llvm::SmallVector<int64_t, 4> sizes = llvm::map_to_vector(
448 llvm::reverse(op.getMixedSizes()), [](mlir::OpFoldResult s) {
449 return mlir::getConstantIntValue(s).value();
451 llvm::SmallVector<int64_t, 4> strides = llvm::map_to_vector(
452 llvm::reverse(op.getMixedStrides()), [](mlir::OpFoldResult s) {
453 return mlir::getConstantIntValue(s).value();
455 llvm::SmallVector<int64_t, 4> offsets = llvm::map_to_vector(
456 llvm::reverse(op.getMixedOffsets()), [](mlir::OpFoldResult s) {
457 return mlir::getConstantIntValue(s).value();
462 return mlir::failure();
466 int64_t N = sizes[0] * sizes[1] * sizes[2];
467 llvm::SmallVector<int64_t, 4> newSizesOuter = {sizes[3], 1, 1, N};
468 llvm::SmallVector<int64_t, 4> newStridesOuter = {strides[3], 0, 0, 1};
475 int64_t linearOffset = offsets[0] * strides[0] + offsets[1] * strides[1] +
476 offsets[2] * strides[2];
477 llvm::SmallVector<int64_t, 4> newOffsetsOuter = {offsets[3], 0, 0,
480 rewriter.replaceOpWithNewOp<AIEX::NpuDmaMemcpyNdOp>(
481 op, op, newOffsetsOuter, newSizesOuter, newStridesOuter);
482 return mlir::success();
487void AIEX::NpuDmaMemcpyNdOp::getCanonicalizationPatterns(
488 mlir::RewritePatternSet &results, mlir::MLIRContext *context) {
489 results.add<LinearizeContiguousTransfer>(context);
495static std::optional<std::string>
497 uint32_t requestedBurstLength) {
498 if (requestedBurstLength != 0) {
500 auto pair = llvm::find_if(bel, [=](
const std::pair<uint32_t, uint32_t> &p) {
501 return p.second == requestedBurstLength;
504 if (pair == bel.end()) {
505 std::string errorMessage =
506 "Requested burst length is not supported by the target. "
507 "Supported burst lengths:";
510 std::accumulate(bel.begin(), bel.end(), errorMessage,
511 [](
const std::string &a,
auto b) {
512 return a +
" " + std::to_string(b.second);
526LogicalResult AIEX::NpuDmaMemcpyNdOp::verifyDynamicSizesStrides(
528 AIE::ShimDMAAllocationOp allocOp) {
532 "runtime sizes/strides require a shim_dma_allocation to resolve the "
533 "tile; none found.");
534 AIE::TileOp tile = allocOp.getTileOp();
536 return emitOpError(
"shim DMA allocation must reference a valid TileOp");
537 if (!targetModel.
isShimNOCTile(tile.getCol(), tile.getRow()))
539 "runtime sizes/strides are only supported for shim NOC tile DMAs.");
542 if (getD0ZeroBefore() || getD1ZeroBefore() || getD2ZeroBefore() ||
543 getD0ZeroAfter() || getD1ZeroAfter() || getD2ZeroAfter())
545 "zero padding is not supported with runtime sizes/strides.");
552 auto sizes = getMixedSizes();
553 auto strides = getMixedStrides();
571 DataLayout dataLayout = DataLayout::closest(getOperation());
572 uint64_t elemWidth = dataLayout.getTypeSizeInBits(buffer.getElementType());
574 llvm::SmallVector<mlir::OpFoldResult, 4> sizesRev(llvm::reverse(sizes));
575 auto checkSize = [&](mlir::OpFoldResult ofr, int64_t hwVal, int64_t hi,
576 llvm::StringRef what) -> LogicalResult {
577 if (!getConstantIntValue(ofr))
579 if (hwVal < 0 || hwVal > hi)
580 return emitOpError(what) <<
" hardware value " << hwVal
581 <<
" exceeds hardware range [0:" << hi <<
"].";
584 auto hwSize = [&](mlir::OpFoldResult ofr) {
585 return getConstantIntValue(ofr).value_or(0);
587 int64_t d0Hw = (int64_t)(hwSize(sizesRev[0]) * elemWidth / gran);
588 int64_t d1Hw = hwSize(sizesRev[1]);
589 int64_t iterRaw = hwSize(sizesRev[3]);
590 int64_t iterHw = iterRaw > 1 ? iterRaw - 1 : 0;
591 std::optional<int64_t> outerStride = getConstantIntValue(strides.front());
592 bool pureRepeat = outerStride && *outerStride == 0;
593 AIE::AIETileType tileType =
594 targetModel.
getTileType(tile.getCol(), tile.getRow());
598 if (failed(checkSize(sizesRev[0], d0Hw, wrapMax,
"d0 size")) ||
599 failed(checkSize(sizesRev[1], d1Hw, wrapMax,
"d1 size")) ||
600 failed(checkSize(sizesRev[3], iterHw, maxCount - 1,
601 pureRepeat ?
"repeat count" :
"iteration size")))
607 llvm::SmallVector<mlir::OpFoldResult, 4> stridesRev(llvm::reverse(strides));
614 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
615 if (errorMessage.has_value())
616 return emitOpError(errorMessage.value());
621LogicalResult AIEX::NpuDmaMemcpyNdOp::verify() {
622 BaseMemRefType buffer = getMemref().getType();
626 if (getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) {
627 uint64_t elemBitWidth = buffer.getElementTypeBitWidth();
628 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
629 return emitOpError(
"offset_parameter requires a whole-byte element type");
632 if (getElementTypeBitwidth() > addressGranularity) {
633 return emitOpError(
"Maximum element bit width allowed is ")
634 << addressGranularity <<
"bits. ";
636 if (buffer.hasStaticShape() &&
637 (buffer.getNumElements() * getElementTypeBitwidth()) <
638 addressGranularity) {
639 return emitOpError(
"Minimum data transfer size required is ")
640 << addressGranularity <<
"bits. ";
642 bool allStridesConstant = llvm::all_of(getMixedStrides(), [](OpFoldResult s) {
643 return getConstantIntValue(s).has_value();
645 bool allSizesConstant = llvm::all_of(getMixedSizes(), [](OpFoldResult s) {
646 return getConstantIntValue(s).has_value();
648 bool allOffsetsConstant = llvm::all_of(getMixedOffsets(), [](OpFoldResult s) {
649 return getConstantIntValue(s).has_value();
652 bool hasLengthParameter =
653 getLengthParameterAttr() || getLengthStateTableIdxAttr();
656 if (hasLengthParameter &&
657 (!allStridesConstant || !allSizesConstant || !allOffsetsConstant))
659 "length_parameter requires constant offsets, sizes and strides");
662 if (!allStridesConstant || !allSizesConstant || !allOffsetsConstant)
665 llvm::SmallVector<int64_t, 4> inputSizes =
666 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
667 return getConstantIntValue(s).value();
669 llvm::SmallVector<int64_t, 4> inputStrides =
670 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
671 return getConstantIntValue(s).value();
673 int64_t offset = getOffsetInBytes();
675 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
676 if (errorMessage.has_value()) {
677 return emitOpError(errorMessage.value());
680 if (hasLengthParameter) {
682 *
this, getLengthUnit(), buffer,
683 inputSizes[0] * inputSizes[1] * inputSizes[2],
685 llvm::ArrayRef<int64_t>(inputSizes).take_front(3))))
694 if (offset % 4 != 0) {
695 return emitOpError(
"Offset must be 4-byte-aligned.");
699 if (
auto packetInfo = getPacket()) {
702 if (packetInfo->getPktType() > 7)
703 return emitOpError(
"Packet type field can only hold 3 bits.");
704 if (packetInfo->assignedId() > 31)
705 return emitOpError(
"Packet ID field can only hold 5 bits.");
714AIEX::NpuDmaMemcpyNdOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
715 BaseMemRefType buffer = getMemref().getType();
717 bool allStridesConstant = llvm::all_of(getMixedStrides(), [](OpFoldResult s) {
718 return getConstantIntValue(s).has_value();
720 bool allSizesConstant = llvm::all_of(getMixedSizes(), [](OpFoldResult s) {
721 return getConstantIntValue(s).has_value();
723 bool allOffsetsConstant = llvm::all_of(getMixedOffsets(), [](OpFoldResult s) {
724 return getConstantIntValue(s).has_value();
727 !allStridesConstant || !allSizesConstant || !allOffsetsConstant;
729 bool hasLengthParameter =
730 getLengthParameterAttr() || getLengthStateTableIdxAttr();
733 if (!isDynamic && !hasLengthParameter &&
737 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
738 auto allocOp = symbolTable.lookupSymbolIn<AIE::ShimDMAAllocationOp>(
739 dev, getMetadata().getRootReference());
741 return verifyDynamicSizesStrides(targetModel, buffer, allocOp);
747 AIE::TileOp tile = allocOp.getTileOp();
749 return emitOpError(
"shim DMA allocation must reference a valid TileOp");
750 int col = tile.getCol();
751 int row = tile.getRow();
752 if (hasLengthParameter &&
758 llvm::SmallVector<int64_t, 4> inputSizes =
759 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
760 return getConstantIntValue(s).value();
762 llvm::SmallVector<int64_t, 4> inputStrides =
763 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
764 return getConstantIntValue(s).value();
766 llvm::SmallVector<int64_t, 4> hardwareSizes(4);
767 llvm::SmallVector<int64_t, 4> hardwareStrides(4);
769 inputStrides, hardwareSizes, hardwareStrides);
781 targetModel, col, row, inputSizes, inputStrides);
788 llvm::SmallVector<int64_t, 4> inputOffsets =
789 llvm::map_to_vector(llvm::reverse(getMixedOffsets()), [](OpFoldResult s) {
790 return getConstantIntValue(s).value();
795 getOperation(), buffer, targetModel, col, row,
796 inputOffsets, inputSizes, inputStrides)) {
799 *
this, [&] {
return emitOpError(); }, buffer, col, row,
800 inputSizes, inputStrides, hardwareSizes, hardwareStrides,
801 skipTransformationChecks))) {
812LogicalResult AIEX::NpuDmaWaitOp::verify() {
813 AIE::DeviceOp dev = (*this)->getParentOfType<AIE::DeviceOp>();
816 if (dev && !dev.lookupSymbol(getSymbol()))
817 return emitOpError(
"couldn't find symbol in parent device");
825LogicalResult AIEX::NpuPushQueueOp::verify() {
827 auto numBds = targetModel.
getNumBDs(getColumn(), getRow());
832 bdId && *bdId >= numBds)
833 return emitOpError(
"BD ID exceeds the maximum ID.");
835 if (std::optional<uint64_t> repeatCount =
837 repeatCount && *repeatCount > maxRepeat)
838 return emitOpError(
"Repeat count exceeds the [0:")
839 << maxRepeat <<
"] range.";
840 if (getRepeatStateTableIdxAttr()) {
842 if (arch != AIE::AIEArch::AIE2 && arch != AIE::AIEArch::AIE2p)
843 return emitOpError(
"repeat_parameter is only supported on AIE2 and "
846 !targetModel.
isMemTile(getColumn(), getRow()))
847 return emitOpError(
"repeat_parameter is only supported on shim NOC "
848 "and mem tiles, got tile (")
849 << getColumn() <<
", " << getRow() <<
")";
851 return emitOpError(
"takes its repeat count from the scratchpad, so its "
852 "repeat_count operand must be 0");
855 return emitOpError(
"takes its repeat count from the scratchpad, so its "
856 "bd_id must be a constant");
858 return emitOpError(
"takes its repeat count from the scratchpad, whose "
859 "update clears the low two bits of the queue word, "
860 "so its bd_id must be a multiple of 4, got ")
870LogicalResult AIEX::NpuWriteBdOp::verify() {
872 auto numBds = targetModel.
getNumBDs(getColumn(), getRow());
874 (getD0Size() >= 1) && (getD1Size() == 1) && (getIterationSize() == 0);
875 if (getBdId() >= numBds)
876 return emitOpError(
"BD ID exceeds the maximum ID.");
877 if (getPacketId() > 31)
878 return emitOpError(
"Packet ID exceeds the maximum supported by 5 bits.");
879 if (getPacketType() > 7)
880 return emitOpError(
"Packet Type exceeds the maximum supported by 3 bits.");
881 int64_t oooId = getOutOfOrderId();
884 return emitOpError(
"out_of_order_id must be in [0, ")
886 if (oooId != 0 && getEnablePacket() == 0)
887 return emitOpError(
"out_of_order_id requires a packet-enabled BD");
892 AIE::AIETileType tileType = targetModel.
getTileType(getColumn(), getRow());
896 uint32_t maxWrap = wrapBits > 0 ? (1u << wrapBits) - 1 : 0;
897 uint32_t maxStep = stepBits > 0 ? (1u << stepBits) - 1 : 0;
898 uint32_t maxIter = iterBits > 0 ? (1u << iterBits) - 1 : 0;
900 if (!isLinearTransfer && getD0Size() > maxWrap)
901 return emitOpError() <<
"D0 Size exceeds the [0:" << maxWrap <<
"] range.";
902 if (getD0Stride() > maxStep)
903 return emitOpError() <<
"D0 Stride exceeds the [0:" << maxStep
905 if (getD1Size() > maxWrap)
906 return emitOpError() <<
"D1 Size exceeds the [0:" << maxWrap <<
"] range.";
907 if (getD1Stride() > maxStep)
908 return emitOpError() <<
"D1 Stride exceeds the [0:" << maxStep
910 if (getD2Stride() > maxStep)
911 return emitOpError() <<
"D2 Stride exceeds the [0:" << maxStep
913 if (getIterationSize() > maxIter)
914 return emitOpError() <<
"Iteration Size exceeds the [0:" << maxIter
916 if (
static_cast<uint32_t
>(getIterationStride()) > maxStep)
917 return emitOpError() <<
"Iteration Stride exceeds the [0:" << maxStep
922 if (getBufferLength() > maxLen)
923 return emitOpError() <<
"Buffer length exceeds the [0:" << maxLen
925 if (targetModel.
isShimNOCTile(getColumn(), getRow()) && getD2Size() != 0)
926 return emitOpError(
"ShimTile only supports 3 dimensions of sizes.");
928 (getD0ZeroBefore() != 0 || getD0ZeroAfter() != 0 ||
929 getD1ZeroBefore() != 0 || getD1ZeroAfter() != 0 ||
930 getD2ZeroBefore() != 0 || getD2ZeroAfter() != 0))
931 return emitOpError(
"ShimTile doesn't support zero padding.");
949 if (getD0ZeroBefore() > 0x3F || getD0ZeroAfter() > 0x3F)
950 return emitOpError(
"D0 pad_before/pad_after exceeds the [0:63] range.");
951 if (getD1ZeroBefore() > 0x1F || getD1ZeroAfter() > 0x1F)
952 return emitOpError(
"D1 pad_before/pad_after exceeds the [0:31] range.");
953 if (getD2ZeroBefore() > 0xF || getD2ZeroAfter() > 0xF)
954 return emitOpError(
"D2 pad_before/pad_after exceeds the [0:15] range.");
957 getBurstLength() != 0)
958 return emitOpError(
"Only ShimTiles support burst length.");
959 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
960 if (errorMessage.has_value()) {
961 return emitOpError(errorMessage.value());
963 if (!targetModel.
isShimNOCTile(getColumn(), getRow()) && getAxcache())
964 return emitOpError(
"Only ShimTiles support AxCACHE configuration.");
969uint32_t AIEX::NpuWriteBdOp::getAxcacheOrDefault() {
975 return static_cast<uint32_t
>(*cst);
983 if (
auto constant = v.getDefiningOp<arith::ConstantOp>())
984 if (
auto attr = dyn_cast<IntegerAttr>(constant.getValue()))
985 return attr.getValue().getZExtValue();
987 if (!mlir::matchPattern(v, mlir::m_ConstantInt(&cst)))
989 return cst.getZExtValue();
993 mlir::Location loc, uint32_t value) {
994 return arith::ConstantOp::create(
995 builder, loc, builder.getI32IntegerAttr(
static_cast<int32_t
>(value)));
999 mlir::Location loc, uint64_t value) {
1000 if (value <= std::numeric_limits<uint32_t>::max())
1002 return arith::ConstantOp::create(
1004 IntegerAttr::get(builder.getIntegerType(64),
1005 static_cast<int64_t
>(value)));
1012template <
typename T>
1013static std::optional<uint32_t>
1014getAbsoluteAddress(T *op, uint32_t addressOffset,
1016 AIE::DeviceOp device =
1017 op->getOperation()->template getParentOfType<AIE::DeviceOp>();
1019 op->emitError(
"Must be inside a device.");
1020 return std::nullopt;
1024 uint32_t address = 0;
1028 if (
auto bufferSym = op->getBuffer()) {
1029 AIE::BufferOp buffer =
1030 names ? names->
lookup<AIE::BufferOp>(op->getBufferAttr().getAttr())
1031 :
AIE::lookupNamedOpIn<
AIE::BufferOp>(device, *bufferSym);
1033 op->emitError() <<
"buffer '" << *bufferSym <<
"' not found in device";
1034 return std::nullopt;
1037 auto bufferAddress = buffer.getAddress();
1038 if (!bufferAddress) {
1039 mlir::InFlightDiagnostic err =
1040 op->emitError(
"referenced buffer must have address assigned");
1041 err.attachNote(buffer.getLoc()) <<
"This buffer must have an address.";
1042 return std::nullopt;
1045 uint32_t col = buffer.getTileOp().getCol();
1046 uint32_t row = buffer.getTileOp().getRow();
1047 address =
static_cast<uint32_t
>(*bufferAddress) +
1048 addressOffset *
sizeof(uint32_t);
1050 ((row & 0xff) << tm.
getRowShift()) | (address & 0xfffff);
1052 address = addressOffset;
1053 std::optional<uint32_t> col = op->getColumn();
1054 std::optional<uint32_t> row = op->getRow();
1059 ((*row & 0xff) << tm.
getRowShift()) | (address & 0xfffff);
1066std::optional<uint32_t>
1070 return std::nullopt;
1071 return ::getAbsoluteAddress(
this, *addressOffset, names);
1078LogicalResult AIEX::NpuAddressPatchOp::verify() {
1082 if (getAddrVal() && !getAddrVal().getType().isInteger(32))
1083 return emitOpError(
"addr_val must be an i32 value");
1084 if (getArgIdx().has_value() ==
static_cast<bool>(getBuffer()))
1085 return emitOpError(
"must name the host buffer either by the 'arg_idx' "
1086 "attribute or by the 'buffer' operand, not both and not "
1095std::optional<uint32_t> AIEX::NpuUpdateFromScratchpadOp::getAbsoluteAddress(
1097 return ::getAbsoluteAddress(
this, getAddress(), names);
1100LogicalResult AIEX::NpuUpdateFromScratchpadOp::verify() {
1102 constexpr uint32_t kMaxStateTableEntries = 32;
1103 if (getStateTableIdx() >= kMaxStateTableEntries)
1104 return emitOpError(
"state_table_idx ")
1105 <<
static_cast<uint32_t
>(getStateTableIdx())
1106 <<
" exceeds maximum StateTable index ("
1107 << (kMaxStateTableEntries - 1) <<
").";
1117LogicalResult AIEX::NpuCreateScratchpadOp::verify() {
1119 if (getUsageType() != 0) {
1120 return emitOpError(
"usage_type must be 0 (got ")
1121 <<
static_cast<uint32_t
>(getUsageType())
1122 <<
"); other layouts are not supported.";
1127 constexpr uint32_t kMaxScratchpadSizeBytes = 128;
1128 if (getSize() == 0) {
1129 return emitOpError(
"size must be greater than 0.");
1131 if (getSize() % 4 != 0) {
1132 return emitOpError(
"size (")
1133 << getSize() <<
") must be a multiple of 4 bytes.";
1135 if (getSize() > kMaxScratchpadSizeBytes) {
1136 return emitOpError(
"size (")
1137 << getSize() <<
" bytes) exceeds maximum scratchpad size of "
1138 << kMaxScratchpadSizeBytes <<
" bytes.";
1143 uint32_t numEntries = getSize() / 4;
1144 if (Block *block = (*this)->getBlock()) {
1145 for (
auto updateOp : block->getOps<AIEX::NpuUpdateFromScratchpadOp>()) {
1146 if (updateOp.getStateTableIdx() < numEntries)
1148 return updateOp.emitOpError(
"state_table_idx ")
1149 <<
static_cast<uint32_t
>(updateOp.getStateTableIdx())
1150 <<
" is out of bounds for scratchpad of size " << getSize()
1151 <<
" bytes (" << numEntries <<
" entries) created by "
1152 << (*this)->getName() <<
".";
1160 auto runtimeSeq = getOperation()->getParentOfType<AIE::RuntimeSequenceOp>();
1165 NpuCreateScratchpadOp firstSeen;
1166 runtimeSeq.walk([&](NpuCreateScratchpadOp op) {
1168 return WalkResult::interrupt();
1170 if (firstSeen != *
this) {
1171 InFlightDiagnostic diag =
1172 emitOpError(
"only one 'aiex.npu.create_scratchpad' is allowed per "
1173 "runtime sequence");
1174 diag.attachNote(firstSeen.getLoc())
1175 <<
"previous 'aiex.npu.create_scratchpad' here";
1186std::optional<uint32_t>
1190 return std::nullopt;
1191 return ::getAbsoluteAddress(
this, *addressOffset, names);
1198std::optional<uint32_t>
1202 return std::nullopt;
1203 return ::getAbsoluteAddress(
this, *addressOffset, names);
1210std::optional<uint32_t>
1212 return ::getAbsoluteAddress(
this, getAddress(), names);
1215DenseIntElementsAttr AIEX::NpuBlockWriteOp::getDataWords() {
1216 Value
memref = this->getData();
1217 DataLayout dataLayout = DataLayout::closest(*
this);
1218 int64_t width = dataLayout.getTypeSizeInBits(
1219 cast<MemRefType>(
memref.getType()).getElementType());
1221 emitWarning(
"Only 32-bit data type is supported for now");
1225 memref::GetGlobalOp getGlobal =
memref.getDefiningOp<memref::GetGlobalOp>();
1227 emitError(
"Only MemRefs from memref.get_global are supported");
1231 auto global = dyn_cast_if_present<memref::GlobalOp>(
1232 (*this)->getParentOfType<AIE::DeviceOp>().lookupSymbol(
1233 getGlobal.getName()));
1235 emitError(
"Global symbol not found");
1239 auto initVal = global.getInitialValue();
1241 emitError(
"Global symbol has no initial value");
1245 auto data = dyn_cast<DenseIntElementsAttr>(*initVal);
1247 emitError(
"Global symbol initial value is not a dense int array");
1258AIE::DMABDOp AIEX::DMAConfigureTaskOp::getFirstBd() {
1259 Region &body = getBody();
1263 auto bd_ops = body.front().getOps<AIE::DMABDOp>();
1264 if (bd_ops.empty() && body.front().getNumSuccessors() == 1) {
1268 Block &chain_entry = *body.front().getSuccessor(0);
1269 bd_ops = chain_entry.getOps<AIE::DMABDOp>();
1271 if (bd_ops.empty()) {
1274 return *bd_ops.begin();
1278AIEX::DMAConfigureTaskOp::canonicalize(AIEX::DMAConfigureTaskOp op,
1279 PatternRewriter &rewriter) {
1281 Region &body = op.getBody();
1282 bool did_rewrite =
false;
1283 for (
auto &block : body) {
1284 if (block.empty()) {
1287 auto ops_it = block.without_terminator();
1288 if (std::distance(ops_it.begin(), ops_it.end()) == 0) {
1289 rewriter.eraseOp(block.getTerminator());
1328 LogicalResult result = success();
1329 body.walk([&](AIE::DMABDOp bd) {
1332 if (failed(bd.verifyMixedSizesAndStrides())) {
1341 size_t numDims = bd.getMixedSizes().size();
1342 if (bd.getIteration()) {
1343 if (numDims + 1 > maxNDims) {
1344 bd.emitOpError() <<
"Cannot give more than "
1345 << std::to_string(maxNDims - 1)
1346 <<
" dimensions for step sizes and wraps alongside "
1347 "the iteration attribute on this tile (got "
1348 << std::to_string(numDims) <<
" dimensions).";
1353 auto isConstant = [](OpFoldResult v) {
1354 return getConstantIntValue(v).has_value();
1356 if (numDims > maxNDims && llvm::all_of(bd.getMixedSizes(), isConstant) &&
1357 llvm::all_of(bd.getMixedStrides(), isConstant))
1359 if (numDims > maxNDims) {
1360 bd.emitOpError() <<
"Cannot give more than " << std::to_string(maxNDims)
1361 <<
" dimensions for step sizes and wraps on this tile "
1363 << std::to_string(numDims) <<
" dimensions).";
1370LogicalResult AIEX::DMAConfigureTaskOp::verify() {
1375 std::optional<int> col = getTileLike().tryGetCol();
1376 std::optional<int> row = getTileLike().tryGetRow();
1378 failed(verifyTaskBDDimensions(targetModel, *col, *row, getBody())))
1380 Region &body = getBody();
1387 LogicalResult result = success();
1388 bool taskHasPacket = getPacket().has_value();
1389 llvm::SmallVector<AIE::DMABDOp> bds;
1390 for (
auto &block : body) {
1391 if (block.empty()) {
1394 if (block.hasNoPredecessors() && !block.isEntryBlock()) {
1395 auto error = block.getTerminator()->emitError(
1396 "Block ending in this terminator does not form a chain with "
1404 block.walk([&](AIE::DMABDOp bd) {
1405 if (bd.getBurstLength() != 0 &&
1406 !targetModel.
isShimNOCTile(getTileID().col, getTileID().row)) {
1407 bd.emitOpError(
"Burst length is only supported in Shim NOC tiles that "
1408 "are connected to the memory-mapped NOC.");
1414 bd, bd.getPacket().value_or(AIE::PacketInfoAttr()))) ||
1416 failed(bd.verifyTransferInBounds()))
1421 if (getOutOfOrder()) {
1423 if (getIssueToken()) {
1425 emitOpError(
"out_of_order channel cannot issue a completion token");
1426 err.attachNote() <<
"set issue_token = false on this out-of-order task";
1430 getOperation(), getDirection(), getOutOfOrder(),
1431 llvm::ArrayRef<AIE::DMABDOp>(bds),
1438LogicalResult AIEX::DMAStartTaskOp::verify() {
1439 if (getRepeatCountAttr() && getRepeatCountVal())
1441 "takes repeat_count or a runtime repeat count, not both");
1442 if (IntegerAttr rc = getRepeatCountAttr(); rc && rc.getInt() < 0)
1443 return emitOpError(
"repeat_count must be non-negative, got ")
1445 if (hasRepeatParameter() && (getRepeatCountAttr() || getRepeatCountVal()))
1446 return emitOpError(
"takes a repeat_parameter or a repeat count, not both");
1447 if (getRepeatParameterAttr() && getRepeatStateTableIdxAttr())
1448 return emitOpError(
"takes repeat_parameter or repeat_state_table_idx, "
1456static std::optional<int64_t> staticBdLenBytes(AIE::DMABDOp bd) {
1457 if (bd.getLengthParameterAttr() || bd.getLengthStateTableIdxAttr())
1458 return std::nullopt;
1460 if (std::optional<int32_t> len = bd.getConstantLen()) {
1462 elems =
static_cast<uint32_t
>(*len);
1466 if (bd.hasLen() || !bd.getSizes().empty() || !bd.getStrides().empty())
1467 return std::nullopt;
1468 auto buffer = dyn_cast<MemRefType>(bd.getBuffer().getType());
1469 if (!buffer || !buffer.hasStaticShape())
1470 return std::nullopt;
1471 elems = buffer.getNumElements();
1473 return llvm::checkedMul(
1474 elems,
static_cast<int64_t
>(bd.getBufferElementTypeWidthInBytes()));
1479static std::optional<int64_t>
1480staticBytesStreamed(AIEX::DMAConfigureTaskForOp task) {
1482 for (Operation *user : task.getResult().getUsers()) {
1483 auto start = dyn_cast<AIEX::DMAStartTaskOp>(user);
1485 if (isa<AIEX::DMAAwaitTaskOp, AIEX::DMAFreeTaskOp>(user))
1489 return std::nullopt;
1493 if (start->getBlock() != task->getBlock() || start.getRepeatCountAttr() ||
1494 start.getRepeatCountVal() || start.hasRepeatParameter())
1495 return std::nullopt;
1500 std::optional<int64_t> repeat =
1501 getConstantIntValue(task.getRepeatCountValue());
1502 if (!repeat || *repeat < 0)
1503 return std::nullopt;
1505 Region &body = task.getBody();
1507 return std::nullopt;
1511 std::optional<int64_t> moved = 0;
1512 for (
auto scc = llvm::scc_begin(&body.front()); !scc.isAtEnd(); ++scc) {
1514 return std::nullopt;
1515 for (AIE::DMABDOp bd : scc->front()->getOps<AIE::DMABDOp>()) {
1516 std::optional<int64_t> len = staticBdLenBytes(bd);
1517 if (!len || bd.getIteration() || bd.getNextBdId())
1518 return std::nullopt;
1519 moved = llvm::checkedAdd(*moved, *len);
1521 return std::nullopt;
1524 std::optional<int64_t> runs = llvm::checkedAdd(*repeat, int64_t{1});
1526 runs = llvm::checkedMul(*runs, starts);
1527 return runs ? llvm::checkedMul(*moved, *runs) : std::nullopt;
1534 SmallVector<int64_t> segmentBytes;
1536 int64_t objectBytes;
1539 static std::optional<BdChain>
get(ArrayRef<int64_t> segmentBytes,
1541 if (segmentBytes.size() == 1)
1543 std::optional<int64_t> objectBytes = 0;
1544 for (int64_t bytes : segmentBytes) {
1545 std::optional<int64_t> run = llvm::checkedMul(bytes, copies);
1546 if (bytes <= 0 || copies <= 0 || !run)
1547 return std::nullopt;
1548 objectBytes = llvm::checkedAdd(*objectBytes, *run);
1550 return std::nullopt;
1552 if (segmentBytes.empty())
1553 return std::nullopt;
1554 return BdChain{SmallVector<int64_t>(segmentBytes), copies, *objectBytes};
1558 bool endsOnBd(int64_t bytes)
const {
1559 int64_t rest = bytes % objectBytes;
1561 for (int64_t segment : segmentBytes) {
1562 if (rest < start + segment * copies)
1563 return (rest - start) % segment == 0;
1564 start += segment * copies;
1569 bool operator==(
const BdChain &other)
const {
1570 return segmentBytes == other.segmentBytes && copies == other.copies;
1582verifyTaskCoversWholeObjects(AIEX::DMAConfigureTaskForOp task,
1583 AIE::ObjectFifoCreateOp fifo) {
1584 if (fifo.getPadDimensions() || fifo.usesStream())
1586 std::optional<int64_t> moved = staticBytesStreamed(task);
1587 if (!moved || *moved == 0)
1589 DataLayout layout = DataLayout::closest(task);
1591 auto objectBytesOf = [&](Type fifoType) -> std::optional<int64_t> {
1592 MemRefType elem = cast<AIE::AIEObjectFifoType>(fifoType).getElementType();
1593 return llvm::checkedMul(
1594 elem.getNumElements(),
1595 static_cast<int64_t
>(layout.getTypeSize(elem.getElementType())));
1605 std::optional<BdChain> linkChain;
1606 if (AIE::DeviceOp device = fifo->getParentOfType<AIE::DeviceOp>())
1607 for (AIE::ObjectFifoLinkOp link : device.getOps<AIE::ObjectFifoLinkOp>()) {
1608 std::vector<AIE::ObjectFifoCreateOp> ins = link.getInputObjectFifos();
1609 std::vector<AIE::ObjectFifoCreateOp> outs = link.getOutputObjectFifos();
1610 if (!llvm::is_contained(ins, fifo) && !llvm::is_contained(outs, fifo))
1612 std::optional<Value> sharedTile = link.getOptionalSharedTile();
1613 if (ins.empty() || outs.empty() || !sharedTile)
1615 linkTile = *sharedTile;
1616 AIE::ObjectFifoCreateOp owner = link.getPoolOwner();
1618 cast<AIE::AIEObjectFifoType>(owner.getElemType()).getElementType();
1620 static_cast<int64_t
>(layout.getTypeSize(pool.getElementType()));
1621 SmallVector<std::pair<int64_t, int64_t>> segments =
1622 link.getPoolSegments();
1623 if (fifo != owner && (link.isJoin() || link.isDistribute())) {
1624 std::vector<AIE::ObjectFifoCreateOp> &many = link.isJoin() ? ins : outs;
1625 segments = {segments[llvm::find(many, fifo) - many.begin()]};
1627 SmallVector<int64_t> segmentBytes;
1628 for (
auto [offset, size] : segments) {
1629 std::optional<int64_t> bytes = llvm::checkedMul(size, width);
1632 segmentBytes.push_back(*bytes);
1634 int64_t copies = llvm::is_contained(outs, fifo)
1635 ? link.getRepeatCount().value_or(1)
1637 if (segmentBytes.size() == segments.size())
1638 linkChain = BdChain::get(segmentBytes, copies);
1646 SmallVector<BdChain> chains;
1647 auto addChain = [&](Value tile, Type fifoType) {
1648 std::optional<BdChain> chain;
1649 if (linkTile && tile == linkTile)
1651 else if (std::optional<int64_t> bytes = objectBytesOf(fifoType))
1652 chain = BdChain::get({*bytes}, 1);
1653 if (chain && !llvm::is_contained(chains, *chain))
1654 chains.push_back(*chain);
1657 dyn_cast_or_null<AIE::TileLike>(fifo.getProducerTile().getDefiningOp());
1660 if (!producer.isShimTile())
1661 addChain(fifo.getProducerTile(), fifo.getElemType());
1662 for (Value consumer : fifo.getConsumerTiles()) {
1663 auto tile = dyn_cast_or_null<AIE::TileLike>(consumer.getDefiningOp());
1664 if (!tile || !tile.isShimTile())
1665 addChain(consumer, fifo.getConsumerElemTypeOrDefault());
1671 auto sequence = task->getParentOfType<AIE::RuntimeSequenceOp>();
1672 if (!sequence || task->getBlock() != &sequence.getBody().front())
1677 if (llvm::any_of(task->getBlock()->getOps<AIEX::DmaChannelResetForOp>(),
1678 [&](AIEX::DmaChannelResetForOp reset) {
1679 return reset.getObjfifoAttr() == task.getAlloc();
1685 for (Operation *prev = task->getPrevNode(); prev; prev = prev->getPrevNode())
1686 if (
auto other = dyn_cast<AIEX::DMAConfigureTaskForOp>(prev);
1687 other && other.getAlloc() == task.getAlloc() &&
1688 staticBytesStreamed(other) != 0)
1690 std::optional<int64_t> total = 0;
1691 int64_t sharing = 0;
1692 WalkResult walk = sequence.walk([&](Operation *op) {
1695 if (
auto memcpy = dyn_cast<AIEX::NpuDmaMemcpyNdOp>(op))
1696 return memcpy.getMetadata() == task.getAlloc() ? WalkResult::interrupt()
1697 : WalkResult::advance();
1698 if (
auto chain = dyn_cast<AIEX::DMAStartBdChainForOp>(op))
1699 return chain.getAllocAttr() == task.getAlloc() ? WalkResult::interrupt()
1700 : WalkResult::advance();
1701 auto other = dyn_cast<AIEX::DMAConfigureTaskForOp>(op);
1702 if (!other || other.getAlloc() != task.getAlloc())
1703 return WalkResult::advance();
1704 std::optional<int64_t> bytes = staticBytesStreamed(other);
1707 return WalkResult::advance();
1708 if (!bytes || other->getBlock() != task->getBlock())
1709 return WalkResult::interrupt();
1710 total = llvm::checkedAdd(*total, *bytes);
1712 return WalkResult::interrupt();
1714 return WalkResult::advance();
1716 if (walk.wasInterrupted())
1718 const BdChain *partial = llvm::find_if(
1719 chains, [&](
const BdChain &chain) {
return !chain.endsOnBd(*total); });
1720 if (partial == chains.end())
1723 InFlightDiagnostic error =
1724 task.emitOpError() <<
"moves " << *moved <<
" bytes through objectFIFO @"
1725 << fifo.getSymName() <<
", which";
1727 error <<
", with this runtime sequence's other transfers through it ("
1728 << *total <<
" bytes in all),";
1729 error <<
" is not a whole number of its " << partial->objectBytes
1731 if (partial->segmentBytes.size() == 1)
1732 return error <<
"; the far end's buffer descriptor is sized by the "
1733 "object, so a partial one never completes";
1734 std::string segments;
1735 llvm::raw_string_ostream os(segments);
1736 llvm::interleaveComma(partial->segmentBytes, os);
1737 error <<
" and doesn't end on a segment boundary (segments of " << segments
1739 if (partial->copies > 1)
1740 error <<
", each sent " << partial->copies <<
" times in a row";
1741 return error <<
"); the far end's buffer descriptors are sized by the "
1742 "segments, so a partial one never completes";
1748LogicalResult AIEX::DMAConfigureTaskForOp::verifySymbolUses(
1749 SymbolTableCollection &symbolTable) {
1750 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
1754 symbolTable.lookupSymbolIn(dev, getAlloc().getRootReference());
1756 return emitOpError() << getAlloc() <<
" does not name a symbol";
1762 LogicalResult named =
1763 TypeSwitch<Operation *, LogicalResult>(target)
1764 .Case([&](AIE::ShimDMAAllocationOp alloc) {
1765 tileValue = alloc.getTile();
1768 .Case([&](AIE::RouteEndpointOp endpoint) -> LogicalResult {
1769 tileValue = endpoint.getTile();
1770 if (endpoint.getBundle() == AIE::WireBundle::DMA)
1772 return emitOpError() << getAlloc() <<
" names a "
1773 << stringifyWireBundle(endpoint.getBundle())
1774 <<
" port, not a DMA channel";
1776 .Case([&](AIE::ObjectFifoCreateOp fifo) {
1777 return verifyTaskCoversWholeObjects(*
this, fifo);
1779 .Default([&](Operation *) -> LogicalResult {
1780 return emitOpError() << getAlloc()
1781 <<
" must name an aie.shim_dma_allocation, an "
1782 "aie.route_endpoint or an aie.objectfifo";
1788 auto tile = tileValue ? tileValue.getDefiningOp<AIE::TileOp>() : nullptr;
1792 return verifyTaskBDDimensions(targetModel, tile.getCol(), tile.getRow(),
1800AIE::BDChainOp AIEX::DMAStartBdChainOp::getBDChainOp() {
1801 AIE::DeviceOp device = (*this)->getParentOfType<AIE::DeviceOp>();
1802 AIE::BDChainOp chain = device.lookupSymbol<AIE::BDChainOp>(getSymbol());
1806LogicalResult AIEX::DMAStartBdChainOp::verify() {
1807 AIE::BDChainOp chain = getBDChainOp();
1809 return emitOpError(
"symbol does not reference valid BD chain");
1812 auto actualArgTypes = getArgs().getTypes();
1813 auto expectedArgTypes = chain.getRegion().getArgumentTypes();
1814 if (actualArgTypes.size() != expectedArgTypes.size()) {
1815 return emitOpError(
"Number of arguments mismatches.");
1817 for (
unsigned i = 0, n = expectedArgTypes.size(); i < n; i++) {
1818 if (actualArgTypes[i] != expectedArgTypes[i]) {
1819 return emitOpError(
"Argument ") << (i + 1) <<
" types mismatch: "
1820 <<
"expected " << expectedArgTypes[i]
1821 <<
" but got " << actualArgTypes[i];
1831uint32_t AIEX::NpuControlPacketOp::getRowFromAddr() {
1833 uint32_t addr = getAddress();
1834 uint32_t rowInt = (addr >> targetModel.
getRowShift()) & 0x1f;
1838uint32_t AIEX::NpuControlPacketOp::getColumnFromAddr() {
1840 uint32_t addr = getAddress();
1849LogicalResult AIEX::SetLockOp::verify() {
1853 return emitOpError(
"SetLockOp is not supported on AIE1.");
1855 if (std::optional<int64_t> value = getConstantIntValue(getValue())) {
1857 return emitOpError(
"Lock value must be non-negative");
1859 return emitOpError(
"Lock value exceeds the maximum value of " +
1863 auto lockOp = getLockOp();
1864 auto lockIDOpt = getLockOp().getLockID();
1873 std::optional<int> col = lockOp.getTileLike().tryGetCol();
1874 std::optional<int> row = lockOp.getTileLike().tryGetRow();
1878 uint32_t lockID = lockOp.getLockIDValue();
1880 if (lockID >= targetModel.
getNumLocks(*col, *row)) {
1881 return emitOpError(
"Lock ID out of range for given tile. Max ID: " +
1882 std::to_string(targetModel.
getNumLocks(*col, *row) - 1));
1886 return emitOpError(
"Invalid lock ID and tile combination when trying to "
1887 "retrieve the local lock address.");
1897static LogicalResult verifyBdPoolPartition(Operation *op,
int col,
int row,
1898 uint32_t begin, uint32_t end) {
1900 if (begin >= end || end > numBds)
1901 return op->emitOpError()
1902 <<
"partition [" << begin <<
", " << end
1903 <<
") is not a nonempty range of the " << numBds
1904 <<
" buffer descriptors on tile (" << col <<
", " << row <<
")";
1908LogicalResult AIEX::DMABdPoolPopOp::verify() {
1909 return verifyBdPoolPartition(*
this, getColumn(), getRow(), getBdBegin(),
1913LogicalResult AIEX::DMABdPoolPushOp::verify() {
1914 return verifyBdPoolPartition(*
this, getColumn(), getRow(), getBdBegin(),
1922LogicalResult AIEX::DmaChannelResetOp::verify() {
1925 auto tile = dyn_cast_or_null<AIE::TileOp>(getTile().getDefiningOp());
1927 return emitOpError() <<
"tile operand must be produced by an aie.tile op";
1928 int col = tile.getCol();
1929 int row = tile.getRow();
1936 return emitOpError() <<
"tile (" << col <<
", " << row
1937 <<
") has no DMA channel reset (only core and mem "
1938 "tiles do; shim NOC DMA has no reset bit)";
1943 uint32_t numChannels = getDirection() == AIE::DMAChannelDir::S2MM
1945 col, row, AIE::WireBundle::DMA)
1947 col, row, AIE::WireBundle::DMA);
1948 if (getChannel() >= numChannels)
1949 return emitOpError() <<
"channel " << getChannel()
1950 <<
" out of range for this tile and direction (tile "
1952 << numChannels <<
" DMA channel(s) in this direction)";
1957LogicalResult AIEX::DmaChannelResetForOp::verify() {
1963 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
1971 return emitOpError() <<
"is not supported on AIE1 devices (the resident "
1972 "re-arm relies on the aie2p start-queue push)";
1973 Operation *target = dev.lookupSymbol(getObjfifo());
1976 if (!isa<AIE::ObjectFifoCreateOp, AIE::RouteEndpoint,
1977 AIE::ObjectFifoRearmBindingOp>(target))
1978 return emitOpError() <<
"'" << getObjfifo()
1979 <<
"' must reference an aie.objectfifo (or its "
1980 "aie.objectfifo_rearm_binding)";
1988LogicalResult AIEX::CoreResetOp::verify() {
1994 return emitOpError(
"aiex.core_reset is not supported on AIE1.");
1996 auto tile = dyn_cast_or_null<AIE::TileOp>(getTile().getDefiningOp());
1998 return emitOpError() <<
"tile operand must be produced by an aie.tile op";
1999 int col = tile.getCol();
2000 int row = tile.getRow();
2009 return emitOpError() <<
"tile (" << col <<
", " << row
2010 <<
") has no core to reset (only core tiles have a "
2011 "CORE_CONTROL register)";
2019uint64_t AIEX::BlockFloatType::getTotalSizeInBits()
const {
2020 return getBlockSize() * getMantissaBits() + getExponentBits() +
2021 getSubtileShiftBits();
2024llvm::TypeSize AIEX::BlockFloatType::getTypeSizeInBits(
2025 const mlir::DataLayout &dataLayout,
2026 mlir::DataLayoutEntryListRef params)
const {
2027 return llvm::TypeSize::getFixed(getTotalSizeInBits());
2030uint64_t AIEX::BlockFloatType::getABIAlignment(
2031 const mlir::DataLayout &dataLayout,
2032 mlir::DataLayoutEntryListRef params)
const {
2038std::optional<AIEX::BlockFloatType::BlockFormat>
2039AIEX::BlockFloatType::getBlockFormat(StringRef blockType) {
2040 static const llvm::StringMap<AIEX::BlockFloatType::BlockFormat>
2042 {
"v8bfp16ebs8", {8, 8, 8, 0}},
2043 {
"v16bfp16ebs16", {16, 8, 8, 0}},
2046 auto it = blockFormatsMap.find(blockType);
2047 if (it != blockFormatsMap.end()) {
2051 return std::nullopt;
2055AIEX::BlockFloatType::verify(function_ref<InFlightDiagnostic()> emitError,
2056 StringRef block_type) {
2057 if (!getBlockFormat(block_type))
2058 return emitError() <<
"Invalid block type: " << block_type
2059 <<
". Known types are: v8bfp16ebs8, v16bfp16ebs16.";
2068AIE::DeviceOp AIEX::ConfigureOp::getReferencedDeviceOp() {
2069 ModuleOp moduleOp = this->getOperation()->getParentOfType<ModuleOp>();
2071 emitError(
"aiex.configure must be inside of a module");
2074 Operation *maybeReferencedDevice =
2075 SymbolTable::lookupSymbolIn(moduleOp.getOperation(), getSymbolAttr());
2076 if (!maybeReferencedDevice) {
2077 emitError(
"No such device: '") << getSymbolAttr() <<
"'";
2080 AIE::DeviceOp referencedDevice =
2081 llvm::dyn_cast<AIE::DeviceOp>(maybeReferencedDevice);
2082 if (!referencedDevice) {
2083 emitError(
"Not a device: '") << getSymbolAttr() <<
"'";
2086 return referencedDevice;
2093LogicalResult AIEX::ReadScratchpadParameterOp::verify() {
2094 auto device = (*this)->getParentOfType<AIE::DeviceOp>();
2096 return emitOpError(
"must be inside an aie.device");
2098 if (!(*this)->getParentOfType<AIE::CoreOp>()) {
2099 return emitOpError(
"must be inside an aie.core");
2101 auto moduleOp = (*this)->getParentOfType<ModuleOp>();
2103 !moduleOp.lookupSymbol<AIEX::ScratchpadParameterOp>(getParameter())) {
2104 return emitOpError(
"references unknown parameter '")
2106 <<
"' (aiex.scratchpad_parameter ops are declared at module scope)";
2108 if (getResult().getType().isF32()) {
2110 "f32 parameters are not supported: the scratchpad encoding zeroes "
2111 "the top 2 bits, which clobbers the sign bit and top exponent bit "
2112 "of an f32. Use bf16 or an integer type up to i32 instead.");
2117LogicalResult AIEX::WaitScratchpadParametersOp::verify() {
2118 if (!(*this)->getParentOfType<AIE::CoreOp>()) {
2119 return emitOpError(
"must be inside an aie.core");
2124LogicalResult AIEX::ConfigureOp::verify() {
2125 AIE::DeviceOp parentDev = getOperation()->getParentOfType<AIE::DeviceOp>();
2126 AIE::DeviceOp referencedDev = getReferencedDeviceOp();
2127 if (!referencedDev) {
2130 if (parentDev.getDevice() != referencedDev.getDevice()) {
2131 emitError(
"Device types do not match: '")
2132 << AIE::stringifyAIEDevice(parentDev.getDevice()) <<
"' vs. '"
2133 << AIE::stringifyAIEDevice(referencedDev.getDevice()) <<
"'";
2143AIE::DeviceOp AIEX::RunOp::getCalleeDeviceOp() {
2144 AIEX::ConfigureOp configureOp =
2145 getOperation()->getParentOfType<AIEX::ConfigureOp>();
2149 AIE::DeviceOp referencedDevice = configureOp.getReferencedDeviceOp();
2150 return referencedDevice;
2153AIE::RuntimeSequenceOp
2155 AIEX::ConfigureOp configureOp =
2156 getOperation()->getParentOfType<AIEX::ConfigureOp>();
2160 AIE::DeviceOp referencedDevice = configureOp.getReferencedDeviceOp();
2161 if (!referencedDevice) {
2165 StringAttr name = getRuntimeSequenceSymbolAttr().getAttr();
2169 return tables->
lookupIn<AIE::RuntimeSequenceOp>(referencedDevice, name);
2176LogicalResult AIEX::NpuLoadPdiOp::canonicalize(AIEX::NpuLoadPdiOp op,
2177 PatternRewriter &rewriter) {
2179 Operation *nextOp = op->getNextNode();
2184 auto nextLoadPdi = dyn_cast<AIEX::NpuLoadPdiOp>(nextOp);
2189 if (op.getDeviceRefAttr() == nextLoadPdi.getDeviceRefAttr() &&
2190 op.getId() == nextLoadPdi.getId() &&
2191 op.getSize() == nextLoadPdi.getSize() &&
2192 op.getAddress() == nextLoadPdi.getAddress()) {
2194 rewriter.eraseOp(op);
2205static bool writesConfig(Operation *op) {
2206 return isa<AIEX::NpuLoadPdiOp, AIEX::NpuWriteConfigOp,
2207 AIEX::NpuWriteTeardownOp>(op);
2214static Operation *findConfigOpBefore(Operation *op,
2215 function_ref<
bool(Operation *)> visit) {
2216 for (Operation *prev = op->getPrevNode(); prev; prev = prev->getPrevNode())
2217 if (writesConfig(prev) && visit(prev))
2219 for (Operation &next : llvm::reverse(*op->getBlock())) {
2222 if (writesConfig(&next) && visit(&next))
2225 return visit(op) ? op :
nullptr;
2230static Operation *precedingReset(Operation *op) {
2231 return findConfigOpBefore(op, [&](Operation *prev) {
2232 return prev != op &&
2233 isa<AIEX::NpuLoadPdiOp, AIEX::NpuWriteTeardownOp>(prev);
2237static bool loadsSamePdi(AIEX::NpuLoadPdiOp a, AIEX::NpuLoadPdiOp b) {
2238 return a.getDeviceRefAttr() == b.getDeviceRefAttr() &&
2239 a.getId() == b.getId() && a.getSize() == b.getSize() &&
2240 a.getAddress() == b.getAddress();
2243LogicalResult AIEX::NpuWriteConfigOp::verify() {
2244 Operation *found = precedingReset(*
this);
2246 return emitOpError(
"must follow the load_pdi that resets the array");
2247 auto reset = dyn_cast<AIEX::NpuLoadPdiOp>(found);
2252 bool tornDown =
true;
2253 Operation *later = reset;
2255 cast<AIEX::NpuLoadPdiOp>(findConfigOpBefore(reset, [&](Operation *op) {
2256 if (isa<AIEX::NpuWriteConfigOp>(op) &&
2257 !isa<AIEX::NpuWriteTeardownOp>(later))
2260 return isa<AIEX::NpuLoadPdiOp>(op);
2262 if (!loadsSamePdi(before, reset) || tornDown)
2264 InFlightDiagnostic diag = emitOpError(
2265 "follows a load_pdi the firmware skips, as it loads the PDI loaded "
2266 "just before it, so the array is not reset");
2267 diag.attachNote(before.getLoc()) <<
"the PDI is loaded here";
2272AIEX::NpuLoadPdiOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2273 FlatSymbolRefAttr ref = getDeviceRefAttr();
2276 auto device = (*this)->getParentOfType<AIE::DeviceOp>();
2277 Operation *scope = device ? device->getParentOp()
2278 : SymbolTable::getNearestSymbolTable(*
this);
2279 if (!isa_and_present<AIE::DeviceOp>(symbolTable.lookupSymbolIn(scope, ref)))
2280 return emitOpError() << ref <<
" is not a device";
2285AIEX::NpuWriteConfigOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2286 Operation *scope = (*this)->getParentOfType<AIE::DeviceOp>()->getParentOp();
2287 if (!isa_and_present<AIE::DeviceOp>(
2288 symbolTable.lookupSymbolIn(scope, getDeviceRefAttr())))
2289 return emitOpError() << getDeviceRefAttr() <<
" is not a device";
2290 auto reset = dyn_cast_or_null<AIEX::NpuLoadPdiOp>(precedingReset(*
this));
2291 FlatSymbolRefAttr resetRef = reset ? reset.getDeviceRefAttr() :
nullptr;
2292 auto empty = resetRef ? dyn_cast_or_null<AIE::DeviceOp>(
2293 symbolTable.lookupSymbolIn(scope, resetRef))
2299 !llvm::all_of(empty.getBody()->without_terminator(), [](Operation &op) {
2300 return isa<AIE::RuntimeSequenceOp>(op) &&
2301 llvm::all_of(op.getRegion(0),
2302 [](Block &block) { return block.empty(); });
2304 return emitOpError(
"must follow a load_pdi of an empty device, which "
2305 "resets the array");
2313LogicalResult AIEX::NpuWriteTeardownOp::verify() {
2314 Operation *prev = (*this)->getPrevNode();
2315 while (prev && !writesConfig(prev))
2316 prev = prev->getPrevNode();
2317 auto config = dyn_cast_or_null<AIEX::NpuWriteConfigOp>(prev);
2318 if (!config || config.getDeviceRefAttr() != getDeviceRefAttr())
2319 return emitOpError(
"must follow the write_config of ")
2320 << getDeviceRefAttr() <<
", with no load_pdi between";
2325AIEX::NpuWriteTeardownOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2326 Operation *scope = (*this)->getParentOfType<AIE::DeviceOp>()->getParentOp();
2327 auto device = dyn_cast_or_null<AIE::DeviceOp>(
2328 symbolTable.lookupSymbolIn(scope, getDeviceRefAttr()));
2330 return emitOpError() << getDeviceRefAttr() <<
" is not a device";
2334 InFlightDiagnostic diag =
2335 emitOpError() << getDeviceRefAttr()
2336 <<
" starts a shim DMA channel, which teardown cannot stop";
2337 diag.attachNote(shimStart->getLoc()) <<
"started here";
2342 Operation *found =
nullptr;
2343 device.walk([&](Operation *op) {
2344 if (!isa<AIE::DMAStartOp, AIE::DMAOp>(op) ||
2345 !isa<AIE::ShimDMAOp>(op->getParentOp()))
2346 return WalkResult::advance();
2348 return WalkResult::interrupt();
bool isCoreTile(int col, int row) const
Return true if the given tile is a Core tile.
virtual uint64_t getDmaBdMaxLen(AIETileType tileType) const =0
Return the maximum transfer length, measured in address-generation granules (see getAddressGenGranula...
virtual AIEArch getTargetArch() const =0
Return the target architecture.
bool isMemTile(int col, int row) const
Return true if the given tile is a Mem tile.
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 getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a 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 getBDMaxDims(AIETileType tileType) const =0
Return the maximum number of addressing dimensions (wraps/strides) a single buffer descriptor support...
virtual std::vector< std::pair< uint32_t, uint32_t > > getShimBurstEncodingsAndLengths() const =0
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 std::optional< uint32_t > getLocalLockAddress(uint32_t lockId, TileID tile) const =0
virtual AIETileType getTileType(int col, int row) const =0
Return the tile type for the given tile coordinates.
virtual uint32_t getMaxRepeatCount() const =0
Return the largest DMA task repeat count (unsupported = 0).
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
virtual uint32_t getMaxLockValue() const =0
Return the maximum value that can be stored in a lock register.
virtual uint32_t getMaxOutOfOrderId() const =0
Return the largest out-of-order BD id (unsupported = 0).
virtual uint32_t getColumnShift() const =0
virtual uint32_t getNumDestSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a switchbox.
virtual uint32_t getRowShift() const =0
virtual uint32_t getAddressGenGranularity() const =0
Return the data bus width of the device.
virtual uint32_t getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
OpTy lookupIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
mlir::Operation * lookup(mlir::StringAttr name) const
const V & get(const std::map< K, V > &pa, K k)
mlir::Operation * findShimDmaStart(AIE::DeviceOp device)
bool isContiguousTransfer(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
void encodeHardwareStridesWraps(Policy &p, uint64_t elemWidth, uint32_t addressGranularity, typename Policy::V inputSizes[4], typename Policy::V inputStrides[4], typename Policy::V sizes[4], typename Policy::V strides[4])
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)
std::optional< uint64_t > getConstantInt64Operand(mlir::Value v)
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...
std::optional< uint32_t > getConstantIntOperand(mlir::Value v)
mlir::Value createConstantArgPlus(mlir::OpBuilder &builder, mlir::Location loc, uint64_t value)
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)
bool isLinearModeTransfer(const AIE::AIETargetModel &targetModel, int col, int row, llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
bool isConstMultipleOfGranule(int64_t value, uint64_t elemWidth, uint32_t addressGranularity)
bool isLinearTransfer(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
mlir::LogicalResult verifyConstBdRealizability(mlir::Operation *op, llvm::ArrayRef< mlir::OpFoldResult > sizes, llvm::ArrayRef< mlir::OpFoldResult > strides, uint64_t elemWidth, uint32_t gran)
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
void printTypedDynamicIndexList(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange values, llvm::ArrayRef< int64_t > integers, mlir::TypeRange types)
mlir::LogicalResult verifyLengthParameterTile(mlir::Operation *op, const AIETargetModel &targetModel, int col, int row)
mlir::LogicalResult verifyAssignedPacket(mlir::Operation *op, PacketInfoAttr packet, llvm::StringRef what="packet")
mlir::ParseResult parseTypedDynamicIndexList(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &values, mlir::DenseI64ArrayAttr &integers, llvm::SmallVectorImpl< mlir::Type > &types)
mlir::Operation * lookupNamedOpIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
mlir::LogicalResult verifyDMABDOutOfOrderId(DMABDOp bd, bool packetEnabledByContext=false)
mlir::LogicalResult verifyOutOfOrderChannel(mlir::Operation *op, DMAChannelDir dir, bool outOfOrder, llvm::ArrayRef< DMABDOp > bds, bool packetEnabledByContext=false)
mlir::LogicalResult verifyLengthParameter(mlir::Operation *op, std::optional< int64_t > lengthUnit, mlir::BaseMemRefType buffer, std::optional< int64_t > lenElems, bool contiguous, llvm::ArrayRef< int64_t > innerSizes)
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
const AIETargetModel & getTargetModel(mlir::Operation *op)
mlir::LogicalResult verifyDMABDIteration(DMABDOp bd, AIETileType tileType)