Skip to content

Instantly share code, notes, and snippets.

@scivision
Last active February 5, 2025 01:02
Show Gist options
  • Save scivision/38b907a684d21b66ce4af5fac5615a1c to your computer and use it in GitHub Desktop.
Save scivision/38b907a684d21b66ce4af5fac5615a1c to your computer and use it in GitHub Desktop.
Identify if CPU is x86_64v3 capable

Detect x86_64v3 CPU support

This C++ program printout ends with "x86_64v3 supported" if x86_64v3 is supported. Some Linux distributions such as RHEL 10 require x86_64v3 support.

Works across operating systems and even with Rosetta 2 on Apple Silicon Macs.

Computers with an x86 CPU can build and run this program natively.

cmake -B build
cmake --build build

build/x86cap

Apple Silicon Macs can build and run this program using AppleClang and Rosetta 2.

arch -x86_64 cmake -B build

cmake --build build

build/x86cap

With current Rosetta 2 and M1 Apple Silicon CPU, "v2" is the highest level supported.

Benchmarks

Phoronix has written several articles on benchmarking operating systems with "v3" enabled:

References

  • Microsoft code for Windows that we enhanced to check more flags
  • CPU level
  • CPU feature bit IDs
cmake_minimum_required(VERSION 3.19...3.31)
project(InstructionSet LANGUAGES CXX)
set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
include(CheckIncludeFileCXX)
set(CMAKE_CXX_STANDARD 11)
message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|AMD64)")
if(WIN32)
add_executable(InstructionSet windows.cpp)
else()
check_include_file_cxx("cpuid.h" HAVE_CPUID)
if(HAVE_CPUID)
add_executable(InstructionSet unix.cpp)
endif()
endif()
endif()
option(steinwurf "Use Steinwurf's cpuid library" ON)
if(NOT steinwurf)
return()
endif()
include(FetchContent)
FetchContent_Populate(cpuid_steinwurf
URL https://github.com/steinwurf/cpuid/archive/refs/tags/9.0.1.tar.gz
)
FetchContent_Populate(platform_steinwurf
URL https://github.com/steinwurf/platform/archive/refs/tags/5.1.1.tar.gz
)
set(s ${cpuid_steinwurf_SOURCE_DIR}/src/cpuid)
add_library(cpuid OBJECT ${s}/cpuinfo.cpp ${s}/version.cpp)
target_include_directories(cpuid PUBLIC ${s} ${platform_steinwurf_SOURCE_DIR}/src)
add_executable(x86cap steinwurf.cpp)
target_link_libraries(x86cap PRIVATE cpuid)
file(GENERATE OUTPUT .gitignore CONTENT "*")
#include <iostream>
#include <cstdlib>
#include <cpuinfo.hpp>
int main()
{
cpuid::cpuinfo m;
// v2: has all of: cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3
// note: cx16 == cmpxchg16b, lahf_lm == lahf
if(m.has_sse4_1() && m.has_sse4_2() && m.has_ssse3())
std::cout << "x86_64v2 supported\n";
// v3: has all of: avx avx2 bmi1 bmi2 f16c fma abm movbe xsave
// note: for Intel, lzcnt is designated for abm
if(m.has_avx() && m.has_avx2() && m.has_f16c())
std::cout << "x86_64v3 supported\n";
// v4: has all of: avx512f avx512bw avx512cd avx512dq avx512vl
if(m.has_avx512_f() && m.has_avx512_bw() && m.has_avx512_cd() && m.has_avx512_dq() && m.has_avx512_vl())
std::cout << "x86_64v4 supported\n";
return EXIT_SUCCESS;
}
// using C++ on Linux, determine the x86_64 instruction set level e.g. v2, v3, or v4
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <string>
#include <vector>
#include <bitset>
#include <cpuid.h>
int main(){
unsigned int eax=0, ebx=0, ecx=0, edx=0;
if (__get_cpuid(0, &eax, &ebx, &ecx, &edx) == 0) {
std::cerr << "CPUID not supported\n";
return EXIT_FAILURE;
}
unsigned int nIds = eax;
std::string vendor(reinterpret_cast<const char*>(&ebx), 4);
vendor += std::string(reinterpret_cast<const char*>(&edx), 4);
vendor += std::string(reinterpret_cast<const char*>(&ecx), 4);
std::cout << "Vendor: " << vendor << "\n";
if (nIds < 1) {
std::cerr << "CPUID leaf 1 not supported\n";
return EXIT_FAILURE;
}
ebx = 0;
ecx = 0;
edx = 0;
if(__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0){
std::cerr << "CPUID 1 not supported\n";
return EXIT_FAILURE;
}
std::bitset<32> const bECX(ecx);
bool const cx16 = bECX[13];
if(cx16) std::cout << "CX16 ";
bool const lahf_lm = bECX[20];
if (lahf_lm) std::cout << "LAHF_LM ";
bool const popcnt = bECX[23];
if (popcnt) std::cout << "POPCNT ";
bool const sse4_1 = bECX[19];
if (sse4_1) std::cout << "SSE4.1 ";
bool const sse4_2 = bECX[20];
if (sse4_2) std::cout << "SSE4.2 ";
bool const ssse3 = bECX[9];
if (ssse3) std::cout << "SSSE3 ";
bool const avx = bECX[28];
if (avx) std::cout << "AVX ";
bool const avx2 = bECX[5];
if (avx2) std::cout << "AVX2 ";
bool const f16c = bECX[29];
if (f16c) std::cout << "F16C ";
bool const fma = bECX[12];
if (fma) std::cout << "FMA ";
bool const abm = bECX[5] && vendor == "AuthenticAMD";
if (abm) std::cout << "ABM ";
bool const lzcnt = bECX[5] && vendor == "GenuineIntel";
if (lzcnt) std::cout << "LZCNT ";
bool const movbe = bECX[22];
if (movbe) std::cout << "MOVBE ";
bool const xsave = bECX[26];
if (xsave) std::cout << "XSAVE ";
std::cout << "\n";
if (cx16 && lahf_lm && popcnt && sse4_1 && sse4_2 && ssse3)
std::cout << "x86_64v2 supported\n";
bool v4 = false;
if(nIds >= 7){
ebx = 0;
ecx = 0;
edx = 0;
if(__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx) == 0){
std::cerr << "CPUID leaf 7, subleaf 0 not supported\n";
return EXIT_FAILURE;
}
std::bitset<32> const b(ebx);
bool const bmi1 = b[3];
if (bmi1) std::cout << "BMI1 ";
bool const bmi2 = b[8];
if (bmi2) std::cout << "BMI2 ";
bool const avx512f = b[16];
if (avx512f) std::cout << "AVX512F ";
bool const avx512bw = b[30];
if (avx512bw) std::cout << "AVX512BW ";
bool const avx512cd = b[28];
if (avx512cd) std::cout << "AVX512CD ";
bool const avx512dq = b[17];
if (avx512dq) std::cout << "AVX512DQ ";
bool const avx512vl = b[31];
if (avx512vl) std::cout << "AVX512VL ";
// v3: has all of: avx avx2 lzcnt bmi1 bmi2 f16c fma movbe xsave
// abm = lzcnt & bmi1 & bmi2
if(avx && avx2 && bmi1 && bmi2 && f16c && fma && (abm || lzcnt) && movbe && xsave)
std::cout << "\nx86_64v3 supported\n";
// v4: has all of: avx512f avx512bw avx512cd avx512dq avx512vl
if (avx512f && avx512bw && avx512cd && avx512dq && avx512vl)
std::cout << "x86_64v4 supported\n";
}
return EXIT_SUCCESS;
}
// Determine supported x86 CPU instruction set extensions
//
// For x86, x86_64 CPUs on WINDOWS ONLY
#include <iostream>
#include <vector>
#include <bitset>
#include <array>
#include <string>
#include <cstring>
#include <intrin.h>
class InstructionSet
{
// forward declarations
class InstructionSet_Internal;
public:
// getters
static std::string Vendor(void) { return CPU_Rep.vendor_; }
static std::string Brand(void) { return CPU_Rep.brand_; }
static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; }
static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; }
static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; }
static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; }
static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; }
static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; }
static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; }
static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; }
static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; }
static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; }
static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; }
static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; }
static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; }
static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; }
static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; }
static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; }
static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; }
static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; }
static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; }
static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; }
static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; }
static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; }
static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; }
static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; }
static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; }
static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; }
static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; }
static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; }
static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; }
static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; }
static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; }
static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; }
static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; }
static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; }
static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; }
static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; }
static bool AVX512VL(void) { return CPU_Rep.f_7_EBX_[31]; }
static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; }
static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; }
static bool AVX512DQ(void) { return CPU_Rep.f_7_EBX_[17]; }
static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; }
static bool AVX512BW(void) { return CPU_Rep.f_7_EBX_[30]; }
static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; }
static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; }
static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; }
static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; }
static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; }
static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; }
static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; }
static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; }
static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; }
static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; }
static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; }
static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; }
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
static bool isIntel(void) { return CPU_Rep.isIntel_; }
static bool isAMD(void) { return CPU_Rep.isAMD_; }
private:
static const InstructionSet_Internal CPU_Rep;
class InstructionSet_Internal
{
public:
InstructionSet_Internal()
: nIds_{ 0 },
nExIds_{ 0 },
isIntel_{ false },
isAMD_{ false },
f_1_ECX_{ 0 },
f_1_EDX_{ 0 },
f_7_EBX_{ 0 },
f_7_ECX_{ 0 },
f_81_ECX_{ 0 },
f_81_EDX_{ 0 },
data_{},
extdata_{}
{
//int cpuInfo[4] = {-1};
// Calling __cpuid with 0x0 as the function_id argument
// gets the number of the highest valid function ID.
std::array<int, 4> cpui;
__cpuid(cpui.data(), 0);
nIds_ = cpui[0];
for (int i = 0; i <= nIds_; ++i)
{
__cpuidex(cpui.data(), i, 0);
data_.push_back(cpui);
}
// Capture vendor string
char vendor[0x20];
memset(vendor, 0, sizeof(vendor));
*reinterpret_cast<int*>(vendor) = data_[0][1];
*reinterpret_cast<int*>(vendor + 4) = data_[0][3];
*reinterpret_cast<int*>(vendor + 8) = data_[0][2];
vendor_ = vendor;
if (vendor_ == "GenuineIntel")
{
isIntel_ = true;
}
else if (vendor_ == "AuthenticAMD")
{
isAMD_ = true;
}
// load bitset with flags for function 0x00000001
if (nIds_ >= 1)
{
f_1_ECX_ = data_[1][2];
f_1_EDX_ = data_[1][3];
}
// load bitset with flags for function 0x00000007
if (nIds_ >= 7)
{
f_7_EBX_ = data_[7][1];
f_7_ECX_ = data_[7][2];
}
// Calling __cpuid with 0x80000000 as the function_id argument
// gets the number of the highest valid extended ID.
__cpuid(cpui.data(), 0x80000000);
nExIds_ = cpui[0];
char brand[0x40];
memset(brand, 0, sizeof(brand));
for (int i = 0x80000000; i <= nExIds_; ++i)
{
__cpuidex(cpui.data(), i, 0);
extdata_.push_back(cpui);
}
// load bitset with flags for function 0x80000001
if (nExIds_ >= 0x80000001)
{
f_81_ECX_ = extdata_[1][2];
f_81_EDX_ = extdata_[1][3];
}
// Interpret CPU brand string if reported
if (nExIds_ >= 0x80000004)
{
memcpy(brand, extdata_[2].data(), sizeof(cpui));
memcpy(brand + 16, extdata_[3].data(), sizeof(cpui));
memcpy(brand + 32, extdata_[4].data(), sizeof(cpui));
brand_ = brand;
}
};
int nIds_;
int nExIds_;
std::string vendor_;
std::string brand_;
bool isIntel_;
bool isAMD_;
std::bitset<32> f_1_ECX_;
std::bitset<32> f_1_EDX_;
std::bitset<32> f_7_EBX_;
std::bitset<32> f_7_ECX_;
std::bitset<32> f_81_ECX_;
std::bitset<32> f_81_EDX_;
std::vector<std::array<int, 4>> data_;
std::vector<std::array<int, 4>> extdata_;
};
};
// Initialize static member data
const InstructionSet::InstructionSet_Internal InstructionSet::CPU_Rep;
// Print out supported instruction set extensions
int main()
{
auto& outstream = std::cout;
auto support_message = [&outstream](std::string isa_feature, bool is_supported) {
outstream << isa_feature << (is_supported ? " supported" : " not supported") << std::endl;
};
std::cout << InstructionSet::Vendor() << std::endl;
std::cout << InstructionSet::Brand() << std::endl;
support_message("3DNOW", InstructionSet::_3DNOW());
support_message("3DNOWEXT", InstructionSet::_3DNOWEXT());
support_message("ABM", InstructionSet::ABM());
support_message("ADX", InstructionSet::ADX());
support_message("AES", InstructionSet::AES());
support_message("AVX", InstructionSet::AVX());
support_message("AVX2", InstructionSet::AVX2());
support_message("AVX512CD", InstructionSet::AVX512CD());
support_message("AVX512ER", InstructionSet::AVX512ER());
support_message("AVX512F", InstructionSet::AVX512F());
support_message("AVX512PF", InstructionSet::AVX512PF());
support_message("BMI1", InstructionSet::BMI1());
support_message("BMI2", InstructionSet::BMI2());
support_message("CLFSH", InstructionSet::CLFSH());
support_message("CMPXCHG16B", InstructionSet::CMPXCHG16B());
support_message("CX8", InstructionSet::CX8());
support_message("ERMS", InstructionSet::ERMS());
support_message("F16C", InstructionSet::F16C());
support_message("FMA", InstructionSet::FMA());
support_message("FSGSBASE", InstructionSet::FSGSBASE());
support_message("FXSR", InstructionSet::FXSR());
support_message("HLE", InstructionSet::HLE());
support_message("INVPCID", InstructionSet::INVPCID());
support_message("LAHF", InstructionSet::LAHF());
support_message("LZCNT", InstructionSet::LZCNT());
support_message("MMX", InstructionSet::MMX());
support_message("MMXEXT", InstructionSet::MMXEXT());
support_message("MONITOR", InstructionSet::MONITOR());
support_message("MOVBE", InstructionSet::MOVBE());
support_message("MSR", InstructionSet::MSR());
support_message("OSXSAVE", InstructionSet::OSXSAVE());
support_message("PCLMULQDQ", InstructionSet::PCLMULQDQ());
support_message("POPCNT", InstructionSet::POPCNT());
support_message("PREFETCHWT1", InstructionSet::PREFETCHWT1());
support_message("RDRAND", InstructionSet::RDRAND());
support_message("RDSEED", InstructionSet::RDSEED());
support_message("RDTSCP", InstructionSet::RDTSCP());
support_message("RTM", InstructionSet::RTM());
support_message("SEP", InstructionSet::SEP());
support_message("SHA", InstructionSet::SHA());
support_message("SSE", InstructionSet::SSE());
support_message("SSE2", InstructionSet::SSE2());
support_message("SSE3", InstructionSet::SSE3());
support_message("SSE4.1", InstructionSet::SSE41());
support_message("SSE4.2", InstructionSet::SSE42());
support_message("SSE4a", InstructionSet::SSE4a());
support_message("SSSE3", InstructionSet::SSSE3());
support_message("SYSCALL", InstructionSet::SYSCALL());
support_message("TBM", InstructionSet::TBM());
support_message("XOP", InstructionSet::XOP());
support_message("XSAVE", InstructionSet::XSAVE());
std::cout << "\n";
// v2: has all of: cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3
// note: cx16 == cmpxchg16b, lahf_lm == lahf
if(InstructionSet::CMPXCHG16B() && InstructionSet::LAHF() && InstructionSet::POPCNT() &&
InstructionSet::SSE41() && InstructionSet::SSE42() && InstructionSet::SSSE3())
std::cout << "x86_64v2 supported\n";
// v3: has all of: avx avx2 bmi1 bmi2 f16c fma abm movbe xsave
// note: for Intel, lzcnt is designated for abm
if(InstructionSet::AVX() && InstructionSet::AVX2() && InstructionSet::BMI1() &&
InstructionSet::BMI2() && InstructionSet::F16C() && InstructionSet::FMA() &&
InstructionSet::MOVBE() && InstructionSet::XSAVE() &&
((InstructionSet::isAMD() && InstructionSet::ABM()) || (InstructionSet::isIntel() && InstructionSet::LZCNT())))
std::cout << "x86_64v3 supported\n";
// v4: has all of: avx512f avx512bw avx512cd avx512dq avx512vl
if(InstructionSet::AVX512F() && InstructionSet::AVX512CD() && InstructionSet::AVX512BW() &&
InstructionSet::AVX512DQ() && InstructionSet::AVX512VL())
std::cout << "x86_64v4 supported\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment