Home | History | Annotate | Download | only in src
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "cpu.h"
     29 
     30 #if V8_CC_MSVC
     31 #include <intrin.h>  // __cpuid()
     32 #endif
     33 #if V8_OS_POSIX
     34 #include <unistd.h>  // sysconf()
     35 #endif
     36 
     37 #include <algorithm>
     38 #include <cctype>
     39 #include <climits>
     40 #include <cstdio>
     41 #include <cstdlib>
     42 #include <cstring>
     43 
     44 #include "checks.h"
     45 #if V8_OS_WIN
     46 #include "win32-headers.h"
     47 #endif
     48 
     49 namespace v8 {
     50 namespace internal {
     51 
     52 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
     53 
     54 // Define __cpuid() for non-MSVC compilers.
     55 #if !V8_CC_MSVC
     56 
     57 static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {
     58 #if defined(__i386__) && defined(__pic__)
     59   // Make sure to preserve ebx, which contains the pointer
     60   // to the GOT in case we're generating PIC.
     61   __asm__ volatile (
     62     "mov %%ebx, %%edi\n\t"
     63     "cpuid\n\t"
     64     "xchg %%edi, %%ebx\n\t"
     65     : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
     66     : "a"(info_type)
     67   );
     68 #else
     69   __asm__ volatile (
     70     "cpuid \n\t"
     71     : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
     72     : "a"(info_type)
     73   );
     74 #endif  // defined(__i386__) && defined(__pic__)
     75 }
     76 
     77 #endif  // !V8_CC_MSVC
     78 
     79 #elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_MIPS
     80 
     81 #if V8_HOST_ARCH_ARM
     82 
     83 // See <uapi/asm/hwcap.h> kernel header.
     84 /*
     85  * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
     86  */
     87 #define HWCAP_SWP (1 << 0)
     88 #define HWCAP_HALF  (1 << 1)
     89 #define HWCAP_THUMB (1 << 2)
     90 #define HWCAP_26BIT (1 << 3)  /* Play it safe */
     91 #define HWCAP_FAST_MULT (1 << 4)
     92 #define HWCAP_FPA (1 << 5)
     93 #define HWCAP_VFP (1 << 6)
     94 #define HWCAP_EDSP  (1 << 7)
     95 #define HWCAP_JAVA  (1 << 8)
     96 #define HWCAP_IWMMXT  (1 << 9)
     97 #define HWCAP_CRUNCH  (1 << 10)
     98 #define HWCAP_THUMBEE (1 << 11)
     99 #define HWCAP_NEON  (1 << 12)
    100 #define HWCAP_VFPv3 (1 << 13)
    101 #define HWCAP_VFPv3D16  (1 << 14) /* also set for VFPv4-D16 */
    102 #define HWCAP_TLS (1 << 15)
    103 #define HWCAP_VFPv4 (1 << 16)
    104 #define HWCAP_IDIVA (1 << 17)
    105 #define HWCAP_IDIVT (1 << 18)
    106 #define HWCAP_VFPD32  (1 << 19) /* set if VFP has 32 regs (not 16) */
    107 #define HWCAP_IDIV  (HWCAP_IDIVA | HWCAP_IDIVT)
    108 #define HWCAP_LPAE  (1 << 20)
    109 
    110 #define AT_HWCAP 16
    111 
    112 // Read the ELF HWCAP flags by parsing /proc/self/auxv.
    113 static uint32_t ReadELFHWCaps() {
    114   uint32_t result = 0;
    115   FILE* fp = fopen("/proc/self/auxv", "r");
    116   if (fp != NULL) {
    117     struct { uint32_t tag; uint32_t value; } entry;
    118     for (;;) {
    119       size_t n = fread(&entry, sizeof(entry), 1, fp);
    120       if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
    121         break;
    122       }
    123       if (entry.tag == AT_HWCAP) {
    124         result = entry.value;
    125         break;
    126       }
    127     }
    128     fclose(fp);
    129   }
    130   return result;
    131 }
    132 
    133 #endif  // V8_HOST_ARCH_ARM
    134 
    135 // Extract the information exposed by the kernel via /proc/cpuinfo.
    136 class CPUInfo V8_FINAL BASE_EMBEDDED {
    137  public:
    138   CPUInfo() : datalen_(0) {
    139     // Get the size of the cpuinfo file by reading it until the end. This is
    140     // required because files under /proc do not always return a valid size
    141     // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
    142     static const char PATHNAME[] = "/proc/cpuinfo";
    143     FILE* fp = fopen(PATHNAME, "r");
    144     if (fp != NULL) {
    145       for (;;) {
    146         char buffer[256];
    147         size_t n = fread(buffer, 1, sizeof(buffer), fp);
    148         if (n == 0) {
    149           break;
    150         }
    151         datalen_ += n;
    152       }
    153       fclose(fp);
    154     }
    155 
    156     // Read the contents of the cpuinfo file.
    157     data_ = new char[datalen_ + 1];
    158     fp = fopen(PATHNAME, "r");
    159     if (fp != NULL) {
    160       for (size_t offset = 0; offset < datalen_; ) {
    161         size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
    162         if (n == 0) {
    163           break;
    164         }
    165         offset += n;
    166       }
    167       fclose(fp);
    168     }
    169 
    170     // Zero-terminate the data.
    171     data_[datalen_] = '\0';
    172   }
    173 
    174   ~CPUInfo() {
    175     delete[] data_;
    176   }
    177 
    178   // Extract the content of a the first occurence of a given field in
    179   // the content of the cpuinfo file and return it as a heap-allocated
    180   // string that must be freed by the caller using delete[].
    181   // Return NULL if not found.
    182   char* ExtractField(const char* field) const {
    183     ASSERT(field != NULL);
    184 
    185     // Look for first field occurence, and ensure it starts the line.
    186     size_t fieldlen = strlen(field);
    187     char* p = data_;
    188     for (;;) {
    189       p = strstr(p, field);
    190       if (p == NULL) {
    191         return NULL;
    192       }
    193       if (p == data_ || p[-1] == '\n') {
    194         break;
    195       }
    196       p += fieldlen;
    197     }
    198 
    199     // Skip to the first colon followed by a space.
    200     p = strchr(p + fieldlen, ':');
    201     if (p == NULL || !isspace(p[1])) {
    202       return NULL;
    203     }
    204     p += 2;
    205 
    206     // Find the end of the line.
    207     char* q = strchr(p, '\n');
    208     if (q == NULL) {
    209       q = data_ + datalen_;
    210     }
    211 
    212     // Copy the line into a heap-allocated buffer.
    213     size_t len = q - p;
    214     char* result = new char[len + 1];
    215     if (result != NULL) {
    216       memcpy(result, p, len);
    217       result[len] = '\0';
    218     }
    219     return result;
    220   }
    221 
    222  private:
    223   char* data_;
    224   size_t datalen_;
    225 };
    226 
    227 
    228 // Checks that a space-separated list of items contains one given 'item'.
    229 static bool HasListItem(const char* list, const char* item) {
    230   ssize_t item_len = strlen(item);
    231   const char* p = list;
    232   if (p != NULL) {
    233     while (*p != '\0') {
    234       // Skip whitespace.
    235       while (isspace(*p)) ++p;
    236 
    237       // Find end of current list item.
    238       const char* q = p;
    239       while (*q != '\0' && !isspace(*q)) ++q;
    240 
    241       if (item_len == q - p && memcmp(p, item, item_len) == 0) {
    242         return true;
    243       }
    244 
    245       // Skip to next item.
    246       p = q;
    247     }
    248   }
    249   return false;
    250 }
    251 
    252 #endif  // V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
    253 
    254 CPU::CPU() : stepping_(0),
    255              model_(0),
    256              ext_model_(0),
    257              family_(0),
    258              ext_family_(0),
    259              type_(0),
    260              implementer_(0),
    261              architecture_(0),
    262              part_(0),
    263              has_fpu_(false),
    264              has_cmov_(false),
    265              has_sahf_(false),
    266              has_mmx_(false),
    267              has_sse_(false),
    268              has_sse2_(false),
    269              has_sse3_(false),
    270              has_ssse3_(false),
    271              has_sse41_(false),
    272              has_sse42_(false),
    273              has_idiva_(false),
    274              has_neon_(false),
    275              has_thumbee_(false),
    276              has_vfp_(false),
    277              has_vfp3_(false),
    278              has_vfp3_d32_(false) {
    279   memcpy(vendor_, "Unknown", 8);
    280 #if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
    281   int cpu_info[4];
    282 
    283   // __cpuid with an InfoType argument of 0 returns the number of
    284   // valid Ids in CPUInfo[0] and the CPU identification string in
    285   // the other three array elements. The CPU identification string is
    286   // not in linear order. The code below arranges the information
    287   // in a human readable form. The human readable order is CPUInfo[1] |
    288   // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
    289   // before using memcpy to copy these three array elements to cpu_string.
    290   __cpuid(cpu_info, 0);
    291   unsigned num_ids = cpu_info[0];
    292   std::swap(cpu_info[2], cpu_info[3]);
    293   memcpy(vendor_, cpu_info + 1, 12);
    294   vendor_[12] = '\0';
    295 
    296   // Interpret CPU feature information.
    297   if (num_ids > 0) {
    298     __cpuid(cpu_info, 1);
    299     stepping_ = cpu_info[0] & 0xf;
    300     model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
    301     family_ = (cpu_info[0] >> 8) & 0xf;
    302     type_ = (cpu_info[0] >> 12) & 0x3;
    303     ext_model_ = (cpu_info[0] >> 16) & 0xf;
    304     ext_family_ = (cpu_info[0] >> 20) & 0xff;
    305     has_fpu_ = (cpu_info[3] & 0x00000001) != 0;
    306     has_cmov_ = (cpu_info[3] & 0x00008000) != 0;
    307     has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
    308     has_sse_ = (cpu_info[3] & 0x02000000) != 0;
    309     has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
    310     has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
    311     has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
    312     has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
    313     has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
    314   }
    315 
    316   // Query extended IDs.
    317   __cpuid(cpu_info, 0x80000000);
    318   unsigned num_ext_ids = cpu_info[0];
    319 
    320   // Interpret extended CPU feature information.
    321   if (num_ext_ids > 0x80000000) {
    322     __cpuid(cpu_info, 0x80000001);
    323     // SAHF is always available in compat/legacy mode,
    324     // but must be probed in long mode.
    325 #if V8_HOST_ARCH_IA32
    326     has_sahf_ = true;
    327 #else
    328     has_sahf_ = (cpu_info[2] & 0x00000001) != 0;
    329 #endif
    330   }
    331 #elif V8_HOST_ARCH_ARM
    332   CPUInfo cpu_info;
    333 
    334   // Extract implementor from the "CPU implementer" field.
    335   char* implementer = cpu_info.ExtractField("CPU implementer");
    336   if (implementer != NULL) {
    337     char* end ;
    338     implementer_ = strtol(implementer, &end, 0);
    339     if (end == implementer) {
    340       implementer_ = 0;
    341     }
    342     delete[] implementer;
    343   }
    344 
    345   // Extract part number from the "CPU part" field.
    346   char* part = cpu_info.ExtractField("CPU part");
    347   if (part != NULL) {
    348     char* end ;
    349     part_ = strtol(part, &end, 0);
    350     if (end == part) {
    351       part_ = 0;
    352     }
    353     delete[] part;
    354   }
    355 
    356   // Extract architecture from the "CPU Architecture" field.
    357   // The list is well-known, unlike the the output of
    358   // the 'Processor' field which can vary greatly.
    359   // See the definition of the 'proc_arch' array in
    360   // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
    361   // same file.
    362   char* architecture = cpu_info.ExtractField("CPU architecture");
    363   if (architecture != NULL) {
    364     char* end;
    365     architecture_ = strtol(architecture, &end, 10);
    366     if (end == architecture) {
    367       architecture_ = 0;
    368     }
    369     delete[] architecture;
    370 
    371     // Unfortunately, it seems that certain ARMv6-based CPUs
    372     // report an incorrect architecture number of 7!
    373     //
    374     // See http://code.google.com/p/android/issues/detail?id=10812
    375     //
    376     // We try to correct this by looking at the 'elf_format'
    377     // field reported by the 'Processor' field, which is of the
    378     // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    379     // an ARMv6-one. For example, the Raspberry Pi is one popular
    380     // ARMv6 device that reports architecture 7.
    381     if (architecture_ == 7) {
    382       char* processor = cpu_info.ExtractField("Processor");
    383       if (HasListItem(processor, "(v6l)")) {
    384         architecture_ = 6;
    385       }
    386       delete[] processor;
    387     }
    388   }
    389 
    390   // Try to extract the list of CPU features from ELF hwcaps.
    391   uint32_t hwcaps = ReadELFHWCaps();
    392   if (hwcaps != 0) {
    393     has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
    394     has_neon_ = (hwcaps & HWCAP_NEON) != 0;
    395     has_thumbee_ = (hwcaps & HWCAP_THUMBEE) != 0;
    396     has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
    397     has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
    398     has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
    399                                    (hwcaps & HWCAP_VFPD32) != 0));
    400   } else {
    401     // Try to fallback to "Features" CPUInfo field.
    402     char* features = cpu_info.ExtractField("Features");
    403     has_idiva_ = HasListItem(features, "idiva");
    404     has_neon_ = HasListItem(features, "neon");
    405     has_thumbee_ = HasListItem(features, "thumbee");
    406     has_vfp_ = HasListItem(features, "vfp");
    407     if (HasListItem(features, "vfpv3")) {
    408       has_vfp3_ = true;
    409       has_vfp3_d32_ = true;
    410     } else if (HasListItem(features, "vfpv3d16")) {
    411       has_vfp3_ = true;
    412     }
    413     delete[] features;
    414   }
    415 
    416   // Some old kernels will report vfp not vfpv3. Here we make an attempt
    417   // to detect vfpv3 by checking for vfp *and* neon, since neon is only
    418   // available on architectures with vfpv3. Checking neon on its own is
    419   // not enough as it is possible to have neon without vfp.
    420   if (has_vfp_ && has_neon_) {
    421     has_vfp3_ = true;
    422   }
    423 
    424   // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
    425   if (architecture_ < 7 && has_vfp3_) {
    426     architecture_ = 7;
    427   }
    428 
    429   // ARMv7 implies ThumbEE.
    430   if (architecture_ >= 7) {
    431     has_thumbee_ = true;
    432   }
    433 
    434   // The earliest architecture with ThumbEE is ARMv6T2.
    435   if (has_thumbee_ && architecture_ < 6) {
    436     architecture_ = 6;
    437   }
    438 
    439   // We don't support any FPUs other than VFP.
    440   has_fpu_ = has_vfp_;
    441 #elif V8_HOST_ARCH_MIPS
    442   // Simple detection of FPU at runtime for Linux.
    443   // It is based on /proc/cpuinfo, which reveals hardware configuration
    444   // to user-space applications.  According to MIPS (early 2010), no similar
    445   // facility is universally available on the MIPS architectures,
    446   // so it's up to individual OSes to provide such.
    447   CPUInfo cpu_info;
    448   char* cpu_model = cpu_info.ExtractField("cpu model");
    449   has_fpu_ = HasListItem(cpu_model, "FPU");
    450   delete[] cpu_model;
    451 #endif
    452 }
    453 
    454 
    455 // static
    456 int CPU::NumberOfProcessorsOnline() {
    457 #if V8_OS_WIN
    458   SYSTEM_INFO info;
    459   GetSystemInfo(&info);
    460   return info.dwNumberOfProcessors;
    461 #else
    462   return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
    463 #endif
    464 }
    465 
    466 } }  // namespace v8::internal
    467