|
13 | 13 | from executorch.backends.qualcomm._passes.qnn_pass_manager import ( |
14 | 14 | get_qnn_pass_manager_cls, |
15 | 15 | ) |
| 16 | +from executorch.backends.qualcomm.builders.qnn_constants import ( |
| 17 | + is_context_loader_node, |
| 18 | + is_context_loader_target, |
| 19 | + OpContextLoader, |
| 20 | +) |
| 21 | +from executorch.backends.qualcomm.partition.qnn_partitioner import QnnOperatorSupport |
| 22 | +from executorch.backends.qualcomm.qnn_preprocess import QnnBackend |
16 | 23 | from executorch.backends.qualcomm.quantizer.quantizer import QnnQuantizer, QuantDtype |
17 | 24 | from executorch.backends.qualcomm.serialization.qc_schema import ( |
18 | 25 | QcomChipset, |
|
28 | 35 | generate_qnn_executorch_compiler_spec, |
29 | 36 | to_edge_transform_and_lower_to_qnn, |
30 | 37 | ) |
31 | | -from executorch.exir import to_edge |
| 38 | +from executorch.exir import EdgeCompileConfig, to_edge |
32 | 39 | from executorch.exir.debug_handle_utils import DEBUG_HANDLE_KEY |
33 | 40 | from executorch.exir.dialects._ops import ops as exir_ops |
| 41 | +from torch.library import Library |
34 | 42 | from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e |
35 | 43 |
|
36 | 44 |
|
37 | 45 | class TestPasses(unittest.TestCase): |
| 46 | + def test_context_loader_edge_op_is_delegated(self): |
| 47 | + op_name = "ctx_loader_delegation" |
| 48 | + graph_name = "forward" |
| 49 | + ctx_bin = b"qnn_context_binary" |
| 50 | + custom_op = Library(OpContextLoader.namespace, "FRAGMENT") |
| 51 | + self.addCleanup(custom_op._destroy) |
| 52 | + custom_op.define(f"{op_name}(Tensor[] inputs) -> Any") |
| 53 | + |
| 54 | + @torch.library.impl( |
| 55 | + custom_op, op_name, dispatch_key="CompositeExplicitAutograd" |
| 56 | + ) |
| 57 | + def op_impl(inputs): |
| 58 | + return (torch.zeros((1, 2), device="meta", dtype=inputs[0].dtype),) |
| 59 | + |
| 60 | + class Model(torch.nn.Module): |
| 61 | + def forward(self, x): |
| 62 | + return getattr( |
| 63 | + getattr(torch.ops, OpContextLoader.namespace), op_name |
| 64 | + ).default((x,)) |
| 65 | + |
| 66 | + exported_program = torch.export.export( |
| 67 | + Model(), (torch.ones(1, 2),), strict=True |
| 68 | + ) |
| 69 | + edge_program_manager = to_edge( |
| 70 | + {graph_name: exported_program}, |
| 71 | + compile_config=EdgeCompileConfig(_check_ir_validity=False), |
| 72 | + ) |
| 73 | + |
| 74 | + context_loader_nodes = [ |
| 75 | + node |
| 76 | + for node in edge_program_manager._edge_programs[graph_name].graph.nodes |
| 77 | + if is_context_loader_node(node, op_name) |
| 78 | + ] |
| 79 | + self.assertEqual(1, len(context_loader_nodes)) |
| 80 | + self.assertTrue(is_context_loader_node(context_loader_nodes[0])) |
| 81 | + context_loader_nodes[0].meta[OpContextLoader.meta_ctx_bin] = ctx_bin |
| 82 | + self.assertEqual( |
| 83 | + ctx_bin, |
| 84 | + context_loader_nodes[0].meta[OpContextLoader.meta_ctx_bin], |
| 85 | + ) |
| 86 | + |
| 87 | + support = QnnOperatorSupport.__new__(QnnOperatorSupport) |
| 88 | + support.phase = "QnnPartitioner" |
| 89 | + self.assertTrue(support.is_node_supported(None, context_loader_nodes[0])) |
| 90 | + |
| 91 | + def test_is_context_loader_target_predicate(self): |
| 92 | + op_name = "ctx_loader_predicate" |
| 93 | + custom_op = Library(OpContextLoader.namespace, "FRAGMENT") |
| 94 | + self.addCleanup(custom_op._destroy) |
| 95 | + custom_op.define(f"{op_name}(Tensor[] inputs) -> Any") |
| 96 | + |
| 97 | + # Plain OpOverload in the context-loader namespace must match (the |
| 98 | + # _op unwrap must not break the non-edge-dialect target case). |
| 99 | + qaisw_op = getattr( |
| 100 | + getattr(torch.ops, OpContextLoader.namespace), op_name |
| 101 | + ).default |
| 102 | + self.assertTrue(is_context_loader_target(qaisw_op, op_name)) |
| 103 | + self.assertFalse(is_context_loader_target(qaisw_op, "different_op")) |
| 104 | + |
| 105 | + # Ops in other namespaces must not match, including an edge op |
| 106 | + # (unwrapped via _op) whose namespace is not the loader's. |
| 107 | + self.assertFalse(is_context_loader_target(torch.ops.aten.add.default)) |
| 108 | + self.assertFalse(is_context_loader_target(exir_ops.edge.aten.add.Tensor)) |
| 109 | + |
| 110 | + def test_build_op_wrappers_returns_context_binary(self): |
| 111 | + op_name = "ctx_loader_build" |
| 112 | + graph_name = "forward" |
| 113 | + ctx_bin = b"qnn_context_binary" |
| 114 | + custom_op = Library(OpContextLoader.namespace, "FRAGMENT") |
| 115 | + self.addCleanup(custom_op._destroy) |
| 116 | + custom_op.define(f"{op_name}(Tensor[] inputs) -> Any") |
| 117 | + |
| 118 | + @torch.library.impl( |
| 119 | + custom_op, op_name, dispatch_key="CompositeExplicitAutograd" |
| 120 | + ) |
| 121 | + def op_impl(inputs): |
| 122 | + return (torch.zeros((1, 2), device="meta", dtype=inputs[0].dtype),) |
| 123 | + |
| 124 | + class Model(torch.nn.Module): |
| 125 | + def forward(self, x): |
| 126 | + return getattr( |
| 127 | + getattr(torch.ops, OpContextLoader.namespace), op_name |
| 128 | + ).default((x,)) |
| 129 | + |
| 130 | + exported_program = torch.export.export( |
| 131 | + Model(), (torch.ones(1, 2),), strict=True |
| 132 | + ) |
| 133 | + edge_program = to_edge( |
| 134 | + {graph_name: exported_program}, |
| 135 | + compile_config=EdgeCompileConfig(_check_ir_validity=False), |
| 136 | + )._edge_programs[graph_name] |
| 137 | + for node in edge_program.graph.nodes: |
| 138 | + if is_context_loader_node(node, op_name): |
| 139 | + node.meta[OpContextLoader.meta_ctx_bin] = ctx_bin |
| 140 | + |
| 141 | + # For a graph whose only op is the context-binary loader, _build_op_wrappers |
| 142 | + # returns the stamped context binary directly, before any QNN compilation. |
| 143 | + result = QnnBackend._build_op_wrappers( |
| 144 | + edge_program, |
| 145 | + enable_tensor_dump=False, |
| 146 | + op_package_infos=[], |
| 147 | + use_mha2sha=False, |
| 148 | + backend_type=QnnExecuTorchBackendType.kHtpBackend, |
| 149 | + ) |
| 150 | + self.assertEqual(ctx_bin, result) |
| 151 | + |
38 | 152 | def _build_quantized_graph(self): |
39 | 153 | """Build a quantized graph through AnnotateQuantAttrs + FoldQDQ.""" |
40 | 154 |
|
|
0 commit comments