Skip to content

Commit

Permalink
beef up CPUID
Browse files Browse the repository at this point in the history
  • Loading branch information
eggrobin committed Mar 30, 2024
1 parent c04ac5c commit 3fdc292
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 47 deletions.
58 changes: 43 additions & 15 deletions base/cpuid.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "base/cpuid.hpp"

#include <map>
#include <string>

#include "base/macros.hpp" // 🧙 For PRINCIPIA_COMPILER_MSVC.
Expand All @@ -9,18 +10,15 @@
#include <cpuid.h>
#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<CPUIDFeatureFlag const&> CPUIDFlags;

Check warning on line 21 in base/cpuid.cpp

View workflow job for this annotation

GitHub Actions / check-cpp

build/include_what_you_use

Add #include <vector> for vector<>

CPUIDResult CPUID(std::uint32_t const eax, std::uint32_t const ecx) {
#if PRINCIPIA_COMPILER_MSVC
Expand All @@ -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');
Expand All @@ -48,17 +65,28 @@ std::string CPUVendorIdentificationString() {
return result;
}

CPUFeatureFlags operator|(CPUFeatureFlags const left,
CPUFeatureFlags const right) {
return static_cast<CPUFeatureFlags>(static_cast<std::uint64_t>(left) |
static_cast<std::uint64_t>(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<CPUFeatureFlags>(
(ecx_bit * leaf_1.ecx | edx_bit * leaf_1.edx) &
static_cast<std::uint64_t>(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
Expand Down
87 changes: 61 additions & 26 deletions base/cpuid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,82 @@

#include <cstdint>
#include <string>
#include <string_view>

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

Check warning on line 82 in base/cpuid.hpp

View workflow job for this annotation

GitHub Actions / check-cpp

readability/namespace

Namespace should be terminated with "// namespace cpuid_feature_flags"
} // namespace base

Check warning on line 83 in base/cpuid.hpp

View workflow job for this annotation

GitHub Actions / check-cpp

readability/namespace

Namespace should be terminated with "// namespace _cpuid"
Expand Down
11 changes: 5 additions & 6 deletions base/cpuid_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3fdc292

Please sign in to comment.