aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Chatelet <gchatelet@google.com>2020-10-09 17:20:25 +0200
committerGitHub <noreply@github.com>2020-10-09 15:20:25 +0000
commit4795373db20ce13433d8895811505e4f8aea428b (patch)
treef38d35ba5d468b2107e9c88ba5840fd55f834ebc
parent22a5362e11120fbf4735d3cddf3575a52c538806 (diff)
downloadcpu_features-4795373db20ce13433d8895811505e4f8aea428b.tar.gz
Fix SSE detection on non-AVX CPUs (#135)
Fixes #4. This is based on #115 with a few modifications: - Removed use of __builtin_cpu_supports since it relies on cpuid and doesn't improve on the current situation, - Added detection for all of sse, sse2, sse3, ssse3, sse4_1 and sse4_2, - Added tests for Atom, Nehalem, and P3 processors, Thx to @gadoofou87 for providing the original PR. It also removes the need for #92 * Fix SSE detection on non-AVX CPUs * Fixes typo * Mock OSX sysctlbyname in tests * Also update other tests * FakeCpu is reset between each tests * Fix conflicting name on Windows * Disable pre AVX cpu sse detection tests on Windows * Guard OS specific code with macros * Fix missing import for tests * Fix wrong function prototype * Fix wrong mocking of P3 on Windows * Completely guard OS specific parts in x86 tests * Store DWORD instead unsigned long for x86 tests
-rw-r--r--CMakeLists.txt13
-rw-r--r--include/cpu_features_macros.h4
-rw-r--r--include/internal/cpuid_x86.h3
-rw-r--r--src/cpuinfo_x86.c166
-rw-r--r--test/CMakeLists.txt6
-rw-r--r--test/cpuinfo_x86_test.cc274
6 files changed, 428 insertions, 38 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 545f38f..0c71ea4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -91,6 +91,11 @@ macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
endif()
endmacro()
+if(UNIX AND PROCESSOR_IS_X86)
+ check_include_file(sys/utsname.h HAVE_UTSNAME_H)
+endif()
+
+
#
# library : utils
#
@@ -148,6 +153,14 @@ set_property(TARGET cpu_features PROPERTY POSITION_INDEPENDENT_CODE ${BUILD_PIC}
target_include_directories(cpu_features
PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpu_features>
)
+if(PROCESSOR_IS_X86)
+ if(HAVE_UTSNAME_H)
+ target_compile_definitions(cpu_features PRIVATE HAVE_UTSNAME_H)
+ endif()
+ if(APPLE)
+ target_compile_definitions(cpu_features PRIVATE HAVE_SYSCTLBYNAME)
+ endif()
+endif()
add_library(CpuFeature::cpu_features ALIAS cpu_features)
#
diff --git a/include/cpu_features_macros.h b/include/cpu_features_macros.h
index fae9f70..86c4968 100644
--- a/include/cpu_features_macros.h
+++ b/include/cpu_features_macros.h
@@ -79,6 +79,10 @@
#define CPU_FEATURES_OS_WINDOWS
#endif
+#if (defined(__apple__) || defined(__APPLE__) || defined(__MACH__))
+#define CPU_FEATURES_OS_DARWIN
+#endif
+
////////////////////////////////////////////////////////////////////////////////
// Compilers
////////////////////////////////////////////////////////////////////////////////
diff --git a/include/internal/cpuid_x86.h b/include/internal/cpuid_x86.h
index 754ca38..6f53875 100644
--- a/include/internal/cpuid_x86.h
+++ b/include/internal/cpuid_x86.h
@@ -26,7 +26,8 @@ typedef struct {
uint32_t eax, ebx, ecx, edx;
} Leaf;
-Leaf CpuIdEx(uint32_t leaf_id, int ecx);
+// Returns the result of a call to the cpuid instruction.
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx);
// Returns the eax value of the XCR0 register.
uint32_t GetXCR0Eax(void);
diff --git a/src/cpuinfo_x86.c b/src/cpuinfo_x86.c
index d5edd30..1a10cc2 100644
--- a/src/cpuinfo_x86.c
+++ b/src/cpuinfo_x86.c
@@ -25,6 +25,21 @@
#error "Cannot compile cpuinfo_x86 on a non x86 platform."
#endif
+// The following includes are necessary to provide SSE detections on pre-AVX
+// microarchitectures.
+#if defined(CPU_FEATURES_OS_WINDOWS)
+#include <windows.h> // IsProcessorFeaturePresent
+#elif defined(HAVE_UTSNAME_H)
+#include <sys/utsname.h>
+
+#include "internal/filesystem.h" // Needed to parse /proc/cpuinfo
+#include "internal/stack_line_reader.h" // Needed to parse /proc/cpuinfo
+#include "internal/string_view.h" // Needed to parse /proc/cpuinfo
+#if defined(HAVE_SYSCTLBYNAME)
+#include <sys/sysctl.h>
+#endif // HAVE_SYSCTLBYNAME
+#endif // HAVE_UTSNAME_H
+
////////////////////////////////////////////////////////////////////////////////
// Definitions for CpuId and GetXCR0Eax.
////////////////////////////////////////////////////////////////////////////////
@@ -35,7 +50,7 @@
#include <cpuid.h>
-Leaf CpuIdEx(uint32_t leaf_id, int ecx) {
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
Leaf leaf;
__cpuid_count(leaf_id, ecx, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
return leaf;
@@ -55,7 +70,7 @@ uint32_t GetXCR0Eax(void) {
#include <immintrin.h>
#include <intrin.h> // For __cpuidex()
-Leaf CpuIdEx(uint32_t leaf_id, int ecx) {
+Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
Leaf leaf;
int data[4];
__cpuidex(data, leaf_id, ecx);
@@ -72,13 +87,13 @@ uint32_t GetXCR0Eax(void) { return (uint32_t)_xgetbv(0); }
#error "Unsupported compiler, x86 cpuid requires either GCC, Clang or MSVC."
#endif
-static Leaf CpuId(uint32_t leaf_id) { return CpuIdEx(leaf_id, 0); }
+static Leaf CpuId(uint32_t leaf_id) { return GetCpuidLeaf(leaf_id, 0); }
static const Leaf kEmptyLeaf;
static Leaf SafeCpuIdEx(uint32_t max_cpuid_leaf, uint32_t leaf_id, int ecx) {
if (leaf_id <= max_cpuid_leaf) {
- return CpuIdEx(leaf_id, ecx);
+ return GetCpuidLeaf(leaf_id, ecx);
} else {
return kEmptyLeaf;
}
@@ -1082,27 +1097,125 @@ static void ParseLeaf4(const int max_cpuid_leaf, CacheInfo* info) {
// Internal structure to hold the OS support for vector operations.
// Avoid to recompute them since each call to cpuid is ~100 cycles.
typedef struct {
- bool have_sse;
+ bool have_sse_via_os;
+ bool have_sse_via_cpuid;
bool have_avx;
bool have_avx512;
bool have_amx;
} OsSupport;
+static const OsSupport kEmptyOsSupport;
+
+static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) {
+ const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
+ const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
+ const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
+ const bool have_xcr0 = have_xsave && have_osxsave;
+
+ OsSupport os_support = kEmptyOsSupport;
+
+ if (have_xcr0) {
+ // AVX capable cpu will expose XCR0.
+ const uint32_t xcr0_eax = GetXCR0Eax();
+ os_support.have_sse_via_cpuid = HasXmmOsXSave(xcr0_eax);
+ os_support.have_avx = HasYmmOsXSave(xcr0_eax);
+ os_support.have_avx512 = HasZmmOsXSave(xcr0_eax);
+ os_support.have_amx = HasTmmOsXSave(xcr0_eax);
+ } else {
+ // Atom based or older cpus need to ask the OS for sse support.
+ os_support.have_sse_via_os = true;
+ }
+
+ return os_support;
+}
+
+#if defined(CPU_FEATURES_OS_WINDOWS)
+#if defined(CPU_FEATURES_MOCK_CPUID_X86)
+extern bool GetWindowsIsProcessorFeaturePresent(DWORD);
+#else // CPU_FEATURES_MOCK_CPUID_X86
+static bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ return IsProcessorFeaturePresent(ProcessorFeature);
+}
+#endif
+#endif // CPU_FEATURES_OS_WINDOWS
+
+#if defined(CPU_FEATURES_OS_DARWIN) && defined(HAVE_SYSCTLBYNAME)
+#if defined(CPU_FEATURES_MOCK_CPUID_X86)
+extern bool GetDarwinSysCtlByName(const char*);
+#else // CPU_FEATURES_MOCK_CPUID_X86
+static bool GetDarwinSysCtlByName(const char* name) {
+ int enabled;
+ size_t enabled_len = sizeof(enabled);
+ const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0);
+ return failure ? false : enabled;
+}
+#endif
+#endif // CPU_FEATURES_OS_DARWIN && HAVE_SYSCTLBYNAME
+
+static void DetectSseViaOs(X86Features* features) {
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
+ features->sse =
+ GetWindowsIsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE);
+ features->sse2 =
+ GetWindowsIsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+ features->sse3 =
+ GetWindowsIsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE);
+#elif defined(HAVE_UTSNAME_H)
+ struct utsname buf;
+ uname(&buf);
+#if defined(CPU_FEATURES_OS_DARWIN) && defined(HAVE_SYSCTLBYNAME)
+ if (CpuFeatures_StringView_IsEquals(str(buf.sysname), str("Darwin"))) {
+ // Handling Darwin platform through sysctlbyname when available.
+ features->sse = GetDarwinSysCtlByName("hw.optional.sse");
+ features->sse2 = GetDarwinSysCtlByName("hw.optional.sse2");
+ features->sse3 = GetDarwinSysCtlByName("hw.optional.sse3");
+ features->ssse3 = GetDarwinSysCtlByName("hw.optional.supplementalsse3");
+ features->sse4_1 = GetDarwinSysCtlByName("hw.optional.sse4_1");
+ features->sse4_2 = GetDarwinSysCtlByName("hw.optional.sse4_2");
+ }
+#elif defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+ if (CpuFeatures_StringView_IsEquals(str(buf.sysname), str("Linux"))) {
+ // Handling Linux platform through /proc/cpuinfo when available.
+ const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
+ if (fd >= 0) {
+ StackLineReader reader;
+ StackLineReader_Initialize(&reader, fd);
+ for (;;) {
+ const LineResult result = StackLineReader_NextLine(&reader);
+ const StringView line = result.line;
+ StringView key, value;
+ if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
+ if (CpuFeatures_StringView_IsEquals(key, str("flags"))) {
+ features->sse = CpuFeatures_StringView_HasWord(value, "sse");
+ features->sse2 = CpuFeatures_StringView_HasWord(value, "sse2");
+ features->sse3 = CpuFeatures_StringView_HasWord(value, "sse3");
+ features->ssse3 = CpuFeatures_StringView_HasWord(value, "ssse3");
+ features->sse4_1 = CpuFeatures_StringView_HasWord(value, "sse4_1");
+ features->sse4_2 = CpuFeatures_StringView_HasWord(value, "sse4_2");
+ break;
+ }
+ }
+ if (result.eof) break;
+ }
+ CpuFeatures_CloseFile(fd);
+ }
+ }
+#else // CPU_FEATURES_OS_DARWIN || CPU_FEATURES_OS_LINUX_OR_ANDROID
+#error "Unsupported fallback detection of SSE OS support."
+#endif
+#else // HAVE_UTSNAME_H
+#error "Unsupported fallback detection of SSE OS support."
+#endif
+}
+
// Reference https://en.wikipedia.org/wiki/CPUID.
-static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
- OsSupport* os_support) {
+static void ParseCpuId(const uint32_t max_cpuid_leaf,
+ const OsSupport os_support, X86Info* info) {
const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
const Leaf leaf_7_1 = SafeCpuIdEx(max_cpuid_leaf, 7, 1);
- const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
- const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
- const uint32_t xcr0_eax = (have_xsave && have_osxsave) ? GetXCR0Eax() : 0;
- os_support->have_sse = HasXmmOsXSave(xcr0_eax);
- os_support->have_avx = HasYmmOsXSave(xcr0_eax);
- os_support->have_avx512 = HasZmmOsXSave(xcr0_eax);
- os_support->have_amx = HasTmmOsXSave(xcr0_eax);
-
const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
@@ -1142,7 +1255,9 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
features->vaes = IsBitSet(leaf_7.ecx, 9);
features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
- if (os_support->have_sse) {
+ if (os_support.have_sse_via_os) {
+ DetectSseViaOs(features);
+ } else if (os_support.have_sse_via_cpuid) {
features->sse = IsBitSet(leaf_1.edx, 25);
features->sse2 = IsBitSet(leaf_1.edx, 26);
features->sse3 = IsBitSet(leaf_1.ecx, 0);
@@ -1151,13 +1266,13 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
}
- if (os_support->have_avx) {
+ if (os_support.have_avx) {
features->fma3 = IsBitSet(leaf_1.ecx, 12);
features->avx = IsBitSet(leaf_1.ecx, 28);
features->avx2 = IsBitSet(leaf_7.ebx, 5);
}
- if (os_support->have_avx512) {
+ if (os_support.have_avx512) {
features->avx512f = IsBitSet(leaf_7.ebx, 16);
features->avx512cd = IsBitSet(leaf_7.ebx, 28);
features->avx512er = IsBitSet(leaf_7.ebx, 27);
@@ -1179,7 +1294,7 @@ static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info,
features->avx512_vp2intersect = IsBitSet(leaf_7.edx, 8);
}
- if (os_support->have_amx) {
+ if (os_support.have_amx) {
features->amx_bf16 = IsBitSet(leaf_7.edx, 22);
features->amx_tile = IsBitSet(leaf_7.edx, 24);
features->amx_int8 = IsBitSet(leaf_7.edx, 25);
@@ -1195,7 +1310,7 @@ static void ParseExtraAMDCpuId(X86Info* info, OsSupport os_support) {
X86Features* const features = &info->features;
- if (os_support.have_sse) {
+ if (os_support.have_sse_via_cpuid) {
features->sse4a = IsBitSet(leaf_80000001.ecx, 6);
}
@@ -1205,22 +1320,21 @@ static void ParseExtraAMDCpuId(X86Info* info, OsSupport os_support) {
}
static const X86Info kEmptyX86Info;
-static const OsSupport kEmptyOsSupport;
static const CacheInfo kEmptyCacheInfo;
X86Info GetX86Info(void) {
X86Info info = kEmptyX86Info;
- OsSupport os_support = kEmptyOsSupport;
const Leaf leaf_0 = CpuId(0);
const bool is_intel = IsVendor(leaf_0, "GenuineIntel");
const bool is_amd = IsVendor(leaf_0, "AuthenticAMD");
SetVendor(leaf_0, info.vendor);
if (is_intel || is_amd) {
const uint32_t max_cpuid_leaf = leaf_0.eax;
- ParseCpuId(max_cpuid_leaf, &info, &os_support);
- }
- if (is_amd) {
- ParseExtraAMDCpuId(&info, os_support);
+ const OsSupport os_support = CheckOsSupport(max_cpuid_leaf);
+ ParseCpuId(max_cpuid_leaf, os_support, &info);
+ if (is_amd) {
+ ParseExtraAMDCpuId(&info, os_support);
+ }
}
return info;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index eb67ac0..3f267bd 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -55,6 +55,12 @@ add_test(NAME unix_features_aggregator_test COMMAND unix_features_aggregator_tes
if(PROCESSOR_IS_X86)
add_executable(cpuinfo_x86_test cpuinfo_x86_test.cc ../src/cpuinfo_x86.c)
target_compile_definitions(cpuinfo_x86_test PUBLIC CPU_FEATURES_MOCK_CPUID_X86)
+ if(HAVE_UTSNAME_H)
+ target_compile_definitions(cpuinfo_x86_test PRIVATE HAVE_UTSNAME_H)
+ endif()
+ if(APPLE)
+ target_compile_definitions(cpuinfo_x86_test PRIVATE HAVE_SYSCTLBYNAME)
+ endif()
target_link_libraries(cpuinfo_x86_test all_libraries)
add_test(NAME cpuinfo_x86_test COMMAND cpuinfo_x86_test)
endif()
diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc
index e11a0ba..9dae540 100644
--- a/test/cpuinfo_x86_test.cc
+++ b/test/cpuinfo_x86_test.cc
@@ -17,7 +17,12 @@
#include <cassert>
#include <cstdio>
#include <map>
+#include <set>
+#if defined(CPU_FEATURES_OS_WINDOWS)
+#include <windows.h> // IsProcessorFeaturePresent
+#endif // CPU_FEATURES_OS_WINDOWS
+#include "filesystem_for_testing.h"
#include "gtest/gtest.h"
#include "internal/cpuid_x86.h"
@@ -25,7 +30,7 @@ namespace cpu_features {
class FakeCpu {
public:
- Leaf CpuIdEx(uint32_t leaf_id, int ecx) const {
+ Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) const {
const auto itr = cpuid_leaves_.find(std::make_pair(leaf_id, ecx));
if (itr != cpuid_leaves_.end()) {
return itr->second;
@@ -43,22 +48,66 @@ class FakeCpu {
xcr0_eax_ = os_backups_extended_registers ? -1 : 0;
}
+#if defined(CPU_FEATURES_OS_DARWIN)
+ bool GetDarwinSysCtlByName(std::string name) const {
+ return darwin_sysctlbyname_.count(name);
+ }
+
+ void SetDarwinSysCtlByName(std::string name) {
+ darwin_sysctlbyname_.insert(name);
+ }
+#endif // CPU_FEATURES_OS_DARWIN
+
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ return windows_isprocessorfeaturepresent_.count(ProcessorFeature);
+ }
+
+ void SetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ windows_isprocessorfeaturepresent_.insert(ProcessorFeature);
+ }
+#endif // CPU_FEATURES_OS_WINDOWS
+
private:
std::map<std::pair<uint32_t, int>, Leaf> cpuid_leaves_;
+#if defined(CPU_FEATURES_OS_DARWIN)
+ std::set<std::string> darwin_sysctlbyname_;
+#endif // CPU_FEATURES_OS_DARWIN
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ std::set<DWORD> windows_isprocessorfeaturepresent_;
+#endif // CPU_FEATURES_OS_WINDOWS
uint32_t xcr0_eax_;
};
-auto* g_fake_cpu = new FakeCpu();
+FakeCpu* g_fake_cpu = nullptr;
-extern "C" Leaf CpuIdEx(uint32_t leaf_id, int ecx) {
- return g_fake_cpu->CpuIdEx(leaf_id, ecx);
+extern "C" Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) {
+ return g_fake_cpu->GetCpuidLeaf(leaf_id, ecx);
}
extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); }
+#if defined(CPU_FEATURES_OS_DARWIN)
+extern "C" bool GetDarwinSysCtlByName(const char* name) {
+ return g_fake_cpu->GetDarwinSysCtlByName(name);
+}
+#endif // CPU_FEATURES_OS_DARWIN
+
+#if defined(CPU_FEATURES_OS_WINDOWS)
+extern "C" bool GetWindowsIsProcessorFeaturePresent(DWORD ProcessorFeature) {
+ return g_fake_cpu->GetWindowsIsProcessorFeaturePresent(ProcessorFeature);
+}
+#endif // CPU_FEATURES_OS_WINDOWS
+
namespace {
-TEST(CpuidX86Test, SandyBridge) {
+class CpuidX86Test : public ::testing::Test {
+ protected:
+ void SetUp() override { g_fake_cpu = new FakeCpu(); }
+ void TearDown() override { delete g_fake_cpu; }
+};
+
+TEST_F(CpuidX86Test, SandyBridge) {
g_fake_cpu->SetOsBackupsExtendedRegisters(true);
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
@@ -104,7 +153,7 @@ TEST(CpuidX86Test, SandyBridge) {
const int KiB = 1024;
const int MiB = 1024 * KiB;
-TEST(CpuidX86Test, SandyBridgeTestOsSupport) {
+TEST_F(CpuidX86Test, SandyBridgeTestOsSupport) {
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000206A6, 0x00100800, 0x1F9AE3BF, 0xBFEBFBFF}},
@@ -118,7 +167,7 @@ TEST(CpuidX86Test, SandyBridgeTestOsSupport) {
EXPECT_TRUE(GetX86Info().features.avx);
}
-TEST(CpuidX86Test, SkyLake) {
+TEST_F(CpuidX86Test, SkyLake) {
g_fake_cpu->SetOsBackupsExtendedRegisters(true);
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
@@ -133,7 +182,7 @@ TEST(CpuidX86Test, SkyLake) {
EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_SKL);
}
-TEST(CpuidX86Test, Branding) {
+TEST_F(CpuidX86Test, Branding) {
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
@@ -149,7 +198,7 @@ TEST(CpuidX86Test, Branding) {
EXPECT_STREQ(brand_string, "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz");
}
-TEST(CpuidX86Test, KabyLakeCache) {
+TEST_F(CpuidX86Test, KabyLakeCache) {
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
@@ -198,7 +247,7 @@ TEST(CpuidX86Test, KabyLakeCache) {
EXPECT_EQ(info.levels[3].partitioning, 1);
}
-TEST(CpuidX86Test, HSWCache) {
+TEST_F(CpuidX86Test, HSWCache) {
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x00000016, 0x756E6547, 0x6C65746E, 0x49656E69}},
{{0x00000001, 0}, Leaf{0x000406E3, 0x00100800, 0x7FFAFBBF, 0xBFEBFBFF}},
@@ -246,8 +295,9 @@ TEST(CpuidX86Test, HSWCache) {
EXPECT_EQ(info.levels[3].tlb_entries, 8192);
EXPECT_EQ(info.levels[3].partitioning, 1);
}
+
// http://users.atw.hu/instlatx64/AuthenticAMD0630F81_K15_Godavari_CPUID.txt
-TEST(CpuidX86Test, AMD_K15) {
+TEST_F(CpuidX86Test, AMD_K15) {
g_fake_cpu->SetLeaves({
{{0x00000000, 0}, Leaf{0x0000000D, 0x68747541, 0x444D4163, 0x69746E65}},
{{0x00000001, 0}, Leaf{0x00630F81, 0x00040800, 0x3E98320B, 0x178BFBFF}},
@@ -273,6 +323,208 @@ TEST(CpuidX86Test, AMD_K15) {
EXPECT_STREQ(brand_string, "AMD A8-7670K Radeon R7, 10 Compute Cores 4C+6G ");
}
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel00106A1_Nehalem_CPUID.txt
+TEST_F(CpuidX86Test, Nehalem) {
+ // Pre AVX cpus don't have xsave
+ g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_XMMI_INSTRUCTIONS_AVAILABLE);
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_SSE3_INSTRUCTIONS_AVAILABLE);
+#endif // CPU_FEATURES_OS_WINDOWS
+#if defined(CPU_FEATURES_OS_DARWIN)
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
+#endif // CPU_FEATURES_OS_DARWIN
+#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(processor :
+flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x000106A2, 0x00100800, 0x00BCE3BD, 0xBFEBFBFF}},
+ {{0x00000002, 0}, Leaf{0x55035A01, 0x00F0B0E3, 0x00000000, 0x09CA212C}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004121, 0x01C0003F, 0x0000003F, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004122, 0x00C0003F, 0x0000007F, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C004143, 0x01C0003F, 0x000001FF, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C03C163, 0x03C0003F, 0x00000FFF, 0x00000002}},
+ {{0x00000005, 0}, Leaf{0x00000040, 0x00000040, 0x00000003, 0x00021120}},
+ {{0x00000006, 0}, Leaf{0x00000001, 0x00000002, 0x00000001, 0x00000000}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000008, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000009, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x0000000A, 0}, Leaf{0x07300403, 0x00000000, 0x00000000, 0x00000603}},
+ {{0x0000000B, 0}, Leaf{0x00000001, 0x00000001, 0x00000100, 0x00000000}},
+ {{0x0000000B, 0}, Leaf{0x00000004, 0x00000002, 0x00000201, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000001, 0x28100000}},
+ {{0x80000002, 0}, Leaf{0x756E6547, 0x20656E69, 0x65746E49, 0x2952286C}},
+ {{0x80000003, 0}, Leaf{0x55504320, 0x20202020, 0x20202020, 0x40202020}},
+ {{0x80000004, 0}, Leaf{0x30303020, 0x20402030, 0x37382E31, 0x007A4847}},
+ {{0x80000005, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000006, 0}, Leaf{0x00000000, 0x00000000, 0x01006040, 0x00000000}},
+ {{0x80000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000100}},
+ {{0x80000008, 0}, Leaf{0x00003028, 0x00000000, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x1A);
+ EXPECT_EQ(info.stepping, 0x02);
+ EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_NHM);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "Genuine Intel(R) CPU @ 0000 @ 1.87GHz");
+
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_TRUE(info.features.sse2);
+ EXPECT_TRUE(info.features.sse3);
+#ifndef CPU_FEATURES_OS_WINDOWS
+ // Currently disabled on Windows as IsProcessorFeaturePresent do not support
+ // feature detection > sse3.
+ EXPECT_TRUE(info.features.ssse3);
+ EXPECT_TRUE(info.features.sse4_1);
+ EXPECT_TRUE(info.features.sse4_2);
+#endif // CPU_FEATURES_OS_WINDOWS
+}
+
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0030673_Silvermont3_CPUID.txt
+TEST_F(CpuidX86Test, Atom) {
+ // Pre AVX cpus don't have xsave
+ g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_XMMI_INSTRUCTIONS_AVAILABLE);
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_XMMI64_INSTRUCTIONS_AVAILABLE);
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_SSE3_INSTRUCTIONS_AVAILABLE);
+#endif // CPU_FEATURES_OS_WINDOWS
+#if defined(CPU_FEATURES_OS_DARWIN)
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse2");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse3");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.supplementalsse3");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_1");
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse4_2");
+#endif // CPU_FEATURES_OS_DARWIN
+#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(
+flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x0000000B, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x00030673, 0x00100800, 0x41D8E3BF, 0xBFEBFBFF}},
+ {{0x00000002, 0}, Leaf{0x61B3A001, 0x0000FFC2, 0x00000000, 0x00000000}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000004, 0}, Leaf{0x1C000121, 0x0140003F, 0x0000003F, 0x00000001}},
+ {{0x00000004, 1}, Leaf{0x1C000122, 0x01C0003F, 0x0000003F, 0x00000001}},
+ {{0x00000004, 2}, Leaf{0x1C00C143, 0x03C0003F, 0x000003FF, 0x00000001}},
+ {{0x00000005, 0}, Leaf{0x00000040, 0x00000040, 0x00000003, 0x33000020}},
+ {{0x00000006, 0}, Leaf{0x00000005, 0x00000002, 0x00000009, 0x00000000}},
+ {{0x00000007, 0}, Leaf{0x00000000, 0x00002282, 0x00000000, 0x00000000}},
+ {{0x00000008, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x00000009, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x0000000A, 0}, Leaf{0x07280203, 0x00000000, 0x00000000, 0x00004503}},
+ {{0x0000000B, 0}, Leaf{0x00000001, 0x00000001, 0x00000100, 0x00000000}},
+ {{0x0000000B, 1}, Leaf{0x00000004, 0x00000004, 0x00000201, 0x00000000}},
+ {{0x80000000, 0}, Leaf{0x80000008, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000001, 0}, Leaf{0x00000000, 0x00000000, 0x00000101, 0x28100000}},
+ {{0x80000002, 0}, Leaf{0x20202020, 0x6E492020, 0x286C6574, 0x43202952}},
+ {{0x80000003, 0}, Leaf{0x72656C65, 0x52286E6F, 0x50432029, 0x4A202055}},
+ {{0x80000004, 0}, Leaf{0x30303931, 0x20402020, 0x39392E31, 0x007A4847}},
+ {{0x80000005, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000000}},
+ {{0x80000006, 0}, Leaf{0x00000000, 0x00000000, 0x04008040, 0x00000000}},
+ {{0x80000007, 0}, Leaf{0x00000000, 0x00000000, 0x00000000, 0x00000100}},
+ {{0x80000008, 0}, Leaf{0x00003024, 0x00000000, 0x00000000, 0x00000000}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x37);
+ EXPECT_EQ(info.stepping, 0x03);
+ EXPECT_EQ(GetX86Microarchitecture(&info),
+ X86Microarchitecture::INTEL_ATOM_SMT);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, " Intel(R) Celeron(R) CPU J1900 @ 1.99GHz");
+
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_TRUE(info.features.sse2);
+ EXPECT_TRUE(info.features.sse3);
+#ifndef CPU_FEATURES_OS_WINDOWS
+ // Currently disabled on Windows as IsProcessorFeaturePresent do not support
+ // feature detection > sse3.
+ EXPECT_TRUE(info.features.ssse3);
+ EXPECT_TRUE(info.features.sse4_1);
+ EXPECT_TRUE(info.features.sse4_2);
+#endif // CPU_FEATURES_OS_WINDOWS
+}
+
+// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt
+TEST_F(CpuidX86Test, P3) {
+ // Pre AVX cpus don't have xsave
+ g_fake_cpu->SetOsBackupsExtendedRegisters(false);
+#if defined(CPU_FEATURES_OS_WINDOWS)
+ g_fake_cpu->SetWindowsIsProcessorFeaturePresent(
+ PF_XMMI_INSTRUCTIONS_AVAILABLE);
+#endif // CPU_FEATURES_OS_WINDOWS
+#if defined(CPU_FEATURES_OS_DARWIN)
+ g_fake_cpu->SetDarwinSysCtlByName("hw.optional.sse");
+#endif // CPU_FEATURES_OS_DARWIN
+#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID)
+ auto& fs = GetEmptyFilesystem();
+ fs.CreateFile("/proc/cpuinfo", R"(
+flags : fpu mmx sse
+)");
+#endif // CPU_FEATURES_OS_LINUX_OR_ANDROID
+ g_fake_cpu->SetLeaves({
+ {{0x00000000, 0}, Leaf{0x00000003, 0x756E6547, 0x6C65746E, 0x49656E69}},
+ {{0x00000001, 0}, Leaf{0x00000673, 0x00000000, 0x00000000, 0x0387FBFF}},
+ {{0x00000002, 0}, Leaf{0x03020101, 0x00000000, 0x00000000, 0x0C040843}},
+ {{0x00000003, 0}, Leaf{0x00000000, 0x00000000, 0x4CECC782, 0x00006778}},
+ });
+ const auto info = GetX86Info();
+
+ EXPECT_STREQ(info.vendor, "GenuineIntel");
+ EXPECT_EQ(info.family, 0x06);
+ EXPECT_EQ(info.model, 0x07);
+ EXPECT_EQ(info.stepping, 0x03);
+ EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::X86_UNKNOWN);
+
+ char brand_string[49];
+ FillX86BrandString(brand_string);
+ EXPECT_STREQ(brand_string, "");
+
+ EXPECT_TRUE(info.features.mmx);
+ EXPECT_TRUE(info.features.sse);
+ EXPECT_FALSE(info.features.sse2);
+ EXPECT_FALSE(info.features.sse3);
+#ifndef CPU_FEATURES_OS_WINDOWS
+ // Currently disabled on Windows as IsProcessorFeaturePresent do not support
+ // feature detection > sse3.
+ EXPECT_FALSE(info.features.ssse3);
+ EXPECT_FALSE(info.features.sse4_1);
+ EXPECT_FALSE(info.features.sse4_2);
+#endif // CPU_FEATURES_OS_WINDOWS
+}
+
// TODO(user): test what happens when xsave/osxsave are not present.
// TODO(user): test what happens when xmm/ymm/zmm os support are not
// present.