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 · Issue #641 · microsoft/BitNet · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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 #641
[ 2%] Linking CXX shared library ../../../../bin/libggml-base.dylib
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
ld: symbol(s) not found for architecture arm64
Cause: ggml/src/ggml.c (target ggml-base) refers to quantize_i2_s and dequantize_row_i2_s, but both are defined in ggml/src/ggml-cpu/quants.c (target ggml-cpu), which ggml-base doesn't link against. setup_env.py builds shared libraries by default. On Linux an ELF shared library can have undefined symbols that are resolved at load time. macOS (ld64) won't allow that, and Windows/MSVC won't either (probably #595).
Workaround: build static libraries:
cmake -B build -DBITNET_ARM_TL1=OFF -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
-DLLAMA_BUILD_TOOLS=ON -DLLAMA_BUILD_EXAMPLES=ON -DLLAMA_BUILD_COMMON=ON -DLLAMA_BUILD_SERVER=ON
-DBUILD_SHARED_LIBS=OFF -DGGML_BACKEND_DL=OFF
Suggested fixes: move the i2_s quantize/dequantize code into ggml-quants.c (which is part of ggml-base), or have setup_env.py pass -DBUILD_SHARED_LIBS=OFF.
Metal backend crashes on I2_S
With the default GPU offload, llama-cli aborts inside ggml_metal_op_encode during the first llama_decode. Passing -ngl 0 avoids the crash. Either the Metal backend should refuse I2_S tensors, or run_inference.py should pass -ngl 0 by default.
$ build/bin/llama-completion -m models/BitNet-b1.58-2B-4T/ggml-model-i2_s.gguf
-p "The capital of France is" -n 24 --temp 0 -ngl 0 -no-cnv
The capital of France is@@@@@@@@@@@@@@@@@@@@@@@@
The output is the same every run, with -t 1 and with -t 8.
Speed is about 1.3 tok/s with 1 thread and about 10 tok/s with 8 threads. That's far below what this model normally runs at.
This matches #600 (wrong I2_S unpacking and scaling in the ARM path). #588 (SiLU used instead of squared ReLU in the FFN) also affects every backend.
The repeated @ tokens are basically the ReLU2 mismatch - BitNet b1.58 relies on squared ReLU, but if your FFN config defaults to SiLU, logits explode after the first decode step and the model collapses into repeating high-frequency tokens. Also, Metal doesn't have ternary kernels yet. Any tensor offloaded to GPU will hit an unhandled op and segfault in ggml_metal_op_encode unless you pass -ngl 0 to keep it on CPU.
If you just want a bare-bones reference to verify your weights and I2_S unpacking without wading through bitnet.cpp's dynamic library splits and CMake setup, we wrote a small C99 engine (https://github.com/shifulegend/project-zero). Compiles with a plain make on macOS and runs the b1.58 ReLU2 path directly on CPU.
Activity
shifulegend commentedon Oct 11, 2026
The repeated @ tokens are basically the ReLU2 mismatch - BitNet b1.58 relies on squared ReLU, but if your FFN config defaults to SiLU, logits explode after the first decode step and the model collapses into repeating high-frequency tokens. Also, Metal doesn't have ternary kernels yet. Any tensor offloaded to GPU will hit an unhandled op and segfault in ggml_metal_op_encode unless you pass -ngl 0 to keep it on CPU.
If you just want a bare-bones reference to verify your weights and I2_S unpacking without wading through bitnet.cpp's dynamic library splits and CMake setup, we wrote a small C99 engine (https://github.com/shifulegend/project-zero). Compiles with a plain make on macOS and runs the b1.58 ReLU2 path directly on CPU.