1414from executorch .backends .arm ._passes .dim_maps import PermuteMap , same_numel , ViewMap
1515from executorch .exir .dialects ._ops import ops as exir_ops
1616from executorch .exir .pass_base import ExportPass , PassResult
17+ from torch .export .exported_program import ExportedProgram
18+ from torch .export .graph_signature import InputSpec , TensorArgument
1719from torch .fx import GraphModule , Node
1820
1921
22+ class NormalizeTransformInputPlaceholdersPass (ExportPass ):
23+ """Normalize placeholder names for lifted transform inputs."""
24+
25+ def __init__ (self , exported_program : ExportedProgram | None = None ) -> None :
26+ super ().__init__ ()
27+ self .exported_program = exported_program
28+
29+ def call (self , graph_module : GraphModule ) -> PassResult :
30+ return PassResult (
31+ graph_module ,
32+ self ._normalize_transform_user_input_placeholders (graph_module ),
33+ )
34+
35+ def _normalize_transform_user_input_placeholders (
36+ self , graph_module : GraphModule
37+ ) -> bool :
38+ if self .exported_program is None :
39+ return False
40+
41+ signature = self .exported_program .graph_signature
42+ placeholder_names = {
43+ node .name for node in graph_module .graph .nodes if node .op == "placeholder"
44+ }
45+ name_updates : dict [str , str ] = {}
46+ modified = False
47+ for node in graph_module .graph .nodes :
48+ if node .op != "placeholder" :
49+ continue
50+ if not self ._placeholder_represents_transform (node ):
51+ continue
52+ old_target = str (node .target )
53+
54+ if node .target != node .name :
55+ node .target = node .name
56+ modified = True
57+ if old_target not in placeholder_names :
58+ name_updates [old_target ] = node .name
59+
60+ if not modified :
61+ return False
62+
63+ if name_updates :
64+ signature .input_specs = [
65+ self ._renamed_input_spec (spec , name_updates )
66+ for spec in signature .input_specs
67+ ]
68+ graph_module .graph .lint ()
69+ graph_module .recompile ()
70+ return True
71+
72+ def _placeholder_represents_transform (self , node : Node ) -> bool :
73+ return any (
74+ self ._matches_transform_placeholder_name (node .name , target )
75+ or self ._matches_transform_placeholder_name (str (node .target ), target )
76+ for target in FuseIdenticalInputTransformsPass ._TARGETS
77+ )
78+
79+ def _matches_transform_placeholder_name (
80+ self , name : str , target : torch .fx .node .Target
81+ ) -> bool :
82+ base_name = self ._target_placeholder_name (target )
83+ if name == base_name :
84+ return True
85+
86+ suffix = name .removeprefix (f"{ base_name } _" )
87+ return suffix != name and suffix .isdecimal ()
88+
89+ def _target_placeholder_name (self , target : torch .fx .node .Target ) -> str :
90+ return target .__name__ .replace ("." , "_" ) # type: ignore[union-attr]
91+
92+ def _renamed_input_spec (
93+ self , spec : InputSpec , name_updates : dict [str , str ]
94+ ) -> InputSpec :
95+ if isinstance (spec .arg , TensorArgument ) and spec .arg .name in name_updates :
96+ return InputSpec (
97+ kind = spec .kind ,
98+ arg = TensorArgument (name = name_updates [spec .arg .name ]),
99+ target = spec .target ,
100+ persistent = spec .persistent ,
101+ )
102+ return spec
103+
104+
20105class FuseIdenticalInputTransformsPass (ArmOpTargetedPass ):
21106 """Sink identical input transforms through pointwise ops with multiple
22107 inputs. Note that this is only valid for data movement transforms; most
@@ -60,8 +145,16 @@ class FuseIdenticalInputTransformsPass(ArmOpTargetedPass):
60145
61146 target_ops = _BINARY_ELEMENTWISE_OPS | _CONCAT_OPS
62147
148+ def __init__ (self , exported_program : ExportedProgram | None = None ) -> None :
149+ super ().__init__ ()
150+ self .exported_program = exported_program
151+
63152 def call (self , graph_module : GraphModule ) -> PassResult :
64- modified = False
153+ modified = (
154+ NormalizeTransformInputPlaceholdersPass (self .exported_program )
155+ .call (graph_module )
156+ .modified
157+ )
65158
66159 while True :
67160 iteration_modified = False
@@ -124,7 +217,7 @@ def _sink_identical_input_transforms(self, node: Node) -> bool:
124217 args = transform_args ,
125218 kwargs = dict (transform .kwargs ),
126219 )
127- new_node .meta = copy . copy ( transform . meta )
220+ new_node .meta = self . _new_transform_meta ( node , transform )
128221 refresh_permute_view_meta (new_node )
129222
130223 for user in list (node .users ):
@@ -133,6 +226,14 @@ def _sink_identical_input_transforms(self, node: Node) -> bool:
133226
134227 return True
135228
229+ def _new_transform_meta (self , node : Node , transform : Node ) -> dict [str , Any ]:
230+ meta = copy .copy (transform .meta )
231+ if "delegation_tag" in node .meta :
232+ meta ["delegation_tag" ] = node .meta ["delegation_tag" ]
233+ else :
234+ meta .pop ("delegation_tag" , None )
235+ return meta
236+
136237 def _updated_node_args (
137238 self , node : Node , transform : Node , node_val : Any , input_transforms : list [Node ]
138239 ) -> (
0 commit comments