Skip to content

Commit 148986d

Browse files
laithsakkameta-codesync[bot]
authored andcommitted
Migrate to Graph.materialize_symints; stop passing raw SymInts
Summary: reland plan for pytorch/pytorch#186272 due to executorch dependency have to stage changes !! Updates the ARM backend passes (convert_full_like_to_full, decompose_linear, decompose_select, insert_dynamic_padding, rewrite_conv, size_adjust_input) and the ExportPass base to materialize SymInt-typed FX-node arguments via Graph.materialize_symints / create_size_node, so they no longer pass raw SymInts to Graph.create_node and the new pytorch warning falls silent. Test assertions in test_decompose_select_pass / test_insert_dynamic_padding_pass / test_dynamic_shape_propagation are updated to expect FX Nodes (with the SymInt stored in meta['val']). This is the second of three diffs split from D107573548. Differential Revision: D107938874
1 parent e0be283 commit 148986d

11 files changed

Lines changed: 146 additions & 28 deletions
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
release/2.12
1+
release/2.13

backends/arm/_passes/convert_full_like_to_full_pass.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ def call_operator(self, op, args, kwargs, meta):
4242
return super().call_operator(op, args, kwargs, meta)
4343

4444
tensor = args[0].data
45-
full_args = (list(tensor.shape), args[1])
45+
# Each entry is an int (static dim) or an aten.sym_size.int ProxyValue (dynamic dim).
46+
size_args = self.call_size_operator_all(args[0], meta)
47+
full_args = (size_args, args[1])
4648
full_kwargs = {"dtype": tensor.dtype}
4749
return super().call_operator(
4850
exir_ops.edge.aten.full.default, full_args, full_kwargs, meta

backends/arm/_passes/decompose_linear_pass.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,27 @@ def call(self, graph_module):
5555
# weights have shape (Co, Ci)
5656
weights_reshaped_shape = [weights_shape[0], weights_shape[1], 1, 1]
5757

58+
# ``Graph.create_node`` rejects raw SymInts in call_function
59+
# args; ``materialize_symints`` walks each value's sympy
60+
# expression and emits the FX subgraph that recomputes it from
61+
# existing producers (placeholders / sym ops). One call shares
62+
# the symbol→Proxy hash-cons across all three shape lists so
63+
# repeated symbols become a single subgraph.
5864
with graph_module.graph.inserting_before(node):
65+
n_in, n_w = (
66+
len(input_reshaped_shape),
67+
len(weights_reshaped_shape),
68+
)
69+
all_sizes = graph_module.graph.materialize_symints(
70+
[
71+
*input_reshaped_shape,
72+
*weights_reshaped_shape,
73+
*output_shape,
74+
]
75+
)
76+
input_reshaped_shape = all_sizes[:n_in]
77+
weights_reshaped_shape = all_sizes[n_in : n_in + n_w]
78+
output_size = all_sizes[n_in + n_w :]
5979
# Reshape input to 4D with shape (N, Ci, 1, 1)
6080
input_reshaped = create_node(
6181
graph=graph_module.graph,
@@ -102,7 +122,7 @@ def call(self, graph_module):
102122
output = create_node(
103123
graph=graph_module.graph,
104124
op_target=exir_ops.edge.aten.view_copy.default,
105-
args=(conv, list(output_shape)),
125+
args=(conv, output_size),
106126
kwargs={},
107127
from_node=node,
108128
inherit_qparams=False,

backends/arm/_passes/decompose_select.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,18 @@ def call(self, graph_module: torch.fx.GraphModule):
5555
index = size_at_dim - abs(index)
5656

5757
with graph_module.graph.inserting_before(node):
58+
# ``Graph.create_node`` rejects raw SymInts in call_function
59+
# args; ``materialize_symints`` lifts symbolic ``index`` /
60+
# ``index + 1`` (from negative-index wrap-around against a
61+
# dynamic shape) into graph nodes in a single call (shares
62+
# producer-discovery + hash-cons). Static ints pass through.
63+
start_arg, end_arg = graph_module.graph.materialize_symints(
64+
[index, index + 1]
65+
)
5866
slice_node = create_node(
5967
graph_module.graph,
6068
slice_op,
61-
(input_node, dim, index, index + 1),
69+
(input_node, dim, start_arg, end_arg),
6270
from_node=node,
6371
inherit_qparams=False,
6472
)

backends/arm/_passes/insert_dynamic_padding.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ class InsertDynamicPaddingPass(ArmOpTargetedPass):
3636
def _is_dynamic_padding(
3737
self, padding: ProxyValue | list[int] | tuple[int, ...]
3838
) -> bool:
39-
return (isinstance(padding, ProxyValue) and is_shape_op_node(padding.node)) or (
40-
(
41-
isinstance(padding, (list, tuple))
42-
and any(isinstance(p, torch.SymInt) for p in padding)
39+
if isinstance(padding, ProxyValue) and is_shape_op_node(padding.node):
40+
return True
41+
if isinstance(padding, (list, tuple)):
42+
return any(
43+
isinstance(p, (torch.SymInt, ProxyValue)) for p in padding
4344
)
44-
)
45+
return False
4546

4647
def call_operator(self, op, args, kwargs, meta, updated=False) -> ProxyValue:
4748
if op not in self.target_ops:

backends/arm/_passes/rewrite_conv_pass.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,29 @@ def call(self, graph_module: torch.fx.GraphModule) -> PassResult: # noqa: C901
607607
dilation,
608608
)
609609

610+
# Compute fake tensor BEFORE materializing SymInts into FX nodes,
611+
# since the underlying op expects ints/SymInts (not FX Nodes).
612+
bias_fake_tensor = get_first_fake_tensor(bias) if bias else None
613+
tosa_node_fake_tensor = target_op(
614+
input_tensor_for_tosa_fake,
615+
weight_fake_tensor,
616+
bias_fake_tensor,
617+
*conv_args[3:],
618+
)
619+
620+
# ``Graph.create_node`` rejects raw SymInts in call_function args.
621+
# If ``pad`` contains symbolic entries, materialize them into FX
622+
# nodes so the TOSA conv node references the producing graph
623+
# subgraph instead of holding raw SymInts.
624+
if isinstance(pad, list) and any(
625+
isinstance(p, torch.SymInt) for p in pad
626+
):
627+
with graph_module.graph.inserting_before(node):
628+
materialized_pad = graph_module.graph.materialize_symints(pad)
629+
new_conv_args = list(conv_args)
630+
new_conv_args[4] = materialized_pad
631+
conv_args = tuple(new_conv_args)
632+
610633
with graph_module.graph.inserting_after(node):
611634
tosa_op = create_node(
612635
graph=graph_module.graph,
@@ -615,13 +638,6 @@ def call(self, graph_module: torch.fx.GraphModule) -> PassResult: # noqa: C901
615638
from_node=node,
616639
inherit_qparams=True,
617640
)
618-
bias_fake_tensor = get_first_fake_tensor(bias) if bias else None
619-
tosa_node_fake_tensor = target_op(
620-
input_tensor_for_tosa_fake,
621-
weight_fake_tensor,
622-
bias_fake_tensor,
623-
*conv_args[3:],
624-
)
625641
tosa_op.meta["val"] = tosa_node_fake_tensor
626642

627643
node_replacement, node_replacement_fake_tensor = (

backends/arm/_passes/size_adjust_input_pass.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,29 @@ def call(self, graph_module: torch.fx.GraphModule) -> PassResult:
233233

234234
parent_node = node.args[0]
235235
with graph_module.graph.inserting_before(node):
236+
# ``Graph.create_node`` rejects raw SymInts in
237+
# call_function args. Aggregate every slice arg across
238+
# all entries and lift via a single ``materialize_symints``
239+
# call -- the helper walks the graph once for producer
240+
# discovery, so a single call amortises that cost and lets
241+
# symints with shared sub-expressions get hash-consed into
242+
# one subgraph. Plain ints pass through unchanged.
243+
flat = [a for args in slice_args for a in args]
244+
materialized = iter(graph.materialize_symints(flat))
245+
# Pop exactly len(args) values from the materialized iterator
246+
# and pack them back into a tuple -- regroups the flat list
247+
# of lifted values into the original (dim, start, end) shape.
248+
lifted_slice_args = [
249+
tuple(next(materialized) for _ in args) for args in slice_args
250+
]
251+
236252
last_node = cast(torch.fx.Node, parent_node)
237-
for args in slice_args:
253+
for args in lifted_slice_args:
238254
slice_node = create_node(
239-
graph, slice_op, (last_node,) + args, from_node=node
255+
graph,
256+
slice_op,
257+
(last_node,) + args,
258+
from_node=node,
240259
)
241260
last_node = slice_node
242261
node.replace_input_with(cast(torch.fx.Node, parent_node), last_node)

backends/arm/test/passes/test_decompose_select_pass.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,18 @@ def test_decompose_select_negative_symbolic_index_uses_symbolic_sub() -> None:
6363

6464
slice_node = slice_nodes[0]
6565
assert slice_node.args[1] == 1
66-
assert slice_node.args[2] != -1
67-
assert isinstance(slice_node.args[2], torch.SymInt)
68-
assert isinstance(slice_node.args[3], torch.SymInt)
69-
assert str(slice_node.args[2]).endswith(" - 1")
70-
assert str(slice_node.args[3]) in str(slice_node.args[2])
66+
# Start/end are now FX nodes (materialized from SymInts) rather than raw
67+
# SymInts, since Graph.create_node rejects raw symbolic leaves in
68+
# call_function args. The original SymInt is preserved in meta['val'].
69+
start_arg, end_arg = slice_node.args[2], slice_node.args[3]
70+
assert isinstance(start_arg, torch.fx.Node)
71+
assert isinstance(end_arg, torch.fx.Node)
72+
start_val = start_arg.meta["val"]
73+
end_val = end_arg.meta["val"]
74+
assert isinstance(start_val, torch.SymInt)
75+
assert isinstance(end_val, torch.SymInt)
76+
assert str(start_val).endswith(" - 1")
77+
assert str(end_val) in str(start_val)
7178
assert squeeze_nodes[0].args == (slice_node, [1])
7279

7380
result.graph_module.graph.lint()

backends/arm/test/passes/test_insert_dynamic_padding_pass.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ def test_insert_dynamic_padding():
5050
n for n in nodes if n.target == exir_ops.backend.tosa.CONV2D.default
5151
)
5252
initial_padding = conv_node.args[4]
53-
assert any(isinstance(p, torch.SymInt) for p in initial_padding)
53+
# SymInts now appear as FX nodes (with the SymInt stored in meta['val'])
54+
# so that Graph.create_node does not reject raw SymInts in call_function args.
55+
assert any(isinstance(p, torch.fx.Node) for p in initial_padding)
56+
initial_padding_vals = [
57+
p.meta["val"] if isinstance(p, torch.fx.Node) else p
58+
for p in initial_padding
59+
]
5460

5561
edge_model = edge_model.transform(
5662
[
@@ -70,5 +76,5 @@ def test_insert_dynamic_padding():
7076
pad_list = padding_node.args[1].meta["val"]
7177
assert len(pad_list) == 8
7278
assert pad_list[:2] == [0, 0] # N-padding
73-
assert pad_list[2:6] == initial_padding # HW-padding in NHWC order
79+
assert pad_list[2:6] == initial_padding_vals # HW-padding in NHWC order
7480
assert pad_list[6:] == [0, 0] # C-padding

exir/pass_base.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,45 @@ def call_operator(
562562
) -> ProxyValue:
563563
return self._fx("call_function", op, args, kwargs, meta)
564564

565+
def call_size_operator(
566+
self,
567+
tensor_proxy: ProxyValue,
568+
dim: int,
569+
meta: NodeMetadata,
570+
) -> Union[ProxyValue, int]:
571+
"""Read ``tensor_proxy.size(dim)`` as a value usable in graph args.
572+
Returns a plain ``int`` for static dims; emits and returns an
573+
``aten.sym_size.int`` ``ProxyValue`` for SymInt dims (since
574+
``Graph.create_node`` rejects raw SymInts in call_function args).
575+
"""
576+
size = tensor_proxy.data.shape[dim]
577+
if isinstance(size, torch.SymInt):
578+
new_proxy = self.call_operator(
579+
torch.ops.aten.sym_size.int, (tensor_proxy, dim), {}, meta
580+
)
581+
# Mirror source's "example_value" if present, so the new node
582+
# matches the surrounding graph's meta-key convention. "val"
583+
# is already set by call_operator → _fx → set_metadata.
584+
if "example_value" in tensor_proxy.node.meta:
585+
new_proxy.node.meta["example_value"] = new_proxy.node.meta["val"]
586+
return new_proxy
587+
return int(size)
588+
589+
def call_size_operator_all(
590+
self,
591+
tensor_proxy: ProxyValue,
592+
meta: NodeMetadata,
593+
) -> list[Union[ProxyValue, int]]:
594+
"""Return all dims of ``tensor_proxy.shape`` as a list of values
595+
usable in graph args. Each entry is an ``int`` (static dim) or an
596+
``aten.sym_size.int`` ``ProxyValue`` (dynamic dim) — see
597+
``call_size_operator``.
598+
"""
599+
return [
600+
self.call_size_operator(tensor_proxy, d, meta)
601+
for d in range(len(tensor_proxy.data.shape))
602+
]
603+
565604
def call_sym(
566605
self,
567606
target: Fn,

0 commit comments

Comments
 (0)