Arm backend: Add transpose propagation pass#20625
Conversation
Adds abstract pass PropagateViewCopyPermutePass and two implementations PropagateViewCopyPermuteUpPass and PropagateViewCopyPermutePass Adds a helper function to ViewMap to update the view-shape after a view-op swap + additional validity checks. Adds new InsertDataLayoutCastsPass under the propagation passes to not move permutes/views outside of their intended dtype cast The move exposed an issue where tosa ops require the same dtype on both inputs which is not guaranteed at that stage in the pipeline. This is fixed by adding CastInt64BuffersToInt32Pass before TOSA dialect rewrite. Signed-off-by: Adrian Lundell <adrian.lundell@arm.com> Change-Id: I31bc29540cebce9eb0ee3d5a3b2c016c19b15809
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20625
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 1 Unrelated FailureAs of commit daabdf5 with merge base 0d8bb3d ( NEW FAILURE - The following job has failed:
FLAKY - The following job failed but was likely due to flakiness present on trunk:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
There was a problem hiding this comment.
Pull request overview
This PR extends the Arm backend TOSA lowering pipeline with a new permute/view propagation pass framework to reduce unnecessary transposes, improves view-dimension mapping utilities to support the propagation logic, and adjusts pipeline ordering to avoid dtype-related issues (including adding an earlier int64→int32 cast before TOSA dialect rewrite). It also adds/updates tests to validate the new behavior and updates expected transpose counts for several model cases.
Changes:
- Add
PropagateViewCopyPermutePassplus Up/Down implementations to propagate and fusepermute_copy/view(_copy)around transparent and arg-updating ops. - Enhance
ViewMapdimension mapping and add helpers to remap view target shapes and unit slices after swaps. - Update Arm pass pipeline ordering (including inserting
CastInt64BuffersToInt32Passearlier) and expandInsertDataLayoutCastsPassoperator coverage; update tests and transpose-count expectations accordingly.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| backends/arm/_passes/propagate_view_copy_permute_pass.py | New propagation pass framework + Up/Down implementations; integrates canonicalization and permute/view fusion. |
| backends/arm/_passes/dim_maps.py | Updates ViewMap.map_dim logic and adds shape/slice remapping helpers needed by propagation. |
| backends/arm/_passes/insert_data_layout_casts_pass.py | Extends cast insertion targeting to include additional TOSA data-layout ops (e.g. TRANSPOSE/SLICE/CONCAT, etc.). |
| backends/arm/_passes/arm_pass_manager.py | Replaces older permute canonicalization/fusion path with new propagation passes; adjusts pass ordering and adds CastInt64BuffersToInt32Pass earlier. |
| backends/arm/_passes/init.py | Exposes new propagation passes from the Arm passes package. |
| backends/arm/test/passes/test_propagate_permutes_views_pass.py | New comprehensive unit tests for propagation behavior (FX-graph and pipeline-level). |
| backends/arm/test/passes/test_insert_data_layout_casts_pass.py | Adds slice coverage for InsertDataLayoutCastsPass under bf16 extension profile. |
| backends/arm/test/passes/test_dim_maps.py | Updates/extends ViewMap mapping expectations and adds unit-slice remap tests. |
| backends/arm/test/misc/test_transpose_counts.py | Updates expected transpose counts after new propagation behavior reduces transposes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def test_propagate_permute_down_through_transparent_ops_tosa_FP() -> None: | ||
| def predicate(targets: list[object]) -> None: | ||
| assert targets.index(PERMUTE) < targets.index(RELU) < targets.index(NEG) | ||
|
|
||
| pipeline = PassPipeline[input_t]( | ||
| DownwardPermute(), | ||
| DownwardPermute.data, | ||
| quantize=False, | ||
| ops_before_pass={ | ||
| "executorch_exir_dialects_edge__ops_aten_permute_copy_default": 1, | ||
| }, | ||
| ops_after_pass={ | ||
| "executorch_exir_dialects_edge__ops_aten_permute_copy_default": 1, | ||
| }, | ||
| pass_list=[PropagateViewCopyPermuteUpPass], | ||
| pass_functions=[_assert_call_targets(predicate)], | ||
| ) | ||
| pipeline.run() | ||
|
|
| def test_propagate_view_down_through_transparent_ops_tosa_FP() -> None: | ||
| def predicate(targets: list[object]) -> None: | ||
| assert targets.index(VIEW) < targets.index(RELU) < targets.index(NEG) | ||
|
|
||
| pipeline = PassPipeline[input_t]( | ||
| DownwardView(), | ||
| DownwardView.data, | ||
| quantize=False, | ||
| ops_before_pass={ | ||
| "executorch_exir_dialects_edge__ops_aten_view_copy_default": 1, | ||
| }, | ||
| ops_after_pass={ | ||
| "executorch_exir_dialects_edge__ops_aten_view_copy_default": 1, | ||
| }, | ||
| pass_list=[PropagateViewCopyPermuteUpPass], | ||
| pass_functions=[_assert_call_targets(predicate)], | ||
| ) | ||
| pipeline.run() | ||
|
|
| class StopAtSharedTransformInput(torch.nn.Module): | ||
| def forward(self, x: torch.Tensor) -> torch.Tensor: | ||
| y = x.permute(0, 2, 3, 1) | ||
| return (y * y.sigmoid()).permute(0, 3, 1, 2) | ||
|
|
||
| data = (torch.randn(1, 2, 3, 4),) | ||
|
|
| if len(next_nodes) == 0: | ||
| assert node.op in ( | ||
| "placeholder", | ||
| "output", | ||
| ), f"{self.__class__.__name__} reached an endpoint node which is not a placeholder or output: {frontier}" | ||
| break |
| assert previous_frontier is not None | ||
| self._move_node(node, frontier, previous_frontier) | ||
| refresh_permute_view_meta(node) | ||
| return True |
Reverts #20625 FYI @AdrianLundell - This broke some internal tests, I'll own getting it re-merged. cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell
Adds abstract pass PropagateViewCopyPermutePass and two implementations PropagateViewCopyPermuteUpPass and PropagateViewCopyPermutePass
Adds a helper function to ViewMap to update the view-shape after a view-op swap + additional validity checks.
Adds new InsertDataLayoutCastsPass under the propagation passes to not move permutes/views outside of their intended dtype cast
The move exposed an issue where tosa ops
require the same dtype on both inputs which is not guaranteed at that stage in the pipeline. This is fixed by adding CastInt64BuffersToInt32Pass before TOSA dialect rewrite.
cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani