cpu.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 The Chromium 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 "base/cpu.h"
6
7#include <string.h>
8
9#include <algorithm>
10
11#include "base/basictypes.h"
12#include "build/build_config.h"
13
14#if defined(ARCH_CPU_X86_FAMILY)
15#if defined(_MSC_VER)
16#include <intrin.h>
17#include <immintrin.h>  // For _xgetbv()
18#endif
19#endif
20
21namespace base {
22
23CPU::CPU()
24  : signature_(0),
25    type_(0),
26    family_(0),
27    model_(0),
28    stepping_(0),
29    ext_model_(0),
30    ext_family_(0),
31    has_mmx_(false),
32    has_sse_(false),
33    has_sse2_(false),
34    has_sse3_(false),
35    has_ssse3_(false),
36    has_sse41_(false),
37    has_sse42_(false),
38    has_avx_(false),
39    has_avx_hardware_(false),
40    has_aesni_(false),
41    has_non_stop_time_stamp_counter_(false),
42    cpu_vendor_("unknown") {
43  Initialize();
44}
45
46namespace {
47
48#if defined(ARCH_CPU_X86_FAMILY)
49#ifndef _MSC_VER
50
51#if defined(__pic__) && defined(__i386__)
52
53void __cpuid(int cpu_info[4], int info_type) {
54  __asm__ volatile (
55    "mov %%ebx, %%edi\n"
56    "cpuid\n"
57    "xchg %%edi, %%ebx\n"
58    : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
59    : "a"(info_type)
60  );
61}
62
63#else
64
65void __cpuid(int cpu_info[4], int info_type) {
66  __asm__ volatile (
67    "cpuid \n\t"
68    : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
69    : "a"(info_type)
70  );
71}
72
73#endif
74
75// _xgetbv returns the value of an Intel Extended Control Register (XCR).
76// Currently only XCR0 is defined by Intel so |xcr| should always be zero.
77uint64 _xgetbv(uint32 xcr) {
78  uint32 eax, edx;
79
80  __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr));
81  return (static_cast<uint64>(edx) << 32) | eax;
82}
83
84#endif  // !_MSC_VER
85#endif  // ARCH_CPU_X86_FAMILY
86
87}  // anonymous namespace
88
89void CPU::Initialize() {
90#if defined(ARCH_CPU_X86_FAMILY)
91  int cpu_info[4] = {-1};
92  char cpu_string[48];
93
94  // __cpuid with an InfoType argument of 0 returns the number of
95  // valid Ids in CPUInfo[0] and the CPU identification string in
96  // the other three array elements. The CPU identification string is
97  // not in linear order. The code below arranges the information
98  // in a human readable form. The human readable order is CPUInfo[1] |
99  // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
100  // before using memcpy to copy these three array elements to cpu_string.
101  __cpuid(cpu_info, 0);
102  int num_ids = cpu_info[0];
103  std::swap(cpu_info[2], cpu_info[3]);
104  memcpy(cpu_string, &cpu_info[1], 3 * sizeof(cpu_info[1]));
105  cpu_vendor_.assign(cpu_string, 3 * sizeof(cpu_info[1]));
106
107  // Interpret CPU feature information.
108  if (num_ids > 0) {
109    __cpuid(cpu_info, 1);
110    signature_ = cpu_info[0];
111    stepping_ = cpu_info[0] & 0xf;
112    model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
113    family_ = (cpu_info[0] >> 8) & 0xf;
114    type_ = (cpu_info[0] >> 12) & 0x3;
115    ext_model_ = (cpu_info[0] >> 16) & 0xf;
116    ext_family_ = (cpu_info[0] >> 20) & 0xff;
117    has_mmx_ =   (cpu_info[3] & 0x00800000) != 0;
118    has_sse_ =   (cpu_info[3] & 0x02000000) != 0;
119    has_sse2_ =  (cpu_info[3] & 0x04000000) != 0;
120    has_sse3_ =  (cpu_info[2] & 0x00000001) != 0;
121    has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
122    has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
123    has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
124    has_avx_hardware_ =
125                 (cpu_info[2] & 0x10000000) != 0;
126    // AVX instructions will generate an illegal instruction exception unless
127    //   a) they are supported by the CPU,
128    //   b) XSAVE is supported by the CPU and
129    //   c) XSAVE is enabled by the kernel.
130    // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
131    has_avx_ =
132        has_avx_hardware_ &&
133        (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
134        (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
135    has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
136  }
137
138  // Get the brand string of the cpu.
139  __cpuid(cpu_info, 0x80000000);
140  const int parameter_end = 0x80000004;
141  int max_parameter = cpu_info[0];
142
143  if (cpu_info[0] >= parameter_end) {
144    char* cpu_string_ptr = cpu_string;
145
146    for (int parameter = 0x80000002; parameter <= parameter_end &&
147         cpu_string_ptr < &cpu_string[sizeof(cpu_string)]; parameter++) {
148      __cpuid(cpu_info, parameter);
149      memcpy(cpu_string_ptr, cpu_info, sizeof(cpu_info));
150      cpu_string_ptr += sizeof(cpu_info);
151    }
152    cpu_brand_.assign(cpu_string, cpu_string_ptr - cpu_string);
153  }
154
155  const int parameter_containing_non_stop_time_stamp_counter = 0x80000007;
156  if (max_parameter >= parameter_containing_non_stop_time_stamp_counter) {
157    __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter);
158    has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
159  }
160#elif defined(ARCH_CPU_ARM_FAMILY)
161  // TODO(piman): Expand this. ARM has a CPUID register, but it's not available
162  // in user mode. /proc/cpuinfo has some information, but it's non standard,
163  // platform-specific, and not accessible from the sandbox.
164  // For some purposes, this first approximation is enough.
165  // crbug.com/313454
166  cpu_brand_.assign("ARM");
167#endif
168}
169
170CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const {
171  if (has_avx()) return AVX;
172  if (has_sse42()) return SSE42;
173  if (has_sse41()) return SSE41;
174  if (has_ssse3()) return SSSE3;
175  if (has_sse3()) return SSE3;
176  if (has_sse2()) return SSE2;
177  if (has_sse()) return SSE;
178  return PENTIUM;
179}
180
181}  // namespace base
182