|
13 | 13 | CortexMPassManager, |
14 | 14 | LiftConstantTensorsPass, |
15 | 15 | ) |
| 16 | +from executorch.backends.cortex_m.passes.explicit_layout_pass import ( |
| 17 | + CortexMPostponeLayoutCopyBelowSqueezePass, |
| 18 | +) |
16 | 19 | from executorch.backends.cortex_m.quantizer.quantizer import CortexMQuantizer |
17 | 20 | from executorch.backends.cortex_m.target_config import CortexM, CortexMTargetConfig |
18 | 21 | from executorch.backends.cortex_m.test.tester import ( |
19 | 22 | CortexMQuantize, |
20 | 23 | CortexMRunPasses, |
21 | 24 | CortexMTester, |
22 | 25 | ) |
| 26 | +from executorch.backends.test.graph_builder import GraphBuilder |
23 | 27 | from executorch.backends.test.harness.stages import Quantize, StageType |
| 28 | +from executorch.backends.transforms.channels_last_layout import LAYOUT_PERMUTE_COPY |
24 | 29 | from executorch.backends.transforms.remove_unused_constants_pass import ( |
25 | 30 | RemoveUnusedConstantsPass, |
26 | 31 | ) |
@@ -88,6 +93,18 @@ def forward(self, x): |
88 | 93 | return self.conv2(self.conv1(torch.tanh(x)).view(1, 4, 32)) |
89 | 94 |
|
90 | 95 |
|
| 96 | +class Conv1dChain(torch.nn.Module): |
| 97 | + def __init__(self) -> None: |
| 98 | + super().__init__() |
| 99 | + self.conv1 = torch.nn.Conv1d(8, 8, 3, padding=1) |
| 100 | + self.conv2 = torch.nn.Conv1d(8, 8, 3, padding=1) |
| 101 | + self.conv3 = torch.nn.Conv1d(8, 8, 1) |
| 102 | + |
| 103 | + def forward(self, x): |
| 104 | + y = torch.tanh(self.conv1(x)) |
| 105 | + return self.conv3(torch.relu(self.conv2(y) + y)) |
| 106 | + |
| 107 | + |
91 | 108 | class Apply(torch.nn.Module): |
92 | 109 | def __init__(self, fn) -> None: |
93 | 110 | super().__init__() |
@@ -669,3 +686,62 @@ def test_unquantized_constant_is_not_precomputed(use_explicit_layout): |
669 | 686 |
|
670 | 687 | assert _count(program, exir_ops.edge.aten.full.default) == 1 |
671 | 688 | tester.run_method_and_compare_outputs(inputs=inputs) |
| 689 | + |
| 690 | + |
| 691 | +def test_conv1d_chain_keeps_only_the_boundary_layout_copies(): |
| 692 | + torch.manual_seed(0) |
| 693 | + inputs = (torch.randn(1, 8, 16),) |
| 694 | + tester = _run_explicit_layout_passes(CortexMTester(Conv1dChain().eval(), inputs)) |
| 695 | + program = tester.get_artifact(StageType.RUN_PASSES).exported_program() |
| 696 | + |
| 697 | + assert _count(program, exir_ops.edge.cortex_m.quantized_conv2d_nhwc.default) == 3 |
| 698 | + assert _count(program, exir_ops.edge.cortex_m.transpose.default) == 2 |
| 699 | + assert _count(program, exir_ops.edge.aten.permute_copy.default) == 0 |
| 700 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 701 | + |
| 702 | + |
| 703 | +def _permute_then_view(permute, shape, view_shape) -> torch.fx.GraphModule: |
| 704 | + builder = GraphBuilder() |
| 705 | + x = builder.placeholder("x", torch.randn(shape)) |
| 706 | + permuted = builder.call_operator(op=permute, args=(x, [0, 3, 1, 2])) |
| 707 | + view = builder.call_operator( |
| 708 | + op=exir_ops.edge.aten.view_copy.default, args=(permuted, view_shape) |
| 709 | + ) |
| 710 | + builder.output([view]) |
| 711 | + return builder.get_graph_module() |
| 712 | + |
| 713 | + |
| 714 | +def test_layout_copy_is_sunk_below_a_squeeze(): |
| 715 | + x = torch.randn(1, 1, 4, 6) |
| 716 | + graph_module = _permute_then_view(LAYOUT_PERMUTE_COPY, x.shape, [1, 6, 4]) |
| 717 | + |
| 718 | + result = CortexMPostponeLayoutCopyBelowSqueezePass()(graph_module) |
| 719 | + |
| 720 | + assert result.modified |
| 721 | + assert [ |
| 722 | + (node.target, node.args[1]) |
| 723 | + for node in result.graph_module.graph.nodes |
| 724 | + if node.op == "call_function" |
| 725 | + ] == [ |
| 726 | + (exir_ops.edge.aten.view_copy.default, [1, 4, 6]), |
| 727 | + (LAYOUT_PERMUTE_COPY, [0, 2, 1]), |
| 728 | + ] |
| 729 | + torch.testing.assert_close( |
| 730 | + result.graph_module(x)[0], x.permute(0, 3, 1, 2).reshape(1, 6, 4) |
| 731 | + ) |
| 732 | + |
| 733 | + |
| 734 | +@pytest.mark.parametrize( |
| 735 | + "permute,shape,view_shape", |
| 736 | + [ |
| 737 | + (LAYOUT_PERMUTE_COPY, (1, 5, 4, 6), [1, 1, 6, 5, 4]), |
| 738 | + (exir_ops.edge.aten.permute_copy.default, (1, 1, 4, 6), [1, 6, 4]), |
| 739 | + ], |
| 740 | + ids=["layout_copy_unsqueeze", "user_permute_squeeze"], |
| 741 | +) |
| 742 | +def test_other_permutes_are_not_sunk(permute, shape, view_shape): |
| 743 | + graph_module = _permute_then_view(permute, shape, view_shape) |
| 744 | + |
| 745 | + result = CortexMPostponeLayoutCopyBelowSqueezePass()(graph_module) |
| 746 | + |
| 747 | + assert not result.modified |
0 commit comments