fix(ir): add UnrollResolved verifier and elevate codegen fallbacks to hard checks#1700
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR adds a new ChangesUnrollResolved Property and Verification
🎯 3 (Moderate) | ⏱️ ~18 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an UnrollResolved IR property and enforces that ForKind::Unroll does not survive past UnrollLoops, with verifier + stricter codegen checks.
Changes:
- Introduces
IRProperty::UnrollResolved(C++ + Python) and registers a new property verifier. - Marks
UnrollLoopsas producingUnrollResolvedand documents the property in the pass docs. - Replaces codegen “warn + fallback” behavior with internal checks when
Unroll/Pipelineloops reach codegen.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ir/verifier/verify_unroll_resolved.cpp | New verifier to detect leftover ForKind::Unroll loops post-unroll. |
| src/ir/verifier/property_verifier_registry.cpp | Registers the new UnrollResolved verifier. |
| src/ir/transforms/ir_property.cpp | Adds stringification for IRProperty::UnrollResolved. |
| src/codegen/pto/pto_control_flow_codegen.cpp | Hard-fails if Unroll/Pipeline loops reach PTO codegen. |
| src/codegen/orchestration/orchestration_codegen.cpp | Hard-fails if Unroll/Pipeline loops reach orchestration codegen. |
| python/pypto/pypto_core/passes.pyi | Exposes UnrollResolved in Python type stubs. |
| python/bindings/modules/passes.cpp | Exposes UnrollResolved in Python bindings with docstring. |
| include/pypto/ir/verifier/verifier.h | Declares factory for UnrollResolved verifier. |
| include/pypto/ir/transforms/pass_properties.h | Declares UnrollLoops produces UnrollResolved. |
| include/pypto/ir/transforms/ir_property.h | Adds UnrollResolved to the C++ enum. |
| docs/en/dev/passes/02-unroll_loops.md | Documents UnrollLoops producing UnrollResolved. |
| CMakeLists.txt | Adds the new verifier source file to the build. |
There was a problem hiding this comment.
Code Review
This pull request introduces the UnrollResolved IR property and its corresponding verifier to ensure that no ForKind::Unroll loops survive past the UnrollLoops pass. It also upgrades fallback warnings to hard assertions in the codegen layers if unexpanded unroll or pipeline loops are encountered. Feedback on the changes highlights an issue in the new verifier: it flags all surviving ForKind::Unroll loops, which will incorrectly cause verification failures for valid chunked unroll loops that are meant to be skipped by UnrollLoops and handled later by SplitChunkedLoops. A suggestion is provided to ignore loops with a chunk_config value.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| : diagnostics_(diagnostics), func_name_(func_name) {} | ||
|
|
||
| void VisitStmt_(const ForStmtPtr& op) override { | ||
| if (op->kind_ == ForKind::Unroll) { |
There was a problem hiding this comment.
The UnrollLoops pass explicitly skips chunked unroll loops (loops with ForKind::Unroll where chunk_config_.has_value() is true) so they can be handled later by SplitChunkedLoops. However, this verifier flags any surviving ForKind::Unroll loop, which will cause verification to fail for valid chunked unroll loops downstream of UnrollLoops.
Please update the condition to ignore loops that have a chunk_config value.
| if (op->kind_ == ForKind::Unroll) { | |
| if (op->kind_ == ForKind::Unroll && !op->chunk_config_.has_value()) { |
There was a problem hiding this comment.
Fixed, and outdated now
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/en/dev/passes/02-unroll_loops.md (1)
88-93:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUpdate the properties table to match the actual pass declaration.
The table states that UnrollLoops requires and produces
TypeChecked, but the actualkUnrollLoopsPropertiesdeclaration inpass_properties.hline 49 only producesUnrollResolved(required and invalidated are empty). Update the table to reflect the actual pass contract:
Property Value Required (none) Produced UnrollResolvedInvalidated (none) Note: The overview text on line 9 also mentions "Requires: TypeChecked property," which contradicts the code. Per the codebase pattern,
TypeCheckedis a structural property verified once at pipeline start, so omitting it from per-passrequiredsets is intentional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/en/dev/passes/02-unroll_loops.md` around lines 88 - 93, The docs table and overview are out of sync with the actual pass properties: update the properties table in docs/en/dev/passes/02-unroll_loops.md to match kUnrollLoopsProperties in pass_properties.h (set Required to (none), Produced to `UnrollResolved`, Invalidated to (none)) and remove or correct the "Requires: TypeChecked" mention in the overview (line ~9) so it does not list TypeChecked as a per-pass required property; reference UnrollLoops and kUnrollLoopsProperties when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@docs/en/dev/passes/02-unroll_loops.md`:
- Around line 88-93: The docs table and overview are out of sync with the actual
pass properties: update the properties table in
docs/en/dev/passes/02-unroll_loops.md to match kUnrollLoopsProperties in
pass_properties.h (set Required to (none), Produced to `UnrollResolved`,
Invalidated to (none)) and remove or correct the "Requires: TypeChecked" mention
in the overview (line ~9) so it does not list TypeChecked as a per-pass required
property; reference UnrollLoops and kUnrollLoopsProperties when making the
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1b80af9a-89f4-45d2-aa07-cfa6843a3a56
📒 Files selected for processing (12)
CMakeLists.txtdocs/en/dev/passes/02-unroll_loops.mdinclude/pypto/ir/transforms/ir_property.hinclude/pypto/ir/transforms/pass_properties.hinclude/pypto/ir/verifier/verifier.hpython/bindings/modules/passes.cpppython/pypto/pypto_core/passes.pyisrc/codegen/orchestration/orchestration_codegen.cppsrc/codegen/pto/pto_control_flow_codegen.cppsrc/ir/transforms/ir_property.cppsrc/ir/verifier/property_verifier_registry.cppsrc/ir/verifier/verify_unroll_resolved.cpp
db1737d to
aa3ed71
Compare
| // -- Loop unrolling pass (runs before SSA) ------------------------------------ | ||
|
|
||
| inline const PassProperties kUnrollLoopsProperties{}; | ||
| inline const PassProperties kUnrollLoopsProperties{.produced = {IRProperty::UnrollResolved}}; |
There was a problem hiding this comment.
Unroll pass will skip the unroll loop with chunk param. So this verifier should be activated after the SplitChunkedLoops pass. And the SplitChunkedLoops pass should lower the skipped unroll to sequential or unroll it. See 02-unroll_loops.md and 07-split_chunked_loops.md.
3705730 to
aa3ed71
Compare
Replace silent LOG_WARN fallbacks for ForKind::Unroll and ForKind::Pipeline in both orchestration and PTO codegen with hard INTERNAL_CHECK_SPAN failures. These ForKind values are transient markers that the pass pipeline must resolve before code emission (UnrollLoops pass 2, LowerPipelineLoops pass 25, CanonicalizeIOOrder pass 26). Falling back to Sequential silently produces semantically degraded or wrong output — missing body replication, broken ping-pong buffer semantics, and unsynchronized cross-core operations. The orchestration codegen already used INTERNAL_CHECK_SPAN; this change closes the PTO codegen gap that previously only warned.
Introduce UnrollResolved IR property produced by the UnrollLoops pass, with a corresponding PropertyVerifier that reports an error if any ForKind::Unroll survives past UnrollLoops (except for chunked unroll loops, which are valid until SplitChunkedLoops). Elevate the soft LOG_WARN fallbacks in codegen to INTERNAL_CHECK_SPAN. The old warnings printed context-free messages that were easy to miss and blamed unimplemented features. Now any ForKind::Unroll or Pipeline that escapes the pipeline and reaches codegen causes an immediate failure with: - A clear "pipeline is incomplete" message - Span information pointing to the offending IR - Guidance on which pass was expected to handle it Affected codegens: orchestration_codegen.cpp, pto_control_flow_codegen.cpp.
5ce3a73 to
d8f28fc
Compare
…nroll markers Move UnrollResolved property from kUnrollLoopsProperties (pass 2) to kSplitChunkedLoopsProperties (pass 7). SplitChunkedLoops now demotes ForKind::Unroll → Sequential on all generated ForStmts via a new DemoteUnrollKind() helper, so the verifier no longer needs a chunk_config carve-out — zero ForKind::Unroll survive past pass 7. Changes: - pass_properties.h: move UnrollResolved to kSplitChunkedLoopsProperties - split_chunked_loops_pass.cpp: add DemoteUnrollKind(), apply at all 10 ForStmt sites across 4 split methods - verify_unroll_resolved.cpp: remove chunk_config carve-out, update class comment to reference SplitChunkedLoops - test_split_chunked_loops.py: test_unroll_chunk expects Sequential - Docs: 02-unroll_loops.md removes Produces, 07-split_chunked_loops.md (EN + ZH-CN) adds Produces: UnrollResolved
d8f28fc to
339d334
Compare
Summary
Replace silent
LOG_WARNfallbacks in both codegen backends with hardINTERNAL_CHECK_SPANassertions, and add anUnrollResolvedproperty verifier so the pass pipeline catches the root cause — staleForKind::Unrollmarkers — before they reach codegen.
The key insight:
ForKind::Unrollis a compile-time-only marker. The only pass that expands it intoSeqStmtsisUnrollLoops(pass 2). After pass 2, no downstream pass ever expandsForKind::Unroll— chunked Unroll loops that survived carried a stale marker. UnrollLoops (pass 2) expands non-chunked Unroll loops into SeqStmts; SplitChunkedLoops (pass 7) demotes chunked Unroll → Sequential. Together they form the complete resolution chain. Before this fix, they reached codegen where the oldLOG_WARNfallback generated a sequentialforloop — the exact same C++ asForKind::Sequential. The IR kind was a lie; runtime behaviorwas always Sequential. This fix makes the IR honestly reflect what the generated code actually does. Zero runtime change.
Changes
1. SplitChunkedLoops now demotes
ForKind::Unroll→SequentialSplitChunkedLoops(pass 7) decomposes chunked loops into nested ForStmts. Previously it copiedForKind::Unrollonto the generated inner/outer loops, leaving stale markers that no downstream pass would ever expand. Now itdemotes
ForKind::UnrolltoSequentialvia aDemoteUnrollKind()helper applied at all 10 ForStmt construction sites across all 4 split methods (LeadingFull, LeadingFullWithIterArgs, Guarded, GuardedWithIterArgs).2.
UnrollResolvedproperty onkSplitChunkedLoopsPropertiesA new
UnrollResolvedPropertyVerifierfires if anyForKind::Unrollsurvives pastSplitChunkedLoops. The property is declared onkSplitChunkedLoopsProperties.produced(notkUnrollLoopsProperties),because chunked Unroll loops are resolved by
SplitChunkedLoops, notUnrollLoops.3. Codegen:
LOG_WARN→INTERNAL_CHECK_SPANBoth
orchestration_codegen.cppandpto_control_flow_codegen.cppnow hard-fail with an actionable message (naming the responsible passes) whenForKind::UnrollorForKind::Pipelinereach codegen.Files changed
split_chunked_loops_pass.cpp—DemoteUnrollKind()helper, 10 call sitesverify_unroll_resolved.cpp(new),ir_property.h/.cpp,pass_properties.h,verifier.h,property_verifier_registry.cpp,CMakeLists.txtorchestration_codegen.cpp,pto_control_flow_codegen.cpppasses.cpp(nanobind),passes.pyi(type stub)07-split_chunked_loops.md(EN + ZH-CN)test_split_chunked_loops.py—test_unroll_chunkexpectsSequentialSplitChunkedLoopsdemotesForKind::Unroll→Sequentialand declares
UnrollResolvedas produced; verifier catches any stragglers.Unroll/Pipelineloops survive as afinal safety net with actionable error messages.