453 bool warnUnsafeBdReuse, EffectFn effectOf) {
456 static_cast<AIE::DMAChannelDir
>(k[2]));
462 bool operator==(
const PathState &other)
const {
463 return queue == other.queue && completion == other.completion;
466 using States = llvm::SmallVector<PathState, 8>;
467 llvm::DenseMap<mlir::Value, unsigned> taskIndices;
468 auto track = [&](mlir::Value value) {
469 if (value && value.getType().isIndex())
470 taskIndices.try_emplace(value, taskIndices.size());
472 std::set<DmaQueueModel::ChannelKey> keys;
473 for (
const auto &[key, channel] : queue.
state())
474 if (depthOf(key) != 0)
476 llvm::DenseMap<mlir::Operation *, QueueEffect> effects;
477 body.walk([&](mlir::Operation *op) {
478 if (warnUnsafeBdReuse) {
479 for (mlir::Value value : op->getOperands())
481 for (mlir::Value value : op->getResults())
483 for (mlir::Region ®ion : op->getRegions())
484 for (mlir::Block &block : region)
485 for (mlir::Value arg : block.getArguments())
489 if (!warnUnsafeBdReuse) {
503 std::map<uint32_t, DmaQueueModel::ChannelKey> keyByStatus;
504 for (
const auto &key : keys)
506 key[0], key[1], key[3],
static_cast<AIE::DMAChannelDir
>(key[2])))
507 keyByStatus[*status] = key;
509 llvm::DenseMap<mlir::Block *, llvm::SmallVector<mlir::Operation *>>;
510 std::map<DmaQueueModel::ChannelKey, Visits> visits;
512 Visits &blocks = visits[key];
513 for (mlir::Operation *at = op; at != body.getParentOp();
514 at = at->getParentOp()) {
515 auto &ops = blocks[at->getBlock()];
516 if (!ops.empty() && ops.back() == at)
521 body.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
522 if (warnUnsafeBdReuse && mlir::isa<mlir::scf::ForOp, mlir::scf::IfOp>(op))
523 for (
const auto &key : keys)
525 if (
auto it = effects.find(op); it != effects.end()) {
526 visit(op, it->second.key);
527 }
else if (
auto poll = mlir::dyn_cast<NpuMaskPollOp>(op)) {
528 if (
auto address = poll.getAbsoluteAddress())
529 if (
auto it = keyByStatus.find(*address); it != keyByStatus.end())
530 visit(op, it->second);
536 for (
const auto &key : keys) {
537 uint32_t depth = depthOf(key);
538 const Visits &channelVisits = visits[key];
539 auto channelEffectOf = [&](mlir::Operation *op) {
540 auto it = effects.find(op);
541 return it != effects.end() && it->second.key == key ? it->second
544 llvm::SetVector<mlir::Operation *> overflowing;
545 llvm::SetVector<mlir::Operation *> unsafeFrees;
546 std::map<mlir::Operation *, DmaQueueModel::ChannelState> witnesses;
547 bool trackCompletion = warnUnsafeBdReuse;
548 auto warnUnsafeFree = [&](mlir::Operation *op) {
549 if (warnUnsafeBdReuse && unsafeFrees.insert(op)) {
551 op, key[0], key[1],
static_cast<AIE::DMAChannelDir
>(key[2]),
553 if (!trackCompletion)
554 diag.attachNote() <<
"task ownership analysis budget exhausted; "
555 "completion is conservatively unproven";
558 size_t completionWork = 0;
559 auto equivalent = [&](
const PathState &a,
const PathState &b) {
560 return a.queue == b.queue &&
561 (!trackCompletion || a.completion == b.completion);
563 auto contains = [&](
const States &states,
const PathState &state) {
564 return llvm::any_of(states, [&](
const PathState &other) {
565 return equivalent(state, other);
568 auto compact = [&](States &states) {
570 for (
const auto &state : states)
571 if (!contains(unique, state))
572 unique.push_back(state);
573 states = std::move(unique);
577 auto append = [&](States &to,
const States &from) {
578 if (warnUnsafeBdReuse && !trackCompletion)
580 for (
const auto &state : from)
581 if (!contains(to, state)) {
582 if (trackCompletion && to.size() >= 256) {
583 trackCompletion =
false;
585 if (contains(to, state))
591 auto dedupe = [&](States &states) {
593 for (
auto &state : states)
594 state.completion.normalize();
595 if (states.size() < 2)
598 append(unique, states);
599 states = std::move(unique);
601 auto transfer = [&](States &states, mlir::ValueRange from,
602 mlir::ValueRange to) {
603 if (!trackCompletion)
605 for (
auto &state : states) {
606 auto previous = state.completion.tasks;
607 for (
auto [source, dest] : llvm::zip_equal(from, to)) {
608 auto src = taskIndices.find(source), dst = taskIndices.find(dest);
609 if (src != taskIndices.end() && dst != taskIndices.end())
610 state.completion.tasks[dst->second] = previous[src->second];
615 auto forgetScope = [&](States &states, mlir::Region ®ion,
616 bool keepArguments =
false) {
617 if (!trackCompletion)
619 for (
auto [value, index] : taskIndices) {
620 mlir::Region *parent = value.getParentRegion();
621 if (parent != ®ion && !region.isAncestor(parent))
624 if (
auto arg = mlir::dyn_cast<mlir::BlockArgument>(value))
625 if (arg.getOwner() == ®ion.front())
627 for (
auto &state : states)
628 state.completion.tasks[index] = 0;
635 static_cast<AIE::DMAChannelDir
>(key[2]), depth)
640 enum class PollEffect { Ignore, Space, Idle };
641 auto pollEffectOf = [&](NpuMaskPollOp poll) {
642 auto address = poll.getAbsoluteAddress();
645 if (!address || !mask || !value)
646 return PollEffect::Ignore;
648 key[0], key[1], key[3],
static_cast<AIE::DMAChannelDir
>(key[2]));
649 if (status != address)
650 return PollEffect::Ignore;
652 if (idleMask && (*mask & idleMask) == idleMask &&
653 (*value & idleMask) == 0)
654 return PollEffect::Idle;
656 if (!fieldMask || (depth & (depth - 1)) != 0)
657 return PollEffect::Ignore;
658 uint32_t depthBit = depth << llvm::countr_zero(fieldMask);
659 return depthBit && (depthBit & fieldMask) == depthBit &&
660 (*mask & depthBit) == depthBit && (*value & depthBit) == 0
662 : PollEffect::Ignore;
667 auto unknownRegion = [&](mlir::Region ®ion, States states) {
668 region.walk([&](mlir::Operation *op) {
674 for (
auto &state : states) {
675 state.queue.pushes.assign(depth,
false);
676 state.queue.unknownTokens =
true;
677 if (trackCompletion) {
678 state.completion.unknownTokens =
true;
679 state.completion.mayHaveDroppedPushes |= !canPoll;
680 for (
unsigned owner : state.completion.tasks)
682 state.completion.forgottenOwners.insert(owner);
684 state.completion.pushes.push_back(
686 state.completion.bound(depth + 1,
false);
688 if (overflowing.insert(op))
689 witnesses[op] = state.queue;
696 std::function<States(mlir::Region &, States)> analyze;
697 analyze = [&](mlir::Region ®ion, States states) -> States {
698 if (!region.hasOneBlock())
699 return unknownRegion(region, std::move(states));
700 auto blockVisits = channelVisits.find(®ion.front());
701 if (blockVisits == channelVisits.end())
703 for (mlir::Operation *visited : blockVisits->second) {
704 if (trackCompletion) {
705 completionWork += states.size();
706 if (completionWork > 16384) {
707 trackCompletion =
false;
711 mlir::Operation &op = *visited;
712 if (
auto loop = mlir::dyn_cast<mlir::scf::ForOp>(&op)) {
713 auto lb = mlir::getConstantIntValue(loop.getLowerBound());
714 auto ub = mlir::getConstantIntValue(loop.getUpperBound());
715 if (lb && ub && *lb >= *ub &&
716 (!loop->hasAttr(
"unsignedCmp") || (*lb >= 0 && *ub >= 0))) {
717 transfer(states, loop.getInitArgs(), loop.getResults());
723 transfer(states, loop.getInitArgs(), loop.getRegionIterArgs());
724 States reachable = states;
725 for (
size_t i = 0; i < reachable.size();) {
726 bool wasTrackingCompletion = trackCompletion;
727 States next = analyze(loop.getRegion(), States{reachable[i]});
728 transfer(next, loop.getYieldedValues(), loop.getRegionIterArgs());
729 forgetScope(next, loop.getRegion(),
true);
730 append(reachable, next);
731 if (wasTrackingCompletion && !trackCompletion) {
739 transfer(reachable, loop.getRegionIterArgs(), loop.getResults());
740 forgetScope(reachable, loop.getRegion());
741 states = std::move(reachable);
744 if (
auto branch = mlir::dyn_cast<mlir::scf::IfOp>(&op)) {
745 States alternatives = analyze(branch.getThenRegion(), states);
746 States otherwise = analyze(branch.getElseRegion(), states);
747 if (branch.getNumResults()) {
748 transfer(alternatives, branch.thenYield().getResults(),
749 branch.getResults());
750 transfer(otherwise, branch.elseYield().getResults(),
751 branch.getResults());
753 forgetScope(alternatives, branch.getThenRegion());
754 forgetScope(otherwise, branch.getElseRegion());
755 append(alternatives, otherwise);
756 states = std::move(alternatives);
759 for (mlir::Region &nested : op.getRegions())
760 states = unknownRegion(nested, std::move(states));
762 if (
auto poll = mlir::dyn_cast<NpuMaskPollOp>(&op))
763 if (
auto effect = pollEffectOf(poll); effect != PollEffect::Ignore) {
764 for (
auto &state : states) {
766 if (trackCompletion) {
767 if (effect == PollEffect::Idle)
768 state.completion.noteIdle();
770 state.completion.bound(depth,
true);
780 if (warnUnsafeBdReuse)
781 for (
const auto &state : states)
782 if (!trackCompletion ||
783 !state.completion.isProvenComplete(
784 state.completion.tasks[taskIndices.lookup(e.
task)]))
788 for (
auto &state : states) {
791 state.completion.tasks[taskIndices.lookup(e.
task)] =
792 taskIndices.size() + 1;
798 state.completion.awaitToken();
802 if (overflowing.insert(&op))
803 witnesses[&op] = state.queue;
805 if (trackCompletion && canPoll)
806 state.completion.bound(depth,
true);
807 else if (trackCompletion)
808 state.completion.mayHaveDroppedPushes =
true;
811 if (trackCompletion) {
813 e.
task ? state.completion.tasks[taskIndices.lookup(e.
task)] : 0;
814 state.completion.pushes.push_back({e.
issuesToken, owner});
815 state.completion.bound(depth + 1,
false);
824 if (
auto it = entry.find(key); it != entry.end())
825 channelEntry = it->second;
826 PathState initial{channelEntry, {}};
827 initial.completion.tasks.resize(taskIndices.size());
828 States exits = analyze(body, States{initial});
829 for (mlir::Operation *push : overflowing) {
830 queue.
setState({{key, witnesses[push]}});
838 for (
const auto &state : exits)
839 if (!(channelExit == state.queue)) {
840 channelExit.
pushes.assign(
841 std::max(channelExit.
pushes.size(), state.queue.pushes.size()),
845 merged[key] = channelExit;