12#include "mlir/IR/IRMapping.h"
13#include "mlir/Pass/Pass.h"
21#include "llvm/ADT/DenseSet.h"
22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SmallBitVector.h"
26#define GEN_PASS_DEF_AIEFINDFLOWS
27#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
30#define DEBUG_TYPE "aie-find-flows"
51 llvm::SmallVector<Operation *, 3> ops;
65 llvm::SmallVector<Via, 4> vias;
67 llvm::SmallVector<Operation *, 8> usedOps;
68 bool isPacket =
false;
75 llvm::SmallVector<Operation *, 3>
ops;
80 bool splitFanouts =
false;
81 mutable llvm::DenseSet<std::pair<Operation *, int>> fanoutIngresses;
82 mutable llvm::DenseSet<std::pair<Operation *, int>> fanoutEgressSeeds;
83 mutable llvm::SmallVector<CircuitFanoutSeed, 8> circuitFanoutSeeds;
84 mutable std::set<std::tuple<Operation *, int, int>> packetMergeEgresses;
85 mutable llvm::DenseSet<Operation *> packetMergeOps;
87 static int encodePort(
Port p) {
88 return static_cast<int>(p.bundle) * 64 + p.channel;
98 auto scan = [&](Operation *sw, Region &connections) {
99 llvm::SmallVector<Port, 8> sources;
100 Block &b = connections.front();
101 for (
auto connectOp : b.getOps<ConnectOp>()) {
102 if (!llvm::is_contained(sources, connectOp.sourcePort())) {
103 sources.push_back(connectOp.sourcePort());
106 for (
auto rulesOp : b.getOps<PacketRulesOp>()) {
107 if (!llvm::is_contained(sources, rulesOp.sourcePort())) {
108 sources.push_back(rulesOp.sourcePort());
112 for (
Port p : sources) {
113 if (getConnectionsThroughSwitchbox(connections, p).size() > 1) {
114 fanoutIngresses.insert({sw, encodePort(p)});
118 const int idMask = (1 << llvm::Log2_32_Ceil(
119 device.getTargetModel().getMaxPacketId() + 1)) -
121 using MergeKey = std::pair<int, int>;
122 std::map<MergeKey, SmallVector<Port, 4>> mergeIngresses;
123 std::map<MergeKey, SmallVector<Operation *, 8>> mergeOps;
124 for (PacketRulesOp rules : b.getOps<PacketRulesOp>()) {
125 for (
int id = 0;
id <= idMask; ++id) {
126 PacketRuleOp selected;
127 for (PacketRuleOp rule :
128 rules.getRules().front().getOps<PacketRuleOp>()) {
129 if ((
id & rule.maskInt()) == rule.valueInt()) {
136 for (MasterSetOp masterSet : b.getOps<MasterSetOp>()) {
137 if (!llvm::is_contained(masterSet.getAmsels(), selected.getAmsel()))
139 MergeKey key{encodePort(masterSet.destPort()),
id};
140 if (!llvm::is_contained(mergeIngresses[key], rules.sourcePort()))
141 mergeIngresses[key].push_back(rules.sourcePort());
143 {selected.getOperation(), masterSet.getOperation(),
144 selected.getAmsel().getDefiningOp()})
145 if (!llvm::is_contained(mergeOps[key], op))
146 mergeOps[key].push_back(op);
150 for (
const auto &[key, ingresses] : mergeIngresses) {
151 if (ingresses.size() < 2)
153 auto [egress, id] = key;
154 packetMergeEgresses.insert({sw, egress,
id});
155 packetMergeOps.insert_range(mergeOps[key]);
158 for (
auto switchOp : device.getOps<SwitchboxOp>()) {
159 scan(switchOp, switchOp.getConnections());
165 return fanoutEgressSeeds;
168 return circuitFanoutSeeds;
171 return packetMergeOps;
174 return packetMergeEgresses;
177 return {
static_cast<WireBundle
>(e / 64), e % 64};
181 std::optional<PortConnection>
182 getConnectionThroughWire(Operation *op,
Port masterPort)
const {
183 LLVM_DEBUG(llvm::dbgs() <<
"Wire:" << *op <<
" "
184 << stringifyWireBundle(masterPort.bundle) <<
" "
185 << masterPort.channel <<
"\n");
186 for (
auto wireOp : device.getOps<WireOp>()) {
187 if (wireOp.getSource().getDefiningOp() == op &&
188 wireOp.getSourceBundle() == masterPort.bundle) {
189 Operation *other = wireOp.getDest().getDefiningOp();
190 Port otherPort = {wireOp.getDestBundle(), masterPort.channel};
191 LLVM_DEBUG(llvm::dbgs() <<
"Connects To:" << *other <<
" "
192 << stringifyWireBundle(otherPort.bundle) <<
" "
193 << otherPort.channel <<
"\n");
197 if (wireOp.getDest().getDefiningOp() == op &&
198 wireOp.getDestBundle() == masterPort.bundle) {
199 Operation *other = wireOp.getSource().getDefiningOp();
200 Port otherPort = {wireOp.getSourceBundle(), masterPort.channel};
201 LLVM_DEBUG(llvm::dbgs() <<
"Connects To:" << *other <<
" "
202 << stringifyWireBundle(otherPort.bundle) <<
" "
203 << otherPort.channel <<
"\n");
207 LLVM_DEBUG(llvm::dbgs() <<
"*** Missing Wire!\n");
211 std::vector<PortMaskValue>
212 getConnectionsThroughSwitchbox(Region &r,
Port sourcePort)
const {
213 LLVM_DEBUG(llvm::dbgs() <<
"Switchbox:\n");
214 Block &b = r.front();
215 std::vector<PortMaskValue> portSet;
216 for (
auto connectOp : b.getOps<ConnectOp>()) {
217 if (connectOp.sourcePort() == sourcePort) {
219 portSet.push_back({connectOp.destPort(), maskValue, {connectOp}});
220 LLVM_DEBUG(llvm::dbgs()
221 <<
"To:" << stringifyWireBundle(connectOp.destPort().bundle)
222 <<
" " << connectOp.destPort().channel <<
"\n");
225 for (
auto connectOp : b.getOps<PacketRulesOp>()) {
226 if (connectOp.sourcePort() == sourcePort) {
227 LLVM_DEBUG(llvm::dbgs()
229 << stringifyWireBundle(connectOp.sourcePort().bundle) <<
" "
230 << sourcePort.channel <<
"\n");
232 for (
auto ruleOp : connectOp.getRules().front().getOps<PacketRuleOp>())
233 for (
auto masterSetOp : b.getOps<MasterSetOp>())
234 for (Value amsel : masterSetOp.getAmsels()) {
235 if (ruleOp.getAmsel() == amsel) {
236 LLVM_DEBUG(llvm::dbgs()
238 << stringifyWireBundle(masterSetOp.destPort().bundle)
239 <<
" " << masterSetOp.destPort().channel <<
"\n");
240 MaskValue maskValue = {ruleOp.maskInt(), ruleOp.valueInt()};
242 {masterSetOp.destPort(),
244 {ruleOp, masterSetOp, amsel.getDefiningOp()}});
252 std::vector<PacketConnection> maskSwitchboxConnections(
253 Operation *switchOp,
Port ingressPort, ArrayRef<Via> currentVias,
254 ArrayRef<Operation *> currentUsedOps,
255 const std::vector<PortMaskValue> &nextPortMaskValues,
MaskValue maskValue,
256 bool currentIsPacket,
bool keepPartialFlows,
257 std::vector<PacketConnection> &partialEndpoints)
const {
258 std::vector<PacketConnection> worklist;
261 Operation *takenBy =
nullptr;
265 if (
auto sb = dyn_cast<SwitchboxOp>(switchOp)) {
266 viaTile = sb.getTile();
268 for (
auto &nextPortMaskValue : nextPortMaskValues) {
269 Port nextPort = nextPortMaskValue.port;
270 MaskValue nextMaskValue = nextPortMaskValue.mv;
271 Operation *rule = nextPortMaskValue.ops.front();
272 if (!isa<PacketRuleOp>(rule)) {
275 if (takenBy && rule != takenBy) {
278 int maskConflicts = nextMaskValue.mask & maskValue.mask;
279 LLVM_DEBUG(llvm::dbgs() <<
"Mask: " << maskValue.mask <<
" "
280 << maskValue.value <<
"\n");
281 LLVM_DEBUG(llvm::dbgs() <<
"NextMask: " << nextMaskValue.mask <<
" "
282 << nextMaskValue.value <<
"\n");
283 LLVM_DEBUG(llvm::dbgs() << maskConflicts <<
"\n");
285 if ((maskConflicts & nextMaskValue.value) !=
286 (maskConflicts & maskValue.value)) {
290 if (rule && (nextMaskValue.mask & ~maskValue.mask) == 0) {
293 MaskValue newMaskValue = {maskValue.mask | nextMaskValue.mask,
295 (nextMaskValue.mask & nextMaskValue.value)};
296 bool reachesMerge =
false;
297 int maxPacketId = device.getTargetModel().getMaxPacketId();
298 for (
int id = 0;
id <= maxPacketId; ++id) {
299 if ((
id & newMaskValue.mask) != newMaskValue.value)
301 if (packetMergeEgresses.count({switchOp, encodePort(nextPort), id})) {
307 partialEndpoints.push_back({{switchOp, ingressPort},
309 llvm::to_vector(currentVias),
310 llvm::to_vector(currentUsedOps),
311 currentIsPacket || rule !=
nullptr});
314 SmallVector<Via, 4> newVias(currentVias.begin(), currentVias.end());
316 newVias.push_back({viaTile, ingressPort, nextPort});
318 SmallVector<Operation *, 8> newUsedOps(currentUsedOps.begin(),
319 currentUsedOps.end());
320 newUsedOps.append(nextPortMaskValue.ops.begin(),
321 nextPortMaskValue.ops.end());
322 auto nextConnection = getConnectionThroughWire(switchOp, nextPort);
324 if (!nextConnection) {
328 if (keepPartialFlows) {
329 partialEndpoints.push_back({{switchOp, nextPort},
333 currentIsPacket || rule !=
nullptr});
338 worklist.push_back({*nextConnection, newMaskValue, newVias, newUsedOps,
339 currentIsPacket || rule !=
nullptr});
347 return getConnectionThroughWire(op, port);
354 if (
auto sb = dyn_cast<SwitchboxOp>(op)) {
355 r = &sb.getConnections();
356 }
else if (
auto sm = dyn_cast<ShimMuxOp>(op)) {
357 r = &sm.getConnections();
362 Block &b = r->front();
363 for (
auto connectOp : b.getOps<ConnectOp>()) {
364 if (connectOp.destPort() == port) {
368 for (
auto masterSetOp : b.getOps<MasterSetOp>()) {
369 if (masterSetOp.destPort() == port) {
376 std::vector<PacketConnection>
traverse(std::vector<PacketConnection> worklist,
377 bool keepPartialFlows)
const {
378 std::vector<PacketConnection> connectedTiles;
379 while (!worklist.empty()) {
382 Operation *other = t.portConnection.op;
383 Port otherPort = t.portConnection.port;
387 connectedTiles.push_back(t);
390 Region *connections =
nullptr;
391 if (
auto switchOp = dyn_cast_or_null<SwitchboxOp>(other)) {
392 connections = &switchOp.getConnections();
393 }
else if (
auto switchOp = dyn_cast_or_null<ShimMuxOp>(other)) {
394 connections = &switchOp.getConnections();
397 LLVM_DEBUG(llvm::dbgs()
398 <<
"*** Connection Terminated at unknown operation: ");
399 LLVM_DEBUG(other->dump());
406 fanoutIngresses.count({other, encodePort(otherPort)})) {
407 connectedTiles.push_back(t);
409 getConnectionsThroughSwitchbox(*connections, otherPort)) {
410 if (isa<ConnectOp>(pmv.ops.front())) {
412 return seed.op == other && seed.ingress == otherPort &&
413 seed.egress == pmv.port;
415 if (!llvm::any_of(circuitFanoutSeeds, sameSeed)) {
416 circuitFanoutSeeds.push_back(
417 {other, otherPort, pmv.port, pmv.ops});
420 fanoutEgressSeeds.insert({other, encodePort(pmv.port)});
425 std::vector<PortMaskValue> nextPortMaskValues =
426 getConnectionsThroughSwitchbox(*connections, otherPort);
427 std::vector<PacketConnection> partialEndpoints;
428 std::vector<PacketConnection> newWorkList = maskSwitchboxConnections(
429 other, otherPort, t.vias, t.usedOps, nextPortMaskValues, maskValue,
430 t.isPacket, keepPartialFlows, partialEndpoints);
431 worklist.insert(worklist.end(), newWorkList.begin(), newWorkList.end());
432 connectedTiles.insert(connectedTiles.end(), partialEndpoints.begin(),
433 partialEndpoints.end());
434 if (!nextPortMaskValues.empty() && newWorkList.empty() &&
435 partialEndpoints.empty()) {
438 LLVM_DEBUG(llvm::dbgs() <<
"No rule matched incoming packet here: ");
439 LLVM_DEBUG(other->dump());
442 return connectedTiles;
449 std::vector<PacketConnection>
452 LLVM_DEBUG(llvm::dbgs()
453 <<
"getConnectedTile(" << stringifyWireBundle(port.bundle) <<
" "
454 << port.channel <<
")");
455 LLVM_DEBUG(tileOp.dump());
457 auto t = getConnectionThroughWire(tileOp.getOperation(), port);
466 std::vector<PacketConnection>
468 bool keepPartialFlows,
479 std::vector<PacketConnection>
481 bool keepPartialFlows,
483 auto t = getConnectionThroughWire(switchOp, egressPort);
491 std::vector<PacketConnection>
493 bool keepPartialFlows)
const {
494 auto connection = getConnectionThroughWire(seed.
op, seed.
egress);
498 Value tile = cast<SwitchboxOp>(seed.
op).getTile();
499 SmallVector<Operation *, 8> usedOps(seed.
ops.begin(), seed.
ops.end());
505 return traverse({std::move(branch)}, keepPartialFlows);
513static constexpr int kCircuitFlow = -1;
514using FlowKey = std::tuple<int, int, int, int, int, int, int, int, int, int>;
520static std::optional<FlowKey> tryGetFlowKey(Value srcTileValue,
Port srcPort,
521 Value destTileValue,
Port destPort,
522 int packetID,
int packetMask) {
523 auto coords = [](Value v) -> std::optional<std::pair<int, int>> {
524 auto tile = llvm::dyn_cast_or_null<TileLike>(v.getDefiningOp());
527 std::optional<int> col = tile.tryGetCol();
528 std::optional<int> row = tile.tryGetRow();
531 return std::make_pair(*col, *row);
533 std::optional<std::pair<int, int>> src = coords(srcTileValue);
534 std::optional<std::pair<int, int>> dest = coords(destTileValue);
539 static_cast<int>(srcPort.bundle),
543 static_cast<int>(destPort.bundle),
551static Value resolveEndpointTile(Operation *op) {
552 if (isa<TileOp>(op)) {
553 return op->getResult(0);
555 if (
auto element = dyn_cast<TileElement>(op)) {
556 return element.getTile();
567 llvm::DenseSet<Operation *>
kept;
568 std::vector<std::pair<PacketFlowOp, SmallVector<Operation *, 8>>>
packetFlows;
571static void emitFlows(OpBuilder &rewriter, Location loc, Value srcTile,
572 WireBundle srcBundle,
int srcChannel,
573 const std::vector<PacketConnection> &endpoints,
574 bool emitVias,
bool dropIntraTile,
int idMask,
576 AIEDialect::IsCtrlPktOverlayAttrHelper overlay(rewriter.getContext());
577 AIEDialect::PriorityRouteAttrHelper prioritizedRule(rewriter.getContext());
579 Operation *destOp = c.portConnection.op;
580 Port destPort = c.portConnection.port;
582 Value destTile = resolveEndpointTile(destOp);
589 bool marked = llvm::any_of(c.usedOps, [&](Operation *op) {
590 Operation *rulesParent =
591 isa_and_nonnull<PacketRuleOp>(op) ? op->getParentOp() : nullptr;
592 return (op && overlay.isAttrPresent(op)) ||
593 (rulesParent && overlay.isAttrPresent(rulesParent));
595 if (marked && (srcBundle == WireBundle::TileControl ||
596 destPort.bundle == WireBundle::TileControl)) {
597 for (Operation *op : c.usedOps) {
599 lifted.
kept.insert(op);
606 if (emitVias && c.vias.empty()) {
611 bool isPacket = c.isPacket;
615 std::optional<FlowKey> key =
616 tryGetFlowKey(srcTile, {srcBundle, srcChannel}, destTile, destPort,
617 isPacket ? maskValue.value : kCircuitFlow,
618 isPacket ? maskValue.mask : 0);
619 if (key && !seen.insert(*key).second) {
623 for (Operation *op : c.usedOps) {
634 BoolAttr keepPktHeader, priorityRoute;
635 bool markedDest =
false, markedSource =
false;
636 for (Operation *op : c.usedOps) {
637 if (
auto ms = dyn_cast_or_null<MasterSetOp>(op)) {
638 if (ms.getDestBundle() == destPort.bundle &&
639 ms.getDestChannel() == destPort.channel) {
640 keepPktHeader = ms.getKeepPktHeaderAttr();
641 markedDest = overlay.isAttrPresent(ms);
644 if (isa_and_nonnull<PacketRuleOp>(op) &&
645 prioritizedRule.isAttrPresent(op)) {
649 if (markedDest && markedSource) {
650 priorityRoute = rewriter.getBoolAttr(
true);
660 if (maskValue.mask != idMask) {
661 mask = rewriter.getI8IntegerAttr(maskValue.mask);
663 MLIRContext *ctx = rewriter.getContext();
664 SmallVector<Value> viaTiles;
665 SmallVector<int32_t> ingressBundles, ingressChannels, egressBundles,
668 for (
const Via &via : c.vias) {
669 viaTiles.push_back(via.
tile);
670 ingressBundles.push_back(
static_cast<int32_t
>(via.
ingress.bundle));
671 ingressChannels.push_back(via.
ingress.channel);
672 egressBundles.push_back(
static_cast<int32_t
>(via.
egress.bundle));
673 egressChannels.push_back(via.
egress.channel);
676 auto ib = viaTiles.empty() ? nullptr
677 : DenseI32ArrayAttr::get(ctx, ingressBundles);
678 auto ic = viaTiles.empty() ? nullptr
679 : DenseI32ArrayAttr::get(ctx, ingressChannels);
680 auto eb = viaTiles.empty() ? nullptr
681 : DenseI32ArrayAttr::get(ctx, egressBundles);
682 auto ec = viaTiles.empty() ? nullptr
683 : DenseI32ArrayAttr::get(ctx, egressChannels);
684 auto flowOp = PacketFlowOp::create(
685 rewriter, loc, rewriter.getI8IntegerAttr(maskValue.value), mask,
686 keepPktHeader, priorityRoute, viaTiles, ib, ic, eb, ec);
687 PacketFlowOp::ensureTerminator(flowOp.getPorts(), rewriter, loc);
688 OpBuilder::InsertPoint ip = rewriter.saveInsertionPoint();
689 rewriter.setInsertionPoint(flowOp.getPorts().front().getTerminator());
690 PacketSourceOp::create(rewriter, loc, srcTile, srcBundle, srcChannel);
691 PacketDestOp::create(rewriter, loc, destTile, destPort.bundle,
693 rewriter.restoreInsertionPoint(ip);
694 lifted.
packetFlows.emplace_back(flowOp, c.usedOps);
699 if (c.usedOps.empty()) {
704 if (dropIntraTile && destTile == srcTile && isa<TileOp>(destOp)) {
707 for (Operation *op : c.usedOps) {
712 MLIRContext *ctx = rewriter.getContext();
713 SmallVector<Value> viaTiles;
714 SmallVector<int32_t> ingressBundles, ingressChannels, egressBundles,
717 for (
const Via &via : c.vias) {
718 viaTiles.push_back(via.
tile);
719 ingressBundles.push_back(
static_cast<int32_t
>(via.
ingress.bundle));
720 ingressChannels.push_back(via.
ingress.channel);
721 egressBundles.push_back(
static_cast<int32_t
>(via.
egress.bundle));
722 egressChannels.push_back(via.
egress.channel);
725 auto ib = viaTiles.empty() ? nullptr
726 : DenseI32ArrayAttr::get(ctx, ingressBundles);
727 auto ic = viaTiles.empty() ? nullptr
728 : DenseI32ArrayAttr::get(ctx, ingressChannels);
730 viaTiles.empty() ? nullptr : DenseI32ArrayAttr::get(ctx, egressBundles);
731 auto ec = viaTiles.empty() ? nullptr
732 : DenseI32ArrayAttr::get(ctx, egressChannels);
733 FlowOp::create(rewriter, loc, srcTile, srcBundle, srcChannel, destTile,
734 destPort.bundle, destPort.channel, viaTiles, ib, ic, eb, ec);
743static std::vector<PacketConnection> exactPacketEndpoints(
744 std::vector<PacketConnection> endpoints,
int idMask,
745 llvm::function_ref<std::vector<PacketConnection>(
MaskValue)> trace,
748 if (llvm::none_of(endpoints, isPacket)) {
751 std::vector<PacketConnection> result;
752 llvm::copy_if(endpoints, std::back_inserter(result),
756 if (a.portConnection.op != b.portConnection.op ||
757 a.portConnection.port != b.portConnection.port ||
758 a.vias.size() != b.vias.size()) {
761 return llvm::equal(a.vias, b.vias, [](
const Via &x,
const Via &y) {
762 return x.tile == y.tile && x.ingress == y.ingress && x.egress == y.egress;
766 return a.portConnection.op == b.portConnection.op &&
767 a.portConnection.port == b.portConnection.port;
769 SmallVector<PacketConnection> paths;
772 return emitVias ? samePath(connection, path) : sameDest(connection, path);
774 if (it != paths.end()) {
775 return static_cast<unsigned>(it - paths.begin());
777 paths.push_back(connection);
778 return static_cast<unsigned>(paths.size() - 1);
787 std::map<SmallVector<unsigned>, SmallVector<int>> idsByDests;
788 std::map<std::pair<int, unsigned>, SmallVector<Operation *, 8>> opsTo;
789 for (
int id = 0;
id <= idMask; ++id) {
790 SmallVector<unsigned> reached;
795 unsigned path = pathIndex(c);
796 if (!llvm::is_contained(reached, path)) {
797 reached.push_back(path);
799 SmallVector<Operation *, 8> &ops = opsTo[{id, path}];
800 for (Operation *op : c.usedOps) {
801 if (!llvm::is_contained(ops, op)) {
806 if (!reached.empty()) {
808 idsByDests[reached].push_back(
id);
813 auto disjointCubes = [&](ArrayRef<int> ids) {
814 llvm::SmallBitVector left(idMask + 1);
818 SmallVector<MaskValue> cubes;
820 MaskValue best = {idMask, left.find_first()};
822 for (
int mask = 0; mask <= idMask; ++mask) {
823 int size = 1 << llvm::popcount(
static_cast<unsigned>(idMask & ~mask));
824 if (size <= bestSize) {
827 for (
int value = 0; value <= idMask && size > bestSize; ++value) {
828 if ((value & mask) != value) {
832 for (
int id = value;
id <= idMask && inside; ++id) {
833 inside = (
id & mask) != value || left.test(
id);
836 best = {mask, value};
841 for (
int id = 0;
id <= idMask; ++id) {
842 if ((
id & best.mask) == best.value) {
846 cubes.push_back(best);
852 SmallVector<std::pair<const SmallVector<unsigned> *, ArrayRef<int>>> groups;
853 for (
const auto &[reached, ids] : idsByDests) {
854 groups.push_back({&reached, ids});
856 llvm::sort(groups, [](
const auto &a,
const auto &b) {
857 return a.second.front() < b.second.front();
859 for (
const auto &[reached, ids] : groups) {
860 SmallVector<MaskValue> cubes = disjointCubes(ids);
861 for (
unsigned path : *reached) {
863 SmallVector<Operation *, 8> ops;
865 if ((
id & cube.mask) != cube.value) {
868 for (Operation *op : opsTo[{id, path}]) {
869 if (!llvm::is_contained(ops, op)) {
875 {paths[path].portConnection, cube, paths[path].vias, ops,
true});
883 OpBuilder &rewriter,
bool keepPartialFlows,
886 Operation *Op = op.getOperation();
887 rewriter.setInsertionPoint(Op->getBlock()->getTerminator());
889 std::vector bundles = {WireBundle::Core, WireBundle::DMA};
890 for (WireBundle bundle : bundles) {
891 LLVM_DEBUG(llvm::dbgs()
892 << op << stringifyWireBundle(bundle) <<
" has "
893 << op.getNumSourceConnections(bundle) <<
" Connections\n");
894 for (
size_t i = 0; i < op.getNumSourceConnections(bundle); i++) {
895 Port port = {bundle, (int)i};
896 std::vector<PacketConnection> tiles = exactPacketEndpoints(
899 return analysis.getConnectedTiles(op, port, keepPartialFlows,
903 LLVM_DEBUG(llvm::dbgs() << tiles.size() <<
" Flows\n");
904 emitFlows(rewriter, Op->getLoc(), Op->getResult(0), bundle, (
int)i, tiles,
905 emitVias,
false, idMask, seen, lifted);
916static void findFlowsFromInterconnect(Operation *switchOp,
919 bool keepPartialFlows,
bool emitVias,
922 Region *connections =
nullptr;
923 if (
auto sb = dyn_cast<SwitchboxOp>(switchOp)) {
924 connections = &sb.getConnections();
925 }
else if (
auto sm = dyn_cast<ShimMuxOp>(switchOp)) {
926 connections = &sm.getConnections();
931 rewriter.setInsertionPoint(switchOp->getBlock()->getTerminator());
934 llvm::SmallVector<Port, 8> sourcePorts;
935 auto addPort = [&](
Port p) {
936 if (!llvm::is_contained(sourcePorts, p)) {
937 sourcePorts.push_back(p);
940 Block &b = connections->front();
941 for (
auto connectOp : b.getOps<ConnectOp>()) {
942 addPort(connectOp.sourcePort());
944 for (
auto rulesOp : b.getOps<PacketRulesOp>()) {
945 addPort(rulesOp.sourcePort());
948 for (
Port p : sourcePorts) {
950 WireBundle srcBundle = p.bundle;
951 int srcChannel = p.channel;
952 if (
auto up = analysis.
upstreamOf(switchOp, p)) {
953 Operation *upOp = up->op;
954 Port upPort = up->port;
958 if (upPort.bundle == WireBundle::Core ||
959 upPort.bundle == WireBundle::DMA) {
962 srcTile = resolveEndpointTile(upOp);
963 srcBundle = upPort.bundle;
964 srcChannel = upPort.channel;
965 }
else if (analysis.
drivesPort(upOp, upPort)) {
970 srcTile = resolveEndpointTile(switchOp);
974 srcTile = resolveEndpointTile(switchOp);
979 std::vector<PacketConnection> tiles = exactPacketEndpoints(
983 return analysis.getConnectedTilesFromInput(switchOp, p,
984 keepPartialFlows, claim);
987 emitFlows(rewriter, switchOp->getLoc(), srcTile, srcBundle, srcChannel,
988 tiles, emitVias,
true, idMask, seen, lifted);
995 registry.insert<func::FuncDialect>();
996 registry.insert<AIEDialect>();
1002 Block &b = connections.front();
1003 return b.getOps<ConnectOp>().empty() && b.getOps<PacketRulesOp>().empty() &&
1004 b.getOps<MasterSetOp>().empty() && b.getOps<AMSelOp>().empty();
1009 DeviceOp d = getOperation();
1011 d.getTargetModel().validate();
1018 (1 << llvm::Log2_32_Ceil(d.getTargetModel().getMaxPacketId() + 1)) - 1;
1023 OpBuilder builder = OpBuilder::atBlockTerminator(d.getBody());
1024 for (
auto tile : d.getOps<TileOp>()) {
1025 findFlowsFrom(tile, analysis, builder, clKeepPartialFlows, clEmitVias,
1026 idMask, seen, lifted);
1030 if (clKeepPartialFlows) {
1031 for (
auto switchOp : d.getOps<SwitchboxOp>()) {
1032 findFlowsFromInterconnect(switchOp, analysis, builder,
1033 clKeepPartialFlows, clEmitVias, idMask, seen,
1036 for (
auto shimMuxOp : d.getOps<ShimMuxOp>()) {
1037 findFlowsFromInterconnect(shimMuxOp, analysis, builder,
1038 clKeepPartialFlows, clEmitVias, idMask, seen,
1044 builder.setInsertionPoint(d.getBody()->getTerminator());
1047 Value srcTile = resolveEndpointTile(seed.
op);
1051 std::vector<PacketConnection> tiles =
1053 clKeepPartialFlows);
1054 emitFlows(builder, seed.
op->getLoc(), srcTile, seed.
ingress.bundle,
1055 seed.
ingress.channel, tiles, clEmitVias,
1056 false, idMask, seen, lifted);
1058 llvm::DenseSet<std::pair<Operation *, int>> seeded;
1059 bool progress =
true;
1062 llvm::SmallVector<std::pair<Operation *, int>> snapshot(
1065 for (
auto &seed : snapshot) {
1066 if (!seeded.insert(seed).second) {
1070 Operation *switchOp = seed.first;
1072 Value srcTile = resolveEndpointTile(switchOp);
1076 std::vector<PacketConnection> tiles =
1078 clKeepPartialFlows);
1079 emitFlows(builder, switchOp->getLoc(), srcTile, egress.bundle,
1080 egress.channel, tiles, clEmitVias,
true,
1081 idMask, seen, lifted);
1085 for (
const auto &[switchOp, encodedEgress,
id] :
1088 Value srcTile = resolveEndpointTile(switchOp);
1091 std::vector<PacketConnection> tiles =
1093 switchOp, egress, clKeepPartialFlows, {idMask,
id});
1094 emitFlows(builder, switchOp->getLoc(), srcTile, egress.bundle,
1095 egress.channel, tiles, clEmitVias,
1096 true, idMask, seen, lifted);
1100 if (!clRemoveLifted) {
1110 for (
bool changed =
true; changed;) {
1112 for (
auto &[flow, usedOps] : lifted.
packetFlows) {
1113 if (!flow || llvm::none_of(usedOps, [&](Operation *op) {
1114 return isa_and_nonnull<PacketRuleOp>(op) &&
1115 lifted.
kept.contains(op);
1119 for (Operation *op : usedOps) {
1121 lifted.
kept.insert(op);
1129 for (Operation *op : lifted.
consumed) {
1130 if (isa<ConnectOp, PacketRuleOp, MasterSetOp>(op) &&
1131 !lifted.
kept.contains(op)) {
1135 auto cleanupInterconnect = [](Region &connections) {
1137 llvm::make_early_inc_range(connections.getOps<AMSelOp>())) {
1138 if (amselOp.use_empty()) {
1143 llvm::make_early_inc_range(connections.getOps<PacketRulesOp>())) {
1144 if (rulesOp.getRules().front().getOps<PacketRuleOp>().empty()) {
1149 for (
auto switchOp : d.getOps<SwitchboxOp>()) {
1150 cleanupInterconnect(switchOp.getConnections());
1152 for (
auto shimMuxOp : d.getOps<ShimMuxOp>()) {
1153 cleanupInterconnect(shimMuxOp.getConnections());
1159 llvm::DenseSet<Operation *> retained;
1160 auto retainNonEmpty = [&](Operation *op, Region &connections) {
1162 retained.insert(op);
1165 for (
auto switchOp : d.getOps<SwitchboxOp>()) {
1166 retainNonEmpty(switchOp, switchOp.getConnections());
1168 for (
auto shimMuxOp : d.getOps<ShimMuxOp>()) {
1169 retainNonEmpty(shimMuxOp, shimMuxOp.getConnections());
1172 llvm::DenseSet<Operation *> wiredToRetained;
1173 for (
auto wireOp : llvm::make_early_inc_range(d.getOps<WireOp>())) {
1174 Operation *src = wireOp.getSource().getDefiningOp();
1175 Operation *dst = wireOp.getDest().getDefiningOp();
1176 if (!retained.contains(src) && !retained.contains(dst)) {
1180 wiredToRetained.insert(src);
1181 wiredToRetained.insert(dst);
1186 auto eraseIfUnused = [&](Operation *op, Region &connections) {
1189 for (
auto switchOp : llvm::make_early_inc_range(d.getOps<SwitchboxOp>())) {
1190 if (eraseIfUnused(switchOp, switchOp.getConnections())) {
1194 for (
auto shimMuxOp : llvm::make_early_inc_range(d.getOps<ShimMuxOp>())) {
1195 if (eraseIfUnused(shimMuxOp, shimMuxOp.getConnections())) {
1203 return std::make_unique<AIEFindFlowsPass>();
std::tuple< int, int, int, int, int, int, int, int, int, int > FlowKey
struct PortMaskValue { Port port; MaskValue mv; llvm::SmallVector< Operation *, 3 > ops;} PortMaskValue
struct MaskValue { int mask; int value;} MaskValue
std::set< FlowKey > FlowKeySet
struct PacketConnection { PortConnection portConnection; MaskValue mv; llvm::SmallVector< Via, 4 > vias; llvm::SmallVector< Operation *, 8 > usedOps; bool isPacket=false;} PacketConnection
struct PortConnection { Operation *op; Port port;} PortConnection
std::vector< PacketConnection > getConnectedTilesFromEgress(Operation *switchOp, Port egressPort, bool keepPartialFlows, MaskValue claim={0, 0}) const
std::set< std::tuple< Operation *, int, int > > & getPacketMergeEgresses()
std::optional< PortConnection > upstreamOf(Operation *op, Port port) const
void enableSectionSplitting()
static Port decodePort(int e)
std::vector< PacketConnection > getConnectedTilesFromCircuitFanout(const CircuitFanoutSeed &seed, bool keepPartialFlows) const
std::vector< PacketConnection > getConnectedTilesFromInput(Operation *switchOp, Port inputPort, bool keepPartialFlows, MaskValue claim={0, 0}) const
const llvm::DenseSet< Operation * > & getPacketMergeOps() const
llvm::SmallVector< CircuitFanoutSeed, 8 > & getCircuitFanoutSeeds()
llvm::DenseSet< std::pair< Operation *, int > > & getFanoutEgressSeeds()
bool drivesPort(Operation *op, Port port) const
std::vector< PacketConnection > traverse(std::vector< PacketConnection > worklist, bool keepPartialFlows) const
std::vector< PacketConnection > getConnectedTiles(TileOp tileOp, Port port, bool keepPartialFlows, MaskValue claim={0, 0}) const
ConnectivityAnalysis(DeviceOp &d)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEFindFlowsPass()
struct Port { WireBundle bundle; int channel; bool operator==(const Port &rhs) const { return std::tie(bundle, channel)==std::tie(rhs.bundle, rhs.channel); } bool operator!=(const Port &rhs) const { return !(*this==rhs);} bool operator<(const Port &rhs) const { return std::tie(bundle, channel)< std::tie(rhs.bundle, rhs.channel); } friend std::ostream &operator<<(std::ostream &os, const Port &port) { os<< "("; os<< stringifyWireBundle(port.bundle).str(); os<< ": "<< std::to_string(port.channel)<< ")"; return os; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Port &port) { os<< to_string(port); return os; } } Port
void getDependentDialects(DialectRegistry ®istry) const override
void runOnOperation() override
static bool isEmptyInterconnect(Region &connections)
llvm::SmallVector< Operation *, 3 > ops
llvm::DenseSet< Operation * > kept
std::vector< std::pair< PacketFlowOp, SmallVector< Operation *, 8 > > > packetFlows
llvm::DenseSet< Operation * > consumed