Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-07 10:37:15

0001 /***************************************************************************
0002  * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and         *
0003  * Martin Renou                                                             *
0004  * Copyright (c) QuantStack                                                 *
0005  * Copyright (c) Serge Guelton                                              *
0006  *                                                                          *
0007  * Distributed under the terms of the BSD 3-Clause License.                 *
0008  *                                                                          *
0009  * The full license is in the file LICENSE, distributed with this software. *
0010  ****************************************************************************/
0011 
0012 #ifndef XSIMD_CPU_FEATURES_X86_HPP
0013 #define XSIMD_CPU_FEATURES_X86_HPP
0014 
0015 #include <array>
0016 #include <cassert>
0017 #include <cstdint>
0018 #include <cstring>
0019 #include <type_traits>
0020 #if XSIMD_CPP_VERSION >= 201703L
0021 #include <string_view>
0022 #endif
0023 
0024 #include "../utils/bits.hpp"
0025 #include "./xsimd_config.hpp"
0026 
0027 #if XSIMD_TARGET_X86 && defined(_MSC_VER)
0028 #include <intrin.h> // Contains the definition of __cpuidex
0029 #endif
0030 
0031 namespace xsimd
0032 {
0033     namespace detail
0034     {
0035         using x86_reg32_t = std::uint32_t;
0036 
0037         using cpuid_reg_t = std::array<x86_reg32_t, 4>;
0038 
0039         /**
0040          * CPU Identification (CPUID) instruction results.
0041          *
0042          * The CPUID instruction provides detailed information about the processor,
0043          * including supported instruction set extensions (SSE, AVX, AVX-512, etc.).
0044          * This function is well defined on all architectures but will return all zeros
0045          * on all non-x86 architectures.
0046          *
0047          * @param leaf The value inputted to the EAX register.
0048          * @param subleaf The value inputted to the ECX register.
0049          *
0050          * @see https://en.wikipedia.org/wiki/CPUID
0051          */
0052         inline cpuid_reg_t x86_cpuid(int leaf, int subleaf = 0) noexcept;
0053 
0054         inline x86_reg32_t x86_xcr0_low() noexcept;
0055 
0056         /** A strongly type bitset for a 32 bits register. */
0057         template <typename E>
0058         using x86_reg32_bitset = utils::uint_bitset<E, x86_reg32_t>;
0059 
0060         /** A wrapper to attach a register bitfield descriptor and its CPUID index. */
0061         template <typename E, x86_reg32_t I>
0062         struct x86_reg_id
0063         {
0064             static constexpr x86_reg32_t index = I;
0065             using bits = E;
0066 
0067             static_assert(index >= 0 && index < 4, "At most 4 register in CPUID");
0068         };
0069 
0070         /** Find the register id with index k. */
0071         template <x86_reg32_t K, typename... reg_ids>
0072         struct find_reg_k;
0073 
0074         /** Find the register id with index k (empty / nothing found case). */
0075         template <x86_reg32_t K, typename... reg_ids>
0076         struct find_reg_k
0077         {
0078             using type = x86_reg_id<void, 0>;
0079         };
0080 
0081         /** Find the register id with index k (recursive case). */
0082         template <x86_reg32_t K, typename reg_id_head, typename... reg_id_tail>
0083         struct find_reg_k<K, reg_id_head, reg_id_tail...>
0084         {
0085             using type = std::conditional_t<
0086                 reg_id_head::index == K,
0087                 reg_id_head,
0088                 typename find_reg_k<K, reg_id_tail...>::type>;
0089         };
0090 
0091         /**
0092          * A class with strongly typed bitfield for `CPUID` registers.
0093          *
0094          * The class stores a variable number of register (up to four) from the CPUID
0095          * output. This is a space optimization to avoid storing many zeros in the
0096          * final `x86_cpu_features`.
0097          * As a result, some of the type aliases `eax`, `ebx`, `ecx`, `edx` may be `void`.
0098          */
0099         template <x86_reg32_t leaf_num, x86_reg32_t subleaf_num, typename... reg_ids>
0100         class x86_cpuid_regs : private x86_reg32_bitset<typename reg_ids::bits>...
0101         {
0102         private:
0103             static_assert(sizeof...(reg_ids) <= 4, "At most 4 register in CPUID");
0104 
0105             /* Parse CPUINFO register value into individual bit components.*/
0106             constexpr explicit x86_cpuid_regs(const cpuid_reg_t& regs) noexcept
0107                 : x86_reg32_bitset<typename reg_ids::bits>(regs[reg_ids::index])...
0108             {
0109             }
0110 
0111         public:
0112             static constexpr x86_reg32_t leaf = leaf_num;
0113             static constexpr x86_reg32_t subleaf = subleaf_num;
0114 
0115             using eax = typename find_reg_k<0, reg_ids...>::type::bits;
0116             using ebx = typename find_reg_k<1, reg_ids...>::type::bits;
0117             using ecx = typename find_reg_k<2, reg_ids...>::type::bits;
0118             using edx = typename find_reg_k<3, reg_ids...>::type::bits;
0119 
0120             inline static x86_cpuid_regs read()
0121             {
0122                 return x86_cpuid_regs(detail::x86_cpuid(leaf, subleaf));
0123             }
0124 
0125             constexpr x86_cpuid_regs() noexcept = default;
0126 
0127             // TODO(C++17) compact version for which this was designed.
0128             // The else clause contains a very verbose port.
0129 #if 0
0130             using x86_reg32_bitset<typename reg_ids::bits>::all_bits_set...;
0131             using x86_reg32_bitset<typename reg_ids::bits>::get_range...;
0132 #else
0133 
0134         private:
0135             template <int N>
0136             struct m_empty_reg
0137             {
0138                 enum class type {};
0139             };
0140 
0141             using eax_or_empty = std::conditional_t<std::is_void<eax>::value, typename m_empty_reg<0>::type, eax>;
0142             using ebx_or_empty = std::conditional_t<std::is_void<ebx>::value, typename m_empty_reg<1>::type, ebx>;
0143             using ecx_or_empty = std::conditional_t<std::is_void<ecx>::value, typename m_empty_reg<2>::type, ecx>;
0144             using edx_or_empty = std::conditional_t<std::is_void<edx>::value, typename m_empty_reg<3>::type, edx>;
0145 
0146         public:
0147             template <eax_or_empty... bits, typename T = eax, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0148             constexpr bool all_bits_set() const noexcept
0149             {
0150                 return x86_reg32_bitset<eax>::template all_bits_set<bits...>();
0151             }
0152 
0153             template <eax_or_empty start, eax_or_empty end, typename T = eax, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0154             constexpr x86_reg32_t get_range() const noexcept
0155             {
0156                 return x86_reg32_bitset<eax>::template get_range<start, end>();
0157             }
0158 
0159             template <ebx_or_empty... bits, typename T = ebx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0160             constexpr bool all_bits_set() const noexcept
0161             {
0162                 return x86_reg32_bitset<ebx>::template all_bits_set<bits...>();
0163             }
0164 
0165             template <ebx_or_empty start, ebx_or_empty end, typename T = ebx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0166             constexpr x86_reg32_t get_range() const noexcept
0167             {
0168                 return x86_reg32_bitset<ebx>::template get_range<start, end>();
0169             }
0170 
0171             template <ecx_or_empty... bits, typename T = ecx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0172             constexpr bool all_bits_set() const noexcept
0173             {
0174                 return x86_reg32_bitset<ecx>::template all_bits_set<bits...>();
0175             }
0176 
0177             template <ecx_or_empty start, ecx_or_empty end, typename T = ecx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0178             constexpr x86_reg32_t get_range() const noexcept
0179             {
0180                 return x86_reg32_bitset<ecx>::template get_range<start, end>();
0181             }
0182 
0183             template <edx_or_empty... bits, typename T = edx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0184             constexpr bool all_bits_set() const noexcept
0185             {
0186                 return x86_reg32_bitset<edx>::template all_bits_set<bits...>();
0187             }
0188 
0189             template <edx_or_empty start, edx_or_empty end, typename T = edx, std::enable_if_t<!std::is_void<T>::value, int> = 0>
0190             constexpr x86_reg32_t get_range() const noexcept
0191             {
0192                 return x86_reg32_bitset<edx>::template get_range<start, end>();
0193             }
0194 
0195 #endif // C++17
0196         };
0197 
0198         template <bool extended>
0199         struct x86_cpuid_highest_func
0200         {
0201         private:
0202             using x86_reg32_t = detail::x86_reg32_t;
0203             using manufacturer_str = std::array<char, 3 * sizeof(x86_reg32_t)>;
0204 
0205         public:
0206             static constexpr x86_reg32_t leaf = extended ? 0x80000000 : 0x0;
0207 
0208             inline static x86_cpuid_highest_func read()
0209             {
0210                 auto regs = detail::x86_cpuid(0);
0211                 x86_cpuid_highest_func out {};
0212                 // Highest function parameter in EAX
0213                 out.m_highest_leaf = regs[0];
0214 
0215                 // Manufacturer string in EBX, EDX, ECX (in that order)
0216                 char* manuf = out.m_manufacturer_id.data();
0217                 std::memcpy(manuf + 0 * sizeof(x86_reg32_t), &regs[1], sizeof(x86_reg32_t));
0218                 std::memcpy(manuf + 1 * sizeof(x86_reg32_t), &regs[3], sizeof(x86_reg32_t));
0219                 std::memcpy(manuf + 2 * sizeof(x86_reg32_t), &regs[2], sizeof(x86_reg32_t));
0220 
0221                 return out;
0222             }
0223 
0224             constexpr x86_cpuid_highest_func() noexcept = default;
0225 
0226             /**
0227              * Highest available leaf in CPUID non-extended range.
0228              *
0229              * This is the highest function parameter (EAX) that can be passed to CPUID.
0230              * This is valid in the specified range:
0231              *   - if `extended` is `false`, that is below `0x80000000`,
0232              *   - if `extended` is `true`, that is above `0x80000000`,
0233              */
0234             constexpr x86_reg32_t highest_leaf() const noexcept
0235             {
0236                 return m_highest_leaf;
0237             }
0238 
0239             /**
0240              * The manufacturer ID string in a static array.
0241              *
0242              * This raw character array is case specific and may contain both leading
0243              * and trailing whitespaces.
0244              * It cannot be assumed to be null terminated.
0245              * This is not implemented for all manufacturer when `extended` is `true`.
0246              */
0247             constexpr manufacturer_str manufacturer_id_raw() const noexcept
0248             {
0249                 return m_manufacturer_id;
0250             }
0251 
0252 #if XSIMD_CPP_VERSION >= 201703L
0253             constexpr std::string_view manufacturer_id() const noexcept
0254             {
0255                 return { m_manufacturer_id.data(), m_manufacturer_id.size() };
0256             }
0257 #endif
0258 
0259         private:
0260             manufacturer_str m_manufacturer_id {};
0261             x86_reg32_t m_highest_leaf {};
0262         };
0263     }
0264 
0265     /**
0266      * Highest CPUID Function Parameter and Manufacturer ID (EAX=0).
0267      *
0268      * Returns the highest leaf value supported by CPUID in the standard range
0269      * (below 0x80000000), and the processor manufacturer ID string.
0270      *
0271      * @see https://en.wikipedia.org/wiki/CPUID
0272      */
0273     using x86_cpuid_leaf0 = detail::x86_cpuid_highest_func<false>;
0274 
0275     /**
0276      * Known processor manufacturer ID strings returned by CPUID leaf 0.
0277      *
0278      * The 12-byte manufacturer ID is stored in EBX, EDX, ECX (in that order).
0279      * Some strings are shared across physical CPUs, emulators, and virtual machines.
0280      * Obscure, defunct, and soft-core CPUs are not represented; they map to `unknown`.
0281      *
0282      * @see https://en.wikipedia.org/wiki/CPUID
0283      */
0284     enum class x86_manufacturer
0285     {
0286         /**
0287          * AMD ("AuthenticAMD", "AMD ISBETTER").
0288          *
0289          * "AMD ISBETTER" was used by early K5 engineering samples.
0290          */
0291         amd,
0292         /**
0293          * Intel ("GenuineIntel", "GenuineIotel").
0294          *
0295          * "GenuineIotel" is a rare typo variant seen on some chips.
0296          */
0297         intel,
0298         /**
0299          * VIA / Centaur ("CentaurHauls", "VIA VIA VIA ").
0300          *
0301          * Centaur Technology was acquired by VIA in 1999;
0302          * older chips report "CentaurHauls", newer ones "VIA VIA VIA ".
0303          */
0304         via,
0305         /** Zhaoxin ("  Shanghai  "). */
0306         zhaoxin,
0307         /** Hygon ("HygonGenuine"). */
0308         hygon,
0309         /**
0310          * Transmeta ("TransmetaCPU", "GenuineTMx86").
0311          *
0312          * Two different ID strings were used across product lines.
0313          */
0314         transmeta,
0315         /** MCST Elbrus ("E2K MACHINE "). */
0316         elbrus,
0317         /** Microsoft Virtual PC / x86-to-ARM ("Virtual CPU "). */
0318         microsoft_vpc,
0319         /** Unrecognized manufacturer ID string. */
0320         unknown,
0321     };
0322 
0323     /**
0324      * Parse a 12-byte CPUID manufacturer ID into an @ref x86_manufacturer value.
0325      *
0326      * The input is the raw character array returned by @ref x86_cpuid_leaf0::manufacturer_id_raw.
0327      * Unrecognized strings map to @ref x86_manufacturer::unknown.
0328      */
0329     inline x86_manufacturer x86_parse_manufacturer(const std::array<char, 12>& id) noexcept
0330     {
0331         auto eq = [&id](const char(&s)[13]) noexcept -> bool
0332         {
0333             return std::memcmp(id.data(), s, 12) == 0;
0334         };
0335         if (eq("GenuineIntel") || eq("GenuineIotel"))
0336             return x86_manufacturer::intel;
0337         if (eq("AuthenticAMD") || eq("AMD ISBETTER"))
0338             return x86_manufacturer::amd;
0339         if (eq("CentaurHauls") || eq("VIA VIA VIA "))
0340             return x86_manufacturer::via;
0341         if (eq("  Shanghai  "))
0342             return x86_manufacturer::zhaoxin;
0343         if (eq("HygonGenuine"))
0344             return x86_manufacturer::hygon;
0345         if (eq("TransmetaCPU") || eq("GenuineTMx86"))
0346             return x86_manufacturer::transmeta;
0347         if (eq("E2K MACHINE "))
0348             return x86_manufacturer::elbrus;
0349         if (eq("Virtual CPU "))
0350             return x86_manufacturer::microsoft_vpc;
0351         return x86_manufacturer::unknown;
0352     };
0353 
0354     /** Return a string representation of an @ref x86_manufacturer value. */
0355     constexpr const char* x86_manufacturer_name(x86_manufacturer m) noexcept
0356     {
0357         switch (m)
0358         {
0359         case x86_manufacturer::intel:
0360             return "intel";
0361         case x86_manufacturer::amd:
0362             return "amd";
0363         case x86_manufacturer::via:
0364             return "via";
0365         case x86_manufacturer::zhaoxin:
0366             return "zhaoxin";
0367         case x86_manufacturer::hygon:
0368             return "hygon";
0369         case x86_manufacturer::transmeta:
0370             return "transmeta";
0371         case x86_manufacturer::elbrus:
0372             return "elbrus";
0373         case x86_manufacturer::microsoft_vpc:
0374             return "microsoft_vpc";
0375         case x86_manufacturer::unknown:
0376             return "unknown";
0377         }
0378         return "invalid";
0379     }
0380 
0381     struct x86_cpuid_leaf1_traits
0382     {
0383         static constexpr detail::x86_reg32_t leaf = 1;
0384         static constexpr detail::x86_reg32_t subleaf = 0;
0385 
0386         enum class eax
0387         {
0388             /* Stepping ID bit range. */
0389             stepping_start = 0,
0390             stepping_end = 4,
0391             /* Model bit range. */
0392             model_start = 4,
0393             model_end = 8,
0394             /* Family ID bit range. */
0395             family_id_start = 8,
0396             family_id_end = 12,
0397             /* Extended Model ID bit range. */
0398             ext_model_start = 16,
0399             ext_model_end = 20,
0400             /* Extended Family ID bit range. */
0401             ext_family_start = 20,
0402             ext_family_end = 28,
0403         };
0404         enum class ecx
0405         {
0406             /* Streaming SIMD Extensions 3. */
0407             sse3 = 0,
0408             /* Supplemental Streaming SIMD Extensions 3. */
0409             ssse3 = 9,
0410             /* Fused multiply-add with 3 operands (FMA3). */
0411             fma3 = 12,
0412             /* Streaming SIMD Extensions 4.1. */
0413             sse4_1 = 19,
0414             /* Streaming SIMD Extensions 4.2. */
0415             sse4_2 = 20,
0416             /* Population count instruction (POPCNT). */
0417             popcnt = 23,
0418             /* Advanced Encryption Standard instruction set. */
0419             aes_ni = 25,
0420             /* OS has enabled XSAVE/XRSTOR for extended processor state management. */
0421             osxsave = 27,
0422             /* Advanced Vector Extensions (256-bit SIMD). */
0423             avx = 28,
0424             /* Half to single floating point conversion. */
0425             f16c = 29,
0426             /* On-chip random number generator. */
0427             rdrnd = 30,
0428         };
0429         enum class edx
0430         {
0431             /* Streaming SIMD Extensions 2. */
0432             sse2 = 26,
0433         };
0434 
0435         using regs_t = detail::x86_cpuid_regs<leaf, subleaf,
0436                                               detail::x86_reg_id<eax, 0>,
0437                                               detail::x86_reg_id<ecx, 2>,
0438                                               detail::x86_reg_id<edx, 3>>;
0439     };
0440 
0441     /**
0442      * Processor Info and Feature Bits.
0443      *
0444      * Utility class that can read and parse the registers for the first leaf level
0445      * of the CPUID instruction.
0446      * This is well defined on all architectures but will return all false on all
0447      * non-x86 architectures.
0448      *
0449      * @see https://en.wikipedia.org/wiki/CPUID
0450      */
0451     using x86_cpuid_leaf1 = typename x86_cpuid_leaf1_traits::regs_t;
0452 
0453     struct x86_cpuid_leaf7_traits
0454     {
0455         static constexpr detail::x86_reg32_t leaf = 7;
0456         static constexpr detail::x86_reg32_t subleaf = 0;
0457 
0458         enum class eax
0459         {
0460             /* Start bit for the encoding of the highest subleaf available. */
0461             highest_subleaf_start = 0,
0462             /* End bit for the encoding of the highest subleaf available. */
0463             highest_subleaf_end = 32,
0464         };
0465         enum class ebx
0466         {
0467             /* Bit Manipulation Instruction Set 1. */
0468             bmi1 = 3,
0469             /* Advanced Vector Extensions 2 (integer 256-bit SIMD). */
0470             avx2 = 5,
0471             /* Bit Manipulation Instruction Set 2. */
0472             bmi2 = 8,
0473             /* AVX-512 Foundation instructions. */
0474             avx512f = 16,
0475             /* AVX-512 Doubleword and Quadword instructions. */
0476             avx512dq = 17,
0477             /* Low-level access to the entropy-generating hardware. */
0478             rdseed = 18,
0479             /* Intel arbitrary precision add carry. */
0480             adx = 19,
0481             /* AVX-512 Integer Fused Multiply-Add instructions. */
0482             avx512ifma = 21,
0483             /* AVX-512 Prefetch instructions. */
0484             avx512pf = 26,
0485             /* AVX-512 Exponential and Reciprocal instructions. */
0486             avx512er = 27,
0487             /* AVX-512 Conflict Detection instructions. */
0488             avx512cd = 28,
0489             /* Sha-1 and Sha-256 extension. */
0490             sha = 29,
0491             /* AVX-512 Byte and Word instructions. */
0492             avx512bw = 30,
0493             /* AVX-512 Vector Length Extensions for xmm and ymm registers. */
0494             avx512vl = 31,
0495         };
0496         enum class ecx
0497         {
0498             /* AVX-512 Vector Bit Manipulation instructions. */
0499             avx512vbmi = 1,
0500             /* AVX-512 Vector Bit Manipulation instructions 2. */
0501             avx512vbmi2 = 6,
0502             /* Galois Field instructions. */
0503             gfni = 8,
0504             /* Vector Advanced Encryption Standard instructions. */
0505             vaes = 9,
0506             /* Carry-less multiplication quadword instruction. */
0507             vpclmulqdq = 10,
0508             /* AVX-512 Vector Neural Network instructions. */
0509             avx512vnni_bw = 11,
0510             /* AVX-512 bit algorithm instructions (BITALG). */
0511             avx512_bitalg = 12,
0512             /* AVX-512 vector population count for doubleword and quadword. */
0513             avx512_vpopcntdq = 14,
0514         };
0515         enum class edx
0516         {
0517             /* AVX-512 4-register neural network instructions (word variable precision). */
0518             avx512_4vnniw = 2,
0519             /* AVX-512 4-register multiply-accumulate single precision. */
0520             avx512_4fmaps = 3,
0521             /* AVX-512 intersect pairs of packed doubleword/quadword integers. */
0522             avx512_vp2intersect = 8,
0523             /* AVX-512 16-bit floating-point instructions. */
0524             avx512_fp16 = 23,
0525 
0526         };
0527 
0528         using regs_t = detail::x86_cpuid_regs<leaf, subleaf,
0529                                               detail::x86_reg_id<eax, 0>,
0530                                               detail::x86_reg_id<ebx, 1>,
0531                                               detail::x86_reg_id<ecx, 2>,
0532                                               detail::x86_reg_id<edx, 3>>;
0533     };
0534 
0535     /**
0536      * Extended Feature Bits (EAX=7, ECX=0).
0537      *
0538      * Utility class that can read and parse the registers for the extended
0539      * feature bits leaf of the CPUID instruction.
0540      * This is well defined on all architectures but will return all false on all
0541      * non-x86 architectures.
0542      *
0543      * @see https://en.wikipedia.org/wiki/CPUID
0544      */
0545     using x86_cpuid_leaf7 = typename x86_cpuid_leaf7_traits::regs_t;
0546 
0547     struct x86_cpuid_leaf7sub1_traits
0548     {
0549         static constexpr detail::x86_reg32_t leaf = 7;
0550         static constexpr detail::x86_reg32_t subleaf = 1;
0551 
0552         enum class eax
0553         {
0554             /* AVX (VEX-encoded) Vector Neural Network instructions. */
0555             avxvnni = 4,
0556             /* AVX-512 BFloat16 instructions. */
0557             avx512_bf16 = 5,
0558         };
0559 
0560         using regs_t = detail::x86_cpuid_regs<leaf, subleaf,
0561                                               detail::x86_reg_id<eax, 0>>;
0562     };
0563 
0564     /**
0565      * Extended Feature Bits (EAX=7, ECX=1).
0566      *
0567      * Utility class that can read and parse the registers for the extended
0568      * feature bits, subleaf 1, of the CPUID instruction.
0569      * This is well defined on all architectures but will return all false on all
0570      * non-x86 architectures.
0571      *
0572      * @see https://en.wikipedia.org/wiki/CPUID
0573      */
0574     using x86_cpuid_leaf7sub1 = typename x86_cpuid_leaf7sub1_traits::regs_t;
0575 
0576     /**
0577      * Highest Extended CPUID Function Parameter (EAX=0x80000000).
0578      *
0579      * Returns the highest leaf value supported by CPUID in the extended range
0580      * (at or above 0x80000000), and the processor manufacturer ID string.
0581      *
0582      * @see https://en.wikipedia.org/wiki/CPUID
0583      */
0584     using x86_cpuid_leaf80000000 = detail::x86_cpuid_highest_func<true>;
0585 
0586     struct x86_cpuid_leaf80000001_traits
0587     {
0588         static constexpr detail::x86_reg32_t leaf = 0x80000001;
0589         static constexpr detail::x86_reg32_t subleaf = 0;
0590 
0591         enum class ecx
0592         {
0593             /* AMD Fused multiply-add with 4 operands (FMA4). */
0594             fma4 = 16,
0595         };
0596 
0597         using regs_t = detail::x86_cpuid_regs<leaf, subleaf,
0598                                               detail::x86_reg_id<ecx, 2>>;
0599     };
0600 
0601     /**
0602      * Extended Processor Info and Feature Bits.
0603      *
0604      * Utility class that can read and parse the registers for the extended
0605      * processor info leaf of the CPUID instruction.
0606      * This is well defined on all architectures but will return all false on all
0607      * non-x86 architectures.
0608      *
0609      * @see https://en.wikipedia.org/wiki/CPUID
0610      */
0611     using x86_cpuid_leaf80000001 = typename x86_cpuid_leaf80000001_traits::regs_t;
0612 
0613     /*
0614      * Extended Control Register 0 (XCR0).
0615      *
0616      * Operating systems can explicitly disable the usage of instruction set (such
0617      * as SSE or AVX extensions) by setting an appropriate flag in XCR0 register.
0618      * This utility parses such bit values.
0619      *
0620      * @see https://docs.kernel.org/admin-guide/hw-vuln/gather_data_sampling.html
0621      */
0622     class x86_xcr0
0623     {
0624     public:
0625         enum class xcr0
0626         {
0627             /** x87 FPU/MMX support (must be 1). */
0628             x87 = 0,
0629             /** XSAVE support for MXCSR and XMM registers. */
0630             sse = 1,
0631             /** AVX enabled and XSAVE support for upper halves of YMM registers. */
0632             avx = 2,
0633             /** MPX enabled and XSAVE support for BND0-BND3 registers. */
0634             bndreg = 3,
0635             /** MPX enabled and XSAVE support for BNDCFGU and BNDSTATUS registers. */
0636             bndcsr = 4,
0637             /** AVX-512 enabled and XSAVE support for opmask registers k0-k7. */
0638             opmask = 5,
0639             /** AVX-512 enabled and XSAVE support for upper halves of lower ZMM registers. */
0640             zmm_hi256 = 6,
0641             /** AVX-512 enabled and XSAVE support for upper ZMM registers. */
0642             hi16_zmm = 7,
0643             /** Saving/restoring Intel Processor Trace state via XSAVE enabled.*/
0644             processor_trace = 8,
0645             /** XSAVE support for PKRU register. */
0646             pkru = 9,
0647         };
0648 
0649         /**
0650          * Create a default value with only SSE enabled.
0651          *
0652          * AVX and AVX512 strictly require OSXSAVE to be enabled by the OS.
0653          * If OSXSAVE is disabled (e.g., via bcdedit /set xsavedisable 1), AVX state won't
0654          * be preserved across context switches, so AVX cannot be used.
0655          * SSE is therefore the only value safe to assume.
0656          */
0657         constexpr static x86_xcr0 safe_default() noexcept
0658         {
0659             x86_reg32_t low = {};
0660             low = utils::make_bit_mask(static_cast<x86_reg32_t>(xcr0::sse));
0661             return x86_xcr0(low);
0662         }
0663 
0664         /**
0665          * Read the XCR0 register from the CPU if on the correct architecture.
0666          *
0667          * This is only safe to call if bit 18 of CR4.OSXSAVE has been set.
0668          *
0669          * @see cpu_id::osxsave
0670          */
0671         inline static x86_xcr0 read()
0672         {
0673             assert(x86_cpuid_leaf1::read().all_bits_set<x86_cpuid_leaf1::ecx::osxsave>());
0674             return x86_xcr0(detail::x86_xcr0_low());
0675         }
0676 
0677         template <xcr0... bits>
0678         constexpr bool all_bits_set() const noexcept
0679         {
0680             return m_low.all_bits_set<bits...>();
0681         }
0682 
0683         /** Create a value which return false to everything. */
0684         constexpr x86_xcr0() noexcept = default;
0685 
0686     private:
0687         using x86_reg32_t = detail::x86_reg32_t;
0688 
0689         using xcr0_reg_t = detail::x86_reg32_bitset<xcr0>;
0690 
0691         /** Parse a XCR0 value into individual components. */
0692         constexpr explicit x86_xcr0(x86_reg32_t low) noexcept
0693             : m_low(low)
0694         {
0695         }
0696 
0697         xcr0_reg_t m_low {};
0698     };
0699 
0700     /**
0701      * Orchestrator for `CPUID` calls.
0702      *
0703      * This class orchestrate `CPUID` and `XCR0` calls so that they are made in the appropriate
0704      * order. It also implements lazy calling and cache mechanism around those calls.
0705      * Works on all platforms, and return all zeros on non `x86` platforms.
0706      */
0707     class x86_cpu_features_backend_cpuid
0708     {
0709     public:
0710         x86_cpu_features_backend_cpuid() noexcept = default;
0711 
0712         inline x86_xcr0 const& xcr0() const noexcept;
0713         inline x86_cpuid_leaf0 const& leaf0() const;
0714         inline x86_cpuid_leaf80000000 const& leaf80000000() const;
0715         inline x86_cpuid_leaf1 const& leaf1() const;
0716         inline x86_cpuid_leaf7 const& leaf7() const;
0717         inline x86_cpuid_leaf7sub1 const& leaf7sub1() const;
0718         inline x86_cpuid_leaf80000001 const& leaf80000001() const;
0719 
0720     private:
0721         enum class status
0722         {
0723             leaf0_valid = 0,
0724             leaf1_valid = 1,
0725             leaf7_valid = 2,
0726             leaf7sub1_valid = 3,
0727             leaf80000000_valid = 4,
0728             leaf80000001_valid = 5,
0729             xcr0_valid = 6,
0730         };
0731 
0732         using status_bitset = utils::uint_bitset<status, std::uint32_t>;
0733 
0734         mutable x86_cpuid_leaf0 m_leaf0 {};
0735         mutable x86_cpuid_leaf1 m_leaf1 {};
0736         mutable x86_cpuid_leaf7 m_leaf7 {};
0737         mutable x86_cpuid_leaf7sub1 m_leaf7sub1 {};
0738         mutable x86_cpuid_leaf80000000 m_leaf80000000 {};
0739         mutable x86_cpuid_leaf80000001 m_leaf80000001 {};
0740         mutable x86_xcr0 m_xcr0 {};
0741         mutable status_bitset m_status {};
0742 
0743         inline bool osxsave() const noexcept;
0744 
0745         /**
0746          * Internal utility to lazily read and cache a CPUID leaf.
0747          *
0748          * @tparam status_id The status bit tracking whether this leaf has been read and cached.
0749          * @tparam L The CPUID leaf type (e.g. x86_cpuid_leaf1, x86_cpuid_leaf7).
0750          * @param leaf_cache A non-const reference to the class member that stores the leaf
0751          *        value. It must be non-const because this function may write to it on first
0752          *        call. It is passed explicitly (rather than accessed via `this`) to allow
0753          *        factoring the caching logic across different leaf members.
0754          * @return A const reference to `leaf_cache`. The non-const input / const-ref output
0755          *         asymmetry is intentional: callers must not modify the cached value, but
0756          *         this function needs write access to populate it.
0757          *
0758          * On first call, checks whether the leaf number is within the range advertised as
0759          * supported by CPUID (via leaf 0 for the standard range, leaf 0x80000000 for the
0760          * extended range). If supported, reads the leaf from the CPU; otherwise leaves
0761          * `leaf_cache` at its zero-initialized default (all feature bits false). Either
0762          * way, `status_id` is set so subsequent calls return immediately.
0763          */
0764         template <status status_id, typename L>
0765         inline auto const& safe_read_leaf(L& leaf_cache) const;
0766     };
0767 
0768     /**
0769      * No-Op orchestrator for `CPUID` calls
0770      *
0771      * This does nothing and return zero-constructed objects on all calls.
0772      * This is meant as an optimization on non `x86` platforms as the
0773      * `x86_cpu_features_backend_cpuid` can be slightly large (hundred of bytes).
0774      */
0775     class x86_cpu_features_backend_noop
0776     {
0777     public:
0778         constexpr x86_xcr0 xcr0() const noexcept { return {}; }
0779         constexpr x86_cpuid_leaf0 leaf0() const { return {}; }
0780         constexpr x86_cpuid_leaf80000000 leaf80000000() const { return {}; }
0781         constexpr x86_cpuid_leaf1 leaf1() const { return {}; }
0782         constexpr x86_cpuid_leaf7 leaf7() const { return {}; }
0783         constexpr x86_cpuid_leaf7sub1 leaf7sub1() const { return {}; }
0784         constexpr x86_cpuid_leaf80000001 leaf80000001() const { return {}; }
0785     };
0786 
0787 #if XSIMD_TARGET_X86
0788     using x86_cpu_features_backend_default = x86_cpu_features_backend_cpuid;
0789 #else
0790     using x86_cpu_features_backend_default = x86_cpu_features_backend_noop;
0791 #endif
0792 
0793     /**
0794      * An opiniated CPU feature detection utility for x86.
0795      *
0796      * These are high level features that combine multiple registers reads in sequence.
0797      * Instead of looking directly at raw CPUID results, this utility also checks that
0798      * permissions (e.g. OSXSAVE) are enabled, and otherwise return conservative defaults.
0799      *
0800      * This is well defined on all architectures. It will always return false on
0801      * non-x86 architectures.
0802      */
0803     class x86_cpu_features : private x86_cpu_features_backend_default
0804     {
0805     public:
0806         x86_cpu_features() noexcept = default;
0807 
0808         inline bool sse_enabled() const noexcept
0809         {
0810             return xcr0().all_bits_set<x86_xcr0::xcr0::sse>();
0811         }
0812 
0813         inline bool avx_enabled() const noexcept
0814         {
0815             // Check both SSE and AVX bits even though AVX must imply SSE
0816             return xcr0().all_bits_set<x86_xcr0::xcr0::sse, x86_xcr0::xcr0::avx>();
0817         }
0818 
0819         inline bool avx512_enabled() const noexcept
0820         {
0821             // Check all SSE, AVX, optmask, and AVX512 bits even though AVX512 must
0822             // imply AVX, SSE, and masked operations.
0823             return xcr0().all_bits_set<x86_xcr0::xcr0::sse, x86_xcr0::xcr0::avx, x86_xcr0::xcr0::opmask, x86_xcr0::xcr0::zmm_hi256>();
0824         }
0825 
0826         /**
0827          * The manufacturer ID string in a static array.
0828          *
0829          * This raw character array is case specific and may contain both leading
0830          * and trailing whitespaces.
0831          * It cannot be assumed to be null terminated.
0832          */
0833         inline auto manufacturer_id_raw() const noexcept
0834         {
0835             return leaf0().manufacturer_id_raw();
0836         }
0837 
0838 #if XSIMD_CPP_VERSION >= 201703L
0839         inline std::string_view manufacturer_id() const noexcept
0840         {
0841             return leaf0().manufacturer_id();
0842         }
0843 #endif
0844 
0845         /** The manufacturer ID string parsed into known common vendors. */
0846         inline x86_manufacturer known_manufacturer() const noexcept
0847         {
0848             return x86_parse_manufacturer(manufacturer_id_raw());
0849         }
0850 
0851         /**
0852          * Indicates whether the OS has enabled extended state management.
0853          *
0854          * When true, the OS has set bit 18 (OSXSAVE) in the CR4 control register,
0855          * enabling the XGETBV/XSETBV instructions to access XCR0 and support
0856          * processor extended state management using XSAVE/XRSTOR.
0857          *
0858          * This value is read from CPUID leaf 0x1, ECX bit 27, which reflects
0859          * the state of CR4.OSXSAVE.
0860          */
0861         inline bool osxsave() const noexcept { return leaf1().all_bits_set<x86_cpuid_leaf1::ecx::osxsave>(); }
0862 
0863         /**
0864          * Effective processor family.
0865          *
0866          * Per wikipedia CPUID page:
0867          * > The actual processor family is derived from the Family ID and Extended Family ID fields.
0868          * > If the Family ID field is equal to 15, the family is equal to the sum of the Extended
0869          * > Family ID and the Family ID fields. Otherwise, the family is equal to the value of the
0870          * > Family ID field.
0871          *
0872          * @see https://en.wikipedia.org/wiki/CPUID#EAX=1:_Processor_Info_and_Feature_Bits
0873          */
0874         inline detail::x86_reg32_t cpu_family() const noexcept
0875         {
0876             using eax = x86_cpuid_leaf1::eax;
0877             auto family_id = leaf1().get_range<eax::family_id_start, eax::family_id_end>();
0878             auto ext_family_id = leaf1().get_range<eax::ext_family_start, eax::ext_family_end>();
0879             return family_id + (family_id == 15 ? ext_family_id : 0);
0880         }
0881 
0882         /**
0883          * Effective processor model.
0884          *
0885          * Per wikipedia CPUID page:
0886          * > The actual processor model is derived from the Model, Extended Model ID and Family ID
0887          * > fields. If the Family ID field is either 6 or 15, the model is equal to the sum of the
0888          * > Extended Model ID field shifted left by 4 bits and the Model field.
0889          * > Otherwise, the model is equal to the value of the Model field.
0890          *
0891          * @see https://en.wikipedia.org/wiki/CPUID#EAX=1:_Processor_Info_and_Feature_Bits
0892          */
0893         inline detail::x86_reg32_t cpu_model() const noexcept
0894         {
0895             using eax = x86_cpuid_leaf1::eax;
0896             auto model = leaf1().get_range<eax::model_start, eax::model_end>();
0897             auto ext_model = leaf1().get_range<eax::ext_model_start, eax::ext_model_end>();
0898             auto family_id = leaf1().get_range<eax::family_id_start, eax::family_id_end>();
0899             return (family_id == 15 || family_id == 6) ? ((ext_model << 4) + model) : model;
0900         }
0901 
0902         inline bool sse2() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::edx::sse2>(); }
0903 
0904         inline bool sse3() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::sse3>(); }
0905 
0906         inline bool ssse3() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::ssse3>(); }
0907 
0908         inline bool sse4_1() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::sse4_1>(); }
0909 
0910         inline bool sse4_2() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::sse4_2>(); }
0911 
0912         inline bool popcnt() const noexcept { return leaf1().all_bits_set<x86_cpuid_leaf1::ecx::popcnt>(); }
0913 
0914         inline bool fma3() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::fma3>(); }
0915 
0916         inline bool avx() const noexcept { return avx_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::avx>(); }
0917 
0918         inline bool avx_128() const noexcept
0919         {
0920             // Avx 128 bit instructions use the same xmm registers from SSE so checking if those
0921             // are enabled is sufficient.
0922             return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::avx>();
0923         }
0924 
0925         inline bool aes_ni() const noexcept { return sse_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::aes_ni>(); }
0926 
0927         inline bool f16c() const noexcept { return avx_enabled() && leaf1().all_bits_set<x86_cpuid_leaf1::ecx::f16c>(); }
0928 
0929         inline bool rdrnd() const noexcept { return leaf1().all_bits_set<x86_cpuid_leaf1::ecx::rdrnd>(); }
0930 
0931         inline bool bmi1() const noexcept { return leaf7().all_bits_set<x86_cpuid_leaf7::ebx::bmi1>(); }
0932 
0933         inline bool avx2() const noexcept { return avx_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx2>(); }
0934 
0935         inline bool avx2_128() const noexcept
0936         {
0937             // Avx 128 bit instructions use the same xmm registers from SSE so checking if those
0938             // are enabled is sufficient.
0939             return sse_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx2>();
0940         }
0941 
0942         inline bool bmi2() const noexcept { return leaf7().all_bits_set<x86_cpuid_leaf7::ebx::bmi2>(); }
0943 
0944         /**
0945          * BMI2 support with efficient PEXT and PDEP instructions.
0946          *
0947          * > AMD processors before Zen 3 that implement PDEP and PEXT do so in microcode, with a
0948          * > latency of 18 cycles rather than (Zen 3) 3 cycles. As a result it is often faster
0949          * > to use other instructions on these processors.
0950          *
0951          * @see https://en.wikipedia.org/wiki/X86_Bit_manipulation_instruction_set
0952          */
0953         inline bool efficient_bmi2() const noexcept
0954         {
0955             if (known_manufacturer() == x86_manufacturer::amd)
0956             {
0957                 // Zen 3 and Zen 4 report family 0x19; Zen 5 reports family 0x1A.
0958                 // Earlier AMD microarchitectures (Zen / Zen+ / Zen 2) report family 0x17.
0959                 return bmi2() && (cpu_family() >= 0x19);
0960             }
0961             return bmi2();
0962         }
0963 
0964         inline bool avx512f() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512f>(); }
0965 
0966         inline bool avx512dq() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512dq>(); }
0967 
0968         inline bool rdseed() const noexcept { return leaf7().all_bits_set<x86_cpuid_leaf7::ebx::rdseed>(); }
0969 
0970         inline bool adx() const noexcept { return leaf7().all_bits_set<x86_cpuid_leaf7::ebx::adx>(); }
0971 
0972         inline bool avx512ifma() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512ifma>(); }
0973 
0974         inline bool avx512pf() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512pf>(); }
0975 
0976         inline bool avx512er() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512er>(); }
0977 
0978         inline bool avx512cd() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512cd>(); }
0979 
0980         inline bool sha() const noexcept { return leaf7().all_bits_set<x86_cpuid_leaf7::ebx::sha>(); }
0981 
0982         inline bool avx512bw() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512bw>(); }
0983 
0984         inline bool avx512vl() const noexcept
0985         {
0986             return xcr0().all_bits_set<x86_xcr0::xcr0::opmask>()
0987                 && leaf7().all_bits_set<x86_cpuid_leaf7::ebx::avx512f, x86_cpuid_leaf7::ebx::avx512vl>();
0988         }
0989 
0990         inline bool avx512vl_128() const noexcept
0991         {
0992             // AVX512 128 bit instructions use the same xmm registers from SSE so checking if those
0993             // are enabled is sufficient.
0994             return sse_enabled() && avx512vl();
0995         }
0996 
0997         inline bool avx512vl_256() const noexcept
0998         {
0999             // AVX512 256 bit instructions use the same ymm registers from AVX so checking if those
1000             // are enabled is sufficient.
1001             return avx_enabled() && avx512vl();
1002         }
1003 
1004         inline bool avx512vbmi() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::avx512vbmi>(); }
1005 
1006         inline bool avx512vbmi2() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::avx512vbmi2>(); }
1007 
1008         inline bool gfni() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::gfni>(); }
1009 
1010         inline bool vaes() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::vaes>(); }
1011 
1012         inline bool vpclmulqdq() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::vpclmulqdq>(); }
1013 
1014         inline bool avx512vnni_bw() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::avx512vnni_bw>(); }
1015 
1016         inline bool avx512_bitalg() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::avx512_bitalg>(); }
1017 
1018         inline bool avx512_vpopcntdq() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::ecx::avx512_vpopcntdq>(); }
1019 
1020         inline bool avx512_4vnniw() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::edx::avx512_4vnniw>(); }
1021 
1022         inline bool avx512_4fmaps() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::edx::avx512_4fmaps>(); }
1023 
1024         inline bool avx512_vp2intersect() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::edx::avx512_vp2intersect>(); }
1025 
1026         inline bool avx512_fp16() const noexcept { return avx512_enabled() && leaf7().all_bits_set<x86_cpuid_leaf7::edx::avx512_fp16>(); }
1027 
1028         inline bool avxvnni() const noexcept { return avx_enabled() && leaf7sub1().all_bits_set<x86_cpuid_leaf7sub1::eax::avxvnni>(); }
1029 
1030         inline bool avx512_bf16() const noexcept { return avx512_enabled() && leaf7sub1().all_bits_set<x86_cpuid_leaf7sub1::eax::avx512_bf16>(); }
1031 
1032         inline bool fma4() const noexcept { return avx_enabled() && leaf80000001().all_bits_set<x86_cpuid_leaf80000001::ecx::fma4>(); }
1033     };
1034 
1035     /********************
1036      *  Implementation  *
1037      ********************/
1038 
1039     template <x86_cpu_features_backend_cpuid::status status_id, typename L>
1040     inline auto const& x86_cpu_features_backend_cpuid::safe_read_leaf(L& leaf_cache) const
1041     {
1042         // Check if already initialized
1043         if (m_status.bit_is_set<status_id>())
1044         {
1045             return leaf_cache;
1046         }
1047 
1048         // Limit where we need to check leaf0 or leaf 80000000.
1049         constexpr auto extended_threshold = x86_cpuid_leaf80000000::leaf;
1050 
1051         // Check if it is safe to call CPUID with this value.
1052         // First we identify if the leaf is in the regular or extended range.
1053         // TODO(C++17): if constexpr
1054         if (L::leaf < extended_threshold)
1055         {
1056             // Check leaf0 in regular range
1057             if (L::leaf <= leaf0().highest_leaf())
1058             {
1059                 leaf_cache = L::read();
1060             }
1061         }
1062         else
1063         {
1064             // Check leaf80000000 in extended range
1065             if (L::leaf <= leaf80000000().highest_leaf())
1066             {
1067                 leaf_cache = L::read();
1068             }
1069         }
1070 
1071         // Mark as valid in all cases, including if it was not read.
1072         // In this case it will be filled with zeros (all false).
1073         m_status.set_bit<status_id>();
1074         return leaf_cache;
1075     }
1076 
1077     inline x86_xcr0 const& x86_cpu_features_backend_cpuid::xcr0() const noexcept
1078     {
1079         if (!m_status.bit_is_set<status::xcr0_valid>())
1080         {
1081             m_xcr0 = osxsave() ? x86_xcr0::read() : x86_xcr0::safe_default();
1082             m_status.set_bit<status::xcr0_valid>();
1083         }
1084         return m_xcr0;
1085     }
1086 
1087     inline x86_cpuid_leaf0 const& x86_cpu_features_backend_cpuid::leaf0() const
1088     {
1089         if (!m_status.bit_is_set<status::leaf0_valid>())
1090         {
1091             m_leaf0 = x86_cpuid_leaf0::read();
1092             m_status.set_bit<status::leaf0_valid>();
1093         }
1094         return m_leaf0;
1095     }
1096 
1097     inline x86_cpuid_leaf80000000 const& x86_cpu_features_backend_cpuid::leaf80000000() const
1098     {
1099         if (!m_status.bit_is_set<status::leaf80000000_valid>())
1100         {
1101             m_leaf80000000 = x86_cpuid_leaf80000000::read();
1102             m_status.set_bit<status::leaf80000000_valid>();
1103         }
1104         return m_leaf80000000;
1105     }
1106 
1107     inline x86_cpuid_leaf1 const& x86_cpu_features_backend_cpuid::leaf1() const
1108     {
1109         return safe_read_leaf<status::leaf1_valid>(m_leaf1);
1110     }
1111 
1112     inline x86_cpuid_leaf7 const& x86_cpu_features_backend_cpuid::leaf7() const
1113     {
1114         return safe_read_leaf<status::leaf7_valid>(m_leaf7);
1115     }
1116 
1117     inline x86_cpuid_leaf7sub1 const& x86_cpu_features_backend_cpuid::leaf7sub1() const
1118     {
1119         // Check if already initialized
1120         if (m_status.bit_is_set<status::leaf7sub1_valid>())
1121         {
1122             return m_leaf7sub1;
1123         }
1124 
1125         // Check if safe to call CPUID with this value as subleaf.
1126         constexpr auto start = x86_cpuid_leaf7::eax::highest_subleaf_start;
1127         constexpr auto end = x86_cpuid_leaf7::eax::highest_subleaf_end;
1128         const auto highest_subleaf7 = leaf7().get_range<start, end>();
1129         if (x86_cpuid_leaf7sub1::subleaf <= highest_subleaf7)
1130         {
1131             m_leaf7sub1 = x86_cpuid_leaf7sub1::read();
1132         }
1133 
1134         // Mark as valid in all cases, including if it was not read.
1135         // In this case it will be filled with zeros (all false).
1136         m_status.set_bit<status::leaf7sub1_valid>();
1137         return m_leaf7sub1;
1138     }
1139 
1140     inline x86_cpuid_leaf80000001 const& x86_cpu_features_backend_cpuid::leaf80000001() const
1141     {
1142         return safe_read_leaf<status::leaf80000001_valid>(m_leaf80000001);
1143     }
1144 
1145     inline bool x86_cpu_features_backend_cpuid::osxsave() const noexcept
1146     {
1147         return leaf1().all_bits_set<x86_cpuid_leaf1::ecx::osxsave>();
1148     }
1149 
1150     namespace detail
1151     {
1152 #if XSIMD_TARGET_X86
1153 
1154         inline cpuid_reg_t x86_cpuid(int leaf, int subleaf) noexcept
1155         {
1156             cpuid_reg_t reg = {};
1157 #if defined(_MSC_VER)
1158             int buf[4];
1159             __cpuidex(buf, leaf, subleaf);
1160             std::memcpy(reg.data(), buf, sizeof(buf));
1161 
1162 // Intel compiler has long had support for `__cpuid`, but only recently for `__cpuidex`.
1163 // Modern Clang and GCC also now support `__cpuidex`.
1164 // It was decided to keep the inline ASM version for maximum compatibility, as the difference
1165 // in ASM is negligible compared to the cost of CPUID.
1166 // https://github.com/xtensor-stack/xsimd/pull/1278
1167 #elif XSIMD_WITH_INLINE_ASM
1168 
1169 #if defined(__i386__) && defined(__PIC__)
1170             // %ebx may be the PIC register
1171             __asm__("xchg{l}\t{%%}ebx, %1\n\t"
1172                     "cpuid\n\t"
1173                     "xchg{l}\t{%%}ebx, %1\n\t"
1174                     : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
1175                     : "0"(leaf), "2"(subleaf));
1176 
1177 #else
1178             __asm__("cpuid\n\t"
1179                     : "=a"(reg[0]), "=b"(reg[1]), "=c"(reg[2]), "=d"(reg[3])
1180                     : "0"(leaf), "2"(subleaf));
1181 #endif
1182 #endif
1183             return reg;
1184         }
1185 
1186         inline x86_reg32_t x86_xcr0_low() noexcept
1187         {
1188 #if defined(_MSC_VER)
1189 #if _MSC_VER >= 1400
1190             return static_cast<x86_reg32_t>(_xgetbv(0));
1191 #else
1192 #error "_MSC_VER < 1400 is not supported"
1193 #endif
1194 
1195 #elif XSIMD_WITH_INLINE_ASM
1196             x86_reg32_t xcr0 = {};
1197             __asm__(
1198                 "xorl %%ecx, %%ecx\n"
1199                 "xgetbv\n"
1200                 : "=a"(xcr0)
1201                 :
1202 #if defined(__i386__)
1203                 : "ecx", "edx"
1204 #else
1205                 : "rcx", "rdx"
1206 #endif
1207             );
1208             return xcr0;
1209 #endif
1210         }
1211 
1212 #else // XSIMD_TARGET_X86
1213 
1214         inline cpuid_reg_t x86_cpuid(int /* leaf */, int /* subleaf */) noexcept
1215         {
1216             return {}; // All bits to zero
1217         }
1218 
1219         inline x86_reg32_t x86_xcr0_low() noexcept
1220         {
1221             return {}; // All bits to zero
1222         }
1223 
1224 #endif // XSIMD_TARGET_X86
1225     }
1226 }
1227 #endif