1818import torch
1919from executorch .backends .apple .coreai .compiler .constants import MAIN_ENTRYPOINT
2020from torch .export .exported_program import ExportedProgram
21- from torch .export .graph_signature import InputKind
21+ from torch .export .graph_signature import InputKind , OutputKind
2222
2323logger = logging .getLogger (__name__ )
2424
@@ -49,18 +49,40 @@ def _coreai_io(program, entrypoint: str = MAIN_ENTRYPOINT):
4949 Ranked tensors -> (element_type_str, shape_tuple). Any non-tensor coreai
5050 type is represented as (str(type), None) so the comparison can flag it,
5151 rather than assuming a tensor and crashing on ``.element_type`` / ``.shape``.
52+
53+ Mutated buffers the delegate owns are skipped: coreai-torch marks the state
54+ argument with ``MutableBuffers.buffer_mutation`` naming its result, and
55+ neither crosses the ExecuTorch boundary.
5256 """
5357 from coreai ._compiler .ir import RankedTensorType
5458
55- func_type = program .get_graph (entrypoint ).function_type .value
59+ graph = program ._get_graph (entrypoint )
60+ func_type = graph .function_type .value
5661
5762 def _describe (t ):
5863 if RankedTensorType .isinstance (t ):
5964 return (str (t .element_type ), tuple (t .shape ))
6065 return (str (t ), None )
6166
62- inputs = [_describe (t ) for t in func_type .inputs ]
63- outputs = [_describe (t ) for t in func_type .results ]
67+ arg_attrs = list (graph .arg_attrs or [])
68+ res_attrs = list (graph .res_attrs or [])
69+ state_results = {
70+ attrs ["MutableBuffers.buffer_mutation" ].value
71+ for attrs in arg_attrs
72+ if "MutableBuffers.buffer_mutation" in attrs
73+ }
74+ inputs = [
75+ _describe (t )
76+ for i , t in enumerate (func_type .inputs )
77+ if i >= len (arg_attrs ) or "MutableBuffers.buffer_mutation" not in arg_attrs [i ]
78+ ]
79+ outputs = [
80+ _describe (t )
81+ for i , t in enumerate (func_type .results )
82+ if i >= len (res_attrs )
83+ or "coreai.name" not in res_attrs [i ]
84+ or res_attrs [i ]["coreai.name" ].value not in state_results
85+ ]
6486 return inputs , outputs
6587
6688
@@ -73,20 +95,20 @@ def _edge_io(edge_program: ExportedProgram):
7395 entries).
7496
7597 Inputs walk ``graph_signature.input_specs``, which is ordered like the
76- placeholders. Mutated buffers are included alongside the user inputs,
77- mirroring ``TorchConverter._register_io``: a mutation is passed in and
78- handed back, so coreai gives it a graph argument.
98+ placeholders, and outputs walk ``output_specs``, ordered like the output
99+ node. Mutated buffers the delegate owns (``BUFFER`` inputs in
100+ ``buffers_to_mutate`` and their ``BUFFER_MUTATION`` outputs) are skipped:
101+ ExecuTorch never hands that state to the delegate. Mutated user inputs
102+ stay, because ExecuTorch does pass those across.
79103 """
80104 placeholders = {
81105 n .name : n for n in edge_program .graph .nodes if n .op == "placeholder"
82106 }
83107 signature = edge_program .graph_signature
84- mutated = set (signature .buffers_to_mutate .values ())
85108
86109 inputs = []
87110 for spec in signature .input_specs :
88- is_mutated_buffer = spec .kind == InputKind .BUFFER and spec .target in mutated
89- if spec .kind != InputKind .USER_INPUT and not is_mutated_buffer :
111+ if spec .kind != InputKind .USER_INPUT :
90112 continue
91113 node = placeholders .get (getattr (spec .arg , "name" , None ))
92114 # A non-tensor input carries its literal value on the spec instead.
@@ -96,7 +118,11 @@ def _edge_io(edge_program: ExportedProgram):
96118 else :
97119 inputs .append ((type (val ).__name__ , None ))
98120 outputs = []
99- for arg in edge_program .graph .output_node ().args [0 ]:
121+ for spec , arg in zip (
122+ signature .output_specs , edge_program .graph .output_node ().args [0 ]
123+ ):
124+ if spec .kind == OutputKind .BUFFER_MUTATION :
125+ continue
100126 val = arg .meta .get ("val" ) if hasattr (arg , "meta" ) else arg
101127 if hasattr (val , "dtype" ):
102128 outputs .append ((val .dtype , tuple (val .shape )))
0 commit comments