8

AMD, Intel, Red Hat, and SUSE have defined a set of "architecture levels" for x86-64 CPUs. For example x86-64-v2 means that a CPU support not only the basic x86-64 instructions set, but also other instructions like SSE4.2, SSSE3 or POPCNT.

How can I check which architecture levels are supported by my CPU?

10

This is based on gioele’s answer; the whole script might as well be written in AWK:

#!/usr/bin/awk -f

BEGIN {
    while (!/flags/) if (getline < "/proc/cpuinfo" != 1) exit 1
    if (/lm/&&/cmov/&&/cx8/&&/fpu/&&/fxsr/&&/mmx/&&/syscall/&&/sse2/) level = 1
    if (level == 1 && /cx16/&&/lahf/&&/popcnt/&&/sse4_1/&&/sse4_2/&&/ssse3/) level = 2
    if (level == 2 && /avx/&&/avx2/&&/bmi1/&&/bmi2/&&/f16c/&&/fma/&&/abm/&&/movbe/&&/xsave/) level = 3
    if (level == 3 && /avx512f/&&/avx512bw/&&/avx512cd/&&/avx512dq/&&/avx512vl/) level = 4
    if (level > 0) { print "CPU supports x86-64-v" level; exit level + 1 }
    exit 1
}

This also checks for the baseline (“level 1” here), only outputs the highest supported level, and exits with an exit code matching the first unsupported level.

6
  • 3
    Some of the checking of earlier levels is redundant, but not a bad idea I guess. In real hardware, SSE4.2 for example already implies support for all previous (Intel) SSE versions (but not AMD SSE4a). In a virtual machine CPUID is virtualized so it's theoretically possible to indicate SSSE3 support without SSE3. Only in a software emulator would could you make SSE3 instructions fault while SSSE3 instructions didn't. (BTW, you omitted /sse3/.) The de-facto standard is that runtime CPU dispatching only needs to check the highest SSE feature flag it depends on. – Peter Cordes Jan 27 at 19:02
  • 1
    There are other de-facto standards like SSE4.2 implying popcnt, but that's good to check explicitly. And other non-SIMD extensions like BMI1 are fully independent of SIMD (although since some BMI1/2 instructions use VEX encoding, they're normally only found on CPUs that support AVX. And unfortunately Intel even disables BMI1/2 on their Pentium/Celeron CPUs, perhaps as a way of fully disabling AVX.). – Peter Cordes Jan 27 at 19:08
  • 1
    BTW, level 2 = Nehalem and current Silvermont, and current-gen Pentium/Celeron. Also AMD Bulldozer family since even Excavator doesn't have BMI2, only AVX2 and FMA3. Level 3 = Haswell (and Zen), and includes most of the really good stuff. MacOS apparently can make fat binaries with baseline x86-64 and Haswell feature-level, allowing usage of BMI2 efficient shift instructions all over the place, and of AVX everywhere. Level 4 = -march=skylake-avx512. – Peter Cordes Jan 27 at 19:12
  • 1
    @PeterCordes yes, there are a number of deficiencies and redundancies here (in particular, I should check full fields instead of using regexes, since for example /lm/ will match anything containing those characters). I followed the exhaustive level definitions as used in the first answer (that’s where /ssse3/ without /sse3/ came from), even though as you say many of them are redundant. (I’ve been following the discussions leading up to the definition of these levels.) – Stephen Kitt Jan 27 at 19:29
  • 1
    TBH this was more an exercise in showing that all the checks could be done in AWK instead of a mixture of AWK ans shell, rather than coming up with the best level checker ;-). – Stephen Kitt Jan 27 at 19:32
6

On Linux, one can check the CPU capabilities reported by /proc/cpuinfo against the requirements described in the x86-psABI documentation.

The following script automates that process (the exit code is the number of the first non-supported architecture level).

#!/bin/sh -eu

flags=$(cat /proc/cpuinfo | grep flags | head -n 1 | cut -d: -f2)

supports_v2='awk "/cx16/&&/lahf/&&/popcnt/&&/sse4_1/&&/sse4_2/&&/ssse3/ {found=1} END {exit !found}"'
supports_v3='awk "/avx/&&/avx2/&&/bmi1/&&/bmi2/&&/f16c/&&/fma/&&/abm/&&/movbe/&&/xsave/ {found=1} END {exit !found}"'
supports_v4='awk "/avx512f/&&/avx512bw/&&/avx512cd/&&/avx512dq/&&/avx512vl/ {found=1} END {exit !found}"'

echo "$flags" | eval $supports_v2 || exit 2 && echo "CPU supports x86-64-v2"
echo "$flags" | eval $supports_v3 || exit 3 && echo "CPU supports x86-64-v3"
echo "$flags" | eval $supports_v4 || exit 4 && echo "CPU supports x86-64-v4"
4
  • 2
    Instead of using a variable and evaling it, you could have used a function – muru Jan 27 at 8:48
  • As an FYI, my old AMD FX-6100 supports v2, but not v3 or v4. – RonJohn Jan 27 at 18:47
  • 1
    @RonJohn: Yup, even Bulldozer-family is only "level 2", even though Excavator has AVX2 and FMA. It's missing BMI2 and movbe. (Piledriver / Steamroller have AVX1 and FMA; Bulldozer has AVX1 and FMA4 but not FMA3; Intel pulled the rug out from under AMD as late as they could. See Stop the instruction set war on Agner Fog's blog.) To be fair, having another level with AVX but not BMI2 would be of limited value, and BMI2 is quite nice for Intel CPUs: variable-count shifts with SHLX/SHRX are 1 uop instead of 3, and can use any reg instead of CL – Peter Cordes Jan 27 at 19:16
  • Level 3 = Haswell and Zen1. Level 4 = -march=skylake-avx512. – Peter Cordes Jan 27 at 19:16
2

Here's a shell script to determine the x86_64 CPU architecture level on Linux. It's compatible with BusyBox. With the option -v, it shows which flags you're missing to reach the next level. See What do the flags in /proc/cpuinfo mean? for an explanation of the flags.

#!/bin/sh
set -e

verbose=
while getopts v OPTLET; do
  case "$OPTLET" in
    v) verbose=1;;
    \?) exit 2;;
  esac
done

flags=$(grep '^flags\b' </proc/cpuinfo | head -n 1)
flags=" ${flags#*:} "

has_flags () {
  for flag; do
    case "$flags" in
      *" $flag "*) :;;
      *)
        if [ -n "$verbose" ]; then
          echo >&2 "Missing $flag for the next level"
        fi
        return 1;;
    esac
  done
}

determine_level () {
  level=0
  has_flags lm cmov cx8 fpu fxsr mmx syscall sse2 || return 0
  level=1
  has_flags cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3 || return 0
  level=2
  has_flags avx avx2 bmi1 bmi2 f16c fma abm movbe xsave || return 0
  level=3
  has_flags avx512f avx512bw avx512cd avx512dq avx512vl || return 0
  level=4
}

determine_level
echo "$level"

(Acknowledgement: I reused the list of flags from Stephen Kitt's answer which in turns builds on gioele's answer.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.