MLIR-AIE
AIEPlacer.cpp
Go to the documentation of this file.
1//===- AIEPlacer.cpp --------------------------------------------*- C++ -*-===//
2//
3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7// (c) Copyright 2026 Advanced Micro Devices, Inc.
8//
9//===----------------------------------------------------------------------===//
10
12#include "llvm/Support/Debug.h"
13
14#include <algorithm>
15#include <numeric>
16
17using namespace mlir;
18using namespace xilinx::AIE;
19
20#define DEBUG_TYPE "aie-placer"
21
23 this->targetModel = &targetModel;
24
25 // Collect all available physical tiles from device
26 for (int col = 0; col < targetModel.columns(); col++) {
27 for (int row = 0; row < targetModel.rows(); row++) {
28 TileID id = {col, row};
29 AIETileType type = targetModel.getTileType(col, row);
30
31 switch (type) {
32 case AIETileType::CoreTile:
33 availability.compTiles.push_back(id);
34 break;
35 case AIETileType::MemTile:
36 case AIETileType::ShimNOCTile:
37 availability.nonCompTiles.push_back(id);
38 break;
39 default:
40 break;
41 }
42 }
43 }
44
45 // Sort tiles for sequential placement
46 // Compute tiles: column-major (fill column vertically before next column)
47 auto compTileCmp = [](TileID a, TileID b) {
48 if (a.col != b.col)
49 return a.col < b.col;
50 return a.row < b.row;
51 };
52
53 // Non-compute tiles: row-major
54 auto rowMajorCmp = [](TileID a, TileID b) {
55 if (a.row != b.row)
56 return a.row < b.row;
57 return a.col < b.col;
58 };
59
60 std::sort(availability.compTiles.begin(), availability.compTiles.end(),
61 compTileCmp);
62 std::sort(availability.nonCompTiles.begin(), availability.nonCompTiles.end(),
63 rowMajorCmp);
64
65 // Limit cores per column if specified
66 if (coresPerCol.has_value()) {
67 // Calculate max cores per column in the device
68 std::map<int, int> coresInColumn;
69 for (const auto &tile : availability.compTiles) {
70 coresInColumn[tile.col]++;
71 }
72 int maxDeviceCoresPerCol = 0;
73 for (const auto &[col, count] : coresInColumn) {
74 maxDeviceCoresPerCol = std::max(maxDeviceCoresPerCol, count);
75 }
76 deviceCoresPerCol = maxDeviceCoresPerCol;
77
78 limitCoresPerColumn(*coresPerCol, targetModel.columns());
79 }
80}
81
82void SequentialPlacer::limitCoresPerColumn(int maxCoresPerCol, int numColumns) {
83 // Group compute tiles by column
84 std::map<int, std::vector<TileID>> tilesByColumn;
85
86 for (const auto &tile : availability.compTiles) {
87 tilesByColumn[tile.col].push_back(tile);
88 }
89
90 // Build new limited list, taking only first maxCoresPerCol from each column
91 std::vector<TileID> limitedTiles;
92
93 for (int col = 0; col < numColumns; col++) {
94 auto it = tilesByColumn.find(col);
95 if (it == tilesByColumn.end())
96 continue; // No tiles in this column
97
98 const auto &tilesInCol = it->second;
99 size_t numToTake =
100 std::min(tilesInCol.size(), static_cast<size_t>(maxCoresPerCol));
101
102 // Take first N tiles from this column (already sorted within column)
103 limitedTiles.insert(limitedTiles.end(), tilesInCol.begin(),
104 tilesInCol.begin() + numToTake);
105 }
106
107 // Replace availability.compTiles with limited list
108 availability.compTiles = limitedTiles;
109}
110
111LogicalResult SequentialPlacer::place(DeviceOp device) {
112 // Phase 0: Validate options
113 if (coresPerCol.has_value() && *coresPerCol > deviceCoresPerCol) {
114 return device.emitError() << "requested cores-per-col (" << *coresPerCol
115 << ") exceeds device capacity ("
116 << deviceCoresPerCol << " cores per column)";
117 }
118
119 // Phase 1: Collect operations needed for placement
120 SmallVector<LogicalTileOp> logicalTiles;
121 SmallVector<ObjectFifoCreateOp> objectFifos;
122 SmallVector<ObjectFifoLinkOp> objectFifoLinks;
123
124 device.walk([&](Operation *op) {
125 if (auto lt = dyn_cast<LogicalTileOp>(op))
126 logicalTiles.push_back(lt);
127 if (auto of = dyn_cast<ObjectFifoCreateOp>(op))
128 objectFifos.push_back(of);
129 if (auto link = dyn_cast<ObjectFifoLinkOp>(op))
130 objectFifoLinks.push_back(link);
131 });
132
133 // Phase 2: Build channel requirements from ObjectFifo connectivity
134 auto channelRequirements =
135 buildChannelRequirements(objectFifos, objectFifoLinks);
136
137 // Phase 3: Place constrained tiles then compute tiles
138 size_t nextCompIdx = 0;
139 for (auto logicalTile : logicalTiles) {
140 // Place fully constrained tiles at their specified coordinates
141 auto col = logicalTile.tryGetCol();
142 auto row = logicalTile.tryGetRow();
143 if (col && row) {
144 TileID tile{*col, *row};
145 if (failed(validateAndUpdateChannelUsage(logicalTile, tile,
146 channelRequirements, true)))
147 return failure();
148
149 result[logicalTile] = tile;
150 // Only remove fully constrained compute tiles from availability.
151 // Mem/Shim may still host additional logical tiles as long as
152 // channel/DMA capacity permits.
153 if (logicalTile.getTileType() == AIETileType::CoreTile)
154 availability.removeTile(tile, logicalTile.getTileType());
155 continue;
156 }
157
158 // Place compute tiles with partial constraint support
159 if (logicalTile.getTileType() == AIETileType::CoreTile) {
160 std::optional<TileID> placement = std::nullopt;
161
162 for (size_t i = nextCompIdx; i < availability.compTiles.size(); ++i) {
163 TileID candidate = availability.compTiles[i];
164
165 // Check partial constraints
166 if (col && candidate.col != *col)
167 continue;
168 if (row && candidate.row != *row)
169 continue;
170
171 // Found valid tile - swap to nextCompIdx position and use
172 std::swap(availability.compTiles[i],
173 availability.compTiles[nextCompIdx]);
174 placement = availability.compTiles[nextCompIdx++];
175 break;
176 }
177
178 if (!placement) {
179 if (col || row) {
180 return logicalTile.emitError()
181 << "no compute tile available matching constraint ("
182 << (col ? std::to_string(*col) : "?") << ", "
183 << (row ? std::to_string(*row) : "?") << ")";
184 }
185 return logicalTile.emitError(
186 "no available compute tiles for placement");
187 }
188
189 if (failed(validateAndUpdateChannelUsage(logicalTile, *placement,
190 channelRequirements, false)))
191 return failure();
192
193 result[logicalTile] = *placement;
194 }
195
196 if (logicalTile.getTileType() == AIETileType::ShimPLTile) {
197 return logicalTile.emitError(
198 "DMA channel-based SequentialPlacer does not support unplaced "
199 "ShimPLTiles (no DMAs).");
200 }
201 }
202
203 // Phase 4: Place mem/shim tiles by ObjectFifo groups
204 llvm::DenseMap<int, SmallVector<ObjectFifoCreateOp>> groupToFifos;
205 llvm::DenseMap<int, SmallVector<LogicalTileOp>> groupToLogicalTiles;
206 buildObjectFifoGroups(objectFifos, objectFifoLinks, groupToFifos,
207 groupToLogicalTiles);
208
209 // Process each group's logical tiles together
210 llvm::DenseSet<int> processedGroups;
211
212 for (auto &[groupId, logicalTiles] : groupToLogicalTiles) {
213 if (processedGroups.count(groupId))
214 continue;
215 processedGroups.insert(groupId);
216
217 // Compute common column from ALL fifos in this group
218 int groupCommonCol = 0;
219 int totalCoreEndpoints = 0;
220 int sumCols = 0;
221
222 auto fifosIt = groupToFifos.find(groupId);
223 if (fifosIt != groupToFifos.end()) {
224 for (auto ofOp : fifosIt->second) {
225 // Get core tile endpoints for this fifo
226 Value producerTile = ofOp.getProducerTile();
227 if (auto *producerOp = producerTile.getDefiningOp()) {
228 if (auto prodLogical = dyn_cast<LogicalTileOp>(producerOp)) {
229 if (prodLogical.getTileType() == AIETileType::CoreTile) {
230 if (result.count(prodLogical.getOperation())) {
231 sumCols += result[prodLogical.getOperation()].col;
232 totalCoreEndpoints++;
233 }
234 }
235 }
236 }
237
238 for (Value consumerTile : ofOp.getConsumerTiles()) {
239 if (auto *consumerOp = consumerTile.getDefiningOp()) {
240 if (auto consLogical = dyn_cast<LogicalTileOp>(consumerOp)) {
241 if (consLogical.getTileType() == AIETileType::CoreTile) {
242 if (result.count(consLogical.getOperation())) {
243 sumCols += result[consLogical.getOperation()].col;
244 totalCoreEndpoints++;
245 }
246 }
247 }
248 }
249 }
250 }
251 }
252
253 // Compute common column as rounded average of all core tile endpoints.
254 // This places mem/shim tiles near the center of their connected cores
255 // to minimize routing distance.
256 if (totalCoreEndpoints > 0) {
257 groupCommonCol = (sumCols + totalCoreEndpoints / 2) / totalCoreEndpoints;
258 }
259
260 // Place each logical tile from this group
261 for (auto logicalTile : logicalTiles) {
262 // Skip if already placed (e.g., constrained)
263 if (result.count(logicalTile.getOperation()))
264 continue;
265
266 // Get channel requirements for this tile
267 auto it = channelRequirements.find(logicalTile.getOperation());
268 int numInputChannels = 0, numOutputChannels = 0;
269 if (it != channelRequirements.end()) {
270 numInputChannels = it->second.first;
271 numOutputChannels = it->second.second;
272 }
273
274 // Use column constraint if specified, otherwise use group's common column
275 auto colConstraint = logicalTile.tryGetCol();
276 int targetCol = colConstraint ? *colConstraint : groupCommonCol;
277
278 // Find tile with capacity near target column
279 auto maybeTile = findTileWithCapacity(
280 targetCol, availability.nonCompTiles, numInputChannels,
281 numOutputChannels, logicalTile.getTileType());
282
283 if (!maybeTile)
284 return logicalTile.emitError()
285 << "no " << stringifyAIETileType(logicalTile.getTileType())
286 << " with sufficient DMA capacity";
287
288 result[logicalTile] = *maybeTile;
289
290 // Update channel usage
291 if (numInputChannels > 0)
292 updateChannelUsage(*maybeTile, false, numInputChannels);
293 if (numOutputChannels > 0)
294 updateChannelUsage(*maybeTile, true, numOutputChannels);
295 }
296 }
297
298 // Phase 5: Fallback for remaining unplaced non-core tiles
299 for (auto logicalTile : logicalTiles) {
300 // Skip already placed tiles
301 if (result.count(logicalTile.getOperation()))
302 continue;
303
304 // Skip core tiles (handled above) and ShimPLTile (unsupported)
305 AIETileType tileType = logicalTile.getTileType();
306 if (tileType == AIETileType::CoreTile ||
307 tileType == AIETileType::ShimPLTile)
308 continue;
309
310 // Use column constraint if specified, otherwise start from column 0
311 auto colConstraint = logicalTile.tryGetCol();
312 int targetCol = colConstraint ? *colConstraint : 0;
313
314 // Find first available tile of matching type (no DMA requirements)
315 auto maybeTile = findTileWithCapacity(targetCol, availability.nonCompTiles,
316 0, 0, tileType);
317
318 if (!maybeTile)
319 return logicalTile.emitError()
320 << "no " << stringifyAIETileType(tileType) << " available";
321
322 result[logicalTile] = *maybeTile;
323 }
324
325 return success();
326}
327
328LogicalResult SequentialPlacer::validateAndUpdateChannelUsage(
329 LogicalTileOp logicalTile, TileID tile,
330 const llvm::DenseMap<Operation *, std::pair<int, int>> &channelRequirements,
331 bool isConstrained) {
332
333 // Get channel requirements
334 auto it = channelRequirements.find(logicalTile.getOperation());
335 int inChannels = 0, outChannels = 0;
336 if (it != channelRequirements.end()) {
337 inChannels = it->second.first;
338 outChannels = it->second.second;
339 }
340
341 // Validate capacity
342 if (!hasAvailableChannels(tile, inChannels, outChannels)) {
343 // Get max channels
344 int maxIn = logicalTile.getNumDestConnections(WireBundle::DMA);
345 int maxOut = logicalTile.getNumSourceConnections(WireBundle::DMA);
346 int availIn = maxIn - availability.inputChannelsUsed[tile];
347 int availOut = maxOut - availability.outputChannelsUsed[tile];
348
349 auto diag = logicalTile.emitError();
350 if (isConstrained)
351 diag << "tile (" << tile.col << ", " << tile.row << ") requires ";
352 else
353 diag << "tile requires ";
354 diag << inChannels << " input/" << outChannels
355 << " output DMA channels, but only " << availIn << " input/"
356 << availOut << " output available";
357 return failure();
358 }
359
360 // Update channel usage
361 if (inChannels > 0)
362 updateChannelUsage(tile, false, inChannels);
363 if (outChannels > 0)
364 updateChannelUsage(tile, true, outChannels);
365
366 return success();
367}
368
369llvm::DenseMap<Operation *, std::pair<int, int>>
370SequentialPlacer::buildChannelRequirements(
371 SmallVector<ObjectFifoCreateOp> &objectFifos,
372 SmallVector<ObjectFifoLinkOp> &objectFifoLinks) {
373 llvm::DenseMap<Operation *, std::pair<int, int>> channelRequirements;
374
375 // Build map of ObjectFifo name -> CreateOp for link processing
376 llvm::StringMap<ObjectFifoCreateOp> fifoNameToOp;
377 for (auto ofOp : objectFifos) {
378 fifoNameToOp[ofOp.getSymName()] = ofOp;
379 }
380
381 // Build set of ObjectFifos that are involved in links
382 // These will be handled specially for channel counting
383 llvm::DenseSet<llvm::StringRef> linkedFifoNames;
384
385 for (auto linkOp : objectFifoLinks) {
386 for (auto srcFifoAttr : linkOp.getFifoIns()) {
387 auto srcFifoName = cast<FlatSymbolRefAttr>(srcFifoAttr).getValue();
388 linkedFifoNames.insert(srcFifoName);
389 }
390
391 for (auto dstFifoAttr : linkOp.getFifoOuts()) {
392 auto dstFifoName = cast<FlatSymbolRefAttr>(dstFifoAttr).getValue();
393 linkedFifoNames.insert(dstFifoName);
394 }
395 }
396
397 // Count channels for non-linked ObjectFifos normally
398 for (auto ofOp : objectFifos) {
399 // Skip linked fifos - they'll be handled separately
400 if (linkedFifoNames.count(ofOp.getSymName()))
401 continue;
402
403 Value producerTile = ofOp.getProducerTile();
404 auto *producerOp = producerTile.getDefiningOp();
405 auto producerLogicalTile = dyn_cast_or_null<LogicalTileOp>(producerOp);
406
407 // Check if ANY consumer is a different tile type (needs DMA channel)
408 bool producerNeedsDMA = false;
409
410 for (Value consumerTile : ofOp.getConsumerTiles()) {
411 auto *consumerOp = consumerTile.getDefiningOp();
412 auto consumerLogicalTile = dyn_cast_or_null<LogicalTileOp>(consumerOp);
413
414 // Skip core-to-core connections (SequentialPlacer doesn't account for
415 // these)
416 if (producerLogicalTile && consumerLogicalTile &&
417 producerLogicalTile.getTileType() == AIETileType::CoreTile &&
418 consumerLogicalTile.getTileType() == AIETileType::CoreTile)
419 continue;
420
421 // This consumer needs a DMA channel
422 if (consumerOp)
423 channelRequirements[consumerOp].first++; // input++
424
425 producerNeedsDMA = true;
426 }
427
428 // Producer needs ONE output channel if any consumer needs DMA
429 if (producerNeedsDMA && producerOp)
430 channelRequirements[producerOp].second++; // output++
431 }
432
433 // For linked ObjectFifos, count channels based on the link structure
434 // Link tiles need channels for all sources and destinations
435 for (auto linkOp : objectFifoLinks) {
436
437 // Find the tile that fifos are linked on
438 Operation *linkTileOp = nullptr;
439
440 // Get link tile from first source fifo's consumer
441 for (auto srcFifoAttr : linkOp.getFifoIns()) {
442 auto srcFifoName = cast<FlatSymbolRefAttr>(srcFifoAttr).getValue();
443 auto it = fifoNameToOp.find(srcFifoName);
444 if (it == fifoNameToOp.end())
445 continue;
446
447 auto srcFifo = it->second;
448 for (Value consumerTile : srcFifo.getConsumerTiles()) {
449 if (auto *consumerOp = consumerTile.getDefiningOp()) {
450 linkTileOp = consumerOp;
451 break;
452 }
453 }
454 if (linkTileOp)
455 break;
456 }
457
458 if (!linkTileOp)
459 continue;
460
461 // Link tile needs:
462 // - Input channels = number of source ObjectFifos
463 // - Output channels = number of dest ObjectFifos
464 int numInputChannels = linkOp.getFifoIns().size();
465 int numOutputChannels = linkOp.getFifoOuts().size();
466
467 channelRequirements[linkTileOp].first += numInputChannels;
468 channelRequirements[linkTileOp].second += numOutputChannels;
469 }
470
471 return channelRequirements;
472}
473
474void SequentialPlacer::buildObjectFifoGroups(
475 SmallVector<ObjectFifoCreateOp> &objectFifos,
476 SmallVector<ObjectFifoLinkOp> &objectFifoLinks,
477 llvm::DenseMap<int, SmallVector<ObjectFifoCreateOp>> &groupToFifos,
478 llvm::DenseMap<int, SmallVector<LogicalTileOp>> &groupToLogicalTiles) {
479
480 // Build map: ObjectFifo name -> group ID (for linked fifos)
481 llvm::StringMap<int> fifoToGroup;
482 int nextGroupId = 0;
483
484 // Group ObjectFifos that are linked together
485 for (auto linkOp : objectFifoLinks) {
486 int groupId = nextGroupId++;
487
488 // All source fifos belong to same group
489 for (auto srcFifoAttr : linkOp.getFifoIns()) {
490 auto srcFifoName = cast<FlatSymbolRefAttr>(srcFifoAttr).getValue();
491 fifoToGroup[srcFifoName] = groupId;
492 }
493
494 // All dest fifos belong to same group
495 for (auto dstFifoAttr : linkOp.getFifoOuts()) {
496 auto dstFifoName = cast<FlatSymbolRefAttr>(dstFifoAttr).getValue();
497 fifoToGroup[dstFifoName] = groupId;
498 }
499 }
500
501 // Build maps: group ID -> logical tiles and fifos
502 int unlinkedGroupId = nextGroupId;
503
504 for (auto ofOp : objectFifos) {
505 int groupId;
506 auto groupIt = fifoToGroup.find(ofOp.getSymName());
507
508 // Linked fifos share a group, unlinked fifos get individual entries
509 if (groupIt != fifoToGroup.end()) {
510 groupId = groupIt->second;
511 } else {
512 groupId = unlinkedGroupId++;
513 }
514
515 groupToFifos[groupId].push_back(ofOp);
516
517 // Collect non-core tiles from this fifo
518 // Check producer tile
519 Value producerTile = ofOp.getProducerTile();
520 if (auto *producerOp = producerTile.getDefiningOp()) {
521 if (auto prodLogical = dyn_cast<LogicalTileOp>(producerOp)) {
522 if (prodLogical.getTileType() != AIETileType::CoreTile) {
523 groupToLogicalTiles[groupId].push_back(prodLogical);
524 }
525 }
526 }
527
528 // Check consumer tiles
529 for (Value consumerTile : ofOp.getConsumerTiles()) {
530 if (auto *consumerOp = consumerTile.getDefiningOp()) {
531 if (auto consLogical = dyn_cast<LogicalTileOp>(consumerOp)) {
532 if (consLogical.getTileType() != AIETileType::CoreTile) {
533 groupToLogicalTiles[groupId].push_back(consLogical);
534 }
535 }
536 }
537 }
538 }
539}
540
541std::optional<TileID> SequentialPlacer::findTileWithCapacity(
542 int targetCol, std::vector<TileID> &tiles, int requiredInputChannels,
543 int requiredOutputChannels, AIETileType requestedType) {
544 int maxCol = targetModel->columns();
545
546 // Search columns rightward
547 for (int offset = 0; offset < maxCol; ++offset) {
548 int searchCol = targetCol + offset;
549 if (searchCol >= maxCol)
550 continue;
551
552 for (auto &tile : tiles) {
553 AIETileType tileType = targetModel->getTileType(tile.col, tile.row);
554 if (tileType != requestedType)
555 continue;
556
557 if (tile.col == searchCol) {
558 // Check if tile has capacity for both input and output channels
559 if (hasAvailableChannels(tile, requiredInputChannels,
560 requiredOutputChannels)) {
561 return tile;
562 }
563 }
564 }
565 }
566
567 return std::nullopt;
568}
569
570void SequentialPlacer::updateChannelUsage(TileID tile, bool isOutput,
571 int numChannels) {
572 if (isOutput) {
573 availability.outputChannelsUsed[tile] += numChannels;
574 } else {
575 availability.inputChannelsUsed[tile] += numChannels;
576 }
577
578 if (!hasAvailableChannels(tile, 0, 0)) {
579 AIETileType type = targetModel->getTileType(tile.col, tile.row);
580 availability.removeTile(tile, type);
581 }
582}
583
584bool SequentialPlacer::hasAvailableChannels(TileID tile, int inputChannels,
585 int outputChannels) {
586 // Get max channels based on tile type and row
587 int maxIn, maxOut;
588 if (tile.row == 0) {
589 // Shim tiles use ShimMux connections
590 maxIn = targetModel->getNumDestShimMuxConnections(tile.col, tile.row,
591 WireBundle::DMA);
592 maxOut = targetModel->getNumSourceShimMuxConnections(tile.col, tile.row,
593 WireBundle::DMA);
594 } else {
595 // Other tiles use Switchbox connections
596 maxIn = targetModel->getNumDestSwitchboxConnections(tile.col, tile.row,
597 WireBundle::DMA);
598 maxOut = targetModel->getNumSourceSwitchboxConnections(tile.col, tile.row,
599 WireBundle::DMA);
600 }
601
602 int currentIn = availability.inputChannelsUsed[tile];
603 int currentOut = availability.outputChannelsUsed[tile];
604
605 return (currentIn + inputChannels <= maxIn) &&
606 (currentOut + outputChannels <= maxOut);
607}
608
609void TileAvailability::removeTile(TileID tile, AIETileType type) {
610 auto removeFromVector = [&](std::vector<TileID> &vec) {
611 vec.erase(std::remove(vec.begin(), vec.end(), tile), vec.end());
612 };
613
614 switch (type) {
615 case AIETileType::CoreTile:
616 removeFromVector(compTiles);
617 break;
618 case AIETileType::MemTile:
619 case AIETileType::ShimNOCTile:
620 case AIETileType::ShimPLTile:
621 removeFromVector(nonCompTiles);
622 break;
623 default:
624 break;
625 }
626}
virtual uint32_t getNumSourceShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a shimmux.
virtual AIETileType getTileType(int col, int row) const =0
Return the tile type for the given tile coordinates.
virtual int rows() const =0
Return the number of rows in the device.
virtual int columns() const =0
Return the number of columns in the device.
virtual uint32_t getNumDestShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a shimmux.
virtual uint32_t getNumDestSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a switchbox.
virtual uint32_t getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
PlacementResult result
Definition AIEPlacer.h:56
void initialize(const AIETargetModel &targetModel) override
Definition AIEPlacer.cpp:22
mlir::LogicalResult place(DeviceOp device) override
Include the generated interface declarations.
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
std::vector< TileID > compTiles
Definition AIEPlacer.h:27
llvm::DenseMap< TileID, int > outputChannelsUsed
Definition AIEPlacer.h:31
void removeTile(TileID tile, AIETileType type)
std::vector< TileID > nonCompTiles
Definition AIEPlacer.h:28
llvm::DenseMap< TileID, int > inputChannelsUsed
Definition AIEPlacer.h:30