# Chantier — matmul HMX robuste pour le DENSE sur HTP (crash 0x2e) Objet : faire tourner le dense (qwen3) sur HTP en **HMX** (prefill ~295) au lieu de HVX (~40), maintenant que le `.pte` est retiré (l'engine est le seul runtime LLM). Bloqueur = crash du matmul HMX sur le dense. ## Symptôme Prefill dense (qwen3-4B Q4_0) sur HTP avec HMX (défaut) → `ggml-hex: dspqueue_read failed: 0x0000002e` dans `flush_pending` → `llama_decode`, dès que le prompt dépasse ~12-16 tokens. HVX (`GGML_HEXAGON_USE_HMX=0`) ne crashe pas. La 3.5 (hybride) ne crashe pas en HMX. ## Repro ``` GGML_HEXAGON_OPLOG=1 GGML_HEXAGON_OPBATCH=1 LD_LIBRARY_PATH=./lib ADSP_LIBRARY_PATH=./lib \ ./llama-cli -m Qwen3-4B-Q4_0.gguf -ngl 99 -dev HTP0 -t 4 --reasoning off -cnv -st --temp 0 -n 2 \ -p 'un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize' ``` ## Hypothèse ÉCARTÉE : overflow fp16 Le matmul fautif a une **activation max-abs = 9.8** (mesuré via OPLOG `s1_amax`), très en-dessous de la plage fp16 (65504). **Ce n'est PAS un overflow.** Confirmé : clamp ±65504 (conversion f32→f16) ET scaling activations ÷32 puis ÷256 (avec ×S en sortie) n'ont rien changé au crash. Donc ni l'entrée ni l'accumulateur fp16 ne débordent. ## Localisation (via instrumentation, voir plus bas) Dernier op avant le 0x2e = un **MUL_MAT q4_0**, juste après `FLASH_ATTN_EXT` : ``` KZOP MUL_MAT dst=[2560,38,1,1] src0=q4_0[4096,2560] src1=f32[4096,38] s1_amax=9.8 <- CRASHE ``` C'est l'**o_proj** (projection de sortie d'attention : in 4096 = 32 têtes×128, out 2560 = hidden), donc **k=4096**. Les autres MUL_MAT du graphe ont **k=2560** et passent (ffn gate/up `[2560,4096]`, qkv `[2560,1024]`). → **suspect : le chemin HMX pour k=4096** (n_dot_tiles = 4096/32 = 128). `OPFILTER=MUL_MAT` (matmuls→CPU) supprime le crash, confirmant que c'est bien un MUL_MAT HMX. ## Instrumentation — ce qui marche / ne marche pas sur ce device (non-rooté) - ❌ **FARF DSP** : bloqué. Le watcher cherche `.farf` dans `/vendor/lib/rfsa/adsp` (lecture seule sans root). `FARF(RUNTIME_HIGH)` ne sort pas en logcat sans ce fichier. `getenv` ne marche PAS côté skel DSP. - ❌ **GGML_HEXAGON_VERBOSE** : utilise `GGML_LOG_DEBUG`, masqué par le cli. - ✅ **OPLOG CPU-side** (committé) : `enqueue_op` (ggml-hexagon.cpp) logge en `fprintf(stderr)` chaque op (type, dims, type/dims src, max-abs activation f32), gaté par `GGML_HEXAGON_OPLOG`. Combiné à `GGML_HEXAGON_OPBATCH=1` (1 op par flush), le **dernier KZOP avant le 0x2e = l'op fautive**. - ℹ️ Build debug FARF dispo si besoin : `cmake -DHEXAGON_HTP_DEBUG=ON` dans le sous-build htp-vNN → active `FARF_HIGH=1` (logs par-matmul m/k/n), mais inutile ici faute de capture (root). ## Avancement (27/05, session chantier) Hypothèses **éliminées** par mesure (OPLOG + isolation) : - ❌ Overflow fp16 d'entrée : activation du matmul fautif amax=**9.8** (≪ 65504). Clamp ±65504 = sans effet. - ❌ Overflow d'accumulation : scaling ÷32 puis ÷256 (×S en sortie) = sans effet. - ❌ NaN/Inf dans les inputs : instrumentation `nan=0 inf=0` sur tous les ops avant le crash (le `amax` initial ratait les NaN car `v>amax` est faux sur NaN ; corrigé pour les détecter → toujours 0). - ❌ Shape pure : le matmul isolé (test-backend-ops, q4_0 k=4096 n=38/512/16 m=2560, **données synthétiques**) **ne crashe PAS** sur HTP (finit, finite). Donc ce n'est pas la shape seule. **Op fautive confirmée** : le **o_proj** `MUL_MAT q4_0 [4096,2560] × f32[4096,38]` (k=4096) juste après FLASH_ATTN_EXT (en OPBATCH=1, le flush de ce matmul survient juste après le log de l'ADD suivant → crash). Inputs propres. **Conclusion** : crash **value/context-spécifique** — un pattern de valeurs réelles (non reproduit par du random synthétique), OU un état de contexte/VTCM/session hérité des ops précédentes (FA), qui fait fauter le MAC HMX. ## Prochaines étapes (révisées après l'avancement ci-dessus) 1. **Dump-and-replay** (tranche value-vs-context) : capturer la vraie activation `[4096,38]` + le poids q4_0 du o_proj fautif (CPU-side dans `enqueue_op`, écrire en fichier), puis rejouer EXACTEMENT ces octets dans un harness qui lance `hmx_matmul_q_f32` sur HTP. Crash en replay → **value-pattern** (pattern de bits précis qui faute le MAC/dequant) ; pas de crash → **context/état** (VTCM/session de la séquence d'ops, ex. FA avant). 2. **Si value-pattern** : trouver l'octet/valeur déclencheur (bisection sur les lignes de l'activation), inspecter le dequant q4_0→fp16 (`dequantize_x4x2_*`) et le MAC (`core_dot_chunk_fp16`, `Q6_mxmem_*`) sur ce pattern. 3. **Si context** : instrumenter l'état VTCM/session avant le matmul (reset explicite entre ops ? interaction FA→matmul). 4. **Instrumentation DSP** (si nécessaire, FARF bloqué sans root) : ajouter dans le skel un **log-vers-buffer-hôte** (écrire diagnostics dans une zone VTCM/DDR que le CPU relit) — contourne l'absence de capture FARF. 5. **Fallback pragmatique** si le fix est trop lourd : gater le HMX pour cette classe de matmul → HVX (comme le gate SSM_CONV). Mais le dense est déjà 100% HVX (USE_HMX=0) côté JNI, donc le fallback = statu quo. ⚠ L'oracle `test-backend-ops -o MUL_MAT` a des FAIL `CPU=nan` PRÉ-EXISTANTS (réf CPU NaN sur petits q4_0) — non fiable comme gate ; valider par la cohérence de génération + l'absence de crash, pas par cet oracle. ## État **Non-bloquant** : le dense tourne déjà sur HTP en **HVX** (~40 prefill, stable, cohérent) via le routage auto du JNI (`USE_HMX=0` pour le dense). Ce chantier = optimisation (40→295). 3.5 intacte (HMX + option C). Outils committés : OPLOG (`GGML_HEXAGON_OPLOG`, dormant par défaut).