|
14 | 14 |
|
15 | 15 | import executorch.backends.vulkan.custom_ops_lib # noqa: F401 |
16 | 16 | import torch |
17 | | -from executorch.backends.vulkan.partitioner.vulkan_partitioner import VulkanPartitioner |
18 | 17 | from executorch.backends.vulkan.serialization import ( |
19 | 18 | vulkan_graph_builder as graph_builder_module, |
20 | 19 | ) |
|
35 | 34 | serialize_vulkan_graph, |
36 | 35 | VulkanDelegateHeader, |
37 | 36 | ) |
38 | | -from executorch.exir import to_edge_transform_and_lower |
| 37 | +from torch.export.graph_signature import OutputKind |
39 | 38 |
|
40 | 39 |
|
41 | 40 | class TestSerialization(unittest.TestCase): |
@@ -175,42 +174,71 @@ def test_mutation_aliasing_user_output_gets_one_slot(self) -> None: |
175 | 174 | # the mutation and the user output are the same node. With aliasing on |
176 | 175 | # the mutation is applied in place and only the user output takes a |
177 | 176 | # slot; without it the mutation still needs a slot to carry the new |
178 | | - # value back, and the graph is left to be rejected rather than silently |
179 | | - # skipping the state update. |
180 | | - class Model(torch.nn.Module): |
181 | | - def __init__(self) -> None: |
182 | | - super().__init__() |
183 | | - self.register_buffer("state", torch.zeros(2, 48)) |
184 | | - |
185 | | - def forward(self, x): |
186 | | - self.state.add_(x) |
187 | | - return self.state.view(2, 48) |
188 | | - |
189 | | - for alias, expected_slots in ((True, 1), (False, 2)): |
190 | | - with self.subTest(alias_buffer_mutations=alias): |
191 | | - captured: List[int] = [] |
192 | | - original = graph_builder_module.VkGraphBuilder.build_graph |
193 | | - |
194 | | - def spy(builder_self, _original=original, _captured=captured): |
195 | | - graph = _original(builder_self) |
196 | | - _captured.append(len(graph.output_ids)) |
197 | | - return graph |
198 | | - |
199 | | - graph_builder_module.VkGraphBuilder.build_graph = spy |
200 | | - try: |
201 | | - program = torch.export.export( |
202 | | - Model(), (torch.ones(2, 48),), strict=True |
203 | | - ) |
204 | | - to_edge_transform_and_lower( |
205 | | - program, |
206 | | - partitioner=[ |
207 | | - VulkanPartitioner({"alias_buffer_mutations": alias}) |
208 | | - ], |
209 | | - ).to_executorch() |
210 | | - finally: |
211 | | - graph_builder_module.VkGraphBuilder.build_graph = original |
212 | | - |
213 | | - self.assertEqual(captured, [expected_slots]) |
| 177 | + # value back. |
| 178 | + for prepack in (False, True): |
| 179 | + for with_specs in (False, True): |
| 180 | + program, _, mutation, _ = self._build_mutation_program( |
| 181 | + prepack, shared_user_output=True |
| 182 | + ) |
| 183 | + if with_specs: |
| 184 | + program.graph_signature.output_specs = [ |
| 185 | + SimpleNamespace(kind=OutputKind.BUFFER_MUTATION), |
| 186 | + SimpleNamespace(kind=OutputKind.USER_OUTPUT), |
| 187 | + ] |
| 188 | + for alias, expected_slots in ((True, 1), (False, 2)): |
| 189 | + with self.subTest( |
| 190 | + prepack=prepack, |
| 191 | + output_specs=with_specs, |
| 192 | + alias_buffer_mutations=alias, |
| 193 | + ): |
| 194 | + builder = graph_builder_module.VkGraphBuilder( |
| 195 | + program, |
| 196 | + graph_builder_module.DelegateMappingBuilder( |
| 197 | + generated_identifiers=True |
| 198 | + ), |
| 199 | + alias_buffer_mutations=alias, |
| 200 | + ) |
| 201 | + graph = builder.build_graph() |
| 202 | + self.assertEqual( |
| 203 | + graph.output_ids, |
| 204 | + [builder.node_to_value_ids[mutation]] * expected_slots, |
| 205 | + ) |
| 206 | + |
| 207 | + def test_duplicate_user_outputs_get_two_slots(self) -> None: |
| 208 | + # `Node.all_input_nodes` de-duplicates, so a graph returning the same |
| 209 | + # value twice must still serialize one output id per output, or the |
| 210 | + # runtime's argument count check fails. |
| 211 | + graph = torch.fx.Graph() |
| 212 | + user_input = graph.placeholder("user_input") |
| 213 | + user_input.meta["spec"] = graph_builder_module.TensorSpec.from_tensor( |
| 214 | + torch.ones(4) |
| 215 | + ) |
| 216 | + output = graph.call_function(torch.ops.aten.mul.Tensor, (user_input, 2.0)) |
| 217 | + output.meta["spec"] = graph_builder_module.TensorSpec.from_tensor(torch.ones(4)) |
| 218 | + graph.output((output, output)) |
| 219 | + |
| 220 | + program = SimpleNamespace( |
| 221 | + constants={}, |
| 222 | + graph_module=torch.fx.GraphModule({}, graph), |
| 223 | + graph_signature=SimpleNamespace( |
| 224 | + buffers_to_mutate={}, |
| 225 | + inputs_to_buffers={}, |
| 226 | + inputs_to_lifted_tensor_constants={}, |
| 227 | + inputs_to_parameters={}, |
| 228 | + non_persistent_buffers=set(), |
| 229 | + user_outputs=(output.name, output.name), |
| 230 | + ), |
| 231 | + state_dict={}, |
| 232 | + ) |
| 233 | + builder = graph_builder_module.VkGraphBuilder( |
| 234 | + program, |
| 235 | + graph_builder_module.DelegateMappingBuilder(generated_identifiers=True), |
| 236 | + ) |
| 237 | + vk_graph = builder.build_graph() |
| 238 | + self.assertEqual( |
| 239 | + vk_graph.output_ids, |
| 240 | + [builder.node_to_value_ids[output]] * 2, |
| 241 | + ) |
214 | 242 |
|
215 | 243 | def _generate_random_const_tensors(self, num_tensors: int) -> List[torch.Tensor]: |
216 | 244 | """ |
|
0 commit comments