fix(s390x): guard the VXE-only helpers upstream left at file scope - #432
Merged
Conversation
The Linux s390x job has been red since b10902 landed, with nothing in this
project involved. Upstream #28667 added a new source file,
ggml/src/ggml-cpu/arch/s390/repack.cpp (absent at b10883), which
ggml-cpu/CMakeLists.txt compiles unconditionally for s390x. Every function
body in it is guarded #if defined(__VXE__) || defined(__VXE2__), but three
static inline helpers -- vxe_dot_acc, vxe_splat_granule, vxe_fold -- sit at
file scope between two guarded blocks with no guard of their own, and their
signatures name int16x8_t / int8x16_t / int32x4_t, which ggml-cpu-impl.h only
typedefs inside that same guard. A non-VXE s390x build therefore dies with
three "does not name a type" errors before reaching any of the code it is
meant to skip. patches/0013 wraps the three definitions in the identical
guard; all 21 call sites are already inside __VXE__ blocks, so nothing moves.
Why this build is non-VXE is the half that is ours, and it is not obvious:
ggml declares option(GGML_VXE "ggml: enable vxe" ${GGML_NATIVE}), so the VXE
default follows GGML_NATIVE. The job passes -DGGML_NATIVE=OFF, which is
correct for a cross-build -- an x86 host must not bake -march=native into an
s390x artifact -- and silently switches VXE off with it. The job has always
produced a scalar s390x binary, which is right for what it is (a big-endian
correctness gate for our own layer, not a performance target); it was simply
invisible until a file arrived that does not compile that way. A comment on
the build step now says so.
The flag-side alternatives were measured with the real cross toolchain, not
reasoned about. -DGGML_VXE=ON alone is strictly worse: the self-define sets
__VXE__ and __VXE2__ together as soon as __VEC__ exists, while -march stays
at the toolchain default arch11, so three errors become dozens of
"'__builtin_s390_vec_*' matching variant requires z14 or higher". Adding
-march=z15 on top does compile, but raises the shipped artifact's hardware
floor to z15 and makes the qemu ctest gate depend on VXE2 emulation -- a real
trade for vector kernels this job never uses. The patch keeps exactly the
configuration that worked through b10883.
Verified: unpatched + the job's own flags reproduces the three CI errors
verbatim; patched + those flags compiles clean; patched + -mvx -mzvector
-march=z15 also compiles clean, so a future vector build is not foreclosed.
Fresh configure applies all ten patches (stamp head 481c65f0), full Release
build clean, ctest 537/537, NativeLibraryLoadSmokeTest 4/4 after a clean.
The docs/history row for b10883-b10902 carried a claim that this disproves --
"the range does not touch our layer" was right about behaviour and wrong
about the build -- and is corrected in place rather than left to mislead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AnNYn8W1xuVxVJtyL34GyH
bernardladenthin
had a problem deploying
to
maven-central
September 11, 2026 08:41 — with
GitHub Actions
Failure
bernardladenthin
had a problem deploying
to
maven-central
September 11, 2026 08:41 — with
GitHub Actions
Failure
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Build and Test Linux s390xfails to compile onmain, with no line of this project's code involved. Seen on run #940 (workflow_dispatchona56829a).Why it appears now
build-linux-s390xjob is untouched since38f00b2, 2026-07-14.ggml/src/ggml-cpu/arch/s390/repack.cppat b10902 (#28667, s390x q4_0 repack).git cat-file -econfirms: absent at b10883, present at b10902 and b10903.ggml-cpu/CMakeLists.txtcompiles it unconditionally for s390x.This is the s390x work I flagged in #430's description as "worth knowing for the s390x job" — the flag was right, the conclusion drawn from it ("the range does not touch our layer") was right about behaviour and wrong about the build. That claim is corrected in
docs/history/rather than left to mislead the next reader.The defect
Every function body in
repack.cppis guarded#if defined(__VXE__) || defined(__VXE2__). Threestatic inlinehelpers —vxe_dot_acc,vxe_splat_granule,vxe_fold— sit at file scope between two guarded blocks with no guard of their own, and their signatures nameint16x8_t/int8x16_t/int32x4_t, whichggml-cpu-impl.honly typedefs inside that same guard. So a non-VXE s390x build dies before reaching any of the code it is supposed to skip:patches/0013wraps the three definitions in the identical guard. All 21 call sites (lines 132–210) are already inside__VXE__blocks, so nothing else moves — the patch is two added lines.Why our s390x build is non-VXE — the half that is ours
Not obvious, and it is what made this reachable:
The VXE default follows
GGML_NATIVE. The job passes-DGGML_NATIVE=OFF, which is correct for a cross-build — an x86 build host must not bake-march=nativeinto an s390x artifact — and silently switches VXE off with it. No-mvx -mzvectoris passed,__VEC__stays undefined, andggml-cpu-impl.h's#if defined(__s390x__) && defined(__VEC__)self-define of__VXE__/__VXE2__never fires. (__VXE__is not a GCC predefined macro — verified with-dM -E, no-marchfrom z13 to z16 defines it; ggml defines it itself.)So this job has always produced a scalar s390x binary. That is exactly right for what it is — a big-endian correctness gate for our own layer (WAV writer, JSON/token/embedding transforms, JNI helpers), not a performance target — and it was simply invisible until a file arrived that does not compile that way. A comment on the build step now says so, so the next person does not "fix" it the wrong way.
Which solution is sensible — measured, not argued
All four run through the real cross toolchain (
s390x-linux-gnu-g++), compiling the actual file:-DGGML_VXE=ON__VXE__and__VXE2__together as soon as__VEC__exists, while-marchstays at the toolchain defaultarch11→ three errors become dozens of'__builtin_s390_vec_*' matching variant requires z14 or higher-DGGML_VXE=ONplus-march=z15ctestgate depend on VXE2 emulation, for vector kernels this job never usesD keeps exactly the configuration that worked through b10883, changes nothing about the shipped artifact, fixes a genuine upstream defect, and does not foreclose a future vector build. B is off the table on evidence, not preference.
Test plan
Tested locally
Added/updated tests — no new test.
build-linux-s390xis the test: the file is compiled only for s390x, so a dropped or stale patch fails loudly at compile time. Noted as such in the patch'sCLAUDE.mdrow.CI passes
Reproduction first, as the discipline requires: unpatched + the job's own flags → the three errors above, byte for byte.
Then the same compile passing: patched + those flags → exit 0; patched +
-mvx -mzvector -march=z15→ exit 0.Applier: fresh
rm -rf build && cmake -B build -DBUILD_TESTING=ON, configure clean, stamp at head481c65f091f74c5e7089dd0a3a1cc6b50cced31ewith all ten patch SHA-256 lines. The s390x re-verification above was re-run on the applier's output, not my hand edit.Native x86: full
cmake --build --config Releaseclean;ctest537/537.JNI:
mvn -pl llama clean test -Dtest=NativeLibraryLoadSmokeTest4/4, 0 skipped.publish.ymlstill parses (yaml.safe_load).No
.javaand no project C++ changed, so spotless / SpotBugs / clang-format have no input.Related issues
None. Upstream-submittable as "ggml-cpu: guard the VXE-only helpers in the s390x repack path"; not yet filed upstream.
Checklist
mvn spotless:applyrun for Java changes) — no Java changed.patches/0013row inCLAUDE.md(mechanism, theGGML_NATIVE→GGML_VXEcoupling, the measured flag comparison, and the standing drop-check); theb10883–b10902row indocs/history/llama-cpp-breaking-changes.mdcorrected in place; an explanatory comment on the s390x build step.Files changed (4)
llama/patches/0013-s390x-repack-guard-vxe-only-helpers.patchCLAUDE.md0013docs/history/llama-cpp-breaking-changes.mdb10883–b10902row.github/workflows/publish.ymlCHANGELOG.mdis deliberately untouched: it records consumer-visible behaviour, and this is a build fix for a pin bump that is itself not recorded there.🤖 Generated with Claude Code
https://claude.ai/code/session_01AnNYn8W1xuVxVJtyL34GyH
Generated by Claude Code