feat(distributed): add HOST-orchestrated builtin.tensor.all_to_all_v - #2243
feat(distributed): add HOST-orchestrated builtin.tensor.all_to_all_v#2243georgebisbas wants to merge 7 commits into
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 Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough
ChangesHOST all-to-all-v
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant HostOrchestration
participant AllToAllVKernel
participant TargetWindow
participant PeerSignals
HostOrchestration->>AllToAllVKernel: submit seven kernel arguments
AllToAllVKernel->>TargetWindow: TPUT fixed MAX_RECV blocks
AllToAllVKernel->>TargetWindow: publish clamped recv_counts
AllToAllVKernel->>PeerSignals: notify peer completion
PeerSignals-->>AllToAllVKernel: GE completion signals
Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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
Adds HOST-orchestrated variable-size all-to-all support across IR lowering, codegen, runtime templates, tests, and documentation.
Changes:
- Registers and lowers
builtin.tensor.all_to_all_v. - Adds MAX_RECV-specific codegen and runtime kernels.
- Extends communication-domain analysis and test coverage.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/ir/op/distributed/collective.cpp |
Registers the internal builtin. |
src/ir/transforms/lower_host_tensor_collectives_pass.cpp |
Adds HOST lowering. |
src/ir/transforms/lower_composite_ops_pass.cpp |
Defers HOST calls. |
src/ir/transforms/materialize_comm_domain_scopes_pass.cpp |
Adds domain inheritance and loop checks. |
src/codegen/distributed/distributed_ops_codegen.cpp |
Emits MAX_RECV-specific variants. |
python/pypto/runtime/builtins/collectives/all_to_all_v/__init__.py |
Defines the template package. |
python/pypto/runtime/builtins/collectives/all_to_all_v/templates/entry.cpp.in |
Adds orchestration entrypoint. |
python/pypto/runtime/builtins/collectives/all_to_all_v/templates/kernel.cpp.in |
Implements the collective kernel. |
python/pypto/runtime/builtins/collectives/all_to_all_v/templates/kernel_config.py.in |
Configures kernel arguments. |
tests/ut/ir/test_distributed_ops.py |
Tests registration and resources. |
tests/ut/ir/transforms/test_lower_composite_ops.py |
Tests HOST deferral. |
tests/ut/ir/transforms/test_lower_host_tensor_collectives.py |
Tests HOST lowering. |
tests/ut/ir/transforms/test_materialize_comm_domain_scopes.py |
Tests domains and loop rejection. |
tests/ut/codegen/distributed/test_host_orch_distributed.py |
Tests generated artifacts. |
tests/st/distributed/test_l3_host_tensor_all_to_all_v.py |
Adds end-to-end system coverage. |
docs/en/dev/distributed_ops.md |
Documents HOST support. |
docs/en/dev/passes/39-materialize_comm_domain_scopes.md |
Documents domain analysis. |
docs/en/dev/passes/40-lower_host_tensor_collectives.md |
Documents HOST lowering. |
docs/zh/dev/distributed_ops.md |
Synchronizes Chinese operator docs. |
docs/zh/dev/passes/39-materialize_comm_domain_scopes.md |
Synchronizes Chinese domain docs. |
docs/zh/dev/passes/40-lower_host_tensor_collectives.md |
Synchronizes Chinese lowering docs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72bfa69fb3
ℹ️ 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".
There was a problem hiding this comment.
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 `@src/ir/transforms/lower_host_tensor_collectives_pass.cpp`:
- Around line 230-268: Validate the HOST-level input and send_counts types
before HostCollectiveRule::scope_buffers calls GetWindowBuffer, since
MakeBuiltinAllToAllV narrows them too late. Update GetWindowBuffer or add a
user-facing CHECK_SPAN at that boundary to require DistributedTensorType, using
the shared range’s argument name in the diagnostic so plain Tensor inputs
produce a clear error instead of an internal check failure.
🪄 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: c8de8f9a-91aa-4b41-9f1a-0e9e8d4c9214
📒 Files selected for processing (21)
docs/en/dev/distributed_ops.mddocs/en/dev/passes/39-materialize_comm_domain_scopes.mddocs/en/dev/passes/40-lower_host_tensor_collectives.mddocs/zh/dev/distributed_ops.mddocs/zh/dev/passes/39-materialize_comm_domain_scopes.mddocs/zh/dev/passes/40-lower_host_tensor_collectives.mdpython/pypto/runtime/builtins/collectives/all_to_all_v/__init__.pypython/pypto/runtime/builtins/collectives/all_to_all_v/templates/entry.cpp.inpython/pypto/runtime/builtins/collectives/all_to_all_v/templates/kernel.cpp.inpython/pypto/runtime/builtins/collectives/all_to_all_v/templates/kernel_config.py.insrc/codegen/distributed/distributed_ops_codegen.cppsrc/ir/op/distributed/collective.cppsrc/ir/transforms/lower_composite_ops_pass.cppsrc/ir/transforms/lower_host_tensor_collectives_pass.cppsrc/ir/transforms/materialize_comm_domain_scopes_pass.cpptests/st/distributed/test_l3_host_tensor_all_to_all_v.pytests/ut/codegen/distributed/test_host_orch_distributed.pytests/ut/ir/test_distributed_ops.pytests/ut/ir/transforms/test_lower_composite_ops.pytests/ut/ir/transforms/test_lower_host_tensor_collectives.pytests/ut/ir/transforms/test_materialize_comm_domain_scopes.py
💤 Files with no reviewable changes (1)
- src/ir/transforms/lower_composite_ops_pass.cpp
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
src/ir/transforms/lower_host_tensor_collectives_pass.cpp:213
- This only verifies the IR type, not that the value is actually window-backed. A HOST parameter typed as
DistributedTensorhas nowindow_buffer_; when used asinputorsend_counts, it passes this check and then triggersGetWindowBuffer's internal-invariant failure. Validatewindow_buffer_here as well so all documented non-window-bound inputs get a user-facing error.
void CheckHostWindowBoundArg(const ExprPtr& expr, const char* op_name, const char* role) {
CHECK_SPAN(As<DistributedTensorType>(expr->GetType()) != nullptr, expr->span_)
<< op_name << " " << role
<< " must be a window-bound DistributedTensor when called from a HOST orchestrator "
"(a plain Tensor is only supported on the InCore composite path)";
src/ir/transforms/lower_host_tensor_collectives_pass.cpp:292
- This range is not guaranteed by the public deducer: a zero-row target yields
max_recv_i64 == 0, and a sufficiently large static target can exceedINT32_MAX. Both are user-reachable HOST inputs, so reporting them as an internal compiler invariant is incorrect. UseCHECK_SPANto produce the intended user-facing validation error.
INTERNAL_CHECK_SPAN(max_recv_i64 > 0 && max_recv_i64 <= static_cast<int64_t>(INT32_MAX), call->span_)
docs/en/dev/passes/40-lower_host_tensor_collectives.md:51
- The added paragraph now runs directly into the existing lowercase “the pass emits” sentence. Add a paragraph break and capitalize the sentence.
`pld.system.notify`) — all five window args must resolve into the same
`CommDomainScopeStmt`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
src/ir/transforms/lower_host_tensor_collectives_pass.cpp:228
- Only control-to-control aliasing is rejected. If
inputaliasessignalorrecv_counts, a faster peer's notify can overwrite source rows before this rank TPUTs them; iftargetaliases any control window, incoming TPUTs race with count reads/notifies or can satisfy/corrupt the barrier. Reject those data/control allocation pairs as well.
void CheckDistinctControlWindows(const CallPtr& call, const char* op_name) {
auto signal_wb = GetWindowBuffer(call->args_[2], "signal");
auto counts_wb = GetWindowBuffer(call->args_[3], "send_counts");
auto recv_wb = GetWindowBuffer(call->args_[4], "recv_counts");
CHECK_SPAN(signal_wb.get() != recv_wb.get(), call->span_)
src/ir/transforms/lower_host_tensor_collectives_pass.cpp:212
- This only checks the IR type, not whether the value is actually window-bound. A
DistributedTensorHOST parameter has no materializedwindow_buffer_, so it passes this check and thenGetWindowBufferraises an internal-invariant error. Include the back-reference in this user-facing validation.
void CheckHostWindowBoundArg(const ExprPtr& expr, const char* op_name, const char* role) {
CHECK_SPAN(As<DistributedTensorType>(expr->GetType()) != nullptr, expr->span_)
<< op_name << " " << role
<< " must be a window-bound DistributedTensor when called from a HOST orchestrator "
"(a plain Tensor is only supported on the InCore composite path)";
tests/st/distributed/test_l3_host_tensor_all_to_all_v.py:227
- These counts never exceed
MAX_RECV(nr - dis at most 4), so the new kernel's runtime clamp branch is not exercised end-to-end. Include at least one count above capacity and assert thatrecv_countsand consumed rows usemin(count, MAX_RECV).
for r in range(nr):
for d in range(nr):
n_rows = nr - d # variable send count, same golden formula as the InCore ST
send_counts[r, d, 0] = n_rows
docs/en/dev/passes/40-lower_host_tensor_collectives.md:51
- The paragraph now ends at
CommDomainScopeStmt, leaving the next line as the lowercase sentence fragment “the pass emits…”. Start a new sentence (or join it to this paragraph) so the pass behavior reads grammatically.
`all_to_all_v` additionally requires `send_counts` (window-bound at this
layer, LOCAL-only) and `recv_counts` (window-bound, published cross-rank via
`pld.system.notify`) — all five window args must resolve into the same
`CommDomainScopeStmt`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.
Suppressed comments (7)
docs/en/dev/passes/40-lower_host_tensor_collectives.md:51
- Capitalize the sentence after the paragraph break.
the pass emits the corresponding `builtin.tensor.*` dispatch per participating
src/codegen/distributed/distributed_ops_codegen.cpp:347
- The correctness reason for including
max_recvin the variant is not covered by the new single-call codegen test: that test would still pass if global deduplication collapsed different capacities. Add one program containing two HOSTall_to_all_vcalls with differentMAX_RECVvalues and assert that both next-level variants are emitted with their respectivekMaxRecvsubstitutions.
const std::string variant =
op->op_->name_ + "__maxrecv" + std::to_string(max_recv) + "__" + Fp32VariantSuffix(dtype);
if (dist_codegen->MarkBuiltinEmitted(variant)) {
dist_codegen->RecordBuiltinNextLevel(
op, variant, {{"max_recv_cpp", std::to_string(max_recv)}, {"dtype_cpp", Fp32TypeCpp(dtype)}});
src/ir/op/distributed/collective.cpp:588
- This description remains internally contradictory: its opening at line 578 says each rank pushes
send_counts[dest]rows, while these lines say every rank physically pushes the fullMAX_RECVblock. Rewrite the opening in terms of logically valid rows versus physical transfer size. The same obsolete contract remains inpython/pypto/language/distributed/op/tensor_ops.py:825-853,python/pypto/ir/op/distributed/tensor_ops.py:399-407, andlower_composite_ops_pass.cpp:1787-1825, so those primary API docstrings/comments also need synchronization with the new HOST path.
"push always transfers the full MAX_RECV-row capacity block per "
"destination (a compile-time-sized ``pld.tile.put``, independent of "
"the runtime count) — rows beyond a sender's actual count still cross "
"the wire, but the receiver skips them using ``recv_counts`` "
"(MPI_Alltoallv semantics apply to the logical result, not the wire "
"transfer). During the same push phase each rank also publishes "
docs/en/dev/distributed_ops.md:312
- The implementation rejects aliasing between every pair of the five operands, but this only documents
input/target. State the full user-visible restriction, including the otherwise non-obvious read-onlyinput/send_countspair.
`builtin.tensor.all_to_all`. `input` and `send_counts` must both be
window-bound `DistributedTensor`s at this layer (narrower than the composite's
`AsTensorTypeLike`, forced by the HOST dispatch codegen, which only supports
window-bound or tile args) — `input` must be distinct from `target`, same
discipline as the symmetric `all_to_all` builtin. `MAX_RECV` is mangled into
docs/en/dev/passes/40-lower_host_tensor_collectives.md:50
- The implementation requires all five
all_to_all_voperand windows to use pairwise-distinct allocations, not only distinctinputandtargetwindows. Document that restriction here so the pass contract matchesCheckAllToAllVDistinctWindows.
`all_to_all_v` additionally requires `send_counts` (window-bound at this
layer, LOCAL-only) and `recv_counts` (window-bound, published cross-rank via
`pld.system.notify`) — all five window args must resolve into the same
`CommDomainScopeStmt`.
docs/zh/dev/distributed_ops.md:274
- 实现会拒绝五个操作数窗口中任意一对共享 allocation,但这里仅记录了
input与target必须不同。请补充完整的两两不同约束,使文档与CheckAllToAllVDistinctWindows一致。
在这一层,`input` 与 `send_counts` 都必须是窗口绑定的 `DistributedTensor`(比
composite 的 `AsTensorTypeLike` 更严格,这是 HOST 派发代码生成强制要求的——
它只支持窗口绑定或 tile 参数)——`input` 必须与 `target` 是不同的窗口,与对称
`all_to_all` builtin 的约束相同。`MAX_RECV` 会被混入代码生成的 variant 字符串
docs/zh/dev/passes/40-lower_host_tensor_collectives.md:47
- 实现要求
all_to_all_v的五个操作数窗口使用两两不同的 allocation,而不仅是input与target不同。请在此明确该约束,使 pass 文档与CheckAllToAllVDistinctWindows一致。
结果窗口。`all_to_all_v` 还额外要求 `send_counts`(在这一层是窗口绑定的,
仅本地使用)和 `recv_counts`(窗口绑定,通过 `pld.system.notify` 跨 rank
发布)——五个窗口参数都必须位于同一个 `CommDomainScopeStmt` 中。
|
Addressed the suppressed-comment findings from Copilot's latest review round (never posted as inline threads, so nothing to resolve there) in
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
src/ir/transforms/lower_host_tensor_collectives_pass.cpp:432
- These operands can materialize into different comm-domain scopes when their staging/consume dispatches use different explicit device subsets:
inputandsend_countsretain their own coverage, whilesignalandrecv_countsinherittarget's. In that user-reachable caseFindScopeForBuffersreturns null andLowerCollectivereports anINTERNAL_CHECK_SPANcompiler failure. Since the docs make same-domain membership a user requirement, this path should issue an actionableCHECK_SPANerror (or materialization should deliberately unify the five operands' coverage).
return std::vector<WindowBufferPtr>{
GetWindowBuffer(call->args_[0], "all_to_all_v input"),
GetWindowBuffer(call->args_[1], "all_to_all_v target"),
GetWindowBuffer(call->args_[2], "all_to_all_v signal"),
GetWindowBuffer(call->args_[3], "all_to_all_v send_counts"),
python/pypto/runtime/builtins/collectives/all_to_all_v/templates/kernel.cpp.in:124
- Only applying the upper bound lets a negative runtime count be published unchanged into the peer's
recv_counts. That value is documented as a valid-row count and is later cast toINDEX/used as a loop bound, so a negative value violates the API contract and may miscompile consumers that rely on INDEX being non-negative. Clamp to[0, kMaxRecv]and make the equivalent change inLowerTensorAllToAllVRuleso HOST and InCore remain aligned.
int32_t raw_count = send_counts_base[dest];
int64_t rows64 = static_cast<int64_t>(raw_count);
if (rows64 > kMaxRecv) rows64 = kMaxRecv;
int32_t rows = static_cast<int32_t>(rows64);
src/ir/op/distributed/collective.cpp:1107
- This argument description still says
recv_countsrecords how many rows were sent, but the new kernel physically sends allMAX_RECVrows. The value is the clamped number of logically valid leading rows; describing it that way avoids contradicting the revised operation description.
.add_argument("recv_counts",
"Window-bound INT32 DistributedTensor [NR, 1] — after the barrier, "
"recv_counts[src, 0] holds how many rows src sent to this rank (InOut)")
8b630db to
baf0b71
Compare
Closes the last gap in host-orchestrator support for variable-size all-to-all: pld.tensor.all_to_all_v could only be called from InCore kernels (PR hw-native-sys#2112), with an explicit rejection if called from a HOST orchestrator function. This adds the missing HOST dispatch path, cloning the proven builtin.tensor.all_to_all (PR hw-native-sys#1997) pattern: - Op registration: builtin.tensor.all_to_all_v, narrowing `input` and `send_counts` to strict window-bound DistributedTensor (forced by EmitBuiltinWindowCollectiveDispatch, which has no dispatch path for a plain Tensor arg at this layer). - Lowering rule (LowerHostTensorCollectives): MakeBuiltinAllToAllV, deriving MAX_RECV = target.shape[0] / signal.shape[0] and forwarding it as a kwarg/attr alongside dtype. - Codegen (distributed_ops_codegen.cpp): variant string mangles both max_recv and dtype (builtin.tensor.all_to_all_v__maxrecv<N>__fp32), since MAX_RECV is baked into the kernel as a compile-time constexpr and codegen state is program-global. - Runtime templates: new all_to_all_v/ package — the kernel always transfers the full MAX_RECV-row block per destination (matching LowerTensorAllToAllVRule's compile-time transfer_shape exactly, for bit-for-bit InCore/HOST parity), publishing the runtime-clamped count into peer recv_counts via TNOTIFY inline with the push. - Comm-domain analysis (MaterializeCommDomainScopes): device-coverage inheritance for signal and recv_counts, plus a HOST-side loop-use guard (repeating_scope_depth_, tracked for both ForStmt and WhileStmt) mirroring the InCore path's CheckAllReduceLoopUse — the HOST path had no equivalent protection before this change. - Removed the explicit "InCore orchestration function only" rejection in LowerCompositeOps now that a HOST rule exists for this op. - Docs (EN+ZH): documents the new HOST path and corrects a pre-existing inaccuracy (the InCore lowering always transfers the full MAX_RECV block; only the published count was ever runtime-gated). Unit tests: full suite green (8539 passed, 13 skipped, 0 failed). Distributed system tests (test_l3_host_tensor_all_to_all_v.py, plus the InCore/HOST all_to_all regression STs) could not be exercised in this environment: the local runtime submodule checkout is ahead of what the sim Docker image's simpler runtime was built against (missing RunTiming in _task_interface), a pre-existing mismatch unrelated to this change — reproduces identically on unmodified sibling tests. NPU/full distributed verification is still pending.
- Add missing <cstdint> include in collective.cpp (clang-tidy misc-include-cleaner: int64_t used directly, only transitively available before this). - Fix a self-contradictory docstring in the pld.tensor.all_to_all_v description: it now consistently says every MAX_RECV row is physically transferred and recv_counts marks which are logically valid, rather than implying some rows are "unwritten." - Add CheckDistinctControlWindows to MakeBuiltinAllToAllV: signal, send_counts, and recv_counts are three separate INT32 control windows with distinct cross-rank semantics, so aliasing any pair is a real race (barrier notify clobbering a published count, or a local count read racing a peer's cross-rank notify write), not just a style nit. The existing type checks alone accepted all three cases. - Add CheckHostWindowBoundArg, called from the all_to_all_v rule's scope_buffers lambda before any window-buffer lookup: the public pld.tensor.all_to_all_v deducer accepts a plain Tensor for `input` and `send_counts` (legitimate on the InCore composite path), so a HOST orchestrator caller passing either as a plain Tensor is user-reachable input, not a compiler invariant violation. GetWindowBuffer's INTERNAL_CHECK_SPAN was previously the first thing to trip on this, surfacing as a compiler-bug-shaped crash instead of a clean CHECK_SPAN ValueError. - Rewrite test_all_to_all_v_signal_and_recv_counts_inherit_data_comm_domain so chip_orch no longer takes signal/recv_counts as params — passing them straight through to a dispatch site let the test pass even with the two new CollectiveConsumer inheritance entries removed. Matches test_allreduce_signal_inherits_data_comm_domain's discipline of leaving the signal-like arg out of the dispatch site. - Add rejection tests for all of the above: plain-Tensor input, plain- Tensor send_counts, aliased signal/recv_counts, and aliased send_counts/recv_counts.
…ty for all_to_all_v - Check all 10 pairwise combinations of all_to_all_v's 5 window operands for aliasing (input/target/signal/send_counts/recv_counts), not just 4. - Require signal shape[0] to exactly equal the participating device count on an explicit static device subset, since MAX_RECV is derived as target_dim0/signal_dim0 and silently mis-lowers when signal is over-provisioned relative to the subset.
…ps for all_to_all_v - Fix the "send_counts[dest] rows only cross the wire" / "rows beyond count never written" contradiction against the actual full-MAX_RECV-block transfer, in collective.cpp's op description, lower_composite_ops_pass.cpp's rule comment (which contradicted its own code below it), and both Python tensor_ops.py docstrings. - Document the all-5-pairwise-distinct-window and exact-static-signal-capacity requirements (added earlier this PR) in distributed_ops.md and 40-lower_host_tensor_collectives.md, en and zh. - Add a codegen test asserting two all_to_all_v calls with different MAX_RECV emit two distinct next-level variants instead of collapsing into one mis-instantiated kernel. Addresses Copilot review comments that were suppressed (not posted as inline threads) on the latest PR review round.
…o_all_v CheckHostWindowBoundArg previously verified only the type kind, so a user-declared pld.DistributedTensor parameter (window_buffer_ == nullopt until pld.tensor.window binds it) passed the check and then tripped GetWindowBuffer's INTERNAL_CHECK_SPAN. Check the window-buffer back-reference as well, so both plain tensors and unbound distributed parameters receive the documented CHECK_SPAN ValueError. Add rejection tests for input and send_counts.
The rebase onto origin/main (which added ring-allreduce HOST support) conflicted in the pass and the EN/ZH pass docs. Keep both mechanisms: - EmitPerDeviceBuiltinCalls: allreduce uses main's ring-aware CheckAllReduceSignalCapacity; non-allreduce ops keep CheckStaticSignalCapacity; all_to_all_v's exact_capacity_check still runs after either branch. - Restore the closing brace of CheckAllReduceSignalCapacity dropped by the keep-both merge (it had nested CheckExactSignalCapacity and MakeBuiltinCallWithAttrs inside it). - Merge the EN/ZH docs to describe both ring mode and all_to_all_v's 5-window pairwise-distinct requirement in a single coherent section.
baf0b71 to
4228eaa
Compare
Summary
Closes the last gap in host-orchestrator support for variable-size all-to-all.
pld.tensor.all_to_all_v(variable-size all-to-all, MPI_Alltoallv pattern) merged as #2112, but only as an InCore composite — calling it from ahost_orchfunction hit an explicit "not supported in a HOST orchestration function" rejection. This adds the missing HOST dispatch path, cloning the provenbuiltin.tensor.all_to_allpattern (#1997) across every layer:collective.cpp):builtin.tensor.all_to_all_v, narrowinginputandsend_countsto strict window-boundDistributedTensor— forced byEmitBuiltinWindowCollectiveDispatch, which has no dispatch path for a plainTensorarg at this layer (same narrowing the existingbuiltin.tensor.all_to_allalready applies to its owninput).lower_host_tensor_collectives_pass.cpp):MakeBuiltinAllToAllV, derivingMAX_RECV = target.shape[0] / signal.shape[0]and forwarding it as a kwarg/attr alongsidedtype.distributed_ops_codegen.cpp): the variant string mangles bothmax_recvanddtype(builtin.tensor.all_to_all_v__maxrecv<N>__fp32) —MAX_RECVis baked into the kernel as a compile-timeconstexpr, and codegen'sMarkBuiltinEmitted/RecordBuiltinNextLevelstate is program-global, so two call sites with differentMAX_RECVwould otherwise silently mis-share one kernel instantiation.all_to_all_v/builtin package. The kernel always transfers the fullMAX_RECV-row block per destination — matchingLowerTensorAllToAllVRule's compile-timetransfer_shapeexactly, for bit-for-bit InCore/HOST parity on the wire — and publishes the runtime-clamped count into peerrecv_countsviaTNOTIFYinline with the push.materialize_comm_domain_scopes_pass.cpp): device-coverage inheritance for bothsignalandrecv_counts(twoCollectiveConsumerentries sharing the same data alloc), plus a HOST-side loop-use guard (repeating_scope_depth_, tracked for bothForStmtandWhileStmt) mirroring the InCore path'sCheckAllReduceLoopUse— the HOST path had no equivalent protection before this change, so a caller could previously reuse a single-use signal across loop iterations undetected.IsInCoreOnlyCollectiverejection inLowerCompositeOpsnow that a HOST rule exists for this op.MAX_RECVblock; only the published count was ever runtime-gated, not the transfer itself.Design notes for reviewers
send_countsmust be staged through a window buffer at the HOST layer even though it's logically pure-local, per-rank data (never cross-rank-published) — this is a real ergonomic cost of the narrowing above, not a bug. The new HOST system test documents this with an explicitfill_counts_step/fill_counts_orchstaging pair.Set(1)/wait≥1protocol, matching every other HOST builtin and the InCoreall_to_all_vrule this clones — the reusable credit-barrier rework (fix(ir): make composite collective barrier signals reusable #2175) is unmerged and out of scope here.CheckSupportedFp32BuiltinVariant), matching every HOST builtin exceptallreduce. Adding FP16 across all HOST builtins is a separate, broader gap.all_to_all/allgather/barriershare the same single-use-signal exposure tohost_orchloops and have no equivalent guard today — pre-existing, out of scope for this PR.Test plan
pypto3-hw-native-sys:sim) — clean.pre-commit run --all-files: clean (headers, English-only, EN/ZH docs parity, docs-nav, clang-format, cpplint, markdownlint, ruff check/format, pyright).tests/st/distributed/test_l3_host_tensor_all_to_all_v.py(new) plus the InCore/HOSTall_to_allregression STs all hitImportError: cannot import name 'RunTiming' from '_task_interface'— a pre-existing local-environment mismatch (theruntimesubmodule checkout is ahead of what the sim image'ssimplerruntime was built against), reproducing identically on unmodified, already-shipped sibling tests. Unrelated to this change, but means the actual distributed exchange logic has not been exercised end-to-end here.