diff --git a/base/cpuid.cpp b/base/cpuid.cpp index 6618ab58b0..79ad7a1304 100644 --- a/base/cpuid.cpp +++ b/base/cpuid.cpp @@ -5,6 +5,7 @@ #include #include "absl/strings/str_join.h" +#include "base/not_null.hpp" #include "base/macros.hpp" // 🧙 For PRINCIPIA_COMPILER_MSVC. #include "glog/logging.h" @@ -20,12 +21,14 @@ namespace base { namespace _cpuid { namespace internal { +using namespace principia::base::_not_null; + namespace { // This vector is not a static member variable of CPUIDFeatureFlag because we do // not want to include in the header. -std::vector& CPUIDFlags() { - static std::vector result; +std::vector>& CPUIDFlags() { + static std::vector> result; return result; } @@ -46,6 +49,35 @@ CPUIDResult CPUID(std::uint32_t const eax, std::uint32_t const ecx) { } // namespace +std::string_view CPUIDFeatureFlag::name() const { + return name_; +} + +bool CPUIDFeatureFlag::IsSet() const { + return CPUID(leaf_, sub_leaf_).*field_ & (1 << bit_); +} + +#define PRINCIPIA_CPUID_FLAG(name, ...) \ + CPUIDFeatureFlag const CPUIDFeatureFlag::name(#name, __VA_ARGS__); + +// 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, Structured Extended Feature Flags Enumeration Leaf. +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); // DWORD and QWORD instructions. +PRINCIPIA_CPUID_FLAG(AVX512VL, 0x07, EBX, 31); // Vector Length Extensions. +PRINCIPIA_CPUID_FLAG(AVX512_FP16, 0x07, ECX, 23); // IEEE-754 binary16. + CPUIDFeatureFlag::CPUIDFeatureFlag(std::string_view const name, std::uint32_t const leaf, std::uint32_t const sub_leaf, @@ -54,15 +86,7 @@ CPUIDFeatureFlag::CPUIDFeatureFlag(std::string_view const name, : name_(name), leaf_(leaf), sub_leaf_(sub_leaf), field_(field), bit_(bit) { CHECK_GE(bit, 0); CHECK_LT(bit, 32); - CPUIDFlags().push_back(*this); -} - -std::string_view CPUIDFeatureFlag::name() const { - return name_; -} - -bool CPUIDFeatureFlag::IsSet() const { - return CPUID(leaf_, sub_leaf_).*field_ & (1 << bit_); + CPUIDFlags().push_back(this); } std::string CPUVendorIdentificationString() { @@ -95,9 +119,9 @@ std::string CPUFeatures() { | std::ranges::to(); #else // TODO(egg): Get rid of this once clang really has C++23. std::vector set_flags; - for (auto const& flag : CPUIDFlags()) { - if (flag.IsSet()) { - set_flags.push_back(flag.name()); + for (not_null const flag : CPUIDFlags()) { + if (flag->IsSet()) { + set_flags.push_back(flag->name()); } } return absl::StrJoin(set_flags, " "); diff --git a/base/cpuid.hpp b/base/cpuid.hpp index 0f1a9511cf..5324701e22 100644 --- a/base/cpuid.hpp +++ b/base/cpuid.hpp @@ -18,6 +18,27 @@ struct CPUIDResult { class CPUIDFeatureFlag { public: + CPUIDFeatureFlag(CPUIDFeatureFlag const&) = delete; + + std::string_view name() const; + bool IsSet() const; + + static const CPUIDFeatureFlag FPU; // x87 Floating Point Unit on chip. + static const CPUIDFeatureFlag PSN; // Processor Serial Number. + static const CPUIDFeatureFlag SSE; // Streaming SIMD Extensions. + static const CPUIDFeatureFlag SSE2; // Streaming SIMD Extensions 2. + static const CPUIDFeatureFlag SSE3; // Streaming SIMD Extensions 3. + + static const CPUIDFeatureFlag FMA; // Fused Multiply Add. + static const CPUIDFeatureFlag SSE4_1; // Streaming SIMD Extensions 4.1. + static const CPUIDFeatureFlag AVX; // Advanced Vector eXtensions. + static const CPUIDFeatureFlag AVX2; // Advanced Vector eXtensions 2. + static const CPUIDFeatureFlag AVX512F; // AVX-512 Foundation. + static const CPUIDFeatureFlag AVX512DQ; // DWORD and QWORD instructions. + static const CPUIDFeatureFlag AVX512VL; // Vector Length Extensions. + static const CPUIDFeatureFlag AVX512_FP16; // IEEE-754 binary16. + + private: CPUIDFeatureFlag(std::string_view const name, std::uint32_t const leaf, std::uint32_t const sub_leaf, @@ -29,10 +50,6 @@ class CPUIDFeatureFlag { std::int8_t const bit) : CPUIDFeatureFlag(name, leaf, 0, field, bit) {} - std::string_view name() const; - bool IsSet() const; - - private: std::string_view name_; std::uint32_t leaf_; std::uint32_t sub_leaf_; @@ -50,36 +67,14 @@ std::string ProcessorBrandString(); std::string CPUFeatures(); -#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, Structured Extended Feature Flags Enumeration Leaf. -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); // DWORD and QWORD instructions. -PRINCIPIA_CPUID_FLAG(AVX512VL, 0x07, EBX, 31); // Vector Length Extensions. -PRINCIPIA_CPUID_FLAG(AVX512_FP16, 0x07, ECX, 23); // IEEE-754 binary16. -} // namespace cpuid_feature_flags } // namespace internal #undef PRINCIPIA_CPUID_FLAG using internal::CPUFeatures; +using internal::CPUIDFeatureFlag; using internal::CPUVendorIdentificationString; using internal::ProcessorBrandString; -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 4dd3a546dc..5504fd3dca 100644 --- a/base/cpuid_test.cpp +++ b/base/cpuid_test.cpp @@ -39,14 +39,14 @@ TEST_F(CPUIDTest, Brand) { TEST_F(CPUIDTest, CPUFeatureFlags) { // We require Prescott or later. - 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()); + EXPECT_TRUE(CPUIDFeatureFlag::FPU.IsSet()); + EXPECT_TRUE(CPUIDFeatureFlag::SSE.IsSet()); + EXPECT_TRUE(CPUIDFeatureFlag::SSE2.IsSet()); + EXPECT_TRUE(CPUIDFeatureFlag::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(cpuid_feature_flags::PSN.IsSet()); + EXPECT_FALSE(CPUIDFeatureFlag::PSN.IsSet()); EXPECT_THAT( CPUFeatures(), AllOf(HasSubstr("FPU"), HasSubstr("SSE2"), Not(HasSubstr("PSN")))); diff --git a/geometry/grassmann_test.cpp b/geometry/grassmann_test.cpp index 217cc55544..a936022376 100644 --- a/geometry/grassmann_test.cpp +++ b/geometry/grassmann_test.cpp @@ -82,7 +82,7 @@ class GrassmannTest : public testing::Test { using GrassmannDeathTest = GrassmannTest; TEST_F(GrassmannTest, VectorFMA) { - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } Length const a = a_.x; @@ -101,7 +101,7 @@ TEST_F(GrassmannTest, VectorFMA) { } TEST_F(GrassmannTest, BivectorFMA) { - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } Length const a = a_.x; @@ -120,7 +120,7 @@ TEST_F(GrassmannTest, BivectorFMA) { } TEST_F(GrassmannTest, TrivectorFMA) { - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } Length const a = a_.x; diff --git a/geometry/point_test.cpp b/geometry/point_test.cpp index 889a1030e2..92881bbad8 100644 --- a/geometry/point_test.cpp +++ b/geometry/point_test.cpp @@ -48,7 +48,7 @@ class PointTest : public testing::Test { using PointDeathTest = PointTest; TEST_F(PointTest, FMA) { - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } EXPECT_THAT(FusedMultiplyAdd(3 * Litre, 5 * Second / Litre, mjd0), diff --git a/numerics/cbrt_test.cpp b/numerics/cbrt_test.cpp index 74b4315556..644ca4b8f9 100644 --- a/numerics/cbrt_test.cpp +++ b/numerics/cbrt_test.cpp @@ -101,7 +101,7 @@ class CubeRootTest : public ::testing::Test { cbrt_y.rounded_to_nearest) { ++method_3²ᴄZ5¹_misroundings; } - if (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()) { + if (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()) { EXPECT_THAT(method_5²Z4¹FMA::Cbrt(y), AnyOf(cbrt_y.rounded_down, cbrt_y.rounded_up)); EXPECT_THAT(method_5²Z4¹FMA::Cbrt(y), @@ -113,7 +113,7 @@ class CubeRootTest : public ::testing::Test { } } EXPECT_THAT(method_3²ᴄZ5¹_misroundings, Eq(expected_3²ᴄZ5¹_misroundings)); - if (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()) { + if (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()) { EXPECT_THAT(method_5²Z4¹FMA_misroundings, Eq(expected_5²Z4¹FMA_misroundings)); } @@ -140,7 +140,7 @@ TEST_F(CubeRootTest, Rescaling) { Eq(Cbrt(2))); EXPECT_THAT(0x1p358 * method_3²ᴄZ5¹::Cbrt(0x1p-1073), Eq(Cbrt(2))); - if (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()) { + if (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()) { EXPECT_THAT(0x1p-340 * method_5²Z4¹FMA::Cbrt(0x1p1021), Eq(Cbrt(2))); EXPECT_THAT(0x1p341 * method_5²Z4¹FMA::Cbrt(0x1p-1022), @@ -206,7 +206,7 @@ TEST_F(CubeRootTest, BoundsOfTheRescalingRange) { Eq(0x1p113 * Cbrt(4))); EXPECT_THAT(method_3²ᴄZ5¹::Cbrt(0x1.0'0000'0000'0002p341), Eq(0x1p113 * Cbrt(0x1.0'0000'0000'0002p2))); - if (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()) { + if (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()) { EXPECT_THAT(method_5²Z4¹FMA::Cbrt(0x1p-438), Eq(0x1p-146)); EXPECT_THAT( @@ -237,7 +237,7 @@ TEST_F(CubeRootTest, ParticularlyDifficultRounding) { Eq(cbrt_y.rounded_to_nearest)); EXPECT_THAT(method_3²ᴄZ5¹::Cbrt(y), AllOf(Ne(cbrt_y.rounded_to_nearest), Eq(cbrt_y.rounded_down))); - if (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()) { + if (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()) { EXPECT_THAT(method_5²Z4¹FMA::Cbrt(y), Eq(cbrt_y.rounded_to_nearest)); EXPECT_THAT(method_5²Z4¹FMA::Cbrt(y), diff --git a/numerics/fma.hpp b/numerics/fma.hpp index ac24631940..e7d72ea8ee 100644 --- a/numerics/fma.hpp +++ b/numerics/fma.hpp @@ -21,7 +21,7 @@ constexpr bool CanEmitFMAInstructions = false; #if PRINCIPIA_USE_FMA_IF_AVAILABLE() #define PRINCIPIA_USE_HARDWARE_FMA_DEFAULT \ - (CanEmitFMAInstructions && cpuid_feature_flags::FMA.IsSet()); + (CanEmitFMAInstructions && CPUIDFeatureFlag::FMA.IsSet()); #else #define PRINCIPIA_USE_HARDWARE_FMA_DEFAULT false; #endif diff --git a/numerics/fma_test.cpp b/numerics/fma_test.cpp index 21a56de45b..391ac74890 100644 --- a/numerics/fma_test.cpp +++ b/numerics/fma_test.cpp @@ -17,7 +17,7 @@ class FMATest : public testing::Test { protected: void SetUp() override { // Note that we test even if |UseHardwareFMA| is false, i.e., even in debug. - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } } diff --git a/quantities/elementary_functions_test.cpp b/quantities/elementary_functions_test.cpp index 7a696f977d..1aec37265a 100644 --- a/quantities/elementary_functions_test.cpp +++ b/quantities/elementary_functions_test.cpp @@ -37,7 +37,7 @@ using namespace principia::testing_utilities::_vanishes_before; class ElementaryFunctionsTest : public testing::Test {}; TEST_F(ElementaryFunctionsTest, FMA) { - if (!CanEmitFMAInstructions || !cpuid_feature_flags::FMA.IsSet()) { + if (!CanEmitFMAInstructions || !CPUIDFeatureFlag::FMA.IsSet()) { GTEST_SKIP() << "Cannot test FMA on a machine without FMA"; } EXPECT_EQ(11 * Coulomb,