Skip to content

perf(autotile): Double-buffer L0C in eligible pipelines - #2178

Open
tonibohnlein wants to merge 7 commits into
hw-native-sys:mainfrom
tonibohnlein:issue-2131-autotile-dbc-prototype
Open

perf(autotile): Double-buffer L0C in eligible pipelines#2178
tonibohnlein wants to merge 7 commits into
hw-native-sys:mainfrom
tonibohnlein:issue-2131-autotile-dbc-prototype

Conversation

@tonibohnlein

Copy link
Copy Markdown
Contributor

Summary

  • recognize canonical, already-tiled matmul software pipelines and automatically enable two-slot L0C accumulator ping-pong under the PyPTO memory planner
  • schedule adjacent iterations as matmul, matmul, drain, drain, while preserving the full user-selected pipeline depth for L0A/L0B and rotating only L0C over two slots
  • admit direct-to-GM and Mat-scratch drains with separate device-calibrated profitability gates
  • conservatively account for the complete function Acc footprint before reserving another L0C slot, so the optimization cannot force unrelated pipeline depth shedding or introduce an L0C overflow
  • document the policy in English and Chinese and add structural, profitability, capacity, planner, and rejection coverage

Relationship to #2131 and #2150

Related to #2131 and complementary to #2150.

Issue #2131 asks for a general public mechanism for stable physical buffer identity, runtime slot selection, destination binding, and explicit lifetime release. PR #2150 provides that explicit user-facing capability through tile buffer sets and destination-form operations, including independently selected L1/L0B/L0C rotations.

This PR does not replace or depend on #2150. It addresses the common canonical form automatically: a normal pl.pipeline containing one already-L0 matmul, one stationary operand, one per-iteration Mat-to-L0 operand transfer, and one loop-carried drain. The source program does not need explicit buffers or annotations. For the #2131 reproduction, AutoTile recognizes the inner 128-wide L0 loop and requests a second accumulator slot automatically.

The two changes therefore serve different layers:

Eligibility and profitability

The recognizer requires:

  • a static pl.pipeline(stage=F) with F >= 2 and a trip count divisible by F
  • exactly one plain tile.matmul with static Left/Right operands
  • exactly one operand produced by a per-iteration Mat-to-L0 tile.extract or tile.move; the other operand is loop-invariant
  • exactly one canonical loop-carried drain and yield chain
  • no nested control flow, additional Acc values, additional stores, indirect Acc uses, or pre-existing explicit overlap policy

Path-specific profitability gates are intentionally conservative:

  • direct-to-GM tile.store: trip count >= 4
  • Acc-to-Mat tile.assemble: trip count >= 8 and aligned Acc footprint >= ceil(L0C / 4)

Before marking any loop, AutoTile computes a conservative whole-function L0C inventory, including pipeline replication of non-cube Acc producers, and adds one extra aligned accumulator slot for every accepted loop. The complete plan is declined unless it fits the backend L0C capacity.

The automatic recognizer is PyPTO-planner-only. PTOAS output remains unchanged: the reproduced pipeline already received four physical Acc placements there, and source marking was performance-neutral in the device study.

Lowering

AutoTile attaches pipeline_double_buffer_c=true and disables the weaker store-overlap policy on accepted loops. LowerPipelineLoops then tags the replicated cube accumulators with pipeline membership. CanonicalizeIOOrder emits depth-two compute/drain chunks and rotates only the innermost Acc membership modulo two before MemoryReuse consumes it. This creates two genuinely co-live L0C accumulators without multiplying L0C usage by a deeper operand pipeline.

Validation

Exact final rebased head:

  • cmake --build build --parallel 2
  • PYTHONPATH=$(pwd)/python python -m pytest tests/ut/ir/transforms/test_auto_tile_matmul_l0.py tests/ut/ir/transforms/test_lower_pipeline_loops.py tests/ut/ir/transforms/test_canonicalize_io_order.py -q
  • result: 123 passed

Ascend 910B2 device campaigns on logic-equivalent pre-rebase commits:

  • [Feature] Explicit buffer-slot binding for fine-grained multi-level double buffering #2131 reproduction: 15/15 correctness runs passed; PyPTO median effective-time improvement was +2.25% with 9/10 pairs favoring the automatic form; PTOAS was tied
  • broad direct-store campaign: structure passed under both planners and 84/84 correctness runs passed with outputs bit-identical across arms
  • Mat-scratch holdout: 4/5 unseen admitted cases were clear wins, the fifth was tied, and 81/81 correctness runs passed with outputs bit-identical across arms
  • stage depths 2/3/4 retained exactly two L0C addresses while preserving operand pipeline depth

The final rebases only incorporated unrelated upstream changes and documentation renumbering; no device-validated implementation behavior changed. The exact final head was rebuilt and host-tested as above.

Limitations

  • this is not a replacement for the explicit buffer-slot API in feat(ir): add explicit tile buffer slots #2150
  • PTOAS is intentionally unchanged
  • the recognizer deliberately rejects non-canonical or ambiguous dataflow
  • the Mat-scratch policy has a known conservative false negative: a measured 24 KiB, trip-count-16 case gained +4.88% but remains below the current L0C / 4 size threshold; broader trip-count/compute-drain modeling is left for follow-up rather than fitting another rule to one point

@coderabbitai

coderabbitai Bot commented Jul 28, 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 Plus

Run ID: 3e0e193d-1b81-4200-9130-4c0e75f532c3

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

AutoTileMatmulL0 now recognizes eligible existing PyPTO pipelines and enables capacity-checked L0C double buffering. CanonicalizeIOOrder schedules depth-two compute/drain groups and rotates Acc memberships, with documentation and comprehensive tests covering supported and rejected pipeline shapes.

Changes

Existing Pipeline L0C Double Buffering

Layer / File(s) Summary
Pipeline recognition and capacity planning
src/ir/transforms/auto_tile_matmul_l0_pass.cpp
Adds structural pipeline recognition, conservative whole-function Acc footprint accounting, profitability checks, and selective pipeline_double_buffer_c application.
Double-buffer scheduling and membership rotation
src/ir/transforms/canonicalize_io_order_pass.cpp, src/ir/transforms/lower_pipeline_loops_pass.cpp
Schedules dbC pipelines in depth-two compute/drain groups and rotates Acc memberships modulo two.
Pipeline dbC behavior validation
tests/ut/ir/transforms/test_auto_tile_matmul_l0.py
Adds coverage for supported pipelines, allocation and schedule results, planner differences, idempotency, profitability, capacity, and rejection cases.
Pass behavior documentation
docs/{en,zh-cn}/dev/passes/*, include/pypto/ir/transforms/*, python/bindings/modules/passes.cpp, python/pypto/pypto_core/passes.pyi
Documents pipeline recognition, planner gates, L0C capacity rules, scheduling, and Acc membership behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AutoTileMatmulL0
  participant LowerPipelineLoops
  participant CanonicalizeIOOrder
  participant MemoryReuse
  AutoTileMatmulL0->>LowerPipelineLoops: set pipeline_double_buffer_c
  LowerPipelineLoops->>CanonicalizeIOOrder: emit staged memberships
  CanonicalizeIOOrder->>MemoryReuse: rotate Acc membership modulo 2
  MemoryReuse->>MemoryReuse: allocate two L0C residues
Loading

Possibly related issues

Possibly related PRs

  • hw-native-sys/pypto#2002 — Provides related PyPTO L0C double-buffer opt-in and cube-accumulator membership behavior.

Suggested labels: enhancement

Poem

I hopped through pipelines, two buffers in flight,
Acc leaves its footprints, rotated just right.
Compute then drain in a depth-two parade,
Capacity checked before ping-pong is made.
Tests guard each path with a carrot-bright cheer!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.69% 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 clearly matches the main change: automatic L0C double-buffering for eligible pipelines.
Description check ✅ Passed The description is detailed and directly aligned with the documented and tested L0C double-buffering changes.
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.

@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: 48407afe5c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/ir/transforms/auto_tile_matmul_l0_pass.cpp

@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: 3

🧹 Nitpick comments (2)
src/ir/transforms/canonicalize_io_order_pass.cpp (1)

55-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse AppendPipelineMembership for the repack.

attrs.h already owns the "group:stage" / ; encoding; hand-rolling it here means two places must stay in sync if the format changes.

♻️ Suggested repack
   std::string result;
   for (const auto& [group, member_stage] : memberships) {
-    if (!result.empty()) result += ";";
-    result += std::to_string(group) + ":" + std::to_string(member_stage);
+    result = AppendPipelineMembership(result, group, member_stage);
   }
   return result;
🤖 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 `@src/ir/transforms/canonicalize_io_order_pass.cpp` around lines 55 - 66,
Update RotateInnermostPipelineStage to repack memberships using the existing
AppendPipelineMembership helper instead of manually concatenating group, stage,
and semicolon separators. Preserve the current rotation logic and returned
encoding while delegating format ownership to AppendPipelineMembership.
src/ir/transforms/auto_tile_matmul_l0_pass.cpp (1)

1724-1795: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename empty: the same set is used as the "no plan" sentinel and as the accepted-plan accumulator.

Lines 1790-1794 insert the selected loops into a variable named empty and return it, which reads as a bug at a glance. A plan local (returning {} for the early bails) is clearer, and the structured bindings then no longer need (void) discards.

♻️ Suggested rename
 std::unordered_set<const ForStmt*> BuildPipelineDbCPlan(const FunctionPtr& func) {
-  std::unordered_set<const ForStmt*> empty;
   const auto* ctx = PassContext::Current();
   const MemoryPlanner planner = ctx ? ctx->GetMemoryPlanner() : MemoryPlanner::PyPTO;
-  if (planner != MemoryPlanner::PyPTO) return empty;
+  if (planner != MemoryPlanner::PyPTO) return {};

...and at the end:

-  for (const auto& [loop, candidate] : candidates.candidates) {
-    (void)candidate;
-    empty.insert(loop);
-  }
-  return empty;
+  std::unordered_set<const ForStmt*> plan;
+  for (const auto& entry : candidates.candidates) plan.insert(entry.first);
+  return plan;
🤖 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 `@src/ir/transforms/auto_tile_matmul_l0_pass.cpp` around lines 1724 - 1795,
Rename the result set in BuildPipelineDbCPlan from empty to plan, returning an
empty set literal or equivalent empty result on all early exits. Insert accepted
loops into plan at the end, and remove the unnecessary (void) discards from the
final structured binding since the candidate value is no longer unused.
🤖 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 `@docs/en/dev/passes/15-auto_tile_matmul_l0.md`:
- Around line 9-10: Update the AutoTile documentation and public API comment to
include tile.write wherever the accepted direct-drain operations are listed,
alongside tile.store. Apply this consistently in
docs/en/dev/passes/15-auto_tile_matmul_l0.md at lines 9-10 and 62-63,
docs/zh-cn/dev/passes/15-auto_tile_matmul_l0.md at lines 9-10 and 62-63, and
include/pypto/ir/transforms/passes.h at lines 475-477; no recognizer code change
is needed.
- Around line 9-10: Update the AutoTile transfer-count contract to match the
recognizer: describe only the selected moving operand’s direct Mat→L0 producer,
rather than requiring exactly one transfer. Apply this correction in
docs/en/dev/passes/15-auto_tile_matmul_l0.md at lines 9-10 and 62-63,
docs/zh-cn/dev/passes/15-auto_tile_matmul_l0.md at lines 9-10 and 62-63, and the
public API comment in include/pypto/ir/transforms/passes.h at lines 471-474; no
implementation change is requested.

In `@tests/ut/ir/transforms/test_auto_tile_matmul_l0.py`:
- Around line 2174-2208: Rename the intentionally unused loop-carried value q_r
in test_rejects_loop_carried_matmul_operand to use the underscore-prefixed
convention, matching nearby unused pipeline outputs and satisfying RUF059. Keep
the yielded q_i value and all test behavior unchanged.

---

Nitpick comments:
In `@src/ir/transforms/auto_tile_matmul_l0_pass.cpp`:
- Around line 1724-1795: Rename the result set in BuildPipelineDbCPlan from
empty to plan, returning an empty set literal or equivalent empty result on all
early exits. Insert accepted loops into plan at the end, and remove the
unnecessary (void) discards from the final structured binding since the
candidate value is no longer unused.

In `@src/ir/transforms/canonicalize_io_order_pass.cpp`:
- Around line 55-66: Update RotateInnermostPipelineStage to repack memberships
using the existing AppendPipelineMembership helper instead of manually
concatenating group, stage, and semicolon separators. Preserve the current
rotation logic and returned encoding while delegating format ownership to
AppendPipelineMembership.
🪄 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 Plus

Run ID: 50259a56-797c-4e37-a9b7-01f1f9fa078a

📥 Commits

Reviewing files that changed from the base of the PR and between 478ddad and 48407af.

📒 Files selected for processing (14)
  • docs/en/dev/passes/15-auto_tile_matmul_l0.md
  • docs/en/dev/passes/26-lower_pipeline_loops.md
  • docs/en/dev/passes/27-canonicalize_io_order.md
  • docs/zh-cn/dev/passes/15-auto_tile_matmul_l0.md
  • docs/zh-cn/dev/passes/26-lower_pipeline_loops.md
  • docs/zh-cn/dev/passes/27-canonicalize_io_order.md
  • include/pypto/ir/transforms/passes.h
  • include/pypto/ir/transforms/utils/attrs.h
  • python/bindings/modules/passes.cpp
  • python/pypto/pypto_core/passes.pyi
  • src/ir/transforms/auto_tile_matmul_l0_pass.cpp
  • src/ir/transforms/canonicalize_io_order_pass.cpp
  • src/ir/transforms/lower_pipeline_loops_pass.cpp
  • tests/ut/ir/transforms/test_auto_tile_matmul_l0.py

Comment thread docs/en/dev/passes/15-auto_tile_matmul_l0.md Outdated
Comment thread tests/ut/ir/transforms/test_auto_tile_matmul_l0.py
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.

1 participant