Skip to content

feat(language): decouple pl.spmd inline body from TaskId capture#1947

Merged
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
Hzfengsy:feat/spmd-inline-with-form
Jul 6, 2026
Merged

feat(language): decouple pl.spmd inline body from TaskId capture#1947
lyfne123 merged 3 commits into
hw-native-sys:mainfrom
Hzfengsy:feat/spmd-inline-with-form

Conversation

@Hzfengsy

@Hzfengsy Hzfengsy commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

Previously, the plain with pl.spmd(n): form required a single kernel call — the only way to write an inline multi-statement SPMD body in a with-style scope was with pl.spmd(n) as tid:, which forced you to also capture a producer TaskId even when you had no use for it. This coupled two orthogonal concerns.

This PR decouples them:

  • Inline body, no as tid required. with pl.spmd(n): now accepts an inline multi-statement body, auto-outlined into a synthetic InCore kernel — exactly like for i in pl.spmd(n): and the as tid form. The dispatch stays a plain Call (no task_id_var, so it never lowers to an ir.Submit).
  • as tid is now purely additive. It shares the same body shapes and only adds the task_id_var attr that lowers the dispatch to an ir.Submit.
  • Shared helper. The body-dispatch tail (single-call direct-dispatch vs InCore-wrap for outlining) is factored out of _parse_spmd_scope and _parse_spmd_scope_with_tid into a new _emit_spmd_body helper; the two forms now differ only in scope_attrs. Existing IR shapes are byte-identical (scope_attrs or None collapses to the prior values on both paths).

New limitation: inline bodies must read the per-block index

An auto-outlined inline body must reference pl.tile.get_block_idx() — without it every block runs identical work, which is almost always a bug. Enforced in the shared helper via _spmd_body_reads_block_idx, so both with-forms reject it consistently. The single-call direct-dispatch shape is exempt (its callee reads the index internally); the for form is unaffected (it auto-binds the index).

Example

# Now valid — inline body, no TaskId captured
with pl.spmd(4):
    i = pl.tile.get_block_idx()
    offset = i * 128
    t = pl.load(a, [offset, 0], [128, 128])
    out = pl.store(pl.add(t, t), [offset, 0], out)

Testing

  • New parser tests (TestSpmdInlineWithForm): no-tid inline path wraps InCore with no task_id_var, no task_invalid placeholder, split wrapping, print→parse round-trip, and missing-block-index rejection on both the plain and as tid forms.
  • New JIT end-to-end test (test_jit_spmd_inline_with_form_no_tid): the inline plain form compiles through the full pipeline and the dispatch stays a Call (never a Submit).
  • Verified end-to-end via the default pipeline for both standalone and pl.cluster()-nested inline forms (the latter works because OutlineIncoreScopes at pass 08 outlines the inline body before OutlineClusterScopes at pass 09).
  • Full regression sweep: parser + jit + outlining/cluster/submit/serialization/simplify/derive-directions/codegen suites — all green (971 passed in the focused run; no regressions).
  • Code review completed; pre-commit (pyright / ruff / markdownlint) clean.

Docs

  • spmd() docstring (forms 1 & 3 + inline example) and the EN + zh-CN docs/.../language/00-python_syntax.md spmd sections updated.

The plain `with pl.spmd(n):` form previously required a single kernel
call. Decouple inline-body support from TaskId capture: the plain form
now also accepts an inline multi-statement body, auto-outlined into a
synthetic InCore kernel exactly like the for-form and the `as tid` form,
without requiring `as tid`. TaskId capture (`as tid`) is now purely
additive — it only adds the task_id_var attr that lowers the dispatch to
an ir.Submit.

Factor the shared body-dispatch tail (single-call direct-dispatch vs
InCore-wrap for outlining) out of `_parse_spmd_scope` and
`_parse_spmd_scope_with_tid` into a new `_emit_spmd_body` helper; both
forms now differ only in scope_attrs.

Add a block-index requirement for inline bodies: an auto-outlined body
must read the per-block index via `pl.tile.get_block_idx()`, otherwise
every block runs identical work. Enforced in the shared helper via
`_spmd_body_reads_block_idx`, so both with-forms reject it consistently;
the single-call direct-dispatch shape is exempt (its callee reads the
index internally).

Update the spmd() docstring, the EN/zh-CN python-syntax docs, and add
parser + JIT end-to-end tests covering the no-tid inline path and the
missing-block-index rejection on both with-forms.
Copilot AI review requested due to automatic review settings July 4, 2026 08:00
@coderabbitai

coderabbitai Bot commented Jul 4, 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: b545d61d-9e5d-42c8-9f9b-d32eb88e24f8

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

Refactors pl.spmd with-form parsing to share a single body-emission helper (_emit_spmd_body) across both no-as tid and as tid forms, requiring inline multi-statement bodies to read pl.tile.get_block_idx(). Updates documentation, docstrings, and adds corresponding unit tests.

Changes

pl.spmd inline body support

Layer / File(s) Summary
Shared inline-body emission helper and wiring
python/pypto/language/parser/ast_parser.py
Adds _spmd_body_reads_block_idx and _emit_spmd_body helpers, enforcing that inline multi-statement bodies read the per-block index; rewires _parse_spmd_scope and _parse_spmd_scope_with_tid to delegate body emission to this shared helper.
Documentation and docstring updates
docs/en/dev/language/00-python_syntax.md, docs/zh-cn/dev/language/00-python_syntax.md, python/pypto/language/dsl_api.py
Updates language spec docs and pl.spmd docstring/examples to describe the two allowed inline body shapes and TaskId capture semantics.
Regression tests for inline SPMD forms
tests/ut/jit/test_spmd.py, tests/ut/language/parser/test_scope_parsing.py
Adds tests validating inline body outlining, absence of task_id_var/manual_dep_edges and placeholder assigns, split propagation, print/reparse round-trip, and rejection when get_block_idx() is missing.

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

Sequence Diagram(s)

sequenceDiagram
  participant User as Developer code
  participant Parser as ASTParser
  participant Helper as _emit_spmd_body
  participant IR as IR builder

  User->>Parser: with pl.spmd(N): ... (inline body)
  Parser->>Helper: emit body(single-call or inline)
  Helper->>Helper: check get_block_idx() usage
  alt inline body missing get_block_idx()
    Helper-->>Parser: raise ParserSyntaxError
  else valid body
    Helper->>IR: build InCoreScopeStmt or direct call
    IR-->>Parser: SpmdScopeStmt
  end
  Parser-->>User: parsed program
Loading

Possibly related PRs

  • hw-native-sys/pypto#1667: Both PRs modify the same SPMD with pl.spmd(...) parsing path in ast_parser.py, overlapping on as tid/deps= capture semantics.
  • hw-native-sys/pypto#1822: Both PRs modify pl.spmd parsing for with/with ... as tid forms in the same file, touching SpmdScopeStmt/ir.Submit attribute handling.

Poem

A block asks "where am I?" with a hop and a stare,
get_block_idx() must answer, or the parser won't care.
One helper now guides both tid and plain forms,
Docs freshly trimmed, tests weathering storms.
🐇 Thump-thump — the spmd code compiles clean and warm.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: allowing inline pl.spmd bodies without TaskId capture.
Description check ✅ Passed The description clearly matches the changeset and explains the new inline pl.spmd behavior, limitation, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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 decouples inline-body support from TaskId capture in pl.spmd(N). It allows the plain with pl.spmd(N): form (without as tid) to accept an inline multi-statement body that is auto-outlined into a synthetic InCore kernel, while ensuring that any inline body explicitly reads the per-block index via pl.tile.get_block_idx(). The parser has been refactored to share the body-emitting logic, and corresponding documentation, docstrings, and unit tests have been added or updated. As there are no review comments provided, I have no additional feedback to offer.

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.

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

This PR updates the PyPTO language parser and documentation so the plain with pl.spmd(n): form can host an inline multi-statement SPMD body without forcing as tid TaskId capture, making TaskId capture purely additive.

Changes:

  • Factor shared SPMD with-scope body emission into _emit_spmd_body, enabling inline bodies for the no-as tid form while preserving the historical single-call direct-dispatch shape.
  • Enforce a new parser rule for auto-outlined inline bodies: they must read pl.tile.get_block_idx() (applies to both with-forms), and add corresponding parser + JIT end-to-end tests.
  • Update spmd() docstring and EN/zh-CN language syntax docs to reflect the decoupled forms and the block-index requirement.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/ut/language/parser/test_scope_parsing.py Adds parser-level regression tests for the new no-as tid inline with-form and shared block-index enforcement.
tests/ut/jit/test_spmd.py Adds an end-to-end JIT test ensuring the new inline no-as tid with-form compiles and does not lower to Submit.
python/pypto/language/parser/ast_parser.py Implements shared _emit_spmd_body and block-index detection/enforcement for inline with-form SPMD bodies.
python/pypto/language/dsl_api.py Updates pl.spmd() docstring to document the new inline plain with-form and the block-index requirement.
docs/en/dev/language/00-python_syntax.md Updates the English language syntax docs for the new SPMD forms and limitation.
docs/zh-cn/dev/language/00-python_syntax.md Updates the Chinese language syntax docs in sync with the English changes.

Comment thread python/pypto/language/parser/ast_parser.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 403da27a12

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread python/pypto/language/parser/ast_parser.py

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@python/pypto/language/parser/ast_parser.py`:
- Around line 3615-3661: The single-call SPMD fast path in _emit_spmd_body is
too broad because it treats any one-line Assign/AnnAssign/Expr call as kernel
dispatch, letting non-kernel calls bypass the block-index check and InCore
wrapping. Narrow is_single_call so it only matches self.<method>(...)
dispatches, and keep all other single-call bodies subject to the existing
_spmd_body_reads_block_idx guard and outlining behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3b09030a-8b52-408d-81bf-0b2bf3a8700d

📥 Commits

Reviewing files that changed from the base of the PR and between 726f678 and 403da27.

📒 Files selected for processing (6)
  • docs/en/dev/language/00-python_syntax.md
  • docs/zh-cn/dev/language/00-python_syntax.md
  • python/pypto/language/dsl_api.py
  • python/pypto/language/parser/ast_parser.py
  • tests/ut/jit/test_spmd.py
  • tests/ut/language/parser/test_scope_parsing.py

Comment thread python/pypto/language/parser/ast_parser.py
Hzfengsy added 2 commits July 4, 2026 16:21
- Tighten the inline-SPMD block-index guard to match only
  `<tile>.get_block_idx()` (pl.tile.get_block_idx / tile.get_block_idx),
  not an unrelated `foo.get_block_idx()` — aligns the check with its own
  error message and docs (Copilot review).
- Add a direct unit test for the AST matcher pinning the accepted
  spellings and the rejected unrelated-receiver case.
…ive-sys#1947

The previous receiver-restricted match (only `<tile>.get_block_idx()`)
regressed a real model (pypto-lib qwen3 14B decode), which reads the
per-block index via the top-level `pl.get_block_idx()` alias — a valid
API exported at pl top-level, equivalent to `pl.tile.get_block_idx()`.

Match `get_block_idx` by trailing call name across every valid spelling
regardless of receiver: `pl.get_block_idx()`, `pl.tile.get_block_idx()`,
`tile.get_block_idx()`, and a bare imported `get_block_idx()`. Matching
by name is deliberately lenient — the name is unique to this API, and
leniency is far safer than rejecting a real block-distinguishing body.

Update the AST-matcher unit test to cover all spellings (including the
pl.get_block_idx() regression case) and add an end-to-end parser test
mirroring the qwen3 inline-spmd body.
@lyfne123 lyfne123 merged commit 9f8ab9a into hw-native-sys:main Jul 6, 2026
20 of 21 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.

3 participants