Skip to content

fix: macOS arm64 build - static libs, src1_cont, CPU-only backends, docs (#2) - #5

Merged
eejd merged 5 commits into
mainfrom
fix/macos-arm64-build
Nov 1, 2026
Merged

eejd merged 5 commits into
mainfrom
fix/macos-arm64-build

Conversation

@eejd

@eejd eejd commented Oct 31, 2026 •

Copy link
Copy Markdown
Owner

Draft. Part of #1, fixes the build half of #2. Does not make I2_S output correct on ARM (that is #3, see the stacked/related PRs).

What changes (one commit per fix)

  1. build: default to static ggml libraries on macOS – libggml-base references quantize_i2_s/dequantize_row_i2_s that live in libggml-cpu; ld64 rejects the shared build (microsoft/BitNet arm64 build broken on main: i2_s sources never wired into CMake; after source fixes llama-cli hangs/SIGSEGV with official i2_s gguf microsoft/BitNet#682, macOS / Apple Silicon: setup_env.py build fails (ggml-base can't find i2_s symbols); after working around it, Metal crashes and CPU output is garbage microsoft/BitNet#641, fix(build): add CPU backend check for tensor transforms and resolve macOS/Windows I2_S linker symbols microsoft/BitNet#635).
  2. build: declare src1_cont in the I2_S fast path – one-line patch for the submodule, carried as patches/llama.cpp/0001-*.patch and applied idempotently at configure time by cmake/ApplyLlamaPatches.cmake (microsoft/BitNet ggml-cpu.c: src1_cont undeclared → build fails on ARM CPUs with i8mm (Apple M2/M3/M4, Graviton3, ...) microsoft/BitNet#618, arm64 build broken on main: i2_s sources never wired into CMake; after source fixes llama-cli hangs/SIGSEGV with official i2_s gguf microsoft/BitNet#682, macOS / Apple Silicon: setup_env.py build fails (ggml-base can't find i2_s symbols); after working around it, Metal crashes and CPU output is garbage microsoft/BitNet#641). 3rdparty/llama.cpp is isHuangXin/llama.cpp, which this repo cannot push to.
  3. build: CPU-only ggml on macOS by default – Metal built (BLAS off) → GGML_ASSERT in ggml_backend_sched_split_graph when the context is created; Metal + Accelerate/BLAS built → ggml_backend_blas claims I2_S MUL_MAT for batches ≥ 32 tokens and the process dies with SIGSEGV (what arm64 build broken on main: i2_s sources never wired into CMake; after source fixes llama-cli hangs/SIGSEGV with official i2_s gguf microsoft/BitNet#682 reports as "SIGSEGV during prompt processing"); BLAS built, Metal off → no crash, but those batches return garbage (synthetic gate with corrected kernels: ppl 4e27 vs 3.7e5). The commit message of this commit says "SIGSEGV" for BLAS in general; the precise matrix is in docs/macos-arm64.md (follow-up commit).
  4. docs: macOS arm64 build recipe – docs/macos-arm64.md (conda-free, out-of-tree build, reproduction notes).

Evidence (Apple M6, macOS 27.0.1, Apple clang 21.0.0, CMake 3.31)

upstream report on M6
microsoft#682 link fails (_quantize_i2_s, _dequantize_row_i2_s) reproduced, same symbols (setup_env.py flags, shared libs)
microsoft#682 src1_cont undeclared reproduced, ggml-cpu.c:1495:40 (flags -mcpu=native+dotprod+i8mm+nosve+sme)
microsoft#682 SIGSEGV during prompt processing reproduced for any prompt ≥ 31 tokens with -ngl 0; backtrace ends in ggml_backend_blas_graph_compute. The ">120 s spin on -p Hi" was not reproduced
microsoft#641 link / src1_cont reproduced
microsoft#641 Metal crash different: with Metal built the failure is the sched GGML_ASSERT at context creation; with BLAS it is the SIGSEGV above
microsoft#641 @@@@ output reproduced on the clean-clone build: The capital of France is@@@@@@@@@@@@@@@@ (cause: #3)
microsoft#635 link symptom reproduced; its tensor->backend guard does not apply (this ggml has no such field); the submodule bump was not used (static libs instead)
microsoft#618 reproduced
microsoft#99 condition reproduced: a MacPorts/PyPI gguf 0.19.0 shadows the submodule's → AttributeError ... 'MODEL_ARCH' has no attribute 'BITNET_B158'; PYTHONPATH=3rdparty/llama.cpp/gguf-py fixes it, no conda needed

Clean clone of this branch, out-of-tree build with the commands in docs/macos-arm64.md (default UI on): configure + build rc 0; llama-cli --version → version: 9918 (390c30775) built with AppleClang 21.0.0.21000334 for Darwin arm64; official ggml-model-i2_s.gguf loads and generates tokens (garbage, see above); synthetic I2_S model: llama-bench and 1..300-token batches at 1 and 4 threads run without crash.

Not done: Windows/Linux, TL1 (-DBITNET_ARM_TL1=ON), the BUILD_SHARED_LIBS=ON + submodule-fix route of microsoft#635.

eejd added 4 commits October 31, 2026 13:05
ggml.c (libggml-base) references quantize_i2_s and dequantize_row_i2_s,
which live in ggml-cpu/quants.c (libggml-cpu). ld64 rejects undefined
symbols in a shared library, so the default shared build fails at
libggml-base with:

  Undefined symbols for architecture arm64:
    "_dequantize_row_i2_s", referenced from: _type_traits in ggml.c.o
    "_quantize_i2_s", referenced from: _ggml_quantize_chunk in ggml.c.o

Default BUILD_SHARED_LIBS to OFF on Apple when the user did not set it.

Refs microsoft#682, microsoft#641, microsoft#635 (reproduced on Apple M6 /
Apple clang 21.0.0, see #2).
…d_mul_mat

ggml-cpu.c #undefs GGML_USE_LLAMAFILE when __ARM_FEATURE_MATMUL_INT8 or
__ARM_FEATURE_SVE is defined, which compiles out the only declaration of
src1_cont. The I2_S GEMV/GEMM fast path uses it unconditionally, so every
arm64 CPU with i8mm (M2 and later, Graviton3, ...) fails with:

  ggml-cpu.c:1495:40: error: use of undeclared identifier 'src1_cont'

The submodule (isHuangXin/llama.cpp) is not ours to modify, so carry the
fix as patches/llama.cpp/0001-*.patch and apply it at configure time with
cmake/ApplyLlamaPatches.cmake (idempotent; BITNET_APPLY_LLAMA_PATCHES=OFF
to disable). The same one-line change is in isHuangXin/llama.cpp#1 and #6.

Refs microsoft#618, microsoft#682, microsoft#641 (reproduced on Apple M6 / Apple
clang 21.0.0, see #2).
…S backend)

bitnet.cpp's I2_S/TL1 kernels exist only for the CPU backend, but on macOS
ggml also builds the Metal and Accelerate (BLAS) backends, and both
interfere with I2_S models even when the user passes -ngl 0:

* Metal built, BLAS not: creating the llama_context aborts in
  ggml_backend_sched_split_graph:
    ggml.c:1789: GGML_ASSERT(view_src == NULL || data_size == 0 ||
      data_size + view_offs <= ggml_nbytes(view_src)) failed
* BLAS built: ggml_backend_blas_device_supports_op accepts MUL_MAT whose
  src0 has a to_float() (I2_S has one) once ne0, ne1 and ne10 are >= 32, so
  every prompt batch of >= 32 tokens is routed to ggml_backend_blas_graph_compute
  and the process dies with SIGSEGV (backtrace in the evidence log).

Default GGML_METAL and GGML_BLAS to OFF on Apple unless the user sets them.
Measured on an Apple M6 with a synthetic I2_S model: Metal+BLAS (old default)
crashes for >= 31-token batches; Metal OFF + BLAS OFF runs 1..300 tokens at
1 and 4 threads.

Refs microsoft#682 (SIGSEGV during prompt processing), microsoft#641 (Metal
crash on I2_S), #2
Metal+BLAS: SIGSEGV in ggml_backend_blas_graph_compute (>= 31 tokens); Metal only: GGML_ASSERT at context creation;
BLAS only: no crash, but I2_S batches >= 32 tokens return garbage (perplexity 4.4e27 vs 3.7e5 on the synthetic model with
the corrected kernels). The previous docs text implied a SIGSEGV for every BLAS build.

Refs #2
@eejd
eejd marked this pull request as ready for review November 1, 2026 20:19
@eejd
eejd merged commit abaf170 into main Nov 1, 2026
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.

1 participant