Program Listing for File instruction-set.hpp

Return to documentation for file (include/proxsuite/helpers/instruction-set.hpp)

//
// Copyright (c) 2022 INRIA
//
#ifndef PROXSUITE_HELPERS_INSTRUCTION_SET_HPP
#define PROXSUITE_HELPERS_INSTRUCTION_SET_HPP

#include <vector>
#include <bitset>
#include <array>

#ifndef _WIN32
#include <cpuid.h>
#else
#include <intrin.h>
#endif

namespace proxsuite {
namespace helpers {

namespace internal {
inline void
cpuid(std::array<int, 4>& cpui, int level)
{
#ifndef _WIN32
  __cpuid(level, cpui[0], cpui[1], cpui[2], cpui[3]);
#else
  __cpuid(cpui.data(), level);
#endif
}

inline void
cpuidex(std::array<int, 4>& cpui, int level, int count)
{
#ifndef _WIN32
  __cpuid_count(level, count, cpui[0], cpui[1], cpui[2], cpui[3]);
#else
  __cpuidex(cpui.data(), level, count);
#endif
}

template<typename T = void>
struct InstructionSetBase
{
protected:
  struct Data
  {
    Data()
      : nIds_{ 0 }
      , nExIds_{ 0 }
      , isIntel_{ false }
      , isAMD_{ false }
      , f_1_ECX_{ 0 }
      , f_1_EDX_{ 0 }
      , f_7_EBX_{ 0 }
      , f_7_ECX_{ 0 }
      , f_81_ECX_{ 0 }
      , f_81_EDX_{ 0 }
      , data_{}
      , extdata_{}
    {
      std::array<int, 4> cpui;
      typedef unsigned long long bistset_equivalent_type;

      // Calling __cpuid with 0x0 as the function_id argument
      // gets the number of the highest valid function ID.
      internal::cpuid(cpui, 0);
      nIds_ = cpui[0];

      for (int i = 0; i <= nIds_; ++i) {
        internal::cpuidex(cpui, i, 0);
        data_.push_back(cpui);
      }

      // Capture vendor string
      char vendor[0x20];
      memset(vendor, 0, sizeof(vendor));
      *reinterpret_cast<int*>(vendor) = data_[0][1];
      *reinterpret_cast<int*>(vendor + 4) = data_[0][3];
      *reinterpret_cast<int*>(vendor + 8) = data_[0][2];
      vendor_ = vendor;
      if (vendor_ == "GenuineIntel") {
        isIntel_ = true;
      } else if (vendor_ == "AuthenticAMD") {
        isAMD_ = true;
      }

      // load bitset with flags for function 0x00000001
      if (nIds_ >= 1) {
        f_1_ECX_ = static_cast<bistset_equivalent_type>(data_[1][2]);
        f_1_EDX_ = static_cast<bistset_equivalent_type>(data_[1][3]);
      }

      // load bitset with flags for function 0x00000007
      if (nIds_ >= 7) {
        f_7_EBX_ = static_cast<bistset_equivalent_type>(data_[7][1]);
        f_7_ECX_ = static_cast<bistset_equivalent_type>(data_[7][2]);
      }

      // Calling __cpuid with 0x80000000 as the function_id argument
      // gets the number of the highest valid extended ID.
      internal::cpuid(cpui, static_cast<int>(0x80000000));
      nExIds_ = cpui[0];

      char brand[0x40];
      memset(brand, 0, sizeof(brand));

      for (int i = static_cast<int>(0x80000000); i <= nExIds_; ++i) {
        internal::cpuidex(cpui, i, 0);
        extdata_.push_back(cpui);
      }

      // load bitset with flags for function 0x80000001
      if (nExIds_ >= static_cast<int>(0x80000001)) {
        f_81_ECX_ = static_cast<bistset_equivalent_type>(extdata_[1][2]);
        f_81_EDX_ = static_cast<bistset_equivalent_type>(extdata_[1][3]);
      }

      // Interpret CPU brand string if reported
      if (nExIds_ >= static_cast<int>(0x80000004)) {
        memcpy(brand, extdata_[2].data(), sizeof(cpui));
        memcpy(brand + 16, extdata_[3].data(), sizeof(cpui));
        memcpy(brand + 32, extdata_[4].data(), sizeof(cpui));
        brand_ = brand;
      }
    };

    int nIds_;
    int nExIds_;
    std::string vendor_;
    std::string brand_;
    bool isIntel_;
    bool isAMD_;
    std::bitset<32> f_1_ECX_;
    std::bitset<32> f_1_EDX_;
    std::bitset<32> f_7_EBX_;
    std::bitset<32> f_7_ECX_;
    std::bitset<32> f_81_ECX_;
    std::bitset<32> f_81_EDX_;
    std::vector<std::array<int, 4>> data_;
    std::vector<std::array<int, 4>> extdata_;
  };

  static const Data data;
};

template<>
const typename InstructionSetBase<>::Data InstructionSetBase<>::data =
  typename InstructionSetBase<>::Data();
} // namespace internal

// Adapted from
// https://docs.microsoft.com/fr-fr/cpp/intrinsics/cpuid-cpuidex?view=msvc-170
struct InstructionSet : public internal::InstructionSetBase<>
{
  typedef internal::InstructionSetBase<> Base;

  static std::string vendor(void) { return Base::data.vendor_; }
  static std::string brand(void) { return Base::data.brand_; }

  static bool has_SSE3(void) { return Base::data.f_1_ECX_[0]; }
  static bool has_PCLMULQDQ(void) { return Base::data.f_1_ECX_[1]; }
  static bool has_MONITOR(void) { return Base::data.f_1_ECX_[3]; }
  static bool has_SSSE3(void) { return Base::data.f_1_ECX_[9]; }
  static bool has_FMA(void) { return Base::data.f_1_ECX_[12]; }
  static bool has_CMPXCHG16B(void) { return Base::data.f_1_ECX_[13]; }
  static bool has_SSE41(void) { return Base::data.f_1_ECX_[19]; }
  static bool has_SSE42(void) { return Base::data.f_1_ECX_[20]; }
  static bool has_MOVBE(void) { return Base::data.f_1_ECX_[22]; }
  static bool has_POPCNT(void) { return Base::data.f_1_ECX_[23]; }
  static bool has_AES(void) { return Base::data.f_1_ECX_[25]; }
  static bool has_XSAVE(void) { return Base::data.f_1_ECX_[26]; }
  static bool has_OSXSAVE(void) { return Base::data.f_1_ECX_[27]; }
  static bool has_AVX(void) { return Base::data.f_1_ECX_[28]; }
  static bool has_F16C(void) { return Base::data.f_1_ECX_[29]; }
  static bool has_RDRAND(void) { return Base::data.f_1_ECX_[30]; }

  static bool has_MSR(void) { return Base::data.f_1_EDX_[5]; }
  static bool has_CX8(void) { return Base::data.f_1_EDX_[8]; }
  static bool has_SEP(void) { return Base::data.f_1_EDX_[11]; }
  static bool has_CMOV(void) { return Base::data.f_1_EDX_[15]; }
  static bool has_CLFSH(void) { return Base::data.f_1_EDX_[19]; }
  static bool has_MMX(void) { return Base::data.f_1_EDX_[23]; }
  static bool has_FXSR(void) { return Base::data.f_1_EDX_[24]; }
  static bool has_SSE(void) { return Base::data.f_1_EDX_[25]; }
  static bool has_SSE2(void) { return Base::data.f_1_EDX_[26]; }

  static bool has_FSGSBASE(void) { return Base::data.f_7_EBX_[0]; }
  static bool has_AVX512VBMI(void) { return Base::data.f_7_EBX_[1]; }
  static bool has_BMI1(void) { return Base::data.f_7_EBX_[3]; }
  static bool has_HLE(void)
  {
    return Base::data.isIntel_ && Base::data.f_7_EBX_[4];
  }
  static bool has_AVX2(void) { return Base::data.f_7_EBX_[5]; }
  static bool has_BMI2(void) { return Base::data.f_7_EBX_[8]; }
  static bool has_ERMS(void) { return Base::data.f_7_EBX_[9]; }
  static bool has_INVPCID(void) { return Base::data.f_7_EBX_[10]; }
  static bool has_RTM(void)
  {
    return Base::data.isIntel_ && Base::data.f_7_EBX_[11];
  }
  static bool has_AVX512F(void) { return Base::data.f_7_EBX_[16]; }
  static bool has_AVX512DQ(void) { return Base::data.f_7_EBX_[17]; }
  static bool has_RDSEED(void) { return Base::data.f_7_EBX_[18]; }
  static bool has_ADX(void) { return Base::data.f_7_EBX_[19]; }
  static bool has_AVX512IFMA(void) { return Base::data.f_7_EBX_[21]; }
  static bool has_AVX512PF(void) { return Base::data.f_7_EBX_[26]; }
  static bool has_AVX512ER(void) { return Base::data.f_7_EBX_[27]; }
  static bool has_AVX512CD(void) { return Base::data.f_7_EBX_[28]; }
  static bool has_SHA(void) { return Base::data.f_7_EBX_[29]; }
  static bool has_AVX512BW(void) { return Base::data.f_7_EBX_[30]; }
  static bool has_AVX512VL(void) { return Base::data.f_7_EBX_[31]; }

  static bool has_PREFETCHWT1(void) { return Base::data.f_7_ECX_[0]; }

  static bool has_LAHF(void) { return Base::data.f_81_ECX_[0]; }
  static bool has_LZCNT(void)
  {
    return Base::data.isIntel_ && Base::data.f_81_ECX_[5];
  }
  static bool has_ABM(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_ECX_[5];
  }
  static bool has_SSE4a(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_ECX_[6];
  }
  static bool has_XOP(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_ECX_[11];
  }
  static bool has_FMA4(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_ECX_[16];
  }
  static bool has_TBM(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_ECX_[21];
  }

  static bool has_SYSCALL(void)
  {
    return Base::data.isIntel_ && Base::data.f_81_EDX_[11];
  }
  static bool has_MMXEXT(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_EDX_[22];
  }
  static bool has_RDTSCP(void)
  {
    return Base::data.isIntel_ && Base::data.f_81_EDX_[27];
  }
  static bool has_x64(void)
  {
    return Base::data.isIntel_ && Base::data.f_81_EDX_[29];
  }
  static bool has_3DNOWEXT(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_EDX_[30];
  }
  static bool has_3DNOW(void)
  {
    return Base::data.isAMD_ && Base::data.f_81_EDX_[31];
  }
};

} // helpers
} // proxsuite

#endif // ifndef PROXSUITE_HELPERS_INSTRUCTION_SET_HPP