blob: cd40d4f62150b2b48b01d6bc81f2e1165b677a82 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
29namespace v8 {
30namespace 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
37static 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.
96static 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
119int __detect_fp64_mode(void) {
120 double result = 0;
121 // Bit representation of (double)1 is 0x3FF0000000000000.
Paul Lind18a7ebb2014-12-17 22:29:32 -0800122 __asm__ volatile(
123 ".set push\n\t"
124 ".set noreorder\n\t"
125 ".set oddspreg\n\t"
126 "lui $t0, 0x3FF0\n\t"
127 "ldc1 $f0, %0\n\t"
128 "mtc1 $t0, $f1\n\t"
129 "sdc1 $f0, %0\n\t"
130 ".set pop\n\t"
131 : "+m"(result)
132 :
133 : "t0", "$f0", "$f1", "memory");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000134
135 return !(result == 1);
136}
137
138
139int __detect_mips_arch_revision(void) {
140 // TODO(dusmil): Do the specific syscall as soon as it is implemented in mips
Paul Lind18a7ebb2014-12-17 22:29:32 -0800141 // kernel.
142 uint32_t result = 0;
143 __asm__ volatile(
144 "move $v0, $zero\n\t"
145 // Encoding for "addi $v0, $v0, 1" on non-r6,
146 // which is encoding for "bovc $v0, %v0, 1" on r6.
147 // Use machine code directly to avoid compilation errors with different
148 // toolchains and maintain compatibility.
149 ".word 0x20420001\n\t"
150 "sw $v0, %0\n\t"
151 : "=m"(result)
152 :
153 : "v0", "memory");
154 // Result is 0 on r6 architectures, 1 on other architecture revisions.
155 // Fall-back to the least common denominator which is mips32 revision 1.
156 return result ? 1 : 6;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000157}
158#endif
159
160// Extract the information exposed by the kernel via /proc/cpuinfo.
161class CPUInfo FINAL {
162 public:
163 CPUInfo() : datalen_(0) {
164 // Get the size of the cpuinfo file by reading it until the end. This is
165 // required because files under /proc do not always return a valid size
166 // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
167 static const char PATHNAME[] = "/proc/cpuinfo";
168 FILE* fp = fopen(PATHNAME, "r");
169 if (fp != NULL) {
170 for (;;) {
171 char buffer[256];
172 size_t n = fread(buffer, 1, sizeof(buffer), fp);
173 if (n == 0) {
174 break;
175 }
176 datalen_ += n;
177 }
178 fclose(fp);
179 }
180
181 // Read the contents of the cpuinfo file.
182 data_ = new char[datalen_ + 1];
183 fp = fopen(PATHNAME, "r");
184 if (fp != NULL) {
185 for (size_t offset = 0; offset < datalen_; ) {
186 size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
187 if (n == 0) {
188 break;
189 }
190 offset += n;
191 }
192 fclose(fp);
193 }
194
195 // Zero-terminate the data.
196 data_[datalen_] = '\0';
197 }
198
199 ~CPUInfo() {
200 delete[] data_;
201 }
202
203 // Extract the content of a the first occurence of a given field in
204 // the content of the cpuinfo file and return it as a heap-allocated
205 // string that must be freed by the caller using delete[].
206 // Return NULL if not found.
207 char* ExtractField(const char* field) const {
208 DCHECK(field != NULL);
209
210 // Look for first field occurence, and ensure it starts the line.
211 size_t fieldlen = strlen(field);
212 char* p = data_;
213 for (;;) {
214 p = strstr(p, field);
215 if (p == NULL) {
216 return NULL;
217 }
218 if (p == data_ || p[-1] == '\n') {
219 break;
220 }
221 p += fieldlen;
222 }
223
224 // Skip to the first colon followed by a space.
225 p = strchr(p + fieldlen, ':');
226 if (p == NULL || !isspace(p[1])) {
227 return NULL;
228 }
229 p += 2;
230
231 // Find the end of the line.
232 char* q = strchr(p, '\n');
233 if (q == NULL) {
234 q = data_ + datalen_;
235 }
236
237 // Copy the line into a heap-allocated buffer.
238 size_t len = q - p;
239 char* result = new char[len + 1];
240 if (result != NULL) {
241 memcpy(result, p, len);
242 result[len] = '\0';
243 }
244 return result;
245 }
246
247 private:
248 char* data_;
249 size_t datalen_;
250};
251
252#if V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
253
254// Checks that a space-separated list of items contains one given 'item'.
255static bool HasListItem(const char* list, const char* item) {
256 ssize_t item_len = strlen(item);
257 const char* p = list;
258 if (p != NULL) {
259 while (*p != '\0') {
260 // Skip whitespace.
261 while (isspace(*p)) ++p;
262
263 // Find end of current list item.
264 const char* q = p;
265 while (*q != '\0' && !isspace(*q)) ++q;
266
267 if (item_len == q - p && memcmp(p, item, item_len) == 0) {
268 return true;
269 }
270
271 // Skip to next item.
272 p = q;
273 }
274 }
275 return false;
276}
277
278#endif // V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
279
280#endif // V8_OS_LINUX
281
282#endif // V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
283
284CPU::CPU() : stepping_(0),
285 model_(0),
286 ext_model_(0),
287 family_(0),
288 ext_family_(0),
289 type_(0),
290 implementer_(0),
291 architecture_(0),
292 part_(0),
293 has_fpu_(false),
294 has_cmov_(false),
295 has_sahf_(false),
296 has_mmx_(false),
297 has_sse_(false),
298 has_sse2_(false),
299 has_sse3_(false),
300 has_ssse3_(false),
301 has_sse41_(false),
302 has_sse42_(false),
303 has_idiva_(false),
304 has_neon_(false),
305 has_thumb2_(false),
306 has_vfp_(false),
307 has_vfp3_(false),
308 has_vfp3_d32_(false),
309 is_fp64_mode_(false) {
310 memcpy(vendor_, "Unknown", 8);
311#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
312 int cpu_info[4];
313
314 // __cpuid with an InfoType argument of 0 returns the number of
315 // valid Ids in CPUInfo[0] and the CPU identification string in
316 // the other three array elements. The CPU identification string is
317 // not in linear order. The code below arranges the information
318 // in a human readable form. The human readable order is CPUInfo[1] |
319 // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
320 // before using memcpy to copy these three array elements to cpu_string.
321 __cpuid(cpu_info, 0);
322 unsigned num_ids = cpu_info[0];
323 std::swap(cpu_info[2], cpu_info[3]);
324 memcpy(vendor_, cpu_info + 1, 12);
325 vendor_[12] = '\0';
326
327 // Interpret CPU feature information.
328 if (num_ids > 0) {
329 __cpuid(cpu_info, 1);
330 stepping_ = cpu_info[0] & 0xf;
331 model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
332 family_ = (cpu_info[0] >> 8) & 0xf;
333 type_ = (cpu_info[0] >> 12) & 0x3;
334 ext_model_ = (cpu_info[0] >> 16) & 0xf;
335 ext_family_ = (cpu_info[0] >> 20) & 0xff;
336 has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
337 has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
338 has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
339 has_sse_ = (cpu_info[3] & 0x02000000) != 0;
340 has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
341 has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
342 has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
343 has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
344 has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
345 }
346
347#if V8_HOST_ARCH_IA32
348 // SAHF is always available in compat/legacy mode,
349 has_sahf_ = true;
350#else
351 // Query extended IDs.
352 __cpuid(cpu_info, 0x80000000);
353 unsigned num_ext_ids = cpu_info[0];
354
355 // Interpret extended CPU feature information.
356 if (num_ext_ids > 0x80000000) {
357 __cpuid(cpu_info, 0x80000001);
358 // SAHF must be probed in long mode.
359 has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
360 }
361#endif
362
363#elif V8_HOST_ARCH_ARM
364
365#if V8_OS_LINUX
366
367 CPUInfo cpu_info;
368
369 // Extract implementor from the "CPU implementer" field.
370 char* implementer = cpu_info.ExtractField("CPU implementer");
371 if (implementer != NULL) {
372 char* end ;
373 implementer_ = strtol(implementer, &end, 0);
374 if (end == implementer) {
375 implementer_ = 0;
376 }
377 delete[] implementer;
378 }
379
380 // Extract part number from the "CPU part" field.
381 char* part = cpu_info.ExtractField("CPU part");
382 if (part != NULL) {
383 char* end ;
384 part_ = strtol(part, &end, 0);
385 if (end == part) {
386 part_ = 0;
387 }
388 delete[] part;
389 }
390
391 // Extract architecture from the "CPU Architecture" field.
392 // The list is well-known, unlike the the output of
393 // the 'Processor' field which can vary greatly.
394 // See the definition of the 'proc_arch' array in
395 // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
396 // same file.
397 char* architecture = cpu_info.ExtractField("CPU architecture");
398 if (architecture != NULL) {
399 char* end;
400 architecture_ = strtol(architecture, &end, 10);
401 if (end == architecture) {
402 architecture_ = 0;
403 }
404 delete[] architecture;
405
406 // Unfortunately, it seems that certain ARMv6-based CPUs
407 // report an incorrect architecture number of 7!
408 //
409 // See http://code.google.com/p/android/issues/detail?id=10812
410 //
411 // We try to correct this by looking at the 'elf_format'
412 // field reported by the 'Processor' field, which is of the
413 // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
414 // an ARMv6-one. For example, the Raspberry Pi is one popular
415 // ARMv6 device that reports architecture 7.
416 if (architecture_ == 7) {
417 char* processor = cpu_info.ExtractField("Processor");
418 if (HasListItem(processor, "(v6l)")) {
419 architecture_ = 6;
420 }
421 delete[] processor;
422 }
423 }
424
425 // Try to extract the list of CPU features from ELF hwcaps.
426 uint32_t hwcaps = ReadELFHWCaps();
427 if (hwcaps != 0) {
428 has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
429 has_neon_ = (hwcaps & HWCAP_NEON) != 0;
430 has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
431 has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
432 has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
433 (hwcaps & HWCAP_VFPD32) != 0));
434 } else {
435 // Try to fallback to "Features" CPUInfo field.
436 char* features = cpu_info.ExtractField("Features");
437 has_idiva_ = HasListItem(features, "idiva");
438 has_neon_ = HasListItem(features, "neon");
439 has_thumb2_ = HasListItem(features, "thumb2");
440 has_vfp_ = HasListItem(features, "vfp");
441 if (HasListItem(features, "vfpv3d16")) {
442 has_vfp3_ = true;
443 } else if (HasListItem(features, "vfpv3")) {
444 has_vfp3_ = true;
445 has_vfp3_d32_ = true;
446 }
447 delete[] features;
448 }
449
450 // Some old kernels will report vfp not vfpv3. Here we make an attempt
451 // to detect vfpv3 by checking for vfp *and* neon, since neon is only
452 // available on architectures with vfpv3. Checking neon on its own is
453 // not enough as it is possible to have neon without vfp.
454 if (has_vfp_ && has_neon_) {
455 has_vfp3_ = true;
456 }
457
458 // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
459 if (architecture_ < 7 && has_vfp3_) {
460 architecture_ = 7;
461 }
462
463 // ARMv7 implies Thumb2.
464 if (architecture_ >= 7) {
465 has_thumb2_ = true;
466 }
467
468 // The earliest architecture with Thumb2 is ARMv6T2.
469 if (has_thumb2_ && architecture_ < 6) {
470 architecture_ = 6;
471 }
472
473 // We don't support any FPUs other than VFP.
474 has_fpu_ = has_vfp_;
475
476#elif V8_OS_QNX
477
478 uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
479 if (cpu_flags & ARM_CPU_FLAG_V7) {
480 architecture_ = 7;
481 has_thumb2_ = true;
482 } else if (cpu_flags & ARM_CPU_FLAG_V6) {
483 architecture_ = 6;
484 // QNX doesn't say if Thumb2 is available.
485 // Assume false for the architectures older than ARMv7.
486 }
487 DCHECK(architecture_ >= 6);
488 has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
489 has_vfp_ = has_fpu_;
490 if (cpu_flags & ARM_CPU_FLAG_NEON) {
491 has_neon_ = true;
492 has_vfp3_ = has_vfp_;
493#ifdef ARM_CPU_FLAG_VFP_D32
494 has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
495#endif
496 }
497 has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;
498
499#endif // V8_OS_LINUX
500
501#elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
502
503 // Simple detection of FPU at runtime for Linux.
504 // It is based on /proc/cpuinfo, which reveals hardware configuration
505 // to user-space applications. According to MIPS (early 2010), no similar
506 // facility is universally available on the MIPS architectures,
507 // so it's up to individual OSes to provide such.
508 CPUInfo cpu_info;
509 char* cpu_model = cpu_info.ExtractField("cpu model");
510 has_fpu_ = HasListItem(cpu_model, "FPU");
511 delete[] cpu_model;
512#ifdef V8_HOST_ARCH_MIPS
513 is_fp64_mode_ = __detect_fp64_mode();
514 architecture_ = __detect_mips_arch_revision();
515#endif
516
517#elif V8_HOST_ARCH_ARM64
518
519 CPUInfo cpu_info;
520
521 // Extract implementor from the "CPU implementer" field.
522 char* implementer = cpu_info.ExtractField("CPU implementer");
523 if (implementer != NULL) {
524 char* end ;
525 implementer_ = strtol(implementer, &end, 0);
526 if (end == implementer) {
527 implementer_ = 0;
528 }
529 delete[] implementer;
530 }
531
532 // Extract part number from the "CPU part" field.
533 char* part = cpu_info.ExtractField("CPU part");
534 if (part != NULL) {
535 char* end ;
536 part_ = strtol(part, &end, 0);
537 if (end == part) {
538 part_ = 0;
539 }
540 delete[] part;
541 }
542
543#endif
544}
545
546} } // namespace v8::base