13#include "mlir/Interfaces/ViewLikeInterface.h"
14#include "llvm/ADT/DenseSet.h"
15#include "llvm/ADT/MapVector.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/TypeSwitch.h"
18#include "llvm/Support/Debug.h"
26#define DEBUG_TYPE "aie-placer"
30static bool outOfOrderNeedsDMA(ObjectFifoCreateOp fifo) {
31 if (!fifo.getOutOfOrder())
33 if (fifo.forcesDMA() || fifo.getRepeatCount() || fifo.usesStream() ||
34 fifo.getConsumerElemType() || !fifo.getDimensionsToStream().empty())
36 for (
auto dims : fifo.getDimensionsFromStreamPerConsumer())
39 auto device = fifo->getParentOfType<DeviceOp>();
40 for (
auto alloc : device.getOps<ObjectFifoAllocateOp>())
41 if (alloc.getObjectFifo() == fifo)
43 for (
auto link : device.getOps<ObjectFifoLinkOp>()) {
44 if (llvm::is_contained(link.getInputObjectFifos(), fifo) ||
45 llvm::is_contained(link.getOutputObjectFifos(), fifo))
51static std::optional<TileID> resolvePeerPosition(TileLike peer,
55 if (!first || !second)
57 unsigned idx =
edges.size();
58 edges.push_back({first, second});
59 if (mlir::isa<LogicalTileOp>(first.getOperation()))
61 if (mlir::isa<LogicalTileOp>(second.getOperation()))
68 auto aT = dyn_cast_or_null<TileLike>(a.getDefiningOp());
69 auto bT = dyn_cast_or_null<TileLike>(b.getDefiningOp());
78 for (
int col = 0; col < tm.
columns(); col++) {
79 for (
int row = 0; row < tm.
rows(); row++) {
82 case AIETileType::CoreTile:
85 case AIETileType::MemTile:
86 case AIETileType::ShimNOCTile:
98 device.walk([&](Operation *op) {
99 llvm::TypeSwitch<Operation *>(op)
100 .Case<LogicalTileOp>([&](
auto lt) { ops.
logicalTiles.push_back(lt); })
101 .Case<ObjectFifoCreateOp>(
103 .Case<ObjectFifoLinkOp>(
105 .Case<ObjectFifoAllocateOp>(
107 .Case<CascadeFlowOp>([&](
auto cf) { ops.
cascadeFlows.push_back(cf); })
108 .Case<FlowOp>([&](
auto f) { ops.
flows.push_back(f); })
109 .Case<PacketFlowOp>([&](
auto pf) { ops.
pktFlows.push_back(pf); })
110 .Case<RouteOp>([&](
auto route) { ops.
routes.push_back(route); });
116 llvm::stable_sort(ops.
logicalTiles, [&](LogicalTileOp a, LogicalTileOp b) {
117 if (a.getTileType() != b.getTileType())
118 return a.getTileType() > b.getTileType();
119 return rank.lookup(a) < rank.lookup(b);
125 if (
auto lt = dyn_cast<LogicalTileOp>(tile)) {
126 if (
auto c = lt.tryGetCol(); c && *c != pos.col)
128 if (
auto r = lt.tryGetRow(); r && *r != pos.row)
141 return a.col < b.col;
142 return a.row < b.row;
146 return a.row < b.row;
147 return a.col < b.col;
157 if (coresPerCol.has_value()) {
158 llvm::DenseMap<int, int> coresInColumn;
160 coresInColumn[tile.col]++;
161 int maxDeviceCoresPerCol = 0;
162 for (
const auto &[col, count] : coresInColumn)
163 maxDeviceCoresPerCol = std::max(maxDeviceCoresPerCol, count);
164 deviceCoresPerCol = maxDeviceCoresPerCol;
165 limitCoresPerColumn(*coresPerCol,
targetModel.columns());
169void SequentialPlacer::limitCoresPerColumn(
int maxCoresPerCol,
int numColumns) {
170 llvm::DenseMap<int, std::vector<TileID>> tilesByColumn;
172 tilesByColumn[tile.col].push_back(tile);
176 std::vector<TileID> limitedTiles;
177 for (
int col = 0; col < numColumns; col++) {
178 auto it = tilesByColumn.find(col);
179 if (it == tilesByColumn.end())
181 const auto &tilesInCol = it->second;
183 std::min(tilesInCol.size(),
static_cast<size_t>(maxCoresPerCol));
184 limitedTiles.insert(limitedTiles.end(), tilesInCol.begin(),
185 tilesInCol.begin() + numToTake);
197 return device.emitError() <<
"SequentialPlacer::place called before "
198 "initialize(); targetModel is null";
199 if (coresPerCol.has_value() && *coresPerCol > deviceCoresPerCol) {
200 return device.emitError() <<
"requested cores-per-col (" << *coresPerCol
201 <<
") exceeds device capacity ("
202 << deviceCoresPerCol <<
" cores per column)";
207 auto &logicalTiles = collected.logicalTiles;
208 auto &objectFifos = collected.objectFifos;
209 auto &objectFifoLinks = collected.objectFifoLinks;
210 auto &cascadeFlows = collected.cascadeFlows;
211 auto &flows = collected.flows;
212 auto &pktFlows = collected.pktFlows;
213 auto &routes = collected.routes;
216 auto bufferAdjacency =
217 buildBufferAdjacency(logicalTiles, collected.objectFifoAllocates);
223 auto channelRequirements =
224 buildChannelRequirements(objectFifos, objectFifoLinks);
225 addChannelRequirementsFromFlows(flows, pktFlows, routes, channelRequirements);
226 countTurns(device, objectFifos, channelRequirements);
227 buildStreamDemand(objectFifos);
228 buildRuntimeBdProfiles(device, objectFifos);
231 device, [&](StringAttr symbol) {
return shimEndOf(device, symbol); });
239 llvm::DenseMap<Operation *, int> forcedSharedMemIn, forcedSharedMemOut;
240 for (
auto ofOp : objectFifos) {
241 if (!ofOp.forcesSharedMemory() || ofOp.getConsumerTiles().size() != 1)
244 dyn_cast_or_null<LogicalTileOp>(ofOp.getProducerTile().getDefiningOp());
245 auto consumer = dyn_cast_or_null<LogicalTileOp>(
246 ofOp.getConsumerTiles()[0].getDefiningOp());
247 if (!producer || producer.getTileType() != AIETileType::CoreTile)
249 if (!consumer || consumer.getTileType() != AIETileType::CoreTile)
251 if (producer.getOperation() == consumer.getOperation())
253 ++forcedSharedMemOut[producer.getOperation()];
254 ++forcedSharedMemIn[consumer.getOperation()];
268 llvm::DenseMap<Operation *, int> needNeighborIn, needNeighborOut;
269 for (
auto lt : logicalTiles) {
270 if (lt.getTileType() != AIETileType::CoreTile)
272 auto [peerIn, peerOut] =
273 totalComputePeers(lt.getOperation(), computePeerAdjacency);
274 auto [inBudget, outBudget] =
276 auto [nonPeerIn, nonPeerOut] =
277 channelRequirements.lookup(lt.getOperation());
278 needNeighborIn[lt.getOperation()] =
279 std::max({0, peerIn - (inBudget - nonPeerIn),
280 forcedSharedMemIn.lookup(lt.getOperation())});
281 needNeighborOut[lt.getOperation()] =
282 std::max({0, peerOut - (outBudget - nonPeerOut),
283 forcedSharedMemOut.lookup(lt.getOperation())});
284 LLVM_DEBUG(llvm::dbgs()
285 <<
DEBUG_TYPE <<
": LTO " << lt.getLoc() <<
" peerIn=" << peerIn
286 <<
" peerOut=" << peerOut <<
" nonPeerIn=" << nonPeerIn
287 <<
" nonPeerOut=" << nonPeerOut
288 <<
" => needNeighborIn=" << needNeighborIn[lt.getOperation()]
289 <<
" needNeighborOut=" << needNeighborOut[lt.getOperation()]
292 PlacementContext ctx{*
targetModel, computePeerAdjacency, needNeighborIn,
298 FlowMembership flowIndex =
299 buildFlowMembership(flows, pktFlows, routes, objectFifos);
300 CoreGroups coreGroups = buildCoreGroups(logicalTiles, flowIndex);
305 auto bufferPred = [
this](
TileID consumerPos,
TileID ownerPos) {
306 return targetModel->isLegalMemAffinity(consumerPos.col, consumerPos.row,
307 ownerPos.col, ownerPos.row);
309 auto bufferLabel = [](
bool thisIsConsumer) -> StringRef {
310 return thisIsConsumer ?
"shared-L1 buffer owner"
311 :
"shared-L1 buffer consumer";
316 auto cascadePred = [
this](
TileID srcPos,
TileID dstPos) {
317 return targetModel->isSouth(srcPos.col, srcPos.row, dstPos.col,
319 targetModel->isEast(srcPos.col, srcPos.row, dstPos.col, dstPos.row);
321 auto cascadeLabel = [](
bool thisIsSrc) -> StringRef {
322 return thisIsSrc ?
"cascade destination" :
"cascade source";
330 return targetModel->isLegalMemAffinity(aPos.col, aPos.row, bPos.col,
332 targetModel->isLegalMemAffinity(bPos.col, bPos.row, aPos.col,
335 auto sharedMemLabel = [](
bool thisIsProducer) -> StringRef {
336 return thisIsProducer ?
"shared-memory consumer" :
"shared-memory producer";
339 AdjacencyKind bufferKind{
340 bufferAdjacency, bufferPred, bufferLabel,
"shared-L1 buffer",
341 "shared-L1 buffer adjacency requires this LTO to be on a tile "
342 "whose L1 is shared with the buffer owner's tile (N/S neighbor, "
343 "or W neighbor per the device's checkerboard rule)"};
344 AdjacencyKind cascadeKind{
345 cascadeAdjacency, cascadePred, cascadeLabel,
"cascade",
346 "cascade adjacency requires the destination tile to be one row "
347 "South or one column East of the source tile"};
348 AdjacencyKind sharedMemKind{
349 sharedMemAdjacency, sharedMemPred, sharedMemLabel,
350 "shared-memory transport",
351 "a `shared_mem` objectFifo requires its producer and consumer to "
352 "land on tiles that share a memory module (N/S neighbor, or W "
353 "neighbor per the device's checkerboard rule)"};
373 SmallVector<LogicalTileOp> orderedTiles;
374 llvm::DenseSet<unsigned> emittedGroups;
375 for (
auto lt : logicalTiles) {
376 auto group = coreGroups.groupOf.find(lt.getOperation());
377 if (group == coreGroups.groupOf.end())
378 orderedTiles.push_back(lt);
379 else if (emittedGroups.insert(group->second).second)
380 llvm::append_range(orderedTiles, coreGroups.members[group->second]);
382 llvm::stable_sort(orderedTiles, [&](LogicalTileOp a, LogicalTileOp b) {
383 auto rank = [](LogicalTileOp lt) {
384 bool hasCol = lt.tryGetCol().has_value();
385 bool hasRow = lt.tryGetRow().has_value();
386 if (hasCol && hasRow)
388 if (hasCol || hasRow)
392 int ra = rank(a), rb = rank(b);
400 int pa = ctx.placementPriority(a.getOperation());
401 int pb = ctx.placementPriority(b.getOperation());
407 return ctx.neighborDemand(a.getOperation()) >
408 ctx.neighborDemand(b.getOperation());
413 auto unpinned = [](LogicalTileOp lt) {
414 return !lt.tryGetCol() && !lt.tryGetRow();
416 SmallVector<LogicalTileOp> sequenced;
417 llvm::DenseSet<Operation *> queued;
418 for (
auto start : orderedTiles) {
419 if (!queued.insert(start.getOperation()).second)
421 size_t next = sequenced.size();
422 sequenced.push_back(start);
423 for (; next < sequenced.size(); ++next) {
424 LogicalTileOp lt = sequenced[next];
428 [&](TileLike peer,
bool ) {
429 auto peerLt = dyn_cast<LogicalTileOp>(peer.getOperation());
430 if (peerLt && unpinned(peerLt) &&
431 queued.insert(peerLt.getOperation()).second)
432 sequenced.push_back(peerLt);
436 orderedTiles = std::move(sequenced);
438 for (
auto logicalTile : orderedTiles) {
440 auto col = logicalTile.tryGetCol();
441 auto row = logicalTile.tryGetRow();
444 if (failed(enforceAdjacency(logicalTile, tile, bufferKind)))
446 if (failed(enforceAdjacency(logicalTile, tile, cascadeKind)))
448 if (failed(enforceAdjacency(logicalTile, tile, sharedMemKind)))
450 if (failed(validateAndUpdateChannelUsage(
451 logicalTile, tile, channelRequirements, PlacementOrigin::Pinned)))
454 recordPlacement(logicalTile, tile, ctx,
"pinned");
459 if (logicalTile.getTileType() == AIETileType::CoreTile) {
460 auto isReservedForOtherBound = [&](Operation *lto,
TileID candidate) {
461 return ctx.isReservedForOther(lto, candidate);
463 SmallVector<int> preferredCols =
464 preferredGroupColumns(logicalTile, coreGroups);
465 UnpinnedPlacementInputs inputs{
466 bufferAdjacency, bufferPred,
467 cascadeAdjacency, cascadePred,
468 sharedMemAdjacency, sharedMemPred,
469 computePeerAdjacency, needNeighborIn,
470 needNeighborOut, isReservedForOtherBound,
473 findUnconstrainedCoreCandidate(logicalTile, col, row, inputs);
474 std::optional<TileID> placement = search.placement;
475 bool sawConstraintMatch = search.sawConstraintMatch;
476 bool allConstraintMatchesFailedAdjacency =
477 search.allConstraintMatchesFailedAdjacency;
478 bool computePeerWasCause = search.computePeerWasCause;
481 bool adjacencyWasCause =
482 sawConstraintMatch && allConstraintMatchesFailedAdjacency &&
483 bufferAdjacency.hasEdges(logicalTile.getOperation());
484 bool hasCascade = cascadeAdjacency.hasEdges(logicalTile.getOperation());
486 sharedMemAdjacency.hasEdges(logicalTile.getOperation());
487 InFlightDiagnostic diag = logicalTile.emitError();
489 diag <<
"no compute tile available matching constraint ("
490 << (col ? std::to_string(*col) :
"?") <<
", "
491 << (row ? std::to_string(*row) :
"?") <<
")"
492 << (adjacencyWasCause ?
" and shared-L1 buffer adjacency" :
"")
493 << (hasCascade ?
" and cascade adjacency" :
"")
494 << (hasSharedMem ?
" and shared-memory transport" :
"")
495 << (computePeerWasCause ?
" and compute-peer DMA budget" :
"");
497 diag <<
"no available compute tiles for placement"
498 << (adjacencyWasCause
499 ?
" (shared-L1 buffer adjacency unsatisfiable)"
501 << (hasCascade ?
" (cascade adjacency unsatisfiable)" :
"")
502 << (hasSharedMem ?
" (shared-memory transport unsatisfiable)"
504 << (computePeerWasCause
505 ?
" (compute-peer DMA budget unsatisfiable)"
510 if (coresPerCol.has_value() && deviceCoresPerCol > 0) {
513 <<
"cores-per-col=" << *coresPerCol <<
" leaves "
514 << columns * *coresPerCol <<
" of this device's "
515 << columns * deviceCoresPerCol
516 <<
" compute tiles placeable and may be limiting placement; "
517 "consider raising it or reducing the design's core count";
519 if (adjacencyWasCause)
520 attachPeerNotes(diag, logicalTile, bufferAdjacency, bufferLabel);
522 attachPeerNotes(diag, logicalTile, cascadeAdjacency, cascadeLabel);
524 attachPeerNotes(diag, logicalTile, sharedMemAdjacency,
526 if (computePeerWasCause) {
527 attachPeerNotes(diag, logicalTile, computePeerAdjacency,
528 [](
bool thisIsProducer) -> StringRef {
529 return thisIsProducer ?
"compute-peer consumer"
530 :
"compute-peer producer";
533 <<
"to fix, pin this LTO to a position that is shared-L1 with "
534 "the placed compute-peers above, or pin one of those peers "
535 "to a different coordinate so this LTO's constraints become "
541 if (failed(validateAndUpdateChannelUsage(logicalTile, *placement,
543 PlacementOrigin::Selected)))
546 recordPlacement(logicalTile, *placement, ctx,
"unconstrained");
549 if (logicalTile.getTileType() == AIETileType::ShimPLTile) {
550 return logicalTile.emitError(
551 "DMA channel-based SequentialPlacer does not support unplaced "
552 "ShimPLTiles (no DMAs).");
558 return placeNonCoreLogicalTiles(logicalTiles, flowIndex, channelRequirements);
561SequentialPlacer::CoreGroups
562SequentialPlacer::buildCoreGroups(ArrayRef<LogicalTileOp> logicalTiles,
563 const FlowMembership &flowIndex)
const {
565 SmallVector<std::pair<LogicalTileOp, llvm::MapVector<Operation *, int>>>
567 llvm::DenseMap<Operation *, int> coresOnHub;
568 for (
auto lt : logicalTiles) {
569 if (lt.getTileType() != AIETileType::CoreTile)
571 llvm::MapVector<Operation *, int> counts;
572 auto it = flowIndex.ltoFlows.find(lt.getResult());
573 if (it != flowIndex.ltoFlows.end())
574 for (
auto &peers : it->second)
575 for (Value p : peers) {
576 auto tile = dyn_cast_or_null<TileLike>(p.getDefiningOp());
577 if (tile && !tile.isCoreTile())
578 ++counts[p.getDefiningOp()];
580 for (
auto &entry : counts)
581 ++coresOnHub[entry.first];
582 coreFlows.emplace_back(lt, std::move(counts));
588 llvm::DenseMap<Operation *, unsigned> groupOfHub;
589 for (
auto &[lt, counts] : coreFlows) {
590 Operation *hub =
nullptr;
592 for (
auto &[peer, n] : counts)
593 if (!hub || n > best ||
594 (n == best && coresOnHub[peer] < coresOnHub[hub])) {
598 unsigned idx = groups.members.size();
600 auto [it, inserted] = groupOfHub.try_emplace(hub, idx);
603 groups.members[idx].push_back(lt);
604 groups.groupOf[lt.getOperation()] = idx;
608 groups.members.push_back({lt});
609 groups.groupOf[lt.getOperation()] = idx;
615SequentialPlacer::preferredGroupColumns(LogicalTileOp logicalTile,
616 const CoreGroups &groups)
const {
617 auto group = groups.groupOf.find(logicalTile.getOperation());
618 if (group == groups.groupOf.end())
620 SmallVector<std::pair<int, int>> placedCols;
622 for (
auto member : groups.members[group->second]) {
623 auto placed =
result.find(member.getOperation());
624 if (placed ==
result.end()) {
628 auto *entry = llvm::find_if(
629 placedCols, [&](
auto &e) {
return e.first == placed->second.col; });
630 if (entry == placedCols.end())
631 placedCols.push_back({placed->second.col, 1});
635 llvm::stable_sort(placedCols, [](
auto &a,
auto &b) {
636 return a.second != b.second ? a.second > b.second : a.first < b.first;
639 SmallVector<int> cols;
640 for (
auto &[col, n] : placedCols)
643 llvm::MapVector<int, int> freeCores;
646 for (
auto &[col, n] : freeCores)
647 if (n >= unplaced && !llvm::is_contained(cols, col))
652LogicalResult SequentialPlacer::placeNonCoreLogicalTiles(
653 ArrayRef<LogicalTileOp> logicalTiles,
const FlowMembership &flowIndex,
654 const llvm::DenseMap<Operation *, std::pair<int, int>>
655 &channelRequirements) {
660 SmallVector<LogicalTileOp> nonCoreOrdered;
661 for (
auto logicalTile : logicalTiles) {
662 if (
result.count(logicalTile.getOperation()))
664 AIETileType tileType = logicalTile.getTileType();
665 if (tileType == AIETileType::CoreTile ||
666 tileType == AIETileType::ShimPLTile)
668 nonCoreOrdered.push_back(logicalTile);
670 llvm::stable_sort(nonCoreOrdered, [&](LogicalTileOp a, LogicalTileOp b) {
671 auto demand = [&](LogicalTileOp lt) {
672 auto chans = channelRequirements.lookup(lt.getOperation());
673 return chans.first + chans.second;
675 return demand(a) > demand(b);
678 for (
auto logicalTile : nonCoreOrdered) {
679 if (failed(placeNonCoreTileByCentroid(logicalTile, flowIndex,
680 channelRequirements)))
686int SequentialPlacer::PlacementContext::placementPriority(Operation *op)
const {
687 int self = neighborDemand(op);
690 [&](TileLike peer,
bool ) {
691 best = std::max(best, neighborDemand(peer.getOperation()));
693 return std::max(self, best);
696void SequentialPlacer::PlacementContext::reserveNeighborSlots(
697 Operation *placedOp,
TileID at) {
698 if (neighborDemand(placedOp) <= 0)
704bool SequentialPlacer::PlacementContext::isReservedForOther(
705 Operation *lto,
TileID candidate)
const {
706 auto it = reservedFor.find(candidate);
707 if (it == reservedFor.end())
709 for (Operation *owner : it->second) {
714 [&](TileLike peer,
bool ) {
715 if (peer.getOperation() == owner)
724SequentialPlacer::UnpinnedSearchResult
725SequentialPlacer::findUnconstrainedCoreCandidate(
726 LogicalTileOp logicalTile, std::optional<int> col, std::optional<int> row,
727 const UnpinnedPlacementInputs &inputs) {
728 UnpinnedSearchResult
result;
729 auto neighborDemand = [&](Operation *op) {
730 return inputs.needNeighborIn.lookup(op) + inputs.needNeighborOut.lookup(op);
740 auto computeNeighborCount = [&](
TileID t) {
743 targetModel->getTileType(t.col, t.row - 1) == AIETileType::CoreTile)
746 targetModel->getTileType(t.col, t.row + 1) == AIETileType::CoreTile)
750 SmallVector<TileID> orderedCandidates(
availability.compTiles.begin(),
752 if (neighborDemand(logicalTile.getOperation()) > 0) {
753 llvm::stable_sort(orderedCandidates, [&](
TileID a,
TileID b) {
755 return a.col < b.col;
756 int na = computeNeighborCount(a);
757 int nb = computeNeighborCount(b);
760 return a.row < b.row;
762 }
else if (!inputs.preferredCols.empty()) {
765 auto rankOf = [&](
TileID t) {
766 return std::distance(inputs.preferredCols.begin(),
767 llvm::find(inputs.preferredCols, t.col));
769 llvm::stable_sort(orderedCandidates, [&](
TileID a,
TileID b) {
770 return rankOf(a) < rankOf(b);
778 auto leavesPeerRoom = [&](
TileID candidate) {
779 llvm::DenseSet<TileID> claimed;
781 forEachPeer(logicalTile.getOperation(), inputs.bufferAdjacency,
782 [&](TileLike peer,
bool thisIsFirst) {
783 if (!room || resolvePeerPosition(peer, this->result))
787 llvm::find_if(availability.compTiles, [&](TileID t) {
788 return t != candidate && !claimed.contains(t) &&
789 (thisIsFirst ? inputs.bufferPred(candidate, t)
790 : inputs.bufferPred(t, candidate));
794 claimed.insert(*free);
799 auto fitsColumnStreams = [&](
TileID candidate) {
800 StreamDemand demand = streamDemand.lookup(logicalTile);
801 StreamDemand used = columnStreamsUsed.lookup(candidate.col);
802 auto incoming = columnIncomingFifos.find(candidate.col);
803 if (incoming != columnIncomingFifos.end())
804 for (Operation *fifo : incomingFifos.lookup(logicalTile))
805 demand.
southIn -= incoming->second.contains(fifo);
808 int bottomRow = candidate.row;
809 while (bottomRow > 0 &&
810 targetModel->isCoreTile(candidate.col, bottomRow - 1))
814 static_cast<int>(
targetModel->getNumSourceSwitchboxConnections(
815 candidate.col, bottomRow, WireBundle::South))) &&
818 static_cast<int>(
targetModel->getNumDestSwitchboxConnections(
819 candidate.col, bottomRow, WireBundle::South)));
825 for (
int pass = 0; pass < 3; ++pass) {
826 for (
const TileID &candidate : orderedCandidates) {
827 if (col && candidate.col != *col)
829 if (row && candidate.row != *row)
832 (inputs.isReservedForOther(logicalTile.getOperation(), candidate) ||
833 !leavesPeerRoom(candidate)))
835 if (pass == 0 && !fitsColumnStreams(candidate))
837 result.sawConstraintMatch =
true;
838 if (!satisfiesAdjacency(logicalTile, candidate, inputs.bufferAdjacency,
841 result.allConstraintMatchesFailedAdjacency =
false;
842 if (!satisfiesAdjacency(logicalTile, candidate, inputs.cascadeAdjacency,
845 if (!satisfiesAdjacency(logicalTile, candidate, inputs.sharedMemAdjacency,
846 inputs.sharedMemPred))
848 if (!satisfiesComputePeer(
849 logicalTile, candidate, inputs.computePeerAdjacency,
850 inputs.needNeighborIn, inputs.needNeighborOut)) {
851 result.computePeerWasCause =
true;
854 result.placement = candidate;
861LogicalResult SequentialPlacer::validateAndUpdateChannelUsage(
862 LogicalTileOp logicalTile,
TileID tile,
863 const llvm::DenseMap<Operation *, std::pair<int, int>> &channelRequirements,
864 PlacementOrigin origin) {
865 auto [inChannels, outChannels] =
866 channelRequirements.lookup(logicalTile.getOperation());
868 if (!hasAvailableChannels(tile, inChannels, outChannels) &&
869 !fitsSharingShim(tile, logicalTile, {inChannels, outChannels})) {
872 int availIn = maxIn -
availability.inputChannelsUsed[tile];
873 int availOut = maxOut -
availability.outputChannelsUsed[tile];
875 auto diag = logicalTile.emitError();
876 diag <<
"tile (" << tile.col <<
", " << tile.row <<
") requires "
877 << inChannels <<
" input/" << outChannels
878 <<
" output DMA channels, but only " << availIn <<
" input/"
879 << availOut <<
" output available";
880 if (origin == PlacementOrigin::Selected)
881 diag.attachNote() <<
"placer selected this tile; to fix, pin this LTO "
882 "to a tile with more spare DMA capacity, or reduce "
883 "the LTO's DMA fanin (e.g. via memtile staging)";
888 updateChannelUsage(tile, DmaDir::In, inChannels);
890 updateChannelUsage(tile, DmaDir::Out, outChannels);
891 recordStreamAndBdUsage(logicalTile, tile);
892 recordShimEnds(tile, logicalTile);
897llvm::DenseMap<Operation *, std::pair<int, int>>
898SequentialPlacer::buildChannelRequirements(
899 SmallVector<ObjectFifoCreateOp> &objectFifos,
900 SmallVector<ObjectFifoLinkOp> &objectFifoLinks) {
901 llvm::DenseMap<Operation *, std::pair<int, int>> channelRequirements;
904 llvm::StringMap<ObjectFifoCreateOp> fifoNameToOp;
905 for (
auto ofOp : objectFifos) {
906 fifoNameToOp[ofOp.getSymName()] = ofOp;
915 llvm::DenseMap<Operation *, Operation *> linkTileOf;
916 llvm::StringMap<SmallVector<Operation *>> linkTilesOfSource;
917 llvm::DenseSet<llvm::StringRef> linkedAsDest;
918 for (
auto linkOp : objectFifoLinks) {
919 Operation *linkTileOp =
nullptr;
920 for (
auto dstFifoAttr : linkOp.getFifoOuts())
921 if (
auto sym = dyn_cast<FlatSymbolRefAttr>(dstFifoAttr)) {
922 linkedAsDest.insert(sym.getValue());
923 auto it = fifoNameToOp.find(sym.getValue());
924 if (!linkTileOp && it != fifoNameToOp.end())
925 linkTileOp = it->second.getProducerTile().getDefiningOp();
927 linkTileOf[linkOp.getOperation()] = linkTileOp;
930 for (
auto srcFifoAttr : linkOp.getFifoIns())
931 if (
auto sym = dyn_cast<FlatSymbolRefAttr>(srcFifoAttr))
932 linkTilesOfSource[sym.getValue()].push_back(linkTileOp);
942 using ChannelKey = std::tuple<Operation *, int, int, int>;
943 auto channelKey = [](LogicalTileOp tile,
int channel) -> ChannelKey {
944 auto col = tile.getCol();
945 auto row = tile.getRow();
947 return {
nullptr,
static_cast<int>(*col),
static_cast<int>(*row), channel};
948 return {tile.getOperation(), -1, -1, channel};
950 llvm::DenseMap<ChannelKey, ObjectFifoCreateOp> sharedOut;
951 auto sharesProducerChannel = [&](ObjectFifoCreateOp fifo,
952 LogicalTileOp logicalTile) ->
bool {
953 ObjectFifoEndPortAttr port = fifo.getProdPortAttr();
954 std::optional<int> pinned =
955 port && !port.isCore() ? port.channelIndex() : std::nullopt;
956 if (!pinned || !logicalTile)
958 AIETileType type = logicalTile.getTileType();
959 bool coreSender = type == AIETileType::CoreTile &&
960 !fifo.streamsFromProducer() &&
962 if (type != AIETileType::ShimNOCTile && type != AIETileType::ShimPLTile &&
966 sharedOut.try_emplace(channelKey(logicalTile, *pinned), fifo);
969 ObjectFifoCreateOp other = it->second;
970 auto reject = [&](
const Twine &why) {
971 fifo.emitError() <<
"shares MM2S channel " << *pinned <<
" with @"
972 << other.getSymName() <<
", but " << why;
975 if (!fifo.packetHeader() || !other.packetHeader())
976 return reject(
"a shared channel carries both at once, which only "
977 "packet-switched routes tell apart");
978 std::optional<uint16_t>
id =
979 fifo.packetHeader() ? fifo.packetHeader().getPktId() : std::nullopt;
980 std::optional<uint16_t> otherId =
981 other.packetHeader() ? other.packetHeader().getPktId() : std::nullopt;
982 if (
id && otherId && *
id == *otherId)
983 return reject(Twine(
"both pin packet id ") +
984 Twine(
static_cast<unsigned>(*
id)));
990 llvm::DenseMap<ChannelKey, ObjectFifoCreateOp> sharedIn;
991 auto sharesConsumerChannel = [&](ObjectFifoCreateOp fifo, Operation *tileOp,
992 unsigned consumer) ->
bool {
993 auto logicalTile = dyn_cast_or_null<LogicalTileOp>(tileOp);
994 ObjectFifoEndPortAttr port = fifo.getConsumerPort(consumer);
995 if (!logicalTile || !port || port.isCore() || !port.channelIndex())
997 int channel = *port.channelIndex();
1001 sharedIn.try_emplace(channelKey(logicalTile, channel), fifo);
1004 ObjectFifoCreateOp other = it->second;
1005 auto reject = [&](
const Twine &why) {
1006 fifo.emitError() <<
"shares S2MM channel " << channel <<
" with @"
1007 << other.getSymName() <<
", but " << why;
1010 if (!fifo.getOutOfOrder() || !other.getOutOfOrder())
1011 return reject(
"a channel its fifos receive in turn holds their order; "
1012 "out_of_order puts that order in each object's header");
1013 std::optional<uint16_t>
id =
1014 fifo.packetHeader() ? fifo.packetHeader().getPktId() : std::nullopt;
1015 std::optional<uint16_t> otherId =
1016 other.packetHeader() ? other.packetHeader().getPktId() : std::nullopt;
1017 if (
id && otherId && *
id == *otherId)
1018 return reject(Twine(
"both pin packet id ") +
1019 Twine(
static_cast<unsigned>(*
id)));
1027 for (
auto ofOp : objectFifos) {
1028 SmallVector<Operation *> linkConsumers =
1029 linkTilesOfSource.lookup(ofOp.getSymName());
1030 bool skipProducerSide = linkedAsDest.count(ofOp.getSymName());
1032 Value producerTile = ofOp.getProducerTile();
1033 auto *producerOp = producerTile.getDefiningOp();
1034 auto producerLogicalTile = dyn_cast_or_null<LogicalTileOp>(producerOp);
1037 bool producerNeedsDMA =
false;
1039 for (
auto [consumerIndex, consumerTile] :
1040 llvm::enumerate(ofOp.getConsumerTiles())) {
1041 auto *consumerOp = consumerTile.getDefiningOp();
1042 auto consumerLogicalTile = dyn_cast_or_null<LogicalTileOp>(consumerOp);
1047 if (producerLogicalTile && consumerLogicalTile &&
1048 producerLogicalTile.getTileType() == AIETileType::CoreTile &&
1049 consumerLogicalTile.getTileType() == AIETileType::CoreTile &&
1050 ofOp.getConsumerTiles().size() == 1 && !outOfOrderNeedsDMA(ofOp))
1054 if (consumerOp && !llvm::is_contained(linkConsumers, consumerOp) &&
1055 !sharesConsumerChannel(ofOp, consumerOp, consumerIndex))
1056 channelRequirements[consumerOp].first++;
1058 producerNeedsDMA =
true;
1064 if (producerNeedsDMA && producerOp && !skipProducerSide &&
1065 !sharesProducerChannel(ofOp, producerLogicalTile))
1066 channelRequirements[producerOp].second++;
1071 for (
auto linkOp : objectFifoLinks) {
1073 Operation *linkTileOp = linkTileOf.lookup(linkOp.getOperation());
1079 int numInputChannels = linkOp.isMerge() ? 1 : linkOp.getFifoIns().size();
1080 int numOutputChannels =
1081 linkOp.isDispatch() ? 1 : linkOp.getFifoOuts().size();
1083 channelRequirements[linkTileOp].first += numInputChannels;
1084 channelRequirements[linkTileOp].second += numOutputChannels;
1087 return channelRequirements;
1090void SequentialPlacer::countTurns(
1091 DeviceOp device, ArrayRef<ObjectFifoCreateOp> objectFifos,
1092 llvm::DenseMap<Operation *, std::pair<int, int>> &requirements) {
1093 for (CoreOp core : device.getOps<CoreOp>()) {
1094 auto tile = dyn_cast_or_null<LogicalTileOp>(core.getTile().getDefiningOp());
1095 if (!tile || !requirements.count(tile))
1097 auto &[in, out] = requirements[tile];
1098 for (
bool coreDrains : {
true,
false}) {
1099 int &need = coreDrains ? in : out;
1100 auto [inBudget, outBudget] =
1102 int budget = coreDrains ? inBudget : outBudget;
1105 llvm::MapVector<Operation *, SmallVector<ObjectFifoCreateOp>> byTile;
1106 for (ObjectFifoCreateOp fifo : objectFifos) {
1107 auto consumers = fifo.getConsumerTiles();
1108 if (consumers.size() != 1)
1110 Value mine = coreDrains ? consumers[0] : fifo.getProducerTile();
1111 Value other = coreDrains ? fifo.getProducerTile() : consumers[0];
1112 auto peer = dyn_cast_or_null<LogicalTileOp>(other.getDefiningOp());
1113 if (mine == tile.getResult() && peer &&
1114 peer.getTileType() == AIETileType::MemTile)
1115 byTile[peer].push_back(fifo);
1117 for (
auto &[peer, fifos] : byTile) {
1118 if (fifos.size() < 2 || need <= budget)
1120 SmallVector<StringAttr> names;
1121 SmallVector<int64_t> depths;
1122 for (ObjectFifoCreateOp fifo : fifos) {
1123 names.push_back(fifo.getSymNameAttr());
1124 depths.push_back(fifo.size(coreDrains ? 1 : 0));
1128 if (succeeded(turns.
read(core, names, coreDrains, reason)) &&
1130 need -=
static_cast<int>(fifos.size()) - 1;
1137static BufferOp traceToBuffer(Value val) {
1138 for (Operation *op = val.getDefiningOp(); op;) {
1139 if (
auto buf = dyn_cast<BufferOp>(op))
1141 if (
auto view = dyn_cast<ViewLikeOpInterface>(op)) {
1142 val = view.getViewSource();
1143 op = val.getDefiningOp();
1151static std::optional<TileID>
1154 return std::nullopt;
1155 auto it = placed.find(peer.getOperation());
1156 if (it != placed.end())
1158 auto col = peer.tryGetCol();
1159 auto row = peer.tryGetRow();
1161 return TileID{*col, *row};
1162 return std::nullopt;
1165Placer::Adjacency SequentialPlacer::buildBufferAdjacency(
1166 ArrayRef<LogicalTileOp> logicalTiles,
1167 ArrayRef<ObjectFifoAllocateOp>
allocates) {
1171 llvm::DenseSet<std::pair<Operation *, Operation *>> seen;
1172 auto addOnce = [&](TileLike user, TileLike owner) {
1173 if (seen.insert({user.getOperation(), owner.getOperation()}).second)
1174 adjacency.addEdge(user, owner);
1180 dyn_cast_or_null<TileLike>(alloc.getDelegateTile().getDefiningOp());
1181 ObjectFifoCreateOp fifo = alloc.getObjectFifo();
1182 if (!delegate || !fifo || delegate.getTileType() != AIETileType::CoreTile)
1184 SmallVector<Value> ends{fifo.getProducerTile()};
1185 llvm::append_range(ends, fifo.getConsumerTiles());
1186 for (Value end : ends) {
1187 auto tile = dyn_cast_or_null<TileLike>(end.getDefiningOp());
1188 if (tile && tile.getTileType() == AIETileType::CoreTile &&
1189 tile.getOperation() != delegate.getOperation())
1190 addOnce(tile, delegate);
1193 for (
auto consumer : logicalTiles) {
1194 if (consumer.getTileType() != AIETileType::CoreTile)
1196 CoreOp core =
nullptr;
1197 for (Operation *user : consumer.getResult().getUsers())
1198 if (
auto c = dyn_cast<CoreOp>(user)) {
1205 core.getBody().walk([&](Operation *op) {
1206 for (Value operand : op->getOperands()) {
1207 BufferOp buf = traceToBuffer(operand);
1210 auto owner = dyn_cast_or_null<TileLike>(buf.getTile().getDefiningOp());
1211 if (owner && owner.getOperation() != consumer.getOperation())
1212 addOnce(TileLike(consumer), owner);
1222 for (
auto cf : cascadeFlows) {
1223 TileLike src = cf.getSourceTileLike();
1224 TileLike dst = cf.getDestTileLike();
1235 for (
auto ofOp : objectFifos) {
1237 dyn_cast_or_null<LogicalTileOp>(ofOp.getProducerTile().getDefiningOp());
1238 if (!producer || producer.getTileType() != AIETileType::CoreTile)
1241 if (ofOp.getConsumerTiles().size() != 1 || outOfOrderNeedsDMA(ofOp))
1243 for (Value consumerVal : ofOp.getConsumerTiles()) {
1245 dyn_cast_or_null<LogicalTileOp>(consumerVal.getDefiningOp());
1246 if (!consumer || consumer.getTileType() != AIETileType::CoreTile)
1251 if (producer.getOperation() == consumer.getOperation())
1253 adjacency.
addEdge(TileLike(producer), TileLike(consumer));
1262 for (
auto ofOp : objectFifos)
1263 if (ofOp.forcesSharedMemory() && ofOp.getConsumerTiles().size() == 1)
1265 ofOp.getConsumerTiles()[0]);
1269bool SequentialPlacer::satisfiesAdjacency(
1270 LogicalTileOp logicalTile,
TileID candidate,
const Adjacency &adjacency,
1271 llvm::function_ref<
bool(
TileID firstPos,
TileID secondPos)> pred)
const {
1273 forEachPeer(logicalTile.getOperation(), adjacency,
1274 [&](TileLike peer,
bool thisIsFirst) {
1277 auto peerPos = resolvePeerPosition(peer, result);
1280 TileID firstPos = thisIsFirst ? candidate : *peerPos;
1281 TileID secondPos = thisIsFirst ? *peerPos : candidate;
1282 if (!pred(firstPos, secondPos))
1288LogicalResult SequentialPlacer::enforceAdjacency(LogicalTileOp logicalTile,
1290 const AdjacencyKind &kind) {
1291 if (satisfiesAdjacency(logicalTile, tile, kind.adjacency, kind.pred))
1293 auto diag = logicalTile.emitError()
1294 <<
"tile (" << tile.col <<
", " << tile.row <<
") violates "
1295 << kind.name <<
" adjacency";
1296 attachPeerNotes(diag, logicalTile, kind.adjacency, kind.peerLabel);
1297 diag.attachNote() << kind.constraintHint;
1301void SequentialPlacer::recordPlacement(LogicalTileOp logicalTile,
TileID tile,
1302 PlacementContext &ctx,
1303 StringRef debugLabel) {
1304 result[logicalTile] = tile;
1305 LLVM_DEBUG(llvm::dbgs() <<
DEBUG_TYPE <<
": placed " << debugLabel <<
" LTO "
1306 << logicalTile.getLoc() <<
" at (" << tile.col <<
", "
1307 << tile.row <<
")\n");
1308 if (logicalTile.getTileType() == AIETileType::CoreTile) {
1309 recordColumnStreamUsage(logicalTile, tile.col);
1311 ctx.reserveNeighborSlots(logicalTile.getOperation(), tile);
1316SequentialPlacer::totalComputePeers(Operation *op,
const Adjacency &adjacency) {
1317 int in = 0, out = 0;
1320 forEachPeer(op, adjacency, [&](TileLike ,
bool thisIsFirst) {
1329bool SequentialPlacer::satisfiesComputePeer(
1330 LogicalTileOp logicalTile,
TileID candidate,
1332 const llvm::DenseMap<Operation *, int> &needNeighborIn,
1333 const llvm::DenseMap<Operation *, int> &needNeighborOut)
const {
1339 if (!computePeerAdjacency.hasEdges(logicalTile.getOperation()))
1344 int selfNonNeighborIn = 0, selfNonNeighborOut = 0;
1345 int selfNeighborIn = 0, selfNeighborOut = 0;
1346 forEachPeer(logicalTile.getOperation(), computePeerAdjacency,
1347 [&](TileLike peer,
bool thisIsFirst) {
1348 auto peerPos = resolvePeerPosition(peer, result);
1352 isSharedComputeMemory(*targetModel, candidate, *peerPos);
1353 bool thisIsConsumer = !thisIsFirst;
1361 ++selfNonNeighborIn;
1363 ++selfNonNeighborOut;
1366 auto [selfTotalIn, selfTotalOut] =
1367 totalComputePeers(logicalTile.getOperation(), computePeerAdjacency);
1368 int selfNeedIn = needNeighborIn.lookup(logicalTile.getOperation());
1369 int selfNeedOut = needNeighborOut.lookup(logicalTile.getOperation());
1370 int selfSlackIn = selfTotalIn - selfNeedIn;
1371 int selfSlackOut = selfTotalOut - selfNeedOut;
1372 if (selfNonNeighborIn > selfSlackIn)
1374 if (selfNonNeighborOut > selfSlackOut)
1384 if (selfNeedIn > 0 || selfNeedOut > 0) {
1385 int freeNeighborSlots = 0;
1388 ++freeNeighborSlots;
1390 int remainingInNeed = std::max(0, selfNeedIn - selfNeighborIn);
1391 int remainingOutNeed = std::max(0, selfNeedOut - selfNeighborOut);
1392 if (remainingInNeed + remainingOutNeed > freeNeighborSlots)
1400 bool symmetricOk =
true;
1402 logicalTile.getOperation(), computePeerAdjacency,
1403 [&](TileLike peer,
bool thisIsFirst) {
1406 auto peerLTO = dyn_cast_or_null<LogicalTileOp>(peer.getOperation());
1409 auto peerPos = resolvePeerPosition(peer, result);
1415 bool logicalIsPeerOut = !thisIsFirst;
1416 int peerNeedDir = logicalIsPeerOut
1417 ? needNeighborOut.lookup(peerLTO.getOperation())
1418 : needNeighborIn.lookup(peerLTO.getOperation());
1419 auto [peerTotalIn, peerTotalOut] =
1420 totalComputePeers(peerLTO.getOperation(), computePeerAdjacency);
1421 int peerTotalDir = logicalIsPeerOut ? peerTotalOut : peerTotalIn;
1422 int peerSlackDir = peerTotalDir - peerNeedDir;
1425 int peerNonNeighborDir = 0;
1427 peerLTO.getOperation(), computePeerAdjacency,
1428 [&](TileLike peerPeer, bool peerLTOIsFirst) {
1433 bool peerIsConsumerHere = !peerLTOIsFirst;
1434 if (logicalIsPeerOut == peerIsConsumerHere)
1436 if (peerPeer.getOperation() == logicalTile.getOperation()) {
1439 if (!isSharedComputeMemory(*targetModel, *peerPos, candidate))
1440 ++peerNonNeighborDir;
1443 auto pp = resolvePeerPosition(peerPeer, result);
1446 if (!isSharedComputeMemory(*targetModel, *peerPos, *pp))
1447 ++peerNonNeighborDir;
1449 if (peerNonNeighborDir > peerSlackDir)
1450 symmetricOk =
false;
1455void SequentialPlacer::attachPeerNotes(
1456 InFlightDiagnostic &diag, LogicalTileOp logicalTile,
1458 llvm::function_ref<StringRef(
bool thisIsFirst)> labelPeer)
const {
1459 int unplacedCount = 0;
1460 forEachPeer(logicalTile.getOperation(), adjacency,
1461 [&](TileLike peer,
bool thisIsFirst) {
1462 auto peerPos = resolvePeerPosition(peer, result);
1467 diag.attachNote(peer.getLoc())
1468 << labelPeer(thisIsFirst) <<
" peer placed at ("
1469 << peerPos->col <<
", " << peerPos->row <<
")";
1471 if (unplacedCount > 0)
1472 diag.attachNote() << unplacedCount
1473 <<
" additional peer(s) on this LTO are not yet placed "
1474 "(placed after this LTO; their positions may "
1475 "subsequently constrain or unblock this placement)";
1481 for (
auto ofOp : objectFifos)
1482 for (Value consumer : ofOp.getConsumerTiles())
1489 ArrayRef<ObjectFifoCreateOp> objectFifos) {
1490 llvm::DenseSet<std::pair<Operation *, Value>> registered;
1491 for (
auto reg : device.getOps<ObjectFifoRegisterExternalBuffersOp>())
1492 registered.insert({reg.getObjectFifo(), reg.getTile()});
1494 auto shareable = [&](ObjectFifoCreateOp fifo, Value tile,
1495 ObjectFifoEndPortAttr port) -> Operation * {
1496 auto lt = dyn_cast_or_null<LogicalTileOp>(tile.getDefiningOp());
1497 if (!lt || lt.getTileType() != AIETileType::ShimNOCTile || fifo.getPlio() ||
1498 (port && port.channelIndex()) || registered.contains({fifo, tile}))
1502 for (
auto fifo : objectFifos) {
1504 shareable(fifo, fifo.getProducerTile(), fifo.getProdPortAttr()))
1505 ends[lt][1].push_back(fifo.getSymNameAttr());
1506 for (
auto [i, consumer] : llvm::enumerate(fifo.getConsumerTiles()))
1507 if (Operation *lt = shareable(fifo, consumer, fifo.getConsumerPort(i)))
1508 ends[lt][0].push_back(fifo.getSymNameAttr());
1510 for (
auto endpoint : device.getOps<RouteEndpointOp>()) {
1511 auto tile = dyn_cast<LogicalTileOp>(endpoint.getTile().getDefiningOp());
1512 if (tile && tile.isShimTile() && endpoint.getBundle() == WireBundle::DMA &&
1513 !endpoint.getChannelIndex())
1514 ends[tile][endpoint.getRouteDirection() == DMAChannelDir::MM2S ? 1 : 0]
1515 .push_back(endpoint.getSymNameAttr());
1521 StringAttr symbol) {
1522 if (
auto endpoint = dyn_cast_or_null<RouteEndpointOp>(
1523 SymbolTable::lookupNearestSymbolFrom(device, symbol)))
1525 DMAChannelDir::MM2S};
1526 auto fifo = dyn_cast_or_null<ObjectFifoCreateOp>(
1527 SymbolTable::lookupNearestSymbolFrom(device, symbol));
1529 return std::nullopt;
1531 dyn_cast_or_null<TileLike>(fifo.getProducerTile().getDefiningOp());
1536 ArrayRef<PacketFlowOp> pktFlows) {
1538 for (
auto flow : flows)
1541 for (
auto pktFlow : pktFlows) {
1542 SmallVector<Value> srcs, dsts;
1543 pktFlow.walk([&](Operation *op) {
1544 if (
auto src = dyn_cast<PacketSourceOp>(op))
1545 srcs.push_back(src.getTile());
1546 else if (
auto dst = dyn_cast<PacketDestOp>(op))
1547 dsts.push_back(dst.getTile());
1565void SequentialPlacer::addChannelRequirementsFromFlows(
1566 ArrayRef<FlowOp> flows, ArrayRef<PacketFlowOp> pktFlows,
1567 ArrayRef<RouteOp> routes,
1568 llvm::DenseMap<Operation *, std::pair<int, int>> &channelRequirements) {
1570 llvm::DenseSet<std::tuple<Operation *, int>> seenSrc, seenDst;
1571 namedChannels.clear();
1573 auto incIfDMA = [&](Operation *tileOp, WireBundle bundle,
1574 std::optional<int> channel,
bool isOutput) {
1575 if (!tileOp || !isa<LogicalTileOp>(tileOp))
1577 if (bundle != WireBundle::DMA)
1579 auto &seen = isOutput ? seenSrc : seenDst;
1580 if (channel && !seen.insert({tileOp, *channel}).second)
1583 namedChannels[tileOp].push_back({isOutput, *channel});
1585 channelRequirements[tileOp].second++;
1587 channelRequirements[tileOp].first++;
1590 for (
auto flow : flows) {
1591 incIfDMA(flow.getSource().getDefiningOp(), flow.getSourceBundle(),
1592 flow.getSourceChannel(),
true);
1593 incIfDMA(flow.getDest().getDefiningOp(), flow.getDestBundle(),
1594 flow.getDestChannel(),
false);
1597 for (
auto pktFlow : pktFlows) {
1598 pktFlow.walk([&](Operation *op) {
1599 if (
auto src = dyn_cast<PacketSourceOp>(op)) {
1600 incIfDMA(src.getTile().getDefiningOp(), src.getBundle(),
1601 src.getChannel(),
true);
1602 }
else if (
auto dst = dyn_cast<PacketDestOp>(op)) {
1603 incIfDMA(dst.getTile().getDefiningOp(), dst.getBundle(),
1604 dst.getChannel(),
false);
1611 llvm::DenseSet<Operation *> countedEndpoints;
1612 for (
auto route : routes) {
1613 auto count = [&](StringRef name,
bool isOutput) {
1614 auto endpoint = SymbolTable::lookupNearestSymbolFrom<RouteEndpoint>(
1615 route, StringAttr::get(route.getContext(), name));
1616 if (endpoint && countedEndpoints.insert(endpoint).second)
1617 incIfDMA(endpoint.getTile().getDefiningOp(), endpoint.getRouteBundle(),
1618 endpoint.getRouteChannel(), isOutput);
1620 for (StringRef source : route.getSourceNames())
1621 count(source,
true);
1622 for (StringRef dest : route.getDestinationNames())
1627SequentialPlacer::FlowMembership SequentialPlacer::buildFlowMembership(
1628 ArrayRef<FlowOp> flows, ArrayRef<PacketFlowOp> pktFlows,
1629 ArrayRef<RouteOp> routes, ArrayRef<ObjectFifoCreateOp> objectFifos) {
1634 auto isLto = [](Value v) {
1635 return v && mlir::isa_and_nonnull<LogicalTileOp>(v.getDefiningOp());
1637 auto addEntry = [&](Value lto, ArrayRef<Value> peers) {
1638 if (!isLto(lto) || peers.empty())
1640 idx.ltoFlows[lto].push_back(SmallVector<Value>(peers.begin(), peers.end()));
1643 for (
auto flow : flows) {
1644 Value s = flow.getSource(), d = flow.getDest();
1648 for (
auto pf : pktFlows) {
1649 SmallVector<Value> srcs, dsts;
1650 pf.walk([&](Operation *op) {
1651 if (
auto src = dyn_cast<PacketSourceOp>(op))
1652 srcs.push_back(src.getTile());
1653 else if (
auto dst = dyn_cast<PacketDestOp>(op))
1654 dsts.push_back(dst.getTile());
1656 for (Value s : srcs)
1658 for (Value d : dsts)
1661 for (
auto route : routes) {
1662 auto tileOf = [&](StringRef name) -> Value {
1663 auto endpoint = SymbolTable::lookupNearestSymbolFrom<RouteEndpoint>(
1664 route, StringAttr::get(route.getContext(), name));
1665 return endpoint ? endpoint.getTile() : Value();
1667 SmallVector<Value> srcs, dsts;
1668 for (StringRef source : route.getSourceNames())
1669 if (Value tile = tileOf(source))
1670 srcs.push_back(tile);
1671 for (StringRef dest : route.getDestinationNames())
1672 if (Value tile = tileOf(dest))
1673 dsts.push_back(tile);
1674 for (Value s : srcs)
1676 for (Value d : dsts)
1679 for (
auto of : objectFifos) {
1680 Value prod = of.getProducerTile();
1681 auto cons = of.getConsumerTiles();
1682 SmallVector<Value> consVec(cons.begin(), cons.end());
1683 addEntry(prod, consVec);
1684 for (Value c : consVec)
1685 addEntry(c, {prod});
1691SequentialPlacer::computeCentroidColumn(LogicalTileOp logicalTile,
1692 const FlowMembership &flowIndex) {
1703 Value ltoVal = logicalTile.getResult();
1704 auto ltoIt = flowIndex.ltoFlows.find(ltoVal);
1705 if (ltoIt == flowIndex.ltoFlows.end())
1706 return std::nullopt;
1711 auto resolvePeerCol = [&](Value v) -> std::optional<int> {
1712 Operation *defOp = v.getDefiningOp();
1714 return std::nullopt;
1716 if (
auto tileOp = dyn_cast<TileOp>(defOp))
1717 return tileOp.getCol();
1718 auto lto = dyn_cast<LogicalTileOp>(defOp);
1720 return std::nullopt;
1721 if (
auto c = lto.tryGetCol())
1723 auto it =
result.find(defOp);
1725 return it->second.col;
1726 return std::nullopt;
1732 auto resolveLtoCoreCols = [&](Value v) {
1733 SmallVector<int> out;
1734 auto it = flowIndex.ltoFlows.find(v);
1735 if (it == flowIndex.ltoFlows.end())
1737 for (
auto &peerList : it->second)
1738 for (Value p : peerList)
1739 if (
auto col = resolvePeerCol(p))
1740 out.push_back(*col);
1744 SmallVector<SmallVector<int>> perFlowDests;
1745 for (
auto &peers : ltoIt->second) {
1746 SmallVector<int> dests;
1747 for (Value p : peers) {
1748 if (
auto col = resolvePeerCol(p)) {
1749 dests.push_back(*col);
1754 for (
int c : resolveLtoCoreCols(p))
1758 perFlowDests.push_back(std::move(dests));
1761 if (perFlowDests.empty())
1762 return std::nullopt;
1764 double meanDestCol = 0.0;
1766 for (
auto &dests : perFlowDests)
1767 for (
int d : dests) {
1771 meanDestCol /= destCount;
1775 int bestCost = std::numeric_limits<int>::max();
1776 double bestTiebreak = std::numeric_limits<double>::infinity();
1777 for (
int S = 0; S < numCols; ++S) {
1779 for (
auto &dests : perFlowDests) {
1780 if (dests.size() == 1) {
1781 cost += std::abs(S - dests[0]);
1783 int lo = *llvm::min_element(dests);
1784 int hi = *llvm::max_element(dests);
1791 double tiebreak = std::abs(S - meanDestCol);
1792 if (cost < bestCost || (cost == bestCost && tiebreak < bestTiebreak)) {
1794 bestTiebreak = tiebreak;
1801LogicalResult SequentialPlacer::placeNonCoreTileByCentroid(
1802 LogicalTileOp logicalTile,
const FlowMembership &flowIndex,
1803 const llvm::DenseMap<Operation *, std::pair<int, int>>
1804 &channelRequirements) {
1805 std::optional<int> centroidCol =
1806 computeCentroidColumn(logicalTile, flowIndex);
1807 auto colConstraint = logicalTile.tryGetCol();
1812 std::optional<int> targetCol = colConstraint ? colConstraint : centroidCol;
1813 if (!targetCol && !spreadUnanchoredTiles)
1816 auto [numInputChannels, numOutputChannels] =
1817 channelRequirements.lookup(logicalTile.getOperation());
1819 auto maybeTile = findTileWithCapacity(targetCol,
availability.nonCompTiles,
1820 numInputChannels, numOutputChannels,
1821 logicalTile.getTileType(), logicalTile,
1824 maybeTile = findTileWithCapacity(targetCol,
availability.nonCompTiles,
1825 numInputChannels, numOutputChannels,
1826 logicalTile.getTileType(), logicalTile,
1829 if (!maybeTile && mergeLogicalTiles &&
1830 logicalTile.getTileType() == AIETileType::ShimNOCTile) {
1831 std::optional<std::pair<int, int>> bestKey;
1832 for (
int col = 0, numCols =
targetModel->columns(); col < numCols; ++col) {
1834 if ((colConstraint && col != *colConstraint) ||
1835 !fitsSharingShim(tile, logicalTile,
1836 {numInputChannels, numOutputChannels}))
1838 std::pair<int, int> key{targetCol ? std::abs(col - *targetCol) : 0, col};
1839 if (!bestKey || key < *bestKey) {
1846 AIETileType requestedType = logicalTile.getTileType();
1847 StringRef tileTypeName = stringifyAIETileType(requestedType);
1848 auto diag = logicalTile.emitError();
1849 CapacityDiagnosis diagnosis = diagnoseCapacityExhaustion(
1850 requestedType, numInputChannels, numOutputChannels);
1851 if (!diagnosis.capacityExistsSomewhere) {
1852 diag <<
"no " << tileTypeName <<
" on the device has " << numInputChannels
1853 <<
" input/" << numOutputChannels
1854 <<
" output DMA channel(s) free: all " << diagnosis.tilesOfType
1855 <<
" " << tileTypeName <<
"(s) are at " << diagnosis.inUsed <<
"/"
1856 << diagnosis.inMax <<
" input, " << diagnosis.outUsed <<
"/"
1857 << diagnosis.outMax <<
" output channels used";
1858 diag.attachNote() <<
"this is the device's total " << tileTypeName
1859 <<
" DMA budget, not a placement choice -- no column "
1860 "has spare capacity to pin to";
1861 int shortIn = diagnosis.closestShortIn;
1862 int shortOut = diagnosis.closestShortOut;
1863 auto &remedy = diag.attachNote();
1864 remedy <<
"the closest " << tileTypeName <<
" is ";
1865 if (shortIn > 0 && shortOut > 0)
1866 remedy <<
"short " << shortIn <<
" input/" << shortOut
1867 <<
" output channel(s); ";
1868 else if (shortIn > 0)
1869 remedy <<
"short " << shortIn <<
" input channel(s); ";
1870 else if (shortOut > 0)
1871 remedy <<
"short " << shortOut <<
" output channel(s); ";
1872 remedy <<
"fan traffic through a MemTile via aie.objectfifo.link, or "
1873 "reduce the number of DMA-attached ObjectFIFOs feeding this "
1876 diag <<
"no " << tileTypeName <<
" has sufficient DMA capacity for "
1877 << numInputChannels <<
" input/" << numOutputChannels
1878 <<
" output channels ";
1880 diag <<
"at column " << *colConstraint;
1881 else if (centroidCol)
1882 diag <<
"near centroid column " << *centroidCol;
1884 diag <<
"on any column";
1885 diag.attachNote() <<
"every " << tileTypeName
1886 <<
" with spare DMA capacity already hosts a "
1887 "different logical tile (merge-logical-tiles is "
1888 "off); enable merging, or pin logical tiles "
1889 "one-per-column if fewer of them than physical "
1890 << tileTypeName <<
"s remain unclaimed";
1895 result[logicalTile] = *maybeTile;
1897 auto showCol = [](std::optional<int> c) {
1898 return c ? std::to_string(*c) : std::string(
"none");
1900 llvm::dbgs() <<
DEBUG_TYPE <<
": placed non-core LTO "
1901 << logicalTile.getLoc() <<
" at (" << maybeTile->col <<
", "
1902 << maybeTile->row <<
") (centroid col=" << showCol(centroidCol)
1903 <<
", target col=" << showCol(targetCol) <<
")\n";
1905 if (!mergeLogicalTiles)
1906 assignedNonCoreTiles.insert(*maybeTile);
1907 if (numInputChannels > 0)
1908 updateChannelUsage(*maybeTile, DmaDir::In, numInputChannels);
1909 if (numOutputChannels > 0)
1910 updateChannelUsage(*maybeTile, DmaDir::Out, numOutputChannels);
1911 recordStreamAndBdUsage(logicalTile, *maybeTile);
1912 recordShimEnds(*maybeTile, logicalTile);
1916bool SequentialPlacer::fitsSharingShim(
TileID tile, Operation *logicalTile,
1917 std::pair<int, int> required) {
1918 if (
targetModel->getTileType(tile.col, tile.row) !=
1919 AIETileType::ShimNOCTile ||
1925 int need[2] = {required.first, required.second};
1926 int max[2] = {maxIn, maxOut};
1927 auto own = shareableShimEnds.lookup(logicalTile);
1928 for (
int dir : {0, 1}) {
1929 if (used[dir] + need[dir] <= max[dir])
1931 SmallVector<StringAttr> ends(shimEndsAt[tile][dir]);
1932 llvm::append_range(ends, own[dir]);
1933 int fixed = used[dir] + need[dir] -
static_cast<int>(ends.size());
1934 if (fixed +
static_cast<int>(shimSpans->groups(ends).size()) > max[dir])
1940void SequentialPlacer::recordShimEnds(
TileID tile, Operation *logicalTile) {
1941 auto own = shareableShimEnds.find(logicalTile);
1942 if (own == shareableShimEnds.end())
1944 for (
int dir : {0, 1})
1945 llvm::append_range(shimEndsAt[tile][dir], own->second[dir]);
1948std::optional<TileID> SequentialPlacer::findTileWithCapacity(
1949 std::optional<int> targetCol, llvm::ArrayRef<TileID> tiles,
1950 int requiredInputChannels,
int requiredOutputChannels,
1951 AIETileType requestedType, Operation *logicalTile,
bool withinBudgets) {
1964 std::optional<TileID> best;
1965 std::tuple<int, int, int, int> bestKey;
1967 for (
auto &tile : tiles) {
1968 if (
targetModel->getTileType(tile.col, tile.row) != requestedType)
1970 if (!hasAvailableChannels(tile, requiredInputChannels,
1971 requiredOutputChannels))
1976 if (!mergeLogicalTiles && assignedNonCoreTiles.contains(tile))
1978 if (withinBudgets && !fitsStreamAndBdBudgets(logicalTile, tile))
1980 if (llvm::any_of(namedChannels.lookup(logicalTile), [&](
auto named) {
1981 return availability.namedChannelsUsed[tile].contains(named);
1984 int dist = targetCol ? std::abs(tile.col - *targetCol) : 0;
1985 if (requestedType == AIETileType::MemTile &&
1986 !cast<LogicalTileOp>(logicalTile).tryGetCol())
1987 dist += broadcastFanoutUsed.lookup(tile);
1990 (requiredInputChannels == 0 && requiredOutputChannels == 0)) {
1995 if (requiredInputChannels > 0)
1997 if (requiredOutputChannels > 0)
2000 std::tuple<int, int, int, int> key{dist, load, tile.col, tile.row};
2001 if (!best || key < bestKey) {
2010SequentialPlacer::CapacityDiagnosis
2011SequentialPlacer::diagnoseCapacityExhaustion(AIETileType requestedType,
2012 int requiredInputChannels,
2013 int requiredOutputChannels) {
2018 CapacityDiagnosis diagnosis;
2019 for (
int col = 0, numCols =
targetModel->columns(); col < numCols; ++col) {
2020 for (
int row = 0, numRows =
targetModel->rows(); row < numRows; ++row) {
2021 if (
targetModel->getTileType(col, row) != requestedType)
2024 ++diagnosis.tilesOfType;
2026 diagnosis.inMax += maxIn;
2027 diagnosis.outMax += maxOut;
2028 int usedIn =
availability.inputChannelsUsed.lookup(tile);
2029 int usedOut =
availability.outputChannelsUsed.lookup(tile);
2030 diagnosis.inUsed += usedIn;
2031 diagnosis.outUsed += usedOut;
2032 int shortIn = std::max(0, requiredInputChannels - (maxIn - usedIn));
2033 int shortOut = std::max(0, requiredOutputChannels - (maxOut - usedOut));
2034 if (diagnosis.tilesOfType == 1 ||
2035 shortIn + shortOut <
2036 diagnosis.closestShortIn + diagnosis.closestShortOut) {
2037 diagnosis.closestShortIn = shortIn;
2038 diagnosis.closestShortOut = shortOut;
2040 if (hasAvailableChannels(tile, requiredInputChannels,
2041 requiredOutputChannels))
2042 diagnosis.capacityExistsSomewhere =
true;
2048void SequentialPlacer::updateChannelUsage(
TileID tile, DmaDir direction,
2050 if (direction == DmaDir::Out)
2055 if (!hasAvailableChannels(tile, 0, 0)) {
2056 AIETileType type =
targetModel->getTileType(tile.col, tile.row);
2061bool SequentialPlacer::hasAvailableChannels(
TileID tile,
int inputChannels,
2062 int outputChannels) {
2066 int currentOut =
availability.outputChannelsUsed[tile];
2068 return (currentIn + inputChannels <= maxIn) &&
2069 (currentOut + outputChannels <= maxOut);
2072void SequentialPlacer::recordColumnStreamUsage(Operation *tile,
int col) {
2073 auto &incoming = columnIncomingFifos[col];
2074 for (Operation *fifo : incomingFifos.lookup(tile))
2075 incoming.insert(fifo);
2076 auto &used = columnStreamsUsed[col];
2077 used += streamDemand.lookup(tile);
2078 used.southIn = incoming.size();
2081void SequentialPlacer::buildStreamDemand(
2082 ArrayRef<ObjectFifoCreateOp> objectFifos) {
2083 streamDemand.clear();
2084 columnStreamsUsed.clear();
2085 incomingFifos.clear();
2086 columnIncomingFifos.clear();
2087 broadcastFanout.clear();
2088 broadcastFanoutUsed.clear();
2091 auto rank = [](Operation *op) -> std::optional<int> {
2092 auto tile = dyn_cast_or_null<TileLike>(op);
2094 return std::nullopt;
2095 if (tile.isShimTile())
2097 return tile.isMemTile() ? 1 : 2;
2099 for (
auto of : objectFifos) {
2100 Operation *producer = of.getProducerTile().getDefiningOp();
2101 std::optional<int> producerRank = rank(producer);
2104 if (*producerRank == 1)
2105 broadcastFanout[producer] +=
2106 std::max(0,
static_cast<int>(of.getConsumerTiles().size()) - 1);
2107 bool north =
false, south =
false;
2108 for (Value consumerTile : of.getConsumerTiles()) {
2109 Operation *consumer = consumerTile.getDefiningOp();
2110 std::optional<int> consumerRank = rank(consumer);
2111 if (!consumerRank || *consumerRank == *producerRank)
2113 if (*consumerRank > *producerRank) {
2115 streamDemand[consumer].southIn++;
2116 if (*consumerRank == 2)
2117 incomingFifos[consumer].push_back(of.getOperation());
2120 streamDemand[consumer].northIn++;
2124 streamDemand[producer].northOut++;
2126 streamDemand[producer].southOut++;
2128 for (
auto &[op, demand] : streamDemand)
2129 if (
auto tile = dyn_cast<TileOp>(op))
2130 if (tile.isCoreTile())
2131 recordColumnStreamUsage(op, tile.getCol());
2132 for (
auto &[op, fanout] : broadcastFanout)
2133 if (
auto tile = dyn_cast<TileOp>(op))
2134 broadcastFanoutUsed[{tile.getCol(), tile.getRow()}] += fanout;
2137void SequentialPlacer::buildRuntimeBdProfiles(
2138 DeviceOp device, ArrayRef<ObjectFifoCreateOp> objectFifos) {
2139 llvm::StringMap<Operation *> shimOfFifo;
2140 for (
auto of : objectFifos) {
2141 SmallVector<Value> ends{of.getProducerTile()};
2142 llvm::append_range(ends, of.getConsumerTiles());
2143 for (Value end : ends) {
2144 auto lt = dyn_cast_or_null<LogicalTileOp>(end.getDefiningOp());
2145 if (lt && lt.getTileType() == AIETileType::ShimNOCTile)
2146 shimOfFifo[of.getSymName()] = lt;
2149 if (shimOfFifo.empty())
2153 return targetModel->getTileType(t.col, t.row) == AIETileType::ShimNOCTile;
2160 SmallVector<std::pair<Operation *, int>> events;
2161 device.walk([&](RuntimeSequenceOp seq) {
2162 llvm::MapVector<Value, std::pair<Operation *, int>> live;
2163 seq.walk([&](Operation *op) {
2164 if (
auto task = dyn_cast<AIEX::DMAConfigureTaskForOp>(op)) {
2166 shimOfFifo.lookup(task.getAlloc().getRootReference().getValue());
2170 task.getBody().walk([&](DMABDOp bd) {
2173 bds += succeeded(plan) && !plan->pieces.empty() ? plan->pieces.size()
2176 live[task.getResult()] = {shim, bds};
2177 events.push_back({shim, bds});
2181 if (
auto await = dyn_cast<AIEX::DMAAwaitTaskOp>(op))
2182 released = await.getTask();
2183 else if (
auto free = dyn_cast<AIEX::DMAFreeTaskOp>(op))
2184 released = free.getTask();
2185 auto *it = live.find(released);
2186 if (it == live.end())
2188 events.push_back({it->second.first, -it->second.second});
2191 for (
auto &[task, held] : live)
2192 events.push_back({held.first, -held.second});
2195 llvm::MapVector<Operation *, int> running;
2196 for (
auto &[shim, delta] : events)
2197 if (running.insert({shim, 0}).second)
2198 runtimeBdProfiles[shim].assign(events.size(), 0);
2199 for (
auto [idx, event] : llvm::enumerate(events)) {
2200 running[
event.first] +=
event.second;
2201 for (
auto &[shim, n] : running)
2202 runtimeBdProfiles[shim][idx] = n;
2206bool SequentialPlacer::fitsStreamAndBdBudgets(Operation *logicalTile,
2210 StreamDemand demand = streamDemand.lookup(logicalTile);
2211 StreamDemand streams =
availability.streamsUsed.lookup(tile);
2216 tile.col, tile.row, WireBundle::North))) ||
2219 tile.col, tile.row, WireBundle::South))) ||
2222 tile.col, tile.row, WireBundle::North))) ||
2225 tile.col, tile.row, WireBundle::South))))
2228 auto profile = runtimeBdProfiles.find(logicalTile);
2229 if (profile == runtimeBdProfiles.end())
2231 SmallVector<int> used =
availability.runtimeBdsUsed.lookup(tile);
2232 used.resize(profile->second.size(), 0);
2233 int numBDs = tm.
getNumBDs(tile.col, tile.row);
2234 return llvm::all_of(llvm::zip(profile->second, used), [&](
auto live) {
2235 return !std::get<0>(live) ||
2236 std::get<0>(live) + std::get<1>(live) <= numBDs;
2240void SequentialPlacer::recordStreamAndBdUsage(Operation *logicalTile,
2242 broadcastFanoutUsed[tile] += broadcastFanout.lookup(logicalTile);
2243 for (
auto named : namedChannels.lookup(logicalTile))
2245 if (
auto it = streamDemand.find(logicalTile); it != streamDemand.end())
2247 auto profile = runtimeBdProfiles.find(logicalTile);
2248 if (profile == runtimeBdProfiles.end())
2250 SmallVector<int> &used =
availability.runtimeBdsUsed[tile];
2251 used.resize(profile->second.size(), 0);
2252 for (
auto [idx, live] : llvm::enumerate(profile->second))
2257 auto removeFromVector = [&](std::vector<TileID> &vec) {
2258 vec.erase(std::remove(vec.begin(), vec.end(), tile), vec.end());
2262 case AIETileType::CoreTile:
2266 case AIETileType::MemTile:
2267 case AIETileType::ShimNOCTile:
2268 case AIETileType::ShimPLTile:
virtual AIETileType getTileType(int col, int row) const =0
Return the tile type for the given tile coordinates.
virtual int rows() const =0
Return the number of rows in the device.
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
virtual int columns() const =0
Return the number of columns in the device.
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 getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
mlir::LogicalResult read(llvm::ArrayRef< ObjectFifoCoreEndpointOp > ends, std::string &reason)
Reads the order off the program of the core working through ends, all draining (the channel fills the...
bool keepsUp(llvm::ArrayRef< int64_t > depths)
Whether a channel moving objects in turns() order forever, through pools of depths,...
llvm::DenseMap< mlir::Operation *, std::array< llvm::SmallVector< mlir::StringAttr >, 2 > > ShareableShimEnds
static Adjacency buildCascadeAdjacency(llvm::ArrayRef< CascadeFlowOp > cascadeFlows)
static bool satisfiesConstraints(mlir::Operation *tile, TileID pos)
static Adjacency buildSharedMemAdjacency(llvm::ArrayRef< ObjectFifoCreateOp > objectFifos)
static Adjacency buildObjectFifoAdjacency(llvm::ArrayRef< ObjectFifoCreateOp > objectFifos)
static Adjacency buildFlowAdjacency(llvm::ArrayRef< FlowOp > flows, llvm::ArrayRef< PacketFlowOp > pktFlows)
const AIETargetModel * targetModel
virtual void initialize(const AIETargetModel &tm)
static Adjacency buildComputePeerAdjacency(llvm::ArrayRef< ObjectFifoCreateOp > objectFifos)
llvm::SmallVector< AllocateInfo > allocates
TileAvailability availability
static ShareableShimEnds collectShareableShimEnds(DeviceOp device, llvm::ArrayRef< ObjectFifoCreateOp > objectFifos)
static CollectedOps collectOperations(DeviceOp device)
static std::optional< ShimTransferSpans::End > shimEndOf(DeviceOp device, mlir::StringAttr symbol)
void initialize(const AIETargetModel &targetModel) override
mlir::LogicalResult place(DeviceOp device) override
mlir::FailureOr< TaskBdDecomposition > decomposeTaskBdPattern(xilinx::AIE::DMABDOp bd, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow)
Plan the split of bd, a runtime task's descriptor, on tile (tileCol, tileRow).
void forEachPeer(mlir::Operation *op, const Placer::Adjacency &adjacency, F &&fn)
llvm::DenseMap< mlir::Operation *, TileID > PlacementResult
void forEachMemAffinityNeighbor(const AIETargetModel &targetModel, TileID at, F &&fn)
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
std::pair< int, int > getDMACapacity(const AIETargetModel &tm, TileID tile)
Get DMA channel capacity (maxIn, maxOut) for a tile position: its S2MM and MM2S channel counts.
void addEdge(TileLike first, TileLike second)
llvm::DenseMap< mlir::Operation *, llvm::SmallVector< unsigned, 2 > > tileToEdges
void addEdgeFromValues(mlir::Value a, mlir::Value b)
llvm::SmallVector< std::pair< TileLike, TileLike >, 4 > edges
llvm::SmallVector< RouteOp > routes
llvm::SmallVector< ObjectFifoLinkOp > objectFifoLinks
llvm::SmallVector< CascadeFlowOp > cascadeFlows
llvm::SmallVector< PacketFlowOp > pktFlows
llvm::SmallVector< FlowOp > flows
llvm::SmallVector< ObjectFifoCreateOp > objectFifos
llvm::SmallVector< ObjectFifoAllocateOp > objectFifoAllocates
llvm::SmallVector< LogicalTileOp > logicalTiles
A shim end a runtime sequence transfers through.
llvm::DenseMap< mlir::Operation *, unsigned > rank() const
Rank the nodes by their features and then, a round at a time until no class splits,...
static StructuralGraph of(DeviceOp device)
The ops directly in device but its runtime sequences, each with the ops nested in it,...
std::vector< TileID > compTiles
void removeTile(TileID tile, AIETileType type)
std::vector< TileID > nonCompTiles
llvm::DenseSet< TileID > compTilesSet