1717#include " mlir/Dialect/SCF/IR/SCF.h"
1818#include " mlir/Dialect/SCF/Utils/Utils.h"
1919#include " mlir/IR/Attributes.h"
20+ #include " mlir/IR/Dominance.h"
2021#include " mlir/Pass/Pass.h"
2122#include " mlir/Transforms/DialectConversion.h"
23+ #include " mlir/Transforms/Mem2Reg.h"
2224
2325#include " mlir/IR/Operation.h"
2426#include " mlir/Interfaces/DataLayoutInterfaces.h"
@@ -2308,6 +2310,9 @@ struct AIEObjectFifoStatefulTransformPass
23082310 //===------------------------------------------------------------------===//
23092311 // Replace ops
23102312 //===------------------------------------------------------------------===//
2313+ // Runtime held-counter allocas collected across all cores. They are
2314+ // promoted to SSA values by mem2reg once all rewriting is complete.
2315+ SmallVector <memref ::AllocaOp > heldCounterAllocas ;
23112316 for (auto coreOp : device.getOps <CoreOp >()) {
23122317 DenseMap<ObjectFifoAcquireOp , std ::vector <BufferOp *>>
23132318 subviews; // maps each "subview" to its buffer references (subviews
@@ -2339,48 +2344,35 @@ struct AIEObjectFifoStatefulTransformPass
23392344 bool isDynamicCore = dynamicLoweringTiles.count(coreOp.getTileOp()) > 0 &&
23402345 device.getTargetModel().hasProperty(
23412346 AIETargetModel::UsesSemaphoreLocks);
2342- BufferOp heldBuffer;
2343- DenseMap<std ::pair <ObjectFifoCreateOp , int >, Value > heldSlotIndex;
2347+ // One runtime "held" counter per (fifo, port), emitted as a
2348+ // single-element memref.alloca so the acquire/release bookkeeping is
2349+ // just plain load/store. These are promoted to SSA values by mem2reg at
2350+ // the end of the pass.
2351+ DenseMap<std ::pair <ObjectFifoCreateOp , int >, memref ::AllocaOp > heldAlloca;
23442352 if (isDynamicCore) {
2345- DenseMap<std ::pair <ObjectFifoCreateOp , int >, int > heldSlot;
2346- // Ordered list of (fifo, port) keys indexed by their slot number, so
2347- // that the counter buffer is initialized deterministically (a plain
2348- // DenseMap iteration order is not stable).
2349- SmallVector<std ::pair <ObjectFifoCreateOp , int >> slotOrder;
2350- auto assignSlot = [&](ObjectFifoCreateOp fifo , ObjectFifoPort p) {
2353+ auto ensureSlot = [&](ObjectFifoCreateOp fifo , ObjectFifoPort p) {
23512354 int pn = p == ObjectFifoPort::Produce ? 0 : 1 ;
2352- if (!heldSlot.count ({fifo , pn })) {
2353- heldSlot[{fifo , pn }] = slotOrder.size();
2354- slotOrder.push_back({fifo , pn });
2355- }
2355+ if (heldAlloca.count({fifo , pn }))
2356+ return;
2357+ OpBuilder::InsertionGuard g(builder);
2358+ builder.setInsertionPointToStart(&(coreOp.getBody().front()));
2359+ auto counterTy = MemRefType::get({}, builder.getI32 Type());
2360+ auto counter =
2361+ memref::AllocaOp::create(builder , coreOp.getLoc(), counterTy);
2362+ counter->setAttr(" aie.held_counter" , builder.getUnitAttr());
2363+ Value zero = arith::ConstantOp::create(builder , coreOp.getLoc(),
2364+ builder.getI32 IntegerAttr(0 ));
2365+ memref::StoreOp::create(builder , coreOp.getLoc(), zero , counter ,
2366+ ValueRange{});
2367+ heldAlloca[{fifo , pn }] = counter;
2368+ heldCounterAllocas.push_back(counter);
23562369 };
23572370 coreOp.walk([&](ObjectFifoAcquireOp a) {
2358- assignSlot (a.getObjectFifo(), a.getPort());
2371+ ensureSlot (a.getObjectFifo(), a.getPort());
23592372 });
23602373 coreOp.walk([&](ObjectFifoReleaseOp r) {
2361- assignSlot (r.getObjectFifo(), r.getPort());
2374+ ensureSlot (r.getObjectFifo(), r.getPort());
23622375 });
2363- if (!slotOrder.empty ()) {
2364- builder.setInsertionPoint(coreOp);
2365- auto heldTy =
2366- MemRefType::get(SmallVector<int64_t >{(int64 _t)slotOrder.size()},
2367- builder.getI32 Type());
2368- heldBuffer = BufferOp::create(
2369- builder , coreOp.getLoc(), heldTy , coreOp.getTile(),
2370- /*sym_name*/ nullptr , /*address*/ nullptr ,
2371- /*initial_value*/ nullptr , /*mem_bank*/ nullptr ,
2372- /*aligned*/ nullptr);
2373- builder.setInsertionPointToStart(&(coreOp.getBody().front()));
2374- Value zero = arith::ConstantOp::create(builder , coreOp.getLoc(),
2375- builder.getI32 IntegerAttr(0 ));
2376- for (int slot = 0 ; slot < (int )slotOrder.size (); ++slot ) {
2377- Value idx = arith::ConstantOp::create(builder , coreOp.getLoc(),
2378- builder.getIndexAttr(slot));
2379- heldSlotIndex[slotOrder [slot ]] = idx;
2380- memref::StoreOp::create(builder , coreOp.getLoc(), zero , heldBuffer ,
2381- ValueRange(ArrayRef({idx })));
2382- }
2383- }
23842376 }
23852377
23862378 //===----------------------------------------------------------------===//
@@ -2423,17 +2415,17 @@ struct AIEObjectFifoStatefulTransformPass
24232415
24242416 // For dynamic tiles, decrement the runtime "held" counter by the
24252417 // number of released elements.
2426- if (isDynamicCore && heldBuffer) {
2427- Value idx = heldSlotIndex[ {op , portNum }];
2428- Value held =
2429- memref::LoadOp::create(builder , releaseOp.getLoc(), heldBuffer ,
2430- ValueRange(ArrayRef({ idx })) );
2418+ if (memref::AllocaOp counter = isDynamicCore
2419+ ? heldAlloca.lookup( {op , portNum })
2420+ : memref::AllocaOp()) {
2421+ Value held = memref::LoadOp::create(builder , releaseOp.getLoc(),
2422+ counter , ValueRange{} );
24312423 Value m = arith::ConstantOp::create(
24322424 builder , releaseOp.getLoc(), builder.getI32 IntegerAttr(numLocks));
24332425 Value newHeld =
24342426 arith::SubIOp::create(builder , releaseOp.getLoc(), held , m);
2435- memref::StoreOp::create(builder , releaseOp.getLoc(), newHeld ,
2436- heldBuffer , ValueRange(ArrayRef({ idx })) );
2427+ memref::StoreOp::create(builder , releaseOp.getLoc(), newHeld , counter ,
2428+ ValueRange{} );
24372429 }
24382430
24392431 // register release op
@@ -2497,14 +2489,15 @@ struct AIEObjectFifoStatefulTransformPass
24972489
24982490 // For dynamic tiles, compute the number of locks to acquire at runtime
24992491 // as max(0, acqNumber - held), using the runtime "held" counter.
2500- if (isDynamicCore && heldBuffer) {
2492+ memref::AllocaOp counter = isDynamicCore
2493+ ? heldAlloca.lookup({op , portNum })
2494+ : memref::AllocaOp();
2495+ if (counter) {
25012496 builder.setInsertionPointAfter(acquireOp);
25022497 int acqNum = acquireOp.acqNumber();
25032498 int repeat = op.getRepeatCount().value_or(1 );
2504- Value idx = heldSlotIndex[{op , portNum }];
2505- Value held =
2506- memref::LoadOp::create(builder , acquireOp.getLoc(), heldBuffer ,
2507- ValueRange(ArrayRef({idx })));
2499+ Value held = memref::LoadOp::create(builder , acquireOp.getLoc(),
2500+ counter , ValueRange{});
25082501 Value nVal = arith::ConstantOp::create(
25092502 builder , acquireOp.getLoc(), builder.getI32 IntegerAttr(acqNum));
25102503 Value zero = arith::ConstantOp::create(builder , acquireOp.getLoc(),
@@ -2523,8 +2516,8 @@ struct AIEObjectFifoStatefulTransformPass
25232516 LockAction::AcquireGreaterEqual , state);
25242517 Value newHeld =
25252518 arith::AddIOp::create(builder , acquireOp.getLoc(), held , delta);
2526- memref::StoreOp::create(builder , acquireOp.getLoc(), newHeld ,
2527- heldBuffer , ValueRange(ArrayRef({ idx })) );
2519+ memref::StoreOp::create(builder , acquireOp.getLoc(), newHeld , counter ,
2520+ ValueRange{} );
25282521
25292522 // Round-robin buffer index per acquired element (see note above).
25302523 for (int i = 0 ; i < acqNum ; i ++) {
@@ -2698,6 +2691,40 @@ struct AIEObjectFifoStatefulTransformPass
26982691 for (auto *op : opsToErase) {
26992692 op->erase();
27002693 }
2694+
2695+ //===------------------------------------------------------------------===//
2696+ // Promote runtime held-counter allocas to SSA values (mem2reg).
2697+ //===------------------------------------------------------------------===//
2698+ // The held counters were emitted as single-element memref.alloca so the
2699+ // acquire/release bookkeeping could be written as plain load/store. Run
2700+ // mem2reg here as an in-pass step so it always executes, then require that
2701+ // every counter was fully promoted: a surviving alloca would mean opaque
2702+ // memory traffic left in the core, which we treat as a hard error.
2703+ if (!heldCounterAllocas.empty ()) {
2704+ SmallVector<PromotableAllocationOpInterface > allocators;
2705+ allocators.reserve(heldCounterAllocas.size());
2706+ for (memref::AllocaOp counter : heldCounterAllocas)
2707+ allocators.push_back(
2708+ cast<PromotableAllocationOpInterface >(counter.getOperation()));
2709+ OpBuilder promoteBuilder(device);
2710+ DominanceInfo dominance;
2711+ const DataLayout dataLayout = DataLayout::closest(device.getOperation());
2712+ (void)tryToPromoteMemorySlots(allocators , promoteBuilder , dataLayout ,
2713+ dominance);
2714+
2715+ bool residualCounter = false ;
2716+ device.walk([&](memref::AllocaOp counter) {
2717+ if (counter->hasAttr(" aie.held_counter" )) {
2718+ counter.emitOpError(
2719+ " dynamic objectFifo held counter could not be promoted to an SSA "
2720+ " value by mem2reg; runtime lock bookkeeping would remain in "
2721+ " memory" );
2722+ residualCounter = true ;
2723+ }
2724+ });
2725+ if (residualCounter)
2726+ return signalPassFailure();
2727+ }
27012728 }
27022729};
27032730
0 commit comments