13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/Dialect/MemRef/IR/MemRef.h"
15#include "mlir/IR/IRMapping.h"
16#include "mlir/Pass/Pass.h"
17#include "mlir/Transforms/DialectConversion.h"
18#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
27#define GEN_PASS_DEF_AIENPUTOCERT
28#define GEN_PASS_DEF_AIECERTPAGES
29#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
35#define DEBUG_TYPE "npu-to-cert"
41static constexpr uint32_t cert_page_size = 8000;
42static constexpr uint32_t cert_page_limit = 8192;
47static constexpr uint32_t cert_job_start_cost = 8;
48static constexpr uint32_t cert_job_end_cost = 4;
54static uint32_t getOrAssignPdiId(AIE::DeviceOp parentDevice,
55 StringRef deviceSymName) {
57 bool foundExisting =
false;
58 parentDevice.walk([&](AIEX::CertLoadPdiOp loadPdiOp) {
59 if (loadPdiOp.getSymbol() == deviceSymName) {
60 pdiId = loadPdiOp.getPdiId();
62 }
else if (!foundExisting) {
63 pdiId = std::max(pdiId, loadPdiOp.getPdiId() + 1);
69struct RuntimeSequenceToCertJob : OpConversionPattern<AIE::RuntimeSequenceOp> {
70 using OpConversionPattern::OpConversionPattern;
72 RuntimeSequenceToCertJob(MLIRContext *context, PatternBenefit benefit = 1)
73 : OpConversionPattern(context, benefit) {}
76 matchAndRewrite(AIE::RuntimeSequenceOp op, OpAdaptor adaptor,
77 ConversionPatternRewriter &rewriter)
const override {
79 auto symName = op.getSymName();
80 uint32_t newJobId = 1;
81 if (symName !=
"configure") {
82 uint32_t maxJobId = 1;
83 op->getParentOp()->walk([&](AIEX::CertJobOp certJobOp) {
84 maxJobId = std::max(maxJobId, certJobOp.getJobId());
86 newJobId = maxJobId + 1;
92 rewriter.setInsertionPoint(op);
93 auto jobOp = AIEX::CertJobOp::create(rewriter, op.getLoc(), newJobId);
97 if (symName ==
"configure")
98 jobOp->setAttr(
"cert.configure", rewriter.getUnitAttr());
104 op.getRegion().cloneInto(&jobOp.getBody(), remap);
105 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), rewriter, op->getLoc());
108 rewriter.eraseOp(op);
114struct NpuWrite32ToCertWrite32 : OpConversionPattern<AIEX::NpuWrite32Op> {
115 using OpConversionPattern::OpConversionPattern;
118 matchAndRewrite(AIEX::NpuWrite32Op op, OpAdaptor adaptor,
119 ConversionPatternRewriter &rewriter)
const override {
121 std::optional<uint32_t> address =
124 if (!address || !value)
125 return op.emitOpError(
126 "cannot lower to cert.write32 with non-constant address or value");
129 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
133 uint32_t absoluteAddr = *absAddress;
136 std::optional<uint32_t> col = op.getColumn();
137 std::optional<uint32_t> row = op.getRow();
140 uint32_t expectedUpperBits = ((*col & 0xff) << tm.getColumnShift()) |
141 ((*row & 0xff) << tm.getRowShift());
144 uint32_t origAddress = *address;
145 uint32_t origUpperBits = origAddress & ~0xfffff;
146 if (origUpperBits != 0 && origUpperBits != expectedUpperBits) {
147 op.emitWarning() <<
"address upper bits (0x"
148 << llvm::utohexstr(origUpperBits)
149 <<
") don't match row=" << *row <<
" col=" << *col
150 <<
" computed bits (0x"
151 << llvm::utohexstr(expectedUpperBits) <<
")";
155 rewriter.replaceOpWithNewOp<AIEX::CertWrite32Op>(op, absoluteAddr, *value);
160struct NpuMaskWrite32ToCertMaskWrite32
161 : OpConversionPattern<AIEX::NpuMaskWrite32Op> {
162 using OpConversionPattern::OpConversionPattern;
165 matchAndRewrite(AIEX::NpuMaskWrite32Op op, OpAdaptor adaptor,
166 ConversionPatternRewriter &rewriter)
const override {
168 std::optional<uint32_t> address =
172 if (!address || !mask || !value)
173 return op.emitOpError(
174 "cannot lower to cert.maskwrite32 with non-constant "
175 "address, mask, or value");
178 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
182 uint32_t absoluteAddr = *absAddress;
185 std::optional<uint32_t> col = op.getColumn();
186 std::optional<uint32_t> row = op.getRow();
189 uint32_t expectedUpperBits = ((*col & 0xff) << tm.getColumnShift()) |
190 ((*row & 0xff) << tm.getRowShift());
193 uint32_t origAddress = *address;
194 uint32_t origUpperBits = origAddress & ~0xfffff;
195 if (origUpperBits != 0 && origUpperBits != expectedUpperBits) {
196 op.emitWarning() <<
"address upper bits (0x"
197 << llvm::utohexstr(origUpperBits)
198 <<
") don't match row=" << *row <<
" col=" << *col
199 <<
" computed bits (0x"
200 << llvm::utohexstr(expectedUpperBits) <<
")";
204 rewriter.replaceOpWithNewOp<AIEX::CertMaskWrite32Op>(op, absoluteAddr,
210struct NpuMaskPollToCertMaskPoll32 : OpConversionPattern<AIEX::NpuMaskPollOp> {
211 using OpConversionPattern::OpConversionPattern;
214 matchAndRewrite(AIEX::NpuMaskPollOp op, OpAdaptor adaptor,
215 ConversionPatternRewriter &rewriter)
const override {
216 std::optional<uint32_t> address =
220 if (!address || !mask || !value)
221 return op.emitOpError(
"cannot lower to cert.maskpoll32 with non-constant "
222 "address, mask, or value");
224 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
228 rewriter.replaceOpWithNewOp<AIEX::CertMaskPoll32Op>(op, *absAddress, *mask,
234struct NpuBlockWriteToCertUcDma : OpConversionPattern<AIEX::NpuBlockWriteOp> {
235 using OpConversionPattern::OpConversionPattern;
238 matchAndRewrite(AIEX::NpuBlockWriteOp op, OpAdaptor adaptor,
239 ConversionPatternRewriter &rewriter)
const override {
241 memref::GetGlobalOp dataOperand =
242 dyn_cast_or_null<memref::GetGlobalOp>(op.getData().getDefiningOp());
245 MemRefType dataType = cast<MemRefType>(dataOperand.getResult().getType());
246 uint32_t dataSize = dataType.getNumElements();
249 std::string symbolName =
"chain_" + std::to_string(
id);
250 while (op->getParentOfType<AIE::DeviceOp>().lookupSymbol(symbolName))
251 symbolName =
"chain_" + std::to_string(++
id);
254 rewriter.replaceOpWithNewOp<AIEX::CertUcDmaWriteDesSyncOp>(op, symbolName);
258 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
267 Block *deviceBody = parentDevice.getBody();
268 Operation *insertAfter =
nullptr;
269 for (Operation &bodyOp : *deviceBody) {
270 if (isa<AIEX::CertUcDmaChainOp>(bodyOp) || isa<memref::GlobalOp>(bodyOp))
271 insertAfter = &bodyOp;
276 rewriter.setInsertionPointAfter(insertAfter);
278 rewriter.setInsertionPointToStart(deviceBody);
280 auto symbolAttr = rewriter.getStringAttr(symbolName);
282 AIEX::CertUcDmaChainOp::create(rewriter, op.getLoc(), symbolAttr);
284 Block *bb =
new Block();
285 chainOp.getRegion().push_back(bb);
286 rewriter.setInsertionPointToStart(bb);
287 AIEX::CertUcDmaBdOp::create(rewriter, op.getLoc(), dataOperand.getName(),
288 op.getAddress(), dataSize,
false);
290 AIEX::CertUcDmaChainOp::ensureTerminator(chainOp.getBody(), rewriter,
296struct NpuSyncToCertWaitTCTS : OpConversionPattern<AIEX::NpuSyncOp> {
297 using OpConversionPattern::OpConversionPattern;
300 matchAndRewrite(AIEX::NpuSyncOp op, OpAdaptor adaptor,
301 ConversionPatternRewriter &rewriter)
const override {
303 std::optional<uint32_t> colOpt =
305 std::optional<uint32_t> channelOpt =
307 std::optional<uint32_t> directionOpt =
309 if (!rowOpt || !colOpt || !channelOpt || !directionOpt)
310 return op.emitOpError(
311 "cannot lower to cert.wait_tcts with non-constant sync parameters");
312 uint32_t row = *rowOpt;
313 uint32_t col = *colOpt;
318 const int row_id_shift = 16;
319 const int col_id_shift = 21;
320 uint16_t tile_id = col << (col_id_shift - row_id_shift) | row;
321 uint32_t channel = *channelOpt;
322 uint32_t direction = *directionOpt;
324 const std::vector<int> chan2actor_shim_s2mm = {0, 2, 3, 4};
325 const std::vector<int> chan2actor_shim_mm2s = {6, 7, 8, 9, 10, 11, 12, 13};
326 const std::vector<int> chan2actor_mem_s2mm = {1, 2, 3, 4, 5, 6, 7};
327 const std::vector<int> chan2actor_mem_mm2s = {16, 17, 18, 19, 20,
329 const std::vector<int> chan2actor_tile_s2mm = {0, 1};
330 const std::vector<int> chan2actor_tile_mm2s = {6};
334 direction ==
static_cast<std::underlying_type_t<AIE::DMAChannelDir>
>(
335 AIE::DMAChannelDir::S2MM);
337 const std::vector<int> *chan2actor =
nullptr;
338 if (tm.isCoreTile(col, row))
339 chan2actor = isS2MM ? &chan2actor_tile_s2mm : &chan2actor_tile_mm2s;
340 else if (tm.isMemTile(col, row))
341 chan2actor = isS2MM ? &chan2actor_mem_s2mm : &chan2actor_mem_mm2s;
343 chan2actor = isS2MM ? &chan2actor_shim_s2mm : &chan2actor_shim_mm2s;
345 size_t chanIdx =
static_cast<size_t>(channel);
346 if (chanIdx >= chan2actor->size()) {
347 op.emitError(
"invalid DMA channel ")
348 << channel <<
" for " << (isS2MM ?
"S2MM" :
"MM2S")
349 <<
" direction in NpuSyncToCertWaitTCTS conversion";
353 uint8_t actor_id =
static_cast<uint8_t
>((*chan2actor)[chanIdx]);
354 uint8_t num_tcts = 1;
355 rewriter.replaceOpWithNewOp<AIEX::CertWaitTCTSOp>(op, tile_id, actor_id,
361struct NpuLoadPdiToCertLoadPdi : OpConversionPattern<AIEX::NpuLoadPdiOp> {
362 using OpConversionPattern::OpConversionPattern;
365 matchAndRewrite(AIEX::NpuLoadPdiOp op, OpAdaptor adaptor,
366 ConversionPatternRewriter &rewriter)
const override {
368 auto deviceRef = op.getDeviceRef();
372 StringRef deviceSymName = *deviceRef;
375 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
380 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
383 rewriter.replaceOpWithNewOp<AIEX::CertLoadPdiOp>(
384 op, rewriter.getUI32IntegerAttr(pdiId),
385 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
392 using OpRewritePattern::OpRewritePattern;
394 LogicalResult matchAndRewrite(AIEX::RunOp op,
395 PatternRewriter &rewriter)
const override {
397 AIE::RuntimeSequenceOp calleeRuntimeSequence =
398 op.getCalleeRuntimeSequenceOp();
399 if (!calleeRuntimeSequence)
403 Region &calleeBody = calleeRuntimeSequence.getBody();
407 ValueRange values = op.getArgs();
408 if (values.size() != calleeBody.getNumArguments()) {
409 op.emitError(
"number of run op arguments (")
411 <<
") does not match number of callee runtime sequence "
413 << calleeBody.getNumArguments() <<
")";
416 for (
unsigned i = 0, n = calleeBody.getNumArguments(); i < n; i++) {
417 BlockArgument arg = calleeBody.getArgument(i);
418 Value val = values[i];
419 argMap.map(arg, val);
423 rewriter.setInsertionPoint(op);
424 for (Operation &o : calleeBody.front().getOperations()) {
426 if (isa<AIE::EndOp>(o))
428 rewriter.clone(o, argMap);
432 rewriter.eraseOp(op);
439 using OpRewritePattern::OpRewritePattern;
441 LogicalResult matchAndRewrite(AIEX::ConfigureOp op,
442 PatternRewriter &rewriter)
const override {
444 AIE::DeviceOp referencedDevice = op.getReferencedDeviceOp();
445 if (!referencedDevice)
449 StringRef deviceSymName = referencedDevice.getSymName();
452 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
457 auto existingSection = dyn_cast_if_present<AIEX::CertSectionOp>(
458 parentDevice.lookupSymbol(deviceSymName));
459 if (existingSection) {
462 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
464 rewriter.setInsertionPoint(op);
465 AIEX::CertLoadPdiOp::create(
466 rewriter, op.getLoc(), rewriter.getUI32IntegerAttr(pdiId),
467 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
470 IRMapping bodyMapper;
471 for (Operation &o : op.getRegion().front().getOperations()) {
472 if (isa<AIE::EndOp>(o))
474 rewriter.clone(o, bodyMapper);
477 rewriter.eraseOp(op);
482 rewriter.setInsertionPoint(parentDevice.getBody()->getTerminator());
483 auto sectionOp = AIEX::CertSectionOp::create(
484 rewriter, op.getLoc(), rewriter.getStringAttr(deviceSymName));
487 Block *sectionBlock =
new Block();
488 sectionOp.getBody().push_back(sectionBlock);
489 rewriter.setInsertionPointToStart(sectionBlock);
492 auto pageOp = AIEX::CertPageOp::create(rewriter, op.getLoc());
493 Block *pageBlock =
new Block();
494 pageOp.getBody().push_back(pageBlock);
495 rewriter.setInsertionPointToStart(pageBlock);
499 uint32_t maxJobId = 0;
500 parentDevice.walk([&](AIEX::CertJobOp certJobOp) {
501 maxJobId = std::max(maxJobId, certJobOp.getJobId());
503 uint32_t sectionJobId = maxJobId + 1;
505 auto jobOp = AIEX::CertJobOp::create(rewriter, op.getLoc(), sectionJobId);
506 Block *jobBlock =
new Block();
507 jobOp.getBody().push_back(jobBlock);
508 rewriter.setInsertionPointToStart(jobBlock);
517 AIE::RuntimeSequenceOp configureSeq =
nullptr;
518 referencedDevice.walk([&](AIE::RuntimeSequenceOp seq) {
519 if (seq.getSymName() ==
"configure") {
527 for (Operation &o : configureSeq.getRegion().front().getOperations()) {
528 if (isa<AIE::EndOp>(o))
530 rewriter.clone(o, mapper);
535 AIEX::CertPageOp configPage =
nullptr;
536 for (
auto &op : referencedDevice.getBody()->getOperations()) {
537 if (
auto page = dyn_cast<AIEX::CertPageOp>(op)) {
546 for (
auto &op : configPage.getBody().front().getOperations()) {
547 if (
auto job = dyn_cast<AIEX::CertJobOp>(op)) {
548 for (Operation &jobOp : job.getBody().front().getOperations()) {
549 if (isa<AIE::EndOp>(jobOp))
551 rewriter.clone(jobOp, mapper);
560 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), rewriter, op.getLoc());
561 AIEX::CertPageOp::ensureTerminator(pageOp.getBody(), rewriter, op.getLoc());
562 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), rewriter,
567 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
569 rewriter.setInsertionPoint(op);
570 AIEX::CertLoadPdiOp::create(
571 rewriter, op.getLoc(), rewriter.getUI32IntegerAttr(pdiId),
572 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
577 IRMapping bodyMapper;
578 for (Operation &o : op.getRegion().front().getOperations()) {
579 if (isa<AIE::EndOp>(o))
581 rewriter.clone(o, bodyMapper);
585 rewriter.eraseOp(op);
590struct NpuAddressPatchToCertApplyOffset57
591 : OpConversionPattern<AIEX::NpuAddressPatchOp> {
592 using OpConversionPattern::OpConversionPattern;
595 matchAndRewrite(AIEX::NpuAddressPatchOp op, OpAdaptor adaptor,
596 ConversionPatternRewriter &rewriter)
const override {
598 Block::iterator it(op);
599 while (it != op->getBlock()->begin()) {
601 auto blockWriteOp = dyn_cast<AIEX::NpuBlockWriteOp>(*it);
606 uint32_t addr = op.getAddr();
607 int col = (addr >> tm.getColumnShift()) & 0x1f;
608 int row = (addr >> tm.getRowShift()) & 0x1f;
609 if (!tm.isValidTile({col, row}))
613 if (blockWriteOp.getAddress() + tm.getDmaBdAddressOffset(col, row) !=
617 Value data = blockWriteOp.getData();
618 auto getGlobalOp = dyn_cast<memref::GetGlobalOp>(data.getDefiningOp());
623 std::optional<uint32_t> argIdx = op.getArgIdx();
626 rewriter.setInsertionPoint(blockWriteOp);
627 rewriter.replaceOpWithNewOp<AIEX::CertApplyOffset57Op>(
628 op, getGlobalOp.getName(), 1, *argIdx);
636struct MergeConsecutiveCertUcDmaWriteDesSyncOps
638 using OpRewritePattern::OpRewritePattern;
640 LogicalResult matchAndRewrite(AIEX::CertUcDmaWriteDesSyncOp op,
641 PatternRewriter &rewriter)
const override {
649 auto isTransparentToMerge = [](Operation *o) {
650 return isa<AIEX::CertNopOp>(o);
654 Block::iterator it(op);
655 AIEX::CertUcDmaWriteDesSyncOp prevWriteDesSync =
nullptr;
656 while (it != op->getBlock()->begin() && !prevWriteDesSync) {
658 Operation *prevOp = &*it;
659 prevWriteDesSync = dyn_cast<AIEX::CertUcDmaWriteDesSyncOp>(prevOp);
660 if (!prevWriteDesSync && !isTransparentToMerge(prevOp))
663 if (!prevWriteDesSync)
667 StringRef sym_name = op.getSymbol();
668 StringRef prev_sym_name = prevWriteDesSync.getSymbol();
669 auto chain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
670 op->getParentOfType<AIE::DeviceOp>().lookupSymbol(sym_name));
671 auto prevChain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
672 prevWriteDesSync->getParentOfType<AIE::DeviceOp>().lookupSymbol(
674 if (!chain || !prevChain)
684 auto deviceOp = op->getParentOfType<AIE::DeviceOp>();
685 auto isSingleUse = [&](AIEX::CertUcDmaChainOp c) {
686 auto uses = SymbolTable::getSymbolUses(c, deviceOp);
687 return uses && llvm::hasSingleElement(*uses);
689 if (!isSingleUse(chain) || !isSingleUse(prevChain))
699 auto chainSize = [](AIEX::CertUcDmaChainOp c) {
701 for (
auto bdOp : c.getBody().front().getOps<AIEX::CertUcDmaBdOp>())
702 size += bdOp.getLength() *
sizeof(int) + 16;
705 if ((chainSize(chain) + chainSize(prevChain)) >= cert_page_size)
709 rewriter.setInsertionPointToStart(&chain.getBody().front());
710 for (
auto &o : prevChain.getBody().front().getOperations()) {
711 auto bdOp = dyn_cast<AIEX::CertUcDmaBdOp>(o);
714 AIEX::CertUcDmaBdOp::create(
715 rewriter, bdOp.getLoc(), bdOp.getRemoteAddress(),
716 bdOp.getLocalAddress(), bdOp.getLength(),
true);
718 rewriter.eraseOp(prevChain);
719 rewriter.eraseOp(prevWriteDesSync);
724struct SplitNpuBlockWriteOpPattern :
OpRewritePattern<AIEX::NpuBlockWriteOp> {
725 using OpRewritePattern::OpRewritePattern;
727 LogicalResult matchAndRewrite(AIEX::NpuBlockWriteOp op,
728 PatternRewriter &rewriter)
const override {
730 memref::GetGlobalOp dataOperand =
731 dyn_cast_or_null<memref::GetGlobalOp>(op.getData().getDefiningOp());
735 MemRefType dataType = cast<MemRefType>(dataOperand.getResult().getType());
736 uint32_t dataSize = dataType.getNumElements();
738 uint32_t dataSizeBytes = dataSize *
sizeof(int);
739 if (dataSizeBytes < cert_page_size)
742 auto loc = op.getLoc();
745 uint32_t splitElements = dataSize / 2;
746 uint32_t firstChunkSize = splitElements;
747 uint32_t secondChunkSize = dataSize - splitElements;
750 auto deviceOp = op->getParentOfType<AIE::DeviceOp>();
751 auto originalGlobal = dyn_cast_if_present<memref::GlobalOp>(
752 deviceOp.lookupSymbol(dataOperand.getName()));
757 auto originalData = originalGlobal.getInitialValue();
761 auto denseData = dyn_cast<DenseIntElementsAttr>(*originalData);
766 auto dataValues = denseData.getValues<APInt>();
767 std::vector<APInt> firstChunkData(dataValues.begin(),
768 dataValues.begin() + firstChunkSize);
769 std::vector<APInt> secondChunkData(dataValues.begin() + firstChunkSize,
773 auto elementType = rewriter.getI32Type();
774 auto firstChunkType = MemRefType::get({firstChunkSize}, elementType);
775 auto secondChunkType = MemRefType::get({secondChunkSize}, elementType);
776 TensorType firstTensorType =
777 RankedTensorType::get({firstChunkSize}, elementType);
778 TensorType secondTensorType =
779 RankedTensorType::get({secondChunkSize}, elementType);
781 auto firstChunkAttr =
782 DenseIntElementsAttr::get(firstTensorType, firstChunkData);
783 auto secondChunkAttr =
784 DenseIntElementsAttr::get(secondTensorType, secondChunkData);
787 std::string firstName = dataOperand.getName().str() +
"_split_0";
788 std::string secondName = dataOperand.getName().str() +
"_split_1";
792 while (deviceOp.lookupSymbol(firstName)) {
794 dataOperand.getName().str() +
"_split_0_" + std::to_string(counter++);
797 while (deviceOp.lookupSymbol(secondName)) {
799 dataOperand.getName().str() +
"_split_1_" + std::to_string(counter++);
803 rewriter.setInsertionPoint(originalGlobal);
804 memref::GlobalOp::create(rewriter, loc, firstName,
805 rewriter.getStringAttr(
"private"), firstChunkType,
806 firstChunkAttr,
true,
nullptr);
808 memref::GlobalOp::create(rewriter, loc, secondName,
809 rewriter.getStringAttr(
"private"), secondChunkType,
810 secondChunkAttr,
true,
nullptr);
813 rewriter.setInsertionPoint(op);
815 auto firstGetGlobal =
816 memref::GetGlobalOp::create(rewriter, loc, firstChunkType, firstName);
817 auto secondGetGlobal =
818 memref::GetGlobalOp::create(rewriter, loc, secondChunkType, secondName);
820 uint32_t baseAddr = op.getAddress();
822 AIEX::NpuBlockWriteOp::create(rewriter, loc, baseAddr,
823 firstGetGlobal.getResult(),
nullptr,
nullptr,
826 AIEX::NpuBlockWriteOp::create(rewriter, loc, baseAddr + firstChunkSize * 4,
827 secondGetGlobal.getResult(),
nullptr,
nullptr,
831 rewriter.eraseOp(op);
833 LLVM_DEBUG(llvm::outs()
834 <<
"Split NpuBlockWriteOp with data size: " << dataSizeBytes
835 <<
" bytes into chunks of " << firstChunkSize <<
" and "
836 << secondChunkSize <<
" elements\n");
842struct AIENpuToCertPass
843 : xilinx::AIEX::impl::AIENpuToCertBase<AIENpuToCertPass> {
844 void runOnOperation()
override {
845 auto moduleOp = getOperation();
847 return signalPassFailure();
850 llvm::SmallVector<AIE::DeviceOp, 4> devices;
851 for (
auto &op : moduleOp.getBody()->getOperations()) {
852 if (
auto deviceOp = dyn_cast<AIE::DeviceOp>(op)) {
853 devices.push_back(deviceOp);
860 AIE::DeviceOp mainDevice =
nullptr;
861 for (
auto dev : devices) {
862 if (dev.getSymName() == deviceName) {
870 moduleOp.emitError(
"no device found matching --device-name \"")
871 << deviceName <<
"\"";
877 for (
auto dev : devices) {
878 if (dev != mainDevice) {
885 llvm::SmallVector<AIE::DeviceOp, 4> devicesToRemove;
886 for (
auto dev : devices) {
887 if (dev != mainDevice) {
888 devicesToRemove.push_back(dev);
892 for (
auto dev : devicesToRemove) {
897 void processDevice(AIE::DeviceOp currentDevice) {
898 auto moduleOp = currentDevice->getParentOfType<ModuleOp>();
905 llvm::SmallVector<StringRef, 4> referencedDeviceSyms;
906 currentDevice.walk([&](AIEX::NpuLoadPdiOp loadPdiOp) {
907 if (loadPdiOp.getDeviceRef())
908 referencedDeviceSyms.push_back(*loadPdiOp.getDeviceRef());
911 if (!referencedDeviceSyms.empty()) {
912 OpBuilder builder(&getContext());
914 for (StringRef refSymName : referencedDeviceSyms) {
916 if (currentDevice.lookupSymbol(refSymName))
920 AIE::DeviceOp refDevice =
nullptr;
921 for (
auto &op : moduleOp.getBody()->getOperations()) {
922 if (
auto dev = dyn_cast<AIE::DeviceOp>(op)) {
923 if (dev.getSymName() == refSymName) {
934 AIE::RuntimeSequenceOp configureSeq =
nullptr;
935 refDevice.walk([&](AIE::RuntimeSequenceOp seq) {
936 if (seq.getSymName() ==
"configure") {
945 builder.setInsertionPoint(currentDevice.getBody()->getTerminator());
947 AIEX::CertSectionOp::create(builder, configureSeq.getLoc(),
948 builder.getStringAttr(refSymName));
950 Block *sectionBlock =
new Block();
951 sectionOp.getBody().push_back(sectionBlock);
952 builder.setInsertionPointToStart(sectionBlock);
956 AIEX::CertPageOp::create(builder, configureSeq.getLoc());
957 Block *pageBlock =
new Block();
958 pageOp.getBody().push_back(pageBlock);
959 builder.setInsertionPointToStart(pageBlock);
962 uint32_t maxJobId = 0;
963 currentDevice.walk([&](AIEX::CertJobOp certJobOp) {
964 maxJobId = std::max(maxJobId, certJobOp.getJobId());
968 auto jobOp = AIEX::CertJobOp::create(builder, configureSeq.getLoc(),
973 configureSeq.getRegion().cloneInto(&jobOp.getBody(), mapper);
976 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), builder,
977 configureSeq.getLoc());
978 AIEX::CertPageOp::ensureTerminator(pageOp.getBody(), builder,
979 configureSeq.getLoc());
980 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), builder,
981 configureSeq.getLoc());
987 RewritePatternSet p_run_inline(&getContext());
988 p_run_inline.insert<RunOpInlining>(&getContext());
989 if (failed(applyPatternsGreedily(currentDevice, std::move(p_run_inline))))
990 return signalPassFailure();
993 RewritePatternSet p_configure(&getContext());
994 p_configure.insert<ConfigureOpToCertSection>(&getContext());
995 if (failed(applyPatternsGreedily(currentDevice, std::move(p_configure))))
996 return signalPassFailure();
998 ConversionTarget target(getContext());
999 target.addIllegalOp<AIE::RuntimeSequenceOp>();
1001 target.addLegalOp<AIEX::CertApplyOffset57Op>();
1002 target.addLegalOp<AIEX::CertJobOp>();
1003 target.addLegalOp<AIEX::CertPageOp>();
1004 target.addLegalOp<AIEX::CertSectionOp>();
1005 target.addLegalOp<AIEX::CertLoadPdiOp>();
1006 target.addLegalOp<AIEX::CertMaskWrite32Op>();
1007 target.addLegalOp<AIEX::CertMaskPoll32Op>();
1008 target.addLegalOp<AIEX::CertUcDmaWriteDesSyncOp>();
1009 target.addLegalOp<AIEX::CertUcDmaChainOp>();
1010 target.addLegalOp<AIEX::CertUcDmaBdOp>();
1011 target.addLegalOp<AIEX::CertWrite32Op>();
1012 target.addLegalOp<AIEX::CertWaitTCTSOp>();
1013 target.addLegalOp<AIEX::ConfigureOp>();
1014 target.addLegalDialect<AIE::AIEDialect>();
1016 RewritePatternSet p0(&getContext());
1017 p0.insert<RuntimeSequenceToCertJob>(&getContext());
1019 if (failed(applyPartialConversion(currentDevice, target, std::move(p0))))
1020 return signalPassFailure();
1022 target.addIllegalOp<AIEX::NpuAddressPatchOp>();
1025 RewritePatternSet p1(&getContext());
1026 p1.insert<NpuAddressPatchToCertApplyOffset57>(&getContext());
1028 if (failed(applyPartialConversion(currentDevice, target, std::move(p1))))
1029 return signalPassFailure();
1033 RewritePatternSet p(&getContext());
1034 p.insert<SplitNpuBlockWriteOpPattern>(&getContext());
1035 if (failed(applyPatternsGreedily(getOperation(), std::move(p))))
1036 signalPassFailure();
1039 target.addIllegalOp<AIEX::NpuBlockWriteOp>();
1040 target.addIllegalOp<AIEX::NpuBlockWriteValuesOp>();
1041 target.addIllegalOp<AIEX::NpuMaskWrite32Op>();
1042 target.addIllegalOp<AIEX::NpuMaskPollOp>();
1043 target.addIllegalOp<AIEX::NpuSyncOp>();
1044 target.addIllegalOp<AIEX::NpuWrite32Op>();
1045 target.addIllegalOp<AIEX::NpuLoadPdiOp>();
1047 RewritePatternSet p2(&getContext());
1048 p2.insert<NpuBlockWriteToCertUcDma>(&getContext());
1049 p2.insert<NpuMaskWrite32ToCertMaskWrite32>(&getContext());
1050 p2.insert<NpuMaskPollToCertMaskPoll32>(&getContext());
1051 p2.insert<NpuWrite32ToCertWrite32>(&getContext());
1052 p2.insert<NpuSyncToCertWaitTCTS>(&getContext());
1053 p2.insert<NpuLoadPdiToCertLoadPdi>(&getContext());
1055 if (failed(applyPartialConversion(currentDevice, target, std::move(p2))))
1056 return signalPassFailure();
1062 RewritePatternSet cleanup(&getContext());
1063 if (failed(applyPatternsGreedily(getOperation(), std::move(cleanup))))
1064 return signalPassFailure();
1070 OpBuilder builder(&getContext());
1073 llvm::SetVector<StringRef> referencedDeviceNames;
1074 currentDevice.walk([&](AIEX::CertLoadPdiOp loadPdiOp) {
1075 StringRef refSymName = loadPdiOp.getSymbol();
1076 if (!currentDevice.lookupSymbol(refSymName))
1077 referencedDeviceNames.insert(refSymName);
1081 for (StringRef refSymName : referencedDeviceNames) {
1083 AIE::DeviceOp refDevice =
nullptr;
1084 for (
auto &op : moduleOp.getBody()->getOperations()) {
1085 if (
auto dev = dyn_cast<AIE::DeviceOp>(op)) {
1086 if (dev.getSymName() == refSymName) {
1098 llvm::SmallVector<AIEX::CertPageOp, 4> certPages;
1099 for (
auto &op : refDevice.getBody()->getOperations()) {
1100 if (
auto pageOp = dyn_cast<AIEX::CertPageOp>(op)) {
1101 certPages.push_back(pageOp);
1105 if (certPages.empty())
1109 builder.setInsertionPoint(currentDevice.getBody()->getTerminator());
1110 auto sectionOp = AIEX::CertSectionOp::create(
1111 builder, refDevice.getLoc(), builder.getStringAttr(refSymName));
1113 Block *sectionBlock =
new Block();
1114 sectionOp.getBody().push_back(sectionBlock);
1118 for (
auto pageOp : certPages) {
1119 builder.setInsertionPointToEnd(sectionBlock);
1120 builder.clone(*pageOp.getOperation(), mapper);
1124 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), builder,
1125 refDevice.getLoc());
1130 currentDevice.walk([&](AIEX::CertJobOp jobOp) {
1131 Block &jobBlock = jobOp.getBody().front();
1134 for (
int i = jobBlock.getNumArguments() - 1; i >= 0; --i) {
1135 BlockArgument arg = jobBlock.getArgument(i);
1136 if (arg.use_empty()) {
1137 jobBlock.eraseArgument(i);
1151static void updateCostForOp(Operation &o, AIE::DeviceOp deviceOp,
1152 uint32_t &text_cost, uint32_t &data_cost) {
1156 if (isa<AIEX::CertLocalBarrierOp>(o)) {
1158 }
else if (isa<AIEX::CertRemoteBarrierOp>(o)) {
1160 }
else if (isa<AIEX::CertRelAcqSyncOp>(o)) {
1162 }
else if (isa<AIEX::CertWaitTCTSOp>(o)) {
1164 }
else if (isa<AIEX::CertMaskWrite32Op, AIEX::CertMaskPoll32Op>(o)) {
1166 }
else if (isa<AIEX::CertWrite32Op>(o)) {
1168 }
else if (isa<AIEX::CertApplyOffset57Op>(o)) {
1170 }
else if (isa<AIEX::CertNopOp>(o)) {
1172 }
else if (isa<AIEX::CertPreemptOp>(o)) {
1174 }
else if (isa<AIEX::CertLoadPdiOp>(o)) {
1177 }
else if (
auto syncOp = dyn_cast<AIEX::CertUcDmaWriteDesSyncOp>(o)) {
1180 StringRef sym_name = syncOp.getSymbol();
1181 auto chain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
1182 deviceOp.lookupSymbol(sym_name));
1185 for (
auto bdOp : chain.getBody().front().getOps<AIEX::CertUcDmaBdOp>()) {
1187 StringRef data_sym_name = bdOp.getRemoteAddress();
1188 auto global = dyn_cast_if_present<memref::GlobalOp>(
1189 deviceOp.lookupSymbol(data_sym_name));
1192 auto initVal = global.getInitialValue();
1195 auto data = dyn_cast<DenseIntElementsAttr>(*initVal);
1198 data_cost += data.getNumElements() * 4;
1203static uint32_t estimateCost(AIEX::CertPageOp op, uint32_t split_target,
1204 AIEX::CertJobOp &split_job,
1205 Block::iterator &split_iter,
1206 bool &found_split_point) {
1207 uint32_t text_cost = 32;
1208 uint32_t data_cost = 0;
1209 found_split_point =
false;
1210 AIE::DeviceOp deviceOp = op->getParentOfType<AIE::DeviceOp>();
1212 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
1215 text_cost += cert_job_start_cost;
1216 for (
auto &o : job.getBody().front().getOperations()) {
1217 Block::iterator current(&o);
1218 if (!found_split_point && !isa<AIE::EndOp>(o) &&
1219 current != job.getBody().front().begin() &&
1220 (text_cost + data_cost) >= split_target) {
1222 split_iter = current;
1223 found_split_point =
true;
1226 updateCostForOp(o, deviceOp, text_cost, data_cost);
1228 if (!found_split_point && (text_cost + data_cost) >= split_target) {
1229 Block::iterator next = current;
1231 if (next != job.getBody().front().end() && !isa<AIE::EndOp>(*next)) {
1234 found_split_point =
true;
1235 }
else if (!isa<AIE::EndOp>(o) &&
1236 current != job.getBody().front().begin()) {
1240 split_iter = current;
1241 found_split_point =
true;
1245 text_cost += cert_job_end_cost;
1247 return text_cost + data_cost;
1253static void computeBarrierSpans(AIEX::CertPageOp page,
1254 std::map<Operation *, int> &flatIndex,
1255 std::map<
int, std::pair<int, int>> &spans) {
1257 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
1258 for (
auto &o : job.getBody().front().getOperations()) {
1259 if (isa<AIE::EndOp>(o))
1261 flatIndex[&o] = idx;
1262 if (
auto bar = dyn_cast<AIEX::CertLocalBarrierOp>(o)) {
1263 int id = bar.getLocalBarrierId();
1264 auto it = spans.find(
id);
1265 if (it == spans.end())
1266 spans[id] = {idx, idx};
1268 it->second.first = std::min(it->second.first, idx);
1269 it->second.second = std::max(it->second.second, idx);
1279static bool cutSeparatesBarrier(
const std::map<
int, std::pair<int, int>> &spans,
1281 return llvm::any_of(spans, [p](
const auto &entry) {
1282 auto [lo, hi] = entry.second;
1283 return lo < p && hi >= p;
1290static bool findLegalSplitPoint(AIEX::CertPageOp page, uint32_t split_target,
1291 const std::map<Operation *, int> &flatIndex,
1292 const std::map<
int, std::pair<int, int>> &spans,
1293 AIEX::CertJobOp &split_job,
1294 Block::iterator &split_iter) {
1295 AIE::DeviceOp deviceOp = page->getParentOfType<AIE::DeviceOp>();
1296 uint32_t text_cost = 32;
1297 uint32_t data_cost = 0;
1299 uint32_t best_delta = 0;
1302 bool sawOpInEarlierJob =
false;
1303 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
1304 text_cost += cert_job_start_cost;
1305 bool sawOpInJob =
false;
1306 for (
auto &o : job.getBody().front().getOperations()) {
1307 if (isa<AIE::EndOp>(o))
1309 Block::iterator current(&o);
1315 if (sawOpInJob || sawOpInEarlierJob) {
1316 auto fi = flatIndex.find(&o);
1317 int p = fi == flatIndex.end() ? 0 : fi->second;
1318 if (!cutSeparatesBarrier(spans, p)) {
1319 uint32_t cost = text_cost + data_cost;
1321 cost > split_target ? cost - split_target : split_target - cost;
1322 if (!found || delta < best_delta) {
1326 split_iter = current;
1330 updateCostForOp(o, deviceOp, text_cost, data_cost);
1333 text_cost += cert_job_end_cost;
1334 sawOpInEarlierJob |= sawOpInJob;
1345 using OpRewritePattern::OpRewritePattern;
1350 static AIEX::CertPageOp makePageAfter(PatternRewriter &rewriter,
1351 Operation *after, Location loc) {
1352 rewriter.setInsertionPointAfter(after);
1353 auto page = AIEX::CertPageOp::create(rewriter, loc);
1354 page.getBody().push_back(
new Block());
1355 AIEX::CertPageOp::ensureTerminator(page.getBody(), rewriter, loc);
1359 LogicalResult matchAndRewrite(AIEX::CertJobOp jobOp,
1360 PatternRewriter &rewriter)
const override {
1363 Operation *fullPageOp =
nullptr;
1364 Block::iterator fullPageOpIter;
1365 for (Block::iterator it = jobOp.getBody().front().begin();
1366 it != jobOp.getBody().front().end(); ++it) {
1367 if (isa<AIEX::CertLoadPdiOp, AIEX::CertPreemptOp>(*it)) {
1369 fullPageOpIter = it;
1377 auto parentPage = jobOp->getParentOfType<AIEX::CertPageOp>();
1383 for (Operation &op : jobOp.getBody().front().getOperations())
1384 if (!isa<AIE::EndOp>(op))
1392 SmallVector<Operation *> leadingSiblings, trailingSiblings;
1393 bool pastSelf =
false;
1394 for (Operation &op : parentPage.getBody().front()) {
1395 if (isa<AIE::EndOp>(op))
1397 if (&op == jobOp.getOperation()) {
1401 (pastSelf ? trailingSiblings : leadingSiblings).push_back(&op);
1405 if (opCount == 1 && leadingSiblings.empty() && trailingSiblings.empty())
1408 auto loc = jobOp.getLoc();
1412 Operation *anchor = parentPage.getOperation();
1416 auto isolatedPage = makePageAfter(rewriter, anchor, loc);
1417 jobOp->moveBefore(isolatedPage.getBody().front().getTerminator());
1418 anchor = isolatedPage;
1422 auto parentDevice = jobOp->getParentOfType<AIE::DeviceOp>();
1423 uint32_t maxJobId = 0;
1424 parentDevice.walk([&](AIEX::CertJobOp certJobOp) {
1425 maxJobId = std::max(maxJobId, certJobOp.getJobId());
1427 uint32_t beforeJobId = jobOp.getJobId();
1428 uint32_t fullPageJobId = maxJobId + 1;
1429 uint32_t afterJobId = maxJobId + 2;
1431 SmallVector<Operation *> beforeOps, afterOps;
1432 for (Block::iterator it = jobOp.getBody().front().begin();
1433 it != fullPageOpIter; ++it)
1434 if (!isa<AIE::EndOp>(*it))
1435 beforeOps.push_back(&*it);
1436 Block::iterator afterStart = std::next(fullPageOpIter);
1437 for (Block::iterator it = afterStart; it != jobOp.getBody().front().end();
1439 if (!isa<AIE::EndOp>(*it))
1440 afterOps.push_back(&*it);
1442 auto emitJobPage = [&](uint32_t jobId, ArrayRef<Operation *> ops) {
1443 auto page = makePageAfter(rewriter, anchor, loc);
1444 rewriter.setInsertionPoint(page.getBody().front().getTerminator());
1445 auto job = AIEX::CertJobOp::create(rewriter, loc, jobId);
1446 job.getBody().push_back(
new Block());
1447 AIEX::CertJobOp::ensureTerminator(job.getBody(), rewriter, loc);
1448 for (Operation *op : ops)
1449 op->moveBefore(job.getBody().front().getTerminator());
1453 if (!beforeOps.empty())
1454 emitJobPage(beforeJobId, beforeOps);
1455 emitJobPage(fullPageJobId, {fullPageOp});
1456 if (!afterOps.empty())
1457 emitJobPage(afterJobId, afterOps);
1459 rewriter.eraseOp(jobOp);
1464 if (!trailingSiblings.empty()) {
1465 auto tailPage = makePageAfter(rewriter, anchor, loc);
1466 for (Operation *op : trailingSiblings)
1467 op->moveBefore(tailPage.getBody().front().getTerminator());
1470 if (llvm::all_of(parentPage.getBody().front(),
1471 [](Operation &op) { return isa<AIE::EndOp>(op); }))
1472 rewriter.eraseOp(parentPage);
1484 SplitCertPageOpPattern(MLIRContext *context,
bool *hadError)
1485 : OpRewritePattern(context), hadError(hadError) {}
1487 LogicalResult matchAndRewrite(AIEX::CertPageOp op,
1488 PatternRewriter &rewriter)
const override {
1490 constexpr uint32_t split_threshold = cert_page_size;
1492 AIEX::CertJobOp split_job;
1493 Block::iterator split_iter;
1494 bool found_split_point =
false;
1495 uint32_t cost = estimateCost(op, cert_page_size / 2, split_job, split_iter,
1497 LLVM_DEBUG(llvm::outs()
1498 <<
"Estimate cost for page: " <<
" is " << cost <<
"\n");
1500 if (cost < split_threshold)
1503 bool isImplicit = op->hasAttr(
"cert.implicit");
1505 std::map<Operation *, int> flatIndex;
1506 std::map<int, std::pair<int, int>> barrierSpans;
1507 computeBarrierSpans(op, flatIndex, barrierSpans);
1511 if (!found_split_point)
1513 findLegalSplitPoint(op, cert_page_size / 2, flatIndex, barrierSpans,
1514 split_job, split_iter);
1521 if (!found_split_point) {
1522 if (cost > cert_page_limit) {
1523 op.emitError() <<
"cert.page is an estimated " << cost
1524 <<
" bytes, over the " << cert_page_limit
1525 <<
"-byte microcontroller page limit, and offers no "
1526 "split point (a split must leave at least one op on "
1527 "each page); break the oversized job into smaller "
1542 if (!barrierSpans.empty()) {
1543 auto fi = flatIndex.find(&*split_iter);
1544 int p = fi == flatIndex.end() ? 0 : fi->second;
1545 if (cutSeparatesBarrier(barrierSpans, p)) {
1546 if (!findLegalSplitPoint(op, cert_page_size / 2, flatIndex,
1547 barrierSpans, split_job, split_iter)) {
1559 if (cost > cert_page_limit) {
1562 <<
"implicit cert.page is an estimated " << cost
1563 <<
" bytes, over the " << cert_page_limit
1564 <<
"-byte microcontroller page limit, and cannot be split "
1565 "without separating a local_barrier participant set "
1566 "across pages; insert an explicit cert.page boundary to "
1567 "co-locate the barrier participants";
1570 <<
"cert.page is an estimated " << cost <<
" bytes, over the "
1572 <<
"-byte microcontroller page limit, and cannot be split "
1573 "without separating a local_barrier participant set "
1574 "across pages; reduce the page's contents or regroup its "
1575 "local_barrier participants so that a legal split point "
1589 <<
"auto-split of implicit cert.page serializes cooperatively-"
1590 "scheduled jobs into separate sequential pages; insert an "
1592 "cert.page boundary to control where the split occurs";
1594 auto loc = op.getLoc();
1598 bool jobBoundaryCut = split_iter == split_job.getBody().front().begin();
1599 [[maybe_unused]]
auto earlierJobHasRealOp = [&]() {
1600 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
1601 if (job == split_job)
1603 for (
auto &o : job.getBody().front().getOperations())
1604 if (!isa<AIE::EndOp>(o))
1609 assert((!jobBoundaryCut || earlierJobHasRealOp()) &&
1610 "a job-boundary cut needs a preceding job with content, else the "
1611 "earlier page is empty and the split makes no progress");
1619 if (!jobBoundaryCut)
1620 op->getParentOfType<AIE::DeviceOp>().walk([&](AIEX::CertJobOp certJobOp) {
1621 if (certJobOp.getJobId() > split_job.getJobId())
1622 certJobOp.setJobId(certJobOp.getJobId() + 1);
1625 auto cloneJobRange = [&](AIEX::CertJobOp sourceJob, uint32_t jobId,
1626 Block::iterator begin, Block::iterator end) {
1627 auto newJobOp = AIEX::CertJobOp::create(rewriter, loc, jobId);
1628 Block *newJobBlock =
new Block();
1629 newJobOp.getBody().push_back(newJobBlock);
1632 Block &sourceBlock = sourceJob.getBody().front();
1633 for (BlockArgument arg : sourceBlock.getArguments()) {
1634 BlockArgument newArg =
1635 newJobBlock->addArgument(arg.getType(), arg.getLoc());
1636 mapper.map(arg, newArg);
1639 rewriter.setInsertionPointToStart(newJobBlock);
1640 for (Block::iterator oi = begin; oi != end; ++oi) {
1641 if (!isa<AIE::EndOp>(*oi))
1642 rewriter.clone(*oi, mapper);
1644 AIEX::CertJobOp::ensureTerminator(newJobOp.getBody(), rewriter, loc);
1647 rewriter.setInsertionPoint(op);
1648 auto newPageOp0 = AIEX::CertPageOp::create(rewriter, loc);
1649 Block *newPageBlock0 =
new Block();
1650 newPageOp0.getBody().push_back(newPageBlock0);
1652 auto newPageOp1 = AIEX::CertPageOp::create(rewriter, loc);
1653 Block *newPageBlock1 =
new Block();
1654 newPageOp1.getBody().push_back(newPageBlock1);
1659 newPageOp0->setAttr(
"cert.implicit", rewriter.getUnitAttr());
1660 newPageOp1->setAttr(
"cert.implicit", rewriter.getUnitAttr());
1663 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
1664 if (job == split_job) {
1665 if (jobBoundaryCut) {
1668 rewriter.setInsertionPointToEnd(newPageBlock1);
1669 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
1670 job.getBody().front().end());
1673 rewriter.setInsertionPointToEnd(newPageBlock0);
1674 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
1677 rewriter.setInsertionPointToEnd(newPageBlock1);
1678 cloneJobRange(job, job.getJobId() + 1, split_iter,
1679 job.getBody().front().end());
1683 if (job->isBeforeInBlock(split_job)) {
1684 rewriter.setInsertionPointToEnd(newPageBlock0);
1685 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
1686 job.getBody().front().end());
1688 rewriter.setInsertionPointToEnd(newPageBlock1);
1689 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
1690 job.getBody().front().end());
1694 AIEX::CertPageOp::ensureTerminator(newPageOp0.getBody(), rewriter, loc);
1695 AIEX::CertPageOp::ensureTerminator(newPageOp1.getBody(), rewriter, loc);
1697 rewriter.eraseOp(op);
1706static void inlineDefaultGroup(AIE::DeviceOp dev) {
1707 Block *body = dev.getBody();
1708 SmallVector<AIEX::CertAttachToGroupOp> zeroGroups;
1709 for (
auto grp : body->getOps<AIEX::CertAttachToGroupOp>())
1710 if (grp.getGroupId() == 0)
1711 zeroGroups.push_back(grp);
1712 for (
auto grp : zeroGroups) {
1713 SmallVector<Operation *> inner;
1714 for (Operation &op : grp.getBody().front())
1715 if (!isa<AIE::EndOp>(op))
1716 inner.push_back(&op);
1717 for (Operation *op : inner)
1718 op->moveBefore(grp.getOperation());
1726static void formImplicitPagesInBlock(Block *body, OpBuilder &builder) {
1728 SmallVector<Operation *> topOps;
1729 for (Operation &op : *body)
1730 topOps.push_back(&op);
1738 auto openPage = [&](Location loc, Operation *before,
1739 bool implicit) -> AIEX::CertPageOp {
1740 builder.setInsertionPoint(before);
1741 auto page = AIEX::CertPageOp::create(builder, loc);
1743 page->setAttr(
"cert.implicit", builder.getUnitAttr());
1744 Block *b =
new Block();
1745 page.getBody().push_back(b);
1746 AIEX::CertPageOp::ensureTerminator(page.getBody(), builder, loc);
1753 auto waitTctsActors = [](AIEX::CertJobOp job) {
1754 std::set<std::pair<int, int>> actors;
1755 job.walk([&](AIEX::CertWaitTCTSOp wait) {
1756 actors.insert({wait.getTileId(), wait.getChannelId()});
1761 AIEX::CertPageOp currentPage;
1763 std::set<std::pair<int, int>> pageActors;
1764 auto closePage = [&]() {
1765 currentPage =
nullptr;
1769 for (Operation *op : topOps) {
1770 if (
auto job = dyn_cast<AIEX::CertJobOp>(op)) {
1771 if (job->hasAttr(
"cert.configure")) {
1777 auto page = openPage(job.getLoc(), job,
false);
1778 job->moveBefore(page.getBody().front().getTerminator());
1788 auto actors = waitTctsActors(job);
1790 llvm::any_of(actors, [&](
const std::pair<int, int> &actor) {
1791 return pageActors.count(actor);
1795 currentPage = openPage(job.getLoc(), job,
true);
1796 job->moveBefore(currentPage.getBody().front().getTerminator());
1797 pageActors.insert(actors.begin(), actors.end());
1798 }
else if (isa<AIEX::CertPageOp>(op)) {
1806static void formImplicitPages(AIE::DeviceOp dev) {
1807 OpBuilder builder(dev.getContext());
1808 formImplicitPagesInBlock(dev.getBody(), builder);
1811 SmallVector<AIEX::CertAttachToGroupOp> groups;
1812 for (
auto grp : dev.getBody()->getOps<AIEX::CertAttachToGroupOp>())
1813 groups.push_back(grp);
1814 for (
auto grp : groups)
1815 formImplicitPagesInBlock(&grp.getBody().front(), builder);
1824static void moveConfigPageFirst(AIE::DeviceOp dev) {
1825 Block *body = dev.getBody();
1826 AIEX::CertPageOp configPage;
1827 for (
auto page : body->getOps<AIEX::CertPageOp>()) {
1828 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
1829 if (job->hasAttr(
"cert.configure")) {
1837 if (configPage && &body->front() != configPage.getOperation())
1838 configPage->moveBefore(&body->front());
1842static void lowerPagePlacementToGroups(AIE::DeviceOp dev) {
1843 Block *body = dev.getBody();
1846 std::map<int32_t, SmallVector<AIEX::CertPageOp, 4>> byGroup;
1847 for (
auto page : body->getOps<AIEX::CertPageOp>()) {
1848 if (
auto placement = page.getPlacementAttr()) {
1849 auto gid =
static_cast<int32_t
>(placement.getInt());
1851 byGroup[gid].push_back(page);
1855 OpBuilder builder(dev.getContext());
1856 for (
auto &entry : byGroup) {
1857 int32_t gid = entry.first;
1858 SmallVector<AIEX::CertPageOp, 4> &pages = entry.second;
1862 builder.setInsertionPoint(pages.front());
1863 auto grp = AIEX::CertAttachToGroupOp::create(builder, dev.getLoc(), gid);
1864 Block *grpBlock =
new Block();
1865 grp.getBody().push_back(grpBlock);
1869 for (AIEX::CertPageOp page : pages) {
1870 page->removeAttr(
"placement");
1871 page->moveBefore(grpBlock, grpBlock->end());
1873 AIEX::CertAttachToGroupOp::ensureTerminator(grp.getBody(), builder,
1878struct AIECertPagesPass
1879 : xilinx::AIEX::impl::AIECertPagesBase<AIECertPagesPass> {
1880 void runOnOperation()
override {
1881 inlineDefaultGroup(getOperation());
1883 formImplicitPages(getOperation());
1886 moveConfigPageFirst(getOperation());
1895 lowerPagePlacementToGroups(getOperation());
1898 RewritePatternSet isolatePatterns(&getContext());
1899 isolatePatterns.insert<IsolateFullPageOpsPattern>(&getContext());
1901 applyPatternsGreedily(getOperation(), std::move(isolatePatterns))))
1902 signalPassFailure();
1908 bool splitError =
false;
1909 RewritePatternSet p1(&getContext());
1910 p1.insert<SplitCertPageOpPattern>(&getContext(), &splitError);
1911 if (failed(applyPatternsGreedily(getOperation(), std::move(p1))))
1912 signalPassFailure();
1914 signalPassFailure();
1917 RewritePatternSet p3(&getContext());
1918 p3.insert<MergeConsecutiveCertUcDmaWriteDesSyncOps>(&getContext());
1919 if (failed(applyPatternsGreedily(getOperation(), std::move(p3))))
1920 return signalPassFailure();
1927 return std::make_unique<AIENpuToCertPass>();
1931 return std::make_unique<AIECertPagesPass>();
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIENpuToCertPass()
std::optional< uint32_t > getConstantIntOperand(mlir::Value v)
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIECertPagesPass()
mlir::LogicalResult inlineWriteConfigs(mlir::ModuleOp module)
const AIETargetModel & getTargetModel(mlir::Operation *op)