cpuid_x86.hpp
Go to the documentation of this file.
1 #include <nil/crypto3/utilities/cpuid/cpuid.hpp>
2 #include <nil/crypto3/utilities/memory_operations.hpp>
3 #include <nil/crypto3/utilities/loadstore.hpp>
4 
5 #if defined(BOOST_ARCH_X86)
6 
7 #if defined(CRYPTO3_BUILD_COMPILER_IS_MSVC)
8 #include <intrin.h>
9 #elif defined(CRYPTO3_BUILD_COMPILER_IS_INTEL)
10 #include <ia32intrin.h>
11 #elif defined(CRYPTO3_BUILD_COMPILER_IS_GCC) || defined(CRYPTO3_BUILD_COMPILER_IS_CLANG)
12 #include <cpuid.h>
13 #endif
14 
15 #endif
16 
17 namespace nil {
18  namespace crypto3 {
19 
20 #if defined(BOOST_ARCH_X86)
21 
22  uint64_t cpuid::detect_cpu_features(size_t *cache_line_size) {
23 #if defined(CRYPTO3_BUILD_COMPILER_IS_MSVC)
24 #define X86_CPUID(type, out) \
25  do { \
26  __cpuid((int *)out, type); \
27  } while (0)
28 #define X86_CPUID_SUBLEVEL(type, level, out) \
29  do { \
30  __cpuidex((int *)out, type, level); \
31  } while (0)
32 
33 #elif defined(CRYPTO3_BUILD_COMPILER_IS_INTEL)
34 #define X86_CPUID(type, out) \
35  do { \
36  __cpuid(out, type); \
37  } while (0)
38 #define X86_CPUID_SUBLEVEL(type, level, out) \
39  do { \
40  __cpuidex((int *)out, type, level); \
41  } while (0)
42 
43 #elif defined(CRYPTO3_TARGET_ARCHITECTURE_IS_X86_64) && defined(CRYPTO3_USE_GCC_INLINE_ASM)
44 #define X86_CPUID(type, out) asm("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type))
45 
46 #define X86_CPUID_SUBLEVEL(type, level, out) \
47  asm("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type), "2"(level))
48 
49 #elif defined(CRYPTO3_BUILD_COMPILER_IS_GCC) || defined(CRYPTO3_BUILD_COMPILER_IS_CLANG)
50 #define X86_CPUID(type, out) \
51  do { \
52  __get_cpuid(type, out, out + 1, out + 2, out + 3); \
53  } while (0)
54 
55 #define X86_CPUID_SUBLEVEL(type, level, out) \
56  do { \
57  __cpuid_count(type, level, out[0], out[1], out[2], out[3]); \
58  } while (0)
59 #else
60 #warning "No way of calling x86 cpuid instruction for this compiler"
61 #define X86_CPUID(type, out) \
62  do { \
63  clear_mem(out, 4); \
64  } while (0)
65 #define X86_CPUID_SUBLEVEL(type, level, out) \
66  do { \
67  clear_mem(out, 4); \
68  } while (0)
69 #endif
70 
71  uint64_t features_detected = 0;
72  uint32_t cpuid[4] = {0};
73 
74  // cpuid 0: vendor identification, max sublevel
75  X86_CPUID(0, cpuid);
76 
77  const uint32_t max_supported_sublevel = cpuid[0];
78 
79  const uint32_t INTEL_CPUID[3] = {0x756E6547, 0x6C65746E, 0x49656E69};
80  const uint32_t AMD_CPUID[3] = {0x68747541, 0x444D4163, 0x69746E65};
81  const bool is_intel = same_mem(cpuid + 1, INTEL_CPUID, 3);
82  const bool is_amd = same_mem(cpuid + 1, AMD_CPUID, 3);
83 
84  if (max_supported_sublevel >= 1) {
85  // cpuid 1: feature bits
86  X86_CPUID(1, cpuid);
87  const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
88 
89  enum x86_CPUID_1_bits : uint64_t {
90  RDTSC = (1ULL << 4),
91  SSE2 = (1ULL << 26),
92  CLMUL = (1ULL << 33),
93  SSSE3 = (1ULL << 41),
94  SSE41 = (1ULL << 51),
95  SSE42 = (1ULL << 52),
96  AESNI = (1ULL << 57),
97  RDRAND = (1ULL << 62)
98  };
99 
100  if (flags0 & x86_CPUID_1_bits::RDTSC)
101  features_detected |= cpuid::CPUID_RDTSC_BIT;
102  if (flags0 & x86_CPUID_1_bits::SSE2)
103  features_detected |= cpuid::CPUID_SSE2_BIT;
104  if (flags0 & x86_CPUID_1_bits::CLMUL)
105  features_detected |= cpuid::CPUID_CLMUL_BIT;
106  if (flags0 & x86_CPUID_1_bits::SSSE3)
107  features_detected |= cpuid::CPUID_SSSE3_BIT;
108  if (flags0 & x86_CPUID_1_bits::SSE41)
109  features_detected |= cpuid::CPUID_SSE41_BIT;
110  if (flags0 & x86_CPUID_1_bits::SSE42)
111  features_detected |= cpuid::CPUID_SSE42_BIT;
112  if (flags0 & x86_CPUID_1_bits::AESNI)
113  features_detected |= cpuid::CPUID_AESNI_BIT;
114  if (flags0 & x86_CPUID_1_bits::RDRAND)
115  features_detected |= cpuid::CPUID_RDRAND_BIT;
116  }
117 
118  if (is_intel) {
119  // Intel cache line size is in cpuid(1) output
120  *cache_line_size = 8 * extract_uint_t<CHAR_BIT>(cpuid[1], 2);
121  } else if (is_amd) {
122  // AMD puts it in vendor zone
123  X86_CPUID(0x80000005, cpuid);
124  *cache_line_size = extract_uint_t<CHAR_BIT>(cpuid[2], 3);
125  }
126 
127  if (max_supported_sublevel >= 7) {
128  clear_mem(cpuid, 4);
129  X86_CPUID_SUBLEVEL(7, 0, cpuid);
130 
131  enum x86_CPUID_7_bits : uint64_t {
132  AVX2 = (1ULL << 5),
133  BMI2 = (1ULL << 8),
134  AVX512F = (1ULL << 16),
135  RDSEED = (1ULL << 18),
136  ADX = (1ULL << 19),
137  SHA = (1ULL << 29),
138  };
139  uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
140 
141  if (flags7 & x86_CPUID_7_bits::AVX2)
142  features_detected |= cpuid::CPUID_AVX2_BIT;
143  if (flags7 & x86_CPUID_7_bits::BMI2)
144  features_detected |= cpuid::CPUID_BMI2_BIT;
145  if (flags7 & x86_CPUID_7_bits::AVX512F)
146  features_detected |= cpuid::CPUID_AVX512F_BIT;
147  if (flags7 & x86_CPUID_7_bits::RDSEED)
148  features_detected |= cpuid::CPUID_RDSEED_BIT;
149  if (flags7 & x86_CPUID_7_bits::ADX)
150  features_detected |= cpuid::CPUID_ADX_BIT;
151  if (flags7 & x86_CPUID_7_bits::SHA)
152  features_detected |= cpuid::CPUID_SHA_BIT;
153  }
154 
155 #undef X86_CPUID
156 #undef X86_CPUID_SUBLEVEL
157 
158  /*
159  * If we don't have access to cpuid, we can still safely assume that
160  * any x86-64 processor has SSE2 and RDTSC
161  */
162 #if defined(CRYPTO3_TARGET_ARCHITECTURE_IS_X86_64)
163  if (features_detected == 0) {
164  features_detected |= cpuid::CPUID_SSE2_BIT;
165  features_detected |= cpuid::CPUID_RDTSC_BIT;
166  }
167 #endif
168 
169  return features_detected;
170  }
171 
172 #endif
173  } // namespace crypto3
174 } // namespace nil
static size_t cache_line_size()
Definition: cpuid.hpp:134
bool same_mem(const T *p1, const T *p2, size_t n)
Definition: memory_operations.hpp:229
void clear_mem(T *ptr, size_t n)
Definition: memory_operations.hpp:175
Definition: pair.hpp:31