18#include "mlir/Dialect/Arith/IR/Arith.h"
19#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
20#include "mlir/Dialect/MemRef/IR/MemRef.h"
21#include "mlir/Dialect/SCF/IR/SCF.h"
22#include "mlir/Dialect/UB/IR/UBOps.h"
23#include "mlir/Dialect/Utils/StaticValueUtils.h"
24#include "mlir/Interfaces/ControlFlowInterfaces.h"
25#include "mlir/Interfaces/SideEffectInterfaces.h"
26#include "mlir/Pass/Pass.h"
27#include "mlir/Pass/PassManager.h"
28#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
29#include "mlir/Transforms/LoopInvariantCodeMotionUtils.h"
30#include "mlir/Transforms/Passes.h"
31#include "mlir/Transforms/WalkPatternRewriteDriver.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/TypeSwitch.h"
36#define GEN_PASS_DEF_AIEDMATASKSTONPU
37#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
47static LogicalResult emitUnplacedTileError(Operation *op,
48 DMAConfigureTaskOp task_op) {
49 auto err = op->emitOpError(
50 "Cannot lower a DMA task whose tile is not placed; run placement first.");
51 if (task_op.getOperation() != op)
52 err.attachNote(task_op.getLoc()) <<
"Task configured here.";
65 PatternRewriter &rewriter)
const override {
66 DMAConfigureTaskOp task_op = op.getTaskOp();
67 if (!task_op && !op.getTask().getDefiningOp<DMAConfigureTaskForOp>()) {
73 return op.emitOpError(
74 "starts a task carried through control flow that does not come "
75 "from exactly one aiex.dma_configure_task, so its queue push has "
76 "no single buffer descriptor ID. Start the task where it is "
84 AIE::TileOp tile = task_op.tryGetTileOp();
86 return emitUnplacedTileError(op, task_op);
87 Location loc = op.getLoc();
91 AIE::DMABDOp head = task_op.getFirstBd();
92 OpFoldResult idOfr = head ? head.getBdIdValue() : OpFoldResult();
94 auto err = op.emitOpError(
95 "First buffer descriptor in chain has not been assigned an ID");
96 err.attachNote() <<
"Run the `aie-assign-runtime-buffer-descriptor-ids` "
97 "pass first or manually assign an ID.";
100 Value bdIdVal =
getAsValue(rewriter, loc, idOfr, rewriter.getI32Type());
101 if (op.getRepeatParameterAttr())
102 return op.emitOpError(
"has a repeat_parameter; run "
103 "--aie-lower-scratchpad-parameters first");
107 IntegerAttr repeatIdx = op.getRepeatStateTableIdxAttr();
108 OpFoldResult repeatOfr = repeatIdx ? rewriter.getI32IntegerAttr(0)
109 : op.getPushRepeatCount(task_op);
111 !repeatIdx && isConstantIntValue(repeatOfr, 0);
112 Value repeatCount = dyn_cast<Value>(repeatOfr);
114 repeatCount =
getAsValue(rewriter, loc, repeatOfr, rewriter.getI32Type());
115 rewriter.replaceOpWithNewOp<NpuPushQueueOp>(
116 op, tile.getCol(), tile.getRow(), task_op.getDirection(),
117 task_op.getChannel(), op.getPushIssueToken(task_op), repeatCount,
137static DMAConfigureTaskOp resolveConfigureThroughCF(Value task) {
138 SmallVector<DMAConfigureTaskOp> configures;
140 return configures.empty() ? nullptr : configures.front();
144 using OpRewritePattern::OpRewritePattern;
147 PatternRewriter &rewriter)
const override {
148 DMAConfigureTaskOp task_op = op.getTaskOp();
152 task_op = resolveConfigureThroughCF(op.getTask());
157 if (!task_op.getIssueToken()) {
158 auto err = op.emitOpError(
159 "Cannot wait on a BD that is not configured to issue a token.");
160 err.attachNote(task_op.getLoc())
161 <<
"Consider adding attribute `issue_token=true` here.";
164 AIE::TileOp tile = task_op.tryGetTileOp();
166 return emitUnplacedTileError(op, task_op);
167 Location loc = op.getLoc();
168 rewriter.replaceOpWithNewOp<NpuSyncOp>(
191 auto it = block.without_terminator();
192 return block.isEntryBlock() && it.begin() == it.end();
196 auto bd_ops = block.getOps<AIE::DMABDOp>();
198 int n_bd_ops = std::distance(bd_ops.begin(), bd_ops.end());
200 auto error = block.getTerminator()->emitError(
201 "Block ending in this terminator does not contain a required "
202 "aie.dma_bd operation.");
203 error.attachNote(block.getParentOp()->getLoc())
204 <<
"Error encountered while lowering this BD configuration.";
208 auto error = block.getTerminator()->emitOpError(
209 "This block contains multiple aie.dma_bd operations. Exactly one "
212 auto it = bd_ops.begin();
214 for (; it != bd_ops.end(); ++it) {
215 error.attachNote((*it)->getLoc()) <<
"Extra aie.dma_bd operation here.";
219 AIE::DMABDOp bd_op = *bd_ops.begin();
220 bool hasRuntimeBdId = bd_op.getBdIdVal() !=
nullptr;
221 if (!hasRuntimeBdId && !bd_op.getBdId().has_value()) {
222 auto error = bd_op.emitOpError(
223 "Cannot lower buffer descriptor without assigned ID.");
225 <<
"Run the `--aie-assign-runtime-sequence-bd-ids` pass first or "
226 "manually assign an ID to this buffer descriptor.";
227 error.attachNote(block.getParentOp()->getLoc())
228 <<
"Error encountered while lowering this BD configuration.";
241 moveLoopInvariantCode(
243 [](Value v, Region *body) {
244 return !body->isAncestor(v.getParentRegion());
246 [](Operation *inner, Region *) {
247 return inner->getNumRegions() == 0 &&
248 (isPure(inner) || isa<cf::AssertOp>(inner));
250 [&](Operation *inner, Region *) { inner->moveBefore(op); });
254 WalkResult unsupported_ops = block.walk([&](Operation *inner_op) {
255 return llvm::TypeSwitch<Operation *, WalkResult>(inner_op)
257 [&](AIE::DMABDOp bd_op) {
return WalkResult::advance(); })
258 .Case<AIE::UseLockOp>(
259 [&](AIE::UseLockOp lock_op) {
return WalkResult::advance(); })
260 .Case<arith::ConstantOp>(
261 [&](arith::ConstantOp const_op) {
return WalkResult::advance(); })
262 .Case<AIE::NextBDOp>(
263 [&](AIE::NextBDOp lock_op) {
return WalkResult::advance(); })
265 [&](AIE::EndOp lock_op) {
return WalkResult::advance(); })
266 .Default([&](Operation *inner_op) {
267 auto error = block.getParentOp()->emitOpError(
268 "Unsupported operation within BD block.");
269 error.attachNote(inner_op->getLoc())
270 <<
"No lowering to NPU instructions available for this "
272 return WalkResult::interrupt();
275 if (unsupported_ops.wasInterrupted()) {
282 auto bd_ops = block.getOps<AIE::DMABDOp>();
283 AIE::DMABDOp bd_op = *bd_ops.begin();
290 OpFoldResult bdId = {}) {
292 auto buf = bd_op.getBuffer();
293 auto col = tile.getCol();
294 auto row = tile.getRow();
296 auto constBdId = bdId ? getConstantIntValue(bdId) : std::nullopt;
297 uint32_t bd_id = constBdId ? *constBdId : bd_op.getBdId().value_or(0);
298 uint64_t register_addr = target_model.
getDmaBdAddress(col, row, bd_id) +
304 Value runtimeRegisterAddr;
305 if (bdId && !getConstantIntValue(bdId)) {
308 runtimeRegisterAddr = arith::AddIOp::create(
309 builder, bd_op.getLoc(), base,
318 if (bd_op.getOffsetStateTableIdxAttr() && runtimeRegisterAddr)
319 return bd_op->emitOpError(
320 "offset_state_table_idx is not supported with a runtime bd_id; the "
321 "scratchpad update targets a compile-time BD register address.");
327 mlir::BlockArgument buf_arg =
nullptr;
330 if (
auto directArg = llvm::dyn_cast<mlir::BlockArgument>(buf)) {
334 buf_arg = traceResult->rootArg;
335 offset = traceResult->offsetInBytes;
339 if (!target_model.
isShimNOCTile(tile.getCol(), tile.getRow())) {
340 return bd_op->emitOpError(
"DDR memory (runtime input arguments) can "
341 "only be referred to on shim tiles.");
346 return bd_op->emitOpError(
347 "buffer must resolve to a memref argument of the runtime sequence");
348 unsigned arg_idx = *hostIdx;
353 OpFoldResult offsetOfr =
354 bd_op.getOffset() ? OpFoldResult(bd_op.getOffset())
355 : OpFoldResult(builder.getI32IntegerAttr(
356 bd_op.getConstantOffset().value_or(0)));
357 OpFoldResult oneStride = builder.getI32IntegerAttr(1);
360 bd_op.getBufferElementTypeWidthInBytes(), offset,
364 NpuAddressPatchOp::create(builder, bd_op.getLoc(),
368 }
else if (AIE::BufferOp buffer =
369 llvm::dyn_cast<AIE::BufferOp>(buf.getDefiningOp())) {
370 std::optional<uint32_t> bufferAddr = buffer.getAddress();
371 if (!bufferAddr.has_value()) {
372 return bd_op->emitOpError(
373 "Cannot lower buffer without associated address. Run pass "
374 "--aie-assign-buffer-addresses first or manually assign an "
377 uint64_t buf_addr = *bufferAddr;
383 col, row, buffer.getTileOp().getCol(), buffer.getTileOp().getRow());
385 buf_addr += addrOffset.value();
390 const AIE::DmaBdLayout *layout = target_model.
getDmaBdLayout(col, row);
392 return bd_op->emitOpError(
393 "has no buffer descriptor layout on this tile");
395 uint64_t divisor = target_model.
isShimNOCTile(col, row) ? 1 : 4;
396 uint64_t maxByteAddr = (uint64_t(addrBits.
mask()) + 1) * divisor - 1;
397 if (buf_addr > maxByteAddr)
398 return bd_op->emitOpError(
"buffer address exceeds the tile's DMA "
403 bool constOffset = !bd_op.getOffset() || bd_op.getConstantOffset();
404 Location loc = bd_op.getLoc();
407 buf_addr += bd_op.getOffsetInBytes();
408 if (buf_addr > maxByteAddr)
409 return bd_op->emitOpError(
"buffer address exceeds the tile's DMA "
416 {OpFoldResult(builder.getI32IntegerAttr(1))},
417 bd_op.getBufferElementTypeWidthInBytes(),
418 (int64_t)buf_addr, divisor);
421 Type i64 = builder.getI64Type();
422 Value fits = builder.createOrFold<arith::CmpIOp>(
423 loc, arith::CmpIPredicate::ule, byteAddr,
424 arith::ConstantOp::create(builder, loc,
425 IntegerAttr::get(i64, maxByteAddr)));
427 "a runtime DMA offset puts the buffer "
428 "address past the tile's DMA address "
432 byteAddr = arith::DivUIOp::create(
433 builder, loc, byteAddr,
434 arith::ConstantOp::create(builder, loc,
435 IntegerAttr::get(i64, divisor)));
436 addrField = arith::TruncIOp::create(builder, loc, builder.getI32Type(),
438 if (addrBits.
shift != 0)
439 addrField = arith::ShLIOp::create(
440 builder, loc, addrField,
447 ? runtimeRegisterAddr
449 static_cast<uint32_t
>(register_addr));
450 uint32_t addrMask = addrBits.
place(0xFFFFFFFF);
451 if (addrMask == 0xFFFFFFFF)
452 NpuWrite32Op::create(builder, loc, regAddrVal, addrField,
nullptr,
455 NpuMaskWrite32Op::create(builder, loc, regAddrVal, addrField,
457 nullptr,
nullptr,
nullptr);
459 return bd_op->emitOpError(
460 "Buffer argument must be a constant aie.buffer, a runtime sequence "
461 "input argument, or a supported chain of memref.subview, "
462 "memref.view, memref.cast, or memref.reinterpret_cast operations "
463 "rooted at a block argument. Subviews must be static and contiguous; "
464 "views must have a constant byte shift and no dynamic result sizes.");
473 if (bd_op.getOffsetStateTableIdxAttr()) {
474 auto bufType = llvm::cast<BaseMemRefType>(bd_op.getBuffer().getType());
485 FailureOr<BdTemplateFields>
489 std::optional<xilinx::AIE::PacketInfoAttr> packet,
490 bool outOfOrder =
false) {
492 if (std::optional<uint32_t> nextBdId = bd_op.getNextBdId()) {
500 auto info = bd_op.getPacket().value_or(packet.value_or(
nullptr));
502 if (!info.isAssigned()) {
503 bd_op.emitOpError(
"packet has no pkt_id; allocation assigns one, so a "
504 "header at this level must carry it");
512 if (std::optional<int32_t> oooId = bd_op.getOutOfOrderId())
515 if (std::optional<AIE::BDIterationAttr> iter = bd_op.getIteration())
520 auto lockValue = [&](AIE::UseLockOp op, int32_t &constOut,
521 Value &valueOut) -> LogicalResult {
522 if (std::optional<int64_t> c = getConstantIntValue(op.getValue())) {
523 constOut = op.acquireGE() ? -
static_cast<int32_t
>(*c)
524 :
static_cast<int32_t
>(*c);
527 Location loc = op.getLoc();
528 int32_t minValue = op.acquireGE() ? 1 : 0;
530 Value fromMin = builder.createOrFold<arith::SubIOp>(
532 Value inRange = builder.createOrFold<arith::CmpIOp>(
533 loc, arith::CmpIPredicate::ule, fromMin,
536 "a runtime lock value must be in [" +
537 Twine(minValue) +
":" + Twine(maxValue) +
540 valueOut = op.getValue();
542 valueOut = arith::SubIOp::create(
547 AIE::UseLockOp acquire_op, release_op;
552 AIE::LockOp rel_lock = release_op.getLockOp();
556 AIE::LockOp acq_lock = acquire_op.getLockOp();
557 if (std::optional<int32_t> acqLockId = acq_lock.getLockID()) {
565 if (std::optional<int32_t> relLockId = rel_lock.getLockID()) {
573 if (target_model.
isMemTile(tile.getCol(), tile.getRow())) {
575 tile.getCol(), tile.getRow(), rel_lock.colIndex(),
576 rel_lock.rowIndex());
577 if (lockOffset && acquire_op &&
578 acquire_op.getLockOp().getLockID().has_value())
580 if (lockOffset && rel_lock.getLockID().has_value())
597 std::optional<xilinx::AIE::PacketInfoAttr> packet,
598 Value runtimeBdId =
nullptr,
bool outOfOrder =
false) {
600 Location loc = bd_op.getLoc();
601 int col = tile.getCol();
602 int row = tile.getRow();
605 target_model, packet, outOfOrder);
606 if (failed(fieldsOr))
616 OpFoldResult bdIdOfr;
618 bdIdOfr = runtimeBdId;
620 std::optional<uint32_t> staticBdId = bd_op.getBdId();
621 assert(staticBdId &&
"static bd_id must be assigned (checked by "
622 "verifyBdInBlock before lowering)");
623 bdIdOfr = builder.getI32IntegerAttr(*staticBdId);
632 SmallVector<OpFoldResult> sizes(bd_op.getMixedSizes());
633 SmallVector<OpFoldResult> strides(bd_op.getMixedStrides());
634 if (sizes.size() > 4 || strides.size() > 4)
635 return bd_op->emitOpError(
636 "At most four data layout transformation dimensions may be "
637 "provided; aie-decompose-large-dma-bd splits a longer one.");
638 OpFoldResult one = builder.getI64IntegerAttr(1);
639 OpFoldResult zeroOfr = builder.getI64IntegerAttr(0);
640 SmallVector<OpFoldResult, 4> sizes4(4, one), strides4(4, zeroOfr);
642 strides4.back() = one;
643 for (
size_t i = 0; i < sizes.size(); i++) {
644 sizes4[4 - sizes.size() + i] = sizes[i];
645 strides4[4 - strides.size() + i] = strides[i];
649 if (std::optional<AIE::BDIterationAttr> iter = bd_op.getIteration()) {
650 sizes4.front() = builder.getI64IntegerAttr(iter->getSize());
651 strides4.front() = builder.getI64IntegerAttr(iter->getStride());
659 if (Value lenOperand = bd_op.getLen())
661 else if (std::optional<int32_t> constLen = bd_op.getConstantLen())
662 lenOfr = builder.getI32IntegerAttr(*constLen);
663 else if (bd_op.getSizes().empty() && bd_op.getStrides().empty()) {
664 auto bufTy = cast<BaseMemRefType>(bd_op.getBuffer().getType());
665 if (!bufTy.hasStaticShape() ||
666 (bd_op.getOffset() && !bd_op.getConstantOffset()))
667 return bd_op->emitOpError(
668 "runtime-valued BD requires an explicit transfer length; provide a "
669 "`len` for this buffer descriptor.");
670 lenOfr = builder.getI64IntegerAttr(bufTy.getNumElements());
675 std::optional<int64_t> constLen =
676 lenOfr ? getConstantIntValue(lenOfr) : std::nullopt;
677 std::optional<int64_t> dimsLen = 1;
678 for (OpFoldResult s : llvm::drop_begin(sizes4)) {
679 std::optional<int64_t> c = getConstantIntValue(s);
687 static_cast<uint64_t
>(bd_op.getBufferElementTypeWidthInBytes()) * 8;
688 if (!sizes.empty() && constLen && dimsLen && *dimsLen != *constLen) {
689 auto err = bd_op->emitOpError(
690 "Buffer descriptor length does not match length of "
691 "transfer expressed by lowest three dimensions of data "
692 "layout transformation strides/wraps. ")
693 <<
"BD length is " << (*constLen * elemWidth / 8)
694 <<
" bytes. Lowest three dimensions of data layout "
695 "transformation would result in transfer of "
696 << (*dimsLen * elemWidth / 8) <<
" bytes. ";
697 err.attachNote() <<
"Do not include the highest dimension size in "
698 "transfer length, as this is the BD repeat count.";
709 (lenOfr && isConstantIntValue(strides4[3], 1) &&
710 llvm::all_of(ArrayRef(sizes4).drop_back(),
711 [](OpFoldResult v) {
return isConstantIntValue(v, 1); }));
720 SmallVector<Value> bdWords;
723 builder, loc, target_model, col, row, f, sizes4, strides4,
724 elemWidth, bd_op.getBurstLength(), bd_op.getAxcacheOrDefault(),
725 linear ? OpFoldResult() : lenOfr, bdRepeatCount, bdWords)))
730 OpFoldResult offsetOfr =
731 bd_op.getOffset() ? OpFoldResult(bd_op.getOffset())
732 : OpFoldResult(builder.getI32IntegerAttr(
733 bd_op.getConstantOffset().value_or(0)));
735 builder, loc, cast<BaseMemRefType>(trace->rootArg.getType()),
736 trace->offsetInBytes, elemWidth / 8, {offsetOfr}, {one}, sizes4,
744 NpuBlockWriteValuesOp::create(builder, loc, bdBase, bdWords);
750 AIE::DMAChannelDir channelDir,
751 std::optional<xilinx::AIE::PacketInfoAttr> packet,
752 bool outOfOrder =
false) {
754 Value runtimeBdId = bd_op.getBdIdVal();
756 auto buffer_type = llvm::cast<BaseMemRefType>(bd_op.getBuffer().getType());
767 bool runtimeLen = bd_op.getLen() && !bd_op.getConstantLen();
768 bool runtimeOffset = bd_op.getOffset() && !bd_op.getConstantOffset();
770 llvm::any_of(bd_op.getMixedSizes(),
771 [](OpFoldResult s) { return !getConstantIntValue(s); }) ||
772 llvm::any_of(bd_op.getMixedStrides(),
773 [](OpFoldResult s) { return !getConstantIntValue(s); });
776 llvm::any_of(block.getOps<AIE::UseLockOp>(), [](AIE::UseLockOp op) {
777 return !getConstantIntValue(op.getValue());
779 if (runtimeLen || runtimeDims || runtimeOffset || runtimeBdId ||
780 runtimeNextBd || runtimeLock) {
784 if (bd_op.getLengthStateTableIdxAttr())
785 return bd_op->emitOpError(
"length_parameter requires a constant "
786 "offset, bd_id, next_bd and lock value");
787 int col = tile.getCol(), row = tile.getRow();
791 return bd_op->emitOpError(
792 "runtime-valued BD size/stride/len/bd_id/next_bd/lock value is "
793 "only supported on tiles with a DMA buffer descriptor layout (shim "
795 "core tiles); use compile-time constants here.");
796 if (bd_op.getPadDimensions().has_value())
797 return bd_op->emitOpError(
798 "zero padding is not supported with runtime sizes/strides/len.");
803 static_cast<uint64_t
>(bd_op.getBufferElementTypeWidthInBytes()) * 8;
804 SmallVector<OpFoldResult, 4> sizesRev(
805 llvm::reverse(bd_op.getMixedSizes()));
806 SmallVector<OpFoldResult, 4> stridesRev(
807 llvm::reverse(bd_op.getMixedStrides()));
809 bd_op, sizesRev, stridesRev, elemWidth,
813 runtimeBdId, outOfOrder);
821 std::optional<uint32_t> bdIdAttr = bd_op.getBdId();
822 assert(bdIdAttr &&
"static bd_id must be assigned (checked by "
823 "verifyBdInBlock before lowering)");
824 uint32_t bd_id = *bdIdAttr;
825 int64_t offset = bd_op.getOffsetInBytes();
826 uint64_t len = bd_op.getLenInBytes();
827 uint64_t len_addr_granularity = len * 8 / addr_granularity;
829 if (offset * 8 % addr_granularity != 0) {
830 return bd_op->emitOpError(
"Offset must be aligned to ")
831 << (addr_granularity / 8) <<
" byte boundary.";
835 if (len < addr_granularity / 8 && !bd_op.getLengthStateTableIdxAttr()) {
836 return bd_op->emitOpError(
"Transfer size of ")
837 << len <<
" bytes falls below minimum hardware transfer unit of "
838 << (addr_granularity / 8) <<
" bytes.";
841 std::optional<llvm::SmallVector<AIE::BDDimLayoutAttr>> dimsStorage =
842 bd_op.getConstantDimensions();
844 return bd_op->emitOpError(
"internal error folding BD dimensions");
847 std::optional<AIE::BDIterationAttr> iter = bd_op.getIteration();
848 if (iter && dimsStorage->empty())
849 dimsStorage->push_back(AIE::BDDimLayoutAttr::get(
850 builder.getContext(), len / bd_op.getBufferElementTypeWidthInBytes(),
852 std::optional<llvm::ArrayRef<AIE::BDDimLayoutAttr>> dims;
853 if (!dimsStorage->empty())
854 dims = llvm::ArrayRef<AIE::BDDimLayoutAttr>(*dimsStorage);
855 llvm::SmallVector<int64_t, 4> sizes = llvm::SmallVector<int64_t, 4>(4, 0);
856 llvm::SmallVector<int64_t, 4> strides = llvm::SmallVector<int64_t, 4>(4, 0);
859 std::optional<llvm::ArrayRef<AIE::BDPadLayoutAttr>> padDims =
860 bd_op.getPadDimensions();
861 llvm::SmallVector<int64_t, 4> padBefore =
862 llvm::SmallVector<int64_t, 4>(4, 0);
863 llvm::SmallVector<int64_t, 4> padAfter =
864 llvm::SmallVector<int64_t, 4>(4, 0);
865 llvm::fill(padBefore, 0);
866 llvm::fill(padAfter, 0);
874 auto iteration_size = 0;
875 auto iteration_stride = 0;
877 if (dims && !dims->empty()) {
878 llvm::SmallVector<int64_t, 4> input_sizes =
879 llvm::SmallVector<int64_t, 4>(4, 1);
880 llvm::SmallVector<int64_t, 4> input_strides =
881 llvm::SmallVector<int64_t, 4>(4, 0);
882 if (dims->size() > 4) {
883 return bd_op->emitOpError(
884 "At most four data layout transformation dimensions may be "
885 "provided; aie-decompose-large-dma-bd splits a longer one.");
888 for (
size_t i = 0; i < dims->size(); i++) {
892 int j = dims->size() - i - 1;
893 input_sizes[i] = (*dims)[j].getSize();
894 input_strides[i] = (*dims)[j].getStride();
897 input_sizes[3] = iter->getSize();
898 input_strides[3] = iter->getStride();
908 input_sizes, input_strides);
909 if (!treatAsLinear && bd_op.getLengthStateTableIdxAttr())
912 if (padDims.has_value()) {
913 if (!target_model.
isMemTile(tile.getCol(), tile.getRow()))
914 return bd_op->emitOpError()
915 <<
"Padding is only supported by memtile dma bds.";
916 if (padDims->size() > dims->size())
917 return bd_op->emitOpError()
918 <<
"Mismatch number of dimensions between padding(s)"
919 <<
" and wrap(s) and stride(s).";
920 if (channelDir == AIE::DMAChannelDir::MM2S) {
921 for (
size_t i = 0; i < padDims->size(); i++) {
922 int j = padDims->size() - i - 1;
923 padBefore[i] = (*padDims)[j].getConstPadBefore();
924 padAfter[i] = (*padDims)[j].getConstPadAfter();
926 for (
size_t i = padDims->size(); i < dims->size(); i++) {
931 return bd_op->emitOpError()
932 <<
"supports padding only for MM2S direction on MemTiles.";
935 input_strides, sizes, strides);
938 bd_op, [&] {
return bd_op->emitOpError(); }, buffer_type,
939 tile.getCol(), tile.getRow(), input_sizes, input_strides, sizes,
940 strides, treatAsLinear))) {
944 iteration_size = sizes[3];
945 iteration_stride = strides[3];
947 if (!treatAsLinear) {
950 d0stride = strides[0];
954 d1stride = strides[1];
957 d2stride = strides[2];
962 d2size = (target_model.
isMemTile(tile.getCol(), tile.getRow()))
966 if (input_sizes[3] > 1 && input_strides[3] == 0) {
972 iteration_stride = 0;
979 uint64_t len_dims_addr_granularity = 1;
980 for (
size_t i = 0; i < 3; i++) {
981 len_dims_addr_granularity *= sizes[i];
984 bool runtimeLengthFromZero =
985 len_addr_granularity == 0 && bd_op.getLengthStateTableIdxAttr();
986 if (len_dims_addr_granularity != len_addr_granularity &&
987 !runtimeLengthFromZero) {
990 "Buffer descriptor length does not match length of transfer "
991 "expressed by lowest three dimensions of data layout "
992 "transformation strides/wraps. ")
993 <<
"BD length is " << (len_addr_granularity * addr_granularity / 8)
995 <<
"Lowest three dimensions of data layout transformation would "
996 "result in transfer of "
997 << (len_dims_addr_granularity * addr_granularity / 8) <<
" bytes. ";
998 err.attachNote() <<
"Do not include the highest dimension size in "
999 "transfer length, as this is the BD repeat count.";
1003 if (padDims && target_model.
isMemTile(tile.getCol(), tile.getRow()) &&
1004 channelDir == AIE::DMAChannelDir::MM2S) {
1005 return bd_op->emitOpError()
1006 <<
"Padding requires n-d data layouts expressed as "
1007 <<
"wrap(s) and stride(s).";
1010 return bd_op->emitOpError()
1011 <<
" Padding is supported only on MemTiles.";
1015 target_model, packet, outOfOrder);
1016 if (failed(fieldsOr))
1021 NpuWriteBdOp::create(
1022 builder, bd_op.getLoc(), tile.getCol(), bd_id, len_addr_granularity,
1042 padBefore[1], padBefore[2],
1043 padAfter[0], padAfter[1],
1045 bd_op.getBurstLength(),
1048 ? builder.getI32IntegerAttr(bd_op.getAxcacheOrDefault())
1056 auto lengthUnit = bd_op.getLengthUnit();
1057 if (bd_op.getLengthStateTableIdxAttr() && lengthUnit)
1059 builder, bd_op, buffer_type, *lengthUnit, target_model, tile.getCol(),
1060 tile.getRow(), bd_id);
1065 Region &body = op.getBody();
1066 for (
auto &block : body) {
1071 if (AIE::NextBDOp next_bd_op =
1072 llvm::dyn_cast<AIE::NextBDOp>(block.getTerminator())) {
1073 if (bd_op.getNextBdId().has_value()) {
1075 bd_op.emitOpError(
"Cannot specify both next_bd_id attribute and "
1076 "aie.next_bd operation.");
1077 error.attachNote(next_bd_op.getLoc())
1078 <<
"Potentially conflicting next buffer descriptor ID specified "
1082 Block &next_bd_block = *next_bd_op.getDest();
1085 if (Value nextBdIdVal = next_dma_bd_op.getBdIdVal())
1088 bd_op.setNextBdId(next_dma_bd_op.getBdId());
1089 OpBuilder builder(next_bd_op);
1090 AIE::EndOp::create(builder, next_bd_op.getLoc());
1098 DMAConfigureTaskOp op,
1101 int col = tile.getCol();
1102 int row = tile.getRow();
1103 int channel = op.getChannel();
1106 col, row, channel, AIE::DMAChannelDir::S2MM);
1108 std::string ctrlRegName =
"DMA_S2MM_" + std::to_string(channel) +
"_Ctrl";
1114 return op.emitOpError(
"target has no ")
1115 << ctrlRegName <<
" register in its register database";
1117 ctrlReg->
getField(
"Enable_Out_of_Order");
1119 return op.emitOpError()
1121 <<
" has no Enable_Out_of_Order field in the register database";
1122 std::optional<uint32_t> oooMask = tm.
getFieldMask(*oooField);
1124 return op.emitOpError()
1126 <<
" Enable_Out_of_Order field does not fit in a 32-bit register";
1129 return op.emitOpError()
1131 <<
" Enable_Out_of_Order field value does not fit in its bit "
1134 Location loc = op.getLoc();
1135 IntegerAttr colAttr = builder.getI32IntegerAttr(col);
1136 IntegerAttr rowAttr = builder.getI32IntegerAttr(row);
1140 NpuMaskWrite32Op::create(builder, loc, addr, val, mask,
nullptr, colAttr,
1146 DMAConfigureTaskOp op,
1147 const llvm::DenseMap<Operation *, bool> &onlyPushedOnce) {
1148 OpBuilder builder(op);
1149 AIE::TileOp tile = op.tryGetTileOp();
1151 return emitUnplacedTileError(op, op);
1153 if (!op.use_empty()) {
1154 auto err = op.emitOpError(
"Cannot lower while op still has uses.");
1155 mlir::Operation::use_range uses = op.getOperation()->getUses();
1156 for (
auto &use : uses) {
1157 err.attachNote(use.getOwner()->getLoc()) <<
"Used here.";
1162 Region &body = op.getBody();
1167 for (
auto &it : body) {
1177 AIE::UseLockOp acquire, release;
1183 if (op.getOutOfOrder()) {
1194 auto channelDir = op.getDirection();
1195 auto packet = op.getPacket();
1200 SmallVector<std::pair<Location, int64_t>> unrepeatedIterations;
1201 if (onlyPushedOnce.lookup(op)) {
1202 for (
auto bd_op : body.getOps<AIE::DMABDOp>()) {
1203 SmallVector<OpFoldResult> sizes = bd_op.getMixedSizes();
1204 std::optional<int64_t> iterations;
1205 if (std::optional<AIE::BDIterationAttr> iter = bd_op.getIteration())
1206 iterations = iter->getSize();
1207 else if (sizes.size() == 4)
1208 iterations = getConstantIntValue(sizes.front());
1209 if (iterations && *iterations > 1)
1210 unrepeatedIterations.emplace_back(bd_op.getLoc(), *iterations);
1215 for (
auto &block : body) {
1220 op.getOutOfOrder()))) {
1225 for (
auto [loc, iterations] : unrepeatedIterations)
1226 mlir::emitWarning(loc)
1227 <<
"iteration dimension of size " << iterations
1228 <<
" is pushed with repeat_count 0, so only its first iteration "
1229 "runs; set repeat_count = "
1238 AIE::DeviceOp device,
1239 const llvm::DenseMap<Operation *, bool> &onlyPushedOnce) {
1240 WalkResult result = device.walk([&](DMAConfigureTaskOp op) {
1242 return WalkResult::interrupt();
1244 return WalkResult::advance();
1246 if (result.wasInterrupted()) {
1273 if (llvm::none_of(device.getOps<AIE::RuntimeSequenceOp>(), [](
auto seq) {
1275 .walk([](RegionBranchOpInterface) {
1276 return WalkResult::interrupt();
1282 RewritePatternSet patterns(&getContext());
1283 scf::ForOp::getCanonicalizationPatterns(patterns, &getContext());
1284 scf::IfOp::getCanonicalizationPatterns(patterns, &getContext());
1285 GreedyRewriteConfig config;
1286 config.enableFolding(
false).enableConstantCSE(
false);
1287 if (failed(applyPatternsGreedily(device, std::move(patterns), config)))
1290 OpPassManager pm(AIE::DeviceOp::getOperationName());
1291 pm.addPass(createRemoveDeadValuesPass());
1292 return runPipeline(pm, device);
1296 AIE::DeviceOp device = getOperation();
1300 RewritePatternSet patterns(&getContext());
1301 llvm::DenseMap<Operation *, bool> onlyPushedOnce;
1304 walkAndApplyPatterns(device, std::move(patterns));
1307 bool unlowered =
false;
1308 device.walk([&](Operation *op) {
1309 if (isa<DMAStartTaskOp, DMAAwaitTaskOp>(op)) {
1310 op->emitError() <<
"failed to legalize operation '" << op->getName()
1316 return signalPassFailure();
1321 signalPassFailure();
1325 signalPassFailure();
1332std::unique_ptr<OperationPass<AIE::DeviceOp>>
1334 return std::make_unique<AIEDMATasksToNPUPass>();
std::optional< uint32_t > encodeFieldValue(const BitFieldInfo &field, uint32_t value) const
Encode a field value with proper bit shifting.
std::optional< uint32_t > getMemLocalBaseAddress(int localCol, int localRow, int memCol, int memRow) const
Return the memory base address (or offset) in the local tile when accessing a neighbor's memory or an...
uint32_t getLocalDmaControlAddress(int col, int row, int channel, AIE::DMAChannelDir direction) const
Return the DMA task-queue register address relative to its tile.
bool isCoreTile(int col, int row) const
Return true if the given tile is a Core tile.
virtual const DmaBdLayout * getDmaBdLayout(AIETileType tileType) const
Return the register layout of a buffer descriptor on the given tile type, or null if the target has n...
bool isMemTile(int col, int row) const
Return true if the given tile is a Mem tile.
std::optional< uint32_t > getLockLocalBaseIndex(int localCol, int localRow, int lockCol, int lockRow) const
Return the lock base index (or offset) in the local tile when accessing a neighbor's lock or an empty...
std::optional< uint32_t > getFieldMask(const BitFieldInfo &field) const
Compute a 32-bit mask for a register field.
const RegisterInfo * lookupRegister(llvm::StringRef name, TileID tile, bool isMem=false) const
Register Database API - provides access to register and event information for trace configuration and...
virtual uint64_t getDmaBdAddress(int col, int row, uint32_t bd_id, int channel=-1, AIE::DMAChannelDir direction=AIE::DMAChannelDir::MM2S) const =0
Return the array address of the dma buffer descriptor for the given col, row, buffer descriptor id,...
virtual uint32_t getDmaBdAddressOffset(int col, int row) const =0
Return the offset of the base address field within the shim dma buffer descriptor.
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual uint32_t getMaxLockValue() const =0
Return the maximum value that can be stored in a lock register.
virtual uint32_t getAddressGenGranularity() const =0
Return the data bus width of the device.
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)
DMAConfigureTaskOp getUniqueReachableConfigure(Value task)
std::optional< SubviewTraceResult > traceSubviewToBlockArgument(Value value)
mlir::Value buildArgPlusValue(mlir::OpBuilder &builder, mlir::Location loc, llvm::ArrayRef< mlir::OpFoldResult > elementOffsets, llvm::ArrayRef< mlir::OpFoldResult > strides, int64_t elemWidthBytes, int64_t baseByteOffset, uint32_t granuleBytes)
mlir::Value getAsValue(mlir::OpBuilder &builder, mlir::Location loc, mlir::OpFoldResult ofr, mlir::Type intType)
mlir::LogicalResult guardWithinHostBuffer(mlir::OpBuilder &builder, mlir::Location loc, mlir::BaseMemRefType hostBufferType, int64_t baseByteOffset, int64_t elemWidthBytes, llvm::ArrayRef< mlir::OpFoldResult > offsets, llvm::ArrayRef< mlir::OpFoldResult > offsetStrides, llvm::ArrayRef< mlir::OpFoldResult > sizes, llvm::ArrayRef< mlir::OpFoldResult > strides)
mlir::LogicalResult buildBdWords(mlir::OpBuilder &builder, mlir::Location loc, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow, const BdTemplateFields &fields, llvm::ArrayRef< mlir::OpFoldResult > mixedSizes, llvm::ArrayRef< mlir::OpFoldResult > mixedStrides, uint64_t elemWidth, uint32_t burstLength, uint32_t axcache, mlir::OpFoldResult lenElems, mlir::Value &repeatCountOut, llvm::SmallVectorImpl< mlir::Value > &wordsOut)
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIEDMATasksToNPUPass()
void eraseDeadArith(mlir::Operation *root)
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)
mlir::Value getBdRegisterBase(mlir::OpBuilder &builder, mlir::Location loc, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow, mlir::OpFoldResult bdId)
mlir::LogicalResult emitRuntimeCheck(mlir::OpBuilder &builder, mlir::Location loc, mlir::Value cond, const llvm::Twine &message)
mlir::LogicalResult verifyConstBdRealizability(mlir::Operation *op, llvm::ArrayRef< mlir::OpFoldResult > sizes, llvm::ArrayRef< mlir::OpFoldResult > strides, uint64_t elemWidth, uint32_t gran)
bool getReachableConfigures(Value task, SmallVectorImpl< DMAConfigureTaskOp > &configures)
LogicalResult emitUpdateBdAddressFromOffsetParameter(OpBuilder &builder, Operation *bdOp, BaseMemRefType bufType, uint64_t registerAddr)
LogicalResult emitUpdateBdLengthFromParameter(OpBuilder &builder, Operation *bdOp, BaseMemRefType bufType, int64_t lengthUnit, const AIE::AIETargetModel &targetModel, int col, int row, int bdId)
std::optional< unsigned > getHostBufferArgIndex(BlockArgument arg)
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
mlir::LogicalResult verifyBdLockPair(mlir::Block &block, bool outOfOrder, UseLockOp &acquire, UseLockOp &release)
const AIETargetModel & getTargetModel(mlir::Operation *op)
void placeRuntimeLengthDimension(llvm::MutableArrayRef< int64_t > sizes, llvm::MutableArrayRef< int64_t > strides)
void runOnOperation() override
LogicalResult dropDeadTaskCarries(AIE::DeviceOp device)
bool shouldSkipBlock(Block &block)
LogicalResult rewriteSingleBDDynamic(OpBuilder &builder, Block &block, AIE::DMABDOp bd_op, AIE::TileOp &tile, std::optional< xilinx::AIE::PacketInfoAttr > packet, Value runtimeBdId=nullptr, bool outOfOrder=false)
void hoistScalarOpsOutOfBdBlocks(DMAConfigureTaskOp op)
LogicalResult rewriteDMAConfigureTaskOp(AIE::DeviceOp device, const llvm::DenseMap< Operation *, bool > &onlyPushedOnce)
LogicalResult verifyNoUnsupportedOpsInBlock(Block &block)
LogicalResult rewriteSingleBD(OpBuilder &builder, Block &block, AIE::TileOp &tile, AIE::DMAChannelDir channelDir, std::optional< xilinx::AIE::PacketInfoAttr > packet, bool outOfOrder=false)
LogicalResult emitOutOfOrderChannelEnable(OpBuilder &builder, DMAConfigureTaskOp op, AIE::TileOp tile)
AIE::DMABDOp getBdForBlock(Block &block)
LogicalResult hoistNextBdOpsIntoAttrs(DMAConfigureTaskOp op)
LogicalResult verifyBdInBlock(Block &block)
llvm::DenseMap< AIE::DMABDOp, Value > runtimeNextBdId
FailureOr< BdTemplateFields > gatherBdTemplateFields(OpBuilder &builder, Block &block, AIE::DMABDOp bd_op, AIE::TileOp &tile, const AIE::AIETargetModel &target_model, std::optional< xilinx::AIE::PacketInfoAttr > packet, bool outOfOrder=false)
LogicalResult rewriteSingleDMAConfigureTaskOp(DMAConfigureTaskOp op, const llvm::DenseMap< Operation *, bool > &onlyPushedOnce)
LogicalResult setAddressForSingleBD(OpBuilder &builder, AIE::DMABDOp &bd_op, AIE::TileOp &tile, OpFoldResult bdId={})
LogicalResult matchAndRewrite(DMAAwaitTaskOp op, PatternRewriter &rewriter) const override
llvm::DenseMap< Operation *, bool > & onlyPushedOnce
LogicalResult matchAndRewrite(DMAStartTaskOp op, PatternRewriter &rewriter) const override
DMAStartTaskOpPattern(MLIRContext *context, llvm::DenseMap< Operation *, bool > &onlyPushedOnce)
mlir::Value next_bd_id_val
int32_t iteration_current
mlir::Value lock_rel_val_val
mlir::Value lock_acq_val_val
Bit field information for a register.
uint32_t place(uint64_t value) const
const BitFieldInfo * getField(llvm::StringRef fieldName) const