MLIR-AIE 1.4.5.dev
AIEObjectFifoLowerCores.cpp
Go to the documentation of this file.
1//===- AIEObjectFifoLowerCores.cpp ------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
10
11#include "mlir/Dialect/Arith/IR/Arith.h"
12#include "mlir/Dialect/MemRef/IR/MemRef.h"
13#include "mlir/Dialect/SCF/IR/SCF.h"
14#include "mlir/IR/Dominance.h"
15#include "mlir/IR/PatternMatch.h"
16#include "mlir/Interfaces/DataLayoutInterfaces.h"
17#include "mlir/Interfaces/MemorySlotInterfaces.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Transforms/Mem2Reg.h"
20#include "mlir/Transforms/WalkPatternRewriteDriver.h"
21
22#include <numeric>
23
24using namespace mlir;
25using namespace xilinx;
26using namespace xilinx::AIE;
27
28namespace xilinx::AIE {
29#define GEN_PASS_DEF_AIEOBJECTFIFOLOWERCORES
30#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
31} // namespace xilinx::AIE
32
33namespace {
34
35// Marks the `memref.alloca`s emitted here for bookkeeping only (objects held,
36// current object index). Memrefs thread through control flow more easily than
37// SSA values; mem2reg turns them back into SSA at the end of the pass, and the
38// marker keeps that sweep off allocas this pass did not create. The slots
39// holding the objects themselves carry kObjectFifoObjectSlotAttrName instead
40// and are left in memory.
41constexpr llvm::StringLiteral kBookkeepingSlotAttrName =
42 "aie.objectfifo.bookkeeping_slot";
43
44/// Build an `scf.index_switch` on `idx` with `n` cases (0..n-1) plus a default;
45/// case k (and the default) yields `elem(k)`. The runtime selection folds to a
46/// constant once the enclosing loops are unrolled. Leaves the builder after the
47/// switch and returns its result.
48Value buildRotatingSwitch(OpBuilder &builder, Location loc, Value idx,
49 Type resultTy, int n,
50 llvm::function_ref<Value(int)> elem) {
51 SmallVector<int64_t, 4> caseValues;
52 for (int c = 0; c < n; ++c) {
53 caseValues.push_back(c);
54 }
55 auto cases = DenseI64ArrayAttr::get(builder.getContext(), caseValues);
56 auto switchOp = scf::IndexSwitchOp::create(builder, loc, TypeRange{resultTy},
57 idx, cases, n);
58 builder.createBlock(&switchOp.getDefaultRegion());
59 builder.setInsertionPointToStart(&switchOp.getDefaultBlock());
60 scf::YieldOp::create(builder, loc, elem(0));
61 for (int c = 0; c < n; ++c) {
62 builder.createBlock(&switchOp.getCaseRegions()[c]);
63 builder.setInsertionPointToStart(&switchOp.getCaseBlock(c));
64 scf::YieldOp::create(builder, loc, elem(c));
65 }
66 builder.setInsertionPointAfter(switchOp);
67 return switchOp.getResult(0);
68}
69
70/// Rotate the objects in `slots` left by `released`, so that the first slot
71/// again holds the next object to acquire.
72void emitRotateObjects(OpBuilder &builder, Location loc,
73 ArrayRef<memref::AllocaOp> slots, int released) {
74 if (slots.empty() || released % static_cast<int>(slots.size()) == 0) {
75 return;
76 }
77 SmallVector<Value> objects;
78 for (memref::AllocaOp slot : slots) {
79 objects.push_back(
80 memref::LoadOp::create(builder, loc, slot.getResult(), ValueRange{}));
81 }
82 for (size_t k = 0; k < slots.size(); ++k) {
83 memref::AllocaOp slot = slots[k];
84 memref::StoreOp::create(builder, loc,
85 objects[(k + released) % objects.size()], slot,
86 ValueRange{});
87 }
88}
89
90/// Advance a rotating object-index counter in its slot:
91/// counter = (counter + released) mod depth.
92void emitAdvanceObjectIndex(OpBuilder &builder, Location loc, Value slot,
93 int depth, int released) {
94 Value size =
95 arith::ConstantOp::create(builder, loc, builder.getI32IntegerAttr(depth));
96 Value old = memref::LoadOp::create(builder, loc, slot, ValueRange{});
97 Value step = arith::ConstantOp::create(builder, loc,
98 builder.getI32IntegerAttr(released));
99 Value sum = arith::AddIOp::create(builder, loc, old, step);
100 Value wrapping =
101 arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::sge, sum, size);
102 // The counter stays in [0, depth), so releasing a single object can only ever
103 // wrap to exactly 0.
104 Value wrapped = released == 1
105 ? Value(arith::ConstantOp::create(
106 builder, loc, builder.getI32IntegerAttr(0)))
107 : Value(arith::SubIOp::create(builder, loc, sum, size));
108 Value next = arith::SelectOp::create(builder, loc, wrapping, wrapped, sum);
109 memref::StoreOp::create(builder, loc, next, slot, ValueRange{});
110}
111
112/// Locks a core endpoint toggles for `action`, one per segment it works: a
113/// filler waits on free objects and hands back full ones, a drainer the
114/// reverse.
115SmallVector<FlatSymbolRefAttr>
116segmentLocksFor(ObjectFifoCoreEndpointOp endpoint, LockAction action) {
117 bool acquiring = action == LockAction::AcquireGreaterEqual;
118 bool wantsProduce = acquiring != endpoint.drains();
119
120 SmallVector<FlatSymbolRefAttr> locks;
121 for (ObjectFifoSegmentOp segment : endpoint.getSelectedSegments()) {
122 if (auto lock = wantsProduce ? segment.getProduceLockAttr()
123 : segment.getConsumeLockAttr()) {
124 locks.push_back(lock);
125 }
126 }
127 return locks;
128}
129
130/// Everything a rewrite pattern needs that is not local to its op, resolved
131/// before the driver runs so that no matchAndRewrite scans the IR.
132struct LoweringContext {
133 DeviceOp device;
134 bool usesSemaphoreLocks;
135 DenseMap<Operation *, SmallVector<Value>> poolBuffers{};
136 /// Per-(core, endpoint) runtime bookkeeping: the rotating object index (which
137 /// selects binary locks), the objects in rotation order, starting with the
138 /// next one to acquire, and, where locks count, the number of objects
139 /// currently held. The object of a single-object pool never moves, so it is
140 /// handed over directly instead.
141 DenseMap<std::pair<Operation *, Operation *>, memref::AllocaOp> objectIndex{};
142 DenseMap<std::pair<Operation *, Operation *>, SmallVector<memref::AllocaOp>>
143 objectSlots{};
144 DenseMap<std::pair<Operation *, Operation *>, Value> soleObject{};
145 DenseMap<std::pair<Operation *, Operation *>, memref::AllocaOp> heldCount{};
146 bool sawError = false;
147
148 ObjectFifoCoreEndpointOp endpointOf(Operation *op, StringRef name) {
149 return SymbolTable::lookupNearestSymbolFrom<ObjectFifoCoreEndpointOp>(
150 device, StringAttr::get(op->getContext(), name));
151 }
152
153 LockOp lockOf(FlatSymbolRefAttr name) {
154 return lookupNamedOp<LockOp>(device, name.getAttr());
155 }
156};
157
158/// Emit the rotating UseLocks for a binary-lock (AIE1) acquire or release. One
159/// lock travels with each object, and the starting index is only known at
160/// runtime, so each of the `count` locks is selected by an scf.index_switch on
161/// the rotation counter -- folded to a concrete lock once the loops unroll.
162void emitBinaryUseLocks(OpBuilder &builder, Location loc,
163 MutableArrayRef<LockOp> locks, int depth, bool drains,
164 Value counterSlot, int count, LockAction action) {
165 if (count == 0 || locks.empty()) {
166 return;
167 }
168 // The value carries the direction: 1 for a filler's release or a drainer's
169 // acquire, 0 for the opposite.
170 bool full = (!drains && action == LockAction::Release) ||
171 (drains && action == LockAction::Acquire);
172 Value counter =
173 memref::LoadOp::create(builder, loc, counterSlot, ValueRange{});
174 Value index =
175 arith::IndexCastOp::create(builder, loc, builder.getIndexType(), counter);
176 Type lockTy = locks[0].getType();
177 for (int i = 0; i < count; i++) {
178 Value lock =
179 buildRotatingSwitch(builder, loc, index, lockTy, depth, [&](int c) {
180 return locks[(i + c) % depth].getResult();
181 });
182 UseLockOp::create(builder, loc, lock, action, full ? 1 : 0);
183 }
184}
185
186struct LowerRelease : OpRewritePattern<ObjectFifoReleaseOp> {
187 LoweringContext &ctx;
188 LowerRelease(MLIRContext *context, LoweringContext &ctx)
189 : OpRewritePattern(context), ctx(ctx) {}
190
191 LogicalResult matchAndRewrite(ObjectFifoReleaseOp releaseOp,
192 PatternRewriter &rewriter) const override {
193 auto endpoint = ctx.endpointOf(releaseOp, releaseOp.getObjFifoName());
194 if (!endpoint) {
195 return failure();
196 }
197 ObjectFifoPoolOp pool = endpoint.getPoolOp();
198 CoreOp core = releaseOp->getParentOfType<CoreOp>();
199 auto key = std::make_pair(core.getOperation(), endpoint.getOperation());
200 memref::AllocaOp index = ctx.objectIndex.lookup(key);
201
202 int count = releaseOp.relNumber() * pool.getRepeatCount().value_or(1);
203 rewriter.setInsertionPointAfter(releaseOp);
204 Location loc = releaseOp.getLoc();
205
206 if (!ctx.usesSemaphoreLocks) {
207 // The rotation counter is created by the matching acquire, so a release
208 // without one is releasing objects never acquired on this tile.
209 if (!index) {
210 releaseOp->emitOpError(
211 "objectFifo release has no corresponding acquire on this tile");
212 ctx.sawError = true;
213 return failure();
214 }
215 SmallVector<LockOp> locks = pool.getLockOps();
216 emitBinaryUseLocks(rewriter, loc, locks, pool.getDepth(),
217 endpoint.drains(), index.getResult(), count,
218 LockAction::Release);
219 } else {
220 Value amount = arith::ConstantOp::create(
221 rewriter, loc, rewriter.getI32IntegerAttr(count));
222 for (FlatSymbolRefAttr name :
223 segmentLocksFor(endpoint, LockAction::Release)) {
224 UseLockOp::create(rewriter, loc, ctx.lockOf(name), LockAction::Release,
225 amount);
226 }
227
228 memref::AllocaOp held = ctx.heldCount.lookup(key);
229 assert(held && "a held counter is created for every released endpoint");
230 Value current =
231 memref::LoadOp::create(rewriter, loc, held.getResult(), ValueRange{});
232 Value next = arith::SubIOp::create(rewriter, loc, current, amount);
233 memref::StoreOp::create(rewriter, loc, next, held.getResult(),
234 ValueRange{});
235 }
236
237 if (index) {
238 emitAdvanceObjectIndex(rewriter, loc, index.getResult(), pool.getDepth(),
239 releaseOp.getSize());
240 }
241 auto slots = ctx.objectSlots.find(key);
242 if (slots != ctx.objectSlots.end()) {
243 emitRotateObjects(rewriter, loc, slots->second, releaseOp.getSize());
244 }
245 return success();
246 }
247};
248
249struct LowerAcquire : OpRewritePattern<ObjectFifoAcquireOp> {
250 LoweringContext &ctx;
251 LowerAcquire(MLIRContext *context, LoweringContext &ctx)
252 : OpRewritePattern(context), ctx(ctx) {}
253
254 LogicalResult matchAndRewrite(ObjectFifoAcquireOp acquireOp,
255 PatternRewriter &rewriter) const override {
256 auto endpoint = ctx.endpointOf(acquireOp, acquireOp.getObjFifoName());
257 if (!endpoint) {
258 return failure();
259 }
260 ObjectFifoPoolOp pool = endpoint.getPoolOp();
261 CoreOp core = acquireOp->getParentOfType<CoreOp>();
262 auto key = std::make_pair(core.getOperation(), endpoint.getOperation());
263
264 int wanted = acquireOp.acqNumber();
265 if (wanted > pool.getDepth()) {
266 acquireOp->emitOpError("acquires ")
267 << wanted << " objects from a pool of " << pool.getDepth();
268 ctx.sawError = true;
269 return failure();
270 }
271 int repeat = pool.getRepeatCount().value_or(1);
272 rewriter.setInsertionPointAfter(acquireOp);
273 Location loc = acquireOp.getLoc();
274
275 if (!ctx.usesSemaphoreLocks) {
276 // Each object has its own rotating binary lock; while the loops are
277 // rolled the offset of the held locks is unknown, so acquire the whole
278 // window [counter, counter + wanted). Redundant re-acquires of
279 // still-held locks are pruned after unrolling.
280 memref::AllocaOp index = ctx.objectIndex.lookup(key);
281 assert(index && "a rotation counter is created for every acquire");
282 SmallVector<LockOp> locks = pool.getLockOps();
283 emitBinaryUseLocks(rewriter, loc, locks, pool.getDepth(),
284 endpoint.drains(), index.getResult(), wanted * repeat,
285 LockAction::Acquire);
286 return replaceWithObjects(acquireOp, key, rewriter);
287 }
288
289 // An acquire names every object the core wants to hold, so only the ones
290 // not already held are taken.
291 memref::AllocaOp held = ctx.heldCount.lookup(key);
292 assert(held && "a held counter is created for every acquire");
293 Value current =
294 memref::LoadOp::create(rewriter, loc, held.getResult(), ValueRange{});
295 Value target = arith::ConstantOp::create(
296 rewriter, loc, rewriter.getI32IntegerAttr(wanted));
297 Value zero =
298 arith::ConstantOp::create(rewriter, loc, rewriter.getI32IntegerAttr(0));
299 Value shortfall = arith::SubIOp::create(rewriter, loc, target, current);
300 Value delta = arith::MaxSIOp::create(rewriter, loc, shortfall, zero);
301 if (repeat > 1) {
302 Value repeatVal = arith::ConstantOp::create(
303 rewriter, loc, rewriter.getI32IntegerAttr(repeat));
304 delta = arith::MulIOp::create(rewriter, loc, delta, repeatVal);
305 }
306 for (FlatSymbolRefAttr name :
307 segmentLocksFor(endpoint, LockAction::AcquireGreaterEqual)) {
308 UseLockOp::create(rewriter, loc, ctx.lockOf(name),
309 LockAction::AcquireGreaterEqual, delta);
310 }
311
312 Value next = arith::AddIOp::create(rewriter, loc, current, delta);
313 memref::StoreOp::create(rewriter, loc, next, held.getResult(),
314 ValueRange{});
315 return replaceWithObjects(acquireOp, key, rewriter);
316 }
317
318 /// Hand back the objects at the front of the rotation, one per result.
319 LogicalResult replaceWithObjects(ObjectFifoAcquireOp acquireOp,
320 std::pair<Operation *, Operation *> key,
321 PatternRewriter &rewriter) const {
322 if (Value object = ctx.soleObject.lookup(key)) {
323 for (Value result : acquireOp.getObjects()) {
324 rewriter.replaceAllUsesWith(result, object);
325 }
326 return success();
327 }
328 auto slots = ctx.objectSlots.find(key);
329 if (slots == ctx.objectSlots.end() || slots->second.empty()) {
330 return success();
331 }
332 for (auto [k, result] : llvm::enumerate(acquireOp.getObjects())) {
333 Value object =
334 memref::LoadOp::create(rewriter, acquireOp.getLoc(),
335 slots->second[k].getResult(), ValueRange{});
336 rewriter.replaceAllUsesWith(result, object);
337 }
338 return success();
339 }
340};
341
342struct AIEObjectFifoLowerCoresPass
343 : public xilinx::AIE::impl::AIEObjectFifoLowerCoresBase<
344 AIEObjectFifoLowerCoresPass> {
345
346 /// A rank-0 `memref.alloca` initialized to `initial` and tagged `marker`.
347 /// mem2reg later threads a bookkeeping slot through the enclosing loops as an
348 /// iter_arg.
349 memref::AllocaOp makeSlot(OpBuilder &builder, Location loc, Value initial,
350 StringRef marker = kBookkeepingSlotAttrName) {
351 auto slotTy = MemRefType::get(SmallVector<int64_t>{}, initial.getType());
352 auto slot = memref::AllocaOp::create(builder, loc, slotTy);
353 slot->setAttr(marker, builder.getUnitAttr());
354 memref::StoreOp::create(builder, loc, initial, slot.getResult(),
355 ValueRange{});
356 return slot;
357 }
358
359 /// One slot per object of the pool, in rotation order from `start`, or the
360 /// object itself if the pool has only one. The slots are not promoted here;
361 /// see kObjectFifoObjectSlotAttrName.
362 void trackObjects(OpBuilder &builder, Location loc,
363 ObjectFifoCoreEndpointOp endpoint, int64_t start,
364 std::pair<Operation *, Operation *> key,
365 LoweringContext &ctx) {
366 ObjectFifoPoolOp pool = endpoint.getPoolOp();
367 SmallVector<Value> buffers = ctx.poolBuffers.lookup(pool.getOperation());
368 // An endpoint holding one side of a join or distribute reaches only its
369 // own run of each object.
370 MemRefType accessType = endpoint.getAccessType();
371 if (accessType != pool.getElemType()) {
372 auto [offset, size] = endpoint.getExtent();
373 for (Value &buffer : buffers) {
374 buffer = memref::SubViewOp::create(
375 builder, loc, accessType, buffer,
376 ArrayRef<OpFoldResult>{builder.getIndexAttr(offset)},
377 {builder.getIndexAttr(size)}, {builder.getIndexAttr(1)});
378 }
379 }
380 if (buffers.size() == 1) {
381 ctx.soleObject[key] = buffers.front();
382 return;
383 }
384 SmallVector<memref::AllocaOp> &slots = ctx.objectSlots[key];
385 for (size_t k = 0; k < buffers.size(); ++k) {
386 slots.push_back(makeSlot(builder, loc,
387 buffers[(start + k) % buffers.size()],
389 }
390 }
391
392 void emitCounters(CoreOp coreOp, LoweringContext &ctx, OpBuilder &builder) {
393 builder.setInsertionPointToStart(&coreOp.getBody().front());
394 Operation *core = coreOp.getOperation();
395 Value zero;
396
397 // A pool whose initial contents fill only its first objects is next
398 // written at the first object they leave empty.
399 auto firstObject = [](ObjectFifoCoreEndpointOp endpoint) -> int64_t {
400 ObjectFifoPoolOp pool = endpoint.getPoolOp();
401 auto initValues = pool.getInitValues();
402 if (endpoint.drains() || !initValues || pool.getDepth() <= 0) {
403 return 0;
404 }
405 return static_cast<int64_t>(initValues->size()) % pool.getDepth();
406 };
407
408 auto record = [&](DenseMap<std::pair<Operation *, Operation *>,
409 memref::AllocaOp> &slots,
410 StringRef name, bool rotating) {
411 auto endpoint = ctx.endpointOf(coreOp, name);
412 if (!endpoint) {
413 return;
414 }
415 auto key = std::make_pair(core, endpoint.getOperation());
416 if (slots.count(key)) {
417 return;
418 }
419 int64_t start = rotating ? firstObject(endpoint) : 0;
420 Value initial;
421 if (start != 0) {
422 initial = arith::ConstantOp::create(builder, coreOp.getLoc(),
423 builder.getI32IntegerAttr(start));
424 } else {
425 if (!zero) {
426 zero = arith::ConstantOp::create(builder, coreOp.getLoc(),
427 builder.getI32IntegerAttr(0));
428 }
429 initial = zero;
430 }
431 slots[key] = makeSlot(builder, coreOp.getLoc(), initial);
432 };
433
434 // Objects are only tracked where the core uses one.
435 coreOp.walk([&](ObjectFifoAcquireOp a) {
436 auto endpoint = ctx.endpointOf(coreOp, a.getObjFifoName());
437 if (!endpoint) {
438 return;
439 }
440 ObjectFifoPoolOp pool = endpoint.getPoolOp();
441 auto [buffers, inserted] =
442 ctx.poolBuffers.try_emplace(pool.getOperation());
443 if (inserted) {
444 for (BufferLike buffer : pool.getBufferOps()) {
445 buffers->second.push_back(buffer.getBuffer());
446 }
447 if (static_cast<int64_t>(buffers->second.size()) != pool.getDepth()) {
448 pool.emitOpError(
449 "expects every one of its 'depth' buffers to resolve");
450 ctx.sawError = true;
451 }
452 }
453 if (ctx.sawError || a->use_empty()) {
454 return;
455 }
456 auto key = std::make_pair(core, endpoint.getOperation());
457 if (!ctx.objectSlots.count(key) && !ctx.soleObject.count(key)) {
458 trackObjects(builder, coreOp.getLoc(), endpoint, firstObject(endpoint),
459 key, ctx);
460 }
461 });
462 if (!ctx.usesSemaphoreLocks) {
463 // Binary locks travel with their objects, so they rotate by index.
464 coreOp.walk([&](ObjectFifoAcquireOp a) {
465 record(ctx.objectIndex, a.getObjFifoName(), /*rotating=*/true);
466 });
467 return;
468 }
469 coreOp.walk([&](ObjectFifoAcquireOp a) {
470 record(ctx.heldCount, a.getObjFifoName(), /*rotating=*/false);
471 });
472 coreOp.walk([&](ObjectFifoReleaseOp r) {
473 record(ctx.heldCount, r.getObjFifoName(), /*rotating=*/false);
474 });
475 }
476
477 /// Tell `--aie-objectFifo-unroll` how far a loop must be unrolled for the
478 /// rotating indices inside it to fold: the least common multiple of the
479 /// depths it touches. Accesses nested in a child loop drive that child, not
480 /// this one, so an ancestor is not over-unrolled.
481 void annotateUnrollHints(DeviceOp device, LoweringContext &ctx,
482 OpBuilder &builder) {
483 for (auto coreOp : device.getOps<CoreOp>()) {
484 coreOp.walk([&](Operation *loop) {
485 if (!isObjectFifoLoop(loop)) {
486 return;
487 }
488 int64_t period = 1;
489 bool touched = false;
490 auto account = [&](Operation *op, StringRef name) {
491 if (getObjectFifoLoopParent(op) != loop) {
492 return;
493 }
494 auto endpoint = ctx.endpointOf(op, name);
495 if (!endpoint) {
496 return;
497 }
498 touched = true;
499 period = std::lcm(period, (int64_t)endpoint.getPoolOp().getDepth());
500 };
501 loop->walk(
502 [&](ObjectFifoAcquireOp a) { account(a, a.getObjFifoName()); });
503 loop->walk(
504 [&](ObjectFifoReleaseOp r) { account(r, r.getObjFifoName()); });
505 if (touched) {
506 loop->setAttr(kObjectFifoUnrollHintAttrName,
507 builder.getI64IntegerAttr(period));
508 }
509 });
510 }
511 }
512
513 LogicalResult promoteBookkeepingSlots(DeviceOp device) {
514 SmallVector<PromotableAllocationOpInterface> allocators;
515 device.walk([&](memref::AllocaOp allocaOp) {
516 if (allocaOp->hasAttr(kBookkeepingSlotAttrName)) {
517 allocators.push_back(
518 cast<PromotableAllocationOpInterface>(allocaOp.getOperation()));
519 }
520 });
521 if (allocators.empty()) {
522 return success();
523 }
524
525 DataLayout dataLayout = DataLayout::closest(device);
526 DominanceInfo dominance(device);
527 OpBuilder builder(device.getContext());
528 (void)tryToPromoteMemorySlots(allocators, builder, dataLayout, dominance);
529
530 WalkResult leftover = device.walk([&](memref::AllocaOp allocaOp) {
531 if (!allocaOp->hasAttr(kBookkeepingSlotAttrName)) {
532 return WalkResult::advance();
533 }
534 allocaOp.emitOpError()
535 << "objectFifo bookkeeping slot could not be promoted to SSA "
536 "(mem2reg left it in place); the objectFifo lowering requires "
537 "all bookkeeping counters to become loop-carried SSA values";
538 return WalkResult::interrupt();
539 });
540 return failure(leftover.wasInterrupted());
541 }
542
543 void runOnOperation() override {
544 DeviceOp device = getOperation();
545 OpBuilder builder(device.getContext());
546
547 LoweringContext ctx{device, device.getTargetModel().hasProperty(
549
550 annotateUnrollHints(device, ctx, builder);
551 for (auto coreOp : device.getOps<CoreOp>()) {
552 emitCounters(coreOp, ctx, builder);
553 }
554 if (ctx.sawError) {
555 return signalPassFailure();
556 }
557
558 RewritePatternSet patterns(device.getContext());
559 patterns.insert<LowerAcquire, LowerRelease>(device.getContext(), ctx);
560 walkAndApplyPatterns(device, std::move(patterns));
561 if (ctx.sawError) {
562 return signalPassFailure();
563 }
564
565 SmallVector<Operation *> toErase;
566 device.walk([&](Operation *op) {
567 if (isa<ObjectFifoAcquireOp, ObjectFifoReleaseOp>(op)) {
568 toErase.push_back(op);
569 }
570 });
571 for (Operation *op : toErase) {
572 op->dropAllUses();
573 op->erase();
574 }
575
576 for (auto endpoint : llvm::make_early_inc_range(
577 device.getOps<ObjectFifoCoreEndpointOp>())) {
578 endpoint.erase();
579 }
580
581 if (failed(promoteBookkeepingSlots(device))) {
582 return signalPassFailure();
583 }
584 }
585};
586
587} // namespace
588
589std::unique_ptr<OperationPass<DeviceOp>>
591 return std::make_unique<AIEObjectFifoLowerCoresPass>();
592}
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoLowerCoresPass()
constexpr llvm::StringLiteral kObjectFifoObjectSlotAttrName
Discardable attribute set by --aie-objectfifo-lower-cores on the rank-0 memref.allocas that hold a co...
Definition AIEPasses.h:34
mlir::Operation * lookupNamedOp(mlir::Operation *from, mlir::StringAttr name)
constexpr llvm::StringLiteral kObjectFifoUnrollHintAttrName
Discardable attribute set by --aie-objectfifo-lower-cores on scf.for and forever scf....
Definition AIEPasses.h:25
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...