Files
serverAppConfigs/scripts/build-llama-rdna4.sh
Frank eb2f1bb6bc build-llama-rdna4: bundle librocm-core
librccl DT_NEEDEDs librocm-core.so.1, so a GGML_HIP_RCCL=ON binary would not
exec from its bundled lib dir (measured: 'error while loading shared
libraries: librocm-core.so.1'). The layer-split build never pulled it in.
2026-09-20 15:25:09 -07:00

158 lines
8.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build mainline llama.cpp for Dankserver's two Radeon AI PRO R9700 (gfx1201,
# Navi 48) with DFlash2 speculative decoding.
#
# Run on Dankserver. Produces a self-contained build: the binary carries an
# RPATH of $ORIGIN/../lib and the ROCm runtime is copied beside it, so nothing
# depends on the host's ROCm packages (Ubuntu's 7.1) or on LD_LIBRARY_PATH.
#
# bash scripts/build-llama-rdna4.sh
#
# WHY A PRIVATE TOOLCHAIN (ROCm 7.14.0a20260624, ~17 GiB extracted): the host's
# ROCm is Ubuntu's 7.1 packaging, and the pre-existing RDNA4 llama.cpp build on
# this box (charlie12345/ROCmFPX) was built against this same TheRock nightly.
# Reusing it keeps the toolchain that is known to emit working gfx1201 code
# objects; the tarball is only downloaded when the prefix is missing.
#
# WHY THIS REF: master at fb27a525 carries what this service needs and nothing
# older does:
# * DFlash2 (`DFlash2DraftModel`): grouped dynamic depthwise convolution plus
# the candidate selector, detected from the draft GGUF
# (`selector_top_k > 0`) and served through the ordinary `--spec-type
# draft-dflash` path. Landed with ggml-org/llama.cpp#27342.
# * qwen35 MTP: the `draft-mtp` implementation covers qwen35's single trained
# MTP head (common/speculative.cpp), which the target GGUF carries.
# Do not "update to latest" casually: both features landed days apart and the
# service depends on the pair.
#
# gfx1201 IS NOT gfx1200. The ROCmFPX fork's docs/BUILD-AMD-ARCHITECTURES.md
# records that a gfx1200 build links, loads the model and then segfaults on
# these cards, so the arch list here is exactly gfx1201 and no other.
set -euo pipefail
LLAMA_SRC="${LLAMA_SRC:-/home/dank/GitPackages/llama.cpp-dflash2}"
LLAMA_REPO="${LLAMA_REPO:-https://github.com/ggml-org/llama.cpp.git}"
LLAMA_REF="${LLAMA_REF:-fb27a525d28381a16a4bb038858a10e4927381ca}"
BUILD_DIR="${BUILD_DIR:-$LLAMA_SRC/build-rdna4-rocm714}"
JOBS="${JOBS:-$(nproc)}"
ROCM_VERSION="${ROCM_VERSION:-7.14.0a20260624}"
ROCM_LOCAL_PATH="${ROCM_LOCAL_PATH:-${XDG_DATA_HOME:-$HOME/.local/share}/rocm-${ROCM_VERSION}}"
HIP_ARCH="${HIP_ARCH:-gfx1201}"
GGML_HIP_RCCL="${GGML_HIP_RCCL:-OFF}"
# ── Source ────────────────────────────────────────────────────────────
if [[ ! -d "$LLAMA_SRC/.git" ]]; then
echo "cloning $LLAMA_REPO into $LLAMA_SRC"
git clone "$LLAMA_REPO" "$LLAMA_SRC"
fi
git -C "$LLAMA_SRC" fetch --quiet origin "$LLAMA_REF" 2>/dev/null || git -C "$LLAMA_SRC" fetch --quiet origin
git -C "$LLAMA_SRC" checkout --detach "$LLAMA_REF"
echo "building llama.cpp $(git -C "$LLAMA_SRC" rev-parse --short HEAD) from $LLAMA_SRC"
# ── ROCm toolchain ────────────────────────────────────────────────────
if [[ ! -x "$ROCM_LOCAL_PATH/llvm/bin/clang" ]]; then
TARBALL_URL="https://rocm.nightlies.amd.com/tarball-multi-arch/therock-dist-linux-gfx120X-all-${ROCM_VERSION}.tar.gz"
TARBALL="$LLAMA_SRC/rocm-${ROCM_VERSION}.tar.gz"
if [[ ! -s "$TARBALL" ]]; then
echo "downloading ROCm $ROCM_VERSION from TheRock nightlies"
curl -fL --retry 5 -o "$TARBALL" "$TARBALL_URL"
fi
echo "extracting into $ROCM_LOCAL_PATH"
mkdir -p "$ROCM_LOCAL_PATH"
tar --use-compress-program=gzip -xf "$TARBALL" -C "$ROCM_LOCAL_PATH" --strip-components=1
fi
CLANG="$ROCM_LOCAL_PATH/llvm/bin/clang"
CLANGXX="$ROCM_LOCAL_PATH/llvm/bin/clang++"
[[ -x "$CLANG" && -x "$CLANGXX" ]] || { echo "no clang/clang++ under $ROCM_LOCAL_PATH" >&2; exit 1; }
# ggml-hip's CMake falls back to $ENV{ROCM_PATH} or /opt/rocm when these are
# unset, which links a second, mismatched HIP runtime next to the local one.
export ROCM_PATH="$ROCM_LOCAL_PATH"
export HIP_PATH="$ROCM_LOCAL_PATH"
# ── Configure ─────────────────────────────────────────────────────────
# GGML_HIP_RCCL defaults OFF: layer-split serving needs no collectives, and
# until 2026-09-20 this host's second card could not dispatch a RCCL kernel at
# all (its PCIe path had no atomic routing). With RCCL off, llama.cpp logs
# "internal AllReduce init failed ... falling back to meta-backend butterfly"
# and only --split-mode row/tensor pays for it. Set GGML_HIP_RCCL=ON (with a
# distinct BUILD_DIR) for the collective-backed runtime; the atomics
# precondition now holds on both cards.
cmake -S "$LLAMA_SRC" -B "$BUILD_DIR" \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER="$CLANG" \
-DCMAKE_CXX_COMPILER="$CLANGXX" \
-DCMAKE_CXX_FLAGS="-I${ROCM_LOCAL_PATH}/include" \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DGGML_HIP=ON \
-DGGML_HIP_RCCL="$GGML_HIP_RCCL" \
-DGGML_CUDA=OFF \
-DGGML_VULKAN=OFF \
-DGGML_NATIVE=OFF \
-DCMAKE_HIP_ARCHITECTURES="$HIP_ARCH" \
-DGPU_TARGETS="$HIP_ARCH" \
-DLLAMA_BUILD_SERVER=ON \
-DLLAMA_BUILD_WEBUI=OFF \
-DLLAMA_USE_PREBUILT_WEBUI=OFF \
-DLLAMA_BUILD_TESTS=OFF \
-DGGML_BUILD_TESTS=OFF \
-DCMAKE_PREFIX_PATH="$ROCM_LOCAL_PATH" \
-DROCM_DIR="$ROCM_LOCAL_PATH" \
-DBUILD_SHARED_LIBS=OFF
# ── Build ─────────────────────────────────────────────────────────────
cmake --build "$BUILD_DIR" -j "$JOBS" \
--target llama-server llama-cli llama-quantize llama-bench
# ── Bundle the runtime ────────────────────────────────────────────────
if command -v patchelf >/dev/null; then
LIB_DIR="$BUILD_DIR/lib"
mkdir -p "$LIB_DIR"
for pat in libamdhip64 librocblas libhipblas libhipblaslt librocsolver \
libroctx64 libamd_comgr libhsa-runtime64 librocm_kpack \
librocroller librocprofiler-register librocm-core; do
cp -v "$ROCM_LOCAL_PATH/lib/$pat".so* "$LIB_DIR/" 2>/dev/null || true
done
cp -v "$ROCM_LOCAL_PATH/lib/llvm/lib"/libLLVM.so* \
"$ROCM_LOCAL_PATH/lib/llvm/lib"/libclang-cpp.so* \
"$ROCM_LOCAL_PATH/lib/llvm/lib"/libomp.so* \
"$ROCM_LOCAL_PATH/lib/llvm/lib"/libomptarget.so* "$LIB_DIR/" 2>/dev/null || true
cp -v "$ROCM_LOCAL_PATH/lib/rocm_sysdeps/lib"/librocm_sysdeps_*.so* "$LIB_DIR/" 2>/dev/null || true
# A RCCL build links librccl.so.1 (imported shared even with
# BUILD_SHARED_LIBS=OFF), and the binaries resolve libraries through
# $ORIGIN/../lib — so the collective library must ride beside the rest or
# llama-server fails to exec at all.
if [[ "$GGML_HIP_RCCL" == "ON" ]]; then
cp -v "$ROCM_LOCAL_PATH"/lib/librccl.so* "$LIB_DIR/" 2>/dev/null || true
fi
# rocBLAS and hipBLASLt look for their device code at a FIXED depth under
# the binary's lib dir: $ORIGIN/../lib/rocblas/library and
# $ORIGIN/../lib/hipblaslt/library/<arch>. `cp -r SRC DST/` with a missing
# DST creates DST as a copy of SRC and flattens the source by one level, so
# the engine loads, serves nothing, and dies on the FIRST request with
# rocBLAS error: Cannot read .../lib/rocblas/library/TensileLibrary.dat:
# No such file or directory for GPU arch : gfx1201
# (measured 2026-09-16, after a build that looked perfectly healthy).
# Declare the destination, then copy the contents into it.
if [[ -d "$ROCM_LOCAL_PATH/lib/rocblas/library" ]]; then
mkdir -p "$LIB_DIR/rocblas/library"
cp -r "$ROCM_LOCAL_PATH/lib/rocblas/library/." "$LIB_DIR/rocblas/library/"
fi
if [[ -d "$ROCM_LOCAL_PATH/lib/hipblaslt/library" ]]; then
mkdir -p "$LIB_DIR/hipblaslt/library"
cp -r "$ROCM_LOCAL_PATH/lib/hipblaslt/library/." "$LIB_DIR/hipblaslt/library/"
fi
for bin in "$BUILD_DIR"/bin/llama-*; do
[[ -f "$bin" ]] && patchelf --set-rpath '$ORIGIN/../lib' "$bin"
done
else
echo "patchelf missing; run with LD_LIBRARY_PATH=$ROCM_LOCAL_PATH/lib:$ROCM_LOCAL_PATH/lib/llvm/lib" >&2
fi
echo
echo "built for $HIP_ARCH:"
"$BUILD_DIR/bin/llama-server" --version 2>&1 | head -3
ldd "$BUILD_DIR/bin/llama-server" 2>/dev/null | grep -E "not found" && exit 1 || true
echo " $BUILD_DIR/bin/llama-server"