|
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 ( |
20 | 23 | CortexMQuantize, |
21 | 24 | CortexMRunPasses, |
22 | 25 | CortexMTester, |
23 | 26 | ) |
| 27 | +from executorch.backends.test.graph_builder import GraphBuilder |
24 | 28 | from executorch.backends.test.harness.stages import Quantize, StageType |
| 29 | +from executorch.backends.transforms.channels_last_layout import LAYOUT_PERMUTE_COPY |
25 | 30 | from executorch.backends.transforms.remove_unused_constants_pass import ( |
26 | 31 | RemoveUnusedConstantsPass, |
27 | 32 | ) |
@@ -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__() |
@@ -644,3 +661,38 @@ def test_unquantized_constant_is_not_precomputed(use_explicit_layout): |
644 | 661 |
|
645 | 662 | assert _count(program, exir_ops.edge.aten.full.default) == 1 |
646 | 663 | tester.run_method_and_compare_outputs(inputs=inputs) |
| 664 | + |
| 665 | + |
| 666 | +def test_conv1d_chain_keeps_only_the_boundary_layout_copies(): |
| 667 | + torch.manual_seed(0) |
| 668 | + inputs = (torch.randn(1, 8, 16),) |
| 669 | + tester = _run_explicit_layout_passes(CortexMTester(Conv1dChain().eval(), inputs)) |
| 670 | + program = tester.get_artifact(StageType.RUN_PASSES).exported_program() |
| 671 | + |
| 672 | + assert _count(program, exir_ops.edge.cortex_m.quantized_conv2d_nhwc.default) == 3 |
| 673 | + assert _count(program, exir_ops.edge.cortex_m.transpose.default) == 2 |
| 674 | + assert _count(program, exir_ops.edge.aten.permute_copy.default) == 0 |
| 675 | + tester.run_method_and_compare_outputs(inputs=inputs, qtol=1) |
| 676 | + |
| 677 | + |
| 678 | +@pytest.mark.parametrize( |
| 679 | + "permute,shape,view_shape,sunk", |
| 680 | + [ |
| 681 | + (LAYOUT_PERMUTE_COPY, (1, 1, 4, 6), [1, 6, 4], True), |
| 682 | + (LAYOUT_PERMUTE_COPY, (1, 5, 4, 6), [1, 1, 6, 5, 4], False), |
| 683 | + (exir_ops.edge.aten.permute_copy.default, (1, 1, 4, 6), [1, 6, 4], False), |
| 684 | + ], |
| 685 | + ids=["layout_copy_squeeze", "layout_copy_unsqueeze", "user_permute_squeeze"], |
| 686 | +) |
| 687 | +def test_only_layout_copies_are_sunk_below_a_squeeze(permute, shape, view_shape, sunk): |
| 688 | + builder = GraphBuilder() |
| 689 | + x = builder.placeholder("x", torch.randn(shape)) |
| 690 | + permuted = builder.call_operator(op=permute, args=(x, [0, 3, 1, 2])) |
| 691 | + view = builder.call_operator( |
| 692 | + op=exir_ops.edge.aten.view_copy.default, args=(permuted, view_shape) |
| 693 | + ) |
| 694 | + builder.output([view]) |
| 695 | + |
| 696 | + result = CortexMPostponeLayoutCopyBelowSqueezePass()(builder.get_graph_module()) |
| 697 | + |
| 698 | + assert result.modified == sunk |
0 commit comments