Visual Servoing Platform  version 3.6.1 under development (2024-10-18)
cpu_x86.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  */
30 
31 /* cpu_x86.cpp
32  *
33  * Author : Alexander J. Yee
34  * Date Created : 04/12/2014
35  * Last Modified : 04/12/2014
36  *
37  * Modification for ViSP:
38  * - UNKNOWN_ARCH (ARM, ...)
39  * - ifndef _XCR_XFEATURE_ENABLED_MASK (MinGW)
40  */
41 
46 // Dependencies
47 #include "cpu_x86.h"
48 #include <cstring>
49 #include <iostream>
54 #if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
55 #if defined(_WIN32) && (!defined(__MINGW32__) || (!defined(__i386) && !defined(_M_IX86)))
56 #include "cpu_x86_Windows.ipp"
57 #elif defined(__GNUC__) || defined(__clang__)
58 #include "cpu_x86_Linux.ipp"
59 #else
60 //# error "No cpuid intrinsic defined for compiler."
61 #define UNKNOWN_ARCH
62 #endif
63 #else
64 //# error "No cpuid intrinsic defined for processor architecture."
65 #define UNKNOWN_ARCH
66 #endif
67 
68 #ifndef _XCR_XFEATURE_ENABLED_MASK
69 #define _XCR_XFEATURE_ENABLED_MASK 0
70 #endif
71 
72 #ifndef DOXYGEN_SHOULD_SKIP_THIS
73 namespace FeatureDetector
74 {
75 using std::cout;
76 using std::endl;
77 using std::memcpy;
78 using std::memset;
83 void cpuX86::print(const char *label, bool yes)
84 {
85  cout << label;
86  cout << (yes ? "Yes" : "No") << endl;
87 }
92 cpuX86::cpuX86()
93 {
94  memset(this, 0, sizeof(*this));
95  detect_host();
96 }
97 bool cpuX86::detect_OS_AVX()
98 {
99 #ifndef UNKNOWN_ARCH
100  // Copied from: http://stackoverflow.com/a/22521619/922184
101 
102  bool avxSupported = false;
103 
104  uint32_t cpuInfo[4];
105  cpuid(cpuInfo, 1);
106  const unsigned int index_2 = 2;
107  const unsigned int val_27 = 27;
108  const unsigned int val_28 = 28;
109 
110  bool osUsesXSAVE_XRSTORE = (cpuInfo[index_2] & (1U << val_27)) != 0;
111  bool cpuAVXSuport = (cpuInfo[index_2] & (1U << val_28)) != 0;
112 
113  if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
114  uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
115  avxSupported = (xcrFeatureMask & 0x6U) == 0x6U;
116  }
117 
118  return avxSupported;
119 #else
120  return false;
121 #endif
122 }
123 bool cpuX86::detect_OS_AVX512()
124 {
125 #ifndef UNKNOWN_ARCH
126  if (!detect_OS_AVX()) {
127  return false;
128  }
129 
130  uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
131  return (xcrFeatureMask & 0xe6U) == 0xe6U;
132 #else
133  return false;
134 #endif
135 }
136 std::string cpuX86::get_vendor_string()
137 {
138 #ifndef UNKNOWN_ARCH
139  uint32_t CPUInfo[4];
140  char name[13];
141 
142  cpuid(CPUInfo, 0);
143  const unsigned int index_1 = 1;
144  const unsigned int index_2 = 2;
145  const unsigned int index_3 = 3;
146  const unsigned int val_4 = 4;
147  memcpy(name + 0, &CPUInfo[index_1], val_4);
148  memcpy(name + 4, &CPUInfo[index_3], val_4);
149  memcpy(name + 8, &CPUInfo[index_2], val_4);
150  name[12] = '\0';
151 
152  return name;
153 #else
154  return std::string();
155 #endif
156 }
161 void cpuX86::detect_host()
162 {
163 #ifndef UNKNOWN_ARCH
164  // OS Features
165  OS_x64 = detect_OS_x64();
166  OS_AVX = detect_OS_AVX();
167  OS_AVX512 = detect_OS_AVX512();
168 
169  // Vendor
170  std::string vendor(get_vendor_string());
171  if (vendor == "GenuineIntel") {
172  Vendor_Intel = true;
173  }
174  else if (vendor == "AuthenticAMD") {
175  Vendor_AMD = true;
176  }
177 
178  uint32_t info[4];
179  cpuid(info, 0);
180  int nIds = info[0];
181 
182  cpuid(info, 0x80000000);
183  uint32_t nExIds = info[0];
184  const unsigned int index_1 = 1;
185  const unsigned int index_2 = 2;
186  const unsigned int index_3 = 3;
187  const unsigned int val_1 = 1;
188  const unsigned int val_3 = 3;
189  const unsigned int val_5 = 5;
190  const unsigned int val_6 = 6;
191  const unsigned int val_8 = 8;
192  const unsigned int val_9 = 9;
193  const unsigned int val_11 = 11;
194  const unsigned int val_12 = 12;
195  const unsigned int val_14 = 14;
196  const unsigned int val_16 = 16;
197  const unsigned int val_17 = 17;
198  const unsigned int val_19 = 19;
199  const unsigned int val_20 = 20;
200  const unsigned int val_21 = 21;
201  const unsigned int val_23 = 23;
202  const unsigned int val_25 = 25;
203  const unsigned int val_26 = 26;
204  const unsigned int val_27 = 27;
205  const unsigned int val_28 = 28;
206  const unsigned int val_29 = 29;
207  const unsigned int val_30 = 30;
208  const unsigned int val_31 = 31;
209 
210  // Detect Features
211  if (nIds >= 0x00000001) {
212  cpuid(info, 0x00000001);
213  HW_MMX = (info[index_3] & (1U << val_23)) != 0U;
214  HW_SSE = (info[index_3] & (1U << val_25)) != 0U;
215  HW_SSE2 = (info[index_3] & (1U << val_26)) != 0U;
216  HW_SSE3 = (info[index_2] & (1U << 0)) != 0U;
217 
218  HW_SSSE3 = (info[index_2] & (1U << val_9)) != 0U;
219  HW_SSE41 = (info[index_2] & (1U << val_19)) != 0U;
220  HW_SSE42 = (info[index_2] & (1U << val_20)) != 0U;
221  HW_AES = (info[index_2] & (1U << val_25)) != 0U;
222 
223  HW_AVX = (info[index_2] & (1U << val_28)) != 0U;
224  HW_FMA3 = (info[index_2] & (1U << val_12)) != 0U;
225 
226  HW_RDRAND = (info[index_2] & (1U << val_30)) != 0U;
227  }
228  if (nIds >= 0x00000007) {
229  cpuid(info, 0x00000007);
230  HW_AVX2 = (info[index_1] & (1U << val_5)) != 0U;
231 
232  HW_BMI1 = (info[index_1] & (1U << val_3)) != 0U;
233  HW_BMI2 = (info[index_1] & (1U << val_8)) != 0U;
234  HW_ADX = (info[index_1] & (1U << val_19)) != 0U;
235  HW_MPX = (info[index_1] & (1U << val_14)) != 0U;
236  HW_SHA = (info[index_1] & (1U << val_29)) != 0U;
237  HW_PREFETCHWT1 = (info[index_2] & (1U << 0)) != 0U;
238 
239  HW_AVX512_F = (info[index_1] & (1U << val_16)) != 0U;
240  HW_AVX512_CD = (info[index_1] & (1U << val_28)) != 0U;
241  HW_AVX512_PF = (info[index_1] & (1U << val_26)) != 0U;
242  HW_AVX512_ER = (info[index_1] & (1U << val_27)) != 0U;
243  HW_AVX512_VL = (info[index_1] & (1U << val_31)) != 0U;
244  HW_AVX512_BW = (info[index_1] & (1U << val_30)) != 0U;
245  HW_AVX512_DQ = (info[index_1] & (1U << val_17)) != 0U;
246  HW_AVX512_IFMA = (info[index_1] & (1U << val_21)) != 0U;
247  HW_AVX512_VBMI = (info[index_2] & (1U << val_1)) != 0U;
248  }
249  if (nExIds >= 0x80000001) {
250  cpuid(info, 0x80000001);
251  HW_x64 = (info[index_3] & (1U << val_29)) != 0U;
252  HW_ABM = (info[index_2] & (1U << val_5)) != 0U;
253  HW_SSE4a = (info[index_2] & (1U << val_6)) != 0U;
254  HW_FMA4 = (info[index_2] & (1U << val_16)) != 0U;
255  HW_XOP = (info[index_2] & (1U << val_11)) != 0U;
256  }
257 #endif
258 }
259 void cpuX86::print() const
260 {
261  cout << "CPU Vendor:" << endl;
262  print(" AMD = ", Vendor_AMD);
263  print(" Intel = ", Vendor_Intel);
264  cout << endl;
265 
266  cout << "OS Features:" << endl;
267 #ifdef _WIN32
268  print(" 64-bit = ", OS_x64);
269 #endif
270  print(" OS AVX = ", OS_AVX);
271  print(" OS AVX512 = ", OS_AVX512);
272  cout << endl;
273 
274  cout << "Hardware Features:" << endl;
275  print(" MMX = ", HW_MMX);
276  print(" x64 = ", HW_x64);
277  print(" ABM = ", HW_ABM);
278  print(" RDRAND = ", HW_RDRAND);
279  print(" BMI1 = ", HW_BMI1);
280  print(" BMI2 = ", HW_BMI2);
281  print(" ADX = ", HW_ADX);
282  print(" MPX = ", HW_MPX);
283  print(" PREFETCHWT1 = ", HW_PREFETCHWT1);
284  cout << endl;
285 
286  cout << "SIMD: 128-bit" << endl;
287  print(" SSE = ", HW_SSE);
288  print(" SSE2 = ", HW_SSE2);
289  print(" SSE3 = ", HW_SSE3);
290  print(" SSSE3 = ", HW_SSSE3);
291  print(" SSE4a = ", HW_SSE4a);
292  print(" SSE4.1 = ", HW_SSE41);
293  print(" SSE4.2 = ", HW_SSE42);
294  print(" AES-NI = ", HW_AES);
295  print(" SHA = ", HW_SHA);
296  cout << endl;
297 
298  cout << "SIMD: 256-bit" << endl;
299  print(" AVX = ", HW_AVX);
300  print(" XOP = ", HW_XOP);
301  print(" FMA3 = ", HW_FMA3);
302  print(" FMA4 = ", HW_FMA4);
303  print(" AVX2 = ", HW_AVX2);
304  cout << endl;
305 
306  cout << "SIMD: 512-bit" << endl;
307  print(" AVX512-F = ", HW_AVX512_F);
308  print(" AVX512-CD = ", HW_AVX512_CD);
309  print(" AVX512-PF = ", HW_AVX512_PF);
310  print(" AVX512-ER = ", HW_AVX512_ER);
311  print(" AVX512-VL = ", HW_AVX512_VL);
312  print(" AVX512-BW = ", HW_AVX512_BW);
313  print(" AVX512-DQ = ", HW_AVX512_DQ);
314  print(" AVX512-IFMA = ", HW_AVX512_IFMA);
315  print(" AVX512-VBMI = ", HW_AVX512_VBMI);
316  cout << endl;
317 
318  cout << "Summary:" << endl;
319  print(" Safe to use AVX: ", HW_AVX && OS_AVX);
320  print(" Safe to use AVX512: ", HW_AVX512_F && OS_AVX512);
321  cout << endl;
322 }
327 } // namespace FeatureDetector
328 #endif