Commit 76a67730f for llama.cpp
commit 76a67730ffc7d650dffa4db04d96be84a59e7ef3
Author: Aparna M P <aparmp@qti.qualcomm.com>
Date: Sun Nov 8 05:55:20 2026 +0530
hexagon: support for automatic loading of libhtp without legacy env vars (#31781)
* hexagon: use absolute skel path
* hexagon: support non-ascii dir and long path in skel-abs path
* hex-libdir: add support for FASTRPC_MANAGE_DSP_LIBRARY_PATH and GGML_HEXAGON_LIBHTP_DIR on all platforms
* hex-libdir: keep extra fwd slash in abs path on Android and Linux
* hex-libpath: always start with absolute path then fallback to session ctl then env
* hex-libdir: add CDSP1,2,3 to the fallback, needed for the IQ10
---------
Co-authored-by: Max Krasnyansky <maxk@qti.qualcomm.com>
diff --git a/ggml/src/ggml-hexagon/ggml-hexagon.cpp b/ggml/src/ggml-hexagon/ggml-hexagon.cpp
index 185aa4d46..c38d0005c 100644
--- a/ggml/src/ggml-hexagon/ggml-hexagon.cpp
+++ b/ggml/src/ggml-hexagon/ggml-hexagon.cpp
@@ -2117,34 +2117,80 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
}
}
- char session_uri[256];
- {
- char htp_uri[256];
- snprintf(htp_uri, sizeof(htp_uri), "file:///libggml-htp-v%u.so?htp_iface_skel_handle_invoke&_modver=1.0", opt_arch);
+ auto open_session = [&](const std::string & prefix, std::string & uri_out) -> int {
+ const std::string htp_uri = prefix + "libggml-htp-v" + std::to_string(opt_arch) +
+ ".so?htp_iface_skel_handle_invoke&_modver=1.0";
+
+ std::vector<char> session_uri(htp_uri.size() + 256);
struct remote_rpc_get_uri u = {};
u.session_id = this->session_id;
u.domain_name = const_cast<char *>(dom_name.c_str());
u.domain_name_len = dom_name.size();
- u.module_uri = const_cast<char *>(htp_uri);
- u.module_uri_len = strlen(htp_uri);
- u.uri = session_uri;
- u.uri_len = sizeof(session_uri);
+ u.module_uri = const_cast<char *>(htp_uri.c_str());
+ u.module_uri_len = htp_uri.size();
+ u.uri = session_uri.data();
+ u.uri_len = session_uri.size();
int err = remote_session_control(FASTRPC_GET_URI, (void *) &u, sizeof(u));
if (err != AEE_SUCCESS) {
- snprintf(session_uri, sizeof(session_uri), "%s&_dom=%s&_session=%u",
- htp_uri, dom_name.c_str(), this->session_id);
+ snprintf(session_uri.data(), session_uri.size(), "%s&_dom=%s&_session=%u",
+ htp_uri.c_str(), dom_name.c_str(), this->session_id);
+
+ HEX_VERBOSE("ggml-hex: %s failed to get URI (physical %d, virtual %d) : error 0x%x. Falling back to single session URI: %s\n",
+ this->c_name(), phys_idx, virt_idx, err, session_uri.data());
+ }
+
+ uri_out = session_uri.data();
+ return htp_iface_open(session_uri.data(), &this->handle);
+ };
+
+ const std::string lib_dir = htp_lib_dir();
+
+ std::string session_uri;
+ int err = AEE_EFAILED;
- GGML_LOG_WARN("ggml-hex: %s failed to get URI (physical %d, virtual %d) : error 0x%x. Falling back to single session URI: %s\n",
- this->c_name(), phys_idx, virt_idx, err, session_uri);
+ // 1. Try opening session with absolute path in URI
+ if (!lib_dir.empty()) {
+ std::string abs_prefix = "file:///" + lib_dir;
+ if (abs_prefix.back() != '/' && abs_prefix.back() != '\\') {
+ abs_prefix += "/";
}
+
+ HEX_VERBOSE("ggml-hex: %s trying absolute path URI: %s\n", this->c_name(), abs_prefix.c_str());
+ err = open_session(abs_prefix, session_uri);
+ if (err != AEE_SUCCESS) {
+ HEX_VERBOSE("ggml-hex: %s failed to open session with absolute path : uri %s error 0x%x\n",
+ this->c_name(), session_uri.c_str(), err);
+ }
+ }
+
+ // 2. Fallback to DSP search path via FASTRPC_MANAGE_DSP_LIBRARY_PATH
+ if (err != AEE_SUCCESS && !lib_dir.empty()) {
+ int sp_err = htpdrv_set_dsp_search_path(this->domain_id, lib_dir, this->c_name());
+ if (sp_err == AEE_SUCCESS) {
+ err = open_session("file:///", session_uri);
+ if (err != AEE_SUCCESS) {
+ HEX_VERBOSE("ggml-hex: %s failed to open session with search path : uri %s error 0x%x\n",
+ this->c_name(), session_uri.c_str(), err);
+ }
+ }
+ }
+
+ // 3. Fallback to process environment variables
+ if (err != AEE_SUCCESS && !lib_dir.empty()) {
+ HEX_VERBOSE("ggml-hex: %s falling back to process env for DSP search path\n", this->c_name());
+ htpdrv_set_dsp_env_path(lib_dir, this->c_name());
+ err = open_session("file:///", session_uri);
+ }
+
+ // Bare name attempt if lib_dir is empty and nothing was tried yet
+ if (err != AEE_SUCCESS && lib_dir.empty()) {
+ err = open_session("file:///", session_uri);
}
- // Open session
- int err = htp_iface_open(session_uri, &this->handle);
if (err != AEE_SUCCESS) {
- GGML_LOG_ERROR("ggml-hex: %s failed to open session : uri %s error 0x%x\n", this->c_name(), session_uri, err);
+ GGML_LOG_ERROR("ggml-hex: %s failed to open session : uri %s error 0x%x\n", this->c_name(), session_uri.c_str(), err);
throw std::runtime_error("ggml-hex: failed to open session (see log for details)");
}
@@ -2194,7 +2240,7 @@ void ggml_hexagon_session::allocate(const ggml_hexagon_device_config & config) n
}
GGML_LOG_INFO("ggml-hex: %s new session : session-id %d domain-id %d uri %s handle 0x%lx\n", this->c_name(),
- this->session_id, this->domain_id, session_uri, (unsigned long) this->handle);
+ this->session_id, this->domain_id, session_uri.c_str(), (unsigned long) this->handle);
const size_t req_q_size = (sizeof(htp_opbatch_req) * opt_opqueue * 2) + 1024;
const size_t rsp_q_size = (sizeof(htp_opbatch_rsp) * opt_opqueue * 2) + 1024;
@@ -3349,8 +3395,8 @@ static void ggml_hexagon_discover_devices() {
discovery_supported = true;
const int n_domains = std::min(domain_info.sys.num_domains, (int) domains.size());
for (int i = 0; i < n_domains; i++) {
- GGML_LOG_INFO("ggml-hex: FASTRPC_GET_DOMAINS[%d]: type %d id %d name '%s' status %d instance-id %d\n",
- i, (int) domains[i].type, domains[i].id, domains[i].name, domains[i].status, domains[i].instance_id);
+ HEX_VERBOSE("ggml-hex: FASTRPC_GET_DOMAINS[%d]: type %d id %d name '%s' status %d instance-id %d\n",
+ i, (int) domains[i].type, domains[i].id, domains[i].name, domains[i].status, domains[i].instance_id);
if (domains[i].type != FASTRPC_NSP) {
GGML_LOG_DEBUG("ggml-hex: skipping non-CDSP domain (type=%d)\n", (int) domains[i].type);
continue;
@@ -3364,10 +3410,10 @@ static void ggml_hexagon_discover_devices() {
domains[i].instance_id, domains[i].id, domains[i].name);
}
} else {
- GGML_LOG_WARN("ggml-hex: FASTRPC_GET_DOMAINS fetch failed (0x%x), using static CDSP domains\n", (unsigned) err);
+ HEX_VERBOSE("ggml-hex: FASTRPC_GET_DOMAINS fetch failed (0x%x), using static CDSP domains\n", (unsigned) err);
}
} else if (err != AEE_SUCCESS) {
- GGML_LOG_DEBUG("ggml-hex: FASTRPC_GET_DOMAINS query failed (0x%x), using static CDSP domains\n", (unsigned) err);
+ HEX_VERBOSE("ggml-hex: FASTRPC_GET_DOMAINS query failed (0x%x), using static CDSP domains\n", (unsigned) err);
}
// Populate domain IDs and names for all configured devices
diff --git a/ggml/src/ggml-hexagon/htp-drv.cpp b/ggml/src/ggml-hexagon/htp-drv.cpp
index 437e367c9..435909bf9 100644
--- a/ggml/src/ggml-hexagon/htp-drv.cpp
+++ b/ggml/src/ggml-hexagon/htp-drv.cpp
@@ -1,7 +1,9 @@
+#include <algorithm>
#include <filesystem>
#include <set>
#include <sstream>
#include <string>
+#include <vector>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
@@ -28,6 +30,13 @@
#include <domain.h>
+extern int opt_verbose;
+
+#ifndef HEX_VERBOSE
+#define HEX_VERBOSE(...) \
+ if (opt_verbose) GGML_LOG_DEBUG(__VA_ARGS__)
+#endif
+
//
// Driver API types
//
@@ -423,3 +432,188 @@ int htpdrv_get_arch(int domain, int * arch) {
*arch = (int) ((val >> 4) * 10 + (val & 0x0f));
return 0;
}
+
+// Directory of the HTP library as a path with a trailing slash, or empty if it cannot be resolved.
+// Checks GGML_HEXAGON_LIBHTP_DIR first, then the module directory and ../lib.
+// On Windows, non-ASCII directories or paths with spaces use the 8.3 short name.
+std::string htp_lib_dir() {
+ namespace fs = std::filesystem;
+
+ std::vector<fs::path> candidates;
+
+ const char * env_dir = getenv("GGML_HEXAGON_LIBHTP_DIR");
+ if (env_dir && *env_dir) {
+ std::error_code ec;
+ fs::path p = fs::absolute(env_dir, ec);
+ if (!ec) {
+ candidates.push_back(p);
+ }
+ }
+
+#ifdef _WIN32
+ HMODULE hm = nullptr;
+ if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCWSTR) &htp_lib_dir, &hm)) {
+ std::wstring buf(32768, L'\0');
+ DWORD n = GetModuleFileNameW(hm, buf.data(), (DWORD) buf.size());
+ if (n > 0 && n < buf.size()) {
+ buf.resize(n);
+ std::error_code ec;
+ fs::path mod_path = fs::canonical(buf, ec);
+ if (ec) {
+ mod_path = fs::absolute(buf, ec);
+ }
+ const fs::path mod_dir = mod_path.parent_path();
+ candidates.push_back(mod_dir);
+ candidates.push_back(mod_dir / "lib");
+ candidates.push_back(mod_dir / ".." / "lib");
+ }
+ }
+#elif defined(__linux__) || defined(__ANDROID__)
+ Dl_info info;
+ if (dladdr((void *) &htp_lib_dir, &info) && info.dli_fname) {
+ std::error_code ec;
+ fs::path mod_path = fs::canonical(info.dli_fname, ec);
+ if (ec) {
+ mod_path = fs::absolute(info.dli_fname, ec);
+ }
+ const fs::path mod_dir = mod_path.parent_path();
+ candidates.push_back(mod_dir);
+ candidates.push_back(mod_dir / "lib");
+ candidates.push_back(mod_dir / ".." / "lib");
+ }
+#endif
+
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ const fs::path & d = candidates[i];
+ std::error_code ec;
+ if (!fs::is_directory(d, ec)) {
+ continue;
+ }
+
+ // For auto-discovered directories, verify it contains an HTP library
+ const bool is_env = (env_dir && i == 0);
+ if (!is_env) {
+ bool found = false;
+ for (const auto & entry : fs::directory_iterator(d, ec)) {
+ if (ec) {
+ break;
+ }
+ const std::string fn = entry.path().filename().string();
+ if (fn.rfind("libggml-htp-", 0) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ }
+
+ std::error_code ec_canon;
+ fs::path abs_d = fs::canonical(d, ec_canon);
+ if (ec_canon) {
+ abs_d = fs::absolute(d, ec_canon);
+ }
+
+#ifdef _WIN32
+ std::wstring dir = abs_d.wstring();
+ auto is_ascii = [](const std::wstring & s) {
+ return std::all_of(s.begin(), s.end(), [](wchar_t c) { return c < 0x80; });
+ };
+ auto has_spaces = [](const std::wstring & s) {
+ return std::any_of(s.begin(), s.end(), [](wchar_t c) { return c == L' '; });
+ };
+
+ if (!is_ascii(dir) || has_spaces(dir)) {
+ DWORD m = GetShortPathNameW(dir.c_str(), nullptr, 0);
+ if (m > 0) {
+ std::wstring sp(m, L'\0');
+ DWORD res = GetShortPathNameW(dir.c_str(), sp.data(), (DWORD) sp.size());
+ if (res > 0 && res < sp.size()) {
+ sp.resize(res);
+ if (is_ascii(sp)) {
+ dir = sp;
+ }
+ }
+ }
+ if (!is_ascii(dir)) {
+ continue;
+ }
+ }
+ if (dir.back() != L'\\' && dir.back() != L'/') {
+ dir += L'\\';
+ }
+ return std::string(dir.begin(), dir.end());
+#else
+ std::string dir = abs_d.string();
+ if (dir.back() != '/') {
+ dir += '/';
+ }
+ return dir;
+#endif
+ }
+
+ return {};
+}
+
+int htpdrv_set_dsp_search_path(int domain_id, const std::string & path, const char * dev_name) {
+ if (path.empty()) {
+ return AEE_SUCCESS;
+ }
+
+ std::string clean_path = path;
+ while (clean_path.size() > 1 && (clean_path.back() == '/' || clean_path.back() == '\\')) {
+ clean_path.pop_back();
+ }
+
+ fastrpc_manage_dsp_lib_path p {};
+ p.effec_domain_id = domain_id;
+ p.type = FASTRPC_SET_PATH;
+ p.path = const_cast<char *>(clean_path.c_str());
+ p.pathlen = clean_path.size() + 1;
+
+ int err = remote_session_control(FASTRPC_MANAGE_DSP_LIBRARY_PATH, (void *) &p, sizeof(p));
+ if (err != AEE_SUCCESS && domain_id != -1) {
+ p.effec_domain_id = -1;
+ err = remote_session_control(FASTRPC_MANAGE_DSP_LIBRARY_PATH, (void *) &p, sizeof(p));
+ }
+
+ const char * name = dev_name ? dev_name : "driver";
+ if (err == AEE_SUCCESS) {
+ GGML_LOG_INFO("ggml-hex: %s set DSP search path (domain %d) to %s\n", name, p.effec_domain_id, clean_path.c_str());
+ return AEE_SUCCESS;
+ }
+
+ HEX_VERBOSE("ggml-hex: %s FASTRPC_MANAGE_DSP_LIBRARY_PATH failed (0x%x)\n", name, err);
+ return err;
+}
+
+void htpdrv_set_dsp_env_path(const std::string & path, const char * dev_name) {
+ if (path.empty()) {
+ return;
+ }
+
+ std::string clean_path = path;
+ while (clean_path.size() > 1 && (clean_path.back() == '/' || clean_path.back() == '\\')) {
+ clean_path.pop_back();
+ }
+
+ const char * name = dev_name ? dev_name : "driver";
+ HEX_VERBOSE("ggml-hex: %s setting DSP search path via process env to %s\n", name, clean_path.c_str());
+
+ static const char * const env_names[] = {
+ "ADSP_LIBRARY_PATH",
+ "CDSP_LIBRARY_PATH",
+ "CDSP1_LIBRARY_PATH",
+ "CDSP2_LIBRARY_PATH",
+ "CDSP3_LIBRARY_PATH",
+ };
+ for (const char * env_name : env_names) {
+#ifdef _WIN32
+ SetEnvironmentVariableA(env_name, clean_path.c_str());
+#else
+ setenv(env_name, clean_path.c_str(), 1);
+#endif
+ }
+}
diff --git a/ggml/src/ggml-hexagon/htp-drv.h b/ggml/src/ggml-hexagon/htp-drv.h
index 8232780e7..c88a72940 100644
--- a/ggml/src/ggml-hexagon/htp-drv.h
+++ b/ggml/src/ggml-hexagon/htp-drv.h
@@ -120,4 +120,10 @@ HTPDRV_API int remote_system_request(system_req_payload * req);
#ifdef __cplusplus
}
+
+#include <string>
+
+std::string htp_lib_dir();
+int htpdrv_set_dsp_search_path(int domain_id, const std::string & path, const char * dev_name = nullptr);
+void htpdrv_set_dsp_env_path(const std::string & path, const char * dev_name = nullptr);
#endif
diff --git a/scripts/snapdragon/run.py b/scripts/snapdragon/run.py
index 451e46086..433640920 100755
--- a/scripts/snapdragon/run.py
+++ b/scripts/snapdragon/run.py
@@ -40,6 +40,7 @@ MANAGED_ENV_NAMES = (
"GGML_HEXAGON_ETM",
"GGML_HEXAGON_ARCH",
"GGML_HEXAGON_OPTRACE",
+ "GGML_HEXAGON_LIBHTP_DIR",
"GGML_OPENCL_PLATFORM",
"GGML_OPENCL_DEVICE",
"GGML_OPENCL_OPFILTER",
@@ -50,26 +51,12 @@ MANAGED_ENV_NAMES = (
"GGML_OPENCL_ADRENO_XMEM_GEMM",
"GGML_OPENCL_ADRENO_USE_LARGE_BUFFER",
"GGML_SCHED_DEBUG",
- "ADSP_LIBRARY_PATH",
- "CDSP_LIBRARY_PATH",
- "CDSP1_LIBRARY_PATH",
- "CDSP2_LIBRARY_PATH",
- "CDSP3_LIBRARY_PATH",
"MTMD_BACKEND_DEVICE",
"D",
"DEVICE",
)
-DSP_LIBRARY_ENV_NAMES = (
- "ADSP_LIBRARY_PATH",
- "CDSP_LIBRARY_PATH",
- "CDSP1_LIBRARY_PATH",
- "CDSP2_LIBRARY_PATH",
- "CDSP3_LIBRARY_PATH",
-)
-
-
def parse_target(target_str):
if not target_str:
return None, None, None
@@ -112,10 +99,7 @@ def default_target_dir(target_type, target_transport, home_dir):
def library_env_parts(lib_dir):
- env_parts = [f"LD_LIBRARY_PATH={lib_dir}"]
- for name in DSP_LIBRARY_ENV_NAMES:
- env_parts.append(f"{name}={lib_dir}")
- return env_parts
+ return [f"LD_LIBRARY_PATH={lib_dir}"]
def adb_root(adb_base):
@@ -242,6 +226,7 @@ def main():
parser.add_argument("--hex-ar-scatter", help="Enable (1) or disable (0) reduce-scatter for fused ALLREDUCE+ADD (GGML_HEXAGON_AR_SCATTER)")
parser.add_argument("--hex-etm", help="Enable Embedded Trace Macrocell hardware tracing / trace logging (GGML_HEXAGON_ETM)")
parser.add_argument("--hex-arch", help="Target Hexagon NPU architecture version override (v73, v75, v79, v81, etc.) (GGML_HEXAGON_ARCH)")
+ parser.add_argument("--hex-libhtp-dir", default=os.environ.get("GGML_HEXAGON_LIBHTP_DIR"), help="Directory containing libggml-htp-v<arch>.so (GGML_HEXAGON_LIBHTP_DIR)")
parser.add_argument("--hex-optrace", help="Trace buffer size in number of records (GGML_HEXAGON_OPTRACE)")
# OpenCL specific parameters
@@ -396,6 +381,7 @@ def main():
set_env("GGML_HEXAGON_AR_SCATTER", args.hex_ar_scatter)
set_env("GGML_HEXAGON_ETM", args.hex_etm)
set_env("GGML_HEXAGON_ARCH", args.hex_arch)
+ set_env("GGML_HEXAGON_LIBHTP_DIR", args.hex_libhtp_dir)
set_env("GGML_HEXAGON_OPTRACE", args.hex_optrace)
set_env("MTMD_BACKEND_DEVICE", args.mtmd_device)
@@ -572,8 +558,6 @@ def main():
for k in MANAGED_ENV_NAMES:
local_env.pop(k, None)
- for name in DSP_LIBRARY_ENV_NAMES:
- local_env[name] = lib_dir
for k, v in env_vars.items():
local_env[k] = v