25#include "mlir/Conversion/ArithToEmitC/ArithToEmitC.h"
26#include "mlir/Conversion/SCFToEmitC/SCFToEmitC.h"
27#include "mlir/Dialect/Arith/IR/Arith.h"
28#include "mlir/Dialect/Arith/Transforms/Passes.h"
29#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
30#include "mlir/Dialect/EmitC/IR/EmitC.h"
31#include "mlir/Dialect/EmitC/Transforms/TypeConversions.h"
32#include "mlir/Dialect/MemRef/IR/MemRef.h"
33#include "mlir/Dialect/SCF/IR/SCF.h"
34#include "mlir/IR/Builders.h"
35#include "mlir/IR/BuiltinOps.h"
36#include "mlir/Interfaces/SideEffectInterfaces.h"
37#include "mlir/Pass/Pass.h"
38#include "mlir/Transforms/DialectConversion.h"
39#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
40#include "mlir/Transforms/RegionUtils.h"
42#include "llvm/ADT/StringSet.h"
43#include "llvm/ADT/TypeSwitch.h"
44#include "llvm/Support/Format.h"
49#define GEN_PASS_DECL_CONVERTAIEXTOEMITC
50#define GEN_PASS_DEF_CONVERTAIEXTOEMITC
51#include "aie/Conversion/Passes.h.inc"
61static std::string refuse(StringRef why,
bool inFormat) {
64 if (ch ==
'\n' || ch ==
'\r')
66 else if (ch ==
'"' || ch ==
'\\')
68 else if (ch ==
'{' && inFormat)
73 return "return aie_runtime::txn_refused(\"" + lit +
"\");";
81constexpr llvm::StringLiteral kTxnVecType =
"std::vector<uint32_t>";
83emitc::OpaqueType getU32Type(MLIRContext *ctx) {
84 return emitc::OpaqueType::get(ctx,
"uint32_t");
90template <
typename PoolOp>
91std::string bdPoolName(PoolOp op) {
92 return "bd_pool_" + std::to_string(op.getColumn()) +
"_" +
93 std::to_string(op.getRow()) +
"_" + std::to_string(op.getBdBegin()) +
94 "_" + std::to_string(op.getBdEnd());
100llvm::SmallVector<uint32_t, 8> staticBdIdsOnTile(AIE::DeviceOp device,
101 uint32_t col, uint32_t row) {
102 llvm::SmallVector<uint32_t, 8> ids;
103 for (AIE::DmaBody program : device.getOps<AIE::DmaBody>()) {
104 auto id = program.getTileID();
105 if (
id.col !=
static_cast<int>(col) ||
id.row !=
static_cast<int>(row))
110 ids.erase(llvm::unique(ids), ids.end());
116Value u32Literal(OpBuilder &b, Location loc, uint32_t val) {
117 return emitc::ConstantOp::create(
118 b, loc, getU32Type(b.getContext()),
119 emitc::OpaqueAttr::get(b.getContext(), std::to_string(val) +
"u"));
125void emitTxnCall(OpBuilder &b, Location loc, StringRef fn, Value txnVec,
127 SmallVector<Value> callArgs;
128 callArgs.push_back(txnVec);
129 callArgs.append(
args.begin(),
args.end());
130 emitc::CallOpaqueOp::create(b, loc, TypeRange{}, (
"aie_runtime::" + fn).str(),
136struct DeviceResolved {
137 llvm::DenseMap<Operation *, uint32_t> absoluteAddr;
138 llvm::DenseMap<Operation *, DenseIntElementsAttr> blockWriteData;
141class AIEXToEmitCConverter {
143 AIEXToEmitCConverter(emitc::FuncOp funcOp, Value txnVec,
144 const DeviceResolved &resolved, Value opCountVar,
145 bool foldDDRAddrOffset)
146 : funcOp(funcOp), txnVec(txnVec), resolved(resolved),
147 opCountVar(opCountVar), foldDDRAddrOffset(foldDDRAddrOffset) {}
153 std::optional<uint32_t> run() {
160 funcOp.walk([&](Operation *op) {
161 if (isa<memref::MemRefDialect>(op->getDialect()) &&
162 isOpTriviallyDead(op)) {
169 SmallVector<Operation *> consumed;
170 convertBlockRecursive(funcOp.getBlocks().front(), count, consumed);
179 for (Operation *op : llvm::reverse(consumed))
181 IRRewriter rewriter(funcOp.getContext());
182 (void)runRegionDCE(rewriter, funcOp.getFunctionBody());
190 void convertBlockRecursive(Block &block, uint32_t &count,
191 SmallVector<Operation *> &consumed) {
192 SmallVector<Operation *> work;
193 for (Operation &op : block)
195 for (Operation *op : work) {
202 if (isa<emitc::EmitCDialect, arith::ArithDialect>(op->getDialect()))
204 if (isa<scf::SCFDialect>(op->getDialect())) {
205 for (Region &r : op->getRegions())
207 convertBlockRecursive(b, count, consumed);
210 convertOne(op, count);
211 if (!isa<memref::GetGlobalOp>(op))
212 consumed.push_back(op);
221 void countOp(OpBuilder &b, Location loc, uint32_t &count) {
224 emitc::VerbatimOp::create(b, loc,
"++{};", ValueRange{opCountVar});
231 void convertOne(Operation *op, uint32_t &count) {
233 Location loc = op->getLoc();
235 llvm::TypeSwitch<Operation *, void>(op)
236 .Case<AIEX::NpuWrite32Op>([&](
auto w) {
240 Value addrV = runtimeOrResolvedAddr(b, loc, w, w.getAddress());
242 return fail(w,
"cannot convert a symbolic/unresolved write32 "
243 "address to the C++ TXN target");
244 emitTxnCall(b, loc,
"txn_append_write32", txnVec,
245 {addrV, w.getValue()});
246 countOp(b, loc, count);
248 .Case<AIEX::NpuMaskWrite32Op>([&](
auto mw) {
249 Value addrV = runtimeOrResolvedAddr(b, loc, mw, mw.getAddress());
251 return fail(mw,
"cannot convert a symbolic/unresolved maskwrite32 "
252 "address to the C++ TXN target");
253 emitTxnCall(b, loc,
"txn_append_maskwrite32", txnVec,
254 {addrV, mw.getValue(), mw.getMask()});
255 countOp(b, loc, count);
257 .Case<AIEX::NpuMaskPollOp>([&](
auto mp) {
258 Value addrV = runtimeOrResolvedAddr(b, loc, mp, mp.getAddress());
260 return fail(mp,
"cannot convert a symbolic/unresolved maskpoll "
261 "address to the C++ TXN target");
262 emitTxnCall(b, loc,
"txn_append_maskpoll32", txnVec,
263 {addrV, mp.getValue(), mp.getMask()});
264 countOp(b, loc, count);
266 .Case<AIEX::NpuSyncOp>([&](
auto s) {
267 emitTxnCall(b, loc,
"txn_append_sync", txnVec,
268 {s.getColumn(), s.getRow(), s.getDirection(),
269 s.getChannel(), s.getColumnNum(), s.getRowNum()});
270 countOp(b, loc, count);
272 .Case<AIEX::NpuLoadPdiOp>([&](
auto pdi) {
273 Value address = emitc::ConstantOp::create(
274 b, loc, emitc::OpaqueType::get(b.getContext(),
"uint64_t"),
275 emitc::OpaqueAttr::get(b.getContext(),
276 std::to_string(pdi.getAddress()) +
"ULL"));
277 emitTxnCall(b, loc,
"txn_append_loadpdi", txnVec,
278 {u32Literal(b, loc, pdi.getId()),
279 u32Literal(b, loc, pdi.getSize()), address});
280 countOp(b, loc, count);
282 .Case<AIEX::NpuPreemptOp>([&](
auto preempt) {
283 emitTxnCall(b, loc,
"txn_append_preempt", txnVec,
284 {u32Literal(b, loc, preempt.getLevel())});
285 countOp(b, loc, count);
287 .Case<AIEX::NpuAddressPatchOp>([&](
auto ap) {
290 Value addrV = ap.getAddrVal() ? ap.getAddrVal()
291 : u32Literal(b, loc, ap.getAddr());
292 std::optional<uint32_t> argIdx = ap.getArgIdx();
294 return fail(ap,
"address_patch still names its host buffer by SSA "
295 "value; run -aie-resolve-address-patch-buffers");
296 Value idxV = emitc::ConstantOp::create(
297 b, loc, emitc::OpaqueType::get(b.getContext(),
"int32_t"),
298 emitc::OpaqueAttr::get(b.getContext(), std::to_string(*argIdx)));
302 Value foldV = emitc::ConstantOp::create(
303 b, loc, emitc::OpaqueType::get(b.getContext(),
"bool"),
304 emitc::OpaqueAttr::get(b.getContext(),
305 foldDDRAddrOffset ?
"true" :
"false"));
306 Value argPlus = ap.getArgPlus();
309 if (argPlus.getType().isInteger(32))
310 argPlus = emitc::CastOp::create(b, loc, getU32Type(b.getContext()),
312 emitTxnCall(b, loc,
"txn_append_arg_patch", txnVec,
313 {addrV, idxV, argPlus, foldV});
314 countOp(b, loc, count);
316 .Case<AIEX::NpuBlockWriteOp>([&](
auto bw) {
317 convertBlockWrite(b, loc, bw);
318 countOp(b, loc, count);
320 .Case<AIEX::NpuBlockWriteValuesOp>([&](
auto bw) {
321 convertBlockWriteValues(b, loc, bw);
322 countOp(b, loc, count);
324 .Case<cf::AssertOp>([&](
auto a) {
328 emitc::VerbatimOp::create(
329 b, loc,
"if (!({})) " + refuse(a.getMsg(),
true),
330 ValueRange{a.getArg()});
332 .Case<AIEX::DMABdPoolPopOp>([&](AIEX::DMABdPoolPopOp pop) {
337 std::string var =
"bd_" + std::to_string(nextPoolVar++);
338 emitc::VerbatimOp::create(
340 "uint32_t " + var +
"; if (!aie_runtime::bd_pool_pop(" +
341 bdPoolName(pop) +
", " + var +
")) " +
342 refuse(
"no free buffer descriptor on tile (" +
343 std::to_string(pop.getColumn()) +
", " +
344 std::to_string(pop.getRow()) +
345 "): the sequence keeps more transfers in flight "
346 "than the tile has descriptors",
349 emitc::LiteralOp::create(b, loc, pop.getBdId().getType(), var);
350 pop.getBdId().replaceAllUsesWith(ref);
352 .Case<AIEX::DMABdPoolPushOp>([&](AIEX::DMABdPoolPushOp push) {
354 emitc::CallOpaqueOp::create(
355 b, loc, TypeRange{},
"aie_runtime::bd_pool_push",
356 ValueRange{poolRef(b, loc, bdPoolName(push)), push.getBdId()});
360 .Case<AIEX::NpuCreateScratchpadOp, AIEX::NpuUpdateFromScratchpadOp>(
362 fail(sp,
"needs the scratchpad of a full-ELF context, which the "
363 "C++ TXN target's per-call streams run without; use a "
364 "runtime sequence argument instead of a scratchpad "
369 .Case<memref::GetGlobalOp>([&](
auto) {})
370 .Default([&](Operation *unsupported) {
371 if (isa<scf::SCFDialect>(unsupported->getDialect()))
373 "control flow in runtime sequences is not yet supported by "
374 "the C++ TXN target");
376 fail(unsupported,
"is not supported by the C++ TXN target");
384 Value resolvedAddr(OpBuilder &b, Location loc, Operation *clone) {
385 auto it = resolved.absoluteAddr.find(clone);
386 if (it == resolved.absoluteAddr.end())
388 return u32Literal(b, loc, it->second);
395 Value runtimeOrResolvedAddr(OpBuilder &b, Location loc, Operation *clone,
397 if (Value lit = resolvedAddr(b, loc, clone))
399 Operation *def = addrOperand ? addrOperand.getDefiningOp() :
nullptr;
400 if (def && !isa<arith::ConstantOp>(def))
406 void fail(Operation *op,
const llvm::Twine &msg) {
407 op->emitOpError(msg);
413 Value poolRef(OpBuilder &b, Location loc, StringRef name) {
414 return emitc::LiteralOp::create(
415 b, loc, emitc::OpaqueType::get(b.getContext(),
"aie_runtime::BdPool"),
419 void convertBlockWrite(OpBuilder &b, Location loc, AIEX::NpuBlockWriteOp bw) {
420 auto addrIt = resolved.absoluteAddr.find(bw);
421 auto dataIt = resolved.blockWriteData.find(bw);
422 if (addrIt == resolved.absoluteAddr.end() ||
423 dataIt == resolved.blockWriteData.end() || !dataIt->second) {
424 bw.emitOpError(
"cannot convert blockwrite without a constant address and "
425 "data to the C++ TXN target");
429 uint32_t addr = addrIt->second;
430 DenseIntElementsAttr data = dataIt->second;
434 MLIRContext *ctx = b.getContext();
435 int64_t n = data.size();
437 emitc::ArrayType::get(ctx, SmallVector<int64_t>{n}, getU32Type(ctx));
438 std::string init =
"{";
439 llvm::raw_string_ostream ss(init);
441 for (APInt word : data.getValues<APInt>()) {
444 ss << llvm::format(
"0x%08xu",
static_cast<unsigned>(word.getZExtValue()));
448 auto arrVar = emitc::VariableOp::create(b, loc, arrTy,
449 emitc::OpaqueAttr::get(ctx, init));
451 uint32_t colVal = 0, rowVal = 0;
452 if (
auto col = bw.getColumn(), row = bw.getRow(); col && row) {
456 emitTxnCall(b, loc,
"txn_append_blockwrite", txnVec,
457 {u32Literal(b, loc, addr), arrVar.getResult(),
458 u32Literal(b, loc,
static_cast<uint32_t
>(n)),
459 u32Literal(b, loc, colVal), u32Literal(b, loc, rowVal)});
467 void convertBlockWriteValues(OpBuilder &b, Location loc,
468 AIEX::NpuBlockWriteValuesOp bw) {
469 MLIRContext *ctx = b.getContext();
470 Value addrV = runtimeOrResolvedAddr(b, loc, bw, bw.getAddress());
472 fail(bw,
"cannot convert a symbolic/unresolved blockwrite_values address "
473 "to the C++ TXN target");
476 ValueRange words = bw.getValues();
477 int64_t n =
static_cast<int64_t
>(words.size());
482 emitc::ArrayType::get(ctx, SmallVector<int64_t>{n}, getU32Type(ctx));
483 auto arrVar = emitc::VariableOp::create(b, loc, arrTy,
484 emitc::OpaqueAttr::get(ctx,
"{}"));
490 for (int64_t i = 0; i < n; i++)
491 emitc::VerbatimOp::create(b, loc,
"{}[" + std::to_string(i) +
"] = {};",
492 ValueRange{arrVar.getResult(), words[i]});
495 emitTxnCall(b, loc,
"txn_append_blockwrite", txnVec,
496 {addrV, arrVar.getResult(),
497 u32Literal(b, loc,
static_cast<uint32_t
>(n)),
498 u32Literal(b, loc, 0), u32Literal(b, loc, 0)});
501 emitc::FuncOp funcOp;
503 const DeviceResolved &resolved;
509 unsigned nextPoolVar = 0;
510 bool foldDDRAddrOffset;
513struct ConvertAIEXToEmitCPass
514 : xilinx::impl::ConvertAIEXToEmitCBase<ConvertAIEXToEmitCPass> {
515 ConvertAIEXToEmitCPass() =
default;
518 explicit ConvertAIEXToEmitCPass(
const ConvertAIEXToEmitCOptions &options)
519 : ConvertAIEXToEmitCBase(options) {}
521 void runOnOperation()
override {
522 ModuleOp moduleOp = getOperation();
523 MLIRContext *ctx = &getContext();
525 return signalPassFailure();
528 AIE::RuntimeSequenceOp seq;
529 AIE::DeviceOp device;
531 SmallVector<SeqInfo> sequences;
532 moduleOp.walk([&](AIE::RuntimeSequenceOp seq) {
533 sequences.push_back({seq, seq->getParentOfType<AIE::DeviceOp>()});
535 if (emitDispatchShim && sequences.size() != 1) {
536 moduleOp.emitOpError()
537 <<
"emit-dispatch-shim needs exactly one aie.runtime_sequence to "
540 return signalPassFailure();
542 if (sequences.empty())
545 OpBuilder builder(ctx);
546 for (
auto &[seqOp, deviceOp] : sequences) {
548 seqOp.emitOpError(
"must be nested inside an aie.device");
549 return signalPassFailure();
551 std::optional<emitc::FuncOp> gen =
552 emitFunction(builder, moduleOp, seqOp, deviceOp);
554 return signalPassFailure();
555 if (emitDispatchShim) {
556 emitc::FuncOp generated = *gen;
557 if (failed(emitDispatchShimFuncs(builder, moduleOp, generated)))
558 return signalPassFailure();
563 SmallVector<Operation *> toErase;
564 for (Operation &op : *moduleOp.getBody())
565 if (!isa<emitc::FuncOp, emitc::IncludeOp>(op))
566 toErase.push_back(&op);
567 for (Operation *op : llvm::reverse(toErase))
573 if (failed(lowerArithAndScfToEmitC(moduleOp)))
574 return signalPassFailure();
576 builder.setInsertionPointToStart(moduleOp.getBody());
577 emitc::IncludeOp::create(builder, moduleOp.getLoc(),
578 "aie/Runtime/TxnEncoding.h",
580 emitc::IncludeOp::create(builder, moduleOp.getLoc(),
"cstdint",
582 emitc::IncludeOp::create(builder, moduleOp.getLoc(),
"vector",
586 emitc::IncludeOp::create(builder, moduleOp.getLoc(),
"optional",
593 LogicalResult lowerArithAndScfToEmitC(ModuleOp moduleOp) {
598 RewritePatternSet expand(moduleOp.getContext());
599 arith::populateCeilFloorDivExpandOpsPatterns(expand);
600 arith::populateExpandMinMaxIPatterns(expand);
603 GreedyRewriteConfig config;
604 config.enableFolding(
false).enableConstantCSE(
false);
605 if (failed(applyPatternsGreedily(moduleOp, std::move(expand), config)))
609 TypeConverter typeConverter;
610 typeConverter.addConversion([](Type t) {
return t; });
611 populateEmitCSizeTTypeConversions(typeConverter);
613 RewritePatternSet patterns(moduleOp.getContext());
614 populateArithToEmitCPatterns(typeConverter, patterns);
615 populateSCFToEmitCConversionPatterns(patterns, typeConverter);
617 ConversionTarget target(*moduleOp.getContext());
618 target.addLegalDialect<emitc::EmitCDialect>();
619 target.addIllegalDialect<arith::ArithDialect>();
620 target.addIllegalDialect<scf::SCFDialect>();
621 return applyPartialConversion(moduleOp, target, std::move(patterns));
628 static std::optional<std::string> cTypeName(Type t) {
629 if (isa<emitc::SizeTType>(t))
630 return std::string(
"size_t");
631 auto intTy = dyn_cast<IntegerType>(t);
632 if (!intTy || !emitc::isSupportedIntegerType(intTy))
634 if (intTy.getWidth() == 1)
635 return std::string(
"bool");
636 return (intTy.isUnsigned() ?
"uint" :
"int") +
637 std::to_string(intTy.getWidth()) +
"_t";
643 LogicalResult emitDispatchShimFuncs(OpBuilder &builder, ModuleOp moduleOp,
645 MLIRContext *ctx = moduleOp.getContext();
646 Location loc = gen.getLoc();
647 TypeRange genParams = gen.getFunctionType().getInputs();
649 SmallVector<std::string> abiTypes;
650 for (Type t : genParams) {
651 std::optional<std::string> name = cTypeName(t);
653 return gen.emitOpError()
654 <<
"runtime-sequence parameter type " << t
655 <<
" has no dispatch ABI spelling; it cannot be passed from the "
656 "host at dispatch time";
657 abiTypes.push_back(*name);
660 builder.setInsertionPointToEnd(moduleOp.getBody());
668 builder.getStrArrayAttr({
"extern \"C\"",
"AIE_DISPATCH_EXPORT"});
673 emitc::PointerType::get(emitc::OpaqueType::get(ctx,
"const char"));
674 auto abiFn = emitc::FuncOp::create(builder, loc,
"dispatch_abi",
675 FunctionType::get(ctx, {}, {charPtrTy}));
676 abiFn.setSpecifiersAttr(externC);
677 OpBuilder ab = OpBuilder::atBlockBegin(abiFn.addEntryBlock());
678 Value abiStr = emitc::LiteralOp::create(
679 ab, loc, charPtrTy,
"\"" + llvm::join(abiTypes,
",") +
"\"");
680 emitc::ReturnOp::create(ab, loc, abiStr);
682 SmallVector<Type> shimParams(genParams);
683 shimParams.push_back(
684 emitc::PointerType::get(emitc::PointerType::get(getU32Type(ctx))));
685 auto i64Ty = emitc::OpaqueType::get(ctx,
"int64_t");
687 emitc::FuncOp::create(builder, loc,
"dispatch_generate",
688 FunctionType::get(ctx, shimParams, {i64Ty}));
689 shimFn.setSpecifiersAttr(externC);
690 Block *shimBlock = shimFn.addEntryBlock();
691 OpBuilder sb = OpBuilder::atBlockBegin(shimBlock);
693 SmallVector<Value> genArgs(shimBlock->getArguments().drop_back());
694 SmallVector<StringRef> holes(genArgs.size(),
"{}");
695 std::string call =
"auto __result = " + gen.getSymName().str() +
"(" +
696 llvm::join(holes,
", ") +
");";
697 emitc::VerbatimOp::create(
698 sb, loc,
"static thread_local " + kTxnVecType.str() +
" __txn;");
699 emitc::VerbatimOp::create(sb, loc,
"aie_runtime::txn_refusal = nullptr;");
700 emitc::VerbatimOp::create(sb, loc, call, genArgs);
701 emitc::VerbatimOp::create(sb, loc,
"if (!__result) return -2;");
702 emitc::VerbatimOp::create(sb, loc,
"__txn = std::move(*__result);");
703 emitc::VerbatimOp::create(sb, loc,
"*{} = __txn.data();",
704 ValueRange{shimBlock->getArguments().back()});
705 emitc::ReturnOp::create(
707 emitc::LiteralOp::create(sb, loc, i64Ty,
708 "static_cast<int64_t>(__txn.size())"));
712 auto whyFn = emitc::FuncOp::create(builder, loc,
"dispatch_last_refusal",
713 FunctionType::get(ctx, {}, {charPtrTy}));
714 whyFn.setSpecifiersAttr(externC);
715 OpBuilder wb = OpBuilder::atBlockBegin(whyFn.addEntryBlock());
716 emitc::ReturnOp::create(
718 emitc::LiteralOp::create(wb, loc, charPtrTy,
719 "aie_runtime::txn_refusal"));
725 static void recordDeviceResolved(Operation *orig, Operation *clone,
726 DeviceResolved &resolved) {
727 if (
auto w = dyn_cast<AIEX::NpuWrite32Op>(orig)) {
728 if (
auto a = w.getAbsoluteAddress())
729 resolved.absoluteAddr[clone] = *a;
730 }
else if (
auto mw = dyn_cast<AIEX::NpuMaskWrite32Op>(orig)) {
731 if (
auto a = mw.getAbsoluteAddress())
732 resolved.absoluteAddr[clone] = *a;
733 }
else if (
auto mp = dyn_cast<AIEX::NpuMaskPollOp>(orig)) {
734 if (
auto a = mp.getAbsoluteAddress())
735 resolved.absoluteAddr[clone] = *a;
736 }
else if (
auto bw = dyn_cast<AIEX::NpuBlockWriteOp>(orig)) {
737 if (
auto a = bw.getAbsoluteAddress())
738 resolved.absoluteAddr[clone] = *a;
739 if (
auto d = bw.getDataWords())
740 resolved.blockWriteData[clone] = d;
741 }
else if (
auto bwv = dyn_cast<AIEX::NpuBlockWriteValuesOp>(orig)) {
744 resolved.absoluteAddr[clone] = *a;
748 FailureOr<emitc::FuncOp> emitFunction(OpBuilder &builder, ModuleOp moduleOp,
749 AIE::RuntimeSequenceOp seqOp,
750 AIE::DeviceOp deviceOp) {
751 Location loc = seqOp.getLoc();
752 Block &entry = seqOp.getBody().front();
757 SmallVector<Type> paramTypes;
759 emitc::OpaqueType::get(moduleOp.getContext(), kTxnVecType);
763 auto txnRetType = emitc::OpaqueType::get(
764 moduleOp.getContext(),
765 (llvm::Twine(
"std::optional<") + kTxnVecType +
">").str());
770 auto sizeT = emitc::SizeTType::get(moduleOp.getContext());
771 auto paramTypeFor = [&](Type t) -> Type {
772 return isa<IndexType>(t) ? Type(sizeT) : t;
774 for (BlockArgument arg : entry.getArguments())
775 if (!isa<BaseMemRefType>(arg.getType()))
776 paramTypes.push_back(paramTypeFor(arg.getType()));
778 builder.setInsertionPointToEnd(moduleOp.getBody());
779 std::string funcName =
"generate_txn_" + deviceOp.getSymName().str() +
"_" +
780 seqOp.getSymName().str();
781 auto funcOp = emitc::FuncOp::create(
782 builder, loc, funcName,
783 FunctionType::get(moduleOp.getContext(), paramTypes,
784 TypeRange{txnRetType}));
785 funcOp.setSpecifiersAttr(builder.getStrArrayAttr({
"inline"}));
786 Block *funcBlock = funcOp.addEntryBlock();
787 OpBuilder fb = OpBuilder::atBlockBegin(funcBlock);
793 emitc::VerbatimOp::create(fb, loc,
"std::vector<uint32_t> txn;");
794 Value txnVec = emitc::LiteralOp::create(fb, loc, txnVecType,
"txn");
795 emitTxnCall(fb, loc,
"txn_init", txnVec, {});
799 llvm::StringSet<> declaredPools;
800 auto declarePool = [&](
auto poolOp) {
801 std::string name = bdPoolName(poolOp);
802 if (!declaredPools.insert(name).second)
804 uint32_t lo = poolOp.getBdBegin(), hi = poolOp.getBdEnd();
805 emitc::VerbatimOp::create(fb, loc,
806 "aie_runtime::BdPool " + name +
807 " = aie_runtime::bd_pool_init_range(" +
808 std::to_string(lo) +
", " +
809 std::to_string(hi) +
");");
812 staticBdIdsOnTile(deviceOp, poolOp.getColumn(), poolOp.getRow()))
813 if (
id >= lo &&
id < hi)
814 emitc::VerbatimOp::create(fb, loc,
815 "aie_runtime::bd_pool_reserve(" + name +
816 ", " + std::to_string(
id) +
");");
818 seqOp.walk([&](Operation *op) {
819 if (
auto pop = dyn_cast<AIEX::DMABdPoolPopOp>(op))
821 else if (
auto push = dyn_cast<AIEX::DMABdPoolPushOp>(op))
828 bool hasControlFlow =
false;
829 seqOp.walk([&](Operation *op) {
830 if (isa<scf::SCFDialect>(op->getDialect()))
831 hasControlFlow =
true;
834 if (hasControlFlow) {
835 emitc::VerbatimOp::create(fb, loc,
"uint32_t __opcount = 0;");
836 opCountVar = emitc::LiteralOp::create(
837 fb, loc, getU32Type(moduleOp.getContext()),
"__opcount");
846 for (BlockArgument arg : entry.getArguments())
847 if (!isa<BaseMemRefType>(arg.getType()))
848 mapping.map(arg, funcBlock->getArgument(p++));
849 DeviceResolved resolved;
850 for (Operation &op : entry.without_terminator()) {
851 fb.clone(op, mapping);
854 op.walk([&](Operation *orig) {
855 Operation *c = mapping.lookupOrNull(orig);
857 recordDeviceResolved(orig, c, resolved);
861 AIEXToEmitCConverter conv(funcOp, txnVec, resolved, opCountVar,
863 std::optional<uint32_t> count = conv.run();
870 const AIE::AIETargetModel &tm = deviceOp.getTargetModel();
871 OpBuilder eb(funcBlock, funcBlock->end());
872 std::string countStr =
873 hasControlFlow ?
"__opcount" : (std::to_string(*count) +
"u");
876 std::string header =
"aie_runtime::txn_prepend_header(txn, " + countStr +
877 ", aie_runtime::txn_device_info(" +
879 std::to_string(tm.
rows()) +
", " +
880 std::to_string(tm.
columns()) +
", " +
882 emitc::VerbatimOp::create(eb, loc, header);
887 Value ret = emitc::LiteralOp::create(eb, loc, txnRetType,
"std::move(txn)");
888 emitc::ReturnOp::create(eb, loc, ret);
895std::unique_ptr<OperationPass<ModuleOp>>
897 return std::make_unique<ConvertAIEXToEmitCPass>();
900std::unique_ptr<OperationPass<ModuleOp>>
902 bool emitDispatchShim) {
903 ConvertAIEXToEmitCOptions options;
904 options.foldDDRAddrOffset = foldDDRAddrOffset;
905 options.emitDispatchShim = emitDispatchShim;
906 return std::make_unique<ConvertAIEXToEmitCPass>(options);
virtual int rows() const =0
Return the number of rows in the device.
virtual int columns() const =0
Return the number of columns in the device.
virtual uint32_t getNumMemTileRows() const =0
std::optional< uint32_t > getConstantIntOperand(mlir::Value v)
uint8_t txnDeviceGen(const AIETargetModel &tm)
Device generation ID written into the TXN header (aie_runtime::TxnDeviceInfo::devGen).
mlir::LogicalResult inlineWriteConfigs(mlir::ModuleOp module)
llvm::SmallVector< uint32_t > getAssignedBdIds(DmaBody program)
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createConvertAIEXToEmitCPass()