17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-01 22:49:16 +01:00
Files
maszyna/utilities/cpu_features.h

181 lines
4.1 KiB
C++

#pragma once
// the processor features the simulator is built to use (ENABLE_AVX2), checked before any code built with them runs.
// header only and free of the C runtime: the simulator checks them from its entry point, before the runtime starts
#if defined(_M_X64) || defined(__x86_64__)
#define EU07_CPU_FEATURES_X64 1
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <cpuid.h>
#endif
#endif
namespace cpu_features
{
enum feature : unsigned int
{
avx2 = 1 << 0,
fma3 = 1 << 1,
bmi1 = 1 << 2,
bmi2 = 1 << 3,
lzcnt = 1 << 4,
// the processor has AVX, but the system does not save its registers, so it is turned off (old systems, some virtual machines)
os_avx = 1 << 5,
};
// the exit code of the simulator stopped by the check of the processor, after it told the user (eu07_starter stays silent then)
constexpr unsigned int exit_unsupported{0xE0450010};
// msvc builds with /arch:AVX2 use the bit manipulation instructions of the same processor generation as well,
// gcc and clang with -mavx2 only AVX2 itself. every processor with AVX2 (Intel Haswell, AMD Excavator and newer) has them all
#ifdef _MSC_VER
constexpr unsigned int required_for_avx2{avx2 | fma3 | bmi1 | bmi2 | lzcnt | os_avx};
#else
constexpr unsigned int required_for_avx2{avx2 | os_avx};
#endif
#ifdef EU07_CPU_FEATURES_X64
inline void cpuid(int const Leaf, int const Subleaf, int Registers[4])
{
#ifdef _MSC_VER
__cpuidex(Registers, Leaf, Subleaf);
#else
unsigned int a, b, c, d;
__cpuid_count(Leaf, Subleaf, a, b, c, d);
Registers[0] = static_cast<int>(a);
Registers[1] = static_cast<int>(b);
Registers[2] = static_cast<int>(c);
Registers[3] = static_cast<int>(d);
#endif
}
// the register state the system saves on a thread switch; xgetbv may be used only when cpuid reports OSXSAVE
inline unsigned long long xcr0()
{
#ifdef _MSC_VER
return _xgetbv(0);
#else
unsigned int low, high;
__asm__ volatile("xgetbv" : "=a"(low), "=d"(high) : "c"(0));
return (static_cast<unsigned long long>(high) << 32) | low;
#endif
}
// the features of the processor, a mask of the feature values
inline unsigned int present()
{
int registers[4];
cpuid(0, 0, registers);
int const maxleaf{registers[0]};
cpuid(0x80000000, 0, registers);
unsigned int const maxextendedleaf{static_cast<unsigned int>(registers[0])};
unsigned int result{0};
cpuid(1, 0, registers);
bool const hasavx{(registers[2] & (1 << 28)) != 0};
bool const hasosxsave{(registers[2] & (1 << 27)) != 0};
if ((registers[2] & (1 << 12)) != 0)
{
result |= fma3;
}
// bits 1 and 2: the SSE and AVX registers
if (hasavx && hasosxsave && ((xcr0() & 0x6) == 0x6))
{
result |= os_avx;
}
if (maxleaf >= 7)
{
cpuid(7, 0, registers);
if ((registers[1] & (1 << 5)) != 0)
{
result |= avx2;
}
if ((registers[1] & (1 << 3)) != 0)
{
result |= bmi1;
}
if ((registers[1] & (1 << 8)) != 0)
{
result |= bmi2;
}
}
if (maxextendedleaf >= 0x80000001)
{
cpuid(0x80000001, 0, registers);
if ((registers[2] & (1 << 5)) != 0)
{
result |= lzcnt;
}
}
return result;
}
// the name of the processor, up to 48 characters
inline void brand(char Output[49])
{
int registers[4];
cpuid(0x80000000, 0, registers);
if (static_cast<unsigned int>(registers[0]) < 0x80000004)
{
Output[0] = '\0';
return;
}
for (int leaf = 0; leaf < 3; ++leaf)
{
cpuid(0x80000002 + leaf, 0, registers);
for (int index = 0; index < 16; ++index)
{
Output[leaf * 16 + index] = reinterpret_cast<char const *>(registers)[index];
}
}
Output[48] = '\0';
}
#else
inline unsigned int present()
{
return ~0u;
}
inline void brand(char Output[49])
{
Output[0] = '\0';
}
#endif
// the required features the processor does not have
inline unsigned int missing(unsigned int const Required)
{
return Required & ~present();
}
// the name of a single feature
inline char const *name(unsigned int const Feature)
{
switch (Feature)
{
case avx2:
return "AVX2";
case fma3:
return "FMA3";
case bmi1:
return "BMI1";
case bmi2:
return "BMI2";
case lzcnt:
return "LZCNT";
case os_avx:
return "AVX support of the operating system";
default:
return "?";
}
}
} // namespace cpu_features