From 54cba6cf0e9db491f939afaf2ef10b33598dc347 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Thu, 8 Oct 2020 16:07:47 +0000 Subject: [PATCH 01/13] Fix SSE detection on non-AVX CPUs Fixes #4 --- CMakeLists.txt | 13 ++++ src/cpuinfo_x86.c | 139 ++++++++++++++++++++++++++++++++------ test/CMakeLists.txt | 6 ++ test/cpuinfo_x86_test.cc | 142 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+), 22 deletions(-) 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/src/cpuinfo_x86.c b/src/cpuinfo_x86.c index d5edd305..184d548d 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_COMPILER_MSC) +#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. //////////////////////////////////////////////////////////////////////////////// @@ -1082,27 +1097,106 @@ 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(HAVE_SYSCTLBYNAME) +static bool SysCtlByName(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 + +static void DetectSseViaOs(X86Features* features) { +#if defined(CPU_FEATURES_COMPILER_MSC) + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent + features->sse = IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE); + features->sse2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE); + features->sse3 = IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE); +#elif defined(HAVE_UTSNAME_H) + struct utsname buf; + uname(&buf); + if (CpuFeatures_StringView_IsEquals(str(buf.sysname), str("Darwin"))) { +#if defined(HAVE_SYSCTLBYNAME) + // Handling Darwin platform through sysctlbyname when available. + features->sse = SysCtlByName("hw.optional.sse"); + features->sse2 = SysCtlByName("hw.optional.sse2"); + features->sse3 = SysCtlByName("hw.optional.sse3"); + features->ssse3 = SysCtlByName("hw.optional.supplementalsse3"); + features->sse4_1 = SysCtlByName("hw.optional.sse4_1"); + features->sse4_2 = SysCtlByName("hw.optional.sse4_2"); +#endif // HAVE_SYSCTLBYNAME + } else 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 { + // Failed to probe the system. + } +#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 +1236,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 +1247,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 +1275,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 +1291,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 +1301,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..bcab1131 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -18,6 +18,7 @@ #include #include +#include "filesystem_for_testing.h" #include "gtest/gtest.h" #include "internal/cpuid_x86.h" @@ -246,6 +247,7 @@ 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) { g_fake_cpu->SetLeaves({ @@ -273,6 +275,146 @@ 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(CpuidX86Test, Nehalem) { + auto& fs = GetEmptyFilesystem(); + fs.CreateFile("/proc/cpuinfo", R"(processor : +flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 +)"); + 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); + EXPECT_TRUE(info.features.ssse3); + EXPECT_TRUE(info.features.sse4_1); + EXPECT_TRUE(info.features.sse4_2); +} + +// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0030673_Silvermont3_CPUID.txt +TEST(CpuidX86Test, Atom) { + auto& fs = GetEmptyFilesystem(); + fs.CreateFile("/proc/cpuinfo", R"( +flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 +)"); + 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); + EXPECT_TRUE(info.features.ssse3); + EXPECT_TRUE(info.features.sse4_1); + EXPECT_TRUE(info.features.sse4_2); +} + +// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt +TEST(CpuidX86Test, P3) { + g_fake_cpu->SetOsBackupsExtendedRegisters(false); + auto& fs = GetEmptyFilesystem(); + fs.CreateFile("/proc/cpuinfo", R"( +flags : fpu mmx sse +)"); + 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); + EXPECT_FALSE(info.features.ssse3); + EXPECT_FALSE(info.features.sse4_1); + EXPECT_FALSE(info.features.sse4_2); +} + // 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. From 39f87b72df828488c811c1f09c4f7bec1c2d1824 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Thu, 8 Oct 2020 16:47:55 +0000 Subject: [PATCH 02/13] Fixes typo --- src/cpuinfo_x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpuinfo_x86.c b/src/cpuinfo_x86.c index 184d548d..545dfbcb 100644 --- a/src/cpuinfo_x86.c +++ b/src/cpuinfo_x86.c @@ -1133,7 +1133,7 @@ static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) { static bool SysCtlByName(const char* name) { int enabled; size_t enabled_len = sizeof(enabled); - const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0)); + const int failure = sysctlbyname(name, &enabled, &enabled_len, NULL, 0); return failure ? false : enabled; } #endif From daf4dd571222c3e420d4254f26100a1b7219a662 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 07:44:48 +0000 Subject: [PATCH 03/13] Mock OSX sysctlbyname in tests --- src/cpuinfo_x86.c | 6 +++++- test/cpuinfo_x86_test.cc | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cpuinfo_x86.c b/src/cpuinfo_x86.c index 545dfbcb..56d3f535 100644 --- a/src/cpuinfo_x86.c +++ b/src/cpuinfo_x86.c @@ -1130,13 +1130,17 @@ static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) { } #if defined(HAVE_SYSCTLBYNAME) +#if defined(CPU_FEATURES_MOCK_CPUID_X86) +extern bool SysCtlByName(const char* name); +#else static bool SysCtlByName(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_MOCK_CPUID_X86 +#endif // HAVE_SYSCTLBYNAME static void DetectSseViaOs(X86Features* features) { #if defined(CPU_FEATURES_COMPILER_MSC) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index bcab1131..a767df24 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -36,6 +36,14 @@ class FakeCpu { uint32_t GetXCR0Eax() const { return xcr0_eax_; } + bool SysCtlByName(std::string name) const { + const auto itr = sysctlbyname_.find(name); + if (itr != sysctlbyname_.end()) { + return itr->second; + } + return 0; + } + void SetLeaves(std::map, Leaf> configuration) { cpuid_leaves_ = std::move(configuration); } @@ -44,8 +52,11 @@ class FakeCpu { xcr0_eax_ = os_backups_extended_registers ? -1 : 0; } + void SetSysCtlByName(std::string name) { sysctlbyname_[name] = 1; } + private: std::map, Leaf> cpuid_leaves_; + std::map sysctlbyname_; uint32_t xcr0_eax_; }; @@ -57,6 +68,10 @@ extern "C" Leaf CpuIdEx(uint32_t leaf_id, int ecx) { extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); } +extern "C" bool SysCtlByName(const char* name) { + return g_fake_cpu->SysCtlByName(name); +} + namespace { TEST(CpuidX86Test, SandyBridge) { @@ -384,6 +399,7 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt TEST(CpuidX86Test, P3) { g_fake_cpu->SetOsBackupsExtendedRegisters(false); + g_fake_cpu->SetSysCtlByName("hw.optional.sse"); auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"( flags : fpu mmx sse From eea45d92308fed074799f7b5de9d08bd2c88fc98 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 07:51:48 +0000 Subject: [PATCH 04/13] Also update other tests --- test/cpuinfo_x86_test.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index a767df24..30518584 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -292,6 +292,16 @@ TEST(CpuidX86Test, AMD_K15) { // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel00106A1_Nehalem_CPUID.txt TEST(CpuidX86Test, Nehalem) { + // Pre AVX cpus don't have xsave + g_fake_cpu->SetOsBackupsExtendedRegisters(false); + // On Darwin we fake sysctlbyname + g_fake_cpu->SetSysCtlByName("hw.optional.sse"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse2"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse3"); + g_fake_cpu->SetSysCtlByName("hw.optional.supplementalsse3"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse4_1"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse4_2"); + // On Linux we fake /proc/cpuinfo auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"(processor : flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 @@ -345,6 +355,16 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0030673_Silvermont3_CPUID.txt TEST(CpuidX86Test, Atom) { + // Pre AVX cpus don't have xsave + g_fake_cpu->SetOsBackupsExtendedRegisters(false); + // On Darwin we fake sysctlbyname + g_fake_cpu->SetSysCtlByName("hw.optional.sse"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse2"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse3"); + g_fake_cpu->SetSysCtlByName("hw.optional.supplementalsse3"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse4_1"); + g_fake_cpu->SetSysCtlByName("hw.optional.sse4_2"); + // On Linux we fake /proc/cpuinfo auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"( flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 @@ -398,8 +418,11 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt TEST(CpuidX86Test, P3) { + // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); + // On Darwin we fake sysctlbyname g_fake_cpu->SetSysCtlByName("hw.optional.sse"); + // On Linux we fake /proc/cpuinfo auto& fs = GetEmptyFilesystem(); fs.CreateFile("/proc/cpuinfo", R"( flags : fpu mmx sse From c1363c39cd1f689f307593bf63193b723fab68a6 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 08:03:50 +0000 Subject: [PATCH 05/13] FakeCpu is reset between each tests --- test/cpuinfo_x86_test.cc | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 30518584..02a61926 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -60,7 +60,7 @@ class FakeCpu { 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); @@ -74,7 +74,13 @@ extern "C" bool SysCtlByName(const char* name) { 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}}, @@ -120,7 +126,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}}, @@ -134,7 +140,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}}, @@ -149,7 +155,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}}, @@ -165,7 +171,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}}, @@ -214,7 +220,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}}, @@ -264,7 +270,7 @@ TEST(CpuidX86Test, HSWCache) { } // 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}}, @@ -291,7 +297,7 @@ TEST(CpuidX86Test, AMD_K15) { } // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel00106A1_Nehalem_CPUID.txt -TEST(CpuidX86Test, Nehalem) { +TEST_F(CpuidX86Test, Nehalem) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); // On Darwin we fake sysctlbyname @@ -354,7 +360,7 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 } // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0030673_Silvermont3_CPUID.txt -TEST(CpuidX86Test, Atom) { +TEST_F(CpuidX86Test, Atom) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); // On Darwin we fake sysctlbyname @@ -417,7 +423,7 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 } // https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel0000673_P3_KatmaiDP_CPUID.txt -TEST(CpuidX86Test, P3) { +TEST_F(CpuidX86Test, P3) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); // On Darwin we fake sysctlbyname From 8578efa3d1e0418c171d14a00e2e97878dece70a Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 11:58:26 +0000 Subject: [PATCH 06/13] Fix conflicting name on Windows --- include/internal/cpuid_x86.h | 3 ++- src/cpuinfo_x86.c | 8 ++++---- test/cpuinfo_x86_test.cc | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) 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 56d3f535..031e7d52 100644 --- a/src/cpuinfo_x86.c +++ b/src/cpuinfo_x86.c @@ -50,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; @@ -70,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); @@ -87,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; } diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 02a61926..6072fe1b 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -26,7 +26,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; @@ -62,8 +62,8 @@ class 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(); } From a889e2702a9136c7eef49621623b7365363fca60 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 12:21:57 +0000 Subject: [PATCH 07/13] Disable pre AVX cpu sse detection tests on Windows --- test/cpuinfo_x86_test.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 6072fe1b..f3b00b27 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -351,12 +351,16 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 FillX86BrandString(brand_string); EXPECT_STREQ(brand_string, "Genuine Intel(R) CPU @ 0000 @ 1.87GHz"); +#ifndef CPU_FEATURES_OS_WINDOWS + // Currently disabled on Windows as IsProcessorFeaturePresent do not support + // feature detection > sse3. EXPECT_TRUE(info.features.sse); EXPECT_TRUE(info.features.sse2); EXPECT_TRUE(info.features.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 @@ -414,12 +418,16 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 FillX86BrandString(brand_string); EXPECT_STREQ(brand_string, " Intel(R) Celeron(R) CPU J1900 @ 1.99GHz"); +#ifndef CPU_FEATURES_OS_WINDOWS + // Currently disabled on Windows as IsProcessorFeaturePresent do not support + // feature detection > sse3. EXPECT_TRUE(info.features.sse); EXPECT_TRUE(info.features.sse2); EXPECT_TRUE(info.features.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 @@ -452,12 +460,16 @@ flags : fpu mmx sse EXPECT_STREQ(brand_string, ""); EXPECT_TRUE(info.features.mmx); +#ifndef CPU_FEATURES_OS_WINDOWS + // Currently disabled on Windows as IsProcessorFeaturePresent do not support + // feature detection > sse3. EXPECT_TRUE(info.features.sse); EXPECT_FALSE(info.features.sse2); EXPECT_FALSE(info.features.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. From e275bacab43c7a6c78083c23d01c32af99d480ed Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 13:39:01 +0000 Subject: [PATCH 08/13] Guard OS specific code with macros --- include/cpu_features_macros.h | 4 ++ src/cpuinfo_x86.c | 59 ++++++++++------ test/cpuinfo_x86_test.cc | 126 ++++++++++++++++++++++++---------- 3 files changed, 129 insertions(+), 60 deletions(-) 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/src/cpuinfo_x86.c b/src/cpuinfo_x86.c index 031e7d52..4957be85 100644 --- a/src/cpuinfo_x86.c +++ b/src/cpuinfo_x86.c @@ -1129,39 +1129,53 @@ static OsSupport CheckOsSupport(const uint32_t max_cpuid_leaf) { return os_support; } -#if defined(HAVE_SYSCTLBYNAME) +#if defined(CPU_FEATURES_OS_WINDOWS) #if defined(CPU_FEATURES_MOCK_CPUID_X86) -extern bool SysCtlByName(const char* name); -#else -static bool SysCtlByName(const char* name) { +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 // CPU_FEATURES_MOCK_CPUID_X86 -#endif // HAVE_SYSCTLBYNAME +#endif +#endif // CPU_FEATURES_OS_DARWIN && HAVE_SYSCTLBYNAME static void DetectSseViaOs(X86Features* features) { -#if defined(CPU_FEATURES_COMPILER_MSC) +#if defined(CPU_FEATURES_OS_WINDOWS) // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent - features->sse = IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE); - features->sse2 = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE); - features->sse3 = IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE); + 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"))) { -#if defined(HAVE_SYSCTLBYNAME) // Handling Darwin platform through sysctlbyname when available. - features->sse = SysCtlByName("hw.optional.sse"); - features->sse2 = SysCtlByName("hw.optional.sse2"); - features->sse3 = SysCtlByName("hw.optional.sse3"); - features->ssse3 = SysCtlByName("hw.optional.supplementalsse3"); - features->sse4_1 = SysCtlByName("hw.optional.sse4_1"); - features->sse4_2 = SysCtlByName("hw.optional.sse4_2"); -#endif // HAVE_SYSCTLBYNAME - } else if (CpuFeatures_StringView_IsEquals(str(buf.sysname), str("Linux"))) { + 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) { @@ -1186,10 +1200,11 @@ static void DetectSseViaOs(X86Features* features) { } CpuFeatures_CloseFile(fd); } - } else { - // Failed to probe the system. } -#else // HAVE_UTSNAME_H +#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 } diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index f3b00b27..d5a62ec5 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "filesystem_for_testing.h" #include "gtest/gtest.h" @@ -36,12 +37,12 @@ class FakeCpu { uint32_t GetXCR0Eax() const { return xcr0_eax_; } - bool SysCtlByName(std::string name) const { - const auto itr = sysctlbyname_.find(name); - if (itr != sysctlbyname_.end()) { - return itr->second; - } - return 0; + bool GetDarwinSysCtlByName(std::string name) const { + return darwin_sysctlbyname_.count(name); + } + + bool GetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { + return windows_isprocessorfeaturepresent_.count(ProcessorFeature); } void SetLeaves(std::map, Leaf> configuration) { @@ -52,11 +53,22 @@ class FakeCpu { xcr0_eax_ = os_backups_extended_registers ? -1 : 0; } - void SetSysCtlByName(std::string name) { sysctlbyname_[name] = 1; } +#if defined(CPU_FEATURES_OS_DARWIN) + void SetDarwinSysCtlByName(std::string name) { + darwin_sysctlbyname_.insert(name); + } +#endif // CPU_FEATURES_OS_DARWIN + +#if defined(CPU_FEATURES_OS_WINDOWS) + bool SetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { + windows_isprocessorfeaturepresent_.insert(ProcessorFeature); + } +#endif // CPU_FEATURES_OS_WINDOWS private: std::map, Leaf> cpuid_leaves_; - std::map sysctlbyname_; + std::set darwin_sysctlbyname_; + std::set windows_isprocessorfeaturepresent_; uint32_t xcr0_eax_; }; @@ -68,9 +80,17 @@ extern "C" Leaf GetCpuidLeaf(uint32_t leaf_id, int ecx) { extern "C" uint32_t GetXCR0Eax(void) { return g_fake_cpu->GetXCR0Eax(); } -extern "C" bool SysCtlByName(const char* name) { - return g_fake_cpu->SysCtlByName(name); +#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 { @@ -300,18 +320,28 @@ TEST_F(CpuidX86Test, AMD_K15) { TEST_F(CpuidX86Test, Nehalem) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); - // On Darwin we fake sysctlbyname - g_fake_cpu->SetSysCtlByName("hw.optional.sse"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse2"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse3"); - g_fake_cpu->SetSysCtlByName("hw.optional.supplementalsse3"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse4_1"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse4_2"); - // On Linux we fake /proc/cpuinfo +#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}}, @@ -351,12 +381,12 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 FillX86BrandString(brand_string); EXPECT_STREQ(brand_string, "Genuine Intel(R) CPU @ 0000 @ 1.87GHz"); -#ifndef CPU_FEATURES_OS_WINDOWS - // Currently disabled on Windows as IsProcessorFeaturePresent do not support - // feature detection > sse3. 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); @@ -367,18 +397,28 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 TEST_F(CpuidX86Test, Atom) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); - // On Darwin we fake sysctlbyname - g_fake_cpu->SetSysCtlByName("hw.optional.sse"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse2"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse3"); - g_fake_cpu->SetSysCtlByName("hw.optional.supplementalsse3"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse4_1"); - g_fake_cpu->SetSysCtlByName("hw.optional.sse4_2"); - // On Linux we fake /proc/cpuinfo +#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}}, @@ -418,12 +458,12 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 FillX86BrandString(brand_string); EXPECT_STREQ(brand_string, " Intel(R) Celeron(R) CPU J1900 @ 1.99GHz"); -#ifndef CPU_FEATURES_OS_WINDOWS - // Currently disabled on Windows as IsProcessorFeaturePresent do not support - // feature detection > sse3. 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); @@ -434,13 +474,23 @@ flags : fpu mmx sse sse2 sse3 ssse3 sse4_1 sse4_2 TEST_F(CpuidX86Test, P3) { // Pre AVX cpus don't have xsave g_fake_cpu->SetOsBackupsExtendedRegisters(false); - // On Darwin we fake sysctlbyname - g_fake_cpu->SetSysCtlByName("hw.optional.sse"); - // On Linux we fake /proc/cpuinfo +#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"); +#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}}, @@ -460,12 +510,12 @@ flags : fpu mmx sse EXPECT_STREQ(brand_string, ""); EXPECT_TRUE(info.features.mmx); -#ifndef CPU_FEATURES_OS_WINDOWS - // Currently disabled on Windows as IsProcessorFeaturePresent do not support - // feature detection > sse3. 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); From a1c800161f62b454f37b9bdd0db583c8addf29bd Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 13:42:52 +0000 Subject: [PATCH 09/13] Fix missing import for tests --- src/cpuinfo_x86.c | 2 +- test/cpuinfo_x86_test.cc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cpuinfo_x86.c b/src/cpuinfo_x86.c index 4957be85..1a10cc2b 100644 --- a/src/cpuinfo_x86.c +++ b/src/cpuinfo_x86.c @@ -27,7 +27,7 @@ // The following includes are necessary to provide SSE detections on pre-AVX // microarchitectures. -#if defined(CPU_FEATURES_COMPILER_MSC) +#if defined(CPU_FEATURES_OS_WINDOWS) #include // IsProcessorFeaturePresent #elif defined(HAVE_UTSNAME_H) #include diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index d5a62ec5..642d20f1 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -18,6 +18,9 @@ #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" From b2bb02f18c61faf1e4ecae0bc6de4b2bcaa54220 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 13:49:12 +0000 Subject: [PATCH 10/13] Fix wrong function prototype --- test/cpuinfo_x86_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 642d20f1..8fc0e0be 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -63,7 +63,7 @@ class FakeCpu { #endif // CPU_FEATURES_OS_DARWIN #if defined(CPU_FEATURES_OS_WINDOWS) - bool SetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { + void SetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { windows_isprocessorfeaturepresent_.insert(ProcessorFeature); } #endif // CPU_FEATURES_OS_WINDOWS From 5e5145f37b36c572178c5d562f98b895116a0976 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 13:52:39 +0000 Subject: [PATCH 11/13] Fix wrong mocking of P3 on Windows --- test/cpuinfo_x86_test.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 8fc0e0be..936edb0d 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -480,10 +480,6 @@ TEST_F(CpuidX86Test, P3) { #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"); From 9ed5371b0a14659492937102c1640a8da1393084 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 13:56:43 +0000 Subject: [PATCH 12/13] Completely guard OS specific parts in x86 tests --- test/cpuinfo_x86_test.cc | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 936edb0d..4f9cf33c 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -40,14 +40,6 @@ class FakeCpu { uint32_t GetXCR0Eax() const { return xcr0_eax_; } - bool GetDarwinSysCtlByName(std::string name) const { - return darwin_sysctlbyname_.count(name); - } - - bool GetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { - return windows_isprocessorfeaturepresent_.count(ProcessorFeature); - } - void SetLeaves(std::map, Leaf> configuration) { cpuid_leaves_ = std::move(configuration); } @@ -57,21 +49,33 @@ class FakeCpu { } #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) - void SetWindowsIsProcessorFeaturePresent(unsigned long ProcessorFeature) { + 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_; - std::set windows_isprocessorfeaturepresent_; +#endif // CPU_FEATURES_OS_DARWIN +#if defined(CPU_FEATURES_OS_WINDOWS) + std::set windows_isprocessorfeaturepresent_; +#endif // CPU_FEATURES_OS_WINDOWS uint32_t xcr0_eax_; }; From 6a671e593ba5c394c4563c52ab3284ab618d91ff Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Fri, 9 Oct 2020 14:15:29 +0000 Subject: [PATCH 13/13] Store DWORD instead unsigned long for x86 tests --- test/cpuinfo_x86_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 4f9cf33c..9dae540b 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -74,7 +74,7 @@ class FakeCpu { std::set darwin_sysctlbyname_; #endif // CPU_FEATURES_OS_DARWIN #if defined(CPU_FEATURES_OS_WINDOWS) - std::set windows_isprocessorfeaturepresent_; + std::set windows_isprocessorfeaturepresent_; #endif // CPU_FEATURES_OS_WINDOWS uint32_t xcr0_eax_; };