11#include "mlir/IR/Attributes.h"
12#include "mlir/Pass/Pass.h"
13#include "llvm/Support/MathExtras.h"
22#define GEN_PASS_DEF_AIEOBJECTFIFOLOWERDMAS
23#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
34 BDDimLayoutArrayAttr dimensions;
35 BDPadLayoutArrayAttr padding;
36 FlatSymbolRefAttr acquireLock;
37 FlatSymbolRefAttr releaseLock;
42 std::optional<int64_t> acquireValue = std::nullopt;
43 std::optional<int64_t> releaseValue = std::nullopt;
46 BDIterationAttr iteration =
nullptr;
49Block *findEndOpBlock(Region ®ion) {
50 Block *endBlock =
nullptr;
51 for (Block &block : region) {
52 if (!block.getOps<EndOp>().empty()) {
59struct AIEObjectFifoLowerDMAsPass
60 :
public xilinx::AIE::impl::AIEObjectFifoLowerDMAsBase<
61 AIEObjectFifoLowerDMAsPass> {
64 int32_t nextSendTag = 0;
65 llvm::DenseSet<int32_t> usedSendTags;
66 OpBuilder builder{
static_cast<MLIRContext *
>(
nullptr)};
70 DmaBody dmaProgramFor(TileLike tile, Location loc) {
71 Value wanted = tile->getResult(0);
72 for (
auto program : device.getOps<DmaBody>()) {
73 if (program.getTile() == wanted) {
75 FusedLoc::get(loc.getContext(), {program->getLoc(), loc}));
80 OpBuilder::InsertionGuard g(builder);
81 builder.setInsertionPoint(device.getBody()->getTerminator());
83 if (tile.isShimTile()) {
84 program = ShimDMAOp::create(builder, loc, builder.getIndexType(), wanted);
85 }
else if (tile.isMemTile()) {
86 program = MemTileDMAOp::create(builder, loc, wanted);
88 program = MemOp::create(builder, loc, wanted);
91 builder.setInsertionPointToStart(&program.getDmaBody().emplaceBlock());
92 EndOp::create(builder, loc);
97 SmallVector<Value> buffersOf(ObjectFifoPoolOp pool) {
98 SmallVector<Value> buffers;
99 for (BufferLike buffer : pool.getBufferOps()) {
100 buffers.push_back(buffer.getBuffer());
109 SmallVector<Descriptor>
110 contiguousDescriptors(ObjectFifoDmaEndpointOp endpoint, Value buffer,
112 ObjectFifoPoolOp pool = endpoint.getPoolOp();
113 ObjectFifoSegmentOp segment = endpoint.getSelectedSegments().front();
114 FlatSymbolRefAttr take =
115 drains ? segment.getConsumeLockAttr() : segment.getProduceLockAttr();
116 FlatSymbolRefAttr give =
117 drains ? segment.getProduceLockAttr() : segment.getConsumeLockAttr();
118 MLIRContext *context = builder.getContext();
119 if (!endpoint.walksStream()) {
120 BDDimLayoutArrayAttr dims;
121 if (
auto all = endpoint.getDimensions(); all && !all->empty())
123 int64_t
object = pool.getObjectSize();
125 if (
auto zeros = pool.getZeroObjects())
126 first = (*zeros)[0] * object;
127 int64_t walkers = endpoint.getNumDescriptors();
128 SmallVector<Descriptor> descriptors;
129 for (int64_t j = 0; j < walkers; j++)
130 descriptors.push_back(
131 {buffer, first + j * object + segment.getOffset(),
132 segment.getSize(), dims, BDPadLayoutArrayAttr(), take, give, j,
133 std::nullopt, std::nullopt,
134 BDIterationAttr::get(context, pool.getDepth() / walkers,
135 walkers * object, 0)});
140 if (failed(endpoint.getStreamWindow(window)))
142 BDDimLayoutArrayAttr dims =
143 BDDimLayoutArrayAttr::get(context, window.
inner);
144 SmallVector<Descriptor> descriptors;
146 descriptors.push_back({buffer, step.first * window.
objectSize,
147 window.
transfer(), dims, BDPadLayoutArrayAttr(),
148 take, give, 0, step.acquire, step.release,
155 SmallVector<Descriptor> descriptorsFor(ObjectFifoDmaEndpointOp endpoint,
156 ArrayRef<Value> buffers,
bool drains) {
157 if (endpoint.getPoolOp().getContiguous())
158 return contiguousDescriptors(endpoint, buffers.front(), drains);
159 std::vector<ObjectFifoSegmentOp> segments = endpoint.getSelectedSegments();
160 SmallVector<BDDimLayoutArrayAttr> dimensions;
161 if (
auto allDimensions = endpoint.getDimensions()) {
162 llvm::append_range(dimensions, *allDimensions);
164 SmallVector<BDPadLayoutArrayAttr> padding;
165 if (
auto allPadding = endpoint.getPadDimensions()) {
166 llvm::append_range(padding, *allPadding);
169 SmallVector<Descriptor> descriptors;
170 for (
auto [index, buffer] : llvm::enumerate(buffers)) {
171 for (
auto [position, segment] : llvm::enumerate(segments)) {
172 descriptors.push_back(
173 {buffer, segment.getOffset(), segment.getSize(),
174 position < dimensions.size() ? dimensions[position]
175 : BDDimLayoutArrayAttr(),
176 position < padding.size() ? padding[position]
177 : BDPadLayoutArrayAttr(),
178 drains ? segment.getConsumeLockAttr()
179 : segment.getProduceLockAttr(),
180 drains ? segment.getProduceLockAttr()
181 : segment.getConsumeLockAttr(),
182 static_cast<int64_t
>(index)});
188 void emitDescriptor(ObjectFifoDmaEndpointOp endpoint, ObjectFifoPoolOp pool,
189 Descriptor &descriptor, PacketInfoAttr packet,
190 Block *successor,
bool binaryLocks,
bool drains,
191 int64_t transmittedObject = -1,
192 ArrayRef<int32_t> sendTags = {}) {
193 Location loc = endpoint.getLoc();
194 int count = drains ? 1 : pool.getRepeatCount().value_or(1);
196 LockOp acquireLock, releaseLock;
197 int acquireValue = count, releaseValue = count;
198 auto action = LockAction::AcquireGreaterEqual;
201 SmallVector<LockOp> locks = pool.getLockOps();
202 if (descriptor.object < (int64_t)locks.size()) {
203 acquireLock = releaseLock = locks[descriptor.object];
204 acquireValue = drains ? 1 : 0;
205 releaseValue = drains ? 0 : 1;
206 action = LockAction::Acquire;
208 }
else if (descriptor.acquireLock) {
215 if (descriptor.acquireValue)
216 acquireValue = *descriptor.acquireValue;
217 if (descriptor.releaseValue)
218 releaseValue = *descriptor.releaseValue;
220 UseLockOp::create(builder, loc, acquireLock, action, acquireValue);
223 DMABDPACKETOp::create(builder, loc, packet.getPktType(),
224 packet.assignedId());
228 if (descriptor.dimensions && drains && descriptor.padding) {
229 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
230 paddedSize(descriptor.dimensions, descriptor.padding,
232 descriptor.dimensions, descriptor.padding);
233 }
else if (descriptor.dimensions) {
234 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
235 descriptor.size, descriptor.dimensions);
237 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
240 if (endpoint.getOutOfOrder()) {
242 drains ? endpoint.getOutOfOrderTags() : pool.getOutOfOrderTags();
244 assert(descriptor.object <
static_cast<int64_t
>(tags->size()) &&
245 "the allocator names every descriptor of an out-of-order pool");
246 int64_t slot = transmittedObject >= 0 && drains
247 ? transmittedObject % tags->size()
249 int32_t tag = (*tags)[slot];
250 if (endpoint.getRouteDirection() == DMAChannelDir::MM2S)
251 bd.setOutOfOrderTag(tag);
256 if (descriptor.iteration && descriptor.iteration.getSize() > 1)
257 bd.setIterationAttr(descriptor.iteration);
258 if (!sendTags.empty()) {
259 assert(descriptor.object <
static_cast<int64_t
>(sendTags.size()) &&
260 "a send tag names every buffer of a core-pushed pool");
261 bd.setBdTag(sendTags[descriptor.object]);
265 UseLockOp::create(builder, loc, releaseLock, LockAction::Release,
268 NextBDOp::create(builder, loc, successor);
273 std::pair<Block *, Block *> startChain(ObjectFifoDmaEndpointOp endpoint,
274 int channel,
int taskCount,
275 bool corePushes =
false) {
276 Location loc = endpoint.getLoc();
277 DmaBody program = dmaProgramFor(endpoint.getTileLike(), loc);
278 Block *endBlock = findEndOpBlock(program.getDmaBody());
279 Block *lastDmaBlock = endBlock->getSinglePredecessor();
280 Block *dmaBlock = builder.createBlock(endBlock);
281 Block *bdBlock = builder.createBlock(endBlock);
283 builder.setInsertionPointToStart(dmaBlock);
284 DMAStartOp::create(builder, loc, endpoint.getRouteDirection(), channel,
285 taskCount, endpoint.getPadValue(),
286 endpoint.getOutOfOrder() &&
287 endpoint.getRouteDirection() == DMAChannelDir::S2MM,
288 corePushes, bdBlock, endBlock);
290 lastDmaBlock->getTerminator()->setSuccessor(dmaBlock, 1);
292 return {bdBlock, endBlock};
297 LogicalResult lowerTurns(ObjectFifoDmaEndpointOp first,
int channel) {
298 SmallVector<ObjectFifoDmaEndpointOp> ends =
turnEndpoints(first);
299 SmallVector<SmallVector<Descriptor>> descriptors;
300 for (ObjectFifoDmaEndpointOp end : ends) {
301 descriptors.push_back(
302 descriptorsFor(end, buffersOf(end.getPoolOp()), end.drains()));
303 if (
static_cast<int64_t
>(descriptors.back().size()) !=
304 end.getNumDescriptors() ||
305 descriptors.back().empty())
306 return end.emitOpError(
"takes turns, but lowers to ")
307 << descriptors.back().size() <<
" descriptors per pass for "
308 << end.getNumDescriptors();
310 SmallVector<unsigned> turns;
311 for (
auto name : first.getTurnsAttr().getAsRange<FlatSymbolRefAttr>())
312 turns.push_back(llvm::find_if(ends,
313 [&](ObjectFifoDmaEndpointOp end) {
314 return end.getSymNameAttr() ==
319 auto [bdBlock, endBlock] = startChain(first, channel, 0);
320 bool binaryLocks = !device.getTargetModel().hasProperty(
322 SmallVector<size_t> next(ends.size(), 0);
323 int64_t total = first.getNumBDs();
324 Block *current = bdBlock;
325 for (int64_t k = 0; k < total; k++) {
326 unsigned i = turns[k % turns.size()];
328 k + 1 < total ? builder.createBlock(endBlock) : bdBlock;
329 builder.setInsertionPointToStart(current);
330 emitDescriptor(ends[i], ends[i].getPoolOp(), descriptors[i][next[i]],
331 PacketInfoAttr(), successor, binaryLocks,
333 next[i] = (next[i] + 1) % descriptors[i].size();
339 LogicalResult lowerEndpoint(ObjectFifoDmaEndpointOp endpoint,
int channel) {
340 bool drains = endpoint.drains();
341 ObjectFifoPoolOp pool = endpoint.getPoolOp();
342 SmallVector<Value> buffers = buffersOf(pool);
343 if (buffers.empty()) {
347 SmallVector<Descriptor> descriptors =
348 descriptorsFor(endpoint, buffers, drains);
349 if (descriptors.empty()) {
353 if (
static_cast<int64_t
>(descriptors.size()) !=
354 endpoint.getNumDescriptors())
355 return endpoint.emitOpError()
356 <<
"lowers to " << descriptors.size()
357 <<
" descriptors per pass, but its channel was allocated for "
358 << endpoint.getNumDescriptors();
360 Location loc = endpoint.getLoc();
362 int64_t repeat = endpoint.getRepeat();
363 std::optional<int32_t> iterCount = endpoint.getIterCount();
365 bool repeatInHardware = endpoint.repeatsInHardware();
368 int64_t passes = endpoint.getDispatchPasses();
369 int taskCount = iterCount ? *iterCount / passes - 1
370 : (repeatInHardware ? repeat - 1 : 0);
371 int64_t copies = repeatInHardware ? 1 : repeat;
378 drains && endpoint.getReleaseStrategy() == ObjectFifoRelease::PushQueue;
379 SmallVector<int32_t> sendTags;
381 bool hasRelease =
false;
382 ObjectFifoCoreEndpointOp producer;
383 auto sentSegments = endpoint.getSelectedSegments();
384 for (
auto coreEnd : device.getOps<ObjectFifoCoreEndpointOp>()) {
385 if (coreEnd.drains() || coreEnd.getPoolOp() != pool ||
386 coreEnd.getTile() != endpoint.getTile())
388 auto filledSegments = coreEnd.getSelectedSegments();
389 if (!llvm::any_of(sentSegments, [&](ObjectFifoSegmentOp segment) {
390 return llvm::is_contained(filledSegments, segment);
394 return endpoint.emitOpError(
395 "push_queue requires a single core filler for its segment");
397 for (
auto core : device.getOps<CoreOp>()) {
398 if (core.getTile() == coreEnd.getTile())
399 core.walk([&](ObjectFifoReleaseOp release) {
400 hasRelease |= release.getObjFifoName() == coreEnd.getSymName();
405 return endpoint.emitOpError(
406 "push_queue requires producer releases in a "
407 "local core to submit send tasks");
409 return endpoint.emitOpError(
410 "push_queue requires one dispatch pass per object");
413 if (
static_cast<int64_t
>(descriptors.size()) != pool.getDepth())
414 return endpoint.emitOpError(
"sends ")
415 << descriptors.size() <<
" descriptors for a pool of "
417 <<
" objects, so a release names no one of them; push_queue "
418 <<
"needs one descriptor per object";
419 for (
size_t slot = 0; slot < descriptors.size(); ++slot) {
420 while (usedSendTags.contains(nextSendTag))
422 sendTags.push_back(nextSendTag++);
424 producer.setCorePushAttr(
425 ObjectFifoCorePushAttr::get(pool.getContext(), channel, sendTags));
428 SmallVector<PacketInfoAttr> turns;
429 if (std::optional<ArrayAttr> headers = endpoint.getDispatchPackets()) {
430 llvm::append_range(turns, headers->getAsRange<PacketInfoAttr>());
431 }
else if (endpoint.getDispatch()) {
432 return endpoint.emitOpError(
433 "has no dispatchPackets; run --aie-objectfifo-allocate");
437 if (!turns.empty() && pool.getContiguous())
438 return endpoint.emitOpError(
439 "dispatches from a contiguous pool, which has no per-object BDs");
440 int64_t depth = pool.getDepth();
442 auto [bdBlock, endBlock] =
443 startChain(endpoint, channel, taskCount, corePushes);
444 bool binaryLocks = !device.getTargetModel().hasProperty(
446 size_t total = descriptors.size() * copies * passes;
448 Block *current = bdBlock;
449 for (int64_t pass = 0; pass < passes; pass++) {
450 for (Descriptor &descriptor : descriptors) {
451 PacketInfoAttr packet = endpoint.getPacketAttr();
453 packet = turns[(pass * depth + descriptor.object) % turns.size()];
454 for (int64_t copy = 0; copy < copies; copy++) {
456 if (emitted + 1 < total) {
457 successor = builder.createBlock(endBlock);
458 }
else if (iterCount) {
460 successor = builder.createBlock(endBlock);
461 builder.setInsertionPointToStart(successor);
462 EndOp::create(builder, loc);
466 builder.setInsertionPointToStart(current);
467 emitDescriptor(endpoint, pool, descriptor, packet, successor,
468 binaryLocks, drains, emitted, sendTags);
478 lowerOutOfOrderReceivers(ArrayRef<ObjectFifoDmaEndpointOp> endpoints,
480 auto first = endpoints.front();
481 SmallVector<SmallVector<Descriptor>> descriptors;
483 bool unbounded =
false;
485 for (
auto endpoint : endpoints) {
486 descriptors.push_back(
487 descriptorsFor(endpoint, buffersOf(endpoint.getPoolOp()),
false));
488 if (descriptors.back().empty() ||
489 static_cast<int64_t
>(descriptors.back().size()) !=
490 endpoint.getNumDescriptors())
491 return endpoint.emitOpError(
492 "incorrect number of receiving descriptors");
493 totalBDs += descriptors.back().size();
494 if (
auto iterations = endpoint.getIterCount())
495 objects += descriptors.back().size() * *iterations;
499 int64_t perTask = device.getTargetModel().getMaxRepeatCount() + 1;
502 int64_t tasks = llvm::divideCeil(objects, perTask);
503 if (tasks > device.getTargetModel().getDmaTaskQueueDepth())
504 return first.emitOpError(
"shared receiving channel needs ")
505 << tasks <<
" initial tasks for " << objects
506 <<
" objects, exceeding its queue capacity; use an unbounded "
507 "receiver with core replenishment";
509 auto [head, endBlock] =
510 startChain(first, channel, std::min(objects, perTask) - 1);
511 for (objects -= perTask; objects > 0; objects -= perTask) {
512 auto [unusedHead, unusedEnd] =
513 startChain(first, channel, std::min(objects, perTask) - 1);
514 unusedHead->replaceAllUsesWith(head);
517 bool binaryLocks = !device.getTargetModel().hasProperty(
519 Block *current = head;
521 for (
auto [index, bds] : llvm::enumerate(descriptors)) {
522 ObjectFifoDmaEndpointOp endpoint = endpoints[index];
523 for (
auto &descriptor : bds) {
524 Block *successor = builder.createBlock(endBlock);
525 if (++emitted == totalBDs) {
526 builder.setInsertionPointToStart(successor);
527 EndOp::create(builder, endpoint.getLoc());
529 builder.setInsertionPointToStart(current);
530 emitDescriptor(endpoint, endpoint.getPoolOp(), descriptor,
531 endpoint.getPacketAttr(), successor, binaryLocks,
false);
538 void runOnOperation()
override {
539 device = getOperation();
540 builder = OpBuilder(device.getContext());
544 usedSendTags.clear();
545 for (
auto pool : device.getOps<ObjectFifoPoolOp>())
546 if (
auto tags = pool.getOutOfOrderTags())
547 usedSendTags.insert(tags->begin(), tags->end());
548 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>())
549 if (
auto push = endpoint.getCorePushAttr())
550 usedSendTags.insert(push.getBdTags().begin(), push.getBdTags().end());
551 device.walk([&](DMABDOp bd) {
552 if (
auto tag = bd.getBdTag())
553 usedSendTags.insert(*tag);
556 SmallVector<ObjectFifoDmaEndpointOp> endpoints(
557 device.getOps<ObjectFifoDmaEndpointOp>());
558 DenseMap<Operation *, ObjectFifoDmaEndpointOp> pushedDrainers;
559 for (ObjectFifoDmaEndpointOp endpoint : endpoints) {
560 if (!endpoint.drains() ||
561 endpoint.getReleaseStrategy() != ObjectFifoRelease::PushQueue)
563 auto [it, inserted] =
564 pushedDrainers.try_emplace(endpoint.getPoolOp(), endpoint);
566 auto diag = endpoint.emitOpError(
567 "a pool supports only one core-pushed DMA drainer");
568 diag.attachNote(it->second.getLoc())
569 <<
"first core-pushed drainer is here";
570 return signalPassFailure();
575 DenseMap<Value, size_t> tileOrder;
576 for (ObjectFifoDmaEndpointOp endpoint : endpoints)
577 tileOrder.try_emplace(endpoint.getTile(), tileOrder.size());
579 endpoints, [&](ObjectFifoDmaEndpointOp a, ObjectFifoDmaEndpointOp b) {
580 return std::make_tuple(tileOrder.lookup(a.getTile()),
581 a.getRouteDirection(),
582 a.getChannelIndex().value_or(-1)) <
583 std::make_tuple(tileOrder.lookup(b.getTile()),
584 b.getRouteDirection(),
585 b.getChannelIndex().value_or(-1));
587 using ChannelKey = std::tuple<int, int, int>;
588 std::map<ChannelKey, SmallVector<ObjectFifoDmaEndpointOp>> receivers;
589 for (
auto endpoint : endpoints) {
590 if (endpoint.getOutOfOrder() && !endpoint.drains()) {
591 auto tile = endpoint.getTile().getDefiningOp<TileOp>();
592 receivers[{tile.getCol(), tile.getRow(),
593 endpoint.getChannelIndex().value_or(-1)}]
594 .push_back(endpoint);
597 for (ObjectFifoDmaEndpointOp endpoint : endpoints) {
598 std::optional<int> channel = endpoint.getChannelIndex();
600 endpoint.emitOpError(
"has no channel; run --aie-objectfifo-allocate");
601 return signalPassFailure();
603 if (endpoint.getOutOfOrder() && !endpoint.drains()) {
604 auto tile = endpoint.getTile().getDefiningOp<TileOp>();
605 auto &group = receivers.at({tile.getCol(), tile.getRow(), *channel});
606 if (group.front() == endpoint &&
607 failed(lowerOutOfOrderReceivers(group, *channel)))
608 return signalPassFailure();
611 SmallVector<ObjectFifoDmaEndpointOp> turns =
turnEndpoints(endpoint);
612 if (turns.empty() ? failed(lowerEndpoint(endpoint, *channel))
613 : turns.front() == endpoint &&
614 failed(lowerTurns(endpoint, *channel)))
615 return signalPassFailure();
617 for (ObjectFifoDmaEndpointOp endpoint : endpoints)
623 llvm::make_early_inc_range(device.getOps<RouteEndpointOp>())) {
631std::unique_ptr<OperationPass<DeviceOp>>
633 return std::make_unique<AIEObjectFifoLowerDMAsPass>();
llvm::SmallVector< ObjectFifoDmaEndpointOp > turnEndpoints(ObjectFifoDmaEndpointOp end)
llvm::SmallVector< StreamWindowStep > streamWindowSteps(const StreamWindow &w)
mlir::Operation * lookupNamedOp(mlir::Operation *from, mlir::StringAttr name)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoLowerDMAsPass()
int64_t paddedSize(llvm::ArrayRef< BDDimLayoutAttr > dims, llvm::ArrayRef< BDPadLayoutAttr > pads, int64_t fallback)
llvm::SmallVector< BDDimLayoutAttr > inner