Path: blob/master/scripts/get_native_properties.sh
625 views
#!/bin/sh12#3# Returns the best architecture supported by the CPU (as expected by src/Makefile ARCH=).4#5# Output format:6# "<true_arch>\n"7#89# ---------------------------10# Helpers (POSIX)11# ---------------------------1213# Test hooks (optional env overrides)14# GP_UNAME_S: override `uname -s`15# GP_UNAME_M: override `uname -m`16# GP_CPUINFO: path to a cpuinfo-like fixture file (defaults to /proc/cpuinfo)17# GP_BITS: override getconf LONG_BIT result (32/64)18# GP_SYSCTL_FEATURES: override sysctl feature strings on Darwin x86_641920cpuinfo_path=${GP_CPUINFO:-/proc/cpuinfo}2122# Normalize to a single-line, space-separated string.23normalize_ws() {24printf '%s\n' "$*" | tr '\n\t' ' ' | tr -s ' '25}2627die() {28printf '%s\n' "$*" >&229exit 130}3132# Populate $flags from /proc/cpuinfo when available,33# removing underscores and dots to reduce naming variations.34get_flags() {35if [ -r "$cpuinfo_path" ]; then36flags=$(37awk '38/^flags[ \t]*:|^Features[ \t]*:/ {39if (!found) {40gsub(/^flags[ \t]*:[ \t]*|^Features[ \t]*:[ \t]*|[_.]/, "");41line=$042found=143}44}45END { print line }46' "$cpuinfo_path" 2>/dev/null47)48else49flags=''50fi51flags=$(printf '%s\n' "$flags" | tr '[:upper:]' '[:lower:]')52flags=$(normalize_ws "$flags")53}5455# Populate $flags from sysctl on Darwin x86_64.56get_sysctl_flags() {57if [ -n "${GP_SYSCTL_FEATURES:-}" ]; then58flags=$(printf '%s\n' "$GP_SYSCTL_FEATURES")59else60flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features 2>/dev/null)61fi62flags=$(printf '%s\n' "$flags" | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '._')63flags=$(normalize_ws "$flags")64}6566# Best-effort bitness for fallback arch selection.67get_bits() {68if [ -n "${GP_BITS:-}" ]; then69bits=$GP_BITS70else71bits=$(getconf LONG_BIT 2>/dev/null)72fi73case $bits in7432|64) : ;;75*) bits=64 ;;76esac77}7879# Extract ARM architecture level (5/6/7/8/...) from /proc/cpuinfo when present.80get_arm_arch_level() {81[ -r "$cpuinfo_path" ] || return 182awk '83/^CPU architecture[ \t]*:/{84s=$085sub(/^[^:]*:[ \t]*/, "", s)86if (match(s, /[0-9]+/)) { print substr(s, RSTART, RLENGTH); exit }87}88/^Processor[ \t]*:/{89s=$090sub(/^[^:]*:[ \t]*/, "", s)91if (match(s, /ARMv[0-9]+/)) { print substr(s, RSTART+4, RLENGTH-4); exit }92}93' "$cpuinfo_path" 2>/dev/null94}9596# Best-effort ARM architecture level (5/6/7/8/...) with a minimal fallback.97# Prefer /proc/cpuinfo when available; fall back to uname -m only when it encodes it.98get_arm_level() {99arm_level=$(get_arm_arch_level || :)100if [ -n "$arm_level" ]; then101printf '%s\n' "$arm_level"102return 0103fi104case ${1:-} in105armv5*) printf '5\n' ;;106armv6*) printf '6\n' ;;107armv7*) printf '7\n' ;;108armv8l) printf '8\n' ;;109*) return 1 ;;110esac111}112113# Whole-token membership check.114has_flag() {115case " $flags " in116*" $1 "*) return 0 ;;117*) return 1 ;;118esac119}120121match_flags() {122for f; do123has_flag "$f" || return 1124done125return 0126}127128match_any_flags() {129for f; do130has_flag "$f" && return 0131done132return 1133}134135# SSE3 is often exposed as "pni" in /proc/cpuinfo.136match_sse3() {137match_any_flags sse3 pni138}139140# AMD Zen1/2 exclusion logic (used for bmi2 tier).141# https://web.archive.org/web/20250821132355/https://en.wikichip.org/wiki/amd/cpuid142is_znver_1_2() (143[ -r "$cpuinfo_path" ] || exit 1144vendor_id=$(awk '/^vendor_id/{print $3; exit}' "$cpuinfo_path" 2>/dev/null)145cpu_family=$(awk '/^cpu family/{print $4; exit}' "$cpuinfo_path" 2>/dev/null)146[ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ]147)148149match_not_znver12_and_flags() {150is_znver_1_2 && return 1151match_flags "$@"152}153154match_sse3_popcnt() {155has_flag popcnt || return 1156match_sse3157}158159match_true() { return 0; }160161# Generic selector: reads lines like "arch|predicate|arg1 arg2 ..."162# First match wins; blank lines and lines starting with '#' are ignored.163select_arch_from_table() {164while IFS='|' read -r arch pred args; do165[ -z "$arch" ] && continue166case $arch in \#*) continue ;; esac167168if [ -n "$args" ]; then169# Intentional splitting of args into words for the predicate.170# shellcheck disable=SC2086171$pred $args && { printf '%s\n' "$arch"; return 0; }172else173$pred && { printf '%s\n' "$arch"; return 0; }174fi175done176return 1177}178179# ---------------------------180# Arch selection (table-driven)181# ---------------------------182183set_arch_loongarch64() {184true_arch=$(185select_arch_from_table <<'EOF'186loongarch64-lasx|match_flags|lasx187loongarch64-lsx|match_flags|lsx188loongarch64|match_true|189EOF190)191}192193set_arch_x86_64() {194true_arch=$(195select_arch_from_table <<'EOF'196# Strongest -> weakest (first match wins)197x86-64-avx512icl|match_flags|avx512f avx512cd avx512vl avx512dq avx512bw avx512ifma avx512vbmi avx512vbmi2 avx512vpopcntdq avx512bitalg avx512vnni vpclmulqdq gfni vaes198x86-64-vnni512|match_flags|avx512vnni avx512dq avx512f avx512bw avx512vl199x86-64-avx512|match_flags|avx512f avx512bw200x86-64-avxvnni|match_flags|avxvnni201x86-64-bmi2|match_not_znver12_and_flags|bmi2202x86-64-avx2|match_flags|avx2203x86-64-sse41-popcnt|match_flags|sse41 popcnt204x86-64-ssse3|match_flags|ssse3205x86-64-sse3-popcnt|match_sse3_popcnt|206x86-64|match_true|207EOF208)209}210211set_arch_x86_32() {212true_arch=$(213select_arch_from_table <<'EOF'214x86-32-sse41-popcnt|match_flags|sse41 popcnt215x86-32-sse2|match_flags|sse2216x86-32|match_true|217EOF218)219}220221# PPC64 needs a little parsing to distinguish vsx vs altivec.222set_arch_ppc_64() {223if [ -r "$cpuinfo_path" ] && grep -q "altivec" "$cpuinfo_path" 2>/dev/null; then224# Typical: "cpu : POWER8E" (extract the number after POWER)225power=$(226awk -F: '/^cpu[ \t]*:/{print $2; exit}' "$cpuinfo_path" 2>/dev/null \227| sed -n 's/.*[Pp][Oo][Ww][Ee][Rr][^0-9]*\([0-9][0-9]*\).*/\1/p'228)229if [ -z "$power" ]; then230power=$(231awk -F: '/^cpu[ \t]*:/{print $2; exit}' "$cpuinfo_path" 2>/dev/null \232| sed -n 's/.*\([0-9][0-9]*\).*/\1/p'233)234fi235case $power in236''|*[!0-9]*)237true_arch='ppc-64-altivec'238;;239*)240if [ "$power" -gt 7 ] 2>/dev/null; then241true_arch='ppc-64-vsx'242else243true_arch='ppc-64-altivec'244fi245;;246esac247else248true_arch='ppc-64'249fi250}251252# ---------------------------253# OS / machine dispatch254# ---------------------------255256uname_s=$(uname -s 2>/dev/null)257uname_m=$(uname -m 2>/dev/null)258uname_s=${GP_UNAME_S:-$uname_s}259uname_m=${GP_UNAME_M:-$uname_m}260261case $uname_s in262Darwin)263case $uname_m in264arm64)265true_arch='apple-silicon'266;;267x86_64)268get_sysctl_flags269set_arch_x86_64270;;271*)272get_bits273if [ "$bits" = "32" ]; then274true_arch='general-32'275else276true_arch='general-64'277fi278;;279esac280;;281282Linux)283get_flags284case $uname_m in285x86_64)286set_arch_x86_64287;;288i?86)289set_arch_x86_32290;;291ppc64*)292set_arch_ppc_64293;;294aarch64|arm64)295true_arch='armv8'296if match_flags asimddp; then297true_arch='armv8-dotprod'298fi299;;300armv5*|armv6*|armv7*|armv8l|arm*)301arm_level=$(get_arm_level "$uname_m" || :)302case $arm_level in3035|6)304true_arch='general-32'305;;3067|8)307true_arch='armv7'308if match_flags neon; then309true_arch='armv7-neon'310fi311;;312*)313true_arch='general-32'314if match_flags neon; then315true_arch='armv7-neon'316fi317;;318esac319;;320loongarch64*)321set_arch_loongarch64322;;323riscv64)324true_arch='riscv64'325;;326e2k*)327true_arch='e2k'328;;329ppc|ppc32|powerpc)330true_arch='ppc-32'331;;332*)333# Don't hard-fail: fall back to general-* so ARCH=native still builds334get_bits335if [ "$bits" = "32" ]; then336true_arch='general-32'337else338true_arch='general-64'339fi340;;341esac342;;343344MINGW*ARM64*)345# Windows ARM64 (MSYS2/MinGW)346# Can't reliably detect ARM CPU features here347true_arch='armv8-dotprod'348;;349350CYGWIN*|MINGW*|MSYS*)351# Windows x86_64 (MSYS2/Cygwin/MinGW)352get_flags353set_arch_x86_64354;;355356*)357die "Unsupported system type: $uname_s"358;;359esac360361printf '%s\n' "$true_arch"362363364