Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 32 additions & 67 deletions include/xsimd/config/xsimd_cpu_features_arm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
#define XSIMD_CPU_FEATURES_ARM_HPP

#include "./xsimd_config.hpp"

#if XSIMD_TARGET_ARM && XSIMD_HAVE_LINUX_GETAUXVAL
#include "../utils/bits.hpp"
#include "./xsimd_getauxval.hpp"

#if XSIMD_TARGET_ARM && XSIMD_HAVE_LINUX_GETAUXVAL
// HWCAP_XXX masks to use on getauxval results.
// Header does not exists on all architectures and masks are architecture
// specific.
Expand All @@ -36,87 +34,54 @@ namespace xsimd
* This is well defined on all architectures.
* It will always return false on non-ARM architectures.
*/
class arm_cpu_features
class arm_cpu_features : private linux_hwcap_backend_default
{
public:
arm_cpu_features() noexcept = default;
inline bool neon() const noexcept;
inline bool neon64() const noexcept;
inline bool sve() const noexcept;
inline bool i8mm() const noexcept;
};

/********************
* Implementation *
********************/

inline bool neon() const noexcept
{
inline bool arm_cpu_features::neon() const noexcept
{
#if XSIMD_TARGET_ARM && !XSIMD_TARGET_ARM64 && XSIMD_HAVE_LINUX_GETAUXVAL
return hwcap().has_feature(HWCAP_NEON);
return hwcap().has_feature(HWCAP_NEON);
#else
return static_cast<bool>(XSIMD_WITH_NEON);
return static_cast<bool>(XSIMD_WITH_NEON);
#endif
}
}

constexpr bool neon64() const noexcept
{
return static_cast<bool>(XSIMD_WITH_NEON64);
}
inline bool arm_cpu_features::neon64() const noexcept
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keeping constexpr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is just this one that would be constexpr. I made them homogeneous to clearly indicate that in general thsi API is for runtime information.

{
return static_cast<bool>(XSIMD_WITH_NEON64);
}

inline bool sve() const noexcept
{
inline bool arm_cpu_features::sve() const noexcept
{
#if XSIMD_TARGET_ARM64 && XSIMD_HAVE_LINUX_GETAUXVAL
return hwcap().has_feature(HWCAP_SVE);
return hwcap().has_feature(HWCAP_SVE);
#else
return false;
return false;
#endif
}

inline bool i8mm() const noexcept
{
}

inline bool arm_cpu_features::i8mm() const noexcept
{
#if XSIMD_TARGET_ARM64 && XSIMD_HAVE_LINUX_GETAUXVAL
#ifdef HWCAP2_I8MM
return hwcap2().has_feature(HWCAP2_I8MM);
return hwcap2().has_feature(HWCAP2_I8MM);
#else
// Possibly missing on older Linux distributions
return hwcap2().has_feature(1 << 13);
// Possibly missing on older Linux distributions
return hwcap2().has_feature(1 << 13);
#endif
#else
return false;
return false;
#endif
}

private:
#if XSIMD_TARGET_ARM && XSIMD_HAVE_LINUX_GETAUXVAL
enum class status
{
hwcap_valid = 0,
hwcap2_valid = 1,
};

using status_bitset = utils::uint_bitset<status, std::uint32_t>;

mutable status_bitset m_status {};

mutable xsimd::linux_auxval m_hwcap {};

inline xsimd::linux_auxval const& hwcap() const noexcept
{
if (!m_status.bit_is_set<status::hwcap_valid>())
{
m_hwcap = xsimd::linux_auxval::read(AT_HWCAP);
m_status.set_bit<status::hwcap_valid>();
}
return m_hwcap;
}

#if XSIMD_TARGET_ARM64
mutable xsimd::linux_auxval m_hwcap2 {};

inline xsimd::linux_auxval const& hwcap2() const noexcept
{
if (!m_status.bit_is_set<status::hwcap2_valid>())
{
m_hwcap2 = xsimd::linux_auxval::read(AT_HWCAP2);
m_status.set_bit<status::hwcap2_valid>();
}
return m_hwcap2;
}
#endif
#endif // XSIMD_TARGET_ARM && XSIMD_HAVE_LINUX_GETAUXVAL
};
}
}
#endif
52 changes: 15 additions & 37 deletions include/xsimd/config/xsimd_cpu_features_riscv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
#define XSIMD_CPU_FEATURES_RISCV_HPP

#include "./xsimd_config.hpp"

#if XSIMD_TARGET_RISCV && XSIMD_HAVE_LINUX_GETAUXVAL
#include "../utils/bits.hpp"
#include "./xsimd_getauxval.hpp"

#if XSIMD_TARGET_RISCV && XSIMD_HAVE_LINUX_GETAUXVAL
// HWCAP_XXX masks to use on getauxval results.
// Header does not exists on all architectures and masks are architecture
// specific.
Expand All @@ -26,49 +24,29 @@

namespace xsimd
{
class riscv_cpu_features
class riscv_cpu_features : private linux_hwcap_backend_default
{
public:
riscv_cpu_features() noexcept = default;
inline bool rvv() const noexcept;
};

inline bool rvv() const noexcept
{
/********************
* Implementation *
********************/

inline bool riscv_cpu_features::rvv() const noexcept
{
#if XSIMD_TARGET_RISCV && XSIMD_HAVE_LINUX_GETAUXVAL
#ifdef HWCAP_V
return hwcap().has_feature(HWCAP_V);
return hwcap().has_feature(HWCAP_V);
#else
// Possibly missing on older Linux distributions
return hwcap().has_feature(1 << ('V' - 'A'));
// Possibly missing on older Linux distributions
return hwcap().has_feature(1 << ('V' - 'A'));
#endif
#else
return false;
#endif
}

private:
#if XSIMD_TARGET_RISCV && XSIMD_HAVE_LINUX_GETAUXVAL
enum class status
{
hwcap_valid = 0,
};

using status_bitset = utils::uint_bitset<status, std::uint32_t>;

mutable status_bitset m_status {};

mutable xsimd::linux_auxval m_hwcap {};

inline xsimd::linux_auxval const& hwcap() const noexcept
{
if (!m_status.bit_is_set<status::hwcap_valid>())
{
m_hwcap = xsimd::linux_auxval::read(AT_HWCAP);
m_status.set_bit<status::hwcap_valid>();
}
return m_hwcap;
}
return false;
#endif
};
}
}

#endif
68 changes: 68 additions & 0 deletions include/xsimd/config/xsimd_getauxval.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#ifndef XSIMD_GETAUXVAL_HPP
#define XSIMD_GETAUXVAL_HPP

#include "../utils/bits.hpp"
#include "./xsimd_config.hpp"

#if XSIMD_HAVE_LINUX_GETAUXVAL
Expand Down Expand Up @@ -68,6 +69,49 @@ namespace xsimd
}
};

class linux_hwcap_backend
{
public:
inline linux_auxval hwcap() const noexcept;

inline linux_auxval hwcap2() const noexcept;

private:
enum class status
{
hwcap_valid = 0,
hwcap2_valid = 1,
};

using status_bitset = utils::uint_bitset<status, std::uint32_t>;

mutable status_bitset m_status {};
mutable xsimd::linux_auxval m_hwcap {};
mutable xsimd::linux_auxval m_hwcap2 {};
};

class linux_hwcap_backend_noop
{
public:
inline linux_auxval hwcap() const noexcept { return {}; }

inline linux_auxval hwcap2() const noexcept { return {}; }
};

#if XSIMD_HAVE_LINUX_GETAUXVAL
using linux_hwcap_backend_default = linux_hwcap_backend;
#else
// Contrary to CPUID that is only used on one architecture, HWCAP are
// available on multiple architectures with different meaning for the
// different bit fields.
// We use the Linux `HWCAP` constants directly to avoid repetition, so
// we could not use a default implementation without already being on
// Linux anyways.
struct linux_hwcap_backend_default
{
};
#endif

/********************
* Implementation *
********************/
Expand All @@ -86,6 +130,30 @@ namespace xsimd
}
#endif
}

inline linux_auxval linux_hwcap_backend::hwcap() const noexcept
{
if (!m_status.bit_is_set<status::hwcap_valid>())
{
#if XSIMD_HAVE_LINUX_GETAUXVAL
m_hwcap = linux_auxval::read(AT_HWCAP);
#endif
m_status.set_bit<status::hwcap_valid>();
}
return m_hwcap;
}

inline linux_auxval linux_hwcap_backend::hwcap2() const noexcept
{
if (!m_status.bit_is_set<status::hwcap2_valid>())
{
#if XSIMD_HAVE_LINUX_GETAUXVAL
m_hwcap2 = linux_auxval::read(AT_HWCAP2);
#endif
m_status.set_bit<status::hwcap2_valid>();
}
return m_hwcap2;
}
}

#endif
Loading