• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 defined(V8_OS_STARBOARD)
8 #include "starboard/cpu_features.h"
9 #endif
10 
11 #if V8_LIBC_MSVCRT
12 #include <intrin.h>  // __cpuid()
13 #endif
14 #if V8_OS_LINUX
15 #include <linux/auxvec.h>  // AT_HWCAP
16 #endif
17 #if V8_GLIBC_PREREQ(2, 16)
18 #include <sys/auxv.h>  // getauxval()
19 #endif
20 #if V8_OS_QNX
21 #include <sys/syspage.h>  // cpuinfo
22 #endif
23 #if V8_OS_LINUX && (V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64)
24 #include <elf.h>
25 #endif
26 #if V8_OS_AIX
27 #include <sys/systemcfg.h>  // _system_configuration
28 #ifndef POWER_8
29 #define POWER_8 0x10000
30 #endif
31 #ifndef POWER_9
32 #define POWER_9 0x20000
33 #endif
34 #ifndef POWER_10
35 #define POWER_10 0x40000
36 #endif
37 #endif
38 #if V8_OS_POSIX
39 #include <unistd.h>  // sysconf()
40 #endif
41 
42 #include <ctype.h>
43 #include <limits.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <algorithm>
49 
50 #include "src/base/logging.h"
51 #include "src/base/platform/wrappers.h"
52 #if V8_OS_WIN
53 #include <windows.h>
54 
55 #include "src/base/win32-headers.h"
56 #endif
57 
58 namespace v8 {
59 namespace base {
60 
61 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
62 
63 // Define __cpuid() for non-MSVC libraries.
64 #if !V8_LIBC_MSVCRT
65 
__cpuid(int cpu_info[4],int info_type)66 static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {
67 // Clear ecx to align with __cpuid() of MSVC:
68 // https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
69 #if defined(__i386__) && defined(__pic__)
70   // Make sure to preserve ebx, which contains the pointer
71   // to the GOT in case we're generating PIC.
72   __asm__ volatile(
73       "mov %%ebx, %%edi\n\t"
74       "cpuid\n\t"
75       "xchg %%edi, %%ebx\n\t"
76       : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
77         "=d"(cpu_info[3])
78       : "a"(info_type), "c"(0));
79 #else
80   __asm__ volatile("cpuid \n\t"
81                    : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
82                      "=d"(cpu_info[3])
83                    : "a"(info_type), "c"(0));
84 #endif  // defined(__i386__) && defined(__pic__)
85 }
86 
87 #endif  // !V8_LIBC_MSVCRT
88 
89 #elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 || V8_HOST_ARCH_MIPS || \
90     V8_HOST_ARCH_MIPS64 || V8_HOST_ARCH_RISCV64
91 
92 #if V8_OS_LINUX
93 
94 #if V8_HOST_ARCH_ARM
95 
96 // See <uapi/asm/hwcap.h> kernel header.
97 /*
98  * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
99  */
100 #define HWCAP_SWP (1 << 0)
101 #define HWCAP_HALF  (1 << 1)
102 #define HWCAP_THUMB (1 << 2)
103 #define HWCAP_26BIT (1 << 3)  /* Play it safe */
104 #define HWCAP_FAST_MULT (1 << 4)
105 #define HWCAP_FPA (1 << 5)
106 #define HWCAP_VFP (1 << 6)
107 #define HWCAP_EDSP  (1 << 7)
108 #define HWCAP_JAVA  (1 << 8)
109 #define HWCAP_IWMMXT  (1 << 9)
110 #define HWCAP_CRUNCH  (1 << 10)
111 #define HWCAP_THUMBEE (1 << 11)
112 #define HWCAP_NEON  (1 << 12)
113 #define HWCAP_VFPv3 (1 << 13)
114 #define HWCAP_VFPv3D16  (1 << 14) /* also set for VFPv4-D16 */
115 #define HWCAP_TLS (1 << 15)
116 #define HWCAP_VFPv4 (1 << 16)
117 #define HWCAP_IDIVA (1 << 17)
118 #define HWCAP_IDIVT (1 << 18)
119 #define HWCAP_VFPD32  (1 << 19) /* set if VFP has 32 regs (not 16) */
120 #define HWCAP_IDIV  (HWCAP_IDIVA | HWCAP_IDIVT)
121 #define HWCAP_LPAE  (1 << 20)
122 
123 #endif  // V8_HOST_ARCH_ARM
124 
125 #if V8_HOST_ARCH_ARM64
126 
127 // See <uapi/asm/hwcap.h> kernel header.
128 /*
129  * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
130  */
131 #define HWCAP_FP (1 << 0)
132 #define HWCAP_ASIMD (1 << 1)
133 #define HWCAP_EVTSTRM (1 << 2)
134 #define HWCAP_AES (1 << 3)
135 #define HWCAP_PMULL (1 << 4)
136 #define HWCAP_SHA1 (1 << 5)
137 #define HWCAP_SHA2 (1 << 6)
138 #define HWCAP_CRC32 (1 << 7)
139 #define HWCAP_ATOMICS (1 << 8)
140 #define HWCAP_FPHP (1 << 9)
141 #define HWCAP_ASIMDHP (1 << 10)
142 #define HWCAP_CPUID (1 << 11)
143 #define HWCAP_ASIMDRDM (1 << 12)
144 #define HWCAP_JSCVT (1 << 13)
145 #define HWCAP_FCMA (1 << 14)
146 #define HWCAP_LRCPC (1 << 15)
147 #define HWCAP_DCPOP (1 << 16)
148 #define HWCAP_SHA3 (1 << 17)
149 #define HWCAP_SM3 (1 << 18)
150 #define HWCAP_SM4 (1 << 19)
151 #define HWCAP_ASIMDDP (1 << 20)
152 #define HWCAP_SHA512 (1 << 21)
153 #define HWCAP_SVE (1 << 22)
154 #define HWCAP_ASIMDFHM (1 << 23)
155 #define HWCAP_DIT (1 << 24)
156 #define HWCAP_USCAT (1 << 25)
157 #define HWCAP_ILRCPC (1 << 26)
158 #define HWCAP_FLAGM (1 << 27)
159 #define HWCAP_SSBS (1 << 28)
160 #define HWCAP_SB (1 << 29)
161 #define HWCAP_PACA (1 << 30)
162 #define HWCAP_PACG (1UL << 31)
163 
164 #endif  // V8_HOST_ARCH_ARM64
165 
166 #if V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64
167 
168 static uint32_t ReadELFHWCaps() {
169   uint32_t result = 0;
170 #if V8_GLIBC_PREREQ(2, 16)
171   result = static_cast<uint32_t>(getauxval(AT_HWCAP));
172 #else
173   // Read the ELF HWCAP flags by parsing /proc/self/auxv.
174   FILE* fp = base::Fopen("/proc/self/auxv", "r");
175   if (fp != nullptr) {
176     struct {
177       uint32_t tag;
178       uint32_t value;
179     } entry;
180     for (;;) {
181       size_t n = fread(&entry, sizeof(entry), 1, fp);
182       if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
183         break;
184       }
185       if (entry.tag == AT_HWCAP) {
186         result = entry.value;
187         break;
188       }
189     }
190     base::Fclose(fp);
191   }
192 #endif
193   return result;
194 }
195 
196 #endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64
197 
198 #if V8_HOST_ARCH_MIPS
199 int __detect_fp64_mode(void) {
200   double result = 0;
201   // Bit representation of (double)1 is 0x3FF0000000000000.
202   __asm__ volatile(
203       ".set push\n\t"
204       ".set noreorder\n\t"
205       ".set oddspreg\n\t"
206       "lui $t0, 0x3FF0\n\t"
207       "ldc1 $f0, %0\n\t"
208       "mtc1 $t0, $f1\n\t"
209       "sdc1 $f0, %0\n\t"
210       ".set pop\n\t"
211       : "+m"(result)
212       :
213       : "t0", "$f0", "$f1", "memory");
214 
215   return !(result == 1);
216 }
217 
218 
219 int __detect_mips_arch_revision(void) {
220   // TODO(dusmil): Do the specific syscall as soon as it is implemented in mips
221   // kernel.
222   uint32_t result = 0;
223   __asm__ volatile(
224       "move $v0, $zero\n\t"
225       // Encoding for "addi $v0, $v0, 1" on non-r6,
226       // which is encoding for "bovc $v0, %v0, 1" on r6.
227       // Use machine code directly to avoid compilation errors with different
228       // toolchains and maintain compatibility.
229       ".word 0x20420001\n\t"
230       "sw $v0, %0\n\t"
231       : "=m"(result)
232       :
233       : "v0", "memory");
234   // Result is 0 on r6 architectures, 1 on other architecture revisions.
235   // Fall-back to the least common denominator which is mips32 revision 1.
236   return result ? 1 : 6;
237 }
238 #endif  // V8_HOST_ARCH_MIPS
239 
240 // Extract the information exposed by the kernel via /proc/cpuinfo.
241 class CPUInfo final {
242  public:
243   CPUInfo() : datalen_(0) {
244     // Get the size of the cpuinfo file by reading it until the end. This is
245     // required because files under /proc do not always return a valid size
246     // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
247     static const char PATHNAME[] = "/proc/cpuinfo";
248     FILE* fp = base::Fopen(PATHNAME, "r");
249     if (fp != nullptr) {
250       for (;;) {
251         char buffer[256];
252         size_t n = fread(buffer, 1, sizeof(buffer), fp);
253         if (n == 0) {
254           break;
255         }
256         datalen_ += n;
257       }
258       base::Fclose(fp);
259     }
260 
261     // Read the contents of the cpuinfo file.
262     data_ = new char[datalen_ + 1];
263     fp = base::Fopen(PATHNAME, "r");
264     if (fp != nullptr) {
265       for (size_t offset = 0; offset < datalen_; ) {
266         size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
267         if (n == 0) {
268           break;
269         }
270         offset += n;
271       }
272       base::Fclose(fp);
273     }
274 
275     // Zero-terminate the data.
276     data_[datalen_] = '\0';
277   }
278 
279   ~CPUInfo() {
280     delete[] data_;
281   }
282 
283   // Extract the content of a the first occurrence of a given field in
284   // the content of the cpuinfo file and return it as a heap-allocated
285   // string that must be freed by the caller using delete[].
286   // Return nullptr if not found.
287   char* ExtractField(const char* field) const {
288     DCHECK_NOT_NULL(field);
289 
290     // Look for first field occurrence, and ensure it starts the line.
291     size_t fieldlen = strlen(field);
292     char* p = data_;
293     for (;;) {
294       p = strstr(p, field);
295       if (p == nullptr) {
296         return nullptr;
297       }
298       if (p == data_ || p[-1] == '\n') {
299         break;
300       }
301       p += fieldlen;
302     }
303 
304     // Skip to the first colon followed by a space.
305     p = strchr(p + fieldlen, ':');
306     if (p == nullptr || !isspace(p[1])) {
307       return nullptr;
308     }
309     p += 2;
310 
311     // Find the end of the line.
312     char* q = strchr(p, '\n');
313     if (q == nullptr) {
314       q = data_ + datalen_;
315     }
316 
317     // Copy the line into a heap-allocated buffer.
318     size_t len = q - p;
319     char* result = new char[len + 1];
320     if (result != nullptr) {
321       memcpy(result, p, len);
322       result[len] = '\0';
323     }
324     return result;
325   }
326 
327  private:
328   char* data_;
329   size_t datalen_;
330 };
331 
332 // Checks that a space-separated list of items contains one given 'item'.
333 static bool HasListItem(const char* list, const char* item) {
334   ssize_t item_len = strlen(item);
335   const char* p = list;
336   if (p != nullptr) {
337     while (*p != '\0') {
338       // Skip whitespace.
339       while (isspace(*p)) ++p;
340 
341       // Find end of current list item.
342       const char* q = p;
343       while (*q != '\0' && !isspace(*q)) ++q;
344 
345       if (item_len == q - p && memcmp(p, item, item_len) == 0) {
346         return true;
347       }
348 
349       // Skip to next item.
350       p = q;
351     }
352   }
353   return false;
354 }
355 
356 #endif  // V8_OS_LINUX
357 
358 #endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 ||
359         // V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64 || V8_HOST_ARCH_RISCV64
360 
361 #if defined(V8_OS_STARBOARD)
362 
StarboardDetectCPU()363 bool CPU::StarboardDetectCPU() {
364   SbCPUFeatures features;
365   if (!SbCPUFeaturesGet(&features)) {
366     return false;
367   }
368   architecture_ = features.arm.architecture_generation;
369   switch (features.architecture) {
370     case kSbCPUFeaturesArchitectureArm:
371     case kSbCPUFeaturesArchitectureArm64:
372       has_neon_ = features.arm.has_neon;
373       has_thumb2_ = features.arm.has_thumb2;
374       has_vfp_ = features.arm.has_vfp;
375       has_vfp3_ = features.arm.has_vfp3;
376       has_vfp3_d32_ = features.arm.has_vfp3_d32;
377       has_idiva_ = features.arm.has_idiva;
378       break;
379     case kSbCPUFeaturesArchitectureX86:
380     case kSbCPUFeaturesArchitectureX86_64:
381       // Following flags are mandatory for V8
382       has_cmov_ = features.x86.has_cmov;
383       has_sse2_ = features.x86.has_sse2;
384       // These flags are optional
385       has_sse3_ = features.x86.has_sse3;
386       has_ssse3_ = features.x86.has_ssse3;
387       has_sse41_ = features.x86.has_sse41;
388       has_sahf_ = features.x86.has_sahf;
389       has_avx_ = features.x86.has_avx;
390       has_avx2_ = features.x86.has_avx2;
391       has_fma3_ = features.x86.has_fma3;
392       has_bmi1_ = features.x86.has_bmi1;
393       has_bmi2_ = features.x86.has_bmi2;
394       has_lzcnt_ = features.x86.has_lzcnt;
395       has_popcnt_ = features.x86.has_popcnt;
396       break;
397     default:
398       return false;
399   }
400 
401   return true;
402 }
403 
404 #endif
405 
CPU()406 CPU::CPU()
407     : stepping_(0),
408       model_(0),
409       ext_model_(0),
410       family_(0),
411       ext_family_(0),
412       type_(0),
413       implementer_(0),
414       architecture_(0),
415       variant_(-1),
416       part_(0),
417       icache_line_size_(kUnknownCacheLineSize),
418       dcache_line_size_(kUnknownCacheLineSize),
419       num_virtual_address_bits_(kUnknownNumVirtualAddressBits),
420       has_fpu_(false),
421       has_cmov_(false),
422       has_sahf_(false),
423       has_mmx_(false),
424       has_sse_(false),
425       has_sse2_(false),
426       has_sse3_(false),
427       has_ssse3_(false),
428       has_sse41_(false),
429       has_sse42_(false),
430       is_atom_(false),
431       has_osxsave_(false),
432       has_avx_(false),
433       has_avx2_(false),
434       has_fma3_(false),
435       has_bmi1_(false),
436       has_bmi2_(false),
437       has_lzcnt_(false),
438       has_popcnt_(false),
439       has_idiva_(false),
440       has_neon_(false),
441       has_thumb2_(false),
442       has_vfp_(false),
443       has_vfp3_(false),
444       has_vfp3_d32_(false),
445       has_jscvt_(false),
446       is_fp64_mode_(false),
447       has_non_stop_time_stamp_counter_(false),
448       is_running_in_vm_(false),
449       has_msa_(false),
450       has_rvv_(false) {
451   memcpy(vendor_, "Unknown", 8);
452 
453 #if defined(V8_OS_STARBOARD)
454   if (StarboardDetectCPU()) {
455     return;
456   }
457 #endif
458 
459 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
460   int cpu_info[4];
461 
462   // __cpuid with an InfoType argument of 0 returns the number of
463   // valid Ids in CPUInfo[0] and the CPU identification string in
464   // the other three array elements. The CPU identification string is
465   // not in linear order. The code below arranges the information
466   // in a human readable form. The human readable order is CPUInfo[1] |
467   // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
468   // before using memcpy to copy these three array elements to cpu_string.
469   __cpuid(cpu_info, 0);
470   unsigned num_ids = cpu_info[0];
471   std::swap(cpu_info[2], cpu_info[3]);
472   memcpy(vendor_, cpu_info + 1, 12);
473   vendor_[12] = '\0';
474 
475   // Interpret CPU feature information.
476   if (num_ids > 0) {
477     __cpuid(cpu_info, 1);
478 
479     int cpu_info7[4] = {0};
480     if (num_ids >= 7) {
481       __cpuid(cpu_info7, 7);
482     }
483 
484     stepping_ = cpu_info[0] & 0xF;
485     model_ = ((cpu_info[0] >> 4) & 0xF) + ((cpu_info[0] >> 12) & 0xF0);
486     family_ = (cpu_info[0] >> 8) & 0xF;
487     type_ = (cpu_info[0] >> 12) & 0x3;
488     ext_model_ = (cpu_info[0] >> 16) & 0xF;
489     ext_family_ = (cpu_info[0] >> 20) & 0xFF;
490     has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
491     has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
492     has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
493     has_sse_ = (cpu_info[3] & 0x02000000) != 0;
494     has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
495     has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
496     has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
497     has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
498     has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
499     has_popcnt_ = (cpu_info[2] & 0x00800000) != 0;
500     has_osxsave_ = (cpu_info[2] & 0x08000000) != 0;
501     has_avx_ = (cpu_info[2] & 0x10000000) != 0;
502     has_avx2_ = (cpu_info7[1] & 0x00000020) != 0;
503     has_fma3_ = (cpu_info[2] & 0x00001000) != 0;
504     // CET shadow stack feature flag. See
505     // https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features
506     has_cetss_ = (cpu_info7[2] & 0x00000080) != 0;
507     // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1."
508     // See https://lwn.net/Articles/301888/
509     // This is checking for any hypervisor. Hypervisors may choose not to
510     // announce themselves. Hypervisors trap CPUID and sometimes return
511     // different results to underlying hardware.
512     is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
513 
514     if (family_ == 0x6) {
515       switch (model_) {
516         case 0x1C:  // SLT
517         case 0x26:
518         case 0x36:
519         case 0x27:
520         case 0x35:
521         case 0x37:  // SLM
522         case 0x4A:
523         case 0x4D:
524         case 0x4C:  // AMT
525         case 0x6E:
526           is_atom_ = true;
527       }
528     }
529   }
530 
531   // There are separate feature flags for VEX-encoded GPR instructions.
532   if (num_ids >= 7) {
533     __cpuid(cpu_info, 7);
534     has_bmi1_ = (cpu_info[1] & 0x00000008) != 0;
535     has_bmi2_ = (cpu_info[1] & 0x00000100) != 0;
536   }
537 
538   // Query extended IDs.
539   __cpuid(cpu_info, 0x80000000);
540   unsigned num_ext_ids = cpu_info[0];
541 
542   // Interpret extended CPU feature information.
543   if (num_ext_ids > 0x80000000) {
544     __cpuid(cpu_info, 0x80000001);
545     has_lzcnt_ = (cpu_info[2] & 0x00000020) != 0;
546     // SAHF must be probed in long mode.
547     has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
548   }
549 
550   // Check if CPU has non stoppable time stamp counter.
551   const unsigned parameter_containing_non_stop_time_stamp_counter = 0x80000007;
552   if (num_ext_ids >= parameter_containing_non_stop_time_stamp_counter) {
553     __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter);
554     has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
555   }
556 
557   const unsigned virtual_physical_address_bits = 0x80000008;
558   if (num_ext_ids >= virtual_physical_address_bits) {
559     __cpuid(cpu_info, virtual_physical_address_bits);
560     num_virtual_address_bits_ = (cpu_info[0] >> 8) & 0xff;
561   }
562 
563   // This logic is replicated from cpu.cc present in chromium.src
564   if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
565     int cpu_info_hv[4] = {};
566     __cpuid(cpu_info_hv, 0x40000000);
567     if (cpu_info_hv[1] == 0x7263694D &&  // Micr
568         cpu_info_hv[2] == 0x666F736F &&  // osof
569         cpu_info_hv[3] == 0x76482074) {  // t Hv
570       // If CPUID says we have a variant TSC and a hypervisor has identified
571       // itself and the hypervisor says it is Microsoft Hyper-V, then treat
572       // TSC as invariant.
573       //
574       // Microsoft Hyper-V hypervisor reports variant TSC as there are some
575       // scenarios (eg. VM live migration) where the TSC is variant, but for
576       // our purposes we can treat it as invariant.
577       has_non_stop_time_stamp_counter_ = true;
578     }
579   }
580 #elif V8_HOST_ARCH_ARM
581 
582 #if V8_OS_LINUX
583 
584   CPUInfo cpu_info;
585 
586   // Extract implementor from the "CPU implementer" field.
587   char* implementer = cpu_info.ExtractField("CPU implementer");
588   if (implementer != nullptr) {
589     char* end;
590     implementer_ = strtol(implementer, &end, 0);
591     if (end == implementer) {
592       implementer_ = 0;
593     }
594     delete[] implementer;
595   }
596 
597   char* variant = cpu_info.ExtractField("CPU variant");
598   if (variant != nullptr) {
599     char* end;
600     variant_ = strtol(variant, &end, 0);
601     if (end == variant) {
602       variant_ = -1;
603     }
604     delete[] variant;
605   }
606 
607   // Extract part number from the "CPU part" field.
608   char* part = cpu_info.ExtractField("CPU part");
609   if (part != nullptr) {
610     char* end;
611     part_ = strtol(part, &end, 0);
612     if (end == part) {
613       part_ = 0;
614     }
615     delete[] part;
616   }
617 
618   // Extract architecture from the "CPU Architecture" field.
619   // The list is well-known, unlike the the output of
620   // the 'Processor' field which can vary greatly.
621   // See the definition of the 'proc_arch' array in
622   // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
623   // same file.
624   char* architecture = cpu_info.ExtractField("CPU architecture");
625   if (architecture != nullptr) {
626     char* end;
627     architecture_ = strtol(architecture, &end, 10);
628     if (end == architecture) {
629       // Kernels older than 3.18 report "CPU architecture: AArch64" on ARMv8.
630       if (strcmp(architecture, "AArch64") == 0) {
631         architecture_ = 8;
632       } else {
633         architecture_ = 0;
634       }
635     }
636     delete[] architecture;
637 
638     // Unfortunately, it seems that certain ARMv6-based CPUs
639     // report an incorrect architecture number of 7!
640     //
641     // See http://code.google.com/p/android/issues/detail?id=10812
642     //
643     // We try to correct this by looking at the 'elf_platform'
644     // field reported by the 'Processor' field, which is of the
645     // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
646     // an ARMv6-one. For example, the Raspberry Pi is one popular
647     // ARMv6 device that reports architecture 7.
648     if (architecture_ == 7) {
649       char* processor = cpu_info.ExtractField("Processor");
650       if (HasListItem(processor, "(v6l)")) {
651         architecture_ = 6;
652       }
653       delete[] processor;
654     }
655 
656     // elf_platform moved to the model name field in Linux v3.8.
657     if (architecture_ == 7) {
658       char* processor = cpu_info.ExtractField("model name");
659       if (HasListItem(processor, "(v6l)")) {
660         architecture_ = 6;
661       }
662       delete[] processor;
663     }
664   }
665 
666   // Try to extract the list of CPU features from ELF hwcaps.
667   uint32_t hwcaps = ReadELFHWCaps();
668   if (hwcaps != 0) {
669     has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
670     has_neon_ = (hwcaps & HWCAP_NEON) != 0;
671     has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
672     has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
673     has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
674                                    (hwcaps & HWCAP_VFPD32) != 0));
675   } else {
676     // Try to fallback to "Features" CPUInfo field.
677     char* features = cpu_info.ExtractField("Features");
678     has_idiva_ = HasListItem(features, "idiva");
679     has_neon_ = HasListItem(features, "neon");
680     has_thumb2_ = HasListItem(features, "thumb2");
681     has_vfp_ = HasListItem(features, "vfp");
682     if (HasListItem(features, "vfpv3d16")) {
683       has_vfp3_ = true;
684     } else if (HasListItem(features, "vfpv3")) {
685       has_vfp3_ = true;
686       has_vfp3_d32_ = true;
687     }
688     delete[] features;
689   }
690 
691   // Some old kernels will report vfp not vfpv3. Here we make an attempt
692   // to detect vfpv3 by checking for vfp *and* neon, since neon is only
693   // available on architectures with vfpv3. Checking neon on its own is
694   // not enough as it is possible to have neon without vfp.
695   if (has_vfp_ && has_neon_) {
696     has_vfp3_ = true;
697   }
698 
699   // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
700   if (architecture_ < 7 && has_vfp3_) {
701     architecture_ = 7;
702   }
703 
704   // ARMv7 implies Thumb2.
705   if (architecture_ >= 7) {
706     has_thumb2_ = true;
707   }
708 
709   // The earliest architecture with Thumb2 is ARMv6T2.
710   if (has_thumb2_ && architecture_ < 6) {
711     architecture_ = 6;
712   }
713 
714   // We don't support any FPUs other than VFP.
715   has_fpu_ = has_vfp_;
716 
717 #elif V8_OS_QNX
718 
719   uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
720   if (cpu_flags & ARM_CPU_FLAG_V7) {
721     architecture_ = 7;
722     has_thumb2_ = true;
723   } else if (cpu_flags & ARM_CPU_FLAG_V6) {
724     architecture_ = 6;
725     // QNX doesn't say if Thumb2 is available.
726     // Assume false for the architectures older than ARMv7.
727   }
728   DCHECK_GE(architecture_, 6);
729   has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
730   has_vfp_ = has_fpu_;
731   if (cpu_flags & ARM_CPU_FLAG_NEON) {
732     has_neon_ = true;
733     has_vfp3_ = has_vfp_;
734 #ifdef ARM_CPU_FLAG_VFP_D32
735     has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
736 #endif
737   }
738   has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;
739 
740 #endif  // V8_OS_LINUX
741 
742 #elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
743 
744   // Simple detection of FPU at runtime for Linux.
745   // It is based on /proc/cpuinfo, which reveals hardware configuration
746   // to user-space applications.  According to MIPS (early 2010), no similar
747   // facility is universally available on the MIPS architectures,
748   // so it's up to individual OSes to provide such.
749   CPUInfo cpu_info;
750   char* cpu_model = cpu_info.ExtractField("cpu model");
751   has_fpu_ = HasListItem(cpu_model, "FPU");
752   char* ASEs = cpu_info.ExtractField("ASEs implemented");
753   has_msa_ = HasListItem(ASEs, "msa");
754   delete[] cpu_model;
755   delete[] ASEs;
756 #ifdef V8_HOST_ARCH_MIPS
757   is_fp64_mode_ = __detect_fp64_mode();
758   architecture_ = __detect_mips_arch_revision();
759 #endif
760 
761 #elif V8_HOST_ARCH_ARM64
762 #ifdef V8_OS_WIN
763   // Windows makes high-resolution thread timing information available in
764   // user-space.
765   has_non_stop_time_stamp_counter_ = true;
766 
767   // Defined in winnt.h, but only in 10.0.20348.0 version of the Windows SDK.
768   // Copy the value here to support older versions as well.
769 #if !defined(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)
770   constexpr int PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44;
771 #endif
772 
773   has_jscvt_ =
774       IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE);
775 
776 #elif V8_OS_LINUX
777   // Try to extract the list of CPU features from ELF hwcaps.
778   uint32_t hwcaps = ReadELFHWCaps();
779   if (hwcaps != 0) {
780     has_jscvt_ = (hwcaps & HWCAP_JSCVT) != 0;
781   } else {
782     // Try to fallback to "Features" CPUInfo field
783     CPUInfo cpu_info;
784     char* features = cpu_info.ExtractField("Features");
785     has_jscvt_ = HasListItem(features, "jscvt");
786     delete[] features;
787   }
788 #elif V8_OS_DARWIN
789   // ARM64 Macs always have JSCVT.
790   has_jscvt_ = true;
791 #endif  // V8_OS_WIN
792 
793 #elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64
794 
795 #ifndef USE_SIMULATOR
796 #if V8_OS_LINUX
797   // Read processor info from /proc/self/auxv.
798   char* auxv_cpu_type = nullptr;
799   FILE* fp = base::Fopen("/proc/self/auxv", "r");
800   if (fp != nullptr) {
801 #if V8_TARGET_ARCH_PPC64
802     Elf64_auxv_t entry;
803 #else
804     Elf32_auxv_t entry;
805 #endif
806     for (;;) {
807       size_t n = fread(&entry, sizeof(entry), 1, fp);
808       if (n == 0 || entry.a_type == AT_NULL) {
809         break;
810       }
811       switch (entry.a_type) {
812         case AT_PLATFORM:
813           auxv_cpu_type = reinterpret_cast<char*>(entry.a_un.a_val);
814           break;
815         case AT_ICACHEBSIZE:
816           icache_line_size_ = entry.a_un.a_val;
817           break;
818         case AT_DCACHEBSIZE:
819           dcache_line_size_ = entry.a_un.a_val;
820           break;
821       }
822     }
823     base::Fclose(fp);
824   }
825 
826   part_ = -1;
827   if (auxv_cpu_type) {
828     if (strcmp(auxv_cpu_type, "power10") == 0) {
829       part_ = kPPCPower10;
830     } else if (strcmp(auxv_cpu_type, "power9") == 0) {
831       part_ = kPPCPower9;
832     } else if (strcmp(auxv_cpu_type, "power8") == 0) {
833       part_ = kPPCPower8;
834     } else if (strcmp(auxv_cpu_type, "power7") == 0) {
835       part_ = kPPCPower7;
836     } else if (strcmp(auxv_cpu_type, "power6") == 0) {
837       part_ = kPPCPower6;
838     } else if (strcmp(auxv_cpu_type, "power5") == 0) {
839       part_ = kPPCPower5;
840     } else if (strcmp(auxv_cpu_type, "ppc970") == 0) {
841       part_ = kPPCG5;
842     } else if (strcmp(auxv_cpu_type, "ppc7450") == 0) {
843       part_ = kPPCG4;
844     } else if (strcmp(auxv_cpu_type, "pa6t") == 0) {
845       part_ = kPPCPA6T;
846     }
847   }
848 
849 #elif V8_OS_AIX
850   switch (_system_configuration.implementation) {
851     case POWER_10:
852       part_ = kPPCPower10;
853       break;
854     case POWER_9:
855       part_ = kPPCPower9;
856       break;
857     case POWER_8:
858       part_ = kPPCPower8;
859       break;
860     case POWER_7:
861       part_ = kPPCPower7;
862       break;
863     case POWER_6:
864       part_ = kPPCPower6;
865       break;
866     case POWER_5:
867       part_ = kPPCPower5;
868       break;
869   }
870 #endif  // V8_OS_AIX
871 #endif  // !USE_SIMULATOR
872 
873 #elif V8_HOST_ARCH_RISCV64
874   CPUInfo cpu_info;
875   char* features = cpu_info.ExtractField("isa");
876 
877   if (HasListItem(features, "rv64imafdc")) {
878     has_fpu_ = true;
879   }
880   if (HasListItem(features, "rv64imafdcv")) {
881     has_fpu_ = true;
882     has_rvv_ = true;
883   }
884 #endif  // V8_HOST_ARCH_RISCV64
885 }
886 
887 }  // namespace base
888 }  // namespace v8
889