Skip to content

Commit

Permalink
Add cpuid.cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
kimwalisch committed Jun 19, 2024
1 parent 7b4fa29 commit fe92aea
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 121 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ set(BIN_SRC src/app/CmdOptions.cpp

set(LIB_SRC src/api.cpp
src/api_c.cpp
src/cpuid.cpp
src/BitSieve240.cpp
src/FactorTable.cpp
src/RiemannR.cpp
Expand Down
3 changes: 3 additions & 0 deletions cmake/multiarch_avx512_vpopcnt.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ check_cxx_source_compiles("
Error: AVX512 BMI2 multiarch not needed!
#endif
#define ENABLE_MULTIARCH_AVX512_BMI2
#include <cpuid.hpp>
#include <cpu_supports_avx512_bmi2.hpp>
#include <immintrin.h>
#include <stdint.h>
Expand Down
40 changes: 40 additions & 0 deletions include/check_enable_cpuid_popcnt.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
///
/// @file check_enable_cpuid_popcnt.hpp
/// @brief Check if runtime POPCNT detection should be enabled on
/// x86 and x64 CPUs.
///
/// Copyright (C) 2024 Kim Walisch, <[email protected]>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef CHECK_ENABLE_CPUID_POPCNT_HPP
#define CHECK_ENABLE_CPUID_POPCNT_HPP

// Enable CPUID on x86 and x86-64 CPUs
#if defined(__x86_64__) || \
defined(__i386__) || \
defined(_M_X64) || \
defined(_M_IX86)

// Both GCC and Clang (even Clang on Windows) define the __POPCNT__
// macro if the user compiles with -mpopcnt. The __POPCNT__
// macro is even defined if the user compiles with other flags
// such as -mavx or -march=native.
#if defined(__POPCNT__)
#define HAS_POPCNT
// The MSVC compiler does not support a POPCNT macro, but if the user
// compiles with e.g. /arch:AVX or /arch:AVX512 then MSVC defines
// the __AVX__ macro and POPCNT is also supported.
#elif defined(_MSC_VER) && defined(__AVX__)
#define HAS_POPCNT
#endif

#if !defined(HAS_POPCNT)
#define ENABLE_CPUID_POPCNT
#endif

#endif // x86 CPUs

#endif
79 changes: 4 additions & 75 deletions include/cpu_supports_avx512_bmi2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,87 +11,16 @@
#ifndef CPU_SUPPORTS_AVX512_BMI2_HPP
#define CPU_SUPPORTS_AVX512_BMI2_HPP

#include <cpuid.hpp>
namespace primecount {

#if defined(_MSC_VER)
#include <immintrin.h>
#endif

// CPUID bits documentation:
// https://en.wikipedia.org/wiki/CPUID

// %ebx bit flags
#define bit_BMI2 (1 << 8)
#define bit_AVX512F (1 << 16)
bool run_cpuid_avx512_bmi2();

// %ecx bit flags
#define bit_AVX512_VPOPCNTDQ (1 << 14)

// xgetbv bit flags
#define XSTATE_SSE (1 << 1)
#define XSTATE_YMM (1 << 2)
#define XSTATE_ZMM (7 << 5)
} // namespace

namespace {

// Get Value of Extended Control Register
inline int get_xcr0()
{
int xcr0;

#if defined(_MSC_VER)
xcr0 = (int) _xgetbv(0);
#else
__asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" );
#endif

return xcr0;
}

inline bool run_cpuid_avx512_bmi2()
{
int eax = 1;
int ebx = 0;
int ecx = 0;
int edx = 0;

run_cpuid(&eax, &ebx, &ecx, &edx);

int osxsave_mask = (1 << 27);

// Ensure OS supports extended processor state management
if ((ecx & osxsave_mask) != osxsave_mask)
return false;

int ymm_mask = XSTATE_SSE | XSTATE_YMM;
int zmm_mask = XSTATE_SSE | XSTATE_YMM | XSTATE_ZMM;
int xcr0 = get_xcr0();

// Check AVX OS support
if ((xcr0 & ymm_mask) != ymm_mask)
return false;

// Check AVX512 OS support
if ((xcr0 & zmm_mask) != zmm_mask)
return false;

eax = 7;
ebx = 0;
ecx = 0;
edx = 0;

run_cpuid(&eax, &ebx, &ecx, &edx);

if ((ebx & bit_BMI2) != bit_BMI2)
return false;

// AVX512F, AVX512VPOPCNTDQ
return ((ebx & bit_AVX512F) == bit_AVX512F &&
(ecx & bit_AVX512_VPOPCNTDQ) == bit_AVX512_VPOPCNTDQ);
}

/// Initialized at startup
bool cpu_supports_avx512_bmi2 = run_cpuid_avx512_bmi2();
bool cpu_supports_avx512_bmi2 = primecount::run_cpuid_avx512_bmi2();

} // namespace

Expand Down
45 changes: 8 additions & 37 deletions include/cpu_supports_popcnt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,23 @@
#ifndef CPU_SUPPORTS_POPCNT_HPP
#define CPU_SUPPORTS_POPCNT_HPP

// Enable CPUID on x86 and x86-64 CPUs
#if defined(__x86_64__) || \
defined(__i386__) || \
defined(_M_X64) || \
defined(_M_IX86)

// Both GCC and Clang (even Clang on Windows) define the __POPCNT__
// macro if the user compiles with -mpopcnt. The __POPCNT__
// macro is even defined if the user compiles with other flags
// such as -mavx or -march=native.
#if defined(__POPCNT__)
#define HAS_POPCNT
// The MSVC compiler does not support a POPCNT macro, but if the user
// compiles with e.g. /arch:AVX or /arch:AVX512 then MSVC defines
// the __AVX__ macro and POPCNT is also supported.
#elif defined(_MSC_VER) && defined(__AVX__)
#define HAS_POPCNT
#endif

#if !defined(HAS_POPCNT)
#include <check_enable_cpuid_popcnt.hpp>

#include <cpuid.hpp>
#define ENABLE_CPUID_POPCNT
#if defined(ENABLE_CPUID_POPCNT)

namespace {
namespace primecount {

inline bool run_cpuid_supports_popcnt()
{
int eax = 1;
int ebx = 0;
int ecx = 0;
int edx = 0;
bool run_cpuid_supports_popcnt();

run_cpuid(&eax, &ebx, &ecx, &edx);
} // namespace

// https://en.wikipedia.org/wiki/CPUID
int bit_POPCNT = 1 << 23;
return (ecx & bit_POPCNT) == bit_POPCNT;
}
namespace {

/// Initialized at startup
bool cpu_supports_popcnt = run_cpuid_supports_popcnt();
bool cpu_supports_popcnt = primecount::run_cpuid_supports_popcnt();

} // namespace

#endif // !defined(HAS_POPCNT)
#endif // CPUID
#endif // ENABLE_CPUID_POPCNT

#endif
118 changes: 109 additions & 9 deletions include/cpuid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#ifndef CPUID_HPP
#define CPUID_HPP

#include <check_enable_cpuid_popcnt.hpp>

#if defined(_MSC_VER)
#include <intrin.h>
#endif
Expand All @@ -23,7 +25,6 @@ inline void run_cpuid(int* eax, int* ebx, int* ecx, int* edx)

int abcd[4];
__cpuidex(abcd, *eax, *ecx);

*eax = abcd[0];
*ebx = abcd[1];
*ecx = abcd[2];
Expand All @@ -37,26 +38,125 @@ inline void run_cpuid(int* eax, int* ebx, int* ecx, int* edx)
"movl %%ebx, %%edi;"
"cpuid;"
"xchgl %%ebx, %%edi;"
: "=D" (*ebx),
"+a" (*eax),
"+c" (*ecx),
"=d" (*edx)
: "=D" (*ebx), "+a" (*eax), "+c" (*ecx), "=d" (*edx)
);

#else

__asm__ (
"cpuid"
: "=a" (*eax),
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "a" (*eax), "c" (*ecx)
);

#endif
}

#if defined(ENABLE_MULTIARCH_AVX512_BMI2)

// Get Value of Extended Control Register
inline int get_xcr0()
{
int xcr0;

#if defined(_MSC_VER)
xcr0 = (int) _xgetbv(0);
#else
__asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" );
#endif

return xcr0;
}

#endif

} // namespace

#if defined(ENABLE_CPUID_POPCNT)

namespace primecount {

bool run_cpuid_supports_popcnt()
{
int eax = 1;
int ebx = 0;
int ecx = 0;
int edx = 0;

run_cpuid(&eax, &ebx, &ecx, &edx);

// https://en.wikipedia.org/wiki/CPUID
int bit_POPCNT = 1 << 23;
return (ecx & bit_POPCNT) == bit_POPCNT;
}

} // namespace

#endif

#if defined(ENABLE_MULTIARCH_AVX512_BMI2)

// CPUID bits documentation:
// https://en.wikipedia.org/wiki/CPUID

// %ebx bit flags
#define bit_BMI2 (1 << 8)
#define bit_AVX512F (1 << 16)

// %ecx bit flags
#define bit_AVX512_VPOPCNTDQ (1 << 14)

// xgetbv bit flags
#define XSTATE_SSE (1 << 1)
#define XSTATE_YMM (1 << 2)
#define XSTATE_ZMM (7 << 5)

namespace primecount {

bool run_cpuid_avx512_bmi2()
{
int eax = 1;
int ebx = 0;
int ecx = 0;
int edx = 0;

run_cpuid(&eax, &ebx, &ecx, &edx);

int osxsave_mask = (1 << 27);

// Ensure OS supports extended processor state management
if ((ecx & osxsave_mask) != osxsave_mask)
return false;

int ymm_mask = XSTATE_SSE | XSTATE_YMM;
int zmm_mask = XSTATE_SSE | XSTATE_YMM | XSTATE_ZMM;
int xcr0 = get_xcr0();

// Check AVX OS support
if ((xcr0 & ymm_mask) != ymm_mask)
return false;

// Check AVX512 OS support
if ((xcr0 & zmm_mask) != zmm_mask)
return false;

eax = 7;
ebx = 0;
ecx = 0;
edx = 0;

run_cpuid(&eax, &ebx, &ecx, &edx);

if ((ebx & bit_BMI2) != bit_BMI2)
return false;

// AVX512F, AVX512VPOPCNTDQ
return ((ebx & bit_AVX512F) == bit_AVX512F &&
(ecx & bit_AVX512_VPOPCNTDQ) == bit_AVX512_VPOPCNTDQ);
}

} // namespace

#endif

#endif
19 changes: 19 additions & 0 deletions src/cpuid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
///
/// @file cpuid.cpp
/// @brief CPUID for x86 and x86-64 CPUs.
///
/// Copyright (C) 2024 Kim Walisch, <[email protected]>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

// Enable CPUID on x86 and x86-64 CPUs
#if defined(__x86_64__) || \
defined(__i386__) || \
defined(_M_X64) || \
defined(_M_IX86)

#include <cpuid.hpp>

#endif

0 comments on commit fe92aea

Please sign in to comment.