Skip to content

feat(sdk): add baseline armv8.0-a linux build variant - #1340

Open
David Qian (Davidqian123) wants to merge 4 commits into
mainfrom
feat/david/armv8-baseline-build
Open

feat(sdk): add baseline armv8.0-a linux build variant#1340
David Qian (Davidqian123) wants to merge 4 commits into
mainfrom
feat/david/armv8-baseline-build

Conversation

@Davidqian123

@Davidqian123 David Qian (Davidqian123) commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Problem

geniex cannot run on baseline ARMv8.0 boards — NPU-less Dragonwing IoT SoCs such as the Arduino UNO Q. #1180 made the failure graceful (a clear "missing CPU features" error instead of a raw SIGILL), but the board still can't do inference at all:

$ geniex infer unsloth/Qwen3-0.6B-GGUF --compute cpu --ngl 0
⚠️ This device is missing CPU features that geniex requires, so it cannot run here.
exit=1

That remaining half is what #1217 stayed open for.

Root cause (recap of #1217)

The aarch64-Linux toolchain compiles everything with -march=armv8.2-a+fp16+dotprod (sdk/cmake/arm64-linux-gnu.cmake:8). armv8.2-a implies FEAT_LSE, so GCC inlines LSE atomics with no runtime fallback — every std::mutex and thread-safe static-local guard lowers to LDADDAL/CAS. UNO Q is ARMv8.0 and implements none of it, so the first atomic in the process traps. Credit to @mengshen_QCOM for decoding the faulting bytes.

Which of the two options

We need to pick solution between a baseline armv8.0-a build variant and option 1 (-moutline-atomics + runtime-dispatched ggml kernels). Option 1 is the larger change, not the smaller one:

  • The runtime dispatch already exists upstream — GGML_CPU_ALL_VARIANTS even ships an armv8.0_1 variant (ggml/src/CMakeLists.txt:408) — but it requires GGML_BACKEND_DL=ON, which we set OFF (sdk/plugins/llama_cpp/CMakeLists.txt:5). Turning it on makes ggml-cpu / hexagon / opencl dlopen'd backends, changing packaging and backend discovery for the shipping Snapdragon artifact.
  • It also wouldn't be sufficient on its own. The LDADDAL that trapped comes from the global -march applied to the SDK's own C++ and to ggml-base — not from ggml-cpu kernels. So option 1 = this PR's flag change plus a risky refactor of the NPU path.

So this PR takes the baseline variant. GGML_CPU_ALL_VARIANTS stays on the table as a later unification, once GGML_BACKEND_DL is de-risked on Snapdragon — at which point the two Linux artifacts could collapse back into one.

Fix

  • sdk/cmake/arm64-linux-gnu-baseline.cmake-march=armv8-a+crc -moutline-atomics. Atomics route through libgcc's runtime-dispatched helper: LSE when HWCAP_ATOMICS is present, LL/SC otherwise. Dropping +fp16/+dotprod means ggml falls back to its fp32 quant kernels — slower, but it runs. +crc is kept; it's the one guarded extension these cores do implement.
  • arm64-linux-baseline-{debug,release} presets — CPU-only (GENIEX_PLUGIN_QAIRT, GGML_HEXAGON, GGML_OPENCL all off). An ARMv8.0 Dragonwing board has no NPU and no Adreno to target, so the plugins would be dead weight.
  • sdk/src/ml.cpp — the HWCAP guard mask is now derived from the __ARM_FEATURE_* macros -march defines, instead of a hand-maintained list with a "keep in sync with -march" comment. For the Snapdragon build the mask is unchanged; for the baseline build it collapses to crc32 and the board passes.
  • CI / releasesdk-linux-arm64-baseline and cli-linux-arm64-baseline artifacts; releases additionally ship geniex-cli-linux-arm64-baseline-<tag>.tar.gz, geniex-sdk-linux-arm64-baseline-<tag>.zip, and a baseline geniex-bench archive.
  • install.sh — picks the baseline asset when /proc/cpuinfo has no atomics, --baseline forces it, and the fastrpc symlink step is skipped there (no HTP plugin to resolve for). The CPU-unsupported CLI hint now points at it instead of only saying "use a newer device".
  • Docs — EN + CN troubleshooting entries and notes/build.md.

Testing

Verified on WADTestBoard01 — the same UNO Q from #1217 (Features: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid; no atomics, no asimddp, no fphp). Artifacts are this PR's pr-check run 31768117636, not a local build.

The trapping instruction is now runtime-dispatched. ldaddal is still present in libgeniex.so, but only inside libgcc's outline helpers, behind a branch on __aarch64_have_lse_atomics:

0000000000518d10 <__aarch64_ldadd4_acq_rel>:
  518d14: ldrb  w16, [x16, #0x778]   ; __aarch64_have_lse_atomics
  518d18: cbz   w16, 0x518d24        ; -> LL/SC path on armv8.0
  518d1c: ldaddal w0, w0, [x1]       ; LSE fast path
  518d20: ret
  518d24: mov   w16, w0
  518d28: ldaxr w0, [x1]             ; <- what UNO Q executes
  518d30: stlxr w15, w17, [x1]
  518d34: cbnz  w15, 0x518d28

Attributing every LSE-class instruction in the artifact to its enclosing function: 100/100 sit inside __aarch64_* helpers, none inlined into our code. dotprod/i8mm instruction count is 0 across libgeniex.so, libggml-cpu.so, and libllama.so.

  • geniex-bench runs, no SIGILL--plugin llama_cpp --device cpu, Qwen3-0.6B Q4_0, -c 512 -p 64 -n 32 -t 4: ttft=4093.3ms prefill=15.6tps decode=8.1tps, exit=0.
  • Output is coherent, not merely non-crashing--accuracy --prompt-file (the timing mode's random-id prefill produces meaningless text by design): "France is located in Europe, and its capital is Paris."
  • CLI works end-to-end — the exact command from #1217 now infers:
    $ geniex infer unsloth/Qwen3-0.6B-GGUF --compute cpu --ngl 0 --nctx 512 --max-tokens 64
    <think>
    Okay, the user is asking for the capital of France. Let me think. France is a country
    located in Europe, so I need to mention that it's in Europe. ...
    — 8.1 tok/s • 64 tok • 1.3 s first token —
    
    geniex version and geniex list also work.
  • No regression to the Snapdragon guard. Since the HWCAP mask derivation changed, I ran the linux-arm64 (Snapdragon) artifact from the same CI run on this same board. It must still refuse, and it does — cleanly, no SIGILL:
    [ERROR] [src/ml.cpp:118:geniex_init] this device's CPU lacks features required by geniex
    ERROR: geniex_init: Operation not supported (code=-100013)
    
  • lint (cpp-format / go-lint / rust-format / ruff), test-rust, all four build-sdk jobs, and both Linux build-cli jobs green.

Throughput on UNO Q (Qwen3-0.6B Q4_0, 4 threads): ~15.5 tok/s prefill, ~8.1–8.6 tok/s decode. Well below what an armv8.2 build achieves, which is the expected cost of losing dotprod/fp16, but usable on a 4-core / 1.7 GB board.

Follow-ups

  • The unversioned S3 pointer lives in another repo. publish-s3 runs from qcom-ai-hub/geniex@chore/publish-s3, so geniex-cli-linux-arm64-baseline.tar.gz (the mutable "latest stable" key) needs the same mapping added there. Until then install.sh on an ARMv8.0 board needs --version vX.Y.Z. @hongzhic_QCOM could you take that side?
  • CI cost. This adds one Linux SDK job and one Linux CLI job to pr-check. Both are CPU-only and ccached, so they should stay under the Snapdragon job's wall time — say the word if you'd rather gate them to release builds only.
  • No QAIRT/GPU path on these boards, by construction. If an ARMv8.0 part ever ships with an NPU, the preset needs revisiting.

Baseline ARMv8.0 boards (NPU-less Dragonwing IoT SoCs such as unoq) trap on
the LSE atomics the armv8.2 Snapdragon toolchain inlines into every mutex, so
add a CPU-only armv8.0-a preset that uses -moutline-atomics instead.

Derive the runtime HWCAP guard from the __ARM_FEATURE_* macros -march defines
so it tracks the toolchain file instead of a hardcoded mask.

Refs #1217

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Qian <yichqian@qti.qualcomm.com>
Refs #1217

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Qian <yichqian@qti.qualcomm.com>
install.sh reads /proc/cpuinfo and falls back to the -baseline asset when LSE
atomics are absent; --baseline forces it. Point the CPU-unsupported hint at it.

Refs #1217

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Qian <yichqian@qti.qualcomm.com>
Refs #1217

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: David Qian <yichqian@qti.qualcomm.com>
@mintlify

mintlify Bot commented Aug 14, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
qualcomm-0801e48b 🟡 Building Aug 14, 2026, 3:52 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@Davidqian123 David Qian (Davidqian123) changed the title Feat/david/armv8 baseline build feat(sdk): baseline armv8.0-a build variant so geniex runs on NPU-less boards (unoq) Aug 14, 2026
@Davidqian123 David Qian (Davidqian123) changed the title feat(sdk): baseline armv8.0-a build variant so geniex runs on NPU-less boards (unoq) feat(sdk): add baseline armv8.0-a linux build variant Aug 14, 2026
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