From 459f6c73181e1ee327ba270606cdbadef5768a5c Mon Sep 17 00:00:00 2001 From: Davide Angelocola Date: Fri, 17 Jul 2026 18:22:57 +0200 Subject: [PATCH] build: experiment with LTO on Linux (ELF only) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add -flto for linux-* classifiers only. zig's Mach-O backend is a self-hosted linker with no LTO support ("error: LTO requires using LLD" even at the -c compile stage) — confirmed no workaround exists locally (no real ld64.lld available, zig cc doesn't expose a linker toggle). ELF is different: zig 0.16 still links it through the bundled real lld by default, and -flto compiles and links cleanly when cross-compiled to x86_64-linux-gnu/aarch64-linux-gnu from this host, with the full public API surface (verified via llvm-readelf --dyn-syms) intact after LTO. What isn't verified locally: whether the LTO-linked library is actually correct at runtime — this Mac can't execute Linux ELF binaries. zstd/pom.xml and integration-tests/pom.xml activate their native-classifier profile by host OS/arch, so ci.yml's ubuntu-latest job will natively build and test-run exactly the linux-x86_64 classifier this touches. Marking this experimental pending that signal, rather than folding it into #71 (asm/arch/hardening), which only contains changes verified end-to-end on this host. osx-*/windows-* classifiers are untouched (LTO_FLAG stays empty); confirmed unaffected locally (rebuilt + zstd/integration-tests suite passes on osx-aarch64, all six classifiers still cross-compile). Ref #70 --- scripts/build-zstd.sh | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/build-zstd.sh b/scripts/build-zstd.sh index cc1db19..9a2db3a 100755 --- a/scripts/build-zstd.sh +++ b/scripts/build-zstd.sh @@ -110,11 +110,25 @@ case "$CLASSIFIER" in *-aarch64) ARCH_FLAG="-mcpu=generic+crc" ;; esac +# LTO: zig's Mach-O backend is a self-hosted linker with no LTO support +# ("error: LTO requires using LLD"), so this is ELF-only for now. zig 0.16 +# still links ELF through its bundled real lld by default (the self-hosted +# ELF linker is early-stage/opt-in), so -flto works there — confirmed by +# cross-compiling this classifier to a .o and a linked .so locally. What +# can't be confirmed off a Linux host: that the LTO-linked library is +# actually correct at runtime, hence gating this on CI (which natively +# builds and test-runs linux-x86_64/linux-aarch64) rather than shipping it +# straight to every platform like the ASM/arch/hardening flags in #70. +LTO_FLAG="" +case "$CLASSIFIER" in + linux-*) LTO_FLAG="-flto" ;; +esac + # Decode (not encode) formats back to zstd v0.4, matching what zstd-jni # ships by default. v0.1-v0.3 stay off (ZSTD_LEGACY_SUPPORT>=4 skips their # zstd_v0{1,2,3}.c decoders per legacy/zstd_legacy.h) — those predate zstd's # 1.0 stabilization and are vanishingly unlikely to show up in the wild. -CFLAGS="-O3 $ARCH_FLAG -DNDEBUG -DZSTD_LEGACY_SUPPORT=4 -DXXH_NAMESPACE=ZSTD_ $VIS_FLAG \ +CFLAGS="-O3 $ARCH_FLAG $LTO_FLAG -DNDEBUG -DZSTD_LEGACY_SUPPORT=4 -DXXH_NAMESPACE=ZSTD_ $VIS_FLAG \ -I$ZSTD_LIB -I$ZSTD_LIB/common -fPIC" i=0 @@ -131,7 +145,7 @@ wait SONAME_FLAG="" [ "$LIB_NAME" = "libzstd.so" ] && SONAME_FLAG="-Wl,-soname,libzstd.so.1" -zig cc -target "$ZIG_TARGET" -shared $STRIP_FLAG $SONAME_FLAG $LINK_EXTRA -o "$DEST_DIR/$LIB_NAME" "$WORK"/*.o +zig cc -target "$ZIG_TARGET" -shared $LTO_FLAG $STRIP_FLAG $SONAME_FLAG $LINK_EXTRA -o "$DEST_DIR/$LIB_NAME" "$WORK"/*.o # lld emits a .pdb (debug database, multiple MB) and a .lib (import library) next # to a Windows .dll; neither is needed at runtime and both would be bundled into