450 bool warnUnsafeBdReuse, EffectFn effectOf) {
453 static_cast<AIE::DMAChannelDir
>(k[2]));
459 bool operator==(
const PathState &other)
const {
460 return queue == other.queue && completion == other.completion;
463 using States = llvm::SmallVector<PathState, 8>;
464 llvm::DenseMap<mlir::Value, unsigned> taskIndices;
465 auto track = [&](mlir::Value value) {
466 if (value && value.getType().isIndex())
467 taskIndices.try_emplace(value, taskIndices.size());
469 std::set<DmaQueueModel::ChannelKey> keys;
470 for (
const auto &[key, channel] : queue.
state())
471 if (depthOf(key) != 0)
473 llvm::DenseMap<mlir::Operation *, QueueEffect> effects;
474 body.walk([&](mlir::Operation *op) {
475 if (warnUnsafeBdReuse) {
476 for (mlir::Value value : op->getOperands())
478 for (mlir::Value value : op->getResults())
480 for (mlir::Region ®ion : op->getRegions())
481 for (mlir::Block &block : region)
482 for (mlir::Value arg : block.getArguments())
486 if (!warnUnsafeBdReuse) {
500 std::map<uint32_t, DmaQueueModel::ChannelKey> keyByStatus;
501 for (
const auto &key : keys)
503 key[0], key[1], key[3],
static_cast<AIE::DMAChannelDir
>(key[2])))
504 keyByStatus[*status] = key;
506 llvm::DenseMap<mlir::Block *, llvm::SmallVector<mlir::Operation *>>;
507 std::map<DmaQueueModel::ChannelKey, Visits> visits;
509 Visits &blocks = visits[key];
510 for (mlir::Operation *at = op; at != body.getParentOp();
511 at = at->getParentOp()) {
512 auto &ops = blocks[at->getBlock()];
513 if (!ops.empty() && ops.back() == at)
518 body.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
519 if (warnUnsafeBdReuse && mlir::isa<mlir::scf::ForOp, mlir::scf::IfOp>(op))
520 for (
const auto &key : keys)
522 if (
auto it = effects.find(op); it != effects.end()) {
523 visit(op, it->second.key);
524 }
else if (
auto poll = mlir::dyn_cast<NpuMaskPollOp>(op)) {
525 if (
auto address = poll.getAbsoluteAddress())
526 if (
auto it = keyByStatus.find(*address); it != keyByStatus.end())
527 visit(op, it->second);
533 for (
const auto &key : keys) {
534 uint32_t depth = depthOf(key);
535 const Visits &channelVisits = visits[key];
536 auto channelEffectOf = [&](mlir::Operation *op) {
537 auto it = effects.find(op);
538 return it != effects.end() && it->second.key == key ? it->second
541 llvm::SetVector<mlir::Operation *> overflowing;
542 llvm::SetVector<mlir::Operation *> unsafeFrees;
543 std::map<mlir::Operation *, DmaQueueModel::ChannelState> witnesses;
544 bool trackCompletion = warnUnsafeBdReuse;
545 auto warnUnsafeFree = [&](mlir::Operation *op) {
546 if (warnUnsafeBdReuse && unsafeFrees.insert(op)) {
548 op, key[0], key[1],
static_cast<AIE::DMAChannelDir
>(key[2]),
550 if (!trackCompletion)
551 diag.attachNote() <<
"task ownership analysis budget exhausted; "
552 "completion is conservatively unproven";
555 size_t completionWork = 0;
556 auto equivalent = [&](
const PathState &a,
const PathState &b) {
557 return a.queue == b.queue &&
558 (!trackCompletion || a.completion == b.completion);
560 auto contains = [&](
const States &states,
const PathState &state) {
561 return llvm::any_of(states, [&](
const PathState &other) {
562 return equivalent(state, other);
565 auto compact = [&](States &states) {
567 for (
const auto &state : states)
568 if (!contains(unique, state))
569 unique.push_back(state);
570 states = std::move(unique);
574 auto append = [&](States &to,
const States &from) {
575 if (warnUnsafeBdReuse && !trackCompletion)
577 for (
const auto &state : from)
578 if (!contains(to, state)) {
579 if (trackCompletion && to.size() >= 256) {
580 trackCompletion =
false;
582 if (contains(to, state))
588 auto dedupe = [&](States &states) {
590 for (
auto &state : states)
591 state.completion.normalize();
592 if (states.size() < 2)
595 append(unique, states);
596 states = std::move(unique);
598 auto transfer = [&](States &states, mlir::ValueRange from,
599 mlir::ValueRange to) {
600 if (!trackCompletion)
602 for (
auto &state : states) {
603 auto previous = state.completion.tasks;
604 for (
auto [source, dest] : llvm::zip_equal(from, to)) {
605 auto src = taskIndices.find(source), dst = taskIndices.find(dest);
606 if (src != taskIndices.end() && dst != taskIndices.end())
607 state.completion.tasks[dst->second] = previous[src->second];
612 auto forgetScope = [&](States &states, mlir::Region ®ion,
613 bool keepArguments =
false) {
614 if (!trackCompletion)
616 for (
auto [value, index] : taskIndices) {
617 mlir::Region *parent = value.getParentRegion();
618 if (parent != ®ion && !region.isAncestor(parent))
621 if (
auto arg = mlir::dyn_cast<mlir::BlockArgument>(value))
622 if (arg.getOwner() == ®ion.front())
624 for (
auto &state : states)
625 state.completion.tasks[index] = 0;
632 static_cast<AIE::DMAChannelDir
>(key[2]), depth)
637 enum class PollEffect { Ignore, Space, Idle };
638 auto pollEffectOf = [&](NpuMaskPollOp poll) {
639 auto address = poll.getAbsoluteAddress();
642 if (!address || !mask || !value)
643 return PollEffect::Ignore;
645 key[0], key[1], key[3],
static_cast<AIE::DMAChannelDir
>(key[2]));
646 if (status != address)
647 return PollEffect::Ignore;
649 if (idleMask && (*mask & idleMask) == idleMask &&
650 (*value & idleMask) == 0)
651 return PollEffect::Idle;
653 if (!fieldMask || (depth & (depth - 1)) != 0)
654 return PollEffect::Ignore;
655 uint32_t depthBit = depth << llvm::countr_zero(fieldMask);
656 return depthBit && (depthBit & fieldMask) == depthBit &&
657 (*mask & depthBit) == depthBit && (*value & depthBit) == 0
659 : PollEffect::Ignore;
664 auto unknownRegion = [&](mlir::Region ®ion, States states) {
665 region.walk([&](mlir::Operation *op) {
671 for (
auto &state : states) {
672 state.queue.pushes.assign(depth,
false);
673 state.queue.unknownTokens =
true;
674 if (trackCompletion) {
675 state.completion.unknownTokens =
true;
676 state.completion.mayHaveDroppedPushes |= !canPoll;
677 for (
unsigned owner : state.completion.tasks)
679 state.completion.forgottenOwners.insert(owner);
681 state.completion.pushes.push_back(
683 state.completion.bound(depth + 1,
false);
685 if (overflowing.insert(op))
686 witnesses[op] = state.queue;
693 std::function<States(mlir::Region &, States)> analyze;
694 analyze = [&](mlir::Region ®ion, States states) -> States {
695 if (!region.hasOneBlock())
696 return unknownRegion(region, std::move(states));
697 auto blockVisits = channelVisits.find(®ion.front());
698 if (blockVisits == channelVisits.end())
700 for (mlir::Operation *visited : blockVisits->second) {
701 if (trackCompletion) {
702 completionWork += states.size();
703 if (completionWork > 16384) {
704 trackCompletion =
false;
708 mlir::Operation &op = *visited;
709 if (
auto loop = mlir::dyn_cast<mlir::scf::ForOp>(&op)) {
710 auto lb = mlir::getConstantIntValue(loop.getLowerBound());
711 auto ub = mlir::getConstantIntValue(loop.getUpperBound());
712 if (lb && ub && *lb >= *ub &&
713 (!loop->hasAttr(
"unsignedCmp") || (*lb >= 0 && *ub >= 0))) {
714 transfer(states, loop.getInitArgs(), loop.getResults());
720 transfer(states, loop.getInitArgs(), loop.getRegionIterArgs());
721 States reachable = states;
722 for (
size_t i = 0; i < reachable.size();) {
723 bool wasTrackingCompletion = trackCompletion;
724 States next = analyze(loop.getRegion(), States{reachable[i]});
725 transfer(next, loop.getYieldedValues(), loop.getRegionIterArgs());
726 forgetScope(next, loop.getRegion(),
true);
727 append(reachable, next);
728 if (wasTrackingCompletion && !trackCompletion) {
736 transfer(reachable, loop.getRegionIterArgs(), loop.getResults());
737 forgetScope(reachable, loop.getRegion());
738 states = std::move(reachable);
741 if (
auto branch = mlir::dyn_cast<mlir::scf::IfOp>(&op)) {
742 States alternatives = analyze(branch.getThenRegion(), states);
743 States otherwise = analyze(branch.getElseRegion(), states);
744 if (branch.getNumResults()) {
745 transfer(alternatives, branch.thenYield().getResults(),
746 branch.getResults());
747 transfer(otherwise, branch.elseYield().getResults(),
748 branch.getResults());
750 forgetScope(alternatives, branch.getThenRegion());
751 forgetScope(otherwise, branch.getElseRegion());
752 append(alternatives, otherwise);
753 states = std::move(alternatives);
756 for (mlir::Region &nested : op.getRegions())
757 states = unknownRegion(nested, std::move(states));
759 if (
auto poll = mlir::dyn_cast<NpuMaskPollOp>(&op))
760 if (
auto effect = pollEffectOf(poll); effect != PollEffect::Ignore) {
761 for (
auto &state : states) {
763 if (trackCompletion) {
764 if (effect == PollEffect::Idle)
765 state.completion.noteIdle();
767 state.completion.bound(depth,
true);
777 if (warnUnsafeBdReuse)
778 for (
const auto &state : states)
779 if (!trackCompletion ||
780 !state.completion.isProvenComplete(
781 state.completion.tasks[taskIndices.lookup(e.
task)]))
785 for (
auto &state : states) {
788 state.completion.tasks[taskIndices.lookup(e.
task)] =
789 taskIndices.size() + 1;
795 state.completion.awaitToken();
799 if (overflowing.insert(&op))
800 witnesses[&op] = state.queue;
802 if (trackCompletion && canPoll)
803 state.completion.bound(depth,
true);
804 else if (trackCompletion)
805 state.completion.mayHaveDroppedPushes =
true;
808 if (trackCompletion) {
810 e.
task ? state.completion.tasks[taskIndices.lookup(e.
task)] : 0;
811 state.completion.pushes.push_back({e.
issuesToken, owner});
812 state.completion.bound(depth + 1,
false);
821 if (
auto it = entry.find(key); it != entry.end())
822 channelEntry = it->second;
823 PathState initial{channelEntry, {}};
824 initial.completion.tasks.resize(taskIndices.size());
825 States exits = analyze(body, States{initial});
826 for (mlir::Operation *push : overflowing) {
827 queue.
setState({{key, witnesses[push]}});
835 for (
const auto &state : exits)
836 if (!(channelExit == state.queue)) {
837 channelExit.
pushes.assign(
838 std::max(channelExit.
pushes.size(), state.queue.pushes.size()),
842 merged[key] = channelExit;