Skip to content

Refactor axcl examples. - #2867

Merged
csukuangfj merged 17 commits into
k2-fsa:masterfrom
csukuangfj:refactor-axcl
Dec 7, 2025
Merged

csukuangfj merged 17 commits into
k2-fsa:masterfrom
csukuangfj:refactor-axcl

Conversation

@csukuangfj

@csukuangfj csukuangfj commented Dec 5, 2025 •

Copy link
Copy Markdown
Collaborator

@Abandon-ht

see also #2849

Summary by CodeRabbit

  • New Features

    • Added a high-level AXCL model API to load, run, and inspect models (file or memory) with tensor I/O and execution helpers.
  • Refactor

    • Replaced legacy AX runner with the new model abstraction; introduced RAII managers/guards and a device-backed pointer for safer lifecycle and reduced header dependencies.
    • Removed the old AX runner interface.
  • Bug Fixes

    • Improved init/finalize and allocation error handling for more robust startup, execution, and cleanup.

✏️ Tip: You can customize this high-level summary in your review settings.

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Dec 5, 2025
@coderabbitai

coderabbitai Bot commented Dec 5, 2025 •

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces the legacy AXCL runner with a new AXCL integration: adds AxclModel, AxclManager, RAII guards (engine, IO, IOInfo), device pointer utilities, CMake entries, and updates offline-sense-voice model to use AxclModel; removes old ax_model_runner_axcl and related headers/impls; tightens move semantics in ascend utils.

Changes

Cohort / File(s) Summary
Build configuration
\sherpa-onnx/csrc/CMakeLists.txt``
Adds new AXCL source files to the sherpa-onnx-core build under SHERPA_ONNX_ENABLE_AXCL.
AXCL public model API
\sherpa-onnx/csrc/axcl/axcl-model.h`, `sherpa-onnx/csrc/axcl/axcl-model.cc``
Adds sherpa_onnx::AxclModel (file/buffer constructors, tensor metadata, Set/Get tensor data, Run, IsInitialized) with Pimpl and full implementation.
Lifecycle & guards
\sherpa-onnx/csrc/axcl/axcl-manager.h`, `sherpa-onnx/csrc/axcl/axcl-manager.cc`, `sherpa-onnx/csrc/axcl/axcl-engine-guard.`, `sherpa-onnx/csrc/axcl/axcl-engine-io-guard.`, `sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.*`
Adds AxclManager (thread-safe ref-counted axclInit/Finalize) and three RAII guard classes for engine, IO, and IOInfo with non-copyable/movable semantics and init/fini logic.
Device memory utility
\sherpa-onnx/csrc/axcl/utils.h`, `sherpa-onnx/csrc/axcl/utils.cc``
Adds AxclDevicePtr RAII wrapper for axclrtMalloc/Free with move semantics, Release, and accessors.
Offline model integration
\sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.h`, `sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc``
Replaces internal ax_runner_axcl usage with std::unique_ptr<AxclModel> model_; adds PostInit() and switches I/O and Run logic to AxclModel API; removes direct AXCL includes from header.
Removed legacy AX runner
\sherpa-onnx/csrc/axcl/ax_model_runner.hpp`, `sherpa-onnx/csrc/axcl/ax_model_runner_axcl.*`, `sherpa-onnx/csrc/axcl/ax_model_runner_axcl.hpp``
Deletes the old ax_runner_base interface and ax_runner_axcl implementation and headers.
Ascend utils signature tightening
\sherpa-onnx/csrc/ascend/utils.h``
Deletes move constructors/assignments for multiple ACL-related classes and changes copy-assignment return types from const to non-const reference (signatures only).

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Mgr as AxclManager
    participant Model as AxclModel
    participant EngGuard as AxclEngineGuard
    participant IOInfo as AxclEngineIOInfoGuard
    participant IOGuard as AxclEngineIOGuard
    participant AXCL as AXCL Runtime

    App->>Mgr: AxclManager(config)
    activate Mgr
    Mgr->>AXCL: axclInit(config)
    AXCL-->>Mgr: OK
    deactivate Mgr

    App->>Model: AxclModel(filename, device_id)
    activate Model
    Model->>EngGuard: AxclEngineGuard(npuKind)
    EngGuard->>AXCL: axclrtEngineInit()
    AXCL-->>EngGuard: OK

    Model->>IOInfo: AxclEngineIOInfoGuard(model_id)
    IOInfo->>AXCL: axclrtEngineGetIOInfo(model_id)
    AXCL-->>IOInfo: io_info

    Model->>IOGuard: AxclEngineIOGuard(io_info)
    IOGuard->>AXCL: axclrtEngineCreateIO(io_info)
    AXCL-->>IOGuard: io_handle

    Note over Model: prepare tensors and device buffers

    App->>Model: SetInputTensorData(name,data)
    Model->>AXCL: copy to device

    App->>Model: Run()
    Model->>AXCL: axclrtEngineRun(io_handle)
    AXCL-->>Model: inference done

    App->>Model: GetOutputTensorData(name)
    Model->>AXCL: copy from device
    deactivate Model

    App->>Mgr: ~AxclManager()
    activate Mgr
    Mgr->>AXCL: axclFinalize()  (when last instance)
    AXCL-->>Mgr: OK
    deactivate Mgr
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files/areas to focus review on:
    • axcl-model.cc / axcl-model.h: device memory lifetimes, tensor binding, error handling, and Pimpl safety.
    • axcl-manager.*: correctness of reference counting and concurrency (mutex/atomic) semantics.
    • RAII guards (engine/io/io-info): init/fini ordering and exit-on-error behavior.
    • offline-sense-voice-model-axcl.*: behavioral parity with prior runner (tensor shapes, frame counts).
    • ascend/utils.h: signature changes deleting move operations — ensure no callers rely on move semantics.
    • CMakeLists.txt: verify new sources are conditionally included and build grouping is correct.

Possibly related PRs

Poem

🐰 I tunneled through old runner code today,
Guarded engines chased the bugs away,
Managers count, devices snug and neat,
AxclModel hops in, inputs snug and sweet,
A carrot-shaped refactor—clean and fleet! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Refactor axcl examples' is vague and generic, using non-descriptive language that doesn't convey the actual scope or nature of the changes. Consider a more specific title that reflects the primary refactoring work, such as 'Replace ax_runner_axcl with new AxclModel abstraction' or 'Refactor AXCL model runner with guard-based resource management'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @csukuangfj, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the AXCL (Ascend Computing Language) integration within the sherpa-onnx project. It moves away from a single, monolithic model runner to a more structured and object-oriented approach, introducing dedicated classes for managing AXCL engine lifecycle, I/O, and model execution. This change aims to enhance code maintainability, improve resource management, and provide a more robust framework for future AXCL-based developments, particularly impacting the OfflineSenseVoiceModelAxcl.

Highlights

  • Refactored AXCL Model Handling: The existing ax_model_runner implementation for AXCL models has been replaced with a new, modular set of C++ classes, including AxclManager, AxclEngineGuard, AxclEngineIOGuard, AxclEngineIOInfoGuard, AxclModel, and AxclDevicePtr.
  • Improved Resource Management: New guard classes (AxclEngineGuard, AxclEngineIOGuard, AxclEngineIOInfoGuard) and a manager class (AxclManager) have been introduced to ensure proper initialization, finalization, and resource allocation/deallocation for AXCL components.
  • Enhanced AXCL Model Abstraction: A new AxclModel class now encapsulates the logic for loading AXCL models, managing input/output tensors, and executing inference, providing a cleaner interface for model interaction.
  • Integration with OfflineSenseVoiceModelAxcl: The OfflineSenseVoiceModelAxcl class has been updated to utilize the new AxclModel for its operations, streamlining its internal implementation.
  • C++ Move Semantics for Acl Utilities: Move constructors and move assignment operators have been added to several Acl utility classes (Acl, AclContext, AclDevicePtr, AclModelDesc, AclModel, AclMdlDataset, AclDataBuffer, AclTensorDesc) in sherpa-onnx/csrc/ascend/utils.h to improve efficiency and resource handling.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request is a significant and well-executed refactoring of the AXCL integration. It replaces a monolithic runner class with a set of smaller, more focused RAII wrapper classes and a high-level AxclModel class. This greatly improves the code's structure, safety, and maintainability, as evidenced by the much cleaner implementation in offline-sense-voice-model-axcl.cc. While the overall direction is excellent, I've identified a few critical bugs and minor issues that should be addressed to ensure correctness and robustness. My comments focus on fixing these issues.

Comment thread sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc
Comment thread sherpa-onnx/csrc/axcl/utils.h
Comment thread sherpa-onnx/csrc/axcl/utils.h
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc Outdated
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc Outdated

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

🧹 Nitpick comments (3)
sherpa-onnx/csrc/axcl/utils.h (1)

40-40: Consider making the implicit conversion operator const.

The operator void*() is non-const while Get() is const. For consistency and to allow usage on const objects, consider:

-  operator void *() { return p_; }
+  operator void *() const { return p_; }
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h (1)

23-23: Consider making the conversion operator const.

For consistency and to allow usage on const objects:

-  operator axclrtEngineIOInfo() { return io_info_; }
+  operator axclrtEngineIOInfo() const { return io_info_; }
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h (1)

21-21: Consider making the conversion operator const.

For consistency with the other guard classes and to allow usage on const objects:

-  operator axclrtEngineIO() { return io_; }
+  operator axclrtEngineIO() const { return io_; }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32fbd3a and a4ac188.

📒 Files selected for processing (19)
  • sherpa-onnx/csrc/CMakeLists.txt (1 hunks)
  • sherpa-onnx/csrc/ascend/utils.h (8 hunks)
  • sherpa-onnx/csrc/axcl/ax_model_runner.hpp (0 hunks)
  • sherpa-onnx/csrc/axcl/ax_model_runner_axcl.cc (0 hunks)
  • sherpa-onnx/csrc/axcl/ax_model_runner_axcl.hpp (0 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-guard.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-guard.h (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-io-guard.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-manager.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-manager.h (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-model.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-model.h (1 hunks)
  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (4 hunks)
  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.h (0 hunks)
  • sherpa-onnx/csrc/axcl/utils.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/utils.h (1 hunks)
💤 Files with no reviewable changes (4)
  • sherpa-onnx/csrc/axcl/ax_model_runner_axcl.hpp
  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.h
  • sherpa-onnx/csrc/axcl/ax_model_runner_axcl.cc
  • sherpa-onnx/csrc/axcl/ax_model_runner.hpp
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-06T04:18:47.981Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:18:47.981Z
Learning: In sherpa-onnx Java API, the native library names in Core.java (WIN_NATIVE_LIBRARY_NAME = "sherpa-onnx-jni.dll", UNIX_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.so", MACOS_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.dylib") are copied directly from the compiled binary filenames and should not be changed to match other libraries' naming conventions.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
  • sherpa-onnx/csrc/CMakeLists.txt
📚 Learning: 2025-08-06T04:23:50.237Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:23:50.237Z
Learning: The sherpa-onnx JNI library files are stored in Hugging Face repository at https://huggingface.co/csukuangfj/sherpa-onnx-libs under versioned directories like jni/1.12.7/, and the actual Windows JNI library filename is "sherpa-onnx-jni.dll" as defined in Core.java constants.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
  • sherpa-onnx/csrc/CMakeLists.txt
🧬 Code graph analysis (13)
sherpa-onnx/csrc/axcl/axcl-manager.h (1)
sherpa-onnx/csrc/axcl/axcl-manager.cc (2)
  • AxclManager (18-29)
  • AxclManager (31-42)
sherpa-onnx/csrc/axcl/utils.cc (1)
sherpa-onnx/csrc/axcl/utils.h (1)
  • AxclDevicePtr (12-47)
sherpa-onnx/csrc/axcl/axcl-engine-guard.h (3)
sherpa-onnx/csrc/axcl/axcl-model.h (1)
  • sherpa_onnx (13-47)
sherpa-onnx/csrc/axcl/utils.h (1)
  • sherpa_onnx (10-49)
sherpa-onnx/csrc/axcl/axcl-engine-guard.cc (2)
  • AxclEngineGuard (14-24)
  • AxclEngineGuard (26-37)
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.cc (2)
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h (1)
  • AxclEngineIOInfoGuard (13-28)
sherpa-onnx/csrc/axcl/axcl-model.cc (1)
  • ret (284-293)
sherpa-onnx/csrc/axcl/axcl-engine-guard.cc (2)
sherpa-onnx/csrc/axcl/axcl-model.cc (1)
  • ret (284-293)
sherpa-onnx/csrc/axera/ax-engine-guard.h (1)
  • sherpa_onnx (9-26)
sherpa-onnx/csrc/axcl/utils.h (2)
sherpa-onnx/csrc/axcl/axcl-engine-guard.h (1)
  • sherpa_onnx (9-25)
sherpa-onnx/csrc/axcl/utils.cc (4)
  • AxclDevicePtr (11-22)
  • AxclDevicePtr (38-38)
  • Release (24-36)
  • Release (24-24)
sherpa-onnx/csrc/axcl/axcl-manager.cc (1)
sherpa-onnx/csrc/axcl/axcl-manager.h (1)
  • AxclManager (13-27)
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.cc (1)
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h (1)
  • AxclEngineIOGuard (11-26)
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h (1)
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.cc (2)
  • AxclEngineIOGuard (14-24)
  • AxclEngineIOGuard (26-37)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (1)
sherpa-onnx/csrc/axera/offline-sense-voice-model-axera.cc (2)
  • features (56-102)
  • features (56-57)
sherpa-onnx/csrc/axcl/axcl-model.cc (1)
sherpa-onnx/csrc/axcl/axcl-model.h (1)
  • AxclModel (15-45)
sherpa-onnx/csrc/ascend/utils.h (1)
sherpa-onnx/csrc/ascend/utils.cc (17)
  • Acl (103-107)
  • Acl (109-114)
  • AclContext (116-119)
  • AclContext (121-126)
  • AclDevicePtr (130-139)
  • AclDevicePtr (141-146)
  • AclModelDesc (148-157)
  • AclModelDesc (159-164)
  • AclModel (166-173)
  • AclModel (175-180)
  • AclModel (182-187)
  • AclMdlDataset (316-322)
  • AclMdlDataset (324-329)
  • AclDataBuffer (344-351)
  • AclDataBuffer (353-358)
  • AclTensorDesc (360-367)
  • AclTensorDesc (369-373)
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h (1)
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.cc (2)
  • AxclEngineIOInfoGuard (14-24)
  • AxclEngineIOInfoGuard (26-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Debug static tts-OFF
  • GitHub Check: Release shared-OFF tts-ON
  • GitHub Check: Debug shared-OFF tts-OFF
  • GitHub Check: Release shared-ON tts-OFF
  • GitHub Check: Release shared-OFF tts-OFF
  • GitHub Check: Debug shared-ON tts-OFF
  • GitHub Check: Debug shared-ON tts-ON
  • GitHub Check: Release shared-ON tts-ON
  • GitHub Check: ubuntu-24.04 3.10
  • GitHub Check: ubuntu-24.04 3.12
  • GitHub Check: rknn shared OFF
  • GitHub Check: Debug shared-OFF tts-ON
  • GitHub Check: rknn shared ON
  • GitHub Check: swift (macos-latest)
  • GitHub Check: ubuntu-24.04 3.13
  • GitHub Check: swift (macos-13)
  • GitHub Check: ubuntu-24.04 3.11
  • GitHub Check: ubuntu-24.04 3.8
  • GitHub Check: ubuntu-24.04 3.9
🔇 Additional comments (16)
sherpa-onnx/csrc/axcl/axcl-engine-guard.h (1)

11-23: LGTM! Clean RAII guard design.

The class correctly implements a non-copyable, non-movable guard pattern with proper state tracking via initialized_. This ensures safe cleanup in the destructor.

sherpa-onnx/csrc/axcl/axcl-engine-io-guard.cc (1)

14-37: LGTM! Proper RAII lifecycle management.

The constructor and destructor correctly manage the axclrtEngineIO lifecycle with appropriate error handling. The initialized_ flag ensures the destructor only attempts cleanup when initialization succeeded.

sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.cc (1)

14-37: LGTM! Consistent with other guard implementations.

The implementation follows the established pattern for AXCL resource guards with proper initialization tracking and cleanup.

sherpa-onnx/csrc/axcl/utils.cc (1)

24-38: LGTM! Proper cleanup logic.

The Release() method correctly handles the null check, frees memory, and nulls the pointer. The destructor delegates to Release() appropriately.

sherpa-onnx/csrc/axcl/axcl-engine-guard.cc (1)

14-37: LGTM! Proper AXCL engine lifecycle management.

The constructor and destructor correctly manage the AXCL engine initialization and finalization with appropriate error handling and state tracking.

sherpa-onnx/csrc/CMakeLists.txt (1)

207-217: LGTM!

The new AXCL source files are correctly added within the SHERPA_ONNX_ENABLE_AXCL conditional block, following the established pattern used for other backends (RKNN, AXERA). The file organization is clear and consistent.

sherpa-onnx/csrc/axcl/axcl-manager.h (1)

13-27: LGTM!

The AxclManager class provides proper thread-safe reference-counted lifecycle management for the AXCL library. The design correctly uses a mutex with an atomic counter to ensure axclInit is called once on first use and axclFinalize on last destruction. Copy and move semantics are appropriately deleted for this singleton-like RAII pattern.

sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h (1)

13-28: LGTM!

The AxclEngineIOInfoGuard class correctly implements RAII for axclrtEngineIOInfo resources. The initialized_ flag properly guards cleanup, and copy/move semantics are appropriately deleted.

sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h (1)

11-26: LGTM!

The AxclEngineIOGuard class correctly implements RAII for axclrtEngineIO resources. The design is consistent with the other guard classes in this PR.

sherpa-onnx/csrc/axcl/axcl-manager.cc (1)

14-29: LGTM! Thread-safe reference-counted AXCL lifecycle management.

The implementation correctly handles initialization on first use with proper cleanup on failure. Minor observation: since instanceCount_ is always accessed under mutex_ protection, std::atomic<int> could be simplified to int. However, the current approach is safe and works correctly.

sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (2)

38-47: Verify thread safety for concurrent Run() calls.

The Axera implementation in sherpa-onnx/csrc/axera/offline-sense-voice-model-axera.cc (lines 55-59) uses a mutex lock in Run() with the comment "TODO(fangjun): Support multi clients". This AXCL version lacks similar protection. If concurrent inference is expected, consider adding a mutex here as well.


50-62: LGTM!

The PostInit() method properly validates model initialization and extracts the required tensor shape metadata.

sherpa-onnx/csrc/axcl/axcl-model.cc (2)

85-96: LGTM!

Destructor correctly guards unload with model_loaded_ check and handles errors appropriately.


295-370: LGTM!

Input and output tensor initialization is thorough, with proper error handling and buffer binding.

sherpa-onnx/csrc/axcl/axcl-model.h (1)

15-45: LGTM! Clean API design with proper encapsulation.

The pimpl idiom effectively hides implementation details. The API provides a complete interface for model lifecycle, tensor metadata access, data transfer, and execution.

sherpa-onnx/csrc/ascend/utils.h (1)

21-25: Explicitly deleted move operations and normalized operator= signatures look good

Across Acl, AclContext, AclDevicePtr, AclModelDesc, AclModel, AclMdlDataset, AclDataBuffer, and AclTensorDesc, making the move constructor/assignment explicitly = delete and changing deleted copy-assignments to return T& instead of const T& is consistent with RAII best practices and clarifies intent. Given these types already had user-defined destructors (and in most cases deleted copy operations), they were effectively non-movable before; this change mainly improves readability and makes the non-movable property explicit without altering behavior. No issues from my side.

Also applies to: 37-41, 57-61, 79-83, 104-108, 153-154, 174-175, 193-194

Comment thread sherpa-onnx/csrc/axcl/axcl-engine-guard.cc Outdated
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc
Comment thread sherpa-onnx/csrc/axcl/utils.cc
Comment thread sherpa-onnx/csrc/axcl/utils.h

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

🧹 Nitpick comments (3)
sherpa-onnx/csrc/axcl/utils.h (2)

22-41: Past move semantics issue is now resolved.

The move constructor and move assignment operator now correctly transfer both p_ and size_ members, addressing the critical bug flagged in previous reviews.

For consistency with standard library best practices, consider marking both move operations noexcept to enable optimizations in standard containers like std::vector.

Apply this diff to mark move operations as noexcept:

-  AxclDevicePtr(AxclDevicePtr &&other) {
+  AxclDevicePtr(AxclDevicePtr &&other) noexcept {
     p_ = other.p_;
     size_ = other.size_;

     other.p_ = nullptr;
     other.size_ = 0;
   }
-  AxclDevicePtr &operator=(AxclDevicePtr &&other) {
+  AxclDevicePtr &operator=(AxclDevicePtr &&other) noexcept {
     if (this == &other) {
       return *this;
     }

46-46: Mark conversion operator as const.

For consistency with Get() (line 45), the operator void *() should be marked const since it doesn't modify object state.

Apply this diff:

-  operator void *() { return p_; }
+  operator void *() const { return p_; }
sherpa-onnx/csrc/axcl/axcl-model.cc (1)

243-243: Minor typo in log message.

"Validate range" should likely be "Valid range" for grammatical correctness.

Apply this diff:

-      SHERPA_ONNX_LOGE("Invalid device_id: %d. Validate range: 0-%d", device_id,
+      SHERPA_ONNX_LOGE("Invalid device_id: %d. Valid range: 0-%d", device_id,
                        lst.num - 1);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4ac188 and f382777.

📒 Files selected for processing (5)
  • sherpa-onnx/csrc/axcl/axcl-engine-guard.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-model.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (4 hunks)
  • sherpa-onnx/csrc/axcl/utils.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/utils.h (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sherpa-onnx/csrc/axcl/axcl-engine-guard.cc
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-06T04:18:47.981Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:18:47.981Z
Learning: In sherpa-onnx Java API, the native library names in Core.java (WIN_NATIVE_LIBRARY_NAME = "sherpa-onnx-jni.dll", UNIX_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.so", MACOS_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.dylib") are copied directly from the compiled binary filenames and should not be changed to match other libraries' naming conventions.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
📚 Learning: 2025-08-06T04:23:50.237Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:23:50.237Z
Learning: The sherpa-onnx JNI library files are stored in Hugging Face repository at https://huggingface.co/csukuangfj/sherpa-onnx-libs under versioned directories like jni/1.12.7/, and the actual Windows JNI library filename is "sherpa-onnx-jni.dll" as defined in Core.java constants.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
🧬 Code graph analysis (4)
sherpa-onnx/csrc/axcl/utils.cc (1)
sherpa-onnx/csrc/axcl/utils.h (1)
  • AxclDevicePtr (12-53)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (1)
sherpa-onnx/csrc/axera/offline-sense-voice-model-axera.cc (2)
  • features (56-102)
  • features (56-57)
sherpa-onnx/csrc/axcl/utils.h (1)
sherpa-onnx/csrc/axcl/utils.cc (4)
  • AxclDevicePtr (11-22)
  • AxclDevicePtr (39-39)
  • Release (24-37)
  • Release (24-24)
sherpa-onnx/csrc/axcl/axcl-model.cc (2)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (6)
  • Impl (21-25)
  • Impl (21-21)
  • Impl (28-33)
  • out (84-84)
  • Run (113-117)
  • Run (113-115)
sherpa-onnx/csrc/axcl/axcl-model.h (1)
  • AxclModel (15-45)
🔇 Additional comments (8)
sherpa-onnx/csrc/axcl/utils.cc (1)

11-39: LGTM!

The RAII implementation is correct and follows best practices:

  • Constructor allocates device memory and stores size
  • Release properly frees memory and resets state
  • Destructor ensures cleanup via Release
  • Comprehensive error handling throughout
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (3)

21-33: LGTM!

The refactoring to use AxclModel is clean and well-structured. Both constructors properly initialize the model (from file or buffer) and call PostInit() for validation.


39-48: LGTM!

The Run method is cleanly refactored to use the AxclModel API. Input data is set via SetInputTensorData, execution happens via Run(), and output is retrieved via GetOutputTensorData.


51-63: LGTM!

The new PostInit method provides clear initialization validation and properly derives num_input_frames_ from the model's tensor shape.

sherpa-onnx/csrc/axcl/axcl-model.cc (4)

33-52: LGTM!

The file-based constructor properly initializes the AXCL engine, loads the model, and calls PostInit(). Error handling is comprehensive, and the previous issue with logging 0 instead of ret at line 45 has been resolved.


54-83: LGTM!

The memory-based constructor correctly copies the model buffer to device memory using AxclDevicePtr RAII wrapper, loads from device memory, and proceeds with PostInit().


155-184: LGTM!

The SetInputTensorData template correctly validates tensor size, performs the memory copy, and now properly returns true on success (line 177), fixing the critical bug flagged in previous reviews.


226-256: LGTM!

Device selection logic is correct with proper validation of device count and device ID range.

Copilot AI 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.

Pull request overview

This PR refactors the AXCL (AX compute library) implementation by replacing legacy runner code with a new, cleaner model abstraction API. The refactoring introduces RAII-based resource management similar to the existing Ascend implementation pattern, improving lifecycle safety and reducing header dependencies.

Key Changes:

  • Introduced a new high-level AxclModel API for loading, running, and querying AXCL models
  • Replaced legacy ax_runner_axcl with RAII guard classes for managing engine, IO, and device memory resources
  • Refactored OfflineSenseVoiceModelAxcl to use the new cleaner API

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
sherpa-onnx/csrc/axcl/utils.h New device memory pointer RAII wrapper with move semantics
sherpa-onnx/csrc/axcl/utils.cc Implementation of AxclDevicePtr memory allocation/deallocation
sherpa-onnx/csrc/axcl/axcl-model.h High-level AXCL model API interface with tensor operations
sherpa-onnx/csrc/axcl/axcl-model.cc Implementation of AxclModel with device setup, model loading, and execution
sherpa-onnx/csrc/axcl/axcl-manager.h Singleton manager for AXCL initialization/finalization with reference counting
sherpa-onnx/csrc/axcl/axcl-manager.cc Thread-safe AXCL lifecycle management using mutex and atomic counter
sherpa-onnx/csrc/axcl/axcl-engine-guard.h RAII guard for engine initialization/finalization
sherpa-onnx/csrc/axcl/axcl-engine-guard.cc Engine guard implementation
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.h RAII guard for engine IO info lifecycle
sherpa-onnx/csrc/axcl/axcl-engine-io-info-guard.cc IO info guard implementation
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.h RAII guard for engine IO lifecycle
sherpa-onnx/csrc/axcl/axcl-engine-io-guard.cc IO guard implementation
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.h Removed axcl.h and runner header dependencies
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc Refactored to use new AxclModel API, simplified initialization and inference
sherpa-onnx/csrc/axcl/ax_model_runner_axcl.hpp Deleted legacy runner header
sherpa-onnx/csrc/axcl/ax_model_runner_axcl.cc Deleted legacy runner implementation (470 lines)
sherpa-onnx/csrc/axcl/ax_model_runner.hpp Deleted legacy runner base class (148 lines)
sherpa-onnx/csrc/ascend/utils.h Added move constructor/operator deletions for consistency
sherpa-onnx/csrc/CMakeLists.txt Updated source list to include new files and remove legacy runner

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (ret != 0) {
SHERPA_ONNX_LOGE("Failed to call axclrtMemcpy(). Return code is: %d",
static_cast<int32_t>(ret));
return;

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If axclrtMemcpy fails (line 63-68), the function returns early without calling SHERPA_ONNX_EXIT(-1), unlike other initialization failures. This leaves the AxclModel object in a partially initialized state with model_loaded_ = false, but an initialized engine_guard_. For consistency with other fatal initialization errors in this file, consider calling SHERPA_ONNX_EXIT(-1) instead of just returning.

Suggested change
return;
SHERPA_ONNX_EXIT(-1);

Copilot uses AI. Check for mistakes.
SHERPA_ONNX_LOGE(
"Failed to call axclrtEngineLoadFromMem(). Return code is: %d",
static_cast<int32_t>(ret));
return;

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If axclrtEngineLoadFromMem fails (line 71-76), the function returns early without calling SHERPA_ONNX_EXIT(-1), unlike axclrtEngineLoadFromFile failure on line 46. This inconsistency leaves the object in a partially initialized state. For consistency with other fatal initialization errors in this file, consider calling SHERPA_ONNX_EXIT(-1) instead of just returning.

Suggested change
return;
SHERPA_ONNX_EXIT(-1);

Copilot uses AI. Check for mistakes.
Comment thread sherpa-onnx/csrc/axcl/axcl-manager.cc Outdated
Comment on lines +20 to +27
if (instanceCount_++ == 0) {
auto ret = axclInit(config);
if (ret != 0) {
instanceCount_--;
SHERPA_ONNX_LOGE("Failed to call axclInit(). Return code: %d",
static_cast<int32_t>(ret));
SHERPA_ONNX_EXIT(-1);
}

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The post-increment instanceCount_++ followed by decrement on error path creates a race condition window. If another thread checks the count between the increment and the error decrement, it will see an incorrect count. Consider using compare-and-swap or only incrementing after successful initialization.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +42
AxclDevicePtr(AxclDevicePtr &&other) {
p_ = other.p_;
size_ = other.size_;

other.p_ = nullptr;
other.size_ = 0;
}
AxclDevicePtr &operator=(AxclDevicePtr &&other) {
if (this == &other) {
return *this;
}
Release();
p_ = other.p_;
size_ = other.size_;

other.p_ = nullptr;
other.size_ = 0;

return *this;
}

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AxclDevicePtr implements move semantics, but the similar AclDevicePtr class in sherpa-onnx/csrc/ascend/utils.h (lines 60-61) deletes move operations to enforce strict single ownership. Consider deleting move operations here as well for consistency with the Ascend implementation pattern, unless there's a specific reason to allow moving device pointers.

Suggested change
AxclDevicePtr(AxclDevicePtr &&other) {
p_ = other.p_;
size_ = other.size_;
other.p_ = nullptr;
other.size_ = 0;
}
AxclDevicePtr &operator=(AxclDevicePtr &&other) {
if (this == &other) {
return *this;
}
Release();
p_ = other.p_;
size_ = other.size_;
other.p_ = nullptr;
other.size_ = 0;
return *this;
}
AxclDevicePtr(AxclDevicePtr &&) = delete;
AxclDevicePtr &operator=(AxclDevicePtr &&) = delete;

Copilot uses AI. Check for mistakes.
Comment thread sherpa-onnx/csrc/axcl/axcl-model.cc Outdated

// device_id counts from 0
if (device_id < 0 || device_id >= lst.num) {
SHERPA_ONNX_LOGE("Invalid device_id: %d. Validate range: 0-%d", device_id,

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in error message: "Validate" should be "Valid".

Suggested change
SHERPA_ONNX_LOGE("Invalid device_id: %d. Validate range: 0-%d", device_id,
SHERPA_ONNX_LOGE("Invalid device_id: %d. Valid range: 0-%d", device_id,

Copilot uses AI. Check for mistakes.

Impl(const void *cpu_buf, size_t buf_len_in_bytes, int32_t device_id) {
if (!SetDevice(device_id)) {
return;

Copilot AI Dec 5, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When SetDevice fails (line 55-56), the constructor returns early without setting model_loaded_ or cleaning up engine_guard_. This leaves the object in an inconsistent state. The destructor will skip unloading, but the AxclEngineGuard destructor will still call axclrtEngineFinalize, potentially interfering with other instances. Consider calling SHERPA_ONNX_EXIT(-1) on SetDevice failure for consistency with other fatal initialization errors.

Suggested change
return;
SHERPA_ONNX_LOGE("Failed to set device (id=%d) in AxclModel::Impl constructor.", device_id);
SHERPA_ONNX_EXIT(-1);

Copilot uses AI. Check for mistakes.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (1)

40-49: Handle SetInputTensorData/Run failures instead of ignoring them

SetInputTensorData and Run return bool but the results are ignored. If the feature length or prompt size mismatches the expected tensor size, or if AXCL copy/execute fails, this path will quietly continue and likely reuse stale device buffers, diverging from the axera backend which exits on such mismatches.

Consider checking these return values and treating failures as fatal (or at least bailing out) before reading outputs. For example:

-    model_->SetInputTensorData("x", features.data(), features.size());
-    model_->SetInputTensorData("prompt", prompt.data(), prompt.size());
-    model_->Run();
-    return model_->GetOutputTensorData("logits");
+    if (!model_->SetInputTensorData("x", features.data(),
+                                    static_cast<int32_t>(features.size()))) {
+      SHERPA_ONNX_LOGE("Failed to set input tensor 'x' for model '%s'",
+                       config_.sense_voice.model.c_str());
+      SHERPA_ONNX_EXIT(-1);
+    }
+
+    if (!model_->SetInputTensorData("prompt", prompt.data(),
+                                    static_cast<int32_t>(prompt.size()))) {
+      SHERPA_ONNX_LOGE("Failed to set input tensor 'prompt' for model '%s'",
+                       config_.sense_voice.model.c_str());
+      SHERPA_ONNX_EXIT(-1);
+    }
+
+    if (!model_->Run()) {
+      SHERPA_ONNX_LOGE("Failed to run inference for model '%s'",
+                       config_.sense_voice.model.c_str());
+      SHERPA_ONNX_EXIT(-1);
+    }
+
+    return model_->GetOutputTensorData("logits");

If this class is ever used from multiple threads, consider also adding a mutex (similar to the axera backend) around this block to avoid concurrent mutation of shared AXCL IO buffers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f382777 and e316c5c.

📒 Files selected for processing (4)
  • sherpa-onnx/csrc/axcl/axcl-manager.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-manager.h (1 hunks)
  • sherpa-onnx/csrc/axcl/axcl-model.cc (1 hunks)
  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sherpa-onnx/csrc/axcl/axcl-manager.h
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-06T04:18:47.981Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:18:47.981Z
Learning: In sherpa-onnx Java API, the native library names in Core.java (WIN_NATIVE_LIBRARY_NAME = "sherpa-onnx-jni.dll", UNIX_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.so", MACOS_NATIVE_LIBRARY_NAME = "libsherpa-onnx-jni.dylib") are copied directly from the compiled binary filenames and should not be changed to match other libraries' naming conventions.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
📚 Learning: 2025-08-06T04:23:50.237Z
Learnt from: litongjava
Repo: k2-fsa/sherpa-onnx PR: 2440
File: sherpa-onnx/java-api/src/main/java/com/k2fsa/sherpa/onnx/core/Core.java:4-6
Timestamp: 2025-08-06T04:23:50.237Z
Learning: The sherpa-onnx JNI library files are stored in Hugging Face repository at https://huggingface.co/csukuangfj/sherpa-onnx-libs under versioned directories like jni/1.12.7/, and the actual Windows JNI library filename is "sherpa-onnx-jni.dll" as defined in Core.java constants.

Applied to files:

  • sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc
🧬 Code graph analysis (3)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (1)
sherpa-onnx/csrc/axera/offline-sense-voice-model-axera.cc (2)
  • features (56-102)
  • features (56-57)
sherpa-onnx/csrc/axcl/axcl-manager.cc (2)
sherpa-onnx/csrc/axcl/axcl-manager.h (1)
  • AxclManager (13-27)
sherpa-onnx/csrc/axcl/axcl-model.cc (1)
  • ret (286-295)
sherpa-onnx/csrc/axcl/axcl-model.cc (2)
sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc (5)
  • Impl (22-26)
  • Impl (22-22)
  • Impl (29-34)
  • Run (114-118)
  • Run (114-116)
sherpa-onnx/csrc/axcl/axcl-model.h (1)
  • AxclModel (15-45)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Release shared tts-OFF
  • GitHub Check: Debug static tts-ON
  • GitHub Check: Debug shared-ON tts-ON
  • GitHub Check: Release shared-OFF tts-ON
  • GitHub Check: Debug shared-OFF tts-OFF
  • GitHub Check: Debug shared-ON tts-OFF
  • GitHub Check: Debug shared-OFF tts-ON
  • GitHub Check: Release shared-ON tts-OFF
  • GitHub Check: Release shared-OFF tts-OFF
  • GitHub Check: Release shared-ON tts-ON
  • GitHub Check: ubuntu-24.04 3.9
  • GitHub Check: ubuntu-24.04 3.10
  • GitHub Check: ubuntu-24.04 3.11
  • GitHub Check: ubuntu-24.04 3.8
  • GitHub Check: ubuntu-24.04 3.13
  • GitHub Check: ubuntu-24.04 3.12
  • GitHub Check: swift (macos-13)
  • GitHub Check: swift (macos-latest)
  • GitHub Check: rknn shared OFF
  • GitHub Check: rknn shared ON
🔇 Additional comments (3)
sherpa-onnx/csrc/axcl/axcl-manager.cc (3)

18-30: Clarify semantics when multiple AxclManager instances use different config values

Only the first construction's config is actually used for axclInit(); later instances silently ignore their config argument if AXCL is already initialized. This could be surprising if callers pass different configs, making bugs hard to diagnose.

Consider one of:

  • Documenting that only the first non‑null config is respected and all subsequent ones must be nullptr; or
  • Storing the first config (e.g., as a canonical string) and asserting/logging if later calls pass a different, incompatible config.

32-43: Add defensive checks around count_ to catch misuse and verify static lifetime safety

--count_ assumes every destructor call is balanced with a prior constructor; if count_ ever hits negative (e.g., via manual destructor calls or odd static‑lifetime interactions), it will silently underflow and never finalize correctly. Consider adding a defensive guard to catch this misuse:

 AxclManager::~AxclManager() {
   std::lock_guard<std::mutex> lock(mutex_);
+  if (count_ <= 0) {
+    SHERPA_ONNX_LOGE("AxclManager destroyed more times than constructed");
+    SHERPA_ONNX_EXIT(-1);
+  }
+
   if (--count_ == 0) {
     auto ret = axclFinalize();

Additionally, verify that all AxclManager instances are non-static (stack- or member-owned). If any instances have static storage duration in other translation units, their destructors might run after mutex_/count_ teardown, causing undefined behavior.


14-30: Thread-safe refcounted init pattern looks solid

Using a single static mutex_ to guard both count_ and the axclInit call, and incrementing count_ only after successful init, gives you a clean, race-free first-use initialization across threads.

However, verification of the complete implementation (destructor, usage patterns, and edge cases) could not be completed due to repository access limitations. To strengthen confidence in this review, consider also examining:

  • The destructor implementation to ensure count_ is properly decremented
  • Whether defensive checks guard against count_ underflow
  • Static storage duration and destruction order if AxclManager instances exist in other translation units
  • How the config parameter is handled across multiple AxclManager instances

Comment on lines +211 to +221
bool Run() const {
uint32_t group = 0;
auto ret =
axclrtEngineExecute(model_id_, context_id_, group, *engine_io_guard_);
if (ret != 0) {
SHERPA_ONNX_LOGE("Failed to call axclrtEngineExecute(), return code: %d",
static_cast<int32_t>(ret));
return false;
}
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid dereferencing engine_io_guard_ when the model isn’t initialized

Run() unconditionally dereferences *engine_io_guard_. If SetDevice or model loading failed in the constructor, PostInit() is never called, engine_io_guard_ stays nullptr, and model_loaded_ remains false. A client that (incorrectly) calls Run() anyway will hit undefined behavior.

It’s cheap to harden this and make the failure mode explicit:

-  bool Run() const {
-    uint32_t group = 0;
-    auto ret =
-        axclrtEngineExecute(model_id_, context_id_, group, *engine_io_guard_);
-    if (ret != 0) {
-      SHERPA_ONNX_LOGE("Failed to call axclrtEngineExecute(), return code: %d",
-                       static_cast<int32_t>(ret));
-      return false;
-    }
-    return true;
-  }
+  bool Run() const {
+    if (!model_loaded_ || !engine_io_guard_) {
+      SHERPA_ONNX_LOGE(
+          "AxclModel::Run() called on an uninitialized model or without IO "
+          "buffers.");
+      return false;
+    }
+
+    uint32_t group = 0;
+    auto ret =
+        axclrtEngineExecute(model_id_, context_id_, group, *engine_io_guard_);
+    if (ret != 0) {
+      SHERPA_ONNX_LOGE("Failed to call axclrtEngineExecute(), return code: %d",
+                       static_cast<int32_t>(ret));
+      return false;
+    }
+    return true;
+  }

This preserves the current behavior on successful initialization while preventing a null dereference in error cases.

🤖 Prompt for AI Agents
In sherpa-onnx/csrc/axcl/axcl-model.cc around lines 211 to 221, Run() currently
dereferences *engine_io_guard_ unconditionally which can be null if PostInit()
never ran; before calling axclrtEngineExecute check that model_loaded_ is true
and engine_io_guard_ is non-null, and if not, log an error (e.g.
SHERPA_ONNX_LOGE with a clear message about uninitialized
model/engine_io_guard_) and return false; otherwise proceed to call
axclrtEngineExecute as before.

Comment on lines +52 to 60
void PostInit() {
if (!model_->IsInitialized()) {
SHERPA_ONNX_LOGE("Failed to initialize the model with '%s'",
config_.sense_voice.model.c_str());
SHERPA_ONNX_EXIT(-1);
}

auto &in0 = runner_.get_input(0);
if (in0.vShape.size() < 2) {
SHERPA_ONNX_LOGE(
"Input tensor rank is too small (rank = %zu). Shape vector is empty "
"or has only 1 dim.",
in0.vShape.size());
SHERPA_ONNX_EXIT(-1);
}
num_input_frames_ = in0.vShape[1];
num_input_frames_ = model_->TensorShape("x")[1];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against missing or malformed input tensor "x" in PostInit()

PostInit() assumes that TensorShape("x") exists and has at least two dimensions, then directly reads [1]. If the model doesn’t expose an input named "x" or has rank < 2, TensorShape logs an error and returns {}, and this indexing becomes undefined behavior.

Adding a shape-size check will make this failure mode explicit:

-  void PostInit() {
-    if (!model_->IsInitialized()) {
-      SHERPA_ONNX_LOGE("Failed to initialize the model with '%s'",
-                       config_.sense_voice.model.c_str());
-      SHERPA_ONNX_EXIT(-1);
-    }
-
-    num_input_frames_ = model_->TensorShape("x")[1];
+  void PostInit() {
+    if (!model_->IsInitialized()) {
+      SHERPA_ONNX_LOGE("Failed to initialize the model with '%s'",
+                       config_.sense_voice.model.c_str());
+      SHERPA_ONNX_EXIT(-1);
+    }
+
+    auto shape = model_->TensorShape("x");
+    if (shape.size() <= 1) {
+      SHERPA_ONNX_LOGE(
+          "Input tensor 'x' has unexpected rank %zu for model '%s'",
+          shape.size(), config_.sense_voice.model.c_str());
+      SHERPA_ONNX_EXIT(-1);
+    }
+
+    num_input_frames_ = shape[1];

This keeps the existing fatal-on-bad-model behavior but avoids UB if the model doesn’t match expectations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void PostInit() {
if (!model_->IsInitialized()) {
SHERPA_ONNX_LOGE("Failed to initialize the model with '%s'",
config_.sense_voice.model.c_str());
SHERPA_ONNX_EXIT(-1);
}
auto &in0 = runner_.get_input(0);
if (in0.vShape.size() < 2) {
SHERPA_ONNX_LOGE(
"Input tensor rank is too small (rank = %zu). Shape vector is empty "
"or has only 1 dim.",
in0.vShape.size());
SHERPA_ONNX_EXIT(-1);
}
num_input_frames_ = in0.vShape[1];
num_input_frames_ = model_->TensorShape("x")[1];
void PostInit() {
if (!model_->IsInitialized()) {
SHERPA_ONNX_LOGE("Failed to initialize the model with '%s'",
config_.sense_voice.model.c_str());
SHERPA_ONNX_EXIT(-1);
}
auto shape = model_->TensorShape("x");
if (shape.size() <= 1) {
SHERPA_ONNX_LOGE(
"Input tensor 'x' has unexpected rank %zu for model '%s'",
shape.size(), config_.sense_voice.model.c_str());
SHERPA_ONNX_EXIT(-1);
}
num_input_frames_ = shape[1];
🤖 Prompt for AI Agents
In sherpa-onnx/csrc/axcl/offline-sense-voice-model-axcl.cc around lines 52 to
60, PostInit() currently calls model_->TensorShape("x")[1] without validating
the returned shape; capture the shape into a local variable, verify it's
non-empty and has at least two dimensions (shape.size() > 1), and if the check
fails log a clear error (including config_.sense_voice.model) and call
SHERPA_ONNX_EXIT(-1); otherwise assign num_input_frames_ = shape[1]. This
preserves the fatal-on-bad-model behavior while avoiding undefined behavior when
"x" is missing or malformed.

@csukuangfj
csukuangfj merged commit 1e742ce into k2-fsa:master Dec 7, 2025
4 of 27 checks passed
@csukuangfj
csukuangfj deleted the refactor-axcl branch December 7, 2025 05:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants