Skip to content

Commit 863cebb

Browse files
committed
Code Review and adding some comments
1 parent d9a6e9c commit 863cebb

6 files changed

Lines changed: 87 additions & 31 deletions

File tree

backends/qualcomm/_passes/lpai_partition_fallback_support.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,11 @@ def get_unsupported_nodes(self, graph_module: torch.fx.GraphModule):
155155
compiler_specs=self.compiler_specs,
156156
skip_node_id_set=self.skip_node_id_set,
157157
skip_node_op_set=self.skip_node_op_set,
158+
is_qnn_partitioner=False,
159+
phase="LpaiPartitionFallbackSupport",
158160
)
159161

160162
unsupported_nodes = []
161-
162163
for node in graph_module.graph.nodes:
163164
if node.op != "call_function":
164165
continue
@@ -263,20 +264,28 @@ def handle_back_to_back_nodes(self, graph_module: torch.fx.GraphModule):
263264
continue
264265
if node.target in q_ops or node.target in dq_ops:
265266
continue
266-
if node.meta.get(QCOM_FALLBACK_NODE):
267+
268+
if node.meta.get(QCOM_FALLBACK_NODE) and QCOM_QUANT_ATTRS in node.meta:
269+
270+
# Skip node like get_attr
271+
input_call_func_nodes = [
272+
input_node
273+
for input_node in node.all_input_nodes
274+
if input_node.op == "call_function"
275+
]
267276
assert all(
268-
input_node.target in dq_ops for input_node in node.all_input_nodes
277+
input_node.target in dq_ops for input_node in input_call_func_nodes
269278
), (
270-
f"Expected every input of fall back node {node.name} to be "
279+
f"Expected every call_function input of fp fall back node {node.name} to be "
271280
f"a dequantize op (insert_partition_qdq should have wrapped "
272281
f"each input with a Q1/DQ1 pair), got: "
273-
f"{[(n.name, n.target) for n in node.all_input_nodes]}"
282+
f"{[(n.name, n.target) for n in input_call_func_nodes]}"
274283
)
275-
for dq_node in list(node.all_input_nodes):
284+
for dq_node in input_call_func_nodes:
276285
q_node = dq_node.args[0]
277286
if q_node.op == "placeholder":
278287
raise RuntimeError(
279-
"Fallback nodes with weights is currently supported."
288+
"Fallback nodes with weights is not currently supported."
280289
)
281290
assert (
282291
q_node.target in q_ops

backends/qualcomm/builders/node_visitor.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,19 @@ def __init__(
118118
external_ids,
119119
edge_program: torch.export.ExportedProgram,
120120
enable_tensor_dump,
121+
is_qnn_partitioner=True,
121122
) -> None:
123+
# is_qnn_partitioner: True for the real qnn_partitioner / qnn_preprocess
124+
# flows, where external_ids is built from the program being processed so
125+
# graph-input ids resolve correctly.
126+
# Set False for lpai_partition_fallback_support pass, where constructor
127+
# takes an older exported_program whose node objects differ from the
128+
# live graph; the input_{id} naming would mismatch there, so we skip
129+
# it during op validation.
122130
self.external_ids = external_ids or {}
123131
self.edge_program = edge_program
124132
self.enable_tensor_dump = enable_tensor_dump
133+
self.is_qnn_partitioner = is_qnn_partitioner
125134

126135
def get_node(self, node):
127136
"""
@@ -371,9 +380,11 @@ def get_tensor_type(
371380
is_output = is_graph_output(node)
372381
# handle logic for input/output tensors
373382
if is_input or is_output:
374-
assert (
375-
node in self.external_ids
376-
), f"Node {node}, is_input: {is_input}, is_output: {is_output}, ext_ids: {self.external_ids.keys()}"
383+
# For more info about existence of self.is_qnn_partitioner, check constructor for explanation.
384+
assert not self.is_qnn_partitioner or node in self.external_ids, (
385+
f"Node {node}, is_input: {is_input}, is_output: {is_output}, "
386+
f"ext_ids: {self.external_ids.keys()}"
387+
)
377388
if is_input:
378389
return PyQnnManager.Qnn_TensorType_t.QNN_TENSOR_TYPE_APP_WRITE
379390
if is_output:
@@ -422,21 +433,24 @@ def get_tensor_name(
422433
# the input order between QNN and the original graph’s forward function may differ.
423434
# The `mutbuf_{id}` is utilized for mapping I/O of mutable buffer at runtime.
424435
# The `output_` is identified as the graph’s output at runtime to prevent confusion with per_tensor_dump.
425-
if is_mutable_buffer_input(node, self.edge_program):
426-
fqn = self.edge_program.graph_signature.inputs_to_buffers[node.target]
427-
position_index = list(
428-
self.edge_program.graph_signature.buffers_to_mutate.values()
429-
).index(fqn)
430-
tensor_name = f"input_{str(self.external_ids[node])}_mutbuf_{str(position_index)}_{tensor_name}"
431-
elif is_graph_input(node, self.edge_program):
432-
tensor_name = f"input_{str(self.external_ids[node])}_{tensor_name}"
433-
elif is_mutable_buffer_output(node, self.edge_program):
434-
position_index = list(
435-
self.edge_program.graph_signature.buffers_to_mutate.keys()
436-
).index(node.name)
437-
tensor_name = f"output_mutbuf_{position_index}_{tensor_name}"
438-
elif is_graph_output(node):
439-
tensor_name = f"output_{tensor_name}"
436+
437+
# For more info about self.is_qnn_partitioner, check constructor for explanation.
438+
if self.is_qnn_partitioner:
439+
if is_mutable_buffer_input(node, self.edge_program):
440+
fqn = self.edge_program.graph_signature.inputs_to_buffers[node.target]
441+
position_index = list(
442+
self.edge_program.graph_signature.buffers_to_mutate.values()
443+
).index(fqn)
444+
tensor_name = f"input_{str(self.external_ids[node])}_mutbuf_{str(position_index)}_{tensor_name}"
445+
elif is_graph_input(node, self.edge_program):
446+
tensor_name = f"input_{str(self.external_ids[node])}_{tensor_name}"
447+
elif is_mutable_buffer_output(node, self.edge_program):
448+
position_index = list(
449+
self.edge_program.graph_signature.buffers_to_mutate.keys()
450+
).index(node.name)
451+
tensor_name = f"output_mutbuf_{position_index}_{tensor_name}"
452+
elif is_graph_output(node):
453+
tensor_name = f"output_{tensor_name}"
440454

441455
# Only add qcom_tensor_name when enable tensor dump.
442456
# Only runs in qnn_preprocess (not op validation) since that's when

backends/qualcomm/builders/node_visitor_manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def get_node_visitors(
5353
edge_program: torch.export.ExportedProgram,
5454
enable_tensor_dump=False,
5555
op_package_infos: List[QnnExecuTorchOpPackageInfo] = None,
56+
is_qnn_partitioner=True,
5657
) -> Dict[str, NodeVisitor]:
5758
"""Create a new class instance at runtime, and put them in a dict"""
5859
node_to_external_map = generate_node_to_external_map(edge_program)
@@ -62,7 +63,7 @@ def get_node_visitors(
6263
visitor
6364
), f"Expecting a callable class, but got {visitor} of type {type(visitor)}"
6465
node_visitors[target] = visitor(
65-
node_to_external_map, edge_program, enable_tensor_dump
66+
node_to_external_map, edge_program, enable_tensor_dump, is_qnn_partitioner
6667
)
6768
if op_package_infos:
6869
custom_ops = []
@@ -73,6 +74,7 @@ def get_node_visitors(
7374
node_to_external_map,
7475
edge_program,
7576
enable_tensor_dump,
77+
is_qnn_partitioner,
7678
)
7779
node_visitors[op_package_info.custom_op_name] = custom_op_builder
7880
custom_ops.append(op_package_info.custom_op_name)

backends/qualcomm/partition/qnn_partitioner.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,23 @@ def __init__(
5656
compiler_specs,
5757
skip_node_id_set: set = None,
5858
skip_node_op_set: set = None,
59+
is_qnn_partitioner=True,
60+
phase="QnnPartitioner",
5961
):
6062
option = generate_qnn_executorch_option(compiler_specs)
6163
python_options = flatbuffer_to_option(option)
6264
self.node_visitors = node_visitor_manager.get_node_visitors(
6365
edge_program,
6466
op_package_infos=python_options.op_package_options.op_package_infos,
67+
is_qnn_partitioner=is_qnn_partitioner,
6568
)
6669

6770
self.skip_node_op_set = skip_node_op_set
6871
self.skip_node_id_set = skip_node_id_set
72+
self.is_qnn_partitioner = is_qnn_partitioner
73+
# Label prefixed to op-support log lines so callers that reuse this
74+
# checker (e.g. the LPAI fallback pass) are distinguishable in the logs.
75+
self.phase = phase
6976
self.nodes_to_wrappers = defaultdict(dict)
7077
self.qnn_manager = get_current_qnn_manager(
7178
python_options.backend_options.backend_type, compiler_specs
@@ -77,12 +84,12 @@ def is_node_supported(self, _, node: torch.fx.Node) -> bool:
7784

7885
if node.target in to_be_implemented_operator:
7986
logger.info(
80-
f"{node.target.__name__} | Skipped, this op can be supported, please report an issue in https://github.com/pytorch/executorch/issues"
87+
f"[{self.phase}] {node.target.__name__} | Skipped, this op can be supported, please report an issue in https://github.com/pytorch/executorch/issues"
8188
)
8289
return False
8390

8491
if node.meta.get(QCOM_FALLBACK_NODE, False):
85-
logger.info(f"{node.target.__name__} | Forced Fallback")
92+
logger.info(f"[{self.phase}] {node.target.__name__} | Forced Fallback")
8693
return False
8794

8895
if (
@@ -92,14 +99,14 @@ def is_node_supported(self, _, node: torch.fx.Node) -> bool:
9299
# bypass dequantize op for parameters & buffers
93100
or node.meta.get(QCOM_BYPASS_NODE, False)
94101
):
95-
logger.info(f"{node.target.__name__} | Forced Passed")
102+
logger.info(f"[{self.phase}] {node.target.__name__} | Forced Passed")
96103
return True
97104

98105
if (
99106
node.name in self.skip_node_id_set
100107
or node.target.__name__ in self.skip_node_op_set
101108
):
102-
logger.info(f"{node.target.__name__} | Skipped")
109+
logger.info(f"[{self.phase}] {node.target.__name__} | Skipped")
103110
return False
104111

105112
supported = False
@@ -121,7 +128,7 @@ def is_node_supported(self, _, node: torch.fx.Node) -> bool:
121128
)
122129

123130
self.nodes_to_wrappers.clear()
124-
logger.info(f"{node.target.__name__} | {supported}")
131+
logger.info(f"[{self.phase}] {node.target.__name__} | {supported}")
125132
return supported
126133

127134

backends/qualcomm/tests/models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,17 @@ def forward(self, x):
23252325
return torch.relu(x) + torch.sqrt(x)
23262326

23272327

2328+
class SkipIntNode(torch.nn.Module):
2329+
def __init__(self):
2330+
super().__init__()
2331+
2332+
def forward(self, x, y):
2333+
a = torch.add(x, y)
2334+
b = torch.mul(a, a)
2335+
c = torch.add(b, y)
2336+
return c
2337+
2338+
23282339
class SkipMultiInput(torch.nn.Module):
23292340

23302341
def __init__(self):

backends/qualcomm/tests/test_qnn_delegate.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6796,6 +6796,19 @@ def test_qnn_backend_skip_node_id_partitioner_last_node(self):
67966796
skip_node_id_set={"aten_add_tensor"},
67976797
)
67986798

6799+
def test_qnn_backend_skip_node_id_partitioner_int_node(self):
6800+
module = SkipIntNode() # noqa: F405
6801+
sample_input = (
6802+
torch.randint(0, 5, (2, 3, 4), dtype=torch.int32),
6803+
torch.randint(0, 5, (2, 3, 4), dtype=torch.int32),
6804+
)
6805+
self.lower_module_and_test_output(
6806+
module,
6807+
sample_input,
6808+
expected_partitions=2,
6809+
skip_node_id_set={"aten_mul_tensor"},
6810+
)
6811+
67996812
def test_qnn_backend_skip_node_id_quantizer(self):
68006813
module = SimpleModel() # noqa: F405
68016814
sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28))

0 commit comments

Comments
 (0)