Refactor: confine host_build_graph orchestration to the host - #2094
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (21)
💤 Files with no reviewable changes (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change moves ChangesHost orchestration runtime split
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: 🔵 Low · up to The refactor removes host orchestration from the device image and clears its callback pointers, but a host-only tensor_access address is still copied to the device. No current device use is identified, so the risk is bounded and mergeable with explicit owner awareness or follow-up to clear that field as well. Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 28.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 17 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. Comment |
9054d50 to
a881547
Compare
host_build_graph builds its graph on the host: the orchestration .so that drives it is dlopened by the host runtime, and the AICPU only ever runs schedulers. The sources implementing that orchestration nonetheless sat under shared/, so the aicpu target compiled all of them -- the ops table, the whole submit path, TensorMap, and Graph recording -- 1.6 MB of a 4.8 MB device image no device code can reach. Nothing under aicpu/, runtime/ or device/ names OrchestratorState or ChipTensorMap, and the AICPU .so gains no undefined symbol on any of the four variants once they are gone, which is what pins that claim. runtime_core.cpp, orchestrator.cpp and tensormap.cpp move under host/, which the build config already excludes from the aicpu target, so no build file changes. OrchestratorState::init goes with them: it calls ChipTensorMap::init_default, and left in shared/runtime_init.cpp it would have given the AICPU an unresolved reference -- which a shared library reports at load time rather than at link time. The AICPU no longer rebinds the ops table at boot, and the host clears rt->ops alongside rt->orchestrator before the copied zone is uploaded, so the device never receives a host address in that field. With one target compiling them, the weak markers those files carried select nothing; they only hid which definition wins behind link order, where nothing checks it, and a missing sink linked cleanly and silently did nothing. get_sys_cnt_aicpu and get_reg_ptr become ordinary definitions in host/aicpu_shims.cpp -- on the host neither is a fallback, since host orchestration needs a real monotonic clock and route_ready_once ODR-uses the doorbell inline -- and the clock's three copies collapse into one. The dep_gen and host_phase no-ops move to tests/ut/cpp/stubs/, carried by HBG_ORCH_SHARED_SOURCES, which the one test that wants the real capture does not use; that split is exclusive, so those need no weak marker either. RuntimeOps was written out three times, once per arch's orchestration_api.h and once in runtime_core.h, and the copies had to agree field for field since the .so calls through the table by offset. runtime_ops.h carries the definition and both sides include it.
…lities hw-native-sys#2094 moved host_build_graph's orchestration to the host. Two device-side dependencies survived that move, both of them reaching for something the host cannot actually use. The orchestrator's per-sub-step profiling called get_sys_cnt_aicpu, a device cycle counter. On the host that resolved to a stand-in reading CLOCK_MONOTONIC and scaling it by PLATFORM_PROF_SYS_CNT_FREQ, so the orchestrator turned nanoseconds into notional device cycles, accumulated those, and the bind logged them as `cycles=` -- neither a real cycle count nor a readable duration, and a dependency on a device frequency constant for timing that never leaves the host. The accumulators now read clock_gettime through a file-local inline and carry nanoseconds: OrchProfilingData's five *_cycle fields and fanin_wait_cycle become *_ns, the macros are ORCH_STEP_START/LAP, and the log line reads `ns=`. That was the host's only use of get_sys_cnt_aicpu, so host/aicpu_shims.cpp goes away. Its other definition, get_reg_ptr, had no caller at all: disassembling the host .so under the default, ORCH_PROFILING and SCHED_PROFILING configurations finds zero call sites in every one, and the comment explaining it named a route_ready_once path that exists nowhere in the tree. OrchestratorState carried SchedulerState *scheduler, pointing at a struct that lives in device memory the host never maps -- residue from when both ran on the AICPU in one address space and the orchestrator could enqueue into the scheduler's queues directly. Its own comment said "For simulated mode only", and nothing read it: submit_task copied it into a local whose only use was a (void) cast, and two call sites asserted it non-null without dereferencing. The member, the init() parameter that filled it, and both assertions are gone. That was orchestrator.h's only use of SchedulerState, so the header no longer includes scheduler/scheduler.h -- 1336 lines of device dispatch logic the host compiled to reach a pointer it never followed. Eight unit tests that had been reaching SchedulerState and SchedulerLayout through that chain now include it directly. The AICPU side is untouched: libaicpu_kernel.so carries its own get_sys_cnt_aicpu from platform/.../device_time.cpp -- 195 call sites under SCHED_PROFILING, zero undefined references -- and never compiled the deleted file. Built in all four profiling configurations, since a path that exists only under a conditional is invisible to a single build.
…lities (#2098) #2094 moved host_build_graph's orchestration to the host. Two device-side dependencies survived that move, both of them reaching for something the host cannot actually use. The orchestrator's per-sub-step profiling called get_sys_cnt_aicpu, a device cycle counter. On the host that resolved to a stand-in reading CLOCK_MONOTONIC and scaling it by PLATFORM_PROF_SYS_CNT_FREQ, so the orchestrator turned nanoseconds into notional device cycles, accumulated those, and the bind logged them as `cycles=` -- neither a real cycle count nor a readable duration, and a dependency on a device frequency constant for timing that never leaves the host. The accumulators now read clock_gettime through a file-local inline and carry nanoseconds: OrchProfilingData's five *_cycle fields and fanin_wait_cycle become *_ns, the macros are ORCH_STEP_START/LAP, and the log line reads `ns=`. That was the host's only use of get_sys_cnt_aicpu, so host/aicpu_shims.cpp goes away. Its other definition, get_reg_ptr, had no caller at all: disassembling the host .so under the default, ORCH_PROFILING and SCHED_PROFILING configurations finds zero call sites in every one, and the comment explaining it named a route_ready_once path that exists nowhere in the tree. OrchestratorState carried SchedulerState *scheduler, pointing at a struct that lives in device memory the host never maps -- residue from when both ran on the AICPU in one address space and the orchestrator could enqueue into the scheduler's queues directly. Its own comment said "For simulated mode only", and nothing read it: submit_task copied it into a local whose only use was a (void) cast, and two call sites asserted it non-null without dereferencing. The member, the init() parameter that filled it, and both assertions are gone. That was orchestrator.h's only use of SchedulerState, so the header no longer includes scheduler/scheduler.h -- 1336 lines of device dispatch logic the host compiled to reach a pointer it never followed. Eight unit tests that had been reaching SchedulerState and SchedulerLayout through that chain now include it directly. The AICPU side is untouched: libaicpu_kernel.so carries its own get_sys_cnt_aicpu from platform/.../device_time.cpp -- 195 call sites under SCHED_PROFILING, zero undefined references -- and never compiled the deleted file. Built in all four profiling configurations, since a path that exists only under a conditional is invisible to a single build.
What
host_build_graphbuilds its graph on the host — the orchestration.sois dlopened by the host runtime, and the AICPU only ever runs schedulers. But the sources implementing that orchestration sat undershared/, so the aicpu target compiled all of them: the ops table, the whole submit path, TensorMap, and Graph recording.The AICPU device image drops from 4.8 MB to 3.1 MB — 1.6 MB (33%) of code no device code could reach.
Why it is safe
Nothing under
aicpu/,runtime/ordevice/namesOrchestratorStateorChipTensorMap, and thegraph_*free functions they define reach no device caller.The load-bearing check is on symbols, not on the build succeeding: a shared library links cleanly with unresolved symbols and only fails at
dlopen. So the undefined-symbol table of each AICPU.sowas captured before the move and compared after — zero new undefined symbols on all four variants (a2a3/a5 × sim/onboard).The three pieces
1. The ops table is host-only. The AICPU no longer rebinds it at boot, and the host now clears
rt->opsalongsidert->orchestratorbefore the copied zone is uploaded, so the device never receives a host address in that field.2.
runtime_core.cpp,orchestrator.cpp,tensormap.cppmove underhost/— which the build config already excludes from the aicpu target, so no build file changes.OrchestratorState::initgoes with them: it callsChipTensorMap::init_default, and left inshared/runtime_init.cppit would have handed the AICPU an unresolved reference.3. Weak markers become link seams. With one target compiling these files,
weakselects nothing — it only hid which definition wins behind link order, where nothing checks it, and a missing sink linked cleanly and silently did nothing.get_sys_cnt_aicpuandget_reg_ptrbecome ordinary definitions inhost/aicpu_shims.cpp(on the host neither is a fallback: host orchestration needs a real monotonic clock, androute_ready_onceODR-uses the doorbell inline), collapsing the clock's three copies into one. Thedep_gen/host_phaseno-ops move totests/ut/cpp/stubs/, carried byHBG_ORCH_SHARED_SOURCES— which the one test wanting the real capture does not use, so that split is exclusive and needs no weak marker either.Removing the weak markers immediately surfaced 6
undefined references at UT link time. Underweakthose same gaps would have been silent no-ops:host_phase_recordrecording nothing,dep_gencapturing nothing, tests still green.Also
RuntimeOpswas written out three times — once per arch'sorchestration_api.h, once inruntime_core.h— and the copies had to agree field for field, since the.socalls through the table by offset. A field added to one copy alone shifts every later entry and dispatches to the wrong function, with nothing to catch it at compile time.runtime_ops.hnow carries the one definition both sides include.Two comments in
graph_recorder_poolexplaining why that TU cannot includeruntime_core.hnamedRuntimeOpsas the type existing twice. The conflict isRuntimeContext— partial inorchestration_api.h, full inruntime_core.h— and it still stands; the comments now name it.What did NOT change
The ops table itself stays.
ChipWorkerdlopens each runtimeRTLD_LOCALso two runtimes' identically-named exports (simpler_init,simpler_run, …) cannot collide anddlclosecan actually unload one (#453). A runtime's symbols therefore never reach the global symbol table, and the orchestration.sohas no way to bind them by name. The table is not a legacy layer — it is how that boundary is crossed.Test
Build: 8 targets, zero warnings
Symbols: 0 new undefined on all four AICPU
.sos; both host shims confirmed absent from the host.so's dynamic symbol table (hiddenintact)ctest: 128/128Scene tests: a2a3sim 10 passed, a5sim 7 passed
pre-commit: clean
a2a3 onboard (real hardware): 35 passed / 1 skipped (host_build_graph) + 2 passed (tensormap_and_ringbuffer),
task-submitexit=0. No507018/507014/507899, and the run's own device log carries noFATAL, deadlock orHandleTaskTimeoutsignature.a5 hardware is covered by CI (
st-onboard-a5), not locally — this box is a2a3.