#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)
extern void __cpuid(int[4], int);
#elif !defined(_WIN32) && !defined(_WIN64)
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 #endif
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace crc_internal {
#if defined(__x86_64__) || defined(_M_X64)
namespace {
enum class Vendor {
kUnknown,
kIntel,
kAmd,
};
Vendor GetVendor() {
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() {
int cpu_info[4];
__cpuid(cpu_info, 1);
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;
if (family == 0x0f) {
family += ext_family;
}
if (family == 0x0f || family == 0x6) {
model_num += (ext_model_num << 4);
}
switch (brand_id) {
case 0: switch (family) {
case 6: switch (model_num) {
case 0x2c: return CpuType::kIntelWestmere;
case 0x2d: return CpuType::kIntelSandybridge;
case 0x3e: return CpuType::kIntelIvybridge;
case 0x3c: case 0x3f: return CpuType::kIntelHaswell;
case 0x4f: case 0x56: return CpuType::kIntelBroadwell;
case 0x55: if ((cpu_info[0] & 0x0f) < 5) { return CpuType::kIntelSkylakeXeon;
} else { return CpuType::kIntelCascadelakeXeon;
}
case 0x5e: return CpuType::kIntelSkylake;
default:
return CpuType::kUnknown;
}
default:
return CpuType::kUnknown;
}
default:
return CpuType::kUnknown;
}
}
CpuType GetAmdCpuType() {
int cpu_info[4];
__cpuid(cpu_info, 1);
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: case 0x1: return CpuType::kAmdNaples;
case 0x30: case 0x31: return CpuType::kAmdRome;
default:
return CpuType::kUnknown;
}
break;
case 0x19:
switch (model_num) {
case 0x0: case 0x1: return CpuType::kAmdMilan;
case 0x10: case 0x11: return CpuType::kAmdGenoa;
case 0x44: return CpuType::kAmdRyzenV3000;
default:
return CpuType::kUnknown;
}
break;
default:
return CpuType::kUnknown;
}
}
}
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() {
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;
switch (implementer) {
case 0x41:
switch (part_number) {
case 0xd0c: return CpuType::kArmNeoverseN1;
case 0xd40: return CpuType::kArmNeoverseV1;
case 0xd49: return CpuType::kArmNeoverseN2;
case 0xd4f: return CpuType::kArmNeoverseV2;
default:
return CpuType::kUnknown;
}
break;
case 0xc0:
switch (part_number) {
case 0xac3: return CpuType::kAmpereSiryn;
default:
return CpuType::kUnknown;
}
break;
default:
return CpuType::kUnknown;
}
}
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
} ABSL_NAMESPACE_END
}