QNN sharded LLM export fails: ResolveDebugHandle is not the last edge pass (dep_table entry overwritten by SplitGraph registration)
メンテナーはふだん 1 日以内に返信
評価
この issue はまだ評価されていません。
説明
🐛 Describe the bug
Exporting any sharded LLM through the QNN static-llama flow (num_sharding > 1, i.e. every model ≥ ~3B such as llama3_2-3b_instruct, or a Qwen3-4B registered locally) fails with:
AssertionError: Please ensure ResolveDebugHandle is the last executed edge pass.
File "backends/qualcomm/_passes/qnn_pass_manager.py", line 432, in _validate_edge_passes
Non-sharded models (Qwen3 0.6B/1.7B, etc.) export fine, which is probably why CI has not caught this.
Root cause
When sharding is enabled, the dynamically created SplitGraph pass is registered like this (3 call sites):
dep_table[SplitGraph] = [FoldQDQ]
dep_table[TagQuantIO] = [SplitGraph] # <-- overwrites TagQuantIO: [LayoutTransform]
examples/qualcomm/oss_scripts/llama/wrappers/llm_wrappers.py(two sites)examples/models/llama/export_llama_lib.py(_export_llama, QNN branch)
The assignment replaces the pre-existing entry TagQuantIO: [LayoutTransform] from get_passes_dependency_for_capture_program(), silently dropping the LayoutTransform → TagQuantIO constraint. With that edge gone, the topological solver schedules TagQuantIO (and transitively ResolveDebugHandle, whose only constraint is "after TagQuantIO") too early, so ResolveDebugHandle is no longer last and the (recently added) _validate_edge_passes assertion fires.
Broken schedule tail: ... → ResolveDebugHandle → AnnotateGetAttr → InsertCastForFpActQuantizedWeight
Minimal repro (no device, no weights, runs in seconds):
from torch.fx.passes.infra.pass_manager import this_before_that_pass_constraint
from executorch.backends.qualcomm._passes import FoldQDQ, TagQuantIO
from executorch.backends.qualcomm._passes.qnn_pass_manager import get_qnn_pass_manager_cls
from executorch.extension.llm.custom_ops import model_sharding
cls = get_qnn_pass_manager_cls()
pm = cls()
passes_job = cls.get_capture_program_passes()
dep_table = cls.get_passes_dependency_for_capture_program()
SplitGraph, setting = model_sharding.get_split_graph_pass(36, shares=4, pattern=r"^layers\.(\d+)")
passes_job[SplitGraph] = setting
dep_table[SplitGraph] = [FoldQDQ]
dep_table[TagQuantIO] = [SplitGraph] # verbatim from the call sites above
for that, these in dep_table.items():
for this in these:
pm.add_constraint(this_before_that_pass_constraint(this, that))
for p in passes_job:
pm.add_pass(p)
pm.solve_constraints()
order = [getattr(p, "__name__", str(p)) for p in pm.passes]
assert order[-1] == "ResolveDebugHandle", f"tail: {order[-3:]}" # fails on main
End-to-end repro: python examples/qualcomm/oss_scripts/llama/llama.py --decoder_model llama3_2-3b_instruct ... (any sharded model).
Proposed fix (PR to follow):
- Call sites: append instead of overwrite —
dep_table.setdefault(TagQuantIO, []).append(SplitGraph). - Structural guarantee in
QnnPassManager.get_to_edge_transform_passes: constrainResolveDebugHandleafter every other pass inpasses_job, so dynamically registered passes can never be scheduled after it. This keeps the invariant that_validate_edge_passesasserts, instead of relying on call sites to remember it.
Verified: with the fix, a sharded Qwen3-4B (16a4w, 4 shards) exports and runs on SM8650 (113.8 tok/s prefill, 7.24 tok/s decode), and TestQNNQuantizedOperator.test_qnn_backend_abs --enable_x86_64 passes.
Found and analyzed with AI assistance (Claude Code).
Versions
- executorch: main @ 5e21c13cc9e34fa2922d5708ba5962e3e365afdb (2026-10-05)
- QNN SDK: 2.37.0.250724, SoC: SM8650 (HTP v75)
- torch 2.14.0+cpu, Python 3.11, Ubuntu (Linux 5.15)
cc @cbilgin @psiddh @larryliu0820 @mergennachin @cccclai @helunwencser @jackzhxng @digantdesai
- 主要言語
- Python
- スター
- 5.1k
- フォーク
- 1.2k
- 平均マージ
- 2日 14分
- マージ済み PR(30日)
- 704
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
pytorch/executorch のほかの issue
-
Portable remainder kernel: wrong sign for integers and float64 rounded to float対応中かも @JakeStevens が 6 日前に担当しました。 オープンbug module: kernels triaged
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
pytorch/executorch#23682 · 担当者 2 名 ·
メンテナーはふだん 1 日以内に返信
-
enhancement triaged
難易度 2/5 半日 初心者へのやさしさ 68/100
pytorch/executorch#21640 ·
メンテナーはふだん 1 日以内に返信
-
[QNN] 16a4w_block stride-2 conv + ReLU fails HTP graph prepare on SM8850 (Relu16bParamsPlaceholder)対応中かも @psiddh が 2 日前に担当しました。 オープンbug module: qnn partner: qualcomm
pytorch/executorch#23922 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 40/100
pytorch/executorch#23913 ·
メンテナーはふだん 1 日以内に返信
-
Cortex-M backend replaces channels-first dw conv with channels-last dw conv without handling the changed layout, fails at runtime対応中かも @rahimuddin140 が 1 日前に担当しました。 オープンbug module: microcontrollers partner: arm
難易度 4/5 3〜5日 初心者へのやさしさ 38/100
pytorch/executorch#23812 · コメント 2 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
pytorch/executorch の issue をすべて見る
似ている issue
-
documentation good first issue help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
drevendev/HungryCrab#273 ·
メンテナーはふだん 1 日以内に返信
-
good first issue help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
drevendev/BeautifulRepo#19 ·
メンテナーはふだん 2 日以内に返信
-
Moonshot: Kimi K3 always sends `reasoning_effort="max"`, though the API now accepts `low` and `high`オープンaccepted
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
UKGovernmentBEIS/inspect_ai#5939 ·
メンテナーはふだん 1 日以内に返信
-
bug linux
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
area:data data-quality
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
reacher-z/Awesome-CUA#1468 ·
メンテナーはふだん 1 日以内に返信