Skip to content

Fix: preserve explicit dependency flags in dep_gen replay - #2057

Open
zmnobug wants to merge 1 commit into
hw-native-sys:mainfrom
zmnobug:fix/issue-1827-dep-gen-explicit-flags
Open

Fix: preserve explicit dependency flags in dep_gen replay#2057
zmnobug wants to merge 1 commit into
hw-native-sys:mainfrom
zmnobug:fix/issue-1827-dep-gen-explicit-flags

Conversation

@zmnobug

@zmnobug zmnobug commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Capture per-dependency WAIT/RETAIN flags in inline and overflow dep_gen records.
  • Replay explicit edges with their recorded flags while preserving runtime OR-dedup semantics with creator dependencies.
  • Cover inline, single-overflow, multi-overflow, wait-only, and explicit/creator dedup cases on A2/A3 and A5, and update the DFX documentation.

Testing

  • test_dep_gen_collector_aicpu
  • A2/A3 simulation dep_gen scene tests with manual cases: 2 passed
  • A5 simulation dep_gen scene tests with manual cases: 2 passed
  • A2/A3 onboard dep_gen scene tests with manual cases: 2 passed
  • pre-commit run --from-ref upstream/main --to-ref HEAD
  • A5 onboard not run locally because this host has A2/A3 silicon; PR CI covers A5 hardware.

Fixes #1827

@coderabbitai

coderabbitai Bot commented Aug 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: 73875a59-9766-4320-ab5f-78c250d0e31e

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ab0c76d9-26d0-4803-a3f9-1245e7662f11

📥 Commits

Reviewing files that changed from the base of the PR and between 482ae6b and 4e485df.

📒 Files selected for processing (18)
  • docs/dfx/dep-gen.md
  • docs/dfx/dfx-buffer-capacity-audit.md
  • src/a2a3/platform/include/common/platform_config.h
  • src/a2a3/runtime/tensormap_and_ringbuffer/host/dep_gen_replay.cpp
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/orchestrator.cpp
  • src/a2a3/runtime/tensormap_and_ringbuffer/runtime/types.h
  • src/a5/platform/include/common/platform_config.h
  • src/a5/runtime/tensormap_and_ringbuffer/host/dep_gen_replay.cpp
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/orchestrator.cpp
  • src/a5/runtime/tensormap_and_ringbuffer/runtime/types.h
  • src/common/platform/include/aicpu/dep_gen_collector_aicpu.h
  • src/common/platform/include/common/dep_gen.h
  • src/common/platform/shared/aicpu/dep_gen_collector_aicpu.cpp
  • tests/st/a2a3/tensormap_and_ringbuffer/dfx/dep_gen/kernels/orchestration/chain_barrier_orch.cpp
  • tests/st/a2a3/tensormap_and_ringbuffer/dfx/dep_gen/test_dep_gen_chain.py
  • tests/st/a5/tensormap_and_ringbuffer/dfx/dep_gen/kernels/orchestration/chain_barrier_orch.cpp
  • tests/st/a5/tensormap_and_ringbuffer/dfx/dep_gen/test_dep_gen_chain.py
  • tests/ut/cpp/common/test_dep_gen_collector_aicpu.cpp

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Dependency capture now stores per-explicit-dependency DepFlags in base and overflow records. Replay validates and preserves these flags, merges duplicate producer edges, and updates both platform implementations. Tests and documentation cover the new 4736-byte layout and 524-dependency overflow capacity.

Dependency kind capture and replay

Layer / File(s) Summary
Record layout and submission contract
src/common/platform/include/common/dep_gen.h, src/common/platform/include/aicpu/dep_gen_collector_aicpu.h, src/{a2a3,a5}/runtime/.../types.h, src/{a2a3,a5}/platform/.../platform_config.h
Records include parallel kind arrays. The submission API accepts per-dependency kinds and a default kind.
Kind-aware dependency capture
src/common/platform/shared/aicpu/dep_gen_collector_aicpu.cpp, src/{a2a3,a5}/runtime/.../orchestrator.cpp
Submission paths pass dependency kinds. The collector copies supplied kinds or applies the default to base and overflow records.
Kind-aware replay and edge merging
src/{a2a3,a5}/runtime/.../host/dep_gen_replay.cpp
Replay reads and validates recorded kinds. Duplicate explicit and creator edges merge flags by producer.
Boundary and flag validation
tests/ut/cpp/common/test_dep_gen_collector_aicpu.cpp, tests/st/{a2a3,a5}/tensormap_and_ringbuffer/dfx/dep_gen/*, docs/dfx/*.md
Tests cover default kinds, mixed overflow records, updated boundaries, and cross-source deduplication. Documentation reflects the new record size and capacity.

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

Merge Risk: 🟡 Moderate · up to 4e485

The change preserves dependency flags but also reduces overflow capacity, which can expose a timeout path that reuses a buffer after it has been published, potentially corrupting dependency metadata used for host replay. Merge should wait for this ownership issue to be fixed or explicitly accepted by the responsible owner.

Sequence Diagram(s)

sequenceDiagram
  participant TaskSubmission
  participant AICPUCollector
  participant DepGenRecord
  participant DepGenReplay
  participant DepsJson
  TaskSubmission->>AICPUCollector: submit explicit dependency kinds
  AICPUCollector->>DepGenRecord: store dependency IDs and kind bytes
  DepGenRecord->>DepGenReplay: provide base and overflow records
  DepGenReplay->>DepsJson: emit validated, merged edge flags
Loading

Poem

I hop through records, neat and bright

With wait and retain tucked just right
Overflow berries line the chain
Replay joins duplicates again
A tidy burrow guards the byte

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 16 files. (2 skipped:… 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 describes the main change: preserving explicit dependency flags during dep_gen replay.
Description check ✅ Passed The description directly covers per-dependency flag capture, replay, deduplication, testing, and documentation updates.
Linked Issues check ✅ Passed The changes satisfy issue #1827 by adding per-dependency flags to base and overflow records, capturing and replaying them, preserving explicit/creator OR-deduplication, and updating related tests and …
Out of Scope Changes check ✅ Passed The changes remain within scope. Header layout updates, platform documentation, replay logic, tests, and DFX documentation directly support the explicit dependency flag fix.
Full details: Linked Issues check

Explanation

The changes satisfy issue #1827 by adding per-dependency flags to base and overflow records, capturing and replaying them, preserving explicit/creator OR-deduplication, and updating related tests and documentation.

Full details: Docstring Coverage

Explanation

Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 16 files. (2 skipped: 2 unsupported.)


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.

@zmnobug
zmnobug force-pushed the fix/issue-1827-dep-gen-explicit-flags branch from 4e485df to 09f9959 Compare August 28, 2026 02:45
- Capture per-dependency flags in base and overflow dep_gen records
- Replay explicit edges with runtime WAIT/RETAIN dedup semantics
- Preserve host ownership when a published DFX buffer hits a gate timeout
- Cover inline and chained records on a2a3 and a5 and update docs

Fixes hw-native-sys#1827
@zmnobug
zmnobug force-pushed the fix/issue-1827-dep-gen-explicit-flags branch from 09f9959 to a1ce5e6 Compare August 28, 2026 07:40
@zmnobug

zmnobug commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Addressed the published-buffer ownership risk in a1ce5e6: once the ready-queue tail advances, a push-gate timeout no longer reports enqueue failure, so callers cannot clear or reuse a buffer already owned by the host. Added a DeviceProfilerEngine regression test for this timeout path.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@zmnobug Thanks for the fix. The updated timeout result preserves host ownership after the ready-queue tail advances. The DeviceProfilerEngine regression test covers the required path.

🐇


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

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

Reviewed against merge-base 89ba2a3f. Verified the wire-layout arithmetic by hand, diffed the a2a3 and a5 trees against each other for parity, and traced each item back to #1827. Two blocking items, both small; no technical objection to the approach.

What this gets right

Not just "it works" — a few things here required actually understanding the subsystem rather than following the issue's Proposed Fix:

  • The layout arithmetic holds together. 4736 B, tensors at offset 640, and DEP_GEN_OVERFLOW_DEPS_PER_RECORD = 524 are mutually consistent (16 + 9N <= 4736), and 640 preserves the original intent that each 128 B tensor blob covers exactly two cache lines instead of straddling three. The static_assert set was updated in step rather than left to rot.
  • The new is_trivially_copyable_v / is_standard_layout_v guards close a pre-existing gap on these host/device wire structs — that is required by the codestyle rule for device-copied structs and was missing before this PR.
  • The OR-dedup handling is the substantive part. Once an explicit edge can be wait-only, emit_creator's !first early-return silently drops the creator's retain. Introducing explicit_edge_index to fold the creator flags back into the explicit edge keeps replay aligned with FaninBuilder's collapse semantics — and it is documented in docs/dfx/dep-gen.md rather than left as folklore. I checked the .at() on that map: it is reachable only from the first == false branch, and annot_preds is cleared per record with STEP 1 as its only writer, so it cannot throw today.
  • a2a3/a5 are strictly symmetric. I diffed the two subtrees' patches against each other; the only differences are block_num vs core_num and line offsets.
  • The tests are real. The scene-test cases are manual only on a2a3sim, so the new flag assertions genuinely execute in st-onboard-a2a3 and st-onboard-a5. Pinning the UT at kDepCount = 64 + 524 + 1 anchors the new constant, and the trailing creator+explicit probe covers the dedup path that the explicit_edge_index change exists for.

Blocking

1. docs/dfx/global-backpressure-design.md now contradicts the code.

The "Timeout backstop" section still states:

When it expires the writer breaks to its single failure exit (return false), and the caller (switch_buffer) accounts the dropped records.

After this change that is only true for the pop side (wait_for_ready_queue_space / wait_for_free_queue_entry). On the push side the timeout is now discarded and nothing is accounted as dropped. Per the repo's doc-consistency rule the fix lands in the same commit as the code change. Splitting the paragraph to distinguish the two sides is enough.

2. return -5 collides with an existing code in the same function.

dep_gen_replay.cpp already returns -5 for write_deps_json() failure (near the end of dep_gen_replay_emit_deps_json). The new invalid-flags path reuses it. Callers in all four device_runner.cpp sites only log dep_gen replay failed (%d), and the header documents codes as "see source", so -5 becomes genuinely ambiguous between "a record carried a corrupt flags byte" and "deps.json could not be written". -7 in both the a2a3 and a5 copies.

Non-blocking, but worth addressing

3. The DFX-engine fix deserves more than one bullet. The commit message does carry Preserve host ownership when a published DFX buffer hits a gate timeout, and the rationale is in this PR's conversation — so this is not an undeclared change. The gap is scope visibility: DeviceProfilerEngine is shared by pmu, scope_stats, l2 phase, l2 AicoreTask, dep_gen and args_dump, so this is a behavior change across six subsystems riding in a PR whose title and body are entirely about dep_gen. The PR body's Summary omits it, and once this squash-merges the conversation context does not follow into git history. A short paragraph in the body — what the bug was, that it affects every DFX module, and that it is independent of #1827 — costs little and is what a future bisect will actually find. Splitting it into its own PR would be cleaner still, since it is independently testable and independently valuable.

4. #1827's third item is dropped silently. The issue also asks to "make the differential gate explicit-kind-aware". Both oracle_preds and annot_preds are still seeded from the same kinds_data[i] in STEP 1, so an explicit-kind divergence remains undetectable by construction — the shared hard-coded constant was replaced by a shared recorded byte. I think this is genuinely infeasible in the current design: the oracle is compute_task_fanin, which does not process explicit deps at all, so there is no independent second source to compare against. That is a fine answer, but it should be stated, because Fixes #1827 will close the issue and take the item with it. Either say so in the body (and ideally in a comment next to STEP 1), or leave a follow-up issue.

5. Rejecting the whole trace over one bad byte is inconsistent with the surrounding code. The same function handles the same class of input — values read back from device shared memory that could be corrupt — by clamping and continuing:

// ...we clamp on read too so a corrupted record never drives an OOB read
if (base_dc > DEP_GEN_MAX_EXPLICIT_DEPS) { LOG_ERROR(...); base_dc = DEP_GEN_MAX_EXPLICIT_DEPS; }

The new raw_kind check instead tears down both tensormaps and discards the entire deps.json. Unknown bits cannot cause an OOB read, and this is a DFX artifact rather than a correctness path, so LOG_ERROR plus raw_kind &= kKnownDepFlags (or falling back to WAIT|RETAIN) would match the established posture and still surface the anomaly.

6. The push-gate timeout is now entirely unobservable. A 30 s expiry means "the host is gone" — previously it at least showed up as an inflated dropped count; now there is no counter, no log, no sticky flag. Worth an on_push_gate_timeout trait hook or a one-shot log (not per-iteration — the AICPU hot-path logging rule applies).

Consider

  • The scene test maps alternating kinds back to producers via sorted(barrier_explicit_edges.items()), i.e. it assumes TaskId::raw ordering equals submission order. True today for a single ring, but a change in id allocation would surface as a confusing "flags mismatch" rather than an obvious failure. Deriving the producer sequence from the order of deps["tasks"] would be sturdier.
  • No test covers the new rejection path. A host-side case feeding a record with explicit_dep_kinds[0] = 0xFF is cheap. (If item 5 is adopted, assert the clamped flags instead.)
  • reinterpret_cast<const uint8_t *>(args.explicit_dep_kinds_data()) relies on enum DepFlags : uint8_t. A static_assert(sizeof(DepFlags) == 1) next to the cast beats making the reader go look it up.
  • The WAIT|RETAIN default is now spelled in three places (Arg::explicit_dep_kind(), both orchestrator call sites, and the UT's bare 3u). A named constant would help; at minimum the UT could use DEP_WAIT | DEP_RETAIN instead of 3u.
  • The flags row in the deps.json schema table is marked always, but host_build_graph's writer (dep_gen_host_graph.cpp) emits no flags field at all. Pre-existing, not introduced here — but since that row is already being rewritten, noting "tensormap_and_ringbuffer only" is nearly free.

Notes

  • DepGenRecord's layout changed with no version field on the host/device boundary, and static_assert only guards one side at compile time. A partially-rebuilt tree (host runtime on 4672, AICPU on 4736) would produce a plausible-looking but garbage deps.json with nothing to detect it. Not a defect in this PR — the format never had a version — but worth a line in the body telling reviewers to do a full rebuild rather than an incremental one.
  • Capacity impact is real but bounded: overflow slots drop 582 -> 524 (-10%), so a submit with e.g. dc = 600 now needs 3 slots where 2 sufficed. The theoretical per-submit ceiling falls from ~595k to ~536k deps, which CHIP_DEP_LIST_POOL_SIZE (16384) puts far out of reach anyway. Record memory grows 1.37% and only when --enable-dep-gen is set, since DepGenCollector::init() is gated on it. The audit doc's added sentence is an accurate characterization.
  • The scene test now makes half the barrier's deps ordering-only, which is a real runtime semantic change and not just an assertion change. Safe here because the producers write the external ext_X rather than a runtime-owned output, so there is no lifetime to lose — worth keeping in mind if that kernel is ever reworked to use owned outputs.
  • pto_isa.pin is untouched and no pto-isa header references changed, so no pin bump is implied.

Items 1 and 2 are the only things holding this up; both are a few lines. The core change is well-built and I have no objection to the approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code Health] dep_gen: DepGenRecord carries no per-dep DepFlags, so explicit add_dep_wait edges are mis-recorded in deps.json

2 participants