Skip to content

Commit 10bc51e

Browse files
authored
Qualcomm AI Engine Direct - Verify Direct Build in External CI (pytorch#19763)
### Summary QNN Backend supports direct build, which is to build library with hexagon tool chain. Since it is using its own tool chain, some of the C/C++ files or functions are not accessible. For example, in this PR, `extension/data_loader/mman.h` uses some MACRO that is not no Hexagon toolchain. Due to this reason, mainline often breaks direct-build when someone included functions that's not supported by Hexagon tool chain. To prevent this to happen, this PR added: 1. direct-build to CI test to ensure changes doesn't break direct build 2. Ensure the direct build artifact size is smaller than 200kb ### Test plan Passing `test-qnn-direct-build-linux` under `pull.yml`
1 parent 286acf5 commit 10bc51e

8 files changed

Lines changed: 167 additions & 17 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
# Copyright (c) Qualcomm Innovation Center, Inc.
3+
# All rights reserved
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
set -eux
9+
10+
source "$(dirname "${BASH_SOURCE[0]}")/../../backends/qualcomm/scripts/install_qnn_sdk.sh"
11+
12+
setup_android_ndk
13+
install_qnn
14+
install_hexagon_sdk
15+
16+
bash backends/qualcomm/scripts/build.sh \
17+
--build_direct_mode 3 --soc_model SM8750 \
18+
--skip_x86_64 --skip_linux_android \
19+
--release
20+
21+
ARTIFACT="build-direct/backends/qualcomm/libqnn_executorch_backend.so"
22+
if [ ! -f "${ARTIFACT}" ]; then
23+
echo "ERROR: direct-mode build did not produce ${ARTIFACT}" >&2
24+
exit 1
25+
fi
26+
27+
MAX_SIZE_BYTES=$((200 * 1024))
28+
ARTIFACT_SIZE=$(stat -c%s "${ARTIFACT}")
29+
if [ "${ARTIFACT_SIZE}" -gt "${MAX_SIZE_BYTES}" ]; then
30+
echo "ERROR: ${ARTIFACT} is ${ARTIFACT_SIZE} bytes, exceeds ${MAX_SIZE_BYTES}-byte (200 KiB) limit" >&2
31+
exit 1
32+
fi
33+
echo "PASSED: direct-mode build produced ${ARTIFACT} (${ARTIFACT_SIZE} bytes, under ${MAX_SIZE_BYTES}-byte limit)"

.github/workflows/pull.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,25 @@ jobs:
948948
PYTHON_EXECUTABLE=python bash .ci/scripts/build-qnn-sdk.sh
949949
PYTHON_EXECUTABLE=python bash .ci/scripts/test_model.sh ${{ matrix.model }} "cmake" "qnn"
950950
951+
test-qnn-direct-build-linux:
952+
name: test-qnn-direct-build-linux
953+
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
954+
permissions:
955+
id-token: write
956+
contents: read
957+
with:
958+
runner: linux.2xlarge
959+
docker-image: ci-image:executorch-ubuntu-22.04-qnn-sdk
960+
submodules: 'recursive'
961+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
962+
timeout: 30
963+
script: |
964+
# The generic Linux job chooses to use base env, not the one setup by the image
965+
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
966+
conda activate "${CONDA_ENV}"
967+
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh --build-tool cmake
968+
PYTHON_EXECUTABLE=python bash .ci/scripts/build-qnn-direct-sdk.sh
969+
951970
test-qnn-testsuite-linux:
952971
name: test-qnn-testsuite-linux
953972
permissions:

CMakeLists.txt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,6 @@ cmake_minimum_required(VERSION 3.24)
4949

5050
set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
5151

52-
# Hexagon toolchain with release build complains about code in third party
53-
# libraries.
54-
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "Hexagon" AND "${CMAKE_BUILD_TYPE}"
55-
STREQUAL "Release"
56-
)
57-
add_compile_options(
58-
-Wno-error=format -Wno-error=implicit-int-conversion
59-
-Wno-error=unused-variable -Wno-error=unused-function
60-
)
61-
endif()
62-
6352
# --- ExecuTorch Version ---
6453
# Parse version from version.txt (single source of truth)
6554
file(READ "${EXECUTORCH_ROOT}/version.txt" ET_VERSION_STRING)
@@ -90,6 +79,18 @@ project(executorch
9079
VERSION "${ET_VERSION_MAJOR}.${ET_VERSION_MINOR}.${ET_VERSION_PATCH}"
9180
)
9281

82+
# Hexagon toolchain with release build complains about code in third party
83+
# libraries. Must come after project(), which runs the toolchain file that sets
84+
# CMAKE_SYSTEM_PROCESSOR, and before add_subdirectory(third-party).
85+
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "Hexagon" AND "${CMAKE_BUILD_TYPE}"
86+
STREQUAL "Release"
87+
)
88+
add_compile_options(
89+
-Wno-error=format -Wno-error=implicit-int-conversion
90+
-Wno-error=unused-variable -Wno-error=unused-function
91+
)
92+
endif()
93+
9394
message(
9495
STATUS
9596
"ExecuTorch version: ${ET_VERSION_MAJOR}.${ET_VERSION_MINOR}.${ET_VERSION_PATCH}"

backends/qualcomm/scripts/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ usage() {
4848
echo "e.g.: executorch$ ./backends/qualcomm/scripts/build.sh --skip_x86_64"
4949
echo ""
5050
echo "Direct mode: Use --build_direct_mode <dsp_type> --soc_model <model> to enable."
51+
echo "<dsp_type> id is mapped to Hexagon SDK dsp id. Refer to Hexagon SDK for more info."
5152
echo "You can choose either LPAI (ADSP) or CDSP (HTP) as the target DSP:"
5253
echo " LPAI (ADSP): dsp_type=0"
5354
echo " CDSP (HTP): dsp_type=3"

backends/qualcomm/scripts/build_utils.sh

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ import sys, os
2121
devnull = open(os.devnull, 'w')
2222
old_stdout = sys.stdout
2323
sys.stdout = devnull
24-
from executorch.backends.qualcomm.utils.utils import get_soc_to_htp_arch_map
24+
from executorch.backends.qualcomm.serialization.qc_schema import _soc_info_table
2525
sys.stdout = old_stdout
26-
m = get_soc_to_htp_arch_map()
26+
m = {soc.name: info.htp_info.htp_arch for soc, info in _soc_info_table.items()}
2727
if '${soc_model}' not in m:
2828
sys.exit(1)
2929
print(m['${soc_model}'].value)
3030
" 2>/dev/null) || {
3131
echo "Error: SoC model '${soc_model}' not found in HTP arch map."
32-
echo "Check supported models in executorch/backends/qualcomm/utils/utils.py get_soc_to_htp_arch_map()."
32+
echo "Check supported models in executorch/backends/qualcomm/serialization/qc_schema.py _soc_info_table."
3333
exit 1
3434
}
3535

@@ -39,15 +39,19 @@ import sys, os
3939
devnull = open(os.devnull, 'w')
4040
old_stdout = sys.stdout
4141
sys.stdout = devnull
42-
from executorch.backends.qualcomm.utils.utils import get_soc_to_lpai_hw_ver_map
42+
from executorch.backends.qualcomm.serialization.qc_schema import _soc_info_table
4343
sys.stdout = old_stdout
44-
m = get_soc_to_lpai_hw_ver_map()
44+
m = {
45+
soc.name: info.lpai_info.lpai_hardware_version
46+
for soc, info in _soc_info_table.items()
47+
if info.lpai_info is not None
48+
}
4549
if '${soc_model}' not in m:
4650
sys.exit(1)
4751
print(m['${soc_model}'].value)
4852
" 2>/dev/null) || {
4953
echo "Error: SoC model '${soc_model}' not found in LPAI hardware version map."
50-
echo "Check supported models in executorch/backends/qualcomm/utils/utils.py get_soc_to_lpai_hw_ver_map()."
54+
echo "Check supported models in executorch/backends/qualcomm/serialization/qc_schema.py _soc_info_table."
5155
exit 1
5256
}
5357
fi

backends/qualcomm/scripts/install_qnn_sdk.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,83 @@ install_qnn() {
109109
echo "Set QNN_SDK_ROOT=${QNN_SDK_ROOT}"
110110
}
111111

112+
# Install the Hexagon SDK required for direct-mode CI builds.
113+
install_hexagon_sdk() {
114+
# Check if already configured externally and valid.
115+
if [ -n "${HEXAGON_SDK_ROOT:-}" ] && [ -d "${HEXAGON_SDK_ROOT:-}" ] \
116+
&& [ -n "${HEXAGON_TOOLS_ROOT:-}" ] && [ -d "${HEXAGON_TOOLS_ROOT:-}" ]; then
117+
echo "Hexagon SDK already set to ${HEXAGON_SDK_ROOT} - skipping installation"
118+
return
119+
fi
120+
121+
echo "Start installing Hexagon SDK v${HEXAGON_SDK_VERSION} (tools v${HEXAGON_TOOLS_VERSION})"
122+
HEXAGON_INSTALLATION_DIR="/tmp/hexagon-sdk"
123+
HEXAGON_SDK_DIR="${HEXAGON_INSTALLATION_DIR}/Hexagon_SDK/${HEXAGON_SDK_VERSION}"
124+
HEXAGON_TOOLS_DIR="${HEXAGON_SDK_DIR}/tools/HEXAGON_Tools/${HEXAGON_TOOLS_VERSION}"
125+
126+
# Return if already exist
127+
if [ -d "${HEXAGON_SDK_DIR}" ] && [ -d "${HEXAGON_TOOLS_DIR}" ]; then
128+
echo "Hexagon SDK already installed at ${HEXAGON_SDK_DIR}"
129+
export HEXAGON_SDK_ROOT="${HEXAGON_SDK_DIR}"
130+
export HEXAGON_TOOLS_ROOT="${HEXAGON_TOOLS_DIR}"
131+
return
132+
fi
133+
134+
mkdir -p "${HEXAGON_INSTALLATION_DIR}"
135+
136+
HEXAGON_ZIP_FILE="Hexagon_SDK_Linux.zip"
137+
# Match install_qnn's retry shape: --fail rejects HTTP errors,
138+
# --retry-all-errors retries transport failures, `unzip -t` validates the
139+
# archive, and the SHA-256 check pins the exact bytes we tested against. All
140+
# are inside the retry condition so a truncated or wrong-content download is
141+
# re-fetched rather than killing the job.
142+
HEXAGON_DOWNLOAD_MAX_ATTEMPTS=5
143+
for attempt in $(seq 1 ${HEXAGON_DOWNLOAD_MAX_ATTEMPTS}); do
144+
rm -f "/tmp/${HEXAGON_ZIP_FILE}"
145+
if curl --fail --retry 3 --retry-delay 5 --retry-connrefused --retry-all-errors \
146+
-Lo "/tmp/${HEXAGON_ZIP_FILE}" "${HEXAGON_SDK_ZIP_URL}" \
147+
&& unzip -tq "/tmp/${HEXAGON_ZIP_FILE}" \
148+
&& echo "${HEXAGON_SDK_ZIP_SHA256} /tmp/${HEXAGON_ZIP_FILE}" | sha256sum -c -; then
149+
break
150+
fi
151+
ls -l "/tmp/${HEXAGON_ZIP_FILE}" 2>&1 || true
152+
if [ "${attempt}" = "${HEXAGON_DOWNLOAD_MAX_ATTEMPTS}" ]; then
153+
echo "ERROR: Hexagon SDK download failed after ${attempt} attempts" >&2
154+
exit 1
155+
fi
156+
echo "Hexagon SDK download attempt ${attempt} failed; retrying in $((attempt * 10))s..."
157+
sleep $((attempt * 10))
158+
done
159+
echo "Finishing downloading Hexagon SDK."
160+
161+
unzip -qo "/tmp/${HEXAGON_ZIP_FILE}" -d "${HEXAGON_INSTALLATION_DIR}"
162+
echo "Finishing unzip Hexagon SDK."
163+
164+
export HEXAGON_SDK_ROOT="${HEXAGON_SDK_DIR}"
165+
export HEXAGON_TOOLS_ROOT="${HEXAGON_TOOLS_DIR}"
166+
167+
# Verify the unzipped layout matches what build.sh and the QNN CMake
168+
# files actually consume. If any of these are missing, a future SDK
169+
# release likely changed the directory shape; updating
170+
# HEXAGON_SDK_VERSION / HEXAGON_TOOLS_VERSION in qnn_config.sh (or the
171+
# extraction layout below) is the fix.
172+
for hexagon_required_path in \
173+
"${HEXAGON_SDK_ROOT}" \
174+
"${HEXAGON_SDK_ROOT}/build/cmake/hexagon_toolchain.cmake" \
175+
"${HEXAGON_TOOLS_ROOT}" \
176+
"${HEXAGON_TOOLS_ROOT}/Tools/target/hexagon"; do
177+
if [ ! -e "${hexagon_required_path}" ]; then
178+
echo "[Hexagon] ERROR: expected path not found: ${hexagon_required_path}" >&2
179+
echo "[Hexagon] Hexagon SDK ${HEXAGON_SDK_VERSION} or tools ${HEXAGON_TOOLS_VERSION} layout differs from what we pinned." >&2
180+
ls -la "$(dirname "${hexagon_required_path}")" >&2 || true
181+
exit 1
182+
fi
183+
done
184+
185+
echo "Set HEXAGON_SDK_ROOT=${HEXAGON_SDK_ROOT}"
186+
echo "Set HEXAGON_TOOLS_ROOT=${HEXAGON_TOOLS_ROOT}"
187+
}
188+
112189
setup_libcpp() {
113190
clang_version=$1
114191
LLVM_VERSION="14.0.0"

backends/qualcomm/scripts/qnn_config.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@
88
# QNN SDK Configuration
99
QNN_VERSION="2.37.0.250724"
1010
QNN_ZIP_URL="https://softwarecenter.qualcomm.com/api/download/software/sdks/Qualcomm_AI_Runtime_Community/All/${QNN_VERSION}/v${QNN_VERSION}.zip"
11+
12+
# Hexagon SDK Configuration (used only by direct-mode CI build).
13+
# HEXAGON_TOOLS_VERSION must match the toolchain shipped inside HEXAGON_SDK_VERSION.
14+
HEXAGON_SDK_VERSION="6.5.0.0"
15+
HEXAGON_TOOLS_VERSION="19.0.07"
16+
HEXAGON_SDK_ZIP_URL="https://apigwx-aws.qualcomm.com/qsc/public/v1/api/download/software/sdks/Hexagon_SDK/Linux/Debian/${HEXAGON_SDK_VERSION}/Hexagon_SDK_Linux.zip"
17+
# SHA-256 of the downloaded zip. Recompute and update when HEXAGON_SDK_VERSION changes. Command to gen followin sha: sha256sum Hexagon_SDK_Linux.zip
18+
HEXAGON_SDK_ZIP_SHA256="668626f75c38ce1ca993768953db9bf4b632753c3e32ed8363a8287e3aaffc9a"

extension/data_loader/mman.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,17 @@ ET_INLINE off_t get_mmap_offset(size_t offset) {
4848
* Hint the kernel to prefetch pages eagerly and to optimize for sequential
4949
* reads. Intended to reduce page-fault stutter during model initialization
5050
* when the caller does not want to mlock the pages into RAM.
51+
*
52+
* MADV_WILLNEED / MADV_SEQUENTIAL are absent on some POSIX libcs (e.g. the
53+
* Hexagon DSP toolchain).
5154
*/
5255
ET_INLINE void madvise_pages_willneed_sequential(void* addr, size_t len) {
56+
#ifdef MADV_WILLNEED
5357
::madvise(addr, len, MADV_WILLNEED);
58+
#endif
59+
#ifdef MADV_SEQUENTIAL
5460
::madvise(addr, len, MADV_SEQUENTIAL);
61+
#endif
5562
}
5663

5764
/**

0 commit comments

Comments
 (0)