Skip to content

Commit 8ffdf05

Browse files
Arm backend: Rerun duplicate-user fusion after TOSA lowering
Late TOSA transformations can introduce equivalent operations after the first FuseDuplicateUsersPass invocation. Rerun it after TOSA and shape transformations, before output nodes are made unique. TOSA-FP operator comparisons: | Model | Before | After | Reduction | |--------------------|-------:|------:|----------:| | SD3 | 1,663 | 1,573 | 90 (5.4%) | | InceptionV3 | 762 | 746 | 16 (2.1%) | | Conformer delegate | 537 | 494 | 43 (8.0%) | All three reference-output tests pass with late fusion enabled. Add regression coverage for the late fusion and output uniqueness pass ordering. Change-Id: Ia4e2335e05b18d16b93c7e035a5502b8f050855c Signed-off-by: Yufeng Shi <yufeng.shi@arm.com>
1 parent f8b7f62 commit 8ffdf05

3 files changed

Lines changed: 55 additions & 4 deletions

File tree

backends/arm/_passes/arm_pass_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,9 +654,14 @@ def _tosa_pipeline(
654654
SymbolicToTosaShapesPass(),
655655
InsertDynamicPaddingPass(),
656656
FuseConsecutiveConcatShapesPass(),
657-
EnsureUniqueOutputNodesPass(),
657+
# No-op removal can expose duplicate users and outputs, so run
658+
# FuseDuplicateUsersPass and EnsureUniqueOutputNodesPass afterward.
658659
RemoveNoopPass(),
659660
InsertRescalePass(),
661+
# Late TOSA transformations can introduce duplicate users after
662+
# the first FuseDuplicateUsersPass invocation.
663+
FuseDuplicateUsersPass(),
664+
EnsureUniqueOutputNodesPass(),
660665
]
661666
)
662667

backends/arm/test/misc/test_transpose_counts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def forward(self, x: torch.Tensor):
441441
0,
442442
),
443443
"model_1_conv_maxpool_residual_linear": TransposeCountCase(
444-
Model1ConvMaxPoolResidualLinear(), (torch.randn(2, 8, 64),), 5
444+
Model1ConvMaxPoolResidualLinear(), (torch.randn(2, 8, 64),), 4
445445
),
446446
"model_2_conv_mha_linear_layernorm": TransposeCountCase(
447447
Model2ConvMhaLinearLayerNorm(), (torch.randn(2, 8, 32),), 8
@@ -462,10 +462,10 @@ def forward(self, x: torch.Tensor):
462462
Model7DwConvBatchNormLinear(), (torch.randn(2, 8, 64),), 1
463463
),
464464
"model_8_conv_batchnorm_maxpool_residual": TransposeCountCase(
465-
Model8ConvBatchNormMaxPoolResidual(), (torch.randn(1, 8, 16, 16),), 4
465+
Model8ConvBatchNormMaxPoolResidual(), (torch.randn(1, 8, 16, 16),), 3
466466
),
467467
"model_9_dilated_conv_batchnorm_avgpool_residual": TransposeCountCase(
468-
Model9DilatedConvBatchNormAvgPoolResidual(), (torch.randn(1, 8, 16, 16),), 4
468+
Model9DilatedConvBatchNormAvgPoolResidual(), (torch.randn(1, 8, 16, 16),), 3
469469
),
470470
"model_10_dwconv_batchnorm_linear_cat": TransposeCountCase(
471471
Model10DwConvBatchNormLinearCat(), (torch.randn(2, 8, 64),), 1

backends/arm/test/passes/test_fuse_duplicate_users_pass.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
import executorch.backends.arm.tosa.dialect # noqa: F401
99
import torch
1010
from executorch.backends.arm._passes import FuseDuplicateUsersPass
11+
from executorch.backends.arm._passes.arm_pass_manager import ArmPassManager
1112
from executorch.backends.arm.test import common
1213
from executorch.backends.arm.test.tester.test_pipeline import PassPipeline
14+
from executorch.backends.arm.tosa.compile_spec import TosaCompileSpec
1315
from executorch.backends.arm.tosa.specification import (
1416
TosaLoweringContext,
1517
TosaSpecification,
1618
)
19+
from executorch.exir import EdgeCompileConfig, to_edge
1720
from executorch.exir.dialects._ops import ops as exir_ops
21+
from torch.export import export
1822
from torch.fx import Graph, GraphModule
1923

2024
input_t = Tuple[torch.Tensor] # Input x
@@ -167,3 +171,45 @@ def test_fuse_duplicate_users_removes_identical_rescale_users():
167171
assert len(rescale_nodes) == 1
168172
output_node = result.graph_module.graph.output_node()
169173
assert output_node.args[0] == (rescale_nodes[0], rescale_nodes[0])
174+
175+
176+
class LateDuplicateUsers(torch.nn.Module):
177+
def __init__(self):
178+
super().__init__()
179+
self.register_buffer("first", torch.ones(2, 3))
180+
self.register_buffer("second", torch.ones(2, 3))
181+
182+
def forward(self, x):
183+
return x + self.first, x + self.second
184+
185+
186+
def test_fuse_duplicate_users_runs_after_tosa_transformations():
187+
exported_program = export(LateDuplicateUsers(), (torch.ones(2, 3),), strict=True)
188+
edge_program = to_edge(
189+
exported_program,
190+
compile_config=EdgeCompileConfig(_check_ir_validity=False),
191+
)
192+
edge_exported_program = edge_program.exported_program()
193+
194+
graph_module = ArmPassManager(
195+
TosaCompileSpec("TOSA-1.0+FP")
196+
).transform_to_backend_pipeline(
197+
edge_exported_program, edge_exported_program.graph_module
198+
)
199+
200+
add_nodes = [
201+
node
202+
for node in graph_module.graph.nodes
203+
if node.target == exir_ops.backend.tosa.ADD.default
204+
]
205+
identity_nodes = [
206+
node
207+
for node in graph_module.graph.nodes
208+
if node.target == exir_ops.backend.tosa.IDENTITY.default
209+
]
210+
211+
graph_module.graph.lint()
212+
assert len(add_nodes) == 1
213+
assert len(identity_nodes) == 2
214+
assert all(node.args[0] is add_nodes[0] for node in identity_nodes)
215+
assert graph_module.graph.output_node().args[0] == tuple(identity_nodes)

0 commit comments

Comments
 (0)