|
27 | 27 | from executorch.backends.apple.metal.metal_backend import MetalBackend |
28 | 28 | from executorch.backends.apple.metal.metal_partitioner import MetalPartitioner |
29 | 29 | from executorch.exir import to_edge_transform_and_lower |
| 30 | +from executorch.exir.backend.compile_spec_schema import CompileSpec |
30 | 31 | from torch import nn |
31 | 32 | from torch.export import export |
32 | 33 | from torch.nn.attention import SDPBackend |
@@ -787,21 +788,25 @@ def linear_filter(m, fqn): |
787 | 788 |
|
788 | 789 |
|
789 | 790 | def export_model_to_metal( |
790 | | - model: nn.Module, example_inputs: Tuple[torch.Tensor, ...] |
| 791 | + model: nn.Module, |
| 792 | + example_inputs: Tuple[torch.Tensor, ...], |
| 793 | + codesign_identity: Optional[str] = None, |
791 | 794 | ) -> Any: |
792 | 795 | """Export model through the Metal backend pipeline.""" |
793 | 796 | method_name = "forward" |
794 | 797 |
|
| 798 | + compile_specs = [MetalBackend.generate_method_name_compile_spec(method_name)] |
| 799 | + if codesign_identity: |
| 800 | + compile_specs.append( |
| 801 | + CompileSpec("codesign_identity", codesign_identity.encode("utf-8")) |
| 802 | + ) |
| 803 | + |
795 | 804 | with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad(): |
796 | 805 | aten_dialect = export(model, example_inputs, strict=False) |
797 | 806 |
|
798 | 807 | edge_program = to_edge_transform_and_lower( |
799 | 808 | aten_dialect, |
800 | | - partitioner=[ |
801 | | - MetalPartitioner( |
802 | | - [MetalBackend.generate_method_name_compile_spec(method_name)] |
803 | | - ) |
804 | | - ], |
| 809 | + partitioner=[MetalPartitioner(compile_specs)], |
805 | 810 | ) |
806 | 811 |
|
807 | 812 | executorch_program = edge_program.to_executorch() |
@@ -1154,6 +1159,23 @@ def run_test_in_directory(test_dir: Path) -> None: |
1154 | 1159 | with tempfile.TemporaryDirectory() as tmpdir: |
1155 | 1160 | run_test_in_directory(Path(tmpdir)) |
1156 | 1161 |
|
| 1162 | + @unittest.skipIf(SKIP_EXPORT_TESTS, SKIP_REASON) |
| 1163 | + @unittest.skipIf(not IS_MACOS, "codesign requires macOS") |
| 1164 | + def test_codesign_export(self): |
| 1165 | + """Test that export with codesign_identity='-' signs the .so and succeeds.""" |
| 1166 | + model, example_inputs = get_model_and_inputs("add", dtype=torch.float32) |
| 1167 | + # codesign -f -s - runs during export; check=True means CalledProcessError |
| 1168 | + # is raised (and the test fails) if signing fails. |
| 1169 | + program = export_model_to_metal(model, example_inputs, codesign_identity="-") |
| 1170 | + self.assertGreater(len(program.buffer), 0) |
| 1171 | + |
| 1172 | + @unittest.skipIf(SKIP_EXPORT_TESTS, SKIP_REASON) |
| 1173 | + def test_export_without_codesign(self): |
| 1174 | + """Test that export without codesign_identity skips signing.""" |
| 1175 | + model, example_inputs = get_model_and_inputs("add", dtype=torch.float32) |
| 1176 | + program = export_model_to_metal(model, example_inputs) |
| 1177 | + self.assertGreater(len(program.buffer), 0) |
| 1178 | + |
1157 | 1179 |
|
1158 | 1180 | # ============================================================================= |
1159 | 1181 | # Dynamically generate test methods for each module and dtype in MODULE_REGISTRY |
|
0 commit comments