Skip to content

fix(codegen): dispatch dual_aiv_dispatch AIV kernels on both vector lanes#2012

Open
luohuan19 wants to merge 3 commits into
hw-native-sys:mainfrom
luohuan19:issue-2006-split-aiv-none-drops-lane
Open

fix(codegen): dispatch dual_aiv_dispatch AIV kernels on both vector lanes#2012
luohuan19 wants to merge 3 commits into
hw-native-sys:mainfrom
luohuan19:issue-2006-split-aiv-none-drops-lane

Conversation

@luohuan19

Copy link
Copy Markdown
Contributor

Summary

A pl.split_aiv region is task-parallel: both AIV lanes run the body on disjoint work selected by aiv_id. SplitVectorKernel stamps such an AIV kernel dual_aiv_dispatch, but only the mixed-kernel Group submit honoured it. The three single-kernel dispatch paths — direct call, Spmd wrapper, and AIV-only Group — all built their submit through CoreTypeToSubmitPrefix(), which unconditionally yields rt_submit_aiv_task for VECTOR.

rt_submit_aiv_task fills only the AIV0 slot of MixedKernels, so the runtime schedules a single-AIV task: one AIV core per block. The second lane never launches, and the lone lane that does reads a get_sub_block_id() the runtime leaves undefined for single-AIV tasks, so its aiv_id is really its physical cluster position. Half the work is silently dropped — the destination rows come back as exact zeros, with no error, no NaN, and a block-parity pattern for which lane survives. A pure-vector kernel (no cube work) hits this on the Spmd-wrapper path.

Changes

  1. fix(language): make split_aiv IR survive print → parse unchanged. Two parser-side asymmetries made python_print → parse lossy for a function carrying a pl.split_aiv region, failing the roundtrip verifier on every pass over such IR:

    • An explicit attrs={"split": pl.SplitMode.NONE} was dropped on parse while the printer emits it. Store every mode instead — behaviour-preserving, since Function::GetSplitMode() maps split == 0 to nullopt and is the attr's sole reader.
    • A pl.split_aiv loop directly in an InCore function body was wrapped in a synthesized InCoreScopeStmt. An InCore function is already a core function — nothing is left to outline — and it is the shape post-outline IR prints as. Emit the region in place there too.
  2. fix(codegen): dispatch dual_aiv_dispatch AIV kernels on both vector lanes. Route the three single-kernel paths through a shared BuildSingleKernelSubmitExpr(), which emits MixedKernels{INVALID_KERNEL_ID, id, id} + rt_submit_task for a dual_aiv_dispatch VECTOR kernel. Two active AIV slots make it a MIX-shape task, so the scheduler places both lanes of one cluster under the same block_idx and gives them sub_block_id 0 and 1; the cluster's AIC core is unused. No runtime change is needed.

A plain vector kernel carries no dual_aiv_dispatch and keeps rt_submit_aiv_task, so it retains its full independent-AIV parallelism. Mixed kernels are untouched.

Testing

  • New roundtrip tests (tests/ut/ir/printing/test_split_aiv_roundtrip.py) guard both parser arms.
  • New orchestration codegen UT (tests/ut/codegen/test_orchestration_codegen_more.py) covers the dual_aiv_dispatch submit shape.
  • The existing NONE-region codegen ST compiled this exact shape but only asserted a .pto was emitted — which is why the defect shipped. It now also asserts the submit launches both lanes.
  • Documentation updated (docs/en/dev/codegen/01-orchestration_codegen.md + zh-cn).

Related Issues

Fixes #2006

Two parser-side asymmetries made python_print -> parse lossy for a function
carrying a pl.split_aiv region, so every pass over such IR failed the roundtrip
verifier (only ever noticed now, because the split_aiv pass tests run under an
empty PassContext with no instruments).

1. An explicit `attrs={"split": pl.SplitMode.NONE}` was dropped on parse while
   the printer emits it. This hit exactly the functions a task-parallel
   `pl.split_aiv(..., mode=NONE)` region produces: OutlineIncoreScopes derives a
   representative function-level `split` mode of NONE from a single NONE region.
   Store every mode instead. This is behaviour-preserving — Function::GetSplitMode()
   maps `split == 0` to nullopt and is the sole reader of the attr, so a stored
   NONE and an absent `split` are indistinguishable to every consumer.

2. A `pl.split_aiv` loop directly in an `@pl.function(type=pl.FunctionType.InCore)`
   body was wrapped in a synthesized InCoreScopeStmt. That wrapper exists to keep a
   bare top-level region eligible for OutlineIncoreScopes, but an InCore function is
   already a core function — there is nothing left to outline. It is also the shape
   post-outline IR prints as (outlining consumes the InCoreScopeStmt and leaves the
   region directly in an InCore body), so the reparsed function grew a scope the
   original did not have. Emit the region in place there too.

Both arms are guarded by new roundtrip tests.
…anes

Fixes hw-native-sys#2006

A `pl.split_aiv` region is task-parallel: both AIV lanes run the body on disjoint
work selected by `aiv_id`. SplitVectorKernel stamps such an AIV kernel
`dual_aiv_dispatch`, but only the mixed-kernel Group submit honoured it. The three
single-kernel dispatch paths — direct call, Spmd wrapper, and AIV-only Group — all
built their submit through CoreTypeToSubmitPrefix(), which unconditionally yields
`rt_submit_aiv_task` for VECTOR.

`rt_submit_aiv_task` fills only the AIV0 slot of MixedKernels, so the runtime
schedules a single-AIV task: one AIV core per block. The second lane never launches,
and the lone lane that does reads a `get_sub_block_id()` the runtime leaves undefined
for single-AIV tasks (runtime common/intrinsic.h), so its `aiv_id` is really its
physical cluster position. Half the work is silently dropped — the destination rows
come back as exact zeros, with no error, no NaN, and a block-parity pattern for which
lane survives. A pure-vector kernel (no cube work) hits this on the Spmd-wrapper path.

Route the three single-kernel paths through a shared BuildSingleKernelSubmitExpr(),
which emits `MixedKernels{INVALID_KERNEL_ID, id, id}` + rt_submit_task for a
`dual_aiv_dispatch` VECTOR kernel. Two active AIV slots make it a MIX-shape task, so
the scheduler places both lanes of one cluster under the same block_idx and gives them
sub_block_id 0 and 1; the cluster's AIC core is unused. No runtime change is needed.

A plain vector kernel carries no `dual_aiv_dispatch` and keeps `rt_submit_aiv_task`,
so it retains its full independent-AIV parallelism; mixed kernels are untouched.

The existing NONE-region codegen ST compiled this exact shape but only asserted a
.pto was emitted, which is why the defect shipped; it now also asserts the submit
launches both lanes.
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 49643d76-1411-4d7a-848a-b94332f49338

📥 Commits

Reviewing files that changed from the base of the PR and between 8ebddcb and e7fd829.

📒 Files selected for processing (8)
  • docs/en/dev/codegen/01-orchestration_codegen.md
  • docs/zh-cn/dev/codegen/01-orchestration_codegen.md
  • python/pypto/language/parser/ast_parser.py
  • python/pypto/language/parser/decorator.py
  • src/codegen/orchestration/orchestration_codegen.cpp
  • tests/st/codegen/dsl/test_split_aiv_none_codegen.py
  • tests/ut/codegen/test_orchestration_codegen_more.py
  • tests/ut/ir/printing/test_split_aiv_roundtrip.py

📝 Walkthrough

Walkthrough

The change preserves SplitMode.NONE during parsing and printing, keeps split_aiv regions inline in core-function bodies, and generates two-lane MixedKernels submissions for dual_aiv_dispatch kernels. Documentation and regression tests cover the updated behavior.

Changes

Dual-AIV Split-NONE dispatch

Layer / File(s) Summary
Parser and round-trip preservation
python/pypto/language/parser/{ast_parser.py,decorator.py}, tests/ut/ir/printing/test_split_aiv_roundtrip.py
Core-function parsing avoids synthesized InCore wrappers, while SplitMode.NONE survives decorator parsing and print-to-parse round trips.
Dual-AIV submit generation
src/codegen/orchestration/orchestration_codegen.cpp, docs/*/dev/codegen/01-orchestration_codegen.md
Submit construction is centralized and selects MixedKernels with repeated AIV kernel IDs for dual_aiv_dispatch kernels instead of single-AIV submission.
Dispatch regression coverage
tests/st/codegen/dsl/test_split_aiv_none_codegen.py, tests/ut/codegen/test_orchestration_codegen_more.py
Tests verify both AIV lanes are scheduled and that generated code omits rt_submit_aiv_task for dual-AIV kernels.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SplitAivParser
  participant OrchestrationCodegen
  participant MixedKernels
  participant Runtime
  SplitAivParser->>OrchestrationCodegen: preserve SplitMode.NONE and dual-AIV function shape
  OrchestrationCodegen->>MixedKernels: configure AIC inactive and both AIV slots
  OrchestrationCodegen->>Runtime: submit rt_submit_task(mixed_...)
  Runtime->>MixedKernels: execute both AIV lanes
Loading

Possibly related PRs

Poem

I’m a rabbit hopping lane to lane,
Two AIV paths now run in sync again.
NONE stays visible, wrappers stay away,
Mixed kernels guide the work each day.
No dropped output—hip hip hooray! 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses dual-AIV dispatch, but it does not show the required standalone 128-item regression proving outputs match the spmd(32) control. Add the standalone reproduction test that compares all 128 outputs against pl.spmd(32), or reject unsupported multi-lane writebacks with a clear parse-time error.
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: dual-AIV dispatch now runs on both vector lanes.
Description check ✅ Passed The description matches the parser, codegen, docs, and test updates in this pull request.
Out of Scope Changes check ✅ Passed The changes are limited to parser, orchestration codegen, tests, and docs needed for the split_aiv/dual-AIV fix.

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.

@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 addresses an issue where dual-AIV vector kernels (e.g., split_aiv kernels) were incorrectly scheduled as single-AIV tasks, causing one of the lanes to never launch and silently dropping half the work. It fixes this by dispatching dual-AIV kernels as two-lane MixedKernels instead of using rt_submit_aiv_task. Additionally, it preserves SplitMode.NONE attributes during parsing to prevent lossy roundtrips, and ensures that split_aiv regions directly inside InCore function bodies do not synthesize spurious wrapper scopes. Feedback from the reviewer points out that checking only ir.FunctionType.InCore in ast_parser.py is too restrictive, as AIC and AIV are also core functions that do not require outlining, and suggests expanding the check to include them.

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.

Comment thread python/pypto/language/parser/ast_parser.py Outdated
- Widen the parser's core-function guard from InCore to the full IsInCoreType()
  set (InCore, AIC, AIV). An @pl.function(type=pl.FunctionType.AIV) body carrying
  a pl.split_aiv region is already a core function, but the InCore-only check sent
  it down the bare-top-level arm and synthesized a spurious InCoreScopeStmt — a
  scope the original does not have, which OutlineIncoreScopes would then try to
  outline out of an already-specialized kernel. Extracted the set as
  _CORE_FUNCTION_TYPES, mirroring the C++ IsInCoreType(). Guarded by a new
  roundtrip test. Reported by gemini-code-assist.

- Fix two pyright reportOptionalMemberAccess errors in the roundtrip tests
  (Program.get_function() returns Function | None) — this was failing pre-commit
  in CI.

- Correct the pass attribution in test_roundtrip_split_none_attr_survives:
  the function-level split attr is stamped by LowerAutoVectorSplit (the only
  stamper that stores NONE — SplitVectorKernel explicitly skips it), not by
  OutlineIncoreScopes, which never touches the attr.

- Condense the new orchestration-codegen doc section (EN + zh-CN): the prose,
  code block and table stated the same fact three times, and the EN file had
  crossed the 700-line split threshold (701 -> 693). Cross-reference the
  MixedKernels table instead of repeating it.

- Scope the ST's rt_submit_aiv_task negative assertion to the dual-AIV kernel's
  own id, so it stays correct if the fixture ever gains a plain vector kernel
  (which legitimately keeps rt_submit_aiv_task).
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.

[Bug] pl.split_aiv(2, SplitMode.NONE) silently drops half the work — one AIV lane per block never writes back

1 participant