diff --git a/.github/ci/universal_matrix.json b/.github/ci/universal_matrix.json index f522cf19a..794b6384a 100644 --- a/.github/ci/universal_matrix.json +++ b/.github/ci/universal_matrix.json @@ -1,18 +1,42 @@ { - "config": [ + "include": [ { - "name": "Ubuntu 22.04 GCC Universal", - "os": "ubuntu-22.04", - "simple_name": "linux", - "archive_ext": "tar.gz" + "config": { + "name": "Ubuntu 22.04 GCC Universal", + "os": "ubuntu-22.04", + "simple_name": "linux", + "archive_ext": "tar.gz" + }, + "binaries": "x86-64-universal" }, { - "name": "Windows 2022 Mingw-w64 GCC Universal", - "os": "windows-2022", - "simple_name": "windows", - "ext": ".exe", - "archive_ext": "zip" + "config": { + "name": "Windows 2022 Mingw-w64 GCC Universal", + "os": "windows-2022", + "simple_name": "windows", + "ext": ".exe", + "archive_ext": "zip" + }, + "binaries": "x86-64-universal" + }, + { + "config": { + "name": "Ubuntu 22.04 GCC Universal arm64", + "os": "ubuntu-22.04-arm", + "simple_name": "linux", + "archive_ext": "tar.gz" + }, + "binaries": "arm64-universal" + }, + { + "config": { + "name": "Windows 11 Clang Universal arm64", + "os": "windows-11-arm", + "simple_name": "windows", + "ext": ".exe", + "archive_ext": "zip" + }, + "binaries": "arm64-universal" } - ], - "binaries": ["x86-64-universal"] + ] } diff --git a/.github/workflows/universal_compilation.yml b/.github/workflows/universal_compilation.yml index aee36d8a3..86eaf49bf 100644 --- a/.github/workflows/universal_compilation.yml +++ b/.github/workflows/universal_compilation.yml @@ -44,10 +44,8 @@ jobs: - name: Check arch selection under SDE run: ./scripts/check_universal.sh ./stockfish-linux-x86-64-universal "$SDE_DIR/sde" "$benchref" - - name: Remove non src files - run: | - rm -fR src/temp_builds - git -C src clean -fx + - name: Remove non-src files + run: git clean -fx -- src - name: Upload artifact for (pre)-release uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -103,10 +101,8 @@ jobs: - name: Check arch selection under SDE run: ./scripts/check_universal.sh ./stockfish-windows-x86-64-universal.exe "$SDE_DIR/sde" "$benchref" - - name: Remove non src files - run: | - rm -fR src/temp_builds - git -C src clean -fx + - name: Remove non-src files + run: git clean -fx -- src - name: Upload artifact for (pre)-release uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -116,3 +112,108 @@ jobs: . !.git !.output + + UniversalLinuxARM64: + name: Linux arm64 + runs-on: ubuntu-22.04-arm + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Install GCC 15 + uses: egor-tensin/setup-gcc@a2861a8b8538f49cf2850980acccf6b05a1b2ae4 # v2.0 + with: + version: 15 + + - name: Install qemu-user + run: | + sudo apt-get update + sudo apt-get install -y qemu-user + + - name: Build universal binary + run: make -j4 -C src ARCH=armv8-universal profile-build + + - name: Strip binary + run: strip src/stockfish + + - name: Rename binary + run: mv src/stockfish stockfish-linux-arm64-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 qemu + run: ./scripts/check_universal_arm.sh ./stockfish-linux-arm64-universal "$benchref" + + - name: Remove non-src files + run: git clean -fx -- src + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: linux arm64-universal + path: | + . + !.git + !.output + + UniversalWindowsARM64: + name: Windows arm64 + runs-on: windows-11-arm + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Setup msys and install required packages + uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 + with: + msystem: clangarm64 + install: mingw-w64-clang-aarch64-clang make git zip + + - name: Build universal binary + run: make -j4 -C src ARCH=armv8-universal profile-build COMP=clang COMPCXX=clang++ + + - name: Strip binary + run: strip src/stockfish.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: Verify bench and compiler + working-directory: src + run: | + ../tests/signature.sh $benchref + ./stockfish compiler 2>&1 | grep -q DOTPROD || { echo "compiler output missing DOTPROD" >&2; exit 1; } + + - name: Rename binary + run: mv src/stockfish.exe stockfish-windows-arm64-universal.exe + + - name: Remove non-src files + run: git clean -fx -- src + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: windows arm64-universal + path: | + . + !.git + !.output diff --git a/scripts/check_universal_arm.sh b/scripts/check_universal_arm.sh new file mode 100755 index 000000000..7cef94a53 --- /dev/null +++ b/scripts/check_universal_arm.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# Verify that the arm64 universal binary selects the correct per-arch build +# under qemu-aarch64 emulation for a range of target CPUs. +# +# Usage: check_universal_arm.sh STOCKFISH_EXE EXPECTED_BENCH + +set -eu + +if [ $# -ne 2 ]; then + echo "Usage: $0 STOCKFISH_EXE EXPECTED_BENCH" >&2 + exit 2 +fi + +STOCKFISH_EXE=$1 +EXPECTED_BENCH=$2 + +PAIRS=" +cortex-a53:armv8 +max:armv8-dotprod +" + +BINARY_SIZE=$(wc -c < "$STOCKFISH_EXE") +MAX_SIZE=$((150 * 1024 * 1024)) +if [ "$BINARY_SIZE" -gt "$MAX_SIZE" ]; then + printf 'check_universal_arm.sh: binary size %d bytes exceeds 150 MB limit\n' "$BINARY_SIZE" >&2 + exit 1 +fi + +FAIL=0 +for pair in $PAIRS; do + cpu=${pair%%:*} + expected_compiler=${pair##*:} + compiler_out=$(qemu-aarch64 -cpu "$cpu" -- "$STOCKFISH_EXE" compiler 2>&1 || true) + bench_out=$(qemu-aarch64 -cpu "$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 + printf 'Full "compiler" output: %s\n' "$compiler_out" + printf 'Full "bench" output: %s\n' "$bench_out" + FAIL=1 + else + printf 'CPU %s ok\n' "$cpu" >&2 + fi +done + +if [ "$FAIL" != 0 ]; then + echo "check_universal_arm.sh: failed" + exit 1 +fi + +echo "check_universal_arm.sh: Good! Universal binary has correct hardware detection." diff --git a/src/Makefile b/src/Makefile index 1794d9ba9..2d3835311 100644 --- a/src/Makefile +++ b/src/Makefile @@ -74,7 +74,7 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp \ engine.cpp score.cpp memory.cpp -OTHER_SRCS = universal/entry_x86.cpp universal/nnue_embed.cpp +OTHER_SRCS = universal/entry_x86.cpp universal/entry_arm64.cpp universal/nnue_embed.cpp HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \ @@ -148,14 +148,14 @@ ifeq ($(ARCH), $(filter $(ARCH), \ 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 \ + armv7 armv7-neon armv8 armv8-dotprod armv8-universal apple-silicon general-64 general-32 riscv64 \ loongarch64 loongarch64-lsx loongarch64-lasx)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false endif -ifeq ($(ARCH), x86-64-universal) +ifneq (,$(findstring universal,$(ARCH))) UNIVERSAL_BUILD=true else UNIVERSAL_BUILD=false @@ -976,6 +976,7 @@ help: echo "ppc-32 > PPC 32-bit" && \ echo "armv7 > ARMv7 32-bit" && \ echo "armv7-neon > ARMv7 32-bit with popcnt and neon" && \ + echo "armv8-universal > ARMv8 64-bit with automatic runtime selection of the best architecture" && \ echo "armv8 > ARMv8 64-bit with popcnt and neon" && \ echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" && \ echo "e2k > Elbrus 2000" && \ @@ -1046,13 +1047,24 @@ profile-build: net config-sanity objclean profileclean endif ifeq ($(comp), clang) -LTO_OBJ_SUFFIX := .lto.o -SAVE_TEMPS := -Wl,--save-temps + LTO_OBJ_SUFFIX := .lto.o + ifeq ($(target_windows),yes) + SAVE_TEMPS := -Wl,--lto-emit-asm # ld.lld doesn't have a save temps functionality + use_lto_emit_asm := yes + else + SAVE_TEMPS := -Wl,--save-temps + endif else -LTO_OBJ_SUFFIX := .ltrans0.ltrans.o -SAVE_TEMPS := -save-temps + LTO_OBJ_SUFFIX := .ltrans0.ltrans.o + SAVE_TEMPS := -save-temps endif +ifeq ($(arch),armv8) + ASM_FLAGS := -mcpu=neoverse-v2 +endif + +MODNAME := $(subst -,_,$(notdir $(CURDIR))) + universal-object-pgo: objclean profileclean universalclean @echo "" @echo "Step 1/4. Building instrumented executable ..." @@ -1066,12 +1078,25 @@ universal-object-pgo: objclean profileclean universalclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='$(SAVE_TEMPS) $(NNUE_EMBED_OBJ)' $(profile_use) @echo "" +ifeq ($(use_lto_emit_asm),yes) + @echo "Step 4/4. Renaming ctors and extracting LTO-optimized relocatable object ..." + awk -v MODNAME=$(MODNAME) -f ../../universal/rewrite_asm_sections.awk *.lto.s > renamed.s +# -mcpu=neoverse-v2 (or another CPU supporting dotprod) is required for clang +# to not complain when assembling + $(CXX) -c renamed.s -o stockfish.o $(ASM_FLAGS) +else @echo "Step 4/4. Extracting LTO-optimized relocatable object ..." cp "$(basename $(EXE))$(LTO_OBJ_SUFFIX)" stockfish.o +endif universal-object-nopgo: objclean universalclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) ENV_LDFLAGS='$(SAVE_TEMPS) $(NNUE_EMBED_OBJ)' all +ifeq ($(use_lto_emit_asm),yes) + awk -v MODNAME=$(MODNAME) -f ../../universal/rewrite_asm_sections.awk *.lto.s > renamed.s + $(CXX) -c renamed.s -o stockfish.o $(ASM_FLAGS) +else cp "$(basename $(EXE))$(LTO_OBJ_SUFFIX)" stockfish.o +endif strip: $(STRIP) $(EXE) @@ -1249,39 +1274,62 @@ endif # 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) +# make -j ARCH=armv8-universal build 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)') +# GCC 15+ or Clang 19+ required for #embed +CXX_MAJOR := $(shell $(CXX) -dumpversion 2>/dev/null | cut -d. -f1) +CXX_IS_CLANG := $(shell $(CXX) --version 2>/dev/null | head -1 | grep -ic clang) +ifeq ($(CXX_IS_CLANG),0) + EMBED_MIN := 15 + EMBED_NAME := GCC +else + EMBED_MIN := 19 + EMBED_NAME := Clang +endif +ifneq ($(shell test "$(CXX_MAJOR)" -ge "$(EMBED_MIN)" 2>/dev/null && echo ok),ok) +$(error Universal build needs $(EMBED_NAME) $(EMBED_MIN)+ for \#embed support; CXX=$(CXX) reports version '$(CXX_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 +ifeq ($(arch),x86_64) + 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 + UNIVERSAL_ENTRY_OBJ := $(TEMP_DIR)/entry_x86.o + UNIVERSAL_ENTRY_SRC := $(UNIVERSAL_SRC_DIR)/entry_x86.cpp + BASELINE_ARCH := x86-64 +else + UNIVERSAL_ARCHES := armv8 armv8-dotprod + UNIVERSAL_ENTRY_OBJ := $(TEMP_DIR)/entry_arm64.o + UNIVERSAL_ENTRY_SRC := $(UNIVERSAL_SRC_DIR)/entry_arm64.cpp + BASELINE_ARCH := armv8 +endif + # Top-level tracked items in src/ (directories and files) TRACKED_TOP := $(sort $(foreach f,$(SRCS) $(HEADERS) Makefile incbin,$(firstword $(subst /, ,$(f))))) -# Baseline x86-64 must come first in the final link (Windows --allow-multiple-definition +# Baseline build 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))) +ARCH_OBJS := $(TEMP_DIR)/$(BASELINE_ARCH)/stockfish.o \ + $(patsubst %,$(TEMP_DIR)/%/stockfish.o,$(filter-out $(BASELINE_ARCH),$(UNIVERSAL_ARCHES))) + +UNIVERSAL_FINAL_FLAGS := -fno-exceptions -Os \ + -Wno-c++26-extensions -Wno-c23-extensions # suppress #embed warnings -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 + UNIVERSAL_FINAL_FLAGS += -static-libstdc++ -static-libgcc -lrt -lpthread +else # windows + UNIVERSAL_FINAL_FLAGS += -Wl,--allow-multiple-definition -static + ifeq ($(COMP),clang) + UNIVERSAL_FINAL_FLAGS += -lpthread + endif endif # $(call arch-namespace,x86-64-avx2) => Stockfish_x86_64_avx2 @@ -1290,10 +1338,19 @@ 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 +# lld-link, used on Windows+clang, doesn't accept --defsym, so +# we define main directly in main.cpp instead and delete it later. +ifeq ($(target_windows)+$(COMP),yes+clang) + arch-defmain = -DUNIVERSAL_NEEDS_MAIN_SHIM +else + arch-defmain = -Wl,--defsym=main=$(call arch-mangled,$(1)) +endif + arch-cxxflags = --embed-dir=$(CURDIR) -DStockfish=$(call arch-namespace,$(1)) -DUNIVERSAL_BINARY \ - -Wl,--defsym=main=$(call arch-mangled,$(1)) -Wno-c++26-extensions + $(call arch-defmain,$(1)) -Wno-c++26-extensions -Wno-c23-extensions UOBJ_TGT ?= universal-object-pgo +OBJCOPY ?= objcopy .PHONY: build profile-build universal-clean build: UOBJ_TGT := universal-object-nopgo @@ -1312,7 +1369,7 @@ $(NET_SENTINEL): | $(TEMP_DIR) $(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) +$(UNIVERSAL_ENTRY_OBJ): $(UNIVERSAL_ENTRY_SRC) | $(TEMP_DIR) $(CXX) -O2 -c $< -o $@ # Symlink tracked top-level items from src/ into temp_builds//. @@ -1332,12 +1389,17 @@ $(TEMP_DIR)/%/stockfish.o: $(TEMP_DIR)/%/.setup $(NNUE_EMBED_OBJ) $(NET_SENTINEL 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 $@ + $(OBJCOPY) -R .group $@ +ifeq ($(use_lto_emit_asm),yes) +# Delete per-arch main shim + $(OBJCOPY) -N main $@ +else # 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 $@ + $(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 $@ + $(OBJCOPY) --set-section-flags $(call arch-suffix,$*)_init=alloc,data $@ +endif $(UNIVERSAL_EXE): $(UNIVERSAL_ENTRY_OBJ) $(NNUE_EMBED_OBJ) $(ARCH_OBJS) $(CXX) -o $@ $(UNIVERSAL_ENTRY_OBJ) $(NNUE_EMBED_OBJ) $(ARCH_OBJS) $(UNIVERSAL_FINAL_FLAGS) diff --git a/src/main.cpp b/src/main.cpp index fccb2f21d..6a5f6fe68 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,7 +30,9 @@ using namespace Stockfish; #ifdef UNIVERSAL_BINARY namespace Stockfish { -int main(int argc, char* argv[]); +int main(int argc, char* argv[]); // silence 'no previous declaration' + +__attribute__((used)) // keep main alive #endif int main(int argc, char* argv[]) { @@ -49,5 +51,9 @@ int main(int argc, char* argv[]) { } #ifdef UNIVERSAL_BINARY -} +} // namespace Stockfish + + #ifdef UNIVERSAL_NEEDS_MAIN_SHIM +int main(int argc, char* argv[]) { return Stockfish::main(argc, argv); } + #endif #endif diff --git a/src/universal/entry_arm64.cpp b/src/universal/entry_arm64.cpp new file mode 100644 index 000000000..1616f1614 --- /dev/null +++ b/src/universal/entry_arm64.cpp @@ -0,0 +1,58 @@ +#include + +#if defined(_WIN32) + #include +#else + #include + #ifndef HWCAP_ASIMDDP + #define HWCAP_ASIMDDP (1 << 20) + #endif +#endif + +#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(armv8) +DEFINE_BUILD(armv8_dotprod) + +struct CpuFeatures { + bool dotprod; +}; + + +static CpuFeatures query_cpu_features() { +#if defined(_WIN32) + return { + .dotprod = (bool) IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE), + }; +#else + unsigned long hwcap = getauxval(AT_HWCAP); + return { + .dotprod = (bool) (hwcap & HWCAP_ASIMDDP), + }; +#endif +} + + +// Selects the most capable ISA variant supported by this CPU and OS +static int dispatch(const CpuFeatures& f, int argc, char* argv[]) { + if (!f.dotprod) + return entry_armv8(argc, argv); + + return entry_armv8_dotprod(argc, argv); +} + +int main(int argc, char* argv[]) { + CpuFeatures features = query_cpu_features(); + return dispatch(features, argc, argv); +} diff --git a/src/universal/rewrite_asm_sections.awk b/src/universal/rewrite_asm_sections.awk new file mode 100644 index 000000000..2b34fe5e6 --- /dev/null +++ b/src/universal/rewrite_asm_sections.awk @@ -0,0 +1,67 @@ +# This is used if the universal binary is compiled on clang+Windows. +# Usage (avx2 example): +# awk -v MODNAME=x86_64_avx2 -f rewrite_asm_sections.awk stockfish.exe.lto.s > renamed.s +# +# The relevant output from --lto-emit-asm looks like: +# ... +# .section .ctors,"dw",associative,_ZN21Stockfish_x86_64_avx217SYSTEM_THREADS_NBE,unique,0 +# .p2align 3, 0x0 +# .quad __cxx_global_var_init +# .section .ctors,"a",unique,0 +# .p2align 3, 0x0 +# .quad _GLOBAL__sub_I_benchmark.cpp +# .quad _GLOBAL__sub_I_evaluate.cpp +# ... +# .quad _GLOBAL__sub_I_engine.cpp +# .quad _GLOBAL__sub_I_score.cpp +# +# .section .bss$_ZN21Stockfish_x86_64_avx2L26STARTUP_PROCESSOR_AFFINITYE,"bw",one_only,_ZN21Stockfish_x86_64_avx2L26STARTUP_PROCESSOR_AFFINITYE,unique,570 +# ... +# +# We want to consolidate everything into a single section named x86_64_avx2_init, and insert +# symbols __start_x86_64_avx2_init/__stop_x86_64_avx2_init for entry_x86.cpp to use. +# (On ELF these symbols are implicitly defined, so naming it this way keeps the universal +# entry point consistent.) + +BEGIN { + in_ctors = 0 +} + +# Match any .section line containing .ctors +/^[[:space:]]*\.section[[:space:]].*\.ctors/ { + + # First .ctors section, emit start symbol + if (!in_ctors) { + in_ctors = 1 + section = MODNAME "_init" + + printf "\t.section\t%s,\"a\"\n", section + printf "\t.globl\t__start_%s\n", section + printf "__start_%s:\n", section + } + + # Suppress all original .ctors section directives + next +} + +# First non-.ctors .section after ctors block, emit stop symbol +in_ctors && /^[[:space:]]*\.section/ { + section = MODNAME "_init" + + printf "\t.globl\t__stop_%s\n", section + printf "__stop_%s:\n", section + + in_ctors = 0 +} + +{ + print +} + +END { + if (in_ctors) { + section = MODNAME "_init" + printf "\t.globl\t__stop_%s\n", section + printf "__stop_%s:\n", section + } +}