cpu.cc 24.1 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/base/cpu.h"
6

johnx's avatar
johnx committed
7 8 9 10
#if defined(STARBOARD)
#include "starboard/cpu_features.h"
#endif

11
#if V8_LIBC_MSVCRT
12 13
#include <intrin.h>  // __cpuid()
#endif
14 15 16 17 18
#if V8_OS_LINUX
#include <linux/auxvec.h>  // AT_HWCAP
#endif
#if V8_GLIBC_PREREQ(2, 16)
#include <sys/auxv.h>  // getauxval()
19
#endif
20 21 22
#if V8_OS_QNX
#include <sys/syspage.h>  // cpuinfo
#endif
23
#if V8_OS_LINUX && (V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64)
24 25
#include <elf.h>
#endif
26 27 28 29 30
#if V8_OS_AIX
#include <sys/systemcfg.h>  // _system_configuration
#ifndef POWER_8
#define POWER_8 0x10000
#endif
bjaideep's avatar
bjaideep committed
31 32 33
#ifndef POWER_9
#define POWER_9 0x20000
#endif
34 35 36
#ifndef POWER_10
#define POWER_10 0x40000
#endif
37
#endif
38 39 40
#if V8_OS_POSIX
#include <unistd.h>  // sysconf()
#endif
41

42 43 44 45 46
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
47

48 49
#include <algorithm>

50
#include "src/base/logging.h"
51
#include "src/base/platform/wrappers.h"
52
#if V8_OS_WIN
53
#include "src/base/win32-headers.h"
54
#endif
55 56

namespace v8 {
57
namespace base {
58

yangguo's avatar
yangguo committed
59
#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
60

61 62
// Define __cpuid() for non-MSVC libraries.
#if !V8_LIBC_MSVCRT
63

64
static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {
65 66
// Clear ecx to align with __cpuid() of MSVC:
// https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
67 68 69
#if defined(__i386__) && defined(__pic__)
  // Make sure to preserve ebx, which contains the pointer
  // to the GOT in case we're generating PIC.
70 71 72 73 74 75 76
  __asm__ volatile(
      "mov %%ebx, %%edi\n\t"
      "cpuid\n\t"
      "xchg %%edi, %%ebx\n\t"
      : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
        "=d"(cpu_info[3])
      : "a"(info_type), "c"(0));
77
#else
78 79 80 81
  __asm__ volatile("cpuid \n\t"
                   : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
                     "=d"(cpu_info[3])
                   : "a"(info_type), "c"(0));
82
#endif  // defined(__i386__) && defined(__pic__)
83 84
}

85
#endif  // !V8_LIBC_MSVCRT
86

87 88
#elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 || V8_HOST_ARCH_MIPS || \
    V8_HOST_ARCH_MIPS64
89

90 91
#if V8_OS_LINUX

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#if V8_HOST_ARCH_ARM

// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
 */
#define HWCAP_SWP (1 << 0)
#define HWCAP_HALF  (1 << 1)
#define HWCAP_THUMB (1 << 2)
#define HWCAP_26BIT (1 << 3)  /* Play it safe */
#define HWCAP_FAST_MULT (1 << 4)
#define HWCAP_FPA (1 << 5)
#define HWCAP_VFP (1 << 6)
#define HWCAP_EDSP  (1 << 7)
#define HWCAP_JAVA  (1 << 8)
#define HWCAP_IWMMXT  (1 << 9)
#define HWCAP_CRUNCH  (1 << 10)
#define HWCAP_THUMBEE (1 << 11)
#define HWCAP_NEON  (1 << 12)
#define HWCAP_VFPv3 (1 << 13)
#define HWCAP_VFPv3D16  (1 << 14) /* also set for VFPv4-D16 */
#define HWCAP_TLS (1 << 15)
#define HWCAP_VFPv4 (1 << 16)
#define HWCAP_IDIVA (1 << 17)
#define HWCAP_IDIVT (1 << 18)
#define HWCAP_VFPD32  (1 << 19) /* set if VFP has 32 regs (not 16) */
#define HWCAP_IDIV  (HWCAP_IDIVA | HWCAP_IDIVT)
#define HWCAP_LPAE  (1 << 20)

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#endif  // V8_HOST_ARCH_ARM

#if V8_HOST_ARCH_ARM64

// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
 */
#define HWCAP_FP (1 << 0)
#define HWCAP_ASIMD (1 << 1)
#define HWCAP_EVTSTRM (1 << 2)
#define HWCAP_AES (1 << 3)
#define HWCAP_PMULL (1 << 4)
#define HWCAP_SHA1 (1 << 5)
#define HWCAP_SHA2 (1 << 6)
#define HWCAP_CRC32 (1 << 7)
#define HWCAP_ATOMICS (1 << 8)
#define HWCAP_FPHP (1 << 9)
#define HWCAP_ASIMDHP (1 << 10)
#define HWCAP_CPUID (1 << 11)
#define HWCAP_ASIMDRDM (1 << 12)
#define HWCAP_JSCVT (1 << 13)
#define HWCAP_FCMA (1 << 14)
#define HWCAP_LRCPC (1 << 15)
#define HWCAP_DCPOP (1 << 16)
#define HWCAP_SHA3 (1 << 17)
#define HWCAP_SM3 (1 << 18)
#define HWCAP_SM4 (1 << 19)
#define HWCAP_ASIMDDP (1 << 20)
#define HWCAP_SHA512 (1 << 21)
#define HWCAP_SVE (1 << 22)
#define HWCAP_ASIMDFHM (1 << 23)
#define HWCAP_DIT (1 << 24)
#define HWCAP_USCAT (1 << 25)
#define HWCAP_ILRCPC (1 << 26)
#define HWCAP_FLAGM (1 << 27)
#define HWCAP_SSBS (1 << 28)
#define HWCAP_SB (1 << 29)
#define HWCAP_PACA (1 << 30)
#define HWCAP_PACG (1UL << 31)

#endif  // V8_HOST_ARCH_ARM64

#if V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64

166
static uint32_t ReadELFHWCaps() {
167
  uint32_t result = 0;
168
#if V8_GLIBC_PREREQ(2, 16)
169
  result = static_cast<uint32_t>(getauxval(AT_HWCAP));
170
#else
171
  // Read the ELF HWCAP flags by parsing /proc/self/auxv.
172
  FILE* fp = base::Fopen("/proc/self/auxv", "r");
173 174 175 176 177 178 179 180 181 182 183 184 185 186
  if (fp != nullptr) {
    struct {
      uint32_t tag;
      uint32_t value;
    } entry;
    for (;;) {
      size_t n = fread(&entry, sizeof(entry), 1, fp);
      if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
        break;
      }
      if (entry.tag == AT_HWCAP) {
        result = entry.value;
        break;
      }
187
    }
188
    base::Fclose(fp);
189
  }
190
#endif
191
  return result;
192 193
}

194
#endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64
195

196 197 198 199
#if V8_HOST_ARCH_MIPS
int __detect_fp64_mode(void) {
  double result = 0;
  // Bit representation of (double)1 is 0x3FF0000000000000.
200 201 202 203 204 205 206 207 208 209 210 211
  __asm__ volatile(
      ".set push\n\t"
      ".set noreorder\n\t"
      ".set oddspreg\n\t"
      "lui $t0, 0x3FF0\n\t"
      "ldc1 $f0, %0\n\t"
      "mtc1 $t0, $f1\n\t"
      "sdc1 $f0, %0\n\t"
      ".set pop\n\t"
      : "+m"(result)
      :
      : "t0", "$f0", "$f1", "memory");
212 213 214 215 216 217 218

  return !(result == 1);
}


int __detect_mips_arch_revision(void) {
  // TODO(dusmil): Do the specific syscall as soon as it is implemented in mips
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  // kernel.
  uint32_t result = 0;
  __asm__ volatile(
      "move $v0, $zero\n\t"
      // Encoding for "addi $v0, $v0, 1" on non-r6,
      // which is encoding for "bovc $v0, %v0, 1" on r6.
      // Use machine code directly to avoid compilation errors with different
      // toolchains and maintain compatibility.
      ".word 0x20420001\n\t"
      "sw $v0, %0\n\t"
      : "=m"(result)
      :
      : "v0", "memory");
  // Result is 0 on r6 architectures, 1 on other architecture revisions.
  // Fall-back to the least common denominator which is mips32 revision 1.
  return result ? 1 : 6;
235
}
236
#endif  // V8_HOST_ARCH_MIPS
237

238
// Extract the information exposed by the kernel via /proc/cpuinfo.
239
class CPUInfo final {
240 241 242 243 244 245
 public:
  CPUInfo() : datalen_(0) {
    // Get the size of the cpuinfo file by reading it until the end. This is
    // required because files under /proc do not always return a valid size
    // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
    static const char PATHNAME[] = "/proc/cpuinfo";
246
    FILE* fp = base::Fopen(PATHNAME, "r");
247
    if (fp != nullptr) {
248 249 250 251 252 253 254 255
      for (;;) {
        char buffer[256];
        size_t n = fread(buffer, 1, sizeof(buffer), fp);
        if (n == 0) {
          break;
        }
        datalen_ += n;
      }
256
      base::Fclose(fp);
257 258 259 260
    }

    // Read the contents of the cpuinfo file.
    data_ = new char[datalen_ + 1];
261
    fp = base::Fopen(PATHNAME, "r");
262
    if (fp != nullptr) {
263 264 265 266 267 268 269
      for (size_t offset = 0; offset < datalen_; ) {
        size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
        if (n == 0) {
          break;
        }
        offset += n;
      }
270
      base::Fclose(fp);
271 272 273 274 275 276 277 278 279 280
    }

    // Zero-terminate the data.
    data_[datalen_] = '\0';
  }

  ~CPUInfo() {
    delete[] data_;
  }

281
  // Extract the content of a the first occurrence of a given field in
282 283
  // the content of the cpuinfo file and return it as a heap-allocated
  // string that must be freed by the caller using delete[].
284
  // Return nullptr if not found.
285
  char* ExtractField(const char* field) const {
286
    DCHECK_NOT_NULL(field);
287

288
    // Look for first field occurrence, and ensure it starts the line.
289 290 291 292
    size_t fieldlen = strlen(field);
    char* p = data_;
    for (;;) {
      p = strstr(p, field);
293 294
      if (p == nullptr) {
        return nullptr;
295 296 297 298 299 300 301 302 303
      }
      if (p == data_ || p[-1] == '\n') {
        break;
      }
      p += fieldlen;
    }

    // Skip to the first colon followed by a space.
    p = strchr(p + fieldlen, ':');
304 305
    if (p == nullptr || !isspace(p[1])) {
      return nullptr;
306 307 308 309 310
    }
    p += 2;

    // Find the end of the line.
    char* q = strchr(p, '\n');
311
    if (q == nullptr) {
312 313 314 315 316 317
      q = data_ + datalen_;
    }

    // Copy the line into a heap-allocated buffer.
    size_t len = q - p;
    char* result = new char[len + 1];
318
    if (result != nullptr) {
319
      base::Memcpy(result, p, len);
320 321 322 323 324 325 326 327 328 329 330 331 332 333
      result[len] = '\0';
    }
    return result;
  }

 private:
  char* data_;
  size_t datalen_;
};

// Checks that a space-separated list of items contains one given 'item'.
static bool HasListItem(const char* list, const char* item) {
  ssize_t item_len = strlen(item);
  const char* p = list;
334
  if (p != nullptr) {
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
    while (*p != '\0') {
      // Skip whitespace.
      while (isspace(*p)) ++p;

      // Find end of current list item.
      const char* q = p;
      while (*q != '\0' && !isspace(*q)) ++q;

      if (item_len == q - p && memcmp(p, item, item_len) == 0) {
        return true;
      }

      // Skip to next item.
      p = q;
    }
  }
  return false;
}

354 355
#endif  // V8_OS_LINUX

356 357
#endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 ||
        // V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
358

johnx's avatar
johnx committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
#if defined(STARBOARD)

bool CPU::StarboardDetectCPU() {
#if (SB_API_VERSION >= 11)
  SbCPUFeatures features;
  if (!SbCPUFeaturesGet(&features)) {
    return false;
  }
  architecture_ = features.arm.architecture_generation;
  switch (features.architecture) {
    case kSbCPUFeaturesArchitectureArm:
    case kSbCPUFeaturesArchitectureArm64:
      has_neon_ = features.arm.has_neon;
      has_thumb2_ = features.arm.has_thumb2;
      has_vfp_ = features.arm.has_vfp;
      has_vfp3_ = features.arm.has_vfp3;
      has_vfp3_d32_ = features.arm.has_vfp3_d32;
      has_idiva_ = features.arm.has_idiva;
      break;
    case kSbCPUFeaturesArchitectureX86:
    case kSbCPUFeaturesArchitectureX86_64:
      // Following flags are mandatory for V8
      has_cmov_ = features.x86.has_cmov;
      has_sse2_ = features.x86.has_sse2;
      // These flags are optional
      has_sse3_ = features.x86.has_sse3;
      has_ssse3_ = features.x86.has_ssse3;
      has_sse41_ = features.x86.has_sse41;
      has_sahf_ = features.x86.has_sahf;
      has_avx_ = features.x86.has_avx;
Zhi An Ng's avatar
Zhi An Ng committed
389
      has_avx2_ = features.x86.has_avx2;
johnx's avatar
johnx committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
      has_fma3_ = features.x86.has_fma3;
      has_bmi1_ = features.x86.has_bmi1;
      has_bmi2_ = features.x86.has_bmi2;
      has_lzcnt_ = features.x86.has_lzcnt;
      has_popcnt_ = features.x86.has_popcnt;
      break;
    default:
      return false;
  }

  return true;
#else  // SB_API_VERSION >= 11
  return false;
#endif
}

#endif

408 409 410 411 412 413 414 415 416
CPU::CPU()
    : stepping_(0),
      model_(0),
      ext_model_(0),
      family_(0),
      ext_family_(0),
      type_(0),
      implementer_(0),
      architecture_(0),
417
      variant_(-1),
418
      part_(0),
419 420
      icache_line_size_(kUnknownCacheLineSize),
      dcache_line_size_(kUnknownCacheLineSize),
421 422 423 424 425 426 427 428 429 430
      has_fpu_(false),
      has_cmov_(false),
      has_sahf_(false),
      has_mmx_(false),
      has_sse_(false),
      has_sse2_(false),
      has_sse3_(false),
      has_ssse3_(false),
      has_sse41_(false),
      has_sse42_(false),
431
      is_atom_(false),
432
      has_osxsave_(false),
433
      has_avx_(false),
Zhi An Ng's avatar
Zhi An Ng committed
434
      has_avx2_(false),
435
      has_fma3_(false),
436 437 438 439
      has_bmi1_(false),
      has_bmi2_(false),
      has_lzcnt_(false),
      has_popcnt_(false),
440 441 442 443 444 445
      has_idiva_(false),
      has_neon_(false),
      has_thumb2_(false),
      has_vfp_(false),
      has_vfp3_(false),
      has_vfp3_d32_(false),
446
      has_jscvt_(false),
447
      is_fp64_mode_(false),
448
      has_non_stop_time_stamp_counter_(false),
449
      is_running_in_vm_(false),
450
      has_msa_(false) {
451
  base::Memcpy(vendor_, "Unknown", 8);
johnx's avatar
johnx committed
452 453 454 455 456 457 458

#if defined(STARBOARD)
  if (StarboardDetectCPU()) {
    return;
  }
#endif

yangguo's avatar
yangguo committed
459
#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
460 461 462 463 464 465 466 467 468 469 470 471
  int cpu_info[4];

  // __cpuid with an InfoType argument of 0 returns the number of
  // valid Ids in CPUInfo[0] and the CPU identification string in
  // the other three array elements. The CPU identification string is
  // not in linear order. The code below arranges the information
  // in a human readable form. The human readable order is CPUInfo[1] |
  // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
  // before using memcpy to copy these three array elements to cpu_string.
  __cpuid(cpu_info, 0);
  unsigned num_ids = cpu_info[0];
  std::swap(cpu_info[2], cpu_info[3]);
472
  base::Memcpy(vendor_, cpu_info + 1, 12);
473 474 475 476 477
  vendor_[12] = '\0';

  // Interpret CPU feature information.
  if (num_ids > 0) {
    __cpuid(cpu_info, 1);
Zhi An Ng's avatar
Zhi An Ng committed
478 479 480 481 482 483

    int cpu_info7[4] = {0};
    if (num_ids >= 7) {
      __cpuid(cpu_info7, 7);
    }

484 485 486
    stepping_ = cpu_info[0] & 0xF;
    model_ = ((cpu_info[0] >> 4) & 0xF) + ((cpu_info[0] >> 12) & 0xF0);
    family_ = (cpu_info[0] >> 8) & 0xF;
487
    type_ = (cpu_info[0] >> 12) & 0x3;
488 489
    ext_model_ = (cpu_info[0] >> 16) & 0xF;
    ext_family_ = (cpu_info[0] >> 20) & 0xFF;
490 491 492 493 494 495 496 497 498
    has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
    has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
    has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
    has_sse_ = (cpu_info[3] & 0x02000000) != 0;
    has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
    has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
    has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
    has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
    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;
Zhi An Ng's avatar
Zhi An Ng committed
502
    has_avx2_ = (cpu_info7[1] & 0x00000020) != 0;
503
    has_fma3_ = (cpu_info[2] & 0x00001000) != 0;
504 505 506 507 508 509
    // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1."
    // See https://lwn.net/Articles/301888/
    // This is checking for any hypervisor. Hypervisors may choose not to
    // announce themselves. Hypervisors trap CPUID and sometimes return
    // different results to underlying hardware.
    is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 0;
510 511 512

    if (family_ == 0x6) {
      switch (model_) {
513
        case 0x1C:  // SLT
514 515 516 517 518
        case 0x26:
        case 0x36:
        case 0x27:
        case 0x35:
        case 0x37:  // SLM
519 520 521 522
        case 0x4A:
        case 0x4D:
        case 0x4C:  // AMT
        case 0x6E:
523 524 525
          is_atom_ = true;
      }
    }
526 527
  }

528 529 530 531 532 533 534
  // There are separate feature flags for VEX-encoded GPR instructions.
  if (num_ids >= 7) {
    __cpuid(cpu_info, 7);
    has_bmi1_ = (cpu_info[1] & 0x00000008) != 0;
    has_bmi2_ = (cpu_info[1] & 0x00000100) != 0;
  }

535 536 537 538 539 540 541
  // Query extended IDs.
  __cpuid(cpu_info, 0x80000000);
  unsigned num_ext_ids = cpu_info[0];

  // Interpret extended CPU feature information.
  if (num_ext_ids > 0x80000000) {
    __cpuid(cpu_info, 0x80000001);
542
    has_lzcnt_ = (cpu_info[2] & 0x00000020) != 0;
543
    // SAHF must be probed in long mode.
544 545
    has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
  }
546

547
  // Check if CPU has non stoppable time stamp counter.
548
  const unsigned parameter_containing_non_stop_time_stamp_counter = 0x80000007;
549 550 551 552 553
  if (num_ext_ids >= parameter_containing_non_stop_time_stamp_counter) {
    __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter);
    has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
  }

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
  // This logic is replicated from cpu.cc present in chromium.src
  if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
    int cpu_info_hv[4] = {};
    __cpuid(cpu_info_hv, 0x40000000);
    if (cpu_info_hv[1] == 0x7263694D &&  // Micr
        cpu_info_hv[2] == 0x666F736F &&  // osof
        cpu_info_hv[3] == 0x76482074) {  // t Hv
      // If CPUID says we have a variant TSC and a hypervisor has identified
      // itself and the hypervisor says it is Microsoft Hyper-V, then treat
      // TSC as invariant.
      //
      // Microsoft Hyper-V hypervisor reports variant TSC as there are some
      // scenarios (eg. VM live migration) where the TSC is variant, but for
      // our purposes we can treat it as invariant.
      has_non_stop_time_stamp_counter_ = true;
    }
  }
571
#elif V8_HOST_ARCH_ARM
572 573 574

#if V8_OS_LINUX

575 576 577 578
  CPUInfo cpu_info;

  // Extract implementor from the "CPU implementer" field.
  char* implementer = cpu_info.ExtractField("CPU implementer");
579
  if (implementer != nullptr) {
580
    char* end;
581 582 583 584 585 586 587
    implementer_ = strtol(implementer, &end, 0);
    if (end == implementer) {
      implementer_ = 0;
    }
    delete[] implementer;
  }

588
  char* variant = cpu_info.ExtractField("CPU variant");
589
  if (variant != nullptr) {
590 591 592 593 594 595 596 597
    char* end;
    variant_ = strtol(variant, &end, 0);
    if (end == variant) {
      variant_ = -1;
    }
    delete[] variant;
  }

598 599
  // Extract part number from the "CPU part" field.
  char* part = cpu_info.ExtractField("CPU part");
600
  if (part != nullptr) {
601
    char* end;
602 603 604 605 606 607 608 609 610 611 612 613 614 615
    part_ = strtol(part, &end, 0);
    if (end == part) {
      part_ = 0;
    }
    delete[] part;
  }

  // Extract architecture from the "CPU Architecture" field.
  // The list is well-known, unlike the the output of
  // the 'Processor' field which can vary greatly.
  // See the definition of the 'proc_arch' array in
  // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
  // same file.
  char* architecture = cpu_info.ExtractField("CPU architecture");
616
  if (architecture != nullptr) {
617 618 619
    char* end;
    architecture_ = strtol(architecture, &end, 10);
    if (end == architecture) {
620 621 622 623 624 625
      // Kernels older than 3.18 report "CPU architecture: AArch64" on ARMv8.
      if (strcmp(architecture, "AArch64") == 0) {
        architecture_ = 8;
      } else {
        architecture_ = 0;
      }
626 627 628 629 630 631 632 633
    }
    delete[] architecture;

    // Unfortunately, it seems that certain ARMv6-based CPUs
    // report an incorrect architecture number of 7!
    //
    // See http://code.google.com/p/android/issues/detail?id=10812
    //
634
    // We try to correct this by looking at the 'elf_platform'
635 636 637 638 639 640 641 642 643 644 645
    // field reported by the 'Processor' field, which is of the
    // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    // an ARMv6-one. For example, the Raspberry Pi is one popular
    // ARMv6 device that reports architecture 7.
    if (architecture_ == 7) {
      char* processor = cpu_info.ExtractField("Processor");
      if (HasListItem(processor, "(v6l)")) {
        architecture_ = 6;
      }
      delete[] processor;
    }
646 647 648 649 650 651 652 653 654

    // elf_platform moved to the model name field in Linux v3.8.
    if (architecture_ == 7) {
      char* processor = cpu_info.ExtractField("model name");
      if (HasListItem(processor, "(v6l)")) {
        architecture_ = 6;
      }
      delete[] processor;
    }
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
  }

  // Try to extract the list of CPU features from ELF hwcaps.
  uint32_t hwcaps = ReadELFHWCaps();
  if (hwcaps != 0) {
    has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
    has_neon_ = (hwcaps & HWCAP_NEON) != 0;
    has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
    has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
    has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
                                   (hwcaps & HWCAP_VFPD32) != 0));
  } else {
    // Try to fallback to "Features" CPUInfo field.
    char* features = cpu_info.ExtractField("Features");
    has_idiva_ = HasListItem(features, "idiva");
    has_neon_ = HasListItem(features, "neon");
671
    has_thumb2_ = HasListItem(features, "thumb2");
672
    has_vfp_ = HasListItem(features, "vfp");
673
    if (HasListItem(features, "vfpv3d16")) {
674
      has_vfp3_ = true;
675
    } else if (HasListItem(features, "vfpv3")) {
676
      has_vfp3_ = true;
677
      has_vfp3_d32_ = true;
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
    }
    delete[] features;
  }

  // Some old kernels will report vfp not vfpv3. Here we make an attempt
  // to detect vfpv3 by checking for vfp *and* neon, since neon is only
  // available on architectures with vfpv3. Checking neon on its own is
  // not enough as it is possible to have neon without vfp.
  if (has_vfp_ && has_neon_) {
    has_vfp3_ = true;
  }

  // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
  if (architecture_ < 7 && has_vfp3_) {
    architecture_ = 7;
  }

695
  // ARMv7 implies Thumb2.
696
  if (architecture_ >= 7) {
697
    has_thumb2_ = true;
698 699
  }

700 701
  // The earliest architecture with Thumb2 is ARMv6T2.
  if (has_thumb2_ && architecture_ < 6) {
702 703 704 705 706
    architecture_ = 6;
  }

  // We don't support any FPUs other than VFP.
  has_fpu_ = has_vfp_;
707 708 709 710 711 712

#elif V8_OS_QNX

  uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
  if (cpu_flags & ARM_CPU_FLAG_V7) {
    architecture_ = 7;
713
    has_thumb2_ = true;
714 715
  } else if (cpu_flags & ARM_CPU_FLAG_V6) {
    architecture_ = 6;
716
    // QNX doesn't say if Thumb2 is available.
717 718
    // Assume false for the architectures older than ARMv7.
  }
719
  DCHECK_GE(architecture_, 6);
720 721 722 723 724 725 726 727 728 729 730 731 732
  has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
  has_vfp_ = has_fpu_;
  if (cpu_flags & ARM_CPU_FLAG_NEON) {
    has_neon_ = true;
    has_vfp3_ = has_vfp_;
#ifdef ARM_CPU_FLAG_VFP_D32
    has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
#endif
  }
  has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;

#endif  // V8_OS_LINUX

733
#elif V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
734

735 736 737 738 739 740 741 742
  // Simple detection of FPU at runtime for Linux.
  // It is based on /proc/cpuinfo, which reveals hardware configuration
  // to user-space applications.  According to MIPS (early 2010), no similar
  // facility is universally available on the MIPS architectures,
  // so it's up to individual OSes to provide such.
  CPUInfo cpu_info;
  char* cpu_model = cpu_info.ExtractField("cpu model");
  has_fpu_ = HasListItem(cpu_model, "FPU");
743 744
  char* ASEs = cpu_info.ExtractField("ASEs implemented");
  has_msa_ = HasListItem(ASEs, "msa");
745
  delete[] cpu_model;
746
  delete[] ASEs;
747 748 749 750
#ifdef V8_HOST_ARCH_MIPS
  is_fp64_mode_ = __detect_fp64_mode();
  architecture_ = __detect_mips_arch_revision();
#endif
751

752
#elif V8_HOST_ARCH_ARM64
753 754 755 756
#ifdef V8_OS_WIN
  // Windows makes high-resolution thread timing information available in
  // user-space.
  has_non_stop_time_stamp_counter_ = true;
757 758 759 760 761 762 763 764 765 766 767 768 769

#elif V8_OS_LINUX
  // Try to extract the list of CPU features from ELF hwcaps.
  uint32_t hwcaps = ReadELFHWCaps();
  if (hwcaps != 0) {
    has_jscvt_ = (hwcaps & HWCAP_JSCVT) != 0;
  } else {
    // Try to fallback to "Features" CPUInfo field
    CPUInfo cpu_info;
    char* features = cpu_info.ExtractField("Features");
    has_jscvt_ = HasListItem(features, "jscvt");
    delete[] features;
  }
770
#elif V8_OS_MACOSX
771 772
  // ARM64 Macs always have JSCVT.
  has_jscvt_ = true;
773
#endif  // V8_OS_WIN
774

775
#elif V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64
776 777 778

#ifndef USE_SIMULATOR
#if V8_OS_LINUX
779
  // Read processor info from /proc/self/auxv.
780
  char* auxv_cpu_type = nullptr;
781
  FILE* fp = base::Fopen("/proc/self/auxv", "r");
782
  if (fp != nullptr) {
783
#if V8_TARGET_ARCH_PPC64
784
    Elf64_auxv_t entry;
785
#else
786
    Elf32_auxv_t entry;
787
#endif
788 789 790
    for (;;) {
      size_t n = fread(&entry, sizeof(entry), 1, fp);
      if (n == 0 || entry.a_type == AT_NULL) {
791
        break;
792 793 794 795 796 797 798 799 800 801 802 803
      }
      switch (entry.a_type) {
        case AT_PLATFORM:
          auxv_cpu_type = reinterpret_cast<char*>(entry.a_un.a_val);
          break;
        case AT_ICACHEBSIZE:
          icache_line_size_ = entry.a_un.a_val;
          break;
        case AT_DCACHEBSIZE:
          dcache_line_size_ = entry.a_un.a_val;
          break;
      }
804
    }
805
    base::Fclose(fp);
806 807 808 809
  }

  part_ = -1;
  if (auxv_cpu_type) {
810 811 812
    if (strcmp(auxv_cpu_type, "power10") == 0) {
      part_ = kPPCPower10;
    } else if (strcmp(auxv_cpu_type, "power9") == 0) {
813
      part_ = kPPCPower9;
814
    } else if (strcmp(auxv_cpu_type, "power8") == 0) {
815
      part_ = kPPCPower8;
816
    } else if (strcmp(auxv_cpu_type, "power7") == 0) {
817
      part_ = kPPCPower7;
818
    } else if (strcmp(auxv_cpu_type, "power6") == 0) {
819
      part_ = kPPCPower6;
820
    } else if (strcmp(auxv_cpu_type, "power5") == 0) {
821
      part_ = kPPCPower5;
822
    } else if (strcmp(auxv_cpu_type, "ppc970") == 0) {
823
      part_ = kPPCG5;
824
    } else if (strcmp(auxv_cpu_type, "ppc7450") == 0) {
825
      part_ = kPPCG4;
826
    } else if (strcmp(auxv_cpu_type, "pa6t") == 0) {
827
      part_ = kPPCPA6T;
828 829 830
    }
  }

831 832
#elif V8_OS_AIX
  switch (_system_configuration.implementation) {
833 834 835
    case POWER_10:
      part_ = kPPCPower10;
      break;
836
    case POWER_9:
837
      part_ = kPPCPower9;
838
      break;
839
    case POWER_8:
840
      part_ = kPPCPower8;
841 842
      break;
    case POWER_7:
843
      part_ = kPPCPower7;
844 845
      break;
    case POWER_6:
846
      part_ = kPPCPower6;
847 848
      break;
    case POWER_5:
849
      part_ = kPPCPower5;
850 851 852
      break;
  }
#endif  // V8_OS_AIX
853
#endif  // !USE_SIMULATOR
854
#endif  // V8_HOST_ARCH_PPC || V8_HOST_ARCH_PPC64
855 856
}

857 858
}  // namespace base
}  // namespace v8