| OLD | NEW |
| (Empty) |
| 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 <stdlib.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 | |
| 12 #include "base/basictypes.h" | |
| 13 #include "base/strings/string_piece.h" | |
| 14 #include "build/build_config.h" | |
| 15 | |
| 16 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) | |
| 17 #include "base/files/file_util.h" | |
| 18 #include "base/lazy_instance.h" | |
| 19 #endif | |
| 20 | |
| 21 #if defined(ARCH_CPU_X86_FAMILY) | |
| 22 #if defined(_MSC_VER) | |
| 23 #include <intrin.h> | |
| 24 #include <immintrin.h> // For _xgetbv() | |
| 25 #endif | |
| 26 #endif | |
| 27 | |
| 28 namespace base { | |
| 29 | |
| 30 CPU::CPU() | |
| 31 : signature_(0), | |
| 32 type_(0), | |
| 33 family_(0), | |
| 34 model_(0), | |
| 35 stepping_(0), | |
| 36 ext_model_(0), | |
| 37 ext_family_(0), | |
| 38 has_mmx_(false), | |
| 39 has_sse_(false), | |
| 40 has_sse2_(false), | |
| 41 has_sse3_(false), | |
| 42 has_ssse3_(false), | |
| 43 has_sse41_(false), | |
| 44 has_sse42_(false), | |
| 45 has_avx_(false), | |
| 46 has_avx_hardware_(false), | |
| 47 has_aesni_(false), | |
| 48 has_non_stop_time_stamp_counter_(false), | |
| 49 has_broken_neon_(false), | |
| 50 cpu_vendor_("unknown") { | |
| 51 Initialize(); | |
| 52 } | |
| 53 | |
| 54 namespace { | |
| 55 | |
| 56 #if defined(ARCH_CPU_X86_FAMILY) | |
| 57 #ifndef _MSC_VER | |
| 58 | |
| 59 #if defined(__pic__) && defined(__i386__) | |
| 60 | |
| 61 void __cpuid(int cpu_info[4], int info_type) { | |
| 62 __asm__ volatile ( | |
| 63 "mov %%ebx, %%edi\n" | |
| 64 "cpuid\n" | |
| 65 "xchg %%edi, %%ebx\n" | |
| 66 : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) | |
| 67 : "a"(info_type) | |
| 68 ); | |
| 69 } | |
| 70 | |
| 71 #else | |
| 72 | |
| 73 void __cpuid(int cpu_info[4], int info_type) { | |
| 74 __asm__ volatile ( | |
| 75 "cpuid \n\t" | |
| 76 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) | |
| 77 : "a"(info_type) | |
| 78 ); | |
| 79 } | |
| 80 | |
| 81 #endif | |
| 82 | |
| 83 // _xgetbv returns the value of an Intel Extended Control Register (XCR). | |
| 84 // Currently only XCR0 is defined by Intel so |xcr| should always be zero. | |
| 85 uint64 _xgetbv(uint32 xcr) { | |
| 86 uint32 eax, edx; | |
| 87 | |
| 88 __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr)); | |
| 89 return (static_cast<uint64>(edx) << 32) | eax; | |
| 90 } | |
| 91 | |
| 92 #endif // !_MSC_VER | |
| 93 #endif // ARCH_CPU_X86_FAMILY | |
| 94 | |
| 95 #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) | |
| 96 class LazyCpuInfoValue { | |
| 97 public: | |
| 98 LazyCpuInfoValue() : has_broken_neon_(false) { | |
| 99 // This function finds the value from /proc/cpuinfo under the key "model | |
| 100 // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7 | |
| 101 // and later for arm64) and is shown once per CPU. "Processor" is used in | |
| 102 // earler versions and is shown only once at the top of /proc/cpuinfo | |
| 103 // regardless of the number CPUs. | |
| 104 const char kModelNamePrefix[] = "model name\t: "; | |
| 105 const char kProcessorPrefix[] = "Processor\t: "; | |
| 106 | |
| 107 // This function also calculates whether we believe that this CPU has a | |
| 108 // broken NEON unit based on these fields from cpuinfo: | |
| 109 unsigned implementer = 0, architecture = 0, variant = 0, part = 0, | |
| 110 revision = 0; | |
| 111 const struct { | |
| 112 const char key[17]; | |
| 113 unsigned *result; | |
| 114 } kUnsignedValues[] = { | |
| 115 {"CPU implementer", &implementer}, | |
| 116 {"CPU architecture", &architecture}, | |
| 117 {"CPU variant", &variant}, | |
| 118 {"CPU part", &part}, | |
| 119 {"CPU revision", &revision}, | |
| 120 }; | |
| 121 | |
| 122 std::string contents; | |
| 123 ReadFileToString(FilePath("/proc/cpuinfo"), &contents); | |
| 124 DCHECK(!contents.empty()); | |
| 125 if (contents.empty()) { | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 std::istringstream iss(contents); | |
| 130 std::string line; | |
| 131 while (std::getline(iss, line)) { | |
| 132 if (brand_.empty() && | |
| 133 (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 || | |
| 134 line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) { | |
| 135 brand_.assign(line.substr(strlen(kModelNamePrefix))); | |
| 136 } | |
| 137 | |
| 138 for (size_t i = 0; i < arraysize(kUnsignedValues); i++) { | |
| 139 const char *key = kUnsignedValues[i].key; | |
| 140 const size_t len = strlen(key); | |
| 141 | |
| 142 if (line.compare(0, len, key) == 0 && | |
| 143 line.size() >= len + 1 && | |
| 144 (line[len] == '\t' || line[len] == ' ' || line[len] == ':')) { | |
| 145 size_t colon_pos = line.find(':', len); | |
| 146 if (colon_pos == std::string::npos) { | |
| 147 continue; | |
| 148 } | |
| 149 | |
| 150 const StringPiece line_sp(line); | |
| 151 StringPiece value_sp = line_sp.substr(colon_pos + 1); | |
| 152 while (!value_sp.empty() && | |
| 153 (value_sp[0] == ' ' || value_sp[0] == '\t')) { | |
| 154 value_sp = value_sp.substr(1); | |
| 155 } | |
| 156 | |
| 157 // The string may have leading "0x" or not, so we use strtoul to | |
| 158 // handle that. | |
| 159 char *endptr; | |
| 160 std::string value(value_sp.as_string()); | |
| 161 unsigned long int result = strtoul(value.c_str(), &endptr, 0); | |
| 162 if (*endptr == 0 && result <= UINT_MAX) { | |
| 163 *kUnsignedValues[i].result = result; | |
| 164 } | |
| 165 } | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 has_broken_neon_ = | |
| 170 implementer == 0x51 && | |
| 171 architecture == 7 && | |
| 172 variant == 1 && | |
| 173 part == 0x4d && | |
| 174 revision == 0; | |
| 175 } | |
| 176 | |
| 177 const std::string& brand() const { return brand_; } | |
| 178 bool has_broken_neon() const { return has_broken_neon_; } | |
| 179 | |
| 180 private: | |
| 181 std::string brand_; | |
| 182 bool has_broken_neon_; | |
| 183 DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue); | |
| 184 }; | |
| 185 | |
| 186 base::LazyInstance<LazyCpuInfoValue>::Leaky g_lazy_cpuinfo = | |
| 187 LAZY_INSTANCE_INITIALIZER; | |
| 188 | |
| 189 #endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || | |
| 190 // defined(OS_LINUX)) | |
| 191 | |
| 192 } // anonymous namespace | |
| 193 | |
| 194 void CPU::Initialize() { | |
| 195 #if defined(ARCH_CPU_X86_FAMILY) | |
| 196 int cpu_info[4] = {-1}; | |
| 197 char cpu_string[48]; | |
| 198 | |
| 199 // __cpuid with an InfoType argument of 0 returns the number of | |
| 200 // valid Ids in CPUInfo[0] and the CPU identification string in | |
| 201 // the other three array elements. The CPU identification string is | |
| 202 // not in linear order. The code below arranges the information | |
| 203 // in a human readable form. The human readable order is CPUInfo[1] | | |
| 204 // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped | |
| 205 // before using memcpy to copy these three array elements to cpu_string. | |
| 206 __cpuid(cpu_info, 0); | |
| 207 int num_ids = cpu_info[0]; | |
| 208 std::swap(cpu_info[2], cpu_info[3]); | |
| 209 memcpy(cpu_string, &cpu_info[1], 3 * sizeof(cpu_info[1])); | |
| 210 cpu_vendor_.assign(cpu_string, 3 * sizeof(cpu_info[1])); | |
| 211 | |
| 212 // Interpret CPU feature information. | |
| 213 if (num_ids > 0) { | |
| 214 __cpuid(cpu_info, 1); | |
| 215 signature_ = cpu_info[0]; | |
| 216 stepping_ = cpu_info[0] & 0xf; | |
| 217 model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0); | |
| 218 family_ = (cpu_info[0] >> 8) & 0xf; | |
| 219 type_ = (cpu_info[0] >> 12) & 0x3; | |
| 220 ext_model_ = (cpu_info[0] >> 16) & 0xf; | |
| 221 ext_family_ = (cpu_info[0] >> 20) & 0xff; | |
| 222 has_mmx_ = (cpu_info[3] & 0x00800000) != 0; | |
| 223 has_sse_ = (cpu_info[3] & 0x02000000) != 0; | |
| 224 has_sse2_ = (cpu_info[3] & 0x04000000) != 0; | |
| 225 has_sse3_ = (cpu_info[2] & 0x00000001) != 0; | |
| 226 has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; | |
| 227 has_sse41_ = (cpu_info[2] & 0x00080000) != 0; | |
| 228 has_sse42_ = (cpu_info[2] & 0x00100000) != 0; | |
| 229 has_avx_hardware_ = | |
| 230 (cpu_info[2] & 0x10000000) != 0; | |
| 231 // AVX instructions will generate an illegal instruction exception unless | |
| 232 // a) they are supported by the CPU, | |
| 233 // b) XSAVE is supported by the CPU and | |
| 234 // c) XSAVE is enabled by the kernel. | |
| 235 // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled | |
| 236 // | |
| 237 // In addition, we have observed some crashes with the xgetbv instruction | |
| 238 // even after following Intel's example code. (See crbug.com/375968.) | |
| 239 // Because of that, we also test the XSAVE bit because its description in | |
| 240 // the CPUID documentation suggests that it signals xgetbv support. | |
| 241 has_avx_ = | |
| 242 has_avx_hardware_ && | |
| 243 (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && | |
| 244 (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && | |
| 245 (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */; | |
| 246 has_aesni_ = (cpu_info[2] & 0x02000000) != 0; | |
| 247 } | |
| 248 | |
| 249 // Get the brand string of the cpu. | |
| 250 __cpuid(cpu_info, 0x80000000); | |
| 251 const int parameter_end = 0x80000004; | |
| 252 int max_parameter = cpu_info[0]; | |
| 253 | |
| 254 if (cpu_info[0] >= parameter_end) { | |
| 255 char* cpu_string_ptr = cpu_string; | |
| 256 | |
| 257 for (int parameter = 0x80000002; parameter <= parameter_end && | |
| 258 cpu_string_ptr < &cpu_string[sizeof(cpu_string)]; parameter++) { | |
| 259 __cpuid(cpu_info, parameter); | |
| 260 memcpy(cpu_string_ptr, cpu_info, sizeof(cpu_info)); | |
| 261 cpu_string_ptr += sizeof(cpu_info); | |
| 262 } | |
| 263 cpu_brand_.assign(cpu_string, cpu_string_ptr - cpu_string); | |
| 264 } | |
| 265 | |
| 266 const int parameter_containing_non_stop_time_stamp_counter = 0x80000007; | |
| 267 if (max_parameter >= parameter_containing_non_stop_time_stamp_counter) { | |
| 268 __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter); | |
| 269 has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; | |
| 270 } | |
| 271 #elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) | |
| 272 cpu_brand_.assign(g_lazy_cpuinfo.Get().brand()); | |
| 273 has_broken_neon_ = g_lazy_cpuinfo.Get().has_broken_neon(); | |
| 274 #endif | |
| 275 } | |
| 276 | |
| 277 CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const { | |
| 278 if (has_avx()) return AVX; | |
| 279 if (has_sse42()) return SSE42; | |
| 280 if (has_sse41()) return SSE41; | |
| 281 if (has_ssse3()) return SSSE3; | |
| 282 if (has_sse3()) return SSE3; | |
| 283 if (has_sse2()) return SSE2; | |
| 284 if (has_sse()) return SSE; | |
| 285 return PENTIUM; | |
| 286 } | |
| 287 | |
| 288 } // namespace base | |
| OLD | NEW |