31#include "mlir/Dialect/Arith/IR/Arith.h"
32#include "mlir/Dialect/SCF/IR/SCF.h"
33#include "mlir/Pass/Pass.h"
34#include "llvm/ADT/DenseMap.h"
35#include "llvm/ADT/DenseSet.h"
36#include "llvm/ADT/SetVector.h"
39#define GEN_PASS_DEF_AIELOWERDYNAMICBDPOOL
40#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
49struct AIELowerDynamicBDPoolPass
50 : xilinx::AIEX::impl::AIELowerDynamicBDPoolBase<AIELowerDynamicBDPoolPass> {
52 xilinx::AIEX::impl::AIELowerDynamicBDPoolBase<AIELowerDynamicBDPoolPass>;
53 AIELowerDynamicBDPoolPass() =
default;
54 AIELowerDynamicBDPoolPass(
const AIELowerDynamicBDPoolOptions &options)
60 llvm::DenseMap<Value, SmallVector<Value, 2>> pairedId;
63 llvm::DenseMap<Value, std::pair<int, int>> tileForTask;
67 llvm::DenseMap<Value, DMAConfigureTaskOp> originConfigure;
73 llvm::DenseSet<Value> taskValues;
77 llvm::DenseMap<Value, unsigned> taskIdCount;
85 SmallVector<unsigned> carried;
86 SmallVector<unsigned> idCount;
88 SmallVector<Operation *> placeholders;
90 SmallVector<Fixup> fixups;
94 static std::pair<uint32_t, uint32_t> bdPartition(DMAConfigureTaskOp cfg) {
95 AIE::TileOp tile = cfg.getTileOp();
97 tile.getCol(), tile.getRow(), cfg.getChannel());
102 static SmallVector<AIE::DMABDOp, 2> chainBds(DMAConfigureTaskOp cfg) {
103 SmallVector<AIE::DMABDOp, 2> bds;
104 cfg.walk([&](AIE::DMABDOp bd) { bds.push_back(bd); });
108 void markTask(Value v,
unsigned idCount, SmallVectorImpl<Value> &worklist) {
109 if (taskValues.insert(v).second) {
110 taskIdCount[v] = idCount;
111 worklist.push_back(v);
120 void computeTaskValues(AIE::RuntimeSequenceOp seq) {
123 SmallVector<Value> worklist;
124 seq.walk([&](DMAConfigureTaskOp c) {
125 markTask(c.getResult(), chainBds(c).size(), worklist);
127 while (!worklist.empty()) {
128 Value v = worklist.pop_back_val();
129 unsigned idCount = taskIdCount.lookup(v);
130 for (OpOperand &use : v.getUses()) {
131 Operation *user = use.getOwner();
132 if (
auto f = dyn_cast<scf::ForOp>(user)) {
133 for (
unsigned k = 0; k < f.getInitArgs().size(); ++k)
134 if (f.getInitArgs()[k] == v)
135 markTask(f.getRegionIterArg(k), idCount, worklist);
136 }
else if (isa<scf::YieldOp>(user)) {
137 unsigned k = use.getOperandNumber();
138 Operation *parent = user->getParentOp();
139 if (
auto pf = dyn_cast<scf::ForOp>(parent))
140 markTask(pf.getResult(k), idCount, worklist);
141 else if (
auto pi = dyn_cast<scf::IfOp>(parent))
142 markTask(pi.getResult(k), idCount, worklist);
151 LogicalResult verifyChainLengths(AIE::RuntimeSequenceOp seq) {
152 auto agree = [&](Operation *op, Value a, Value b,
153 StringRef what) -> LogicalResult {
154 if (!taskValues.contains(a) || !taskValues.contains(b) ||
155 taskIdCount.lookup(a) == taskIdCount.lookup(b))
157 return op->emitOpError()
158 << what <<
" a " << taskIdCount.lookup(a) <<
"-BD chain and a "
159 << taskIdCount.lookup(b)
160 <<
"-BD chain at the same position; a pool-allocated task "
161 "carries one buffer descriptor id per BD, so every task "
162 "flowing through one position must have the same chain length";
164 WalkResult wr = seq.walk([&](Operation *op) -> WalkResult {
165 if (
auto ifOp = dyn_cast<scf::IfOp>(op)) {
166 if (ifOp.getNumResults() == 0 || ifOp.elseBlock() ==
nullptr)
167 return WalkResult::advance();
168 auto thenYield = cast<scf::YieldOp>(ifOp.thenBlock()->getTerminator());
169 auto elseYield = cast<scf::YieldOp>(ifOp.elseBlock()->getTerminator());
170 for (
unsigned k = 0; k < ifOp.getNumResults(); ++k)
171 if (failed(agree(ifOp, thenYield.getOperand(k),
172 elseYield.getOperand(k),
173 "yields from its branches")))
174 return WalkResult::interrupt();
175 }
else if (
auto forOp = dyn_cast<scf::ForOp>(op)) {
176 auto yield = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
177 for (
unsigned k = 0; k < forOp.getInitArgs().size(); ++k)
178 if (failed(agree(forOp, forOp.getInitArgs()[k], yield.getOperand(k),
179 "carries from its init and its yield")))
180 return WalkResult::interrupt();
182 return WalkResult::advance();
184 return failure(wr.wasInterrupted());
187 LogicalResult lowerConfigure(DMAConfigureTaskOp cfg) {
188 SmallVector<AIE::DMABDOp, 2> bds = chainBds(cfg);
189 if (llvm::any_of(bds, [](AIE::DMABDOp bd) {
return bd.getBdIdVal(); }))
195 for (AIE::DMABDOp bd : bds)
196 if (bd.getBdId().has_value())
197 return bd.emitOpError(
198 "pins a buffer descriptor ID inside a runtime-bound "
199 "sequence that draws IDs from the dynamic pool; a pinned "
200 "ID would collide with a runtime-allocated one. Allocate "
201 "every BD in this sequence from the pool (drop the "
202 "bd_id), or make the sequence straight-line so the static "
203 "allocator assigns all IDs.");
205 AIE::TileOp tile = cfg.getTileOp();
206 auto [begin, end] = bdPartition(cfg);
207 if (bds.size() > end - begin)
208 return cfg.emitOpError()
209 <<
"chains " << bds.size() <<
" buffer descriptors, but channel "
210 << cfg.getChannel() <<
" of tile (" << tile.getCol() <<
","
211 << tile.getRow() <<
") draws from a pool of only " << end - begin
214 SmallVector<Value, 2> ids;
215 for (AIE::DMABDOp bd : bds) {
217 DMABdPoolPopOp::create(b, cfg.getLoc(), b.getI32Type(), tile.getCol(),
218 tile.getRow(), begin, end)
220 bd.getBdIdValMutable().assign(bdId);
223 pairedId[cfg.getResult()] = ids;
233 scf::ForOp carryIdsThroughLoop(scf::ForOp forOp) {
234 SmallVector<unsigned> carried;
235 SmallVector<unsigned> idCount;
236 SmallVector<Value> newInits;
237 for (
unsigned k = 0; k < forOp.getInitArgs().size(); ++k) {
238 Value init = forOp.getInitArgs()[k];
239 if (!taskValues.contains(init))
241 SmallVector<Value, 2> ids = pairedId.lookup(init);
242 assert(ids.size() == taskIdCount.lookup(init) &&
243 "for init carries a task but its ids are not all paired");
244 carried.push_back(k);
245 idCount.push_back(ids.size());
246 newInits.append(ids.begin(), ids.end());
251 unsigned nOrig = forOp.getInitArgs().size();
252 IRRewriter rewriter(forOp);
253 auto newYields = [&](OpBuilder &b, Location loc,
254 ArrayRef<BlockArgument> newBBArgs) {
258 return SmallVector<Value>(newBBArgs.begin(), newBBArgs.end());
261 FailureOr<LoopLikeOpInterface> newLoop = forOp.replaceWithAdditionalYields(
264 assert(succeeded(newLoop) &&
"scf.for additional-yields rewrite failed");
267 auto newFor = cast<scf::ForOp>(
268 newLoop->getOperation());
270 unsigned off = nOrig;
271 for (
auto [i, k] : llvm::enumerate(carried)) {
275 taskValues.insert(newFor.getRegionIterArg(k));
276 taskValues.insert(newFor.getResult(k));
277 taskIdCount[newFor.getRegionIterArg(k)] = idCount[i];
278 taskIdCount[newFor.getResult(k)] = idCount[i];
279 SmallVector<Value, 2> argIds, resIds;
280 for (
unsigned j = 0; j < idCount[i]; ++j) {
281 argIds.push_back(newFor.getRegionIterArg(off + j));
282 resIds.push_back(newFor.getResult(off + j));
284 pairedId[newFor.getRegionIterArg(k)] = argIds;
285 pairedId[newFor.getResult(k)] = resIds;
288 fixups.push_back({newFor, carried, idCount, nOrig, {}});
297 LogicalResult carryIdsThroughIf(scf::IfOp ifOp) {
298 if (ifOp.getNumResults() == 0 || ifOp.elseBlock() ==
nullptr)
301 auto thenYield = cast<scf::YieldOp>(ifOp.thenBlock()->getTerminator());
302 auto elseYield = cast<scf::YieldOp>(ifOp.elseBlock()->getTerminator());
304 SmallVector<unsigned> carried;
305 SmallVector<unsigned> idCount;
306 for (
unsigned k = 0; k < ifOp.getNumResults(); ++k) {
307 bool thenT = taskValues.contains(thenYield.getOperand(k));
308 bool elseT = taskValues.contains(elseYield.getOperand(k));
309 if (thenT && elseT) {
310 carried.push_back(k);
311 idCount.push_back(taskIdCount.lookup(thenYield.getOperand(k)));
312 }
else if (thenT || elseT)
313 return ifOp.emitOpError(
314 "yields a pool-allocated task on only one branch of an scf.if; "
316 "branches must yield the task (and its buffer descriptor id) so it "
317 "can be freed after the if");
322 unsigned nOrig = ifOp.getNumResults();
324 SmallVector<Type> resultTypes(ifOp.getResultTypes());
325 unsigned numIds = llvm::sum_of(idCount);
326 resultTypes.append(numIds, b.getI32Type());
329 scf::IfOp::create(b, ifOp.getLoc(), resultTypes, ifOp.getCondition(),
331 newIf.getThenRegion().takeBody(ifOp.getThenRegion());
332 newIf.getElseRegion().takeBody(ifOp.getElseRegion());
338 SmallVector<Operation *> placeholders;
339 auto appendPlaceholders = [&](scf::YieldOp y) {
341 SmallVector<Value> ids;
342 for (
unsigned j = 0; j < numIds; ++j) {
344 arith::ConstantOp::create(yb, y.getLoc(), yb.getI32IntegerAttr(0));
345 placeholders.push_back(c);
348 y->insertOperands(y->getNumOperands(), ids);
350 appendPlaceholders(cast<scf::YieldOp>(newIf.thenBlock()->getTerminator()));
351 appendPlaceholders(cast<scf::YieldOp>(newIf.elseBlock()->getTerminator()));
353 unsigned off = nOrig;
354 for (
auto [i, k] : llvm::enumerate(carried)) {
357 taskValues.insert(newIf.getResult(k));
358 taskIdCount[newIf.getResult(k)] = idCount[i];
359 SmallVector<Value, 2> resIds;
360 for (
unsigned j = 0; j < idCount[i]; ++j)
361 resIds.push_back(newIf.getResult(off + j));
362 pairedId[newIf.getResult(k)] = resIds;
365 for (
unsigned k = 0; k < nOrig; ++k)
366 ifOp.getResult(k).replaceAllUsesWith(newIf.getResult(k));
368 fixups.push_back({newIf, carried, idCount, nOrig, placeholders});
377 for (Fixup &fx : fixups) {
378 SmallVector<scf::YieldOp, 2> yields;
379 if (
auto forOp = dyn_cast<scf::ForOp>(fx.op)) {
380 yields.push_back(cast<scf::YieldOp>(forOp.getBody()->getTerminator()));
382 auto ifOp = cast<scf::IfOp>(fx.op);
383 for (Block *blk : {ifOp.thenBlock(), ifOp.elseBlock()})
384 yields.push_back(cast<scf::YieldOp>(blk->getTerminator()));
386 for (scf::YieldOp yield : yields) {
387 unsigned off = fx.nOrig;
388 for (
auto [i, k] : llvm::enumerate(fx.carried)) {
389 SmallVector<Value, 2> ids = pairedId.lookup(yield.getOperand(k));
390 assert(ids.size() == fx.idCount[i] &&
391 "carried task yields the wrong number of ids");
392 for (
auto [j,
id] : llvm::enumerate(ids))
393 yield.setOperand(off + j,
id);
394 off += fx.idCount[i];
398 for (Fixup &fx : fixups)
399 for (Operation *p : fx.placeholders)
414 LogicalResult computeMetadata(AIE::RuntimeSequenceOp seq) {
416 originConfigure.clear();
417 SmallVector<Value> worklist;
418 auto setMeta = [&](Value v, std::pair<int, int> tile,
419 DMAConfigureTaskOp origin) {
420 if (tileForTask.count(v))
422 tileForTask[v] = tile;
423 originConfigure[v] = origin;
424 worklist.push_back(v);
426 seq.walk([&](DMAConfigureTaskOp c) {
427 AIE::TileOp t = c.getTileOp();
428 setMeta(c.getResult(), {t.getCol(), t.getRow()}, c);
430 while (!worklist.empty()) {
431 Value v = worklist.pop_back_val();
432 auto tile = tileForTask[v];
433 DMAConfigureTaskOp origin = originConfigure[v];
434 for (OpOperand &use : v.getUses()) {
435 Operation *user = use.getOwner();
436 if (
auto f = dyn_cast<scf::ForOp>(user)) {
437 for (
unsigned k = 0; k < f.getInitArgs().size(); ++k)
438 if (f.getInitArgs()[k] == v) {
439 setMeta(f.getRegionIterArg(k), tile, origin);
440 setMeta(f.getResult(k), tile, origin);
442 }
else if (isa<scf::YieldOp>(user)) {
443 auto ifOp = dyn_cast<scf::IfOp>(user->getParentOp());
444 if (ifOp && user->getBlock() == ifOp.thenBlock())
445 setMeta(ifOp.getResult(use.getOperandNumber()), tile, origin);
452 WalkResult wr = seq.walk([&](scf::IfOp ifOp) -> WalkResult {
453 if (ifOp.getNumResults() == 0 || ifOp.elseBlock() ==
nullptr)
454 return WalkResult::advance();
455 auto thenYield = cast<scf::YieldOp>(ifOp.thenBlock()->getTerminator());
456 auto elseYield = cast<scf::YieldOp>(ifOp.elseBlock()->getTerminator());
457 for (
unsigned k = 0; k < ifOp.getNumResults(); ++k)
458 if (failed(checkChannelAgreement(
459 ifOp, thenYield.getOperand(k), elseYield.getOperand(k),
460 "yields tasks on different physical channels "
461 "(tile/direction/channel) from its two branches at "
462 "the same result; a pooled buffer descriptor id and "
463 "its completion sync belong to one channel")))
464 return WalkResult::interrupt();
465 return WalkResult::advance();
467 if (wr.wasInterrupted())
476 WalkResult forWr = seq.walk([&](scf::ForOp forOp) -> WalkResult {
477 auto yield = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
478 for (
unsigned k = 0; k < forOp.getInitArgs().size(); ++k)
479 if (failed(checkChannelAgreement(
480 forOp, forOp.getInitArgs()[k], yield.getOperand(k),
481 "carries a task on different physical channels "
482 "(tile/direction/channel) between its init and its "
483 "per-iteration reconfiguration; a pooled buffer "
484 "descriptor id and its completion sync belong to one "
486 return WalkResult::interrupt();
487 return WalkResult::advance();
489 return forWr.wasInterrupted() ? failure() : success();
497 LogicalResult checkChannelAgreement(Operation *op, Value a, Value b,
499 DMAConfigureTaskOp originA = originConfigure.lookup(a);
500 DMAConfigureTaskOp originB = originConfigure.lookup(b);
501 if (originA && originB && channelKey(originA) != channelKey(originB)) {
502 op->emitOpError(msg);
516 static llvm::SetVector<Value> aliasesAfter(Value task) {
517 llvm::SetVector<Value> aliases;
518 aliases.insert(task);
519 for (
unsigned i = 0; i < aliases.size(); ++i) {
520 for (OpOperand &use : aliases[i].getUses()) {
521 Operation *user = use.getOwner();
522 auto branch = dyn_cast<RegionBranchOpInterface>(user);
523 if (!branch && user->hasTrait<OpTrait::IsTerminator>())
525 dyn_cast_or_null<RegionBranchOpInterface>(user->getParentOp());
528 RegionBranchSuccessorMapping mapping;
529 branch.getSuccessorOperandInputMapping(mapping);
530 aliases.insert_range(mapping.lookup(&use));
539 static LogicalResult verifyNoStartAfterFree(Value task, Operation *freeOp) {
540 llvm::SetVector<Value> aliases = aliasesAfter(task);
541 auto startsAfterFree = [&](DMAStartTaskOp start) {
542 for (Block *block = freeOp->getBlock(); block;
543 block = block->getParentOp()->getBlock()) {
544 Operation *startAt = block->findAncestorOpInBlock(*start);
547 Operation *freeAt = block->findAncestorOpInBlock(*freeOp);
548 if (freeAt != startAt && freeAt->isBeforeInBlock(startAt))
552 Value started = start.getTask();
553 for (
auto loop = freeOp->getParentOfType<scf::ForOp>(); loop;
554 loop = loop->getParentOfType<scf::ForOp>()) {
555 if (!loop->isAncestor(start))
557 if (!loop.getBody()->getParent()->isAncestor(started.getParentRegion()))
559 auto arg = dyn_cast<BlockArgument>(started);
560 if (arg && arg.getOwner() == loop.getBody() &&
561 arg.getArgNumber() >= loop.getNumInductionVars()) {
562 unsigned k = arg.getArgNumber() - loop.getNumInductionVars();
563 if (aliases.contains(loop.getYieldedValues()[k]))
569 for (Value alias : aliases)
570 for (Operation *user : alias.getUsers()) {
571 auto start = dyn_cast<DMAStartTaskOp>(user);
572 if (!start || !startsAfterFree(start))
574 auto diag = start.emitOpError(
575 "starts a task whose buffer descriptor ID was already returned "
576 "to the runtime pool; the pool may have handed it to another "
577 "task. Free the task after its last start instead.");
578 diag.attachNote(freeOp->getLoc()) <<
"returned here";
584 LogicalResult lowerRelease(Value task, Operation *op) {
585 SmallVector<Value, 2> ids = pairedId.lookup(task);
587 return op->emitOpError(
588 "does not resolve to a task allocated from the runtime pool; cannot "
589 "return its buffer descriptor ID");
590 if (failed(verifyNoStartAfterFree(task, op)))
592 auto tile = tileForTask.lookup(task);
593 auto [begin, end] = bdPartition(originConfigure.lookup(task));
596 DMABdPoolPushOp::create(b, op->getLoc(), tile.first, tile.second, begin,
610 void guardQueueDepth(AIE::RuntimeSequenceOp seq) {
611 const AIE::AIETargetModel &tm =
614 auto effectOf = [&](Operation *op) -> QueueEffect {
615 if (
auto push = dyn_cast<NpuPushQueueOp>(op))
617 static_cast<int>(push.getRow()),
618 static_cast<int>(push.getDirection()),
619 static_cast<int>(push.getChannel())},
620 push.getIssueToken());
621 if (
auto sync = dyn_cast<NpuSyncOp>(op)) {
626 if (
auto copy = dyn_cast<NpuDmaMemcpyNdOp>(op)) {
627 auto allocation = AIE::ShimDMAAllocationOp::getForSymbol(
628 seq->getParentOfType<AIE::DeviceOp>(),
629 copy.getMetadata().getRootReference());
631 if (AIE::TileOp tile = allocation.getTileOp())
634 allocation.getChannelIndex()),
635 copy.getIssueToken() ||
636 allocation.getChannelDir() == AIE::DMAChannelDir::S2MM);
639 if (
auto wait = dyn_cast<NpuDmaWaitOp>(op)) {
640 auto allocation = AIE::ShimDMAAllocationOp::getForSymbol(
641 seq->getParentOfType<AIE::DeviceOp>(), wait.getSymbol());
643 if (AIE::TileOp tile = allocation.getTileOp())
646 allocation.getChannelIndex()));
649 DMAConfigureTaskOp cfg;
651 bool isConfigure =
false;
653 bool issuesToken =
false;
654 if (
auto start = dyn_cast<DMAStartTaskOp>(op)) {
655 task = start.getTask();
656 cfg = originConfigure.lookup(task);
658 issuesToken = cfg && start.getPushIssueToken(cfg);
659 }
else if (
auto configure = dyn_cast<DMAConfigureTaskOp>(op)) {
660 if (!warnUnsafeBdReuse)
663 task = configure.getResult();
665 }
else if (
auto await = dyn_cast<DMAAwaitTaskOp>(op)) {
666 cfg = originConfigure.lookup(await.getTask());
668 if (cfg && !cfg.getIssueToken())
670 }
else if (
auto freeOp = dyn_cast<DMAFreeTaskOp>(op)) {
671 if (!warnUnsafeBdReuse)
673 cfg = originConfigure.lookup(freeOp.getTask());
676 AIE::TileOp tile = cfg.tryGetTileOp();
687 AIE::TileOp tile = cfg.tryGetTileOp();
695 warnUnsafeBdReuse ? task : Value{})
701 warnUnsafeBdReuse, effectOf);
704 void runOnOperation()
override {
705 AIE::DeviceOp device = getOperation();
707 WalkResult wr = device.walk([&](AIE::RuntimeSequenceOp seq) -> WalkResult {
708 bool hasRuntimeCF =
false;
709 seq.walk([&](Operation *op) {
710 if (isa<scf::ForOp, scf::IfOp, scf::WhileOp>(op))
714 return WalkResult::advance();
720 bool alreadyLowered =
false;
721 seq.walk([&](DMABdPoolPopOp) { alreadyLowered =
true; });
723 return WalkResult::advance();
728 WalkResult whileCheck = seq.walk([&](scf::WhileOp w) {
729 w.emitOpError(
"scf.while in a runtime sequence is not supported by the "
730 "dynamic BD pool path; only scf.for and scf.if are "
732 return WalkResult::interrupt();
734 if (whileCheck.wasInterrupted())
735 return WalkResult::interrupt();
739 originConfigure.clear();
744 WalkResult r = seq.walk([&](DMAConfigureTaskOp cfg) -> WalkResult {
745 return failed(lowerConfigure(cfg)) ? WalkResult::interrupt()
746 : WalkResult::advance();
748 if (r.wasInterrupted())
749 return WalkResult::interrupt();
761 computeTaskValues(seq);
762 if (failed(verifyChainLengths(seq)))
763 return WalkResult::interrupt();
764 SmallVector<Operation *> cf;
765 seq.walk<WalkOrder::PreOrder>([&](Operation *o) {
766 if (isa<scf::ForOp, scf::IfOp>(o))
769 for (Operation *o : cf) {
770 if (
auto f = dyn_cast<scf::ForOp>(o)) {
771 carryIdsThroughLoop(f);
772 }
else if (failed(carryIdsThroughIf(cast<scf::IfOp>(o)))) {
773 return WalkResult::interrupt();
780 if (failed(computeMetadata(seq)))
781 return WalkResult::interrupt();
800 SmallVector<Operation *> toErase;
801 r = seq.walk([&](Operation *op) -> WalkResult {
802 if (
auto freeOp = dyn_cast<DMAFreeTaskOp>(op)) {
803 if (failed(lowerRelease(freeOp.getTask(), freeOp)))
804 return WalkResult::interrupt();
805 toErase.push_back(freeOp);
807 return WalkResult::advance();
809 if (r.wasInterrupted())
810 return WalkResult::interrupt();
815 guardQueueDepth(seq);
816 for (Operation *op : toErase)
818 return WalkResult::advance();
820 if (wr.wasInterrupted())
821 return signalPassFailure();
827std::unique_ptr<OperationPass<AIE::DeviceOp>>
829 return std::make_unique<AIELowerDynamicBDPoolPass>();
832std::unique_ptr<OperationPass<AIE::DeviceOp>>
834 const AIELowerDynamicBDPoolOptions &options) {
835 return std::make_unique<AIELowerDynamicBDPoolPass>(options);
std::array< int, 4 > ChannelKey
{col, row, direction, channel}.
static ChannelKey keyOf(AIE::TileOp tile, AIE::DMAChannelDir direction, int64_t channel)
std::pair< uint32_t, uint32_t > getBdIdRangeForChannel(int col, int row, int channel) const
Return the half-open range [first, last) of buffer descriptor ids that channel channel of the tile at...
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIELowerDynamicBDPoolPass()
std::optional< DmaQueueModel::ChannelKey > syncChannelKey(NpuSyncOp sync)
The channel an aiex.npu.sync retires on, or nullopt when it cannot be pinned down.
void guardSequenceQueueDepth(mlir::Region &body, DmaQueueModel &queue, const AIE::AIETargetModel &tm, bool enforce, bool warnUnsafeBdReuse, EffectFn effectOf)
Guard every push in a runtime sequence that can land on a full queue, and (when warnUnsafeBdReuse) wa...
const AIETargetModel & getTargetModel(mlir::Operation *op)
static QueueEffect await(DmaQueueModel::ChannelKey key)
static QueueEffect configure(DmaQueueModel::ChannelKey key, mlir::Value task)
static QueueEffect free(DmaQueueModel::ChannelKey key, mlir::Value task)
static QueueEffect push(DmaQueueModel::ChannelKey key, bool issuesToken, mlir::Value task={})