diff --git a/.github/buildcheck/nativedeps.py b/.github/buildcheck/nativedeps.py index abd80f26..deebeafe 100755 --- a/.github/buildcheck/nativedeps.py +++ b/.github/buildcheck/nativedeps.py @@ -86,8 +86,9 @@ def ucrt_forwarder(name): # What each CPU library needed when this check was introduced (5.1.0 plus the RPC backend, # which adds nothing: its sockets are libc/libSystem/WS2_32, all already present). The manylinux_2_28 # builds (glibc 2.28, before the libpthread/libdl/librt merge of 2.34) name those three separately; -# s390x is one of them since its build moved to the manylinux_2_28 sysroot (2026-10-25), and its -# single static library needs libpthread and libdl, not librt (measured). +# s390x is one of them since its build moved to the manylinux_2_28 sysroot (2026-10-25); its modular +# directory (6.1.0) needs libpthread (libjllama, libmtmd, libggml-base) and libdl (libggml), not +# librt (measured), as did the single static library before it. # libgomp.so.1 is deliberately absent from the three Linux cpu entries: llama/CMakeLists.txt forces # GGML_OPENMP=OFF there so the jar loads in slim and distroless images, which do not ship it, and # this list is what keeps it that way -- a build that regains the dependency fails `package` instead diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f9c6c24c..35c0551b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -886,24 +886,25 @@ jobs: path: ${{ github.workspace }}/llama/src/main/natives/net/ladenthin/llama/ build-linux-s390x: - name: Build and Test Linux s390x (big-endian, qemu) + name: Build Linux s390x (big-endian, CPU variants) needs: [startgate, build-webui] - # Cross-compile for IBM Z (s390x, BIG-ENDIAN), then run the full C++ unit suite under qemu-user -- - # a real big-endian correctness gate for our helpers and serializers (esp. the little-endian WAV - # writer, JSON/token/embedding transforms). The BUILD is native speed: clang on the x86-64 runner - # against the manylinux_2_28 s390x image unpacked as the sysroot (.github/manylinux/ + # Cross-compile the cpu-linux-s390x library jar for IBM Z (s390x, BIG-ENDIAN) as a modular build + # (JLLAMA_BACKEND_DL, CLAUDE.md "Linux s390x"): libjllama, libllama, libmtmd, ggml and one CPU + # module per IBM Z level -- ggml-cpu-z13 (scalar, the baseline patches/0023 adds), -z15 (VXE2) + # and -z16 (VXE2 + NNPA) -- of which ggml loads the best for the running machine. Up to 6.0.x + # the jar was one static, scalar library: 4.5x slower prompt processing and 4x slower generation + # on a z16 (measured on cfarm191, patches/0023). The BUILD is native speed: clang on the x86-64 + # runner against the manylinux_2_28 s390x image unpacked as the sysroot (.github/manylinux/ # manylinux_2_28-s390x-sysroot and s390x-toolchain.cmake), linked by binutils-s390x-linux-gnu, so # the jar has the floor of the other Linux CPU jars -- glibc 2.28, GLIBCXX_3.4.25 -- which # verify-native-deps.py holds and smoke-glibc-floor proves on RHEL 8. (Until 2026-10-25 the job # used the runner's GCC 13 cross toolchain and its glibc 2.39 sysroot: the 6.0.0 jar needed glibc - # 2.38 and GCC 13's libstdc++, CLAUDE.md "Linux s390x".) Only the test binary is emulated, and it - # runs against the sysroot's glibc 2.28 and GCC 8 libstdc++.so.6, the oldest runtime the jar - # promises. s390x is a CPU platform like aarch64 and ships as the cpu-linux-s390x natives jar. - # Model-backed Java tests are NOT run under emulation (a JVM + GGUF inference under qemu-user is - # slow/flaky); the C++ gate covers the actual byte-order risk since the Java<->JNI boundary uses - # host-native array copies. GGML_OPENMP=OFF avoids cross-libgomp issues (ggml uses its own - # std::thread pool). The toolchain's CMAKE_CROSSCOMPILING_EMULATOR runs the s390x executables - # through qemu with QEMU_LD_PREFIX pointing at the sysroot. + # 2.38 and GCC 13's libstdc++, CLAUDE.md "Linux s390x".) build.sh runs the plain-C test c_abi + # through the toolchain's qemu (CMAKE_CROSSCOMPILING_EMULATOR, QEMU_LD_PREFIX at the sysroot): + # upstream's C API out of the shipped directory, with ggml scoring the IBM Z modules against + # qemu's CPU model. The C++ unit suite cannot link against a modular build; it runs in + # test-cpp-linux-s390x, under qemu as well. GGML_OPENMP=OFF avoids cross-libgomp issues (ggml + # uses its own std::thread pool). # Pinned to ubuntu-24.04: the toolchain names its clang-18. runs-on: ubuntu-24.04 env: @@ -930,22 +931,26 @@ jobs: .github/manylinux/manylinux_2_28-s390x-sysroot "$RUNNER_TEMP/s390x-sysroot" echo "JLLAMA_S390X_SYSROOT=$RUNNER_TEMP/s390x-sysroot" >> "$GITHUB_ENV" # NOTE: GGML_NATIVE=OFF is required here (an x86 build host must not bake -march=native into an - # s390x artifact), and it has a non-obvious second effect: ggml declares - # `option(GGML_VXE "ggml: enable vxe" ${GGML_NATIVE})`, so VXE is off too. No `-mvx -mzvector` is - # passed, `__VEC__` stays undefined, and ggml-cpu-impl.h's `#if defined(__s390x__) && defined(__VEC__)` - # never self-defines `__VXE__`/`__VXE2__`. This job therefore builds a SCALAR s390x binary -- which is - # exactly right for what it is (a big-endian correctness gate for our own layer, not a perf target). - # Do NOT "fix" a VXE-related compile error by adding -DGGML_VXE=ON: that define sets __VXE__ and - # __VXE2__ together while -march stays at z13 (arch11, s390x-toolchain.cmake), so every z14+ - # builtin is rejected. See the `0013` note in docs/history/dropped-llama-patches.md for the measured - # comparison (the patch itself is gone -- upstream merged it as ggml-org/llama.cpp#28775). + # s390x artifact). The vector code comes from GGML_CPU_ALL_VARIANTS instead (set by + # JLLAMA_BACKEND_DL): each variant module carries its own -march=z15/z16 -mvx -mzvector, while + # ggml-base, libllama and libjllama stay at the toolchain's -march=z13. Do NOT pass + # -DGGML_VXE=ON: ggml-cpu/CMakeLists.txt adds VXE2 to every variant when it is set, so the z13 + # module would become a VXE2 one, score 0 without VXE2, and leave a z13/z14 without any CPU + # backend. - name: Build libraries (cross-compile s390x) shell: bash run: | mvn --no-transfer-progress -f llama/pom.xml compile - .github/build.sh "-DGGML_NATIVE=OFF -DGGML_OPENMP=OFF -DBUILD_TESTING=ON -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/.github/manylinux/s390x-toolchain.cmake -DOS_NAME=Linux -DOS_ARCH=s390x" - - name: Run C++ unit tests under qemu-s390x (big-endian gate) - run: ctest --test-dir llama/build --no-tests=error --output-on-failure + .github/build.sh "-DGGML_NATIVE=OFF -DGGML_OPENMP=OFF -DJLLAMA_BACKEND_DL=ON -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/.github/manylinux/s390x-toolchain.cmake -DOS_NAME=Linux -DOS_ARCH=s390x" + # A module ggml could not build is a missing file, not a failure ggml reports: the configure + # skips a variant whose flag the compiler rejects (as it does z17 with clang 18). z13 is the + # only one every IBM Z the jar supports can load, and comes from patches/0023. + - name: Check the IBM Z CPU modules + run: | + d=llama/src/main/natives/net/ladenthin/llama/Linux/s390x/cpu + for v in z13 z15 z16; do + test -f "$d/libggml-cpu-$v.so" || { echo "::error::$d has no libggml-cpu-$v.so"; ls -la "$d"; exit 1; } + done # The same check `package` runs over the merged tree, here so a lost floor fails in this job: # GLIBC_2.28, GLIBCXX_3.4.25, CXXABI_1.3.11, the exact dependency list, and every typeinfo name # at an even address (s390x-typeinfo-names.ld + -fdata-sections; the link alone fails only when @@ -2706,6 +2711,48 @@ jobs: - name: Run C++ unit tests run: ctest --test-dir llama/build --no-tests=error --output-on-failure + test-cpp-linux-s390x: + name: C++ Tests Linux s390x (big-endian, qemu) + needs: startgate + # The full C++ unit suite on IBM Z (BIG-ENDIAN) under qemu-user -- the big-endian correctness + # gate for our helpers and serializers (the little-endian WAV writer, the JSON/token/embedding + # transforms, the JNI helpers). Same toolchain as build-linux-s390x (clang-18 against the + # manylinux_2_28 sysroot, s390x-toolchain.cmake), but the default static build: the shipped CPU + # variants configuration cannot host jllama_test (its CPU and RPC backends are modules), the + # same split as test-cpp-linux-x86_64 / -aarch64. The test binary runs against the sysroot's + # glibc 2.28 and GCC 8 libstdc++.so.6, the oldest runtime the jar promises. Model-backed Java + # tests are NOT run under emulation (a JVM + GGUF inference under qemu-user is slow and flaky); + # the Java<->JNI boundary uses host-native array copies, so this gate covers the byte-order risk. + # Until 6.1.0 the suite rode along in build-linux-s390x, whose library was static then. + runs-on: ubuntu-24.04 + env: + SCCACHE_WEBDAV_TOKEN: ${{ secrets.DEPOT_TOKEN }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - name: Display CPU Info + uses: bernardladenthin/actions/common/print-host-info@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0 + - uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6 + with: + distribution: 'temurin' + java-version-file: .java-version + - name: Install clang, the s390x binutils and qemu-user + run: | + sudo apt-get update + sudo apt-get install -y clang-18 binutils-s390x-linux-gnu qemu-user-static + - name: Unpack the manylinux_2_28 s390x sysroot + run: | + .github/manylinux/manylinux_2_28-s390x-sysroot "$RUNNER_TEMP/s390x-sysroot" + echo "JLLAMA_S390X_SYSROOT=$RUNNER_TEMP/s390x-sysroot" >> "$GITHUB_ENV" + - name: Build libraries (cross-compile s390x) + shell: bash + run: | + mvn --no-transfer-progress -f llama/pom.xml compile + .github/build.sh "-DGGML_NATIVE=OFF -DGGML_OPENMP=OFF -DBUILD_TESTING=ON -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/.github/manylinux/s390x-toolchain.cmake -DOS_NAME=Linux -DOS_ARCH=s390x" + - name: Verify llama.cpp patches are applied + run: .github/verify-patches-applied.sh + - name: Run C++ unit tests under qemu-s390x (big-endian gate) + run: ctest --test-dir llama/build --no-tests=error --output-on-failure + test-cpp-windows-x86_64: name: C++ Tests Windows 2025 x86_64 (clang) needs: startgate @@ -3140,6 +3187,7 @@ jobs: - build-windows-x86_64-openvino - test-cpp-linux-x86_64 - test-cpp-linux-aarch64 + - test-cpp-linux-s390x - test-cpp-windows-x86_64 - test-cpp-windows-arm64 - build-macos-arm64-metal-15 diff --git a/CHANGELOG.md b/CHANGELOG.md index 01270b2e..f4c2f671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,14 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by measures as faster grammar sampling. Metal no longer overflows the output offset of very large matrix multiplications (#30072), the SYCL module bounds its reorder buffer to 32 MiB (#31062), DeepSeek-V4.1 review fixes (#31137), WebUI layout work (#31030, #31119). No patch changed. +- **`cpu-linux-s390x` uses the IBM Z vector unit.** The jar was one static, scalar library; it is a + modular one now, like the other Linux CPU jars, with three CPU modules of which the best for the + machine is loaded at start: `z13` (scalar, the floor stays z13), `z15` (VXE2) and `z16` (VXE2 + + NNPA). On a z16 (LinuxONE, Qwen3-0.6B Q4_K_M, 4 threads) prompt processing went from 19.7 to + 82.6-95.5 tokens per second and generation from 13.6 to 51.9-58.3. New patch `0023`: clang could + not build ggml's IBM Z vector code (a duplicate `` include that only GCC's header + guard hid), and upstream's variant list had no module for machines older than z15. The C++ test + suite for s390x runs in its own job (`test-cpp-linux-s390x`), still under qemu. - **`bernardladenthin/actions` pinned at v1.1.0.** v1.1.0 adds `java/audit-publication`, which `post-release.yml` uses (see Added); nothing changes for the actions already in use. diff --git a/CLAUDE.md b/CLAUDE.md index c7c8c529..9d3771c9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -103,7 +103,7 @@ it or is checked against it: | `.github/merge-native-artifacts.sh` | reads the list: every listed `natives-*` artifact present, no other, each holding its library (or module) and nothing outside its directory, each with a `jllama-build.txt` naming a tag, the provenance keys and the row's classifier, next to a `jllama-sbom.json` naming the same classifier, a module directory holding nothing but its module, the `jllama-*.txt` lists and the BOM, no path claimed twice; writes `jllama-extras.txt` (sibling files loaded before the library, e.g. OpenVINO's `OpenCL.dll` on Windows -- never the files a build's own `jllama-files.txt` names, which are only extracted; each of those must exist) | | `.github/package-smoke-sets.sh` | reads the list through `check-natives.py smoke-targets` / `smoke-set`: fails when a natives jar of a set was not built; also writes the glibc-floor set (`floor-set`) | | the glibc-floor set | **derived**: the library rows whose directory promises a glibc floor (`nativedeps.GLIBC_CEILING`: the three Linux CPU jars) — `check-natives.py floor-set` prints them; `package` uploads them with the classes jar and its dependencies as `llama-glibc-floor`, and `smoke-glibc-floor` loads each on RHEL 8.10 (glibc 2.28, GCC 8's libstdc++) on its own architecture, s390x under qemu (`smoke-glibc-floor.sh`, `NativeLoadSmoke.java` in the UBI 8 OpenJDK 21 runtime image); `check-natives.py` holds the job's matrix rows to the set | -| `.github/verify-native-deps.py` | every library of a directory: exact dependency allowlist per library directory (`cpu`/`metal`) plus the files next to it; **the export set of every `jllama` library** (`JNI_OnLoad`, `JNI_OnUnload`, `Java_*`, `jllama_*` and nothing else -- `cmake/jllama-exports.map` on ELF and `cmake/jllama-exports-macos.txt` on macOS keep the statically linked llama/ggml/mtmd/BoringSSL symbols local, Windows exports only `dllexport`; before the lists the Linux library exported 14,220 symbols, 47 of them JNI), and for the Android OpenCL module (bionic + `libOpenCL.so` + `libggml-base.so`); denylist for the other GPU modules, each of which must import `ggml-base` and, as ELF, reach it only through run path `$ORIGIN` (an Android library carries no run path -- bionic's search path is the app's native-library directory -- and only a build-tree one fails there); the manylinux_2_28 builds (both Linux CPU jars, CUDA) held to `GLIBC_2.28`; every Android library 16 KB page-aligned (Google Play); every typeinfo name (`_ZTS*`) of the s390x library at an even address (see "Linux s390x"). Runs in `package` and on the staged AAR libraries | +| `.github/verify-native-deps.py` | every library of a directory: exact dependency allowlist per library directory (`cpu`/`metal`) plus the files next to it; **the export set of every `jllama` library** (`JNI_OnLoad`, `JNI_OnUnload`, `Java_*`, `jllama_*` and nothing else -- `cmake/jllama-exports.map` on ELF and `cmake/jllama-exports-macos.txt` on macOS keep the statically linked llama/ggml/mtmd/BoringSSL symbols local, Windows exports only `dllexport`; before the lists the Linux library exported 14,220 symbols, 47 of them JNI), and for the Android OpenCL module (bionic + `libOpenCL.so` + `libggml-base.so`); denylist for the other GPU modules, each of which must import `ggml-base` and, as ELF, reach it only through run path `$ORIGIN` (an Android library carries no run path -- bionic's search path is the app's native-library directory -- and only a build-tree one fails there); the manylinux_2_28 builds (both Linux CPU jars, CUDA) held to `GLIBC_2.28`; every Android library 16 KB page-aligned (Google Play); every typeinfo name (`_ZTS*`) of the s390x libraries at an even address (see "Linux s390x"). Runs in `package` and on the staged AAR libraries | | `.github/smoke-natives-jars.sh` (`package` job) | loads the real jars: classes + all 23 natives jars at once, on the classpath **and** the module path; it passes `net.ladenthin.llama.backend` naming **every** module of the runner's platform, opt-in ones included, and the loader line must then name each -- so a module jar the loader skipped fails here, and this is also the only place in CI that loads an opt-in module at all | | `.github/smoke-lib.sh` / `smoke-test-natives.ps1` | the model-backed smoke rows expect every module of the set **except** the opt-in ones (`optin` column), because the loader deliberately leaves those out -- it is the same list, read with one more condition | @@ -141,7 +141,7 @@ step, not by Maven. ## Modular natives: `JLLAMA_BACKEND_DL` (library jars) and `JLLAMA_MODULE_ONLY` (GPU module jars) Every platform a GPU backend can join is built **modular**, the way upstream builds its own release -binaries (`GGML_BACKEND_DL`): ggml and ggml-base as shared libraries, every backend a module ggml +binaries (`GGML_BACKEND_DL`), and so is s390x, for its CPU variants (see "Linux s390x"): ggml and ggml-base as shared libraries, every backend a module ggml loads at run time -- the CPU backend as one small `ggml-cpu-` module per instruction-set level where ggml has a variant list (`GGML_CPU_ALL_VARIANTS`: Linux and Android x86-64 / aarch64, Windows x86-64), of which `ggml_backend_load_best` loads the best for the running CPU, and as one @@ -162,9 +162,9 @@ Windows x86-64), of which `ggml_backend_load_best` loads the best for the runnin configure built, so its ABI is the library jar's of the same tag (what `jllama-build.txt` pins). Upstream's GPU zips do the same (`--target ggml-cuda` etc., Vulkan/OpenCL with `GGML_CPU=OFF`). -**What stays static**: s390x (`BUILD_SHARED_LIBS OFF`, one `libjllama.so` with ggml linked in, no GPU -can join it) and macOS (`metal-macos-aarch64`: Metal is compiled into the library, upstream ships -macOS static too). There the loader extracts and loads a single file (plus `jllama-extras.txt` and a +**What stays static**: macOS (`metal-macos-aarch64`: Metal is compiled into the library, upstream +ships macOS static too). s390x was static until 6.1.0 (one scalar `libjllama.so`, no GPU can join +it) and is modular now for its IBM Z CPU variants. There the loader extracts and loads a single file (plus `jllama-extras.txt` and a `ggml-metal.metal` a build does not embed). **Every directory carries `jllama-build.txt`** (`llama.cpp=`, the `JLLAMA_GIT_TAG` number of this @@ -213,7 +213,7 @@ measurements behind the Windows step are in `docs/handover/local-agent-report-b1 its own block as before, shared in this build (`libmtmd.so`, `mtmd.h`). Both are unversioned like ggml and get the `$ORIGIN` run path; the configure refuses a `llama`/`mtmd` that came out static or a `llama-common`/`cpp-httplib` that came out shared (one more file to ship). On the static - platforms (macOS, s390x) all of it stays inside `libjllama`. **Every ggml default llama.cpp's own + platform (macOS) all of it stays inside `libjllama`. **Every ggml default llama.cpp's own CMakeLists changes before its `add_subdirectory(ggml)` is repeated** -- `GGML_LLAMAFILE_DEFAULT` and `GGML_CUDA_GRAPHS_DEFAULT` at b11901 (unchanged since b11639) -- because adding ggml first skips that block; re-check with `grep -n _DEFAULT CMakeLists.txt` at the new tag on every bump. The second one was missing once: @@ -227,7 +227,8 @@ measurements behind the Windows step are in `docs/handover/local-agent-report-b1 `sse42`, `sandybridge`, `ivybridge`, `piledriver`, `haswell`, `skylakex`, `cannonlake`, `cascadelake`, `cooperlake`, `icelake`, `zen4`, `alderlake`, `sapphirerapids`) and `libggml-rpc.so`; **14 on Linux aarch64** (8 modules, `armv8.0_1` to `armv9.2_2`: dotprod, fp16, - SVE, i8mm, SVE2, SME); **13 on Android aarch64** (ggml's own Android list of 7, `android_armv8.0_1` + SVE, i8mm, SVE2, SME); **9 on Linux s390x** (3 modules: `z13`, scalar, `z15` with VXE2, `z16` with + VXE2 + NNPA; see "Linux s390x"); **13 on Android aarch64** (ggml's own Android list of 7, `android_armv8.0_1` to `android_armv9.2_2`); **7 on Windows arm64** (one `ggml-cpu.dll`) -- ELF with run path `$ORIGIN` only, **except on Android, where the libraries carry no run path at all**: CMake's Android platform drops the flag @@ -300,7 +301,7 @@ measurements behind the Windows step are in `docs/handover/local-agent-report-b1 build. - **Built in manylinux_2_28** (AlmaLinux 8, gcc-toolset-14, glibc 2.28): x86-64 in the dockcross image the CUDA job already used, aarch64 natively on `ubuntu-24.04-arm` inside the pypa image - (`.github/manylinux/manylinux_2_28-aarch64`, see "Linux aarch64"); the static s390x library is + (`.github/manylinux/manylinux_2_28-aarch64`, see "Linux aarch64"); the s390x libraries are cross-compiled against the pypa s390x image as a sysroot (see "Linux s390x"). manylinux2014's GCC 10 lacked `-mavxvnni` and `-mamx-tile` (`alderlake`, `sapphirerapids`; ggml does not check). The glibc floors meet at **2.28**: x86-64 rises from 2.17, aarch64 falls from ~2.39, s390x from 2.38. @@ -311,7 +312,7 @@ measurements behind the Windows step are in `docs/handover/local-agent-report-b1 CPU jars on RHEL 8.10. - **One `ctest` in a modular build job, `c_abi`, and no `jllama_test`**: `jllama_test` calls the CPU/RPC backends directly, so the option refuses `BUILD_TESTING`; what a library-jar build does - run (`JLLAMA_C_ABI_TEST`, on by default, not when cross-compiling) is `src/test/c/test_c_abi.c` + run (`JLLAMA_C_ABI_TEST`, on by default, not when cross-compiling -- s390x excepted, which runs it under its toolchain's qemu) is `src/test/c/test_c_abi.c` -- plain C11, linked against the shared `llama` and `ggml-base` only, no JNI, no C++: it calls `ggml_backend_load_all_from_path` on the natives directory, `llama_backend_init`, requires at least one device (the CPU module must have loaded), prints `llama_print_system_info`, and loads a @@ -491,11 +492,11 @@ new job:** > specific to a configuration the tests cannot run in are covered by the layers that *can* see > them -- the packaging check and the Java suite against the real artifact.** -Today that means 2 jobs build a shipped artifact **and** run `ctest` (`build-linux-s390x`, -`build-macos-arm64-metal-15` -- the two static platforms; every modular library-jar job additionally -runs the one plain-C test `c_abi` against the shared `llama` it ships, see "Modular natives") and 7 +Today that means 1 job builds a shipped artifact **and** runs `ctest` (`build-macos-arm64-metal-15` +-- the one static platform; every modular library-jar job additionally runs the one plain-C test +`c_abi` against the shared `llama` it ships, see "Modular natives", the s390x one under qemu) and 8 only test -(`test-cpp-linux-x86_64`, `test-cpp-linux-aarch64`, `test-cpp-windows-x86_64`, +(`test-cpp-linux-x86_64`, `test-cpp-linux-aarch64`, `test-cpp-linux-s390x`, `test-cpp-windows-x86_64`, `test-cpp-windows-arm64` -- forced, see below -- plus `build-windows-x86_64-msvc`, the MSVC toolchain build that ships no jar since the modular natives but still runs `ctest` and the Java suite, and `build-macos-arm64-metal` and `build-macos-arm64-no-metal`, whose artifacts are not @@ -1531,6 +1532,7 @@ Current patches: | `0020-ggml-alloc-validate-leafs-before-reusing-an-allocation.patch` | **An `--embedding` model that is asked to generate crashes the process in `ggml_gallocr_alloc_graph`** (SIGSEGV on Linux, 0xC0000005 with both Windows toolchains), since upstream #30445 (`cb55ffe6e`, "graph: skip the LM head for pooled embeddings", b11574..b11612). `ggml_gallocr_alloc_graph()` assigns every tensor by **position** from the graph it was reserved with, and `ggml_gallocr_needs_realloc()` validates every node and every node's sources but of the **leafs only the count**. Since #30445 the LM head is expanded by `build_logits()` *after* `build_pooling()`, so an embeddings context reserves a graph that expands `t_embd` first and `t_logits` second, while a generation step on it (the server calls `llama_set_embeddings(ctx, false)` for a slot that samples) expands only `t_logits`, whose first source is `output.weight`: same node and leaf counts, every leaf after it shifted by one. An input leaf then lands on the slot of a weight (`buffer_id -1`) and, with the asserts compiled out, `ggml_vbuffer_tensor_alloc()` indexes `galloc->buffers[-1]`; a leaf landing on another input's slot is not size-checked either. The patch validates the leafs exactly as the nodes are validated, so an invalid leaf makes the allocator reserve again (one buffer) or return false, on which `ggml_backend_sched` reserves the graph itself. **Found by** Publish run 38648257387 (2026-10-14, the first full run since b11558): `ErrorHandlingTest` loads its model with `enableEmbedding()` and posts an infill request to it -- its first test in JUnit's order -- and the fork died in all three Java test jobs (`ggml_gallocr_alloc_graph <- ggml_backend_sched_alloc_graph <- llama_context::process_ubatch <- decode <- server_context_impl::update_slots`). **Reproduced locally without a downloaded model** (a random 2-layer llama GGUF with FIM tokens, context 128): infill crashes with `--embedding` and works without; a debug print showed leaf 31 `out_ids` arriving at a slot with no allocation; reverting `cb55ffe6e` makes it pass, and so does this patch -- with greedy sampling the `--embedding` model then returns the same tokens as the model without, run after run. Upstream master (`a0e6dc738`) still lacks the check; **not yet filed upstream**. **Runnable guard: `src/test/cpp/test_gallocr_leafs.cpp`** (two graphs with the same counts and the leafs swapped). **Drop-check on every bump:** read `ggml_gallocr_needs_realloc()` in `ggml/src/ggml-alloc.c` at the new tag -- once it walks `graph->leafs` itself, drop this patch rather than refreshing it. | | `0021-server-clear-the-slot-cache-when-the-server-adapters-change.patch` | **A slot's prompt cache survived a change of the LoRA adapters it was computed with.** `launch_slot_with_task()` (`tools/server/server-context.cpp`) clears the cache when a request names its own adapters and they differ from the slot's (`lora_should_clear_cache`, which keeps it only for aLoRA), but a request that names none takes `params_base.lora_adapters` in the `else` branch without that check. So the cache survived `SET_LORA` (`LlamaModel.setLoraAdapter(s)`, upstream's `POST /lora-adapters`) and the return to the server's adapters after a request with its own, and the slot answered like neither adapter set, silently. **Measured** (J9 of the bug-hunt brief, b11639, upstream's own LoRA test pair `stories15M_MOE-F16.gguf` + `moe_shakespeare15M.gguf`, greedy, one slot): after `setLoraAdapter(0, 0f)` the warm slot answered ". He saw a picture of a bird ..." where an emptied slot with the same scale answers " was a little girl ..."; only `eraseSlot(0)` restored it (F67). The patch applies the per-request rule in the `else` branch too; equal sets keep the cache as before. **A second hunk starts every slot with the server's adapters in `init()`** (`slot.lora = params_base.lora_adapters;`): `slot.lora` used to start empty, so a never-used slot of a server started with `--lora` read as "computed under no adapters", and its first task logged `0 loras -> 1 loras` and cleared a prompt it already held -- a state restored into it (`SLOT_RESTORE`) or loaded from the host-memory prompt cache by `get_available_slot()`, both computed under the server's adapters (F240). Upstream has the same defect on its HTTP route, so the patch is upstream-shaped; **not yet filed upstream**. Not covered, and said in `setLoraAdapters`' Javadoc: the host-memory prompt cache (`--cache-ram`, on by default) stores slot states without their adapters. **Runnable guard: `src/test/cpp/test_lora_cache.cpp`** (a random llama model and a random LoRA adapter on K, V and the FFN output, written by the test; five cases). **Drop-check on every bump, one per hunk:** read the `else` branch of the "process per-request lora adapters" block in `launch_slot_with_task()` at the new tag -- once it compares `params_base.lora_adapters` with `slot.lora` and clears the prompt (or `SET_LORA` clears the slots), drop that hunk rather than refreshing it; and read the slot loop of `init()` -- once it sets `slot.lora` from `params_base.lora_adapters`, drop that hunk. The patch goes when both have. | | `0022-ggml-cpu-repack-batch-the-tokens-of-an-expert.patch` | **The expert matmuls of a MoE model over repacked CPU weights ran one token at a time on a static thread split.** `forward_mul_mat_id()` (`ggml/src/ggml-cpu/repack.cpp`) -- the path of every MoE model whose experts are repacked at load: MXFP4 (gpt-oss), Q4_0, IQ4_NL and Q4_K on AVX2, their interleaved forms on ARM -- multiplied each token routed to an expert with its own gemv, although the gemm kernel for the type exists, and gave thread `ith` a fixed row range of every expert, so each expert matmul waited for the slowest thread. Measured with a random-weight gpt-oss-20b in ggml-org's MXFP4 layout on the `alderlake` module (the Core Ultra's), 4 threads: 72 % of prompt time was that gemv loop, 13.9 % of generation time the barrier wait after it ([`docs/benchmarks/moe-mxfp4-cpu-alderlake.md`](docs/benchmarks/moe-mxfp4-cpu-alderlake.md)). The patch adds a batched path -- (expert, column chunk) units handed out through `ggml_threadpool_chunk_add`, every group of up to 16 of an expert's tokens quantized interleaved (`ggml_quantize_mat_t`, as `forward_mul_mat` does) and multiplied with `gemm`, a rest of fewer than four by gemv -- and **keeps upstream's path behind a process-wide switch**: `ggml_backend_cpu_set/get_repack_mmid_batched` (`ggml-cpu.h`, and in the CPU backend's `get_proc_address` table, which is how libjllama reaches it in a `GGML_BACKEND_DL` build), on by default, `GGML_CPU_REPACK_MMID_BATCHED=0` in the environment starts it off. Thread 0 reads it once per op and publishes the choice in the work buffer, so a flip during a compute cannot split the threads; `work_size()` reserves the batched path's scratch either way (~420 KB per thread for gpt-oss). Exposed as `LlamaModel.setCpuMoeBatched` / `isCpuMoeBatched`, `jllama_set/get_cpu_moe_batched` and the FFM `Jllama`: the switch exists so a user can measure both paths on their own hardware -- the P-core/E-core effect the static split suggests is unmeasured (TODO.md). **Measured, one binary, the environment variable flipped:** prompt processing +47 % (42.63 -> 62.76 t/s), token generation +26 % (7.90 -> 9.97), logits equal to rounding (`llama-perplexity --kl-divergence`: mean KLD 0.000000, max 0.000002, same top token 100 %). **Runnable guard: `src/test/cpp/test_moe_repack.cpp`** (both paths against ggml's generic `MUL_MAT_ID` over the same weights; the switch resolved through `get_proc_address`). Upstream-shaped without the switch; **not yet filed upstream**. **Drop-check on every bump:** read `forward_mul_mat_id()` in `ggml/src/ggml-cpu/repack.cpp` at the new tag -- once it calls `gemm` for an expert's batched rows and hands its work out through `ggml_threadpool_chunk_add`, drop this patch rather than refreshing it; the switch goes with it, and `setCpuMoeBatched` then throws (`LlamaException`, no entry point), so the Java, C and FFM API needs the decision whether to remove it or keep it as a no-op. | +| `0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch` | **The IBM Z CPU variants (`GGML_CPU_ALL_VARIANTS` on s390x) did not build with clang, and had no module below z15.** (1) `ggml-cpu-impl.h` and `llamafile/sgemm.h` both include `` under `__VXE__ \|\| __VXE2__`; clang's header has no include guard (GCC's has `_VECINTRIN_H`), so `ggml-cpu.c`, which includes both, failed with "redefinition of 'vec_extract'". The include in `sgemm.h` came with ggml-org/llama.cpp#14273 and is not needed: the header declares no vector type, and both of its includers include `ggml-cpu-impl.h` -- `sgemm.cpp` after it, where `__VXE__` is not defined yet (`ggml-cpu-impl.h` defines it from `__VEC__`), so it never fired there. The patch removes it. (2) Upstream's list starts at `z15`, so a z13/z14 would have found no CPU backend at all; the patch adds `ggml-cpu-z13` (scalar, `-march=z13`, score 1), so the jar keeps the z13 floor the static library had. Measured on cfarm191 (z16, b11901, Qwen3-0.6B Q4_K_M big-endian, 4 threads): `z13` 19.7 / 13.6 t/s, `z15` 90.0-95.5 / 57.3-58.3, `z16` 82.6-88.3 / 51.9-53.9 (prompt / generation); Q4_K_M against a Q8_0 reference on z13: mean KLD 0.0777 on `z13`, 0.0746 on `z16`. **Guard:** `build-linux-s390x` builds the variants with clang 18 -- without part (1) the build fails -- and checks that `libggml-cpu-z13/z15/z16.so` all exist, which is what fails without part (2). Not yet filed upstream. **Drop-check on every bump:** `grep -n vecintrin ggml/src/ggml-cpu/llamafile/sgemm.h` (a hit: part 1 still needed) and the s390x block of `ggml_add_cpu_backend_variant` calls in `ggml/src/CMakeLists.txt` (no `z13`/scalar entry: part 2 still needed). Measured at b11901: both still needed. | **Dropped patches** (`0004`, `0005`, `0009`, `0010`, `0011`, `0012`, `0013`, `0016`, `0019` -- upstream fixed each defect itself, or for `0016` added the model; `0019` is this project's own fix, merged upstream as #30505) are recorded in [`docs/history/dropped-llama-patches.md`](docs/history/dropped-llama-patches.md): @@ -2099,7 +2101,7 @@ folder name is produced on both ends. A plain `cmake -B build` is the **static** build: `CMakeLists.txt` forces `BUILD_SHARED_LIBS OFF`, so upstream `llama` and `ggml` are static libraries linked into one `jllama` library (what ships for -s390x and macOS, and what every `test-cpp-*` job and the local test loop build). `LlamaLoader` then +macOS, and what every `test-cpp-*` job and the local test loop build). `LlamaLoader` then extracts and loads that single file (plus the files in its `jllama-extras.txt`, and `ggml-metal.metal` from a Metal build that does not embed it). The shipped Linux / Windows / Android library jars are the **modular** build, `cmake -B build -DJLLAMA_BACKEND_DL=ON` (see "Modular natives"): ggml shared, @@ -2568,11 +2570,37 @@ Wiring: ARM hardware, as this job did before. - Branch protection: a required check pinned to "Build and Test Linux aarch64" must be repointed. -### Linux s390x: big-endian cross-build + qemu test gate +### Linux s390x: big-endian cross-build, IBM Z CPU variants, qemu test gate `build-linux-s390x` adds **IBM Z (s390x, big-endian)** — the one target whose byte order differs from every other platform. It **cross-compiles** at native x86 speed — no emulated -build — and then runs the **full C++ unit suite under `qemu-user`**. +build — a **modular library jar with one CPU module per IBM Z level**, and `test-cpp-linux-s390x` +runs the **full C++ unit suite under `qemu-user`** against the static build of the same toolchain. + +**The CPU variants (since 6.1.0).** `-DJLLAMA_BACKEND_DL=ON` with the toolchain below: `libjllama`, +`libllama`, `libmtmd`, `libggml`, `libggml-base`, `libggml-rpc` and `libggml-cpu-z13` (scalar), +`-z15` (VXE2) and `-z16` (VXE2 + NNPA), of which `ggml_backend_load_best` loads the best by the +kernel's `AT_HWCAP` (`arch/s390/cpu-feats.cpp`: VXE2 bit 15, NNPA bit 20). Everything but the two +vector modules is built at the toolchain's `-march=z13`, so the floor stays z13. **Two things in +upstream's s390x variant list needed `llama/patches/0023`:** clang could not build the vector modules +(`ggml-cpu-impl.h` and `llamafile/sgemm.h` both include ``, and clang's copy has no +include guard -- "redefinition of 'vec_extract'"; GCC's has one, and upstream documents GCC as the +s390x compiler), and the list started at z15, so a z13/z14 would have had no CPU backend at all. The +patch drops the include from `sgemm.h` (it declares no vector type, and both its includers include +`ggml-cpu-impl.h` too; it came with ggml-org/llama.cpp#14273) and adds the scalar `z13` variant. z17 +(`-march=arch15`) is skipped by ggml's own compiler check: clang 18 does not accept the flag. +**Measured on cfarm191** (z16, LinuxONE, Debian 13, clang 19.1.7, b11901 with the patch; Qwen3-0.6B +Q4_K_M converted to big-endian, `llama-bench -t 4 -p 64 -n 16`, two interleaved rounds): `z13` +19.7-19.8 / 13.6-13.8 t/s, `z15` 90.0-95.5 / 57.3-58.3, `z16` 82.6-88.3 / 51.9-53.9 (prompt / +generation) -- 4.5x and 4x over the scalar library every jar up to 6.0.x shipped. Correctness, by +`llama-perplexity --kl-divergence` against a Q8_0 reference computed on `z13`: the Q4_K_M model +lands at a mean KLD of 0.0777 on `z13` and 0.0746 on `z16` -- equally far from it; the 0.0087 +between the two modules is about a tenth of what the quantization costs. **Under qemu** (the +`c_abi` run of `build-linux-s390x`, the RHEL 8 row of `smoke-glibc-floor`) ggml loads `z15`: +qemu-user's CPU model has VXE2 and no NNPA, so CI runs the vector code too, in emulation (measured +locally with the job's toolchain: `c_abi` reports `CPU : VXE = 1`, and the library loads in the UBI 8 +OpenJDK 21 image on RHEL 8.10). `build-linux-s390x` checks that all three modules were built: a +variant whose `-march` the compiler rejects is skipped by the configure, not reported. **The toolchain gives it the manylinux_2_28 floor** (glibc 2.28, `GLIBCXX_3.4.25`, `CXXABI_1.3.11`), like the other two Linux CPU jars, without an s390x runner: @@ -2610,10 +2638,11 @@ and succeeds with it, for an executable, a PIE and a shared object, and the prog to COMDAT names; those of classes with a key function and of internal types (lambdas) sat in plain `.rodata`, so the toolchain also passes **`-fdata-sections`**. **And the link is not the test:** it fails only where GCC code happens to reference an odd name, so `verify-native-deps.py` reads the -static symbol table of the s390x `libjllama.so` and fails any `_ZTS*` at an odd address -- by every -binding, because the export map makes all of them local (`TYPEINFO_NAME_ALIGNMENT` in -`nativedeps.py`; a library without any typeinfo name fails too, since it would pass vacuously). It -runs in `build-linux-s390x` after the build. Measured on two local builds of the library with the +static symbol table of every s390x library and fails any `_ZTS*` at an odd address -- by every +binding, because the export map makes all of `libjllama`'s local (`TYPEINFO_NAME_ALIGNMENT` in +`nativedeps.py`; a `libjllama` without any typeinfo name fails too, since it would pass vacuously; +the other libraries of the directory are checked when they carry any). It runs in +`build-linux-s390x` after the build. Measured on two local builds of the library with the stand-in sysroot: script alone, 444 of 1833 typeinfo names odd; script plus `-fdata-sections`, 0. Until 2026-10-25 the job used the runner's `g++-s390x-linux-gnu` (GCC 13) with its glibc 2.39 @@ -2625,15 +2654,17 @@ dependencies `libpthread.so.0` and `libdl.so.2` (glibc 2.28 has not merged them tests under qemu against the sysroot and natively on a LinuxONE z16 (cfarm191, Debian 13), and the jar loads on RHEL 8.10 s390x where the 6.0.0 one fails with `GLIBC_2.38 not found`. -That `ctest` run is a **real big-endian correctness gate** for the byte-order-sensitive surface — the +That `ctest` run (`test-cpp-linux-s390x`) is a **real big-endian correctness gate** for the byte-order-sensitive surface — the little-endian WAV writer (`tts_wav.hpp`), the JSON/token/embedding transforms, and the JNI helpers — which is where an endian bug in *our* code could hide. Model-backed **Java** tests are deliberately **not** run under emulation (a JVM + GGUF inference under `qemu-user` is slow and flaky); the Java↔JNI boundary uses host-native array copies (endian-transparent), so the C++ gate covers the actual risk. `-DGGML_OPENMP=OFF` sidesteps cross-libgomp issues (ggml uses its own `std::thread` pool). -`-DGGML_NATIVE=OFF` also switches VXE off, so the binary is **scalar** on purpose; **do not "fix" a -VXE-related compile error with `-DGGML_VXE=ON`** (it needs `-march=z15` too and raises the hardware -floor) -- measured in the `0013` note of `docs/history/dropped-llama-patches.md`. s390x is a +`-DGGML_NATIVE=OFF` switches VXE off for everything outside the variant modules, which carry their +own `-march`; **do not pass `-DGGML_VXE=ON`**: ggml then adds VXE2 to every variant, the `z13` one +included, which would score 0 without VXE2 and leave a z13/z14 without a CPU backend (the static +build of the test job stays scalar; the `0013` note of `docs/history/dropped-llama-patches.md` +measured what the flag does to it). s390x is a CPU platform like aarch64, so it ships as the natives jar `cpu-linux-s390x`, part of `llama-platform` (`OSInfo` maps `os.arch=s390x` → `Linux/s390x`). **Fail-loud** and in `package.needs` like every other build. (Upstream llama.cpp already supports s390x diff --git a/README.md b/README.md index b926f30a..a876e819 100644 --- a/README.md +++ b/README.md @@ -212,10 +212,10 @@ If any of these match your platform, you can include the Maven dependency and ge same artifact, selected by a Maven `` of the form `--`, of two kinds: - **One library jar per platform** (`cpu--`, `metal-macos-aarch64`): `libjllama` -- on - Linux x86-64 / aarch64, Windows and Android next to llama.cpp's and ggml's shared libraries + Linux x86-64 / aarch64 / s390x, Windows and Android next to llama.cpp's and ggml's shared libraries (`libllama`, `libmtmd`, `libggml*`) and the CPU backend modules (one per instruction-set level, of which the best for the running CPU is loaded at start; a single one on Windows arm64), on macOS - and s390x one static library with llama.cpp and ggml linked in. Exactly one is loaded, the one of + one static library with llama.cpp and ggml linked in. Exactly one is loaded, the one of the running platform; `llama-platform` collects them for every desktop platform. - **GPU module jars** (`cuda13-…`, `rocm-…`, `sycl-…`, `vulkan-…`, `opencl-…`, `openvino-…`): each holds only ggml's backend module for that GPU (`libggml-cuda.so`, `ggml-vulkan.dll`, …). They are @@ -265,7 +265,7 @@ iGPU, the example being a Core Ultra 7 165U). |---|---|---|---| | `cpu-linux-x86-64` | CPU | Linux x86-64 | A JDK 8+ JVM; glibc ≥ 2.28 (manylinux_2_28: RHEL 8, Ubuntu 20.04, Debian 10 and later). Ships one CPU backend module per instruction-set level (x86-64 baseline, SSE4.2, AVX, AVX2, AVX-512, AVX-VNNI, AMX), of which ggml loads the best for the running CPU at start-up — a CPU without AVX2 works, and an AVX-512/AMX machine uses its kernels. | | `cpu-linux-aarch64` | CPU | Linux aarch64 | glibc ≥ 2.28 (RHEL 8, Ubuntu 20.04, Debian 10, Amazon Linux 2023 and later) — built in the manylinux_2_28 image on an arm64 runner. Ships one CPU backend module per ARM feature level (armv8.0 up to armv9.2: dotprod, fp16, SVE, i8mm, SVE2, SME), chosen at start-up like the x86-64 ones. | -| `cpu-linux-s390x` | CPU | Linux s390x (IBM Z, big-endian) | A JDK 8+ JVM; glibc ≥ 2.28 (RHEL 8, Ubuntu 20.04, Debian 10 and later) on z13 or newer — cross-compiled against the manylinux_2_28 s390x image, so the floor of the other Linux CPU jars applies; CI loads it on RHEL 8.10. (The 6.0.0 jar needed glibc 2.38 and did not load on RHEL 8/9 or Ubuntu 22.04.) | +| `cpu-linux-s390x` | CPU | Linux s390x (IBM Z, big-endian) | A JDK 8+ JVM; glibc ≥ 2.28 (RHEL 8, Ubuntu 20.04, Debian 10 and later) on z13 or newer — cross-compiled against the manylinux_2_28 s390x image, so the floor of the other Linux CPU jars applies; CI loads it on RHEL 8.10. Three CPU modules, of which the best for the machine is loaded at start: `z13` (scalar), `z15` (vector, VXE2) and `z16` (VXE2 + NNPA); on a z16 the vector modules process prompts about 4.5x and generate about 4x faster than the scalar library of 6.0.x. (The 6.0.0 jar needed glibc 2.38 and did not load on RHEL 8/9 or Ubuntu 22.04.) | | `cpu-windows-x86-64` | CPU | Windows x86-64 | A JDK 8+ JVM; **Windows 10 or newer**. Ships one CPU backend module per instruction-set level (as the Linux x86-64 jar: x86-64 baseline, SSE4.2, AVX, AVX2, AVX-512, AVX-VNNI, AMX), of which ggml loads the best for the running CPU at start-up. Built with clang, hybrid CRT: the STL and vcruntime are static (no `msvcp140.dll` / `vcruntime140.dll`, no VC++ redistributable), the Universal CRT is the OS one (`ucrtbase.dll`), so Microsoft's UCRT security updates apply. | | `cpu-windows-aarch64` | CPU | Windows on ARM (Snapdragon X / Surface) | A JDK 8+ JVM; **Windows 10 or newer**. Built natively on `windows-11-arm` with `clang-cl`, same hybrid CRT as the x86-64 jar; one CPU backend module (ggml builds no ARM variant set for Windows), so the Vulkan and OpenCL module jars can join it. | | `metal-macos-aarch64` | Metal + CPU | macOS aarch64 (Apple silicon) | A JDK 8+ JVM. | diff --git a/TODO.md b/TODO.md index 596fd0ef..7c900a87 100644 --- a/TODO.md +++ b/TODO.md @@ -112,7 +112,7 @@ measurements below remain open hardening/coverage work; they do not establish a - [ ] Set `JLLAMA_C_ABI_MODEL` in a suitable CI job and exercise the model-backed half of `c_abi`. The existing plain-C compile/smoke runs without a model; Java/C++/FFM inference - coverage does not exercise this particular consumer. macOS and s390x remain static builds. + coverage does not exercise this particular consumer. macOS remains a static build. ### 8. Cached/uncached reproducibility measurement (C.3) @@ -151,7 +151,7 @@ Kept as a list because each entry names the machine it needs -- the harness is | **OpenVINO on an Intel GPU/NPU** | suitable Intel device, unavailable to these sessions | the 21.7x/336x figures are CPU-plugin fallback only; they do not prove GPU/NPU execution | | the two **SME** aarch64 modules | Linux aarch64 with SME, absent from current CI/cloud machines | macOS's static Metal library does not exercise these modules | | **gpt-oss-20b on both MoE paths of `llama/patches/0022`**, on the **Core Ultra 7 165U** (`-t 6/8/10`) | the laptop; switch with `LlamaModel.setCpuMoeBatched` or `GGML_CPU_REPACK_MMID_BATCHED=0` ([`docs/benchmarks/moe-mxfp4-cpu-alderlake.md`](docs/benchmarks/moe-mxfp4-cpu-alderlake.md)) | upstream's static work split meets real P-, E- and LP-E-cores only there; the sandbox could emulate one slow core for prompt processing (−49 % upstream's path, −24 % batched) but not for generation | -| **`llama/patches/0022` with real weights on every CPU family whose repack path it changes**: gpt-oss-20b (MXFP4) and a Q4_K MoE (e.g. Qwen3-30B-A3B Q4_K_M), both switch positions -- `llama-perplexity --kl-divergence` of the batched path against upstream's, `llama-bench` pp/tg at several thread counts, and once through the jar (`LlamaModel.setCpuMoeBatched`) | x86-64 with AVX-512/AMX and Zen 4 (EC2 or Vultr: the `sapphirerapids` and `zen4` modules); aarch64 with dotprod (cfarm424, Neoverse-N1, 64 cores) and with i8mm/SVE (Graviton 3/4); s390x with VXE (cfarm191, z16 -- ggml repacks only Q4_0 there, so a Q4_0 MoE converted to big-endian, and only once s390x ships CPU variants, see "Linux s390x") | #578 measured random weights in the real shapes, on the `alderlake` module of an AVX-512 host. KLD 0 there shows the batched path is right for that gemm kernel, not for the NEON, i8mm or VXE kernels it calls elsewhere, and random weights route tokens to experts more evenly than a trained model does | +| **`llama/patches/0022` with real weights on every CPU family whose repack path it changes**: gpt-oss-20b (MXFP4) and a Q4_K MoE (e.g. Qwen3-30B-A3B Q4_K_M), both switch positions -- `llama-perplexity --kl-divergence` of the batched path against upstream's, `llama-bench` pp/tg at several thread counts, and once through the jar (`LlamaModel.setCpuMoeBatched`) | x86-64 with AVX-512/AMX and Zen 4 (EC2 or Vultr: the `sapphirerapids` and `zen4` modules); aarch64 with dotprod (cfarm424, Neoverse-N1, 64 cores) and with i8mm/SVE (Graviton 3/4); s390x with VXE (cfarm191, z16 -- ggml repacks only Q4_0 there, so a Q4_0 MoE converted to big-endian, on the vector modules `z15`/`z16`, see "Linux s390x" in CLAUDE.md) | #578 measured random weights in the real shapes, on the `alderlake` module of an AVX-512 host. KLD 0 there shows the batched path is right for that gemm kernel, not for the NEON, i8mm or VXE kernels it calls elsewhere, and random weights route tokens to experts more evenly than a trained model does | | [`docs/local-test-plan.md`](docs/local-test-plan.md) parts A–D on the **Core Ultra 7 165U** | the laptop the Windows guide (#575) is written for | part A -- which CPU module ggml picks there and what it gains -- is what CI cannot measure; the only report so far is the Zen 3 one (`docs/handover/local-agent-report-b11538-windows.md`). One result file per machine, unmeasured values marked as such | ## Open — jllama-specific @@ -370,8 +370,8 @@ because each is a design decision in code this project does not own. And say pla downstream instead (the opt-in, the non-aborting registration, the `-w` on `ggml-hip`), so a maintainer can see what a merged change would let this project delete. -There are **thirteen** patches today (`0001`–`0003`, `0006`–`0008`, `0014`, `0015`, -`0017`, `0018`, `0020`, `0021`, `0022`). Each +There are **fourteen** patches today (`0001`–`0003`, `0006`–`0008`, `0014`, `0015`, +`0017`, `0018`, `0020`–`0023`). Each accepted upstream change deletes a patch from the bump checklist once the pin is bumped past it. - **Submittable as they are** (self-contained, no jllama specifics): `0001` (Windows arg-parse embed @@ -388,7 +388,9 @@ accepted upstream change deletes a patch from the bump checklist once the pin is change; both hunks have independent drop-checks in `CLAUDE.md`), `0022` (batch an expert's tokens through gemm in the repack `MUL_MAT_ID` and hand its work out dynamically -- submitted **without** its switch, which exists for measurements here; the evidence is - `docs/benchmarks/moe-mxfp4-cpu-alderlake.md`). + `docs/benchmarks/moe-mxfp4-cpu-alderlake.md`), `0023` (IBM Z: drop the duplicate `` + include that breaks clang, and a scalar `z13` variant below upstream's `z15` -- two questions, + since upstream documents GCC for s390x and may prefer its own baseline name). - **Permanent:** `0003` carries upstream PR #22393, which upstream closed without merging. - **Two upstream-only defects worth an issue on that day, no patch here:** a lost RPC server mid-inference still aborts the process (the ggml backend interface has no error return for a lost @@ -545,13 +547,22 @@ loads on RHEL 8.10 s390x (UBI 8 OpenJDK 21 runtime under qemu) where the 6.0.0 j rounds on the shared host: generation 0.62–1.87 t/s (clang) against 0.31–0.93 t/s (GCC), prompt 2.0–6.5 against 1.9–2.8 t/s. Clang was ahead in every round. Absolute numbers are noisy and low. -- [ ] **s390x runs scalar.** Both the GCC and the clang build have `GGML_NATIVE=OFF`, so `GGML_VXE` is - off: no vector code for any IBM Z. ggml's `GGML_CPU_ALL_VARIANTS` has s390x variants for - z15–z17 (`-march=z15/z16/arch15` with VXE2, NNPA on z16+) that a `GGML_BACKEND_DL` build picks - at run time, as the x86 jars do. Moving s390x from the static single library to the modular - build with those variants would keep the z13 floor (a scalar baseline variant) and use the - vector unit where it exists. To check first: whether clang 18 builds the z15–z17 variants, and - what they gain on cfarm191. +- [x] **s390x ran scalar** (fixed on `feat/s390x-cpu-variants`, 2026-10-27; CLAUDE.md "Linux s390x"). + `build-linux-s390x` is a modular build now with the CPU modules `z13` (scalar), `z15` (VXE2) and + `z16` (VXE2 + NNPA); `llama/patches/0023` makes clang build the vector modules (a duplicate + `` include that only GCC's header guard hid) and adds the `z13` baseline, which + upstream's list lacks. On cfarm191 (b11901, Qwen3-0.6B Q4_K_M big-endian, 4 threads): `z13` + 19.7 / 13.6 t/s, `z15` 90.0-95.5 / 57.3-58.3, `z16` 82.6-88.3 / 51.9-53.9 (prompt / generation). + The C++ suite moved to `test-cpp-linux-s390x` (a modular build cannot link `jllama_test`). +- [ ] **The CI-built modular s390x jar on cfarm191**, once a Publish run of the branch is green: + `NativeLoadSmoke` and the `ServerLauncher` round on the big-endian Qwen3-0.6B as on 2026-10-26, + plus which module the loader line and `JNI_OnLoad`'s registry log name (`z16` expected), and the + speed against the 6.1.0-SNAPSHOT static jar of run 40400361809 (0.33 t/s generation at 8 threads + on the shared host, 13.6 t/s for the scalar build at 4). Only a real IBM Z runs the vector modules + at their own speed; qemu scores them against its CPU model. +- [ ] **z17 (`-march=arch15`)** is skipped: clang 18 rejects the flag (clang 19 on cfarm191 too), and + ggml's configure drops the variant silently. Re-check when the toolchain's clang moves, and + measure on a z17 if the farm gets one. - **Ruled out:** `ubuntu-22.04` as the runner (glibc 2.35 still misses RHEL 8/9, and its cross GCC 11 cannot build llama.cpp, which needs GCC >= 12). `-static-libstdc++` alone is ruled out too: it fixes GLIBCXX but not GLIBC. diff --git a/docs/patches.md b/docs/patches.md index ce23672a..6c755188 100644 --- a/docs/patches.md +++ b/docs/patches.md @@ -18,7 +18,7 @@ upstream-shaped, and dropped here the moment upstream carries the fix (the drop- in its `jllama-build.txt` (`patches=`) and a hash over the set (`patches.sha256=`), so a consumer can match a binary to this page. -**13 patches.** Ids as `jllama-build.txt` lists them under `patches=`. +**14 patches.** Ids as `jllama-build.txt` lists them under `patches=`. | Id | Patch | Title | Files | Hunks | |---|---|---|---:|---:| @@ -35,6 +35,7 @@ match a binary to this page. | 0020 | [`0020-ggml-alloc-validate-leafs-before-reusing-an-allocation.patch`](#0020-ggml-alloc-validate-leafs-before-reusing-an-allocationpatch) | ggml-alloc : validate the leafs before reusing a graph's allocation | 1 | 1 | | 0021 | [`0021-server-clear-the-slot-cache-when-the-server-adapters-change.patch`](#0021-server-clear-the-slot-cache-when-the-server-adapters-changepatch) | server : clear a slot's cache when the server's LoRA adapters change | 1 | 2 | | 0022 | [`0022-ggml-cpu-repack-batch-the-tokens-of-an-expert.patch`](#0022-ggml-cpu-repack-batch-the-tokens-of-an-expertpatch) | ggml-cpu : batch the tokens of an expert in the repack MUL_MAT_ID, and hand its work out dynamically | 3 | 7 | +| 0023 | [`0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch`](#0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baselinepatch) | ggml-cpu(s390x): CPU variants that build with clang, and a scalar z13 baseline | 3 | 3 | ## 0001-win32-arg-parse-embed-guard.patch @@ -577,3 +578,61 @@ Drop-check on every bump: read forward_mul_mat_id() in ggml/src/ggml-cpu/repack. -- once upstream calls gemm for an expert's batched rows and hands the work out through ggml_threadpool_chunk_add(), drop this patch (and with it the switch) rather than refreshing it. ``` + +## 0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch + +**Files (3):** `ggml/src/CMakeLists.txt`, `ggml/src/ggml-cpu/CMakeLists.txt`, `ggml/src/ggml-cpu/llamafile/sgemm.h` + +The patch's own header, verbatim: + +```text +ggml-cpu(s390x): CPU variants that build with clang, and a scalar z13 baseline + +GGML_CPU_ALL_VARIANTS builds the IBM Z modules ggml-cpu-z15 (VXE2) and ggml-cpu-z16 (VXE2 + NNPA) +with -march=z15/z16 -mvx -mzvector. Two things stand between that list and a jar that runs on +every machine the scalar library runs on today. + +1. With clang, every translation unit that includes both ggml-cpu-impl.h and llamafile/sgemm.h + fails: both include under `defined(__VXE__) || defined(__VXE2__)`, and clang's + vecintrin.h has no include guard (GCC's has _VECINTRIN_H), so the second inclusion redefines + vec_extract, vec_insert and the rest ("redefinition of 'vec_extract'", first in ggml-cpu.c, + which includes ggml-cpu-impl.h at line 7 and llamafile/sgemm.h at line 51). Nothing in the + object code is duplicated; it is a compile error. The include in sgemm.h came with + ggml-org/llama.cpp#14273 ("llamafile : support s390x SIMD instruction set", 2025-06-19), and it + is not needed: sgemm.h declares no vector type, and both files that include it include + ggml-cpu-impl.h as well -- sgemm.cpp right after it (where __VXE__ is not defined yet, because + ggml-cpu-impl.h is what defines it from __VEC__, so the include there never fired), ggml-cpu.c + before it (where it is the duplicate). Upstream documents GCC as the s390x compiler + (docs/build-s390x.md), which is why it went unnoticed. This patch removes the include from + sgemm.h; ggml-cpu-impl.h keeps the one that is used. + +2. The list starts at z15, so a machine without VXE2 (z13, z14) would find no CPU backend at all, + where the scalar build ran. The patch adds ggml-cpu-z13: no VXE, -march=z13 (the range of the + cross-compile flags starts at 13 for it), score 1 in arch/s390/cpu-feats.cpp, so + ggml_backend_load_best() takes it only where z15 and z16 score 0. z13 is the oldest machine + RHEL 8, SLES 15 and Ubuntu support, and the floor java-llama.cpp's s390x jar has had. + +z17 (-march=arch15) stays as upstream has it: added only when the compiler accepts the flag +(GCC 15.1, newer clang); clang 18 and 19 do not, and the configure skips it. + +Measured on cfarm191 (IBM z16, LinuxONE, machine 3932, 8 IFLs shared, Debian 13, clang 19.1.7) +at b11901 with this patch, Qwen3-0.6B Q4_K_M converted to big-endian with gguf_convert_endian.py, +`llama-bench -t 4 -p 64 -n 16 -r 2`, two interleaved rounds, t/s: + + module pp64 tg16 + z13 (scalar) 19.7 - 19.8 13.6 - 13.8 + z15 90.0 - 95.5 57.3 - 58.3 + z16 82.6 - 88.3 51.9 - 53.9 + z16, GCC 14.2 (unpatched, for comparison) + 85.2 - 86.4 44.1 - 53.9 + +With all three modules ggml loaded z16, without it z15, without both z13. Greedy output of +llama-simple is byte-identical between z15 and z16. Against z13 (llama-perplexity --kl-divergence, +9 chunks of 256 tokens) the vector modules differ by a mean KLD of 0.0087 (same top token 94.8 %) -- +rounding, not a defect: against a Q8_0 reference computed on z13, the Q4_K_M model lands at a mean +KLD of 0.0777 on z13 and 0.0746 on z16 -- equally far from it, and the 0.0087 between them is about +a tenth of what the quantization itself costs. Q8_0 itself, z16 against z13: mean KLD 0.0012, same +top token 98.5 %. + +Not filed upstream yet (nothing is filed before the owner says so). +``` diff --git a/llama/CMakeLists.txt b/llama/CMakeLists.txt index 3878923d..a98df210 100644 --- a/llama/CMakeLists.txt +++ b/llama/CMakeLists.txt @@ -119,7 +119,7 @@ option(BUILD_TESTING "Build C++ unit tests for jni_helpers / json_helpers / util # (libggml-cuda.so, ggml-vulkan.dll, ...), built against the same ggml with GGML_CPU=OFF, and # LlamaLoader puts it next to the CPU jar's libraries, where ggml finds it. Exactly upstream's # Windows release layout (win-cpu + win-cuda holding one ggml-cuda.dll). Both OFF by default: a plain -# `cmake -B build` is the static single-library build (macOS/Metal and s390x ship that way). +# `cmake -B build` is the static single-library build (macOS/Metal ships that way). option(JLLAMA_BACKEND_DL "Build ggml as shared libraries and the backends as modules ggml loads at run time (GGML_BACKEND_DL)" OFF) option(JLLAMA_MODULE_ONLY "Build only the GPU backend module of the one GGML_ flag set, for a GPU natives jar (implies JLLAMA_BACKEND_DL)" OFF) if(JLLAMA_MODULE_ONLY) @@ -485,17 +485,22 @@ if(NOT EXISTS "${llama.cpp_SOURCE_DIR}/vendor/nlohmann/json.hpp") "back on the include path.") endif() if(JLLAMA_BACKEND_DL) - # Where ggml can be loaded as modules here (CLAUDE.md "Modular natives"): Linux x86-64 / aarch64 - # (the dockcross Android toolchain reports Linux too, see the Android block above) and Windows - # x86-64 / arm64. CPU variants wherever ggml has a list for the OS/arch: x86-64 everywhere, ARM on - # Linux/Android (ggml-cpu/CMakeLists.txt has no ARM list for Windows, so Windows arm64 gets the - # one ggml-cpu module upstream ships there too). macOS stays static (Metal is compiled in, one - # CPU generation per Metal build), s390x has no variants upstream and no GPU. + # Where ggml can be loaded as modules here (CLAUDE.md "Modular natives"): Linux x86-64 / aarch64 / + # s390x (the dockcross Android toolchain reports Linux too, see the Android block above) and + # Windows x86-64 / arm64. CPU variants wherever ggml has a list for the OS/arch: x86-64 + # everywhere, ARM on Linux/Android (ggml-cpu/CMakeLists.txt has no ARM list for Windows, so + # Windows arm64 gets the one ggml-cpu module upstream ships there too), and IBM Z on Linux: z13 + # (scalar, the baseline patches/0023 adds), z15 (VXE2) and z16 (VXE2 + NNPA). s390x has no GPU; + # its variants are the reason it is modular. macOS stays static (Metal is compiled in, one CPU + # generation per Metal build). set(_jllama_dl_ok OFF) set(_jllama_all_variants OFF) if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Android)$" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64|aarch64|arm64)$") set(_jllama_dl_ok ON) set(_jllama_all_variants ON) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x") + set(_jllama_dl_ok ON) + set(_jllama_all_variants ON) elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$") set(_jllama_dl_ok ON) set(_jllama_all_variants ON) @@ -503,7 +508,7 @@ if(JLLAMA_BACKEND_DL) set(_jllama_dl_ok ON) endif() if(NOT _jllama_dl_ok) - message(FATAL_ERROR "JLLAMA_BACKEND_DL is supported on Linux/Android x86-64 and aarch64 and on Windows x86-64 and arm64 only") + message(FATAL_ERROR "JLLAMA_BACKEND_DL is supported on Linux/Android x86-64 and aarch64, Linux s390x and on Windows x86-64 and arm64 only") endif() if(BUILD_TESTING) # jllama_test calls the CPU and RPC backends directly (ggml_backend_cpu_init, ...), which are @@ -1188,7 +1193,7 @@ if(JLLAMA_BACKEND_DL) # file below is meant to be byte-identical to what that script writes, which is LF. file(GENERATE OUTPUT ${JLLAMA_DIR}/jllama-files.txt CONTENT "${JLLAMA_FILES_CONTENT}" NEWLINE_STYLE LF) else() - # The static build (macOS, s390x, a plain local build) ships one library and the C API header; + # The static build (macOS, a plain local build) ships one library and the C API header; # the list exists so the header is extracted and not loaded (see the C API block above). A # static Windows build (the MSVC build-and-test job, the test-cpp jobs; nothing ships from them) # gets its import library listed like the modular one. @@ -1258,10 +1263,16 @@ endif() # libjllama (layer A) and against libjllama itself (layer B: jllama_api.h must compile as C and the # jllama_* symbols must be exported) and registers it as the ctest `c_abi` -- the first and only test of the modular build # jobs, which refuse BUILD_TESTING (jllama_test needs the CPU backend statically). build.sh and -# build.bat run `ctest -R c_abi` right after the build. Off when cross-compiling (the Android jobs -# cannot run the executable). Also useful locally: `ctest --test-dir build -R c_abi -V`. +# build.bat run `ctest -R c_abi` right after the build. Off when cross-compiling, with one exception: +# s390x runs it through the emulator of its own toolchain (s390x-toolchain.cmake, qemu with the +# sysroot as loader prefix), which also makes ggml score the IBM Z variants against qemu's CPU +# model. "Has an emulator" is not the test: the dockcross Android images set +# CMAKE_CROSSCOMPILING_EMULATOR too (qemu-arm, which cannot run an aarch64 bionic executable -- +# "Invalid ELF image for this architecture", the first run of the s390x variants). Also useful +# locally: `ctest --test-dir build -R c_abi -V`. option(JLLAMA_C_ABI_TEST "Build and run the plain-C test of the shared upstream libraries of a modular build" ON) -if(JLLAMA_BACKEND_DL AND NOT JLLAMA_MODULE_ONLY AND JLLAMA_C_ABI_TEST AND NOT CMAKE_CROSSCOMPILING) +if(JLLAMA_BACKEND_DL AND NOT JLLAMA_MODULE_ONLY AND JLLAMA_C_ABI_TEST + AND (NOT CMAKE_CROSSCOMPILING OR (CMAKE_CROSSCOMPILING_EMULATOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "s390x"))) enable_language(C) add_executable(jllama_c_abi src/test/c/test_c_abi.c) target_compile_features(jllama_c_abi PRIVATE c_std_11) diff --git a/llama/patches/0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch b/llama/patches/0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch new file mode 100644 index 00000000..78c0cd50 --- /dev/null +++ b/llama/patches/0023-ggml-cpu-s390x-variants-build-with-clang-and-a-z13-baseline.patch @@ -0,0 +1,95 @@ +ggml-cpu(s390x): CPU variants that build with clang, and a scalar z13 baseline + +GGML_CPU_ALL_VARIANTS builds the IBM Z modules ggml-cpu-z15 (VXE2) and ggml-cpu-z16 (VXE2 + NNPA) +with -march=z15/z16 -mvx -mzvector. Two things stand between that list and a jar that runs on +every machine the scalar library runs on today. + +1. With clang, every translation unit that includes both ggml-cpu-impl.h and llamafile/sgemm.h + fails: both include under `defined(__VXE__) || defined(__VXE2__)`, and clang's + vecintrin.h has no include guard (GCC's has _VECINTRIN_H), so the second inclusion redefines + vec_extract, vec_insert and the rest ("redefinition of 'vec_extract'", first in ggml-cpu.c, + which includes ggml-cpu-impl.h at line 7 and llamafile/sgemm.h at line 51). Nothing in the + object code is duplicated; it is a compile error. The include in sgemm.h came with + ggml-org/llama.cpp#14273 ("llamafile : support s390x SIMD instruction set", 2025-06-19), and it + is not needed: sgemm.h declares no vector type, and both files that include it include + ggml-cpu-impl.h as well -- sgemm.cpp right after it (where __VXE__ is not defined yet, because + ggml-cpu-impl.h is what defines it from __VEC__, so the include there never fired), ggml-cpu.c + before it (where it is the duplicate). Upstream documents GCC as the s390x compiler + (docs/build-s390x.md), which is why it went unnoticed. This patch removes the include from + sgemm.h; ggml-cpu-impl.h keeps the one that is used. + +2. The list starts at z15, so a machine without VXE2 (z13, z14) would find no CPU backend at all, + where the scalar build ran. The patch adds ggml-cpu-z13: no VXE, -march=z13 (the range of the + cross-compile flags starts at 13 for it), score 1 in arch/s390/cpu-feats.cpp, so + ggml_backend_load_best() takes it only where z15 and z16 score 0. z13 is the oldest machine + RHEL 8, SLES 15 and Ubuntu support, and the floor java-llama.cpp's s390x jar has had. + +z17 (-march=arch15) stays as upstream has it: added only when the compiler accepts the flag +(GCC 15.1, newer clang); clang 18 and 19 do not, and the configure skips it. + +Measured on cfarm191 (IBM z16, LinuxONE, machine 3932, 8 IFLs shared, Debian 13, clang 19.1.7) +at b11901 with this patch, Qwen3-0.6B Q4_K_M converted to big-endian with gguf_convert_endian.py, +`llama-bench -t 4 -p 64 -n 16 -r 2`, two interleaved rounds, t/s: + + module pp64 tg16 + z13 (scalar) 19.7 - 19.8 13.6 - 13.8 + z15 90.0 - 95.5 57.3 - 58.3 + z16 82.6 - 88.3 51.9 - 53.9 + z16, GCC 14.2 (unpatched, for comparison) + 85.2 - 86.4 44.1 - 53.9 + +With all three modules ggml loaded z16, without it z15, without both z13. Greedy output of +llama-simple is byte-identical between z15 and z16. Against z13 (llama-perplexity --kl-divergence, +9 chunks of 256 tokens) the vector modules differ by a mean KLD of 0.0087 (same top token 94.8 %) -- +rounding, not a defect: against a Q8_0 reference computed on z13, the Q4_K_M model lands at a mean +KLD of 0.0777 on z13 and 0.0746 on z16 -- equally far from it, and the 0.0087 between them is about +a tenth of what the quantization itself costs. Q8_0 itself, z16 against z13: mean KLD 0.0012, same +top token 98.5 %. + +Not filed upstream yet (nothing is filed before the owner says so). + +diff --git a/ggml/src/CMakeLists.txt b/ggml/src/CMakeLists.txt +index fe001a0..082f987 100644 +--- a/ggml/src/CMakeLists.txt ++++ b/ggml/src/CMakeLists.txt +@@ -569,6 +569,9 @@ if (GGML_CPU_ALL_VARIANTS) + endif() + elseif (GGML_SYSTEM_ARCH STREQUAL "s390x") + if (CMAKE_SYSTEM_NAME MATCHES "Linux") ++ # The scalar baseline: every 64-bit IBM Z the toolchain targets loads a CPU backend, and ++ # z15+ machines pick a vector variant below by score. ++ ggml_add_cpu_backend_variant(z13 Z13) + ggml_add_cpu_backend_variant(z15 Z15 VXE2) + ggml_add_cpu_backend_variant(z16 Z16 VXE2 NNPA) + +diff --git a/ggml/src/ggml-cpu/CMakeLists.txt b/ggml/src/ggml-cpu/CMakeLists.txt +index 8327c52..1a6a5f2 100644 +--- a/ggml/src/ggml-cpu/CMakeLists.txt ++++ b/ggml/src/ggml-cpu/CMakeLists.txt +@@ -588,9 +588,9 @@ function(ggml_add_cpu_backend_variant_impl tag_name) + endif() + # for cross-compilation + elseif(GGML_CPU_ALL_VARIANTS) +- # range through IBM z15 to z17 ++ # range through IBM z13 to z17 + # NOTE: update when a new hardware level is released +- foreach (ZHW RANGE 15 17) ++ foreach (ZHW RANGE 13 17) + if(DEFINED GGML_INTERNAL_Z${ZHW}) + message(STATUS "z${ZHW} cross-compile target") + if (ZHW EQUAL 17) +diff --git a/ggml/src/ggml-cpu/llamafile/sgemm.h b/ggml/src/ggml-cpu/llamafile/sgemm.h +index 867b0c0..74dc557 100644 +--- a/ggml/src/ggml-cpu/llamafile/sgemm.h ++++ b/ggml/src/ggml-cpu/llamafile/sgemm.h +@@ -2,10 +2,6 @@ + #include + #include + +-#if defined(__VXE__) || defined(__VXE2__) +-#include +-#endif +- + #ifdef _MSC_VER + #define NOINLINE __declspec(noinline) + #else