Skip to content

Commit a3a0e0e

Browse files
authored
Merge branch 'main' into cuda-quiet-symbol-promotion
2 parents b5207cf + 20fa24e commit a3a0e0e

55 files changed

Lines changed: 4325 additions & 75 deletions

Some content is hidden

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

‎.ci/scripts/setup-emscripten.sh‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ install_node() {
1515
}
1616

1717
install_emscripten() {
18+
# The WebGPU backend's instance API (WGPUInstanceFeatureName /
19+
# WGPUInstanceDescriptor::requiredFeatures) needs the emdawnwebgpu port
20+
# pinned by Emscripten 4.0.13 or newer; 4.0.10 ships the older
21+
# capabilities-based descriptor and does not compile it.
22+
local emscripten_version="${EMSCRIPTEN_VERSION:-4.0.19}"
1823
git clone https://github.com/emscripten-core/emsdk.git
1924
pushd emsdk || return
20-
./emsdk install 4.0.10
21-
./emsdk activate 4.0.10
25+
./emsdk install "${emscripten_version}"
26+
./emsdk activate "${emscripten_version}"
2227
source ./emsdk_env.sh
2328
popd || return
2429
}

‎.ci/scripts/tests/test_cu134_dependencies.py‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,17 @@ def test_python315_cpu_domains_use_available_torchaudio(self):
150150
self.assertIn(f"torchaudio=={audio}", domains)
151151
self.assertIn("torchvision==0.29.0", domains)
152152

153-
def test_python315_preserves_cuda_and_source_pins(self):
153+
def test_python315_cuda_domains_use_available_torchaudio(self):
154+
for machine in ("x86_64", "aarch64"):
155+
with self.subTest(machine=machine):
156+
_, _, domains, _ = self.install_commands(
157+
(13, 2), machine=machine, python_version=(3, 15)
158+
)
159+
self.assertIn("torchaudio==2.11.1", domains)
160+
self.assertIn("torchvision==0.29.0", domains)
161+
self.assertIn("https://download.pytorch.org/whl/test/cu132", domains)
162+
163+
def test_python315_preserves_cu134_and_source_pins(self):
154164
for cuda in ((13, 2), (13, 4)):
155165
for nightly in (True, False):
156166
with self.subTest(cuda=cuda, nightly=nightly):
@@ -160,7 +170,7 @@ def test_python315_preserves_cuda_and_source_pins(self):
160170
audio = (
161171
"torchaudio==2.11.0.dev20260922+cu134"
162172
if nightly and cuda == (13, 4)
163-
else "torchaudio==2.11.0" if nightly else "torchaudio"
173+
else "torchaudio==2.11.1" if nightly else "torchaudio"
164174
)
165175
self.assertIn(audio, domains)
166176

‎.ci/scripts/wheel/envvar_linux.sh‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,3 @@
99
# any variables so that subprocesses will see them.
1010

1111
source "${GITHUB_WORKSPACE}/${REPOSITORY}/.ci/scripts/wheel/envvar_base.sh"
12-
13-
# The reusable smoke installer selects PyTorch's index; ET also needs PyPI packages.
14-
export PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL:+${PIP_EXTRA_INDEX_URL} }https://pypi.org/simple"

‎.ci/scripts/wheel/pre_build_script.sh‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ set -euxo pipefail
1111

1212
# This script is run before building ExecuTorch binaries
1313

14+
# The reusable smoke installer selects PyTorch's index; ET also needs PyPI packages.
15+
export PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL:+${PIP_EXTRA_INDEX_URL} }https://pypi.org/simple"
16+
echo "PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}" >> "${GITHUB_ENV}"
17+
1418
# Initialize submodules here instead of during checkout so we can use OpenSSL
1519
# on Windows (schannel fails with SEC_E_ILLEGAL_MESSAGE on some gitlab hosts).
1620
UNAME_S=$(uname -s)
@@ -160,6 +164,21 @@ fi
160164

161165
"${GITHUB_WORKSPACE}/${REPOSITORY}/install_requirements.sh" --example
162166

167+
if [[ $UNAME_S == *"MINGW"* || $UNAME_S == *"MSYS"* ]]; then
168+
PYTHON_BIN=$(command -v python || command -v python3)
169+
if "${PYTHON_BIN}" -c 'import sys; sys.exit(sys.version_info < (3, 15))'; then
170+
# No cp315 tokenizer wheel is published. Build the pinned source with the
171+
# existing build dependencies and Visual Studio environment before smoke install.
172+
# MSBuild's file tracker needs a short path even during CMake compiler checks.
173+
tokenizer_source_dir=$(mktemp -d /c/et-tok.XXXXXX)
174+
# Dereference submodule symlinks because MSYS cannot recreate them on every runner.
175+
cp -RL extension/llm/tokenizers/. "${tokenizer_source_dir}"
176+
cmd //c '.ci\scripts\wheel\vc_env_helper.bat' python -m pip install \
177+
--no-build-isolation "$(cygpath -w "${tokenizer_source_dir}")"
178+
rm -rf "${tokenizer_source_dir}"
179+
fi
180+
fi
181+
163182
# Enable VGF in pybind wheel builds when the platform-specific build input is
164183
# available from pip.
165184
if [[ "$UNAME_S" == "Linux" || "$UNAME_S" == "Darwin" ]]; then

‎.github/workflows/pull.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,10 @@ jobs:
15811581
# Test selective build
15821582
PYTHON_EXECUTABLE=python bash examples/wasm/test_build_wasm.sh
15831583
1584+
# Build the WebGPU configuration of the Wasm bindings. Build only:
1585+
# running a delegated model needs a browser with WebGPU.
1586+
PYTHON_EXECUTABLE=python bash scripts/build_wasm_tests.sh --webgpu
1587+
15841588
unittest-wasm-bindings:
15851589
needs: docker-image
15861590
name: unittest-wasm-bindings

‎CMakeLists.txt‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,10 +1543,6 @@ if(EXECUTORCH_BUILD_PYBIND)
15431543
executorch_target_copy_mlx_metallib(portable_lib)
15441544
endif()
15451545

1546-
if(EXECUTORCH_BUILD_WASM)
1547-
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extension/wasm)
1548-
endif()
1549-
15501546
if(EXECUTORCH_BUILD_TOKENIZERS_WASM)
15511547
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extension/wasm/tokenizers)
15521548
endif()
@@ -1602,6 +1598,12 @@ if(EXECUTORCH_BUILD_VGF)
16021598
list(APPEND _executorch_backends vgf_backend)
16031599
endif()
16041600

1601+
# Added after the backends so EXECUTORCH_BUILD_WASM_WEBGPU can link
1602+
# webgpu_backend, which has to already be defined at that point.
1603+
if(EXECUTORCH_BUILD_WASM)
1604+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/extension/wasm)
1605+
endif()
1606+
16051607
# Top-level interface targets.
16061608

16071609
# A target containing all configured backends.

‎backends/cortex_m/passes/cortex_m_pass_manager.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,15 @@ class CortexMPassManager(ExportedProgramPassManager):
111111
legacy_pass_list: list[PassClass] = [
112112
# Run before folding so qparams attach to max_pool2d values, not tuple + getitem.
113113
RemoveGetItemPass,
114-
FoldAndAnnotateQParamsPass,
114+
# Retrace scalar ops while the graph still has floating-point Q/DQ inputs.
115115
ReplaceScalarWithTensorArgPass,
116-
ReplaceQuantNodesPass,
116+
FoldAndAnnotateQParamsPass,
117117
ActivationFusionPass,
118118
QuantizedClampActivationPass,
119119
DecomposeHardswishPass,
120120
AtenToCortexMPass,
121+
# Retrace only after quantized ATen ops have become Cortex-M kernels.
122+
ReplaceQuantNodesPass,
121123
FuseConvPaddingPass,
122124
InitializeScratchBuffersPass,
123125
LiftConstantTensorsPass,

‎backends/cortex_m/test/build_test_runner.sh‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ ops_list=(
5151
aten::cat.out
5252
aten::full.out
5353
aten::ge.Tensor_out
54+
aten::permute_copy.out
55+
aten::slice_copy.Tensor_out
5456
aten::unsqueeze_copy.out
5557
aten::squeeze_copy.dim_out
58+
aten::squeeze_copy.dims_out
5659
aten::select_copy.int_out
5760
aten::amax.out
5861
cortex_m::quantize_per_tensor.out
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
import torch
7+
from executorch.backends.arm.test.common import parametrize
8+
from executorch.backends.cortex_m.test.tester import CortexMTester, McuTestCase
9+
10+
11+
class CortexMAttention(torch.nn.Module):
12+
ops_before_transforms = {
13+
"executorch_exir_dialects_edge__ops_aten_linear_default": 1,
14+
"executorch_exir_dialects_edge__ops_aten_bmm_default": 2,
15+
"executorch_exir_dialects_edge__ops_aten_mul_Tensor": 1,
16+
"executorch_exir_dialects_edge__ops_aten__softmax_default": 1,
17+
}
18+
19+
ops_after_transforms = {
20+
"executorch_exir_dialects_edge__ops_cortex_m_quantized_linear_default": 1,
21+
"executorch_exir_dialects_edge__ops_cortex_m_quantized_batch_matmul_default": 2,
22+
"executorch_exir_dialects_edge__ops_cortex_m_softmax_default": 1,
23+
}
24+
25+
def __init__(self):
26+
super().__init__()
27+
self.qkv = torch.nn.Linear(12, 36)
28+
29+
def forward(self, x):
30+
qkv = self.qkv(x).reshape(1, 5, 3, 3, 4).permute(2, 0, 3, 1, 4)
31+
q, k, v = qkv.unbind(0)
32+
scores = (q * 0.5) @ k.transpose(-2, -1)
33+
return (scores.softmax(dim=-1) @ v).transpose(1, 2).reshape(1, 5, 12)
34+
35+
36+
test_cases = {
37+
"qkv_attention": McuTestCase(CortexMAttention(), (torch.randn(1, 5, 12),)),
38+
}
39+
40+
41+
@parametrize("test_case", test_cases)
42+
def test_dialect_attention(test_case, cortex_m_target):
43+
tester = CortexMTester(
44+
test_case.model, test_case.example_inputs, target_config=cortex_m_target
45+
)
46+
tester.test_dialect(
47+
test_case.model.ops_before_transforms,
48+
test_case.model.ops_after_transforms,
49+
qtol=2,
50+
)
51+
52+
53+
@parametrize("test_case", test_cases)
54+
def test_implementation_attention(test_case, cortex_m_target):
55+
tester = CortexMTester(
56+
test_case.model, test_case.example_inputs, target_config=cortex_m_target
57+
)
58+
tester.test_implementation(qtol=2)

‎backends/cortex_m/test/ops/test_sdpa.py‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,6 @@ def _qkv(query_shape, key_shape, value_shape, dtype=torch.float32):
129129
"explicit_scale": (_SCALE_NOT_FOLDED, RuntimeError),
130130
"multihead_attention": (_LINEAR_RETRACED_AS_INT32, Exception),
131131
}
132-
xfail_cases_impl: dict[str, xfail_type] = {
133-
"multihead_attention": (_LINEAR_RETRACED_AS_INT32, Exception),
134-
}
135132

136133

137134
def _transform_for_annotation(model, example_inputs, dynamic_shapes=None):
@@ -161,7 +158,7 @@ def test_dialect_sdpa(test_case, cortex_m_target):
161158
)
162159

163160

164-
@parametrize("test_case", test_cases, xfails=xfail_cases_impl)
161+
@parametrize("test_case", test_cases)
165162
def test_implementation_sdpa(test_case, cortex_m_target):
166163
tester = CortexMTester(
167164
test_case.model, test_case.example_inputs, target_config=cortex_m_target

0 commit comments

Comments
 (0)