Skip to content

Converge a5 host_build_graph onto the host-orchestrated runtime - #1661

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:a5-hbg-phase1-scaffold
Aug 4, 2026
Merged

Converge a5 host_build_graph onto the host-orchestrated runtime#1661
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:a5-hbg-phase1-scaffold

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

a5 host_build_graph was the original standalone graph builder — add_task / add_successor, fixed-size task tables, no TensorMap, no ring, no scopes — while a2a3 host_build_graph is the host-orchestrated variant of tensormap_and_ringbuffer. They shared a name and nothing else, so every improvement to the host-orchestration path stopped at a2a3.

a5 now runs the same tree.

The runtime differs from a2a3 in exactly four files

Most hardware differences are already isolated in src/a5/platform/. What remains:

File Difference
host/runtime_compile_info.cpp A host-orchestrated runtime dlopens the orchestration .so on the host, so its toolchain is the host g++ on every platform — no aarch64 cross-compile branch.
runtime/scheduler/scheduler_completion.cpp a5's PMU collector keys a record by the 32-bit register token AICore wrote into dual_issue_slots, so it takes pmu_aicpu_complete_record with that token alongside the PTO2 task id; a2a3 takes pmu_aicpu_record_task with the id alone.
runtime/backend/sdma/sdma_completion_scheduler.h a2a3 invalidates the cache line before each read of the SDMA-written SdmaEventRecord, and flushes after clearing the head, because rtMemcpy is not cache-coherent on a2a3. It is on a5, so those three calls are dead weight and a5's copy omits them.
runtime/backend/sdma/sdma_completion_kernel.h The pto-isa intrinsic header is pto/comm/async/sdma/. a2a3 still spells it pto/npu/comm/async/sdma/, a path that no longer exists — unnoticed because nothing includes this header today.

The SDMA engine itself is identical on both chips, so nothing else in that backend changes.

URMA exists only on a5 and is not carried over. It sits behind PTO_URMA_SUPPORTED, defined nowhere in this repo, so a5 host_build_graph registers COUNTER + SDMA completion ops where a5 tensormap_and_ringbuffer registers COUNTER + SDMA + URMA. Adding it would mean shipping code that cannot be exercised; it belongs with whatever change enables the URMA workspace overlay.

Why nothing else needed porting

The diff between the two tensormap_and_ringbuffer trees is 2257 lines, which looks like a large porting job. It isn't:

  • 57% is comment wording (1287 of 2257 lines).
  • The code differences are API refactors (get_ready_tasks_batch(queues, shape, …)(shape, …)), field renames (block_idxs_block_idx), and style cleanups — exactly the divergence issue [Code Health] Reconcile a2a3 vs a5 tensormap_and_ringbuffer runtime implementation divergence #1582 already tracks. None of it is an architecture requirement.
  • runtime/ carries no a2a3-vs-a5 conditional compilation, and the one arch-specific helper it references (compute_allowed_cpus) lives in src/a5/platform/onboard/host/aicpu_topology_probe.*.

So the tree was copied verbatim and compiled. The compiler reported exactly one error — the PMU difference. A three-way merge was tried first and rejected: it silently applied 1529 lines of the a5 tree's own refactors with no conflict marker, including deleting comment blocks the host-orchestration path still needs.

Compile-driven porting has a blind spot, though: it cannot see code behind a disabled CMake option. Both SDMA differences above sit in that blind spot (SIMPLER_ENABLE_PTO_SDMA_WORKSPACE is OFF on a5) and were found by inspection, not by the compiler.

AICore kernels are not architecture-neutral

The first push assumed they were, and a5 onboard caught two ways that is false. Both are invisible to a5sim, which compiles kernels with g++-15 rather than ccec:

  • Stride is ambiguous under a5's ccec/pto-isa and must be spelled pto::Stride. g++-15 accepts the bare form, so only a5 rejects it.
  • a5 synchronises AIV stages with set_flag/wait_flag. pipe_barrier(PIPE_V) is rejected outright — error: the range of 1st parameter must be [4, 6].

So paged_attention takes the a5 tensormap_and_ringbuffer kernels, which already carry the correct shape; dump_args, prepared_callable and the new vector_example take the a2a3 host_build_graph versions with Stride qualified. All nine kernels now compile under a5 ccec + pto-isa, verified locally by driving KernelCompiler(platform="a5") directly — the exact step CI failed on.

task_timing

tests/st/task_timing/task_timing_a5hbg/ existed only to cover the legacy add_task API on a5, and test_task_timing_e2e.py skipped a5 host_build_graph for the same reason. With the ABI unified, the dedicated test is removed and the skip dropped — the two host_build_graph e2e cases now run on a5 as well.

Testing

  • a5sim and a5 onboard both compile clean
  • All nine a5 scene-test kernels compile under a5 ccec + pto-isa (locally, via KernelCompiler(platform="a5"))
  • a5sim scene tests (tests/st/a5 + examples/a5 + task_timing) — 35 passed, of which host_build_graph 8
  • a2a3sim host_build_graph + task_timing — 17 passed / 4 skipped
  • cpput — 75 passed
  • pre-commit — all hooks pass

a5 onboard scene tests run in CI only; this development box is a2a3 silicon.

@coderabbitai

coderabbitai Bot commented Aug 3, 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: 736736ff-1657-4782-b65a-7eb6e606bd74

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

Adds the A5 host-build-graph Runtime2 implementation. It introduces shared runtime contracts, host orchestration, TensorMap dependency tracking, scheduler dispatch, asynchronous completion handling, AICPU coordination, AICore execution, profiling, and runtime documentation.

Changes

Host-build-graph Runtime2

Layer / File(s) Summary
Runtime contracts and data layouts
src/a5/runtime/host_build_graph2/runtime/*, src/a5/runtime/host_build_graph2/common/*
Adds task, tensor, shared-memory, dispatch-payload, submission, completion, and runtime-state contracts.
Host runtime and orchestration
src/a5/runtime/host_build_graph2/host/*, src/a5/runtime/host_build_graph2/orchestration/*, src/a5/runtime/host_build_graph2/runtime/orchestrator_core/*
Adds runtime loading, relocation, tensor staging, dependency capture, scopes, task submission, TensorMap tracking, and tensor access.
Scheduler dispatch and lifecycle
src/a5/runtime/host_build_graph2/runtime/scheduler/*
Adds ready queues, core tracking, normal and early dispatch, sync-start draining, completion processing, diagnostics, profiling, and shutdown.
Asynchronous completion
src/a5/runtime/host_build_graph2/runtime/aicore_completion_*, src/a5/runtime/host_build_graph2/runtime/pto_async_*, src/a5/runtime/host_build_graph2/runtime/backend/sdma/*
Adds deferred completion slabs, the AICore mailbox, async wait processing, and SDMA completion support.
AICPU and AICore execution
src/a5/runtime/host_build_graph2/aicpu/*, src/a5/runtime/host_build_graph2/aicore/*
Adds executor initialization, register handshakes, payload dispatch, kernel execution, acknowledgments, profiling, and cleanup.
Runtime documentation
src/a5/runtime/host_build_graph2/docs/*
Documents execution flow, scalar access, cluster submission, device profiling, and profiling levels.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Host
  participant AICPU
  participant Scheduler
  participant AICore
  participant Runtime
  Host->>Runtime: stage tensors and build runtime image
  Host->>AICPU: invoke aicpu_execute
  AICPU->>Scheduler: initialize shared runtime and classify tasks
  Scheduler->>AICore: publish dispatch payload
  AICore-->>Scheduler: acknowledge execution and completion
  Scheduler-->>AICPU: report runtime status
  AICPU-->>Host: return execution result
Loading

Possibly related issues

Possibly related PRs

Poem

A rabbit hops through queues of light,
With payloads packed and counters right.
AICore runs, AICPU steers,
Mailboxes hush completion fears.
The runtime blooms from host to chip—
Then bounds away with one neat hop.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.80% 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
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.
Title check ✅ Passed The title clearly identifies the main change: moving a5 host_build_graph toward the host-orchestrated runtime.
Description check ✅ Passed The description directly explains the runtime convergence, architecture differences, kernel updates, and validation results.

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.

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

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (13)
src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md-768-774 (1)

768-774: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Align the API summary with the canonical declarations.

This table omits PTO2Runtime* and num_args from rt_submit_task, rt_submit_aic_task, and rt_submit_aiv_task. SUBMIT_BY_CLUSTER.md documents those parameters.

If this table is shorthand, label it as shorthand. Otherwise, document the complete signatures to avoid inconsistent orchestration API usage.

🤖 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/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md` around lines 768 -
774, Update the API summary table to match the canonical declarations by
including PTO2Runtime* and num_args in rt_submit_task, rt_submit_aic_task, and
rt_submit_aiv_task. If the entries are intentionally abbreviated, explicitly
label the table as shorthand; otherwise, document each complete signature
consistently with SUBMIT_BY_CLUSTER.md.
src/a5/runtime/host_build_graph2/docs/profiling_levels.md-420-426 (1)

420-426: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify the profiling log-count units.

Level 0 says diagnostic and progress LOG_INFO messages can still appear, but the summary table reports LOG_INFO Count = 0. Levels 2 through 4 also omit counts even though earlier sections state counts of 18, 30, and 34.

Specify whether these are profiling-only counts, per-thread counts, or total per-run counts. The current table is not consistent with the preceding sections.

🤖 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/a5/runtime/host_build_graph2/docs/profiling_levels.md` around lines 420 -
426, Update the profiling-level summary table to define the LOG_INFO Count unit
explicitly, using the same profiling-only, total-per-run counting convention
established in the preceding sections. Add the correct counts for levels 0
through 4, including the existing 18, 30, and 34 values, and clarify that level
0 excludes unrelated diagnostic or progress messages.
src/a5/runtime/host_build_graph2/docs/SUBMIT_BY_CLUSTER.md-203-214 (1)

203-214: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Point validation commands at the A5 host_build_graph2 scaffold.

These commands run the a2a3 tensormap_and_ringbuffer test and use --platform a2a3. They do not validate src/a5/runtime/host_build_graph2, which the PR objective says remains undiscovered.

Mark these commands as main-branch baseline tests, or add A5-specific checks for discovery, build exclusion, and documentation consistency.

🤖 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/a5/runtime/host_build_graph2/docs/SUBMIT_BY_CLUSTER.md` around lines 203
- 214, Update the validation section in SUBMIT_BY_CLUSTER.md so the existing
a2a3 commands are clearly labeled as main-branch baseline tests, and add
A5-specific checks covering host_build_graph2 discovery, build exclusion, and
documentation consistency. Keep the current baseline commands unchanged while
ensuring the added validation explicitly targets the A5 host_build_graph2
scaffold.
src/a5/runtime/host_build_graph2/host/dep_gen_host_graph.cpp-176-195 (1)

176-195: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Clamp the stored ndims, not only the copy loop.

fill_consumer and fill_producer guard the copy loop with i < MAX_TENSOR_DIMS, but they store the unclamped t.ndims / entry.ndims. write_deps_json then uses that value as the array length at Line 348, Line 351, Line 355 and Line 358. If a source value ever exceeds MAX_TENSOR_DIMS, the writer reads past consumer_shape / producer_shape. The same pattern exists for slot.ndims at Line 419 and its use at Line 310 and Line 313.

Either clamp the stored value, or drop the loop guard because the invariant already holds.

🛡️ Proposed fix: clamp the stored dimension count
 void fill_consumer(EdgeAnnot &e, const Tensor &t) {
     e.consumer_dtype = static_cast<uint8_t>(t.dtype);
-    e.consumer_ndims = t.ndims;
+    e.consumer_ndims = t.ndims < MAX_TENSOR_DIMS ? t.ndims : MAX_TENSOR_DIMS;
     e.consumer_start_offset = t.start_offset;
-    for (uint32_t i = 0; i < t.ndims && i < MAX_TENSOR_DIMS; i++) {
+    for (uint32_t i = 0; i < e.consumer_ndims; i++) {
         e.consumer_shape[i] = t.shapes[i];
         e.consumer_strides[i] = t.strides[i];
     }
 }
@@
 void fill_producer(EdgeAnnot &e, const PTO2TensorMapEntry &entry) {
-    e.producer_ndims = entry.ndims;
+    e.producer_ndims = entry.ndims < MAX_TENSOR_DIMS ? entry.ndims : MAX_TENSOR_DIMS;
     e.producer_start_offset = entry.start_offset;
-    for (uint32_t i = 0; i < entry.ndims && i < MAX_TENSOR_DIMS; i++) {
+    for (uint32_t i = 0; i < e.producer_ndims; i++) {
         e.producer_shape[i] = entry.shapes[i];
         e.producer_strides[i] = entry.strides[i];
     }
 }
🤖 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/a5/runtime/host_build_graph2/host/dep_gen_host_graph.cpp` around lines
176 - 195, Clamp the stored dimension counts in fill_consumer and fill_producer
to MAX_TENSOR_DIMS before write_deps_json uses them for array lengths, while
preserving the bounded copy loops. Apply the same clamping to slot.ndims at its
construction site so the values consumed by the slot serialization paths cannot
exceed the backing array capacity.
src/a5/runtime/host_build_graph2/host/runtime_maker.cpp-607-633 (1)

607-633: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unlink the temp .so on the dlopen and dlsym failure paths.

create_orch_so_tempfile writes a file under /tmp. The code unlinks it only at Line 633, after both symbol lookups succeed. If dlopen fails at Line 613, or either dlsym fails, the file stays on disk for the process lifetime. Repeated failed prepares accumulate files in /tmp.

♻️ Proposed fix: unlink on every exit path
         void *handle = dlopen(so_path.c_str(), RTLD_NOW | RTLD_LOCAL);
         if (handle == nullptr) {
             LOG_ERROR("host-orch: dlopen failed: %s", dlerror());
+            unlink(so_path.c_str());
             return -1;
         }
+        // Safe to unlink now: the handle keeps the .so mapped regardless of path.
+        unlink(so_path.c_str());
         void *entry = dlsym(handle, orch_func_name);
         if (entry == nullptr) {
             LOG_ERROR("host-orch: dlsym('%s') failed: %s", orch_func_name, dlerror());
             dlclose(handle);
             return -1;
         }
@@
         if (bind_sym == nullptr) {
             LOG_ERROR("host-orch: orch .so does not export framework_bind_runtime: %s", dlerror());
             dlclose(handle);
             return -1;
         }
-        // Safe to unlink now: the handle keeps the .so mapped regardless of path.
-        unlink(so_path.c_str());
🤖 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/a5/runtime/host_build_graph2/host/runtime_maker.cpp` around lines 607 -
633, Ensure the temporary orchestration .so created by create_orch_so_tempfile
is unlinked before every failure return in the dlopen, orch_func_name dlsym, and
framework_bind_runtime dlsym paths. Preserve the existing successful-path unlink
and close handles as currently required.
src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp-76-82 (1)

76-82: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a single last_task_alive value in print_stats().

ring_sched_state is not an array; it has one last_task_alive and one ring member. This loop prints the same shared value multiple times with different Ring %d: labels. Replace it with one check/print, or model per-ring state if the scheduler supports multiple rings.

🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp` around
lines 76 - 82, Update print_stats() to stop iterating over PTO2_MAX_RING_DEPTH
for the shared ring_sched_state.last_task_alive value; perform one check and
print the value once with an appropriate non-per-ring label, preserving the
existing statistics output.
src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_completion.cpp-104-105 (1)

104-105: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

mailbox is computed with a null guard, then dereferenced without one.

Line 104 tolerates rt_ == nullptr and leaves mailbox null. Lines 139 and 165 call mailbox->try_push_condition and mailbox->try_push_normal_done unconditionally. If rt_ is ever null on this path, the AICPU faults. Either drop the conditional at Line 104 and assert rt_ != nullptr, or latch PTO2_ERROR_ASYNC_REGISTRATION_FAILED when mailbox is null before the push loops.

Also applies to: 137-143, 162-168

🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_completion.cpp`
around lines 104 - 105, Update the scheduler completion path around the mailbox
initialization and push loops to handle a null mailbox consistently: either
assert rt_ is non-null before deriving AICoreCompletionMailbox, or detect a null
mailbox and latch PTO2_ERROR_ASYNC_REGISTRATION_FAILED before reaching the
try_push_condition and try_push_normal_done calls. Ensure neither push loop can
dereference mailbox when it is null.
src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp-976-1029 (1)

976-1029: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

deinit() does not release the sp_queues_ buffers.

CompletedTaskQueue::init allocates buf with new[], and on_orchestration_done calls destroy() only for t < active_sched_threads_ of the run that is starting. deinit() resets every other owned field but never frees these buffers. Two consequences:

  • The buffers stay allocated after the final teardown.
  • If a later run uses fewer scheduler threads, the buffers of the higher indices are never freed and never reused.

Destroy all MAX_AICPU_THREADS queues here.

🛡️ Proposed fix
     regs_ = 0;
     sched_ = nullptr;
     rt_ = nullptr;
     func_id_to_addr_ = nullptr;
+
+    // Release the per-S completed-task rings allocated in on_orchestration_done.
+    for (int32_t t = 0; t < MAX_AICPU_THREADS; t++) {
+        sp_queues_[t].destroy();
+    }
 }
🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp`
around lines 976 - 1029, Update SchedulerContext::deinit() to destroy every
queue in sp_queues_, iterating across all MAX_AICPU_THREADS entries rather than
only active_sched_threads_. Reuse each CompletedTaskQueue’s destroy() operation
so all buffers allocated by init() are released, including queues from prior
runs with higher thread counts.
src/a5/runtime/host_build_graph2/runtime/pto_types.h-285-301 (1)

285-301: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

clear() does not reset launch_spec.

clear() resets every other per-submit field: explicit_deps_, explicit_dep_count_, allow_early_resolve_, task_timing_slot_, and predicate_. It leaves launch_spec untouched. An Arg that is reused after clear() or reset() keeps the previous block_num and require_sync_start, so the next submit launches with the wrong SPMD shape. L2TaskArgs::create_from_chip_args calls reset() and depends on a clean state.

If the persistence is intentional, state the reason in a comment.

🐛 Proposed fix
         allow_early_resolve_ = false;
         task_timing_slot_ = TASK_TIMING_SLOT_NONE;
         predicate_ = L0TaskPredicate{};
+        launch_spec = PTO2LaunchSpec{};
     }
🤖 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/a5/runtime/host_build_graph2/runtime/pto_types.h` around lines 285 - 301,
Update Arg::clear() to reset launch_spec along with the other per-submit fields,
ensuring reused arguments restore default block_num and require_sync_start
values before the next submission. Keep reset()’s existing delegation to clear()
so L2TaskArgs::create_from_chip_args receives clean state.
src/a5/runtime/host_build_graph2/runtime/tensor_create_info.h-91-92 (1)

91-92: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

The defaulted default constructor leaves every field indeterminate.

The class documentation states that start_offset is pre-zeroed and is_contiguous is pre-set to true, and init_tensor_from_create_info relies on both. TensorCreateInfo() = default; gives none of those guarantees. A default-constructed instance that reaches init_tensor_from_create_info copies 64 bytes of indeterminate data into Tensor cache line 1, which sets a garbage version, is_contiguous, manual_dep, and start_offset. The always_assert(ci.ndims > 0 && ci.ndims <= MAX_TENSOR_DIMS) catches only part of that.

Either delete the default constructor, or value-initialize the members that the contract promises.

🛡️ Proposed change
-    TensorCreateInfo() = default;
+    // No default state satisfies the line-1 contract (start_offset == 0,
+    // is_contiguous == true), so require the shapes constructor.
+    TensorCreateInfo() = delete;
🤖 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/a5/runtime/host_build_graph2/runtime/tensor_create_info.h` around lines
91 - 92, Update TensorCreateInfo’s default construction so the documented
defaults are guaranteed: initialize start_offset to zero and is_contiguous to
true, while preserving valid initialization for all other fields consumed by
init_tensor_from_create_info. Replace the defaulted constructor or explicitly
value-initialize the relevant members in TensorCreateInfo.
src/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h-94-96 (1)

94-96: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Clarify event.handle == 0 and keep the fast path safe.

This path does not register a completion on handle == 0. The URMA/simulation tests call WaitAll() after synchronous async ops with handle=0, but this wrapper only handles that at the invalid AsyncCtx guard. Add a short comment if handle == 0 is an expected synchronous/result-null case after TGET_ASYNC()/TPUT_ASYNC(), or defer PTO2_ERROR_ASYNC_COMPLETION_INVALID when handle == 0 is unexpected.

🤖 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/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h`
around lines 94 - 96, Clarify the handle-zero branch in the completion wrapper
around the existing event.handle check: document that handle == 0 is the
expected synchronous/result-null case after TGET_ASYNC()/TPUT_ASYNC(), while
preserving the safe early return. If handle == 0 is not valid, replace the
silent return with deferred PTO2_ERROR_ASYNC_COMPLETION_INVALID handling
instead.
src/a5/runtime/host_build_graph2/runtime/pto_submit_types.h-57-75 (1)

57-75: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard the sign-extension shift and correct the size comment.

Two points in DispatchPredicate:

  1. If op != PredicateOp::NONE and elem_size == 0, then bits == 0 and shift == 64. A shift of a 64-bit value by 64 is undefined behavior. elem_size is populated at submit, so a partly filled predicate reaches this path. Treat bits == 0 as "no predicate" or assert the valid widths (1/2/4/8).
  2. The comment states 18 bytes. The struct holds 18 bytes of members, but sizeof(DispatchPredicate) is 24 because of trailing padding to the 8-byte alignment. State both numbers so payload-layout readers do not mis-size the field.
🛡️ Proposed guard
     bool pass() const {
         if (op == PredicateOp::NONE) return true;
+        if (elem_size == 0 || elem_size > sizeof(int64_t)) return true;
         int64_t v = 0;
         __builtin_memcpy(&v, reinterpret_cast<const void *>(addr), elem_size);
         uint32_t bits = static_cast<uint32_t>(elem_size) * 8u;
🤖 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/a5/runtime/host_build_graph2/runtime/pto_submit_types.h` around lines 57
- 75, Update DispatchPredicate::pass to handle elem_size == 0 before computing
or applying the sign-extension shift, treating it as no predicate or enforcing
the valid widths 1, 2, 4, and 8; ensure no 64-bit shift occurs. Correct the
DispatchPredicate layout comment to state that its members total 18 bytes while
sizeof(DispatchPredicate) is 24 bytes due to trailing alignment padding.
src/a5/runtime/host_build_graph2/runtime/pto_shared_memory.h-117-140 (1)

117-140: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include <algorithm> for std::max.

<atomic> does not define std::max, and this header does not receive <algorithm> transitively. Add the include so the header remains self-contained.

🔧 Proposed fix
 `#pragma` once
 
+#include <algorithm>
 `#include` <stddef.h>
 
 `#include` "utils/device_arena.h"
 `#include` "pto_runtime2_types.h"
🤖 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/a5/runtime/host_build_graph2/runtime/pto_shared_memory.h` around lines
117 - 140, Add the <algorithm> header include to pto_shared_memory.h so std::max
used in update_completed_watermark is defined independently of transitive
includes.
🧹 Nitpick comments (15)
src/a5/runtime/host_build_graph2/orchestration/common.cpp (1)

56-69: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Quote the executable path in the addr2line command.

addr_to_line interpolates dl_info.dli_fname straight into a shell command string and runs it with popen. A module path that contains a space, a quote, or a shell metacharacter produces a wrong command or an unintended shell action. Wrap the path in single quotes so the shell treats it as one literal token.

Also note snprintf truncates at 512 bytes. A long module path then yields a malformed command. Truncation only degrades the diagnostic, so it is not a correctness risk.

♻️ Proposed fix: quote the path
-    snprintf(cmd, sizeof(cmd), "addr2line -e %s -f -C -p -i %p 2>/dev/null", executable, addr);
+    snprintf(cmd, sizeof(cmd), "addr2line -e '%s' -f -C -p -i %p 2>/dev/null", executable, addr);
🤖 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/a5/runtime/host_build_graph2/orchestration/common.cpp` around lines 56 -
69, Update addr_to_line to shell-quote the executable path when constructing the
addr2line command, ensuring paths containing spaces or shell metacharacters
remain a single literal argument. Preserve the existing command behavior and
accept the current snprintf truncation handling.
src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp (1)

94-98: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Tie shape_names[] size to PTO2_NUM_RESOURCE_SHAPES with a static_assert.

shape_names[] is a fixed 3-element array, and the loop indexes it up to PTO2_NUM_RESOURCE_SHAPES. If that constant grows without updating this array, the loop reads out of bounds. This codebase already uses static_asserts elsewhere (for example in pto2_dispatch_payload.h) to guard exactly this kind of drift.

🛡️ Proposed fix
     const char *shape_names[] = {"AIC", "AIV", "MIX"};
+    static_assert(sizeof(shape_names) / sizeof(shape_names[0]) == PTO2_NUM_RESOURCE_SHAPES,
+                  "shape_names must match PTO2_NUM_RESOURCE_SHAPES");
🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp` around
lines 94 - 98, Add a compile-time static_assert next to shape_names in the
scheduler logging code to verify its element count equals
PTO2_NUM_RESOURCE_SHAPES, while preserving the existing loop and logging
behavior.
src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_dispatch.cpp (1)

900-906: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated scheduler-timeout resolution.

run_resolution_thread and resolve_and_dispatch compute scheduler_timeout_cycles from get_scheduler_timeout_ms() with identical code. Extract one small private helper so a future change to the override semantics stays in one place. This is not a request for a new configuration knob; the existing override stays as is.

Also applies to: 1161-1170

🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_dispatch.cpp`
around lines 900 - 906, The scheduler timeout-cycle calculation is duplicated in
run_resolution_thread and resolve_and_dispatch. Extract the shared
default-and-override resolution into one small private helper, then have both
methods call it while preserving SCHEDULER_TIMEOUT_CYCLES and the existing
get_scheduler_timeout_ms() override semantics.

Source: Learnings

src/a5/runtime/host_build_graph2/aicpu/aicpu_executor.cpp (1)

184-188: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Add a spin hint to the init barriers.

These three spin loops poll shared atomics with no back-off. Every other spin in this cohort uses SPIN_WAIT_HINT() (see run() at Lines 314-330). Add the hint so the waiting AICPU threads do not saturate the interconnect during the parallel handshake.

Also applies to: 196-196, 206-210

🤖 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/a5/runtime/host_build_graph2/aicpu/aicpu_executor.cpp` around lines 184 -
188, Add SPIN_WAIT_HINT() inside each init barrier polling loop, including the
loops around hs_setup_done_ and the additional ranges at the referenced
locations. Keep the existing atomic checks and return behavior unchanged,
placing the hint on each iteration that continues waiting.
src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp (1)

723-726: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix the error text and the stated limit.

The message says "more then" and names 64 cores. The real limit is CoreTracker::MAX_CORE_PER_THREAD, which is MAX_CLUSTERS * PLATFORM_CORES_PER_BLOCKDIM. Report the computed and allowed values so the failure is actionable.

♻️ Proposed fix
-        LOG_ERROR("Can't assign more then 64 cores in per scheduler");
+        LOG_ERROR(
+            "Cannot assign %d cores per scheduler thread (max=%d)", thread_cores_num,
+            CoreTracker::MAX_CORE_PER_THREAD
+        );
🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp`
around lines 723 - 726, Update the validation error in the scheduler
core-assignment path to use “than” and report both the requested
thread_cores_num value and the allowed CoreTracker::MAX_CORE_PER_THREAD value.
Remove the hardcoded 64-core wording while preserving the existing failure
return.
src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.h (1)

161-199: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

The profiling push overload does not write task_id_snapshot.

push_tagged sets slot->task_id_snapshot, and pop_tagged reads it. This profiling overload writes only slot_state and sequence, so the slot keeps the snapshot of a previous occupant. Any pop_tagged consumer (for example early_dispatch_shape) then compares against a stale id. Set the field to 0 here to match the non-profiling push.

♻️ Proposed fix
         slot->slot_state = slot_state;
+        slot->task_id_snapshot = 0;
         slot->sequence.store(static_cast<int64_t>(pos + 1), std::memory_order_release);
🤖 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/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.h` around
lines 161 - 199, Update the profiling push overload in PTO2ReadyQueueSlot::push
to assign slot->task_id_snapshot to 0 when populating the slot, alongside
slot->slot_state and before publishing sequence. Preserve the existing profiling
counters and queue synchronization logic.
src/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h (1)

121-143: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Align the return value with the documented contract.

The comment states that the function records the error and returns false on failure. register_pto_async_event can call defer_error(ctx, PTO2_ERROR_ASYNC_COMPLETION_INVALID) for a non-SDMA engine or a failed PrepareEventCheck, but send_request_entry still returns true. A caller that branches on the return value treats a failed completion registration as success. Return the registration result, or restate the contract in the comment.

♻️ Proposed change to propagate the registration result
-template <typename PtoAsyncEvent, typename PtoAsyncSession>
-inline __aicore__ void
-register_pto_async_event(AsyncCtx &ctx, const PtoAsyncEvent &event, const PtoAsyncSession &session) {
+template <typename PtoAsyncEvent, typename PtoAsyncSession>
+inline __aicore__ bool
+register_pto_async_event(AsyncCtx &ctx, const PtoAsyncEvent &event, const PtoAsyncSession &session) {
     if (ctx.task_token.is_invalid() || ctx.completion_count == nullptr || ctx.completion_entries == nullptr) {
         (void)event.Wait(session);
-        return;
+        return true;
     }
     if (event.handle == 0) {
-        return;
+        return true;
     }
 
     const uint32_t engine = static_cast<uint32_t>(event.engine);
     if (engine != static_cast<uint32_t>(::pto::comm::DmaEngine::SDMA)) {
         defer_error(ctx, PTO2_ERROR_ASYNC_COMPLETION_INVALID);
-        return;
+        return false;
     }
-    pto2::detail::register_pto_async_event(ctx, event, session);
+    const bool registered = pto2::detail::register_pto_async_event(ctx, event, session);
     pto2::detail::defer_flush(ctx);
-    return true;
+    return registered;
🤖 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/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h`
around lines 121 - 143, Update send_request_entry to propagate the
success/failure result from register_pto_async_event, ensuring registration
failures return false in accordance with the documented contract while
preserving the existing deferred flush behavior.
src/a5/runtime/host_build_graph2/common/intrinsic.h (1)

98-106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider binding the context indices to their derivation.

The comment states that the values derive from MAX_TENSOR_ARGS(32) + MAX_SCALAR_ARGS(16), but the constants are hardcoded. If either limit changes, the kernel-side accessors read the wrong args slots and fail silently. A static_assert in a translation unit that already includes the args-limit header would pin the relation.

🤖 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/a5/runtime/host_build_graph2/common/intrinsic.h` around lines 98 - 106,
Bind SPMD_LOCAL_CONTEXT_INDEX and SPMD_GLOBAL_CONTEXT_INDEX to the
MAX_TENSOR_ARGS and MAX_SCALAR_ARGS derivation instead of hardcoding 48 and 49.
Add a static_assert in an appropriate translation unit that includes the
args-limit definitions, verifying both context indices remain immediately after
the combined argument limits.
src/a5/runtime/host_build_graph2/runtime/pto_types.h (1)

443-453: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider rejecting a null values pointer.

add_scalars guards count < 0 and the capacity, then calls memcpy. A null values with count > 0 dereferences null inside memcpy. The sibling API set_dependencies already rejects a null pointer with set_error. The same guard keeps the two entry points consistent.

🤖 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/a5/runtime/host_build_graph2/runtime/pto_types.h` around lines 443 - 453,
Update add_scalars to reject a null values pointer when count is greater than
zero, alongside its existing count and capacity validation, by calling set_error
and returning before memcpy; preserve the existing behavior for zero-count calls
and valid inputs.
src/a5/runtime/host_build_graph2/runtime/pto_tensormap.h (1)

183-198: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the TensorCreateInfo layout mirror.

copy_tensor_create_info does a raw 64-byte memcpy from TensorCreateInfo into cache line 1 of the entry. The equivalent Tensor mirror is protected by the static_assert block at lines 337-345, but no assert covers TensorCreateInfo. If a field in TensorCreateInfo moves, this copy silently writes the wrong start_offset, ndims, dtype, or shapes[], and overlap detection produces wrong dependencies. Add matching static_asserts for the fields this memcpy relies on.

🤖 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/a5/runtime/host_build_graph2/runtime/pto_tensormap.h` around lines 183 -
198, Add static_assert layout checks for TensorCreateInfo alongside the existing
Tensor mirror assertions, covering the offsets of start_offset, ndims, dtype,
and shapes[] plus the expected 64-byte copied layout. Keep
copy_tensor_create_info’s raw memcpy unchanged and anchor the checks to the
TensorCreateInfo and Tensor mirror definitions.
src/a5/runtime/host_build_graph2/runtime/shared/pto_runtime2_init.cpp (2)

261-265: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the seen-epoch size from one source.

Line 262 sizes the memset from layout.tensor_map.task_window_size, but line 251 does the SM address arithmetic with the task_window_size parameter. reserve_layout reserved the region from its own task_window_size argument. The three values agree only because the current call path passes the same number everywhere. If they diverge, this memset clears past the reserved region. Use the layout-recorded value for both, or store the reserved byte count in PTO2OrchestratorLayout.

🤖 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/a5/runtime/host_build_graph2/runtime/shared/pto_runtime2_init.cpp` around
lines 261 - 265, Update the seen-epoch initialization around
PTO2OrchestratorLayout so the memset size and SM address arithmetic both derive
from the same layout-recorded task-window value used when reserving the region.
Replace the independent task_window_size parameter usage in this path, or reuse
a stored reserved byte count, ensuring memset never exceeds the reserved
fanin_seen_epoch region.

347-388: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Fix the stale prebuilt_layout doc comment.

src/a5/runtime/host_build_graph2/host/runtime_maker.cpp:880 assigns rt->prebuilt_layout = layout; runtime_init_data_from_layout and runtime_wire_arena_pointers only populate it indirectly as rt is the image target. Update the pto_runtime2.h comment so it describes the writer correctly instead of implying those two functions write it.

🤖 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/a5/runtime/host_build_graph2/runtime/shared/pto_runtime2_init.cpp` around
lines 347 - 388, Update the prebuilt_layout documentation in pto_runtime2.h to
identify runtime_maker.cpp’s assignment to rt->prebuilt_layout as the writer.
Remove the implication that runtime_init_data_from_layout or
runtime_wire_arena_pointers populate this field indirectly, while preserving the
rest of the field’s documented behavior.
src/a5/runtime/host_build_graph2/runtime/shared/pto_shared_memory.cpp (1)

164-171: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse ring_segment_offsets instead of walking the layout by hand.

Lines 164-171 recompute the segment walk manually. Only task_descriptors_offset is consumed; the three offset += statements at lines 169-171 produce a value that nothing reads. The walk also omits the completion_flags segment that ring_segment_offsets includes. The header comment in pto_shared_memory.h (lines 315-320) declares ring_segment_offsets the single source of truth for this layout, so this second walk can drift from it. Read the offset from the helper.

♻️ Proposed refactor
-    // Ring layout info
-    uint64_t offset = PTO2_ALIGN_UP(sizeof(PTO2SharedMemoryHeader), PTO2_ALIGN_SIZE);
     header->ring.task_window_size = task_window_sizes[0];
     header->ring.task_window_mask = static_cast<int32_t>(task_window_sizes[0] - 1);
     header->ring.heap_size = heap_sizes[0];
-    header->ring.task_descriptors_offset = offset;
-    offset += PTO2_ALIGN_UP(task_window_sizes[0] * sizeof(PTO2TaskDescriptor), PTO2_ALIGN_SIZE);
-    offset += PTO2_ALIGN_UP(task_window_sizes[0] * sizeof(PTO2TaskPayload), PTO2_ALIGN_SIZE);
-    offset += PTO2_ALIGN_UP(task_window_sizes[0] * sizeof(PTO2TaskSlotState), PTO2_ALIGN_SIZE);
+    header->ring.task_descriptors_offset =
+        pto2_sm_layout::ring_segment_offsets(task_window_sizes[0]).descriptors;
🤖 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/a5/runtime/host_build_graph2/runtime/shared/pto_shared_memory.cpp` around
lines 164 - 171, Update the shared-memory header initialization to obtain
task_descriptors_offset from the existing ring_segment_offsets layout helper
instead of manually advancing offset. Remove the unused offset increments for
task descriptors, payloads, and slot state, while preserving the initial aligned
offset only if required by ring_segment_offsets. Use the established helper as
the single source of truth for the ring layout.
src/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h (1)

25-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include guards are copied from a2a3 and do not identify the a5 tree. The scaffold reuses the a2a3 guard macros unchanged. Two variants appear: guards that name an a2a3 path, and guards with unqualified names. Both make an a5 header and its a2a3 twin mutually exclusive in one translation unit, so the first one included wins and the second expands to nothing. The comment in pto_runtime2_types.h (lines 37-44) records that this cross-tree collision already happened in the shared host-dispatcher translation unit. The PR states path references are retained for later cleanup, so track these together in that cleanup.

  • src/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h#L25-L26: rename the guard from SRC_A2A3_RUNTIME_TENSORMAP_AND_RINGBUFFER_RUNTIME_PTO_RUNTIME2_TYPES_H_ to the a5 host_build_graph2 path, and update the matching #endif comment on line 552.
  • src/a5/runtime/host_build_graph2/runtime/pto_orchestrator.h#L28-L29: replace PTO_ORCHESTRATOR_H with a path-qualified guard, and update the #endif comment on line 186.
  • src/a5/runtime/host_build_graph2/runtime/pto_ring_buffer.h#L34-L35: replace PTO_RING_BUFFER_H with a path-qualified guard, and update the #endif comment on line 500.
  • src/a5/runtime/host_build_graph2/runtime/host_tensor_access.h#L50-L51: rename the guard from SRC_A2A3_RUNTIME_HOST_BUILD_GRAPH_RUNTIME_HOST_TENSOR_ACCESS_H_ to the a5 path, and update the #endif comment on line 91.
  • src/a5/runtime/host_build_graph2/runtime/pto_constants.h#L12-L13: rename the guard from SRC_A2A3_RUNTIME_TENSORMAP_AND_RINGBUFFER_RUNTIME_PTO_CONSTANTS_H_ to the a5 path, and update the #endif comment on line 19.
🤖 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/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h` around lines
25 - 26, Rename the include guards and matching `#endif` comments in
src/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h (lines 25-26 and
552), pto_orchestrator.h (28-29 and 186), pto_ring_buffer.h (34-35 and 500),
host_tensor_access.h (50-51 and 91), and pto_constants.h (12-13 and 19) to
unique path-qualified a5 host_build_graph2 identifiers, replacing the copied
a2a3 or unqualified guards while keeping each guard’s opening and closing names
consistent.
src/a5/runtime/host_build_graph2/runtime/shared/runtime.cpp (1)

97-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include <cstring> for std::strncpy and memset.

runtime.cpp uses memset in the constructor and std::strncpy in set_device_orch_func_name()/set_device_orch_config_name(), but it does not include <cstring> or <string.h>. Add the header after #include "runtime.h" so this translation unit does not rely on transitive includes.

Proposed fix
 `#include` "runtime.h"
 
+#include <cstring>
+
 `#include` "common/unified_log.h"
🤖 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/a5/runtime/host_build_graph2/runtime/shared/runtime.cpp` around lines 97
- 113, Add the <cstring> header immediately after the runtime.h include in
runtime.cpp so memset in the constructor and std::strncpy used by
set_device_orch_func_name() and set_device_orch_config_name() are declared
directly by this translation unit.
🤖 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/a5/runtime/host_build_graph2/aicore/aicore_executor.cpp`:
- Around line 192-206: Bound argument population in the tensor/scalar fill loops
near the existing SPMD context-index comment using a compile-time-derived
capacity equal to the space available before the context slots in
exec_payload->args. Ensure the combined tensor_count and scalar_count writes
never exceed that capacity, stopping or otherwise preventing further writes
while preserving the existing tensor-before-scalar ordering and barrier
behavior.

In `@src/a5/runtime/host_build_graph2/aicpu/aicpu_executor.cpp`:
- Around line 453-464: Make the cleanup decision exclusive in the flow around
AicpuExecutor::run and the finished_ check: return or otherwise claim a single
“was last thread” result from run(), then call deinit() only for that thread. Do
not let every thread independently observe finished_ and invoke deinit(),
preserving the existing cleanup behavior and shared-state reset.

In `@src/a5/runtime/host_build_graph2/docs/profiling_levels.md`:
- Around line 384-398: Update the compile-time command examples in “Setting
Profiling Macros” to use the documented SIMPLER_DFX, SIMPLER_ORCH_PROFILING, and
SIMPLER_TENSORMAP_PROFILING macros instead of the undefined PTO2_* names, while
preserving the examples’ intended profiling configurations.

In `@src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md`:
- Around line 199-211: Revise the lifecycle guidance in RUNTIME_LOGIC.md so
reclaiming-ring behavior is explicitly scoped to tensormap_and_ringbuffer-only
and not presented as host_build_graph behavior. Update the sections covering
allocation, slot recycling, heap/TensorMap reclamation, scope-reference
deadlocks, CONSUMED transitions, and last_task_alive advancement, including the
referenced lines, to consistently describe host_build_graph as
whole-graph-resident with no slot recycling or runtime watermark advancement.
- Around line 139-149: Update the shared-memory layout documentation around
PTO2SharedMemoryHeader and pto2_sm_layout::ring_segment_offsets to represent the
single-ring contract used by host_build_graph, including one ring and its
task-window regions. Ensure the accompanying size formula and diagram
consistently describe one whole-graph-resident ring, or explicitly label the
presentation as generic multi-ring behavior where applicable.
- Around line 731-739: Runtime2 documentation incorrectly attributes
device-orchestrated SO loading and timing behavior to host_build_graph. In
src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md lines 731-739, move the
AICPU dlopen and orchestration-entry flow into a tensormap_and_ringbuffer-only
section; in src/a5/runtime/host_build_graph2/docs/device_log_profiling.md lines
19-35, mark Block 1 as tensormap_and_ringbuffer-only and add host-side timing
for host_build_graph; in
src/a5/runtime/host_build_graph2/docs/profiling_levels.md lines 175-207, remove
device-orchestrator claims from host_build_graph profiling levels or label the
entire section device-orchestrated-only.

In `@src/a5/runtime/host_build_graph2/docs/SCALAR_DATA_ACCESS.md`:
- Around line 3-7: Update the get_tensor_data/set_tensor_data documentation and
examples to scope them to tensormap_and_ringbuffer runtimes that can execute
during orchestration, or explicitly document a separate host execution boundary
that makes blocking waits valid. Ensure the overview no longer implies
host_build_graph can obtain results from device tasks before graph construction
completes.
- Around line 38-45: Align the host_build_graph protocol documentation across
all three sites: in src/a5/runtime/host_build_graph2/docs/SCALAR_DATA_ACCESS.md
lines 38-45, replace the fanout_refcount consumer wait with the inline
host-build consumer-wait contract; in
src/a5/runtime/host_build_graph2/docs/SUBMIT_BY_CLUSTER.md lines 163-170, mark
dep_pool and fanout requirements as future or tensormap_and_ringbuffer-only; and
in src/a5/runtime/host_build_graph2/docs/device_log_profiling.md lines 119-124,
describe completion and readiness using completion_flags, wake lists, and
completed_watermark.

In
`@src/a5/runtime/host_build_graph2/runtime/orchestrator_core/pto_orchestrator.cpp`:
- Around line 430-433: Validate block_num in submit_task() before computing
total_required_subtasks, enforcing a maximum that keeps block_num multiplied by
the active core count within int16_t; reject invalid launch specifications
rather than allowing overflow. Preserve the existing positive-value assertion
and ensure logical_block_num and total_required_subtasks cannot represent
wrapped task counts.

In `@src/a5/runtime/host_build_graph2/runtime/orchestrator_core/pto_runtime2.cpp`:
- Around line 147-204: Guard slot.task before dereferencing it in both
wait_one_producer and wait_one_consumers. Resolve local_id defensively to a safe
diagnostic value when slot.task is null, while preserving the existing wait and
timeout behavior and avoiding null dereferences on entry.

In `@src/a5/runtime/host_build_graph2/runtime/pto_async_kernel_api.h`:
- Around line 76-83: Update defer_flush to derive the flushed byte range from
the end of the completion_entries slab rather than summing optional field sizes.
When completion_entries is present, compute the range from ctx.completion_count
through count DeferredCompletionEntry elements using the entries pointer;
preserve flushing the counter-only range when no entries exist and avoid relying
on the assumed counter/error-code/entries layout.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_completion.cpp`:
- Around line 659-665: Guard the pending-task load in the staging completion
path before dereferencing it: if drain_state_.pending_task.load(...) returns
nullptr, return immediately. Apply this around the logic using slot_state,
including the gated decision, stage_sync_start_cores, and finalize block, while
preserving the existing behavior for non-null task slots.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_dispatch.cpp`:
- Around line 394-408: Replace the 64-bit shift construction with
CoreTracker::BitStates::bit(cluster_offset) in both MIX cluster scan sites:
scheduler_dispatch.cpp lines 394-408 when setting selected_mix_clusters and
lines 745-754 when setting bucket. Use the existing 128-bit-aware helper for
both updates.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_types.h`:
- Around line 512-519: Initialize CoreTracker::cluster_count_ with a safe
default member value so a default-constructed tracker has a valid core count.
Update the cluster_count_ declaration in CoreTracker, preserving init()’s
behavior for explicitly assigned trackers and ensuring the uninitialized
P-thread tracker remains safe when core_num() and core_ids() are read.

---

Minor comments:
In `@src/a5/runtime/host_build_graph2/docs/profiling_levels.md`:
- Around line 420-426: Update the profiling-level summary table to define the
LOG_INFO Count unit explicitly, using the same profiling-only, total-per-run
counting convention established in the preceding sections. Add the correct
counts for levels 0 through 4, including the existing 18, 30, and 34 values, and
clarify that level 0 excludes unrelated diagnostic or progress messages.

In `@src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md`:
- Around line 768-774: Update the API summary table to match the canonical
declarations by including PTO2Runtime* and num_args in rt_submit_task,
rt_submit_aic_task, and rt_submit_aiv_task. If the entries are intentionally
abbreviated, explicitly label the table as shorthand; otherwise, document each
complete signature consistently with SUBMIT_BY_CLUSTER.md.

In `@src/a5/runtime/host_build_graph2/docs/SUBMIT_BY_CLUSTER.md`:
- Around line 203-214: Update the validation section in SUBMIT_BY_CLUSTER.md so
the existing a2a3 commands are clearly labeled as main-branch baseline tests,
and add A5-specific checks covering host_build_graph2 discovery, build
exclusion, and documentation consistency. Keep the current baseline commands
unchanged while ensuring the added validation explicitly targets the A5
host_build_graph2 scaffold.

In `@src/a5/runtime/host_build_graph2/host/dep_gen_host_graph.cpp`:
- Around line 176-195: Clamp the stored dimension counts in fill_consumer and
fill_producer to MAX_TENSOR_DIMS before write_deps_json uses them for array
lengths, while preserving the bounded copy loops. Apply the same clamping to
slot.ndims at its construction site so the values consumed by the slot
serialization paths cannot exceed the backing array capacity.

In `@src/a5/runtime/host_build_graph2/host/runtime_maker.cpp`:
- Around line 607-633: Ensure the temporary orchestration .so created by
create_orch_so_tempfile is unlinked before every failure return in the dlopen,
orch_func_name dlsym, and framework_bind_runtime dlsym paths. Preserve the
existing successful-path unlink and close handles as currently required.

In
`@src/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h`:
- Around line 94-96: Clarify the handle-zero branch in the completion wrapper
around the existing event.handle check: document that handle == 0 is the
expected synchronous/result-null case after TGET_ASYNC()/TPUT_ASYNC(), while
preserving the safe early return. If handle == 0 is not valid, replace the
silent return with deferred PTO2_ERROR_ASYNC_COMPLETION_INVALID handling
instead.

In `@src/a5/runtime/host_build_graph2/runtime/pto_shared_memory.h`:
- Around line 117-140: Add the <algorithm> header include to pto_shared_memory.h
so std::max used in update_completed_watermark is defined independently of
transitive includes.

In `@src/a5/runtime/host_build_graph2/runtime/pto_submit_types.h`:
- Around line 57-75: Update DispatchPredicate::pass to handle elem_size == 0
before computing or applying the sign-extension shift, treating it as no
predicate or enforcing the valid widths 1, 2, 4, and 8; ensure no 64-bit shift
occurs. Correct the DispatchPredicate layout comment to state that its members
total 18 bytes while sizeof(DispatchPredicate) is 24 bytes due to trailing
alignment padding.

In `@src/a5/runtime/host_build_graph2/runtime/pto_types.h`:
- Around line 285-301: Update Arg::clear() to reset launch_spec along with the
other per-submit fields, ensuring reused arguments restore default block_num and
require_sync_start values before the next submission. Keep reset()’s existing
delegation to clear() so L2TaskArgs::create_from_chip_args receives clean state.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp`:
- Around line 76-82: Update print_stats() to stop iterating over
PTO2_MAX_RING_DEPTH for the shared ring_sched_state.last_task_alive value;
perform one check and print the value once with an appropriate non-per-ring
label, preserving the existing statistics output.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp`:
- Around line 976-1029: Update SchedulerContext::deinit() to destroy every queue
in sp_queues_, iterating across all MAX_AICPU_THREADS entries rather than only
active_sched_threads_. Reuse each CompletedTaskQueue’s destroy() operation so
all buffers allocated by init() are released, including queues from prior runs
with higher thread counts.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_completion.cpp`:
- Around line 104-105: Update the scheduler completion path around the mailbox
initialization and push loops to handle a null mailbox consistently: either
assert rt_ is non-null before deriving AICoreCompletionMailbox, or detect a null
mailbox and latch PTO2_ERROR_ASYNC_REGISTRATION_FAILED before reaching the
try_push_condition and try_push_normal_done calls. Ensure neither push loop can
dereference mailbox when it is null.

In `@src/a5/runtime/host_build_graph2/runtime/tensor_create_info.h`:
- Around line 91-92: Update TensorCreateInfo’s default construction so the
documented defaults are guaranteed: initialize start_offset to zero and
is_contiguous to true, while preserving valid initialization for all other
fields consumed by init_tensor_from_create_info. Replace the defaulted
constructor or explicitly value-initialize the relevant members in
TensorCreateInfo.

---

Nitpick comments:
In `@src/a5/runtime/host_build_graph2/aicpu/aicpu_executor.cpp`:
- Around line 184-188: Add SPIN_WAIT_HINT() inside each init barrier polling
loop, including the loops around hs_setup_done_ and the additional ranges at the
referenced locations. Keep the existing atomic checks and return behavior
unchanged, placing the hint on each iteration that continues waiting.

In `@src/a5/runtime/host_build_graph2/common/intrinsic.h`:
- Around line 98-106: Bind SPMD_LOCAL_CONTEXT_INDEX and
SPMD_GLOBAL_CONTEXT_INDEX to the MAX_TENSOR_ARGS and MAX_SCALAR_ARGS derivation
instead of hardcoding 48 and 49. Add a static_assert in an appropriate
translation unit that includes the args-limit definitions, verifying both
context indices remain immediately after the combined argument limits.

In `@src/a5/runtime/host_build_graph2/orchestration/common.cpp`:
- Around line 56-69: Update addr_to_line to shell-quote the executable path when
constructing the addr2line command, ensuring paths containing spaces or shell
metacharacters remain a single literal argument. Preserve the existing command
behavior and accept the current snprintf truncation handling.

In
`@src/a5/runtime/host_build_graph2/runtime/backend/sdma/sdma_completion_kernel.h`:
- Around line 121-143: Update send_request_entry to propagate the
success/failure result from register_pto_async_event, ensuring registration
failures return false in accordance with the documented contract while
preserving the existing deferred flush behavior.

In `@src/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h`:
- Around line 25-26: Rename the include guards and matching `#endif` comments in
src/a5/runtime/host_build_graph2/runtime/pto_runtime2_types.h (lines 25-26 and
552), pto_orchestrator.h (28-29 and 186), pto_ring_buffer.h (34-35 and 500),
host_tensor_access.h (50-51 and 91), and pto_constants.h (12-13 and 19) to
unique path-qualified a5 host_build_graph2 identifiers, replacing the copied
a2a3 or unqualified guards while keeping each guard’s opening and closing names
consistent.

In `@src/a5/runtime/host_build_graph2/runtime/pto_tensormap.h`:
- Around line 183-198: Add static_assert layout checks for TensorCreateInfo
alongside the existing Tensor mirror assertions, covering the offsets of
start_offset, ndims, dtype, and shapes[] plus the expected 64-byte copied
layout. Keep copy_tensor_create_info’s raw memcpy unchanged and anchor the
checks to the TensorCreateInfo and Tensor mirror definitions.

In `@src/a5/runtime/host_build_graph2/runtime/pto_types.h`:
- Around line 443-453: Update add_scalars to reject a null values pointer when
count is greater than zero, alongside its existing count and capacity
validation, by calling set_error and returning before memcpy; preserve the
existing behavior for zero-count calls and valid inputs.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.cpp`:
- Around line 94-98: Add a compile-time static_assert next to shape_names in the
scheduler logging code to verify its element count equals
PTO2_NUM_RESOURCE_SHAPES, while preserving the existing loop and logging
behavior.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/pto_scheduler.h`:
- Around line 161-199: Update the profiling push overload in
PTO2ReadyQueueSlot::push to assign slot->task_id_snapshot to 0 when populating
the slot, alongside slot->slot_state and before publishing sequence. Preserve
the existing profiling counters and queue synchronization logic.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_cold_path.cpp`:
- Around line 723-726: Update the validation error in the scheduler
core-assignment path to use “than” and report both the requested
thread_cores_num value and the allowed CoreTracker::MAX_CORE_PER_THREAD value.
Remove the hardcoded 64-core wording while preserving the existing failure
return.

In `@src/a5/runtime/host_build_graph2/runtime/scheduler/scheduler_dispatch.cpp`:
- Around line 900-906: The scheduler timeout-cycle calculation is duplicated in
run_resolution_thread and resolve_and_dispatch. Extract the shared
default-and-override resolution into one small private helper, then have both
methods call it while preserving SCHEDULER_TIMEOUT_CYCLES and the existing
get_scheduler_timeout_ms() override semantics.

In `@src/a5/runtime/host_build_graph2/runtime/shared/pto_runtime2_init.cpp`:
- Around line 261-265: Update the seen-epoch initialization around
PTO2OrchestratorLayout so the memset size and SM address arithmetic both derive
from the same layout-recorded task-window value used when reserving the region.
Replace the independent task_window_size parameter usage in this path, or reuse
a stored reserved byte count, ensuring memset never exceeds the reserved
fanin_seen_epoch region.
- Around line 347-388: Update the prebuilt_layout documentation in
pto_runtime2.h to identify runtime_maker.cpp’s assignment to rt->prebuilt_layout
as the writer. Remove the implication that runtime_init_data_from_layout or
runtime_wire_arena_pointers populate this field indirectly, while preserving the
rest of the field’s documented behavior.

In `@src/a5/runtime/host_build_graph2/runtime/shared/pto_shared_memory.cpp`:
- Around line 164-171: Update the shared-memory header initialization to obtain
task_descriptors_offset from the existing ring_segment_offsets layout helper
instead of manually advancing offset. Remove the unused offset increments for
task descriptors, payloads, and slot state, while preserving the initial aligned
offset only if required by ring_segment_offsets. Use the established helper as
the single source of truth for the ring layout.

In `@src/a5/runtime/host_build_graph2/runtime/shared/runtime.cpp`:
- Around line 97-113: Add the <cstring> header immediately after the runtime.h
include in runtime.cpp so memset in the constructor and std::strncpy used by
set_device_orch_func_name() and set_device_orch_config_name() are declared
directly by this translation unit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

Comment thread src/a5/runtime/host_build_graph2/aicore/aicore_executor.cpp Outdated
Comment thread src/a5/runtime/host_build_graph2/aicpu/aicpu_executor.cpp Outdated
Comment thread src/a5/runtime/host_build_graph/docs/profiling_levels.md
Comment thread src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md Outdated
Comment thread src/a5/runtime/host_build_graph2/docs/RUNTIME_LOGIC.md Outdated
Comment thread src/a5/runtime/host_build_graph/runtime/pto_async_kernel_api.h
Comment thread src/a5/runtime/host_build_graph/runtime/scheduler/scheduler_types.h
@ChaoWao
ChaoWao force-pushed the a5-hbg-phase1-scaffold branch from ebb3edf to 70564dd Compare August 3, 2026 11:51
@ChaoWao ChaoWao changed the title Scaffold the a5 host-orchestrated runtime tree Converge a5 host_build_graph onto the host-orchestrated runtime Aug 3, 2026
@ChaoWao

ChaoWao commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks @coderabbitai — 14 inline findings plus the 13 minor ones in the review summary, all answered inline and resolved.

Every one of them lands on code or docs this PR copies verbatim from src/a2a3/runtime/host_build_graph/. I checked each cited file with diff against the a2a3 tree: all return 0, so none of the findings is introduced here. The one file that does differ (scheduler_completion.cpp, 9 lines for a5's PMU record API) is not where the slot_state finding points.

They are deferred, not skipped, and will be fixed on both trees in a follow-up so the two stay in lockstep — patching only the a5 copy would fork them immediately, which is exactly what this PR exists to prevent.

One correction worth recording: the scheduler_dispatch.cpp shift finding is real and Critical, and it is worse than the comment states. BitStates::Storage is unsigned __int128 because a cluster occupies 3 bits, so the span is 24x3=72 bits on a2a3 and 36x3=108 bits on a5 — both overflow a 1ULL shift, and BitStates::bit(offset) already exists for exactly this. That one goes first in the follow-up.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Tip

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

@ChaoWao Thanks for the detailed verification.

The parity requirement makes deferral appropriate for this scaffold PR. A local fix in src/a5/runtime/host_build_graph2/ would break the intended lockstep with src/a2a3/runtime/host_build_graph/.

I also confirm the correction to the Critical finding. 1ULL << offset is invalid for offsets at or above 64, while BitStates::bit(offset) uses the unsigned __int128 storage type. The follow-up should update both trees.

Would you like me to create a GitHub issue to track the shared follow-up?

🐇✅


✏️ Learnings added
Learnt from: ChaoWao
URL: https://github.com/hw-native-sys/simpler/pull/1661

Timestamp: 2026-08-03T12:02:18.435Z
Learning: For the paired runtime trees `src/a2a3/runtime/host_build_graph/` and `src/a5/runtime/host_build_graph2/`, preserve source parity during the a5 migration. Fix shared defects in both trees in the same follow-up rather than patching only the a5 scaffold. In `scheduler_dispatch.cpp`, `BitStates::Storage` is `unsigned __int128`; use `BitStates::bit(offset)` instead of `1ULL << offset` because cluster offsets can exceed 63 (72 bits on a2a3 and 108 bits on a5).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@ChaoWao
ChaoWao force-pushed the a5-hbg-phase1-scaffold branch 4 times, most recently from c6c0c80 to 24e734a Compare August 3, 2026 13:45
a5 host_build_graph was the original standalone graph builder — add_task /
add_successor, fixed-size task tables, no TensorMap, no ring, no scopes — while
a2a3 host_build_graph is the host-orchestrated variant of
tensormap_and_ringbuffer. The two shared a name and nothing else, so every
improvement to the host-orchestration path stopped at a2a3.

a5 now runs the same tree. The runtime is a byte-for-byte copy of a2a3
host_build_graph except for six files, because most hardware differences are
already isolated in src/a5/platform/:

- host/runtime_compile_info.cpp — a host-orchestrated runtime dlopens the
  orchestration .so on the host, so its toolchain is the host g++ on every
  platform, with no aarch64 cross-compile branch.
- runtime/scheduler/scheduler_completion.cpp — a5's PMU collector keys a record
  by the 32-bit register token AICore wrote into dual_issue_slots, so it takes
  pmu_aicpu_complete_record with that token alongside the PTO2 task id, where
  a2a3 takes pmu_aicpu_record_task with the id alone.
- runtime/backend/sdma/sdma_completion_scheduler.h — a2a3 invalidates the cache
  line before each read of the SDMA-written SdmaEventRecord and flushes it after
  clearing the head, because rtMemcpy is not cache-coherent there. a5's is, so
  those three calls are dead weight and its copy omits them.
- runtime/backend/sdma/sdma_completion_kernel.h — the pto-isa intrinsic header
  is pto/comm/async/sdma/, which is where it lives; a2a3 still spells it
  pto/npu/comm/async/sdma/, a path that no longer exists. Nothing includes this
  header today, which is why that has gone unnoticed.
- runtime/runtime.h and runtime/scheduler/scheduler_context.h —
  RUNTIME_MAX_WORKER is 108 (36 AIC + 72 AIV) rather than a2a3's 72 (24 + 48).
  It sizes Handshake workers[], so the a2a3 value made prepare_launch_shape
  reject every run on a5 with "block_dim (36) exceeds RUNTIME_MAX_WORKER (72)".
  Both values compile, so only execution on a5 silicon exposes this.

The SDMA engine itself is identical on both, so nothing else in that backend
changes. URMA exists only on a5 and is not carried over: it sits behind
PTO_URMA_SUPPORTED, which is defined nowhere in this repo, so a5
host_build_graph registers COUNTER + SDMA completion ops where a5
tensormap_and_ringbuffer registers COUNTER + SDMA + URMA.

Nothing else needed porting. The 2257-line diff between the two
tensormap_and_ringbuffer trees is 57% comment wording and, in the rest, API
refactors, field renames and style cleanups that issue hw-native-sys#1582 already tracks as
divergence to reconcile — none of it is an architecture requirement. Rather than
review that diff, the tree was copied verbatim and compiled: the compiler named
the single real difference.

The scene tests move from the old orchestration ABI (add_task / add_successor /
record_tensor_pair / orchestration_api.h) to submit_task / Tensor /
pto_orchestration_api.h. dump_args, prepared_callable and the new vector_example
take the a2a3 host_build_graph versions; paged_attention takes the a5
tensormap_and_ringbuffer one, because AICore kernels are not architecture-neutral
in two ways the a2a3 sources trip over:

- `Stride` is ambiguous under a5's ccec/pto-isa and must be spelled
  `pto::Stride`, which g++-15 accepts either way — so a5sim compiles the bare
  form and only a5 rejects it.
- a5 synchronises AIV stages with set_flag/wait_flag; pipe_barrier(PIPE_V) is
  rejected outright ("the range of 1st parameter must be [4, 6]"), and the a5
  paged_attention kernels already carry the correct shape.

task_timing_a5hbg existed only to cover the legacy add_task API on a5 and is
removed; test_task_timing_e2e.py drops the skip that excluded a5
host_build_graph and now runs its two host_build_graph cases on a5 too.

paged_attention's batch=256 Case1 is marked manual, matching the a2a3
host_build_graph case of the same size: host orchestration populates the whole
graph before the device schedules, so the ring cannot reclaim mid-orchestration
and 16384 slots do not hold ~64K tasks. The a5 tensormap_and_ringbuffer case it
came from runs unmarked because a device-side orchestrator reclaims as it goes.

Verified: a5sim and a5 onboard both compile clean; all nine a5 scene-test
kernels compile under a5 ccec + pto-isa locally (the step CI failed on); a5sim
scene tests 35 passed, host_build_graph 8; a2a3sim host_build_graph 17 passed /
4 skipped with task_timing green; cpput 75 passed; pre-commit clean. a5 onboard
scene tests are CI-only — this box is a2a3 silicon.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChaoWao
ChaoWao merged commit 26b67fd into hw-native-sys:main Aug 4, 2026
18 checks passed
@ChaoWao
ChaoWao deleted the a5-hbg-phase1-scaffold branch August 4, 2026 00:51
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.

1 participant