Skip to content

Commit 67b6009

Browse files
authored
Qualcomm AI Engine Direct - Enable custom operator (#8726)
Summary: - Support to register op package in QNN Backend - Add example script to run torch custom op with QNN Op package - Allow op package override torch built-in operator - Add op package example - Modify the flag of dlopen for QNN library - Generate custom op based on the meta and _schema.arguments of torch.fx.Node - Add README for the custom op Reproduce commands: ```bash # Follow the README to Install qpm # Follow the README to install hexagon-sdk and hexagon-tool # install hexagon sdk 5.4.0 for SM8650 # qpm-cli --install hexagonsdk5.x --version 5.4.0.3 --path /path/to/Qualcomm/Hexagon_SDK/hexagon-sdk-5.4.0 # install hexagon sdk 6.0.0 for x86 # qpm-cli --install hexagonsdk6.x --version 6.0.0.2 --path /path/to/Qualcomm/Hexagon_SDK/hexagon-sdk-6.0.0 # install hexagon tool 8.8.02 for x86 # qpm-cli --extract hexagon8.8 --version 8.8.02.1 --path /path/to/Qualcomm/Hexagon_SDK/hexagon-sdk-6.0.0/tools/HEXAGON_Tools/8.8.02 export HEXAGON_SDK_ROOT=/path/to/hexagon-sdk-5.4.0 export ANDROID_NDK_ROOT=/path/to/android-ndk-r26c # use clang-9.0.0 export X86_CXX=/path/to/clang++ # run custom op with example script python3 examples/qualcomm/custom_op/custom_ops_1.py --build_folder build-android -s <device_serial> -H <host> -m SM8650 --op_package_dir examples/qualcomm/custom_op/example_op_package_htp/ExampleOpPackage --build_op_package # run custom op with unit test python3 backends/qualcomm/tests/test_qnn_delegate.py TestUtilScript.test_custom_op -b build-android -s <device_serial> -H <host> -m SM8650 --op_package_dir examples/qualcomm/custom_op/example_op_package_htp/ExampleOpPackage -r </path/to/executorch> -a </path/to/artifacts> ```
1 parent cf81301 commit 67b6009

File tree

114 files changed

+2067
-201
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

114 files changed

+2067
-201
lines changed

backends/qualcomm/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ add_library(qnn_implementation STATIC)
130130
add_library(qnn_logger STATIC)
131131
add_library(qnn_manager STATIC)
132132
add_library(qnn_mem_manager STATIC)
133+
add_library(qnn_op_package_manager STATIC)
133134
add_library(qnn_profiler STATIC)
134135
add_library(qnn_schema INTERFACE ${_qnn_schema__outputs})
135136
add_library(qnn_sys_function_interface INTERFACE)
@@ -152,7 +153,7 @@ target_link_libraries(
152153
target_link_libraries(qnn_executorch_logging PRIVATE qnn_schema)
153154
target_link_libraries(qnn_profiler PRIVATE qnn_executorch_logging)
154155
target_link_libraries(qnn_logger PRIVATE qnn_implementation ${android_log})
155-
target_link_libraries(qnn_backend PRIVATE qnn_implementation qnn_logger)
156+
target_link_libraries(qnn_backend PRIVATE qnn_implementation qnn_logger qnn_op_package_manager)
156157
target_link_libraries(qnn_custom_protocol PRIVATE qnn_logger)
157158
target_link_libraries(
158159
qnn_device PRIVATE qnn_executorch_logging qnn_implementation qnn_logger

backends/qualcomm/builders/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ import torch
176176
from executorch.backends.qualcomm.utils.constants import QCOM_DATA
177177
# op builder will inherit NodeVisitor and have its own implementation
178178
# register_node_visitor for book-keeping the dictionary of target name v.s. callback
179-
from .node_visitor import NodeVisitor, register_node_visitor
179+
from .node_visitor import NodeVisitor
180+
from .node_visitor_manager import register_node_visitor
180181
# the definitions required to build operator in QNN
181182
from .qnn_constants import OpLayerNorm, QNN_OP_PACKAGE_NAME_QTI_AISW
182183
# utility to get parameter value when creating tensor in QNN

backends/qualcomm/builders/node_visitor.py

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@
6363
torch.int64: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_INT_64,
6464
torch.uint8: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_UINT_8,
6565
torch.uint16: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_UINT_16,
66+
torch.uint32: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_UINT_32,
6667
float: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_FLOAT_32,
68+
int: PyQnnWrapper.Qnn_DataType_t.QNN_DATATYPE_UINT_32,
6769
}
6870

6971
PER_CHANNEL_ENCODING = {
@@ -470,51 +472,3 @@ def define_node(
470472
) -> PyQnnWrapper.PyQnnOpWrapper:
471473
"""Convert torch.fx.Node to OpWrapper"""
472474
raise NotImplementedError("NodeVisitor must be extended!")
473-
474-
475-
# This will hold mapping of all node names to the visitor class
476-
_node_visitor_dict = {}
477-
478-
479-
def register_node_visitor(visitor):
480-
"""Register node visitor into _node_visitor_dict"""
481-
assert (
482-
isinstance(visitor, type)
483-
and issubclass(visitor, NodeVisitor)
484-
and hasattr(visitor, "target")
485-
), f"Illformed NodeVisitor subclass, can't register!, got: {visitor}"
486-
for target in visitor.target:
487-
_node_visitor_dict[target] = visitor
488-
489-
490-
def generate_node_to_external_map(
491-
edge_program: torch.export.ExportedProgram,
492-
) -> Dict[torch.fx.Node, int]:
493-
node_to_external_map = {}
494-
for node in edge_program.graph_module.graph.nodes:
495-
# The order in which we visit the placeholder node is same as the *args
496-
# order for the forward(*args) signature for this gm. Using the order of
497-
# the nodes as external_id to extract the right arg from *args at runtime
498-
if is_graph_input(node, edge_program):
499-
node_to_external_map[node] = len(node_to_external_map)
500-
for node in edge_program.graph_module.graph.nodes:
501-
if is_graph_output(node):
502-
node_to_external_map[node] = len(node_to_external_map)
503-
return node_to_external_map
504-
505-
506-
def get_node_visitors(
507-
edge_program: torch.export.ExportedProgram,
508-
enable_tensor_dump=False,
509-
) -> Dict[str, NodeVisitor]:
510-
"""Create a new class instance at runtime, and put them in a dict"""
511-
node_to_external_map = generate_node_to_external_map(edge_program)
512-
node_visitors = {}
513-
for target, visitor in _node_visitor_dict.items():
514-
assert callable(
515-
visitor
516-
), f"Expeting a callable class, but got {visitor} of type {type(visitor)}"
517-
node_visitors[target] = visitor(
518-
node_to_external_map, edge_program, enable_tensor_dump
519-
)
520-
return node_visitors
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 typing import Dict, List
8+
9+
import torch
10+
from executorch.backends.qualcomm.serialization.qc_schema import (
11+
QnnExecuTorchOpPackageInfo,
12+
)
13+
14+
from .node_visitor import NodeVisitor
15+
from .op_custom_op import CustomOp
16+
from .utils import is_graph_input, is_graph_output
17+
18+
19+
# This will hold mapping of all node names to the visitor class
20+
_node_visitor_dict = {}
21+
22+
23+
def register_node_visitor(visitor):
24+
"""Register node visitor into _node_visitor_dict"""
25+
assert (
26+
isinstance(visitor, type)
27+
and issubclass(visitor, NodeVisitor)
28+
and hasattr(visitor, "target")
29+
), f"Informed NodeVisitor subclass, can't register!, got: {visitor}"
30+
for target in visitor.target:
31+
_node_visitor_dict[target] = visitor
32+
33+
34+
def generate_node_to_external_map(
35+
edge_program: torch.export.ExportedProgram,
36+
) -> Dict[torch.fx.Node, int]:
37+
node_to_external_map = {}
38+
for node in edge_program.graph_module.graph.nodes:
39+
# The order in which we visit the placeholder node is same as the *args
40+
# order for the forward(*args) signature for this gm. Using the order of
41+
# the nodes as external_id to extract the right arg from *args at runtime
42+
if is_graph_input(node, edge_program):
43+
node_to_external_map[node] = len(node_to_external_map)
44+
for node in edge_program.graph_module.graph.nodes:
45+
if is_graph_output(node):
46+
node_to_external_map[node] = len(node_to_external_map)
47+
return node_to_external_map
48+
49+
50+
def get_node_visitors(
51+
edge_program: torch.export.ExportedProgram,
52+
enable_tensor_dump=False,
53+
op_package_infos: List[QnnExecuTorchOpPackageInfo] = None,
54+
) -> Dict[str, NodeVisitor]:
55+
"""Create a new class instance at runtime, and put them in a dict"""
56+
node_to_external_map = generate_node_to_external_map(edge_program)
57+
node_visitors = {}
58+
for target, visitor in _node_visitor_dict.items():
59+
assert callable(
60+
visitor
61+
), f"Expecting a callable class, but got {visitor} of type {type(visitor)}"
62+
node_visitors[target] = visitor(
63+
node_to_external_map, edge_program, enable_tensor_dump
64+
)
65+
if op_package_infos:
66+
custom_ops = []
67+
for op_package_info in op_package_infos:
68+
if op_package_info.custom_op_name not in custom_ops:
69+
custom_op_builder = CustomOp(
70+
op_package_info,
71+
node_to_external_map,
72+
edge_program,
73+
enable_tensor_dump,
74+
)
75+
node_visitors[op_package_info.custom_op_name] = custom_op_builder
76+
custom_ops.append(op_package_info.custom_op_name)
77+
return node_visitors

backends/qualcomm/builders/op_abs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpElementWiseAbs, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_adaptive_avg_pool2d.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
import torch
1313

14-
from .node_visitor import NodeVisitor, register_node_visitor
14+
from .node_visitor import NodeVisitor
15+
from .node_visitor_manager import register_node_visitor
1516
from .qnn_constants import OpPoolAvg2d, QNN_OP_PACKAGE_NAME_QTI_AISW
1617

1718

backends/qualcomm/builders/op_add.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpElementWiseAdd, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_amax.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import torch
1313
from executorch.backends.qualcomm.utils.constants import QCOM_AXIS_ORDER, QCOM_DATA
1414

15-
from .node_visitor import NodeVisitor, register_node_visitor
15+
from .node_visitor import NodeVisitor
16+
from .node_visitor_manager import register_node_visitor
1617
from .qnn_constants import OpReduceMax, QNN_OP_PACKAGE_NAME_QTI_AISW
1718

1819

backends/qualcomm/builders/op_and.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpElementWiseAnd, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_arange.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314

1415

1516
@register_node_visitor

backends/qualcomm/builders/op_argmin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import torch
1111
from executorch.backends.qualcomm.utils.constants import QCOM_AXIS_ORDER, QCOM_DATA
1212

13-
from .node_visitor import NodeVisitor, register_node_visitor
13+
from .node_visitor import NodeVisitor
14+
from .node_visitor_manager import register_node_visitor
1415
from .qnn_constants import OpArgmin, QNN_OP_PACKAGE_NAME_QTI_AISW
1516

1617

backends/qualcomm/builders/op_avg_pool2d.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import torch
1313
from executorch.backends.qualcomm.utils.constants import QCOM_DATA
1414

15-
from .node_visitor import NodeVisitor, register_node_visitor
15+
from .node_visitor import NodeVisitor
16+
from .node_visitor_manager import register_node_visitor
1617
from .qnn_constants import OpPoolAvg2d, QNN_OP_PACKAGE_NAME_QTI_AISW
1718

1819

backends/qualcomm/builders/op_batch_norm.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
)
1919
from executorch.exir.dialects._ops import ops as exir_ops
2020

21-
from .node_visitor import NodeVisitor, register_node_visitor
21+
from .node_visitor import NodeVisitor
22+
from .node_visitor_manager import register_node_visitor
2223
from .qnn_constants import OpBatchnorm, QNN_OP_PACKAGE_NAME_QTI_AISW
2324
from .utils import get_parameter
2425

backends/qualcomm/builders/op_bmm.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpMatMul, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_cat.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import torch
1313
from executorch.backends.qualcomm.utils.constants import QCOM_AXIS_ORDER, QCOM_DATA
1414

15-
from .node_visitor import NodeVisitor, register_node_visitor
15+
from .node_visitor import NodeVisitor
16+
from .node_visitor_manager import register_node_visitor
1617
from .qnn_constants import OpConcat, QNN_OP_PACKAGE_NAME_QTI_AISW
1718

1819

backends/qualcomm/builders/op_ceil.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpElementWiseCeil, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_clamp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import torch
1212
from executorch.backends.qualcomm.utils.constants import QCOM_DATA
1313

14-
from .node_visitor import NodeVisitor, register_node_visitor
14+
from .node_visitor import NodeVisitor
15+
from .node_visitor_manager import register_node_visitor
1516
from .qnn_constants import OpReluMinMax, QNN_OP_PACKAGE_NAME_QTI_AISW
1617

1718

backends/qualcomm/builders/op_conv2d.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
import torch
1313
from executorch.backends.qualcomm.utils.constants import QCOM_DATA
1414

15-
from .node_visitor import NodeVisitor, register_node_visitor
15+
from .node_visitor import NodeVisitor
16+
from .node_visitor_manager import register_node_visitor
1617
from .qnn_constants import (
1718
OpConv2d,
1819
OpDepthWiseConv2d,

backends/qualcomm/builders/op_cos.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
import torch
1111

12-
from .node_visitor import NodeVisitor, register_node_visitor
12+
from .node_visitor import NodeVisitor
13+
from .node_visitor_manager import register_node_visitor
1314
from .qnn_constants import OpElementWiseCos, QNN_OP_PACKAGE_NAME_QTI_AISW
1415

1516

backends/qualcomm/builders/op_cum_sum.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import torch
1212
from executorch.backends.qualcomm.utils.constants import QCOM_AXIS_ORDER, QCOM_DATA
1313

14-
from .node_visitor import NodeVisitor, register_node_visitor
14+
from .node_visitor import NodeVisitor
15+
from .node_visitor_manager import register_node_visitor
1516
from .qnn_constants import OpCumulativeSum, QNN_OP_PACKAGE_NAME_QTI_AISW
1617

1718

0 commit comments

Comments
 (0)