diff --git a/base/cpuid.cpp b/base/cpuid.cpp index 384576f9bb..6ece902572 100644 --- a/base/cpuid.cpp +++ b/base/cpuid.cpp @@ -1,5 +1,6 @@ #include "base/cpuid.hpp" +#include #include #include "base/macros.hpp" // 🧙 For PRINCIPIA_COMPILER_MSVC. @@ -9,18 +10,15 @@ #include #endif +#include "glog/logging.h" + namespace principia { namespace base { namespace _cpuid { namespace internal { namespace { -struct CPUIDResult { - std::uint32_t eax; - std::uint32_t ebx; - std::uint32_t ecx; - std::uint32_t edx; -}; +std::vector CPUIDFlags; CPUIDResult CPUID(std::uint32_t const eax, std::uint32_t const ecx) { #if PRINCIPIA_COMPILER_MSVC @@ -39,6 +37,25 @@ CPUIDResult CPUID(std::uint32_t const eax, std::uint32_t const ecx) { } // namespace +CPUIDFeatureFlag::CPUIDFeatureFlag(std::string_view const name, + std::uint32_t const leaf, + std::uint32_t const sub_leaf, + std::uint32_t CPUIDResult::*const field, + std::int8_t const bit) + : name_(name), leaf_(leaf), sub_leaf_(sub_leaf), field_(field), bit_(bit) { + CHECK_GE(bit, 0); + CHECK_LT(bit, 32); + CPUIDFlags.emplace_back(*this); +} + +std::string_view CPUIDFeatureFlag::name() const { + return name_; +} + +bool CPUIDFeatureFlag::IsSet() const { + return CPUID(leaf_, sub_leaf_).*field_ & (1 << bit_); +} + std::string CPUVendorIdentificationString() { auto const leaf_0 = CPUID(0, 0); std::string result(12, '\0'); @@ -48,17 +65,28 @@ std::string CPUVendorIdentificationString() { return result; } -CPUFeatureFlags operator|(CPUFeatureFlags const left, - CPUFeatureFlags const right) { - return static_cast(static_cast(left) | - static_cast(right)); +std::string ProcessorBrandString() { + std::string result(48, '\0'); + for (int n = 0; n < 4; ++n) { + auto const piece = CPUID(0x80000002 + n, 0); + std::memcpy(&result[n * 16], &piece.eax, 4); + std::memcpy(&result[n * 16 + 4], &piece.ebx, 4); + std::memcpy(&result[n * 16 + 8], &piece.ecx, 4); + std::memcpy(&result[n * 16 + 12], &piece.edx, 4); + } + return result; } -bool HasCPUFeatures(CPUFeatureFlags const flags) { - auto const leaf_1 = CPUID(1, 0); - return static_cast( - (ecx_bit * leaf_1.ecx | edx_bit * leaf_1.edx) & - static_cast(flags)) == flags; +std::string CPUFeatures() { + std::string result = ""; + for (auto const& flag : CPUIDFlags) { + if (flag.IsSet()) { + if (!result.empty()) { + result += " "; + } + result += flag.name(); + } + } } } // namespace internal diff --git a/base/cpuid.hpp b/base/cpuid.hpp index 15d4d72e2b..1b1920ca00 100644 --- a/base/cpuid.hpp +++ b/base/cpuid.hpp @@ -2,47 +2,82 @@ #include #include +#include namespace principia { namespace base { namespace _cpuid { namespace internal { -// See the Intel® 64 and IA-32 Architectures Software Developer’s Manual, -// Volume 2A, CPUID—CPU Identification. +struct CPUIDResult { + std::uint32_t eax; + std::uint32_t ebx; + std::uint32_t ecx; + std::uint32_t edx; +}; -// Leaf 0. -std::string CPUVendorIdentificationString(); +class CPUIDFeatureFlag { + public: + CPUIDFeatureFlag(std::string_view const name, + std::uint32_t const leaf, + std::uint32_t const sub_leaf, + std::uint32_t CPUIDResult::*const field, + std::int8_t const bit); + CPUIDFeatureFlag(std::string_view const name, + std::uint32_t const leaf, + std::uint32_t CPUIDResult::*const field, + std::int8_t const bit) + : CPUIDFeatureFlag(name, leaf, 0, field, bit) {} + + std::string_view name() const; + bool IsSet() const; -// Leaf 1. -// We represent feature flags as EDX + 2³² ECX. -constexpr std::uint64_t edx_bit = 1; -constexpr std::uint64_t ecx_bit = edx_bit << 32; -enum class CPUFeatureFlags : std::uint64_t { - // Table 3-11. - FPU = edx_bit << 0, // x87 Floating Point Unit on chip. - PSN = edx_bit << 18, // Processor Serial Number. - SSE = edx_bit << 25, // Streaming SIMD Extensions. - SSE2 = edx_bit << 26, // Streaming SIMD Extensions 2. - // Table 3-10. - SSE3 = ecx_bit << 0, // Streaming SIMD Extensions 3. - FMA = ecx_bit << 12, // Fused Multiply Add. - SSE4_1 = ecx_bit << 19, // Streaming SIMD Extensions 4.1. - AVX = ecx_bit << 28, // Advanced Vector eXtensions. + private: + std::string_view name_; + std::uint32_t leaf_; + std::uint32_t sub_leaf_; + std::uint32_t CPUIDResult::*field_; + std::int8_t bit_; }; -// Bitwise or of feature flags; the result represents the union of all features -// in |left| and |right|. -CPUFeatureFlags operator|(CPUFeatureFlags left, CPUFeatureFlags right); +constexpr std::uint32_t CPUIDResult::*EAX = &CPUIDResult::eax; +constexpr std::uint32_t CPUIDResult::*EBX = &CPUIDResult::ebx; +constexpr std::uint32_t CPUIDResult::*ECX = &CPUIDResult::ecx; +constexpr std::uint32_t CPUIDResult::*EDX = &CPUIDResult::edx; + +std::string CPUVendorIdentificationString(); +std::string ProcessorBrandString(); + +std::string CPUFeatures(); -// Whether the CPU has all features listed in |flags|. -bool HasCPUFeatures(CPUFeatureFlags flags); +#define PRINCIPIA_CPUID_FLAG(name, ...) \ + inline CPUIDFeatureFlag const name(#name, __VA_ARGS__); +namespace cpuid_feature_flags { +// Table 3-11. +PRINCIPIA_CPUID_FLAG(FPU, 0x01, EDX, 0); // x87 Floating Point Unit on chip. +PRINCIPIA_CPUID_FLAG(PSN, 0x01, EDX, 18); // Processor Serial Number. +PRINCIPIA_CPUID_FLAG(SSE, 0x01, EDX, 25); // Streaming SIMD Extensions. +PRINCIPIA_CPUID_FLAG(SSE2, 0x01, EDX, 26); // Streaming SIMD Extensions 2. +PRINCIPIA_CPUID_FLAG(SSE3, 0x01, ECX, 0); // Streaming SIMD Extensions 3. +PRINCIPIA_CPUID_FLAG(FMA, 0x01, ECX, 12); // Fused Multiply Add. +PRINCIPIA_CPUID_FLAG(SSE4_1, 0x01, ECX, 19); // Streaming SIMD Extensions 4.1. +PRINCIPIA_CPUID_FLAG(AVX, 0x01, ECX, 28); // Advanced Vector eXtensions. +// Table 3-8. +PRINCIPIA_CPUID_FLAG(AVX2, 0x07, EBX, 5); // Advanced Vector eXtensions 2. +PRINCIPIA_CPUID_FLAG(AVX512F, 0x07, EBX, 16); // AVX-512 Foundation. +PRINCIPIA_CPUID_FLAG(AVX512DQ, 0x07, EBX, 17); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512_IFMA, 0x07, EBX, 21); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512PF, 0x07, EBX, 26); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512ER, 0x07, EBX, 27); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512CD, 0x07, EBX, 28); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512BW, 0x07, EBX, 30); // AVX-512 . +PRINCIPIA_CPUID_FLAG(AVX512VL, 0x07, EBX, 31); // AVX-512 . +} // namespace cpuid_feature_flags } // namespace internal -using internal::CPUFeatureFlags; using internal::CPUVendorIdentificationString; -using internal::HasCPUFeatures; +namespace cpuid_feature_flags = internal::cpuid_feature_flags } // namespace _cpuid } // namespace base diff --git a/base/cpuid_test.cpp b/base/cpuid_test.cpp index 69dc6233c9..4bd8c98232 100644 --- a/base/cpuid_test.cpp +++ b/base/cpuid_test.cpp @@ -22,15 +22,14 @@ TEST_F(CPUIDTest, Vendor) { TEST_F(CPUIDTest, CPUFeatureFlags) { // We require Prescott or later. - EXPECT_TRUE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE | - CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3)); + EXPECT_TRUE(cpuid_feature_flags::FPU.IsSet()); + EXPECT_TRUE(cpuid_feature_flags::SSE.IsSet()); + EXPECT_TRUE(cpuid_feature_flags::SSE2.IsSet()); + EXPECT_TRUE(cpuid_feature_flags::SSE3.IsSet()); // Check that we don’t always return true. // We are not running these tests on a Pentium III, so we do not have the // Processor Serial Number feature. - EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::PSN)); - EXPECT_FALSE(HasCPUFeatures(CPUFeatureFlags::FPU | CPUFeatureFlags::SSE | - CPUFeatureFlags::SSE2 | CPUFeatureFlags::SSE3 | - CPUFeatureFlags::PSN)); + EXPECT_FALSE(cpuid_feature_flags::PSN.IsSet()); } } // namespace base