Skip to content

Fix #547: scalar fallback for i2_s dot kernels on x86 without AVX2#580

Open
amitpatole wants to merge 1 commit into
microsoft:mainfrom
amitpatole:fix/i2s-x86-no-avx2-fallback
Open

Fix #547: scalar fallback for i2_s dot kernels on x86 without AVX2#580
amitpatole wants to merge 1 commit into
microsoft:mainfrom
amitpatole:fix/i2s-x86-no-avx2-fallback

Conversation

@amitpatole

Copy link
Copy Markdown

Summary

Fixes #547i2_s inference produces garbage output (a repeating token loop such as GGGG… / !!!!…) on x86 CPUs without AVX2 (e.g. Ivy Bridge, or any build configured with -DGGML_AVX2=OFF), while standard quants like Q2_K work fine on the same hardware.

Root cause

The four ggml_vec_dot_i2_i8_s_* kernels in src/ggml-bitnet-mad.cpp are gated as:

#if defined(__AVX2__)
    ... // AVX2 implementation
#elif defined(__ARM_NEON)
    ... // NEON implementation
#endif   // <-- no #else

On an x86 target without AVX2, neither branch is compiled, so each kernel has an empty body and never writes its output s. The uninitialized dot-product results propagate into the softmax, and generation collapses to a constant token. This matches the reporter's environment exactly (AVX = 1 | AVX2 = 0 | MATMUL_INT8 = 0).

Fix

Add a portable scalar #else fallback to all four kernels (_1x1, _1xN, _Nx1, _1x4_32W) through a shared helper:

static inline int ggml_i2_s_block_dot(const uint8_t * px, const int8_t * py, int nb);

It reproduces the AVX2 ternary unpack bit-for-bit: 2-bit weight codes are extracted at bit offsets 6/4/2/0 and paired with activation sub-blocks 0/1/2/3 (32 lanes each), summed into int32. QK_I2_S is also defined for the no-SIMD case so the 128-element block layout is interpreted identically. No behavioural change on AVX2 or NEON targets — the new code only compiles when neither SIMD path is available.

Verification

1. Bit-exactness vs AVX2 (unit). tests/test-i2s-scalar-fallback.cpp re-implements the exact AVX2 kernels as ground truth and cross-checks the scalar fallback over 7163 random cases across all four kernels — 0 mismatches.

g++ -O2 -mavx2 tests/test-i2s-scalar-fallback.cpp -o /tmp/t && /tmp/t
checks=7163 fails=0 -> ALL BIT-EXACT MATCH

2. End-to-end (real model). Built with -DGGML_AVX2=OFF (AVX = 1 | AVX2 = 0 | SSSE3 = 1 | MATMUL_INT8 = 0) and ran BitNet-b1.58-2B-4T (ggml-model-i2_s.gguf):

Output for prompt "The capital of France is"
Before (main) The capital of France is!!!!!!!!!!!!!!!!!!!!!!!!…
After (this PR) The capital of France is Paris. Paris is a city that is known for its rich history, culture, and architecture. It is also a major center for art, fashion, and cuisine…

Notes

…thout AVX2

The ggml_vec_dot_i2_i8_s_* kernels were gated on
`#if defined(__AVX2__) ... #elif defined(__ARM_NEON)` with no other
branch. On x86 CPUs without AVX2 (e.g. Ivy Bridge, or any build with
-DGGML_AVX2=OFF) neither branch is compiled, so the kernels have an
empty body and never write the output `s`. The uninitialized result
makes i2_s inference emit a garbage token loop ("GGGG..."), while
non-BitNet quants like Q2_K work fine on the same hardware.

Add a portable scalar `#else` fallback to all four kernels
(_1x1, _1xN, _Nx1, _1x4_32W) via a shared ggml_i2_s_block_dot() helper
that reproduces the AVX2 ternary unpack bit-for-bit (2-bit codes at bit
offsets 6/4/2/0 paired with activation sub-blocks 0/1/2/3). Also define
QK_I2_S for the no-SIMD case so the block layout matches.

Verified:
- tests/test-i2s-scalar-fallback.cpp cross-checks the scalar fallback
  against the exact AVX2 kernels over 7163 random cases: bit-exact.
- Built BitNet with -DGGML_AVX2=OFF (AVX=1, AVX2=0, MATMUL_INT8=0) and
  ran BitNet-b1.58-2B-4T i2_s:
    before: "The capital of France is!!!!!!!!!!!!!..."  (garbage)
    after:  "The capital of France is Paris. Paris is a city known for
             its rich history, culture, and architecture..."  (coherent)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@amitpatole

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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.

[Bug]: i2_s quantization produces garbage output on x86 CPUs without AVX2 (Missing fallback)

1 participant