feat(pto/ptodsl): restore unroll="enable" metadata hint (issue #1242 Req 2) - #1339
feat(pto/ptodsl): restore unroll="enable" metadata hint (issue #1242 Req 2)#1339jimmychou0 wants to merge 1 commit into
Conversation
92eaca6 to
6265616
Compare
| // --------------------------------------------------------------------------- | ||
|
|
||
| std::unique_ptr<Pass> mlir::pto::createPTOLowerLoopHintsPass() { | ||
| return std::make_unique<PTOLowerLoopHints>(); |
There was a problem hiding this comment.
PTOLowerLoopHintsPass虽然用于传递 unroll="enable" hint,但实现中还会将带 hint 的 scf.for提前降低成多个cf block,以便把 llvm.loop_annotation挂到loop latch。
当只有内层 loop 带 hint 时:
scf.for %i = ... { // 外层无 hint
scf.for %j = ... { // 内层 enable
...
} {pto.unroll = "enable"}
}
该 pass 只lower内层 loop,内层生成的 condition、body、latch、exit blocks会被放入外层scf.for的 region,使外层 region 包含多个 block,违反scf.for的 SingleBlock 约束。相同问题也可
能发生在scf.if等single-block structured operation中。
由于emitter的PassManager开启了 enableVerifier(),验证会在 PTOLowerLoopHintsPass结束后、标准 ConvertSCFToCFPass执行前。因此 pipeline 会直接失败报错。
当前 test case 的内外层都带unroll enable,两层都会在当前 pass 中降低,因此没有覆盖这个问题。
建议不要在独立 hint pass 中局部降低 annotated loop。可以将 hint 处理合并到一个 PTOAS-specific 的完整 SCF-to-CF pass:
annotated scf.for使用自定义 lowering,将llvm.loop.unroll.enable放到对应 latch;- 无 hint 的外层
scf.for、scf.if、scf.while使用标准 lowering; - 同一个 pass 内完成所有结构转换,结束时不残留 multi-block SCF operation。
There was a problem hiding this comment.
已按方案改为单 pass 完成完整 SCF→CF 转换。
| } | ||
|
|
||
| if (unrollAttr) | ||
| if (unrollAttr) { |
There was a problem hiding this comment.
pto-unroll-loops在识别unroll="enable"之前,会先检查empty body和induction variable类型。非 index loop会进入不支持分支,导致 enable 属性被删除,无法继续传递为llvm.loop.unroll.enable。
这些限制只应作用于full和unroll_factor,因为只有它们调用 PTOAS 的native-unroll utility;enable只负责metadata透传,不应受native-unroll能力限制。
建议将 enable 判断移动到所有 native-unroll guard 之前,并增加非 index loop 的属性保留。PTODSL 通常生成 index loop,因此影响范围有限,但当前行为违反了 PR 定义的“enable 由 metadata pass 独占消费”契约。
There was a problem hiding this comment.
核对代码后发现影响面比预估稍大:除了非 index loop,空 body 的 enable loop 也会被吃掉 hint——两个 guard 都在 enable 判断之前,且无条件 removeAttr(pto.unroll)
c427632 to
175f30e
Compare
| VPTO emission pipeline(两个 emitter): | ||
| [Pass B] pto-lower-loop-hints | ||
| └─ "enable" → #llvm.loop_annotation, | ||
| 自行 SCF→CF 降级并挂 latch cf.br |
| @@ -0,0 +1,278 @@ | |||
| // Copyright (c) 2026 Huawei Technologies Co., Ltd. | |||
There was a problem hiding this comment.
建议名字为PTOConvertSCFToCFWithLoopHintsPass,因为现在已经不仅处理hint了,也会真正处理控制流。
…-native-sys#1242 Req 2) PR hw-native-sys#1295 removed the metadata forwarding stage together with the cost-model-delegating hints, but hw-native-sys#1242 Req2's acceptance criteria require exactly that path: "public pto.for_ can carry an unroll-enable hint" and "the hint reaches LLVM/BiSheng instead of being force-unrolled by the frontend". Restore it as a slimmed-down single channel - enable only; disable stays unsupported; full/factor semantics unchanged. Backend (pto-convert-scf-to-cf-with-loop-hints): - The new pass owns this pipeline's SCF-to-CF conversion (it replaces createConvertSCFToCFPass in both VPTO emitters) and adds the one thing the stock pass cannot do on LLVM 19: carrying {pto.unroll = "enable"} over to the loop latch as #llvm.loop_annotation<unroll = <disable = false>>, which the MLIR-to-LLVM-IR translation turns into !llvm.loop.unroll.enable (LLVM's ForceEnable semantics: the cost model picks full/partial unroll with its budget veto lifted). An existing llvm.loop_annotation is merged, not overwritten (a pre-existing unroll entry is replaced with a warning). - It runs populateSCFToControlFlowConversionPatterns plus a benefit=2 pattern for annotated loops under one applyPartialConversion. Converting the whole function is required for correctness: lowering an annotated loop in isolation leaves its new condition/body/latch/exit blocks inside whatever enclosing single-block region held it (an outer unannotated scf.for, an scf.if, ...) and fails that op's verifier before the stock conversion ever runs. - The annotation is stored on the latch cf.br under the bare ODS name loop_annotation, which BrOp::getLoopAnnotationAttr() looks up during translation; convert-cf-to-llvm forwards branch attributes verbatim. - pto-unroll-loops validates "enable" as legal and passes it through untouched, checked before every native-unroll guard (empty body, non-index induction variable): those guards exist because loopUnrollByFactor cannot handle such loops, which is irrelevant for a hint that only becomes metadata. "disable" and every other unknown value remain hard errors, and the fixpoint now propagates UnrollOutcome::Error instead of treating a hard-error loop as unchanged. - pto-test-opt registers the LLVM dialect so tests can write llvm.loop_annotation inputs. Frontend: - normalize_unroll_hint accepts "enable" again (only "disable" stays unsupported); pto.for_ / pto.range / tile-template paths get it for free; docstrings updated. Tests: - lit convert_scf_to_cf_with_loop_hints.pto covers the enable translation, iter_args threading, nested enable loops, annotation merge/overwrite warning, empty-body lowering, an inner-only annotated loop (the single-block hazard above), annotated loops inside scf.if and scf.while, a non-index annotated loop, a hint-free function still being converted, and an end-to-end ptoas --emit-vpto-llvm-ir check that !llvm.loop.unroll.enable reaches the final LLVM IR. - unroll_loops.pto locks the pass-through, including on the loop shapes the native-unroll guards reject; unroll_loops_invalid.pto keeps the remaining hard errors. - Frontend tests: enable attribute emission; rejection now covers only "disable". - dsl-st unroll_hint_numeric: enable case back (out[3], golden 28) - the hinted loop executes normally and computes correctly. Docs: - Design doc v4: the metadata channel is restored (enable only) and the pass is documented as the pipeline's SCF-to-CF conversion; semantics matrix, architecture diagram, pass section, diagnostics and test plans updated bilingually. - User guide: enable back in the hint table; the no-semantic-change rule distinguishes the metadata hint from the native-unroll hints. Validation (144): full lit suite green except three element/predicate/vsts offset-index tests that fail identically with this pass disabled - they belong to main's hw-native-sys#1330 and are unrelated. Frontend test_loop_unroll_hints / test_ptoas_frontend_verify pass; CANN simulator dsl-st suite passes with no failures; compliance check reports errors=0.
175f30e to
5acad2f
Compare
概述
恢复
unroll="enable"hint 的 LLVM metadata 通道,闭环 issue #1242 Requirement 2。PR #1295 在评审后移除了整个阶段一(metadata 透传),但 #1242 Req2 的验收标准要求的正是这条路径:
full/unroll_factor的原生展开与 enable 语义互斥(前者是前端强制展开,后者是把 full/partial 的决定留给编译器 cost model),无法互相替代。本 PR 以最小子集恢复该通道:只恢复enable,disable不再恢复,full/factor的现有语义完全不变。实现
pto-lower-loop-hints(精简版,仅 enable):接在两个 VPTO emitter 管线的convert-scf-to-cf之前。把{pto.unroll = "enable"}翻译成#llvm.loop_annotation<unroll = <disable = false>>,最终成为!llvm.loop.unroll.enablemetadata(LLVM ForceEnable 语义:full/partial 由 cost model 选择,预算否决权被解除)。已有llvm.loop_annotation时合并而非覆盖(已有 unroll 条目被替换时 warning)。完整 SCF→CF 转换:LLVM 19 的
convert-scf-to-cf不把llvm.loop_annotation传到 latch,因此本 pass 接管该函数的整个 SCF→CF 转换——运行上游 conversion patterns,外加一个更高 benefit 的 pattern 处理带注解 loop(镜像上游ForLowering),注解以 ODS 裸名loop_annotation挂到 latchcf.br(BrOp::getLoopAnnotationAttr()按裸名查找)。为什么必须整体转换(review 意见闭环):只降级带注解的 loop 会把新生成的 condition/body/latch/exit blocks 留在外层 single-block region 内(无 hint 的外层
scf.for、scf.if等),在 stock 转换运行前就触发该 op 的 SingleBlock verifier 失败。因此本 pass 替代两条 emitter pipeline 中的createConvertSCFToCFPass,而不是排在它前面。pto-unroll-loops:enable校验为合法并原样放行(不消费);disable及其他未知值仍为硬错误。两者职责互斥:每个 loop 的 attr 只被一个 pass 消费,[Feature] 支持Loop Unroll Hint #1000 的重复展开担忧在构造上不存在。其 fixpoint 现在会传播UnrollOutcome::Error(此前硬错误 loop 被当作 unchanged,emitError后 pass 仍返回成功)。前端:
normalize_unroll_hint恢复"enable"为合法值(仅"disable"仍不支持);pto.for_/pto.range/tile-template 路径自动获得支持。测试与验证
lower_loop_hints.pto覆盖 enable 翻译、iter_args 穿线、嵌套 enable、annotation 合并/覆盖 warning、空 body 降级、仅内层带注解(上述 single-block 隐患的回归用例)、scf.if内带注解 loop、scf.while内带注解 loop、无 hint 函数仍被完整转换,以及ptoas --emit-vpto-llvm-ir端到端验证!llvm.loop.unroll.enable到达最终 LLVM IR;unroll_loops.pto锁定 Pass A 对 enable 的透传行为;unroll_loops_invalid.pto保留其余硬错误。test/dsl-st/unroll_hint_numeric.py恢复 enable 数值用例(out[3]=28):带 enable 的 loop 在设备上正常执行、数值正确。test_loop_unroll_hints、test_ptoas_frontend_verify全过;dsl-st 模拟器全套 PASS(含 enable 数值用例);合规检查 errors=0。unroll_hint_numeric的 enable 数值用例)。A5 硬件验证以模拟器覆盖为准(验证期间 238 设备处于 Alarm 被占状态)。提交列表
c427632b7feat(pto/ptodsl): restore unroll="enable" metadata hint (issue [Feature][PTODSL][SIMTVF] 增加 per-element vector constructor 与 loop-unroll hint #1242 Req 2)