合法oprewritepattern需满足三条件:操作设hascanonicalizer=1、模式注册到getcanonicalizationpatterns、matchandrewrite中调用rewriter.replaceop;模板参数须为具体op类型,benefit值控制优先级,返回logicalresult,严防blockargument解引用、ssa破坏、类型不匹配及插入点错误。

直接用 OpRewritePattern 重写操作是 MLIR 中最常用、最可控的方式,但必须满足三个硬性条件:操作得有 hasCanonicalizer = 1、模式得注册进 getCanonicalizationPatterns、且 matchAndRewrite 里不能漏掉 rewriter.replaceOp 或等效调用。
怎么写一个合法的 OpRewritePattern 子类
核心不是“能编译”,而是让 MLIR 的 Canonicalizer 能识别并触发它。以消除双重转置为例:
-
OpRewritePattern模板参数必须是具体操作类型(如TransposeOp),不能是基类或Operation* - 构造函数里传入
benefit值(整数),值越大越优先匹配;通常设为 1 就够用,除非你有多个冲突模式 -
matchAndRewrite必须返回LogicalResult:成功用success(),失败用failure(),不能只写逻辑不返值 - 匹配时别直接用
getDefiningOp<someop>()</someop>判空后就往下走——如果返回nullptr,getOperand()可能是 block argument,这时再取.getDefiningOp会 crash
matchAndRewrite 里最容易崩的几处
这个函数看着简单,实操中 70% 的 segfault 或 IR 验证失败都出在这里:
- 没检查 operand 是否为
BlockArgument:比如op.getOperand().isa<blockargument>()</blockargument>为真时,.getDefiningOp<...>()</...>返回nullptr,后续解引用直接挂 - 替换时用了错误的重写器接口:想删掉整个 op 并替换成一个值,必须用
rewriter.replaceOp(op, {new_value});若误用rewriter.eraseOp(op)+ 手动插入新 op,会破坏 SSA 使用链,IR 验证通不过 - 新值的类型没对齐:比如原 op 输出是
tensor,你 replace 成一个tensor,类型不兼容,replaceOp会静默失败(返回failure())但不报错 - 在 pattern 里调用了
rewriter.create<...>()</...>却没设好插入点:默认插入点可能在 module 顶层,导致新 op 不在合法 region 内;应先用rewriter.setInsertionPoint(op)或rewriter.setInsertionPointAfter(op)
怎么让 Canonicalizer 真正跑起来
写了 pattern 不等于它会被调用。MLIR 不会自动扫描所有 OpRewritePattern 子类:
- 必须在对应操作的
getCanonicalizationPatterns静态方法里显式results.add<yourpattern>(context)</yourpattern> - 对应操作定义的 ODS 文件(
.td)里得有hasCanonicalizer = 1,否则该方法根本不会被调用 - 运行 pass 时得明确启用 canonicalizer:比如用
mlir::createCanonicalizerPass(),或者在PassManager里加pm.addPass(mlir::createCanonicalizerPass()) - 如果你用的是自定义工具(如
toyc),确保它在构建PassManager时加载了你的 dialect,并调用了loadDialect<yourdialect>()</yourdialect>,否则 pattern 注册代码压根不会执行
为什么有时 pattern 匹配了却不生效
常见但难定位的原因集中在“上下文生命周期”和“多次迭代顺序”上:
- pattern 构造时传的
MLIRContext*和实际运行时PassManager用的 context 不是同一个实例(比如你在测试代码里 new 了一个 context,但 pass 运行在另一个里),会导致 pattern 被忽略 - canonicalizer 是贪婪迭代的:第一次只消掉外层
transpose(transpose(x)),生成新 IR 后会再跑一轮;但如果新 IR 里又引入了别的可优化结构(比如常量折叠),而你的 pattern 没覆盖那个 case,就会卡住 - 多个 pattern 的
benefit值相同,MLIR 不保证执行顺序,可能 A pattern 改了 IR 导致 B pattern 失效,反过来也一样——调试时建议一次只注册一个 pattern - IR 验证失败会终止整个 canonicalizer pass,但错误信息常藏在 verbose 日志里(加
-debug-only=canonicalize才能看到),表面看起来就是“没反应”
真正麻烦的从来不是写 pattern,而是确认它在正确的 context 下、被正确的 pass 调用、作用在正确的 operand 上、并用正确的类型和位置完成替换。少一个环节,IR 就停在那儿不动。











