From d9fb604cd70046f3d5cd966a3debc18541be1bf4 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 9 Apr 2026 17:20:00 +0200 Subject: [PATCH 1/2] Add a getauxval backend --- .../xsimd/config/xsimd_cpu_features_arm.hpp | 45 +------------- .../xsimd/config/xsimd_cpu_features_riscv.hpp | 30 +--------- include/xsimd/config/xsimd_getauxval.hpp | 60 +++++++++++++++++++ 3 files changed, 64 insertions(+), 71 deletions(-) diff --git a/include/xsimd/config/xsimd_cpu_features_arm.hpp b/include/xsimd/config/xsimd_cpu_features_arm.hpp index d52a8a219..ec4c07bb1 100644 --- a/include/xsimd/config/xsimd_cpu_features_arm.hpp +++ b/include/xsimd/config/xsimd_cpu_features_arm.hpp @@ -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. @@ -36,7 +34,7 @@ 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; @@ -78,45 +76,6 @@ namespace xsimd 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; - - 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()) - { - m_hwcap = xsimd::linux_auxval::read(AT_HWCAP); - m_status.set_bit(); - } - 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()) - { - m_hwcap2 = xsimd::linux_auxval::read(AT_HWCAP2); - m_status.set_bit(); - } - return m_hwcap2; - } -#endif -#endif // XSIMD_TARGET_ARM && XSIMD_HAVE_LINUX_GETAUXVAL }; } #endif diff --git a/include/xsimd/config/xsimd_cpu_features_riscv.hpp b/include/xsimd/config/xsimd_cpu_features_riscv.hpp index 2520a249e..c407ed6da 100644 --- a/include/xsimd/config/xsimd_cpu_features_riscv.hpp +++ b/include/xsimd/config/xsimd_cpu_features_riscv.hpp @@ -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. @@ -26,7 +24,7 @@ namespace xsimd { - class riscv_cpu_features + class riscv_cpu_features : private linux_hwcap_backend_default { public: riscv_cpu_features() noexcept = default; @@ -44,30 +42,6 @@ namespace xsimd return false; #endif } - - private: -#if XSIMD_TARGET_RISCV && XSIMD_HAVE_LINUX_GETAUXVAL - enum class status - { - hwcap_valid = 0, - }; - - using status_bitset = utils::uint_bitset; - - 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()) - { - m_hwcap = xsimd::linux_auxval::read(AT_HWCAP); - m_status.set_bit(); - } - return m_hwcap; - } -#endif }; } diff --git a/include/xsimd/config/xsimd_getauxval.hpp b/include/xsimd/config/xsimd_getauxval.hpp index dfd2c3b7d..33fd38eda 100644 --- a/include/xsimd/config/xsimd_getauxval.hpp +++ b/include/xsimd/config/xsimd_getauxval.hpp @@ -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 @@ -68,6 +69,41 @@ namespace xsimd } }; + class linux_hwcap_backend_full + { + 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; + + 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_full; +#else + using linux_hwcap_backend_default = linux_hwcap_backend_noop; +#endif + /******************** * Implementation * ********************/ @@ -86,6 +122,30 @@ namespace xsimd } #endif } + + inline linux_auxval linux_hwcap_backend_full::hwcap() const noexcept + { + if (!m_status.bit_is_set()) + { +#if XSIMD_HAVE_LINUX_GETAUXVAL + m_hwcap = linux_auxval::read(AT_HWCAP); +#endif + m_status.set_bit(); + } + return m_hwcap; + } + + inline linux_auxval linux_hwcap_backend_full::hwcap2() const noexcept + { + if (!m_status.bit_is_set()) + { +#if XSIMD_HAVE_LINUX_GETAUXVAL + m_hwcap2 = linux_auxval::read(AT_HWCAP2); +#endif + m_status.set_bit(); + } + return m_hwcap2; + } } #endif From 2db459a6b167c54d5f92dd9cd7cd8007dd9f3778 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 9 Apr 2026 17:25:53 +0200 Subject: [PATCH 2/2] Split implementation --- .../xsimd/config/xsimd_cpu_features_arm.hpp | 54 ++++++++++--------- .../xsimd/config/xsimd_cpu_features_riscv.hpp | 22 ++++---- include/xsimd/config/xsimd_getauxval.hpp | 18 +++++-- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/include/xsimd/config/xsimd_cpu_features_arm.hpp b/include/xsimd/config/xsimd_cpu_features_arm.hpp index ec4c07bb1..add0162c4 100644 --- a/include/xsimd/config/xsimd_cpu_features_arm.hpp +++ b/include/xsimd/config/xsimd_cpu_features_arm.hpp @@ -37,45 +37,51 @@ namespace xsimd 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(XSIMD_WITH_NEON); + return static_cast(XSIMD_WITH_NEON); #endif - } + } - constexpr bool neon64() const noexcept - { - return static_cast(XSIMD_WITH_NEON64); - } + inline bool arm_cpu_features::neon64() const noexcept + { + return static_cast(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 - } - }; + } } #endif diff --git a/include/xsimd/config/xsimd_cpu_features_riscv.hpp b/include/xsimd/config/xsimd_cpu_features_riscv.hpp index c407ed6da..62e900d3b 100644 --- a/include/xsimd/config/xsimd_cpu_features_riscv.hpp +++ b/include/xsimd/config/xsimd_cpu_features_riscv.hpp @@ -27,22 +27,26 @@ namespace xsimd class riscv_cpu_features : private linux_hwcap_backend_default { public: - riscv_cpu_features() noexcept = default; + inline bool rvv() const noexcept; + }; + + /******************** + * Implementation * + ********************/ - inline bool rvv() const noexcept - { + 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; + return false; #endif - } - }; + } } #endif diff --git a/include/xsimd/config/xsimd_getauxval.hpp b/include/xsimd/config/xsimd_getauxval.hpp index 33fd38eda..0c9cff57e 100644 --- a/include/xsimd/config/xsimd_getauxval.hpp +++ b/include/xsimd/config/xsimd_getauxval.hpp @@ -69,7 +69,7 @@ namespace xsimd } }; - class linux_hwcap_backend_full + class linux_hwcap_backend { public: inline linux_auxval hwcap() const noexcept; @@ -99,9 +99,17 @@ namespace xsimd }; #if XSIMD_HAVE_LINUX_GETAUXVAL - using linux_hwcap_backend_default = linux_hwcap_backend_full; + using linux_hwcap_backend_default = linux_hwcap_backend; #else - using linux_hwcap_backend_default = linux_hwcap_backend_noop; + // 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 /******************** @@ -123,7 +131,7 @@ namespace xsimd #endif } - inline linux_auxval linux_hwcap_backend_full::hwcap() const noexcept + inline linux_auxval linux_hwcap_backend::hwcap() const noexcept { if (!m_status.bit_is_set()) { @@ -135,7 +143,7 @@ namespace xsimd return m_hwcap; } - inline linux_auxval linux_hwcap_backend_full::hwcap2() const noexcept + inline linux_auxval linux_hwcap_backend::hwcap2() const noexcept { if (!m_status.bit_is_set()) {