diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 473665ec2..53ee63e07 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -92,3 +92,102 @@ jobs: . !.git !.output + + UniversalLinux: + name: Ubuntu x86-64 Universal + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Install GCC 15 + uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 + with: + version: 15 + + - name: Download SDE package + uses: petarpetrovt/setup-sde@f0fa5971dc275704531e94264dd23250c442aa41 # @v2.4 + with: + environmentVariableName: SDE_DIR + sdeVersion: 9.33.0 + + - name: Build universal binary + run: make -j4 -C src ARCH=x86-64-universal profile-build RUN_PREFIX="$SDE_DIR/sde -future --" + + - name: Strip binary + run: strip src/stockfish + + - name: Rename binary + run: mv src/stockfish stockfish-ubuntu-x86-64-universal + + - name: Extract the bench number from the commit history + run: | + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" + + - name: Check arch selection under SDE + run: ./scripts/check_universal.sh ./stockfish-ubuntu-x86-64-universal "$SDE_DIR/sde" "$benchref" + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@v4 + with: + name: ubuntu x86-64-universal + path: stockfish-ubuntu-x86-64-universal + + UniversalWindows: + name: Windows x86-64 Universal + runs-on: windows-2022 + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Setup msys and install required packages + uses: msys2/setup-msys2@v2 + with: + msystem: mingw64 + install: mingw-w64-x86_64-gcc make git zip + + - name: Download SDE package + uses: petarpetrovt/setup-sde@f0fa5971dc275704531e94264dd23250c442aa41 # @v2.4 + with: + environmentVariableName: SDE_DIR + sdeVersion: 9.33.0 + + - name: Build universal binary + run: | + SDE_PATH=$(cygpath "$SDE_DIR") + make -j4 -C src ARCH=x86-64-universal profile-build RUN_PREFIX="$SDE_PATH/sde.exe -future --" + + - name: Strip binary + run: strip src/stockfish.exe + + - name: Rename binary + run: mv src/stockfish.exe stockfish-windows-x86-64-universal.exe + + - name: Extract the bench number from the commit history + run: | + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" + + - name: Check arch selection under SDE + run: ./scripts/check_universal.sh ./stockfish-windows-x86-64-universal.exe "$SDE_DIR/sde" "$benchref" + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@v4 + with: + name: windows x86-64-universal + path: stockfish-windows-x86-64-universal.exe diff --git a/.gitignore b/.gitignore index 2a3d8299a..7ed725ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ **/*.o **/*.s src/.depend +src/temp_builds .build_sha.txt .build_date.txt @@ -16,4 +17,4 @@ src/-lstdc++.res tsan.supp __pycache__/ tests/syzygy -tests/bench_tmp.epd \ No newline at end of file +tests/bench_tmp.epd diff --git a/scripts/check_universal.sh b/scripts/check_universal.sh new file mode 100755 index 000000000..9ed8b2cc2 --- /dev/null +++ b/scripts/check_universal.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +# Verify that the universal binary selects the correct per-arch build under +# Intel SDE emulation for a range of target CPUs +# +# Usage: check_universal.sh STOCKFISH_EXE SDE_EXE EXPECTED_BENCH + +set -eu + +if [ $# -ne 3 ]; then + echo "Usage: $0 STOCKFISH_EXE SDE_EXE EXPECTED_BENCH" >&2 + exit 2 +fi + +STOCKFISH_EXE=$1 +SDE_EXE=$2 +EXPECTED_BENCH=$3 + +if [ "$(uname)" = 'Linux' ]; then +# Windows 11 doesn't support these old arches +PAIRS=" +p4p:x86-64 +nhm:x86-64-sse41-popcnt +" +else +PAIRS='' +fi + +PAIRS="$PAIRS +snb:x86-64-sse41-popcnt +ivb:x86-64-sse41-popcnt +hsw:x86-64-bmi2 +skl:x86-64-bmi2 +skx:x86-64-avx512 +clx:x86-64-vnni512 +icl:x86-64-avx512icl +adl:x86-64-avxvnni +" + +FAIL=0 +i=0 +for pair in $PAIRS; do + i=$((i + 1)) + cpu=${pair%%:*} + expected_compiler=${pair##*:} + compiler_out=$("$SDE_EXE" "-$cpu" -- "$STOCKFISH_EXE" compiler 2>&1 || true) + bench_out=$("$SDE_EXE" "-$cpu" -- "$STOCKFISH_EXE" bench 2>&1 || true) + actual_compiler=$(printf '%s\n' "$compiler_out" | awk -F: '/Compilation architecture/ { + sub(/^[[:space:]]+/, "", $2); sub(/[[:space:]]+$/, "", $2); print $2; exit + }') + actual_bench=$(printf '%s\n' "$bench_out" | awk -F: '/Nodes searched/ { + sub(/^[[:space:]]+/, "", $2); sub(/[[:space:]]+$/, "", $2); print $2; exit + }') + if [ "$actual_compiler" != "$expected_compiler" ] || [ "$actual_bench" != "$EXPECTED_BENCH" ]; then + printf '===== CPU %s output (expected %s/%s, got %s/%s) =====\n' \ + "$cpu" "$expected_compiler" "$EXPECTED_BENCH" "${actual_compiler:--}" "$actual_bench" >&2 + FAIL=1 + else + printf 'CPU %s ok\n' "$cpu" >&2 + fi +done + +if [ "$FAIL" != 0 ]; then + echo "check_universal.sh: failed" + exit 1 +fi + +echo "check_universal.sh: Good! Universal binary has correct hardware detection." diff --git a/src/Makefile b/src/Makefile index f5d0d3c03..fd37fcd24 100644 --- a/src/Makefile +++ b/src/Makefile @@ -143,7 +143,7 @@ endif # explicitly check for the list of supported architectures (as listed with make help), # the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true` ifeq ($(ARCH), $(filter $(ARCH), \ - x86-64-avx512icl x86-64-vnni512 x86-64-avx512 x86-64-avxvnni \ + x86-64-universal x86-64-avx512icl x86-64-vnni512 x86-64-avx512 x86-64-avxvnni \ x86-64-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \ armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \ @@ -153,6 +153,13 @@ else SUPPORTED_ARCH=false endif +ifeq ($(ARCH), x86-64-universal) + UNIVERSAL_BUILD=true +else + UNIVERSAL_BUILD=false +endif + + optimize = yes debug = no sanitize = none @@ -944,6 +951,7 @@ help: echo "Supported archs:" && \ echo "" && \ echo "native > select the best architecture for the host processor (default)" && \ + echo "x86-64-universal > x86 64-bit with automatic runtime selection of the best architecture" && \ echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lake or AMD Zen 4" && \ echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \ echo "x86-64-avx512 > x86 64-bit with avx512 support" && \ @@ -985,14 +993,15 @@ help: echo "" && \ echo "Simple examples. If you don't know what to do, you likely want to run one of: " && \ echo "" && \ - echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " && \ - echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems " && \ - echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " && \ + echo "make -j profile-build ARCH=native # a fast compile for local use" && \ + echo "make -j profile-build ARCH=x86-64-avx2 # A portable compile for most x86-64 systems " && \ + echo "make -j profile-build ARCH=x86-64 # A portable compile for x86-64 systems " && \ echo "" && \ echo "Advanced examples, for experienced users: " && \ echo "" && \ echo "make -j profile-build ARCH=x86-64-avxvnni" && \ - echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" && \ + echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc CXX=g++-12.0" && \ + echo 'make -j profile-build ARCH=x86-64-universal RUN_PREFIX="/path/to/sde -future --" CXX=g++-15' && \ echo "make -j build ARCH=x86-64-ssse3 COMP=clang" && \ echo "" ifneq ($(SUPPORTED_ARCH), true) @@ -1011,6 +1020,7 @@ endif analyze: net config-sanity objclean $(MAKE) -k ARCH=$(ARCH) COMP=$(COMP) $(OBJS) +ifeq ($(UNIVERSAL_BUILD),false) build: net config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all @@ -1029,6 +1039,27 @@ profile-build: net config-sanity objclean profileclean @echo "" @echo "Step 4/4. Deleting profile data ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean +endif + +universal-object-pgo: objclean profileclean universalclean + @echo "" + @echo "Step 1/4. Building instrumented executable ..." + $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) + @echo "" + @echo "Step 2/4. Running benchmark for pgo-build ..." + $(PGOBENCH) > PGOBENCH.out 2>&1 + tail -n 4 PGOBENCH.out + @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) + @echo "" + @echo "Step 4/4. Extracting LTO-optimized relocatable object ..." + cp $(basename $(EXE)).ltrans0.ltrans.o 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 strip: $(STRIP) $(EXE) @@ -1039,7 +1070,7 @@ install: $(STRIP) $(BINDIR)/$(EXE) # clean all -clean: objclean profileclean +clean: objclean profileclean universalclean @rm -f .depend *~ core # clean binaries and objects @@ -1056,6 +1087,9 @@ profileclean: @rm -f stockfish.res @rm -f ./-lstdc++.res +universalclean: + @rm -rf $(TEMP_DIR) + # evaluation network (nnue) net: @$(SHELL) ../scripts/net.sh @@ -1191,5 +1225,105 @@ icx-profile-use: -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean format config-sanity)) +ifeq ($(UNIVERSAL_BUILD),false) -include .depend endif +endif + +### ========================================================================== +### Section 6. Universal binary driver +### ========================================================================== +# +# Usage: +# make -j ARCH=x86-64-universal build # non-PGO +# make -j ARCH=x86-64-universal profile-build # PGO (use RUN_PREFIX for intel SDE) + +TEMP_DIR := $(CURDIR)/temp_builds +UNIVERSAL_EXE := $(CURDIR)/$(EXE) + +ifeq ($(UNIVERSAL_BUILD),true) + +UNIVERSAL_ARCHES := x86-64 x86-64-sse41-popcnt x86-64-avx2 x86-64-bmi2 \ + x86-64-avxvnni x86-64-avx512 x86-64-vnni512 x86-64-avx512icl + +# GCC 15+ required for #embed +GCC_MAJOR := $(shell $(CXX) -dumpversion 2>/dev/null | cut -d. -f1) +ifneq ($(shell test "$(GCC_MAJOR)" -ge 15 2>/dev/null && echo ok),ok) +$(error Universal build needs GCC 15+ for \#embed support; CXX=$(CXX) reports version '$(GCC_MAJOR)') +endif + +UNIVERSAL_SRC_DIR := $(CURDIR)/universal +NNUE_EMBED_OBJ := $(TEMP_DIR)/nnue_embed.o +UNIVERSAL_ENTRY_OBJ := $(TEMP_DIR)/entry_x86.o +NET_SENTINEL := $(TEMP_DIR)/.net-done + +# Top-level tracked items in src/ (directories and files) +TRACKED_TOP := $(shell git -C $(CURDIR) ls-files 2>/dev/null | awk -F/ '{print $$1}' | sort -u) + +# Baseline x86-64 must come first in the final link (Windows --allow-multiple-definition +# uses the first copy of inline duplicates) +ARCH_OBJS := $(TEMP_DIR)/x86-64/stockfish.o \ + $(patsubst %,$(TEMP_DIR)/%/stockfish.o,$(filter-out x86-64,$(UNIVERSAL_ARCHES))) + +UNIVERSAL_FINAL_FLAGS := -fno-exceptions -Os -static-libstdc++ -static-libgcc -Wno-c++26-extensions +ifeq ($(KERNEL),Linux) + UNIVERSAL_FINAL_FLAGS += -lrt -lpthread +else + UNIVERSAL_FINAL_FLAGS += -Wl,--allow-multiple-definition -static +endif + +# $(call arch-namespace,x86-64-avx2) => Stockfish_x86_64_avx2 +arch-suffix = $(subst -,_,$(1)) +arch-namespace = Stockfish_$(call arch-suffix,$(1)) +# Itanium ABI mangled name for Stockfish_::main(int, char**) +arch-mangled = _ZN$(shell printf %s '$(call arch-namespace,$(1))' | wc -c)$(call arch-namespace,$(1))4mainEiPPc + +arch-cxxflags = --embed-dir=$(CURDIR) -DStockfish=$(call arch-namespace,$(1)) -DUNIVERSAL_BINARY \ + -Wl,--defsym=main=$(call arch-mangled,$(1)) -Wno-c++26-extensions + +UOBJ_TGT ?= universal-object-pgo + +.PHONY: build profile-build universal-clean +build: UOBJ_TGT := universal-object-nopgo +build: $(UNIVERSAL_EXE) +profile-build: UOBJ_TGT := universal-object-pgo +profile-build: $(UNIVERSAL_EXE) + +$(TEMP_DIR): + @mkdir -p $@ + +# Download nnue files once in src/ (reused by every sub-arch via --embed-dir). +$(NET_SENTINEL): | $(TEMP_DIR) + @cd $(CURDIR) && $(SHELL) ../scripts/net.sh + @touch $@ + +$(NNUE_EMBED_OBJ): $(UNIVERSAL_SRC_DIR)/nnue_embed.cpp $(NET_SENTINEL) | $(TEMP_DIR) + $(CXX) -O2 -Wno-c++26-extensions --embed-dir=$(CURDIR) -c $< -o $@ + +$(UNIVERSAL_ENTRY_OBJ): $(UNIVERSAL_SRC_DIR)/entry_x86.cpp | $(TEMP_DIR) + $(CXX) -O2 -c $< -o $@ + +# Symlink tracked top-level items from src/ into temp_builds//. +$(TEMP_DIR)/%/.setup: | $(TEMP_DIR) + @rm -rf $(TEMP_DIR)/$* + @mkdir -p $(TEMP_DIR)/$* + @$(foreach f,$(TRACKED_TOP),ln -s $(CURDIR)/$(f) $(TEMP_DIR)/$*/$(f);) + @touch $@ + +# Per-arch stockfish.o +.SECONDARY: $(patsubst %,$(TEMP_DIR)/%/.setup,$(UNIVERSAL_ARCHES)) + +$(TEMP_DIR)/%/stockfish.o: $(TEMP_DIR)/%/.setup $(NNUE_EMBED_OBJ) $(NET_SENTINEL) + +ENV_CXXFLAGS='$(ENV_CXXFLAGS) $(call arch-cxxflags,$*)' \ + $(MAKE) -C $(TEMP_DIR)/$* $(UOBJ_TGT) \ + ARCH=$* CXX=$(CXX) \ + NNUE_EMBED_OBJ=$(NNUE_EMBED_OBJ) + objcopy --rename-section .init_array=$(call arch-suffix,$*)_init $@ + objcopy --rename-section .ctors=$(call arch-suffix,$*)_init $@ + objcopy --set-section-flags $(call arch-suffix,$*)_init=alloc,data $@ + +$(UNIVERSAL_EXE): $(UNIVERSAL_ENTRY_OBJ) $(NNUE_EMBED_OBJ) $(ARCH_OBJS) + $(CXX) -o $@ $(UNIVERSAL_ENTRY_OBJ) $(NNUE_EMBED_OBJ) $(ARCH_OBJS) $(UNIVERSAL_FINAL_FLAGS) + @echo "Universal binary built: $@" + +endif # UNIVERSAL_BUILD diff --git a/src/main.cpp b/src/main.cpp index 9a7376efb..fccb2f21d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,12 @@ using namespace Stockfish; +#ifdef UNIVERSAL_BINARY +namespace Stockfish { + +int main(int argc, char* argv[]); +#endif + int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; @@ -41,3 +47,7 @@ int main(int argc, char* argv[]) { return 0; } + +#ifdef UNIVERSAL_BINARY +} +#endif diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index d1f2b14c3..e2b90b080 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -43,9 +43,26 @@ // const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end // const unsigned int gEmbeddedNNUESize; // the size of the embedded file // Note that this does not work in Microsoft Visual Studio. -#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) +#if !defined(UNIVERSAL_BINARY) && !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig); INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall); +#elif defined(UNIVERSAL_BINARY) + // When building for the universal binary, use C++26 #embed with weak symbols so that a + // separate, non-LTO nnue_embed.o (with strong symbols) can override them during the LTO link, + // (INCBIN can't deduplicate.) + #define WEAK_SYM __attribute__((weak)) +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 = { + #embed EvalFileDefaultNameSmall +}; +extern const unsigned int gEmbeddedNNUESmallSize WEAK_SYM = sizeof(gEmbeddedNNUESmallData); +extern const unsigned char* const gEmbeddedNNUESmallEnd WEAK_SYM = + gEmbeddedNNUESmallData + gEmbeddedNNUESmallSize; #else const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1]; diff --git a/src/universal/entry_x86.cpp b/src/universal/entry_x86.cpp new file mode 100644 index 000000000..fb1d8bc93 --- /dev/null +++ b/src/universal/entry_x86.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +#define DEFINE_BUILD(x) \ + namespace Stockfish_##x { \ + extern int main(int argc, char* argv[]); \ + } \ + extern "C" void (*__start_##x##_init[])(void); \ + extern "C" void (*__stop_##x##_init[])(void); \ + int entry_##x(int argc, char* argv[]) { \ + unsigned count = __stop_##x##_init - __start_##x##_init; \ + for (unsigned i = 0; i < count; i++) \ + __start_##x##_init[i](); \ + return Stockfish_##x::main(argc, argv); \ + } + +DEFINE_BUILD(x86_64) +DEFINE_BUILD(x86_64_sse41_popcnt) +DEFINE_BUILD(x86_64_avx2) +DEFINE_BUILD(x86_64_bmi2) +DEFINE_BUILD(x86_64_avxvnni) +DEFINE_BUILD(x86_64_avx512) +DEFINE_BUILD(x86_64_vnni512) +DEFINE_BUILD(x86_64_avx512icl) + +// Zen, Zen+ and Zen 2 (AMD family 17h) have microcoded pdep/pext +static bool has_slow_bmi2() { + unsigned int eax, ebx, ecx, edx; + __cpuid(0, eax, ebx, ecx, edx); + if (ebx != 0x68747541 || edx != 0x69746e65 || ecx != 0x444d4163) // "AuthenticAMD" + return false; + __cpuid(1, eax, ebx, ecx, edx); + unsigned family = (eax >> 8) & 0xf; + if (family == 0xf) + family += (eax >> 20) & 0xff; + return family == 0x17; +} + +constexpr uint64_t XCR0_SSE_AVX_MASK = 0x06; +constexpr uint64_t XCR0_AVX512_MASK = 0xE6; + +int main(int argc, char* argv[]) { + unsigned _; + unsigned max_leaf = __get_cpuid_max(0, &_); + if (max_leaf < 1U) + { + return entry_x86_64(argc, argv); + } + unsigned int eax, ebx, ecx, edx; + __cpuid(1, eax, ebx, ecx, edx); + if (!(ecx & (1U << 19)) || !(ecx & (1U << 23))) + { // no popcnt or no sse4.1 + return entry_x86_64(argc, argv); + } + + bool xgetbv_ok = (ecx & (1U << 27)) != 0; // OSXSAVE + uint64_t xcr0 = 0; + + if (xgetbv_ok) + { + uint32_t xcr0_eax, xcr0_edx; + uint32_t xcr0_ecx = 0; + asm("xgetbv" : "=a"(xcr0_eax), "=d"(xcr0_edx) : "c"(xcr0_ecx)); + xcr0 = (static_cast(xcr0_edx) << 32) | xcr0_eax; + } + + + bool leaf_supported = __get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx); + if (!leaf_supported || !(ebx & (1U << 5)) || !xgetbv_ok + || (xcr0 & XCR0_SSE_AVX_MASK) != XCR0_SSE_AVX_MASK) + { + // CPUID query not supported, no avx2, missing xgetbv, or missing OS restore for AVX regs + return entry_x86_64_sse41_popcnt(argc, argv); + } + + if (!(ebx & (1U << 8)) || has_slow_bmi2()) + { // no or slow bmi2 + return entry_x86_64_avx2(argc, argv); + } + + if (!(ebx & (1U << 16)) || !(ebx & (1U << 31)) || !(ebx & (1U << 30)) + || (xcr0 & XCR0_AVX512_MASK) != XCR0_AVX512_MASK) + { + // no avx512f/vl/bw, or OS doesn't restore AVX-512 regs + bool leaf_supported = __get_cpuid_count(7, 1, &eax, &ebx, &ecx, &edx); + if (leaf_supported && eax & (1U << 4)) + { // avxvnni + return entry_x86_64_avxvnni(argc, argv); + } + else + { + return entry_x86_64_bmi2(argc, argv); + } + } + + if (!(ecx & 1U << 11 /* vnni512 */)) + { + return entry_x86_64_avx512(argc, argv); + } + + if (!(ebx & 1U << 21 /* ifma */) || !(ecx & 1U << 1 /* vbmi */) || !(ecx & 1U << 6 /* vbmi2 */) + || !(ecx & 1U << 14 /* vpopcntdq */) || !(ecx & 1U << 12 /* bitalg */) + || !(ecx & 1U << 10 /* vpclmulqdq */) || !(ecx & 1U << 8 /* gfni */) + || !(ecx & 1U << 9 /* vaes */)) + { + return entry_x86_64_vnni512(argc, argv); + } + + return entry_x86_64_avx512icl(argc, argv); +} diff --git a/src/universal/nnue_embed.cpp b/src/universal/nnue_embed.cpp new file mode 100644 index 000000000..738cd8ac7 --- /dev/null +++ b/src/universal/nnue_embed.cpp @@ -0,0 +1,16 @@ +// Standalone NNUE embedding for universal binary builds + +#include "../evaluate.h" + +extern const unsigned char gEmbeddedNNUEBigData[] = { +#embed EvalFileDefaultNameBig +}; +extern const unsigned int gEmbeddedNNUEBigSize = sizeof(gEmbeddedNNUEBigData); +extern const unsigned char* const gEmbeddedNNUEBigEnd = gEmbeddedNNUEBigData + gEmbeddedNNUEBigSize; + +extern const unsigned char gEmbeddedNNUESmallData[] = { +#embed EvalFileDefaultNameSmall +}; +extern const unsigned int gEmbeddedNNUESmallSize = sizeof(gEmbeddedNNUESmallData); +extern const unsigned char* const gEmbeddedNNUESmallEnd = + gEmbeddedNNUESmallData + gEmbeddedNNUESmallSize;