Skip to content

Commit 0f01418

Browse files
authored
Merge branch 'pytorch:main' into main
2 parents cdb8db3 + fed6ff1 commit 0f01418

59 files changed

Lines changed: 824 additions & 166 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.ci/scripts/benchmark_tooling/get_benchmark_analysis_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class BenchmarkFilters:
104104

105105
BASE_URLS = {
106106
"local": "http://localhost:3000",
107-
"prod": "https://hud.pytorch.org",
107+
"prod": "https://hud.pytorch.org", # @lint-ignore
108108
}
109109

110110

.ci/scripts/test_lora.sh

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,18 @@ else
117117
echo "Expected result prefix: ${EXPECTED_PREFIX}"
118118
echo "Actual result: ${RESULT}"
119119
echo "Test 2: Failure; results not the same"
120-
# cleanup_files
120+
cleanup_files
121121
exit 1
122122
fi
123123

124124
# Confirm file sizes.
125125
FOUNDATION_SIZE=$(stat -c%s qwen_foundation.ptd)
126126
if [[ $FOUNDATION_SIZE -le "2400000000" ]]; then
127-
echo "qwen_foundation_q.ptd size is: $FOUNDATION_SIZE"
127+
echo "qwen_foundation_q.ptd size is: $FOUNDATION_SIZE"
128128
else
129-
echo "qwen_foundation_q.ptd size: $FOUNDATION_SIZE is greater than threshold 2.4GB"
130-
cleanup_files
131-
exit 1
129+
echo "qwen_foundation_q.ptd size: $FOUNDATION_SIZE is greater than threshold 2.4GB"
130+
cleanup_files
131+
exit 1
132132
fi
133133

134134
### QUANTIZATION & PROGRAM DATA SEPARATION ###
@@ -161,13 +161,19 @@ $PYTHON_EXECUTABLE -m extension.llm.export.export_llm \
161161
+quantization.qmode="8da4w" \
162162
+quantization.group_size=32
163163

164-
# Confirm that qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd are the same.
165-
if diff -q qwen_foundation_lora_q.ptd qwen_foundation_q.ptd > /dev/null; then
166-
echo "qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd are identical."
164+
# Confirm that qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd are the same size.
165+
# TODO(lfq): confirm they are the same (deserialize and check fields)
166+
size1=$(stat -c%s qwen_foundation_lora_q.ptd)
167+
size2=$(stat -c%s qwen_foundation_q.ptd)
168+
169+
if [ "$size1" -eq "$size2" ]; then
170+
echo "qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd are the same size: $size1."
167171
else
168-
echo "qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd are not identical."
169-
cleanup_files
170-
exit 1
172+
echo "qwen_foundation_lora_q.ptd and qwen_foundation_q.ptd have different sizes."
173+
ls -la qwen_foundation_lora_q.ptd
174+
ls -la qwen_foundation_q.ptd
175+
cleanup_files
176+
exit 1
171177
fi
172178

173179
# Run quantized qwen model (no adapter).
@@ -214,11 +220,11 @@ fi
214220
# Confirm qwen_foundation_q.ptd file size.
215221
FOUNDATION_Q_SIZE=$(stat -c%s qwen_foundation_q.ptd)
216222
if [[ $FOUNDATION_Q_SIZE -le "1000000000" ]]; then
217-
echo "qwen_foundation_q.ptd size is: $FOUNDATION_Q_SIZE"
223+
echo "qwen_foundation_q.ptd size is: $FOUNDATION_Q_SIZE"
218224
else
219-
echo "qwen_foundation_q.ptd size: $FOUNDATION_Q_SIZE is greater than threshold 1GB"
220-
cleanup_files
221-
exit 1
225+
echo "qwen_foundation_q.ptd size: $FOUNDATION_Q_SIZE is greater than threshold 1GB"
226+
cleanup_files
227+
exit 1
222228
fi
223229

224230
cleanup_files

.ci/scripts/tests/test_get_benchmark_analysis_data.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ def setUp(self):
179179
def test_init(self):
180180
"""Test initialization of ExecutorchBenchmarkFetcher."""
181181
self.assertEqual(self.fetcher.env, "prod")
182-
self.assertEqual(self.fetcher.base_url, "https://hud.pytorch.org")
182+
self.assertEqual(
183+
self.fetcher.base_url, "https://hud.pytorch.org" # @lint-ignore
184+
)
183185
self.assertEqual(
184186
self.fetcher.query_group_table_by_fields,
185187
["model", "backend", "device", "arch"],
@@ -193,7 +195,9 @@ def test_init(self):
193195

194196
def test_get_base_url(self):
195197
"""Test _get_base_url method."""
196-
self.assertEqual(self.fetcher._get_base_url(), "https://hud.pytorch.org")
198+
self.assertEqual(
199+
self.fetcher._get_base_url(), "https://hud.pytorch.org" # @lint-ignore
200+
)
197201

198202
# Test with local environment
199203
local_fetcher = self.module.ExecutorchBenchmarkFetcher(env="local")

.github/workflows/doc-build.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,12 @@ jobs:
117117
fi
118118
echo "Target Folder: ${TARGET_FOLDER}"
119119
120-
mkdir -p "${TARGET_FOLDER}"
121120
# Clean up target folder if exists and copy html output to the
122121
# Target folder
123-
rm -rf "${TARGET_FOLDER}"/*
122+
if [ -d "${TARGET_FOLDER}" ]; then
123+
rm -rf "${TARGET_FOLDER}"
124+
fi
125+
mkdir -p "${TARGET_FOLDER}"
124126
mv "${RUNNER_ARTIFACT_DIR}"/html/* "${TARGET_FOLDER}"
125127
git add "${TARGET_FOLDER}" || true
126128

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ executorch
2626
│ ├── <a href="backends/apple">apple</a> - Apple-specific backends.
2727
│ │ ├── <a href="backends/apple/coreml">coreml</a> - CoreML backend for Apple devices. See <a href="docs/source/backends/coreml/coreml-overview.md">doc</a>.
2828
│ │ └── <a href="backends/apple/mps">mps</a> - Metal Performance Shaders backend for Apple devices. See <a href="docs/source/backends/mps/mps-overview.md">doc</a>.
29-
│ ├── <a href="backends/arm">arm</a> - ARM architecture backends. See <a href="docs/source/backends-arm-ethos-u.md">doc</a>.
29+
│ ├── <a href="backends/arm">arm</a> - ARM architecture backends. See <a href="docs/source/backends/arm-ethos-u/arm-ethos-u-overview.md">doc</a>.
3030
│ ├── <a href="backends/cadence">cadence</a> - Cadence-specific backends. See <a href="docs/source/backends-cadence.md">doc</a>.
3131
│ ├── <a href="backends/example">example</a> - Example backend implementations.
3232
│ ├── <a href="backends/mediatek">mediatek</a> - MediaTek-specific backends. See <a href="docs/source/backends-mediatek.md">doc</a>.
@@ -324,7 +324,8 @@ the code you're modifying and find an author who has more context. Ask them
324324
for their help in the PR comments.
325325

326326
### Continuous Integration
327-
See https://hud.pytorch.org/hud/pytorch/executorch/main for the current state of
327+
328+
See https://hud.pytorch.org/hud/pytorch/executorch/main for the current state of <!-- @lint-ignore -->
328329
the CI (continuous integration) jobs. If `main` is broken, consider rebasing
329330
your PR onto the `viable/strict` branch, which points to the most recent
330331
all-green commit.

backends/arm/_passes/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from .decompose_add_sub_alpha_pass import DecomposeAddSubAlphaPass # noqa
3232
from .decompose_addmm_pass import DecomposeAddmmPass # noqa
3333
from .decompose_any_pass import DecomposeAnyPass # noqa
34+
from .decompose_as_strided_copy_pass import DecomposeAsStridedCopyPass # noqa
3435
from .decompose_asin_and_acos_pass import DecomposeAsinAndAcosPass # noqa
3536
from .decompose_asinh_pass import DecomposeAsinhPass # noqa
3637
from .decompose_atan_pass import DecomposeAtanPass # noqa

backends/arm/_passes/arm_pass_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
DecomposeAnyPass,
3939
DecomposeAsinAndAcosPass,
4040
DecomposeAsinhPass,
41+
DecomposeAsStridedCopyPass,
4142
DecomposeAtanhPass,
4243
DecomposeAtanPass,
4344
DecomposeAvgPool2dPass,
@@ -333,6 +334,7 @@ def _tosa_pipeline(
333334
ConvertExpandCopyToRepeatPass(),
334335
UnsqueezeBeforeRepeatPass(),
335336
DecomposeCumsumPass(exported_program),
337+
DecomposeAsStridedCopyPass(),
336338
DecomposeMaxPool2dPass(),
337339
SizeAdjustInputPass(),
338340
DecomposeSelectPass(),
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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 Dict, Optional, Set, Tuple, Type
7+
8+
import torch
9+
10+
from executorch.backends.arm._passes import ArmPass
11+
from executorch.backends.arm.common.as_strided_utils import (
12+
contiguous_strides,
13+
maybe_static_sequence,
14+
to_int,
15+
to_int_tuple,
16+
)
17+
from executorch.exir.dialects._ops import ops as exir_ops
18+
from executorch.exir.pass_base import ExportPass
19+
20+
21+
class DecomposeAsStridedCopyPass(ArmPass):
22+
"""
23+
Replace contiguous `aten.as_strided_copy` with `aten.view_copy`.
24+
25+
The TOSA backend only supports the contiguous-as-strided case where the stride matches
26+
row-major layout and the storage offset is zero. In that scenario the operator is
27+
equivalent to a reshape with copy semantics and can be lowered via `view_copy`.
28+
"""
29+
30+
_passes_required_after: Set[Type[ExportPass]] = set()
31+
32+
_EDGE_OPS = (exir_ops.edge.aten.as_strided_copy.default,)
33+
_ATEN_OPS = (torch.ops.aten.as_strided_copy.default,)
34+
35+
def _extract_args(
36+
self, args: Tuple[object, ...], kwargs: dict
37+
) -> Optional[Tuple[Tuple[int, ...], Tuple[int, ...], int]]:
38+
"""Return (size, stride, storage_offset) when they are statically known."""
39+
if len(args) < 3:
40+
return None
41+
42+
size_arg = args[1]
43+
stride_arg = args[2]
44+
offset_arg = (
45+
kwargs.get("storage_offset") if "storage_offset" in kwargs else None
46+
)
47+
if offset_arg is None and len(args) > 3:
48+
offset_arg = args[3]
49+
50+
size_seq = maybe_static_sequence(size_arg)
51+
stride_seq = maybe_static_sequence(stride_arg)
52+
if size_seq is None or stride_seq is None:
53+
return None
54+
55+
size_tuple = to_int_tuple(size_seq)
56+
stride_tuple = to_int_tuple(stride_seq)
57+
if size_tuple is None or stride_tuple is None:
58+
return None
59+
60+
if len(size_tuple) != len(stride_tuple):
61+
return None
62+
63+
if any(stride < 0 for stride in stride_tuple):
64+
return None
65+
66+
if offset_arg is None:
67+
storage_offset = 0
68+
else:
69+
parsed_offset = to_int(offset_arg)
70+
if parsed_offset is None:
71+
return None
72+
storage_offset = parsed_offset
73+
74+
return size_tuple, stride_tuple, storage_offset
75+
76+
def call_operator(self, op, args, kwargs, meta, updated: Optional[bool] = False):
77+
if op not in (*self._EDGE_OPS, *self._ATEN_OPS):
78+
return super().call_operator(op, args, kwargs, meta, updated)
79+
80+
extracted = self._extract_args(args, kwargs)
81+
if extracted is None:
82+
return super().call_operator(op, args, kwargs, meta, updated)
83+
84+
size_tuple, stride_tuple, storage_offset = extracted
85+
if storage_offset != 0:
86+
return super().call_operator(op, args, kwargs, meta, updated)
87+
88+
expected_strides = contiguous_strides(size_tuple)
89+
90+
def _stride_matches(idx: int, dim: int) -> bool:
91+
stride = stride_tuple[idx]
92+
expected = expected_strides[idx]
93+
if idx == len(size_tuple) - 1:
94+
return stride >= expected
95+
if dim == 1 or expected == 0:
96+
return True
97+
return stride == expected
98+
99+
if any(not _stride_matches(i, dim) for i, dim in enumerate(size_tuple)):
100+
return super().call_operator(op, args, kwargs, meta, updated)
101+
102+
view_args = (args[0], tuple(size_tuple))
103+
view_kwargs: Dict[str, object] = {}
104+
105+
view_op = (
106+
exir_ops.edge.aten.view_copy.default
107+
if op in self._EDGE_OPS
108+
else torch.ops.aten.view_copy.default
109+
)
110+
111+
return super().call_operator(
112+
view_op, view_args, view_kwargs, meta, updated=True
113+
)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
"""Utility helpers shared across as_strided_copy handling."""
6+
7+
from __future__ import annotations
8+
9+
import numbers
10+
11+
from collections.abc import Sequence
12+
from typing import Optional, Tuple, TypeVar
13+
14+
import torch
15+
import torch.fx as fx
16+
17+
T = TypeVar("T", bound=Sequence)
18+
19+
20+
def to_int(value: object) -> Optional[int]:
21+
"""Return an int for supported numeric types, otherwise None."""
22+
if isinstance(value, (numbers.Integral, torch.SymInt)):
23+
return int(value)
24+
return None
25+
26+
27+
def maybe_static_sequence(value: object) -> Optional[Sequence]:
28+
"""
29+
Return a Python sequence for literal or FX-constant values.
30+
31+
FX exporters often wrap constant lists in nodes where the materialised
32+
value is stored in ``node.meta["val"]``. This helper unwraps that so the
33+
rest of the logic can treat them uniformly.
34+
"""
35+
if isinstance(value, (str, bytes)):
36+
return None
37+
if isinstance(value, fx.Node):
38+
const_val = value.meta.get("val")
39+
if isinstance(const_val, Sequence):
40+
return const_val
41+
return None
42+
if isinstance(value, Sequence):
43+
return value
44+
return None
45+
46+
47+
def to_int_tuple(value: object) -> Optional[Tuple[int, ...]]:
48+
"""Best-effort conversion of a sequence of integers/SymInts to a tuple[int, ...]."""
49+
seq = maybe_static_sequence(value)
50+
if seq is None:
51+
return None
52+
53+
result: list[int] = []
54+
for item in seq:
55+
converted = to_int(item)
56+
if converted is None:
57+
return None
58+
result.append(converted)
59+
return tuple(result)
60+
61+
62+
def contiguous_strides(shape: Sequence[int]) -> Tuple[int, ...]:
63+
"""Compute row-major contiguous strides for the provided shape."""
64+
strides = [0] * len(shape)
65+
running = 1
66+
for idx in reversed(range(len(shape))):
67+
dim_val = shape[idx]
68+
strides[idx] = running if dim_val != 0 else 1
69+
running *= max(dim_val, 1)
70+
return tuple(strides)

backends/arm/operator_support/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66

77
from . import ( # noqa
8+
as_strided_copy_support,
89
clone_dim_order_support,
910
control_flow_support,
1011
convolution_support,

0 commit comments

Comments
 (0)