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,
bool out_of_order) {
625 if (packet && mode == ObjectFifoTransportMode::SharedMem)
626 return emitError() <<
"`packet` belongs to a dma or auto transport, not "
627 << stringifyObjectFifoTransportMode(mode);
628 if (out_of_order && !packet)
629 return emitError() <<
"`out_of_order` requires a packet transport";
638ObjectFifoEndPortAttr::verify(function_ref<InFlightDiagnostic()> emitError,
640 std::optional<uint32_t> channel) {
641 if (bundle != WireBundle::DMA && bundle != WireBundle::Core)
642 return emitError() <<
"an objectFifo end is driven by a DMA channel or a "
643 "Core stream port, not "
644 << stringifyWireBundle(bundle);
645 if (bundle == WireBundle::Core) {
647 return emitError() <<
"a Core end needs its stream port";
649 return emitError() <<
"a Core end's stream port is 0 or 1, not "
659LogicalResult ObjectFifoCreateOp::verify() {
660 for (Type fifoType : {Type(getElemType()), getConsumerElemTypeOrDefault()})
661 if (MemRefType memref = cast<AIEObjectFifoType>(fifoType).getElementType();
662 !memref.hasStaticShape())
663 return emitOpError(
"type should be static shaped memref, but got ")
669 static constexpr std::pair<llvm::StringLiteral, llvm::StringLiteral>
671 {
"out_of_order",
"transport = #aie.transport<dma, packet = "
672 "#aie.packet_info<>, out_of_order = true>"},
673 {
"via_DMA",
"transport = #aie.transport<dma>"},
674 {
"packet",
"transport = #aie.transport<dma, packet = "
675 "#aie.packet_info<>>"},
676 {
"packet_id",
"transport = #aie.transport<dma, packet = "
677 "#aie.packet_info<pkt_id = 3>>"},
678 {
"aie_stream",
"prod_port = #aie.end_port<Core : 0> or cons_ports "
679 "= [#aie.end_port<Core : 0>]"},
680 {
"aie_stream_port",
"prod_port = #aie.end_port<Core : 0> or "
681 "cons_ports = [#aie.end_port<Core : 0>]"},
682 {
"prod_dma_channel",
"prod_port = #aie.end_port<DMA : 1>"},
683 {
"cons_dma_channels",
"cons_ports = [#aie.end_port<DMA : 1>]"}};
684 for (
auto [gone, replacement] : replaced)
685 if ((*this)->hasAttr(gone))
686 return emitOpError(
"`")
687 << gone <<
"` has been replaced; write " << replacement;
689 if (isa<ArrayAttr>(getElemNumber())) {
690 if (
size_t numDepths = dyn_cast<ArrayAttr>(getElemNumber()).size();
691 numDepths != getConsumerTiles().size() + 1)
692 return emitOpError(
"does not have enough depths specified for producer "
693 "and for each consumer.");
696 if (ArrayAttr consPorts = getConsPortsAttr();
697 consPorts && consPorts.size() != getConsumerTiles().size())
698 return emitOpError(
"`cons_ports` has ")
699 << consPorts.size() <<
" entries for " << getConsumerTiles().size()
703 auto getTileLikeFromValue = [](Value v) -> TileLike {
704 return llvm::dyn_cast<TileLike>(v.getDefiningOp());
707 TileLike producerTile = getTileLikeFromValue(getProducerTile());
709 return emitError(
"producer tile must implement TileLike interface");
712 if (producerTile.isShimTile() && !getDimensionsToStream().empty()) {
714 "`dimensionsToStream` data layout transformations are not supported "
715 "on shim tile producers");
717 for (
auto consTileVal : getConsumerTiles()) {
718 TileLike consTile = getTileLikeFromValue(consTileVal);
720 return emitError(
"consumer tile must implement TileLike interface");
721 if (consTile.isShimTile() &&
722 !getDimensionsFromStream(consTileVal).empty()) {
724 "`dimensionsFromStreamPerConsumer` data layout transformations are "
725 "not supported on shim tile consumers");
729 if (getRepeatCount().has_value()) {
730 if (producerTile.isShimTile())
731 return emitError(
"`repeat_count` unavailable for shim tiles");
734 if (getPadValue() != 0) {
735 if (!getPadDimensions().has_value())
736 return emitError(
"`padValue` requires `padDimensions`");
738 return emitError(
"`padValue` requires the CONSTANT_PAD_VALUE register, "
739 "unavailable on this target");
743 if (getConsumerTiles().size() > 1)
744 return emitError(
"a Core stream port end can only be used in 1-to-1 "
747 if (forcesSharedMemory())
748 return emitError(
"a Core stream port end sends over the stream, not "
749 "through shared memory");
751 if (streamsFromProducer()) {
752 if (producerTile.isShimTile() || producerTile.isMemTile())
754 "a Core stream port end is not available for shim and mem tiles");
756 if (getRepeatCount().has_value())
757 return emitError(
"`repeat_count` unavailable on stream end");
759 if (getInitValues().has_value())
760 return emitError(
"`init_values` unavailable on stream end");
762 if (getIterCount().has_value())
763 return emitError(
"`iter_count` unavailable on stream end");
765 if (!getDimensionsToStream().empty())
766 return emitError(
"`dimensionsToStream` data layout transformations are "
767 "unavailable on stream end");
770 if (streamsToConsumer(0)) {
771 TileLike consTile = getTileLikeFromValue(getConsumerTiles()[0]);
772 if (consTile && (consTile.isShimTile() || consTile.isMemTile()))
774 "a Core stream port end is not available for shim and mem tiles");
777 if (!getDimensionsFromStreamPerConsumer()[0].empty())
778 return emitError(
"`dimensionsFromStreamPerConsumer` data layout "
779 "transformations are unavailable on stream end");
782 if (getInitValues().has_value()) {
783 if (producerTile.isShimTile())
784 return emitError(
"`init_values` unavailable for shim tiles");
787 if (
auto initValues = getInitValues()) {
788 if ((
int)initValues->size() != size())
789 return emitError(
"`init_values` does not initialize all objects");
792 if (
auto iterCountAttr = getIterCount()) {
793 int iterCount = *iterCountAttr;
794 if (iterCount < 1 || iterCount > 256)
795 return emitError(
"`iter_count` must be between 1 and 256");
798 if (
auto consumerElemType = getConsumerElemType()) {
799 auto consType = llvm::dyn_cast<AIEObjectFifoType>(*consumerElemType);
801 return emitError(
"consumer element type must be an "
802 "!aie.objectfifo<memref<...>> type");
803 auto prodType = llvm::cast<AIEObjectFifoType>(getElemType());
804 auto prodMemref = prodType.getElementType();
805 auto consMemref = consType.getElementType();
806 if (prodMemref.getElementType() != consMemref.getElementType())
807 return emitError(
"producer and consumer must have the same scalar "
808 "element type, but got ")
809 << prodMemref.getElementType() <<
" vs "
810 << consMemref.getElementType();
811 int64_t prodSize = prodMemref.getNumElements();
812 int64_t consSize = consMemref.getNumElements();
814 return emitError(
"consumer element count must be positive");
815 if (prodSize % consSize != 0)
816 return emitError(
"producer element size (")
817 << prodSize <<
") must be an integer multiple of consumer "
818 <<
"element size (" << consSize <<
")";
824TileOp ObjectFifoCreateOp::getProducerTileOp() {
825 return cast<TileOp>(getProducerTile().getDefiningOp());
835template <
typename OpTy>
836SmallVector<OpTy> lookupAll(Operation *from, std::optional<ArrayAttr> names) {
837 SmallVector<OpTy> ops;
839 for (
auto name : names->getAsRange<FlatSymbolRefAttr>()) {
841 dyn_cast_or_null<OpTy>(
lookupNamedOp(from, name.getAttr()))) {
851TileLike ObjectFifoPoolOp::getTileLike() {
852 return dyn_cast<TileLike>(getTile().getDefiningOp());
855int64_t ObjectFifoPoolOp::getObjectSize() {
856 return llvm::cast<MemRefType>(getElemType()).getNumElements();
859int64_t ObjectFifoPoolOp::getObjectsPerBuffer() {
860 if (!getContiguous())
862 int64_t objects = getDepth();
863 if (
auto zeros = getZeroObjects())
864 objects += (*zeros)[0] + (*zeros)[1];
868LogicalResult ObjectFifoPoolOp::verify() {
869 auto &target = (*this)->getParentOfType<DeviceOp>().
getTargetModel();
874 if (
auto locks = getLocks()) {
875 if (semaphoreLocks) {
876 return emitOpError(
"'locks' names binary locks, which this device has "
879 if (
static_cast<int64_t
>(locks->size()) != getDepth()) {
880 return emitOpError(
"expects one lock per buffer");
884 if (
auto zeros = getZeroObjects()) {
885 if (!getContiguous() || zeros->size() != 2 ||
886 llvm::any_of(*zeros, [](int32_t count) { return count < 0; }))
887 return emitOpError(
"'zeroObjects' expects [before, after] counts on a "
891 if (
auto buffers = getBuffers()) {
892 if (getContiguous() && buffers->size() != 1) {
893 return emitOpError(
"expects one buffer holding its objects side by side");
895 if (!getContiguous() &&
896 static_cast<int64_t
>(buffers->size()) != getDepth()) {
897 return emitOpError(
"expects 'depth' buffers");
901 if (
auto initValues = getInitValues()) {
902 if (
static_cast<int64_t
>(initValues->size()) > getDepth()) {
903 return emitOpError(
"has more 'init_values' than 'depth' objects");
907 std::vector<ObjectFifoSegmentOp> segments = getSegmentOps();
908 if (segments.empty()) {
909 return emitOpError(
"expects at least one segment");
912 int64_t previous = -1;
914 for (ObjectFifoSegmentOp segment : segments) {
915 if (
static_cast<int64_t
>(segment.getOffset()) <= previous) {
916 return emitOpError(
"segments must be ordered by increasing offset");
918 if (
static_cast<int64_t
>(segment.getOffset()) != covered) {
919 return segment.emitOpError(
"leaves a gap or overlaps the segment before "
920 "it; segments must tile the object");
922 previous = segment.getOffset();
923 covered = segment.getOffset() + segment.getSize();
924 if (!semaphoreLocks &&
925 (segment.getProduceLock() || segment.getConsumeLock())) {
926 return emitOpError(
"segment locks are counting locks, which this "
927 "device has no use for");
931 if (covered != getObjectSize()) {
932 return emitOpError(
"segments cover ")
933 << covered <<
" of " << getObjectSize() <<
" elements";
936 if (segments.size() > 1 && !semaphoreLocks) {
938 "multi-segment pools are unsupported on binary lock architectures");
944int64_t ObjectFifoPoolOp::getObjectSizeInBytes() {
945 MemRefType elemType = getElemType();
946 DataLayout layout = DataLayout::closest(*
this);
947 return elemType.getNumElements() *
948 layout.getTypeSize(elemType.getElementType());
951std::vector<ObjectFifoSegmentOp> ObjectFifoPoolOp::getSegmentOps() {
952 auto ops = getSegments().getOps<ObjectFifoSegmentOp>();
953 return {ops.begin(), ops.end()};
956Value BufferOp::getBufferTile() {
return getTile(); }
958SmallVector<BufferLike> ObjectFifoPoolOp::getBufferOps() {
961 return lookupAll<BufferLike>((*this)->getParentOfType<DeviceOp>(),
965SmallVector<LockOp> ObjectFifoPoolOp::getLockOps() {
966 auto device = (*this)->getParentOfType<DeviceOp>();
967 SmallVector<LockOp> locks = lookupAll<LockOp>(device, getLocks());
968 for (ObjectFifoSegmentOp segment : getSegmentOps()) {
969 for (std::optional<FlatSymbolRefAttr> name :
970 {segment.getProduceLockAttr(), segment.getConsumeLockAttr()}) {
973 locks.push_back(lock);
981StringRef ObjectFifoPoolOp::getBaseName() {
982 StringRef name = getSymName();
983 return name.consume_back(
"__pool") ? name : getSymName();
992LogicalResult verifyEndpoint(Operation *op, ObjectFifoPoolOp pool,
993 std::optional<ArrayAttr> segments) {
994 std::vector<ObjectFifoSegmentOp> all = pool.getSegmentOps();
996 if (all.size() > 1) {
997 return op->emitOpError(
998 "must list segments explicitly for a multi-segment pool");
1002 if (segments->empty()) {
1003 return op->emitOpError(
"expects at least one segment");
1006 int64_t previous = -1;
1007 for (
auto name : segments->getAsRange<FlatSymbolRefAttr>()) {
1008 auto segment = SymbolTable::lookupNearestSymbolFrom<ObjectFifoSegmentOp>(
1009 pool, name.getAttr());
1011 return op->emitOpError(
"references undefined segment '")
1012 << name.getValue() <<
"' in pool '" << pool.getSymName() <<
"'";
1016 if (
static_cast<int64_t
>(segment.getOffset()) <= previous) {
1017 return op->emitOpError(
"segments must be named in increasing offset "
1020 previous = segment.getOffset();
1025ObjectFifoPoolOp lookupPool(Operation *op, StringRef name) {
1026 auto device = op->getParentOfType<DeviceOp>();
1027 return SymbolTable::lookupNearestSymbolFrom<ObjectFifoPoolOp>(
1028 device, StringAttr::get(op->getContext(), name));
1035static std::vector<ObjectFifoSegmentOp>
1036selectSegments(ObjectFifoPoolOp pool, std::optional<ArrayAttr> selected) {
1040 std::vector<ObjectFifoSegmentOp> all = pool.getSegmentOps();
1042 return all.empty() ? std::vector<ObjectFifoSegmentOp>{}
1043 : std::vector<ObjectFifoSegmentOp>{all.front()};
1045 std::vector<ObjectFifoSegmentOp> chosen;
1046 for (
auto name : selected->getAsRange<FlatSymbolRefAttr>()) {
1048 SymbolTable::lookupNearestSymbolFrom<ObjectFifoSegmentOp>(
1049 pool, name.getAttr())) {
1050 chosen.push_back(segment);
1056TileLike ObjectFifoCoreEndpointOp::getTileLike() {
1057 return dyn_cast<TileLike>(getTile().getDefiningOp());
1060ObjectFifoPoolOp ObjectFifoCoreEndpointOp::getPoolOp() {
1061 return lookupPool(*
this, getPool());
1064std::vector<ObjectFifoSegmentOp>
1065ObjectFifoCoreEndpointOp::getSelectedSegments() {
1066 return selectSegments(getPoolOp(), getSegments());
1069LogicalResult ObjectFifoCoreEndpointOp::verify() {
1070 ObjectFifoPoolOp pool = getPoolOp();
1072 return emitOpError(
"references undefined pool '") << getPool() <<
"'";
1074 if (failed(verifyEndpoint(*
this, pool, getSegments()))) {
1078 if (
auto push = getCorePushAttr()) {
1080 return emitOpError(
"core_push requires a filling core endpoint");
1081 if (
auto values = pool.getInitValues(); values && !values->empty())
1082 return emitOpError(
"core_push does not support initialized objects");
1083 if (push.getBdTags().size() !=
static_cast<size_t>(pool.getDepth()))
1084 return emitOpError(
"core_push requires one BD tag per pool buffer");
1085 if (llvm::any_of(push.getBdTags(), [](int32_t tag) { return tag < 0; }))
1086 return emitOpError(
"core_push requires nonnegative BD tags");
1092 for (ObjectFifoSegmentOp segment : getSelectedSegments()) {
1093 if (next >= 0 && segment.getOffset() != next) {
1094 return emitOpError(
"a core endpoint's segments must be contiguous");
1096 next = segment.getOffset() + segment.getSize();
1101std::pair<int64_t, int64_t> ObjectFifoCoreEndpointOp::getExtent() {
1102 std::vector<ObjectFifoSegmentOp> selected = getSelectedSegments();
1103 if (selected.empty()) {
1106 int64_t offset = selected.front().getOffset();
1107 int64_t end = selected.back().getOffset() + selected.back().getSize();
1108 return {offset, end - offset};
1111MemRefType ObjectFifoCoreEndpointOp::getAccessType() {
1112 MemRefType elemType = getPoolOp().getElemType();
1113 auto [offset, size] = getExtent();
1114 if (offset == 0 && size == elemType.getNumElements()) {
1117 return cast<MemRefType>(memref::SubViewOp::inferRankReducedResultType(
1118 {size}, elemType, {offset}, {size}, {1}));
1121TileLike ObjectFifoDmaEndpointOp::getTileLike() {
1122 return dyn_cast<TileLike>(getTile().getDefiningOp());
1125ObjectFifoPoolOp ObjectFifoDmaEndpointOp::getPoolOp() {
1126 return lookupPool(*
this, getPool());
1129std::vector<ObjectFifoSegmentOp>
1130ObjectFifoDmaEndpointOp::getSelectedSegments() {
1131 return selectSegments(getPoolOp(), getSegments());
1134int64_t ObjectFifoDmaEndpointOp::getNumDescriptors() {
1135 ObjectFifoPoolOp pool = getPoolOp();
1138 if (pool.getContiguous()) {
1140 if (!walksStream()) {
1141 SmallVector<ObjectFifoDmaEndpointOp> ends =
turnEndpoints(*
this);
1144 ArrayRef<Attribute> turns = getTurnsAttr().getValue();
1145 SmallVector<std::pair<int64_t, int64_t>> counts;
1146 for (ObjectFifoDmaEndpointOp end : ends)
1148 {llvm::count(turns, FlatSymbolRefAttr::get(end.getSymNameAttr())),
1149 end.getPoolOp().getContiguous() ? 0 : end.getNumDescriptors()});
1150 return llvm::count(turns, FlatSymbolRefAttr::get(getSymNameAttr())) *
1154 return succeeded(getStreamWindow(window)) ? window.
outputs : 0;
1156 return pool.getDepth() * getSelectedSegments().size();
1159bool ObjectFifoDmaEndpointOp::walksStream() {
1160 ObjectFifoPoolOp pool = getPoolOp();
1161 auto dims = getDimensions();
1162 return pool && pool.getContiguous() && drains() && dims && !dims->empty() &&
1163 !(*dims)[0].getValue().empty();
1166LogicalResult ObjectFifoDmaEndpointOp::getStreamWindow(
StreamWindow &window) {
1167 auto dims = getDimensions();
1168 if (!dims || dims->empty())
1169 return emitOpError(
"walks no stream: it has no dimensions");
1170 ArrayRef<BDPadLayoutAttr> pads;
1171 if (
auto padding = getPadDimensions(); padding && !padding->empty())
1172 pads = (*padding)[0].getValue();
1174 getPoolOp().getObjectSize(),
1175 [&] {
return emitOpError(); }, window);
1178int64_t ObjectFifoDmaEndpointOp::getRepeat() {
1179 ObjectFifoPoolOp pool = getPoolOp();
1181 return pool && drains() ? pool.getRepeatCount().value_or(1) : 1;
1184bool ObjectFifoDmaEndpointOp::repeatsInHardware() {
1185 return getRepeat() > 1 &&
1186 (getReleaseStrategy() == ObjectFifoRelease::PushQueue ||
1187 (getNumDescriptors() == 1 && !getIterCount()));
1190int64_t ObjectFifoDmaEndpointOp::getDispatchPasses() {
1191 ObjectFifoPoolOp pool = getPoolOp();
1192 std::optional<ArrayAttr> turns = getDispatch();
1193 if (!pool || !turns || turns->empty())
1195 int64_t depth = std::max<int64_t>(pool.getDepth(), 1);
1196 return std::lcm(depth,
static_cast<int64_t
>(turns->size())) / depth;
1199SmallVector<ObjectFifoDmaEndpointOp>
1201 SmallVector<ObjectFifoDmaEndpointOp> ends;
1202 if (std::optional<ArrayAttr> turns = endpoint.getTurns())
1203 for (
auto name : turns->getAsRange<FlatSymbolRefAttr>())
1205 SymbolTable::lookupNearestSymbolFrom<ObjectFifoDmaEndpointOp>(
1207 end && !llvm::is_contained(ends, end))
1208 ends.push_back(end);
1214 for (
auto [appearances, count] : ends)
1216 passes = std::lcm(passes, count / std::gcd(count, appearances));
1220int64_t ObjectFifoDmaEndpointOp::getNumBDs() {
1221 SmallVector<ObjectFifoDmaEndpointOp> ends =
turnEndpoints(*
this);
1223 return getNumDescriptors() * (repeatsInHardware() ? 1 : getRepeat()) *
1224 getDispatchPasses();
1225 if (ends.front() != *
this)
1227 ArrayRef<Attribute> turns = getTurnsAttr().getValue();
1228 SmallVector<std::pair<int64_t, int64_t>> counts;
1229 for (ObjectFifoDmaEndpointOp end : ends)
1231 {llvm::count(turns, FlatSymbolRefAttr::get(end.getSymNameAttr())),
1232 end.getPoolOp().getContiguous() ? 0 : end.getNumDescriptors()});
1233 return turnPasses(counts) *
static_cast<int64_t
>(turns.size());
1236DMAChannelDir ObjectFifoDmaEndpointOp::getRouteDirection() {
1237 return drains() ? DMAChannelDir::MM2S : DMAChannelDir::S2MM;
1240WireBundle ObjectFifoDmaEndpointOp::getRouteBundle() {
return WireBundle::DMA; }
1242std::optional<int> ObjectFifoDmaEndpointOp::getRouteChannel() {
1243 return getChannelIndex();
1246void ObjectFifoDmaEndpointOp::setRouteChannel(
int channel) {
1247 setChannelIndex(channel);
1250LogicalResult ObjectFifoDmaEndpointOp::verify() {
1251 ObjectFifoPoolOp pool = getPoolOp();
1253 return emitOpError(
"references undefined pool '") << getPool() <<
"'";
1256 if (failed(verifyEndpoint(*
this, pool, getSegments()))) {
1263 if (getReleaseStrategy() == ObjectFifoRelease::PushQueue) {
1264 if (!drains() || !getTileLike().isCoreTile() ||
1266 return emitOpError(
"push_queue requires a sending DMA on a core tile "
1267 "with core-issued task support");
1268 if (
auto values = pool.getInitValues(); values && !values->empty())
1269 return emitOpError(
"push_queue does not support initialized objects");
1272 "push_queue repeat_count exceeds the hardware task limit");
1273 if (getOutOfOrder() && getRepeat() > 1)
1275 "push_queue cannot repeat out-of-order packets: "
1276 "each repetition may need a different destination BD ID");
1279 if (std::optional<ArrayAttr> turns = getDispatch()) {
1281 return emitOpError(
"only a draining endpoint can dispatch");
1283 return emitOpError(
"dispatch names no destination");
1284 if (getPacketAttr())
1285 return emitOpError(
"a dispatching endpoint carries a header per turn in "
1286 "`dispatchPackets`, not one `packet`");
1287 if (pool.getRepeatCount().value_or(1) > 1)
1288 return emitOpError(
"cannot dispatch a pool that repeats its objects");
1289 if (std::optional<int32_t> iterCount = getIterCount();
1290 iterCount && *iterCount % std::max<int64_t>(getDispatchPasses(), 1))
1291 return emitOpError(
"iterCount ")
1292 << *iterCount <<
" is not a whole number of dispatch rounds ("
1293 << getDispatchPasses() <<
" passes over the pool)";
1295 if (std::optional<ArrayAttr> headers = getDispatchPackets()) {
1296 std::optional<ArrayAttr> turns = getDispatch();
1297 if (!turns || turns->size() != headers->size())
1298 return emitOpError(
"dispatchPackets needs one header per dispatch turn");
1299 for (
auto header : headers->getAsRange<PacketInfoAttr>())
1303 if (std::optional<ArrayAttr> turns = getTurns()) {
1304 if (!llvm::is_contained(*turns, FlatSymbolRefAttr::get(getSymNameAttr())))
1305 return emitOpError(
"takes no turn of its own in `turns`");
1306 for (
auto name : turns->getAsRange<FlatSymbolRefAttr>())
1307 if (!SymbolTable::lookupNearestSymbolFrom<ObjectFifoDmaEndpointOp>(*
this,
1309 return emitOpError(
"turns names ")
1310 << name <<
", which is no objectfifo.dma_endpoint";
1311 if (getDispatch() || getPacketAttr() || getIterCount() || getRepeat() > 1)
1312 return emitOpError(
"taking turns on a channel, it cannot also dispatch, "
1313 "carry a header, stop after iterCount or repeat");
1315 if (end.getTile() != getTile() || end.getRole() != getRole() ||
1316 end.getTurnsAttr() != getTurnsAttr() ||
1317 end.getChannelIndexAttr() != getChannelIndexAttr())
1318 return emitOpError(
"takes turns with ")
1319 << end.getSymNameAttr()
1320 <<
", which is not on this tile and channel with the same turns";
1321 if (pool.getContiguous() && !walksStream() &&
1322 pool.getDepth() % getNumDescriptors())
1323 return emitOpError(
"walks a contiguous pool of ")
1324 << pool.getDepth() <<
" objects in " << getNumDescriptors()
1325 <<
" turns a pass, which do not divide them";
1328 std::optional<ArrayAttr> segmentNames = getSegments();
1329 size_t selectedCount = segmentNames ? segmentNames->size() : 1;
1330 auto dimensions = getDimensions();
1331 if (dimensions && dimensions->size() != selectedCount) {
1332 return emitOpError(
"dimensions has ")
1333 << dimensions->size() <<
" entries for " << selectedCount
1334 <<
" selected segments";
1336 auto padding = getPadDimensions();
1337 if (padding && padding->size() != selectedCount) {
1338 return emitOpError(
"padDimensions has ")
1339 << padding->size() <<
" entries for " << selectedCount
1340 <<
" selected segments";
1342 if (padding && !drains()) {
1343 return emitOpError(
"padDimensions is only valid on a draining endpoint");
1345 if (getPadValue() != 0 &&
1346 (!padding || llvm::none_of(*padding, [](BDPadLayoutArrayAttr entry) {
1347 return !entry.empty();
1349 return emitOpError(
"nonzero padValue requires a non-empty padDimensions "
1356 return getStreamWindow(window);
1358 std::vector<ObjectFifoSegmentOp> selected = getSelectedSegments();
1359 for (
size_t position = 0; position < selectedCount; ++position) {
1360 BDDimLayoutArrayAttr dims =
1361 dimensions ? (*dimensions)[position] : BDDimLayoutArrayAttr();
1362 BDPadLayoutArrayAttr pads =
1363 padding ? (*padding)[position] : BDPadLayoutArrayAttr();
1364 if (pads && !pads.empty() && (!dims || dims.empty())) {
1365 return emitOpError(
"padDimensions entry ")
1366 << position <<
" requires dimensions for the same segment";
1368 if (dims && pads && !pads.empty() && dims.size() != pads.size()) {
1369 return emitOpError(
"dimensions and padDimensions entry ")
1370 << position <<
" have different ranks";
1372 if (dims && !dims.empty() &&
1373 getDimsMaxIdx(dims) >= selected[position].getSize()) {
1374 return emitOpError(
"dimensions entry ")
1375 << position <<
" exceeds selected segment '"
1376 << selected[position].getSymName() <<
"' of size "
1377 << selected[position].getSize();
1383TileLike RouteEndpointOp::getTileLike() {
1384 return dyn_cast<TileLike>(getTile().getDefiningOp());
1387DMAChannelDir RouteEndpointOp::getRouteDirection() {
1390 auto device = getOperation()->getParentOfType<DeviceOp>();
1391 StringRef name = getSymName();
1393 for (
auto flow : device.getOps<RouteOp>()) {
1394 if (flow.namesAsSource(name)) {
1395 return DMAChannelDir::MM2S;
1397 named |= flow.namesAsDestination(name);
1399 assert(named &&
"endpoint is named by no flow, so it has no direction to "
1400 "read; --aie-objectfifo-verify rejects this");
1402 return DMAChannelDir::S2MM;
1405WireBundle RouteEndpointOp::getRouteBundle() {
return getBundle(); }
1407std::optional<int> RouteEndpointOp::getRouteChannel() {
1408 return getChannelIndex();
1411void RouteEndpointOp::setRouteChannel(
int channel) { setChannelIndex(channel); }
1413LogicalResult RouteEndpointOp::verify() {
1414 TileLike tile = getTileLike();
1416 return emitOpError(
"tile operand is not an aie.tile or aie.logical_tile");
1424 StringRef name = getSymName();
1425 SmallVector<RouteOp> sources, dests;
1426 for (
auto flow : (*this)->getParentOfType<DeviceOp>().getOps<RouteOp>()) {
1427 if (flow.namesAsSource(name))
1428 sources.push_back(flow);
1429 if (flow.namesAsDestination(name))
1430 dests.push_back(flow);
1432 if (!sources.empty() && !dests.empty()) {
1433 return emitOpError(
"is named as both a source and a destination, so its "
1434 "direction is ambiguous");
1436 ArrayRef<RouteOp> sharers = sources.empty() ? dests : sources;
1437 if (sharers.size() > 1) {
1438 if (llvm::any_of(sharers, [](RouteOp flow) {
return !flow.getPacket(); }))
1439 return emitOpError(
"drives one channel, which a circuit flow owns, but "
1441 << sharers.size() <<
" times";
1442 llvm::SmallDenseSet<int> ids;
1443 for (RouteOp flow : sources) {
1444 std::optional<uint16_t>
id = flow.getPacketAttr().getPktId();
1446 return emitOpError(
"sends several packet flows, so each must pin its "
1447 "pkt_id for the BDs to state it");
1448 if (!ids.insert(*id).second)
1449 return emitOpError(
"sends several packet flows, two of which pin the "
1451 <<
static_cast<int>(*id) <<
"; each needs its own";
1454 if (llvm::any_of(dests, [&](RouteOp flow) {
1455 return flow.getKeepPktHeader() != dests[0].getKeepPktHeader();
1457 return emitOpError(
"receives several packet flows, so they must agree "
1458 "on keep_pkt_header");
1460 switch (getBundle()) {
1461 case WireBundle::DMA:
1465 case WireBundle::PLIO:
1466 if (!tile.isShimTile()) {
1467 return emitOpError(
"a PLIO end is on a shim");
1470 case WireBundle::Core:
1473 return emitOpError(
"bundle must be DMA, PLIO or Core");
1483static ParseResult parseRouteSources(OpAsmParser &parser, ArrayAttr &sources) {
1484 SmallVector<Attribute> names;
1485 auto parseOne = [&]() -> ParseResult {
1486 FlatSymbolRefAttr name;
1487 if (parser.parseAttribute(name))
1489 names.push_back(name);
1492 if (succeeded(parser.parseOptionalLSquare())) {
1493 if (failed(parser.parseOptionalRSquare()) &&
1494 (parser.parseCommaSeparatedList(parseOne) || parser.parseRSquare()))
1496 }
else if (parseOne()) {
1499 sources = parser.getBuilder().getArrayAttr(names);
1503static void printRouteSources(OpAsmPrinter &p, Operation *, ArrayAttr sources) {
1504 if (sources.size() == 1) {
1505 p.printAttribute(sources[0]);
1509 llvm::interleaveComma(sources, p,
1510 [&](Attribute name) { p.printAttribute(name); });
1514LogicalResult RouteOp::verify() {
1515 if (getSources().empty()) {
1516 return emitOpError(
"expects at least one source");
1518 if (getDestinations().empty()) {
1519 return emitOpError(
"expects at least one destination");
1522 if (getKeepPktHeader() && !getPacket()) {
1523 return emitOpError(
"keep_pkt_header is only meaningful on a packet flow");
1526 auto device = (*this)->getParentOfType<DeviceOp>();
1527 auto lookup = [&](StringRef name) {
1528 return dyn_cast_or_null<RouteEndpoint>(SymbolTable::lookupNearestSymbolFrom(
1529 device, StringAttr::get(getContext(), name)));
1532 llvm::SmallDenseSet<StringRef> seen;
1533 for (StringRef source : getSourceNames()) {
1534 if (!lookup(source)) {
1535 return emitOpError(
"source '") << source <<
"' is not an endpoint";
1537 if (!seen.insert(source).second) {
1538 return emitOpError(
"names '@") << source <<
"' as a source twice";
1541 for (StringRef destination : getDestinationNames()) {
1542 if (!lookup(destination)) {
1543 return emitOpError(
"destination '")
1544 << destination <<
"' is not an endpoint";
1546 if (!seen.insert(destination).second) {
1547 return emitOpError(
"names '@")
1549 <<
"' more than once; an end is a source or a "
1550 "destination, and only one of each";
1556 if (isFanIn() && !getPacket()) {
1557 return emitOpError(
"has several sources, so it needs a `packet` header; "
1558 "a circuit cannot merge streams");
1564ObjectFifoCreateOp::getDimensionsFromStream(Value consumerTile) {
1566 for (
auto cons : getConsumerTiles()) {
1567 if (cons == consumerTile)
1571 return getDimensionsFromStreamPerConsumer()[dimsIndex];
1575 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
1576 BDDimLayoutArrayAttr &dimensions) {
1577 std::vector<BDDimLayoutAttr> emptyDims = {};
1578 if (parser.parseOperand(operand))
1580 if (succeeded(parser.parseOptionalKeyword(
"dimensionsToStream"))) {
1581 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
1587 BDDimLayoutArrayAttr::get(parser.getContext(), ArrayRef(emptyDims));
1593 Operation *op, Value operand,
1594 BDDimLayoutArrayAttr dimensions) {
1596 if (!dimensions.empty()) {
1597 printer <<
" dimensionsToStream ";
1598 printer.printStrippedAttrOrType(dimensions);
1603 Attribute &channel) {
1605 if (succeeded(parser.parseOptionalSymbolName(name))) {
1606 channel = FlatSymbolRefAttr::get(name);
1610 if (parser.parseInteger(index))
1612 channel = parser.getBuilder().getI32IntegerAttr(index);
1617 bool resolved =
true;
1618 device.walkOutsideRuntimeSequences<DMAStartOp>([&](DMAStartOp start) {
1619 if (FlatSymbolRefAttr endpoint = start.getEndpoint()) {
1620 start.emitOpError() <<
"names route endpoint " << endpoint
1621 <<
" in place of a channel index; run "
1622 "--aie-objectfifo-allocate to assign one";
1626 return success(resolved);
1630 Attribute channel) {
1631 if (
auto index = dyn_cast<IntegerAttr>(channel))
1632 printer << index.getInt();
1634 printer.printAttributeWithoutType(channel);
1639 ObjectFifoPortAttr &port,
1640 SmallVectorImpl<Type> &objects) {
1641 if (parser.parseLParen())
1645 SMLoc portLoc = parser.getCurrentLocation();
1646 if (succeeded(parser.parseOptionalKeyword(&portName))) {
1647 std::optional<ObjectFifoPort> parsed = symbolizeObjectFifoPort(portName);
1649 return parser.emitError(portLoc,
"invalid objectFifo port: ") << portName;
1650 port = ObjectFifoPortAttr::get(parser.getContext(), *parsed);
1651 if (parser.parseComma())
1656 SMLoc countLoc = parser.getCurrentLocation();
1657 if (parser.parseInteger(count) || parser.parseRParen() ||
1658 parser.parseColon() || parser.parseTypeList(objects))
1661 if (count !=
static_cast<int64_t
>(objects.size()))
1662 return parser.emitError(countLoc,
"acquires ")
1663 << count <<
" objects but names " << objects.size() <<
" of them";
1669 ObjectFifoPortAttr port,
1670 TypeRange objects) {
1673 printer << stringifyObjectFifoPort(port.getValue()) <<
", ";
1674 printer << objects.size() <<
") : ";
1675 llvm::interleaveComma(objects, printer);
1679 ObjectFifoPortAttr &port,
1680 IntegerAttr &size) {
1681 if (parser.parseLParen())
1685 SMLoc portLoc = parser.getCurrentLocation();
1686 if (succeeded(parser.parseOptionalKeyword(&portName))) {
1687 std::optional<ObjectFifoPort> parsed = symbolizeObjectFifoPort(portName);
1689 return parser.emitError(portLoc,
"invalid objectFifo port: ") << portName;
1690 port = ObjectFifoPortAttr::get(parser.getContext(), *parsed);
1691 if (parser.parseComma())
1696 if (parser.parseInteger(count) || parser.parseRParen())
1698 size = parser.getBuilder().getI32IntegerAttr(count);
1704 ObjectFifoPortAttr port,
1708 printer << stringifyObjectFifoPort(port.getValue()) <<
", ";
1709 printer << size.getInt() <<
")";
1713 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &tiles,
1714 BDDimLayoutArrayArrayAttr &dimensions) {
1717 std::vector<BDDimLayoutArrayAttr> tileDims = {};
1719 auto parseOneOperand = [&]() -> ParseResult {
1720 if (parser.parseOperand(tiles.emplace_back(),
true)) {
1726 BDDimLayoutArrayAttr dimAttr =
1727 BDDimLayoutArrayAttr::get(parser.getContext(), {});
1729 if (succeeded(parser.parseOptionalKeyword(
"dimensionsFromStream"))) {
1731 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
1736 tileDims.emplace_back(dimAttr);
1740 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::None,
1741 parseOneOperand,
" in operand list"))
1744 dimensions = BDDimLayoutArrayArrayAttr::get(parser.getContext(), tileDims);
1749 OpAsmPrinter &printer, Operation *op, OperandRange tiles,
1750 BDDimLayoutArrayArrayAttr dimsPerTileAttr) {
1752 for (
auto tile : tiles) {
1754 if (dimsPerTileAttr && tileIdx < dimsPerTileAttr.size() &&
1755 dimsPerTileAttr[tileIdx] && !dimsPerTileAttr[tileIdx].empty()) {
1756 printer <<
" dimensionsFromStream ";
1757 printer.printStrippedAttrOrType(dimsPerTileAttr[tileIdx]);
1759 if (tileIdx < tiles.size() - 1) {
1766static void printObjectFifoConsumerElemType(OpAsmPrinter &p,
1767 ObjectFifoCreateOp op,
1768 TypeAttr consumerElemType) {
1769 if (consumerElemType)
1770 p <<
" -> " << consumerElemType;
1774 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vias,
1775 DenseI32ArrayAttr &ingressBundles, DenseI32ArrayAttr &ingressChannels,
1776 DenseI32ArrayAttr &egressBundles, DenseI32ArrayAttr &egressChannels) {
1777 SmallVector<int32_t> inBundles, inChannels, egBundles, egChannels;
1778 auto parseBundle = [&](SmallVectorImpl<int32_t> &out) -> ParseResult {
1780 if (parser.parseKeyword(&kw)) {
1783 auto bundle = symbolizeWireBundle(kw);
1785 return parser.emitError(parser.getCurrentLocation(),
1786 "invalid wire bundle '" + kw +
"'");
1788 out.push_back(
static_cast<int32_t
>(*bundle));
1792 if (succeeded(parser.parseOptionalKeyword(
"via"))) {
1793 auto parseOne = [&]() -> ParseResult {
1794 int32_t inChan, egChan;
1795 if (parser.parseOperand(vias.emplace_back()) || parser.parseColon() ||
1796 parseBundle(inBundles) || parser.parseColon() ||
1797 parser.parseInteger(inChan) || parser.parseArrow() ||
1798 parseBundle(egBundles) || parser.parseColon() ||
1799 parser.parseInteger(egChan)) {
1802 inChannels.push_back(inChan);
1803 egChannels.push_back(egChan);
1806 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Paren, parseOne)) {
1810 if (!vias.empty()) {
1811 MLIRContext *ctx = parser.getContext();
1812 ingressBundles = DenseI32ArrayAttr::get(ctx, inBundles);
1813 ingressChannels = DenseI32ArrayAttr::get(ctx, inChannels);
1814 egressBundles = DenseI32ArrayAttr::get(ctx, egBundles);
1815 egressChannels = DenseI32ArrayAttr::get(ctx, egChannels);
1822 DenseI32ArrayAttr ingressBundles,
1823 DenseI32ArrayAttr ingressChannels,
1824 DenseI32ArrayAttr egressBundles,
1825 DenseI32ArrayAttr egressChannels) {
1830 for (
size_t i = 0; i < vias.size(); i++) {
1834 printer << vias[i] <<
" : "
1835 << stringifyWireBundle(
static_cast<WireBundle
>(ingressBundles[i]))
1836 <<
" : " << ingressChannels[i] <<
" -> "
1837 << stringifyWireBundle(
static_cast<WireBundle
>(egressBundles[i]))
1838 <<
" : " << egressChannels[i];
1843static ParseResult parseObjectFifoConsumerElemType(OpAsmParser &parser,
1844 TypeAttr &consumerElemType) {
1845 if (failed(parser.parseOptionalArrow()))
1848 if (parser.parseType(type))
1850 consumerElemType = TypeAttr::get(type);
1854static void printObjectFifoInitValues(OpAsmPrinter &p, ObjectFifoCreateOp op,
1855 Attribute numElem, TypeAttr type,
1856 Attribute initValues) {
1857 if (op.getInitValues()) {
1863 if (isa<ArrayAttr>(numElem)) {
1865 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::ArrayAttr>(numElem)[0])
1868 depth = llvm::cast<mlir::IntegerAttr>(numElem).getInt();
1870 for (
int i = 0; i < depth; i++) {
1871 p.printStrippedAttrOrType(llvm::cast<mlir::ArrayAttr>(initValues)[i]);
1872 if (i < depth - 1) {
1880static ParseResult parseObjectFifoInitValues(OpAsmParser &parser,
1881 Attribute numElem, TypeAttr type,
1882 Attribute &initValues) {
1884 if (isa<ArrayAttr>(numElem)) {
1886 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::ArrayAttr>(numElem)[0])
1889 depth = llvm::cast<mlir::IntegerAttr>(numElem).getInt();
1891 auto objfifoType = llvm::cast<AIEObjectFifoType>(type.getValue());
1892 auto memrefType = llvm::cast<MemRefType>(objfifoType.getElementType());
1894 if (!memrefType.hasStaticShape())
1895 return parser.emitError(parser.getNameLoc())
1896 <<
"type should be static shaped memref, but got " << memrefType;
1898 if (parser.parseOptionalEqual())
1901 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
1902 if (parser.parseAttribute(initValues, tensorType))
1904 for (
int i = 0; i < depth; i++) {
1905 auto initialValues = llvm::dyn_cast<mlir::ArrayAttr>(initValues);
1906 if ((
int)initialValues.size() != depth)
1907 return parser.emitError(parser.getNameLoc())
1908 <<
"initial values should initialize all objects";
1909 if (!llvm::isa<ElementsAttr>(initialValues[i]))
1910 return parser.emitError(parser.getNameLoc())
1911 <<
"initial value should be an elements attribute";
1921LogicalResult ObjectFifoAllocateOp::verify() {
1922 ObjectFifoCreateOp objFifo = getObjectFifo();
1924 return emitError(
"cannot retrieve associated object FIFO");
1925 if (objFifo.getConsumerTiles().size() != 1)
1926 return emitError(
"can only be used in 1-to-1 object FIFOs");
1927 if (objFifo.forcesDMA())
1928 return emitError(
"cannot allocate a shared memory module to objectfifo "
1929 "that asks for a dma transport");
1930 if (objFifo.getRepeatCount().has_value())
1931 return emitError(
"cannot allocate a shared memory module to objectfifo "
1932 "with set `repeat_count` attribute");
1933 if (!objFifo.getDimensionsToStream().empty())
1934 return emitError(
"cannot allocate a shared memory module to objectfifo "
1935 "with set dimensions attribute");
1936 if (objFifo.usesStream())
1937 return emitError(
"cannot allocate a shared memory module to objectfifo "
1938 "using stream port");
1939 for (Operation *op = (*this)->getPrevNode(); op; op = op->getPrevNode())
1940 if (
auto other = dyn_cast<ObjectFifoAllocateOp>(op);
1941 other && other.getObjFifoName() == getObjFifoName())
1942 return emitOpError(
"objectfifo ")
1943 << getObjFifoNameAttr() <<
" already has an allocate operation";
1947TileOp ObjectFifoAllocateOp::getDelegateTileOp() {
1948 return cast<TileOp>(getDelegateTile().getDefiningOp());
1951ObjectFifoCreateOp ObjectFifoAllocateOp::getObjectFifo() {
1952 Operation *parent = getOperation();
1953 while ((parent = parent->getParentOp())) {
1954 if (parent->hasTrait<OpTrait::SymbolTable>()) {
1955 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
1956 isa_and_nonnull<ObjectFifoCreateOp>(st))
1957 return cast<ObjectFifoCreateOp>(st);
1967LogicalResult ObjectFifoLinkOp::verify() {
1968 if (isJoin() && isDistribute())
1969 return emitError(
"ObjectFifoLinkOp does not support 'join' and "
1970 "'distribute' at the same time");
1973 if (isMerge() && isDispatch())
1974 return emitOpError(
"a time link merges or dispatches, not both");
1975 if (!isMerge() && !isDispatch())
1976 return emitOpError(
"a time link takes turns, so it needs several inputs "
1977 "or several outputs");
1978 if (!getSrcOffsets().empty() || !getDstOffsets().empty())
1979 return emitOpError(
"a time link moves whole objects and takes no "
1981 std::vector<ObjectFifoCreateOp> fifos = getInputObjectFifos();
1982 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
1983 fifos.insert(fifos.end(), outs.begin(), outs.end());
1984 for (ObjectFifoCreateOp fifo : fifos) {
1985 if (fifo.getElemType() != fifos.front().getElemType())
1986 return emitOpError(
"a time link's participants carry one object "
1988 << fifo.name().getValue() <<
"' carries " << fifo.getElemType()
1989 <<
" and '" << fifos.front().name().getValue() <<
"' carries "
1990 << fifos.front().getElemType();
1993 for (ObjectFifoCreateOp out : outs)
1994 if (out.getConsumerTiles().size() != 1)
1995 return emitOpError(
"each turn of a dispatch reaches one consumer, "
1997 << out.name().getValue() <<
"' has "
1998 << out.getConsumerTiles().size();
2001 PacketInfoAttr pinned;
2002 for (ObjectFifoCreateOp in : getInputObjectFifos()) {
2003 PacketInfoAttr header = in.packetHeader();
2004 if (!header || !header.isAssigned())
2006 if (pinned && pinned != header)
2007 return emitOpError(
"a merge is one route with one header, but its "
2008 "inputs pin different ones");
2014 auto sharedTile = getOptionalSharedTile();
2016 return emitError(
"ObjectFifoLinkOp must have a link point, i.e., a "
2017 "shared tile between objectFifos");
2022 DenseMap<Operation *, size_t> reads;
2024 (*this)->getParentOfType<DeviceOp>().getOps<ObjectFifoLinkOp>()) {
2025 if (other == *
this) {
2028 for (ObjectFifoCreateOp fifo : getOutputObjectFifos())
2029 if (llvm::is_contained(other.getOutputObjectFifos(), fifo))
2030 return fifo.emitOpError(
2031 "objectfifo cannot be filled by more than one ObjectFifoLinkOp");
2032 for (ObjectFifoCreateOp fifo : getInputObjectFifos())
2033 if (llvm::is_contained(other.getInputObjectFifos(), fifo) &&
2034 other.getOptionalSharedTile() == sharedTile)
2037 for (ObjectFifoCreateOp fifo : getInputObjectFifos())
2038 if (reads.lookup(fifo) >=
2039 static_cast<size_t>(llvm::count(fifo.getConsumerTiles(), *sharedTile)))
2040 return fifo.emitOpError(
"objectfifo is read by more ObjectFifoLinkOps "
2041 "on a tile than it has consumer ends there");
2043 TileLike tile = llvm::dyn_cast<TileLike>(sharedTile.value().getDefiningOp());
2045 return emitError(
"shared tile must implement TileLike interface");
2049 if (isJoin() || isDistribute()) {
2050 if (tile.isShimTile())
2051 return emitError(
"ObjectFifoLinkOp join and distribute are "
2052 "unavailable on shim tiles");
2055 return emitError(
"ObjectFifoLinkOp join and distribute require "
2056 "semaphore locks, which this device lacks");
2058 if (isTime() && tile.isShimTile())
2059 return emitOpError(
"a time link holds its objects on the link point, which "
2060 "a shim tile has no memory for");
2064 auto elementsOf = [](Type objectFifoType) {
2065 return cast<MemRefType>(
2066 cast<AIEObjectFifoType>(objectFifoType).getElementType())
2070 DataLayout dataLayout = DataLayout::closest(getOperation());
2071 auto bytesOf = [&](Type objectFifoType, int64_t elements) {
2073 static_cast<int64_t
>(dataLayout.getTypeSize(
2075 cast<AIEObjectFifoType>(objectFifoType).getElementType())
2076 .getElementType()));
2078 auto checkSlot = [&](StringRef role, ObjectFifoCreateOp participant,
2079 int64_t object, ObjectFifoCreateOp shared,
2080 int64_t slotBytes, ArrayAttr offsets) -> LogicalResult {
2082 return emitOpError() << role <<
" @" << participant.name().getValue()
2083 <<
" has empty objects";
2084 if (slotBytes %
object == 0)
2086 std::string offsetText;
2087 llvm::raw_string_ostream os(offsetText);
2088 llvm::interleaveComma(offsets, os, [&](Attribute offset) {
2089 os << *getConstantIntValue(offset);
2091 return emitOpError() << role <<
" @" << participant.name().getValue()
2092 <<
" has " <<
object <<
" B objects, but its "
2093 << slotBytes <<
" B slot in @"
2094 << shared.name().getValue() <<
" from offsets ["
2095 << offsetText <<
"] is not a whole number of them";
2099 if (getFifoIns().size() != getSrcOffsets().size())
2100 return emitOpError(
"number of provided src offsets must be equal "
2101 "to the number of input objectFifos");
2103 if (!getDstOffsets().empty())
2104 return emitOpError(
"dst offsets should be empty for join");
2108 for (
auto [idx, len] : llvm::enumerate(getJoinTransferLengths()))
2110 return emitOpError(
"src offsets must increase: input objectFifo ")
2111 << idx <<
" would transfer " << len <<
" elements";
2113 ObjectFifoCreateOp fifoOut = getOutputObjectFifos()[0];
2114 for (
auto [fifoIn, slot] :
2115 llvm::zip(getInputObjectFifos(), getJoinTransferLengths())) {
2117 fifoIn.getConsumerElemType()
2118 ? bytesOf(fifoIn.getConsumerElemTypeOrDefault(),
2119 elementsOf(fifoIn.getConsumerElemTypeOrDefault()))
2120 : bytesOf(fifoIn.getElemType(),
2122 fifoIn.getPadDimensions().value_or(
2123 ArrayRef<BDPadLayoutAttr>{}),
2124 elementsOf(fifoIn.getElemType())));
2125 if (failed(checkSlot(
"input", fifoIn,
object, fifoOut,
2126 bytesOf(fifoOut.getElemType(), slot),
2130 if (!fifoOut.getDimensionsToStream().empty()) {
2131 int64_t maxIdx = getDimsMaxIdx(fifoOut.getDimensionsToStream());
2132 int64_t minInputBufferSize = -1;
2133 for (
auto lenIn : getJoinTransferLengths()) {
2134 if (lenIn <= minInputBufferSize || minInputBufferSize < 0)
2135 minInputBufferSize = lenIn;
2137 if (minInputBufferSize <= maxIdx) {
2138 return emitOpError()
2139 <<
"specified output stride(s) and size(s) result in out "
2140 "of bounds access in join input, for index "
2141 << std::to_string(maxIdx) <<
" in transfer of length "
2142 << std::to_string(minInputBufferSize) <<
".";
2146 }
else if (isDistribute()) {
2147 if (getFifoOuts().size() != getDstOffsets().size())
2148 return emitOpError(
"number of provided dst offsets must be equal "
2149 "to the number of output objectFifos");
2151 if (!getSrcOffsets().empty())
2152 return emitOpError(
"src offsets should be empty for distribute");
2154 for (
auto [idx, len] : llvm::enumerate(getDistributeTransferLengths()))
2156 return emitOpError(
"dst offsets must increase: output objectFifo ")
2157 << idx <<
" would transfer " << len <<
" elements";
2159 ObjectFifoCreateOp fifoIn = getInputObjectFifos()[0];
2160 for (
auto [fifoOut, slot] :
2161 llvm::zip(getOutputObjectFifos(), getDistributeTransferLengths())) {
2163 if (failed(checkSlot(
2165 bytesOf(fifoOut.getElemType(), elementsOf(fifoOut.getElemType())),
2167 bytesOf(fifoIn.getElemType(),
2169 fifoOut.getPadDimensions().value_or(
2170 ArrayRef<BDPadLayoutAttr>{}),
2177 for (
size_t seen = 0; end < fifoIn.getConsumerTiles().size(); end++)
2178 if (fifoIn.getConsumerTiles()[end] == *sharedTile &&
2179 seen++ == reads.lookup(fifoIn))
2181 BDDimLayoutArrayAttr dims =
2182 fifoIn.getDimensionsFromStreamPerConsumer()[end];
2183 if (!dims.empty()) {
2184 int64_t maxIdx = getDimsMaxIdx(dims);
2185 int64_t minOutputBufferSize = -1;
2186 for (
auto lenOut : getDistributeTransferLengths()) {
2187 if (lenOut <= minOutputBufferSize || minOutputBufferSize < 0)
2188 minOutputBufferSize = lenOut;
2190 if (minOutputBufferSize <= maxIdx) {
2191 return emitOpError()
2192 <<
"specified input stride(s) and size(s) result in out "
2193 "of bounds access in distribute output, for index "
2194 << std::to_string(maxIdx) <<
" in transfer of length "
2195 << std::to_string(minOutputBufferSize) <<
".";
2199 if (!getSrcOffsets().empty() && !getDstOffsets().empty())
2200 return emitOpError(
"all offsets should be empty if there is no "
2201 "join or distribute");
2204 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2205 if (!isTime() && !walksStream() && !outs.empty()) {
2206 ArrayRef<BDDimLayoutAttr> dims = outs[0].getDimensionsToStream();
2207 std::optional<ArrayRef<BDPadLayoutAttr>> pads =
2208 outs[0].getPadDimensions();
2210 for (BDDimLayoutAttr dim : dims)
2211 walked *= dim.getSize();
2212 int64_t
object = elementsOf(getPoolOwner().getElemType());
2213 if (!dims.empty() && (!pads || pads->empty()) && walked !=
object)
2214 return emitOpError()
2215 <<
"@" << outs[0].name().getValue() <<
"'s walk covers "
2216 << walked <<
" elements, but each transfer sends a whole "
2218 <<
"-element object; to send part of each input object, walk "
2219 "the stream (an outer dimension stepping whole input "
2224 if (walksStream()) {
2226 if (failed(getStreamWindow(window)))
2228 if (!tile.isMemTile())
2229 return emitOpError(
"a walk over a stream needs a MemTile link point");
2230 ObjectFifoCreateOp in = getInputObjectFifos()[0];
2231 ObjectFifoCreateOp out = getOutputObjectFifos()[0];
2232 if (in.getRepeatCount() || out.getRepeatCount())
2233 return emitOpError(
"a walk over a stream does not repeat objects");
2234 if (in.getIterCount() || out.getIterCount())
2235 return emitOpError(
"a walk over a stream runs without an iter_count");
2240 return emitOpError() <<
"a walk over a stream of " << window.
objects
2241 <<
" objects needs a lock counting that many, "
2242 "but locks count to "
2244 int64_t bds = target.
getNumBDs(AIETileType::MemTile) / 2;
2246 return emitOpError() <<
"a walk over a stream lowers to one descriptor "
2248 << window.
outputs <<
", but a MemTile channel has "
2254 if (isDistribute()) {
2255 auto fifos = getOutputObjectFifos();
2256 for (
size_t i = 1; i < fifos.size(); ++i) {
2257 ObjectFifoCreateOp first = fifos[0], fifo = fifos[i];
2258 int repeat = first.getRepeatCount().value_or(1);
2259 int otherRepeat = fifo.getRepeatCount().value_or(1);
2260 if (repeat != otherRepeat)
2261 return emitOpError()
2262 <<
"distribute outputs must have equal effective repeat counts: "
2264 << first.name().getValue() <<
" has " << repeat
2265 << (first.getRepeatCount() ?
"" :
" (default)") <<
", @"
2266 << fifo.name().getValue() <<
" has " << otherRepeat
2267 << (fifo.getRepeatCount() ?
"" :
" (default)");
2274std::optional<Value> ObjectFifoLinkOp::getOptionalSharedTile() {
2278 Value tile = getOutputObjectFifos()[0].getProducerTile();
2279 for (
auto fifoIn : getInputObjectFifos())
2280 if (!llvm::is_contained(fifoIn.getConsumerTiles(), tile))
2285 if (isDistribute()) {
2286 Value tile = getOutputObjectFifos()[0].getProducerTile();
2287 for (
auto fifoOut : getOutputObjectFifos())
2288 if (fifoOut.getProducerTile() != tile)
2290 if (!llvm::is_contained(getInputObjectFifos()[0].getConsumerTiles(), tile))
2295 auto fifoIn = getInputObjectFifos();
2296 if (
auto fifoOut = getOutputObjectFifos();
2297 !fifoIn.empty() && !fifoOut.empty())
2298 for (
auto consumerIn : fifoIn[0].getConsumerTiles())
2299 if (consumerIn == fifoOut[0].getProducerTile())
2300 return {fifoOut[0].getProducerTile()};
2304bool ObjectFifoLinkOp::walksStream() {
2305 if (isJoin() || isDistribute())
2307 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2310 ArrayRef<BDDimLayoutAttr> dims = outs[0].getDimensionsToStream();
2311 if (dims.size() < 2 || dims.front().getSize() < 2)
2314 for (BDDimLayoutAttr dim : dims.drop_front())
2315 inner *= dim.getSize();
2316 int64_t objectSize =
2317 cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType())
2321 return getDimsMaxIdx(dims) >= objectSize &&
2322 inner == cast<AIEObjectFifoType>(outs[0].getElemType())
2327LogicalResult ObjectFifoLinkOp::getStreamWindow(
StreamWindow &window) {
2328 ObjectFifoCreateOp in = getInputObjectFifos()[0];
2329 ObjectFifoCreateOp out = getOutputObjectFifos()[0];
2330 ArrayRef<BDPadLayoutAttr> pads;
2331 if (
auto padding = out.getPadDimensions())
2334 out.getDimensionsToStream(), pads,
2335 cast<AIEObjectFifoType>(in.getElemType())
2338 [&] { return emitOpError(); }, window);
2341ObjectFifoCreateOp ObjectFifoLinkOp::getPoolOwner() {
2342 std::vector<ObjectFifoCreateOp> ins = getInputObjectFifos();
2343 std::vector<ObjectFifoCreateOp> outs = getOutputObjectFifos();
2344 if (isJoin() || isMerge())
2346 if (isDistribute() || isDispatch())
2351 auto count = [](ObjectFifoCreateOp fifo) {
2352 return cast<AIEObjectFifoType>(fifo.getElemType())
2357 if (outs[0].getInitValues() ||
2358 (count(outs[0]) > count(ins[0]) && !outs[0].getPadDimensions()))
2363SmallVector<std::pair<int64_t, int64_t>> ObjectFifoLinkOp::getPoolSegments() {
2364 auto count = [](ObjectFifoCreateOp fifo) {
2365 return cast<AIEObjectFifoType>(fifo.getElemType())
2369 int64_t whole = count(getPoolOwner());
2370 if (!isJoin() && !isDistribute())
2371 return {{0, whole}};
2372 ArrayAttr offsets = isJoin() ? getSrcOffsets() : getDstOffsets();
2373 std::vector<ObjectFifoCreateOp> side =
2374 isJoin() ? getInputObjectFifos() : getOutputObjectFifos();
2375 SmallVector<std::pair<int64_t, int64_t>> segments;
2376 for (
auto [index, participant] : llvm::enumerate(side)) {
2377 int64_t offset = cast<IntegerAttr>(offsets[index]).getInt();
2378 int64_t next = index + 1 < side.size()
2379 ? cast<IntegerAttr>(offsets[index + 1]).getInt()
2381 int64_t size = next - offset;
2383 size = count(participant);
2384 segments.emplace_back(offset, size);
2389std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getInputObjectFifos() {
2390 std::vector<ObjectFifoCreateOp> inputObjFifos;
2391 Operation *parent = getOperation();
2392 while ((parent = parent->getParentOp())) {
2393 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2394 for (
auto sym : getFifoIns()) {
2395 auto name = cast<FlatSymbolRefAttr>(sym);
2396 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
2397 isa_and_nonnull<ObjectFifoCreateOp>(st))
2398 inputObjFifos.push_back(cast<ObjectFifoCreateOp>(st));
2402 return inputObjFifos;
2405std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getOutputObjectFifos() {
2406 std::vector<ObjectFifoCreateOp> outputObjFifos;
2407 Operation *parent = getOperation();
2408 while ((parent = parent->getParentOp())) {
2409 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2410 for (
auto sym : getFifoOuts()) {
2411 auto name = cast<FlatSymbolRefAttr>(sym);
2412 if (
auto *st = mlir::SymbolTable::lookupSymbolIn(parent, name);
2413 isa_and_nonnull<ObjectFifoCreateOp>(st))
2414 outputObjFifos.push_back(cast<ObjectFifoCreateOp>(st));
2418 return outputObjFifos;
2421std::vector<int> ObjectFifoLinkOp::getJoinTransferLengths() {
2422 std::vector<int> lengths;
2425 llvm::cast<AIEObjectFifoType>(getOutputObjectFifos()[0].getElemType());
2426 auto elemTypeOut = llvm::cast<MemRefType>(fifoOut.getElementType());
2427 int lenOut = elemTypeOut.getNumElements();
2430 auto srcOffset = [&](
size_t idx) ->
int {
2431 std::optional<int64_t> v = getConstantIntValue(getSrcOffsets()[idx]);
2432 assert(v &&
"src_offsets element must be a constant integer");
2433 return static_cast<int>(*v);
2435 for (
size_t i = 0; i < getFifoIns().size(); i++) {
2437 int offset = srcOffset(i);
2438 if (i == getFifoIns().size() - 1)
2439 len = lenOut - offset;
2441 len = srcOffset(i + 1) - offset;
2442 lengths.push_back(len);
2448std::vector<int> ObjectFifoLinkOp::getDistributeTransferLengths() {
2449 std::vector<int> lengths;
2450 if (isDistribute()) {
2452 llvm::cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType());
2453 auto elemTypeIn = llvm::cast<MemRefType>(fifoIn.getElementType());
2454 int lenIn = elemTypeIn.getNumElements();
2457 auto dstOffset = [&](
size_t idx) ->
int {
2458 std::optional<int64_t> v = getConstantIntValue(getDstOffsets()[idx]);
2459 assert(v &&
"dst_offsets element must be a constant integer");
2460 return static_cast<int>(*v);
2462 for (
size_t i = 0; i < getFifoOuts().size(); i++) {
2463 int offset = dstOffset(i);
2465 if (i == getFifoOuts().size() - 1)
2466 len = lenIn - offset;
2468 len = dstOffset(i + 1) - offset;
2469 lengths.push_back(len);
2475std::optional<int> ObjectFifoLinkOp::getRepeatCount() {
2476 for (
auto fifoOut : getOutputObjectFifos())
2477 if (fifoOut.getRepeatCount().has_value())
2478 return {fifoOut.getRepeatCount()};
2486TileOp ObjectFifoRegisterExternalBuffersOp::getTileOp() {
2487 return cast<TileOp>(getTile().getDefiningOp());
2490ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
2491 Operation *parent = getOperation();
2492 while ((parent = parent->getParentOp())) {
2493 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2494 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2495 isa_and_nonnull<ObjectFifoCreateOp>(st))
2496 return cast<ObjectFifoCreateOp>(st);
2510Operation *lookupAccessTarget(Operation *op, StringRef name) {
2511 for (Operation *parent = op->getParentOp(); parent;
2512 parent = parent->getParentOp()) {
2513 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2514 if (Operation *target = SymbolTable::lookupSymbolIn(parent, name)) {
2524Type accessElementType(Operation *target) {
2525 if (
auto fifo = dyn_cast_or_null<ObjectFifoCreateOp>(target)) {
2526 return llvm::cast<AIEObjectFifoType>(fifo.getElemType()).getElementType();
2528 if (
auto endpoint = dyn_cast_or_null<ObjectFifoCoreEndpointOp>(target)) {
2529 if (endpoint.getPoolOp()) {
2530 return endpoint.getAccessType();
2537LogicalResult verifyAccessPlacement(Operation *op, Operation *target,
2538 std::optional<ObjectFifoPort> port) {
2539 auto core = op->getParentOfType<CoreOp>();
2540 Value coreTile = core.getTile();
2542 if (
auto endpoint = dyn_cast_or_null<ObjectFifoCoreEndpointOp>(target)) {
2544 return op->emitOpError(
"port is implied by the endpoint's role");
2546 if (endpoint.getTile() != coreTile) {
2547 return core.emitOpError(
2548 "objectFifo endpoint accessed by core running on another tile");
2553 auto fifo = dyn_cast_or_null<ObjectFifoCreateOp>(target);
2555 return op->emitError(
"cannot retrieve associated object FIFO");
2558 return op->emitOpError(
"port is required when accessing an objectFifo");
2561 if (*port == ObjectFifoPort::Produce) {
2562 if (coreTile != fifo.getProducerTile()) {
2563 return core.emitOpError(
2564 "producer port of objectFifo accessed by core running "
2565 "on non-producer tile");
2568 if (!llvm::is_contained(fifo.getConsumerTiles(), coreTile)) {
2569 return core.emitOpError(
2570 "consumer port of objectFifo accessed by core running "
2571 "on non-consumer tile");
2579LogicalResult ObjectFifoAcquireOp::verify() {
2580 auto parent = getOperation()->getParentOfType<CoreOp>();
2581 if (parent ==
nullptr) {
2582 return emitOpError(
"must be called from inside a CoreOp");
2585 Operation *target = lookupAccessTarget(*
this, getObjFifoName());
2586 if (failed(verifyAccessPlacement(*
this, target, getPort()))) {
2590 if (getObjects().empty()) {
2591 return emitOpError(
"must acquire at least one object");
2594 Type elem = accessElementType(target);
2598 auto fifo = dyn_cast<ObjectFifoCreateOp>(target);
2602 if (getPort() == ObjectFifoPort::Consume) {
2603 for (
auto [position, consumer] :
2604 llvm::enumerate(fifo.getConsumerTiles())) {
2605 if (consumer == parent.getTile()) {
2606 index = position + 1;
2611 if (
static_cast<int>(getObjects().size()) > fifo.size(index)) {
2612 return emitOpError(
"acquires ")
2613 << getObjects().size() <<
" objects from an objectFifo that holds "
2614 << fifo.size(index);
2618 auto consType = fifo ? llvm::dyn_cast<AIEObjectFifoType>(
2619 fifo.getConsumerElemTypeOrDefault())
2621 if (fifo && !consType) {
2622 return emitOpError(
"ObjectFifo consumer element type must be an "
2623 "!aie.objectfifo<memref<...>> type");
2626 for (Type
object : getObjects().getTypes()) {
2627 if (
object != elem && (!consType || consType.getElementType() !=
object)) {
2628 return emitOpError(
"acquired object type ")
2629 <<
object <<
" does not match the objectFifo's " << elem;
2636ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() {
2637 Operation *parent = getOperation();
2638 while ((parent = parent->getParentOp())) {
2639 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2640 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2641 isa_and_nonnull<ObjectFifoCreateOp>(st))
2642 return cast<ObjectFifoCreateOp>(st);
2652LogicalResult ObjectFifoReleaseOp::verify() {
2653 auto parent = getOperation()->getParentOfType<CoreOp>();
2654 if (parent ==
nullptr)
2655 return emitOpError(
"must be called from inside a CoreOp");
2657 Operation *target = lookupAccessTarget(*
this, getObjFifoName());
2658 return verifyAccessPlacement(*
this, target, getPort());
2661ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() {
2662 Operation *parent = getOperation();
2663 while ((parent = parent->getParentOp())) {
2664 if (parent->hasTrait<OpTrait::SymbolTable>()) {
2665 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
2666 isa_and_nonnull<ObjectFifoCreateOp>(st))
2667 return cast<ObjectFifoCreateOp>(st);
2677LogicalResult CascadeFlowOp::verify() {
2678 TileLike src = getSourceTileLike();
2679 TileLike dst = getDestTileLike();
2682 return emitOpError(
"source and dest must be tile-like operations");
2684 if (src.isShimTile() || dst.isShimTile())
2685 return emitOpError(
"shimTile row has no cascade stream interface");
2686 if (src.isMemTile() || dst.isMemTile())
2687 return emitOpError(
"memTile row has no cascade stream interface");
2689 std::optional<int> srcCol = src.tryGetCol();
2690 std::optional<int> srcRow = src.tryGetRow();
2691 std::optional<int> dstCol = dst.tryGetCol();
2692 std::optional<int> dstRow = dst.tryGetRow();
2694 if (srcCol && srcRow && dstCol && dstRow) {
2696 if (!t.isSouth(*srcCol, *srcRow, *dstCol, *dstRow) &&
2697 !t.isWest(*srcCol, *srcRow, *dstCol, *dstRow) &&
2698 !t.isNorth(*srcCol, *srcRow, *dstCol, *dstRow) &&
2699 !t.isEast(*srcCol, *srcRow, *dstCol, *dstRow)) {
2700 return emitOpError(
"tiles must be adjacent");
2707TileLike CascadeFlowOp::getSourceTileLike() {
2708 return dyn_cast<TileLike>(getSourceTile().getDefiningOp());
2711TileLike CascadeFlowOp::getDestTileLike() {
2712 return dyn_cast<TileLike>(getDestTile().getDefiningOp());
2715TileOp CascadeFlowOp::getSourceTileOp() {
2716 if (
auto tileOp = dyn_cast_or_null<TileOp>(getSourceTile().getDefiningOp()))
2718 llvm::report_fatal_error(
"Calling getSourceTileOp requires TileOp.");
2721TileOp CascadeFlowOp::getDestTileOp() {
2722 if (
auto tileOp = dyn_cast_or_null<TileOp>(getDestTile().getDefiningOp()))
2724 llvm::report_fatal_error(
"Calling getDestTileOp requires TileOp.");
2731LogicalResult ConfigureCascadeOp::verify() {
2732 if (!isa<TileOp>(getTile().getDefiningOp()))
2733 return emitOpError(
"requires a placed tile (aie.tile), not a logical tile");
2736 TileOp tile = cast<TileOp>(getTile().getDefiningOp());
2737 CascadeDir inputDir = getInputDir();
2738 CascadeDir outputDir = getOutputDir();
2740 if (tile.isShimTile())
2741 return emitOpError(
"shimTile row has no cascade stream interface");
2742 if (tile.isMemTile())
2743 return emitOpError(
"memTile row has no cascade stream interface");
2745 if (isa<AIE2TargetModel>(t)) {
2746 if (inputDir == CascadeDir::South || inputDir == CascadeDir::East) {
2747 return emitOpError(
"input direction of cascade must be North or West on ")
2748 << stringifyAIEArch(t.getTargetArch());
2750 if (outputDir == CascadeDir::North || outputDir == CascadeDir::West) {
2752 "output direction of cascade must be South or East on ")
2753 << stringifyAIEArch(t.getTargetArch());
2756 return emitOpError(
"cascade not supported in ")
2757 << stringifyAIEArch(t.getTargetArch());
2766LogicalResult PutCascadeOp::verify() {
2768 Type type = getCascadeValue().getType();
2769 DataLayout dataLayout = DataLayout::closest(*
this);
2770 auto bits = dataLayout.getTypeSizeInBits(type);
2771 auto archbits = targetModel.getAccumulatorCascadeSize();
2772 if (bits != archbits)
2773 return emitOpError(
"type must match architecture cascade width (")
2774 << archbits <<
" bits in "
2775 << stringifyAIEArch(targetModel.getTargetArch()) <<
")";
2783LogicalResult GetCascadeOp::verify() {
2785 Type type = getCascadeValue().getType();
2786 DataLayout dataLayout = DataLayout::closest(*
this);
2787 auto bits = dataLayout.getTypeSizeInBits(type);
2788 if (isa<AIE1TargetModel>(targetModel)) {
2790 return emitOpError(
"must be a 384-bit type");
2791 }
else if (isa<AIE2TargetModel>(targetModel)) {
2793 return emitOpError(
"must be a 512-bit type");
2795 return emitOpError(
"cascade not supported in ")
2796 << stringifyAIEArch(targetModel.getTargetArch());
2809using PortKey = std::tuple<int, int, int, int>;
2816std::optional<PortKey> tryGetPortKey(Value tileValue, WireBundle bundle,
2818 auto tile = llvm::dyn_cast_or_null<TileLike>(tileValue.getDefiningOp());
2820 return std::nullopt;
2821 std::optional<int> col = tile.tryGetCol();
2822 std::optional<int> row = tile.tryGetRow();
2824 return std::nullopt;
2825 return PortKey{*col, *row,
static_cast<int>(bundle), channel};
2828std::string to_string(
const PortKey &key) {
2829 auto [col, row, bundle, channel] = key;
2830 return "(" + std::to_string(col) +
", " + std::to_string(row) +
") " +
2831 stringifyWireBundle(
static_cast<WireBundle
>(bundle)).str() +
" : " +
2832 std::to_string(channel);
2840LogicalResult verifyNoDuplicateFlows(DeviceOp device) {
2841 std::map<std::pair<PortKey, PortKey>, FlowOp> flowSeen;
2842 for (FlowOp flow : device.getOps<FlowOp>()) {
2843 std::optional<PortKey> src = tryGetPortKey(
2844 flow.getSource(), flow.getSourceBundle(), flow.getSourceChannel());
2845 std::optional<PortKey> dst = tryGetPortKey(
2846 flow.getDest(), flow.getDestBundle(), flow.getDestChannel());
2849 auto [it, inserted] = flowSeen.try_emplace({*src, *dst}, flow);
2851 InFlightDiagnostic diag = flow.emitOpError()
2852 <<
"duplicates an earlier flow; "
2853 << to_string(*src) <<
" -> " << to_string(*dst)
2854 <<
" is already declared";
2855 diag.attachNote(it->second.getLoc()) <<
"the other flow is here";
2867LogicalResult verifyNoDuplicatePacketFlows(DeviceOp device) {
2868 using PacketFlowKey =
2869 std::tuple<int, int, std::vector<PortKey>, std::vector<PortKey>>;
2870 std::map<PacketFlowKey, PacketFlowOp> packetFlowSeen;
2871 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
2872 Region &body = packetFlow.getPorts();
2877 std::vector<PortKey> sources, dests;
2878 bool comparable =
true;
2879 for (Operation &op : body.front()) {
2880 std::vector<PortKey> *endpoints =
nullptr;
2881 std::optional<PortKey> port;
2882 if (
auto source = dyn_cast<PacketSourceOp>(op)) {
2883 endpoints = &sources;
2884 port = tryGetPortKey(source.getTile(), source.getBundle(),
2885 source.getChannel());
2886 }
else if (
auto dest = dyn_cast<PacketDestOp>(op)) {
2889 tryGetPortKey(dest.getTile(), dest.getBundle(), dest.getChannel());
2898 endpoints->push_back(*port);
2900 if (!comparable || sources.empty() || dests.empty())
2902 llvm::sort(sources);
2905 llvm::Log2_32_Ceil(device.getTargetModel().getMaxPacketId() + 1);
2906 int mask = packetFlow.getMask().value_or((1 << idBits) - 1);
2907 auto [it, inserted] = packetFlowSeen.try_emplace(
2908 {packetFlow.IDInt(), mask, std::move(sources), std::move(dests)},
2911 InFlightDiagnostic diag =
2912 packetFlow.emitOpError()
2913 <<
"duplicates an earlier packet flow; ID " << packetFlow.IDInt()
2914 <<
" under mask 0x" << llvm::utohexstr(mask)
2915 <<
" is already declared between the same sources and destinations";
2916 diag.attachNote(it->second.getLoc()) <<
"the other packet flow is here";
2926LogicalResult verifyNoSharedCircuitPorts(DeviceOp device) {
2927 std::map<PortKey, FlowOp> circuitSources, circuitDests;
2928 for (FlowOp flow : device.getOps<FlowOp>()) {
2929 std::optional<PortKey> src = tryGetPortKey(
2930 flow.getSource(), flow.getSourceBundle(), flow.getSourceChannel());
2931 std::optional<PortKey> dst = tryGetPortKey(
2932 flow.getDest(), flow.getDestBundle(), flow.getDestChannel());
2934 circuitSources.try_emplace(*src, flow);
2937 auto [it, inserted] = circuitDests.try_emplace(*dst, flow);
2940 InFlightDiagnostic diag =
2942 <<
"ends at " << to_string(*dst)
2943 <<
", where another circuit flow ends; a port takes one circuit";
2944 diag.attachNote(it->second.getLoc()) <<
"the other circuit flow is here";
2947 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
2948 Region &body = packetFlow.getPorts();
2951 for (Operation &op : body.front()) {
2952 std::optional<PortKey> port;
2953 std::map<PortKey, FlowOp> *circuits =
nullptr;
2955 if (
auto source = dyn_cast<PacketSourceOp>(op)) {
2956 port = tryGetPortKey(source.getTile(), source.getBundle(),
2957 source.getChannel());
2958 circuits = &circuitSources;
2960 }
else if (
auto dest = dyn_cast<PacketDestOp>(op)) {
2962 tryGetPortKey(dest.getTile(), dest.getBundle(), dest.getChannel());
2963 circuits = &circuitDests;
2968 auto it = circuits->find(*port);
2969 if (it == circuits->end())
2971 InFlightDiagnostic diag =
2972 packetFlow.emitOpError()
2973 << role <<
" at " << to_string(*port) <<
", where a circuit flow "
2974 << role <<
"; a port carries either one circuit or packets";
2975 diag.attachNote(it->second.getLoc()) <<
"the circuit flow is here";
2982LogicalResult verifyNoSharedCircuitViaEgresses(DeviceOp device) {
2983 enum class ClaimKind { FlowDest, PacketDest,
Connect, MasterSet, Via };
2987 size_t viaIndex = 0;
2989 std::map<PortKey, Claim> claimedEgresses;
2991 auto addInterconnectClaims = [&](
auto interconnect) {
2992 Value tile = interconnect.getTile();
2993 for (ConnectOp connect :
2994 interconnect.getConnections().template getOps<ConnectOp>()) {
2995 Port dest = connect.destPort();
2996 if (std::optional<PortKey> port =
2997 tryGetPortKey(tile, dest.bundle, dest.channel))
2998 claimedEgresses.try_emplace(*port, Claim{connect, ClaimKind::Connect});
3000 for (MasterSetOp masterSet :
3001 interconnect.getConnections().template getOps<MasterSetOp>()) {
3002 Port dest = masterSet.destPort();
3003 if (std::optional<PortKey> port =
3004 tryGetPortKey(tile, dest.bundle, dest.channel))
3005 claimedEgresses.try_emplace(*port,
3006 Claim{masterSet, ClaimKind::MasterSet});
3009 for (SwitchboxOp switchbox : device.getOps<SwitchboxOp>())
3010 addInterconnectClaims(switchbox);
3011 for (ShimMuxOp shimMux : device.getOps<ShimMuxOp>())
3012 addInterconnectClaims(shimMux);
3013 for (FlowOp flow : device.getOps<FlowOp>()) {
3014 if (std::optional<PortKey> dest = tryGetPortKey(
3015 flow.getDest(), flow.getDestBundle(), flow.getDestChannel()))
3016 claimedEgresses.try_emplace(*dest, Claim{flow, ClaimKind::FlowDest});
3018 for (PacketFlowOp packetFlow : device.getOps<PacketFlowOp>()) {
3019 for (PacketDestOp dest : packetFlow.getPorts().getOps<PacketDestOp>()) {
3020 if (std::optional<PortKey> port = tryGetPortKey(
3021 dest.getTile(), dest.getBundle(), dest.getChannel()))
3022 claimedEgresses.try_emplace(*port, Claim{dest, ClaimKind::PacketDest});
3026 WalkResult result = device.walk([&](FlowOp flow) {
3027 ArrayRef<int32_t> bundles =
3028 flow.getViaEgressBundlesAttr()
3029 ? flow.getViaEgressBundlesAttr().asArrayRef()
3030 : ArrayRef<int32_t>();
3031 ArrayRef<int32_t> channels =
3032 flow.getViaEgressChannelsAttr()
3033 ? flow.getViaEgressChannelsAttr().asArrayRef()
3034 : ArrayRef<int32_t>();
3035 if (bundles.size() != flow.getVias().size() ||
3036 channels.size() != flow.getVias().size()) {
3037 return WalkResult::advance();
3039 for (
auto [index, via] : llvm::enumerate(flow.getVias())) {
3040 std::optional<PortKey> egress = tryGetPortKey(
3041 via,
static_cast<WireBundle
>(bundles[index]), channels[index]);
3045 Claim viaClaim{flow, ClaimKind::Via, index};
3046 auto [it, inserted] = claimedEgresses.try_emplace(*egress, viaClaim);
3049 if (index + 1 == flow.getVias().size() && it->second.op == flow &&
3050 it->second.kind == ClaimKind::FlowDest) {
3051 it->second = viaClaim;
3054 InFlightDiagnostic diag = flow.emitOpError()
3055 <<
"via " << index <<
" claims circuit egress "
3056 << to_string(*egress)
3057 <<
", which another operation already claims";
3058 Diagnostic ¬e = diag.attachNote(it->second.op->getLoc());
3059 switch (it->second.kind) {
3060 case ClaimKind::FlowDest:
3061 note <<
"a circuit flow ends at the same port";
3063 case ClaimKind::PacketDest:
3064 note <<
"a packet flow ends at the same port";
3066 case ClaimKind::Connect:
3067 note <<
"a fixed connect drives the same port";
3069 case ClaimKind::MasterSet:
3070 note <<
"a fixed master set drives the same port";
3072 case ClaimKind::Via:
3073 note <<
"via " << it->second.viaIndex
3074 <<
" claims the same circuit egress";
3077 return WalkResult::interrupt();
3079 return WalkResult::advance();
3081 return failure(result.wasInterrupted());
3089LogicalResult verifyNoDuplicateNames(Operation *symbolTableOp) {
3090 if (symbolTableOp->getNumRegions() == 0 ||
3091 symbolTableOp->getRegion(0).empty()) {
3094 DenseMap<StringAttr, Operation *> nameSeen;
3095 for (Operation &op : symbolTableOp->getRegion(0).front()) {
3096 auto name = op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
3100 auto [it, inserted] = nameSeen.try_emplace(name, &op);
3106 if (isa<SymbolOpInterface>(op) && isa<SymbolOpInterface>(it->second)) {
3109 InFlightDiagnostic diag = op.emitError() <<
"redefinition of symbol named '"
3110 << name.getValue() <<
"'";
3111 diag.attachNote(it->second->getLoc())
3112 <<
"see existing symbol definition here";
3120LogicalResult DeviceOp::verify() {
3129 DenseMap<TileID, CoreOp> coreAtTile;
3130 for (CoreOp core : getOps<CoreOp>()) {
3132 llvm::dyn_cast_or_null<TileLike>(core.getTile().getDefiningOp());
3135 std::optional<int> col = tile.tryGetCol();
3136 std::optional<int> row = tile.tryGetRow();
3140 auto [it, inserted] = coreAtTile.try_emplace(
id, core);
3142 InFlightDiagnostic diag =
3144 <<
"tile (" << *col <<
", " << *row
3145 <<
") already has a core; each compute tile can host only one core";
3146 diag.attachNote(it->second.getLoc()) <<
"the other core is here";
3153 if (failed(verifyNoDuplicateFlows(*
this)))
3155 if (failed(verifyNoDuplicatePacketFlows(*
this)))
3157 if (failed(verifyNoSharedCircuitPorts(*
this)))
3159 if (failed(verifyNoSharedCircuitViaEgresses(*
this))) {
3163 if (failed(verifyNoDuplicateNames(*
this))) {
3170LogicalResult DeviceOp::verifyRegions() {
3175 WalkResult result = getOperation()->walk<WalkOrder::PreOrder>(
3176 [&](Operation *op) -> WalkResult {
3177 if (op != getOperation() && isa<DeviceOp>(op))
3178 return WalkResult::skip();
3179 if (
auto user = dyn_cast<NamedOpUserOpInterface>(op))
3180 if (failed(user.verifyNamedOpUses(names)))
3181 return WalkResult::interrupt();
3182 return WalkResult::advance();
3184 return failure(result.wasInterrupted());
3191xilinx::AIE::DeviceOp DeviceOp::getForSymbolInModule(mlir::ModuleOp module,
3192 llvm::StringRef symbol) {
3194 if (symbol.empty()) {
3198 Operation *maybeDeviceOp = mlir::SymbolTable::lookupSymbolIn(module, symbol);
3199 if (!maybeDeviceOp) {
3202 deviceOp = llvm::dyn_cast<DeviceOp>(maybeDeviceOp);
3206xilinx::AIE::DeviceOp
3207DeviceOp::getForSymbolInModuleOrError(mlir::ModuleOp module,
3208 llvm::StringRef symbol) {
3209 DeviceOp deviceOp = getForSymbolInModule(module, symbol);
3211 if (!symbol.empty()) {
3212 module.emitError("No such device: ") << symbol;
3214 module.emitError("No 'main' device in module");
3224TileOp TileElement::tryGetTileOp() {
3225 auto element = cast<TileElement>(this->getOperation());
3226 return dyn_cast_or_null<TileOp>(element.getTile().getDefiningOp());
3229TileOp TileElement::getTileOp() {
3230 if (
auto tileOp = tryGetTileOp())
3232 llvm::report_fatal_error(
"Calling getTileOp requires TileOp.");
3239LogicalResult LogicalTileOp::verify() {
3241 *
this, (*this)->getAttrOfType<PacketInfoAttr>(
"controller_id"),
3245 int columns = targetModel.columns();
3246 int rows = targetModel.rows();
3249 if (
auto col = getCol()) {
3250 if (*col >= columns)
3251 return emitOpError(
"column index (")
3253 <<
") must be less than the number of columns in the device ("
3256 if (
auto row = getRow()) {
3258 return emitOpError(
"row index (")
3259 << *row <<
") must be less than the number of rows in the device ("
3264 AIETileType tileType = getTileType();
3265 bool tileTypeExists =
false;
3266 for (
int col = 0; col < columns && !tileTypeExists; col++) {
3267 for (
int row = 0; row < rows && !tileTypeExists; row++) {
3268 if (targetModel.getTileType(col, row) == tileType)
3269 tileTypeExists =
true;
3272 if (!tileTypeExists) {
3273 return emitOpError(
"tile type '")
3274 << stringifyAIETileType(tileType)
3275 <<
"' does not exist on the target device";
3280 if (
auto col = tryGetCol()) {
3281 if (
auto row = tryGetRow()) {
3282 if (targetModel.getTileType(*col, *row) != tileType) {
3283 return emitOpError(
"declared logical tile type does not match "
3284 "the tile type at coordinates (")
3285 << *col <<
", " << *row <<
")";
3293TileID LogicalTileOp::getCanonicalTileID() {
3297 std::optional<int32_t> col = getCol();
3298 std::optional<int32_t> row = getRow();
3299 if (col.has_value() && row.has_value())
3300 return {*col, *row};
3303 AIETileType tileType = getTileType();
3304 for (
int col = 0; col < targetModel.columns(); col++) {
3305 for (
int row = 0; row < targetModel.rows(); row++) {
3306 if (targetModel.getTileType(col, row) == tileType) {
3311 llvm_unreachable(
"No tile of matching tile type found in AIE device");
3314size_t LogicalTileOp::getNumSourceConnections(WireBundle bundle) {
3316 TileID tile = getCanonicalTileID();
3318 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
3320 if (isShimNOCorPLTile())
3321 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
3323 return targetModel.getNumDestSwitchboxConnections(tile.col, tile.row,
3329size_t LogicalTileOp::getNumDestConnections(WireBundle bundle) {
3331 TileID tile = getCanonicalTileID();
3333 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
3335 if (isShimNOCorPLTile())
3336 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
3338 return targetModel.getNumSourceSwitchboxConnections(tile.col, tile.row,
3344std::optional<int> LogicalTileOp::tryGetCol() {
3345 if (
auto col = getCol())
3347 return std::nullopt;
3350std::optional<int> LogicalTileOp::tryGetRow() {
3351 if (
auto row = getRow())
3353 return std::nullopt;
3360ParseResult LogicalTileOp::parse(OpAsmParser &parser, OperationState &result) {
3361 AIETileType tileType;
3362 if (parser.parseLess())
3365 StringRef tileTypeStr;
3366 if (parser.parseKeyword(&tileTypeStr))
3369 auto tileTypeOpt = symbolizeAIETileType(tileTypeStr);
3371 return parser.emitError(parser.getCurrentLocation(),
3372 "unknown logical tile type: ")
3374 tileType = *tileTypeOpt;
3376 if (parser.parseGreater())
3379 if (parser.parseLParen())
3382 std::optional<int32_t> col;
3383 if (succeeded(parser.parseOptionalQuestion())) {
3387 if (parser.parseInteger(colVal))
3392 if (parser.parseComma())
3395 std::optional<int32_t> row;
3396 if (succeeded(parser.parseOptionalQuestion())) {
3400 if (parser.parseInteger(rowVal))
3405 if (parser.parseRParen())
3409 if (parser.parseOptionalAttrDict(result.attributes))
3413 result.getOrAddProperties<LogicalTileOp::Properties>().tile_type =
3414 AIETileTypeAttr::get(parser.getContext(), tileType);
3416 result.getOrAddProperties<LogicalTileOp::Properties>().col =
3417 parser.getBuilder().getI32IntegerAttr(*col);
3419 result.getOrAddProperties<LogicalTileOp::Properties>().row =
3420 parser.getBuilder().getI32IntegerAttr(*row);
3423 result.addTypes(parser.getBuilder().getIndexType());
3428void LogicalTileOp::print(OpAsmPrinter &printer) {
3429 printer <<
"<" << stringifyAIETileType(getTileType()) <<
">";
3432 if (
auto col = getCol())
3437 if (
auto row = getRow())
3443 SmallVector<StringRef, 3> elidedAttrs = {
"tile_type",
"col",
"row"};
3444 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
3451LogicalResult TileOp::verify() {
3453 *
this, (*this)->getAttrOfType<PacketInfoAttr>(
"controller_id"),
3457 int columns = targetModel.columns();
3458 int rows = targetModel.rows();
3459 if (colIndex() >= columns)
3460 return emitOpError(
"column index (")
3462 <<
") must be less than the number of columns in the device ("
3464 if (rowIndex() >= rows)
3465 return emitOpError(
"row index (")
3467 <<
") must be less than the number of rows in the device (" << rows
3470 auto users = getResult().getUsers();
3472 for (
auto *user : users) {
3473 if (llvm::isa<SwitchboxOp>(*user)) {
3475 return emitOpError(
"can only have one switchbox");
3483size_t TileOp::getNumSourceConnections(WireBundle bundle) {
3485 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
3489 if (isShimNOCorPLTile())
3490 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
3492 return targetModel.getNumDestSwitchboxConnections(getCol(), getRow(),
3498size_t TileOp::getNumDestConnections(WireBundle bundle) {
3500 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
3504 if (isShimNOCorPLTile())
3505 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
3507 return targetModel.getNumSourceSwitchboxConnections(getCol(), getRow(),
3513std::optional<int> TileOp::tryGetCol() {
return getCol(); }
3514std::optional<int> TileOp::tryGetRow() {
return getRow(); }
3516AIETileType TileOp::getTileType() {
3518 return targetModel.getTileType(getCol(), getRow());
3521static bool isLegalTileConnection(TileOp tile,
3523 MasterSetOp masterOp, PacketRulesOp slaveOp) {
3524 auto srcBundle = slaveOp.sourcePort().bundle;
3525 auto srcChan = slaveOp.sourcePort().channel;
3526 auto dstBundle = masterOp.destPort().bundle;
3527 auto dstChan = masterOp.destPort().channel;
3529 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
3532static bool isLegalTileConnection(TileOp tile,
3534 ConnectOp connectOp) {
3535 auto srcBundle = connectOp.getSourceBundle();
3536 auto srcChan = connectOp.getSourceChannel();
3537 auto dstBundle = connectOp.getDestBundle();
3538 auto dstChan = connectOp.getDestChannel();
3540 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
3543TileOp TileOp::getOrCreate(mlir::OpBuilder builder, DeviceOp device,
int col,
3544 int row, std::optional<mlir::Location> loc) {
3545 TileOp tile =
nullptr;
3547 for (
auto t : device.getOps<AIE::TileOp>()) {
3548 if (t.getRow() == row && t.getCol() == col) {
3555 OpBuilder::InsertionGuard guard(builder);
3556 mlir::Block &device_start_block = *device.getBodyRegion().begin();
3557 builder.setInsertionPointToStart(&device_start_block);
3558 tile = TileOp::create(builder, loc.value_or(device.getLoc()),
3559 builder.getIndexType(), col, row);
3568LogicalResult ShimMuxOp::verify() {
3573 if (!isa<TileOp>(getTile().getDefiningOp()))
3576 Region &body = getConnections();
3577 DenseSet<Port> destset;
3579 return emitOpError(
"should have non-empty body");
3581 for (
auto &ops : body.front()) {
3582 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
3583 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
3584 if (destset.count(dest))
3585 return connectOp.emitOpError(
"targets same destination ")
3586 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
3587 <<
" as another connect operation";
3588 destset.insert(dest);
3589 }
else if (isa<EndOp>(ops)) {
3592 return ops.emitOpError(
"cannot be contained in a Switchbox op");
3598size_t ShimMuxOp::getNumSourceConnections(WireBundle bundle) {
3599 auto tile = getTileOp();
3602 tile.getRow(), bundle);
3605size_t ShimMuxOp::getNumDestConnections(WireBundle bundle) {
3606 auto tile = getTileOp();
3616LogicalResult ShimDMAOp::verify() {
3623TileOp ShimDMAOp::getTileOp() {
3624 return cast<TileElement>(this->getOperation()).getTileOp();
3627LogicalResult PacketRulesOp::verify() {
3628 if (Region &body = getRules(); body.empty())
3629 return emitOpError(
"should have non-empty body");
3633LogicalResult MasterSetOp::verify() {
3635 for (
auto val : getAmsels()) {
3636 auto amsel = dyn_cast_if_present<AMSelOp>(val.getDefiningOp());
3638 return emitOpError(
"amsel operand must be produced by an 'aie.amsel' op");
3639 if (arbiter != -1 && arbiter != amsel.arbiterIndex())
3640 return emitOpError(
"a master port can only be tied to one arbiter");
3641 arbiter = amsel.arbiterIndex();
3646bool MasterSetOp::keepsPktHeader() {
3648 (*this)->getParentOfType<SwitchboxOp>().getTileOp().getTileID(),
3649 destPort(), getKeepPktHeader());
3653 std::optional<bool> keep) {
3656 return master.bundle != WireBundle::DMA &&
3657 (tile.row != 0 || master.bundle != WireBundle::South);
3662static LogicalResult verifyFlowEndpoint(Operation *op, Value tile,
Port port,
3664 if (port.bundle != WireBundle::Core && port.bundle != WireBundle::DMA)
3666 auto tileOp = dyn_cast_or_null<TileOp>(tile.getDefiningOp());
3669 size_t channels = isSource ? tileOp.getNumDestConnections(port.bundle)
3670 : tileOp.getNumSourceConnections(port.bundle);
3671 if (
static_cast<size_t>(port.channel) < channels)
3673 return op->emitOpError() << (isSource ?
"source " :
"destination ")
3674 << stringifyWireBundle(port.bundle) <<
":"
3675 << port.channel <<
" does not exist: tile ("
3676 << tileOp.getCol() <<
", " << tileOp.getRow()
3677 <<
") has " << channels <<
" "
3678 << stringifyWireBundle(port.bundle) <<
" port"
3679 << (channels == 1 ?
"" :
"s")
3680 << (isSource ?
" into" :
" out of")
3681 <<
" its stream switch";
3684static LogicalResult verifyFlowVias(Operation *op, OperandRange vias,
3685 DenseI32ArrayAttr ingressBundles,
3686 DenseI32ArrayAttr ingressChannels,
3687 DenseI32ArrayAttr egressBundles,
3688 DenseI32ArrayAttr egressChannels) {
3689 size_t numVias = vias.size();
3690 DenseI32ArrayAttr arrays[] = {ingressBundles, ingressChannels, egressBundles,
3692 for (DenseI32ArrayAttr array : arrays) {
3693 size_t size = array ? array.size() : 0;
3694 if (size != numVias) {
3695 return op->emitOpError(
"has ")
3696 << numVias <<
" via tile(s) but a via port array of size " << size;
3703 for (
auto [index, via] : llvm::enumerate(vias)) {
3704 if (!isa_and_nonnull<TileLike>(via.getDefiningOp())) {
3705 return op->emitOpError(
"has via operand at index ")
3706 << index <<
" that is not defined by a tile-like operation";
3710 auto verifyBundles = [&](DenseI32ArrayAttr bundles,
3711 StringRef direction) -> LogicalResult {
3712 for (
auto [index, bundle] : llvm::enumerate(bundles.asArrayRef())) {
3714 bundle >
static_cast<int32_t
>(getMaxEnumValForWireBundle())) {
3715 return op->emitOpError(
"has invalid via ")
3716 << direction <<
" bundle " << bundle <<
" at index " << index;
3721 auto verifyChannels = [&](DenseI32ArrayAttr channels,
3722 StringRef direction) -> LogicalResult {
3723 for (
auto [index, channel] : llvm::enumerate(channels.asArrayRef())) {
3725 return op->emitOpError(
"has negative via ")
3726 << direction <<
" channel " << channel <<
" at index " << index;
3731 if (failed(verifyBundles(ingressBundles,
"ingress")) ||
3732 failed(verifyChannels(ingressChannels,
"ingress")) ||
3733 failed(verifyBundles(egressBundles,
"egress")) ||
3734 failed(verifyChannels(egressChannels,
"egress"))) {
3740LogicalResult FlowOp::verify() {
3741 if (failed(verifyFlowEndpoint(*
this, getSource(),
3742 {getSourceBundle(), sourceIndex()},
true)))
3744 if (failed(verifyFlowEndpoint(*
this, getDest(),
3745 {getDestBundle(), destIndex()},
false))) {
3749 return verifyFlowVias(*
this, getVias(), getViaIngressBundlesAttr(),
3750 getViaIngressChannelsAttr(), getViaEgressBundlesAttr(),
3751 getViaEgressChannelsAttr());
3754LogicalResult PacketSourceOp::verify() {
3755 return verifyFlowEndpoint(*
this, getTile(), port(),
true);
3758LogicalResult PacketDestOp::verify() {
3759 return verifyFlowEndpoint(*
this, getTile(), port(),
false);
3762LogicalResult PacketFlowOp::verify() {
3763 Region &body = getPorts();
3765 return emitOpError(
"should have non-empty body");
3767 int numSources = 0, numDests = 0;
3768 for (
auto &ops : body.front()) {
3769 if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
3770 return ops.emitOpError(
"cannot be contained in a PacketFlow op");
3771 if (isa<PacketSourceOp>(ops))
3773 if (isa<PacketDestOp>(ops))
3778 return emitOpError(
"must have at least one aie.packet_source");
3780 return emitOpError(
"must have at least one aie.packet_dest");
3781 if (!getVias().empty() && numSources != 1) {
3783 "with via waypoints must have exactly one aie.packet_source");
3785 if (!getVias().empty() && numDests != 1) {
3787 "with via waypoints must have exactly one aie.packet_dest");
3790 uint8_t
id = getID();
3791 if (uint32_t maxId =
getTargetModel(getOperation()).getMaxPacketId();
3793 return emitOpError(
"packet id ")
3794 <<
static_cast<int>(id) <<
" exceeds the maximum of " << maxId;
3798 if (std::optional<uint8_t> mask = getMask()) {
3799 if ((
id & *mask) !=
id) {
3800 return emitOpError(
"has ID 0x")
3801 << llvm::utohexstr(
id) <<
" outside mask 0x"
3802 << llvm::utohexstr(*mask) <<
", which no packet can match";
3806 return verifyFlowVias(*
this, getVias(), getViaIngressBundlesAttr(),
3807 getViaIngressChannelsAttr(), getViaEgressBundlesAttr(),
3808 getViaEgressChannelsAttr());
3816static uint32_t channelsOfOwnTile(TileOp tile, DMAChannelDir direction,
3818 int col = tile.getCol(), row = tile.getRow();
3819 if (direction == DMAChannelDir::MM2S)
3825LogicalResult DmaPushQueueOp::verify() {
3826 if (getOutOfOrder() && getDirection() != DMAChannelDir::S2MM)
3827 return emitOpError(
"out_of_order is only supported on S2MM channels");
3828 if (getBdId().has_value() == getBdTag().has_value())
3829 return emitOpError(
"names the descriptor it pushes either by bd_id or, "
3830 "before aie-assign-bd-ids runs, by bd_tag");
3831 auto core = (*this)->getParentOfType<CoreOp>();
3833 if (
auto tile = core.getTile().getDefiningOp<TileOp>()) {
3834 uint32_t channels = channelsOfOwnTile(tile, getDirection(), targetModel);
3835 if (getChannel() >=
static_cast<int32_t
>(channels))
3836 return emitOpError(
"pushes onto ")
3837 << stringifyDMAChannelDir(getDirection()) <<
" channel "
3838 << getChannel() <<
", but tile (" << tile.getCol() <<
", "
3839 << tile.getRow() <<
") has " << channels;
3841 uint32_t maxRepeat = targetModel.getMaxRepeatCount();
3842 if (getRepeatCount() < 0 ||
3843 static_cast<uint32_t
>(getRepeatCount()) > maxRepeat)
3844 return emitOpError(
"repeat_count must be in [0, ") << maxRepeat <<
"]";
3848LogicalResult DmaPollForQueueSpaceOp::verify() {
3849 auto core = (*this)->getParentOfType<CoreOp>();
3851 if (
auto tile = core.getTile().getDefiningOp<TileOp>()) {
3852 uint32_t channels = channelsOfOwnTile(tile, getDirection(), targetModel);
3853 if (getChannel() >=
static_cast<int32_t
>(channels))
3854 return emitOpError(
"waits on ")
3855 << stringifyDMAChannelDir(getDirection()) <<
" channel "
3856 << getChannel() <<
", but tile (" << tile.getCol() <<
", "
3857 << tile.getRow() <<
") has " << channels;
3859 if (targetModel.getDmaTaskQueueDepth() == 0)
3860 return emitOpError(
"waits on a task queue, which this device has none of");
3864LogicalResult CoreOp::verify() {
3865 if (getBody().empty())
3866 return emitOpError(
"should have non-empty body");
3873 "When `elf_file` attribute is specified, core body must be empty "
3874 "(consist of exactly one `aie.end` op).");
3877 if (getLinkWith() && getLinkFiles())
3879 "cannot specify both 'link_with' (deprecated) and 'link_files' "
3880 "on the same core; run aie-assign-core-link-files to migrate");
3881 if (getLinkWith() && getLinkMergeFiles())
3883 "cannot specify both 'link_with' (deprecated) and 'link_merge_files' "
3884 "on the same core; run aie-assign-core-link-files to migrate");
3887 if (
auto linkFiles = getLinkFiles())
3888 if (
auto mergeFiles = getLinkMergeFiles()) {
3889 llvm::SmallSet<StringRef, 8> linked;
3890 for (
auto f : linkFiles->getAsRange<StringAttr>())
3891 linked.insert(f.getValue());
3892 for (
auto f : mergeFiles->getAsRange<StringAttr>())
3893 if (linked.count(f.getValue()))
3894 return emitOpError(
"artifact '")
3896 <<
"' appears in both 'link_files' and 'link_merge_files'; an "
3897 "artifact must be either merged or linked, not both";
3901 if (
auto measured = getMeasuredDataSize())
3902 if (
auto declared = getDataSize(); declared && *declared < *measured)
3903 return emitOpError(
"data_size ")
3904 << *declared <<
" is smaller than the " << *measured
3905 <<
" bytes this core's linked sections occupy";
3909 int64_t localMem = targetModel.getLocalMemorySize();
3910 auto validAlignment = [localMem](int64_t alignment) {
3911 return alignment > 0 && alignment <= localMem &&
3912 llvm::isPowerOf2_64(alignment);
3914 if (
auto alignment = getMeasuredDataAlignment();
3915 alignment && !validAlignment(*alignment))
3916 return emitOpError(
"measured_data_alignment must be a power of two between "
3918 << localMem <<
" bytes";
3919 if (
auto alignments = getMeasuredBankAlignments())
3920 if (!llvm::all_of(*alignments, validAlignment))
3921 return emitOpError(
"measured_bank_alignments must contain only powers of "
3922 "two between 1 and ")
3923 << localMem <<
" bytes";
3925 auto tile = dyn_cast_if_present<TileOp>(getTile().getDefiningOp());
3927 tile ? targetModel.getNumBanks(tile.getCol(), tile.getRow()) : 0;
3929 numBanks > 0 ? targetModel.getLocalMemorySize() / numBanks : 0;
3930 if (
auto bank = getStackBank()) {
3931 if (tile && *bank >= numBanks)
3932 return emitOpError(
"stack_bank ")
3933 << *bank <<
" does not exist; this tile has " << numBanks
3936 if (bankSize > 0 && stackRun.
size > bankSize)
3937 return emitOpError(
"stack_bank pins a ")
3938 << stackRun.
size <<
"-byte stack to bank " << *bank
3939 <<
", which holds " << bankSize
3940 <<
" bytes; omit stack_bank and stack_address for legacy "
3942 if (getStackAddress() && bankSize > 0 && stackRun.
start / bankSize != *bank)
3943 return emitOpError(
"stack_address 0x")
3944 << llvm::utohexstr(stackRun.
start) <<
" lies in bank "
3945 << stackRun.
start / bankSize <<
", but stack_bank requests bank "
3947 if (getStackAddress() && bankSize > 0 &&
3948 stackRun.
end() > (*bank + 1) * bankSize)
3949 return emitOpError(
"a ")
3950 << stackRun.
size <<
"-byte stack at 0x"
3951 << llvm::utohexstr(stackRun.
start) <<
" runs past stack_bank "
3952 << *bank <<
" (ending at 0x"
3953 << llvm::utohexstr((*bank + 1) * bankSize) <<
")";
3958 if (stackRun.
size >= localMem)
3959 return emitOpError(getStackSize() ?
"stack_size "
3960 :
"measured stack requirement ")
3962 <<
" leaves no local memory for this tile's buffers (" << localMem
3964 if (stackRun.
end() > localMem)
3965 return emitOpError(
"a ") << stackRun.
size <<
"-byte stack at 0x"
3966 << llvm::utohexstr(stackRun.
start)
3967 <<
" runs past this tile's local memory ("
3968 << localMem <<
" bytes total)";
3969 if (getStackAddress() &&
3970 stackRun.
start % targetModel.getCoreStackAlignment() != 0)
3971 return emitOpError(
"stack_address must be aligned to ")
3972 << targetModel.getCoreStackAlignment()
3973 <<
" bytes for this target's stack ABI";
3977bool CoreOp::isEmpty() {
3978 Region &body = getBody();
3980 return (body.hasOneBlock() && body.front().getOperations().size() == 1 &&
3981 llvm::isa<AIE::EndOp>(body.front().front()));
3984TileOp CoreOp::getTileOp() {
3985 return cast<TileElement>(this->getOperation()).getTileOp();
3988uint32_t CoreOp::getEffectiveStackSize() {
3989 if (
auto declared = getStackSize())
3992 uint32_t fallback = targetModel.getDefaultCoreStackSize();
3993 if (
auto measured = getMeasuredStackSize())
3994 return std::max<uint32_t>(
3996 llvm::alignTo(*measured, targetModel.getCoreStackAlignment()));
4001 return {getStackAddress().value_or(0), getEffectiveStackSize()};
4008int64_t BufferOp::getAllocationSize() {
4009 auto type = llvm::cast<MemRefType>(getType());
4010 DataLayout dataLayout = DataLayout::closest(getOperation());
4011 return type.getNumElements() * dataLayout.getTypeSize(type.getElementType());
4014LogicalResult BufferOp::verify() {
4015 if (UsesAreAccessible::verifyTrait(*this).failed()) {
4021 auto tile = dyn_cast_if_present<TileOp>(getTile().getDefiningOp());
4028 if (getCoreData()) {
4029 for (Operation *user : tile.getResult().getUsers()) {
4030 auto other = dyn_cast<BufferOp>(user);
4031 if (other && other != *
this && other.getCoreData()) {
4032 return emitOpError(
"is a second core_data buffer on tile (")
4033 << tile.getCol() <<
", " << tile.getRow()
4034 <<
"); the core's data region is one extent";
4042 std::optional<int32_t> address = getAddress();
4043 std::optional<int32_t> memBank = getMemBank();
4044 if (address && memBank && getAllocationSize() > 0) {
4046 int64_t numBanks = targetModel.getNumBanks(tile.getCol(), tile.getRow());
4047 int64_t memSize = tile.isMemTile() ? targetModel.getMemTileSize()
4048 : targetModel.getLocalMemorySize();
4050 int64_t bankSize = memSize / numBanks;
4051 int64_t bankOfAddress = *address / bankSize;
4052 if (bankOfAddress != *memBank) {
4053 return emitOpError(
"address 0x")
4054 << llvm::utohexstr(*address) <<
" lies in bank " << bankOfAddress
4055 <<
", but mem_bank requests bank " << *memBank;
4065 if (
auto buf = dyn_cast<BufferOp>(bufOp)) {
4066 std::optional<int32_t> address = buf.getAddress();
4067 assert(address.has_value() &&
"buffer must have address assigned");
4070 if (isa_and_nonnull<ExternalBufferOp>(bufOp))
4071 llvm::report_fatal_error(
4072 "External buffer addresses are assigned at runtime.");
4073 llvm::report_fatal_error(
"unknown buffer type");
4077 DenseMap<TileID, Operation *> &tiles) {
4078 for (
auto tile : device.getOps<TileOp>()) {
4079 tiles[tile.getTileID()] = tile;
4085 DenseMap<Operation *, SmallVector<BufferOp, 4>> &buffers) {
4086 for (BufferOp buffer : device.getOps<BufferOp>()) {
4087 Operation *tileOp = buffer.getTile().getDefiningOp();
4088 buffers[tileOp].push_back(buffer);
4092static void printBufferInitialValue(OpAsmPrinter &p, BufferOp op, Type type,
4093 Attribute initialValue) {
4094 if (op.getInitialValue()) {
4096 p.printAttributeWithoutType(initialValue);
4100static ParseResult parseBufferInitialValue(OpAsmParser &parser, Type &type,
4101 Attribute &initialValue) {
4102 auto memrefType = llvm::cast<MemRefType>(type);
4103 if (!memrefType.hasStaticShape())
4104 return parser.emitError(parser.getNameLoc())
4105 <<
"type should be static shaped memref, but got " << type;
4107 if (parser.parseOptionalEqual())
4110 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
4111 if (parser.parseAttribute(initialValue, tensorType))
4113 if (!llvm::isa<ElementsAttr>(initialValue))
4114 return parser.emitError(parser.getNameLoc())
4115 <<
"initial value should be an elements attribute";
4119TileOp BufferOp::getTileOp() {
4120 return cast<TileElement>(this->getOperation()).getTileOp();
4127LogicalResult MemOp::verify() {
4128 Region &body = getBody();
4133 for (
auto &bodyOp : body.getOps()) {
4134 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp))
4135 if (!allocOp->getAttr(
"id"))
4136 return allocOp.emitOpError()
4137 <<
"allocOp in MemOp region should have an id attribute";
4142TileOp MemOp::getTileOp() {
4143 return cast<TileElement>(this->getOperation()).getTileOp();
4150LogicalResult MemTileDMAOp::verify() {
4151 assert(getOperation()->getNumRegions() == 1 &&
4152 "MemTileDMAOp has zero region!");
4158 for (
auto &bodyOp : getBody().getOps()) {
4159 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp)) {
4160 if (!allocOp->getAttr(
"id"))
4161 return allocOp.emitOpError()
4162 <<
"allocOp in MemTileDMAOp region should have an id attribute";
4164 if (
auto startOp = dyn_cast<DMAStartOp>(bodyOp)) {
4167 if (!startOp.getEndpoint() && startOp.getChannelIndex() > 3) {
4173 llvm::SmallSet<Block *, 16> reachable;
4174 SmallVector<Block *, 16> worklist;
4175 Block *firstBD = startOp.getSuccessor(0);
4176 reachable.insert(firstBD);
4177 worklist.push_back(firstBD);
4178 while (!worklist.empty()) {
4179 Block *block = worklist.pop_back_val();
4182 auto successors = block->getTerminator()->getSuccessors();
4183 for (
auto *i : successors) {
4184 if (!reachable.contains(i)) {
4185 reachable.insert(i);
4186 worklist.push_back(i);
4190 for (Block *b : reachable) {
4191 for (DMABDOp bd : b->getOps<DMABDOp>()) {
4192 if (
auto bufferOp = bd.getBufferOp();
4193 bufferOp.getTile() != getTile()) {
4194 InFlightDiagnostic err =
4196 <<
"is reachable from DMA channel "
4197 << startOp.getChannelIndex()
4198 <<
" and attempts to access a non-local buffer\n";
4199 err.attachNote(startOp->getLoc()) <<
"channel";
4200 err.attachNote(bufferOp->getLoc()) <<
"buffer";
4204 for (
auto useLock : b->getOps<UseLockOp>()) {
4205 if (
auto lockOp = useLock.getLockOp();
4206 lockOp.getTile() != getTile()) {
4207 InFlightDiagnostic err =
4208 useLock.emitOpError()
4209 <<
"is reachable from DMA channel "
4210 << startOp.getChannelIndex()
4211 <<
" and attempts to access a non-local lock\n";
4212 err.attachNote(startOp->getLoc()) <<
"channel";
4213 err.attachNote(lockOp->getLoc()) <<
"lock";
4225TileOp MemTileDMAOp::getTileOp() {
4226 return cast<TileElement>(this->getOperation()).getTileOp();
4233static bool isBdPacketEnabled(DMABDOp bd) {
4234 if (bd.getPacket().has_value())
4236 if (Block *blk = bd->getBlock())
4237 return !blk->getOps<DMABDPACKETOp>().empty();
4242 PacketInfoAttr packet,
4244 if (packet && !packet.isAssigned())
4245 return op->emitOpError()
4247 <<
" has no pkt_id; allocation assigns one, so a header at "
4248 "this level must carry it";
4254 std::optional<int32_t> oooId = bd.getOutOfOrderId();
4255 if (!oooId.has_value())
4259 return bd.emitOpError(
"out_of_order_id is not supported on this device");
4260 if (!packetEnabledByContext && !isBdPacketEnabled(bd))
4261 return bd.emitOpError(
"out_of_order_id requires a packet-enabled BD");
4263 if (*oooId < 0 ||
static_cast<uint32_t
>(*oooId) > maxOooId)
4264 return bd.emitOpError(
"out_of_order_id must be in [0, ") << maxOooId <<
"]";
4274 AIETileType tileType) {
4275 std::optional<BDIterationAttr> iter = bd.getIteration();
4280 return bd.emitOpError(
"BD iteration is not supported on this target");
4281 uint32_t size = iter->getSize(), current = iter->getCurrent();
4283 if (size < 1 || size > maxSize)
4284 return bd.emitOpError(
"BD iteration size must be in [1, ")
4287 int64_t strideInBytes =
static_cast<int64_t
>(iter->getStride()) *
4288 bd.getBufferElementTypeWidthInBytes();
4289 if (strideInBytes % 4)
4290 return bd.emitOpError(
4291 "BD iteration stride must be aligned to 32-bit words");
4292 int64_t stepInWords = strideInBytes / 4;
4294 if (stepInWords < 1 || stepInWords > maxStep)
4295 return bd.emitOpError() <<
"BD iteration stride must be in [1, "
4296 << maxStep <<
"] 32-bit words";
4298 if (current >= size)
4299 return bd.emitOpError(
"BD iteration current must be in [0, size)");
4303static LogicalResult verifyDMARepeatCount(Operation *op, int32_t repeatCount) {
4305 if (maxRepeat == 0) {
4306 if (repeatCount != 0)
4307 return op->emitOpError(
"repeat_count is not supported on this target");
4310 if (repeatCount < 0 ||
static_cast<uint32_t
>(repeatCount) > maxRepeat)
4311 return op->emitOpError(
"repeat_count ")
4312 << repeatCount <<
" is out of range [0, " << maxRepeat
4313 <<
"] for this target";
4319 bool outOfOrder, ArrayRef<DMABDOp> bds,
4320 bool packetEnabledByContext) {
4323 if (dir != DMAChannelDir::S2MM)
4324 return op->emitOpError(
"out_of_order is only valid on an S2MM channel");
4326 return op->emitOpError(
4327 "out-of-order S2MM DMA is not supported on this device");
4329 return op->emitOpError(
"out-of-order S2MM channel must have at least one "
4330 "receive buffer descriptor");
4331 for (DMABDOp bd : bds) {
4332 if (!packetEnabledByContext && !isBdPacketEnabled(bd))
4333 return bd.emitOpError(
4334 "out-of-order S2MM receive buffer descriptor must be packet-enabled");
4335 if (bd.getOutOfOrderId().has_value())
4336 return bd.emitOpError(
4337 "out_of_order_id belongs on the sender buffer descriptor, not an "
4338 "out-of-order S2MM receive buffer descriptor");
4341 DenseMap<Operation *, Operation *> releasedByRecvBd;
4342 for (DMABDOp bd : bds)
4343 for (
auto useLock : bd->getBlock()->getOps<UseLockOp>())
4344 if (useLock.release())
4345 if (Operation *lockDef = useLock.getLock().getDefiningOp())
4346 releasedByRecvBd.try_emplace(lockDef, bd.getOperation());
4347 for (DMABDOp bd : bds)
4348 for (
auto useLock : bd->getBlock()->getOps<UseLockOp>())
4349 if (useLock.acquire() || useLock.acquireGE())
4350 if (Operation *lockDef = useLock.getLock().getDefiningOp()) {
4351 auto it = releasedByRecvBd.find(lockDef);
4352 if (it != releasedByRecvBd.end() && it->second != bd.getOperation())
4353 return bd.emitOpError(
4354 "out-of-order S2MM prohibits inter-BD lock dependencies; "
4362 UseLockOp &release) {
4365 auto reject = [&](UseLockOp at) {
4366 auto diag = at.emitOpError(
4367 "does not fit its buffer descriptor, which has one lock-acquire field "
4368 "and one lock-release field; a buffer descriptor block uses either no "
4369 "lock or one use_lock(acquire) and one use_lock(release)");
4371 diag <<
", or a use_lock(release) alone when out-of-order";
4374 for (UseLockOp op : block.getOps<UseLockOp>()) {
4375 UseLockOp &slot = op.release() ? release : acquire;
4380 if (!acquire && !release)
4382 if (release && (acquire || outOfOrder))
4384 return reject(acquire ? acquire : release);
4387LogicalResult DMAOp::verify() {
4388 auto *parentOp = getOperation()->getParentOp();
4389 if (parentOp->getRegion(0).getBlocks().size() > 1)
4390 return emitOpError(
"DMAOp can only appear in single block region");
4391 if (!parentOp->getRegion(0).getOps<DMAStartOp>().empty())
4392 return emitOpError(
"DMAOp is not compatible with DMAStart ops");
4393 auto bdRegions = getBds();
4394 for (
auto &bdRegion : bdRegions) {
4395 if (!bdRegion.hasOneBlock())
4396 return emitOpError(
"DMAOp regions must have only one block");
4397 auto bds = llvm::to_vector_of<DMABDOp>(bdRegion.front().getOps<DMABDOp>());
4398 if (bds.size() != 1)
4399 return emitOpError(
"DMAOp regions/blocks must have exactly one DMABDOp");
4401 llvm::to_vector_of<UseLockOp>(bdRegion.front().getOps<UseLockOp>());
4402 if (useLocks.size() != 2)
4404 "DMAOp regions/blocks must have exactly two UseLock ops");
4406 if (getPadValue() != 0) {
4407 if (!isa<MemTileDMAOp>(parentOp))
4408 return emitOpError(
"pad_value is only supported on memtile DMA channels");
4409 if (getChannelDir() != DMAChannelDir::MM2S)
4410 return emitOpError(
"pad_value is only supported on MM2S DMA channels");
4411 if (!
getTargetModel(getOperation()).isMemTilePadValueSupported())
4412 return emitOpError(
"pad_value requires the CONSTANT_PAD_VALUE register, "
4413 "unavailable on this target");
4415 if (failed(verifyDMARepeatCount(getOperation(), getRepeatCount())))
4417 SmallVector<DMABDOp> bds;
4418 for (
auto &bdRegion : getBds())
4419 llvm::append_range(bds, bdRegion.front().getOps<DMABDOp>());
4421 getOutOfOrder(), bds);
4428BufferOp DMABDOp::getBufferOp() {
4429 return cast<BufferOp>(getBuffer().getDefiningOp());
4434static ParseResult parseOptionalOperandType(OpAsmParser &parser, Type &type,
4437 if (succeeded(parser.parseOptionalColon()))
4438 return parser.parseType(type);
4442static void printOptionallyTypedOperand(OpAsmPrinter &printer, Value operand,
4445 if (operand.getType() != defaultType)
4446 printer <<
" : " << operand.getType();
4455parseDynamicScalar(OpAsmParser &parser,
4456 std::optional<OpAsmParser::UnresolvedOperand> &operand,
4457 IntegerAttr &staticAttr, Type &type) {
4459 OptionalParseResult intResult = parser.parseOptionalInteger(intValue);
4460 if (intResult.has_value()) {
4461 if (failed(*intResult))
4464 parser.getBuilder().getI32IntegerAttr(
static_cast<int32_t
>(intValue));
4467 OpAsmParser::UnresolvedOperand op;
4468 if (parser.parseOperand(op) ||
4469 parseOptionalOperandType(parser, type, parser.getBuilder().getI32Type()))
4475static void printDynamicScalar(OpAsmPrinter &printer, Operation *,
4476 Value operand, IntegerAttr staticAttr, Type) {
4478 printOptionallyTypedOperand(printer, operand,
4479 IntegerType::get(operand.getContext(), 32));
4481 printer << staticAttr.getInt();
4485 OpAsmParser &parser,
4486 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &values,
4487 DenseI64ArrayAttr &integers, SmallVectorImpl<Type> &types) {
4488 SmallVector<int64_t> ints;
4489 auto parseEntry = [&]() -> ParseResult {
4490 OpAsmParser::UnresolvedOperand operand;
4491 OptionalParseResult isOperand = parser.parseOptionalOperand(operand);
4492 if (!isOperand.has_value())
4493 return parser.parseInteger(ints.emplace_back());
4495 if (failed(*isOperand) ||
4496 parseOptionalOperandType(parser, type,
4497 parser.getBuilder().getI64Type()))
4499 values.push_back(operand);
4500 types.push_back(type);
4501 ints.push_back(ShapedType::kDynamic);
4504 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::Square, parseEntry))
4506 integers = parser.getBuilder().getDenseI64ArrayAttr(ints);
4511 OperandRange values,
4512 ArrayRef<int64_t> integers,
4515 auto value = values.begin();
4516 llvm::interleaveComma(integers, printer, [&](int64_t i) {
4517 if (!ShapedType::isDynamic(i)) {
4522 printOptionallyTypedOperand(printer, v,
4523 IntegerType::get(v.getContext(), 64));
4530static void splitScalarOfr(mlir::OpBuilder &builder, mlir::OpFoldResult ofr,
4531 mlir::Value &operand, mlir::IntegerAttr &attr) {
4536 if (
auto v = llvm::dyn_cast_if_present<mlir::Value>(ofr)) {
4541 llvm::cast<mlir::IntegerAttr>(llvm::cast<mlir::Attribute>(ofr));
4542 attr = builder.getI32IntegerAttr(
static_cast<int32_t
>(intAttr.getInt()));
4545void DMABDOp::buildMixed(mlir::OpBuilder &builder, mlir::OperationState &state,
4546 mlir::Value buffer, mlir::OpFoldResult offset,
4547 mlir::OpFoldResult len,
4548 llvm::ArrayRef<mlir::OpFoldResult> sizes,
4549 llvm::ArrayRef<mlir::OpFoldResult> strides,
4550 BDPadLayoutArrayAttr padDims, PacketInfoAttr packet) {
4553 llvm::SmallVector<int64_t> staticSizes, staticStrides;
4554 llvm::SmallVector<mlir::Value> dynSizes, dynStrides;
4555 mlir::dispatchIndexOpFoldResults(sizes, dynSizes, staticSizes);
4556 mlir::dispatchIndexOpFoldResults(strides, dynStrides, staticStrides);
4560 DenseI64ArrayAttr staticSizesAttr =
4561 staticSizes.empty() ? DenseI64ArrayAttr{}
4562 : builder.getDenseI64ArrayAttr(staticSizes);
4563 DenseI64ArrayAttr staticStridesAttr =
4564 staticStrides.empty() ? DenseI64ArrayAttr{}
4565 : builder.getDenseI64ArrayAttr(staticStrides);
4569 mlir::Value offsetVal, lenVal;
4570 mlir::IntegerAttr offsetAttr, lenAttr;
4571 splitScalarOfr(builder, offset, offsetVal, offsetAttr);
4572 splitScalarOfr(builder, len, lenVal, lenAttr);
4574 build(builder, state, buffer, offsetVal, lenVal,
4575 offsetAttr, lenAttr,
4576 dynSizes, dynStrides,
4598void DMABDOp::buildWithConstants(mlir::OpBuilder &builder,
4599 mlir::OperationState &state,
4600 mlir::Value buffer, int32_t offset,
4601 int32_t len, BDDimLayoutArrayAttr dims,
4602 BDPadLayoutArrayAttr padDims,
4603 PacketInfoAttr packet) {
4607 mlir::OpFoldResult offsetOfr = builder.getI32IntegerAttr(offset);
4608 mlir::OpFoldResult lenOfr = builder.getI32IntegerAttr(len);
4612 llvm::SmallVector<mlir::OpFoldResult> sizes, strides;
4614 for (BDDimLayoutAttr d : dims) {
4615 sizes.push_back(builder.getI64IntegerAttr(d.getSize()));
4616 strides.push_back(builder.getI64IntegerAttr(d.getStride()));
4619 buildMixed(builder, state, buffer, offsetOfr, lenOfr, sizes, strides, padDims,
4623llvm::SmallVector<mlir::OpFoldResult> DMABDOp::getMixedSizes() {
4624 return ::mlir::getMixedValues(
4625 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{}), getSizes(),
4629llvm::SmallVector<mlir::OpFoldResult> DMABDOp::getMixedStrides() {
4630 return ::mlir::getMixedValues(
4631 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{}), getStrides(),
4635LogicalResult DMABDOp::verifyMixedSizesAndStrides() {
4636 llvm::ArrayRef<int64_t> staticSizes =
4637 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{});
4638 llvm::ArrayRef<int64_t> staticStrides =
4639 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{});
4640 if (failed(mlir::verifyListOfOperandsOrIntegers(
4641 *
this,
"sizes", staticSizes.size(), staticSizes, getSizes())))
4643 if (failed(mlir::verifyListOfOperandsOrIntegers(
4644 *
this,
"strides", staticStrides.size(), staticStrides, getStrides())))
4646 if (staticSizes.size() != staticStrides.size())
4647 return emitOpError(
"expected the same number of sizes (")
4648 << staticSizes.size() <<
") and strides (" << staticStrides.size()
4653std::optional<llvm::SmallVector<BDDimLayoutAttr>> DMABDOp::getFoldedDimensions(
4654 llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
4655 llvm::ArrayRef<int64_t> sizes =
4656 getStaticSizes().value_or(llvm::ArrayRef<int64_t>{});
4657 llvm::ArrayRef<int64_t> strides =
4658 getStaticStrides().value_or(llvm::ArrayRef<int64_t>{});
4659 if (sizes.size() != strides.size()) {
4660 emitError() <<
"expected the same number of sizes (" << sizes.size()
4661 <<
") and strides (" << strides.size() <<
")";
4662 return std::nullopt;
4664 mlir::OperandRange dynSizes = getSizes(), dynStrides = getStrides();
4665 unsigned nextSize = 0, nextStride = 0;
4666 llvm::SmallVector<BDDimLayoutAttr> dims;
4667 dims.reserve(sizes.size());
4668 for (
auto [s, t] : llvm::zip(sizes, strides)) {
4669 std::optional<int64_t> sc =
4670 ShapedType::isDynamic(s)
4671 ? mlir::getConstantIntValue(dynSizes[nextSize++])
4673 std::optional<int64_t> tc =
4674 ShapedType::isDynamic(t)
4675 ? mlir::getConstantIntValue(dynStrides[nextStride++])
4678 emitError() <<
"buffer descriptor size/stride is a runtime value; a "
4679 "compile-time constant is required on this path";
4680 return std::nullopt;
4682 dims.push_back(BDDimLayoutAttr::get(
4683 getContext(),
static_cast<uint32_t
>(*sc),
static_cast<uint32_t
>(*tc)));
4688std::optional<llvm::SmallVector<BDDimLayoutAttr>>
4689DMABDOp::getConstantDimensions() {
4690 return getFoldedDimensions([&]() {
return emitOpError(); });
4696LogicalResult DMABDOp::verifyTransferInBounds() {
4697 auto buffer = llvm::dyn_cast<MemRefType>(getBuffer().getType());
4698 if (!buffer || !buffer.hasStaticShape() ||
4699 llvm::isa<BlockArgument>(getBuffer()))
4701 if ((getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) && !hasLen())
4702 return emitOpError(
"offset_parameter requires an explicit `len` on a local "
4703 "buffer; the default whole-buffer length runs past the "
4704 "end for any nonzero runtime offset");
4705 std::optional<int32_t> offset = hasOffset() ? getConstantOffset() : 0;
4706 if (!offset || *offset < 0)
4708 int64_t numElements = buffer.getNumElements();
4709 SmallVector<OpFoldResult> sizes = getMixedSizes();
4710 SmallVector<OpFoldResult> strides = getMixedStrides();
4711 if (sizes.empty()) {
4712 std::optional<int64_t> len =
4713 hasLen() ? std::optional<int64_t>(getConstantLen()) : numElements;
4714 if (!len || *len <= 0 || *offset + *len <= numElements)
4716 InFlightDiagnostic err = emitOpError(
"transfer of ")
4717 << *len <<
" elements at offset " << *offset
4718 <<
" runs past the end of the " << numElements
4719 <<
"-element buffer";
4721 err.attachNote() <<
"an omitted `len` is the whole buffer; give the "
4722 "length to transfer from this offset";
4725 int64_t maxIdx = *offset;
4726 for (
auto [size, stride] : llvm::zip(sizes, strides)) {
4727 std::optional<int64_t> s = getConstantIntValue(size);
4728 std::optional<int64_t> t = getConstantIntValue(stride);
4731 maxIdx += *t * (*s - 1);
4733 if (maxIdx < numElements)
4735 return emitOpError() <<
"Specified stride(s) and size(s) result in out of "
4736 "bounds access in buffer, for index "
4737 << maxIdx <<
" in memref of length " << numElements
4745 llvm::ArrayRef<BDPadLayoutAttr> pads,
4747 if (dims.empty() || pads.empty())
4750 for (
auto [dim, pad] : llvm::zip(dims, pads))
4751 size *= dim.getSize() + pad.getConstPadBefore() + pad.getConstPadAfter();
4759 if (dims.back().getStride() != 1)
4763 uint64_t product = 1;
4764 for (
int i =
static_cast<int>(dims.size()) - 1; i >= 1; --i) {
4765 product *= dims[i].getSize();
4767 if (dims[i - 1].getSize() > 1 && dims[i - 1].getStride() != product)
4773std::optional<SmallVector<BDDimLayoutAttr>>
4775 SmallVector<BDDimLayoutAttr> kept;
4776 int64_t product = 1;
4777 for (BDDimLayoutAttr dim : dims) {
4778 product *= dim.getSize();
4779 if (dim.getSize() != 1)
4780 kept.push_back(dim);
4782 if (kept.size() == dims.size() || len != product)
4783 return std::nullopt;
4785 return SmallVector<BDDimLayoutAttr>{};
4795 BaseMemRefType buffer) {
4796 uint64_t elemBitWidth = buffer.getElementTypeBitWidth();
4797 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
4798 return op->emitOpError(
"length_parameter requires a whole-byte element "
4800 std::optional<int64_t> unitBytes =
4801 llvm::checkedMul(lengthUnit,
static_cast<int64_t
>(elemBitWidth / 8));
4802 if (!unitBytes || *unitBytes / 4 > std::numeric_limits<uint32_t>::max())
4803 return op->emitOpError(
"length_unit (")
4805 <<
" elements) exceeds the 32-bit word count of a BD length";
4806 if (*unitBytes % 16 != 0)
4807 return op->emitOpError(
"length_unit must be a multiple of 16 bytes, got ")
4808 << *unitBytes <<
" bytes";
4813 Operation *op, std::optional<int64_t> lengthUnit, BaseMemRefType buffer,
4814 std::optional<int64_t> lenElems,
bool contiguous,
4815 llvm::ArrayRef<int64_t> innerSizes) {
4817 return op->emitOpError(
"length_parameter requires length_unit");
4820 int64_t elemBytes = buffer.getElementTypeBitWidth() / 8;
4822 return op->emitOpError(
"length_parameter requires a constant length");
4823 if ((*lenElems * elemBytes) % 16 != 0)
4824 return op->emitOpError(
"length_parameter requires a static length that is "
4825 "a multiple of 16 bytes, got ")
4826 << *lenElems * elemBytes <<
" bytes";
4829 llvm::SmallVector<int64_t, 3> sizes(innerSizes);
4830 llvm::SmallVector<int64_t, 3> strides(3, 0);
4833 return op->emitOpError(
"length_parameter on a non-contiguous pattern "
4834 "requires its second or third dimension to have a "
4835 "size above one: the added length continues it");
4836 int64_t rowElems = sizes[0] * sizes[1];
4837 if (*lengthUnit % rowElems != 0)
4838 return op->emitOpError(
"length_unit (")
4839 << *lengthUnit <<
" elements) must be a multiple of the " << rowElems
4840 <<
" elements moved per step of the dimension the added length "
4846 llvm::MutableArrayRef<int64_t> sizes,
4847 llvm::MutableArrayRef<int64_t> strides) {
4848 if (sizes[2] > 1 || sizes[1] <= 1)
4850 sizes[2] = sizes[1];
4851 strides[2] = strides[1];
4857 Operation *op,
const AIETargetModel &targetModel,
int col,
int row) {
4860 return op->emitOpError(
"length_parameter is only supported on AIE2 and "
4863 return op->emitOpError(
"length_parameter is only supported on shim NOC "
4864 "tiles, got tile (")
4865 << col <<
", " << row <<
")";
4869 return op->emitOpError(
"length_parameter requires a BD whose buffer "
4870 "length fills a 32-bit register");
4875 llvm::SmallVector<uint32_t> ids;
4876 program.getDmaBody().walk([&](DMABDOp bd) {
4877 if (
auto id = bd.getBdId())
4883LogicalResult DMABDOp::verify() {
4884 if (getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) {
4885 uint64_t elemBitWidth = llvm::cast<BaseMemRefType>(getBuffer().getType())
4886 .getElementTypeBitWidth();
4887 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
4888 return emitOpError(
"offset_parameter requires a whole-byte element type");
4891 if (getLengthParameterAttr() || getLengthStateTableIdxAttr()) {
4894 if (llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(
4895 (*this)->getParentOp()))
4896 return emitOpError(
"length_parameter is only supported on a BD in a "
4897 "runtime sequence");
4898 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims =
4899 getFoldedDimensions([&]() {
4900 return emitOpError(
"length_parameter requires a static pattern: ");
4905 if (dims->size() > 4)
4906 return emitOpError(
"length_parameter requires at most 4 dimensions, got ")
4910 llvm::ArrayRef<BDDimLayoutAttr> inner(*dims);
4911 if (inner.size() == 4)
4912 inner = inner.drop_front();
4913 llvm::SmallVector<int64_t, 3> innerSizes(3, 1);
4914 for (
auto [i, dim] : llvm::enumerate(llvm::reverse(inner)))
4915 innerSizes[i] = dim.getSize();
4916 auto buffer = llvm::cast<BaseMemRefType>(getBuffer().getType());
4917 std::optional<int64_t> lenElems;
4918 if (std::optional<int32_t> len = getConstantLen())
4920 else if (!hasLen() && buffer.hasStaticShape())
4921 lenElems = buffer.getNumElements();
4931 Operation *p = (*this)->getParentOp();
4932 if (!llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(*p)) {
4937 bool isUnrankedMemRef = llvm::isa<UnrankedMemRefType>(getBuffer().getType());
4941 if (!isUnrankedMemRef) {
4942 if (!isa<BufferOp, ExternalBufferOp>(getBuffer().getDefiningOp()))
4944 "BDs only support BufferOp or ExternalBufferOp operands.");
4947 if (getLenInBytes() % 4)
4948 return emitOpError(
"transfer length must be multiple of 4 (i.e., represent "
4949 "4 byte aligned address)");
4951 TileElement parentTileElement = getParentTileElement(getOperation());
4952 TileLike parentTile = parentTileElement.getTileLike();
4954 return emitOpError(
"parent tile must implement TileLike interface");
4956 if (!isUnrankedMemRef && getOperation()->getParentOfType<MemOp>() &&
4957 getBufferOp().getTile() != parentTileElement.getTile())
4959 "Core tile DMAs can only access a buffer in the same tile.");
4963 uint32_t maxBds = targetModel.
getNumBDs(parentTile.getTileType());
4964 if (std::optional<int32_t> bdId = getBdId();
4965 bdId.has_value() &&
static_cast<uint32_t
>(*bdId) >= maxBds)
4966 return emitOpError(
"bdId attribute exceeds max: ") << maxBds - 1;
4967 if (std::optional<int32_t> nextBdId = getNextBdId();
4968 nextBdId.has_value() &&
static_cast<uint32_t
>(*nextBdId) >= maxBds)
4969 return emitOpError(
"nextBdId attribute exceeds max: ") << maxBds - 1;
4971 if (getBdIdVal() && getBdId().has_value())
4972 return emitOpError(
"bd_id and bd_id_val are mutually exclusive");
4983 bool lenStaticallyKnown = getConstantLen().has_value();
4984 if (!lenStaticallyKnown && !hasLen()) {
4985 if (
auto shaped = llvm::dyn_cast<mlir::ShapedType>(getBuffer().getType()))
4986 lenStaticallyKnown = shaped.hasStaticShape();
4991 if (lenStaticallyKnown) {
4992 if (
auto constLen = getConstantLen();
4993 constLen.has_value() && *constLen <= 0) {
4996 uint64_t lenInBytes = getLenInBytes();
4998 if (granularity != 0) {
4999 uint64_t lenInWords = lenInBytes * 8 / granularity;
5002 if (lenInWords > maxLen)
5003 return emitOpError()
5004 <<
"buffer descriptor length (" << lenInWords
5005 <<
" 32-bit words) exceeds the maximum of " << maxLen
5006 <<
" words supported by this tile type";
5012 if (failed(verifyMixedSizesAndStrides()) || failed(verifyTransferInBounds()))
5017 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims;
5018 if (!getMixedSizes().empty()) {
5019 dims = getConstantDimensions();
5020 if (!dims.has_value())
5023 if (dims.has_value()) {
5027 size_t maxNDims = targetModel.
getBDMaxDims(parentTile.getTileType());
5028 if (dims->size() > maxNDims)
5029 return emitOpError() <<
"Cannot give more than "
5030 << std::to_string(maxNDims)
5031 <<
" dimensions for step sizes and wraps on this "
5033 << std::to_string(dims->size()) <<
" dimensions).";
5035 auto buffer = llvm::dyn_cast<MemRefType>(getBuffer().getType());
5037 return emitOpError() <<
"dimensions attribute cannot be used with "
5038 "unranked memref buffer type.";
5049 bool skipSizeCheck =
5061 uint32_t wrapBits = targetModel.
getDmaBdWrapBits(parentTile.getTileType());
5062 uint32_t stepBits = targetModel.
getDmaBdStepBits(parentTile.getTileType());
5063 uint64_t maxSize = wrapBits > 0 ? (1ULL << wrapBits) - 1 : 0;
5064 uint64_t maxStride = stepBits > 0 ? (1ULL << stepBits) : 0;
5066 for (BDDimLayoutAttr dim : *dims) {
5067 if (0 == dim.getStride() && dim.getSize() != 1)
5068 return emitOpError()
5069 <<
"Invalid step size; must be a positive integer.";
5070 if (dim.getStride() > buffer.getNumElements())
5071 return emitOpError() <<
"Step size " << std::to_string(dim.getStride())
5072 <<
" exceeds memref size "
5073 << std::to_string(buffer.getNumElements());
5074 if (!skipSizeCheck && dim.getSize() > maxSize)
5075 return emitOpError() <<
"Size may not exceed " << maxSize <<
".";
5076 if (dim.getStride() > maxStride)
5077 return emitOpError() <<
"Stride may not exceed " << maxStride <<
".";
5083 if (getBufferElementTypeWidthInBytes() < 4 && dims->back().getStride() != 1)
5085 "For <32b width datatypes, inner-most dim stride must be 1");
5087 if (getBufferElementTypeWidthInBytes() > 4 && dims->back().getStride() != 1)
5089 "For >32b width datatypes, inner-most dim stride must be 1");
5112 int32_t elementWidthInBytes = getBufferElementTypeWidthInBytes();
5113 if (elementWidthInBytes % 4 != 0) {
5114 std::optional<size_t> effectiveInnermost;
5115 for (
size_t i = 0; i < dims->size(); i++)
5116 if ((*dims)[i].getSize() > 1)
5117 effectiveInnermost = i;
5118 if (effectiveInnermost.has_value()) {
5119 for (
size_t i = 0; i < dims->size(); i++) {
5120 BDDimLayoutAttr dim = (*dims)[i];
5121 if (dim.getSize() <= 1)
5123 if (i == *effectiveInnermost) {
5126 if ((dim.getSize() * elementWidthInBytes) % 4)
5127 return emitOpError()
5128 <<
"Innermost dim size (" << dim.getSize() <<
") * "
5129 << elementWidthInBytes <<
"-byte element width = "
5130 << (dim.getSize() * elementWidthInBytes)
5131 <<
" bytes: innermost dim size must be a multiple of 4 "
5132 "bytes for sub-32b element types (the hardware size "
5133 "register is 32-bit-word granularity).";
5138 if ((dim.getStride() * elementWidthInBytes) % 4)
5139 return emitOpError()
5140 <<
"Dim " << i <<
" stride (" << dim.getStride() <<
") * "
5141 << elementWidthInBytes <<
"-byte element width = "
5142 << (dim.getStride() * elementWidthInBytes)
5143 <<
" bytes: non-innermost dim stride must be a multiple "
5144 "of 4 bytes for sub-32b element types (the hardware "
5145 "stepsize register is 32-bit-word granularity).";
5151 if (
auto paddims = getPadDimensions(); paddims.has_value()) {
5152 if (!dims.has_value())
5153 return emitOpError() <<
"Padding requires n-d data layouts expressed as"
5154 <<
" wrap(s) and stride(s).";
5155 if (!parentTile.isMemTile())
5156 return emitOpError() <<
"Padding is only supported by memtile dma bds.";
5157 if (dims->size() != paddims->size())
5158 return emitOpError() <<
"Mismatch number of dimensions between padding(s)"
5159 <<
" and wrap(s) and stride(s).";
5162 if (paddims->size() == 4)
5163 for (BDPadLayoutAttr pad : paddims->take_front(2))
5164 if (pad.getConstPadBefore() || pad.getConstPadAfter())
5165 return emitOpError()
5166 <<
"a four-dimensional walk pads only its two innermost "
5167 "dimensions; the DMA does not apply d2 or d3 padding";
5169 for (
unsigned i = 0; i < paddims->size(); i++) {
5170 auto dim = (*dims)[i];
5171 auto paddim = (*paddims)[i];
5172 actuallen *= paddim.getConstPadBefore() + paddim.getConstPadAfter() +
5174 if (std::optional<int32_t> len = getConstantLen(); len.has_value()) {
5175 if (actuallen > *len)
5176 return emitOpError() <<
"Data exceeds len after padding.";
5177 }
else if (getLen()) {
5178 return emitOpError()
5179 <<
"Padding with a runtime len operand is not yet supported; "
5180 "use a compile-time constant len with padded BDs.";
5183 if ((paddims->back().getConstPadBefore() *
5184 getBufferElementTypeWidthInBytes()) %
5186 return emitOpError() <<
"Inner-most padding-before count must result in"
5187 <<
" padding in 32-bit words.";
5188 if ((paddims->back().getConstPadAfter() *
5189 getBufferElementTypeWidthInBytes()) %
5191 return emitOpError() <<
"Inner-most padding-after count must result in"
5192 <<
" padding in 32-bit words.";
5194 if (!isUnrankedMemRef &&
5195 (parentTile.isMemTile() || parentTile.isCoreTile())) {
5196 if (
auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
5197 int64_t offsetInBytes = *baseAddr + getOffsetInBytes();
5198 if (offsetInBytes % 4)
5199 return emitOpError(
"bd address must be 4 byte (32b) aligned; got "
5201 << offsetInBytes <<
" (bytes)";
5204 if (
auto packetInfo = getPacket()) {
5207 if (packetInfo->getPktType() > 7)
5208 return emitOpError(
"Packet type field can only hold 3 bits.");
5209 if (packetInfo->assignedId() >
5211 return emitOpError(
"Packet ID field can only hold 5 bits.");
5219 if (!hasLen() && !getBuffer().getType().hasStaticShape())
5220 return emitOpError() <<
"buffer with dynamic shape requires static length.";
5222 if (getBurstLength() != 0 && !parentTile.isShimNOCTile())
5223 return emitOpError(
"Burst length is only supported in Shim NOC tiles that "
5224 "are connected to the memory-mapped NOC.");
5226 if (
auto axcache = getAxcache()) {
5227 if (!parentTile.isShimNOCTile()) {
5228 return emitOpError(
"AxCACHE is only supported in Shim NOC tiles "
5229 "that are connected to the memory-mapped NOC.");
5231 if (axcache > 0xF) {
5232 return emitOpError(
"AxCache value out of 4-bit range.");
5239LogicalResult DMABDPACKETOp::verify() {
5242 if (getPacketType() < 0 || getPacketType() > 7)
5243 return emitOpError(
"Packet type field can only hold 3 bits.");
5244 if (getPacketID() < 0 ||
5246 static_cast<int32_t
>(
getTargetModel(getOperation()).getMaxPacketId()))
5247 return emitOpError(
"Packet ID field can only hold 5 bits.");
5251uint32_t DMABDOp::getAxcacheOrDefault() {
5252 return getAxcache().value_or(
5260static LogicalResult FoldDMAStartOp(DMAStartOp op, PatternRewriter &rewriter) {
5262 llvm::SetVector<Block *> reachable;
5263 SmallVector<Block *, 16> worklist;
5264 Block *firstBD = op.getSuccessor(0);
5265 reachable.insert(firstBD);
5266 worklist.push_back(firstBD);
5267 while (!worklist.empty()) {
5268 Block *block = worklist.pop_back_val();
5271 auto successors = block->getTerminator()->getSuccessors();
5272 for (
auto *i : successors) {
5273 if (!reachable.contains(i)) {
5274 reachable.insert(i);
5275 worklist.push_back(i);
5282 if (isa<EndOp>((reachable.back())->getTerminator()))
5286 auto areEquivalentBDs = [](Block *b1, Block *b2) {
5287 auto b1OpRange = b1->without_terminator();
5288 auto b2OpRange = b2->without_terminator();
5289 if (llvm::range_size(b1OpRange) != llvm::range_size(b2OpRange))
5291 if (!llvm::all_of(llvm::zip_equal(b1OpRange, b2OpRange),
5292 [](std::tuple<Operation &, Operation &> pair) {
5293 return OperationEquivalence::isEquivalentTo(
5294 &std::get<0>(pair), &std::get<1>(pair),
5295 OperationEquivalence::IgnoreLocations);
5302 SmallVector<Block *> uniquePattern;
5303 const auto *patternIt = reachable.begin();
5304 while (patternIt != reachable.end() &&
5305 llvm::none_of(uniquePattern, [patternIt, areEquivalentBDs](Block *b1) {
5306 return areEquivalentBDs(*patternIt, b1);
5308 uniquePattern.push_back(*patternIt);
5313 if (patternIt == reachable.end())
5317 while (patternIt != reachable.end()) {
5319 if (!areEquivalentBDs(*patternIt, uniquePattern[idx]))
5322 idx = (idx + 1) % uniquePattern.size();
5326 auto lastBDTerm = cast<NextBDOp>(reachable.back()->getTerminator());
5327 auto lastUniqueBDTerm = cast<NextBDOp>(uniquePattern.back()->getTerminator());
5328 rewriter.modifyOpInPlace(lastUniqueBDTerm, [&] {
5329 lastUniqueBDTerm.setSuccessor(lastBDTerm.getSuccessor());
5339struct LinearizeContiguousBDTransfer :
public mlir::OpRewritePattern<DMABDOp> {
5340 using OpRewritePattern::OpRewritePattern;
5343 matchAndRewrite(DMABDOp op, mlir::PatternRewriter &rewriter)
const override {
5344 if (op.getStaticSizes().value_or(llvm::ArrayRef<int64_t>{}).empty())
5345 return mlir::failure();
5350 bool bufferIsExternal =
5351 isa_and_nonnull<ExternalBufferOp>(op.getBuffer().getDefiningOp());
5352 TileElement parentElem = getParentTileElement(op.getOperation());
5353 TileLike parentTileLike =
5354 parentElem ? parentElem.getTileLike() : TileLike{};
5355 bool parentIsShim = parentTileLike && parentTileLike.isShimTile();
5356 bool inShimDMA = (bool)op->getParentOfType<ShimDMAOp>();
5357 if (!bufferIsExternal && !parentIsShim && !inShimDMA)
5358 return mlir::failure();
5363 auto isConstant = [](mlir::Value v) {
5364 return mlir::getConstantIntValue(v).has_value();
5366 if (!llvm::all_of(op.getSizes(), isConstant) ||
5367 !llvm::all_of(op.getStrides(), isConstant))
5368 return mlir::failure();
5369 std::optional<llvm::SmallVector<BDDimLayoutAttr>> dims =
5370 op.getFoldedDimensions([&]() {
return op.emitError(); });
5371 if (!dims.has_value() || dims->empty())
5372 return mlir::failure();
5374 return mlir::failure();
5376 if (dims->size() == 1 && dims->front().getStride() == 1)
5377 return mlir::failure();
5379 int64_t product = 1;
5380 for (BDDimLayoutAttr dim : *dims)
5381 product *= dim.getSize();
5387 if (!op.hasLen() && !op.getBuffer().getType().hasStaticShape())
5388 return mlir::failure();
5389 std::optional<int32_t> lenVal = op.getConstantLen();
5390 if (lenVal.has_value() &&
static_cast<int64_t
>(*lenVal) != product)
5391 return mlir::failure();
5392 int32_t len =
static_cast<int32_t
>(product);
5396 rewriter.modifyOpInPlace(op, [&]() {
5397 op.getLenMutable().clear();
5398 op.setStaticLen(len);
5399 op.getSizesMutable().clear();
5400 op.getStridesMutable().clear();
5401 op.setStaticSizes(std::nullopt);
5402 op.setStaticStrides(std::nullopt);
5404 return mlir::success();
5412struct FoldConstantBDDimList :
public mlir::OpRewritePattern<DMABDOp> {
5413 using OpRewritePattern::OpRewritePattern;
5416 matchAndRewrite(DMABDOp op, mlir::PatternRewriter &rewriter)
const override {
5417 if (op.getSizes().empty() && op.getStrides().empty() && !op.getOffset() &&
5419 return mlir::failure();
5420 llvm::SmallVector<mlir::OpFoldResult> sizes = op.getMixedSizes();
5421 llvm::SmallVector<mlir::OpFoldResult> strides = op.getMixedStrides();
5427 bool changed = succeeded(mlir::foldDynamicIndexList(sizes)) |
5428 succeeded(mlir::foldDynamicIndexList(strides));
5431 std::optional<int32_t> foldOffset, foldLen;
5433 if (
auto c = mlir::getConstantIntValue(op.getOffset()))
5434 foldOffset =
static_cast<int32_t
>(*c);
5436 if (
auto c = mlir::getConstantIntValue(op.getLen()))
5437 foldLen =
static_cast<int32_t
>(*c);
5438 changed |= foldOffset.has_value() || foldLen.has_value();
5441 return mlir::failure();
5443 llvm::SmallVector<int64_t> staticSizes, staticStrides;
5444 llvm::SmallVector<mlir::Value> dynSizes, dynStrides;
5445 mlir::dispatchIndexOpFoldResults(sizes, dynSizes, staticSizes);
5446 mlir::dispatchIndexOpFoldResults(strides, dynStrides, staticStrides);
5451 bool wasDynamic = !op.getSizes().empty() || !op.getStrides().empty();
5452 if (!op.hasLen() && wasDynamic && dynSizes.empty() && dynStrides.empty() &&
5453 !staticSizes.empty()) {
5455 for (int64_t s : llvm::ArrayRef(staticSizes).take_back(3))
5457 foldLen =
static_cast<int32_t
>(extent);
5460 rewriter.modifyOpInPlace(op, [&]() {
5461 op.getSizesMutable().assign(dynSizes);
5462 op.getStridesMutable().assign(dynStrides);
5464 if (staticSizes.empty())
5465 op.removeStaticSizesAttr();
5467 op.setStaticSizes(staticSizes);
5468 if (staticStrides.empty())
5469 op.removeStaticStridesAttr();
5471 op.setStaticStrides(staticStrides);
5473 op.getOffsetMutable().clear();
5474 op.setStaticOffset(foldOffset);
5477 op.getLenMutable().clear();
5478 op.setStaticLen(foldLen);
5481 return mlir::success();
5486void DMABDOp::getCanonicalizationPatterns(RewritePatternSet &results,
5487 MLIRContext *context) {
5488 results.add<FoldConstantBDDimList, LinearizeContiguousBDTransfer>(context);
5491void DMAStartOp::getCanonicalizationPatterns(RewritePatternSet &results,
5492 MLIRContext *context) {
5493 results.add(FoldDMAStartOp);
5496RouteEndpointOp DMAStartOp::getEndpointOp() {
5497 FlatSymbolRefAttr name = getEndpoint();
5500 return SymbolTable::lookupNearestSymbolFrom<RouteEndpointOp>(*
this, name);
5503LogicalResult DMAStartOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5504 FlatSymbolRefAttr name = getEndpoint();
5507 auto program = dyn_cast<DmaBody>((*this)->getParentOp());
5508 auto device = (*this)->getParentOfType<DeviceOp>();
5509 if (!program || !device)
5510 return emitOpError(
"only a tile's DMA program may name its channel by a "
5512 auto endpoint = symbolTable.lookupSymbolIn<RouteEndpointOp>(device, name);
5514 return emitOpError() << name <<
" is not an aie.route_endpoint";
5515 if (endpoint.getBundle() != WireBundle::DMA)
5516 return emitOpError() << name <<
" names a "
5517 << stringifyWireBundle(endpoint.getBundle())
5518 <<
" port, not a DMA channel";
5519 auto here = cast<TileLike>(program.getTile().getDefiningOp());
5520 TileLike there = endpoint.getTileLike();
5521 bool sameTile = endpoint.getTile() == program.getTile() ||
5522 (here.tryGetCol() && here.tryGetRow() &&
5523 here.tryGetCol() == there.tryGetCol() &&
5524 here.tryGetRow() == there.tryGetRow());
5526 return emitOpError() << name
5527 <<
" is on a different tile than this DMA program";
5531LogicalResult DMAStartOp::verify() {
5532 if (getPadValue() != 0) {
5533 if (!isa<MemTileDMAOp>(getOperation()->getParentOp()))
5534 return emitOpError(
"pad_value is only supported on memtile DMA channels");
5535 if (getChannelDir() != DMAChannelDir::MM2S)
5536 return emitOpError(
"pad_value is only supported on MM2S DMA channels");
5537 if (!
getTargetModel(getOperation()).isMemTilePadValueSupported())
5538 return emitOpError(
"pad_value requires the CONSTANT_PAD_VALUE register, "
5539 "unavailable on this target");
5541 if (failed(verifyDMARepeatCount(getOperation(), getRepeatCount())))
5543 SmallVector<DMABDOp> bds;
5544 if (getOutOfOrder()) {
5545 llvm::SmallPtrSet<Block *, 8> visited;
5546 for (Block *b = getDest(); b && visited.insert(b).second;
5547 b = b->getNumSuccessors() > 0 ? b->getSuccessor(0) :
nullptr)
5548 llvm::append_range(bds, b->getOps<DMABDOp>());
5551 getOutOfOrder(), bds);
5558LogicalResult SwitchboxOp::verify() {
5564 if (!isa<TileOp>(getTile().getDefiningOp()))
5567 Region &body = getConnections();
5568 DenseSet<Port> sourceset;
5569 DenseSet<Port> destset;
5570 auto tile = getTileOp();
5573 return emitOpError(
"should have non-empty body");
5575 for (
auto connectOp : body.front().getOps<ConnectOp>())
5576 sourceset.insert({connectOp.getSourceBundle(), connectOp.sourceIndex()});
5577 for (
auto &ops : body.front()) {
5579 auto checkBound = [&ops](StringRef dir, WireBundle bundle,
int index,
5580 int bound) -> LogicalResult {
5581 if (index >= bound) {
5583 return ops.emitOpError(
"index ")
5584 << index <<
" for " << dir <<
" bundle "
5585 << stringifyWireBundle(bundle) <<
" must be less than "
5587 return ops.emitOpError()
5588 << dir <<
" bundle " << stringifyWireBundle(bundle)
5589 <<
" not supported; index: " << index <<
", bound: " << bound;
5594 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
5595 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
5597 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
5598 if (destset.count(dest)) {
5599 return connectOp.emitOpError()
5600 <<
"; connecting " << to_string(source) <<
" to "
5601 << to_string(dest) <<
" on "
5602 << to_string(this->getTileOp().getTileID())
5603 <<
" targets same dst as another connect op; existing "
5605 << llvm::join(llvm::map_range(
5606 destset, [](
auto &p) {
return to_string(p); }),
5609 destset.insert(dest);
5611 if (connectOp.sourceIndex() < 0)
5612 return connectOp.emitOpError(
"source index cannot be less than zero");
5614 if (checkBound(
"source", connectOp.getSourceBundle(),
5615 connectOp.sourceIndex(),
5616 getNumSourceConnections(connectOp.getSourceBundle()))
5620 if (connectOp.destIndex() < 0)
5621 return connectOp.emitOpError(
"dest index cannot be less than zero");
5623 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
5624 getNumDestConnections(connectOp.getDestBundle()))
5629 if (!isLegalTileConnection(tile, targetModel, connectOp))
5630 return connectOp.emitOpError(
"illegal stream switch connection");
5632 }
else if (
auto connectOp = dyn_cast<MasterSetOp>(ops)) {
5633 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
5634 if (destset.count(dest))
5635 return connectOp.emitOpError(
"targets same destination ")
5636 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
5637 <<
" as another connect or masterset operation";
5638 destset.insert(dest);
5640 if (connectOp.destIndex() < 0)
5641 return connectOp.emitOpError(
"dest index cannot be less than zero");
5643 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
5644 getNumDestConnections(connectOp.getDestBundle()))
5649 }
else if (
auto connectOp = dyn_cast<PacketRulesOp>(ops)) {
5650 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
5651 if (sourceset.count(source))
5652 return connectOp.emitOpError(
"packet switched source ")
5653 << stringifyWireBundle(source.bundle) << source.channel
5654 <<
" cannot match another connect or masterset operation";
5655 sourceset.insert(source);
5657 }
else if (
auto amselOp = dyn_cast<AMSelOp>(ops)) {
5658 std::vector<MasterSetOp> mstrs;
5659 std::vector<PacketRulesOp> slvs;
5660 for (
auto *user : amselOp.getResult().getUsers()) {
5661 if (
auto s = dyn_cast<PacketRuleOp>(user)) {
5662 auto pktRules = cast<PacketRulesOp>(s->getParentOp());
5663 slvs.push_back(pktRules);
5664 }
else if (
auto m = dyn_cast<MasterSetOp>(user))
5667 for (
auto m : mstrs) {
5668 for (
auto s : slvs) {
5670 if (!isLegalTileConnection(tile, targetModel, m, s)) {
5671 return amselOp->emitOpError(
"illegal stream switch connection");
5675 }
else if (isa<EndOp>(ops)) {
5678 return ops.emitOpError(
"cannot be contained in a Switchbox op");
5685template <
typename... ParentOpTypes>
5688 Operation *operation = op->getParentOp();
5690 if (llvm::isa_and_nonnull<ParentOpTypes...>(operation))
5692 operation = operation->getParentOp();
5698TileOp LockOp::getTileOp() {
5699 return cast<TileElement>(this->getOperation()).getTileOp();
5702LogicalResult LockOp::verify() {
5703 if (
auto result = UsesAreAccessible::verifyTrait(*
this); result.failed())
5706 if (
auto lockID = getLockID()) {
5707 TileLike tileLike = getTileLike();
5709 return emitOpError(
"tile operand must implement TileLike interface");
5711 auto tileType = tileLike.getTileType();
5712 if (
int numLocks = targetModel.
getNumLocks(tileType); *lockID >= numLocks)
5713 return emitOpError(
"lock assigned invalid id (maximum is ")
5714 << numLocks - 1 <<
")";
5722static std::optional<int32_t> getConstantLockValue(UseLockOp op) {
5723 if (
auto constant = op.getValue().getDefiningOp<arith::ConstantOp>())
5724 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(constant.getValue()))
5725 return (int32_t)intAttr.getInt();
5726 return std::nullopt;
5731 auto *block = op->getBlock();
5733 for (
auto op : block->getOps<UseLockOp>()) {
5734 if (
auto lock = dyn_cast<LockOp>(op.getLock().getDefiningOp());
5735 lock.getLockID().has_value()) {
5736 if (lockID != -1 && lockID != lock.getLockIDValue())
5738 lockID = lock.getLockIDValue();
5747 auto *block = op->getBlock();
5748 int acqValue = -1, relValue = -1;
5749 for (
auto op : block->getOps<UseLockOp>()) {
5752 auto value = getConstantLockValue(op);
5755 if (op.acquire() || op.acquireGE()) {
5756 if (acqValue != -1 && acqValue != *value) {
5760 }
else if (op.release()) {
5761 if (relValue != -1 && relValue != *value) {
5773 if (
auto memOp = op->getParentOfType<MemOp>()) {
5774 auto useLock = cast<UseLockOp>(op);
5775 if (
auto lock = useLock.getLockOp(); lock.getTile() != memOp.getTile())
5782LogicalResult UseLockOp::verify() {
5784 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>((*this)->getParentOp()))
5785 return (*this)->emitOpError(
"must be used in a core or memory operation.");
5788 if (targetModel.
getTargetArch() == AIEArch::AIE1 && acquireGE())
5789 return (*this)->emitOpError(
5790 "AcquireGreaterEqual is not supported in AIE1.");
5796 !getConstantLockValue(*
this))
5797 return (*this)->emitOpError(
5798 "lock value in a DMA/BD block must be a compile-time constant "
5799 "(defined by an arith.constant).");
5805 if (!(*this)->getBlock())
5806 return (*this)->emitOpError(
"is not in a block.");
5810 return (*this)->emitOpError(
5811 "used in a DMA block that have multiple locks.");
5814 return (*this)->emitOpError(
"acquires/releases the lock in a DMA block "
5815 "from/to multiple states.");
5819 return (*this)->emitOpError(
"can only access a lock in the same tile");
5831 Operation *operation = (*this)->getParentOp();
5833 StringRef name = operation->getName().getStringRef();
5834 if (name ==
"aiex.dma_configure_task" ||
5835 name ==
"aiex.dma_configure_task_for")
5837 operation = operation->getParentOp();
5840 return (*this)->emitOpError()
5841 <<
"expects some parent op to be one of "
5842 <<
"AIE::device, AIE::core, func::func, AIE::mem, AIE::shimDMA, or "
5843 "AIEX::dma_configure_task";
5846#include "aie/Dialect/AIE/IR/AIEEnums.cpp.inc"
5847#include "aie/Dialect/AIE/IR/AIEInterfaces.cpp.inc"
5854static ParseResult parseTraceEventValue(AsmParser &parser, Attribute &value) {
5859static void printTraceEventValue(AsmPrinter &printer, Attribute value) {
5864static ParseResult parseTraceRegValue(OpAsmParser &parser, Attribute &value) {
5868 OptionalParseResult parseResult = parser.parseOptionalInteger(intValue);
5869 if (parseResult.has_value() && succeeded(parseResult.value())) {
5870 MLIRContext *ctx = parser.getContext();
5871 value = IntegerAttr::get(IntegerType::get(ctx, 32), intValue);
5878static void printTraceRegValue(OpAsmPrinter &printer, Operation *op,
5881 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(value)) {
5882 printer << intAttr.getInt();
5889#define GET_OP_CLASSES
5890#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
5892FailureOr<int32_t> UseLockOp::getConstantValue() {
5893 if (
auto value = getConstantLockValue(*
this))
5895 return emitOpError(
"expected the lock value to be a compile-time constant "
5896 "(defined by an arith.constant).");
5899LogicalResult UseLockOp::canonicalize(UseLockOp op, PatternRewriter &rewriter) {
5903 if (op.acquireGE() && op->getBlock()->getOps<DMABDOp>().empty()) {
5904 if (
auto value = getConstantLockValue(op); value && *value == 0) {
5905 rewriter.eraseOp(op);
5912TileOp SwitchboxOp::getTileOp() {
5913 return cast<TileElement>(this->getOperation()).getTileOp();
5916size_t SwitchboxOp::getNumSourceConnections(WireBundle bundle) {
5917 auto tile = getTileOp();
5920 tile.getRow(), bundle);
5923size_t SwitchboxOp::getNumDestConnections(WireBundle bundle) {
5924 auto tile = getTileOp();
5927 tile.getRow(), bundle);
5930TileOp ShimMuxOp::getTileOp() {
5931 return cast<TileElement>(this->getOperation()).getTileOp();
5936 case WireBundle::North:
5937 return WireBundle::South;
5938 case WireBundle::South:
5939 return WireBundle::North;
5940 case WireBundle::East:
5941 return WireBundle::West;
5942 case WireBundle::West:
5943 return WireBundle::East;
5949std::optional<std::pair<TileID, Port>>
5952 switch (port.bundle) {
5953 case WireBundle::North:
5956 case WireBundle::South:
5959 case WireBundle::East:
5962 case WireBundle::West:
5966 return std::nullopt;
5969 return std::nullopt;
5977ParseResult BDChainOp::parse(OpAsmParser &parser, OperationState &result) {
5978 SmallVector<OpAsmParser::Argument> entryArgs;
5981 StringAttr symNameAttr;
5982 if (parser.parseSymbolName(symNameAttr, SymbolTable::getSymbolAttrName(),
5983 result.attributes)) {
5988 ParseResult argParseResult = parser.parseCommaSeparatedList(
5989 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
5990 OpAsmParser::Argument argument;
5991 if (parser.parseArgument(argument,
true,
true)) {
5994 entryArgs.push_back(argument);
5997 if (argParseResult) {
5998 return argParseResult;
6002 auto *body = result.addRegion();
6003 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
6004 if (bodyParseResult) {
6005 return bodyParseResult;
6011void BDChainOp::print(OpAsmPrinter &printer) {
6014 ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
6017 printer.printSymbolName(taskName);
6019 Region &body = getRegion();
6020 auto argsIter = body.getArguments();
6022 for (
auto *it = argsIter.begin(); it != argsIter.end(); ++it) {
6023 if (it != argsIter.begin()) {
6026 printer.printRegionArgument(*it);
6031 printer.printRegion(body,
false,
true);
6038LogicalResult ShimDMAAllocationOp::verify() {
6039 TileLike tileLike = llvm::dyn_cast<TileLike>(getTile().getDefiningOp());
6041 return emitOpError(
"tile operand must implement TileLike interface");
6044 if (!tileLike.isShimNOCorPLTile()) {
6046 auto col = tileLike.tryGetCol();
6047 auto row = tileLike.tryGetRow();
6049 return emitOpError(
"tile must be a shim tile, but got tile(")
6050 << *col <<
", " << *row <<
")";
6052 return emitOpError(
"tile must be a shim tile");
6058TileOp ShimDMAAllocationOp::getTileOp() {
6059 return dyn_cast_or_null<TileOp>(getTile().getDefiningOp());
6062ShimDMAAllocationOp ShimDMAAllocationOp::getForSymbol(DeviceOp device,
6063 llvm::StringRef symbol) {
6064 Operation *maybeOp = device.lookupSymbol(symbol);
6066 if (ShimDMAAllocationOp op = dyn_cast<ShimDMAAllocationOp>(maybeOp)) {
6077LogicalResult ObjectFifoRearmBindingOp::verify() {
6078 if (getChannelDirs().size() != getChannelTiles().size())
6079 return emitOpError(
"expected one channel_dirs entry per channel tile (")
6080 << getChannelTiles().size() <<
" tiles, " << getChannelDirs().size()
6082 if (getChannelIndices().size() != getChannelTiles().size())
6083 return emitOpError(
"expected one channel_indices entry per channel tile (")
6084 << getChannelTiles().size() <<
" tiles, "
6085 << getChannelIndices().size() <<
" indices)";
6086 if (getLockInits().size() != getLocks().size())
6087 return emitOpError(
"expected one lock_inits entry per lock (")
6088 << getLocks().size() <<
" locks, " << getLockInits().size()
6094 if (getHeadBdIds().has_value() != getRepeatCounts().has_value())
6095 return emitOpError(
"head_bd_ids and repeat_counts must both be set or both "
6097 if (
auto headBdIds = getHeadBdIds();
6098 headBdIds.has_value() && headBdIds->size() != getChannelTiles().size())
6099 return emitOpError(
"expected one head_bd_ids entry per channel tile (")
6100 << getChannelTiles().size() <<
" tiles, " << headBdIds->size()
6102 if (
auto repeatCounts = getRepeatCounts();
6103 repeatCounts.has_value() &&
6104 repeatCounts->size() != getChannelTiles().size())
6105 return emitOpError(
"expected one repeat_counts entry per channel tile (")
6106 << getChannelTiles().size() <<
" tiles, " << repeatCounts->size()
6108 for (int32_t dir : getChannelDirs())
6109 if (dir != 0 && dir != 1)
6110 return emitOpError(
"channel_dirs entries must be 0 (S2MM) or 1 (MM2S), "
6116 for (Value tile : getChannelTiles()) {
6117 auto tileOp = tile.getDefiningOp<TileOp>();
6119 return emitOpError(
"channel_tiles operands must be aie.tile values");
6124 if (tileOp.isShimTile())
6125 return emitOpError(
"channel_tiles must be non-shim tiles; a shim DMA "
6126 "endpoint is host-managed and is not re-armed here");
6128 for (Value lock : getLocks())
6129 if (!lock.getDefiningOp<LockOp>())
6130 return emitOpError(
"locks operands must be aie.lock values");
6138ParseResult RuntimeSequenceOp::parse(OpAsmParser &parser,
6139 OperationState &result) {
6142 StringAttr nameAttr;
6143 (void)parser.parseOptionalSymbolName(
6144 nameAttr, mlir::SymbolTable::getSymbolAttrName(), result.attributes);
6146 SmallVector<OpAsmParser::Argument> entryArgs;
6149 ParseResult argParseResult = parser.parseCommaSeparatedList(
6150 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
6151 OpAsmParser::Argument argument;
6152 if (parser.parseArgument(argument,
true,
true)) {
6155 entryArgs.push_back(argument);
6158 if (argParseResult) {
6159 return argParseResult;
6164 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
6168 auto *body = result.addRegion();
6169 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
6170 if (bodyParseResult) {
6171 return bodyParseResult;
6177void RuntimeSequenceOp::print(OpAsmPrinter &printer) {
6178 Region &body = getRegion();
6180 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
6181 mlir::SymbolTable::getSymbolAttrName());
6183 nameAttr != ::mlir::OpBuilder((*this)->getContext())
6184 .getStringAttr(getDefaultRuntimeSequenceName())) {
6186 printer.printSymbolName(nameAttr);
6190 for (
unsigned i = 0, n = body.getNumArguments(); i < n; i++) {
6194 printer.printRegionArgument(body.getArgument(i));
6198 printer.printOptionalAttrDictWithKeyword(
6199 (*this)->getAttrs(),
6200 {mlir::SymbolTable::getSymbolAttrName()});
6203 printer.printRegion(body,
false,
true);
6206LogicalResult RuntimeSequenceOp::verify() {
6207 DeviceOp device = (*this)->getParentOfType<DeviceOp>();
6210 (*this)->emitOpError() <<
"must be inside AIE device operation.";
6217RuntimeSequenceOp::getForSymbolInDevice(DeviceOp deviceOp,
6218 llvm::StringRef symbol) {
6219 RuntimeSequenceOp runtimeSequenceOp;
6220 if (symbol.empty()) {
6221 auto range = deviceOp.getOps<RuntimeSequenceOp>();
6222 if (range.begin() == range.end()) {
6227 runtimeSequenceOp = *range.begin();
6229 Operation *maybeRuntimeSequenceOp =
6230 mlir::SymbolTable::lookupSymbolIn(deviceOp, symbol);
6231 if (!maybeRuntimeSequenceOp) {
6235 llvm::dyn_cast<RuntimeSequenceOp>(maybeRuntimeSequenceOp);
6237 return runtimeSequenceOp;
6241RuntimeSequenceOp::getForSymbolInDeviceOrError(DeviceOp deviceOp,
6242 llvm::StringRef symbol) {
6243 RuntimeSequenceOp runtimeSequenceOp = getForSymbolInDevice(deviceOp, symbol);
6244 if (!runtimeSequenceOp) {
6245 if (!symbol.empty()) {
6246 deviceOp.emitError(
"No such runtime sequence: ") << symbol;
6248 deviceOp.emitError(
"No runtime sequence in device");
6251 return runtimeSequenceOp;
6254LogicalResult RuntimeSequenceOp::verifyBeforeMaterialization() {
6258 auto result = (*this)->walk([&](Operation *op) {
6259 for (NamedAttribute namedAttr : op->getAttrs()) {
6260 Attribute attr = namedAttr.getValue();
6261 auto walkResult = attr.walk([&](SymbolRefAttr symbolRef) {
6262 Operation *symbolDefOp =
6263 SymbolTable::lookupNearestSymbolFrom(*
this, symbolRef);
6265 if (
auto flat = dyn_cast<FlatSymbolRefAttr>(symbolRef)) {
6270 if (!llvm::isa<ShimDMAAllocationOp>(symbolDefOp) &&
6271 !llvm::isa<DeviceOp>(symbolDefOp) &&
6272 !llvm::isa<RuntimeSequenceOp>(symbolDefOp) &&
6273 !llvm::isa<BufferOp>(symbolDefOp) &&
6274 !llvm::isa<ObjectFifoRearmBindingOp>(symbolDefOp) &&
6275 !llvm::isa<memref::GlobalOp>(symbolDefOp)) {
6277 <<
"references symbol '"
6278 << symbolRef.getRootReference().getValue()
6279 <<
"' which must be either a ShimDMAAllocationOp, DeviceOp, "
6280 "RuntimeSequenceOp, BufferOp, ObjectFifoRearmBindingOp, or "
6281 "GlobalOp, but got: "
6282 << symbolDefOp->getName().getStringRef();
6283 return WalkResult::interrupt();
6285 if (BufferOp bufferOp = llvm::dyn_cast<BufferOp>(symbolDefOp)) {
6286 if (!bufferOp.getAddress()) {
6288 <<
"Unallocated buffer; fixed addresses are required before "
6289 "runtime sequence materialization.";
6290 return WalkResult::interrupt();
6294 return WalkResult::advance();
6296 if (walkResult.wasInterrupted()) {
6297 return WalkResult::interrupt();
6300 return WalkResult::advance();
6303 if (result.wasInterrupted()) {
6311#define GET_ATTRDEF_CLASSES
6312#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 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 getDmaTaskQueueDepth() const =0
Return how many DMA tasks a single channel's task queue holds (unsupported = 0).
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.
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 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 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