File indexing completed on 2026-11-07 10:37:15
0001
0002
0003
0004
0005
0006
0007
0008
0009
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
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
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
0057 template <typename E>
0058 using x86_reg32_bitset = utils::uint_bitset<E, x86_reg32_t>;
0059
0060
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
0071 template <x86_reg32_t K, typename... reg_ids>
0072 struct find_reg_k;
0073
0074
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
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
0093
0094
0095
0096
0097
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
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
0128
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
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
0213 out.m_highest_leaf = regs[0];
0214
0215
0216 char* manuf = out.m_manufacturer_id.data();
0217 std::memcpy(manuf + 0 * sizeof(x86_reg32_t), ®s[1], sizeof(x86_reg32_t));
0218 std::memcpy(manuf + 1 * sizeof(x86_reg32_t), ®s[3], sizeof(x86_reg32_t));
0219 std::memcpy(manuf + 2 * sizeof(x86_reg32_t), ®s[2], sizeof(x86_reg32_t));
0220
0221 return out;
0222 }
0223
0224 constexpr x86_cpuid_highest_func() noexcept = default;
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 constexpr x86_reg32_t highest_leaf() const noexcept
0235 {
0236 return m_highest_leaf;
0237 }
0238
0239
0240
0241
0242
0243
0244
0245
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
0267
0268
0269
0270
0271
0272
0273 using x86_cpuid_leaf0 = detail::x86_cpuid_highest_func<false>;
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284 enum class x86_manufacturer
0285 {
0286
0287
0288
0289
0290
0291 amd,
0292
0293
0294
0295
0296
0297 intel,
0298
0299
0300
0301
0302
0303
0304 via,
0305
0306 zhaoxin,
0307
0308 hygon,
0309
0310
0311
0312
0313
0314 transmeta,
0315
0316 elbrus,
0317
0318 microsoft_vpc,
0319
0320 unknown,
0321 };
0322
0323
0324
0325
0326
0327
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
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
0389 stepping_start = 0,
0390 stepping_end = 4,
0391
0392 model_start = 4,
0393 model_end = 8,
0394
0395 family_id_start = 8,
0396 family_id_end = 12,
0397
0398 ext_model_start = 16,
0399 ext_model_end = 20,
0400
0401 ext_family_start = 20,
0402 ext_family_end = 28,
0403 };
0404 enum class ecx
0405 {
0406
0407 sse3 = 0,
0408
0409 ssse3 = 9,
0410
0411 fma3 = 12,
0412
0413 sse4_1 = 19,
0414
0415 sse4_2 = 20,
0416
0417 popcnt = 23,
0418
0419 aes_ni = 25,
0420
0421 osxsave = 27,
0422
0423 avx = 28,
0424
0425 f16c = 29,
0426
0427 rdrnd = 30,
0428 };
0429 enum class edx
0430 {
0431
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
0443
0444
0445
0446
0447
0448
0449
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
0461 highest_subleaf_start = 0,
0462
0463 highest_subleaf_end = 32,
0464 };
0465 enum class ebx
0466 {
0467
0468 bmi1 = 3,
0469
0470 avx2 = 5,
0471
0472 bmi2 = 8,
0473
0474 avx512f = 16,
0475
0476 avx512dq = 17,
0477
0478 rdseed = 18,
0479
0480 adx = 19,
0481
0482 avx512ifma = 21,
0483
0484 avx512pf = 26,
0485
0486 avx512er = 27,
0487
0488 avx512cd = 28,
0489
0490 sha = 29,
0491
0492 avx512bw = 30,
0493
0494 avx512vl = 31,
0495 };
0496 enum class ecx
0497 {
0498
0499 avx512vbmi = 1,
0500
0501 avx512vbmi2 = 6,
0502
0503 gfni = 8,
0504
0505 vaes = 9,
0506
0507 vpclmulqdq = 10,
0508
0509 avx512vnni_bw = 11,
0510
0511 avx512_bitalg = 12,
0512
0513 avx512_vpopcntdq = 14,
0514 };
0515 enum class edx
0516 {
0517
0518 avx512_4vnniw = 2,
0519
0520 avx512_4fmaps = 3,
0521
0522 avx512_vp2intersect = 8,
0523
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
0537
0538
0539
0540
0541
0542
0543
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
0555 avxvnni = 4,
0556
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
0566
0567
0568
0569
0570
0571
0572
0573
0574 using x86_cpuid_leaf7sub1 = typename x86_cpuid_leaf7sub1_traits::regs_t;
0575
0576
0577
0578
0579
0580
0581
0582
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
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
0603
0604
0605
0606
0607
0608
0609
0610
0611 using x86_cpuid_leaf80000001 = typename x86_cpuid_leaf80000001_traits::regs_t;
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622 class x86_xcr0
0623 {
0624 public:
0625 enum class xcr0
0626 {
0627
0628 x87 = 0,
0629
0630 sse = 1,
0631
0632 avx = 2,
0633
0634 bndreg = 3,
0635
0636 bndcsr = 4,
0637
0638 opmask = 5,
0639
0640 zmm_hi256 = 6,
0641
0642 hi16_zmm = 7,
0643
0644 processor_trace = 8,
0645
0646 pkru = 9,
0647 };
0648
0649
0650
0651
0652
0653
0654
0655
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
0666
0667
0668
0669
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
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
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
0702
0703
0704
0705
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
0747
0748
0749
0750
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760
0761
0762
0763
0764 template <status status_id, typename L>
0765 inline auto const& safe_read_leaf(L& leaf_cache) const;
0766 };
0767
0768
0769
0770
0771
0772
0773
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
0795
0796
0797
0798
0799
0800
0801
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
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
0822
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
0828
0829
0830
0831
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
0846 inline x86_manufacturer known_manufacturer() const noexcept
0847 {
0848 return x86_parse_manufacturer(manufacturer_id_raw());
0849 }
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861 inline bool osxsave() const noexcept { return leaf1().all_bits_set<x86_cpuid_leaf1::ecx::osxsave>(); }
0862
0863
0864
0865
0866
0867
0868
0869
0870
0871
0872
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
0884
0885
0886
0887
0888
0889
0890
0891
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
0921
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
0938
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
0946
0947
0948
0949
0950
0951
0952
0953 inline bool efficient_bmi2() const noexcept
0954 {
0955 if (known_manufacturer() == x86_manufacturer::amd)
0956 {
0957
0958
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
0993
0994 return sse_enabled() && avx512vl();
0995 }
0996
0997 inline bool avx512vl_256() const noexcept
0998 {
0999
1000
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
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
1043 if (m_status.bit_is_set<status_id>())
1044 {
1045 return leaf_cache;
1046 }
1047
1048
1049 constexpr auto extended_threshold = x86_cpuid_leaf80000000::leaf;
1050
1051
1052
1053
1054 if (L::leaf < extended_threshold)
1055 {
1056
1057 if (L::leaf <= leaf0().highest_leaf())
1058 {
1059 leaf_cache = L::read();
1060 }
1061 }
1062 else
1063 {
1064
1065 if (L::leaf <= leaf80000000().highest_leaf())
1066 {
1067 leaf_cache = L::read();
1068 }
1069 }
1070
1071
1072
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
1120 if (m_status.bit_is_set<status::leaf7sub1_valid>())
1121 {
1122 return m_leaf7sub1;
1123 }
1124
1125
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
1135
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
1163
1164
1165
1166
1167 #elif XSIMD_WITH_INLINE_ASM
1168
1169 #if defined(__i386__) && defined(__PIC__)
1170
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
1213
1214 inline cpuid_reg_t x86_cpuid(int , int ) noexcept
1215 {
1216 return {};
1217 }
1218
1219 inline x86_reg32_t x86_xcr0_low() noexcept
1220 {
1221 return {};
1222 }
1223
1224 #endif
1225 }
1226 }
1227 #endif