157 DeviceOp targetOp = getOperation();
159 return signalPassFailure();
162 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
163 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
164 llvm::append_range(memOps, targetOp.getOps<ShimDMAOp>());
170 for (TileElement memOp : memOps) {
171 int col = memOp.getTileID().col;
172 int row = memOp.getTileID().row;
175 gens.try_emplace(std::make_pair(col, row), col, row, targetModel);
177 if (emplaced.second) {
178 for (TileOp tile : targetOp.getOps<TileOp>()) {
179 if (tile.getCol() != col || tile.getRow() != row)
181 if (
auto reserved = tile->getAttrOfType<DenseI32ArrayAttr>(
182 "aiex.reserved_bd_ids"))
183 for (int32_t bdId : reserved.asArrayRef()) {
184 auto id =
static_cast<uint32_t
>(bdId);
190 bool bdIdCollision =
false;
191 memOp->walk<WalkOrder::PreOrder>([&](DMABDOp bd) {
192 if (!bd.getBdId().has_value())
194 uint32_t bdId = bd.getBdId().value();
196 bd.emitOpError() <<
"assigned bd_id " << bdId
197 <<
" is already used by another BD on this tile";
198 bdIdCollision =
true;
204 return signalPassFailure();
209 int col, row, channel;
211 SmallVector<Assignment> assignments;
212 std::map<int32_t, SmallVector<size_t>> receiversOfTag;
213 for (TileElement memOp : memOps) {
214 DenseMap<Block *, int> channels;
215 Region ®ion = memOp->getRegion(0);
216 for (Block &block : region)
217 for (
auto start : block.getOps<DMAStartOp>()) {
218 Block *dest = start.getDest();
219 while (dest && !channels.contains(dest)) {
220 channels[dest] = start.getChannelIndex();
221 dest = dest->hasNoSuccessors() ? nullptr : dest->getSuccessor(0);
224 memOp->walk([&](DMABDOp bd) {
225 auto dma = bd->getParentOfType<DMAOp>();
227 dma ? dma.getChannelIndex() : channels.lookup(bd->getBlock());
228 if (
auto tag = bd.getBdTag())
229 receiversOfTag[*tag].push_back(assignments.size());
230 assignments.push_back(
231 {bd, memOp.getTileID().col, memOp.getTileID().row, channel});
237 for (
auto &[tag, indices] : receiversOfTag) {
238 if (indices.size() < 2)
240 std::optional<uint32_t> fixed;
241 for (
size_t i : indices) {
242 if (
auto id = assignments[i].bd.getBdId()) {
243 if (fixed && *fixed !=
static_cast<uint32_t
>(*
id)) {
244 assignments[i].bd.emitOpError(
245 "broadcast receiving buffers must have matching bd_id values "
248 return signalPassFailure();
253 auto available = [&, &indices = indices](uint32_t id) {
254 std::set<std::pair<int, int>> tiles;
255 for (
size_t i : indices) {
256 auto &a = assignments[i];
257 auto tile = std::make_pair(a.col, a.row);
258 if (!tiles.insert(tile).second ||
259 id >= targetModel.
getNumBDs(a.col, a.row) ||
261 (!a.bd.getBdId() && gens.at(tile).bdIdAlreadyAssigned(
id)))
266 uint32_t
id = fixed.value_or(0);
267 auto &first = assignments[indices.front()];
268 uint32_t limit = targetModel.
getNumBDs(first.col, first.row);
270 while (
id < limit && !available(
id))
272 if (
id >= limit || !available(
id)) {
273 first.bd.emitOpError(
"cannot allocate a common bd_id for broadcast "
274 "receiving buffers with bd_tag ")
276 return signalPassFailure();
278 for (
size_t i : indices) {
279 auto &a = assignments[i];
280 if (!a.bd.getBdId()) {
281 gens.at({a.col, a.row}).assignBdId(
id);
286 for (
auto &a : assignments) {
287 auto &gen = gens.at({a.col, a.row});
288 if (
auto id = a.bd.getBdId()) {
291 <<
"assigned bd_id " << *
id <<
" is not accessible from channel "
292 << a.channel <<
" on this tile";
293 return signalPassFailure();
296 auto nextId = gen.nextBdId(a.channel);
299 <<
"Allocator exhausted available BD IDs (maximum "
301 <<
" available for channel " << a.channel <<
").";
302 return signalPassFailure();
304 a.bd.setBdId(*nextId);
307 for (TileElement memOp : memOps) {
308 auto dmaOps = memOp.getOperation()->getRegion(0).getOps<DMAOp>();
309 if (!dmaOps.empty()) {
310 for (
auto dmaOp : dmaOps) {
311 auto bdRegions = dmaOp.getBds();
312 for (
auto *bdRegionIt = bdRegions.begin();
313 bdRegionIt != bdRegions.end();) {
314 auto &block = bdRegionIt->getBlocks().front();
315 DMABDOp bd = *block.getOps<DMABDOp>().begin();
316 std::optional<int> nextBdId;
317 if (++bdRegionIt != bdRegions.end())
319 (*bdRegionIt->getBlocks().front().getOps<DMABDOp>().begin())
321 else if (dmaOp.getLoop())
322 nextBdId = (*bdRegions.front()
328 bd.setNextBdId(nextBdId);
332 DenseMap<Block *, int> blockBdIdMap;
333 for (Block &block : memOp.getOperation()->getRegion(0)) {
334 if (block.getOps<DMABDOp>().empty())
336 DMABDOp bd = *block.getOps<DMABDOp>().begin();
337 auto bdId = bd.getBdId();
338 assert(bdId.has_value() &&
339 "DMABDOp should have bd_id assigned by now");
340 blockBdIdMap[&block] = *bdId;
343 for (Block &block : memOp.getOperation()->getRegion(0)) {
344 if (block.getOps<DMABDOp>().empty())
346 DMABDOp bd = *block.getOps<DMABDOp>().begin();
347 std::optional<int> nextBdId;
348 if (block.getNumSuccessors()) {
349 assert(llvm::range_size(block.getSuccessors()) == 1 &&
350 "should have only one successor block");
351 Block *nextBlock = block.getSuccessor(0);
352 if (blockBdIdMap.contains(nextBlock)) {
353 nextBdId = blockBdIdMap[nextBlock];
354 }
else if (nextBlock->getOperations().size() != 1 ||
355 !isa<EndOp>(nextBlock->getOperations().front())) {
356 bd.emitOpError(
"must be followed by a block with a dma_bd or "
357 "by a block with only aie.end");
358 return signalPassFailure();
360 bd.setNextBdId(nextBdId);
367 return signalPassFailure();
368 populateRearmBindings(targetOp);
373 DenseMap<int32_t, int> idOfTag;
374 DenseMap<std::pair<Value, int32_t>,
int> localIdOfTag;
375 WalkResult receivers = device.walk([&](DMABDOp bd) {
376 std::optional<uint32_t>
id = bd.getBdId();
377 if (std::optional<int32_t> tag = bd.getBdTag(); tag &&
id) {
378 auto tile = bd->getParentOfType<TileElement>();
380 localIdOfTag[{tile.getTile(), *tag}] = *id;
381 auto [it, inserted] = idOfTag.try_emplace(*tag, *
id);
382 if (!inserted && it->second !=
static_cast<int>(*
id)) {
383 bd.emitOpError(
"broadcast receiving buffers must have matching bd_id "
384 "values for bd_tag ")
386 return WalkResult::interrupt();
389 bd.removeBdTagAttr();
390 return WalkResult::advance();
392 if (receivers.wasInterrupted())
394 WalkResult walked = device.walk([&](DMABDOp bd) {
395 std::optional<int32_t> tag = bd.getOutOfOrderTag();
397 return WalkResult::advance();
398 auto it = idOfTag.find(*tag);
399 if (it == idOfTag.end()) {
400 bd.emitOpError(
"sends out of order into a descriptor that no "
401 "receiving tile holds");
402 return WalkResult::interrupt();
404 bd.setOutOfOrderId(it->second);
405 bd.removeOutOfOrderTagAttr();
406 return WalkResult::advance();
408 if (walked.wasInterrupted())
410 walked = device.walk([&](DmaPushQueueOp push) {
411 std::optional<int32_t> tag = push.getBdTag();
413 return WalkResult::advance();
414 Value tile = push->getParentOfType<CoreOp>().getTile();
415 auto it = localIdOfTag.find({tile, *tag});
416 if (it == localIdOfTag.end()) {
417 push.emitOpError(
"pushes a descriptor that this tile does not hold");
418 return WalkResult::interrupt();
420 push.setBdId(it->second);
421 push.removeBdTagAttr();
422 return WalkResult::advance();
424 return failure(walked.wasInterrupted());