Skip to content

Commit 1c7e394

Browse files
Qualcomm AI Engine Direct - Enforce terminal pass ordering (#23572)
### Summary Fix the pass error when running llama3.2 3b. <img width="1398" height="168" alt="image" src="https://github.com/user-attachments/assets/a142888e-c302-4b06-99d2-2e5e7d4840fb" /> ### Test plan ``` python -m pytest backends/qualcomm/tests/rework/passes/test.py -k test_pass_manager ``` Without PR: <img width="1835" height="97" alt="image" src="https://github.com/user-attachments/assets/43ab00d1-a51d-49ba-a2df-bdb3c055b6e5" /> With PR: <img width="1825" height="294" alt="image" src="https://github.com/user-attachments/assets/4efdcffd-1744-4f09-97c0-838d2e9f3c96" />
1 parent e006801 commit 1c7e394

4 files changed

Lines changed: 87 additions & 1 deletion

File tree

‎backends/qualcomm/_passes/backends/lpai/qnn_lpai_pass_manager.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ def get_passes_dependency_for_capture_program(cls):
5656
)
5757
return deps
5858

59+
@classmethod
60+
def _get_terminal_passes(cls):
61+
return [ResolveDebugHandle, LpaiPartitionFallbackSupport]
62+
5963
def _validate_edge_passes(self) -> None:
6064
assert isinstance(
6165
self.passes[-2], ResolveDebugHandle

‎backends/qualcomm/_passes/qnn_pass_manager.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,17 @@ def get_passes_dependency_for_capture_program(cls):
348348
ResolveDebugHandle: [TagQuantIO],
349349
}
350350

351+
@classmethod
352+
def _get_terminal_passes(cls):
353+
return [ResolveDebugHandle]
354+
355+
@classmethod
356+
def _move_terminal_passes_to_end(cls, passes):
357+
for p in cls._get_terminal_passes():
358+
passes.remove(p)
359+
passes.append(p)
360+
return passes
361+
351362
@classmethod
352363
def get_capture_program_passes(cls):
353364
"""Build an ordered mapping of passes with activation flags and init defaults.
@@ -411,7 +422,7 @@ def get_to_edge_transform_passes(
411422
self.add_pass(p)
412423
self.solve_constraints()
413424

414-
sorted_passes = self.passes
425+
sorted_passes = self._move_terminal_passes_to_end(self.passes)
415426
self._reset()
416427
for p in sorted_passes:
417428
if not passes_job[p][QCOM_PASS_ACTIVATE_KEY]:

‎backends/qualcomm/tests/rework/passes/test.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
enumerate_backends_quantized,
1515
repack_pass_fixtures,
1616
)
17+
from executorch.backends.qualcomm.tests.rework.src.pass_manager import PassManager
1718
from executorch.backends.qualcomm.tests.rework.src.pattern import * # noqa: F403
1819

1920

@@ -432,3 +433,14 @@ def test_seq_mse(request, kwargs):
432433
@repack_pass_fixtures
433434
def test_tag_quant_io(request, kwargs):
434435
TagQuantIO.test(request, kwargs) # noqa: F405
436+
437+
438+
@pytest.mark.parametrize(
439+
"backend_type",
440+
[
441+
QnnExecuTorchBackendType.kHtpBackend,
442+
QnnExecuTorchBackendType.kLpaiBackend,
443+
],
444+
)
445+
def test_pass_manager(backend_type):
446+
PassManager.test(backend_type)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright (c) Qualcomm Innovation Center, Inc.
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
from executorch.backends.qualcomm._passes import (
8+
LpaiPartitionFallbackSupport,
9+
ResolveDebugHandle,
10+
TagQuantIO,
11+
)
12+
from executorch.backends.qualcomm._passes.qnn_pass_manager import (
13+
get_qnn_pass_manager_cls,
14+
)
15+
from executorch.backends.qualcomm.serialization.qc_schema import (
16+
QnnExecuTorchBackendType,
17+
)
18+
from executorch.backends.qualcomm.utils.constants import (
19+
QCOM_PASS_ACTIVATE_KEY,
20+
QCOM_PASS_ARGS_KWARGS_DEFAULTS_KEY,
21+
)
22+
from executorch.exir.pass_base import ExportPass, PassResult
23+
24+
25+
class _CustomPass(ExportPass):
26+
def call(self, graph_module):
27+
return PassResult(graph_module, False)
28+
29+
30+
class PassManager:
31+
@staticmethod
32+
def test(backend_type: QnnExecuTorchBackendType):
33+
pass_manager_cls = get_qnn_pass_manager_cls(backend_type)
34+
terminal_passes = (
35+
(ResolveDebugHandle, LpaiPartitionFallbackSupport)
36+
if backend_type == QnnExecuTorchBackendType.kLpaiBackend
37+
else (ResolveDebugHandle,)
38+
)
39+
40+
passes_job = pass_manager_cls.get_capture_program_passes()
41+
passes_job[TagQuantIO][QCOM_PASS_ACTIVATE_KEY] = True
42+
passes_job[_CustomPass] = {
43+
QCOM_PASS_ACTIVATE_KEY: True,
44+
QCOM_PASS_ARGS_KWARGS_DEFAULTS_KEY: {},
45+
}
46+
dep_table = pass_manager_cls.get_passes_dependency_for_capture_program()
47+
dep_table[_CustomPass] = [TagQuantIO]
48+
49+
passes = pass_manager_cls().get_to_edge_transform_passes(
50+
exported_program=None,
51+
passes_job=passes_job,
52+
dep_table=dep_table,
53+
)
54+
55+
# HTP ends with ResolveDebugHandle; LPAI runs it immediately before
56+
# LpaiPartitionFallbackSupport, which must remain the final pass.
57+
assert (
58+
tuple(type(p) for p in passes[-len(terminal_passes) :]) == terminal_passes
59+
)

0 commit comments

Comments
 (0)