From 3cdde79cc40e5768a0fbc51366f3140286b7f50c Mon Sep 17 00:00:00 2001 From: Pascal Wachowski Date: Tue, 11 Aug 2026 08:20:32 +0200 Subject: [PATCH] ggml: fix MSVC linkage conflict on the TurboQuant vec_dot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ggml_vec_dot_turbo3_0 and _turbo4_0 were declared twice: in ggml-quants.h with GGML_API, and in ggml-cpu/quants.h without it. ggml-cpu/quants.c includes both, so MSVC saw one declaration with __declspec and one without and rejected it: ggml-quants.h(112,15): error C2375: 'ggml_vec_dot_turbo3_0': redefinition; different linkage gcc and clang accept this silently, which is why it only ever showed up in the Windows job of an inherited upstream workflow. GGML_API is the correct attribute here, not the spurious one: unlike every other vec_dot, TurboQuant's is defined in ggml-quants.c, which builds into ggml-base, while its only caller is the type-traits table in ggml-cpu — a different shared library. So the declaration without the attribute is the one that had to go, and ggml-cpu.c now takes it from ggml-quants.h directly. Adds a Windows MSVC job to the fork's CI. This class of error is invisible to both existing jobs, and windows-latest is free on a public repo. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Q5jMcvdevae8j4T36C9h2z --- .github/workflows/turboquant-ci.yml | 26 ++++++++++++++++++++++++++ ggml/src/ggml-cpu/ggml-cpu.c | 1 + ggml/src/ggml-cpu/quants.h | 8 ++++---- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/workflows/turboquant-ci.yml b/.github/workflows/turboquant-ci.yml index 844c1a8b5..705622eed 100644 --- a/.github/workflows/turboquant-ci.yml +++ b/.github/workflows/turboquant-ci.yml @@ -62,6 +62,32 @@ jobs: - name: Backend ops (setup + quantization paths) run: ./build/bin/test-backend-ops -o FLASH_ATTN_EXT + # MSVC rejects things gcc and clang accept silently. A duplicate declaration of + # the TurboQuant vec_dot — once with GGML_API, once without — was a hard error + # here (C2375, different linkage) and invisible everywhere else. Cheap on a + # public repo, and it covers a compiler neither other job does. + windows: + name: Windows MSVC compile check + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Configure + run: | + cmake -B build ` + -DGGML_NATIVE=OFF ` + -DLLAMA_BUILD_TESTS=ON ` + -DLLAMA_BUILD_EXAMPLES=OFF ` + -DLLAMA_BUILD_SERVER=OFF + + # ggml-cpu is the target the linkage conflict broke; test-turboquant pulls + # in the quantization code itself and can actually run here. + - name: Build + run: cmake --build build --config Release --target ggml-cpu test-turboquant + + - name: TurboQuant CPU reference tests + run: ./build/bin/Release/test-turboquant.exe + cuda: name: CUDA compile check runs-on: ubuntu-latest diff --git a/ggml/src/ggml-cpu/ggml-cpu.c b/ggml/src/ggml-cpu/ggml-cpu.c index 4d6ead497..13a5e547c 100644 --- a/ggml/src/ggml-cpu/ggml-cpu.c +++ b/ggml/src/ggml-cpu/ggml-cpu.c @@ -6,6 +6,7 @@ #include "traits.h" #include "ggml-cpu-impl.h" #include "ggml-impl.h" +#include "ggml-quants.h" // TurboQuant vec_dot lives in ggml-base #include "quants.h" #include "ggml-threading.h" #include "unary-ops.h" diff --git a/ggml/src/ggml-cpu/quants.h b/ggml/src/ggml-cpu/quants.h index cb82824a9..6629bdf9a 100644 --- a/ggml/src/ggml-cpu/quants.h +++ b/ggml/src/ggml-cpu/quants.h @@ -42,10 +42,10 @@ void ggml_vec_dot_q5_0_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const voi void ggml_vec_dot_q5_1_q8_1(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc); void ggml_vec_dot_q8_0_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc); -// TurboQuant. Defined in ggml-quants.c (not arch-specific): dequantizes the -// left-hand side internally, so the right-hand side is plain f32. -void ggml_vec_dot_turbo3_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc); -void ggml_vec_dot_turbo4_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc); +// TurboQuant's vec_dot is declared in ggml-quants.h instead of here. It is the +// only one defined in ggml-quants.c (ggml-base) rather than in this library, so +// it needs GGML_API to cross the library boundary. Declaring it here as well — +// without that attribute — is a linkage conflict MSVC rejects outright. void ggml_vec_dot_mxfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc); void ggml_vec_dot_nvfp4_q8_0(int n, float * GGML_RESTRICT s, size_t bs, const void * GGML_RESTRICT vx, size_t bx, const void * GGML_RESTRICT vy, size_t by, int nrc);