Skip to content

Commit 6f46374

Browse files
author
pytorchbot
committed
2026-06-10 nightly release (b771dab)
1 parent 37ccd8a commit 6f46374

265 files changed

Lines changed: 12320 additions & 6695 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.

.github/workflows/mlx.yml

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313
- backends/mlx/**
1414
- extension/llm/export/**
1515
- extension/audio/**
16+
- examples/models/gemma4_31b/**
1617
- examples/models/parakeet/**
1718
- examples/models/voxtral_realtime/**
1819
- examples/models/qwen3_5_moe/**
@@ -77,6 +78,8 @@ jobs:
7778
backends/mlx/test/test_passes.py \
7879
backends/mlx/test/test_pattern_utils.py \
7980
backends/mlx/test/test_partitioner.py \
81+
backends/mlx/test/test_serialization_dedup.py \
82+
examples/models/gemma4_31b/quant/tests/test_pack_mlx.py \
8083
examples/models/gemma4_31b/tests/test_mlx_pipeline.py \
8184
-v
8285
echo "::endgroup::"
@@ -89,20 +92,16 @@ jobs:
8992
./cmake-out/backends/mlx/test/multi_thread_test_runner
9093
echo "::endgroup::"
9194
92-
echo "::group::Run gated_delta_rule op tests"
93-
${CONDA_RUN} python -m executorch.backends.mlx.model_ops.test_gated_delta_rule run -v
94-
echo "::endgroup::"
95-
96-
echo "::group::Run tq_norm op tests"
97-
${CONDA_RUN} python -m executorch.backends.mlx.model_ops.test_tq_norm run -v
98-
echo "::endgroup::"
99-
100-
echo "::group::Run tq4_compress op tests"
101-
${CONDA_RUN} python -m executorch.backends.mlx.model_ops.test_tq4_compress run -v
102-
echo "::endgroup::"
103-
104-
echo "::group::Run tq_dequant op tests"
105-
${CONDA_RUN} python -m executorch.backends.mlx.model_ops.test_tq_dequant run -v
95+
echo "::group::Run custom_kernel_ops op tests"
96+
# Run every custom_kernel_ops/**/test/test_*.py via its OpTestCase `run`
97+
# CLI. Recurses into per-format subpackages (e.g. gguf/test), so adding a
98+
# new op test file requires no change here.
99+
set -e
100+
for t in $(find backends/mlx/custom_kernel_ops -path '*/test/test_*.py' | sort); do
101+
mod="executorch.$(echo "${t%.py}" | tr '/' '.')"
102+
echo "--- ${mod} ---"
103+
${CONDA_RUN} python -m "${mod}" run -v
104+
done
106105
echo "::endgroup::"
107106
108107
test-mlx-qwen35-moe:

.github/workflows/test-backend-coreml.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ jobs:
4343
&& '["coreml"]'
4444
|| '["coreml", "coreml_static_int8"]' }}
4545
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
46-
timeout: 120
46+
timeout: 180
4747
run-macos: true

.github/workflows/trunk.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ jobs:
258258
- test_arm_backend: test_pytest_models_ethos_u85
259259
- test_arm_backend: test_run_ethos_u85
260260
- test_arm_backend: test_smaller_stories_llama_tosa
261+
- test_arm_backend: test_model_smollm2_135M_ethos_u85
261262
- test_arm_backend: test_memory_allocation
262263
- test_arm_backend: test_ootb_tests_ethos_u
263264
- test_arm_backend: test_ootb_tests_tosa

Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ parakeet-vulkan:
261261

262262
dinov2-cuda:
263263
@echo "==> Building and installing ExecuTorch with CUDA..."
264-
cmake --workflow --preset llm-release-cuda
264+
cmake --preset llm-release-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON
265+
cmake --build --preset llm-release-cuda-install
265266
@echo "==> Building DINOv2 runner with CUDA..."
266267
cd examples/models/dinov2 && cmake --workflow --preset dinov2-cuda
267268
@echo ""
@@ -270,7 +271,8 @@ dinov2-cuda:
270271

271272
dinov2-cuda-debug:
272273
@echo "==> Building and installing ExecuTorch with CUDA (debug mode)..."
273-
cmake --workflow --preset llm-debug-cuda
274+
cmake --preset llm-debug-cuda -DEXECUTORCH_BUILD_EXTENSION_IMAGE=ON
275+
cmake --build --preset llm-debug-cuda-install
274276
@echo "==> Building DINOv2 runner with CUDA (debug mode)..."
275277
cd examples/models/dinov2 && cmake --workflow --preset dinov2-cuda-debug
276278
@echo ""

backends/aoti/aoti_partitioner.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
Partitioner,
1515
PartitionResult,
1616
)
17-
from executorch.exir.backend.utils import tag_constant_data, tag_mutated_buffer
17+
from executorch.exir.backend.utils import (
18+
get_non_lowered_nodes,
19+
tag_constant_data,
20+
tag_mutated_buffer,
21+
)
1822
from torch._export.utils import is_buffer, is_lifted_tensor_constant, is_param
1923
from torch.export.exported_program import ExportedProgram
2024

@@ -60,8 +64,17 @@ def is_control_flow(node: torch.fx.Node) -> bool:
6064
torch.ops.higher_order.while_loop,
6165
]
6266

67+
# Nodes already lowered by an earlier partitioner (e.g. a preceding
68+
# TensorRT partition) appear as executorch_call_delegate calls and their
69+
# output getitems; re-delegating them would nest a foreign delegate. Tag
70+
# only the remaining non-lowered ops so this partitioner composes after
71+
# others.
72+
non_lowered_nodes = set(get_non_lowered_nodes(exported_program.graph))
73+
6374
for node in exported_program.graph.nodes:
6475
if node.op == "call_function":
76+
if node not in non_lowered_nodes:
77+
continue
6578
node.meta["delegation_tag"] = tag
6679
# Tag get_attr nodes that are used by control flow operations
6780
elif node.op == "get_attr":
@@ -76,17 +89,22 @@ def is_control_flow(node: torch.fx.Node) -> bool:
7689
tag_constant_data(exported_program)
7790
tag_mutated_buffer(exported_program)
7891

79-
# Tag constant placeholders that have no users
80-
# tag_constant_data only tags constants that have users with delegation_tag
81-
# but we need to tag all constants for this partition
92+
# A constant that still has users feeds only a prior delegate; tagging it
93+
# would fail backend lowering's same-tag check (its user keeps the prior
94+
# tag). tag_constant_data already claimed the ones this partition uses, so
95+
# tag only the genuinely unused constants here.
8296
for node in exported_program.graph.nodes:
83-
if node.op == "placeholder" and (
84-
is_param(exported_program, node)
85-
or is_buffer(exported_program, node)
86-
or is_lifted_tensor_constant(exported_program, node)
97+
if (
98+
node.op == "placeholder"
99+
and not node.users
100+
and "delegation_tag" not in node.meta
101+
and (
102+
is_param(exported_program, node)
103+
or is_buffer(exported_program, node)
104+
or is_lifted_tensor_constant(exported_program, node)
105+
)
87106
):
88-
if "delegation_tag" not in node.meta:
89-
node.meta["delegation_tag"] = tag
107+
node.meta["delegation_tag"] = tag
90108

91109
return PartitionResult(
92110
tagged_exported_program=exported_program, partition_tags=partition_tags

backends/aoti/slim/core/slim_tensor.h

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,15 +497,17 @@ class SlimTensor {
497497
static_cast<size_t>(dst_offset), elem_size, &dst_byte_offset),
498498
"copy_: byte offset overflow");
499499

500-
// Copy elem_size bytes from src to dst
500+
char* dst_byte_offset_ptr =
501+
add_byte_offset_checked(dst_data, dst_byte_offset);
502+
const char* src_byte_offset_ptr =
503+
add_byte_offset_checked(src_data, src_byte_offset);
501504
if (this->device().is_cpu() && other.device().is_cpu()) {
502-
std::memcpy(
503-
dst_data + dst_byte_offset, src_data + src_byte_offset, elem_size);
505+
std::memcpy(dst_byte_offset_ptr, src_byte_offset_ptr, elem_size);
504506
} else if (this->device().is_cuda() || other.device().is_cuda()) {
505507
#if defined(CUDA_AVAILABLE)
506508
DeviceTraits<c10::DeviceType::CUDA>::memcpy(
507-
dst_data + dst_byte_offset,
508-
src_data + src_byte_offset,
509+
dst_byte_offset_ptr,
510+
src_byte_offset_ptr,
509511
elem_size,
510512
device(), // dst device
511513
other.device() // src device
@@ -555,6 +557,17 @@ class SlimTensor {
555557
}
556558

557559
private:
560+
template <typename T>
561+
static T* add_byte_offset_checked(T* data, size_t byte_offset) {
562+
uintptr_t data_int = reinterpret_cast<uintptr_t>(data);
563+
uintptr_t data_offset_int = 0;
564+
ET_CHECK_MSG(
565+
!::c10::add_overflows(data_int, byte_offset, &data_offset_int),
566+
"copy_: data pointer overflow");
567+
return reinterpret_cast<T*>( // NOLINT(performance-no-int-to-ptr)
568+
data_offset_int);
569+
}
570+
558571
SlimTensor _clone_impl(
559572
c10::IntArrayRef sizes,
560573
c10::IntArrayRef strides,

backends/aoti/slim/core/test/test_slimtensor_copy.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88

99
#include <gtest/gtest.h>
1010

11+
#include <limits>
12+
1113
#include <executorch/backends/aoti/slim/core/slim_tensor.h>
1214
#include <executorch/backends/aoti/slim/core/storage.h>
1315
#include <executorch/backends/aoti/slim/factory/empty.h>
16+
#include <executorch/runtime/platform/platform.h>
1417

1518
namespace executorch::backends::aoti::slim {
1619

@@ -214,6 +217,42 @@ TEST(SlimTensorCopyTest, CopyNonContiguousDst) {
214217
EXPECT_FLOAT_EQ(dst_data[5], 5.0f);
215218
}
216219

220+
// =============================================================================
221+
// Overflow Validation Tests
222+
// =============================================================================
223+
224+
TEST(SlimTensorCopyTest, CopyFailsWhenDataPointerOverflows) {
225+
std::vector<int64_t> sizes = {2};
226+
std::vector<int64_t> src_strides = {1};
227+
// For Short, INT64_MAX elements becomes UINTPTR_MAX - 1 bytes. That
228+
// passes byte-offset validation and only fails at checked pointer addition.
229+
std::vector<int64_t> dst_strides = {std::numeric_limits<int64_t>::max()};
230+
231+
Storage src_storage = make_cpu_storage(2 * sizeof(int16_t));
232+
int16_t* src_data = static_cast<int16_t*>(src_storage->data());
233+
src_data[0] = 1;
234+
src_data[1] = 2;
235+
SlimTensor src(
236+
std::move(src_storage),
237+
makeArrayRef(sizes),
238+
makeArrayRef(src_strides),
239+
c10::ScalarType::Short);
240+
241+
Storage dst_storage = make_cpu_storage(sizeof(int16_t));
242+
SlimTensor dst(
243+
std::move(dst_storage),
244+
makeArrayRef(sizes),
245+
makeArrayRef(dst_strides),
246+
c10::ScalarType::Short);
247+
248+
EXPECT_DEATH(
249+
{
250+
et_pal_init();
251+
dst.copy_(src);
252+
},
253+
"copy_: data pointer overflow");
254+
}
255+
217256
// =============================================================================
218257
// Storage Offset Tests
219258
// =============================================================================

backends/arm/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ Below is an overview of some of the testing options this script provides:
251251
| `test_arm_backend.sh test_pytest_ops_vkml` | Runs operator unit tests for VKML/VGF specific use-cases. |
252252
| `test_arm_backend.sh test_pytest_models_vkml` | Runs model unit tests for VKML/VGF specific use-cases. |
253253
| `test_arm_backend.sh test_run_vkml` | Runs end-to-end unit tests for VKML/VGF specific use-cases. |
254-
| `test_arm_backend.sh test_model_smollm2_135M` | Runs some models with Corstone FVP. |
254+
| `test_arm_backend.sh test_model_smollm2_135M_ethos_u85` | Runs smollm2_135M for Ethos-U85 specific use-cases. |
255255
| `test_arm_backend.sh test_ootb_tests_ethos_u` | Runs out-of-the-box tests for Ethos-U. |
256256
| `test_arm_backend.sh test_ootb_tests_tosa` | Runs out-of-the-box tests for TOSA. |
257257
| `test_arm_backend.sh test_ootb_tests_vgf` | Runs out-of-the-box tests for VKML/VGF. |

backends/arm/_passes/fuse_duplicate_users_pass.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def call(self, graph_module: GraphModule) -> PassResult:
3434
graph = graph_module.graph
3535
modified = False
3636

37+
node_order = {node: index for index, node in enumerate(graph.nodes)}
3738
producers: Deque[Node] = deque(node for node in graph.nodes)
3839

3940
while producers:
@@ -48,7 +49,7 @@ def call(self, graph_module: GraphModule) -> PassResult:
4849
if len(user_nodes) < 2:
4950
continue
5051

51-
candidate_groups = self._get_candidate_groups(user_nodes)
52+
candidate_groups = self._get_candidate_groups(node_order, user_nodes)
5253

5354
signature_to_user: Dict[Tuple[Hashable, ...], Node] = {}
5455
for group in candidate_groups:
@@ -84,7 +85,7 @@ def call(self, graph_module: GraphModule) -> PassResult:
8485

8586
return PassResult(graph_module, modified)
8687

87-
def _get_candidate_groups(self, user_nodes):
88+
def _get_candidate_groups(self, node_order, user_nodes):
8889
users_by_target: Dict[Tuple[str, Hashable], List[Node]] = {}
8990
for user in user_nodes:
9091
if user.graph is None:
@@ -98,9 +99,12 @@ def _get_candidate_groups(self, user_nodes):
9899
target_signature = (user.op, target_key)
99100
users_by_target.setdefault(target_signature, []).append(user)
100101

101-
candidate_groups = [
102-
group for group in users_by_target.values() if len(group) > 1
103-
]
102+
candidate_groups = []
103+
for group in users_by_target.values():
104+
if len(group) > 1:
105+
candidate_groups.append(
106+
sorted(group, key=lambda node: node_order[node])
107+
)
104108

105109
return candidate_groups
106110

backends/arm/_passes/rewrite_avg_pool2d_pass.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ def call_operator(self, op, args, kwargs, meta, updated=False):
6565
# Materialize output zero-point as a scalar tensor
6666
output_zp = super().call_scalar(out_zp_val, meta)
6767

68-
# Determine accumulator dtype for AVG_POOL2D: INT32 for integer inputs, FP32 otherwise
68+
# Determine accumulator dtype for AVG_POOL2D.
6969
if x.data.dtype in (torch.int8, torch.int16):
7070
acc_type = torch.int32
71+
elif x.data.dtype in (torch.float8_e4m3fn, torch.float8_e5m2):
72+
acc_type = torch.float16
7173
else:
7274
acc_type = torch.float32
7375

0 commit comments

Comments
 (0)