Skip to content

Commit 66b5448

Browse files
Arm backend: SymInt fixes (pytorch#18092)
Avoid computations that collapses the symint representation and materializes the symbolic values to its hints. cc @digantdesai @freddan80 @per @zingo @mansnils @Sebastian-Larsson @robell Signed-off-by: Oscar Andersson <oscar.andersson@arm.com>
1 parent 0112ada commit 66b5448

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

‎backends/arm/_passes/decompose_linear_pass.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
from typing import Set, Type
88

9-
import numpy as np
9+
import torch
10+
1011
from executorch.backends.arm._passes import ArmPass
1112
from executorch.backends.arm._passes.arm_pass_utils import (
1213
create_node,
@@ -44,7 +45,9 @@ def call(self, graph_module):
4445
output_shape = get_first_fake_tensor(node).shape
4546
input_shape = get_first_fake_tensor(input).shape
4647
weights_shape = get_first_fake_tensor(weights).shape
47-
batches = int(np.prod(input_shape[:-1])) if len(input_shape) > 1 else 1
48+
batches = torch.sym_int(1)
49+
for dim in input_shape[:-1]:
50+
batches *= dim
4851
# input has shape (..., Ci)
4952
input_reshaped_shape = [batches, input_shape[-1], 1, 1]
5053
# weights have shape (Co, Ci)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from typing import Tuple
7+
8+
import torch
9+
from executorch.backends.arm._passes import DecomposeLinearPass
10+
from executorch.exir import to_edge
11+
from executorch.exir.capture._config import EdgeCompileConfig
12+
from torch.export import Dim, export
13+
14+
15+
class Linear(torch.nn.Module):
16+
17+
def __init__(self) -> None:
18+
super(Linear, self).__init__()
19+
self.linear = torch.nn.Linear(16, 16)
20+
21+
def forward(self, x: torch.Tensor) -> torch.Tensor:
22+
return self.linear(x)
23+
24+
def get_example_inputs(self) -> Tuple[torch.Tensor]:
25+
return (torch.randn(1, 3, 16),)
26+
27+
28+
def test_decompose_linear_no_target_dynamic() -> None:
29+
module = Linear()
30+
example_inputs = module.get_example_inputs()
31+
ep = export(
32+
module,
33+
example_inputs,
34+
strict=True,
35+
dynamic_shapes={
36+
"x": {
37+
1: Dim("batch_1", min=1, max=16),
38+
}
39+
},
40+
)
41+
edge_model = to_edge(
42+
ep,
43+
compile_config=EdgeCompileConfig(
44+
_core_aten_ops_exception_list=[torch.ops.aten.linear.default],
45+
preserve_ops=[torch.ops.aten.linear.default],
46+
),
47+
)
48+
edge_model = edge_model.transform([DecomposeLinearPass()])
49+
output_node = edge_model.exported_program().graph_module.graph.output_node()
50+
assert output_node.op == "output"
51+
outputs = output_node.args[0]
52+
# Make sure the output shape has symbolic dimensions after the pass
53+
for output in outputs:
54+
assert any(isinstance(dim, torch.SymInt) for dim in output.meta["val"].shape)

0 commit comments

Comments
 (0)