xref: /third_party/node/deps/v8/src/base/cpu.cc (revision 1cb0ef41)
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
58namespace v8 {
59namespace 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
66static 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
168static 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
199int __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
219int __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.
241class 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'.
333static 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
363bool 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
406CPU::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