Skip to content

Merge upstream ml-explore/mlx main afc03c511 (2026-10-23) - #38

Merged
Gajesh2007 merged 307 commits into
mainfrom
sync/upstream-2026-10-22
Oct 23, 2026
Merged

Gajesh2007 merged 307 commits into
mainfrom
sync/upstream-2026-10-22

Conversation

@davidtai

@davidtai davidtai commented Oct 22, 2026 •

Copy link
Copy Markdown

Proposed changes

This PR merges upstream ml-explore/mlx main at c6abf0dbb (2026-10-22) into the fork. It uses a merge commit, not a rebase. David ordered the merge on 2026-10-22.

What upstream brings

Fork changes that stay

Every fork.yaml section that describes fork work stays:

Where the fork now takes upstream

Fork change Upstream change Result
PR #10, sync to v0.32.2 Upstream has v0.32.2 Section removed. Every file that PR #10 alone changed is now upstream's file.
PR #27, backport of ml-explore#4567 and ml-explore#4572 Upstream has both Section removed. matmul.cpp, matmul.h, steel_gemm_gather*.h, gather_mm_offsets.metal, the benchmarks and .gitignore are upstream's files.
PR #32, flooring integer Divide functors ml-explore#4515 and ml-explore#4642 add a FloorDivide primitive The fork functors are removed, so integer Divide truncates, as in upstream. The PR #32 tests stay. The fork adds one guard to upstream's CPU floor_divide for min // -1.
PR #15, bias-sum operand promotion ml-explore#4516 promotes the same operands The sentence is removed from fork.yaml.

Conflicts and how I resolved them

git merge stopped on 69 files. 39 of them were files that only PR #10 changed, and PR #10 equals v0.32.2 there. For those 39 files I took upstream's file. They are listed at the end of this section.

PR #10 applied upstream content as a fork commit, so git used the old base 77a0c1e8 and saw that content as fork work. For the files that the fork changed after v0.32.2, I merged a second time with v0.32.2 as the base. That merge matches the backport lines in PR #27 to upstream's lines. I compared the two merges and checked each difference. A function-level audit then compared each definition in the result with the fork, upstream and v0.32.2.

File Resolution
.github/actions/{build,build-wheel,test-linux,test-wheel,test-windows}/action.yml, .github/workflows/release.yml The fork deleted them (PR #23). Upstream changed them. They stay deleted.
.github/workflows/update_bypass_list.yml, .github/scripts/{find-eligible-contributors,update-bypass-list}.js No conflict. The merge added them again. I deleted them again (PR #23).
.github/workflows/build_and_test.yml Upstream changed only the jobs that the fork deleted. Kept the fork file.
.gitignore, benchmarks/python/swiglu_bench.py, benchmarks/python/swiglu_qmm_bench.py These came from the PR #27 backport. Took upstream.
mlx/backend/cpu/binary_ops.h and mlx/backend/metal/kernels/binary_ops.h Took upstream FloorDivide. Removed the PR #32 Divide functors.
mlx/backend/cpu/simd/base_simd.h, mlx/backend/cpu/simd/accelerate_simd.h Upstream's CPU floor_divide divides min by -1. That overflows and traps on x86: the PR #32 test crashed with SIGFPE in the Fedora x86_64 job. Added the PR #32 guard: divide by 1 there, so the result wraps to min.
tests/CMakeLists.txt Upstream renamed METAL_TEST_SOURCES to GPU_TEST_SOURCES. Kept gptoss_mxfp4_tests.cpp under the new name.
python/tests/test_conv.py Took upstream. Kept the GPU-only skip in test_conv2d_winograd_batch_tiling (PR #30). Added the same skip to upstream's new test_conv2d_winograd_disable, because the fork's CPU run cannot tell the two paths apart.
python/tests/test_fft.py, tests/ops_tests.cpp Took upstream. The fork tests and skips merged clean.
python/tests/test_ops.py, python/tests/test_quantized.py, python/tests/test_fast_sdpa.py Kept the fork tests next to upstream's new tests. test_fast_sdpa.py imports numpy again, because upstream removed the import and the fork tests use it.
mlx/backend/metal/device.h, mlx/fast.h Context conflicts only. Took upstream's lines. The fork declarations merged clean.
mlx/memory.h Kept both include lists.
mlx/compile.cpp, mlx/compile_impl.h Kept the fork's compile-cache registry and the compatible compile_erase(fun_id) / compile_clear_cache(). The rest is upstream.
mlx/backend/metal/kernels/sdpa_vector.h git merged it without a conflict, but the result had two copies of sdpa_vector_2pass_1_gqa. I rebuilt it from upstream and applied the FP32 partials again (PR #16).
mlx/backend/metal/kernels/scaled_dot_product_attention.metal, mlx/backend/metal/scaled_dot_product_attention.cpp Took upstream's GQA, D512 and tk1 work. Renamed every two-pass kernel name, including the new gqa_<g> and _tk1 names, to sdpa_vector_2pass_fp32partials_*. Kept the float32 intermediate.
mlx/backend/metal/kernels/quantized.h Kept the row and packed-column bound in the affine load_safe. The merge put upstream's new typename U = T template parameter on the fork's qmm_t_expert_impl. I moved it to qmm_t_impl.
mlx/backend/metal/kernels/quantized_nax.h Kept the fork's distinct names and column bounds. Renamed get_pack_factor in upstream's new staged and split-K code. Kept the revert of the ml-explore#4352 simdgroup skip in affine_gather_qmm_rhs_nax.
mlx/backend/metal/kernels/fp_quantized.h Took upstream's global scale and Fp4HalfLoader. Kept the fork names and column bounds. fp_gather_qmv_fast_tail now follows upstream's fp_gather_qmv_fast: the new partial_rows template parameter and the global scale in adjust_matrix_offsets.
mlx/backend/metal/kernels/fp_quantized_nax.h Took upstream's global scale and the new fp_qmv_nax. Kept the fork names, the out-of-bounds scale guard and the revert of the ml-explore#4352 simdgroup skip in fp_gather_qmm_rhs_nax.
mlx/backend/metal/quantized.cpp Took upstream's global scale, the fixed buffer slots and NAX for non-transposed calls (ml-explore#4684). Kept the fork's bool gather_qmm_rhs with the retract, the Gemma 4 expert-tile route, the takes_sorted_rhs_route gate, the GPT-OSS prefill tile and the decode fast tail.
mlx/backend/metal/primitives.cpp, mlx/fence.h, CLAUDE.md git merged them without a conflict, but PR #10 content that upstream later removed came back: duplicate CrossEntropy Metal stubs (ml-explore#4520), the Fence::cast const overload (ml-explore#4552) and the CLAUDE.md symlink. Took upstream.
scripts/check_forkdiff.py Upstream turned on ruff rule E501. Wrapped four long lines.
mlx/backend/cpu/gemm.h, gemms/cblas.cpp, masked_mm.cpp, conv.cpp Latent upstream bug (worth an upstream issue; I did not open one). The fork CPU pass found NaN in upstream's test_fp_gather_qmm_non_multiple_of_32 (K=512, N=1040, sorted, float32). The NaN was only in the reference x @ w_sel (CPU matmul, M=1). Its 266240-byte output reused the buffer of the previous subtest's packed fp4 weights, and Accelerate read C with beta = 0 (NaN * 0). One helper, clear_gemm_output in gemm.h, now fills the exact C region with zeros before every BLAS gemm with beta = 0: the float32/float64/complex64 matmul wrappers (Matmul, AddMM, SegmentedMM), BlockMaskedMM and GatherMM in masked_mm.cpp, and the two explicit-gemm convolutions in conv.cpp. No other BLAS gemm or gemv call exists in the CPU backend. MLX's own SIMD gemm already skips C when beta is 0.
mlx/scheduler.{h,cpp}, mlx/transforms.cpp, tests/scheduler_tests.cpp Upstream ordering race. StreamThread::enqueue throws the stream's stored task error on the main thread (ml-explore#3742). async_eval enqueues several tasks, so when the stream thread finishes a failing task first, async_eval throws. ml-explore#4338 expects the error only from synchronize. test_async_eval_error_in_synchronize failed on 2 of 6 heads in the fork CPU pass. async_eval now holds scheduler::DeferStreamErrors, so synchronize raises the error. A deterministic C++ test covers it.
mlx/ops.cpp (qqmm) Upstream's CPU QQMatmul has no global-scale input and ignored the scales: wrong results with no error (upstream TODO at test_quantized.py:335). The fork CPU test pass found it. qqmm now raises "Global scale is only supported on the GPU" on a non-GPU stream, as gather_qmm does.
python/tests/test_fast_sdpa.py, python/tests/test_quantized.py (GPU-path tests) Upstream tests that check only GPU kernels (test_sdpa_vector_2pass_threadgroup_limit with force_fused=True, the global-scale qqmm and gather_qmm tests) now check, when the default device is the CPU, that the documented error is raised. The GPU pass runs them as before.
python/tests/test_reduce.py (test_init_reduce_large_output) Capability skip, ruled by David on 2026-10-23. The test skips only when the default device cannot hold its 4 GiB output: on the GPU when max_buffer_length is less than 4 GiB, on the CPU when memory_size is less than 16 GiB. The hosted macos-26 runner caps Metal buffers at 3.5 GiB. The test cannot be smaller: it is the regression test for ml-explore#4437 and needs 2^32 output elements (more than one 32-bit grid dimension), and bool outputs of one byte each give 4 GiB.
.github/workflows/fork-tests.yml When a CPU test file fails, the job prints the end of its log at once.
tests/allocator_tests.cpp, tests/gptoss_mxfp4_tests.cpp Upstream allocator::malloc now returns an owning allocator::Data, and allocator::free is gone. The fork tests use allocator::allocator().malloc / free, as upstream's tests do. Upstream gather_qmm has a new global_scale argument; the GPT-OSS tests pass std::nullopt.

Files that only PR #10 changed, now upstream's files (39): .pre-commit-config.yaml, CMakeLists.txt, CONTRIBUTING.md, setup.py, mlx/version.h, mlx/ops.cpp, mlx/primitives.cpp, mlx/fft.cpp, mlx/backend/metal/{conv.cpp,kernels.h,nojit_kernels.cpp}, mlx/backend/metal/kernels/fp_quantized_nax.metal, mlx/backend/metal/kernels/steel/attn/kernels/{steel_attention.metal,steel_attention_nax.h,steel_attention_nax.metal}, mlx/backend/cuda/{CMakeLists.txt,event.cu,fence.cpp}, mlx/backend/no_gpu/{fence.cpp,primitives.cpp}, python/src/{CMakeLists.txt,fast.cpp,indexing.cpp,ops.cpp,stream.cpp}, python/mlx/nn/losses.py, python/mlx/optimizers/optimizers.py, python/tests/{run.py,test_array.py,test_compile.py,test_eval.py,test_fast.py,test_load.py,test_reduce.py,test_vmap.py}, tests/load_tests.cpp, benchmarks/python/sdpa_bench.py, examples/cmake_extension/{pyproject.toml,requirements.txt}.

fork.yaml and FORKDIFF.md

  • base.hash is now c6abf0dbb4fc141fad739b32ae486c06728e49e5, the merge-base with upstream main.
  • Removed sections: "Upstream v0.32.2 sync carried in PR chore: upgrade core to MLX 0.32.2 #10" and "Upstream gather_mm and gather_qmm row-tile scheduling". Upstream has both changes.
  • New sections "CPU qqmm refuses a global scale" (mlx/ops.cpp) and "CPU BLAS gemm clears C when beta is 0" (mlx/backend/cpu/{gemm.h,gemms/cblas.cpp,masked_mm.cpp,conv.cpp}). The coordinator added "64-bit products in the JACCL ring and the vector SDPA layout check" for two CodeQL findings.
  • The simdgroup-skip revert has its own section now. Before, it was a paragraph in the removed backport section.
  • "Integer floor_divide rounds down" is now "Integer floor_divide tests".
  • "Fork tooling" lists the bypass-list workflow and scripts.
  • FORKDIFF.md has a new sync policy. The fork merges upstream when David directs it. Each merge moves base.hash to the new merge-base and checks every section again. Between merges, an upstream fix that the fork needs is ported as its own commit.

python3 scripts/check_forkdiff.py --upstream-ref refs/remotes/upstream/main passes: 85 changed files, 85 described.

Downstream pins

Layr-Labs/mlx-c and Layr-Labs/mlx-swift point at the head SHA of this PR. Merge this PR with a merge commit, not a squash. A squash makes a new SHA, and the dependents must then pin again.

Test plan

I did not build or run tests on the laptop. The checks are:

  • Fork Tests / metal-build-and-test: Metal build, C++ and Python tests, the second run on the CPU, the coverage report.
  • Build and Test / Check Lint: pre-commit (clang-format, cmake-format, ruff).
  • Build and Test / Linux Sanitizer Tests (ASAN) and (UBSAN).
  • Build and Test / Linux Fedora (x86_64) and (aarch64).
  • Fork Diff Check.
  • A Release build on a Mac box (-DMLX_BUILD_TESTS=ON -DMLX_METAL_JIT=OFF) found compile errors in the merged kernels and in two fork test files. The fixes are in this PR. The box build of dbf463fc (library, mlx.metallib, C++ tests) has 0 errors. On the box I ran only the CPU test "test floor_divide integer edge cases on the CPU": it passes. The box GPU needs a lock protocol, so CI runs the GPU tests.

Observed CI flake

On head 1f7e9063 the ring distributed test in the GPU pass failed: the first failure is "[METAL] Command buffer execution failed: Caused GPU Timeout Error" in test_all_reduce_extra, and four more failures followed it. The same ring tests passed on 4588237c and aaec1b63. The only code change between aaec1b63 and 1f7e9063 is the qqmm guard, which the ring test does not use. I read it as a GPU timeout on the hosted runner. I did not change the test. Upstream's test-macos action does not fail the step on these ring failures; it checks only for [WARN].

Checklist

  • I have read the CONTRIBUTING document
  • I have run pre-commit run --all-files to format my code / installed pre-commit prior to committing changes (I ran the same hook versions on the changed files: clang-format 21.1.8, ruff 0.16.10, cmake-format 0.6.13.)
  • I have added tests that prove my fix is effective or that my feature works (No new feature. The merge keeps the fork tests and adds upstream's tests.)
  • I have updated the necessary documentation (if needed) (fork.yaml and FORKDIFF.md are updated. No other docs change.)
  • I understand it is strictly prohibited to use AI to write PR description
  • AI usage disclosure: An AI agent (Claude Opus 5.5 in Claude Code) did the merge, resolved the conflicts, wrote the commits and wrote this description. David directed the work. The fork inherits upstream's rule against AI-written PR text, so the box above is not ticked. David decides.

🤖 Generated with Claude Code

Reference uniqueness without buffer ownership (commit 699649d38164)

Upstream 5dbe9991 (ml-explore#4634) makes is_donatable() require that MLX owns the buffer. That is right for donation. But the fork's mlx-c buffer-info API used is_donatable() as a "no other reference" proof, which mlx-swift-lm relies on for paged KV segments, checkpoint memory accounting and MTP state, including wrapped host pages. The fork adds array::is_uniquely_referenced(), the same reference-count test without the ownership test. is_donatable() and donation do not change. Pushed by the orchestrator; fork.yaml section "Reference uniqueness without buffer ownership".

kapellirohith and others added 30 commits August 22, 2026 10:48
Signed-off-by: Pradyot Ranjan <[email protected]>
Co-authored-by: Pradyot Ranjan <[email protected]>
Co-authored-by: Cheng <[email protected]>
begcdn and others added 3 commits October 23, 2026 12:46
test_fp_gather_qmm_non_multiple_of_32 (K=512, N=1040, sorted,
float32) gave NaN on the CPU. The diagnostic job found the NaN only in
the reference y_hat = x @ w_sel, a batched matmul with M = 1. Its
inputs had no NaN. The test failed alone too, but only after the
earlier subtests of the same test: y_hat is 266240 bytes, the same
size as the packed fp4 weights of the previous subtest, so the output
reused that buffer from the allocator cache. Accelerate read C even
with beta = 0 and computed NaN * 0.

Fill C with zeros before cblas_sgemm, cblas_dgemm and cblas_cgemm
when beta is 0. MLX's own SIMD gemm already skips C when beta is 0.

Remove the temporary diagnostic job.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The previous commit cleared C only in the gemms/cblas.cpp wrappers.
Four more call sites call cblas_sgemm directly with beta 0:
BlockMaskedMM and GatherMM (the CPU MoE gathered matmul) in
masked_mm.cpp, and the two explicit-gemm convolutions in conv.cpp.
They can give the same NaN * 0 from a reused output buffer.

Add one helper, clear_gemm_output in gemm.h, that fills the exact C
region of a call (rows by columns, with its ldc) with zeros. Use it at
all five call sites, the wrappers included. No other BLAS gemm or gemv
call exists in the CPU backend.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
davidtai added a commit to Layr-Labs/mlx-c that referenced this pull request Oct 23, 2026
Layr-Labs/mlx#38 moved from 1f7e9063 to cb73247a. The code change: every
CPU BLAS gemm call with beta 0 clears its output region first, because
Accelerate can read C even when beta is 0 and a reused buffer can hold NaN
(latent upstream bug). No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
davidtai added a commit to Layr-Labs/mlx-swift that referenced this pull request Oct 23, 2026
Layr-Labs/mlx#38 moved to cb73247a: every CPU BLAS gemm call with beta 0
clears its output region first, because Accelerate can read C even when
beta is 0 and a reused buffer can hold NaN (latent upstream bug; affects
CPU matmul, gather_mm, block-masked matmul and convolution). Layr-Labs/mlx-c#17
moved to 44763458, which only moves its CMake GIT_TAG to the same mlx commit.
The changed files are C++ sources that Cmlx compiles directly, so no generated
sources change.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
davidtai and others added 6 commits October 22, 2026 23:26
Split the fork-tests job into parallel jobs. build-wheel and build-cpp
build in parallel. build-cpp also runs the C++ tests on the GPU and on
the CPU. python-gpu (3 shards) and python-cpu (4 shards) run the Python
tests from the uploaded wheel. metal-build-and-test merges the coverage
profiles of all jobs, writes the same coverage report, and fails when
another job failed.

python_shards.py checks the shard map: each python/tests/test*.py file
must be in each pass exactly one time. Split files must partition their
test IDs. The last job fails when a test ran in two shards.

fork-build-cache restores and saves ccache on pull requests and on main,
and keeps a cache of the compiled Metal kernels through an xcrun wrapper
keyed on the preprocessed source, the arguments and the toolchain.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Split test_fast_sdpa and test_quantized into named tests and rest
entries in the CPU shards, so that no CPU shard has an entry longer than
test_gated_delta_sequential. Put the entries in longest-first order.

Save the ccache files and the Metal kernel cache only when the build had
misses.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The full --version text of clang and metal names the install folder and
the OS, and it changed between three macos-26 runner images, so every
job started from an empty cache. Key ccache on the clang major.minor
version and fall back to a cache of another clang version; ccache keeps
compiler_check=content. Key the Metal kernel cache on the metal compiler
version and the kernels hash, and fall back only to a cache of the same
metal compiler. Each Metal entry key holds the first line of the metal
--version text and the SDK build.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Upstream 5dbe999 (ml-explore#4634) makes is_donatable() also require that MLX owns
the buffer, so the allocator never reuses a wrapped external buffer. The
fork's mlx-c buffer-info API reported `unique` from is_donatable(), and
mlx-swift-lm uses that as a "no other reference" proof for paged KV
segments, checkpoint memory accounting and MTP state, including wrapped
host pages, which now always read as not unique.

Add is_uniquely_referenced(): the same reference-count test without the
ownership test. is_donatable() and donation behavior do not change.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
test_eval.test_async_eval_error_in_synchronize (upstream ml-explore#4338) failed
on some runs of the fork's CPU test pass: mx.async_eval raised the
LU factorization error that the test expects only from
mx.synchronize.

Cause: StreamThread::enqueue throws the stream's stored error when it
runs on the main thread (ml-explore#3742). async_eval enqueues several tasks.
When the stream thread finishes the failing task before async_eval
enqueues the next one, that enqueue throws. The order depends on
thread timing, so the failure is intermittent. The fork does not
change this path.

async_eval now holds a scheduler::DeferStreamErrors guard. While it
lives, enqueue on that thread does not throw the stored error, and
synchronize throws it later. eval does not change.

Add a C++ test that stores the error before async_eval, so it fails
on every run without the fix.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
davidtai added a commit to Layr-Labs/mlx-c that referenced this pull request Oct 23, 2026
Layr-Labs/mlx#38 moved from 699649d3 to 36f043ec: async_eval no longer
throws an error that an earlier CPU stream task stored. It fixes an
upstream ordering race between #3742 and #4338, and synchronize still
raises the error. No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
tivris and others added 8 commits October 23, 2026 15:41
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The hosted macOS runners are a shared pool. Pack the GPU pass into 2
shards and the CPU pass into 3 shards, balanced by the durations that
run 39902948153 measured. A run now uses 8 macOS jobs, and at most 6 run
at the same time (before: 10 jobs, 8 at the same time).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The preprocessed source of the NAX kernels names the Metal toolchain
mount folder, which has a random suffix on each runner. The 10 NAX
kernels missed the Metal cache in every warm build (41 of 51 hits).
Remove the suffix from the text that the key hashes, and keep the
toolchain version.

CPU shard 0 was the slowest shard in the cold and warm runs of b72420d
(651 s and 470 s of tests). Move test_quantized:rest to CPU shard 1
(221 s).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
ci: build once and run the Metal and CPU test passes in parallel
Merge the 9 upstream commits after c6abf0d: ml-explore#4729, ml-explore#4730, ml-explore#4731,
ml-explore#4728, ml-explore#4736, ml-explore#4737, ml-explore#4724, ml-explore#4738 and ml-explore#4744. The merge has no
conflict. Every merged file gets exactly upstream's change.

ml-explore#4728 widens the affine qmv_wide input-vector tile from 5 to 8. The
fork has no qmv_wide change in this repository, and the GPT-OSS MXFP4
decode tail is an fp gather_qmv kernel that ml-explore#4728 does not touch.
Each input vector still accumulates in its own registers, so the
results do not change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Set base.hash to the new merge-base after the merge of the 9 upstream
commits. No fork section changes: the merge has no conflict, and each
merged file gets exactly upstream's change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
davidtai added a commit to Layr-Labs/mlx-c that referenced this pull request Oct 23, 2026
Layr-Labs/mlx#38 merged 9 more upstream ml-explore/mlx commits, through
afc03c511 (2026-10-23): large-offset gather/scatter and pad fixes, a ring
send/recv data race fix, mx.take negative indices, normal loc/scale shapes,
padding jvp/vjp, and wider affine qmv_wide row tiles. No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@davidtai davidtai changed the title Merge upstream ml-explore/mlx main c6abf0dbb (2026-10-22) Merge upstream ml-explore/mlx main afc03c511 (2026-10-23) Oct 23, 2026
@Gajesh2007
Gajesh2007 merged commit 7b0ebaf into main Oct 23, 2026
24 of 26 checks passed
@Gajesh2007
Gajesh2007 deleted the sync/upstream-2026-10-22 branch October 23, 2026 19:15
Gajesh2007 pushed a commit to Layr-Labs/mlx-c that referenced this pull request Oct 23, 2026
* Support MLX v0.32.2 (ml-explore#123)

* bump to MLX v0.32.0

* fix generator (parser not supporting thread_local)

* overloads for mlx_astype

* regenerate

* fix const char* return assignement

* regenerate

* bump to MLX v0.32.1

* fix overloads to maintain backward-compatibility

* add support for mlx_compile_cache (std::weak_ptr<mlx::core::detail::CompileCache> aka CompileCacheWeakPtr)

* regenerate C files

* bump to MLX v0.32.2

* regenerate

* add missing mlx_compile_cache new/free functions

* fix mlx_string return type (again)

* add support for some missing overloads

* fix flip overloads

* Bind new_thread_unsafe_stream (ml-explore#122)

Streams are thread affine as of mlx 0.31.2: a stream's GPU command encoder
is registered in a thread_local map, so evaluating on a stream from a thread
other than the one that created it raises "There is no Stream(gpu, N) in
current thread."

mlx added `new_thread_unsafe_stream` for language bindings that do their own
locking (ml-explore/mlx#3578), but mlx-c does not expose it, so those bindings
have no way to reach it. mlx-swift currently carries a local C shim for this.

Requires mlx 0.32.0, so the pin moves from v0.31.2. The existing bindings
build and the examples pass unchanged against it.

* example-ops (ml-explore#121)

* Add examples for ops added in 0.32.0

* fix examples

---------

Co-authored-by: Ronan Collobert <[email protected]>

* fix custom io reader/writer API (ml-explore#130)

* Implement missing stream functions (ml-explore#132)

* prefer forward-declaration to avoid circular references

* support for missing stream functions

* cosmetics

* version 0.7.0 (ml-explore#135)

* bump to v0.32.4 (ml-explore#140)

* Pin the MLX core to the Layr-Labs/mlx upstream sync merge

Move the FetchContent GIT_TAG from ad5966879ec5 to 3c3750f4ed40.
That commit merges upstream ml-explore/mlx main c6abf0dbb into
Layr-Labs/mlx main. Its parent on Layr-Labs/mlx main has the same tree
as ad5966879ec5, so no fork core change is lost. The new revision adds
the core functions that the upstream bindings call, for example
new_thread_local_stream and get_array_buffer_size.

GIT_REPOSITORY stays on Layr-Labs/mlx. fork.yaml describes the new pin.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Pin the MLX core to the repaired Layr-Labs/mlx sync head

Move the FetchContent GIT_TAG from 3c3750f4ed40 to e5b6550d5898.
e5b6550d5898 is 3c3750f4ed40 plus two commits on the Layr-Labs/mlx
sync branch. They repair the merge in the quantized Metal kernels
(qmm_t_impl template parameters and the fp_gather_qmv_fast_tail
adjust_matrix_offsets call). At 3c3750f4ed40 quantized.metal and
fp_quantized.metal did not compile.

The pin stays provisional until the Layr-Labs/mlx sync PR is green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Pin the MLX core to the Layr-Labs/mlx sync PR head

Move the FetchContent GIT_TAG from e5b6550d5898 to 780b57ea90cc, the
head of Layr-Labs/mlx#38. 780b57ea90cc adds one commit over
e5b6550d5898. It changes only tests/allocator_tests.cpp and
tests/gptoss_mxfp4_tests.cpp, so the core library sources are the same.

The pin stays provisional until Layr-Labs/mlx#38 CI is green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head dbf463fc93ca

Move the FetchContent GIT_TAG from 780b57ea90cc to dbf463fc93ca, the
new head of Layr-Labs/mlx#38. dbf463fc93ca adds one commit over
780b57ea90cc. It changes the CPU SIMD headers base_simd.h and
accelerate_simd.h (guard INT_MIN / -1 in floor_divide) and fork.yaml.

The pin stays provisional until Layr-Labs/mlx#38 CI is green.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head 089acfe1

Layr-Labs/mlx#38 moved from dbf463fc to 089acfe1. The new commit computes
the JACCL ring wire offsets in 64 bits (CodeQL review fix). It changes only
mlx/distributed/jaccl/lib/jaccl/ring.cpp, ring_impl.h and fork.yaml, so no
mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head 4588237c

Layr-Labs/mlx#38 moved from 089acfe1 to 4588237c. The new commit computes
the vector SDPA query layout check in 64 bits (CodeQL alert 29). It changes
only mlx/backend/metal/scaled_dot_product_attention.cpp and fork.yaml, so no
mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head 1f7e9063

Layr-Labs/mlx#38 moved from 4588237c through aaec1b63 (tests and CI only)
to 1f7e9063. The code change is in mlx/ops.cpp: qqmm raises "Global scale
is only supported on the GPU" on a CPU stream, because the CPU kernel has no
global-scale input and returned wrong results. No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head cb73247a

Layr-Labs/mlx#38 moved from 1f7e9063 to cb73247a. The code change: every
CPU BLAS gemm call with beta 0 clears its output region first, because
Accelerate can read C even when beta is 0 and a reused buffer can hold NaN
(latent upstream bug). No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Report buffer uniqueness from reference counts, not donatability

mlx_array_get_buffer_info set `unique` from is_donatable(). Since upstream
mlx 5dbe9991 (#4634), is_donatable() also requires that MLX owns the buffer,
so a wrapped external buffer always read as not unique. The field means "no
other array references this array or its data", and mlx-swift-lm relies on
that for paged KV segments, checkpoint memory accounting and MTP state.

Use array::is_uniquely_referenced() from Layr-Labs/mlx 699649d3 and pin the
core there.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head 36f043ec

Layr-Labs/mlx#38 moved from 699649d3 to 36f043ec: async_eval no longer
throws an error that an earlier CPU stream task stored. It fixes an
upstream ordering race between #3742 and #4338, and synchronize still
raises the error. No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* Pin the MLX core to Layr-Labs/mlx#38 head 83dbdb04

Layr-Labs/mlx#38 merged 9 more upstream ml-explore/mlx commits, through
afc03c511 (2026-10-23): large-offset gather/scatter and pad fixes, a ring
send/recv data race fix, mx.take negative indices, normal loc/scale shapes,
padding jvp/vjp, and wider affine qmv_wide row tiles. No mlx-c source changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Ronan Collobert <[email protected]>
Co-authored-by: Charlie Le <[email protected]>
Co-authored-by: Eric Saxby <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
davidtai added a commit that referenced this pull request Oct 23, 2026
Main's tree equals 83dbdb0, the base of this branch, so the merge
keeps this branch's tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
davidtai added a commit that referenced this pull request Oct 24, 2026
Restore the upstream sync history squashed by #38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.