Goal
Experiment with the ExecuTorch Qualcomm (QNN) backend: export a few easy
models and get them running correctly on-device on Qualcomm hardware (HTP /
Hexagon). Start with MobileNetV2 (small image classifier, quantizes cleanly
to int8 on HTP) as the proof-of-concept, then generalize.
Export scripts land in the GitLab export-scripts repo (universal-exporter
harness); RNE runtime integration is a separate follow-up (see below).
Key constraint: AOT compile needs x86_64 Linux (not macOS)
The QNN ahead-of-time (AOT) compiler is only shipped/verified for Ubuntu
22.04 x86_64 (or WSL). The QNN SDK ships no macOS host library — its host
libs are x86_64-linux-clang / x86_64-windows-msvc only. So the export flow
cannot run on an Apple Silicon Mac; only the device runtime libs are
cross-platform. Everything below runs on a Linux x86_64 box; only the phone is
shared.
Setup required
Export-scripts (universal-exporter) integration
Add QNN to the @nk/universal-exporter harness. QNN is a new backend axis — no
qnn/qualcomm reference exists there yet (inspect_pte.py's KNOWN_BACKENDS
already lists QnnBackend/HtpBackend, but there is no export path).
On-device validation
Follow-up: react-native-executorch runtime integration (rne-rewrite)
Running a QNN-delegated .pte inside the RNE app is a separate, larger task
and is out of scope for the first milestone:
Milestone
Milestone 1 = a validated on-device MobileNetV2 QNN .pte (top-k sane via the
runner). RNE app integration follows once the toolchain is proven.
Goal
Experiment with the ExecuTorch Qualcomm (QNN) backend: export a few easy
models and get them running correctly on-device on Qualcomm hardware (HTP /
Hexagon). Start with MobileNetV2 (small image classifier, quantizes cleanly
to int8 on HTP) as the proof-of-concept, then generalize.
Export scripts land in the GitLab
export-scriptsrepo (universal-exporterharness); RNE runtime integration is a separate follow-up (see below).
Key constraint: AOT compile needs x86_64 Linux (not macOS)
The QNN ahead-of-time (AOT) compiler is only shipped/verified for Ubuntu
22.04 x86_64 (or WSL). The QNN SDK ships no macOS host library — its host
libs are
x86_64-linux-clang/x86_64-windows-msvconly. So the export flowcannot run on an Apple Silicon Mac; only the device runtime libs are
cross-platform. Everything below runs on a Linux x86_64 box; only the phone is
shared.
Setup required
x86_64 Linux host — Ubuntu 22.04 (or WSL on Windows),
g++≥ 13.QNN SDK 2.37.0 (Qualcomm AI Engine Direct) from the Qualcomm developer
portal, unpacked to e.g.
~/qnn-sdk/qairt/2.37.0.250724. 2.37 is theversion ExecuTorch currently recommends for stability.
Android NDK (for building the on-device
qnn_executor_runner).ExecuTorch built from source with the Qualcomm backend enabled — the
pip
executorchwheel does not ship the QNN AOT libraries.bash git clone https://github.com/software-mansion-labs/executorch.git cd executorch && git submodule sync && git submodule update --init export QNN_SDK_ROOT=$HOME/qnn-sdk/qairt/2.37.0.250724 export ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk/<version> ./install_executorch.sh ./backends/qualcomm/scripts/build.sh # QNN AOT libs + android runtimeEnv vars in every export shell:
bash export QNN_SDK_ROOT=$HOME/qnn-sdk/qairt/2.37.0.250724 export EXECUTORCH_ROOT=$HOME/executorch source $QNN_SDK_ROOT/bin/envsetup.sh # sets LD_LIBRARY_PATH for QNN export PYTHONPATH=$EXECUTORCH_ROOT/..:$PYTHONPATHA Qualcomm SoC device connected over
adb. Read the chip and map itto a
QcomChipsetmember (needed for--soc):bash adb shell getprop ro.soc.model # e.g. SM8650|
ro.soc.model|QcomChipset| Marketing | Hexagon ||----------------|---------------|----------------------|---------|
| SM8450 | SM8450 | Snapdragon 8 Gen 1 | v69 |
| SM8550 | SM8550 | Snapdragon 8 Gen 2 | v73 |
| SM8650 | SM8650 | Snapdragon 8 Gen 3 | v75 |
| SM8750 | SM8750 | Snapdragon 8 Elite | v79 |
Export-scripts (universal-exporter) integration
Add QNN to the
@nk/universal-exporterharness. QNN is a new backend axis — noqnn/qualcommreference exists there yet (inspect_pte.py'sKNOWN_BACKENDSalready lists
QnnBackend/HtpBackend, but there is no export path)."qnn"to_BACKENDSinsrc/exporter/spec.py(the harness hard-rejects unknown backends).exporter_models/mobilenet_v2/:model.json—backendsincludes"qnn".qnn/config.json— local spec (no HF repo ships a QNN config). Variants:int8(8a8w PTQ,quantized: true) andfp16.forward:input
[1,3,224,224]float32 → output[1,1000]float32.qnn/export.py— implementsexport(config, out_dir); lowers inline viathe QNN HTP path (
make_quantizer8a8w →prepare_pt2e→ calibrate →convert_pt2e→to_edge_transform_and_lower_to_qnnwith an HTPcompiler spec for the target SoC). ExecuTorch imports stay inside
export()per the harness contract. Calibrate on a small ImageNet-minisplit for usable int8 accuracy.
skills/backends/qnn/SKILL.md— document the Linux-x86_64 + QNN-SDKrequirement and the lowering recipe, mirroring the existing backend skills.
On-device validation
qnn_executor_runnerfor android (done bybuild.sh), push the.pte+ inputs, run on the HTP, and sanity-check top-1/top-5. Fastest pathis ExecuTorch's upstream one-shot example:
bash python -m examples.qualcomm.scripts.mobilenet_v2 \ -b build-android -m SM8650 -s <serial> --dataset ./imagenet-mini/val # expect roughly top_1 ~70%, top_5 ~90%A healthy top-k proves the QNN compile + Hexagon execution path works on
the target SoC.
Follow-up: react-native-executorch runtime integration (
rne-rewrite)Running a QNN-delegated
.pteinside the RNE app is a separate, larger taskand is out of scope for the first milestone:
libs are XNNPACK/CoreML only).
.sos + Hexagon skels in the APK..pte.Milestone
Milestone 1 = a validated on-device MobileNetV2 QNN
.pte(top-k sane via therunner). RNE app integration follows once the toolchain is proven.