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/IR/Matchers.h"
17#include "mlir/IR/SymbolTable.h"
18#include "mlir/Pass/Pass.h"
19#include "mlir/Transforms/DialectConversion.h"
20#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
21#include "llvm/ADT/DenseSet.h"
22#include "llvm/ADT/StringMap.h"
31#define GEN_PASS_DEF_AIENPUTOCERT
32#define GEN_PASS_DEF_AIECERTPAGES
33#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
39#define DEBUG_TYPE "npu-to-cert"
45static constexpr uint32_t cert_page_size = 8000;
46static constexpr uint32_t cert_page_limit = 8192;
51static constexpr uint32_t cert_job_start_cost = 8;
52static constexpr uint32_t cert_job_end_cost = 4;
58static uint32_t getOrAssignPdiId(AIE::DeviceOp parentDevice,
59 StringRef deviceSymName) {
61 bool foundExisting =
false;
62 parentDevice.walk([&](AIEX::CertLoadPdiOp loadPdiOp) {
63 if (loadPdiOp.getSymbol() == deviceSymName) {
64 pdiId = loadPdiOp.getPdiId();
66 }
else if (!foundExisting) {
67 pdiId = std::max(pdiId, loadPdiOp.getPdiId() + 1);
73struct RuntimeSequenceToCertJob : OpConversionPattern<AIE::RuntimeSequenceOp> {
74 using OpConversionPattern::OpConversionPattern;
76 RuntimeSequenceToCertJob(MLIRContext *context, PatternBenefit benefit = 1)
77 : OpConversionPattern(context, benefit) {}
80 matchAndRewrite(AIE::RuntimeSequenceOp op, OpAdaptor adaptor,
81 ConversionPatternRewriter &rewriter)
const override {
83 auto symName = op.getSymName();
84 uint32_t newJobId = 1;
85 if (symName !=
"configure") {
86 uint32_t maxJobId = 1;
87 op->getParentOp()->walk([&](AIEX::CertJobOp certJobOp) {
88 maxJobId = std::max(maxJobId, certJobOp.getJobId());
90 newJobId = maxJobId + 1;
96 rewriter.setInsertionPoint(op);
97 auto jobOp = AIEX::CertJobOp::create(rewriter, op.getLoc(), newJobId);
101 if (symName ==
"configure")
102 jobOp->setAttr(
"cert.configure", rewriter.getUnitAttr());
108 op.getRegion().cloneInto(&jobOp.getBody(), remap);
109 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), rewriter, op->getLoc());
112 rewriter.eraseOp(op);
118struct NpuWrite32ToCertWrite32 : OpConversionPattern<AIEX::NpuWrite32Op> {
119 using OpConversionPattern::OpConversionPattern;
122 matchAndRewrite(AIEX::NpuWrite32Op op, OpAdaptor adaptor,
123 ConversionPatternRewriter &rewriter)
const override {
125 std::optional<uint32_t> address =
128 if (!address || !value)
129 return op.emitOpError(
130 "cannot lower to cert.write32 with non-constant address or value");
133 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
137 uint32_t absoluteAddr = *absAddress;
140 std::optional<uint32_t> col = op.getColumn();
141 std::optional<uint32_t> row = op.getRow();
144 uint32_t expectedUpperBits = ((*col & 0xff) << tm.getColumnShift()) |
145 ((*row & 0xff) << tm.getRowShift());
148 uint32_t origAddress = *address;
149 uint32_t origUpperBits = origAddress & ~0xfffff;
150 if (origUpperBits != 0 && origUpperBits != expectedUpperBits) {
151 op.emitWarning() <<
"address upper bits (0x"
152 << llvm::utohexstr(origUpperBits)
153 <<
") don't match row=" << *row <<
" col=" << *col
154 <<
" computed bits (0x"
155 << llvm::utohexstr(expectedUpperBits) <<
")";
159 rewriter.replaceOpWithNewOp<AIEX::CertWrite32Op>(op, absoluteAddr, *value);
164struct NpuMaskWrite32ToCertMaskWrite32
165 : OpConversionPattern<AIEX::NpuMaskWrite32Op> {
166 using OpConversionPattern::OpConversionPattern;
169 matchAndRewrite(AIEX::NpuMaskWrite32Op op, OpAdaptor adaptor,
170 ConversionPatternRewriter &rewriter)
const override {
172 std::optional<uint32_t> address =
176 if (!address || !mask || !value)
177 return op.emitOpError(
178 "cannot lower to cert.maskwrite32 with non-constant "
179 "address, mask, or value");
182 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
186 uint32_t absoluteAddr = *absAddress;
189 std::optional<uint32_t> col = op.getColumn();
190 std::optional<uint32_t> row = op.getRow();
193 uint32_t expectedUpperBits = ((*col & 0xff) << tm.getColumnShift()) |
194 ((*row & 0xff) << tm.getRowShift());
197 uint32_t origAddress = *address;
198 uint32_t origUpperBits = origAddress & ~0xfffff;
199 if (origUpperBits != 0 && origUpperBits != expectedUpperBits) {
200 op.emitWarning() <<
"address upper bits (0x"
201 << llvm::utohexstr(origUpperBits)
202 <<
") don't match row=" << *row <<
" col=" << *col
203 <<
" computed bits (0x"
204 << llvm::utohexstr(expectedUpperBits) <<
")";
208 rewriter.replaceOpWithNewOp<AIEX::CertMaskWrite32Op>(op, absoluteAddr,
214struct NpuMaskPollToCertMaskPoll32 : OpConversionPattern<AIEX::NpuMaskPollOp> {
215 using OpConversionPattern::OpConversionPattern;
218 matchAndRewrite(AIEX::NpuMaskPollOp op, OpAdaptor adaptor,
219 ConversionPatternRewriter &rewriter)
const override {
220 std::optional<uint32_t> address =
224 if (!address || !mask || !value)
225 return op.emitOpError(
"cannot lower to cert.maskpoll32 with non-constant "
226 "address, mask, or value");
228 std::optional<uint32_t> absAddress = op.getAbsoluteAddress();
232 rewriter.replaceOpWithNewOp<AIEX::CertMaskPoll32Op>(op, *absAddress, *mask,
238struct NpuBlockWriteToCertUcDma : OpConversionPattern<AIEX::NpuBlockWriteOp> {
241 NpuBlockWriteToCertUcDma(MLIRContext *ctx,
unsigned &nextChainId)
242 : OpConversionPattern(ctx), nextChainId(nextChainId) {}
245 matchAndRewrite(AIEX::NpuBlockWriteOp op, OpAdaptor adaptor,
246 ConversionPatternRewriter &rewriter)
const override {
248 memref::GetGlobalOp dataOperand =
249 dyn_cast_or_null<memref::GetGlobalOp>(op.getData().getDefiningOp());
252 MemRefType dataType = cast<MemRefType>(dataOperand.getResult().getType());
253 uint32_t dataSize = dataType.getNumElements();
257 rewriter.eraseOp(op);
261 std::string symbolName =
"chain_" + std::to_string(nextChainId++);
262 while (op->getParentOfType<AIE::DeviceOp>().lookupSymbol(symbolName))
263 symbolName =
"chain_" + std::to_string(nextChainId++);
266 rewriter.replaceOpWithNewOp<AIEX::CertUcDmaWriteDesSyncOp>(op, symbolName);
270 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
279 Block *deviceBody = parentDevice.getBody();
280 Operation *insertAfter =
nullptr;
281 for (Operation &bodyOp : *deviceBody) {
282 if (isa<AIEX::CertUcDmaChainOp>(bodyOp) || isa<memref::GlobalOp>(bodyOp))
283 insertAfter = &bodyOp;
288 rewriter.setInsertionPointAfter(insertAfter);
290 rewriter.setInsertionPointToStart(deviceBody);
292 auto symbolAttr = rewriter.getStringAttr(symbolName);
294 AIEX::CertUcDmaChainOp::create(rewriter, op.getLoc(), symbolAttr);
296 Block *bb =
new Block();
297 chainOp.getRegion().push_back(bb);
298 rewriter.setInsertionPointToStart(bb);
299 AIEX::CertUcDmaBdOp::create(rewriter, op.getLoc(), dataOperand.getName(),
300 op.getAddress(), dataSize,
false);
302 AIEX::CertUcDmaChainOp::ensureTerminator(chainOp.getBody(), rewriter,
307 unsigned &nextChainId;
310struct NpuSyncToCertWaitTCTS : OpConversionPattern<AIEX::NpuSyncOp> {
311 using OpConversionPattern::OpConversionPattern;
314 matchAndRewrite(AIEX::NpuSyncOp op, OpAdaptor adaptor,
315 ConversionPatternRewriter &rewriter)
const override {
317 std::optional<uint32_t> colOpt =
319 std::optional<uint32_t> channelOpt =
321 std::optional<uint32_t> directionOpt =
323 if (!rowOpt || !colOpt || !channelOpt || !directionOpt)
324 return op.emitOpError(
325 "cannot lower to cert.wait_tcts with non-constant sync parameters");
326 uint32_t row = *rowOpt;
327 uint32_t col = *colOpt;
332 const int row_id_shift = 16;
333 const int col_id_shift = 21;
334 uint16_t tile_id = col << (col_id_shift - row_id_shift) | row;
335 uint32_t channel = *channelOpt;
336 uint32_t direction = *directionOpt;
338 const std::vector<int> chan2actor_shim_s2mm = {0, 2, 3, 4};
339 const std::vector<int> chan2actor_shim_mm2s = {6, 7, 8, 9, 10, 11, 12, 13};
340 const std::vector<int> chan2actor_mem_s2mm = {1, 2, 3, 4, 5, 6, 7};
341 const std::vector<int> chan2actor_mem_mm2s = {16, 17, 18, 19, 20,
343 const std::vector<int> chan2actor_tile_s2mm = {0, 1};
344 const std::vector<int> chan2actor_tile_mm2s = {6};
348 direction ==
static_cast<std::underlying_type_t<AIE::DMAChannelDir>
>(
349 AIE::DMAChannelDir::S2MM);
351 const std::vector<int> *chan2actor =
nullptr;
352 if (tm.isCoreTile(col, row))
353 chan2actor = isS2MM ? &chan2actor_tile_s2mm : &chan2actor_tile_mm2s;
354 else if (tm.isMemTile(col, row))
355 chan2actor = isS2MM ? &chan2actor_mem_s2mm : &chan2actor_mem_mm2s;
357 chan2actor = isS2MM ? &chan2actor_shim_s2mm : &chan2actor_shim_mm2s;
359 size_t chanIdx =
static_cast<size_t>(channel);
360 if (chanIdx >= chan2actor->size()) {
361 op.emitError(
"invalid DMA channel ")
362 << channel <<
" for " << (isS2MM ?
"S2MM" :
"MM2S")
363 <<
" direction in NpuSyncToCertWaitTCTS conversion";
367 uint8_t actor_id =
static_cast<uint8_t
>((*chan2actor)[chanIdx]);
368 uint8_t num_tcts = 1;
369 rewriter.replaceOpWithNewOp<AIEX::CertWaitTCTSOp>(op, tile_id, actor_id,
375struct NpuLoadPdiToCertLoadPdi : OpConversionPattern<AIEX::NpuLoadPdiOp> {
376 using OpConversionPattern::OpConversionPattern;
379 matchAndRewrite(AIEX::NpuLoadPdiOp op, OpAdaptor adaptor,
380 ConversionPatternRewriter &rewriter)
const override {
382 auto deviceRef = op.getDeviceRef();
386 StringRef deviceSymName = *deviceRef;
389 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
394 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
397 rewriter.replaceOpWithNewOp<AIEX::CertLoadPdiOp>(
398 op, rewriter.getUI32IntegerAttr(pdiId),
399 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
406 using OpRewritePattern::OpRewritePattern;
408 LogicalResult matchAndRewrite(AIEX::RunOp op,
409 PatternRewriter &rewriter)
const override {
411 AIE::RuntimeSequenceOp calleeRuntimeSequence =
412 op.getCalleeRuntimeSequenceOp();
413 if (!calleeRuntimeSequence)
417 Region &calleeBody = calleeRuntimeSequence.getBody();
421 ValueRange values = op.getArgs();
422 if (values.size() != calleeBody.getNumArguments()) {
423 op.emitError(
"number of run op arguments (")
425 <<
") does not match number of callee runtime sequence "
427 << calleeBody.getNumArguments() <<
")";
430 for (
unsigned i = 0, n = calleeBody.getNumArguments(); i < n; i++) {
431 BlockArgument arg = calleeBody.getArgument(i);
432 Value val = values[i];
433 argMap.map(arg, val);
437 rewriter.setInsertionPoint(op);
438 for (Operation &o : calleeBody.front().getOperations()) {
440 if (isa<AIE::EndOp>(o))
442 rewriter.clone(o, argMap);
446 rewriter.eraseOp(op);
453 using OpRewritePattern::OpRewritePattern;
455 LogicalResult matchAndRewrite(AIEX::ConfigureOp op,
456 PatternRewriter &rewriter)
const override {
458 AIE::DeviceOp referencedDevice = op.getReferencedDeviceOp();
459 if (!referencedDevice)
463 StringRef deviceSymName = referencedDevice.getSymName();
466 auto parentDevice = op->getParentOfType<AIE::DeviceOp>();
471 auto existingSection = dyn_cast_if_present<AIEX::CertSectionOp>(
472 parentDevice.lookupSymbol(deviceSymName));
473 if (existingSection) {
476 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
478 rewriter.setInsertionPoint(op);
479 AIEX::CertLoadPdiOp::create(
480 rewriter, op.getLoc(), rewriter.getUI32IntegerAttr(pdiId),
481 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
484 IRMapping bodyMapper;
485 for (Operation &o : op.getRegion().front().getOperations()) {
486 if (isa<AIE::EndOp>(o))
488 rewriter.clone(o, bodyMapper);
491 rewriter.eraseOp(op);
496 rewriter.setInsertionPoint(parentDevice.getBody()->getTerminator());
497 auto sectionOp = AIEX::CertSectionOp::create(
498 rewriter, op.getLoc(), rewriter.getStringAttr(deviceSymName));
501 Block *sectionBlock =
new Block();
502 sectionOp.getBody().push_back(sectionBlock);
503 rewriter.setInsertionPointToStart(sectionBlock);
506 auto pageOp = AIEX::CertPageOp::create(rewriter, op.getLoc());
507 Block *pageBlock =
new Block();
508 pageOp.getBody().push_back(pageBlock);
509 rewriter.setInsertionPointToStart(pageBlock);
513 uint32_t maxJobId = 0;
514 parentDevice.walk([&](AIEX::CertJobOp certJobOp) {
515 maxJobId = std::max(maxJobId, certJobOp.getJobId());
517 uint32_t sectionJobId = maxJobId + 1;
519 auto jobOp = AIEX::CertJobOp::create(rewriter, op.getLoc(), sectionJobId);
520 Block *jobBlock =
new Block();
521 jobOp.getBody().push_back(jobBlock);
522 rewriter.setInsertionPointToStart(jobBlock);
531 AIE::RuntimeSequenceOp configureSeq =
nullptr;
532 referencedDevice.walk([&](AIE::RuntimeSequenceOp seq) {
533 if (seq.getSymName() ==
"configure") {
541 for (Operation &o : configureSeq.getRegion().front().getOperations()) {
542 if (isa<AIE::EndOp>(o))
544 rewriter.clone(o, mapper);
549 AIEX::CertPageOp configPage =
nullptr;
550 for (
auto &op : referencedDevice.getBody()->getOperations()) {
551 if (
auto page = dyn_cast<AIEX::CertPageOp>(op)) {
560 for (
auto &op : configPage.getBody().front().getOperations()) {
561 if (
auto job = dyn_cast<AIEX::CertJobOp>(op)) {
562 for (Operation &jobOp : job.getBody().front().getOperations()) {
563 if (isa<AIE::EndOp>(jobOp))
565 rewriter.clone(jobOp, mapper);
574 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), rewriter, op.getLoc());
575 AIEX::CertPageOp::ensureTerminator(pageOp.getBody(), rewriter, op.getLoc());
576 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), rewriter,
581 uint32_t pdiId = getOrAssignPdiId(parentDevice, deviceSymName);
583 rewriter.setInsertionPoint(op);
584 AIEX::CertLoadPdiOp::create(
585 rewriter, op.getLoc(), rewriter.getUI32IntegerAttr(pdiId),
586 FlatSymbolRefAttr::get(rewriter.getContext(), deviceSymName));
591 IRMapping bodyMapper;
592 for (Operation &o : op.getRegion().front().getOperations()) {
593 if (isa<AIE::EndOp>(o))
595 rewriter.clone(o, bodyMapper);
599 rewriter.eraseOp(op);
606static AIEX::NpuBlockWriteOp
607findPatchedBlockWrite(AIEX::NpuAddressPatchOp patch) {
609 uint32_t addr = patch.getAddr();
610 int col = (addr >> tm.getColumnShift()) & 0x1f;
611 int row = (addr >> tm.getRowShift()) & 0x1f;
612 if (!tm.isValidTile({col, row}))
614 for (Block::iterator it(patch); it != patch->getBlock()->begin();) {
615 auto blockWrite = dyn_cast<AIEX::NpuBlockWriteOp>(*--it);
618 if (blockWrite.getAddress() + tm.getDmaBdAddressOffset(col, row) != addr)
630static LogicalResult prepareAddressPatchTemplates(AIE::DeviceOp device) {
631 LogicalResult result = success();
632 SymbolTable symbols(device);
637 std::optional<llvm::DenseMap<StringAttr, unsigned>> symbolUseCounts;
638 if (
auto uses = SymbolTable::getSymbolUses(&device.getRegion())) {
639 symbolUseCounts.emplace();
640 for (
const SymbolTable::SymbolUse &use : *uses)
641 ++(*symbolUseCounts)[use.getSymbolRef().getRootReference()];
643 SmallVector<AIEX::NpuAddressPatchOp> patches;
644 device.walk([&](AIEX::NpuAddressPatchOp patch) { patches.push_back(patch); });
645 for (AIEX::NpuAddressPatchOp patch : patches) {
646 AIEX::NpuBlockWriteOp blockWrite = findPatchedBlockWrite(patch);
649 auto getGlobal = dyn_cast_or_null<memref::GetGlobalOp>(
650 blockWrite.getData().getDefiningOp());
653 auto global = symbols.lookup<memref::GlobalOp>(getGlobal.getName());
656 std::optional<Attribute> initialValue = global.getInitialValue();
657 auto words = initialValue ? dyn_cast<DenseIntElementsAttr>(*initialValue)
658 : DenseIntElementsAttr();
662 if (!patch.getArgIdx()) {
663 patch.emitError(
"cert lowering requires an address_patch with arg_idx");
668 if (!matchPattern(patch.getArgPlus(), m_ConstantInt(&argPlus))) {
669 patch.emitError(
"cert lowering requires a constant arg_plus: "
670 "apply_offset_57 cannot add a runtime offset");
676 if (argPlus.getActiveBits() > 32) {
677 patch.emitError(
"arg_plus ")
678 << argPlus.getSExtValue()
679 <<
" does not fit the BD's 32-bit address word";
683 unsigned word = (patch.getAddr() - blockWrite.getAddress()) / 4;
684 if (word >= words.getNumElements())
689 if (!symbolUseCounts ||
690 symbolUseCounts->lookup(global.getSymNameAttr()) != 1 ||
691 !getGlobal->hasOneUse()) {
692 OpBuilder builder(global);
693 auto copy = cast<memref::GlobalOp>(builder.clone(*global));
695 StringAttr copyName = symbols.insert(copy);
696 OpBuilder atBlockWrite(blockWrite);
698 memref::GetGlobalOp::create(atBlockWrite, getGlobal.getLoc(),
699 getGlobal.getType(), copy.getSymName());
700 blockWrite.getDataMutable().assign(privateGet);
702 (*symbolUseCounts)[copyName] = 1;
703 if (getGlobal->use_empty()) {
705 --(*symbolUseCounts)[global.getSymNameAttr()];
711 auto values = llvm::to_vector(words.getValues<APInt>());
712 values[word] = argPlus.zextOrTrunc(values[word].getBitWidth());
713 global.setInitialValueAttr(
714 DenseIntElementsAttr::get(words.getType(), values));
719struct NpuAddressPatchToCertApplyOffset57
720 : OpConversionPattern<AIEX::NpuAddressPatchOp> {
721 using OpConversionPattern::OpConversionPattern;
724 matchAndRewrite(AIEX::NpuAddressPatchOp op, OpAdaptor adaptor,
725 ConversionPatternRewriter &rewriter)
const override {
726 AIEX::NpuBlockWriteOp blockWriteOp = findPatchedBlockWrite(op);
727 std::optional<uint32_t> argIdx = op.getArgIdx();
728 if (!blockWriteOp || !argIdx)
731 dyn_cast<memref::GetGlobalOp>(blockWriteOp.getData().getDefiningOp());
734 rewriter.setInsertionPoint(blockWriteOp);
735 rewriter.replaceOpWithNewOp<AIEX::CertApplyOffset57Op>(
736 op, getGlobalOp.getName(), 1, *argIdx);
741struct MergeConsecutiveCertUcDmaWriteDesSyncOps
743 using OpRewritePattern::OpRewritePattern;
745 LogicalResult matchAndRewrite(AIEX::CertUcDmaWriteDesSyncOp op,
746 PatternRewriter &rewriter)
const override {
754 auto isTransparentToMerge = [](Operation *o) {
755 return isa<AIEX::CertNopOp>(o);
759 Block::iterator it(op);
760 AIEX::CertUcDmaWriteDesSyncOp prevWriteDesSync =
nullptr;
761 while (it != op->getBlock()->begin() && !prevWriteDesSync) {
763 Operation *prevOp = &*it;
764 prevWriteDesSync = dyn_cast<AIEX::CertUcDmaWriteDesSyncOp>(prevOp);
765 if (!prevWriteDesSync && !isTransparentToMerge(prevOp))
768 if (!prevWriteDesSync)
772 StringRef sym_name = op.getSymbol();
773 StringRef prev_sym_name = prevWriteDesSync.getSymbol();
774 auto chain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
775 op->getParentOfType<AIE::DeviceOp>().lookupSymbol(sym_name));
776 auto prevChain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
777 prevWriteDesSync->getParentOfType<AIE::DeviceOp>().lookupSymbol(
779 if (!chain || !prevChain)
789 auto deviceOp = op->getParentOfType<AIE::DeviceOp>();
790 auto isSingleUse = [&](AIEX::CertUcDmaChainOp c) {
791 auto uses = SymbolTable::getSymbolUses(c, deviceOp);
792 return uses && llvm::hasSingleElement(*uses);
794 if (!isSingleUse(chain) || !isSingleUse(prevChain))
804 auto chainSize = [](AIEX::CertUcDmaChainOp c) {
806 for (
auto bdOp : c.getBody().front().getOps<AIEX::CertUcDmaBdOp>())
807 size += bdOp.getLength() *
sizeof(int) + 16;
810 if ((chainSize(chain) + chainSize(prevChain)) >= cert_page_size)
814 rewriter.setInsertionPointToStart(&chain.getBody().front());
815 for (
auto &o : prevChain.getBody().front().getOperations()) {
816 auto bdOp = dyn_cast<AIEX::CertUcDmaBdOp>(o);
819 AIEX::CertUcDmaBdOp::create(
820 rewriter, bdOp.getLoc(), bdOp.getRemoteAddress(),
821 bdOp.getLocalAddress(), bdOp.getLength(),
true);
823 rewriter.eraseOp(prevChain);
824 rewriter.eraseOp(prevWriteDesSync);
829struct SplitNpuBlockWriteOpPattern :
OpRewritePattern<AIEX::NpuBlockWriteOp> {
830 using OpRewritePattern::OpRewritePattern;
832 LogicalResult matchAndRewrite(AIEX::NpuBlockWriteOp op,
833 PatternRewriter &rewriter)
const override {
835 memref::GetGlobalOp dataOperand =
836 dyn_cast_or_null<memref::GetGlobalOp>(op.getData().getDefiningOp());
840 MemRefType dataType = cast<MemRefType>(dataOperand.getResult().getType());
841 uint32_t dataSize = dataType.getNumElements();
843 uint32_t dataSizeBytes = dataSize *
sizeof(int);
844 if (dataSizeBytes < cert_page_size)
847 auto loc = op.getLoc();
850 uint32_t splitElements = dataSize / 2;
851 uint32_t firstChunkSize = splitElements;
852 uint32_t secondChunkSize = dataSize - splitElements;
855 auto deviceOp = op->getParentOfType<AIE::DeviceOp>();
856 auto originalGlobal = dyn_cast_if_present<memref::GlobalOp>(
857 deviceOp.lookupSymbol(dataOperand.getName()));
862 auto originalData = originalGlobal.getInitialValue();
866 auto denseData = dyn_cast<DenseIntElementsAttr>(*originalData);
871 auto dataValues = denseData.getValues<APInt>();
872 std::vector<APInt> firstChunkData(dataValues.begin(),
873 dataValues.begin() + firstChunkSize);
874 std::vector<APInt> secondChunkData(dataValues.begin() + firstChunkSize,
878 auto elementType = rewriter.getI32Type();
879 auto firstChunkType = MemRefType::get({firstChunkSize}, elementType);
880 auto secondChunkType = MemRefType::get({secondChunkSize}, elementType);
881 TensorType firstTensorType =
882 RankedTensorType::get({firstChunkSize}, elementType);
883 TensorType secondTensorType =
884 RankedTensorType::get({secondChunkSize}, elementType);
886 auto firstChunkAttr =
887 DenseIntElementsAttr::get(firstTensorType, firstChunkData);
888 auto secondChunkAttr =
889 DenseIntElementsAttr::get(secondTensorType, secondChunkData);
892 std::string firstName = dataOperand.getName().str() +
"_split_0";
893 std::string secondName = dataOperand.getName().str() +
"_split_1";
897 while (deviceOp.lookupSymbol(firstName)) {
899 dataOperand.getName().str() +
"_split_0_" + std::to_string(counter++);
902 while (deviceOp.lookupSymbol(secondName)) {
904 dataOperand.getName().str() +
"_split_1_" + std::to_string(counter++);
908 rewriter.setInsertionPoint(originalGlobal);
909 memref::GlobalOp::create(rewriter, loc, firstName,
910 rewriter.getStringAttr(
"private"), firstChunkType,
911 firstChunkAttr,
true,
nullptr);
913 memref::GlobalOp::create(rewriter, loc, secondName,
914 rewriter.getStringAttr(
"private"), secondChunkType,
915 secondChunkAttr,
true,
nullptr);
918 rewriter.setInsertionPoint(op);
920 auto firstGetGlobal =
921 memref::GetGlobalOp::create(rewriter, loc, firstChunkType, firstName);
922 auto secondGetGlobal =
923 memref::GetGlobalOp::create(rewriter, loc, secondChunkType, secondName);
925 uint32_t baseAddr = op.getAddress();
927 AIEX::NpuBlockWriteOp::create(rewriter, loc, baseAddr,
928 firstGetGlobal.getResult(),
nullptr,
nullptr,
931 AIEX::NpuBlockWriteOp::create(rewriter, loc, baseAddr + firstChunkSize * 4,
932 secondGetGlobal.getResult(),
nullptr,
nullptr,
936 rewriter.eraseOp(op);
938 LLVM_DEBUG(llvm::outs()
939 <<
"Split NpuBlockWriteOp with data size: " << dataSizeBytes
940 <<
" bytes into chunks of " << firstChunkSize <<
" and "
941 << secondChunkSize <<
" elements\n");
947struct AIENpuToCertPass
948 : xilinx::AIEX::impl::AIENpuToCertBase<AIENpuToCertPass> {
949 void runOnOperation()
override {
950 auto moduleOp = getOperation();
952 return signalPassFailure();
955 llvm::SmallVector<AIE::DeviceOp, 4> devices;
956 for (
auto &op : moduleOp.getBody()->getOperations()) {
957 if (
auto deviceOp = dyn_cast<AIE::DeviceOp>(op)) {
958 devices.push_back(deviceOp);
965 AIE::DeviceOp mainDevice =
nullptr;
966 for (
auto dev : devices) {
967 if (dev.getSymName() == deviceName) {
974 moduleOp.emitError(
"no device found matching --device-name \"")
975 << deviceName <<
"\"";
980 migrateGlobalsIntoMainDevice(mainDevice, devices);
981 processDevice(mainDevice);
984 for (
auto dev : devices) {
985 if (dev != mainDevice) {
992 llvm::SmallVector<AIE::DeviceOp, 4> devicesToRemove;
993 for (
auto dev : devices) {
994 if (dev != mainDevice) {
995 devicesToRemove.push_back(dev);
999 for (
auto dev : devicesToRemove) {
1007 void migrateGlobalsIntoMainDevice(AIE::DeviceOp mainDevice,
1008 ArrayRef<AIE::DeviceOp> devices) {
1009 auto isDeviceName = [&](StringRef candidate) {
1010 return llvm::any_of(devices, [&](AIE::DeviceOp device) {
1011 return device.getSymName() == candidate;
1015 for (
auto global : llvm::to_vector(mainDevice.getOps<memref::GlobalOp>())) {
1016 StringRef oldName = global.getSymName();
1017 if (!isDeviceName(oldName))
1019 std::string newName;
1020 unsigned counter = 0;
1022 newName = oldName.str() +
"_" + std::to_string(counter++);
1023 while (nameTaken(mainDevice, newName) || isDeviceName(newName));
1024 renamePayloadUses(mainDevice, oldName, newName);
1025 global.setSymName(newName);
1028 for (
auto dev : devices) {
1029 if (dev == mainDevice)
1031 auto globals = llvm::to_vector(dev.getOps<memref::GlobalOp>());
1032 for (
auto global : globals) {
1033 StringRef oldName = global.getSymName();
1034 std::string newName = oldName.str();
1035 unsigned counter = 0;
1038 auto taken = [&](StringRef candidate) {
1039 return nameTaken(mainDevice, candidate) || isDeviceName(candidate) ||
1040 (candidate != oldName && nameTaken(dev, candidate));
1042 while (taken(newName))
1043 newName = oldName.str() +
"_" + std::to_string(counter++);
1044 if (newName != oldName) {
1045 renamePayloadUses(dev, oldName, newName);
1046 global.setSymName(newName);
1048 global->moveBefore(mainDevice.getBody(), mainDevice.getBody()->begin());
1055 static bool nameTaken(AIE::DeviceOp dev, StringRef name) {
1056 return llvm::any_of(dev.getBody()->getOperations(), [&](Operation &op) {
1058 op.getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());
1059 return symName && symName.getValue() == name;
1067 static void renamePayloadUses(AIE::DeviceOp dev, StringRef oldName,
1068 StringRef newName) {
1069 auto newRef = FlatSymbolRefAttr::get(dev.getContext(), newName);
1070 dev.walk<WalkOrder::PreOrder>([&](Operation *op) {
1071 if (op != dev && op->hasTrait<OpTrait::SymbolTable>())
1072 return WalkResult::skip();
1073 if (
auto get = dyn_cast<memref::GetGlobalOp>(op)) {
1074 if (
get.getName() == oldName)
1075 get.setNameAttr(newRef);
1076 }
else if (
auto bd = dyn_cast<AIEX::CertUcDmaBdOp>(op)) {
1077 if (bd.getRemoteAddress() == oldName)
1078 bd.setRemoteAddressAttr(newRef);
1079 }
else if (
auto patch = dyn_cast<AIEX::CertApplyOffset57Op>(op)) {
1080 if (patch.getSymbol() == oldName)
1081 patch.setSymbolAttr(newRef);
1083 return WalkResult::advance();
1087 void processDevice(AIE::DeviceOp currentDevice) {
1088 auto moduleOp = currentDevice->getParentOfType<ModuleOp>();
1095 llvm::SmallVector<StringRef, 4> referencedDeviceSyms;
1096 currentDevice.walk([&](AIEX::NpuLoadPdiOp loadPdiOp) {
1097 if (loadPdiOp.getDeviceRef())
1098 referencedDeviceSyms.push_back(*loadPdiOp.getDeviceRef());
1101 if (!referencedDeviceSyms.empty()) {
1102 OpBuilder builder(&getContext());
1104 for (StringRef refSymName : referencedDeviceSyms) {
1106 if (currentDevice.lookupSymbol(refSymName))
1110 AIE::DeviceOp refDevice =
nullptr;
1111 for (
auto &op : moduleOp.getBody()->getOperations()) {
1112 if (
auto dev = dyn_cast<AIE::DeviceOp>(op)) {
1113 if (dev.getSymName() == refSymName) {
1124 AIE::RuntimeSequenceOp configureSeq =
nullptr;
1125 refDevice.walk([&](AIE::RuntimeSequenceOp seq) {
1126 if (seq.getSymName() ==
"configure") {
1135 builder.setInsertionPoint(currentDevice.getBody()->getTerminator());
1137 AIEX::CertSectionOp::create(builder, configureSeq.getLoc(),
1138 builder.getStringAttr(refSymName));
1140 Block *sectionBlock =
new Block();
1141 sectionOp.getBody().push_back(sectionBlock);
1142 builder.setInsertionPointToStart(sectionBlock);
1146 AIEX::CertPageOp::create(builder, configureSeq.getLoc());
1147 Block *pageBlock =
new Block();
1148 pageOp.getBody().push_back(pageBlock);
1149 builder.setInsertionPointToStart(pageBlock);
1152 uint32_t maxJobId = 0;
1153 currentDevice.walk([&](AIEX::CertJobOp certJobOp) {
1154 maxJobId = std::max(maxJobId, certJobOp.getJobId());
1158 auto jobOp = AIEX::CertJobOp::create(builder, configureSeq.getLoc(),
1163 configureSeq.getRegion().cloneInto(&jobOp.getBody(), mapper);
1166 AIEX::CertJobOp::ensureTerminator(jobOp.getBody(), builder,
1167 configureSeq.getLoc());
1168 AIEX::CertPageOp::ensureTerminator(pageOp.getBody(), builder,
1169 configureSeq.getLoc());
1170 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), builder,
1171 configureSeq.getLoc());
1177 RewritePatternSet p_run_inline(&getContext());
1178 p_run_inline.insert<RunOpInlining>(&getContext());
1179 if (failed(applyPatternsGreedily(currentDevice, std::move(p_run_inline))))
1180 return signalPassFailure();
1183 RewritePatternSet p_configure(&getContext());
1184 p_configure.insert<ConfigureOpToCertSection>(&getContext());
1185 if (failed(applyPatternsGreedily(currentDevice, std::move(p_configure))))
1186 return signalPassFailure();
1188 ConversionTarget target(getContext());
1189 target.addIllegalOp<AIE::RuntimeSequenceOp>();
1191 target.addLegalOp<AIEX::CertApplyOffset57Op>();
1192 target.addLegalOp<AIEX::CertJobOp>();
1193 target.addLegalOp<AIEX::CertPageOp>();
1194 target.addLegalOp<AIEX::CertSectionOp>();
1195 target.addLegalOp<AIEX::CertLoadPdiOp>();
1196 target.addLegalOp<AIEX::CertMaskWrite32Op>();
1197 target.addLegalOp<AIEX::CertMaskPoll32Op>();
1198 target.addLegalOp<AIEX::CertUcDmaWriteDesSyncOp>();
1199 target.addLegalOp<AIEX::CertUcDmaChainOp>();
1200 target.addLegalOp<AIEX::CertUcDmaBdOp>();
1201 target.addLegalOp<AIEX::CertWrite32Op>();
1202 target.addLegalOp<AIEX::CertWaitTCTSOp>();
1203 target.addLegalOp<AIEX::ConfigureOp>();
1204 target.addLegalDialect<AIE::AIEDialect>();
1206 RewritePatternSet p0(&getContext());
1207 p0.insert<RuntimeSequenceToCertJob>(&getContext());
1209 if (failed(applyPartialConversion(currentDevice, target, std::move(p0))))
1210 return signalPassFailure();
1212 if (failed(prepareAddressPatchTemplates(currentDevice)))
1213 return signalPassFailure();
1215 target.addIllegalOp<AIEX::NpuAddressPatchOp>();
1218 RewritePatternSet p1(&getContext());
1219 p1.insert<NpuAddressPatchToCertApplyOffset57>(&getContext());
1221 if (failed(applyPartialConversion(currentDevice, target, std::move(p1))))
1222 return signalPassFailure();
1226 RewritePatternSet p(&getContext());
1227 p.insert<SplitNpuBlockWriteOpPattern>(&getContext());
1228 if (failed(applyPatternsGreedily(getOperation(), std::move(p))))
1229 signalPassFailure();
1232 target.addIllegalOp<AIEX::NpuBlockWriteOp>();
1233 target.addIllegalOp<AIEX::NpuBlockWriteValuesOp>();
1234 target.addIllegalOp<AIEX::NpuMaskWrite32Op>();
1235 target.addIllegalOp<AIEX::NpuMaskPollOp>();
1236 target.addIllegalOp<AIEX::NpuSyncOp>();
1237 target.addIllegalOp<AIEX::NpuWrite32Op>();
1238 target.addIllegalOp<AIEX::NpuLoadPdiOp>();
1240 unsigned nextChainId = 0;
1241 RewritePatternSet p2(&getContext());
1242 p2.insert<NpuBlockWriteToCertUcDma>(&getContext(), nextChainId);
1243 p2.insert<NpuMaskWrite32ToCertMaskWrite32>(&getContext());
1244 p2.insert<NpuMaskPollToCertMaskPoll32>(&getContext());
1245 p2.insert<NpuWrite32ToCertWrite32>(&getContext());
1246 p2.insert<NpuSyncToCertWaitTCTS>(&getContext());
1247 p2.insert<NpuLoadPdiToCertLoadPdi>(&getContext());
1249 if (failed(applyPartialConversion(currentDevice, target, std::move(p2))))
1250 return signalPassFailure();
1256 RewritePatternSet cleanup(&getContext());
1257 if (failed(applyPatternsGreedily(getOperation(), std::move(cleanup))))
1258 return signalPassFailure();
1264 OpBuilder builder(&getContext());
1267 llvm::SetVector<StringRef> referencedDeviceNames;
1268 currentDevice.walk([&](AIEX::CertLoadPdiOp loadPdiOp) {
1269 StringRef refSymName = loadPdiOp.getSymbol();
1270 if (!currentDevice.lookupSymbol(refSymName))
1271 referencedDeviceNames.insert(refSymName);
1275 for (StringRef refSymName : referencedDeviceNames) {
1277 AIE::DeviceOp refDevice =
nullptr;
1278 for (
auto &op : moduleOp.getBody()->getOperations()) {
1279 if (
auto dev = dyn_cast<AIE::DeviceOp>(op)) {
1280 if (dev.getSymName() == refSymName) {
1292 llvm::SmallVector<AIEX::CertPageOp, 4> certPages;
1293 for (
auto &op : refDevice.getBody()->getOperations()) {
1294 if (
auto pageOp = dyn_cast<AIEX::CertPageOp>(op)) {
1295 certPages.push_back(pageOp);
1299 if (certPages.empty())
1303 builder.setInsertionPoint(currentDevice.getBody()->getTerminator());
1304 auto sectionOp = AIEX::CertSectionOp::create(
1305 builder, refDevice.getLoc(), builder.getStringAttr(refSymName));
1307 Block *sectionBlock =
new Block();
1308 sectionOp.getBody().push_back(sectionBlock);
1312 for (
auto pageOp : certPages) {
1313 builder.setInsertionPointToEnd(sectionBlock);
1314 builder.clone(*pageOp.getOperation(), mapper);
1318 AIEX::CertSectionOp::ensureTerminator(sectionOp.getBody(), builder,
1319 refDevice.getLoc());
1324 currentDevice.walk([&](AIEX::CertJobOp jobOp) {
1325 Block &jobBlock = jobOp.getBody().front();
1328 for (
int i = jobBlock.getNumArguments() - 1; i >= 0; --i) {
1329 BlockArgument arg = jobBlock.getArgument(i);
1330 if (arg.use_empty()) {
1331 jobBlock.eraseArgument(i);
1341static StringRef pushedChain(Operation &o) {
1342 if (
auto push = dyn_cast<AIEX::CertUcDmaWriteDesSyncOp>(o))
1343 return push.getSymbol();
1344 if (
auto push = dyn_cast<AIEX::CertUcDmaWriteDesOp>(o))
1345 return push.getSymbol();
1353static bool isLocalRegister(uint32_t r) {
return r < 8; }
1356static void collectLocalRegisterDefsUses(Operation &o,
1357 SmallVectorImpl<uint32_t> &uses,
1358 SmallVectorImpl<uint32_t> &defs) {
1359 auto add = [](SmallVectorImpl<uint32_t> ®s, uint32_t r) {
1360 if (isLocalRegister(r))
1363 if (
auto op = dyn_cast<AIEX::CertUcDmaWriteDesOp>(o)) {
1364 add(defs, op.getWaitHandle());
1365 }
else if (
auto op = dyn_cast<AIEX::CertWaitUcDmaOp>(o)) {
1366 add(uses, op.getWaitHandle());
1367 }
else if (
auto op = dyn_cast<AIEX::CertRead32Op>(o)) {
1368 add(defs, op.getValue());
1369 }
else if (
auto op = dyn_cast<AIEX::CertRead32DOp>(o)) {
1370 add(uses, op.getAddress());
1371 add(defs, op.getValue());
1372 }
else if (
auto op = dyn_cast<AIEX::CertWrite32DOp>(o)) {
1373 if (op.getAddressIsReg())
1374 add(uses, op.getAddress());
1375 if (op.getValueIsReg())
1376 add(uses, op.getValue());
1382static bool jobUsesLocalRegister(AIEX::CertJobOp job) {
1383 SmallVector<uint32_t> uses, defs;
1384 for (Operation &o : job.getBody().front())
1385 collectLocalRegisterDefsUses(o, uses, defs);
1386 return !uses.empty() || !defs.empty();
1391static std::optional<uint32_t>
1392findLocalRegisterCrossingIn(ArrayRef<Operation *> ops) {
1393 llvm::SmallDenseSet<uint32_t> defined;
1394 SmallVector<uint32_t> uses, defs;
1395 for (Operation *o : ops) {
1398 collectLocalRegisterDefsUses(*o, uses, defs);
1399 for (uint32_t r : uses)
1400 if (!defined.contains(r))
1402 defined.insert(defs.begin(), defs.end());
1404 return std::nullopt;
1416static void addChainDataCost(StringRef sym_name, AIE::DeviceOp deviceOp,
1418 auto chain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
1419 deviceOp.lookupSymbol(sym_name));
1420 if (!chain || !seen.
chains.insert(chain).second)
1422 for (
auto bdOp : chain.getBody().front().getOps<AIEX::CertUcDmaBdOp>()) {
1424 auto global = dyn_cast_if_present<memref::GlobalOp>(
1425 deviceOp.lookupSymbol(bdOp.getRemoteAddress()));
1426 if (!global || !seen.
payloads.insert(global).second)
1428 auto initVal = global.getInitialValue();
1431 auto data = dyn_cast<DenseIntElementsAttr>(*initVal);
1434 data_cost += data.getNumElements() * 4;
1443static void updateCostForOp(Operation &o, AIE::DeviceOp deviceOp,
1444 uint32_t &text_cost, uint32_t &data_cost,
1449 if (isa<AIEX::CertLocalBarrierOp>(o)) {
1451 }
else if (isa<AIEX::CertRemoteBarrierOp>(o)) {
1453 }
else if (isa<AIEX::CertRelAcqSyncOp>(o)) {
1455 }
else if (isa<AIEX::CertWaitTCTSOp>(o)) {
1457 }
else if (isa<AIEX::CertMaskWrite32Op, AIEX::CertMaskPoll32Op>(o)) {
1459 }
else if (isa<AIEX::CertWrite32Op>(o)) {
1461 }
else if (isa<AIEX::CertApplyOffset57Op>(o)) {
1463 }
else if (isa<AIEX::CertNopOp>(o)) {
1465 }
else if (isa<AIEX::CertPreemptOp>(o)) {
1467 }
else if (isa<AIEX::CertLoadPdiOp>(o)) {
1470 }
else if (
auto syncOp = dyn_cast<AIEX::CertUcDmaWriteDesSyncOp>(o)) {
1472 addChainDataCost(syncOp.getSymbol(), deviceOp, data_cost, seen);
1473 }
else if (
auto desOp = dyn_cast<AIEX::CertUcDmaWriteDesOp>(o)) {
1475 addChainDataCost(desOp.getSymbol(), deviceOp, data_cost, seen);
1476 }
else if (isa<AIEX::CertWaitUcDmaOp>(o)) {
1478 }
else if (isa<AIEX::CertRead32Op>(o)) {
1480 }
else if (isa<AIEX::CertRead32DOp>(o)) {
1482 }
else if (isa<AIEX::CertWrite32DOp>(o)) {
1484 }
else if (isa<AIEX::CertSaveTimestampsOp>(o)) {
1486 }
else if (isa<AIEX::CertSaveRegisterOp>(o)) {
1491static uint32_t estimateCost(AIEX::CertPageOp op, uint32_t split_target,
1492 AIEX::CertJobOp &split_job,
1493 Block::iterator &split_iter,
1494 bool &found_split_point) {
1495 uint32_t text_cost = 32;
1496 uint32_t data_cost = 0;
1498 found_split_point =
false;
1499 AIE::DeviceOp deviceOp = op->getParentOfType<AIE::DeviceOp>();
1501 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
1503 bool splittable = !jobUsesLocalRegister(job);
1506 text_cost += cert_job_start_cost;
1507 for (
auto &o : job.getBody().front().getOperations()) {
1508 Block::iterator current(&o);
1509 if (splittable && !found_split_point && !isa<AIE::EndOp>(o) &&
1510 current != job.getBody().front().begin() &&
1511 (text_cost + data_cost) >= split_target) {
1513 split_iter = current;
1514 found_split_point =
true;
1517 updateCostForOp(o, deviceOp, text_cost, data_cost, seen);
1519 if (splittable && !found_split_point &&
1520 (text_cost + data_cost) >= split_target) {
1521 Block::iterator next = current;
1523 if (next != job.getBody().front().end() && !isa<AIE::EndOp>(*next)) {
1526 found_split_point =
true;
1527 }
else if (!isa<AIE::EndOp>(o) &&
1528 current != job.getBody().front().begin()) {
1532 split_iter = current;
1533 found_split_point =
true;
1537 text_cost += cert_job_end_cost;
1539 return text_cost + data_cost;
1555static void computeIndivisibleSpans(AIEX::CertPageOp page,
1556 std::map<Operation *, int> &flatIndex,
1557 SmallVectorImpl<IndivisibleSpan> &spans) {
1558 AIE::DeviceOp deviceOp = page->getParentOfType<AIE::DeviceOp>();
1559 std::map<int, std::pair<int, int>> barriers;
1562 llvm::StringMap<int> firstPatch;
1563 auto payloadsOf = [&](StringRef chainSym) {
1564 SmallVector<StringRef> payloads;
1565 auto chain = dyn_cast_if_present<AIEX::CertUcDmaChainOp>(
1566 deviceOp ? deviceOp.lookupSymbol(chainSym) :
nullptr);
1567 if (!chain || chain.getBody().empty())
1569 for (
auto bd : chain.getBody().front().getOps<AIEX::CertUcDmaBdOp>())
1570 payloads.push_back(bd.getRemoteAddress());
1575 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
1576 for (
auto &o : job.getBody().front().getOperations()) {
1577 if (isa<AIE::EndOp>(o))
1579 flatIndex[&o] = idx;
1580 if (
auto bar = dyn_cast<AIEX::CertLocalBarrierOp>(o)) {
1581 int id =
static_cast<int>(bar.getLocalBarrierId());
1582 auto it = barriers.find(
id);
1583 if (it == barriers.end())
1584 barriers[id] = {idx, idx};
1586 it->second.first = std::min(it->second.first, idx);
1587 it->second.second = std::max(it->second.second, idx);
1589 }
else if (
auto patch = dyn_cast<AIEX::CertApplyOffset57Op>(o)) {
1590 firstPatch.try_emplace(patch.getSymbol(), idx);
1591 }
else if (StringRef pushed = pushedChain(o); !pushed.empty()) {
1592 for (StringRef payload : payloadsOf(pushed)) {
1593 auto it = firstPatch.find(payload);
1594 if (it != firstPatch.end())
1601 for (
auto &[
id, span] : barriers)
1606static bool cutSeparatesSpan(ArrayRef<IndivisibleSpan> spans,
int p,
1608 const auto *it = llvm::find_if(
1610 if (it == spans.end())
1620static bool findLegalSplitPoint(AIEX::CertPageOp page, uint32_t split_target,
1621 const std::map<Operation *, int> &flatIndex,
1622 ArrayRef<IndivisibleSpan> spans,
1623 AIEX::CertJobOp &split_job,
1624 Block::iterator &split_iter) {
1625 AIE::DeviceOp deviceOp = page->getParentOfType<AIE::DeviceOp>();
1626 uint32_t text_cost = 32;
1627 uint32_t data_cost = 0;
1630 uint32_t best_delta = 0;
1633 bool sawOpInEarlierJob =
false;
1634 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
1635 text_cost += cert_job_start_cost;
1636 bool splittable = !jobUsesLocalRegister(job);
1637 bool sawOpInJob =
false;
1638 for (
auto &o : job.getBody().front().getOperations()) {
1639 if (isa<AIE::EndOp>(o))
1641 Block::iterator current(&o);
1647 if (sawOpInJob ? splittable : sawOpInEarlierJob) {
1648 auto fi = flatIndex.find(&o);
1649 int p = fi == flatIndex.end() ? 0 : fi->second;
1650 if (!cutSeparatesSpan(spans, p)) {
1651 uint32_t cost = text_cost + data_cost;
1653 cost > split_target ? cost - split_target : split_target - cost;
1654 if (!found || delta < best_delta) {
1658 split_iter = current;
1662 updateCostForOp(o, deviceOp, text_cost, data_cost, seen);
1665 text_cost += cert_job_end_cost;
1666 sawOpInEarlierJob |= sawOpInJob;
1679 IsolateFullPageOpsPattern(MLIRContext *context,
bool *hadError)
1680 : OpRewritePattern(context), hadError(hadError) {}
1685 static AIEX::CertPageOp makePageAfter(PatternRewriter &rewriter,
1686 Operation *after, Location loc) {
1687 rewriter.setInsertionPointAfter(after);
1688 auto page = AIEX::CertPageOp::create(rewriter, loc);
1689 page.getBody().push_back(
new Block());
1690 AIEX::CertPageOp::ensureTerminator(page.getBody(), rewriter, loc);
1694 LogicalResult matchAndRewrite(AIEX::CertJobOp jobOp,
1695 PatternRewriter &rewriter)
const override {
1698 Operation *fullPageOp =
nullptr;
1699 Block::iterator fullPageOpIter;
1700 for (Block::iterator it = jobOp.getBody().front().begin();
1701 it != jobOp.getBody().front().end(); ++it) {
1702 if (isa<AIEX::CertLoadPdiOp, AIEX::CertPreemptOp>(*it)) {
1704 fullPageOpIter = it;
1712 auto parentPage = jobOp->getParentOfType<AIEX::CertPageOp>();
1718 for (Operation &op : jobOp.getBody().front().getOperations())
1719 if (!isa<AIE::EndOp>(op))
1727 SmallVector<Operation *> leadingSiblings, trailingSiblings;
1728 bool pastSelf =
false;
1729 for (Operation &op : parentPage.getBody().front()) {
1730 if (isa<AIE::EndOp>(op))
1732 if (&op == jobOp.getOperation()) {
1736 (pastSelf ? trailingSiblings : leadingSiblings).push_back(&op);
1740 if (opCount == 1 && leadingSiblings.empty() && trailingSiblings.empty())
1743 auto loc = jobOp.getLoc();
1747 Operation *anchor = parentPage.getOperation();
1751 auto isolatedPage = makePageAfter(rewriter, anchor, loc);
1752 jobOp->moveBefore(isolatedPage.getBody().front().getTerminator());
1753 anchor = isolatedPage;
1757 auto parentDevice = jobOp->getParentOfType<AIE::DeviceOp>();
1758 uint32_t maxJobId = 0;
1759 parentDevice.walk([&](AIEX::CertJobOp certJobOp) {
1760 maxJobId = std::max(maxJobId, certJobOp.getJobId());
1762 uint32_t beforeJobId = jobOp.getJobId();
1763 uint32_t fullPageJobId = maxJobId + 1;
1764 uint32_t afterJobId = maxJobId + 2;
1766 SmallVector<Operation *> beforeOps, afterOps;
1767 for (Block::iterator it = jobOp.getBody().front().begin();
1768 it != fullPageOpIter; ++it)
1769 if (!isa<AIE::EndOp>(*it))
1770 beforeOps.push_back(&*it);
1771 Block::iterator afterStart = std::next(fullPageOpIter);
1772 for (Block::iterator it = afterStart; it != jobOp.getBody().front().end();
1774 if (!isa<AIE::EndOp>(*it))
1775 afterOps.push_back(&*it);
1779 if (
auto crossing = findLocalRegisterCrossingIn(afterOps)) {
1780 StringRef name = fullPageOp->getName().getStringRef();
1782 return jobOp.emitError()
1783 <<
"job " << jobOp.getJobId() <<
" must be split around its "
1784 << name <<
", but local register r" << *crossing
1785 <<
" is written before it and read after it; keep that "
1786 "register's uses on one side of the "
1787 << name <<
", or put them in a separate job";
1790 auto emitJobPage = [&](uint32_t jobId, ArrayRef<Operation *> ops) {
1791 auto page = makePageAfter(rewriter, anchor, loc);
1792 rewriter.setInsertionPoint(page.getBody().front().getTerminator());
1793 auto job = AIEX::CertJobOp::create(rewriter, loc, jobId);
1794 job.getBody().push_back(
new Block());
1795 AIEX::CertJobOp::ensureTerminator(job.getBody(), rewriter, loc);
1796 for (Operation *op : ops)
1797 op->moveBefore(job.getBody().front().getTerminator());
1801 if (!beforeOps.empty())
1802 emitJobPage(beforeJobId, beforeOps);
1803 emitJobPage(fullPageJobId, {fullPageOp});
1804 if (!afterOps.empty())
1805 emitJobPage(afterJobId, afterOps);
1807 rewriter.eraseOp(jobOp);
1812 if (!trailingSiblings.empty()) {
1813 auto tailPage = makePageAfter(rewriter, anchor, loc);
1814 for (Operation *op : trailingSiblings)
1815 op->moveBefore(tailPage.getBody().front().getTerminator());
1818 if (llvm::all_of(parentPage.getBody().front(),
1819 [](Operation &op) { return isa<AIE::EndOp>(op); }))
1820 rewriter.eraseOp(parentPage);
1834 SplitCertPageOpPattern(MLIRContext *context,
bool *hadError)
1835 : OpRewritePattern(context), hadError(hadError) {}
1837 LogicalResult matchAndRewrite(AIEX::CertPageOp op,
1838 PatternRewriter &rewriter)
const override {
1840 constexpr uint32_t split_threshold = cert_page_size;
1842 AIEX::CertJobOp split_job;
1843 Block::iterator split_iter;
1844 bool found_split_point =
false;
1845 uint32_t cost = estimateCost(op, cert_page_size / 2, split_job, split_iter,
1847 LLVM_DEBUG(llvm::outs()
1848 <<
"Estimate cost for page: " <<
" is " << cost <<
"\n");
1850 if (cost < split_threshold)
1853 bool isImplicit = op->hasAttr(
"cert.implicit");
1855 std::map<Operation *, int> flatIndex;
1856 SmallVector<IndivisibleSpan> spans;
1857 computeIndivisibleSpans(op, flatIndex, spans);
1863 if (!found_split_point)
1864 found_split_point = findLegalSplitPoint(op, cert_page_size / 2, flatIndex,
1865 {}, split_job, split_iter);
1872 if (!found_split_point) {
1873 if (cost > cert_page_limit) {
1874 auto jobs = op.getBody().front().getOps<AIEX::CertJobOp>();
1875 auto blocker = llvm::find_if(jobs, jobUsesLocalRegister);
1876 if (blocker != jobs.end())
1877 op.emitError() <<
"cert.page is an estimated " << cost
1878 <<
" bytes, over the " << cert_page_limit
1879 <<
"-byte microcontroller page limit, and cannot be "
1881 << (*blocker).getJobId()
1882 <<
" uses local registers r0..r7, which do not "
1883 "survive a split; break it into smaller jobs "
1884 "that each keep a register's definition and "
1887 op.emitError() <<
"cert.page is an estimated " << cost
1888 <<
" bytes, over the " << cert_page_limit
1889 <<
"-byte microcontroller page limit, and offers no "
1890 "split point (a split must leave at least one op "
1891 "on each page); break the oversized job into "
1906 if (!spans.empty()) {
1907 auto fi = flatIndex.find(&*split_iter);
1908 int p = fi == flatIndex.end() ? 0 : fi->second;
1910 if (cutSeparatesSpan(spans, p, &kind)) {
1911 if (!findLegalSplitPoint(op, cert_page_size / 2, flatIndex, spans,
1912 split_job, split_iter)) {
1918 if (cost > cert_page_limit) {
1920 StringRef what = barrier
1921 ?
"a local_barrier participant set"
1922 :
"a cert.apply_offset_57 from a uC-DMA push "
1923 "of the payload it patches";
1926 <<
"implicit cert.page is an estimated " << cost
1927 <<
" bytes, over the " << cert_page_limit
1928 <<
"-byte microcontroller page limit, and cannot be split "
1929 "without separating "
1931 <<
" across pages; insert an explicit cert.page boundary to "
1932 << (barrier ?
"co-locate the barrier participants"
1933 :
"co-locate each apply_offset_57 with its "
1937 <<
"cert.page is an estimated " << cost <<
" bytes, over the "
1939 <<
"-byte microcontroller page limit, and cannot be split "
1940 "without separating "
1941 << what <<
" across pages; reduce the page's contents or "
1942 << (barrier ?
"regroup its local_barrier participants"
1943 :
"regroup its apply_offset_57 ops and their "
1945 <<
" so that a legal split point exists";
1958 <<
"auto-split of implicit cert.page serializes cooperatively-"
1959 "scheduled jobs into separate sequential pages; insert an "
1961 "cert.page boundary to control where the split occurs";
1963 auto loc = op.getLoc();
1967 bool jobBoundaryCut = split_iter == split_job.getBody().front().begin();
1968 [[maybe_unused]]
auto earlierJobHasRealOp = [&]() {
1969 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
1970 if (job == split_job)
1972 for (
auto &o : job.getBody().front().getOperations())
1973 if (!isa<AIE::EndOp>(o))
1978 assert((!jobBoundaryCut || earlierJobHasRealOp()) &&
1979 "a job-boundary cut needs a preceding job with content, else the "
1980 "earlier page is empty and the split makes no progress");
1988 if (!jobBoundaryCut)
1989 op->getParentOfType<AIE::DeviceOp>().walk([&](AIEX::CertJobOp certJobOp) {
1990 if (certJobOp.getJobId() > split_job.getJobId())
1991 certJobOp.setJobId(certJobOp.getJobId() + 1);
1994 auto cloneJobRange = [&](AIEX::CertJobOp sourceJob, uint32_t jobId,
1995 Block::iterator begin, Block::iterator end) {
1996 auto newJobOp = AIEX::CertJobOp::create(rewriter, loc, jobId);
1997 Block *newJobBlock =
new Block();
1998 newJobOp.getBody().push_back(newJobBlock);
2001 Block &sourceBlock = sourceJob.getBody().front();
2002 for (BlockArgument arg : sourceBlock.getArguments()) {
2003 BlockArgument newArg =
2004 newJobBlock->addArgument(arg.getType(), arg.getLoc());
2005 mapper.map(arg, newArg);
2008 rewriter.setInsertionPointToStart(newJobBlock);
2009 for (Block::iterator oi = begin; oi != end; ++oi) {
2010 if (!isa<AIE::EndOp>(*oi))
2011 rewriter.clone(*oi, mapper);
2013 AIEX::CertJobOp::ensureTerminator(newJobOp.getBody(), rewriter, loc);
2016 rewriter.setInsertionPoint(op);
2017 auto newPageOp0 = AIEX::CertPageOp::create(rewriter, loc);
2018 Block *newPageBlock0 =
new Block();
2019 newPageOp0.getBody().push_back(newPageBlock0);
2021 auto newPageOp1 = AIEX::CertPageOp::create(rewriter, loc);
2022 Block *newPageBlock1 =
new Block();
2023 newPageOp1.getBody().push_back(newPageBlock1);
2028 newPageOp0->setAttr(
"cert.implicit", rewriter.getUnitAttr());
2029 newPageOp1->setAttr(
"cert.implicit", rewriter.getUnitAttr());
2032 for (
auto job : op.getBody().front().getOps<AIEX::CertJobOp>()) {
2033 if (job == split_job) {
2034 if (jobBoundaryCut) {
2037 rewriter.setInsertionPointToEnd(newPageBlock1);
2038 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
2039 job.getBody().front().end());
2042 rewriter.setInsertionPointToEnd(newPageBlock0);
2043 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
2046 rewriter.setInsertionPointToEnd(newPageBlock1);
2047 cloneJobRange(job, job.getJobId() + 1, split_iter,
2048 job.getBody().front().end());
2052 if (job->isBeforeInBlock(split_job)) {
2053 rewriter.setInsertionPointToEnd(newPageBlock0);
2054 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
2055 job.getBody().front().end());
2057 rewriter.setInsertionPointToEnd(newPageBlock1);
2058 cloneJobRange(job, job.getJobId(), job.getBody().front().begin(),
2059 job.getBody().front().end());
2063 AIEX::CertPageOp::ensureTerminator(newPageOp0.getBody(), rewriter, loc);
2064 AIEX::CertPageOp::ensureTerminator(newPageOp1.getBody(), rewriter, loc);
2066 rewriter.eraseOp(op);
2075static void inlineDefaultGroup(AIE::DeviceOp dev) {
2076 Block *body = dev.getBody();
2077 SmallVector<AIEX::CertAttachToGroupOp> zeroGroups;
2078 for (
auto grp : body->getOps<AIEX::CertAttachToGroupOp>())
2079 if (grp.getGroupId() == 0)
2080 zeroGroups.push_back(grp);
2081 for (
auto grp : zeroGroups) {
2082 SmallVector<Operation *> inner;
2083 for (Operation &op : grp.getBody().front())
2084 if (!isa<AIE::EndOp>(op))
2085 inner.push_back(&op);
2086 for (Operation *op : inner)
2087 op->moveBefore(grp.getOperation());
2095static void formImplicitPagesInBlock(Block *body, OpBuilder &builder) {
2097 SmallVector<Operation *> topOps;
2098 for (Operation &op : *body)
2099 topOps.push_back(&op);
2107 auto openPage = [&](Location loc, Operation *before,
2108 bool implicit) -> AIEX::CertPageOp {
2109 builder.setInsertionPoint(before);
2110 auto page = AIEX::CertPageOp::create(builder, loc);
2112 page->setAttr(
"cert.implicit", builder.getUnitAttr());
2113 Block *b =
new Block();
2114 page.getBody().push_back(b);
2115 AIEX::CertPageOp::ensureTerminator(page.getBody(), builder, loc);
2122 auto waitTctsActors = [](AIEX::CertJobOp job) {
2123 std::set<std::pair<int, int>> actors;
2124 job.walk([&](AIEX::CertWaitTCTSOp wait) {
2125 actors.insert({wait.getTileId(), wait.getChannelId()});
2130 AIEX::CertPageOp currentPage;
2132 std::set<std::pair<int, int>> pageActors;
2133 auto closePage = [&]() {
2134 currentPage =
nullptr;
2138 for (Operation *op : topOps) {
2139 if (
auto job = dyn_cast<AIEX::CertJobOp>(op)) {
2140 if (job->hasAttr(
"cert.configure")) {
2146 auto page = openPage(job.getLoc(), job,
false);
2147 job->moveBefore(page.getBody().front().getTerminator());
2157 auto actors = waitTctsActors(job);
2159 llvm::any_of(actors, [&](
const std::pair<int, int> &actor) {
2160 return pageActors.count(actor);
2164 currentPage = openPage(job.getLoc(), job,
true);
2165 job->moveBefore(currentPage.getBody().front().getTerminator());
2166 pageActors.insert(actors.begin(), actors.end());
2167 }
else if (isa<AIEX::CertPageOp>(op)) {
2175static void formImplicitPages(AIE::DeviceOp dev) {
2176 OpBuilder builder(dev.getContext());
2177 formImplicitPagesInBlock(dev.getBody(), builder);
2180 SmallVector<AIEX::CertAttachToGroupOp> groups;
2181 for (
auto grp : dev.getBody()->getOps<AIEX::CertAttachToGroupOp>())
2182 groups.push_back(grp);
2183 for (
auto grp : groups)
2184 formImplicitPagesInBlock(&grp.getBody().front(), builder);
2193static void moveConfigPageFirst(AIE::DeviceOp dev) {
2194 Block *body = dev.getBody();
2195 AIEX::CertPageOp configPage;
2196 for (
auto page : body->getOps<AIEX::CertPageOp>()) {
2197 for (
auto job : page.getBody().front().getOps<AIEX::CertJobOp>()) {
2198 if (job->hasAttr(
"cert.configure")) {
2206 if (configPage && &body->front() != configPage.getOperation())
2207 configPage->moveBefore(&body->front());
2211static void lowerPagePlacementToGroups(AIE::DeviceOp dev) {
2212 Block *body = dev.getBody();
2215 std::map<int32_t, SmallVector<AIEX::CertPageOp, 4>> byGroup;
2216 for (
auto page : body->getOps<AIEX::CertPageOp>()) {
2217 if (
auto placement = page.getPlacementAttr()) {
2218 auto gid =
static_cast<int32_t
>(placement.getInt());
2220 byGroup[gid].push_back(page);
2224 OpBuilder builder(dev.getContext());
2225 for (
auto &entry : byGroup) {
2226 int32_t gid = entry.first;
2227 SmallVector<AIEX::CertPageOp, 4> &pages = entry.second;
2231 builder.setInsertionPoint(pages.front());
2232 auto grp = AIEX::CertAttachToGroupOp::create(builder, dev.getLoc(), gid);
2233 Block *grpBlock =
new Block();
2234 grp.getBody().push_back(grpBlock);
2238 for (AIEX::CertPageOp page : pages) {
2239 page->removeAttr(
"placement");
2240 page->moveBefore(grpBlock, grpBlock->end());
2242 AIEX::CertAttachToGroupOp::ensureTerminator(grp.getBody(), builder,
2247struct AIECertPagesPass
2248 : xilinx::AIEX::impl::AIECertPagesBase<AIECertPagesPass> {
2249 void runOnOperation()
override {
2250 inlineDefaultGroup(getOperation());
2252 formImplicitPages(getOperation());
2255 moveConfigPageFirst(getOperation());
2264 lowerPagePlacementToGroups(getOperation());
2267 bool isolateError =
false;
2268 RewritePatternSet isolatePatterns(&getContext());
2269 isolatePatterns.insert<IsolateFullPageOpsPattern>(&getContext(),
2272 applyPatternsGreedily(getOperation(), std::move(isolatePatterns))))
2273 signalPassFailure();
2275 return signalPassFailure();
2281 bool splitError =
false;
2282 RewritePatternSet p1(&getContext());
2283 p1.insert<SplitCertPageOpPattern>(&getContext(), &splitError);
2284 if (failed(applyPatternsGreedily(getOperation(), std::move(p1))))
2285 signalPassFailure();
2287 signalPassFailure();
2290 RewritePatternSet p3(&getContext());
2291 p3.insert<MergeConsecutiveCertUcDmaWriteDesSyncOps>(&getContext());
2292 if (failed(applyPatternsGreedily(getOperation(), std::move(p3))))
2293 return signalPassFailure();
2300 return std::make_unique<AIENpuToCertPass>();
2304 return std::make_unique<AIECertPagesPass>();
const V & get(const std::map< K, V > &pa, K k)
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)
llvm::DenseSet< Operation * > chains
llvm::DenseSet< Operation * > payloads