Skip to content

Commit

Permalink
Class members
Browse files Browse the repository at this point in the history
  • Loading branch information
eggrobin committed Mar 31, 2024
1 parent 2b6e810 commit a732ed6
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 62 deletions.
58 changes: 40 additions & 18 deletions base/cpuid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>

#include "absl/strings/str_join.h"
#include "base/not_null.hpp"
#include "base/macros.hpp" // 🧙 For PRINCIPIA_COMPILER_MSVC.
#include "glog/logging.h"

Expand All @@ -20,17 +21,17 @@ 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 <vector> in the header.
// The function is not in the unnamed namespace below; the flag registry must be
// global.
std::vector<CPUIDFeatureFlag>& CPUIDFlags() {
static std::vector<CPUIDFeatureFlag> result;
std::vector<not_null<CPUIDFeatureFlag const*>>& CPUIDFlags() {
static std::vector<not_null<CPUIDFeatureFlag const*>> result;
return result;
}

namespace {

CPUIDResult CPUID(std::uint32_t const eax, std::uint32_t const ecx) {
#if PRINCIPIA_COMPILER_MSVC
int result[4];
Expand All @@ -48,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,
Expand All @@ -56,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() {
Expand Down Expand Up @@ -97,9 +119,9 @@ std::string CPUFeatures() {
| std::ranges::to<std::string>();
#else // TODO(egg): Get rid of this once clang really has C++23.
std::vector<std::string_view> 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, " ");
Expand Down
49 changes: 22 additions & 27 deletions base/cpuid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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_;
Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions base/cpuid_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"))));
Expand Down
6 changes: 3 additions & 3 deletions geometry/grassmann_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion geometry/point_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
10 changes: 5 additions & 5 deletions numerics/cbrt_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Rounding::Faithful>(y),
AnyOf(cbrt_y.rounded_down, cbrt_y.rounded_up));
EXPECT_THAT(method_5²Z4¹FMA::Cbrt<Rounding::Correct>(y),
Expand All @@ -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));
}
Expand All @@ -140,7 +140,7 @@ TEST_F(CubeRootTest, Rescaling) {
Eq(Cbrt(2)));
EXPECT_THAT(0x1p358 * method_3²ᴄZ5¹::Cbrt<Rounding::Correct>(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<Rounding::Correct>(0x1p1021),
Eq(Cbrt(2)));
EXPECT_THAT(0x1p341 * method_5²Z4¹FMA::Cbrt<Rounding::Correct>(0x1p-1022),
Expand Down Expand Up @@ -206,7 +206,7 @@ TEST_F(CubeRootTest, BoundsOfTheRescalingRange) {
Eq(0x1p113 * Cbrt(4)));
EXPECT_THAT(method_3²ᴄZ5¹::Cbrt<Rounding::Correct>(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<Rounding::Correct>(0x1p-438),
Eq(0x1p-146));
EXPECT_THAT(
Expand Down Expand Up @@ -237,7 +237,7 @@ TEST_F(CubeRootTest, ParticularlyDifficultRounding) {
Eq(cbrt_y.rounded_to_nearest));
EXPECT_THAT(method_3²ᴄZ5¹::Cbrt<Rounding::Faithful>(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<Rounding::Correct>(y),
Eq(cbrt_y.rounded_to_nearest));
EXPECT_THAT(method_5²Z4¹FMA::Cbrt<Rounding::Faithful>(y),
Expand Down
2 changes: 1 addition & 1 deletion numerics/fma.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion numerics/fma_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
}
Expand Down
2 changes: 1 addition & 1 deletion quantities/elementary_functions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit a732ed6

Please sign in to comment.