Skip to content

Commit 667c91b

Browse files
Arm backend: Rerun duplicate-user fusion after TOSA lowering (#21041)
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 cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Yufeng Shi <yufeng.shi@arm.com>
1 parent 2c92642 commit 667c91b

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

backends/arm/_passes/arm_pass_manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,9 +656,14 @@ def _tosa_pipeline(
656656
SymbolicToTosaShapesPass(),
657657
InsertDynamicPaddingPass(),
658658
FuseConsecutiveConcatShapesPass(),
659-
EnsureUniqueOutputNodesPass(),
659+
# No-op removal can expose duplicate users and outputs, so run
660+
# FuseDuplicateUsersPass and EnsureUniqueOutputNodesPass afterward.
660661
RemoveNoopPass(),
661662
InsertRescalePass(),
663+
# Late TOSA transformations can introduce duplicate users after
664+
# the first FuseDuplicateUsersPass invocation.
665+
FuseDuplicateUsersPass(),
666+
EnsureUniqueOutputNodesPass(),
662667
]
663668
)
664669

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)