mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 20:57:10 +00:00
Excavator also supports BMI2, where it has the same microcoded performance implications as on Zen pre-3. closes https://github.com/official-stockfish/Stockfish/pull/6868 No functional change
365 lines
8.0 KiB
Bash
Executable File
365 lines
8.0 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
#
|
|
# Returns the best architecture supported by the CPU (as expected by src/Makefile ARCH=).
|
|
#
|
|
# Output format:
|
|
# "<true_arch>\n"
|
|
#
|
|
|
|
# ---------------------------
|
|
# Helpers (POSIX)
|
|
# ---------------------------
|
|
|
|
# Test hooks (optional env overrides)
|
|
# GP_UNAME_S: override `uname -s`
|
|
# GP_UNAME_M: override `uname -m`
|
|
# GP_CPUINFO: path to a cpuinfo-like fixture file (defaults to /proc/cpuinfo)
|
|
# GP_BITS: override getconf LONG_BIT result (32/64)
|
|
# GP_SYSCTL_FEATURES: override sysctl feature strings on Darwin x86_64
|
|
|
|
cpuinfo_path=${GP_CPUINFO:-/proc/cpuinfo}
|
|
|
|
# Normalize to a single-line, space-separated string.
|
|
normalize_ws() {
|
|
printf '%s\n' "$*" | tr '\n\t' ' ' | tr -s ' '
|
|
}
|
|
|
|
die() {
|
|
printf '%s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Populate $flags from /proc/cpuinfo when available,
|
|
# removing underscores and dots to reduce naming variations.
|
|
get_flags() {
|
|
if [ -r "$cpuinfo_path" ]; then
|
|
flags=$(
|
|
awk '
|
|
/^flags[ \t]*:|^Features[ \t]*:/ {
|
|
if (!found) {
|
|
gsub(/^flags[ \t]*:[ \t]*|^Features[ \t]*:[ \t]*|[_.]/, "");
|
|
line=$0
|
|
found=1
|
|
}
|
|
}
|
|
END { print line }
|
|
' "$cpuinfo_path" 2>/dev/null
|
|
)
|
|
else
|
|
flags=''
|
|
fi
|
|
flags=$(printf '%s\n' "$flags" | tr '[:upper:]' '[:lower:]')
|
|
flags=$(normalize_ws "$flags")
|
|
}
|
|
|
|
# Populate $flags from sysctl on Darwin x86_64.
|
|
get_sysctl_flags() {
|
|
if [ -n "${GP_SYSCTL_FEATURES:-}" ]; then
|
|
flags=$(printf '%s\n' "$GP_SYSCTL_FEATURES")
|
|
else
|
|
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features 2>/dev/null)
|
|
fi
|
|
flags=$(printf '%s\n' "$flags" | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '._')
|
|
flags=$(normalize_ws "$flags")
|
|
}
|
|
|
|
# Best-effort bitness for fallback arch selection.
|
|
get_bits() {
|
|
if [ -n "${GP_BITS:-}" ]; then
|
|
bits=$GP_BITS
|
|
else
|
|
bits=$(getconf LONG_BIT 2>/dev/null)
|
|
fi
|
|
case $bits in
|
|
32|64) : ;;
|
|
*) bits=64 ;;
|
|
esac
|
|
}
|
|
|
|
# Extract ARM architecture level (5/6/7/8/...) from /proc/cpuinfo when present.
|
|
get_arm_arch_level() {
|
|
[ -r "$cpuinfo_path" ] || return 1
|
|
awk '
|
|
/^CPU architecture[ \t]*:/{
|
|
s=$0
|
|
sub(/^[^:]*:[ \t]*/, "", s)
|
|
if (match(s, /[0-9]+/)) { print substr(s, RSTART, RLENGTH); exit }
|
|
}
|
|
/^Processor[ \t]*:/{
|
|
s=$0
|
|
sub(/^[^:]*:[ \t]*/, "", s)
|
|
if (match(s, /ARMv[0-9]+/)) { print substr(s, RSTART+4, RLENGTH-4); exit }
|
|
}
|
|
' "$cpuinfo_path" 2>/dev/null
|
|
}
|
|
|
|
# Best-effort ARM architecture level (5/6/7/8/...) with a minimal fallback.
|
|
# Prefer /proc/cpuinfo when available; fall back to uname -m only when it encodes it.
|
|
get_arm_level() {
|
|
arm_level=$(get_arm_arch_level || :)
|
|
if [ -n "$arm_level" ]; then
|
|
printf '%s\n' "$arm_level"
|
|
return 0
|
|
fi
|
|
case ${1:-} in
|
|
armv5*) printf '5\n' ;;
|
|
armv6*) printf '6\n' ;;
|
|
armv7*) printf '7\n' ;;
|
|
armv8l) printf '8\n' ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# Whole-token membership check.
|
|
has_flag() {
|
|
case " $flags " in
|
|
*" $1 "*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
match_flags() {
|
|
for f; do
|
|
has_flag "$f" || return 1
|
|
done
|
|
return 0
|
|
}
|
|
|
|
match_any_flags() {
|
|
for f; do
|
|
has_flag "$f" && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# SSE3 is often exposed as "pni" in /proc/cpuinfo.
|
|
match_sse3() {
|
|
match_any_flags sse3 pni
|
|
}
|
|
|
|
# AMD Excavator/Zen1/2 exclusion logic (used for bmi2 tier).
|
|
# https://web.archive.org/web/20250821132355/https://en.wikichip.org/wiki/amd/cpuid
|
|
# All of Bulldozer through Excavator are matched here, but pre-Excavator doesn't support bmi2 anyway
|
|
has_slow_bmi2() (
|
|
[ -r "$cpuinfo_path" ] || exit 1
|
|
vendor_id=$(awk '/^vendor_id/{print $3; exit}' "$cpuinfo_path" 2>/dev/null)
|
|
cpu_family=$(awk '/^cpu family/{print $4; exit}' "$cpuinfo_path" 2>/dev/null)
|
|
[ "$vendor_id" = "AuthenticAMD" ] \
|
|
&& { [ "$cpu_family" = "21" ] || [ "$cpu_family" = "23" ]; }
|
|
)
|
|
|
|
match_not_slow_bmi2_and_flags() {
|
|
has_slow_bmi2 && return 1
|
|
match_flags "$@"
|
|
}
|
|
|
|
match_sse3_popcnt() {
|
|
has_flag popcnt || return 1
|
|
match_sse3
|
|
}
|
|
|
|
match_true() { return 0; }
|
|
|
|
# Generic selector: reads lines like "arch|predicate|arg1 arg2 ..."
|
|
# First match wins; blank lines and lines starting with '#' are ignored.
|
|
select_arch_from_table() {
|
|
while IFS='|' read -r arch pred args; do
|
|
[ -z "$arch" ] && continue
|
|
case $arch in \#*) continue ;; esac
|
|
|
|
if [ -n "$args" ]; then
|
|
# Intentional splitting of args into words for the predicate.
|
|
# shellcheck disable=SC2086
|
|
$pred $args && { printf '%s\n' "$arch"; return 0; }
|
|
else
|
|
$pred && { printf '%s\n' "$arch"; return 0; }
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# ---------------------------
|
|
# Arch selection (table-driven)
|
|
# ---------------------------
|
|
|
|
set_arch_loongarch64() {
|
|
true_arch=$(
|
|
select_arch_from_table <<'EOF'
|
|
loongarch64-lasx|match_flags|lasx
|
|
loongarch64-lsx|match_flags|lsx
|
|
loongarch64|match_true|
|
|
EOF
|
|
)
|
|
}
|
|
|
|
set_arch_x86_64() {
|
|
true_arch=$(
|
|
select_arch_from_table <<'EOF'
|
|
# Strongest -> weakest (first match wins)
|
|
x86-64-avx512icl|match_flags|avx512f avx512cd avx512vl avx512dq avx512bw avx512ifma avx512vbmi avx512vbmi2 avx512vpopcntdq avx512bitalg avx512vnni vpclmulqdq gfni vaes
|
|
x86-64-vnni512|match_flags|avx512vnni avx512dq avx512f avx512bw avx512vl
|
|
x86-64-avx512|match_flags|avx512f avx512bw
|
|
x86-64-avxvnni|match_flags|avxvnni
|
|
x86-64-bmi2|match_not_slow_bmi2_and_flags|bmi2
|
|
x86-64-avx2|match_flags|avx2
|
|
x86-64-sse41-popcnt|match_flags|sse41 popcnt
|
|
x86-64-ssse3|match_flags|ssse3
|
|
x86-64-sse3-popcnt|match_sse3_popcnt|
|
|
x86-64|match_true|
|
|
EOF
|
|
)
|
|
}
|
|
|
|
set_arch_x86_32() {
|
|
true_arch=$(
|
|
select_arch_from_table <<'EOF'
|
|
x86-32-sse41-popcnt|match_flags|sse41 popcnt
|
|
x86-32-sse2|match_flags|sse2
|
|
x86-32|match_true|
|
|
EOF
|
|
)
|
|
}
|
|
|
|
# PPC64 needs a little parsing to distinguish vsx vs altivec.
|
|
set_arch_ppc_64() {
|
|
if [ -r "$cpuinfo_path" ] && grep -q "altivec" "$cpuinfo_path" 2>/dev/null; then
|
|
# Typical: "cpu : POWER8E" (extract the number after POWER)
|
|
power=$(
|
|
awk -F: '/^cpu[ \t]*:/{print $2; exit}' "$cpuinfo_path" 2>/dev/null \
|
|
| sed -n 's/.*[Pp][Oo][Ww][Ee][Rr][^0-9]*\([0-9][0-9]*\).*/\1/p'
|
|
)
|
|
if [ -z "$power" ]; then
|
|
power=$(
|
|
awk -F: '/^cpu[ \t]*:/{print $2; exit}' "$cpuinfo_path" 2>/dev/null \
|
|
| sed -n 's/.*\([0-9][0-9]*\).*/\1/p'
|
|
)
|
|
fi
|
|
case $power in
|
|
''|*[!0-9]*)
|
|
true_arch='ppc-64-altivec'
|
|
;;
|
|
*)
|
|
if [ "$power" -gt 7 ] 2>/dev/null; then
|
|
true_arch='ppc-64-vsx'
|
|
else
|
|
true_arch='ppc-64-altivec'
|
|
fi
|
|
;;
|
|
esac
|
|
else
|
|
true_arch='ppc-64'
|
|
fi
|
|
}
|
|
|
|
# ---------------------------
|
|
# OS / machine dispatch
|
|
# ---------------------------
|
|
|
|
uname_s=$(uname -s 2>/dev/null)
|
|
uname_m=$(uname -m 2>/dev/null)
|
|
uname_s=${GP_UNAME_S:-$uname_s}
|
|
uname_m=${GP_UNAME_M:-$uname_m}
|
|
|
|
case $uname_s in
|
|
Darwin)
|
|
case $uname_m in
|
|
arm64)
|
|
true_arch='apple-silicon'
|
|
;;
|
|
x86_64)
|
|
get_sysctl_flags
|
|
set_arch_x86_64
|
|
;;
|
|
*)
|
|
get_bits
|
|
if [ "$bits" = "32" ]; then
|
|
true_arch='general-32'
|
|
else
|
|
true_arch='general-64'
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
Linux)
|
|
get_flags
|
|
case $uname_m in
|
|
x86_64)
|
|
set_arch_x86_64
|
|
;;
|
|
i?86)
|
|
set_arch_x86_32
|
|
;;
|
|
ppc64*)
|
|
set_arch_ppc_64
|
|
;;
|
|
aarch64|arm64)
|
|
true_arch='armv8'
|
|
if match_flags asimddp; then
|
|
true_arch='armv8-dotprod'
|
|
fi
|
|
;;
|
|
armv5*|armv6*|armv7*|armv8l|arm*)
|
|
arm_level=$(get_arm_level "$uname_m" || :)
|
|
case $arm_level in
|
|
5|6)
|
|
true_arch='general-32'
|
|
;;
|
|
7|8)
|
|
true_arch='armv7'
|
|
if match_flags neon; then
|
|
true_arch='armv7-neon'
|
|
fi
|
|
;;
|
|
*)
|
|
true_arch='general-32'
|
|
if match_flags neon; then
|
|
true_arch='armv7-neon'
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
loongarch64*)
|
|
set_arch_loongarch64
|
|
;;
|
|
riscv64)
|
|
true_arch='riscv64'
|
|
;;
|
|
e2k*)
|
|
true_arch='e2k'
|
|
;;
|
|
ppc|ppc32|powerpc)
|
|
true_arch='ppc-32'
|
|
;;
|
|
*)
|
|
# Don't hard-fail: fall back to general-* so ARCH=native still builds
|
|
get_bits
|
|
if [ "$bits" = "32" ]; then
|
|
true_arch='general-32'
|
|
else
|
|
true_arch='general-64'
|
|
fi
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
MINGW*ARM64*)
|
|
# Windows ARM64 (MSYS2/MinGW)
|
|
# Can't reliably detect ARM CPU features here
|
|
true_arch='armv8-dotprod'
|
|
;;
|
|
|
|
CYGWIN*|MINGW*|MSYS*)
|
|
# Windows x86_64 (MSYS2/Cygwin/MinGW)
|
|
get_flags
|
|
set_arch_x86_64
|
|
;;
|
|
|
|
*)
|
|
die "Unsupported system type: $uname_s"
|
|
;;
|
|
esac
|
|
|
|
printf '%s\n' "$true_arch"
|