summaryrefslogtreecommitdiff
path: root/abseil-cpp/absl/crc/internal/cpu_detect.cc
diff options
context:
space:
mode:
Diffstat (limited to 'abseil-cpp/absl/crc/internal/cpu_detect.cc')
-rw-r--r--abseil-cpp/absl/crc/internal/cpu_detect.cc262
1 files changed, 262 insertions, 0 deletions
diff --git a/abseil-cpp/absl/crc/internal/cpu_detect.cc b/abseil-cpp/absl/crc/internal/cpu_detect.cc
new file mode 100644
index 0000000..8383808
--- /dev/null
+++ b/abseil-cpp/absl/crc/internal/cpu_detect.cc
@@ -0,0 +1,262 @@
+// Copyright 2022 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/crc/internal/cpu_detect.h"
+
+#include <cstdint>
+#include <string>
+
+#include "absl/base/config.h"
+
+#if defined(__aarch64__) && defined(__linux__)
+#include <asm/hwcap.h>
+#include <sys/auxv.h>
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <intrin.h>
+#endif
+
+#if defined(__x86_64__) || defined(_M_X64)
+#if ABSL_HAVE_BUILTIN(__cpuid)
+// MSVC-equivalent __cpuid intrinsic declaration for clang-like compilers
+// for non-Windows build environments.
+extern void __cpuid(int[4], int);
+#elif !defined(_WIN32) && !defined(_WIN64)
+// MSVC defines this function for us.
+// https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+static void __cpuid(int cpu_info[4], int info_type) {
+ __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));
+}
+#endif // !defined(_WIN32) && !defined(_WIN64)
+#endif // defined(__x86_64__) || defined(_M_X64)
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace crc_internal {
+
+#if defined(__x86_64__) || defined(_M_X64)
+
+namespace {
+
+enum class Vendor {
+ kUnknown,
+ kIntel,
+ kAmd,
+};
+
+Vendor GetVendor() {
+ // Get the vendor string (issue CPUID with eax = 0).
+ int cpu_info[4];
+ __cpuid(cpu_info, 0);
+
+ std::string vendor;
+ vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4);
+ vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4);
+ vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4);
+ if (vendor == "GenuineIntel") {
+ return Vendor::kIntel;
+ } else if (vendor == "AuthenticAMD") {
+ return Vendor::kAmd;
+ } else {
+ return Vendor::kUnknown;
+ }
+}
+
+CpuType GetIntelCpuType() {
+ // To get general information and extended features we send eax = 1 and
+ // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
+ // (See Intel 64 and IA-32 Architectures Software Developer's Manual
+ // Volume 2A: Instruction Set Reference, A-M CPUID).
+ // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
+ // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+ int cpu_info[4];
+ __cpuid(cpu_info, 1);
+
+ // Response in eax bits as follows:
+ // 0-3 (stepping id)
+ // 4-7 (model number),
+ // 8-11 (family code),
+ // 12-13 (processor type),
+ // 16-19 (extended model)
+ // 20-27 (extended family)
+
+ int family = (cpu_info[0] >> 8) & 0x0f;
+ int model_num = (cpu_info[0] >> 4) & 0x0f;
+ int ext_family = (cpu_info[0] >> 20) & 0xff;
+ int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
+
+ int brand_id = cpu_info[1] & 0xff;
+
+ // Process the extended family and model info if necessary
+ if (family == 0x0f) {
+ family += ext_family;
+ }
+
+ if (family == 0x0f || family == 0x6) {
+ model_num += (ext_model_num << 4);
+ }
+
+ switch (brand_id) {
+ case 0: // no brand ID, so parse CPU family/model
+ switch (family) {
+ case 6: // Most PentiumIII processors are in this category
+ switch (model_num) {
+ case 0x2c: // Westmere: Gulftown
+ return CpuType::kIntelWestmere;
+ case 0x2d: // Sandybridge
+ return CpuType::kIntelSandybridge;
+ case 0x3e: // Ivybridge
+ return CpuType::kIntelIvybridge;
+ case 0x3c: // Haswell (client)
+ case 0x3f: // Haswell
+ return CpuType::kIntelHaswell;
+ case 0x4f: // Broadwell
+ case 0x56: // BroadwellDE
+ return CpuType::kIntelBroadwell;
+ case 0x55: // Skylake Xeon
+ if ((cpu_info[0] & 0x0f) < 5) { // stepping < 5 is skylake
+ return CpuType::kIntelSkylakeXeon;
+ } else { // stepping >= 5 is cascadelake
+ return CpuType::kIntelCascadelakeXeon;
+ }
+ case 0x5e: // Skylake (client)
+ return CpuType::kIntelSkylake;
+ default:
+ return CpuType::kUnknown;
+ }
+ default:
+ return CpuType::kUnknown;
+ }
+ default:
+ return CpuType::kUnknown;
+ }
+}
+
+CpuType GetAmdCpuType() {
+ // To get general information and extended features we send eax = 1 and
+ // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
+ // (See Intel 64 and IA-32 Architectures Software Developer's Manual
+ // Volume 2A: Instruction Set Reference, A-M CPUID).
+ // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
+ int cpu_info[4];
+ __cpuid(cpu_info, 1);
+
+ // Response in eax bits as follows:
+ // 0-3 (stepping id)
+ // 4-7 (model number),
+ // 8-11 (family code),
+ // 12-13 (processor type),
+ // 16-19 (extended model)
+ // 20-27 (extended family)
+
+ int family = (cpu_info[0] >> 8) & 0x0f;
+ int model_num = (cpu_info[0] >> 4) & 0x0f;
+ int ext_family = (cpu_info[0] >> 20) & 0xff;
+ int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
+
+ if (family == 0x0f) {
+ family += ext_family;
+ model_num += (ext_model_num << 4);
+ }
+
+ switch (family) {
+ case 0x17:
+ switch (model_num) {
+ case 0x0: // Stepping Ax
+ case 0x1: // Stepping Bx
+ return CpuType::kAmdNaples;
+ case 0x30: // Stepping Ax
+ case 0x31: // Stepping Bx
+ return CpuType::kAmdRome;
+ default:
+ return CpuType::kUnknown;
+ }
+ break;
+ case 0x19:
+ switch (model_num) {
+ case 0x1: // Stepping B0
+ return CpuType::kAmdMilan;
+ default:
+ return CpuType::kUnknown;
+ }
+ break;
+ default:
+ return CpuType::kUnknown;
+ }
+}
+
+} // namespace
+
+CpuType GetCpuType() {
+ switch (GetVendor()) {
+ case Vendor::kIntel:
+ return GetIntelCpuType();
+ case Vendor::kAmd:
+ return GetAmdCpuType();
+ default:
+ return CpuType::kUnknown;
+ }
+}
+
+bool SupportsArmCRC32PMULL() { return false; }
+
+#elif defined(__aarch64__) && defined(__linux__)
+
+#ifndef HWCAP_CPUID
+#define HWCAP_CPUID (1 << 11)
+#endif
+
+#define ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \
+ asm("mrs %0, " #id : "=r"(val))
+
+CpuType GetCpuType() {
+ // MIDR_EL1 is not visible to EL0, however the access will be emulated by
+ // linux if AT_HWCAP has HWCAP_CPUID set.
+ //
+ // This method will be unreliable on heterogeneous computing systems (ex:
+ // big.LITTLE) since the value of MIDR_EL1 will change based on the calling
+ // thread.
+ uint64_t hwcaps = getauxval(AT_HWCAP);
+ if (hwcaps & HWCAP_CPUID) {
+ uint64_t midr = 0;
+ ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
+ uint32_t implementer = (midr >> 24) & 0xff;
+ uint32_t part_number = (midr >> 4) & 0xfff;
+ if (implementer == 0x41 && part_number == 0xd0c) {
+ return CpuType::kArmNeoverseN1;
+ }
+ }
+ return CpuType::kUnknown;
+}
+
+bool SupportsArmCRC32PMULL() {
+ uint64_t hwcaps = getauxval(AT_HWCAP);
+ return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL);
+}
+
+#else
+
+CpuType GetCpuType() { return CpuType::kUnknown; }
+
+bool SupportsArmCRC32PMULL() { return false; }
+
+#endif
+
+} // namespace crc_internal
+ABSL_NAMESPACE_END
+} // namespace absl