From fdd05f26e3e5077a9ad6f69bf7bf2d27e2767acf Mon Sep 17 00:00:00 2001 From: Siddartha Pothapragada Date: Mon, 20 Jul 2026 19:26:30 -0700 Subject: [PATCH] Fix embedding lowering crash when QNN_SDK_ROOT is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: D110960687 (pytorch/executorch#20686) rewrote `Embedding.define_node` to select between the optimized and legacy pcq-embedding lowerings based on `is_qnn_sdk_version_less_than("2.48")`. That helper resolves the SDK version via `get_sdk_build_id`, which builds a path from `os.environ["QNN_SDK_ROOT"]`. `define_node` runs during AOT partitioning (`is_node_supported`), and AOT-only environments do not necessarily have `QNN_SDK_ROOT` set. In that case `os.path.join(os.environ.get("QNN_SDK_ROOT", None), ...)` raised `TypeError: expected str, bytes or os.PathLike object, not NoneType`, breaking every QNN lowering that contains an embedding — including the internal `test_dummy_llama_qnn_16a4w_aot_and_runtime`. Fall back to the legacy embedding lowering (valid on all QNN versions) when `QNN_SDK_ROOT` is unavailable, so the version-gated optimization is only taken when the SDK version can actually be determined. Also make `get_sdk_build_id` raise a clear `EnvironmentError` instead of a cryptic `TypeError` when the environment variable is missing. This diff was authored with Claude Code. Differential Revision: D112944232 --- backends/qualcomm/builders/op_embedding.py | 7 ++++++- backends/qualcomm/utils/check_qnn_version.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backends/qualcomm/builders/op_embedding.py b/backends/qualcomm/builders/op_embedding.py index ea4ccc26dba..e8a60e120d7 100644 --- a/backends/qualcomm/builders/op_embedding.py +++ b/backends/qualcomm/builders/op_embedding.py @@ -3,6 +3,7 @@ # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. +import os from typing import Dict import executorch.backends.qualcomm.python.PyQnnManagerAdaptor as PyQnnManager @@ -242,6 +243,10 @@ def define_node( node: torch.fx.Node, nodes_to_wrappers: Dict[torch.fx.Node, PyQnnManager.TensorWrapper], ) -> PyQnnManager.PyQnnOpWrapper: - if is_qnn_sdk_version_less_than("2.48"): + # The optimized pattern requires QNN 2.48+. Resolving the SDK version + # relies on QNN_SDK_ROOT; when it is unavailable (e.g. AOT-only + # environments) fall back to the legacy lowering, which is valid on all + # QNN versions. + if not os.environ.get("QNN_SDK_ROOT") or is_qnn_sdk_version_less_than("2.48"): return self.define_node_legacy(node, nodes_to_wrappers) return self.define_node_optimize(node, nodes_to_wrappers) diff --git a/backends/qualcomm/utils/check_qnn_version.py b/backends/qualcomm/utils/check_qnn_version.py index e6cccd56984..b2342d312bf 100644 --- a/backends/qualcomm/utils/check_qnn_version.py +++ b/backends/qualcomm/utils/check_qnn_version.py @@ -26,8 +26,13 @@ def _get_qnn_host_lib_dir_name() -> str: def get_sdk_build_id(): + qnn_sdk_root = os.environ.get("QNN_SDK_ROOT") + if not qnn_sdk_root: + raise EnvironmentError( + "QNN_SDK_ROOT must be set to query the QNN SDK build id." + ) htp_library_path = os.path.join( - os.environ.get("QNN_SDK_ROOT", None), + qnn_sdk_root, "lib", _get_qnn_host_lib_dir_name(), get_qnn_lib_name("QnnHtp"),