Skip to content

Commit c515d98

Browse files
committed
AIE: promote dynamic objectFifo held counters to SSA via mem2reg
Emit the per-(fifo,port) runtime held-element counters as single-element memref.alloca (tagged aie.held_counter) so acquire/release bookkeeping is plain load/store, then run mem2reg in-pass to promote them to SSA values. Hard-error if any counter survives promotion. Regenerate the dynamic objectFifo lock CHECK lines accordingly.
1 parent afc2c7e commit c515d98

12 files changed

Lines changed: 1097 additions & 1234 deletions

lib/Dialect/AIE/Transforms/AIEObjectFifoStatefulTransform.cpp

Lines changed: 76 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
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.getI32Type());
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.getI32IntegerAttr(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.getI32Type());
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.getI32IntegerAttr(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.getI32IntegerAttr(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.getI32IntegerAttr(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

Comments
 (0)