toy.print不会自动降到llvm,因其未被标记为非法操作且缺少配套conversionpattern;需显式标记非法、实现matchandrewrite生成全局字符串和printf调用,并用applyfullconversion注册模式。

toy.print 这类自定义操作无法被 MLIR 的标准 lowering 框架自动转到 LLVM 方言,必须手动提供 ConversionPattern 实现。这是最常卡住的地方——不是框架不支持,而是它默认只处理标准 dialect(如 arith、memref、affine),对用户自定义的 toy::PrintOp 完全“视而不见”。
为什么 toy.print 不会自动降到 LLVM?
MLIR 的方言转换框架(DialectConversion)只对注册为“非法操作”的 op 触发 lowering,但前提是:你明确告诉它哪些是非法的、哪些目标 dialect 是合法的。默认情况下,toy::PrintOp 既没被标记为非法,也没有配套的 lowering pattern,所以它会原封不动留在 IR 里,直到报错:failed to legalize operation 'toy.print'。
-
ConversionTarget必须显式标记toy::PrintOp为非法:target.markOpAsIllegal<:printop>()</:printop> - 仅标记非法还不够——没有
ConversionPattern,框架连“怎么降”都不知道 - LLVM 方言本身不提供
printf的封装,得你自己在 module 里声明或插入LLVM::LLVMFuncOp
PrintOpLowering 的 matchAndRewrite 要写什么?
核心是两件事:生成字符串常量、调用 printf。不能直接 emit 字符串字面量,得先用 LLVM::GlobalOp 声明全局 C 字符串,再取其地址传给 printf。示例关键片段:
LogicalResult matchAndRewrite(toy::PrintOp op, ArrayRef<value> operands,
ConversionPatternRewriter& rewriter) const override {
Location loc = op.getLoc();
auto module = op->getParentOfType<moduleop>();
auto printfRef = getOrInsertPrintf(rewriter, module);
// 创建 C 风格字符串:"\n\0"
auto stringAttr = rewriter.getStringAttr("\n");
auto global = rewriter.create<:globalop>(
loc, LLVM::LLVMPointerType::get(IntegerType::get(rewriter.getContext(), 8)),
/*isConstant=*/true, LLVM::Linkage::Internal, "str", stringAttr);
auto stringPtr = rewriter.create<:addressofop>(loc, global);
// 调用 printf("%s", ptr)
rewriter.create<:callop>(loc, printfRef, IntegerType::get(rewriter.getContext(), 32),
ArrayRef<value>{stringPtr});
rewriter.eraseOp(op);
return success();
}</value></:callop></:addressofop></:globalop></moduleop></value>
- 别漏掉
rewriter.eraseOp(op),否则旧 op 还在,后续 pass 会重复处理或冲突 -
LLVM::AddressOfOp是必须的——LLVM 方言不接受裸字符串字面量,只认指针 - 如果要打印 tensor 内容,得额外加循环 +
memref.load+ 类型转换,不是一行printf能搞定的
怎么把 pattern 注册进 lowering 流程?
不是写了 class 就生效。必须在 pass 中显式插入 pattern 列表,并确保它和 ConversionTarget、TypeConverter 三者协同工作:
OwningRewritePatternList patterns(&getContext()); patterns.insert<printoplowering>(&getContext()); // 注意:这里必须用 FullConversion,PartialConversion 会放过未处理的 op if (failed(applyFullConversion(getOperation(), target, std::move(patterns)))) signalPassFailure();</printoplowering>
- 用
applyFullConversion,不是applyPartialConversion——后者允许残留非法 op,但最终生成 LLVM IR 时会失败 -
PrintOpLowering构造函数里传的toy::PrintOp::getOperationName()必须和实际 op 名完全一致(含命名空间),大小写敏感 - 如果你的 op 有 operand 或 result 类型(比如
tensor<f64></f64>),还得配TypeConverter把tensor转成memref,否则类型不匹配直接 crash
printf)的依赖是隐式的。你生成了 call,但没保证链接时能 resolve ——JIT 执行前得手动注入 printf 符号,或者用 ExecutionEngine 绑定系统 libc。这步不做,createJIT 会静默失败或 segfault。











