V8 Project
cpu.cc
Go to the documentation of this file.
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/base/cpu.h"
6 
7 #if V8_LIBC_MSVCRT
8 #include <intrin.h> // __cpuid()
9 #endif
10 #if V8_OS_POSIX
11 #include <unistd.h> // sysconf()
12 #endif
13 #if V8_OS_QNX
14 #include <sys/syspage.h> // cpuinfo
15 #endif
16 
17 #include <ctype.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <algorithm>
23 
24 #include "src/base/logging.h"
25 #if V8_OS_WIN
26 #include "src/base/win32-headers.h" // NOLINT
27 #endif
28 
29 namespace v8 {
30 namespace base {
31 
32 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
33 
34 // Define __cpuid() for non-MSVC libraries.
35 #if !V8_LIBC_MSVCRT
36 
37 static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {
38 #if defined(__i386__) && defined(__pic__)
39  // Make sure to preserve ebx, which contains the pointer
40  // to the GOT in case we're generating PIC.
41  __asm__ volatile (
42  "mov %%ebx, %%edi\n\t"
43  "cpuid\n\t"
44  "xchg %%edi, %%ebx\n\t"
45  : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
46  : "a"(info_type)
47  );
48 #else
49  __asm__ volatile (
50  "cpuid \n\t"
51  : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
52  : "a"(info_type)
53  );
54 #endif // defined(__i386__) && defined(__pic__)
55 }
56 
57 #endif // !V8_LIBC_MSVCRT
58 
59 #elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 \
60  || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
61 
62 #if V8_OS_LINUX
63 
64 #if V8_HOST_ARCH_ARM
65 
66 // See <uapi/asm/hwcap.h> kernel header.
67 /*
68  * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
69  */
70 #define HWCAP_SWP (1 << 0)
71 #define HWCAP_HALF (1 << 1)
72 #define HWCAP_THUMB (1 << 2)
73 #define HWCAP_26BIT (1 << 3) /* Play it safe */
74 #define HWCAP_FAST_MULT (1 << 4)
75 #define HWCAP_FPA (1 << 5)
76 #define HWCAP_VFP (1 << 6)
77 #define HWCAP_EDSP (1 << 7)
78 #define HWCAP_JAVA (1 << 8)
79 #define HWCAP_IWMMXT (1 << 9)
80 #define HWCAP_CRUNCH (1 << 10)
81 #define HWCAP_THUMBEE (1 << 11)
82 #define HWCAP_NEON (1 << 12)
83 #define HWCAP_VFPv3 (1 << 13)
84 #define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
85 #define HWCAP_TLS (1 << 15)
86 #define HWCAP_VFPv4 (1 << 16)
87 #define HWCAP_IDIVA (1 << 17)
88 #define HWCAP_IDIVT (1 << 18)
89 #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
90 #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
91 #define HWCAP_LPAE (1 << 20)
92 
93 #define AT_HWCAP 16
94 
95 // Read the ELF HWCAP flags by parsing /proc/self/auxv.
96 static uint32_t ReadELFHWCaps() {
97  uint32_t result = 0;
98  FILE* fp = fopen("/proc/self/auxv", "r");
99  if (fp != NULL) {
100  struct { uint32_t tag; uint32_t value; } entry;
101  for (;;) {
102  size_t n = fread(&entry, sizeof(entry), 1, fp);
103  if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
104  break;
105  }
106  if (entry.tag == AT_HWCAP) {
107  result = entry.value;
108  break;
109  }
110  }
111  fclose(fp);
112  }
113  return result;
114 }
115 
116 #endif // V8_HOST_ARCH_ARM
117 
118 #if V8_HOST_ARCH_MIPS
119 int __detect_fp64_mode(void) {
120  double result = 0;
121  // Bit representation of (double)1 is 0x3FF0000000000000.
122  asm(
123  "lui $t0, 0x3FF0\n\t"
124  "ldc1 $f0, %0\n\t"
125  "mtc1 $t0, $f1\n\t"
126  "sdc1 $f0, %0\n\t"
127  : "+m" (result)
128  : : "t0", "$f0", "$f1", "memory");
129 
130  return !(result == 1);
131 }
132 
133 
134 int __detect_mips_arch_revision(void) {
135  // TODO(dusmil): Do the specific syscall as soon as it is implemented in mips
136  // kernel. Currently fail-back to the least common denominator which is
137  // mips32 revision 1.
138  return 1;
139 }
140 #endif
141 
142 // Extract the information exposed by the kernel via /proc/cpuinfo.
143 class CPUInfo FINAL {
144  public:
145  CPUInfo() : datalen_(0) {
146  // Get the size of the cpuinfo file by reading it until the end. This is
147  // required because files under /proc do not always return a valid size
148  // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
149  static const char PATHNAME[] = "/proc/cpuinfo";
150  FILE* fp = fopen(PATHNAME, "r");
151  if (fp != NULL) {
152  for (;;) {
153  char buffer[256];
154  size_t n = fread(buffer, 1, sizeof(buffer), fp);
155  if (n == 0) {
156  break;
157  }
158  datalen_ += n;
159  }
160  fclose(fp);
161  }
162 
163  // Read the contents of the cpuinfo file.
164  data_ = new char[datalen_ + 1];
165  fp = fopen(PATHNAME, "r");
166  if (fp != NULL) {
167  for (size_t offset = 0; offset < datalen_; ) {
168  size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
169  if (n == 0) {
170  break;
171  }
172  offset += n;
173  }
174  fclose(fp);
175  }
176 
177  // Zero-terminate the data.
178  data_[datalen_] = '\0';
179  }
180 
181  ~CPUInfo() {
182  delete[] data_;
183  }
184 
185  // Extract the content of a the first occurence of a given field in
186  // the content of the cpuinfo file and return it as a heap-allocated
187  // string that must be freed by the caller using delete[].
188  // Return NULL if not found.
189  char* ExtractField(const char* field) const {
190  DCHECK(field != NULL);
191 
192  // Look for first field occurence, and ensure it starts the line.
193  size_t fieldlen = strlen(field);
194  char* p = data_;
195  for (;;) {
196  p = strstr(p, field);
197  if (p == NULL) {
198  return NULL;
199  }
200  if (p == data_ || p[-1] == '\n') {
201  break;
202  }
203  p += fieldlen;
204  }
205 
206  // Skip to the first colon followed by a space.
207  p = strchr(p + fieldlen, ':');
208  if (p == NULL || !isspace(p[1])) {
209  return NULL;
210  }
211  p += 2;
212 
213  // Find the end of the line.
214  char* q = strchr(p, '\n');
215  if (q == NULL) {
216  q = data_ + datalen_;
217  }
218 
219  // Copy the line into a heap-allocated buffer.
220  size_t len = q - p;
221  char* result = new char[len + 1];
222  if (result != NULL) {
223  memcpy(result, p, len);
224  result[len] = '\0';
225  }
226  return result;
227  }
228 
229  private:
230  char* data_;
231  size_t datalen_;
232 };
233 
234 #if V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
235 
236 // Checks that a space-separated list of items contains one given 'item'.
237 static bool HasListItem(const char* list, const char* item) {
238  ssize_t item_len = strlen(item);
239  const char* p = list;
240  if (p != NULL) {
241  while (*p != '\0') {
242  // Skip whitespace.
243  while (isspace(*p)) ++p;
244 
245  // Find end of current list item.
246  const char* q = p;
247  while (*q != '\0' && !isspace(*q)) ++q;
248 
249  if (item_len == q - p && memcmp(p, item, item_len) == 0) {
250  return true;
251  }
252 
253  // Skip to next item.
254  p = q;
255  }
256  }
257  return false;
258 }
259 
260 #endif // V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
261 
262 #endif // V8_OS_LINUX
263 
264 #endif // V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
265 
266 CPU::CPU() : stepping_(0),
267  model_(0),
268  ext_model_(0),
269  family_(0),
270  ext_family_(0),
271  type_(0),
272  implementer_(0),
273  architecture_(0),
274  part_(0),
275  has_fpu_(false),
276  has_cmov_(false),
277  has_sahf_(false),
278  has_mmx_(false),
279  has_sse_(false),
280  has_sse2_(false),
281  has_sse3_(false),
282  has_ssse3_(false),
283  has_sse41_(false),
284  has_sse42_(false),
285  has_idiva_(false),
286  has_neon_(false),
287  has_thumb2_(false),
288  has_vfp_(false),
289  has_vfp3_(false),
290  has_vfp3_d32_(false),
291  is_fp64_mode_(false) {
292  memcpy(vendor_, "Unknown", 8);
293 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
294  int cpu_info[4];
295 
296  // __cpuid with an InfoType argument of 0 returns the number of
297  // valid Ids in CPUInfo[0] and the CPU identification string in
298  // the other three array elements. The CPU identification string is
299  // not in linear order. The code below arranges the information
300  // in a human readable form. The human readable order is CPUInfo[1] |
301  // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
302  // before using memcpy to copy these three array elements to cpu_string.
303  __cpuid(cpu_info, 0);
304  unsigned num_ids = cpu_info[0];
305  std::swap(cpu_info[2], cpu_info[3]);
306  memcpy(vendor_, cpu_info + 1, 12);
307  vendor_[12] = '\0';
308 
309  // Interpret CPU feature information.
310  if (num_ids > 0) {
311  __cpuid(cpu_info, 1);
312  stepping_ = cpu_info[0] & 0xf;
313  model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
314  family_ = (cpu_info[0] >> 8) & 0xf;
315  type_ = (cpu_info[0] >> 12) & 0x3;
316  ext_model_ = (cpu_info[0] >> 16) & 0xf;
317  ext_family_ = (cpu_info[0] >> 20) & 0xff;
318  has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
319  has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
320  has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
321  has_sse_ = (cpu_info[3] & 0x02000000) != 0;
322  has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
323  has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
324  has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
325  has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
326  has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
327  }
328 
329 #if V8_HOST_ARCH_IA32
330  // SAHF is always available in compat/legacy mode,
331  has_sahf_ = true;
332 #else
333  // Query extended IDs.
334  __cpuid(cpu_info, 0x80000000);
335  unsigned num_ext_ids = cpu_info[0];
336 
337  // Interpret extended CPU feature information.
338  if (num_ext_ids > 0x80000000) {
339  __cpuid(cpu_info, 0x80000001);
340  // SAHF must be probed in long mode.
341  has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
342  }
343 #endif
344 
345 #elif V8_HOST_ARCH_ARM
346 
347 #if V8_OS_LINUX
348 
349  CPUInfo cpu_info;
350 
351  // Extract implementor from the "CPU implementer" field.
352  char* implementer = cpu_info.ExtractField("CPU implementer");
353  if (implementer != NULL) {
354  char* end ;
355  implementer_ = strtol(implementer, &end, 0);
356  if (end == implementer) {
357  implementer_ = 0;
358  }
359  delete[] implementer;
360  }
361 
362  // Extract part number from the "CPU part" field.
363  char* part = cpu_info.ExtractField("CPU part");
364  if (part != NULL) {
365  char* end ;
366  part_ = strtol(part, &end, 0);
367  if (end == part) {
368  part_ = 0;
369  }
370  delete[] part;
371  }
372 
373  // Extract architecture from the "CPU Architecture" field.
374  // The list is well-known, unlike the the output of
375  // the 'Processor' field which can vary greatly.
376  // See the definition of the 'proc_arch' array in
377  // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
378  // same file.
379  char* architecture = cpu_info.ExtractField("CPU architecture");
380  if (architecture != NULL) {
381  char* end;
382  architecture_ = strtol(architecture, &end, 10);
383  if (end == architecture) {
384  architecture_ = 0;
385  }
386  delete[] architecture;
387 
388  // Unfortunately, it seems that certain ARMv6-based CPUs
389  // report an incorrect architecture number of 7!
390  //
391  // See http://code.google.com/p/android/issues/detail?id=10812
392  //
393  // We try to correct this by looking at the 'elf_format'
394  // field reported by the 'Processor' field, which is of the
395  // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
396  // an ARMv6-one. For example, the Raspberry Pi is one popular
397  // ARMv6 device that reports architecture 7.
398  if (architecture_ == 7) {
399  char* processor = cpu_info.ExtractField("Processor");
400  if (HasListItem(processor, "(v6l)")) {
401  architecture_ = 6;
402  }
403  delete[] processor;
404  }
405  }
406 
407  // Try to extract the list of CPU features from ELF hwcaps.
408  uint32_t hwcaps = ReadELFHWCaps();
409  if (hwcaps != 0) {
410  has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
411  has_neon_ = (hwcaps & HWCAP_NEON) != 0;
412  has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
413  has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
414  has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
415  (hwcaps & HWCAP_VFPD32) != 0));
416  } else {
417  // Try to fallback to "Features" CPUInfo field.
418  char* features = cpu_info.ExtractField("Features");
419  has_idiva_ = HasListItem(features, "idiva");
420  has_neon_ = HasListItem(features, "neon");
421  has_thumb2_ = HasListItem(features, "thumb2");
422  has_vfp_ = HasListItem(features, "vfp");
423  if (HasListItem(features, "vfpv3d16")) {
424  has_vfp3_ = true;
425  } else if (HasListItem(features, "vfpv3")) {
426  has_vfp3_ = true;
427  has_vfp3_d32_ = true;
428  }
429  delete[] features;
430  }
431 
432  // Some old kernels will report vfp not vfpv3. Here we make an attempt
433  // to detect vfpv3 by checking for vfp *and* neon, since neon is only
434  // available on architectures with vfpv3. Checking neon on its own is
435  // not enough as it is possible to have neon without vfp.
436  if (has_vfp_ && has_neon_) {
437  has_vfp3_ = true;
438  }
439 
440  // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
441  if (architecture_ < 7 && has_vfp3_) {
442  architecture_ = 7;
443  }
444 
445  // ARMv7 implies Thumb2.
446  if (architecture_ >= 7) {
447  has_thumb2_ = true;
448  }
449 
450  // The earliest architecture with Thumb2 is ARMv6T2.
451  if (has_thumb2_ && architecture_ < 6) {
452  architecture_ = 6;
453  }
454 
455  // We don't support any FPUs other than VFP.
456  has_fpu_ = has_vfp_;
457 
458 #elif V8_OS_QNX
459 
460  uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
461  if (cpu_flags & ARM_CPU_FLAG_V7) {
462  architecture_ = 7;
463  has_thumb2_ = true;
464  } else if (cpu_flags & ARM_CPU_FLAG_V6) {
465  architecture_ = 6;
466  // QNX doesn't say if Thumb2 is available.
467  // Assume false for the architectures older than ARMv7.
468  }
469  DCHECK(architecture_ >= 6);
470  has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
471  has_vfp_ = has_fpu_;
472  if (cpu_flags & ARM_CPU_FLAG_NEON) {
473  has_neon_ = true;
474  has_vfp3_ = has_vfp_;
475 #ifdef ARM_CPU_FLAG_VFP_D32
476  has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
477 #endif
478  }
479  has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;
480 
481 #endif // V8_OS_LINUX
482 
483 #elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
484 
485  // Simple detection of FPU at runtime for Linux.
486  // It is based on /proc/cpuinfo, which reveals hardware configuration
487  // to user-space applications. According to MIPS (early 2010), no similar
488  // facility is universally available on the MIPS architectures,
489  // so it's up to individual OSes to provide such.
490  CPUInfo cpu_info;
491  char* cpu_model = cpu_info.ExtractField("cpu model");
492  has_fpu_ = HasListItem(cpu_model, "FPU");
493  delete[] cpu_model;
494 #ifdef V8_HOST_ARCH_MIPS
495  is_fp64_mode_ = __detect_fp64_mode();
496  architecture_ = __detect_mips_arch_revision();
497 #endif
498 
499 #elif V8_HOST_ARCH_ARM64
500 
501  CPUInfo cpu_info;
502 
503  // Extract implementor from the "CPU implementer" field.
504  char* implementer = cpu_info.ExtractField("CPU implementer");
505  if (implementer != NULL) {
506  char* end ;
507  implementer_ = strtol(implementer, &end, 0);
508  if (end == implementer) {
509  implementer_ = 0;
510  }
511  delete[] implementer;
512  }
513 
514  // Extract part number from the "CPU part" field.
515  char* part = cpu_info.ExtractField("CPU part");
516  if (part != NULL) {
517  char* end ;
518  part_ = strtol(part, &end, 0);
519  if (end == part) {
520  part_ = 0;
521  }
522  delete[] part;
523  }
524 
525 #endif
526 }
527 
528 } } // namespace v8::base
#define FINAL
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be NULL
#define DCHECK(condition)
Definition: logging.h:205
const Register fp
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
#define V8_INLINE
Definition: v8config.h:306