Skip to content

fix(ir): add UnrollResolved verifier and elevate codegen fallbacks to hard checks#1700

Merged
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
georgebisbas:fix/codegen-fallback-to-check
Jun 15, 2026
Merged

fix(ir): add UnrollResolved verifier and elevate codegen fallbacks to hard checks#1700
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
georgebisbas:fix/codegen-fallback-to-check

Conversation

@georgebisbas

@georgebisbas georgebisbas commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace silent LOG_WARN fallbacks in both codegen backends with hard INTERNAL_CHECK_SPAN assertions, and add an UnrollResolved property verifier so the pass pipeline catches the root cause — stale ForKind::Unroll
markers — before they reach codegen.

The key insight: ForKind::Unroll is a compile-time-only marker. The only pass that expands it into SeqStmts is UnrollLoops (pass 2). After pass 2, no downstream pass ever expands ForKind::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 old LOG_WARN fallback generated a sequential for loop — the exact same C++ as ForKind::Sequential. The IR kind was a lie; runtime behavior
was always Sequential. This fix makes the IR honestly reflect what the generated code actually does. Zero runtime change.

Changes

1. SplitChunkedLoops now demotes ForKind::UnrollSequential

SplitChunkedLoops (pass 7) decomposes chunked loops into nested ForStmts. Previously it copied ForKind::Unroll onto the generated inner/outer loops, leaving stale markers that no downstream pass would ever expand. Now it
demotes ForKind::Unroll to Sequential via a DemoteUnrollKind() helper applied at all 10 ForStmt construction sites across all 4 split methods (LeadingFull, LeadingFullWithIterArgs, Guarded, GuardedWithIterArgs).

2. UnrollResolved property on kSplitChunkedLoopsProperties

A new UnrollResolvedPropertyVerifier fires if any ForKind::Unroll survives past SplitChunkedLoops. The property is declared on kSplitChunkedLoopsProperties.produced (not kUnrollLoopsProperties),
because chunked Unroll loops are resolved by SplitChunkedLoops, not UnrollLoops.

3. Codegen: LOG_WARNINTERNAL_CHECK_SPAN

Both orchestration_codegen.cpp and pto_control_flow_codegen.cpp now hard-fail with an actionable message (naming the responsible passes) when ForKind::Unroll or ForKind::Pipeline reach codegen.

Files changed

Layer Files
Pass logic split_chunked_loops_pass.cppDemoteUnrollKind() helper, 10 call sites
Verifier verify_unroll_resolved.cpp (new), ir_property.h/.cpp, pass_properties.h, verifier.h, property_verifier_registry.cpp, CMakeLists.txt
Codegen orchestration_codegen.cpp, pto_control_flow_codegen.cpp
Bindings passes.cpp (nanobind), passes.pyi (type stub)
Docs 07-split_chunked_loops.md (EN + ZH-CN)
Tests test_split_chunked_loops.pytest_unroll_chunk expects Sequential
  1. Producer side: SplitChunkedLoops demotes ForKind::UnrollSequential
    and declares UnrollResolved as produced; verifier catches any stragglers.
  2. Consumer side: Codegen asserts no Unroll/Pipeline loops survive as a
    final safety net with actionable error messages.

Copilot AI review requested due to automatic review settings June 8, 2026 12:47
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ef513e1-d807-4bb5-aba6-05f75d9bd81d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a new UnrollResolved IR property verifier that enforces the post-UnrollLoops invariant. The implementation includes property definition, visitor-based verification logic, registry integration, Python bindings, and codegen fail-fast assertions replacing prior warning-based fallbacks.

Changes

UnrollResolved Property and Verification

Layer / File(s) Summary
IR Property contract definition
include/pypto/ir/transforms/ir_property.h, src/ir/transforms/ir_property.cpp, include/pypto/ir/transforms/pass_properties.h, docs/en/dev/passes/02-unroll_loops.md
New IRProperty::UnrollResolved enum value is added to the property system, mapped to string representation, declared as produced by UnrollLoops pass, and documented in the pass specification.
Verifier implementation for UnrollResolved
src/ir/verifier/verify_unroll_resolved.cpp
UnrollKindLeftoverChecker visitor detects ForStmt instances with ForKind::Unroll remaining after UnrollLoops, and UnrollResolvedPropertyVerifierImpl iterates function bodies to report error diagnostics with context and remediation guidance.
Verifier registration and system integration
include/pypto/ir/verifier/verifier.h, src/ir/verifier/property_verifier_registry.cpp, CMakeLists.txt
Factory function is declared and registered in the property verifier registry, and the implementation file is added to CMakeLists for compilation into the core library.
Python bindings and type stubs
python/bindings/modules/passes.cpp, python/pypto/pypto_core/passes.pyi
UnrollResolved property is exposed to Python via nanobind C++ bindings and Python stub type definitions.
Codegen fail-fast for incomplete pipelines
src/codegen/orchestration/orchestration_codegen.cpp, src/codegen/pto/pto_control_flow_codegen.cpp
Orchestration and PTO codegens now assert via INTERNAL_CHECK_SPAN that ForKind::Unroll and ForKind::Pipeline loop kinds do not reach codegen, replacing prior LOG_WARN fallback-generation behavior with clear error messages indicating required passes.

🎯 3 (Moderate) | ⏱️ ~18 minutes

🐰 Hops excitedly through the IR forest wide,
Where Unroll loops once hid their resolve,
Now verified, no more they hide—
The pass pipeline's promise we solve,
Fail-fast and true, no fallbacks to confide!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding a verifier for UnrollResolved property and replacing fallback logging with hard checks in codegen.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description accurately describes the changeset: replacing LOG_WARN fallbacks with hard INTERNAL_CHECK_SPAN assertions and adding an UnrollResolved property verifier to catch stale ForKind::Unroll markers.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 UnrollLoops as producing UnrollResolved and documents the property in the pass docs.
  • Replaces codegen “warn + fallback” behavior with internal checks when Unroll/Pipeline loops 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.

Comment thread include/pypto/ir/transforms/pass_properties.h Outdated
Comment thread src/codegen/pto/pto_control_flow_codegen.cpp
Comment thread src/codegen/orchestration/orchestration_codegen.cpp
Comment thread src/ir/verifier/verify_unroll_resolved.cpp

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
if (op->kind_ == ForKind::Unroll) {
if (op->kind_ == ForKind::Unroll && !op->chunk_config_.has_value()) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, and outdated now

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Update the properties table to match the actual pass declaration.

The table states that UnrollLoops requires and produces TypeChecked, but the actual kUnrollLoopsProperties declaration in pass_properties.h line 49 only produces UnrollResolved (required and invalidated are empty). Update the table to reflect the actual pass contract:

Property Value
Required (none)
Produced UnrollResolved
Invalidated (none)

Note: The overview text on line 9 also mentions "Requires: TypeChecked property," which contradicts the code. Per the codebase pattern, TypeChecked is a structural property verified once at pipeline start, so omitting it from per-pass required sets 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

📥 Commits

Reviewing files that changed from the base of the PR and between bda839d and 5258548.

📒 Files selected for processing (12)
  • CMakeLists.txt
  • docs/en/dev/passes/02-unroll_loops.md
  • include/pypto/ir/transforms/ir_property.h
  • include/pypto/ir/transforms/pass_properties.h
  • include/pypto/ir/verifier/verifier.h
  • python/bindings/modules/passes.cpp
  • python/pypto/pypto_core/passes.pyi
  • src/codegen/orchestration/orchestration_codegen.cpp
  • src/codegen/pto/pto_control_flow_codegen.cpp
  • src/ir/transforms/ir_property.cpp
  • src/ir/verifier/property_verifier_registry.cpp
  • src/ir/verifier/verify_unroll_resolved.cpp

@georgebisbas georgebisbas marked this pull request as draft June 8, 2026 13:02
@georgebisbas georgebisbas force-pushed the fix/codegen-fallback-to-check branch 2 times, most recently from db1737d to aa3ed71 Compare June 8, 2026 13:04
@georgebisbas georgebisbas marked this pull request as ready for review June 9, 2026 11:38
// -- Loop unrolling pass (runs before SSA) ------------------------------------

inline const PassProperties kUnrollLoopsProperties{};
inline const PassProperties kUnrollLoopsProperties{.produced = {IRProperty::UnrollResolved}};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@georgebisbas georgebisbas force-pushed the fix/codegen-fallback-to-check branch 3 times, most recently from 3705730 to aa3ed71 Compare June 11, 2026 19:34
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.
@georgebisbas georgebisbas force-pushed the fix/codegen-fallback-to-check branch 3 times, most recently from 5ce3a73 to d8f28fc Compare June 12, 2026 14:03
…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
@georgebisbas georgebisbas force-pushed the fix/codegen-fallback-to-check branch from d8f28fc to 339d334 Compare June 12, 2026 14:09
@lyfne123 lyfne123 merged commit dd5b556 into hw-native-sys:main Jun 15, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants