14#include "mlir/Dialect/SCF/IR/SCF.h"
15#include "mlir/Pass/Pass.h"
16#include "llvm/ADT/MapVector.h"
17#include "llvm/ADT/SetVector.h"
18#include "llvm/Support/Debug.h"
29#define DEBUG_TYPE "aie-objectfifo-verify"
32#define GEN_PASS_DEF_AIEOBJECTFIFOVERIFY
33#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
39 SmallVector<Operation *> fillers;
40 SmallVector<Operation *> drainers;
45struct TransferBudget {
46 ObjectFifoDmaEndpointOp endpoint;
56static constexpr int64_t kRunsForever = (int64_t{1} << 24) - 2;
58static std::optional<int64_t> getStaticTripCount(scf::ForOp forOp) {
59 if (forOp.getSingleLowerBound() && forOp.getSingleUpperBound() &&
60 forOp.getSingleStep()) {
61 if (std::optional<llvm::APInt> tc = forOp.getStaticTripCount()) {
62 return tc->getSExtValue();
72static std::optional<int64_t> staticExecutions(Operation *op) {
74 for (Operation *cur = op; !isa<CoreOp, RuntimeSequenceOp>(cur);
75 cur = cur->getParentOp()) {
76 if (cur->getBlock() != &cur->getParentRegion()->front()) {
79 Operation *parent = cur->getParentOp();
80 if (isa<CoreOp, RuntimeSequenceOp>(parent)) {
83 auto forOp = dyn_cast<scf::ForOp>(parent);
87 std::optional<int64_t> trip = getStaticTripCount(forOp);
88 if (!trip || *trip >= kRunsForever ||
89 (*trip != 0 && count > std::numeric_limits<int64_t>::max() / *trip)) {
101static std::optional<int64_t> heldAfter(Block &block, StringRef name,
103 for (Operation &op : block) {
104 if (
auto acquire = dyn_cast<ObjectFifoAcquireOp>(op)) {
105 if (acquire.getObjFifoName() == name)
106 held = std::max<int64_t>(held, acquire.acqNumber());
109 if (
auto release = dyn_cast<ObjectFifoReleaseOp>(op)) {
110 if (release.getObjFifoName() == name)
111 held -= release.relNumber();
116 bool touches = op.getNumRegions() != 0 &&
117 op.walk([&](Operation *inner) {
118 auto acquire = dyn_cast<ObjectFifoAcquireOp>(inner);
119 auto release = dyn_cast<ObjectFifoReleaseOp>(inner);
120 if ((acquire && acquire.getObjFifoName() == name) ||
121 (release && release.getObjFifoName() == name))
122 return WalkResult::interrupt();
123 return WalkResult::advance();
127 auto forOp = dyn_cast<scf::ForOp>(op);
128 std::optional<int64_t> trip =
129 forOp ? getStaticTripCount(forOp) : std::nullopt;
134 std::optional<int64_t> once = heldAfter(*forOp.getBody(), name, held);
137 if (*trip > 1 && heldAfter(*forOp.getBody(), name, *once) != once)
144struct AIEObjectFifoVerifyPass
145 :
public xilinx::AIE::impl::AIEObjectFifoVerifyBase<
146 AIEObjectFifoVerifyPass> {
150 std::shared_ptr<PoolLocks> poolLocks;
156 LogicalResult verifyActors(ObjectFifoPoolOp pool,
157 ArrayRef<SegmentActors> actors) {
158 bool external = llvm::any_of(pool.getBufferOps(), [](BufferLike buffer) {
159 return isa<ExternalBufferOp>(buffer.getOperation());
161 std::vector<ObjectFifoSegmentOp> segmentOps = pool.getSegmentOps();
162 for (
auto [index, segment] : llvm::enumerate(actors)) {
163 ObjectFifoSegmentOp segmentOp = segmentOps[index];
164 bool manualFill = segmentOp.getManualFill();
165 bool manualDrain = segmentOp.getManualDrain();
166 if (segment.fillers.size() > 1 ||
167 (manualFill && !segment.fillers.empty())) {
168 return pool.emitOpError(
"segment ")
169 << index <<
" is filled by more than one endpoint";
171 if (segment.drainers.size() > 1 ||
172 (manualDrain && !segment.drainers.empty())) {
173 return pool.emitOpError(
"segment ")
174 << index <<
" is drained by more than one endpoint";
178 bool hostDrains = external && !segment.fillers.empty();
179 bool hostFills = external && !segment.drainers.empty();
180 if (segment.drainers.empty() && !hostDrains && !manualDrain) {
181 return pool.emitOpError(
"segment ") << index <<
" has no drainer";
183 if (segment.fillers.empty() && !pool.getInitValues() && !hostFills &&
185 return pool.emitOpError(
"segment ") << index <<
" has no filler";
192 LogicalResult verifyFlows(DeviceOp device,
193 SymbolTableCollection &symbolTables) {
194 DenseMap<StringRef, int> appearances;
195 for (
auto flow : device.getOps<RouteOp>()) {
196 for (StringRef source : flow.getSourceNames()) {
197 appearances[source]++;
199 for (StringRef dest : flow.getDestinationNames()) {
204 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
206 appearances.lookup(cast<SymbolOpInterface>(*endpoint).getName());
208 return endpoint->emitOpError(
"is not connected by any flow");
210 if (
auto dma = dyn_cast<ObjectFifoDmaEndpointOp>(*endpoint);
211 dma && dma.getDispatch()) {
212 if (failed(verifyDispatch(device, dma, symbolTables)))
217 if (count > 1 && !isa<RouteEndpointOp>(endpoint.getOperation())) {
218 return endpoint->emitOpError(
219 "drives one channel, so at most one flow may name it, but "
221 << count <<
" times";
230 LogicalResult verifyDispatch(DeviceOp device,
231 ObjectFifoDmaEndpointOp endpoint,
232 SymbolTableCollection &symbolTables) {
233 StringRef name = endpoint.getSymName();
234 llvm::SmallSetVector<StringRef, 4> turns;
235 for (StringRef dest :
236 endpoint.getDispatchAttr().getAsValueRange<FlatSymbolRefAttr>())
238 llvm::SmallDenseSet<StringRef, 4> reached;
239 for (
auto flow : device.getOps<RouteOp>()) {
240 if (flow.namesAsDestination(name))
241 return flow.emitOpError(
"names dispatching endpoint @")
242 << name <<
" as a destination";
243 if (!flow.namesAsSource(name))
245 if (flow.getSources().size() != 1 || flow.getDestinations().size() != 1)
246 return flow.emitOpError(
"leaves dispatching endpoint @")
248 <<
" for one destination, so it has one source and "
250 if (!flow.getPacket())
251 return flow.emitOpError(
"leaves dispatching endpoint @")
252 << name <<
" and needs a packet header to tell its turns apart";
253 StringRef dest = *flow.getDestinationNames().begin();
254 if (!turns.contains(dest))
255 return flow.emitOpError(
"reaches @")
256 << dest <<
", which dispatching endpoint @" << name
257 <<
" never takes a turn for";
258 reached.insert(dest);
260 for (StringRef dest : turns)
261 if (!reached.contains(dest))
262 return endpoint.emitOpError(
"dispatches to @")
263 << dest <<
", but no route from it reaches there";
268 int64_t elements = transferElements(endpoint);
269 for (StringRef dest : turns) {
270 auto destEndpoint = symbolTables.lookupSymbolIn<ObjectFifoDmaEndpointOp>(
271 device, StringAttr::get(device.getContext(), dest));
274 int64_t destElements = transferElements(destEndpoint);
275 if (destElements != elements)
276 return endpoint.emitOpError(
"dispatches, so every turn moves ")
277 << elements <<
" elements, but @" << dest <<
" moves "
285 static int64_t transferElements(ObjectFifoDmaEndpointOp endpoint) {
286 int64_t elements = 0;
287 std::optional<ArrayRef<BDDimLayoutArrayAttr>> dimensions =
288 endpoint.getDimensions();
289 for (
auto [index, segment] :
290 llvm::enumerate(endpoint.getSelectedSegments())) {
291 BDDimLayoutArrayAttr dims;
292 if (dimensions && index < dimensions->size()) {
293 dims = (*dimensions)[index];
295 if (dims && !dims.empty()) {
297 for (BDDimLayoutAttr dim : dims) {
298 product *= dim.getSize();
302 elements += segment.getSize();
312 LogicalResult verifyFanIn(DeviceOp device,
313 SymbolTableCollection &symbolTables) {
314 for (
auto flow : device.getOps<RouteOp>()) {
315 if (!flow.isFanIn()) {
318 std::optional<std::pair<StringRef, int64_t>> first;
319 auto check = [&](StringRef name) -> LogicalResult {
320 auto endpoint = symbolTables.lookupSymbolIn<ObjectFifoDmaEndpointOp>(
321 device, StringAttr::get(device.getContext(), name));
325 int64_t elements = transferElements(endpoint);
327 first = {name, elements};
330 if (elements != first->second) {
331 return flow.emitOpError(
"fans in, so every end moves one object of "
332 "the same size, but @")
333 << first->first <<
" moves " << first->second
334 <<
" elements and @" << name <<
" moves " << elements;
338 for (StringRef name : flow.getSourceNames()) {
339 if (failed(check(name))) {
343 for (StringRef name : flow.getDestinationNames()) {
344 if (failed(check(name))) {
357 LogicalResult verifyOverRelease(DeviceOp device) {
358 for (
auto coreOp : device.getOps<CoreOp>()) {
359 WalkResult result = coreOp.walk([&](Operation *loop) {
361 return WalkResult::advance();
363 auto directlyIn = [&](Operation *op) {
366 DenseMap<StringRef, int64_t> acquired;
367 DenseMap<StringRef, int64_t> released;
368 DenseMap<StringRef, Operation *> blame;
369 DenseSet<StringRef> spansNestedLoop;
371 loop->walk([&](ObjectFifoAcquireOp a) {
372 StringRef key = a.getObjFifoName();
373 if (!directlyIn(a)) {
374 spansNestedLoop.insert(key);
376 acquired[key] += a.acqNumber();
377 blame.try_emplace(key, a);
380 loop->walk([&](ObjectFifoReleaseOp r) {
381 StringRef key = r.getObjFifoName();
382 if (!directlyIn(r)) {
383 spansNestedLoop.insert(key);
385 released[key] += r.relNumber();
386 blame.try_emplace(key, r);
390 for (
auto &[key, count] : released) {
391 if (spansNestedLoop.contains(key) || count <= acquired.lookup(key)) {
394 blame.lookup(key)->emitOpError(
395 "cannot release more elements than are already acquired");
396 return WalkResult::interrupt();
398 return WalkResult::advance();
400 if (result.wasInterrupted()) {
416 LogicalResult verifyTransferBudgets(DeviceOp device) {
417 DenseMap<Operation *, TransferBudget> budgets;
418 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>()) {
419 std::optional<int32_t> iterations = endpoint.getIterCount();
420 ObjectFifoPoolOp pool = endpoint.getPoolOp();
421 if (!iterations || !pool) {
424 budgets[pool] = {endpoint, *iterations,
425 int64_t{*iterations} * pool.getDepth()};
427 if (budgets.empty()) {
431 DenseMap<StringRef, int64_t> released;
432 DenseMap<StringRef, ObjectFifoReleaseOp> blame;
433 DenseSet<StringRef> uncounted;
434 device.walk([&](ObjectFifoReleaseOp release) {
435 StringRef key = release.getObjFifoName();
436 std::optional<int64_t> runs = staticExecutions(release);
438 uncounted.insert(key);
441 released[key] += *runs * release.relNumber();
442 blame.try_emplace(key, release);
444 DenseSet<StringRef> acquired;
445 device.walk([&](ObjectFifoAcquireOp acquire) {
446 acquired.insert(acquire.getObjFifoName());
449 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
451 auto budget = budgets.find(endpoint.getPoolOp());
452 if (budget == budgets.end() || endpoint.getSegments()) {
455 StringRef name = endpoint.getSymName();
456 int64_t count = released.lookup(name);
457 ObjectFifoDmaEndpointOp dma = budget->second.endpoint;
458 ObjectFifoPoolOp pool = endpoint.getPoolOp();
459 int64_t depth = pool.getDepth();
460 if (count > budget->second.objects) {
461 InFlightDiagnostic diag =
462 blame.lookup(name).emitOpError(
"releases ")
463 << count <<
" objects through @" << name
464 <<
" over the run, but the pool's DMA endpoint stops after "
465 << budget->second.objects <<
" (iterCount "
466 << budget->second.iterations <<
" x depth " << depth <<
")";
468 if (!endpoint.drains() && count <= budget->second.objects + depth)
469 diag <<
", leaving " << count - budget->second.objects
470 <<
" in the pool unsent";
472 diag <<
", so the core would wait forever for the rest";
473 diag.attachNote(dma.getLoc()) <<
"the DMA endpoint is here";
477 if (!acquired.contains(name) || uncounted.contains(name) ||
478 dma.getSegments() || pool.getInitValues() ||
479 dma.drains() == endpoint.drains() || !poolLocks->needsLocks(pool)) {
482 int64_t reachable = endpoint.drains() ? count + depth : count;
483 if (budget->second.objects <= reachable) {
486 InFlightDiagnostic diag =
487 dma.emitOpError(dma.drains() ?
"drains " :
"fills ")
488 << budget->second.objects <<
" objects (iterCount "
489 << budget->second.iterations <<
" x depth " << depth
490 <<
") before it stops, but the core releases exactly " << count
491 <<
" through @" << name;
492 if (endpoint.drains())
493 diag <<
", leaving room for only " << reachable;
494 diag <<
", so the DMA endpoint would wait forever for the rest";
495 diag.attachNote(endpoint.getLoc()) <<
"the core endpoint is here";
505 LogicalResult verifyHeldAtEnd(DeviceOp device) {
506 for (
auto core : device.getOps<CoreOp>()) {
507 Region &body = core.getBody();
508 if (!body.hasOneBlock())
511 core.walk([](Operation *op) {
512 auto forOp = dyn_cast<scf::ForOp>(op);
513 std::optional<int64_t> trip =
514 forOp ? getStaticTripCount(forOp) : std::nullopt;
515 if (isa<scf::WhileOp>(op) || (trip && *trip >= kRunsForever))
516 return WalkResult::interrupt();
517 return WalkResult::advance();
522 llvm::MapVector<StringRef, ObjectFifoAcquireOp> firstAcquire;
523 core.walk([&](ObjectFifoAcquireOp acquire) {
524 firstAcquire.insert({acquire.getObjFifoName(), acquire});
526 for (
auto &[name, acquire] : firstAcquire) {
528 SymbolTable::lookupNearestSymbolFrom<ObjectFifoCoreEndpointOp>(
529 device, acquire.getObjFifoNameAttr());
530 if (endpoint && endpoint.getPoolOp() &&
531 !poolLocks->needsLocks(endpoint.getPoolOp()))
533 std::optional<int64_t> held = heldAfter(body.front(), name, 0);
534 if (!held || *held == 0)
536 InFlightDiagnostic diag =
537 core.emitOpError(
"ends with ")
538 << *held <<
" acquired from @" << name
539 <<
" never released, so their locks stay taken into the next run "
540 "of this configuration";
541 diag.attachNote(acquire.getLoc()) <<
"acquired here";
555 LogicalResult verifyAdvances(DeviceOp device) {
556 auto repeatsForever = [](Operation *loop) {
557 for (Operation *op = loop; op && !isa<CoreOp>(op);
558 op = op->getParentOp()) {
559 auto whileOp = dyn_cast<scf::WhileOp>(op);
560 auto forOp = dyn_cast<scf::ForOp>(op);
561 std::optional<int64_t> trip =
562 forOp ? getStaticTripCount(forOp) : std::nullopt;
564 (trip && *trip >= kRunsForever))
570 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
571 ObjectFifoPoolOp pool = endpoint.getPoolOp();
572 if (!pool || pool.getInitValues() || endpoint.getSegments() ||
573 !poolLocks->needsLocks(pool))
575 Operation *keepsMoving =
nullptr;
576 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>())
577 if (dma.getPoolOp() == pool && dma.drains() != endpoint.drains() &&
578 !dma.getIterCount() && !dma.getSegments())
580 for (
auto other : device.getOps<ObjectFifoCoreEndpointOp>()) {
581 if (keepsMoving || other.getPoolOp() != pool ||
582 other.drains() == endpoint.drains() || other.getSegments())
584 CoreTurns otherTurns;
585 std::string otherReason;
586 if (succeeded(otherTurns.
read({other}, otherReason)) &&
595 if (succeeded(turns.
read({endpoint}, reason)) || !turns.
movesNone() ||
599 dyn_cast_or_null<ObjectFifoAcquireOp>(turns.
periodAcquire());
602 InFlightDiagnostic diag =
603 acquire.emitOpError(
"takes from @")
604 << endpoint.getSymName()
605 <<
" each time its loop repeats, but the loop releases none of "
607 if (endpoint.drains())
608 diag <<
"the core keeps the same objects forever and the end "
609 "filling the pool stops once it is full";
611 diag <<
"nothing the core writes there is ever sent";
612 diag.attachNote(keepsMoving->getLoc()) <<
"the other end is here";
621 static std::optional<int64_t> hostBytes(RuntimeSequenceOp sequence,
623 std::optional<SymbolTable::UseRange> uses =
624 SymbolTable::getSymbolUses(name, &sequence.getBody());
628 for (
const SymbolTable::SymbolUse &use : *uses) {
629 auto task = dyn_cast<AIEX::DMAConfigureTaskForOp>(use.getUser());
630 std::optional<int64_t> runs =
631 task ? staticExecutions(task) : std::nullopt;
632 if (!runs || task.getRepeatCountVal())
635 WalkResult result = task.getBody().walk([&](DMABDOp bd) {
636 if (bd.getLengthParameter() || !bd.getConstantLen())
637 return WalkResult::interrupt();
638 perRun += bd.getLenInBytes();
639 return WalkResult::advance();
641 if (result.wasInterrupted())
643 bytes += *runs * (task.getRepeatCount() + 1) * perRun;
649 static int64_t objectBytes(ObjectFifoDmaEndpointOp endpoint) {
650 auto type = cast<MemRefType>(endpoint.getPoolOp().getElemType());
651 return transferElements(endpoint) * type.getElementTypeBitWidth() / 8;
657 std::optional<int64_t> hostObjects(DeviceOp device,
658 RuntimeSequenceOp sequence,
659 ObjectFifoDmaEndpointOp endpoint,
661 if (endpoint.getIterCount() || endpoint.getDispatch())
663 StringRef name = endpoint.getSymName();
664 SmallVector<StringRef> peers;
665 for (
auto route : device.getOps<RouteOp>()) {
666 if (endpoint.drains() ? route.namesAsSource(name)
667 : route.namesAsDestination(name)) {
668 auto names = endpoint.drains() ? route.getDestinationNames()
669 : route.getSourceNames();
670 peers.append(names.begin(), names.end());
673 if (peers.size() != 1)
675 Operation *peer = SymbolTable::lookupNearestSymbolFrom(
676 device, StringAttr::get(device.getContext(), peers.front()));
678 int64_t sent = objectBytes(endpoint) * endpoint.getRepeat();
679 int64_t perObject = endpoint.drains() ? sent : objectBytes(endpoint);
682 if (
auto shim = dyn_cast_or_null<RouteEndpointOp>(peer)) {
683 std::optional<int64_t> host = hostBytes(sequence, shim.getSymNameAttr());
687 }
else if (
auto other = dyn_cast_or_null<ObjectFifoDmaEndpointOp>(peer);
688 other && throughPool && other.drains() != endpoint.drains()) {
689 ObjectFifoDmaEndpointOp toward;
690 for (
auto candidate : device.getOps<ObjectFifoDmaEndpointOp>()) {
691 if (candidate == other || candidate.getPoolOp() != other.getPoolOp() ||
692 candidate.drains() == other.drains())
700 std::optional<int64_t> objects =
701 hostObjects(device, sequence, toward,
false);
705 *objects * (other.drains() ? objectBytes(other) * other.getRepeat()
706 : objectBytes(other));
710 if (perObject == 0 || bytes % perObject)
712 return bytes / perObject;
721 LogicalResult verifyDispatchVolumes(DeviceOp device) {
722 RuntimeSequenceOp sequence;
724 for (
auto candidate : device.getOps<RuntimeSequenceOp>()) {
725 auto ops = candidate.getBody()
726 .getOps<AIEX::SyncScratchpadParametersFromHostOp>();
727 int64_t count = std::distance(ops.begin(), ops.end());
731 BoolAttr preamble = candidate.getEmitParameterSyncPreambleAttr();
732 count = !preamble || preamble.getValue();
738 sequence = candidate;
744 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
745 ObjectFifoPoolOp pool = endpoint.getPoolOp();
746 if (!pool || pool.getInitValues() || endpoint.getSegments() ||
747 !poolLocks->needsLocks(pool))
749 ObjectFifoDmaEndpointOp dma;
751 for (
auto candidate : device.getOps<ObjectFifoDmaEndpointOp>()) {
752 if (candidate.getPoolOp() != pool ||
753 candidate.drains() == endpoint.drains())
758 if (!dma || !single || dma.getSegments())
763 if (failed(turns.
read({endpoint}, reason)))
765 auto loop = dyn_cast<scf::WhileOp>(turns.
periodLoop());
768 ->getOps<AIEX::WaitScratchpadParametersOp>()
771 std::optional<int64_t> objects = hostObjects(device, sequence, dma,
true);
772 int64_t perDispatch = syncs *
static_cast<int64_t
>(turns.
turns().size());
773 LLVM_DEBUG(llvm::dbgs()
774 <<
"dispatch volume of @" << endpoint.getSymName()
775 <<
": the core moves " << perDispatch <<
", @"
776 << sequence.getSymName() <<
" moves "
777 << (objects ? std::to_string(*objects) :
"an unknown count")
779 if (!objects || *objects == perDispatch)
782 ObjectFifoAcquireOp acquire;
783 loop.walk([&](ObjectFifoAcquireOp op) {
784 if (!acquire && op.getObjFifoName() == endpoint.getSymName())
789 InFlightDiagnostic diag =
790 acquire.emitOpError()
791 << (endpoint.drains() ?
"takes " :
"hands over ") << perDispatch
792 <<
" objects through @" << endpoint.getSymName()
793 <<
" for each dispatch, one pass of a loop that waits for each "
794 "dispatch's parameters";
796 diag <<
" (synced " << syncs <<
" times per run)";
797 diag <<
", but each run of runtime sequence @" << sequence.getSymName()
798 <<
" moves " << *objects
799 <<
", so the core drifts out of step with the dispatches";
800 diag.attachNote(sequence.getLoc()) <<
"the runtime sequence is here";
810 LogicalResult verifyOutOfOrderPools(DeviceOp device) {
811 auto lookup = [&](FlatSymbolRefAttr name) {
812 return dyn_cast_or_null<ObjectFifoDmaEndpointOp>(
813 SymbolTable::lookupNearestSymbolFrom(device, name.getAttr()));
815 auto objectBytes = [](ObjectFifoPoolOp pool) -> int64_t {
816 auto type = dyn_cast<MemRefType>(pool.getElemType());
819 return (type.getNumElements() * type.getElementTypeBitWidth() + 7) / 8;
821 for (
auto flow : device.getOps<RouteOp>()) {
822 if (flow.getSources().size() != 1)
824 ObjectFifoDmaEndpointOp src =
825 lookup(cast<FlatSymbolRefAttr>(flow.getSources()[0]));
826 ObjectFifoDmaEndpointOp firstReceiver;
828 flow.getDestinations().getAsRange<FlatSymbolRefAttr>()) {
829 ObjectFifoDmaEndpointOp dst = lookup(destName);
830 if (!dst || !dst.getOutOfOrder())
834 if (firstReceiver.getNumDescriptors() != dst.getNumDescriptors() ||
835 objectBytes(firstReceiver.getPoolOp()) !=
836 objectBytes(dst.getPoolOp()))
837 return flow.emitOpError(
838 "out-of-order broadcast destinations require matching buffer "
839 "counts and object sizes");
840 if (!src || !src.getOutOfOrder())
842 ObjectFifoPoolOp from = src.getPoolOp(), to = dst.getPoolOp();
845 if (src.getNumDescriptors() != dst.getNumDescriptors() ||
846 objectBytes(from) != objectBytes(to))
847 return src.emitOpError(
"sends out of order from ")
848 << src.getNumDescriptors() <<
" buffers of "
849 << objectBytes(from) <<
" bytes into "
850 << dst.getNumDescriptors() <<
" of " << objectBytes(to)
851 <<
"; an out-of-order fifo needs the same buffers at both "
852 "ends, because one descriptor names one whole buffer";
858 void runOnOperation()
override {
859 DeviceOp device = getOperation();
860 poolLocks = std::make_shared<PoolLocks>(device);
862 DenseMap<Operation *, SmallVector<SegmentActors>> actorsPerPool;
863 for (
auto pool : device.getOps<ObjectFifoPoolOp>()) {
864 actorsPerPool[pool].resize(pool.getSegmentOps().size());
867 auto record = [&](
auto endpoint) {
868 ObjectFifoPoolOp pool = endpoint.getPoolOp();
872 auto &actors = actorsPerPool[pool];
873 std::vector<ObjectFifoSegmentOp> all = pool.getSegmentOps();
874 for (ObjectFifoSegmentOp segment : endpoint.getSelectedSegments()) {
875 size_t index = llvm::find(all, segment) - all.begin();
876 (endpoint.drains() ? actors[index].drainers : actors[index].fillers)
877 .push_back(endpoint);
881 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
884 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>()) {
888 for (
auto pool : device.getOps<ObjectFifoPoolOp>()) {
889 if (failed(verifyActors(pool, actorsPerPool[pool]))) {
890 return signalPassFailure();
894 SymbolTableCollection symbolTables;
895 if (failed(verifyFlows(device, symbolTables)) ||
896 failed(verifyFanIn(device, symbolTables)) ||
897 failed(verifyOverRelease(device)) ||
898 failed(verifyTransferBudgets(device)) ||
899 failed(verifyHeldAtEnd(device)) || failed(verifyAdvances(device)) ||
900 failed(verifyDispatchVolumes(device)) ||
901 failed(verifyOutOfOrderPools(device))) {
902 return signalPassFailure();
909std::unique_ptr<OperationPass<DeviceOp>>
911 return std::make_unique<AIEObjectFifoVerifyPass>();
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 movesNone() const
Whether read found a steady state in which the period moves none of the objects: the core keeps what ...
mlir::Operation * periodAcquire() const
The first acquire the period runs, with its branches folded; null when it runs none.
llvm::ArrayRef< unsigned > turns() const
Indices into ends, one per object the channel moves in a period of the core's steady state.
mlir::Operation * periodLoop() const
The loop read took as the core's period, once it found one.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoVerifyPass()
bool isForeverLoop(mlir::scf::WhileOp whileOp)
Whether whileOp repeats forever: its condition is a constant true, and its condition region does noth...
mlir::Operation * getObjectFifoLoopParent(mlir::Operation *op)
The innermost loop around op that isObjectFifoLoop accepts, or null.
bool isObjectFifoLoop(mlir::Operation *op)
Whether op is a loop that objectFifo lowering unrolls by the rotation period of the objectFifos acces...