11#include "mlir/Dialect/Arith/IR/Arith.h"
12#include "mlir/Dialect/Func/IR/FuncOps.h"
13#include "mlir/Dialect/MemRef/IR/MemRef.h"
14#include "mlir/Dialect/Utils/StaticValueUtils.h"
15#include "mlir/IR/DialectImplementation.h"
16#include "mlir/IR/Matchers.h"
17#include "mlir/IR/OpDefinition.h"
18#include "mlir/IR/SymbolTable.h"
19#include "mlir/Interfaces/FoldInterfaces.h"
20#include "mlir/Interfaces/ViewLikeInterface.h"
21#include "mlir/Transforms/InliningUtils.h"
22#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallPtrSet.h"
25#include "llvm/ADT/SmallSet.h"
26#include "llvm/ADT/TypeSwitch.h"
27#include "llvm/Support/CheckedArithmetic.h"
28#include "llvm/Support/MathExtras.h"
44#include "aie/Dialect/AIE/IR/AIEDialect.cpp.inc"
46#define GET_TYPEDEF_CLASSES
47#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
51struct AIEInlinerInterface : DialectInlinerInterface {
52 using DialectInlinerInterface::DialectInlinerInterface;
55 bool isLegalToInline(Region *dest, Region *src,
bool wouldBeCloned,
56 IRMapping &valueMapping)
const final {
62 bool isLegalToInline(Operation *op, Region *,
bool wouldBeCloned,
63 IRMapping &)
const final {
69 void handleTerminator(Operation *op, Block *newDest)
const final {}
73 void handleTerminator(Operation *op, ValueRange valuesToRepl)
const final {}
76struct AIEDialectFoldInterface : DialectFoldInterface {
77 using DialectFoldInterface::DialectFoldInterface;
82 bool shouldMaterializeInto(Region *region)
const final {
95 return isa<CoreOp, RuntimeSequenceOp, MemOp, MemTileDMAOp, ShimDMAOp>(
96 region->getParentOp());
102void AIEDialect::initialize() {
104#define GET_TYPEDEF_LIST
105#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
108#define GET_ATTRDEF_LIST
109#include "aie/Dialect/AIE/IR/AIEAttrs.cpp.inc"
113#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
115 addInterfaces<AIEInlinerInterface, AIEDialectFoldInterface>();
122static std::pair<uint32_t, uint32_t>
124 uint32_t burstLength) {
126 std::vector<std::pair<uint32_t, uint32_t>> bel =
131 if (burstLength == 0) {
132 return *llvm::max_element(bel, [](
auto pair1,
auto pair2) {
133 return pair1.second < pair2.second;
139 return *llvm::find_if(bel, [=](
auto p) {
return p.second == burstLength; });
143 uint32_t burstLength) {
145 return getShimBurstLength(tm, burstLength).second;
149 uint32_t burstLength) {
151 return getShimBurstLength(tm, burstLength).first;
156 if (!symbolTableOp->hasTrait<mlir::OpTrait::SymbolTable>() ||
157 symbolTableOp->getRegion(0).empty()) {
162 for (Operation &op : symbolTableOp->getRegion(0).front()) {
163 if (op.getAttrOfType<StringAttr>(mlir::SymbolTable::getSymbolAttrName()) ==
172 : context(symbolTableOp->getContext()) {
173 if (!symbolTableOp->hasTrait<mlir::OpTrait::SymbolTable>() ||
174 symbolTableOp->getRegion(0).empty()) {
177 for (Operation &op : symbolTableOp->getRegion(0).front()) {
178 if (auto name = op.getAttrOfType<StringAttr>(
179 mlir::SymbolTable::getSymbolAttrName())) {
180 ops.try_emplace(name, &op);
188 StringAttr::get(symbolTableOp->getContext(), name));
192 Operation *symbolTableOp = mlir::SymbolTable::getNearestSymbolTable(from);
197 return lookupNamedOp(from, StringAttr::get(from->getContext(), name));
201 unsigned &counter)
const {
204 name = (prefix + llvm::Twine(counter++)).str();
205 }
while (ops.count(StringAttr::get(context, name)));
211 std::array<unsigned, 3> maxRanks = op.getArrayAttrMaxRanks();
217 if (failed(verifyListOfOperandsOrIntegers(
218 op,
"offset", maxRanks[0], op.getStaticOffsets(), op.getOffsets())))
220 if (failed(verifyListOfOperandsOrIntegers(
221 op,
"size", maxRanks[1], op.getStaticSizes(), op.getSizes())))
223 if (failed(verifyListOfOperandsOrIntegers(
224 op,
"stride", maxRanks[2], op.getStaticStrides(), op.getStrides())))
227 const size_t numMixedOffsets = op.getMixedOffsets().size();
228 const size_t numMixedSizes = op.getMixedSizes().size();
229 if (!(numMixedOffsets == 1 && maxRanks[0] == 1) &&
230 numMixedOffsets != numMixedSizes)
231 return op->emitError(
232 "expected mixed offsets rank to match mixed sizes rank (")
233 << numMixedOffsets <<
" vs " << numMixedSizes
234 <<
") so the rank of the result type is well-formed.";
235 for (int64_t offset : op.getStaticOffsets())
236 if (offset < 0 && !ShapedType::isDynamic(offset))
237 return op->emitError(
"expected offsets to be non-negative, but got ")
239 for (int64_t size : op.getStaticSizes())
240 if (size < 0 && !ShapedType::isDynamic(size))
241 return op->emitError(
"expected sizes to be non-negative, but got ")
265 if (
auto t = dyn_cast<AIETarget>(op))
266 return t.getTargetModel();
267 if (
auto t = op->getParentOfType<AIETarget>())
268 return t.getTargetModel();
282 case AIEDevice::xcvc1902:
284 case AIEDevice::xcve2302:
286 case AIEDevice::xcve2802:
288 case AIEDevice::npu1:
290 case AIEDevice::npu1_1col:
292 case AIEDevice::npu1_2col:
294 case AIEDevice::npu1_3col:
296 case AIEDevice::npu2:
298 case AIEDevice::npu2_1col:
299 return NPU2model1col;
300 case AIEDevice::npu2_2col:
301 return NPU2model2col;
302 case AIEDevice::npu2_3col:
303 return NPU2model3col;
304 case AIEDevice::npu2_4col:
305 return NPU2model4col;
306 case AIEDevice::npu2_5col:
307 return NPU2model5col;
308 case AIEDevice::npu2_6col:
309 return NPU2model6col;
310 case AIEDevice::npu2_7col:
311 return NPU2model7col;
312 case AIEDevice::xcve3858:
318 llvm::report_fatal_error(
"getTargetModel: unknown AIEDevice value " +
319 llvm::Twine(
static_cast<uint32_t
>(device)));
324static TileElement getParentTileElement(Operation *op) {
325 auto *parent = op->getParentOp();
326 while (!llvm::isa_and_nonnull<DeviceOp, ModuleOp>(parent)) {
327 if (
auto element = llvm::dyn_cast<TileElement>(parent))
329 parent = parent->getParentOp();
331 return llvm::dyn_cast<TileElement>(parent);
335static int64_t getDimsMaxIdx(ArrayRef<BDDimLayoutAttr> dims) {
337 for (BDDimLayoutAttr dim : dims) {
338 maxIdx += dim.getStride() * (dim.getSize() - 1);
344 int64_t elements = 1;
345 for (BDDimLayoutAttr dim :
inner)
346 elements *= dim.getSize();
351 ArrayRef<BDDimLayoutAttr> dims, ArrayRef<BDPadLayoutAttr> pads,
352 int64_t objectSize, function_ref<InFlightDiagnostic()> emitError,
355 return emitError() <<
"a walk over a stream needs an outer dimension of "
356 "outputs and at least one inner dimension";
359 w.
outputs = dims.front().getSize();
360 int64_t outerStride = dims.front().getStride();
361 if (outerStride % objectSize)
362 return emitError() <<
"a walk over a stream steps whole objects between "
363 "outputs, but its outer stride "
364 << outerStride <<
" is not a multiple of the "
365 << objectSize <<
"-element object";
366 w.
advance = outerStride / objectSize;
367 w.
inner.assign(dims.begin() + 1, dims.end());
370 for (
auto [index, dim] : llvm::enumerate(w.
inner)) {
371 if (dim.getSize() > 1 && dim.getStride() >= objectSize) {
372 if (w.
rowDim >= 0 || dim.getStride() != objectSize)
373 return emitError() <<
"a walk over a stream may step one inner "
374 "dimension by exactly one "
375 << objectSize <<
"-element object";
379 reach += dim.getStride() * (dim.getSize() - 1);
381 if (reach >= objectSize)
382 return emitError() <<
"a walk over a stream reads " << reach + 1
383 <<
" elements of each object, past the " << objectSize
384 <<
"-element object";
387 for (
auto [index, pad] : llvm::enumerate(pads)) {
389 w.
top = pad.getConstPadBefore();
390 w.
bottom = pad.getConstPadAfter();
391 }
else if (pad.getConstPadBefore() || pad.getConstPadAfter()) {
392 return emitError() <<
"a walk over a stream pads only its outer "
393 "dimension, with the objects before and after "
399 return emitError() <<
"a walk over a stream of " << w.
objects
400 <<
" objects with " << w.
top <<
" and " << w.
bottom
401 <<
" padding objects leaves an output without data";
406SmallVector<StreamWindowStep>
408 SmallVector<StreamWindowStep> steps;
409 int64_t acquired = 0, released = 0;
410 for (int64_t j = 0; j < w.
outputs; j++) {
412 int64_t newest = std::max(
413 acquired, start + std::clamp<int64_t>(w.
objects - start, 0, w.
span));
416 ? std::clamp<int64_t>((j + 1) * w.
advance - w.
top, 0, newest)
418 next = std::max(released, next);
419 steps.push_back({j * w.
advance, newest - acquired, next - released});
428struct UsesAreAccessible {
429 static LogicalResult verifyTrait(Operation *op) {
430 auto thisElement = cast<TileElement>(op);
434 if (!isa<TileOp>(thisElement.getTile().getDefiningOp()))
437 auto thisID = thisElement.getTileID();
438 auto users = op->getResult(0).getUsers();
440 for (
auto *user : users) {
443 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>(user->getParentOp())) {
452 TileElement element = llvm::dyn_cast<TileElement>(user);
454 element = getParentTileElement(user);
458 return op->emitOpError(
"is accessed outside of a tile")
459 .attachNote(user->getLoc())
462 auto tileID = element.getTileID();
463 if (!targetModel.isLegalMemAffinity(tileID.col, tileID.row, thisID.col,
465 return (op->emitOpError(
"in Column ")
466 << thisID.col <<
" and Row " << thisID.row
467 <<
" is accessed from an unreachable tile in Column "
468 << tileID.col <<
" and Row " << tileID.row)
469 .attachNote(user->getLoc())
481template <
typename... TerminatorOpTypes>
484 for (
auto ®ion : op->getRegions()) {
485 for (
auto &block : region) {
486 if (!block.empty()) {
487 if (Operation *operation = &block.back();
488 !llvm::isa_and_nonnull<TerminatorOpTypes...>(operation))
489 return operation->emitOpError(
"is not an allowed terminator")
490 .attachNote(op->getLoc())
491 .append(
"in this context: ");
501template <
typename ConcreteType>
503 auto element = cast<ConcreteType>(op);
506 TileLike tile = element.getTileLike();
508 return op->emitOpError(
"tile must implement TileLike interface");
510 int bdMax = targetModel.getNumBDs(tile.getTileType());
513 for (
auto &block : element.getBody()) {
514 auto bdOps = llvm::to_vector_of<DMABDOp>(block.template getOps<DMABDOp>());
521 if (bdNum >= bdMax) {
522 return (op->emitOpError(
"has more than ") << bdMax <<
" blocks")
523 .attachNote(bdOps.front().getLoc())
524 .append(
"no space for this BD");
529 if (bdOps.size() != 1) {
530 return (op->emitOpError(
"BD block must have exactly one DMABDOp, found ")
532 .attachNote(block.front().getLoc())
533 .append(
"in this BD block");
538 llvm::to_vector_of<UseLockOp>(block.template getOps<UseLockOp>());
539 int acquireCount = 0;
540 int releaseCount = 0;
541 for (
auto useLock : useLockOps) {
542 if (useLock.acquire() || useLock.acquireGE())
544 else if (useLock.release())
548 if (acquireCount > 1) {
549 return (op->emitOpError(
550 "BD block must have at most one acquire UseLockOp, found ")
552 .attachNote(block.front().getLoc())
553 .append(
"in this BD block");
555 if (releaseCount > 1) {
556 return (op->emitOpError(
557 "BD block must have at most one release UseLockOp, found ")
559 .attachNote(block.front().getLoc())
560 .append(
"in this BD block");
568template <
typename ConcreteType>
570 DenseSet<DMAChannel> inputChannels;
571 DenseSet<DMAChannel> outputChannels;
572 DenseSet<std::pair<DMAChannelDir, StringAttr>> namedChannels;
573 auto element = cast<ConcreteType>(op);
574 Region &body = element.getBody();
576 return op->emitOpError(
"should have non-empty body");
577 for (
auto &bodyOp : body.getOps()) {
579 if (
auto dmaStart = dyn_cast<DMAStartOp>(bodyOp)) {
580 if (FlatSymbolRefAttr endpoint = dmaStart.getEndpoint()) {
581 namedChannels.insert({dmaStart.getChannelDir(), endpoint.getAttr()});
584 DMAChannel dmaChan = {dmaStart.getChannelDir(),
585 dmaStart.getChannelIndex()};
588 if (dmaChan.direction == DMAChannelDir::S2MM)
589 inputChannels.insert(dmaChan);
591 outputChannels.insert(dmaChan);
595 TileLike tile = element.getTileLike();
597 return op->emitOpError(
"tile must implement TileLike interface");
599 auto named = [&](DMAChannelDir dir) ->
size_t {
600 return llvm::count_if(namedChannels,
601 [&](
auto channel) {
return channel.first == dir; });
603 if (inputChannels.size() + named(DMAChannelDir::S2MM) >
604 tile.getNumSourceConnections(WireBundle::DMA))
605 return op->emitOpError(
606 "uses more input channels than available on this tile");
608 if (outputChannels.size() + named(DMAChannelDir::MM2S) >
609 tile.getNumDestConnections(WireBundle::DMA))
610 return op->emitOpError(
611 "uses more output channels than available on this tile");
620ObjectFifoTransportAttr::verify(function_ref<InFlightDiagnostic()> emitError,
621 ObjectFifoTransportMode mode,
622 PacketInfoAttr packet) {
625 if (packet && mode == ObjectFifoTransportMode::SharedMem)
626 return emitError() <<
"`packet` belongs to a dma or auto transport, not "
627 << stringifyObjectFifoTransportMode(mode);
636ObjectFifoEndPortAttr::verify(function_ref<InFlightDiagnostic()> emitError,
638 std::optional<uint32_t> channel) {
639 if (bundle != WireBundle::DMA && bundle != WireBundle::Core)
640 return emitError() <<
"an objectFifo end is driven by a DMA channel or a "
641 "Core stream port, not "
642 << stringifyWireBundle(bundle);
643 if (bundle == WireBundle::Core) {
645 return emitError() <<
"a Core end needs its stream port";
647 return emitError() <<
"a Core end's stream port is 0 or 1, not "
657LogicalResult ObjectFifoCreateOp::verify() {
658 for (Type fifoType : {Type(getElemType()), getConsumerElemTypeOrDefault()})
659 if (MemRefType memref = cast<AIEObjectFifoType>(fifoType).getElementType();
660 !memref.hasStaticShape())
661 return emitOpError(
"type should be static shaped memref, but got ")
667 static constexpr std::pair<llvm::StringLiteral, llvm::StringLiteral>
669 {
"via_DMA",
"transport = #aie.transport<dma>"},
670 {
"packet",
"transport = #aie.transport<dma, packet = "
671 "#aie.packet_info<>>"},
672 {
"packet_id",
"transport = #aie.transport<dma, packet = "
673 "#aie.packet_info<pkt_id = 3>>"},
674 {
"aie_stream",
"prod_port = #aie.end_port<Core : 0> or cons_ports "
675 "= [#aie.end_port<Core : 0>]"},
676 {
"aie_stream_port",
"prod_port = #aie.end_port<Core : 0> or "
677 "cons_ports = [#aie.end_port<Core : 0>]"},
678 {
"prod_dma_channel",
"prod_port = #aie.end_port<DMA : 1>"},
679 {
"cons_dma_channels",
"cons_ports = [#aie.end_port<DMA : 1>]"}};
680 for (
auto [gone, replacement] : replaced)
681 if ((*this)->hasAttr(gone))
682 return emitOpError(
"`")
683 << gone <<
"` has been replaced; write " << replacement;
685 if (isa<ArrayAttr>(getElemNumber())) {
686 if (
size_t numDepths = dyn_cast<ArrayAttr>(getElemNumber()).size();
687 numDepths != getConsumerTiles().size() + 1)
688 return emitOpError(
"does not have enough depths specified for producer "
689 "and for each consumer.");
692 if (ArrayAttr consPorts = getConsPortsAttr();
693 consPorts && consPorts.size() != getConsumerTiles().size())
694 return emitOpError(
"`cons_ports` has ")
695 << consPorts.size() <<
" entries for " << getConsumerTiles().size()
699 auto getTileLikeFromValue = [](Value v) -> TileLike {
700 return llvm::dyn_cast<TileLike>(v.getDefiningOp());
703 TileLike producerTile = getTileLikeFromValue(getProducerTile());
705 return emitError(
"producer tile must implement TileLike interface");
708 if (producerTile.isShimTile() && !getDimensionsToStream().empty()) {
710 "`dimensionsToStream` data layout transformations are not supported "
711 "on shim tile producers");
713 for (
auto consTileVal : getConsumerTiles()) {
714 TileLike consTile = getTileLikeFromValue(consTileVal);
716 return emitError(
"consumer tile must implement TileLike interface");
717 if (consTile.isShimTile() &&
718 !getDimensionsFromStream(consTileVal).empty()) {
720 "`dimensionsFromStreamPerConsumer` data layout transformations are "
721 "not supported on shim tile consumers");
725 if (getRepeatCount().has_value()) {
726 if (producerTile.isShimTile())
727 return emitError(
"`repeat_count` unavailable for shim tiles");
730 if (getPadValue() != 0) {
731 if (!getPadDimensions().has_value())
732 return emitError(
"`padValue` requires `padDimensions`");
734 return emitError(
"`padValue` requires the CONSTANT_PAD_VALUE register, "
735 "unavailable on this target");
739 if (getConsumerTiles().size() > 1)
740 return emitError(
"a Core stream port end can only be used in 1-to-1 "
743 if (forcesSharedMemory())
744 return emitError(
"a Core stream port end sends over the stream, not "
745 "through shared memory");
747 if (streamsFromProducer()) {
748 if (producerTile.isShimTile() || producerTile.isMemTile())
750 "a Core stream port end is not available for shim and mem tiles");
752 if (getRepeatCount().has_value())
753 return emitError(
"`repeat_count` unavailable on stream end");
755 if (getInitValues().has_value())
756 return emitError(
"`init_values` unavailable on stream end");
758 if (getIterCount().has_value())
759 return emitError(
"`iter_count` unavailable on stream end");
761 if (!getDimensionsToStream().empty())
762 return emitError(
"`dimensionsToStream` data layout transformations are "
763 "unavailable on stream end");
766 if (streamsToConsumer(0)) {
767 TileLike consTile = getTileLikeFromValue(getConsumerTiles()[0]);
768 if (consTile && (consTile.isShimTile() || consTile.isMemTile()))
770 "a Core stream port end is not available for shim and mem tiles");
773 if (!getDimensionsFromStreamPerConsumer()[0].empty())
774 return emitError(
"`dimensionsFromStreamPerConsumer` data layout "
775 "transformations are unavailable on stream end");
778 if (getInitValues().has_value()) {
779 if (producerTile.isShimTile())
780 return emitError(
"`init_values` unavailable for shim tiles");
783 if (
auto initValues = getInitValues()) {
784 if ((
int)initValues->size() != size())
785 return emitError(
"`init_values` does not initialize all objects");
788 if (
auto iterCountAttr = getIterCount()) {
789 int iterCount = *iterCountAttr;
790 if (iterCount < 1 || iterCount > 256)
791 return emitError(
"`iter_count` must be between 1 and 256");
794 if (
auto consumerElemType = getConsumerElemType()) {
795 auto consType = llvm::dyn_cast<AIEObjectFifoType>(*consumerElemType);
797 return emitError(
"consumer element type must be an "
798 "!aie.objectfifo<memref<...>> type");
799 auto prodType = llvm::cast<AIEObjectFifoType>(getElemType());
800 auto prodMemref = prodType.getElementType();
801 auto consMemref = consType.getElementType();
802 if (prodMemref.getElementType() != consMemref.getElementType())
803 return emitError(
"producer and consumer must have the same scalar "
804 "element type, but got ")
805 << prodMemref.getElementType() <<
" vs "
806 << consMemref.getElementType();
807 int64_t prodSize = prodMemref.getNumElements();
808 int64_t consSize = consMemref.getNumElements();
810 return emitError(
"consumer element count must be positive");
811 if (prodSize % consSize != 0)
812 return emitError(
"producer element size (")
813 << prodSize <<
") must be an integer multiple of consumer "
814 <<
"element size (" << consSize <<
")";
820TileOp ObjectFifoCreateOp::getProducerTileOp() {
821 return cast<TileOp>(getProducerTile().getDefiningOp());
831template <
typename OpTy>
832SmallVector<OpTy> lookupAll(Operation *from, std::optional<ArrayAttr> names) {
833 SmallVector<OpTy> ops;
835 for (
auto name : names->getAsRange<FlatSymbolRefAttr>()) {
837 dyn_cast_or_null<OpTy>(
lookupNamedOp(from, name.getAttr()))) {
847TileLike ObjectFifoPoolOp::getTileLike() {
848 return dyn_cast<TileLike>(getTile().getDefiningOp());
851int64_t ObjectFifoPoolOp::getObjectSize() {
852 return llvm::cast<MemRefType>(getElemType()).getNumElements();
855int64_t ObjectFifoPoolOp::getObjectsPerBuffer() {
856 if (!getContiguous())
858 int64_t objects = getDepth();
859 if (
auto zeros = getZeroObjects())
860 objects += (*zeros)[0] + (*zeros)[1];
864LogicalResult ObjectFifoPoolOp::verify() {
865 auto &target = (*this)->getParentOfType<DeviceOp>().
getTargetModel();
870 if (
auto locks = getLocks()) {
871 if (semaphoreLocks) {
872 return emitOpError(
"'locks' names binary locks, which this device has "
875 if (
static_cast<int64_t
>(locks->size()) != getDepth()) {
876 return emitOpError(
"expects one lock per buffer");
880 if (
auto zeros = getZeroObjects()) {
881 if (!getContiguous() || zeros->size() != 2 ||
882 llvm::any_of(*zeros, [](int32_t count) { return count < 0; }))
883 return emitOpError(
"'zeroObjects' expects [before, after] counts on a "
887 if (
auto buffers = getBuffers()) {
888 if (getContiguous() && buffers->size() != 1) {
889 return emitOpError(
"expects one buffer holding its objects side by side");
891 if (!getContiguous() &&
892 static_cast<int64_t
>(buffers->size()) != getDepth()) {
893 return emitOpError(
"expects 'depth' buffers");
897 if (
auto initValues = getInitValues()) {
898 if (
static_cast<int64_t
>(initValues->size()) > getDepth()) {
899 return emitOpError(
"has more 'init_values' than 'depth' objects");
903 std::vector<ObjectFifoSegmentOp> segments = getSegmentOps();
904 if (segments.empty()) {
905 return emitOpError(
"expects at least one segment");
908 int64_t previous = -1;
910 for (ObjectFifoSegmentOp segment : segments) {
911 if (
static_cast<int64_t
>(segment.getOffset()) <= previous) {
912 return emitOpError(
"segments must be ordered by increasing offset");
914 if (
static_cast<int64_t
>(segment.getOffset()) != covered) {
915 return segment.emitOpError(
"leaves a gap or overlaps the segment before "
916 "it; segments must tile the object");
918 previous = segment.getOffset();
919 covered = segment.getOffset() + segment.getSize();
920 if (!semaphoreLocks &&
921 (segment.getProduceLock() || segment.getConsumeLock())) {
922 return emitOpError(
"segment locks are counting locks, which this "
923 "device has no use for");
927 if (covered != getObjectSize()) {
928 return emitOpError(
"segments cover ")
929 << covered <<
" of " << getObjectSize() <<
" elements";
932 if (segments.size() > 1 && !semaphoreLocks) {
934 "multi-segment pools are unsupported on binary lock architectures");
940int64_t ObjectFifoPoolOp::getObjectSizeInBytes() {
941 MemRefType elemType = getElemType();
942 DataLayout layout = DataLayout::closest(*
this);
943 return elemType.getNumElements() *
944 layout.getTypeSize(elemType.getElementType());
947std::vector<ObjectFifoSegmentOp> ObjectFifoPoolOp::getSegmentOps() {
948 auto ops = getSegments().getOps<ObjectFifoSegmentOp>();
949 return {ops.begin(), ops.end()};
952Value BufferOp::getBufferTile() {
return getTile(); }
954SmallVector<BufferLike> ObjectFifoPoolOp::getBufferOps() {
957 return lookupAll<BufferLike>((*this)->getParentOfType<DeviceOp>(),
961SmallVector<LockOp> ObjectFifoPoolOp::getLockOps() {
962 auto device = (*this)->getParentOfType<DeviceOp>();
963 SmallVector<LockOp> locks = lookupAll<LockOp>(device, getLocks());
964 for (ObjectFifoSegmentOp segment : getSegmentOps()) {
965 for (std::optional<FlatSymbolRefAttr> name :
966 {segment.getProduceLockAttr(), segment.getConsumeLockAttr()}) {
969 locks.push_back(lock);
977StringRef ObjectFifoPoolOp::getBaseName() {
978 StringRef name = getSymName();
979 return name.consume_back(
"_pool") ? name : getSymName();
988LogicalResult verifyEndpoint(Operation *op, ObjectFifoPoolOp pool,
989 std::optional<ArrayAttr> segments) {
990 std::vector<ObjectFifoSegmentOp> all = pool.getSegmentOps();
992 if (all.size() > 1) {
993 return op->emitOpError(
994 "must list segments explicitly for a multi-segment pool");
998 if (segments->empty()) {
999 return op->emitOpError(
"expects at least one segment");
1002 int64_t previous = -1;
1003 for (
auto name : segments->getAsRange<FlatSymbolRefAttr>()) {
1004 auto segment = SymbolTable::lookupNearestSymbolFrom<ObjectFifoSegmentOp>(
1005 pool, name.getAttr());
1007 return op->emitOpError(
"references undefined segment '")
1008 << name.getValue() <<
"' in pool '" << pool.getSymName() <<
"'";
1012 if (
static_cast<int64_t
>(segment.getOffset()) <= previous) {
1013 return op->emitOpError(
"segments must be named in increasing offset "
1016 previous = segment.getOffset();
1021ObjectFifoPoolOp lookupPool(Operation *op, StringRef name) {
1022 auto device = op->getParentOfType<DeviceOp>();
1023 return SymbolTable::lookupNearestSymbolFrom<ObjectFifoPoolOp>(
1024 device, StringAttr::get(op->getContext(), name));
1031static std::vector<ObjectFifoSegmentOp>
1032selectSegments(ObjectFifoPoolOp pool, std::optional<ArrayAttr> selected) {
1036 std::vector<ObjectFifoSegmentOp> all = pool.getSegmentOps();
1038 return all.empty() ? std::vector<ObjectFifoSegmentOp>{}
1039 : std::vector<ObjectFifoSegmentOp>{all.front()};
1041 std::vector<ObjectFifoSegmentOp> chosen;
1042 for (
auto name : selected->getAsRange<FlatSymbolRefAttr>()) {
1044 SymbolTable::lookupNearestSymbolFrom<ObjectFifoSegmentOp>(
1045 pool, name.getAttr())) {
1046 chosen.push_back(segment);
1052TileLike ObjectFifoCoreEndpointOp::getTileLike() {
1053 return dyn_cast<TileLike>(getTile().getDefiningOp());
1056ObjectFifoPoolOp ObjectFifoCoreEndpointOp::getPoolOp() {
1057 return lookupPool(*
this, getPool());
1060std::vector<ObjectFifoSegmentOp>
1061ObjectFifoCoreEndpointOp::getSelectedSegments() {
1062 return selectSegments(getPoolOp(), getSegments());
1065LogicalResult ObjectFifoCoreEndpointOp::verify() {
1066 ObjectFifoPoolOp pool = getPoolOp();
1068 return emitOpError(
"references undefined pool '") << getPool() <<
"'";
1070 if (failed(verifyEndpoint(*
this, pool, getSegments()))) {
1077 for (ObjectFifoSegmentOp segment : getSelectedSegments()) {
1078 if (next >= 0 && segment.getOffset() != next) {
1079 return emitOpError(
"a core endpoint's segments must be contiguous");
1081 next = segment.getOffset() + segment.getSize();
1086std::pair<int64_t, int64_t> ObjectFifoCoreEndpointOp::getExtent() {
1087 std::vector<ObjectFifoSegmentOp> selected = getSelectedSegments();
1088 if (selected.empty()) {
1091 int64_t offset = selected.front().getOffset();
1092 int64_t end = selected.back().getOffset() + selected.back().getSize();
1093 return {offset, end - offset};
1096MemRefType ObjectFifoCoreEndpointOp::getAccessType() {
1097 MemRefType elemType = getPoolOp().getElemType();
1098 auto [offset, size] = getExtent();
1099 if (offset == 0 && size == elemType.getNumElements()) {
1102 return cast<MemRefType>(memref::SubViewOp::inferRankReducedResultType(
1103 {size}, elemType, {offset}, {size}, {1}));
1106TileLike ObjectFifoDmaEndpointOp::getTileLike() {
1107 return dyn_cast<TileLike>(getTile().getDefiningOp());
1110ObjectFifoPoolOp ObjectFifoDmaEndpointOp::getPoolOp() {
1111 return lookupPool(*
this, getPool());
1114std::vector<ObjectFifoSegmentOp>
1115ObjectFifoDmaEndpointOp::getSelectedSegments() {
1116 return selectSegments(getPoolOp(), getSegments());
1119int64_t ObjectFifoDmaEndpointOp::getNumDescriptors() {
1120 ObjectFifoPoolOp pool = getPoolOp();
1123 if (pool.getContiguous()) {
1125 if (!walksStream()) {
1126 SmallVector<ObjectFifoDmaEndpointOp> ends =
turnEndpoints(*
this);
1129 ArrayRef<Attribute> turns = getTurnsAttr().getValue();
1130 SmallVector<std::pair<int64_t, int64_t>> counts;
1131 for (ObjectFifoDmaEndpointOp end : ends)
1133 {llvm::count(turns, FlatSymbolRefAttr::get(end.getSymNameAttr())),
1134 end.getPoolOp().getContiguous() ? 0 : end.getNumDescriptors()});
1135 return llvm::count(turns, FlatSymbolRefAttr::get(getSymNameAttr())) *
1139 return succeeded(getStreamWindow(window)) ? window.
outputs : 0;
1141 return pool.getDepth() * getSelectedSegments().size();
1144bool ObjectFifoDmaEndpointOp::walksStream() {
1145 ObjectFifoPoolOp pool = getPoolOp();
1146 auto dims = getDimensions();
1147 return pool && pool.getContiguous() && drains() && dims && !dims->empty() &&
1148 !(*dims)[0].getValue().empty();
1151LogicalResult ObjectFifoDmaEndpointOp::getStreamWindow(
StreamWindow &window) {
1152 auto dims = getDimensions();
1153 if (!dims || dims->empty())
1154 return emitOpError(
"walks no stream: it has no dimensions");
1155 ArrayRef<BDPadLayoutAttr> pads;
1156 if (
auto padding = getPadDimensions(); padding && !padding->empty())
1157 pads = (*padding)[0].getValue();
1159 getPoolOp().getObjectSize(),
1160 [&] {
return emitOpError(); }, window);
1163int64_t ObjectFifoDmaEndpointOp::getRepeat() {
1164 ObjectFifoPoolOp pool = getPoolOp();
1166 return pool && drains() ? pool.getRepeatCount().value_or(1) : 1;
1169bool ObjectFifoDmaEndpointOp::repeatsInHardware() {
1170 return getRepeat() > 1 && getNumDescriptors() == 1 && !getIterCount();
1173int64_t ObjectFifoDmaEndpointOp::getDispatchPasses() {
1174 ObjectFifoPoolOp pool = getPoolOp();
1175 std::optional<ArrayAttr> turns = getDispatch();
1176 if (!pool || !turns || turns->empty())
1178 int64_t depth = std::max<int64_t>(pool.getDepth(), 1);
1179 return std::lcm(depth,
static_cast<int64_t
>(turns->size())) / depth;
1182SmallVector<ObjectFifoDmaEndpointOp>
1184 SmallVector<ObjectFifoDmaEndpointOp> ends;
1185 if (std::optional<ArrayAttr> turns = endpoint.getTurns())
1186 for (
auto name : turns->getAsRange<FlatSymbolRefAttr>())
1188 SymbolTable::lookupNearestSymbolFrom<ObjectFifoDmaEndpointOp>(
1190 end && !llvm::is_contained(ends, end))
1191 ends.push_back(end);
1197 for (
auto [appearances, count] : ends)
1199 passes = std::lcm(passes, count / std::gcd(count, appearances));
1203int64_t ObjectFifoDmaEndpointOp::getNumBDs() {
1204 SmallVector<ObjectFifoDmaEndpointOp> ends =
turnEndpoints(*
this);
1206 return getNumDescriptors() * (repeatsInHardware() ? 1 : getRepeat()) *
1207 getDispatchPasses();
1208 if (ends.front() != *
this)
1210 ArrayRef<Attribute> turns = getTurnsAttr().getValue();
1211 SmallVector<std::pair<int64_t, int64_t>> counts;
1212 for (ObjectFifoDmaEndpointOp end : ends)
1214 {llvm::count(turns, FlatSymbolRefAttr::get(end.getSymNameAttr())),
1215 end.getPoolOp().getContiguous() ? 0 : end.getNumDescriptors()});
1216 return turnPasses(counts) *
static_cast<int64_t
>(turns.size());
1219DMAChannelDir ObjectFifoDmaEndpointOp::getRouteDirection() {
1220 return drains() ? DMAChannelDir::MM2S : DMAChannelDir::S2MM;
1223WireBundle ObjectFifoDmaEndpointOp::getRouteBundle() {
return WireBundle::DMA; }
1225std::optional<int> ObjectFifoDmaEndpointOp::getRouteChannel() {
1226 return getChannelIndex();
1229void ObjectFifoDmaEndpointOp::setRouteChannel(
int channel) {
1230 setChannelIndex(channel);
1233LogicalResult ObjectFifoDmaEndpointOp::verify() {
1234 ObjectFifoPoolOp pool = getPoolOp();
1236 return emitOpError(
"references undefined pool '") << getPool() <<
"'";
1239 if (failed(verifyEndpoint(*
this, pool, getSegments()))) {
1246 if (std::optional<ArrayAttr> turns = getDispatch()) {
1248 return emitOpError(
"only a draining endpoint can dispatch");
1250 return emitOpError(
"dispatch names no destination");
1251 if (getPacketAttr())
1252 return emitOpError(
"a dispatching endpoint carries a header per turn in "
1253 "`dispatchPackets`, not one `packet`");
1254 if (pool.getRepeatCount().value_or(1) > 1)
1255 return emitOpError(
"cannot dispatch a pool that repeats its objects");
1256 if (std::optional<int32_t> iterCount = getIterCount();
1257 iterCount && *iterCount % std::max<int64_t>(getDispatchPasses(), 1))
1258 return emitOpError(
"iterCount ")
1259 << *iterCount <<
" is not a whole number of dispatch rounds ("
1260 << getDispatchPasses() <<
" passes over the pool)";
1262 if (std::optional<ArrayAttr> headers = getDispatchPackets()) {
1263 std::optional<ArrayAttr> turns = getDispatch();
1264 if (!turns || turns->size() != headers->size())
1265 return emitOpError(
"dispatchPackets needs one header per dispatch turn");
1266 for (
auto header : headers->getAsRange<PacketInfoAttr>())
1270 if (std::optional<ArrayAttr> turns = getTurns()) {
1271 if (!llvm::is_contained(*turns, FlatSymbolRefAttr::get(getSymNameAttr())))
1272 return emitOpError(
"takes no turn of its own in `turns`");
1273 for (
auto name : turns->getAsRange<FlatSymbolRefAttr>())
1274 if (!SymbolTable::lookupNearestSymbolFrom<ObjectFifoDmaEndpointOp>(*
this,
1276 return emitOpError(
"turns names ")
1277 << name <<
", which is no objectfifo.dma_endpoint";
1278 if (getDispatch() || getPacketAttr() || getIterCount() || getRepeat() > 1)
1279 return emitOpError(
"taking turns on a channel, it cannot also dispatch, "
1280 "carry a header, stop after iterCount or repeat");
1282 if (end.getTile() != getTile() || end.getRole() != getRole() ||
1283 end.getTurnsAttr() != getTurnsAttr() ||
1284 end.getChannelIndexAttr() != getChannelIndexAttr())
1285 return emitOpError(
"takes turns with ")
1286 << end.getSymNameAttr()
1287 <<
", which is not on this tile and channel with the same turns";
1288 if (pool.getContiguous() && !walksStream() &&
1289 pool.getDepth() % getNumDescriptors())
1290 return emitOpError(
"walks a contiguous pool of ")
1291 << pool.getDepth() <<
" objects in " << getNumDescriptors()
1292 <<
" turns a pass, which do not divide them";
1295 std::optional<ArrayAttr> segmentNames = getSegments();
1296 size_t selectedCount = segmentNames ? segmentNames->size() : 1;
1297 auto dimensions = getDimensions();
1298 if (dimensions && dimensions->size() != selectedCount) {
1299 return emitOpError(
"dimensions has ")
1300 << dimensions->size() <<
" entries for " << selectedCount
1301 <<
" selected segments";
1303 auto padding = getPadDimensions();
1304 if (padding && padding->size() != selectedCount) {
1305 return emitOpError(
"padDimensions has ")
1306 << padding->size() <<
" entries for " << selectedCount
1307 <<
" selected segments";
1309 if (padding && !drains()) {
1310 return emitOpError(
"padDimensions is only valid on a draining endpoint");
1312 if (getPadValue() != 0 &&
1313 (!padding || llvm::none_of(*padding, [](BDPadLayoutArrayAttr entry) {
1314 return !entry.empty();
1316 return emitOpError(
"nonzero padValue requires a non-empty padDimensions "
1323 return getStreamWindow(window);
1325 std::vector<ObjectFifoSegmentOp> selected = getSelectedSegments();
1326 for (
size_t position = 0; position < selectedCount; ++position) {
1327 BDDimLayoutArrayAttr dims =
1328 dimensions ? (*dimensions)[position] : BDDimLayoutArrayAttr();
1329 BDPadLayoutArrayAttr pads =
1330 padding ? (*padding)[position] : BDPadLayoutArrayAttr();
1331 if (pads && !pads.empty() && (!dims || dims.empty())) {
1332 return emitOpError(
"padDimensions entry ")
1333 << position <<
" requires dimensions for the same segment";
1335 if (dims && pads && !pads.empty() && dims.size() != pads.size()) {
1336 return emitOpError(
"dimensions and padDimensions entry ")
1337 << position <<
" have different ranks";
1339 if (dims && !dims.empty() &&
1340 getDimsMaxIdx(dims) >= selected[position].getSize()) {
1341 return emitOpError(
"dimensions entry ")
1342 << position <<
" exceeds selected segment '"
1343 << selected[position].getSymName() <<
"' of size "
1344 << selected[position].getSize();
1350TileLike RouteEndpointOp::getTileLike() {
1351 return dyn_cast<TileLike>(getTile().getDefiningOp());
1354DMAChannelDir RouteEndpointOp::getRouteDirection() {
1357 auto device = getOperation()->getParentOfType<DeviceOp>();
1358 StringRef name = getSymName();
1360 for (
auto flow : device.getOps<RouteOp>()) {
1361 if (flow.namesAsSource(name)) {
1362 return DMAChannelDir::MM2S;
1364 named |= flow.namesAsDestination(name);
1366 assert(named &&
"endpoint is named by no flow, so it has no direction to "
1367 "read; --aie-objectfifo-verify rejects this");
1369 return DMAChannelDir::S2MM;
1372WireBundle RouteEndpointOp::getRouteBundle() {
return getBundle(); }
1374std::optional<int> RouteEndpointOp::getRouteChannel() {
1375 return getChannelIndex();
1378void RouteEndpointOp::setRouteChannel(
int channel) { setChannelIndex(channel); }
1380LogicalResult RouteEndpointOp::verify() {
1381 TileLike tile = getTileLike();
1383 return emitOpError(
"tile operand is not an aie.tile or aie.logical_tile");
1391 StringRef name = getSymName();
1392 SmallVector<RouteOp> sources, dests;
1393 for (
auto flow : (*this)->getParentOfType<DeviceOp>().getOps<RouteOp>()) {
1394 if (flow.namesAsSource(name))
1395 sources.push_back(flow);
1396 if (flow.namesAsDestination(name))
1397 dests.push_back(flow);
1399 if (!sources.empty() && !dests.empty()) {
1400 return emitOpError(
"is named as both a source and a destination, so its "
1401 "direction is ambiguous");
1403 ArrayRef<RouteOp> sharers = sources.empty() ? dests : sources;
1404 if (sharers.size() > 1) {
1405 if (llvm::any_of(sharers, [](RouteOp flow) {
return !flow.getPacket(); }))
1406 return emitOpError(
"drives one channel, which a circuit flow owns, but "
1408 << sharers.size() <<
" times";
1409 llvm::SmallDenseSet<int> ids;
1410 for (RouteOp flow : sources) {
1411 std::optional<uint16_t>
id = flow.getPacketAttr().getPktId();
1413 return emitOpError(
"sends several packet flows, so each must pin its "
1414 "pkt_id for the BDs to state it");
1415 if (!ids.insert(*id).second)
1416 return emitOpError(
"sends several packet flows, two of which pin the "
1418 <<
static_cast<int>(*id) <<
"; each needs its own";
1421 if (llvm::any_of(dests, [&](RouteOp flow) {
1422 return flow.getKeepPktHeader() != dests[0].getKeepPktHeader();
1424 return emitOpError(
"receives several packet flows, so they must agree "
1425 "on keep_pkt_header");
1427 switch (getBundle()) {
1428 case WireBundle::DMA:
1432 case WireBundle::PLIO:
1433 if (!tile.isShimTile()) {
1434 return emitOpError(
"a PLIO end is on a shim");
1437 case WireBundle::Core:
1440 return emitOpError(
"bundle must be DMA, PLIO or Core");
1450static ParseResult parseRouteSources(OpAsmParser &parser, ArrayAttr &sources) {
1451 SmallVector<Attribute> names;
1452 auto parseOne = [&]() -> ParseResult {
1453 FlatSymbolRefAttr name;
1454 if (parser.parseAttribute(name))
1456 names.push_back(name);
1459 if (succeeded(parser.parseOptionalLSquare())) {
1460 if (failed(parser.parseOptionalRSquare()) &&
1461 (parser.parseCommaSeparatedList(parseOne) || parser.parseRSquare()))
1463 }
else if (parseOne()) {
1466 sources = parser.getBuilder().getArrayAttr(names);
1470static void printRouteSources(OpAsmPrinter &p, Operation *, ArrayAttr sources) {
1471 if (sources.size() == 1) {
1472 p.printAttribute(sources[0]);
1476 llvm::interleaveComma(sources, p,
1477 [&](Attribute name) { p.printAttribute(name); });
1481LogicalResult RouteOp::verify() {
1482 if (getSources().empty()) {
1483 return emitOpError(
"expects at least one source");
1485 if (getDestinations().empty()) {
1486 return emitOpError(
"expects at least one destination");
1489 if (getKeepPktHeader() && !getPacket()) {
1490 return emitOpError(
"keep_pkt_header is only meaningful on a packet flow");
1493 auto device = (*this)->getParentOfType<DeviceOp>();
1494 auto lookup = [&](StringRef name) {
1495 return dyn_cast_or_null<RouteEndpoint>(SymbolTable::lookupNearestSymbolFrom(
1496 device, StringAttr::get(getContext(), name)));
1499 llvm::SmallDenseSet<StringRef> seen;
1500 for (StringRef source : getSourceNames()) {
1501 if (!lookup(source)) {
1502 return emitOpError(
"source '") << source <<
"' is not an endpoint";
1504 if (!seen.insert(source).second) {
1505 return emitOpError(
"names '@") << source <<
"' as a source twice";
1508 for (StringRef destination : getDestinationNames()) {
1509 if (!lookup(destination)) {
1510 return emitOpError(
"destination '")
1511 << destination <<
"' is not an endpoint";
1513 if (!seen.insert(destination).second) {
1514 return emitOpError(
"names '@")
1516 <<
"' more than once; an end is a source or a "
1517 "destination, and only one of each";
1523 if (isFanIn() && !getPacket()) {
1524 return emitOpError(
"has several sources, so it needs a `packet` header; "
1525 "a circuit cannot merge streams");
1531ObjectFifoCreateOp::getDimensionsFromStream(Value consumerTile) {
1533 for (
auto cons : getConsumerTiles()) {
1534 if (cons == consumerTile)
1538 return getDimensionsFromStreamPerConsumer()[dimsIndex];
1542 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
1543 BDDimLayoutArrayAttr &dimensions) {
1544 std::vector<BDDimLayoutAttr> emptyDims = {};
1545 if (parser.parseOperand(operand))
1547 if (succeeded(parser.parseOptionalKeyword(
"dimensionsToStream"))) {
1548 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
1554 BDDimLayoutArrayAttr::get(parser.getContext(), ArrayRef(emptyDims));
1560 Operation *op, Value operand,
1561 BDDimLayoutArrayAttr dimensions) {
1563 if (!dimensions.empty()) {
1564 printer <<
" dimensionsToStream ";
1565 printer.printStrippedAttrOrType(dimensions);
1570 Attribute &channel) {
1572 if (succeeded(parser.parseOptionalSymbolName(name))) {
1573 channel = FlatSymbolRefAttr::get(name);
1577 if (parser.parseInteger(index))
1579 channel = parser.getBuilder().getI32IntegerAttr(index);
1584 bool resolved =
true;
1585 device.walkOutsideRuntimeSequences<DMAStartOp>([&](DMAStartOp start) {
1586 if (FlatSymbolRefAttr endpoint = start.getEndpoint()) {
1587 start.emitOpError() <<
"names route endpoint " << endpoint
1588 <<
" in place of a channel index; run "
1589 "--aie-objectfifo-allocate to assign one";
1593 return success(resolved);
1597 Attribute channel) {
1598 if (
auto index = dyn_cast<IntegerAttr>(channel))
1599 printer << index.getInt();
1601 printer.printAttributeWithoutType(channel);
1606 ObjectFifoPortAttr &port,
1607 SmallVectorImpl<Type> &objects) {
1608 if (parser.parseLParen())
1612 SMLoc portLoc = parser.getCurrentLocation();
1613 if (succeeded(parser.parseOptionalKeyword(&portName))) {
1614 std::optional<ObjectFifoPort> parsed = symbolizeObjectFifoPort(portName);
1616 return parser.emitError(portLoc,
"invalid objectFifo port: ") << portName;
1617 port = ObjectFifoPortAttr::get(parser.getContext(), *parsed);
1618 if (parser.parseComma())
1623 SMLoc countLoc = parser.getCurrentLocation();
1624 if (parser.parseInteger(count) || parser.parseRParen() ||
1625 parser.parseColon() || parser.parseTypeList(objects))
1628 if (count !=
static_cast<int64_t
>(objects.size()))
1629 return parser.emitError(countLoc,
"acquires ")
1630 << count <<
" objects but names " << objects.size() <<
" of them";
1636 ObjectFifoPortAttr port,
1637 TypeRange objects) {
1640 printer << stringifyObjectFifoPort(port.getValue()) <<
", ";
1641 printer << objects.size() <<
") : ";
1642 llvm::interleaveComma(objects, printer);
1646 ObjectFifoPortAttr &port,
1647 IntegerAttr &size) {
1648 if (parser.parseLParen())
1652 SMLoc portLoc = parser.getCurrentLocation();
1653 if (succeeded(parser.parseOptionalKeyword(&portName))) {
1654 std::optional<ObjectFifoPort> parsed = symbolizeObjectFifoPort(portName);
1656 return parser.emitError(portLoc,
"invalid objectFifo port: ") << portName;
1657 port = ObjectFifoPortAttr::get(parser.getContext(), *parsed);
1658 if (parser.parseComma())
1663 if (parser.parseInteger(count) || parser.parseRParen())
1665 size = parser.getBuilder().getI32IntegerAttr(count);
1671 ObjectFifoPortAttr port,
1675 printer << stringifyObjectFifoPort(port.getValue()) <<
", ";
1676 printer << size.getInt() <<
")";
1680 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &tiles,
1681 BDDimLayoutArrayArrayAttr &dimensions) {
1684 std::vector<BDDimLayoutArrayAttr> tileDims = {};
1686 auto parseOneOperand = [&]() -> ParseResult {
1687 if (parser.parseOperand(tiles.emplace_back(),
true)) {
1693 BDDimLayoutArrayAttr dimAttr =
1694 BDDimLayoutArrayAttr::get(parser.getContext(), {});
1696 if (succeeded(parser.parseOptionalKeyword(
"dimensionsFromStream"))) {
1698 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
1703 tileDims.emplace_back(dimAttr);
1707 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::None,
1708 parseOneOperand,
" in operand list"))
1711 dimensions = BDDimLayoutArrayArrayAttr::get(parser.getContext(), tileDims);
1716 OpAsmPrinter &printer, Operation *op, OperandRange tiles,
1717 BDDimLayoutArrayArrayAttr dimsPerTileAttr) {
1719 for (
auto tile : tiles) {
1721 if (dimsPerTileAttr && tileIdx < dimsPerTileAttr.size() &&
1722 dimsPerTileAttr[tileIdx] && !dimsPerTileAttr[tileIdx].empty()) {
1723 printer <<
" dimensionsFromStream ";
1724 printer.printStrippedAttrOrType(dimsPerTileAttr[tileIdx]);
1726 if (tileIdx < tiles.size() - 1) {
1733static void printObjectFifoConsumerElemType(OpAsmPrinter &p,
1734 ObjectFifoCreateOp op,
1735 TypeAttr consumerElemType) {
1736 if (consumerElemType)
1737 p <<
" -> " << consumerElemType;
1741 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vias,
1742 DenseI32ArrayAttr &ingressBundles, DenseI32ArrayAttr &ingressChannels,
1743 DenseI32ArrayAttr &egressBundles, DenseI32ArrayAttr &egressChannels) {
1744 SmallVector<int32_t> inBundles, inChannels, egBundles, egChannels;
1745 auto parseBundle = [&](SmallVectorImpl<int32_t> &out) -> ParseResult {
1747 if (parser.parseKeyword(&kw)) {
1750 auto bundle = symbolizeWireBundle(kw);
1752 return parser.emitError(parser.getCurrentLocation(),
1753 "invalid wire bundle '" + kw +
"'");
1755 out.push_back(
static_cast<int32_t
>(*bundle));
1759 if (succeeded(parser.parseOptionalKeyword(
"via"))) {
1760 auto parseOne = [&]() -> ParseResult {
1761 int32_t inChan, egChan;
1762 if (parser.parseOperand(vias.emplace_back()) || parser.parseColon() ||
1763 parseBundle(inBundles) || parser.parseColon() ||
1764 parser.parseInteger(inChan) || parser.parseArrow() ||
1765 parseBundle(egBundles) || parser.parseColon() ||
1766 parser.parseInteger(egChan)) {
1769 inChannels.push_back(inChan);
1770 egChannels.push_back(egChan);
1773 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, parseOne)) {
1777 if (!vias.empty()) {
1778 MLIRContext *ctx = parser.getContext();
1779 ingressBundles = DenseI32ArrayAttr::get(ctx, inBundles);
1780 ingressChannels = DenseI32ArrayAttr::get(ctx, inChannels);
1781 egressBundles = DenseI32ArrayAttr::get(ctx, egBundles);
1782 egressChannels = DenseI32ArrayAttr::get(ctx, egChannels);
1789 DenseI32ArrayAttr ingressBundles,
1790 DenseI32ArrayAttr ingressChannels,
1791 DenseI32ArrayAttr egressBundles,
1792 DenseI32ArrayAttr egressChannels) {
1797 for (
size_t i = 0; i < vias.size(); i++) {
1801 printer << vias[i] <<
" : "
1802 << stringifyWireBundle(
static_cast<WireBundle
>(ingressBundles[i]))
1803 <<
" : " << ingressChannels[i] <<
" -> "
1804 << stringifyWireBundle(
static_cast<WireBundle
>(egressBundles[i]))
1805 <<
" : " << egressChannels[i];
1810static ParseResult parseObjectFifoConsumerElemType(OpAsmParser &parser,
1811 TypeAttr &consumerElemType) {
1812 if (failed(parser.parseOptionalArrow()))
1815 if (parser.parseType(type))
1817 consumerElemType = TypeAttr::get(type);
1821static void printObjectFifoInitValues(OpAsmPrinter &p, ObjectFifoCreateOp op,
1822 Attribute numElem, TypeAttr type,
1823 Attribute initValues) {
1824 if (op.getInitValues()) {
1830 if (isa<ArrayAttr>(numElem)) {
1832 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::ArrayAttr>(numElem)[0])
1835 depth = llvm::cast<mlir::IntegerAttr>(numElem).getInt();
1837 for (
int i = 0; i < depth; i++) {
1838 p.printStrippedAttrOrType(llvm::cast<mlir::ArrayAttr>(initValues)[i]);
1839 if (i < depth - 1) {
1847static ParseResult parseObjectFifoInitValues(OpAsmParser &parser,
1848 Attribute numElem, TypeAttr type,
1849 Attribute &initValues) {
1851 if (isa<ArrayAttr>(numElem)) {
1853 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::ArrayAttr>(numElem)[0])
1856 depth = llvm::cast<mlir::IntegerAttr>(numElem).getInt();
1858 auto objfifoType = llvm::cast<AIEObjectFifoType>(type.getValue());
1859 auto memrefType = llvm::cast<MemRefType>(objfifoType.getElementType());
1861 if (!memrefType.hasStaticShape())
1862 return parser.emitError(parser.getNameLoc())
1863 <<
"type should be static shaped memref, but got " << memrefType;
1865 if (parser.parseOptionalEqual())
1868 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
1869 if (parser.parseAttribute(initValues, tensorType))
1871 for (
int i = 0; i < depth; i++) {
1872 auto initialValues = llvm::dyn_cast<mlir::ArrayAttr>(initValues);
1873 if ((
int)initialValues.size() != depth)
1874 return parser.emitError(parser.getNameLoc())
1875 <<
"initial values should initialize all objects";
1876 if (!llvm::isa<ElementsAttr>(initialValues[i]))
1877 return parser.emitError(parser.getNameLoc())
1878 <<
"initial value should be an elements attribute";
1888LogicalResult ObjectFifoAllocateOp::verify() {
1889 ObjectFifoCreateOp objFifo = getObjectFifo();
1891 return emitError(
"cannot retrieve associated object FIFO");
1892 if (objFifo.getConsumerTiles().size() != 1)
1893 return emitError(
"can only be used in 1-to-1 object FIFOs");
1894 if (objFifo.forcesDMA())
1895 return emitError(
"cannot allocate a shared memory module to objectfifo "
1896 "that asks for a dma transport");
1897 if (objFifo.getRepeatCount().has_value())
1898 return emitError(
"cannot allocate a shared memory module to objectfifo "
1899 "with set `repeat_count` attribute");
1900 if (!objFifo.getDimensionsToStream().empty())
1901 return emitError(
"cannot allocate a shared memory module to objectfifo "
1902 "with set dimensions attribute");
1903 if (objFifo.usesStream())
1904 return emitError(
"cannot allocate a shared memory module to objectfifo "
1905 "using stream port");
1906 for (Operation *op = (*this)->getPrevNode(); op; op = op->getPrevNode())
1907 if (
auto other = dyn_cast<ObjectFifoAllocateOp>(op);
1908 other && other.getObjFifoName() == getObjFifoName())
1909 return emitOpError(
"objectfifo ")
1910 << getObjFifoNameAttr() <<
" already has an allocate operation";
1914TileOp ObjectFifoAllocateOp::getDelegateTileOp() {
1915 return cast<TileOp>(getDelegateTile().getDefiningOp());
1918ObjectFifoCreateOp ObjectFifoAllocateOp::getObjectFifo() {
1919 Operation *parent = getOperation();
1920 while ((parent = parent->getParentOp())) {
1921 if (parent->hasTrait<OpTrait::SymbolTable>()) {
1922 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
1923 isa_and_nonnull<ObjectFifoCreateOp>(st))
1924 return cast<ObjectFifoCreateOp>(st);
1934LogicalResult ObjectFifoLinkOp::verify() {
1935 if (isJoin() && isDistribute())
1936 return emitError(
"ObjectFifoLinkOp does not support 'join' and "
1937 "'distribute' at the same time");
1940 if (isMerge() && isDispatch())
1941 return emitOpError(
"a time link merges or dispatches, not both");
1942 if (!isMerge() && !isDispatch())
1943 return emitOpError(
"a time link takes turns, so it needs several inputs "
1944 "or several outputs");
1945 if (!getSrcOffsets().empty() || !getDstOffsets().empty())
1946 return emitOpError(
"a time link moves whole objects and takes no "
1948 std::vector<ObjectFifoCreateOp> fifos = getInputObjectFifos();
1949 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
1950 fifos.insert(fifos.end(), outs.begin(), outs.end());
1951 for (ObjectFifoCreateOp fifo : fifos) {
1952 if (fifo.getElemType() != fifos.front().getElemType())
1953 return emitOpError(
"a time link's participants carry one object "
1955 << fifo.name().getValue() <<
"' carries " << fifo.getElemType()
1956 <<
" and '" << fifos.front().name().getValue() <<
"' carries "
1957 << fifos.front().getElemType();
1960 for (ObjectFifoCreateOp out : outs)
1961 if (out.getConsumerTiles().size() != 1)
1962 return emitOpError(
"each turn of a dispatch reaches one consumer, "
1964 << out.name().getValue() <<
"' has "
1965 << out.getConsumerTiles().size();
1968 PacketInfoAttr pinned;
1969 for (ObjectFifoCreateOp in : getInputObjectFifos()) {
1970 PacketInfoAttr header = in.packetHeader();
1971 if (!header || !header.isAssigned())
1973 if (pinned && pinned != header)
1974 return emitOpError(
"a merge is one route with one header, but its "
1975 "inputs pin different ones");
1981 auto sharedTile = getOptionalSharedTile();
1983 return emitError(
"ObjectFifoLinkOp must have a link point, i.e., a "
1984 "shared tile between objectFifos");
1989 DenseMap<Operation *, size_t> reads;
1991 (*this)->getParentOfType<DeviceOp>().getOps<ObjectFifoLinkOp>()) {
1992 if (other == *
this) {
1995 for (ObjectFifoCreateOp fifo : getOutputObjectFifos())
1996 if (llvm::is_contained(other.getOutputObjectFifos(), fifo))
1997 return fifo.emitOpError(
1998 "objectfifo cannot be filled by more than one ObjectFifoLinkOp");
1999 for (ObjectFifoCreateOp fifo : getInputObjectFifos())
2000 if (llvm::is_contained(other.getInputObjectFifos(), fifo) &&
2001 other.getOptionalSharedTile() == sharedTile)
2004 for (ObjectFifoCreateOp fifo : getInputObjectFifos())
2005 if (reads.lookup(fifo) >=
2006 static_cast<size_t>(llvm::count(fifo.getConsumerTiles(), *sharedTile)))
2007 return fifo.emitOpError(
"objectfifo is read by more ObjectFifoLinkOps "
2008 "on a tile than it has consumer ends there");
2010 TileLike tile = llvm::dyn_cast<TileLike>(sharedTile.value().getDefiningOp());
2012 return emitError(
"shared tile must implement TileLike interface");
2016 if (isJoin() || isDistribute()) {
2017 if (tile.isShimTile())
2018 return emitError(
"ObjectFifoLinkOp join and distribute are "
2019 "unavailable on shim tiles");
2022 return emitError(
"ObjectFifoLinkOp join and distribute require "
2023 "semaphore locks, which this device lacks");
2025 if (isTime() && tile.isShimTile())
2026 return emitOpError(
"a time link holds its objects on the link point, which "
2027 "a shim tile has no memory for");
2031 auto elementsOf = [](Type objectFifoType) {
2032 return cast<MemRefType>(
2033 cast<AIEObjectFifoType>(objectFifoType).getElementType())
2037 DataLayout dataLayout = DataLayout::closest(getOperation());
2038 auto bytesOf = [&](Type objectFifoType, int64_t elements) {
2040 static_cast<int64_t
>(dataLayout.getTypeSize(
2042 cast<AIEObjectFifoType>(objectFifoType).getElementType())
2043 .getElementType()));
2045 auto checkSlot = [&](StringRef role, ObjectFifoCreateOp participant,
2046 int64_t object, ObjectFifoCreateOp shared,
2047 int64_t slotBytes, ArrayAttr offsets) -> LogicalResult {
2049 return emitOpError() << role <<
" @" << participant.name().getValue()
2050 <<
" has empty objects";
2051 if (slotBytes %
object == 0)
2053 std::string offsetText;
2054 llvm::raw_string_ostream os(offsetText);
2055 llvm::interleaveComma(offsets, os, [&](Attribute offset) {
2056 os << *getConstantIntValue(offset);
2058 return emitOpError() << role <<
" @" << participant.name().getValue()
2059 <<
" has " <<
object <<
" B objects, but its "
2060 << slotBytes <<
" B slot in @"
2061 << shared.name().getValue() <<
" from offsets ["
2062 << offsetText <<
"] is not a whole number of them";
2066 if (getFifoIns().size() != getSrcOffsets().size())
2067 return emitOpError(
"number of provided src offsets must be equal "
2068 "to the number of input objectFifos");
2070 if (!getDstOffsets().empty())
2071 return emitOpError(
"dst offsets should be empty for join");
2075 for (
auto [idx, len] : llvm::enumerate(getJoinTransferLengths()))
2077 return emitOpError(
"src offsets must increase: input objectFifo ")
2078 << idx <<
" would transfer " << len <<
" elements";
2080 ObjectFifoCreateOp fifoOut = getOutputObjectFifos()[0];
2081 for (
auto [fifoIn, slot] :
2082 llvm::zip(getInputObjectFifos(), getJoinTransferLengths())) {
2084 fifoIn.getConsumerElemType()
2085 ? bytesOf(fifoIn.getConsumerElemTypeOrDefault(),
2086 elementsOf(fifoIn.getConsumerElemTypeOrDefault()))
2087 : bytesOf(fifoIn.getElemType(),
2089 fifoIn.getPadDimensions().value_or(
2090 ArrayRef<BDPadLayoutAttr>{}),
2091 elementsOf(fifoIn.getElemType())));
2092 if (failed(checkSlot(
"input", fifoIn,
object, fifoOut,
2093 bytesOf(fifoOut.getElemType(), slot),
2097 if (!fifoOut.getDimensionsToStream().empty()) {
2098 int64_t maxIdx = getDimsMaxIdx(fifoOut.getDimensionsToStream());
2099 int64_t minInputBufferSize = -1;
2100 for (
auto lenIn : getJoinTransferLengths()) {
2101 if (lenIn <= minInputBufferSize || minInputBufferSize < 0)
2102 minInputBufferSize = lenIn;
2104 if (minInputBufferSize <= maxIdx) {
2105 return emitOpError()
2106 <<
"specified output stride(s) and size(s) result in out "
2107 "of bounds access in join input, for index "
2108 << std::to_string(maxIdx) <<
" in transfer of length "
2109 << std::to_string(minInputBufferSize) <<
".";
2113 }
else if (isDistribute()) {
2114 if (getFifoOuts().size() != getDstOffsets().size())
2115 return emitOpError(
"number of provided dst offsets must be equal "
2116 "to the number of output objectFifos");
2118 if (!getSrcOffsets().empty())
2119 return emitOpError(
"src offsets should be empty for distribute");
2121 for (
auto [idx, len] : llvm::enumerate(getDistributeTransferLengths()))
2123 return emitOpError(
"dst offsets must increase: output objectFifo ")
2124 << idx <<
" would transfer " << len <<
" elements";
2126 ObjectFifoCreateOp fifoIn = getInputObjectFifos()[0];
2127 for (
auto [fifoOut, slot] :
2128 llvm::zip(getOutputObjectFifos(), getDistributeTransferLengths())) {
2130 if (failed(checkSlot(
2132 bytesOf(fifoOut.getElemType(), elementsOf(fifoOut.getElemType())),
2134 bytesOf(fifoIn.getElemType(),
2136 fifoOut.getPadDimensions().value_or(
2137 ArrayRef<BDPadLayoutAttr>{}),
2142 if (!fifoIn.getDimensionsFromStream(sharedTile.value()).empty()) {
2144 getDimsMaxIdx(fifoIn.getDimensionsFromStream(sharedTile.value()));
2145 int64_t minOutputBufferSize = -1;
2146 for (
auto lenOut : getDistributeTransferLengths()) {
2147 if (lenOut <= minOutputBufferSize || minOutputBufferSize < 0)
2148 minOutputBufferSize = lenOut;
2150 if (minOutputBufferSize <= maxIdx) {
2151 return emitOpError()
2152 <<
"specified input stride(s) and size(s) result in out "
2153 "of bounds access in distribute output, for index "
2154 << std::to_string(maxIdx) <<
" in transfer of length "
2155 << std::to_string(minOutputBufferSize) <<
".";
2159 if (!getSrcOffsets().empty() && !getDstOffsets().empty())
2160 return emitOpError(
"all offsets should be empty if there is no "
2161 "join or distribute");
2164 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2165 if (!isTime() && !walksStream() && !outs.empty()) {
2166 ArrayRef<BDDimLayoutAttr> dims = outs[0].getDimensionsToStream();
2167 std::optional<ArrayRef<BDPadLayoutAttr>> pads =
2168 outs[0].getPadDimensions();
2170 for (BDDimLayoutAttr dim : dims)
2171 walked *= dim.getSize();
2172 int64_t
object = elementsOf(getPoolOwner().getElemType());
2173 if (!dims.empty() && (!pads || pads->empty()) && walked !=
object)
2174 return emitOpError()
2175 <<
"@" << outs[0].name().getValue() <<
"'s walk covers "
2176 << walked <<
" elements, but each transfer sends a whole "
2178 <<
"-element object; to send part of each input object, walk "
2179 "the stream (an outer dimension stepping whole input "
2184 if (walksStream()) {
2186 if (failed(getStreamWindow(window)))
2188 if (!tile.isMemTile())
2189 return emitOpError(
"a walk over a stream needs a MemTile link point");
2190 ObjectFifoCreateOp in = getInputObjectFifos()[0];
2191 ObjectFifoCreateOp out = getOutputObjectFifos()[0];
2192 if (in.getRepeatCount() || out.getRepeatCount())
2193 return emitOpError(
"a walk over a stream does not repeat objects");
2194 if (in.getIterCount() || out.getIterCount())
2195 return emitOpError(
"a walk over a stream runs without an iter_count");
2200 return emitOpError() <<
"a walk over a stream of " << window.
objects
2201 <<
" objects needs a lock counting that many, "
2202 "but locks count to "
2204 int64_t bds = target.
getNumBDs(AIETileType::MemTile) / 2;
2206 return emitOpError() <<
"a walk over a stream lowers to one descriptor "
2208 << window.
outputs <<
", but a MemTile channel has "
2214 if (isDistribute()) {
2215 auto fifos = getOutputObjectFifos();
2216 for (
size_t i = 1; i < fifos.size(); ++i) {
2217 ObjectFifoCreateOp first = fifos[0], fifo = fifos[i];
2218 int repeat = first.getRepeatCount().value_or(1);
2219 int otherRepeat = fifo.getRepeatCount().value_or(1);
2220 if (repeat != otherRepeat)
2221 return emitOpError()
2222 <<
"distribute outputs must have equal effective repeat counts: "
2224 << first.name().getValue() <<
" has " << repeat
2225 << (first.getRepeatCount() ?
"" :
" (default)") <<
", @"
2226 << fifo.name().getValue() <<
" has " << otherRepeat
2227 << (fifo.getRepeatCount() ?
"" :
" (default)");
2234std::optional<Value> ObjectFifoLinkOp::getOptionalSharedTile() {
2236 auto fifoOut = getOutputObjectFifos()[0];
2237 for (
auto fifoIn : getInputObjectFifos())
2238 if (fifoOut.getProducerTile() != fifoIn.getConsumerTiles()[0])
2240 return {fifoOut.getProducerTile()};
2243 if (isDistribute()) {
2244 auto fifoIn = getInputObjectFifos()[0];
2245 for (
auto fifoOut : getOutputObjectFifos())
2246 if (fifoIn.getConsumerTiles()[0] != fifoOut.getProducerTile())
2248 return {fifoIn.getConsumerTiles()[0]};
2251 auto fifoIn = getInputObjectFifos();
2252 if (
auto fifoOut = getOutputObjectFifos();
2253 !fifoIn.empty() && !fifoOut.empty())
2254 for (
auto consumerIn : fifoIn[0].getConsumerTiles())
2255 if (consumerIn == fifoOut[0].getProducerTile())
2256 return {fifoOut[0].getProducerTile()};
2260bool ObjectFifoLinkOp::walksStream() {
2261 if (isJoin() || isDistribute())
2263 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2266 ArrayRef<BDDimLayoutAttr> dims = outs[0].getDimensionsToStream();
2267 if (dims.size() < 2 || dims.front().getSize() < 2)
2270 for (BDDimLayoutAttr dim : dims.drop_front())
2271 inner *= dim.getSize();
2272 int64_t objectSize =
2273 cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType())
2277 return getDimsMaxIdx(dims) >= objectSize &&
2278 inner == cast<AIEObjectFifoType>(outs[0].getElemType())
2283LogicalResult ObjectFifoLinkOp::getStreamWindow(
StreamWindow &window) {
2284 ObjectFifoCreateOp in = getInputObjectFifos()[0];
2285 ObjectFifoCreateOp out = getOutputObjectFifos()[0];
2286 ArrayRef<BDPadLayoutAttr> pads;
2287 if (
auto padding = out.getPadDimensions())
2290 out.getDimensionsToStream(), pads,
2291 cast<AIEObjectFifoType>(in.getElemType())
2294 [&] { return emitOpError(); }, window);
2297ObjectFifoCreateOp ObjectFifoLinkOp::getPoolOwner() {
2298 std::vector<ObjectFifoCreateOp> ins = getInputObjectFifos();
2299 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2300 if (isJoin() || isMerge())
2302 if (isDistribute() || isDispatch())
2307 auto count = [](ObjectFifoCreateOp fifo) {
2308 return cast<AIEObjectFifoType>(fifo.getElemType())
2313 if (outs[0].getInitValues() ||
2314 (count(outs[0]) > count(ins[0]) && !outs[0].getPadDimensions()))
2319SmallVector<std::pair<int64_t, int64_t>> ObjectFifoLinkOp::getPoolSegments() {
2320 auto count = [](ObjectFifoCreateOp fifo) {
2321 return cast<AIEObjectFifoType>(fifo.getElemType())
2325 int64_t whole = count(getPoolOwner());
2326 if (!isJoin() && !isDistribute())
2327 return {{0, whole}};
2328 ArrayAttr offsets = isJoin() ? getSrcOffsets() : getDstOffsets();
2329 std::vector<ObjectFifoCreateOp> side =
2330 isJoin() ? getInputObjectFifos() : getOutputObjectFifos();
2331 SmallVector<std::pair<int64_t, int64_t>> segments;
2332 for (
auto [index, participant] : llvm::enumerate(side)) {
2333 int64_t offset = cast<IntegerAttr>(offsets[index]).getInt();
2334 int64_t next = index + 1 < side.size()
2335 ? cast<IntegerAttr>(offsets[index + 1]).getInt()
2337 int64_t size = next - offset;
2339 size = count(participant);
2340 segments.emplace_back(offset, size);
2345std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getInputObjectFifos() {
2346 std::vector<ObjectFifoCreateOp> inputObjFifos;
2347 Operation *parent = getOperation();
2348 while ((parent = parent->getParentOp())) {
2349 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2350 for (
auto sym : getFifoIns()) {
2351 auto name = cast<FlatSymbolRefAttr>(sym);
2352 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
2353 isa_and_nonnull<ObjectFifoCreateOp>(st))
2354 inputObjFifos.push_back(cast<ObjectFifoCreateOp>(st));
2358 return inputObjFifos;
2361std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getOutputObjectFifos() {
2362 std::vector<ObjectFifoCreateOp> outputObjFifos;
2363 Operation *parent = getOperation();
2364 while ((parent = parent->getParentOp())) {
2365 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2366 for (
auto sym : getFifoOuts()) {
2367 auto name = cast<FlatSymbolRefAttr>(sym);
2368 if (
auto *st = mlir::SymbolTable::lookupSymbolIn(parent, name);
2369 isa_and_nonnull<ObjectFifoCreateOp>(st))
2370 outputObjFifos.push_back(cast<ObjectFifoCreateOp>(st));
2374 return outputObjFifos;
2377std::vector<int> ObjectFifoLinkOp::getJoinTransferLengths() {
2378 std::vector<int> lengths;
2381 llvm::cast<AIEObjectFifoType>(getOutputObjectFifos()[0].getElemType());
2382 auto elemTypeOut = llvm::cast<MemRefType>(fifoOut.getElementType());
2383 int lenOut = elemTypeOut.getNumElements();
2386 auto srcOffset = [&](
size_t idx) ->
int {
2387 std::optional<int64_t> v = getConstantIntValue(getSrcOffsets()[idx]);
2388 assert(v &&
"src_offsets element must be a constant integer");
2389 return static_cast<int>(*v);
2391 for (
size_t i = 0; i < getFifoIns().size(); i++) {
2393 int offset = srcOffset(i);
2394 if (i == getFifoIns().size() - 1)
2395 len = lenOut - offset;
2397 len = srcOffset(i + 1) - offset;
2398 lengths.push_back(len);
2404std::vector<int> ObjectFifoLinkOp::getDistributeTransferLengths() {
2405 std::vector<int> lengths;
2406 if (isDistribute()) {
2408 llvm::cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType());
2409 auto elemTypeIn = llvm::cast<MemRefType>(fifoIn.getElementType());
2410 int lenIn = elemTypeIn.getNumElements();
2413 auto dstOffset = [&](
size_t idx) ->
int {
2414 std::optional<int64_t> v = getConstantIntValue(getDstOffsets()[idx]);
2415 assert(v &&
"dst_offsets element must be a constant integer");
2416 return static_cast<int>(*v);
2418 for (
size_t i = 0; i < getFifoOuts().size(); i++) {
2419 int offset = dstOffset(i);
2421 if (i == getFifoOuts().size() - 1)
2422 len = lenIn - offset;
2424 len = dstOffset(i + 1) - offset;
2425 lengths.push_back(len);
2431std::optional<int> ObjectFifoLinkOp::getRepeatCount() {
2432 for (
auto fifoOut : getOutputObjectFifos())
2433 if (fifoOut.getRepeatCount().has_value())
2434 return {fifoOut.getRepeatCount()};
2442TileOp ObjectFifoRegisterExternalBuffersOp::getTileOp() {
2443 return cast<TileOp>(getTile().getDefiningOp());
2446ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
2447 Operation *parent = getOperation();
2448 while ((parent = parent->getParentOp())) {
2449 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2450 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2451 isa_and_nonnull<ObjectFifoCreateOp>(st))
2452 return cast<ObjectFifoCreateOp>(st);
2466Operation *lookupAccessTarget(Operation *op, StringRef name) {
2467 for (Operation *parent = op->getParentOp(); parent;
2468 parent = parent->getParentOp()) {
2469 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2470 if (Operation *target = SymbolTable::lookupSymbolIn(parent, name)) {
2480Type accessElementType(Operation *target) {
2481 if (
auto fifo = dyn_cast_or_null<ObjectFifoCreateOp>(target)) {
2482 return llvm::cast<AIEObjectFifoType>(fifo.getElemType()).getElementType();
2484 if (
auto endpoint = dyn_cast_or_null<ObjectFifoCoreEndpointOp>(target)) {
2485 if (endpoint.getPoolOp()) {
2486 return endpoint.getAccessType();
2493LogicalResult verifyAccessPlacement(Operation *op, Operation *target,
2494 std::optional<ObjectFifoPort> port) {
2495 auto core = op->getParentOfType<CoreOp>();
2496 Value coreTile = core.getTile();
2498 if (
auto endpoint = dyn_cast_or_null<ObjectFifoCoreEndpointOp>(target)) {
2500 return op->emitOpError(
"port is implied by the endpoint's role");
2502 if (endpoint.getTile() != coreTile) {
2503 return core.emitOpError(
2504 "objectFifo endpoint accessed by core running on another tile");
2509 auto fifo = dyn_cast_or_null<ObjectFifoCreateOp>(target);
2511 return op->emitError(
"cannot retrieve associated object FIFO");
2514 return op->emitOpError(
"port is required when accessing an objectFifo");
2517 if (*port == ObjectFifoPort::Produce) {
2518 if (coreTile != fifo.getProducerTile()) {
2519 return core.emitOpError(
2520 "producer port of objectFifo accessed by core running "
2521 "on non-producer tile");
2524 if (!llvm::is_contained(fifo.getConsumerTiles(), coreTile)) {
2525 return core.emitOpError(
2526 "consumer port of objectFifo accessed by core running "
2527 "on non-consumer tile");
2535LogicalResult ObjectFifoAcquireOp::verify() {
2536 auto parent = getOperation()->getParentOfType<CoreOp>();
2537 if (parent ==
nullptr) {
2538 return emitOpError(
"must be called from inside a CoreOp");
2541 Operation *target = lookupAccessTarget(*
this, getObjFifoName());
2542 if (failed(verifyAccessPlacement(*
this, target, getPort()))) {
2546 if (getObjects().empty()) {
2547 return emitOpError(
"must acquire at least one object");
2550 Type elem = accessElementType(target);
2554 auto fifo = dyn_cast<ObjectFifoCreateOp>(target);
2558 if (getPort() == ObjectFifoPort::Consume) {
2559 for (
auto [position, consumer] :
2560 llvm::enumerate(fifo.getConsumerTiles())) {
2561 if (consumer == parent.getTile()) {
2562 index = position + 1;
2567 if (
static_cast<int>(getObjects().size()) > fifo.size(index)) {
2568 return emitOpError(
"acquires ")
2569 << getObjects().size() <<
" objects from an objectFifo that holds "
2570 << fifo.size(index);
2574 auto consType = fifo ? llvm::dyn_cast<AIEObjectFifoType>(
2575 fifo.getConsumerElemTypeOrDefault())
2577 if (fifo && !consType) {
2578 return emitOpError(
"ObjectFifo consumer element type must be an "
2579 "!aie.objectfifo<memref<...>> type");
2582 for (Type
object : getObjects().getTypes()) {
2583 if (
object != elem && (!consType || consType.getElementType() !=
object)) {
2584 return emitOpError(
"acquired object type ")
2585 <<
object <<
" does not match the objectFifo's " << elem;
2592ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() {
2593 Operation *parent = getOperation();
2594 while ((parent = parent->getParentOp())) {
2595 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2596 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2597 isa_and_nonnull<ObjectFifoCreateOp>(st))
2598 return cast<ObjectFifoCreateOp>(st);
2608LogicalResult ObjectFifoReleaseOp::verify() {
2609 auto parent = getOperation()->getParentOfType<CoreOp>();
2610 if (parent ==
nullptr)
2611 return emitOpError(
"must be called from inside a CoreOp");
2613 Operation *target = lookupAccessTarget(*
this, getObjFifoName());
2614 return verifyAccessPlacement(*
this, target, getPort());
2617ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() {
2618 Operation *parent = getOperation();
2619 while ((parent = parent->getParentOp())) {
2620 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2621 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2622 isa_and_nonnull<ObjectFifoCreateOp>(st))
2623 return cast<ObjectFifoCreateOp>(st);
2633LogicalResult CascadeFlowOp::verify() {
2634 TileLike src = getSourceTileLike();
2635 TileLike dst = getDestTileLike();
2638 return emitOpError(
"source and dest must be tile-like operations");
2640 if (src.isShimTile() || dst.isShimTile())
2641 return emitOpError(
"shimTile row has no cascade stream interface");
2642 if (src.isMemTile() || dst.isMemTile())
2643 return emitOpError(
"memTile row has no cascade stream interface");
2645 std::optional<int> srcCol = src.tryGetCol();
2646 std::optional<int> srcRow = src.tryGetRow();
2647 std::optional<int> dstCol = dst.tryGetCol();
2648 std::optional<int> dstRow = dst.tryGetRow();
2650 if (srcCol && srcRow && dstCol && dstRow) {
2652 if (!t.isSouth(*srcCol, *srcRow, *dstCol, *dstRow) &&
2653 !t.isWest(*srcCol, *srcRow, *dstCol, *dstRow) &&
2654 !t.isNorth(*srcCol, *srcRow, *dstCol, *dstRow) &&
2655 !t.isEast(*srcCol, *srcRow, *dstCol, *dstRow)) {
2656 return emitOpError(
"tiles must be adjacent");
2663TileLike CascadeFlowOp::getSourceTileLike() {
2664 return dyn_cast<TileLike>(getSourceTile().getDefiningOp());
2667TileLike CascadeFlowOp::getDestTileLike() {
2668 return dyn_cast<TileLike>(getDestTile().getDefiningOp());
2671TileOp CascadeFlowOp::getSourceTileOp() {
2672 if (
auto tileOp = dyn_cast_or_null<TileOp>(getSourceTile().getDefiningOp()))
2674 llvm::report_fatal_error(
"Calling getSourceTileOp requires TileOp.");
2677TileOp CascadeFlowOp::getDestTileOp() {
2678 if (
auto tileOp = dyn_cast_or_null<TileOp>(getDestTile().getDefiningOp()))
2680 llvm::report_fatal_error(
"Calling getDestTileOp requires TileOp.");
2687LogicalResult ConfigureCascadeOp::verify() {
2688 if (!isa<TileOp>(getTile().getDefiningOp()))
2689 return emitOpError(
"requires a placed tile (aie.tile), not a logical tile");
2692 TileOp tile = cast<TileOp>(getTile().getDefiningOp());
2693 CascadeDir inputDir = getInputDir();
2694 CascadeDir outputDir = getOutputDir();
2696 if (tile.isShimTile())
2697 return emitOpError(
"shimTile row has no cascade stream interface");
2698 if (tile.isMemTile())
2699 return emitOpError(
"memTile row has no cascade stream interface");
2701 if (isa<AIE2TargetModel>(t)) {
2702 if (inputDir == CascadeDir::South || inputDir == CascadeDir::East) {
2703 return emitOpError(
"input direction of cascade must be North or West on ")
2704 << stringifyAIEArch(t.getTargetArch());
2706 if (outputDir == CascadeDir::North || outputDir == CascadeDir::West) {
2708 "output direction of cascade must be South or East on ")
2709 << stringifyAIEArch(t.getTargetArch());
2712 return emitOpError(
"cascade not supported in ")
2713 << stringifyAIEArch(t.getTargetArch());
2722LogicalResult PutCascadeOp::verify() {
2724 Type type = getCascadeValue().getType();
2725 DataLayout dataLayout = DataLayout::closest(*
this);
2726 auto bits = dataLayout.getTypeSizeInBits(type);
2727 auto archbits = targetModel.getAccumulatorCascadeSize();
2728 if (bits != archbits)
2729 return emitOpError(
"type must match architecture cascade width (")
2730 << archbits <<
" bits in "
2731 << stringifyAIEArch(targetModel.getTargetArch()) <<
")";
2739LogicalResult GetCascadeOp::verify() {
2741 Type type = getCascadeValue().getType();
2742 DataLayout dataLayout = DataLayout::closest(*
this);
2743 auto bits = dataLayout.getTypeSizeInBits(type);
2744 if (isa<AIE1TargetModel>(targetModel)) {
2746 return emitOpError(
"must be a 384-bit type");
2747 }
else if (isa<AIE2TargetModel>(targetModel)) {
2749 return emitOpError(
"must be a 512-bit type");
2751 return emitOpError(
"cascade not supported in ")
2752 << stringifyAIEArch(targetModel.getTargetArch());
2765using PortKey = std::tuple<int, int, int, int>;
2772std::optional<PortKey> tryGetPortKey(Value tileValue, WireBundle bundle,
2774 auto tile = llvm::dyn_cast_or_null<TileLike>(tileValue.getDefiningOp());
2776 return std::nullopt;
2777 std::optional<int> col = tile.tryGetCol();
2778 std::optional<int> row = tile.tryGetRow();
2780 return std::nullopt;
2781 return PortKey{*col, *row,
static_cast<int>(bundle), channel};
2784std::string to_string(
const PortKey &key) {
2785 auto [col, row, bundle, channel] = key;
2786 return "(" + std::to_string(col) +
", " + std::to_string(row) +
") " +
2787 stringifyWireBundle(
static_cast<WireBundle
>(bundle)).str() +
" : " +
2788 std::to_string(channel);
2796LogicalResult verifyNoDuplicateFlows(DeviceOp device) {
2797 std::map<std::pair<PortKey, PortKey>, FlowOp> flowSeen;
2798 for (FlowOp flow : device.getOps<FlowOp>()) {
2799 std::optional<PortKey> src = tryGetPortKey(
2800 flow.getSource(), flow.getSourceBundle(), flow.getSourceChannel());
2801 std::optional<PortKey> dst = tryGetPortKey(
2802 flow.getDest(), flow.getDestBundle(), flow.getDestChannel());
2805 auto [it, inserted] = flowSeen.try_emplace({*src, *dst}, flow);
2807 InFlightDiagnostic diag = flow.emitOpError()
2808 <<
"duplicates an earlier flow; "
2809 << to_string(*src) <<
" -> " << to_string(*dst)
2810 <<
" is already declared";
2811 diag.attachNote(it->second.getLoc()) <<
"the other flow is here";
2823LogicalResult verifyNoDuplicatePacketFlows(DeviceOp device) {
2824 using PacketFlowKey =
2825 std::tuple<int, int, std::vector<PortKey>, std::vector<PortKey>>;
2826 std::map<PacketFlowKey, PacketFlowOp> packetFlowSeen;
2827 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
2828 Region &body = packetFlow.getPorts();
2833 std::vector<PortKey> sources, dests;
2834 bool comparable =
true;
2835 for (Operation &op : body.front()) {
2836 std::vector<PortKey> *endpoints =
nullptr;
2837 std::optional<PortKey> port;
2838 if (
auto source = dyn_cast<PacketSourceOp>(op)) {
2839 endpoints = &sources;
2840 port = tryGetPortKey(source.getTile(), source.getBundle(),
2841 source.getChannel());
2842 }
else if (
auto dest = dyn_cast<PacketDestOp>(op)) {
2845 tryGetPortKey(dest.getTile(), dest.getBundle(), dest.getChannel());
2854 endpoints->push_back(*port);
2856 if (!comparable || sources.empty() || dests.empty())
2858 llvm::sort(sources);
2861 llvm::Log2_32_Ceil(device.getTargetModel().getMaxPacketId() + 1);
2862 int mask = packetFlow.getMask().value_or((1 << idBits) - 1);
2863 auto [it, inserted] = packetFlowSeen.try_emplace(
2864 {packetFlow.IDInt(), mask, std::move(sources), std::move(dests)},
2867 InFlightDiagnostic diag =
2868 packetFlow.emitOpError()
2869 <<
"duplicates an earlier packet flow; ID " << packetFlow.IDInt()
2870 <<
" under mask 0x" << llvm::utohexstr(mask)
2871 <<
" is already declared between the same sources and destinations";
2872 diag.attachNote(it->second.getLoc()) <<
"the other packet flow is here";
2882LogicalResult verifyNoSharedCircuitPorts(DeviceOp device) {
2883 std::map<PortKey, FlowOp> circuitSources, circuitDests;
2884 for (FlowOp flow : device.getOps<FlowOp>()) {
2885 std::optional<PortKey> src = tryGetPortKey(
2886 flow.getSource(), flow.getSourceBundle(), flow.getSourceChannel());
2887 std::optional<PortKey> dst = tryGetPortKey(
2888 flow.getDest(), flow.getDestBundle(), flow.getDestChannel());
2890 circuitSources.try_emplace(*src, flow);
2893 auto [it, inserted] = circuitDests.try_emplace(*dst, flow);
2896 InFlightDiagnostic diag =
2898 <<
"ends at " << to_string(*dst)
2899 <<
", where another circuit flow ends; a port takes one circuit";
2900 diag.attachNote(it->second.getLoc()) <<
"the other circuit flow is here";
2903 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
2904 Region &body = packetFlow.getPorts();
2907 for (Operation &op : body.front()) {
2908 std::optional<PortKey> port;
2909 std::map<PortKey, FlowOp> *circuits =
nullptr;
2911 if (
auto source = dyn_cast<PacketSourceOp>(op)) {
2912 port = tryGetPortKey(source.getTile(), source.getBundle(),
2913 source.getChannel());
2914 circuits = &circuitSources;
2916 }
else if (
auto dest = dyn_cast<PacketDestOp>(op)) {
2918 tryGetPortKey(dest.getTile(), dest.getBundle(), dest.getChannel());
2919 circuits = &circuitDests;
2924 auto it = circuits->find(*port);
2925 if (it == circuits->end())
2927 InFlightDiagnostic diag =
2928 packetFlow.emitOpError()
2929 << role <<
" at " << to_string(*port) <<
", where a circuit flow "
2930 << role <<
"; a port carries either one circuit or packets";
2931 diag.attachNote(it->second.getLoc()) <<
"the circuit flow is here";
2938LogicalResult verifyNoSharedCircuitViaEgresses(DeviceOp device) {
2939 enum class ClaimKind { FlowDest, PacketDest,
Connect, MasterSet, Via };
2943 size_t viaIndex = 0;
2945 std::map<PortKey, Claim> claimedEgresses;
2947 auto addInterconnectClaims = [&](
auto interconnect) {
2948 Value tile = interconnect.getTile();
2949 for (ConnectOp connect :
2950 interconnect.getConnections().template getOps<ConnectOp>()) {
2951 Port dest = connect.destPort();
2952 if (std::optional<PortKey> port =
2953 tryGetPortKey(tile, dest.bundle, dest.channel))
2954 claimedEgresses.try_emplace(*port, Claim{connect, ClaimKind::Connect});
2956 for (MasterSetOp masterSet :
2957 interconnect.getConnections().template getOps<MasterSetOp>()) {
2958 Port dest = masterSet.destPort();
2959 if (std::optional<PortKey> port =
2960 tryGetPortKey(tile, dest.bundle, dest.channel))
2961 claimedEgresses.try_emplace(*port,
2962 Claim{masterSet, ClaimKind::MasterSet});
2965 for (SwitchboxOp switchbox : device.getOps<SwitchboxOp>())
2966 addInterconnectClaims(switchbox);
2967 for (ShimMuxOp shimMux : device.getOps<ShimMuxOp>())
2968 addInterconnectClaims(shimMux);
2969 for (FlowOp flow : device.getOps<FlowOp>()) {
2970 if (std::optional<PortKey> dest = tryGetPortKey(
2971 flow.getDest(), flow.getDestBundle(), flow.getDestChannel()))
2972 claimedEgresses.try_emplace(*dest, Claim{flow, ClaimKind::FlowDest});
2974 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
2975 for (PacketDestOp dest : packetFlow.getPorts().getOps<PacketDestOp>()) {
2976 if (std::optional<PortKey> port = tryGetPortKey(
2977 dest.getTile(), dest.getBundle(), dest.getChannel()))
2978 claimedEgresses.try_emplace(*port, Claim{dest, ClaimKind::PacketDest});
2982 WalkResult result = device.walk([&](FlowOp flow) {
2983 ArrayRef<int32_t> bundles =
2984 flow.getViaEgressBundlesAttr()
2985 ? flow.getViaEgressBundlesAttr().asArrayRef()
2986 : ArrayRef<int32_t>();
2987 ArrayRef<int32_t> channels =
2988 flow.getViaEgressChannelsAttr()
2989 ? flow.getViaEgressChannelsAttr().asArrayRef()
2990 : ArrayRef<int32_t>();
2991 if (bundles.size() != flow.getVias().size() ||
2992 channels.size() != flow.getVias().size()) {
2993 return WalkResult::advance();
2995 for (
auto [index, via] : llvm::enumerate(flow.getVias())) {
2996 std::optional<PortKey> egress = tryGetPortKey(
2997 via,
static_cast<WireBundle
>(bundles[index]), channels[index]);
3001 Claim viaClaim{flow, ClaimKind::Via, index};
3002 auto [it, inserted] = claimedEgresses.try_emplace(*egress, viaClaim);
3005 if (index + 1 == flow.getVias().size() && it->second.op == flow &&
3006 it->second.kind == ClaimKind::FlowDest) {
3007 it->second = viaClaim;
3010 InFlightDiagnostic diag = flow.emitOpError()
3011 <<
"via " << index <<
" claims circuit egress "
3012 << to_string(*egress)
3013 <<
", which another operation already claims";
3014 Diagnostic ¬e = diag.attachNote(it->second.op->getLoc());
3015 switch (it->second.kind) {
3016 case ClaimKind::FlowDest:
3017 note <<
"a circuit flow ends at the same port";
3019 case ClaimKind::PacketDest:
3020 note <<
"a packet flow ends at the same port";
3022 case ClaimKind::Connect:
3023 note <<
"a fixed connect drives the same port";
3025 case ClaimKind::MasterSet:
3026 note <<
"a fixed master set drives the same port";
3028 case ClaimKind::Via:
3029 note <<
"via " << it->second.viaIndex
3030 <<
" claims the same circuit egress";
3033 return WalkResult::interrupt();
3035 return WalkResult::advance();
3037 return failure(result.wasInterrupted());
3045LogicalResult verifyNoDuplicateNames(Operation *symbolTableOp) {
3046 if (symbolTableOp->getNumRegions() == 0 ||
3047 symbolTableOp->getRegion(0).empty()) {
3050 DenseMap<StringAttr, Operation *> nameSeen;
3051 for (Operation &op : symbolTableOp->getRegion(0).front()) {
3052 auto name = op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
3056 auto [it, inserted] = nameSeen.try_emplace(name, &op);
3062 if (isa<SymbolOpInterface>(op) && isa<SymbolOpInterface>(it->second)) {
3065 InFlightDiagnostic diag = op.emitError() <<
"redefinition of symbol named '"
3066 << name.getValue() <<
"'";
3067 diag.attachNote(it->second->getLoc())
3068 <<
"see existing symbol definition here";
3076LogicalResult DeviceOp::verify() {
3085 DenseMap<TileID, CoreOp> coreAtTile;
3086 for (CoreOp core : getOps<CoreOp>()) {
3088 llvm::dyn_cast_or_null<TileLike>(core.getTile().getDefiningOp());
3091 std::optional<int> col = tile.tryGetCol();
3092 std::optional<int> row = tile.tryGetRow();
3096 auto [it, inserted] = coreAtTile.try_emplace(
id, core);
3098 InFlightDiagnostic diag =
3100 <<
"tile (" << *col <<
", " << *row
3101 <<
") already has a core; each compute tile can host only one core";
3102 diag.attachNote(it->second.getLoc()) <<
"the other core is here";
3109 if (failed(verifyNoDuplicateFlows(*
this)))
3111 if (failed(verifyNoDuplicatePacketFlows(*
this)))
3113 if (failed(verifyNoSharedCircuitPorts(*
this)))
3115 if (failed(verifyNoSharedCircuitViaEgresses(*
this))) {
3119 if (failed(verifyNoDuplicateNames(*
this))) {
3130xilinx::AIE::DeviceOp DeviceOp::getForSymbolInModule(mlir::ModuleOp module,
3131 llvm::StringRef symbol) {
3133 if (symbol.empty()) {
3137 Operation *maybeDeviceOp = mlir::SymbolTable::lookupSymbolIn(module, symbol);
3138 if (!maybeDeviceOp) {
3141 deviceOp = llvm::dyn_cast<DeviceOp>(maybeDeviceOp);
3145xilinx::AIE::DeviceOp
3146DeviceOp::getForSymbolInModuleOrError(mlir::ModuleOp module,
3147 llvm::StringRef symbol) {
3148 DeviceOp deviceOp = getForSymbolInModule(module, symbol);
3150 if (!symbol.empty()) {
3151 module.emitError("No such device: ") << symbol;
3153 module.emitError("No 'main' device in module");
3163TileOp TileElement::tryGetTileOp() {
3164 auto element = cast<TileElement>(this->getOperation());
3165 return dyn_cast_or_null<TileOp>(element.getTile().getDefiningOp());
3168TileOp TileElement::getTileOp() {
3169 if (
auto tileOp = tryGetTileOp())
3171 llvm::report_fatal_error(
"Calling getTileOp requires TileOp.");
3178LogicalResult LogicalTileOp::verify() {
3180 *
this, (*this)->getAttrOfType<PacketInfoAttr>(
"controller_id"),
3184 int columns = targetModel.columns();
3185 int rows = targetModel.rows();
3188 if (
auto col = getCol()) {
3189 if (*col >= columns)
3190 return emitOpError(
"column index (")
3192 <<
") must be less than the number of columns in the device ("
3195 if (
auto row = getRow()) {
3197 return emitOpError(
"row index (")
3198 << *row <<
") must be less than the number of rows in the device ("
3203 AIETileType tileType = getTileType();
3204 bool tileTypeExists =
false;
3205 for (
int col = 0; col < columns && !tileTypeExists; col++) {
3206 for (
int row = 0; row < rows && !tileTypeExists; row++) {
3207 if (targetModel.getTileType(col, row) == tileType)
3208 tileTypeExists =
true;
3211 if (!tileTypeExists) {
3212 return emitOpError(
"tile type '")
3213 << stringifyAIETileType(tileType)
3214 <<
"' does not exist on the target device";
3219 if (
auto col = tryGetCol()) {
3220 if (
auto row = tryGetRow()) {
3221 if (targetModel.getTileType(*col, *row) != tileType) {
3222 return emitOpError(
"declared logical tile type does not match "
3223 "the tile type at coordinates (")
3224 << *col <<
", " << *row <<
")";
3232TileID LogicalTileOp::getCanonicalTileID() {
3236 std::optional<int32_t> col = getCol();
3237 std::optional<int32_t> row = getRow();
3238 if (col.has_value() && row.has_value())
3239 return {*col, *row};
3242 AIETileType tileType = getTileType();
3243 for (
int col = 0; col < targetModel.columns(); col++) {
3244 for (
int row = 0; row < targetModel.rows(); row++) {
3245 if (targetModel.getTileType(col, row) == tileType) {
3250 llvm_unreachable(
"No tile of matching tile type found in AIE device");
3253size_t LogicalTileOp::getNumSourceConnections(WireBundle bundle) {
3255 TileID tile = getCanonicalTileID();
3257 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
3259 if (isShimNOCorPLTile())
3260 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
3262 return targetModel.getNumDestSwitchboxConnections(tile.col, tile.row,
3268size_t LogicalTileOp::getNumDestConnections(WireBundle bundle) {
3270 TileID tile = getCanonicalTileID();
3272 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
3274 if (isShimNOCorPLTile())
3275 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
3277 return targetModel.getNumSourceSwitchboxConnections(tile.col, tile.row,
3283std::optional<int> LogicalTileOp::tryGetCol() {
3284 if (
auto col = getCol())
3286 return std::nullopt;
3289std::optional<int> LogicalTileOp::tryGetRow() {
3290 if (
auto row = getRow())
3292 return std::nullopt;
3299ParseResult LogicalTileOp::parse(OpAsmParser &parser, OperationState &result) {
3300 AIETileType tileType;
3301 if (parser.parseLess())
3304 StringRef tileTypeStr;
3305 if (parser.parseKeyword(&tileTypeStr))
3308 auto tileTypeOpt = symbolizeAIETileType(tileTypeStr);
3310 return parser.emitError(parser.getCurrentLocation(),
3311 "unknown logical tile type: ")
3313 tileType = *tileTypeOpt;
3315 if (parser.parseGreater())
3318 if (parser.parseLParen())
3321 std::optional<int32_t> col;
3322 if (succeeded(parser.parseOptionalQuestion())) {
3326 if (parser.parseInteger(colVal))
3331 if (parser.parseComma())
3334 std::optional<int32_t> row;
3335 if (succeeded(parser.parseOptionalQuestion())) {
3339 if (parser.parseInteger(rowVal))
3344 if (parser.parseRParen())
3348 if (parser.parseOptionalAttrDict(result.attributes))
3352 result.getOrAddProperties<LogicalTileOp::Properties>().tile_type =
3353 AIETileTypeAttr::get(parser.getContext(), tileType);
3355 result.getOrAddProperties<LogicalTileOp::Properties>().col =
3356 parser.getBuilder().getI32IntegerAttr(*col);
3358 result.getOrAddProperties<LogicalTileOp::Properties>().row =
3359 parser.getBuilder().getI32IntegerAttr(*row);
3362 result.addTypes(parser.getBuilder().getIndexType());
3367void LogicalTileOp::print(OpAsmPrinter &printer) {
3368 printer <<
"<" << stringifyAIETileType(getTileType()) <<
">";
3371 if (
auto col = getCol())
3376 if (
auto row = getRow())
3382 SmallVector<StringRef, 3> elidedAttrs = {
"tile_type",
"col",
"row"};
3383 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
3390LogicalResult TileOp::verify() {
3392 *
this, (*this)->getAttrOfType<PacketInfoAttr>(
"controller_id"),
3396 int columns = targetModel.columns();
3397 int rows = targetModel.rows();
3398 if (colIndex() >= columns)
3399 return emitOpError(
"column index (")
3401 <<
") must be less than the number of columns in the device ("
3403 if (rowIndex() >= rows)
3404 return emitOpError(
"row index (")
3406 <<
") must be less than the number of rows in the device (" << rows
3409 auto users = getResult().getUsers();
3411 for (
auto *user : users) {
3412 if (llvm::isa<SwitchboxOp>(*user)) {
3414 return emitOpError(
"can only have one switchbox");
3422size_t TileOp::getNumSourceConnections(WireBundle bundle) {
3424 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
3428 if (isShimNOCorPLTile())
3429 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
3431 return targetModel.getNumDestSwitchboxConnections(getCol(), getRow(),
3437size_t TileOp::getNumDestConnections(WireBundle bundle) {
3439 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
3443 if (isShimNOCorPLTile())
3444 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
3446 return targetModel.getNumSourceSwitchboxConnections(getCol(), getRow(),
3452std::optional<int> TileOp::tryGetCol() {
return getCol(); }
3453std::optional<int> TileOp::tryGetRow() {
return getRow(); }
3455AIETileType TileOp::getTileType() {
3457 return targetModel.getTileType(getCol(), getRow());
3460static bool isLegalTileConnection(TileOp tile,
3462 MasterSetOp masterOp, PacketRulesOp slaveOp) {
3463 auto srcBundle = slaveOp.sourcePort().bundle;
3464 auto srcChan = slaveOp.sourcePort().channel;
3465 auto dstBundle = masterOp.destPort().bundle;
3466 auto dstChan = masterOp.destPort().channel;
3468 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
3471static bool isLegalTileConnection(TileOp tile,
3473 ConnectOp connectOp) {
3474 auto srcBundle = connectOp.getSourceBundle();
3475 auto srcChan = connectOp.getSourceChannel();
3476 auto dstBundle = connectOp.getDestBundle();
3477 auto dstChan = connectOp.getDestChannel();
3479 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
3482TileOp TileOp::getOrCreate(mlir::OpBuilder builder, DeviceOp device,
int col,
3483 int row, std::optional<mlir::Location> loc) {
3484 TileOp tile =
nullptr;
3486 for (
auto t : device.getOps<AIE::TileOp>()) {
3487 if (t.getRow() == row && t.getCol() == col) {
3494 OpBuilder::InsertionGuard guard(builder);
3495 mlir::Block &device_start_block = *device.getBodyRegion().begin();
3496 builder.setInsertionPointToStart(&device_start_block);
3497 tile = TileOp::create(builder, loc.value_or(device.getLoc()),
3498 builder.getIndexType(), col, row);
3507LogicalResult ShimMuxOp::verify() {
3512 if (!isa<TileOp>(getTile().getDefiningOp()))
3515 Region &body = getConnections();
3516 DenseSet<Port> destset;
3518 return emitOpError(
"should have non-empty body");
3520 for (
auto &ops : body.front()) {
3521 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
3522 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
3523 if (destset.count(dest))
3524 return connectOp.emitOpError(
"targets same destination ")
3525 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
3526 <<
" as another connect operation";
3527 destset.insert(dest);
3528 }
else if (isa<EndOp>(ops)) {
3531 return ops.emitOpError(
"cannot be contained in a Switchbox op");
3537size_t ShimMuxOp::getNumSourceConnections(WireBundle bundle) {
3538 auto tile = getTileOp();
3541 tile.getRow(), bundle);
3544size_t ShimMuxOp::getNumDestConnections(WireBundle bundle) {
3545 auto tile = getTileOp();
3555LogicalResult ShimDMAOp::verify() {
3562TileOp ShimDMAOp::getTileOp() {
3563 return cast<TileElement>(this->getOperation()).getTileOp();
3566LogicalResult PacketRulesOp::verify() {
3567 if (Region &body = getRules(); body.empty())
3568 return emitOpError(
"should have non-empty body");
3572LogicalResult MasterSetOp::verify() {
3574 for (
auto val : getAmsels()) {
3575 auto amsel = dyn_cast_if_present<AMSelOp>(val.getDefiningOp());
3577 return emitOpError(
"amsel operand must be produced by an 'aie.amsel' op");
3578 if (arbiter != -1 && arbiter != amsel.arbiterIndex())
3579 return emitOpError(
"a master port can only be tied to one arbiter");
3580 arbiter = amsel.arbiterIndex();
3585bool MasterSetOp::keepsPktHeader() {
3587 (*this)->getParentOfType<SwitchboxOp>().getTileOp().getTileID(),
3588 destPort(), getKeepPktHeader());
3592 std::optional<bool> keep) {
3595 return master.bundle != WireBundle::DMA &&
3596 (tile.row != 0 || master.bundle != WireBundle::South);
3601static LogicalResult verifyFlowEndpoint(Operation *op, Value tile,
Port port,
3603 if (port.bundle != WireBundle::Core && port.bundle != WireBundle::DMA)
3605 auto tileOp = dyn_cast_or_null<TileOp>(tile.getDefiningOp());
3608 size_t channels = isSource ? tileOp.getNumDestConnections(port.bundle)
3609 : tileOp.getNumSourceConnections(port.bundle);
3610 if (
static_cast<size_t>(port.channel) < channels)
3612 return op->emitOpError() << (isSource ?
"source " :
"destination ")
3613 << stringifyWireBundle(port.bundle) <<
":"
3614 << port.channel <<
" does not exist: tile ("
3615 << tileOp.getCol() <<
", " << tileOp.getRow()
3616 <<
") has " << channels <<
" "
3617 << stringifyWireBundle(port.bundle) <<
" port"
3618 << (channels == 1 ?
"" :
"s")
3619 << (isSource ?
" into" :
" out of")
3620 <<
" its stream switch";
3623static LogicalResult verifyFlowVias(Operation *op, OperandRange vias,
3624 DenseI32ArrayAttr ingressBundles,
3625 DenseI32ArrayAttr ingressChannels,
3626 DenseI32ArrayAttr egressBundles,
3627 DenseI32ArrayAttr egressChannels) {
3628 size_t numVias = vias.size();
3629 DenseI32ArrayAttr arrays[] = {ingressBundles, ingressChannels, egressBundles,
3631 for (DenseI32ArrayAttr array : arrays) {
3632 size_t size = array ? array.size() : 0;
3633 if (size != numVias) {
3634 return op->emitOpError(
"has ")
3635 << numVias <<
" via tile(s) but a via port array of size " << size;
3642 for (
auto [index, via] : llvm::enumerate(vias)) {
3643 if (!isa_and_nonnull<TileLike>(via.getDefiningOp())) {
3644 return op->emitOpError(
"has via operand at index ")
3645 << index <<
" that is not defined by a tile-like operation";
3649 auto verifyBundles = [&](DenseI32ArrayAttr bundles,
3650 StringRef direction) -> LogicalResult {
3651 for (
auto [index, bundle] : llvm::enumerate(bundles.asArrayRef())) {
3653 bundle >
static_cast<int32_t
>(getMaxEnumValForWireBundle())) {
3654 return op->emitOpError(
"has invalid via ")
3655 << direction <<
" bundle " << bundle <<
" at index " << index;
3660 auto verifyChannels = [&](DenseI32ArrayAttr channels,
3661 StringRef direction) -> LogicalResult {
3662 for (
auto [index, channel] : llvm::enumerate(channels.asArrayRef())) {
3664 return op->emitOpError(
"has negative via ")
3665 << direction <<
" channel " << channel <<
" at index " << index;
3670 if (failed(verifyBundles(ingressBundles,
"ingress")) ||
3671 failed(verifyChannels(ingressChannels,
"ingress")) ||
3672 failed(verifyBundles(egressBundles,
"egress")) ||
3673 failed(verifyChannels(egressChannels,
"egress"))) {
3679LogicalResult FlowOp::verify() {
3680 if (failed(verifyFlowEndpoint(*
this, getSource(),
3681 {getSourceBundle(), sourceIndex()},
true)))
3683 if (failed(verifyFlowEndpoint(*
this, getDest(),
3684 {getDestBundle(), destIndex()},
false))) {
3688 return verifyFlowVias(*
this, getVias(), getViaIngressBundlesAttr(),
3689 getViaIngressChannelsAttr(), getViaEgressBundlesAttr(),
3690 getViaEgressChannelsAttr());
3693LogicalResult PacketSourceOp::verify() {
3694 return verifyFlowEndpoint(*
this, getTile(), port(),
true);
3697LogicalResult PacketDestOp::verify() {
3698 return verifyFlowEndpoint(*
this, getTile(), port(),
false);
3701LogicalResult PacketFlowOp::verify() {
3702 Region &body = getPorts();
3704 return emitOpError(
"should have non-empty body");
3706 int numSources = 0, numDests = 0;
3707 for (
auto &ops : body.front()) {
3708 if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
3709 return ops.emitOpError(
"cannot be contained in a PacketFlow op");
3710 if (isa<PacketSourceOp>(ops))
3712 if (isa<PacketDestOp>(ops))
3717 return emitOpError(
"must have at least one aie.packet_source");
3719 return emitOpError(
"must have at least one aie.packet_dest");
3720 if (!getVias().empty() && numSources != 1) {
3722 "with via waypoints must have exactly one aie.packet_source");
3724 if (!getVias().empty() && numDests != 1) {
3726 "with via waypoints must have exactly one aie.packet_dest");
3731 if (std::optional<uint8_t> mask = getMask()) {
3732 uint8_t
id = getID();
3733 if ((
id & *mask) !=
id) {
3734 return emitOpError(
"has ID 0x")
3735 << llvm::utohexstr(
id) <<
" outside mask 0x"
3736 << llvm::utohexstr(*mask) <<
", which no packet can match";
3740 return verifyFlowVias(*
this, getVias(), getViaIngressBundlesAttr(),
3741 getViaIngressChannelsAttr(), getViaEgressBundlesAttr(),
3742 getViaEgressChannelsAttr());
3749LogicalResult CoreOp::verify() {
3750 if (getBody().empty())
3751 return emitOpError(
"should have non-empty body");
3758 "When `elf_file` attribute is specified, core body must be empty "
3759 "(consist of exactly one `aie.end` op).");
3762 if (getLinkWith() && getLinkFiles())
3764 "cannot specify both 'link_with' (deprecated) and 'link_files' "
3765 "on the same core; run aie-assign-core-link-files to migrate");
3766 if (getLinkWith() && getLinkMergeFiles())
3768 "cannot specify both 'link_with' (deprecated) and 'link_merge_files' "
3769 "on the same core; run aie-assign-core-link-files to migrate");
3772 if (
auto linkFiles = getLinkFiles())
3773 if (
auto mergeFiles = getLinkMergeFiles()) {
3774 llvm::SmallSet<StringRef, 8> linked;
3775 for (
auto f : linkFiles->getAsRange<StringAttr>())
3776 linked.insert(f.getValue());
3777 for (
auto f : mergeFiles->getAsRange<StringAttr>())
3778 if (linked.count(f.getValue()))
3779 return emitOpError(
"artifact '")
3781 <<
"' appears in both 'link_files' and 'link_merge_files'; an "
3782 "artifact must be either merged or linked, not both";
3786 if (
auto measured = getMeasuredDataSize())
3787 if (
auto declared = getDataSize(); declared && *declared < *measured)
3788 return emitOpError(
"data_size ")
3789 << *declared <<
" is smaller than the " << *measured
3790 <<
" bytes this core's linked sections occupy";
3795 auto validAlignment = [localMem](int64_t alignment) {
3796 return alignment > 0 && alignment <= localMem &&
3797 llvm::isPowerOf2_64(alignment);
3799 if (
auto alignment = getMeasuredDataAlignment();
3800 alignment && !validAlignment(*alignment))
3801 return emitOpError(
"measured_data_alignment must be a power of two between "
3803 << localMem <<
" bytes";
3804 if (
auto alignments = getMeasuredBankAlignments())
3805 if (!llvm::all_of(*alignments, validAlignment))
3806 return emitOpError(
"measured_bank_alignments must contain only powers of "
3807 "two between 1 and ")
3808 << localMem <<
" bytes";
3810 auto tile = dyn_cast_if_present<TileOp>(getTile().getDefiningOp());
3812 tile ? targetModel.
getNumBanks(tile.getCol(), tile.getRow()) : 0;
3815 if (
auto bank = getStackBank()) {
3816 if (tile && *bank >= numBanks)
3817 return emitOpError(
"stack_bank ")
3818 << *bank <<
" does not exist; this tile has " << numBanks
3821 if (bankSize > 0 && stackRun.
size > bankSize)
3822 return emitOpError(
"stack_bank pins a ")
3823 << stackRun.
size <<
"-byte stack to bank " << *bank
3824 <<
", which holds " << bankSize
3825 <<
" bytes; omit stack_bank and stack_address for legacy "
3827 if (getStackAddress() && bankSize > 0 && stackRun.
start / bankSize != *bank)
3828 return emitOpError(
"stack_address 0x")
3829 << llvm::utohexstr(stackRun.
start) <<
" lies in bank "
3830 << stackRun.
start / bankSize <<
", but stack_bank requests bank "
3832 if (getStackAddress() && bankSize > 0 &&
3833 stackRun.
end() > (*bank + 1) * bankSize)
3834 return emitOpError(
"a ")
3835 << stackRun.
size <<
"-byte stack at 0x"
3836 << llvm::utohexstr(stackRun.
start) <<
" runs past stack_bank "
3837 << *bank <<
" (ending at 0x"
3838 << llvm::utohexstr((*bank + 1) * bankSize) <<
")";
3843 if (stackRun.
size >= localMem)
3844 return emitOpError(getStackSize() ?
"stack_size "
3845 :
"measured stack requirement ")
3847 <<
" leaves no local memory for this tile's buffers (" << localMem
3849 if (stackRun.
end() > localMem)
3850 return emitOpError(
"a ") << stackRun.
size <<
"-byte stack at 0x"
3851 << llvm::utohexstr(stackRun.
start)
3852 <<
" runs past this tile's local memory ("
3853 << localMem <<
" bytes total)";
3854 if (getStackAddress() &&
3856 return emitOpError(
"stack_address must be aligned to ")
3858 <<
" bytes for this target's stack ABI";
3862bool CoreOp::isEmpty() {
3863 Region &body = getBody();
3865 return (body.hasOneBlock() && body.front().getOperations().size() == 1 &&
3866 llvm::isa<AIE::EndOp>(body.front().front()));
3869TileOp CoreOp::getTileOp() {
3870 return cast<TileElement>(this->getOperation()).getTileOp();
3873uint32_t CoreOp::getEffectiveStackSize() {
3874 if (
auto declared = getStackSize())
3878 if (
auto measured = getMeasuredStackSize())
3879 return std::max<uint32_t>(
3886 return {getStackAddress().value_or(0), getEffectiveStackSize()};
3893int64_t BufferOp::getAllocationSize() {
3894 auto type = llvm::cast<MemRefType>(getType());
3895 DataLayout dataLayout = DataLayout::closest(getOperation());
3896 return type.getNumElements() * dataLayout.getTypeSize(type.getElementType());
3899LogicalResult BufferOp::verify() {
3900 if (UsesAreAccessible::verifyTrait(*this).failed()) {
3906 auto tile = dyn_cast_if_present<TileOp>(getTile().getDefiningOp());
3913 if (getCoreData()) {
3914 for (Operation *user : tile.getResult().getUsers()) {
3915 auto other = dyn_cast<BufferOp>(user);
3916 if (other && other != *
this && other.getCoreData()) {
3917 return emitOpError(
"is a second core_data buffer on tile (")
3918 << tile.getCol() <<
", " << tile.getRow()
3919 <<
"); the core's data region is one extent";
3927 std::optional<int32_t> address = getAddress();
3928 std::optional<int32_t> memBank = getMemBank();
3929 if (address && memBank && getAllocationSize() > 0) {
3931 int64_t numBanks = targetModel.
getNumBanks(tile.getCol(), tile.getRow());
3932 int64_t memSize = tile.isMemTile() ? targetModel.
getMemTileSize()
3935 int64_t bankSize = memSize / numBanks;
3936 int64_t bankOfAddress = *address / bankSize;
3937 if (bankOfAddress != *memBank) {
3938 return emitOpError(
"address 0x")
3939 << llvm::utohexstr(*address) <<
" lies in bank " << bankOfAddress
3940 <<
", but mem_bank requests bank " << *memBank;
3950 if (
auto buf = dyn_cast<BufferOp>(bufOp)) {
3951 std::optional<int32_t> address = buf.getAddress();
3952 assert(address.has_value() &&
"buffer must have address assigned");
3955 if (isa_and_nonnull<ExternalBufferOp>(bufOp))
3956 llvm::report_fatal_error(
3957 "External buffer addresses are assigned at runtime.");
3958 llvm::report_fatal_error(
"unknown buffer type");
3962 DenseMap<TileID, Operation *> &tiles) {
3963 for (
auto tile : device.getOps<TileOp>()) {
3964 tiles[tile.getTileID()] = tile;
3970 DenseMap<Operation *, SmallVector<BufferOp, 4>> &buffers) {
3971 for (BufferOp buffer : device.getOps<BufferOp>()) {
3972 Operation *tileOp = buffer.getTile().getDefiningOp();
3973 buffers[tileOp].push_back(buffer);
3977static void printBufferInitialValue(OpAsmPrinter &p, BufferOp op, Type type,
3978 Attribute initialValue) {
3979 if (op.getInitialValue()) {
3981 p.printAttributeWithoutType(initialValue);
3985static ParseResult parseBufferInitialValue(OpAsmParser &parser, Type &type,
3986 Attribute &initialValue) {
3987 auto memrefType = llvm::cast<MemRefType>(type);
3988 if (!memrefType.hasStaticShape())
3989 return parser.emitError(parser.getNameLoc())
3990 <<
"type should be static shaped memref, but got " << type;
3992 if (parser.parseOptionalEqual())
3995 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
3996 if (parser.parseAttribute(initialValue, tensorType))
3998 if (!llvm::isa<ElementsAttr>(initialValue))
3999 return parser.emitError(parser.getNameLoc())
4000 <<
"initial value should be an elements attribute";
4004TileOp BufferOp::getTileOp() {
4005 return cast<TileElement>(this->getOperation()).getTileOp();
4012LogicalResult MemOp::verify() {
4013 Region &body = getBody();
4018 for (
auto &bodyOp : body.getOps()) {
4019 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp))
4020 if (!allocOp->getAttr(
"id"))
4021 return allocOp.emitOpError()
4022 <<
"allocOp in MemOp region should have an id attribute";
4027TileOp MemOp::getTileOp() {
4028 return cast<TileElement>(this->getOperation()).getTileOp();
4035LogicalResult MemTileDMAOp::verify() {
4036 assert(getOperation()->getNumRegions() == 1 &&
4037 "MemTileDMAOp has zero region!");
4043 for (
auto &bodyOp : getBody().getOps()) {
4044 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp)) {
4045 if (!allocOp->getAttr(
"id"))
4046 return allocOp.emitOpError()
4047 <<
"allocOp in MemTileDMAOp region should have an id attribute";
4049 if (
auto startOp = dyn_cast<DMAStartOp>(bodyOp)) {
4052 if (!startOp.getEndpoint() && startOp.getChannelIndex() > 3) {
4058 llvm::SmallSet<Block *, 16> reachable;
4059 SmallVector<Block *, 16> worklist;
4060 Block *firstBD = startOp.getSuccessor(0);
4061 reachable.insert(firstBD);
4062 worklist.push_back(firstBD);
4063 while (!worklist.empty()) {
4064 Block *block = worklist.pop_back_val();
4067 auto successors = block->getTerminator()->getSuccessors();
4068 for (
auto *i : successors) {
4069 if (!reachable.contains(i)) {
4070 reachable.insert(i);
4071 worklist.push_back(i);
4075 for (Block *b : reachable) {
4076 for (DMABDOp bd : b->getOps<DMABDOp>()) {
4077 if (
auto bufferOp = bd.getBufferOp();
4078 bufferOp.getTile() != getTile()) {
4079 InFlightDiagnostic err =
4081 <<
"is reachable from DMA channel "
4082 << startOp.getChannelIndex()
4083 <<
" and attempts to access a non-local buffer\n";
4084 err.attachNote(startOp->getLoc()) <<
"channel";
4085 err.attachNote(bufferOp->getLoc()) <<
"buffer";
4089 for (
auto useLock : b->getOps<UseLockOp>()) {
4090 if (
auto lockOp = useLock.getLockOp();
4091 lockOp.getTile() != getTile()) {
4092 InFlightDiagnostic err =
4093 useLock.emitOpError()
4094 <<
"is reachable from DMA channel "
4095 << startOp.getChannelIndex()
4096 <<
" and attempts to access a non-local lock\n";
4097 err.attachNote(startOp->getLoc()) <<
"channel";
4098 err.attachNote(lockOp->getLoc()) <<
"lock";
4110TileOp MemTileDMAOp::getTileOp() {
4111 return cast<TileElement>(this->getOperation()).getTileOp();
4118static bool isBdPacketEnabled(DMABDOp bd) {
4119 if (bd.getPacket().has_value())
4121 if (Block *blk = bd->getBlock())
4122 return !blk->getOps<DMABDPACKETOp>().empty();
4127 PacketInfoAttr packet,
4129 if (packet && !packet.isAssigned())
4130 return op->emitOpError()
4132 <<
" has no pkt_id; allocation assigns one, so a header at "
4133 "this level must carry it";
4139 std::optional<int32_t> oooId = bd.getOutOfOrderId();
4140 if (!oooId.has_value())
4144 return bd.emitOpError(
"out_of_order_id is not supported on this device");
4145 if (!packetEnabledByContext && !isBdPacketEnabled(bd))
4146 return bd.emitOpError(
"out_of_order_id requires a packet-enabled BD");
4148 if (*oooId < 0 ||
static_cast<uint32_t
>(*oooId) > maxOooId)
4149 return bd.emitOpError(
"out_of_order_id must be in [0, ") << maxOooId <<
"]";
4159 AIETileType tileType) {
4160 std::optional<BDIterationAttr> iter = bd.getIteration();
4165 return bd.emitOpError(
"BD iteration is not supported on this target");
4166 uint32_t size = iter->getSize(), current = iter->getCurrent();
4168 if (size < 1 || size > maxSize)
4169 return bd.emitOpError(
"BD iteration size must be in [1, ")
4172 int64_t strideInBytes =
static_cast<int64_t
>(iter->getStride()) *
4173 bd.getBufferElementTypeWidthInBytes();
4174 if (strideInBytes % 4)
4175 return bd.emitOpError(
4176 "BD iteration stride must be aligned to 32-bit words");
4177 int64_t stepInWords = strideInBytes / 4;
4179 if (stepInWords < 1 || stepInWords > maxStep)
4180 return bd.emitOpError() <<
"BD iteration stride must be in [1, "
4181 << maxStep <<
"] 32-bit words";
4183 if (current >= size)
4184 return bd.emitOpError(
"BD iteration current must be in [0, size)");
4188static LogicalResult verifyDMARepeatCount(Operation *op, int32_t repeatCount) {
4190 if (maxRepeat == 0) {
4191 if (repeatCount != 0)
4192 return op->emitOpError(
"repeat_count is not supported on this target");
4195 if (repeatCount < 0 ||
static_cast<uint32_t
>(repeatCount) > maxRepeat)
4196 return op->emitOpError(
"repeat_count ")
4197 << repeatCount <<
" is out of range [0, " << maxRepeat
4198 <<
"] for this target";
4204 bool outOfOrder, ArrayRef<DMABDOp> bds,
4205 bool packetEnabledByContext) {
4208 if (dir != DMAChannelDir::S2MM)
4209 return op->emitOpError(
"out_of_order is only valid on an S2MM channel");
4211 return op->emitOpError(
4212 "out-of-order S2MM DMA is not supported on this device");
4214 return op->emitOpError(
"out-of-order S2MM channel must have at least one "
4215 "receive buffer descriptor");
4216 for (DMABDOp bd : bds) {
4217 if (!packetEnabledByContext && !isBdPacketEnabled(bd))
4218 return bd.emitOpError(
4219 "out-of-order S2MM receive buffer descriptor must be packet-enabled");
4220 if (bd.getOutOfOrderId().has_value())
4221 return bd.emitOpError(
4222 "out_of_order_id belongs on the sender buffer descriptor, not an "
4223 "out-of-order S2MM receive buffer descriptor");
4226 DenseMap<Operation *, Operation *> releasedByRecvBd;
4227 for (DMABDOp bd : bds)
4228 for (
auto useLock : bd->getBlock()->getOps<UseLockOp>())
4229 if (useLock.release())
4230 if (Operation *lockDef = useLock.getLock().getDefiningOp())
4231 releasedByRecvBd.try_emplace(lockDef, bd.getOperation());
4232 for (DMABDOp bd : bds)
4233 for (
auto useLock : bd->getBlock()->getOps<UseLockOp>())
4234 if (useLock.acquire() || useLock.acquireGE())
4235 if (Operation *lockDef = useLock.getLock().getDefiningOp()) {
4236 auto it = releasedByRecvBd.find(lockDef);
4237 if (it != releasedByRecvBd.end() && it->second != bd.getOperation())
4238 return bd.emitOpError(
4239 "out-of-order S2MM prohibits inter-BD lock dependencies; "
4247 UseLockOp &release) {
4250 auto reject = [&](UseLockOp at) {
4251 auto diag = at.emitOpError(
4252 "does not fit its buffer descriptor, which has one lock-acquire field "
4253 "and one lock-release field; a buffer descriptor block uses either no "
4254 "lock or one use_lock(acquire) and one use_lock(release)");
4256 diag <<
", or a use_lock(release) alone when out-of-order";
4259 for (UseLockOp op : block.getOps<UseLockOp>()) {
4260 UseLockOp &slot = op.release() ? release : acquire;
4265 if (!acquire && !release)
4267 if (release && (acquire || outOfOrder))
4269 return reject(acquire ? acquire : release);
4272LogicalResult DMAOp::verify() {
4273 auto *parentOp = getOperation()->getParentOp();
4274 if (parentOp->getRegion(0).getBlocks().size() > 1)
4275 return emitOpError(
"DMAOp can only appear in single block region");
4276 if (!parentOp->getRegion(0).getOps<DMAStartOp>().empty())
4277 return emitOpError(
"DMAOp is not compatible with DMAStart ops");
4278 auto bdRegions = getBds();
4279 for (
auto &bdRegion : bdRegions) {
4280 if (!bdRegion.hasOneBlock())
4281 return emitOpError(
"DMAOp regions must have only one block");
4282 auto bds = llvm::to_vector_of<DMABDOp>(bdRegion.front().getOps<DMABDOp>());
4283 if (bds.size() != 1)
4284 return emitOpError(
"DMAOp regions/blocks must have exactly one DMABDOp");
4286 llvm::to_vector_of<UseLockOp>(bdRegion.front().getOps<UseLockOp>());
4287 if (useLocks.size() != 2)
4289 "DMAOp regions/blocks must have exactly two UseLock ops");
4291 if (getPadValue() != 0) {
4292 if (!isa<MemTileDMAOp>(parentOp))
4293 return emitOpError(
"pad_value is only supported on memtile DMA channels");
4294 if (getChannelDir() != DMAChannelDir::MM2S)
4295 return emitOpError(
"pad_value is only supported on MM2S DMA channels");
4296 if (!
getTargetModel(getOperation()).isMemTilePadValueSupported())
4297 return emitOpError(
"pad_value requires the CONSTANT_PAD_VALUE register, "
4298 "unavailable on this target");
4300 if (failed(verifyDMARepeatCount(getOperation(), getRepeatCount())))
4302 SmallVector<DMABDOp> bds;
4303 for (
auto &bdRegion : getBds())
4304 llvm::append_range(bds, bdRegion.front().getOps<DMABDOp>());
4306 getOutOfOrder(), bds);
4313BufferOp DMABDOp::getBufferOp() {
4314 return cast<BufferOp>(getBuffer().getDefiningOp());
4319static ParseResult parseOptionalOperandType(OpAsmParser &parser, Type &type,
4322 if (succeeded(parser.parseOptionalColon()))
4323 return parser.parseType(type);
4327static void printOptionallyTypedOperand(OpAsmPrinter &printer, Value operand,
4330 if (operand.getType() != defaultType)
4331 printer <<
" : " << operand.getType();
4340parseDynamicScalar(OpAsmParser &parser,
4341 std::optional<OpAsmParser::UnresolvedOperand> &operand,
4342 IntegerAttr &staticAttr, Type &type) {
4344 OptionalParseResult intResult = parser.parseOptionalInteger(intValue);
4345 if (intResult.has_value()) {
4346 if (failed(*intResult))
4349 parser.getBuilder().getI32IntegerAttr(
static_cast<int32_t
>(intValue));
4352 OpAsmParser::UnresolvedOperand op;
4353 if (parser.parseOperand(op) ||
4354 parseOptionalOperandType(parser, type, parser.getBuilder().getI32Type()))
4360static void printDynamicScalar(OpAsmPrinter &printer, Operation *,
4361 Value operand, IntegerAttr staticAttr, Type) {
4363 printOptionallyTypedOperand(printer, operand,
4364 IntegerType::get(operand.getContext(), 32));
4366 printer << staticAttr.getInt();
4370 OpAsmParser &parser,
4371 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &values,
4372 DenseI64ArrayAttr &integers, SmallVectorImpl<Type> &types) {
4373 SmallVector<int64_t> ints;
4374 auto parseEntry = [&]() -> ParseResult {
4375 OpAsmParser::UnresolvedOperand operand;
4376 OptionalParseResult isOperand = parser.parseOptionalOperand(operand);
4377 if (!isOperand.has_value())
4378 return parser.parseInteger(ints.emplace_back());
4380 if (failed(*isOperand) ||
4381 parseOptionalOperandType(parser, type,
4382 parser.getBuilder().getI64Type()))
4384 values.push_back(operand);
4385 types.push_back(type);
4386 ints.push_back(ShapedType::kDynamic);
4389 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Square, parseEntry))
4391 integers = parser.getBuilder().getDenseI64ArrayAttr(ints);
4396 OperandRange values,
4397 ArrayRef<int64_t> integers,
4400 auto value = values.begin();
4401 llvm::interleaveComma(integers, printer, [&](int64_t i) {
4402 if (!ShapedType::isDynamic(i)) {
4407 printOptionallyTypedOperand(printer, v,
4408 IntegerType::get(v.getContext(), 64));
4415static void splitScalarOfr(mlir::OpBuilder &builder, mlir::OpFoldResult ofr,
4416 mlir::Value &operand, mlir::IntegerAttr &attr) {
4421 if (
auto v = llvm::dyn_cast_if_present<mlir::Value>(ofr)) {
4426 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::Attribute>(ofr));
4427 attr = builder.getI32IntegerAttr(
static_cast<int32_t
>(intAttr.getInt()));
4430void DMABDOp::buildMixed(mlir::OpBuilder &builder, mlir::OperationState &state,
4431 mlir::Value buffer, mlir::OpFoldResult offset,
4432 mlir::OpFoldResult len,
4433 llvm::ArrayRef<mlir::OpFoldResult> sizes,
4434 llvm::ArrayRef<mlir::OpFoldResult> strides,
4435 BDPadLayoutArrayAttr padDims, PacketInfoAttr packet) {
4438 llvm::SmallVector<int64_t> staticSizes, staticStrides;
4439 llvm::SmallVector<mlir::Value> dynSizes, dynStrides;
4440 mlir::dispatchIndexOpFoldResults(sizes, dynSizes, staticSizes);
4441 mlir::dispatchIndexOpFoldResults(strides, dynStrides, staticStrides);
4445 DenseI64ArrayAttr staticSizesAttr =
4446 staticSizes.empty() ? DenseI64ArrayAttr{}
4447 : builder.getDenseI64ArrayAttr(staticSizes);
4448 DenseI64ArrayAttr staticStridesAttr =
4449 staticStrides.empty() ? DenseI64ArrayAttr{}
4450 : builder.getDenseI64ArrayAttr(staticStrides);
4454 mlir::Value offsetVal, lenVal;
4455 mlir::IntegerAttr offsetAttr, lenAttr;
4456 splitScalarOfr(builder, offset, offsetVal, offsetAttr);
4457 splitScalarOfr(builder, len, lenVal, lenAttr);
4459 build(builder, state, buffer, offsetVal, lenVal,
4460 offsetAttr, lenAttr,
4461 dynSizes, dynStrides,
4481void DMABDOp::buildWithConstants(mlir::OpBuilder &builder,
4482 mlir::OperationState &state,
4483 mlir::Value buffer, int32_t offset,
4484 int32_t len, BDDimLayoutArrayAttr dims,
4485 BDPadLayoutArrayAttr padDims,
4486 PacketInfoAttr packet) {
4490 mlir::OpFoldResult offsetOfr = builder.getI32IntegerAttr(offset);
4491 mlir::OpFoldResult lenOfr = builder.getI32IntegerAttr(len);
4495 llvm::SmallVector<mlir::OpFoldResult> sizes, strides;
4497 for (BDDimLayoutAttr d : dims) {
4498 sizes.push_back(builder.getI64IntegerAttr(d.getSize()));
4499 strides.push_back(builder.getI64IntegerAttr(d.getStride()));
4502 buildMixed(builder, state, buffer, offsetOfr, lenOfr, sizes, strides, padDims,
4506llvm::SmallVector<mlir::OpFoldResult> DMABDOp::getMixedSizes() {
4507 return ::mlir::getMixedValues(
4508 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{}), getSizes(),
4512llvm::SmallVector<mlir::OpFoldResult> DMABDOp::getMixedStrides() {
4513 return ::mlir::getMixedValues(
4514 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{}), getStrides(),
4518LogicalResult DMABDOp::verifyMixedSizesAndStrides() {
4519 llvm::ArrayRef<int64_t> staticSizes =
4520 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{});
4521 llvm::ArrayRef<int64_t> staticStrides =
4522 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{});
4523 if (failed(mlir::verifyListOfOperandsOrIntegers(
4524 *
this,
"sizes", staticSizes.size(), staticSizes, getSizes())))
4526 if (failed(mlir::verifyListOfOperandsOrIntegers(
4527 *
this,
"strides", staticStrides.size(), staticStrides, getStrides())))
4529 if (staticSizes.size() != staticStrides.size())
4530 return emitOpError(
"expected the same number of sizes (")
4531 << staticSizes.size() <<
") and strides (" << staticStrides.size()
4536std::optional<llvm::SmallVector<BDDimLayoutAttr>> DMABDOp::getFoldedDimensions(
4537 llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
4538 llvm::ArrayRef<int64_t> sizes =
4539 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{});
4540 llvm::ArrayRef<int64_t> strides =
4541 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{});
4542 if (sizes.size() != strides.size()) {
4543 emitError() <<
"expected the same number of sizes (" << sizes.size()
4544 <<
") and strides (" << strides.size() <<
")";
4545 return std::nullopt;
4547 mlir::OperandRange dynSizes = getSizes(), dynStrides = getStrides();
4548 unsigned nextSize = 0, nextStride = 0;
4549 llvm::SmallVector<BDDimLayoutAttr> dims;
4550 dims.reserve(sizes.size());
4551 for (
auto [s, t] : llvm::zip(sizes, strides)) {
4552 std::optional<int64_t> sc =
4553 ShapedType::isDynamic(s)
4554 ? mlir::getConstantIntValue(dynSizes[nextSize++])
4556 std::optional<int64_t> tc =
4557 ShapedType::isDynamic(t)
4558 ? mlir::getConstantIntValue(dynStrides[nextStride++])
4561 emitError() <<
"buffer descriptor size/stride is a runtime value; a "
4562 "compile-time constant is required on this path";
4563 return std::nullopt;
4565 dims.push_back(BDDimLayoutAttr::get(
4566 getContext(),
static_cast<uint32_t
>(*sc),
static_cast<uint32_t
>(*tc)));
4571std::optional<llvm::SmallVector<BDDimLayoutAttr>>
4572DMABDOp::getConstantDimensions() {
4573 return getFoldedDimensions([&]() {
return emitOpError(); });
4579LogicalResult DMABDOp::verifyTransferInBounds() {
4580 auto buffer = llvm::dyn_cast<MemRefType>(getBuffer().getType());
4581 if (!buffer || !buffer.hasStaticShape() ||
4582 llvm::isa<BlockArgument>(getBuffer()))
4584 if ((getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) && !hasLen())
4585 return emitOpError(
"offset_parameter requires an explicit `len` on a local "
4586 "buffer; the default whole-buffer length runs past the "
4587 "end for any nonzero runtime offset");
4588 std::optional<int32_t> offset = hasOffset() ? getConstantOffset() : 0;
4589 if (!offset || *offset < 0)
4591 int64_t numElements = buffer.getNumElements();
4592 SmallVector<OpFoldResult> sizes = getMixedSizes();
4593 SmallVector<OpFoldResult> strides = getMixedStrides();
4594 if (sizes.empty()) {
4595 std::optional<int64_t> len =
4596 hasLen() ? std::optional<int64_t>(getConstantLen()) : numElements;
4597 if (!len || *len <= 0 || *offset + *len <= numElements)
4599 InFlightDiagnostic err = emitOpError(
"transfer of ")
4600 << *len <<
" elements at offset " << *offset
4601 <<
" runs past the end of the " << numElements
4602 <<
"-element buffer";
4604 err.attachNote() <<
"an omitted `len` is the whole buffer; give the "
4605 "length to transfer from this offset";
4608 int64_t maxIdx = *offset;
4609 for (
auto [size, stride] : llvm::zip(sizes, strides)) {
4610 std::optional<int64_t> s = getConstantIntValue(size);
4611 std::optional<int64_t> t = getConstantIntValue(stride);
4614 maxIdx += *t * (*s - 1);
4616 if (maxIdx < numElements)
4618 return emitOpError() <<
"Specified stride(s) and size(s) result in out of "
4619 "bounds access in buffer, for index "
4620 << maxIdx <<
" in memref of length " << numElements
4628 llvm::ArrayRef<BDPadLayoutAttr> pads,
4630 if (dims.empty() || pads.empty())
4633 for (
auto [dim, pad] : llvm::zip(dims, pads))
4634 size *= dim.getSize() + pad.getConstPadBefore() + pad.getConstPadAfter();
4642 if (dims.back().getStride() != 1)
4646 uint64_t product = 1;
4647 for (
int i =
static_cast<int>(dims.size()) - 1; i >= 1; --i) {
4648 product *= dims[i].getSize();
4650 if (dims[i - 1].getSize() > 1 && dims[i - 1].getStride() != product)
4656std::optional<SmallVector<BDDimLayoutAttr>>
4658 SmallVector<BDDimLayoutAttr> kept;
4659 int64_t product = 1;
4660 for (BDDimLayoutAttr dim : dims) {
4661 product *= dim.getSize();
4662 if (dim.getSize() != 1)
4663 kept.push_back(dim);
4665 if (kept.size() == dims.size() || len != product)
4666 return std::nullopt;
4668 return SmallVector<BDDimLayoutAttr>{};
4678 BaseMemRefType buffer) {
4679 uint64_t elemBitWidth = buffer.getElementTypeBitWidth();
4680 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
4681 return op->emitOpError(
"length_parameter requires a whole-byte element "
4683 std::optional<int64_t> unitBytes =
4684 llvm::checkedMul(lengthUnit,
static_cast<int64_t
>(elemBitWidth / 8));
4685 if (!unitBytes || *unitBytes / 4 > std::numeric_limits<uint32_t>::max())
4686 return op->emitOpError(
"length_unit (")
4688 <<
" elements) exceeds the 32-bit word count of a BD length";
4689 if (*unitBytes % 16 != 0)
4690 return op->emitOpError(
"length_unit must be a multiple of 16 bytes, got ")
4691 << *unitBytes <<
" bytes";
4696 Operation *op, std::optional<int64_t> lengthUnit, BaseMemRefType buffer,
4697 std::optional<int64_t> lenElems,
bool contiguous,
4698 llvm::ArrayRef<int64_t> innerSizes) {
4700 return op->emitOpError(
"length_parameter requires length_unit");
4703 int64_t elemBytes = buffer.getElementTypeBitWidth() / 8;
4705 return op->emitOpError(
"length_parameter requires a constant length");
4706 if ((*lenElems * elemBytes) % 16 != 0)
4707 return op->emitOpError(
"length_parameter requires a static length that is "
4708 "a multiple of 16 bytes, got ")
4709 << *lenElems * elemBytes <<
" bytes";
4712 llvm::SmallVector<int64_t, 3> sizes(innerSizes);
4713 llvm::SmallVector<int64_t, 3> strides(3, 0);
4716 return op->emitOpError(
"length_parameter on a non-contiguous pattern "
4717 "requires its second or third dimension to have a "
4718 "size above one: the added length continues it");
4719 int64_t rowElems = sizes[0] * sizes[1];
4720 if (*lengthUnit % rowElems != 0)
4721 return op->emitOpError(
"length_unit (")
4722 << *lengthUnit <<
" elements) must be a multiple of the " << rowElems
4723 <<
" elements moved per step of the dimension the added length "
4729 llvm::MutableArrayRef<int64_t> sizes,
4730 llvm::MutableArrayRef<int64_t> strides) {
4731 if (sizes[2] > 1 || sizes[1] <= 1)
4733 sizes[2] = sizes[1];
4734 strides[2] = strides[1];
4740 Operation *op,
const AIETargetModel &targetModel,
int col,
int row) {
4743 return op->emitOpError(
"length_parameter is only supported on AIE2 and "
4746 return op->emitOpError(
"length_parameter is only supported on shim NOC "
4747 "tiles, got tile (")
4748 << col <<
", " << row <<
")";
4752 return op->emitOpError(
"length_parameter requires a BD whose buffer "
4753 "length fills a 32-bit register");
4758 llvm::SmallVector<uint32_t> ids;
4759 program.getDmaBody().walk([&](DMABDOp bd) {
4760 if (
auto id = bd.getBdId())
4766LogicalResult DMABDOp::verify() {
4767 if (getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) {
4768 uint64_t elemBitWidth = llvm::cast<BaseMemRefType>(getBuffer().getType())
4769 .getElementTypeBitWidth();
4770 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
4771 return emitOpError(
"offset_parameter requires a whole-byte element type");
4774 if (getLengthParameterAttr() || getLengthStateTableIdxAttr()) {
4777 if (llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(
4778 (*this)->getParentOp()))
4779 return emitOpError(
"length_parameter is only supported on a BD in a "
4780 "runtime sequence");
4781 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims =
4782 getFoldedDimensions([&]() {
4783 return emitOpError(
"length_parameter requires a static pattern: ");
4788 if (dims->size() > 4)
4789 return emitOpError(
"length_parameter requires at most 4 dimensions, got ")
4793 llvm::ArrayRef<BDDimLayoutAttr> inner(*dims);
4794 if (inner.size() == 4)
4795 inner = inner.drop_front();
4796 llvm::SmallVector<int64_t, 3> innerSizes(3, 1);
4797 for (
auto [i, dim] : llvm::enumerate(llvm::reverse(inner)))
4798 innerSizes[i] = dim.getSize();
4799 auto buffer = llvm::cast<BaseMemRefType>(getBuffer().getType());
4800 std::optional<int64_t> lenElems;
4801 if (std::optional<int32_t> len = getConstantLen())
4803 else if (!hasLen() && buffer.hasStaticShape())
4804 lenElems = buffer.getNumElements();
4814 Operation *p = (*this)->getParentOp();
4815 if (!llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(*p)) {
4820 bool isUnrankedMemRef = llvm::isa<UnrankedMemRefType>(getBuffer().getType());
4824 if (!isUnrankedMemRef) {
4825 if (!isa<BufferOp, ExternalBufferOp>(getBuffer().getDefiningOp()))
4827 "BDs only support BufferOp or ExternalBufferOp operands.");
4830 if (getLenInBytes() % 4)
4831 return emitOpError(
"transfer length must be multiple of 4 (i.e., represent "
4832 "4 byte aligned address)");
4834 TileElement parentTileElement = getParentTileElement(getOperation());
4835 TileLike parentTile = parentTileElement.getTileLike();
4837 return emitOpError(
"parent tile must implement TileLike interface");
4839 if (!isUnrankedMemRef && getOperation()->getParentOfType<MemOp>() &&
4840 getBufferOp().getTile() != parentTileElement.getTile())
4842 "Core tile DMAs can only access a buffer in the same tile.");
4846 uint32_t maxBds = targetModel.
getNumBDs(parentTile.getTileType());
4847 if (std::optional<int32_t> bdId = getBdId();
4848 bdId.has_value() &&
static_cast<uint32_t
>(*bdId) >= maxBds)
4849 return emitOpError(
"bdId attribute exceeds max: ") << maxBds - 1;
4850 if (std::optional<int32_t> nextBdId = getNextBdId();
4851 nextBdId.has_value() &&
static_cast<uint32_t
>(*nextBdId) >= maxBds)
4852 return emitOpError(
"nextBdId attribute exceeds max: ") << maxBds - 1;
4854 if (getBdIdVal() && getBdId().has_value())
4855 return emitOpError(
"bd_id and bd_id_val are mutually exclusive");
4866 bool lenStaticallyKnown = getConstantLen().has_value();
4867 if (!lenStaticallyKnown && !hasLen()) {
4868 if (
auto shaped = llvm::dyn_cast<mlir::ShapedType>(getBuffer().getType()))
4869 lenStaticallyKnown = shaped.hasStaticShape();
4874 if (lenStaticallyKnown) {
4875 if (
auto constLen = getConstantLen();
4876 constLen.has_value() && *constLen <= 0) {
4879 uint64_t lenInBytes = getLenInBytes();
4881 if (granularity != 0) {
4882 uint64_t lenInWords = lenInBytes * 8 / granularity;
4885 if (lenInWords > maxLen)
4886 return emitOpError()
4887 <<
"buffer descriptor length (" << lenInWords
4888 <<
" 32-bit words) exceeds the maximum of " << maxLen
4889 <<
" words supported by this tile type";
4895 if (failed(verifyMixedSizesAndStrides()) || failed(verifyTransferInBounds()))
4900 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims;
4901 if (!getMixedSizes().empty()) {
4902 dims = getConstantDimensions();
4903 if (!dims.has_value())
4906 if (dims.has_value()) {
4910 size_t maxNDims = targetModel.
getBDMaxDims(parentTile.getTileType());
4911 if (dims->size() > maxNDims)
4912 return emitOpError() <<
"Cannot give more than "
4913 << std::to_string(maxNDims)
4914 <<
" dimensions for step sizes and wraps on this "
4916 << std::to_string(dims->size()) <<
" dimensions).";
4918 auto buffer = llvm::dyn_cast<MemRefType>(getBuffer().getType());
4920 return emitOpError() <<
"dimensions attribute cannot be used with "
4921 "unranked memref buffer type.";
4932 bool skipSizeCheck =
4944 uint32_t wrapBits = targetModel.
getDmaBdWrapBits(parentTile.getTileType());
4945 uint32_t stepBits = targetModel.
getDmaBdStepBits(parentTile.getTileType());
4946 uint64_t maxSize = wrapBits > 0 ? (1ULL << wrapBits) - 1 : 0;
4947 uint64_t maxStride = stepBits > 0 ? (1ULL << stepBits) : 0;
4949 for (BDDimLayoutAttr dim : *dims) {
4950 if (0 == dim.getStride() && dim.getSize() != 1)
4951 return emitOpError()
4952 <<
"Invalid step size; must be a positive integer.";
4953 if (dim.getStride() > buffer.getNumElements())
4954 return emitOpError() <<
"Step size " << std::to_string(dim.getStride())
4955 <<
" exceeds memref size "
4956 << std::to_string(buffer.getNumElements());
4957 if (!skipSizeCheck && dim.getSize() > maxSize)
4958 return emitOpError() <<
"Size may not exceed " << maxSize <<
".";
4959 if (dim.getStride() > maxStride)
4960 return emitOpError() <<
"Stride may not exceed " << maxStride <<
".";
4966 if (getBufferElementTypeWidthInBytes() < 4 && dims->back().getStride() != 1)
4968 "For <32b width datatypes, inner-most dim stride must be 1");
4970 if (getBufferElementTypeWidthInBytes() > 4 && dims->back().getStride() != 1)
4972 "For >32b width datatypes, inner-most dim stride must be 1");
4995 int32_t elementWidthInBytes = getBufferElementTypeWidthInBytes();
4996 if (elementWidthInBytes % 4 != 0) {
4997 std::optional<size_t> effectiveInnermost;
4998 for (
size_t i = 0; i < dims->size(); i++)
4999 if ((*dims)[i].getSize() > 1)
5000 effectiveInnermost = i;
5001 if (effectiveInnermost.has_value()) {
5002 for (
size_t i = 0; i < dims->size(); i++) {
5003 BDDimLayoutAttr dim = (*dims)[i];
5004 if (dim.getSize() <= 1)
5006 if (i == *effectiveInnermost) {
5009 if ((dim.getSize() * elementWidthInBytes) % 4)
5010 return emitOpError()
5011 <<
"Innermost dim size (" << dim.getSize() <<
") * "
5012 << elementWidthInBytes <<
"-byte element width = "
5013 << (dim.getSize() * elementWidthInBytes)
5014 <<
" bytes: innermost dim size must be a multiple of 4 "
5015 "bytes for sub-32b element types (the hardware size "
5016 "register is 32-bit-word granularity).";
5021 if ((dim.getStride() * elementWidthInBytes) % 4)
5022 return emitOpError()
5023 <<
"Dim " << i <<
" stride (" << dim.getStride() <<
") * "
5024 << elementWidthInBytes <<
"-byte element width = "
5025 << (dim.getStride() * elementWidthInBytes)
5026 <<
" bytes: non-innermost dim stride must be a multiple "
5027 "of 4 bytes for sub-32b element types (the hardware "
5028 "stepsize register is 32-bit-word granularity).";
5034 if (
auto paddims = getPadDimensions(); paddims.has_value()) {
5035 if (!dims.has_value())
5036 return emitOpError() <<
"Padding requires n-d data layouts expressed as"
5037 <<
" wrap(s) and stride(s).";
5038 if (!parentTile.isMemTile())
5039 return emitOpError() <<
"Padding is only supported by memtile dma bds.";
5040 if (dims->size() != paddims->size())
5041 return emitOpError() <<
"Mismatch number of dimensions between padding(s)"
5042 <<
" and wrap(s) and stride(s).";
5045 if (paddims->size() == 4)
5046 for (BDPadLayoutAttr pad : paddims->take_front(2))
5047 if (pad.getConstPadBefore() || pad.getConstPadAfter())
5048 return emitOpError()
5049 <<
"a four-dimensional walk pads only its two innermost "
5050 "dimensions; the DMA does not apply d2 or d3 padding";
5052 for (
unsigned i = 0; i < paddims->size(); i++) {
5053 auto dim = (*dims)[i];
5054 auto paddim = (*paddims)[i];
5055 actuallen *= paddim.getConstPadBefore() + paddim.getConstPadAfter() +
5057 if (std::optional<int32_t> len = getConstantLen(); len.has_value()) {
5058 if (actuallen > *len)
5059 return emitOpError() <<
"Data exceeds len after padding.";
5060 }
else if (getLen()) {
5061 return emitOpError()
5062 <<
"Padding with a runtime len operand is not yet supported; "
5063 "use a compile-time constant len with padded BDs.";
5066 if ((paddims->back().getConstPadBefore() *
5067 getBufferElementTypeWidthInBytes()) %
5069 return emitOpError() <<
"Inner-most padding-before count must result in"
5070 <<
" padding in 32-bit words.";
5071 if ((paddims->back().getConstPadAfter() *
5072 getBufferElementTypeWidthInBytes()) %
5074 return emitOpError() <<
"Inner-most padding-after count must result in"
5075 <<
" padding in 32-bit words.";
5077 if (!isUnrankedMemRef &&
5078 (parentTile.isMemTile() || parentTile.isCoreTile())) {
5079 if (
auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
5080 int64_t offsetInBytes = *baseAddr + getOffsetInBytes();
5081 if (offsetInBytes % 4)
5082 return emitOpError(
"bd address must be 4 byte (32b) aligned; got "
5084 << offsetInBytes <<
" (bytes)";
5087 if (
auto packetInfo = getPacket()) {
5090 if (packetInfo->getPktType() > 7)
5091 return emitOpError(
"Packet type field can only hold 3 bits.");
5092 if (packetInfo->assignedId() >
5094 return emitOpError(
"Packet ID field can only hold 5 bits.");
5102 if (!hasLen() && !getBuffer().getType().hasStaticShape())
5103 return emitOpError() <<
"buffer with dynamic shape requires static length.";
5105 if (getBurstLength() != 0 && !parentTile.isShimNOCTile())
5106 return emitOpError(
"Burst length is only supported in Shim NOC tiles that "
5107 "are connected to the memory-mapped NOC.");
5109 if (
auto axcache = getAxcache()) {
5110 if (!parentTile.isShimNOCTile()) {
5111 return emitOpError(
"AxCACHE is only supported in Shim NOC tiles "
5112 "that are connected to the memory-mapped NOC.");
5114 if (axcache > 0xF) {
5115 return emitOpError(
"AxCache value out of 4-bit range.");
5122LogicalResult DMABDPACKETOp::verify() {
5125 if (getPacketType() < 0 || getPacketType() > 7)
5126 return emitOpError(
"Packet type field can only hold 3 bits.");
5127 if (getPacketID() < 0 ||
5129 static_cast<int32_t
>(
getTargetModel(getOperation()).getMaxPacketId()))
5130 return emitOpError(
"Packet ID field can only hold 5 bits.");
5134uint32_t DMABDOp::getAxcacheOrDefault() {
5135 return getAxcache().value_or(
5143static LogicalResult FoldDMAStartOp(DMAStartOp op, PatternRewriter &rewriter) {
5145 llvm::SetVector<Block *> reachable;
5146 SmallVector<Block *, 16> worklist;
5147 Block *firstBD = op.getSuccessor(0);
5148 reachable.insert(firstBD);
5149 worklist.push_back(firstBD);
5150 while (!worklist.empty()) {
5151 Block *block = worklist.pop_back_val();
5154 auto successors = block->getTerminator()->getSuccessors();
5155 for (
auto *i : successors) {
5156 if (!reachable.contains(i)) {
5157 reachable.insert(i);
5158 worklist.push_back(i);
5165 if (isa<EndOp>((reachable.back())->getTerminator()))
5169 auto areEquivalentBDs = [](Block *b1, Block *b2) {
5170 auto b1OpRange = b1->without_terminator();
5171 auto b2OpRange = b2->without_terminator();
5172 if (llvm::range_size(b1OpRange) != llvm::range_size(b2OpRange))
5174 if (!llvm::all_of(llvm::zip_equal(b1OpRange, b2OpRange),
5175 [](std::tuple<Operation &, Operation &> pair) {
5176 return OperationEquivalence::isEquivalentTo(
5177 &std::get<0>(pair), &std::get<1>(pair),
5178 OperationEquivalence::IgnoreLocations);
5185 SmallVector<Block *> uniquePattern;
5186 const auto *patternIt = reachable.begin();
5187 while (patternIt != reachable.end() &&
5188 llvm::none_of(uniquePattern, [patternIt, areEquivalentBDs](Block *b1) {
5189 return areEquivalentBDs(*patternIt, b1);
5191 uniquePattern.push_back(*patternIt);
5196 if (patternIt == reachable.end())
5200 while (patternIt != reachable.end()) {
5202 if (!areEquivalentBDs(*patternIt, uniquePattern[idx]))
5205 idx = (idx + 1) % uniquePattern.size();
5209 auto lastBDTerm = cast<NextBDOp>(reachable.back()->getTerminator());
5210 auto lastUniqueBDTerm = cast<NextBDOp>(uniquePattern.back()->getTerminator());
5211 rewriter.modifyOpInPlace(lastUniqueBDTerm, [&] {
5212 lastUniqueBDTerm.setSuccessor(lastBDTerm.getSuccessor());
5222struct LinearizeContiguousBDTransfer :
public mlir::OpRewritePattern<DMABDOp> {
5223 using OpRewritePattern::OpRewritePattern;
5226 matchAndRewrite(DMABDOp op, mlir::PatternRewriter &rewriter)
const override {
5227 if (op.getStaticSizes().value_or(llvm::ArrayRef<int64_t>{}).empty())
5228 return mlir::failure();
5233 bool bufferIsExternal =
5234 isa_and_nonnull<ExternalBufferOp>(op.getBuffer().getDefiningOp());
5235 TileElement parentElem = getParentTileElement(op.getOperation());
5236 TileLike parentTileLike =
5237 parentElem ? parentElem.getTileLike() : TileLike{};
5238 bool parentIsShim = parentTileLike && parentTileLike.isShimTile();
5239 bool inShimDMA = (bool)op->getParentOfType<ShimDMAOp>();
5240 if (!bufferIsExternal && !parentIsShim && !inShimDMA)
5241 return mlir::failure();
5246 auto isConstant = [](mlir::Value v) {
5247 return mlir::getConstantIntValue(v).has_value();
5249 if (!llvm::all_of(op.getSizes(), isConstant) ||
5250 !llvm::all_of(op.getStrides(), isConstant))
5251 return mlir::failure();
5252 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims =
5253 op.getFoldedDimensions([&]() {
return op.emitError(); });
5254 if (!dims.has_value() || dims->empty())
5255 return mlir::failure();
5257 return mlir::failure();
5259 if (dims->size() == 1 && dims->front().getStride() == 1)
5260 return mlir::failure();
5262 int64_t product = 1;
5263 for (BDDimLayoutAttr dim : *dims)
5264 product *= dim.getSize();
5270 if (!op.hasLen() && !op.getBuffer().getType().hasStaticShape())
5271 return mlir::failure();
5272 std::optional<int32_t> lenVal = op.getConstantLen();
5273 if (lenVal.has_value() &&
static_cast<int64_t
>(*lenVal) != product)
5274 return mlir::failure();
5275 int32_t len =
static_cast<int32_t
>(product);
5279 rewriter.modifyOpInPlace(op, [&]() {
5280 op.getLenMutable().clear();
5281 op.setStaticLen(len);
5282 op.getSizesMutable().clear();
5283 op.getStridesMutable().clear();
5284 op.setStaticSizes(std::nullopt);
5285 op.setStaticStrides(std::nullopt);
5287 return mlir::success();
5295struct FoldConstantBDDimList :
public mlir::OpRewritePattern<DMABDOp> {
5296 using OpRewritePattern::OpRewritePattern;
5299 matchAndRewrite(DMABDOp op, mlir::PatternRewriter &rewriter)
const override {
5300 if (op.getSizes().empty() && op.getStrides().empty() && !op.getOffset() &&
5302 return mlir::failure();
5303 llvm::SmallVector<mlir::OpFoldResult> sizes = op.getMixedSizes();
5304 llvm::SmallVector<mlir::OpFoldResult> strides = op.getMixedStrides();
5310 bool changed = succeeded(mlir::foldDynamicIndexList(sizes)) |
5311 succeeded(mlir::foldDynamicIndexList(strides));
5314 std::optional<int32_t> foldOffset, foldLen;
5316 if (
auto c = mlir::getConstantIntValue(op.getOffset()))
5317 foldOffset =
static_cast<int32_t
>(*c);
5319 if (
auto c = mlir::getConstantIntValue(op.getLen()))
5320 foldLen =
static_cast<int32_t
>(*c);
5321 changed |= foldOffset.has_value() || foldLen.has_value();
5324 return mlir::failure();
5326 llvm::SmallVector<int64_t> staticSizes, staticStrides;
5327 llvm::SmallVector<mlir::Value> dynSizes, dynStrides;
5328 mlir::dispatchIndexOpFoldResults(sizes, dynSizes, staticSizes);
5329 mlir::dispatchIndexOpFoldResults(strides, dynStrides, staticStrides);
5334 bool wasDynamic = !op.getSizes().empty() || !op.getStrides().empty();
5335 if (!op.hasLen() && wasDynamic && dynSizes.empty() && dynStrides.empty() &&
5336 !staticSizes.empty()) {
5338 for (int64_t s : llvm::ArrayRef(staticSizes).take_back(3))
5340 foldLen =
static_cast<int32_t
>(extent);
5343 rewriter.modifyOpInPlace(op, [&]() {
5344 op.getSizesMutable().assign(dynSizes);
5345 op.getStridesMutable().assign(dynStrides);
5347 if (staticSizes.empty())
5348 op.removeStaticSizesAttr();
5350 op.setStaticSizes(staticSizes);
5351 if (staticStrides.empty())
5352 op.removeStaticStridesAttr();
5354 op.setStaticStrides(staticStrides);
5356 op.getOffsetMutable().clear();
5357 op.setStaticOffset(foldOffset);
5360 op.getLenMutable().clear();
5361 op.setStaticLen(foldLen);
5364 return mlir::success();
5369void DMABDOp::getCanonicalizationPatterns(RewritePatternSet &results,
5370 MLIRContext *context) {
5371 results.add<FoldConstantBDDimList, LinearizeContiguousBDTransfer>(context);
5374void DMAStartOp::getCanonicalizationPatterns(RewritePatternSet &results,
5375 MLIRContext *context) {
5376 results.add(FoldDMAStartOp);
5379RouteEndpointOp DMAStartOp::getEndpointOp() {
5380 FlatSymbolRefAttr name = getEndpoint();
5383 return SymbolTable::lookupNearestSymbolFrom<RouteEndpointOp>(*
this, name);
5386LogicalResult DMAStartOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5387 FlatSymbolRefAttr name = getEndpoint();
5390 auto program = dyn_cast<DmaBody>((*this)->getParentOp());
5391 auto device = (*this)->getParentOfType<DeviceOp>();
5392 if (!program || !device)
5393 return emitOpError(
"only a tile's DMA program may name its channel by a "
5395 auto endpoint = symbolTable.lookupSymbolIn<RouteEndpointOp>(device, name);
5397 return emitOpError() << name <<
" is not an aie.route_endpoint";
5398 if (endpoint.getBundle() != WireBundle::DMA)
5399 return emitOpError() << name <<
" names a "
5400 << stringifyWireBundle(endpoint.getBundle())
5401 <<
" port, not a DMA channel";
5402 auto here = cast<TileLike>(program.getTile().getDefiningOp());
5403 TileLike there = endpoint.getTileLike();
5404 bool sameTile = endpoint.getTile() == program.getTile() ||
5405 (here.tryGetCol() && here.tryGetRow() &&
5406 here.tryGetCol() == there.tryGetCol() &&
5407 here.tryGetRow() == there.tryGetRow());
5409 return emitOpError() << name
5410 <<
" is on a different tile than this DMA program";
5414LogicalResult DMAStartOp::verify() {
5415 if (getPadValue() != 0) {
5416 if (!isa<MemTileDMAOp>(getOperation()->getParentOp()))
5417 return emitOpError(
"pad_value is only supported on memtile DMA channels");
5418 if (getChannelDir() != DMAChannelDir::MM2S)
5419 return emitOpError(
"pad_value is only supported on MM2S DMA channels");
5420 if (!
getTargetModel(getOperation()).isMemTilePadValueSupported())
5421 return emitOpError(
"pad_value requires the CONSTANT_PAD_VALUE register, "
5422 "unavailable on this target");
5424 if (failed(verifyDMARepeatCount(getOperation(), getRepeatCount())))
5426 SmallVector<DMABDOp> bds;
5427 if (getOutOfOrder()) {
5428 llvm::SmallPtrSet<Block *, 8> visited;
5429 for (Block *b = getDest(); b && visited.insert(b).second;
5430 b = b->getNumSuccessors() > 0 ? b->getSuccessor(0) :
nullptr)
5431 llvm::append_range(bds, b->getOps<DMABDOp>());
5434 getOutOfOrder(), bds);
5441LogicalResult SwitchboxOp::verify() {
5447 if (!isa<TileOp>(getTile().getDefiningOp()))
5450 Region &body = getConnections();
5451 DenseSet<Port> sourceset;
5452 DenseSet<Port> destset;
5453 auto tile = getTileOp();
5456 return emitOpError(
"should have non-empty body");
5458 for (
auto connectOp : body.front().getOps<ConnectOp>())
5459 sourceset.insert({connectOp.getSourceBundle(), connectOp.sourceIndex()});
5460 for (
auto &ops : body.front()) {
5462 auto checkBound = [&ops](StringRef dir, WireBundle bundle,
int index,
5463 int bound) -> LogicalResult {
5464 if (index >= bound) {
5466 return ops.emitOpError(
"index ")
5467 << index <<
" for " << dir <<
" bundle "
5468 << stringifyWireBundle(bundle) <<
" must be less than "
5470 return ops.emitOpError()
5471 << dir <<
" bundle " << stringifyWireBundle(bundle)
5472 <<
" not supported; index: " << index <<
", bound: " << bound;
5477 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
5478 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
5480 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
5481 if (destset.count(dest)) {
5482 return connectOp.emitOpError()
5483 <<
"; connecting " << to_string(source) <<
" to "
5484 << to_string(dest) <<
" on "
5485 << to_string(this->getTileOp().getTileID())
5486 <<
" targets same dst as another connect op; existing "
5488 << llvm::join(llvm::map_range(
5489 destset, [](
auto &p) {
return to_string(p); }),
5492 destset.insert(dest);
5494 if (connectOp.sourceIndex() < 0)
5495 return connectOp.emitOpError(
"source index cannot be less than zero");
5497 if (checkBound(
"source", connectOp.getSourceBundle(),
5498 connectOp.sourceIndex(),
5499 getNumSourceConnections(connectOp.getSourceBundle()))
5503 if (connectOp.destIndex() < 0)
5504 return connectOp.emitOpError(
"dest index cannot be less than zero");
5506 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
5507 getNumDestConnections(connectOp.getDestBundle()))
5512 if (!isLegalTileConnection(tile, targetModel, connectOp))
5513 return connectOp.emitOpError(
"illegal stream switch connection");
5515 }
else if (
auto connectOp = dyn_cast<MasterSetOp>(ops)) {
5516 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
5517 if (destset.count(dest))
5518 return connectOp.emitOpError(
"targets same destination ")
5519 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
5520 <<
" as another connect or masterset operation";
5521 destset.insert(dest);
5523 if (connectOp.destIndex() < 0)
5524 return connectOp.emitOpError(
"dest index cannot be less than zero");
5526 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
5527 getNumDestConnections(connectOp.getDestBundle()))
5532 }
else if (
auto connectOp = dyn_cast<PacketRulesOp>(ops)) {
5533 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
5534 if (sourceset.count(source))
5535 return connectOp.emitOpError(
"packet switched source ")
5536 << stringifyWireBundle(source.bundle) << source.channel
5537 <<
" cannot match another connect or masterset operation";
5538 sourceset.insert(source);
5540 }
else if (
auto amselOp = dyn_cast<AMSelOp>(ops)) {
5541 std::vector<MasterSetOp> mstrs;
5542 std::vector<PacketRulesOp> slvs;
5543 for (
auto *user : amselOp.getResult().getUsers()) {
5544 if (
auto s = dyn_cast<PacketRuleOp>(user)) {
5545 auto pktRules = cast<PacketRulesOp>(s->getParentOp());
5546 slvs.push_back(pktRules);
5547 }
else if (
auto m = dyn_cast<MasterSetOp>(user))
5550 for (
auto m : mstrs) {
5551 for (
auto s : slvs) {
5553 if (!isLegalTileConnection(tile, targetModel, m, s)) {
5554 return amselOp->emitOpError(
"illegal stream switch connection");
5558 }
else if (isa<EndOp>(ops)) {
5561 return ops.emitOpError(
"cannot be contained in a Switchbox op");
5568template <
typename... ParentOpTypes>
5571 Operation *operation = op->getParentOp();
5573 if (llvm::isa_and_nonnull<ParentOpTypes...>(operation))
5575 operation = operation->getParentOp();
5581TileOp LockOp::getTileOp() {
5582 return cast<TileElement>(this->getOperation()).getTileOp();
5585LogicalResult LockOp::verify() {
5586 if (
auto result = UsesAreAccessible::verifyTrait(*
this); result.failed())
5589 if (
auto lockID = getLockID()) {
5590 TileLike tileLike = getTileLike();
5592 return emitOpError(
"tile operand must implement TileLike interface");
5594 auto tileType = tileLike.getTileType();
5595 if (
int numLocks = targetModel.
getNumLocks(tileType); *lockID >= numLocks)
5596 return emitOpError(
"lock assigned invalid id (maximum is ")
5597 << numLocks - 1 <<
")";
5605static std::optional<int32_t> getConstantLockValue(UseLockOp op) {
5606 if (
auto constant = op.getValue().getDefiningOp<arith::ConstantOp>())
5607 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(constant.getValue()))
5608 return (int32_t)intAttr.getInt();
5609 return std::nullopt;
5614 auto *block = op->getBlock();
5616 for (
auto op : block->getOps<UseLockOp>()) {
5617 if (
auto lock = dyn_cast<LockOp>(op.getLock().getDefiningOp());
5618 lock.getLockID().has_value()) {
5619 if (lockID != -1 && lockID != lock.getLockIDValue())
5621 lockID = lock.getLockIDValue();
5630 auto *block = op->getBlock();
5631 int acqValue = -1, relValue = -1;
5632 for (
auto op : block->getOps<UseLockOp>()) {
5635 auto value = getConstantLockValue(op);
5638 if (op.acquire() || op.acquireGE()) {
5639 if (acqValue != -1 && acqValue != *value) {
5643 }
else if (op.release()) {
5644 if (relValue != -1 && relValue != *value) {
5656 if (
auto memOp = op->getParentOfType<MemOp>()) {
5657 auto useLock = cast<UseLockOp>(op);
5658 if (
auto lock = useLock.getLockOp(); lock.getTile() != memOp.getTile())
5665LogicalResult UseLockOp::verify() {
5667 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>((*this)->getParentOp()))
5668 return (*this)->emitOpError(
"must be used in a core or memory operation.");
5671 if (targetModel.
getTargetArch() == AIEArch::AIE1 && acquireGE())
5672 return (*this)->emitOpError(
5673 "AcquireGreaterEqual is not supported in AIE1.");
5679 !getConstantLockValue(*
this))
5680 return (*this)->emitOpError(
5681 "lock value in a DMA/BD block must be a compile-time constant "
5682 "(defined by an arith.constant).");
5688 if (!(*this)->getBlock())
5689 return (*this)->emitOpError(
"is not in a block.");
5693 return (*this)->emitOpError(
5694 "used in a DMA block that have multiple locks.");
5697 return (*this)->emitOpError(
"acquires/releases the lock in a DMA block "
5698 "from/to multiple states.");
5702 return (*this)->emitOpError(
"can only access a lock in the same tile");
5714 Operation *operation = (*this)->getParentOp();
5716 StringRef name = operation->getName().getStringRef();
5717 if (name ==
"aiex.dma_configure_task" ||
5718 name ==
"aiex.dma_configure_task_for")
5720 operation = operation->getParentOp();
5723 return (*this)->emitOpError()
5724 <<
"expects some parent op to be one of "
5725 <<
"AIE::device, AIE::core, func::func, AIE::mem, AIE::shimDMA, or "
5726 "AIEX::dma_configure_task";
5729#include "aie/Dialect/AIE/IR/AIEEnums.cpp.inc"
5730#include "aie/Dialect/AIE/IR/AIEInterfaces.cpp.inc"
5737static ParseResult parseTraceEventValue(AsmParser &parser, Attribute &value) {
5742static void printTraceEventValue(AsmPrinter &printer, Attribute value) {
5747static ParseResult parseTraceRegValue(OpAsmParser &parser, Attribute &value) {
5751 OptionalParseResult parseResult = parser.parseOptionalInteger(intValue);
5752 if (parseResult.has_value() && succeeded(parseResult.value())) {
5753 MLIRContext *ctx = parser.getContext();
5754 value = IntegerAttr::get(IntegerType::get(ctx, 32), intValue);
5761static void printTraceRegValue(OpAsmPrinter &printer, Operation *op,
5764 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(value)) {
5765 printer << intAttr.getInt();
5772#define GET_OP_CLASSES
5773#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
5775FailureOr<int32_t> UseLockOp::getConstantValue() {
5776 if (
auto value = getConstantLockValue(*
this))
5778 return emitOpError(
"expected the lock value to be a compile-time constant "
5779 "(defined by an arith.constant).");
5782LogicalResult UseLockOp::canonicalize(UseLockOp op, PatternRewriter &rewriter) {
5786 if (op.acquireGE() && op->getBlock()->getOps<DMABDOp>().empty()) {
5787 if (
auto value = getConstantLockValue(op); value && *value == 0) {
5788 rewriter.eraseOp(op);
5795TileOp SwitchboxOp::getTileOp() {
5796 return cast<TileElement>(this->getOperation()).getTileOp();
5799size_t SwitchboxOp::getNumSourceConnections(WireBundle bundle) {
5800 auto tile = getTileOp();
5803 tile.getRow(), bundle);
5806size_t SwitchboxOp::getNumDestConnections(WireBundle bundle) {
5807 auto tile = getTileOp();
5810 tile.getRow(), bundle);
5813TileOp ShimMuxOp::getTileOp() {
5814 return cast<TileElement>(this->getOperation()).getTileOp();
5819 case WireBundle::North:
5820 return WireBundle::South;
5821 case WireBundle::South:
5822 return WireBundle::North;
5823 case WireBundle::East:
5824 return WireBundle::West;
5825 case WireBundle::West:
5826 return WireBundle::East;
5832std::optional<std::pair<TileID, Port>>
5835 switch (port.bundle) {
5836 case WireBundle::North:
5839 case WireBundle::South:
5842 case WireBundle::East:
5845 case WireBundle::West:
5849 return std::nullopt;
5852 return std::nullopt;
5860ParseResult BDChainOp::parse(OpAsmParser &parser, OperationState &result) {
5861 SmallVector<OpAsmParser::Argument> entryArgs;
5864 StringAttr symNameAttr;
5865 if (parser.parseSymbolName(symNameAttr, SymbolTable::getSymbolAttrName(),
5866 result.attributes)) {
5871 ParseResult argParseResult = parser.parseCommaSeparatedList(
5872 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
5873 OpAsmParser::Argument argument;
5874 if (parser.parseArgument(argument,
true,
true)) {
5877 entryArgs.push_back(argument);
5880 if (argParseResult) {
5881 return argParseResult;
5885 auto *body = result.addRegion();
5886 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
5887 if (bodyParseResult) {
5888 return bodyParseResult;
5894void BDChainOp::print(OpAsmPrinter &printer) {
5897 ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
5900 printer.printSymbolName(taskName);
5902 Region &body = getRegion();
5903 auto argsIter = body.getArguments();
5905 for (
auto *it = argsIter.begin(); it != argsIter.end(); ++it) {
5906 if (it != argsIter.begin()) {
5909 printer.printRegionArgument(*it);
5914 printer.printRegion(body,
false,
true);
5921LogicalResult ShimDMAAllocationOp::verify() {
5922 TileLike tileLike = llvm::dyn_cast<TileLike>(getTile().getDefiningOp());
5924 return emitOpError(
"tile operand must implement TileLike interface");
5927 if (!tileLike.isShimNOCorPLTile()) {
5929 auto col = tileLike.tryGetCol();
5930 auto row = tileLike.tryGetRow();
5932 return emitOpError(
"tile must be a shim tile, but got tile(")
5933 << *col <<
", " << *row <<
")";
5935 return emitOpError(
"tile must be a shim tile");
5941TileOp ShimDMAAllocationOp::getTileOp() {
5942 return cast<TileOp>(getTile().getDefiningOp());
5945ShimDMAAllocationOp ShimDMAAllocationOp::getForSymbol(DeviceOp device,
5946 llvm::StringRef symbol) {
5947 Operation *maybeOp = device.lookupSymbol(symbol);
5949 if (ShimDMAAllocationOp op = dyn_cast<ShimDMAAllocationOp>(maybeOp)) {
5960LogicalResult ObjectFifoRearmBindingOp::verify() {
5961 if (getChannelDirs().size() != getChannelTiles().size())
5962 return emitOpError(
"expected one channel_dirs entry per channel tile (")
5963 << getChannelTiles().size() <<
" tiles, " << getChannelDirs().size()
5965 if (getChannelIndices().size() != getChannelTiles().size())
5966 return emitOpError(
"expected one channel_indices entry per channel tile (")
5967 << getChannelTiles().size() <<
" tiles, "
5968 << getChannelIndices().size() <<
" indices)";
5969 if (getLockInits().size() != getLocks().size())
5970 return emitOpError(
"expected one lock_inits entry per lock (")
5971 << getLocks().size() <<
" locks, " << getLockInits().size()
5977 if (getHeadBdIds().has_value() != getRepeatCounts().has_value())
5978 return emitOpError(
"head_bd_ids and repeat_counts must both be set or both "
5980 if (
auto headBdIds = getHeadBdIds();
5981 headBdIds.has_value() && headBdIds->size() != getChannelTiles().size())
5982 return emitOpError(
"expected one head_bd_ids entry per channel tile (")
5983 << getChannelTiles().size() <<
" tiles, " << headBdIds->size()
5985 if (
auto repeatCounts = getRepeatCounts();
5986 repeatCounts.has_value() &&
5987 repeatCounts->size() != getChannelTiles().size())
5988 return emitOpError(
"expected one repeat_counts entry per channel tile (")
5989 << getChannelTiles().size() <<
" tiles, " << repeatCounts->size()
5991 for (int32_t dir : getChannelDirs())
5992 if (dir != 0 && dir != 1)
5993 return emitOpError(
"channel_dirs entries must be 0 (S2MM) or 1 (MM2S), "
5999 for (Value tile : getChannelTiles()) {
6000 auto tileOp = tile.getDefiningOp<TileOp>();
6002 return emitOpError(
"channel_tiles operands must be aie.tile values");
6007 if (tileOp.isShimTile())
6008 return emitOpError(
"channel_tiles must be non-shim tiles; a shim DMA "
6009 "endpoint is host-managed and is not re-armed here");
6011 for (Value lock : getLocks())
6012 if (!lock.getDefiningOp<LockOp>())
6013 return emitOpError(
"locks operands must be aie.lock values");
6021ParseResult RuntimeSequenceOp::parse(OpAsmParser &parser,
6022 OperationState &result) {
6025 StringAttr nameAttr;
6026 (void)parser.parseOptionalSymbolName(
6027 nameAttr, mlir::SymbolTable::getSymbolAttrName(), result.attributes);
6029 SmallVector<OpAsmParser::Argument> entryArgs;
6032 ParseResult argParseResult = parser.parseCommaSeparatedList(
6033 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
6034 OpAsmParser::Argument argument;
6035 if (parser.parseArgument(argument,
true,
true)) {
6038 entryArgs.push_back(argument);
6041 if (argParseResult) {
6042 return argParseResult;
6047 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
6051 auto *body = result.addRegion();
6052 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
6053 if (bodyParseResult) {
6054 return bodyParseResult;
6060void RuntimeSequenceOp::print(OpAsmPrinter &printer) {
6061 Region &body = getRegion();
6063 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
6064 mlir::SymbolTable::getSymbolAttrName());
6066 nameAttr != ::mlir::OpBuilder((*this)->getContext())
6067 .getStringAttr(getDefaultRuntimeSequenceName())) {
6069 printer.printSymbolName(nameAttr);
6073 for (
unsigned i = 0, n = body.getNumArguments(); i < n; i++) {
6077 printer.printRegionArgument(body.getArgument(i));
6081 printer.printOptionalAttrDictWithKeyword(
6082 (*this)->getAttrs(),
6083 {mlir::SymbolTable::getSymbolAttrName()});
6086 printer.printRegion(body,
false,
true);
6089LogicalResult RuntimeSequenceOp::verify() {
6090 DeviceOp device = (*this)->getParentOfType<DeviceOp>();
6093 (*this)->emitOpError() <<
"must be inside AIE device operation.";
6100RuntimeSequenceOp::getForSymbolInDevice(DeviceOp deviceOp,
6101 llvm::StringRef symbol) {
6102 RuntimeSequenceOp runtimeSequenceOp;
6103 if (symbol.empty()) {
6104 auto range = deviceOp.getOps<RuntimeSequenceOp>();
6105 if (range.begin() == range.end()) {
6110 runtimeSequenceOp = *range.begin();
6112 Operation *maybeRuntimeSequenceOp =
6113 mlir::SymbolTable::lookupSymbolIn(deviceOp, symbol);
6114 if (!maybeRuntimeSequenceOp) {
6118 llvm::dyn_cast<RuntimeSequenceOp>(maybeRuntimeSequenceOp);
6120 return runtimeSequenceOp;
6124RuntimeSequenceOp::getForSymbolInDeviceOrError(DeviceOp deviceOp,
6125 llvm::StringRef symbol) {
6126 RuntimeSequenceOp runtimeSequenceOp = getForSymbolInDevice(deviceOp, symbol);
6127 if (!runtimeSequenceOp) {
6128 if (!symbol.empty()) {
6129 deviceOp.emitError(
"No such runtime sequence: ") << symbol;
6131 deviceOp.emitError(
"No runtime sequence in device");
6134 return runtimeSequenceOp;
6137LogicalResult RuntimeSequenceOp::verifyBeforeMaterialization() {
6141 auto result = (*this)->walk([&](Operation *op) {
6142 for (NamedAttribute namedAttr : op->getAttrs()) {
6143 Attribute attr = namedAttr.getValue();
6144 auto walkResult = attr.walk([&](SymbolRefAttr symbolRef) {
6145 Operation *symbolDefOp =
6146 SymbolTable::lookupNearestSymbolFrom(*
this, symbolRef);
6148 if (
auto flat = dyn_cast<FlatSymbolRefAttr>(symbolRef)) {
6153 if (!llvm::isa<ShimDMAAllocationOp>(symbolDefOp) &&
6154 !llvm::isa<DeviceOp>(symbolDefOp) &&
6155 !llvm::isa<RuntimeSequenceOp>(symbolDefOp) &&
6156 !llvm::isa<BufferOp>(symbolDefOp) &&
6157 !llvm::isa<ObjectFifoRearmBindingOp>(symbolDefOp) &&
6158 !llvm::isa<memref::GlobalOp>(symbolDefOp)) {
6160 <<
"references symbol '"
6161 << symbolRef.getRootReference().getValue()
6162 <<
"' which must be either a ShimDMAAllocationOp, DeviceOp, "
6163 "RuntimeSequenceOp, BufferOp, ObjectFifoRearmBindingOp, or "
6164 "GlobalOp, but got: "
6165 << symbolDefOp->getName().getStringRef();
6166 return WalkResult::interrupt();
6168 if (BufferOp bufferOp = llvm::dyn_cast<BufferOp>(symbolDefOp)) {
6169 if (!bufferOp.getAddress()) {
6171 <<
"Unallocated buffer; fixed addresses are required before "
6172 "runtime sequence materialization.";
6173 return WalkResult::interrupt();
6177 return WalkResult::advance();
6179 if (walkResult.wasInterrupted()) {
6180 return WalkResult::interrupt();
6183 return WalkResult::advance();
6186 if (result.wasInterrupted()) {
6194#define GET_ATTRDEF_CLASSES
6195#include "aie/Dialect/AIE/IR/AIEAttrs.cpp.inc"
virtual uint64_t getDmaBdMaxLen(AIETileType tileType) const =0
Return the maximum transfer length, measured in address-generation granules (see getAddressGenGranula...
virtual const DmaBdLayout * getDmaBdLayout(AIETileType tileType) const
Return the register layout of a buffer descriptor on the given tile type, or null if the target has n...
virtual AIEArch getTargetArch() const =0
Return the target architecture.
virtual uint32_t getNumSourceShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a shimmux.
virtual uint32_t getDmaBdWrapBits(AIETileType tileType) const =0
Return the bit width of the wrap (size) field in a DMA buffer descriptor for the given tile type.
virtual uint32_t getLocalMemorySize() const =0
Return the size (in bytes) of the local data memory of a core.
virtual uint32_t getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a given tile type.
virtual uint32_t getDmaBdIterBits(AIETileType tileType) const =0
Return the bit width of the iteration (repeat) wrap/stride fields in a DMA buffer descriptor for the ...
virtual uint32_t getBDMaxDims(AIETileType tileType) const =0
Return the maximum number of addressing dimensions (wraps/strides) a single buffer descriptor support...
virtual std::vector< std::pair< uint32_t, uint32_t > > getShimBurstEncodingsAndLengths() const =0
virtual bool isLegalTileConnection(int col, int row, WireBundle srcBundle, int srcChan, WireBundle dstBundle, int dstChan) const =0
virtual uint32_t getNumBanks(int col, int row) const =0
Return the number of memory banks of a given tile.
virtual uint32_t getDmaBdStepBits(AIETileType tileType) const =0
Return the bit width of the step (stride) field in a DMA buffer descriptor for the given tile type.
uint32_t getCoreStackAlignment() const
Stack-pointer alignment in bytes, matching Peano's AIE frame lowering: AIE1/AIE2 require 32B; AIE2P/A...
virtual bool isValidTile(TileID src) const
Return true if the given tile ID is valid.
virtual uint32_t getMaxRepeatCount() const =0
Return the largest DMA task repeat count (unsupported = 0).
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
virtual uint32_t getMaxLockValue() const =0
Return the maximum value that can be stored in a lock register.
virtual uint32_t getMaxOutOfOrderId() const =0
Return the largest out-of-order BD id (unsupported = 0).
bool hasProperty(ModelProperty Prop) const
virtual uint32_t getDefaultCoreStackSize() const
Return the default stack reservation (in bytes) for a core that does not state one.
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.
SharedMemory
Which of two tiles' memory modules both of them can address.
SharedMemory getSharedMemory(TileID a, TileID b) const
Return the memory module a and b can both reach, if any.
virtual uint32_t getAddressGenGranularity() const =0
Return the data bus width of the device.
virtual uint32_t getMemTileSize() const =0
Return the size (in bytes) of a MemTile.
virtual uint32_t getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
NamedOpTable(mlir::Operation *symbolTableOp)
std::string uniqueName(llvm::StringRef prefix, unsigned &counter) const
bool isContiguousBDTransfer(llvm::ArrayRef< BDDimLayoutAttr > dims)
mlir::ParseResult parseFlowVias(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &vias, mlir::DenseI32ArrayAttr &ingressBundles, mlir::DenseI32ArrayAttr &ingressChannels, mlir::DenseI32ArrayAttr &egressBundles, mlir::DenseI32ArrayAttr &egressChannels)
void printObjectFifoProducerTile(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::Value operand, BDDimLayoutArrayAttr dimensions)
void printTypedDynamicIndexList(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange values, llvm::ArrayRef< int64_t > integers, mlir::TypeRange types)
llvm::SmallVector< ObjectFifoDmaEndpointOp > turnEndpoints(ObjectFifoDmaEndpointOp end)
std::optional< llvm::SmallVector< BDDimLayoutAttr > > normalizedBDDims(llvm::ArrayRef< BDDimLayoutAttr > dims, int64_t len)
mlir::LogicalResult verifyLengthParameterTile(mlir::Operation *op, const AIETargetModel &targetModel, int col, int row)
uint32_t getShimBurstLengthBytes(const AIE::AIETargetModel &tm, uint32_t burstLength)
void collectTiles(DeviceOp &device, llvm::DenseMap< TileID, mlir::Operation * > &tiles)
void collectBuffers(DeviceOp &device, llvm::DenseMap< mlir::Operation *, llvm::SmallVector< BufferOp, 4 > > &buffers)
mlir::ParseResult parseTraceEvent(mlir::AsmParser &parser, mlir::Attribute &result)
void printObjectFifoAcquireObjects(mlir::OpAsmPrinter &printer, mlir::Operation *op, ObjectFifoPortAttr port, mlir::TypeRange objects)
mlir::LogicalResult verifyBdLockPair(mlir::Block &block, bool outOfOrder, UseLockOp &acquire, UseLockOp &release)
mlir::LogicalResult verifyAssignedPacket(mlir::Operation *op, PacketInfoAttr packet, llvm::StringRef what="packet")
mlir::ParseResult parseTypedDynamicIndexList(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &values, mlir::DenseI64ArrayAttr &integers, llvm::SmallVectorImpl< mlir::Type > &types)
void printObjectFifoConsumerTiles(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange tiles, BDDimLayoutArrayArrayAttr dimsPerTileAttr)
mlir::Operation * lookupNamedOpIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
uint32_t getShimBurstLengthEncoding(const AIE::AIETargetModel &tm, uint32_t burstLength)
mlir::LogicalResult verifyDMABDOutOfOrderId(DMABDOp bd, bool packetEnabledByContext=false)
mlir::ParseResult parseObjectFifoAcquireObjects(mlir::OpAsmParser &parser, ObjectFifoPortAttr &port, llvm::SmallVectorImpl< mlir::Type > &objects)
mlir::LogicalResult verifyOutOfOrderChannel(mlir::Operation *op, DMAChannelDir dir, bool outOfOrder, llvm::ArrayRef< DMABDOp > bds, bool packetEnabledByContext=false)
struct Connect { Port src; Port dst; bool operator==(const Connect &rhs) const { return std::tie(src, dst)==std::tie(rhs.src, rhs.dst); } bool operator!=(const Connect &rhs) const { return !(*this==rhs);} bool operator<(const Connect &rhs) const { return std::tie(src, dst)< std::tie(rhs.src, rhs.dst); } } Connect
void printDMAStartChannel(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::Attribute channel)
mlir::LogicalResult verifyLengthParameter(mlir::Operation *op, std::optional< int64_t > lengthUnit, mlir::BaseMemRefType buffer, std::optional< int64_t > lenElems, bool contiguous, llvm::ArrayRef< int64_t > innerSizes)
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
int32_t getBufferBaseAddress(mlir::Operation *bufOp)
mlir::ParseResult parseDMAStartChannel(mlir::OpAsmParser &parser, mlir::Attribute &channel)
llvm::SmallVector< uint32_t > getAssignedBdIds(DmaBody program)
llvm::SmallVector< StreamWindowStep > streamWindowSteps(const StreamWindow &w)
int64_t turnPasses(llvm::ArrayRef< std::pair< int64_t, int64_t > > ends)
const AIETargetModel & getTargetModel(mlir::Operation *op)
bool keepsPktHeader(TileID tile, Port master, std::optional< bool > keep)
struct DMAChannel { DMAChannelDir direction; int channel; bool operator==(const DMAChannel &rhs) const { return std::tie(direction, channel)==std::tie(rhs.direction, rhs.channel); } } DMAChannel
mlir::LogicalResult verifyDMAChannelsResolved(DeviceOp device)
mlir::Operation * lookupNamedOp(mlir::Operation *from, mlir::StringAttr name)
mlir::ParseResult parseObjectFifoProducerTile(mlir::OpAsmParser &parser, mlir::OpAsmParser::UnresolvedOperand &operand, BDDimLayoutArrayAttr &dimensions)
void printTraceEventEnum(mlir::AsmPrinter &printer, mlir::Attribute attr)
void placeRuntimeLengthDimension(llvm::MutableArrayRef< int64_t > sizes, llvm::MutableArrayRef< int64_t > strides)
mlir::LogicalResult myVerifyOffsetSizeAndStrideOp(mlir::OffsetSizeAndStrideOpInterface op)
mlir::LogicalResult inferStreamWindow(llvm::ArrayRef< BDDimLayoutAttr > dims, llvm::ArrayRef< BDPadLayoutAttr > pads, int64_t objectSize, llvm::function_ref< mlir::InFlightDiagnostic()> emitError, StreamWindow &window)
mlir::LogicalResult verifyDMABDIteration(DMABDOp bd, AIETileType tileType)
void printFlowVias(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange vias, mlir::DenseI32ArrayAttr ingressBundles, mlir::DenseI32ArrayAttr ingressChannels, mlir::DenseI32ArrayAttr egressBundles, mlir::DenseI32ArrayAttr egressChannels)
struct Port { WireBundle bundle; int channel; bool operator==(const Port &rhs) const { return std::tie(bundle, channel)==std::tie(rhs.bundle, rhs.channel); } bool operator!=(const Port &rhs) const { return !(*this==rhs);} bool operator<(const Port &rhs) const { return std::tie(bundle, channel)< std::tie(rhs.bundle, rhs.channel); } friend std::ostream &operator<<(std::ostream &os, const Port &port) { os<< "("; os<< stringifyWireBundle(port.bundle).str(); os<< ": "<< std::to_string(port.channel)<< ")"; return os; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Port &port) { os<< to_string(port); return os; } } Port
mlir::ParseResult parseObjectFifoReleaseCount(mlir::OpAsmParser &parser, ObjectFifoPortAttr &port, mlir::IntegerAttr &size)
AIETargetModel::SharedMemory sharedMemory(TileOp a, TileOp b)
Which of a's and b's memory modules both tiles can address.
std::optional< std::pair< TileID, Port > > getConnectingWireEndpoint(const AIETargetModel &targetModel, TileID tile, Port port)
mlir::FailureOr< int64_t > getLengthUnitBytes(mlir::Operation *op, int64_t lengthUnit, mlir::BaseMemRefType buffer)
int64_t paddedSize(llvm::ArrayRef< BDDimLayoutAttr > dims, llvm::ArrayRef< BDPadLayoutAttr > pads, int64_t fallback)
mlir::ParseResult parseObjectFifoConsumerTiles(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &tiles, BDDimLayoutArrayArrayAttr &dimensions)
void printObjectFifoReleaseCount(mlir::OpAsmPrinter &printer, mlir::Operation *op, ObjectFifoPortAttr port, mlir::IntegerAttr size)
WireBundle getConnectingBundle(WireBundle dir)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
The register layout of one tile type's DMA buffer descriptor.
static mlir::LogicalResult verifyTrait(mlir::Operation *op)
static mlir::LogicalResult verifyTrait(mlir::Operation *op)
llvm::SmallVector< BDDimLayoutAttr > inner