From 8f181af8fb51ee1a1d860fe174b1c1becf9ab48d Mon Sep 17 00:00:00 2001 From: anematode Date: Sun, 26 Apr 2026 09:30:03 +0200 Subject: [PATCH] Get universal binary working for clang on Linux A few changes were necessary, mainly because clang LTO works a bit differently than GCC Doesn't yet work on Windows small Makefile cleanups closes https://github.com/official-stockfish/Stockfish/pull/6764 No functional change --- scripts/check_universal.sh | 7 +++++++ src/Makefile | 21 +++++++++++++++++---- src/nnue/network.cpp | 30 +++++++++++------------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/scripts/check_universal.sh b/scripts/check_universal.sh index 9ed8b2cc2..00209157a 100755 --- a/scripts/check_universal.sh +++ b/scripts/check_universal.sh @@ -37,6 +37,13 @@ icl:x86-64-avx512icl adl:x86-64-avxvnni " +BINARY_SIZE=$(wc -c < "$STOCKFISH_EXE") +MAX_SIZE=$((150 * 1024 * 1024)) +if [ "$BINARY_SIZE" -gt "$MAX_SIZE" ]; then + printf 'check_universal.sh: binary size %d bytes exceeds 150 MB limit\n' "$BINARY_SIZE" >&2 + exit 1 +fi + FAIL=0 i=0 for pair in $PAIRS; do diff --git a/src/Makefile b/src/Makefile index 0d31c9f5c..e5226f95c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1043,6 +1043,14 @@ profile-build: net config-sanity objclean profileclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean endif +ifeq ($(comp), clang) +LTO_OBJ_SUFFIX := .lto.o +SAVE_TEMPS := -Wl,--save-temps +else +LTO_OBJ_SUFFIX := .ltrans0.ltrans.o +SAVE_TEMPS := -save-temps +endif + universal-object-pgo: objclean profileclean universalclean @echo "" @echo "Step 1/4. Building instrumented executable ..." @@ -1054,14 +1062,14 @@ universal-object-pgo: objclean profileclean universalclean @echo "" @echo "Step 3/4. Building optimized object files ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='-save-temps $(NNUE_EMBED_OBJ)' $(profile_use) + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='$(SAVE_TEMPS) $(NNUE_EMBED_OBJ)' $(profile_use) @echo "" @echo "Step 4/4. Extracting LTO-optimized relocatable object ..." - cp $(basename $(EXE)).ltrans0.ltrans.o stockfish.o + cp "$(basename $(EXE))$(LTO_OBJ_SUFFIX)" stockfish.o universal-object-nopgo: objclean universalclean - $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='-save-temps $(NNUE_EMBED_OBJ)' all - cp $(basename $(EXE)).ltrans0.ltrans.o stockfish.o + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='$(SAVE_TEMPS) $(NNUE_EMBED_OBJ)' all + cp "$(basename $(EXE))$(LTO_OBJ_SUFFIX)" stockfish.o strip: $(STRIP) $(EXE) @@ -1320,8 +1328,13 @@ $(TEMP_DIR)/%/stockfish.o: $(TEMP_DIR)/%/.setup $(NNUE_EMBED_OBJ) $(NET_SENTINEL $(MAKE) -C $(TEMP_DIR)/$* $(UOBJ_TGT) \ ARCH=$* CXX=$(CXX) \ NNUE_EMBED_OBJ=$(NNUE_EMBED_OBJ) +# Drop COMDAT groups; clang LTO leaves them around internalized LOCAL +# template instantiations which causes problems at the final link + objcopy -R .group $@ +# Rename .init_array (Linux) or .ctors (Windows) so we can manually invoke them objcopy --rename-section .init_array=$(call arch-suffix,$*)_init $@ objcopy --rename-section .ctors=$(call arch-suffix,$*)_init $@ +# Make the array inert objcopy --set-section-flags $(call arch-suffix,$*)_init=alloc,data $@ $(UNIVERSAL_EXE): $(UNIVERSAL_ENTRY_OBJ) $(NNUE_EMBED_OBJ) $(ARCH_OBJS) diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index e2b90b080..b77d87178 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -54,32 +54,24 @@ INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall); extern const unsigned char gEmbeddedNNUEBigData[] WEAK_SYM = { #embed EvalFileDefaultNameBig }; -extern const unsigned int gEmbeddedNNUEBigSize WEAK_SYM = sizeof(gEmbeddedNNUEBigData); -extern const unsigned char* const gEmbeddedNNUEBigEnd WEAK_SYM = - gEmbeddedNNUEBigData + gEmbeddedNNUEBigSize; -extern const unsigned char gEmbeddedNNUESmallData[] WEAK_SYM = { +extern const unsigned int gEmbeddedNNUEBigSize WEAK_SYM = sizeof(gEmbeddedNNUEBigData); +extern const unsigned char gEmbeddedNNUESmallData[] WEAK_SYM = { #embed EvalFileDefaultNameSmall }; -extern const unsigned int gEmbeddedNNUESmallSize WEAK_SYM = sizeof(gEmbeddedNNUESmallData); -extern const unsigned char* const gEmbeddedNNUESmallEnd WEAK_SYM = - gEmbeddedNNUESmallData + gEmbeddedNNUESmallSize; +extern const unsigned int gEmbeddedNNUESmallSize WEAK_SYM = sizeof(gEmbeddedNNUESmallData); #else -const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; -const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1]; -const unsigned int gEmbeddedNNUEBigSize = 1; -const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; -const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1]; -const unsigned int gEmbeddedNNUESmallSize = 1; +const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; +const unsigned int gEmbeddedNNUEBigSize = 1; +const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; +const unsigned int gEmbeddedNNUESmallSize = 1; #endif namespace { struct EmbeddedNNUE { - EmbeddedNNUE(const unsigned char* embeddedData, - const unsigned char* embeddedEnd, - const unsigned int embeddedSize) : + EmbeddedNNUE(const unsigned char* embeddedData, const unsigned int embeddedSize) : data(embeddedData), - end(embeddedEnd), + end(embeddedData + embeddedSize), size(embeddedSize) {} const unsigned char* data; const unsigned char* end; @@ -90,9 +82,9 @@ using namespace Stockfish::Eval::NNUE; EmbeddedNNUE get_embedded(EmbeddedNNUEType type) { if (type == EmbeddedNNUEType::BIG) - return EmbeddedNNUE(gEmbeddedNNUEBigData, gEmbeddedNNUEBigEnd, gEmbeddedNNUEBigSize); + return EmbeddedNNUE(gEmbeddedNNUEBigData, gEmbeddedNNUEBigSize); else - return EmbeddedNNUE(gEmbeddedNNUESmallData, gEmbeddedNNUESmallEnd, gEmbeddedNNUESmallSize); + return EmbeddedNNUE(gEmbeddedNNUESmallData, gEmbeddedNNUESmallSize); } }