Skip to content

Commit 7a9a6c7

Browse files
authored
Merge branch 'main' into add_moreTC_quantization
2 parents 316b7b0 + 1eaafd9 commit 7a9a6c7

File tree

142 files changed

+29028
-264
lines changed

Some content is hidden

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

142 files changed

+29028
-264
lines changed

.ci/scripts/setup-qnn-deps.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ source "$(dirname "${BASH_SOURCE[0]}")/../../backends/qualcomm/scripts/install_q
1111

1212
setup_libcpp 12
1313
setup_android_ndk
14-
install_qnn
14+
install_qnn
15+
pip install -r backends/qualcomm/requirements.txt

.ci/scripts/test_wheel_package_qnn.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ PY
177177
"$PIPBIN" install . --no-build-isolation
178178
popd > /dev/null
179179

180+
# Install qualcomm backend dependencies
181+
"$PIPBIN" install -r "$REPO_ROOT/backends/qualcomm/requirements.txt"
182+
180183
echo "=== [$LABEL] Import smoke tests ==="
181184
"$PYBIN" -c "import executorch; print('executorch imported successfully')"
182185
"$PYBIN" -c "import executorch.backends.qualcomm; print('executorch.backends.qualcomm imported successfully')"

.githooks/README.md

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,63 @@
11
# Git Hooks
22

3-
This directory contains Git hooks for the ExecuTorch repository.
3+
This directory contains Git hooks for the ExecuTorch repository. It is used as
4+
`core.hooksPath`, so git looks here instead of `.git/hooks/`.
45

5-
## Pre-commit Hook
6+
## Hooks
67

7-
The pre-commit hook automatically updates the PyTorch commit pin in `.ci/docker/ci_commit_pins/pytorch.txt` whenever `torch_pin.py` is modified.
8+
### pre-commit
89

9-
### How It Works
10+
Runs on every commit:
1011

11-
1. When you commit changes to `torch_pin.py`, the hook detects the change
12-
2. It parses the `NIGHTLY_VERSION` field (e.g., `dev20251004`)
13-
3. Converts it to a date string (e.g., `2025-10-04`)
14-
4. Fetches the corresponding commit hash from the PyTorch nightly branch at https://github.com/pytorch/pytorch/tree/nightly
15-
5. Updates `.ci/docker/ci_commit_pins/pytorch.txt` with the new commit hash
16-
6. Automatically stages the updated file for commit
12+
1. **torch_pin sync** — when `torch_pin.py` is staged, updates the PyTorch commit
13+
pin in `.ci/docker/ci_commit_pins/pytorch.txt` and syncs grafted c10 files.
14+
2. **lintrunner** — runs `lintrunner -a --revision HEAD^ --skip MYPY` on changed
15+
files. Auto-fixes formatting and blocks on lint errors. Soft-fails if lintrunner
16+
is not installed. Runs `lintrunner init` automatically when `.lintrunner.toml`
17+
changes.
1718

18-
### Installation
19+
### pre-push
1920

20-
To install the Git hooks, run:
21+
Delegates to `.git/hooks/pre-push` if one exists. This allows backend-specific
22+
pre-push hooks (e.g., ARM's license and commit message checks) to work alongside
23+
the repo-wide hooks.
24+
25+
## Installation
26+
27+
Hooks are installed automatically by `./install_executorch.sh`.
28+
29+
To install manually:
2130

2231
```bash
23-
.githooks/install.sh
32+
git config core.hooksPath .githooks
2433
```
2534

26-
This will copy the pre-commit hook to `.git/hooks/` and make it executable.
35+
### ARM backend pre-push
2736

28-
### Manual Usage
29-
30-
You can also run the update script manually at any time:
37+
ARM contributors should additionally install the ARM-specific pre-push hook:
3138

3239
```bash
33-
python .github/scripts/update_pytorch_pin.py
40+
cp backends/arm/scripts/pre-push .git/hooks/
3441
```
3542

36-
### Uninstalling
43+
## Bypassing
3744

38-
To remove the pre-commit hook:
45+
To skip hooks for a single commit or push:
3946

4047
```bash
41-
rm .git/hooks/pre-commit
48+
git commit --no-verify
49+
git push --no-verify
4250
```
4351

4452
## Troubleshooting
4553

46-
If the hook fails during a commit:
54+
If the torch_pin hook fails:
4755

4856
1. Check that Python 3 is available in your PATH
4957
2. Ensure you have internet connectivity to fetch commits from GitHub
5058
3. Verify that the `NIGHTLY_VERSION` in `torch_pin.py` is in the correct format (`devYYYYMMDD`)
51-
4. Make sure the corresponding nightly release exists in the PyTorch nightly branch
5259

53-
You can run the script manually to see detailed error messages:
60+
If lintrunner fails:
5461

55-
```bash
56-
python .github/scripts/update_pytorch_pin.py
57-
```
62+
1. Run `lintrunner init` to install linter tools
63+
2. Check that your virtual environment is activated

.githooks/install.sh

Lines changed: 0 additions & 23 deletions
This file was deleted.

.githooks/pre-commit

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,68 @@ if git diff --cached --name-only | grep -q "^torch_pin.py$"; then
2727
fi
2828
fi
2929

30-
exit 0
30+
# --- lintrunner ---
31+
32+
if ! command -v lintrunner >/dev/null 2>&1; then
33+
echo "Warning: lintrunner not found. Skipping lint checks."
34+
echo "Install with: pip install lintrunner lintrunner-adapters && lintrunner init"
35+
exit 0
36+
fi
37+
38+
if [ ! -f .lintrunner.toml ]; then
39+
echo "Warning: .lintrunner.toml not found. Skipping lint checks."
40+
exit 0
41+
fi
42+
43+
git_dir=$(git rev-parse --git-dir)
44+
45+
# Portable hash: sha256sum (Linux) or shasum (macOS)
46+
if command -v sha256sum >/dev/null 2>&1; then
47+
toml_hash=$(sha256sum .lintrunner.toml | cut -d' ' -f1)
48+
else
49+
toml_hash=$(shasum -a 256 .lintrunner.toml | cut -d' ' -f1)
50+
fi
51+
stored_hash=""
52+
[ -f "${git_dir}/.lintrunner_init_hash" ] && stored_hash=$(cat "${git_dir}/.lintrunner_init_hash")
53+
54+
if [ "${toml_hash}" != "${stored_hash}" ]; then
55+
echo "Running lintrunner init..."
56+
if lintrunner init; then
57+
echo "${toml_hash}" > "${git_dir}/.lintrunner_init_hash"
58+
else
59+
echo "lintrunner init failed. Run 'lintrunner init' manually."
60+
exit 1
61+
fi
62+
fi
63+
64+
staged_files=$(git diff --cached --name-only --diff-filter=ACMR)
65+
66+
# Use HEAD^ if it exists (skip on initial commit)
67+
revision_flag="--revision HEAD^"
68+
if ! git rev-parse HEAD^ >/dev/null 2>&1; then
69+
revision_flag=""
70+
fi
71+
72+
lintrunner -a $revision_flag --skip MYPY
73+
lint_status=$?
74+
75+
# Check if lintrunner modified any staged files. If so, block the commit
76+
# so the user can review the changes before committing.
77+
files_modified=0
78+
while IFS= read -r path; do
79+
[ -z "${path}" ] && continue
80+
if ! git diff --quiet -- "${path}" 2>/dev/null; then
81+
files_modified=1
82+
break
83+
fi
84+
done <<< "${staged_files}"
85+
86+
if [ $files_modified -eq 1 ]; then
87+
echo "Lintrunner modified files. Review with 'git diff', then 'git add -u && git commit'."
88+
exit 1
89+
fi
90+
91+
if [ $lint_status -ne 0 ]; then
92+
echo "Lint errors found. Fix them and try again, or use 'git commit --no-verify' to skip."
93+
exit 1
94+
fi

.githooks/pre-push

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
# Delegate to local pre-push hook if present (e.g., ARM backend).
4+
# .githooks/ is set as core.hooksPath, so git won't look in .git/hooks/
5+
# automatically. This passthrough ensures local hooks still run.
6+
local_hook="$(git rev-parse --git-dir)/hooks/pre-push"
7+
if [ -x "$local_hook" ]; then
8+
"$local_hook" "$@"
9+
fi

.github/workflows/mlx.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
name: MLX
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- release/*
8+
pull_request:
9+
paths:
10+
- .github/workflows/mlx.yml
11+
- backends/mlx/**
12+
- extension/llm/export/**
13+
workflow_dispatch:
14+
15+
permissions: {}
16+
17+
jobs:
18+
test-mlx:
19+
uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
20+
with:
21+
job-name: test-mlx
22+
runner: macos-14-xlarge
23+
python-version: "3.12"
24+
submodules: recursive
25+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
26+
timeout: 90
27+
script: |
28+
set -eux
29+
30+
echo "::group::Install ExecuTorch and configure build"
31+
${CONDA_RUN} python install_executorch.py > /dev/null
32+
# The sanitizers fail on github VM runner, but pass on real device
33+
# TODO: figure out why
34+
${CONDA_RUN} cmake --preset mlx-release -DEXECUTORCH_BUILD_TESTS=ON -DEXECUTORCH_MLX_ENABLE_SANITIZERS=OFF
35+
echo "::endgroup::"
36+
37+
${CONDA_RUN} pip list
38+
39+
echo "::group::Build test runners"
40+
${CONDA_RUN} cmake --build cmake-out --target op_test_runner multi_thread_test_runner -j$(( $(sysctl -n hw.ncpu) - 1 ))
41+
echo "::endgroup::"
42+
43+
echo "::group::Run op unit tests"
44+
${CONDA_RUN} python -m executorch.backends.mlx.test.run_all_tests -j4 --max-tasks-per-worker 10 --clean-after
45+
echo "::endgroup::"
46+
47+
echo "::group::Run Python unit tests"
48+
${CONDA_RUN} python -m pytest \
49+
backends/mlx/test/test_passes.py \
50+
backends/mlx/test/test_pattern_utils.py \
51+
backends/mlx/test/test_partitioner.py \
52+
-v
53+
echo "::endgroup::"
54+
55+
echo "::group::Run multi-thread stress test"
56+
${CONDA_RUN} python backends/mlx/test/export_multi_thread_test_model.py /tmp/multi_thread_test_model.pte
57+
ET_TESTING_MODEL_PATH=/tmp/multi_thread_test_model.pte \
58+
ET_TESTING_NUM_THREADS=50 \
59+
ET_PREDICTIONS_PER_THREAD=100 \
60+
./cmake-out/backends/mlx/test/multi_thread_test_runner
61+
echo "::endgroup::"
62+
63+
backend-tester:
64+
strategy:
65+
fail-fast: false
66+
matrix:
67+
suite: [models, operators]
68+
uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
69+
with:
70+
job-name: test-mlx-backend-${{ matrix.suite }}
71+
runner: macos-14-xlarge
72+
python-version: "3.12"
73+
submodules: recursive
74+
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
75+
timeout: 120
76+
script: |
77+
set -eux
78+
79+
echo "::group::Install ExecuTorch"
80+
${CONDA_RUN} python install_executorch.py > /dev/null
81+
echo "::endgroup::"
82+
83+
${CONDA_RUN} pip list
84+
85+
echo "::group::Run backend test suite (${{ matrix.suite }})"
86+
${CONDA_RUN} pytest -c /dev/null backends/test/suite/${{ matrix.suite }}/ -m flow_mlx -n auto 2>&1 | tee pytest_output.txt || true
87+
echo "::endgroup::"
88+
89+
# Parse pytest summary and check failure threshold
90+
if grep -E "^=+ .* =+$" pytest_output.txt | tail -1 | grep -q "failed"; then
91+
FAILED=$(grep -E "^=+ .* =+$" pytest_output.txt | tail -1 | grep -oE "[0-9]+ failed" | grep -oE "[0-9]+")
92+
else
93+
FAILED=0
94+
fi
95+
96+
if [ "${{ matrix.suite }}" = "operators" ]; then
97+
MAX_FAILURES=0
98+
else
99+
MAX_FAILURES=3
100+
fi
101+
102+
echo "Failed tests: $FAILED (max allowed: $MAX_FAILURES)"
103+
if [ "$FAILED" -gt "$MAX_FAILURES" ]; then
104+
echo "::error::Too many test failures: $FAILED > $MAX_FAILURES"
105+
exit 1
106+
fi

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,7 @@ xcuserdata/
7474
*.dll
7575
*.pyd
7676

77+
7778
# Agents
7879
.claude/*.local.*
80+
extension/pybindings/mlx.metallib

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@
6767
[submodule "third-party/json"]
6868
path = third-party/json
6969
url = https://github.com/nlohmann/json.git
70+
[submodule "backends/mlx/third-party/mlx"]
71+
path = backends/mlx/third-party/mlx
72+
url = https://github.com/ml-explore/mlx.git
73+
shallow = true

CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ if(EXECUTORCH_BUILD_MPS)
682682
list(APPEND _executorch_backends mpsdelegate)
683683
endif()
684684

685+
if(EXECUTORCH_BUILD_MLX)
686+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/backends/mlx)
687+
list(APPEND _executorch_backends mlxdelegate)
688+
endif()
689+
685690
if(EXECUTORCH_BUILD_NEURON)
686691
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/backends/mediatek)
687692
list(APPEND _executorch_backends neuron_backend)
@@ -982,6 +987,10 @@ if(EXECUTORCH_BUILD_PYBIND)
982987
list(APPEND _dep_libs mpsdelegate)
983988
endif()
984989

990+
if(EXECUTORCH_BUILD_MLX)
991+
list(APPEND _dep_libs mlxdelegate)
992+
endif()
993+
985994
if(EXECUTORCH_BUILD_OPENVINO)
986995
list(APPEND _dep_libs openvino_backend)
987996
endif()
@@ -1082,6 +1091,12 @@ if(EXECUTORCH_BUILD_PYBIND)
10821091
install(TARGETS data_loader
10831092
LIBRARY DESTINATION executorch/extension/pybindings
10841093
)
1094+
1095+
# Copy MLX metallib next to _portable_lib.so for editable installs. MLX uses
1096+
# dladdr() to find the directory containing the library with MLX code, then
1097+
# looks for mlx.metallib in that directory. When MLX is statically linked into
1098+
# _portable_lib.so, we need the metallib colocated with it.
1099+
executorch_target_copy_mlx_metallib(portable_lib)
10851100
endif()
10861101

10871102
if(EXECUTORCH_BUILD_WASM)

0 commit comments

Comments
 (0)