|
14 | 14 | CortexMPassManager, |
15 | 15 | LiftConstantTensorsPass, |
16 | 16 | ) |
| 17 | +from executorch.backends.cortex_m.passes.explicit_layout_pass import ( |
| 18 | + CortexMPostponeLayoutCopyBelowSqueezePass, |
| 19 | +) |
17 | 20 | from executorch.backends.cortex_m.quantizer.quantizer import CortexMQuantizer |
18 | 21 | from executorch.backends.cortex_m.target_config import CortexM, CortexMTargetConfig |
19 | 22 | from executorch.backends.cortex_m.test.tester import ( |
|
25 | 28 | from executorch.backends.transforms.remove_unused_constants_pass import ( |
26 | 29 | RemoveUnusedConstantsPass, |
27 | 30 | ) |
| 31 | +from executorch.backends.test.graph_builder import GraphBuilder |
| 32 | +from executorch.backends.transforms.channels_last_layout import LAYOUT_PERMUTE_COPY |
28 | 33 | from executorch.exir import to_edge |
29 | 34 | from executorch.exir.dialects._ops import ops as exir_ops |
30 | 35 | from torch.export import ExportedProgram |
@@ -89,6 +94,18 @@ def forward(self, x): |
89 | 94 | return self.conv2(self.conv1(torch.tanh(x)).view(1, 4, 32)) |
90 | 95 |
|
91 | 96 |
|
| 97 | +class Conv1dChain(torch.nn.Module): |
| 98 | + def __init__(self) -> None: |
| 99 | + super().__init__() |
| 100 | + self.conv1 = torch.nn.Conv1d(8, 8, 3, padding=1) |
| 101 | + self.conv2 = torch.nn.Conv1d(8, 8, 3, padding=1) |
| 102 | + self.conv3 = torch.nn.Conv1d(8, 8, 1) |
| 103 | + |
| 104 | + def forward(self, x): |
| 105 | + y = torch.tanh(self.conv1(x)) |
| 106 | + return self.conv3(torch.relu(self.conv2(y) + y)) |
| 107 | + |
| 108 | + |
92 | 109 | class Apply(torch.nn.Module): |
93 | 110 | def __init__(self, fn) -> None: |
94 | 111 | super().__init__() |
@@ -650,3 +667,40 @@ def test_unquantized_constant_is_not_precomputed(use_explicit_layout): |
650 | 667 |
|
651 | 668 | assert _count(program, exir_ops.edge.aten.full.default) == 1 |
652 | 669 | tester.run_method_and_compare_outputs(inputs=inputs) |
| 670 | + |
| 671 | + |
| 672 | +def test_conv1d_chain_keeps_only_the_boundary_layout_copies(): |
| 673 | + torch.manual_seed(0) |
| 674 | + inputs = (torch.randn(1, 8, 16),) |
| 675 | + tester = _run_explicit_layout_passes(CortexMTester(Conv1dChain().eval(), inputs)) |
| 676 | + program = tester.get_artifact(StageType.RUN_PASSES).exported_program() |
| 677 | + |
| 678 | + assert _count(program, exir_ops.edge.cortex_m.quantized_conv2d_nhwc.default) == 3 |
| 679 | + assert _count(program, exir_ops.edge.cortex_m.transpose.default) == 2 |
| 680 | + assert _count(program, exir_ops.edge.aten.permute_copy.default) == 0 |
| 681 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 682 | + |
| 683 | + |
| 684 | +@pytest.mark.parametrize( |
| 685 | + "permute,shape,view_shape,sunk", |
| 686 | + [ |
| 687 | + (LAYOUT_PERMUTE_COPY, (1, 1, 4, 6), [1, 6, 4], True), |
| 688 | + (LAYOUT_PERMUTE_COPY, (1, 5, 4, 6), [1, 1, 6, 5, 4], False), |
| 689 | + (exir_ops.edge.aten.permute_copy.default, (1, 1, 4, 6), [1, 6, 4], False), |
| 690 | + ], |
| 691 | + ids=["layout_copy_squeeze", "layout_copy_unsqueeze", "user_permute_squeeze"], |
| 692 | +) |
| 693 | +def test_only_layout_copies_are_sunk_below_a_squeeze( |
| 694 | + permute, shape, view_shape, sunk |
| 695 | +): |
| 696 | + builder = GraphBuilder() |
| 697 | + x = builder.placeholder("x", torch.randn(shape)) |
| 698 | + permuted = builder.call_operator(op=permute, args=(x, [0, 3, 1, 2])) |
| 699 | + view = builder.call_operator( |
| 700 | + op=exir_ops.edge.aten.view_copy.default, args=(permuted, view_shape) |
| 701 | + ) |
| 702 | + builder.output([view]) |
| 703 | + |
| 704 | + result = CortexMPostponeLayoutCopyBelowSqueezePass()(builder.get_graph_module()) |
| 705 | + |
| 706 | + assert result.modified == sunk |
0 commit comments