diff --git a/CMakeLists.txt b/CMakeLists.txt index 545f38fb..0c71ea43 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 $ ) +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 fae9f700..86c49684 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 754ca386..6f538752 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 d5edd305..1a10cc2b 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 // IsProcessorFeaturePresent +#elif defined(HAVE_UTSNAME_H) +#include + +#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 +#endif // HAVE_SYSCTLBYNAME +#endif // HAVE_UTSNAME_H + //////////////////////////////////////////////////////////////////////////////// // Definitions for CpuId and GetXCR0Eax. //////////////////////////////////////////////////////////////////////////////// @@ -35,7 +50,7 @@ #include -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 #include // 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 eb67ac09..3f267bd0 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 e11a0ba6..9dae540b 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -17,7 +17,12 @@ #include #include #include +#include +#if defined(CPU_FEATURES_OS_WINDOWS) +#include // 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, Leaf> cpuid_leaves_; +#if defined(CPU_FEATURES_OS_DARWIN) + std::set darwin_sysctlbyname_; +#endif // CPU_FEATURES_OS_DARWIN +#if defined(CPU_FEATURES_OS_WINDOWS) + std::set 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.