4#include "native/config.h"
16 mmx, sse, sse2, sse3, ssse3, sse41,
18 bmi1, bmi2, avx512f, avx512dq, avx512bw, avx512vl,
19 avx512bf16, avx512fp16, aes, pclmul, cx16, avx512cd,
20 avx512ifma,
lzcnt, movbe, sahf,
mwaitx, waitpkg,
crc32, gfni, avx512vpopcntdq,
21 vpclmulqdq, avxvnni, avx512vnni, avxvnniint8, avxvnniint16, avx512bitalg, avx512vbmi, avx512vbmi2, sha, vaes, avxifma, adx, sha512, sm3, sm4, avxneconvert
25 neon, neon_fp16, neon_bf16, aes, sha2, sha3,
26 crc, lse, rdm, fp16fml, dotprod, complxnum,
27 jsconv, rcpc, pauth, i8mm,
pmull, sha1, sha512, ebf16, sm3, sm4
39 template<architecture Family=target_arch>
struct isa;
42 template<
class T>
concept instruction_feature=
43 std::same_as<T,x86_feature> || std::same_as<T,arm_feature> || std::same_as<T,wasm_feature>;
44 template<instruction_feature E>
inline constexpr std::size_t feature_count=
47 template<instruction_feature E>
inline constexpr architecture family_of=
48 std::same_as<E,x86_feature> ?
x86 : std::same_as<E,arm_feature> ?
arm :
wasm;
49 template<architecture Family>
struct feature_traits;
53 enum class unknown_feature : std::uint64_t {};
54 template<>
struct feature_traits<
architecture::
unknown> {
using type=unknown_feature;
static constexpr std::size_t count=0; };
58 template<architecture Family>
struct isa {
64 static constexpr auto feature_count=detail::feature_traits<Family>::count;
68 constexpr isa() noexcept=default;
73 auto i=std::uint64_t(f);
79 auto i=std::uint64_t(f);
84 auto mask=std::uint64_t{1}<<(i%64);
85 auto & word=
flags[i/64];
91 constexpr bool has(
isa other)
const noexcept {
92 for(std::size_t i=0;i<
flags.size();++i)
93 if((
flags[i]&other.flags[i])!=other.flags[i])
return false;
97 constexpr bool valid() const noexcept {
99 constexpr auto mask=used ? (std::uint64_t{1}<<used)-1 : 0;
117 constexpr isa() noexcept=default;
122 auto i=std::uint64_t(f);
128 auto i=std::uint64_t(f);
133 auto mask=std::uint64_t{1}<<(i%64);
134 auto & word=
flags[i/64];
140 constexpr bool has(
isa other)
const noexcept {
141 for(std::size_t i=0;i<
flags.size();++i)
142 if((
flags[i]&other.flags[i])!=other.flags[i])
return false;
146 constexpr bool valid() const noexcept {
148 constexpr auto mask=used ? (std::uint64_t{1}<<used)-1 : 0;
153 __declspec(property(
get=get_mmx,put=set_mmx))
bool mmx;
154 __declspec(property(
get=get_sse,put=set_sse))
bool sse;
155 __declspec(property(
get=get_sse2,put=set_sse2))
bool sse2;
156 __declspec(property(
get=get_sse3,put=set_sse3))
bool sse3;
157 __declspec(property(
get=get_ssse3,put=set_ssse3))
bool ssse3;
158 __declspec(property(
get=get_sse41,put=set_sse41))
bool sse41;
159 __declspec(property(
get=get_sse42,put=set_sse42))
bool sse42;
160 __declspec(property(
get=get_popcnt,put=set_popcnt))
bool popcnt;
161 __declspec(property(
get=get_avx,put=set_avx))
bool avx;
162 __declspec(property(
get=get_avx2,put=set_avx2))
bool avx2;
163 __declspec(property(
get=get_fma,put=set_fma))
bool fma;
164 __declspec(property(
get=get_f16c,put=set_f16c))
bool f16c;
165 __declspec(property(
get=get_bmi1,put=set_bmi1))
bool bmi1;
166 __declspec(property(
get=get_bmi2,put=set_bmi2))
bool bmi2;
167 __declspec(property(
get=get_avx512f,put=set_avx512f))
bool avx512f;
168 __declspec(property(
get=get_avx512dq,put=set_avx512dq))
bool avx512dq;
169 __declspec(property(
get=get_avx512bw,put=set_avx512bw))
bool avx512bw;
170 __declspec(property(
get=get_avx512vl,put=set_avx512vl))
bool avx512vl;
171 __declspec(property(
get=get_avx512bf16,put=set_avx512bf16))
bool avx512bf16;
172 __declspec(property(
get=get_avx512fp16,put=set_avx512fp16))
bool avx512fp16;
173 __declspec(property(
get=get_aes,put=set_aes))
bool aes;
174 __declspec(property(
get=get_pclmul,put=set_pclmul))
bool pclmul;
175 __declspec(property(
get=get_cx16,put=set_cx16))
bool cx16;
176 __declspec(property(
get=get_avx512cd,put=set_avx512cd))
bool avx512cd;
177 __declspec(property(
get=get_avx512ifma,put=set_avx512ifma))
bool avx512ifma;
178 __declspec(property(
get=get_lzcnt,put=set_lzcnt))
bool lzcnt;
179 __declspec(property(
get=get_movbe,put=set_movbe))
bool movbe;
180 __declspec(property(
get=get_sahf,put=set_sahf))
bool sahf;
181 __declspec(property(
get=get_mwaitx,put=set_mwaitx))
bool mwaitx;
182 __declspec(property(
get=get_waitpkg,put=set_waitpkg))
bool waitpkg;
183 __declspec(property(
get=get_crc32,put=set_crc32))
bool crc32;
184 __declspec(property(
get=get_gfni,put=set_gfni))
bool gfni;
185 __declspec(property(
get=get_vpclmulqdq,put=set_vpclmulqdq))
bool vpclmulqdq;
187 __declspec(property(
get=get_avxvnni,put=set_avxvnni))
bool avxvnni;
188 __declspec(property(
get=get_avx512vnni,put=set_avx512vnni))
bool avx512vnni;
192 __declspec(property(
get=get_avx512vbmi,put=set_avx512vbmi))
bool avx512vbmi;
194 __declspec(property(
get=get_sha,put=set_sha))
bool sha;
195 __declspec(property(
get=get_vaes,put=set_vaes))
bool vaes;
196 __declspec(property(
get=get_avxifma,put=set_avxifma))
bool avxifma;
197 __declspec(property(
get=get_adx,put=set_adx))
bool adx;
198 __declspec(property(
get=get_sha512,put=set_sha512))
bool sha512;
199 __declspec(property(
get=get_sm3,put=set_sm3))
bool sm3;
200 __declspec(property(
get=get_sm4,put=set_sm4))
bool sm4;
215 constexpr isa() noexcept=default;
220 auto i=std::uint64_t(f);
226 auto i=std::uint64_t(f);
231 auto mask=std::uint64_t{1}<<(i%64);
232 auto & word=
flags[i/64];
238 constexpr bool has(
isa other)
const noexcept {
239 for(std::size_t i=0;i<
flags.size();++i)
240 if((
flags[i]&other.flags[i])!=other.flags[i])
return false;
244 constexpr bool valid() const noexcept {
246 constexpr auto mask=used ? (std::uint64_t{1}<<used)-1 : 0;
251 __declspec(property(
get=get_neon,put=set_neon))
bool neon;
252 __declspec(property(
get=get_neon_fp16,put=set_neon_fp16))
bool neon_fp16;
253 __declspec(property(
get=get_neon_bf16,put=set_neon_bf16))
bool neon_bf16;
254 __declspec(property(
get=get_pmull,put=set_pmull))
bool pmull;
255 __declspec(property(
get=get_sha1,put=set_sha1))
bool sha1;
256 __declspec(property(
get=get_sha512,put=set_sha512))
bool sha512;
257 __declspec(property(
get=get_ebf16,put=set_ebf16))
bool ebf16;
260 __declspec(property(
get=get_sm3,put=set_sm3))
bool sm3;
263 __declspec(property(
get=get_sm4,put=set_sm4))
bool sm4;
264 __declspec(property(
get=get_aes,put=set_aes))
bool aes;
265 __declspec(property(
get=get_sha2,put=set_sha2))
bool sha2;
266 __declspec(property(
get=get_sha3,put=set_sha3))
bool sha3;
267 __declspec(property(
get=get_crc,put=set_crc))
bool crc;
268 __declspec(property(
get=get_lse,put=set_lse))
bool lse;
269 __declspec(property(
get=get_rdm,put=set_rdm))
bool rdm;
270 __declspec(property(
get=get_fp16fml,put=set_fp16fml))
bool fp16fml;
271 __declspec(property(
get=get_dotprod,put=set_dotprod))
bool dotprod;
272 __declspec(property(
get=get_complxnum,put=set_complxnum))
bool complxnum;
273 __declspec(property(
get=get_jsconv,put=set_jsconv))
bool jsconv;
274 __declspec(property(
get=get_rcpc,put=set_rcpc))
bool rcpc;
275 __declspec(property(
get=get_pauth,put=set_pauth))
bool pauth;
276 __declspec(property(
get=get_i8mm,put=set_i8mm))
bool i8mm;
290 constexpr isa() noexcept=default;
295 auto i=std::uint64_t(f);
301 auto i=std::uint64_t(f);
306 auto mask=std::uint64_t{1}<<(i%64);
307 auto & word=
flags[i/64];
313 constexpr bool has(
isa other)
const noexcept {
314 for(std::size_t i=0;i<
flags.size();++i)
315 if((
flags[i]&other.flags[i])!=other.flags[i])
return false;
319 constexpr bool valid() const noexcept {
321 constexpr auto mask=used ? (std::uint64_t{1}<<used)-1 : 0;
326 __declspec(property(
get=get_simd128,put=set_simd128))
bool simd128;
331 template<detail::instruction_feature E>
isa(E)->
isa<detail::family_of<E>>;
334 template<
class T>
inline constexpr bool is_isa=
false;
335 template<architecture Family>
inline constexpr bool is_isa<isa<Family>> =
true;
336 template<
class T>
struct arch_family;
337 template<instruction_feature E>
struct arch_family<E> : std::integral_constant<architecture,family_of<E>> {};
338 template<architecture Family>
struct arch_family<
isa<Family>> : std::integral_constant<architecture,Family> {};
341 template<
class T>
concept arch=detail::instruction_feature<T> || detail::is_isa<T>;
343 template<arch T>
inline constexpr architecture arch_family_v=arch_family<T>::value;
344 template<
class A,
class B>
concept same_arch=
arch<A> &&
arch<B> && arch_family_v<A> == arch_family_v<B>;
348 template<arch A,arch B>
requires detail::same_arch<A,B>
351 for(std::size_t i=0;i<result.
flags.size();++i) result.
flags[i]|=other.flags[i];
380 template<arch A,arch B>
requires detail::same_arch<A,B>
383 template<arch A,arch B>
requires detail::same_arch<A,B>
386 template<arch A,arch B>
requires detail::same_arch<A,B>
389 template<arch A,arch B>
requires detail::same_arch<A,B>
390 constexpr bool operator>(A a,B b)
noexcept {
return b<a; }
394 template<
auto A,
auto... Choices>
395 requires arch<
decltype(A)> && ((detail::same_arch<
decltype(A),
decltype(Choices)>) && ...)
396 inline constexpr
int target=[]() consteval {
397 using value_type=
isa<detail::arch_family_v<
decltype(A)>>;
398 constexpr std::array<value_type,
sizeof...(Choices)> choices{value_type(Choices)...};
400 for(std::size_t i=0;i<choices.size();++i)
401 for(std::size_t j=i+1;j<choices.size();++j)
402 if(choices[i]<=choices[j])
return false;
404 }(),
"a later target is shadowed by an earlier one");
405 for(std::size_t i=0;i<choices.size();++i)
406 if(choices[i]<=value_type(A))
return int(i);
411 template<architecture Family>
413 for(std::size_t i=0;i<a.flags.size();++i) a.flags[i]&=b.flags[i];
416 enum class feature_register { leaf1_ecx, leaf1_edx, leaf7_ebx, leaf7_ecx, leaf7_edx, leaf7_1_eax, leaf7_1_edx, extended1_ecx, arm, wasm };
417 template<architecture Family>
struct feature_record {
419 std::string_view spelling;
421 feature_register location;
427 constexpr feature_record(
typename feature_traits<Family>::type f,std::string_view spelling,
isa<Family> implies,
428 feature_register location,
unsigned bit,
isa<Family> target_implies={},
bool targetable=
true)
noexcept:
429 value(f),spelling(spelling),implies(implies),location(location),bit(bit),index(std::size_t(f)),
430 target_implies(target_implies),targetable(targetable) {}
433 template<architecture Family>
inline constexpr auto feature_registry=std::array<feature_record<Family>,0>{};
434 template<>
inline constexpr auto feature_registry<x86> = std::to_array<feature_record<x86>>({
435 {x86_feature::mmx,
"mmx",{},feature_register::leaf1_edx,23},
436 {x86_feature::sse,
"sse",
isa(x86_feature::mmx),feature_register::leaf1_edx,25},
437 {x86_feature::sse2,
"sse2",
isa(x86_feature::sse),feature_register::leaf1_edx,26},
438 {x86_feature::sse3,
"sse3",
isa(x86_feature::sse2),feature_register::leaf1_ecx,0},
439 {x86_feature::ssse3,
"ssse3",
isa(x86_feature::sse3),feature_register::leaf1_ecx,9},
440 {x86_feature::sse41,
"sse4.1",
isa(x86_feature::ssse3),feature_register::leaf1_ecx,19},
441 {x86_feature::sse42,
"sse4.2",x86_feature::sse41&x86_feature::popcnt&x86_feature::crc32,feature_register::leaf1_ecx,20},
442 {x86_feature::popcnt,
"popcnt",{},feature_register::leaf1_ecx,23},
443 {x86_feature::avx,
"avx",
isa(x86_feature::sse42),feature_register::leaf1_ecx,28},
444 {x86_feature::avx2,
"avx2",
isa(x86_feature::avx),feature_register::leaf7_ebx,5},
445 {x86_feature::fma,
"fma",
isa(x86_feature::avx),feature_register::leaf1_ecx,12},
446 {x86_feature::f16c,
"f16c",
isa(x86_feature::avx),feature_register::leaf1_ecx,29},
447 {x86_feature::bmi1,
"bmi",{},feature_register::leaf7_ebx,3},
448 {x86_feature::bmi2,
"bmi2",{},feature_register::leaf7_ebx,8},
449 {x86_feature::mwaitx,
"mwaitx",{},feature_register::extended1_ecx,29},
450 {x86_feature::waitpkg,
"waitpkg",{},feature_register::leaf7_ecx,5},
451 {x86_feature::crc32,
"crc32",{},feature_register::leaf1_ecx,20},
452 {x86_feature::gfni,
"gfni",
isa(x86_feature::sse2),feature_register::leaf7_ecx,8},
453 {x86_feature::avx512f,
"avx512f",x86_feature::avx2&x86_feature::f16c&x86_feature::fma,feature_register::leaf7_ebx,16},
454 {x86_feature::avx512dq,
"avx512dq",
isa(x86_feature::avx512f),feature_register::leaf7_ebx,17},
455 {x86_feature::avx512bw,
"avx512bw",
isa(x86_feature::avx512f),feature_register::leaf7_ebx,30},
456 {x86_feature::avx512vl,
"avx512vl",
isa(x86_feature::avx512f),feature_register::leaf7_ebx,31},
457 {x86_feature::avx512bf16,
"avx512bf16",
isa(x86_feature::avx512bw),feature_register::leaf7_1_eax,5},
458 {x86_feature::avx512fp16,
"avx512fp16",
isa(x86_feature::avx512bw),feature_register::leaf7_edx,23},
459 {x86_feature::avx512vbmi,
"avx512vbmi",
isa(x86_feature::avx512bw),feature_register::leaf7_ecx,1},
460 {x86_feature::avx512vbmi2,
"avx512vbmi2",
isa(x86_feature::avx512bw),feature_register::leaf7_ecx,6},
461 {x86_feature::avx512bitalg,
"avx512bitalg",
isa(x86_feature::avx512bw),feature_register::leaf7_ecx,12},
462 {x86_feature::avx512vpopcntdq,
"avx512vpopcntdq",
isa(x86_feature::avx512f),feature_register::leaf7_ecx,14},
463 {x86_feature::avxvnni,
"avxvnni",
isa(x86_feature::avx2),feature_register::leaf7_1_eax,4},
464 {x86_feature::avx512vnni,
"avx512vnni",
isa(x86_feature::avx512f),feature_register::leaf7_ecx,11},
465 {x86_feature::avxvnniint8,
"avxvnniint8",
isa(x86_feature::avx2),feature_register::leaf7_1_edx,4},
466 {x86_feature::avxvnniint16,
"avxvnniint16",
isa(x86_feature::avx2),feature_register::leaf7_1_edx,10},
467 {x86_feature::aes,
"aes",
isa(x86_feature::sse2),feature_register::leaf1_ecx,25},
468 {x86_feature::pclmul,
"pclmul",
isa(x86_feature::sse2),feature_register::leaf1_ecx,1},
469 {x86_feature::sha,
"sha",
isa(x86_feature::sse2),feature_register::leaf7_ebx,29},
470 {x86_feature::vaes,
"vaes",
isa(x86_feature::avx),feature_register::leaf7_ecx,9,
471 x86_feature::aes&x86_feature::avx2},
472 {x86_feature::vpclmulqdq,
"vpclmulqdq",x86_feature::avx&x86_feature::pclmul,feature_register::leaf7_ecx,10},
473 {x86_feature::cx16,
"cx16",{},feature_register::leaf1_ecx,13},
474 {x86_feature::avx512cd,
"avx512cd",
isa(x86_feature::avx512f),feature_register::leaf7_ebx,28},
475 {x86_feature::avx512ifma,
"avx512ifma",
isa(x86_feature::avx512f),feature_register::leaf7_ebx,21},
476 {x86_feature::avxifma,
"avxifma",
isa(x86_feature::avx),feature_register::leaf7_1_eax,23,
isa(x86_feature::avx2)},
477 {x86_feature::adx,
"adx",{},feature_register::leaf7_ebx,19},
478 {x86_feature::sha512,
"sha512",
isa(x86_feature::avx),feature_register::leaf7_1_eax,0,
isa(x86_feature::avx2)},
479 {x86_feature::sm3,
"sm3",
isa(x86_feature::avx),feature_register::leaf7_1_eax,1},
480 {x86_feature::sm4,
"sm4",
isa(x86_feature::avx),feature_register::leaf7_1_eax,2,
isa(x86_feature::avx2)},
481 {x86_feature::avxneconvert,
"avxneconvert",
isa(x86_feature::avx),feature_register::leaf7_1_edx,5,
isa(x86_feature::avx2)},
482 {x86_feature::lzcnt,
"lzcnt",{},feature_register::extended1_ecx,5},
483 {x86_feature::movbe,
"movbe",{},feature_register::leaf1_ecx,22},
484 {x86_feature::sahf,
"sahf",{},feature_register::extended1_ecx,0}
486 template<>
inline constexpr auto feature_registry<arm> = std::to_array<feature_record<arm>>({
487 {arm_feature::neon,
"neon",{},feature_register::arm,0},
488 {arm_feature::neon_fp16,
"fullfp16",
isa(arm_feature::neon),feature_register::arm,1},
489 {arm_feature::neon_bf16,
"bf16",
isa(arm_feature::neon),feature_register::arm,2},
490 {arm_feature::aes,
"aes",
isa(arm_feature::neon),feature_register::arm,3,
isa(arm_feature::pmull)},
491 {arm_feature::sha2,
"sha2",
isa(arm_feature::neon),feature_register::arm,4,
isa(arm_feature::sha1)},
492 {arm_feature::sha3,
"sha3",
isa(arm_feature::neon),feature_register::arm,5,arm_feature::sha1&arm_feature::sha2&arm_feature::sha512},
493 {arm_feature::crc,
"crc",{},feature_register::arm,6},
494 {arm_feature::lse,
"lse",
isa(arm_feature::neon),feature_register::arm,7},
495 {arm_feature::rdm,
"rdm",
isa(arm_feature::neon),feature_register::arm,8},
496 {arm_feature::fp16fml,
"fp16fml",
isa(arm_feature::neon_fp16),feature_register::arm,9},
497 {arm_feature::dotprod,
"dotprod",
isa(arm_feature::neon),feature_register::arm,10},
498 {arm_feature::complxnum,
"complxnum",
isa(arm_feature::neon),feature_register::arm,11},
499 {arm_feature::jsconv,
"jsconv",
isa(arm_feature::neon),feature_register::arm,12},
500 {arm_feature::rcpc,
"rcpc",
isa(arm_feature::neon),feature_register::arm,13},
501 {arm_feature::pauth,
"pauth",
isa(arm_feature::neon),feature_register::arm,14},
502 {arm_feature::i8mm,
"i8mm",
isa(arm_feature::neon),feature_register::arm,15},
503 {arm_feature::pmull,
"pmull",
isa(arm_feature::neon),feature_register::arm,16,{},
false},
504 {arm_feature::sha1,
"sha1",
isa(arm_feature::neon),feature_register::arm,17,{},
false},
505 {arm_feature::sha512,
"sha512",
isa(arm_feature::neon),feature_register::arm,18,{},
false},
506 {arm_feature::ebf16,
"ebf16",
isa(arm_feature::neon_bf16),feature_register::arm,19,{},
false},
507 {arm_feature::sm3,
"sm3",
isa(arm_feature::neon),feature_register::arm,20,{},
false},
508 {arm_feature::sm4,
"sm4",
isa(arm_feature::neon),feature_register::arm,21,
isa(arm_feature::sm3)}
510 template<>
inline constexpr auto feature_registry<wasm> = std::to_array<feature_record<wasm>>({
511 {wasm_feature::simd128,
"simd128",{},feature_register::wasm,0},
512 {wasm_feature::relaxed_simd,
"relaxed-simd",
isa(wasm_feature::simd128),feature_register::wasm,1}
514 template<architecture Family>
inline constexpr isa<Family> known_features=[] {
516 for(
auto const & entry:feature_registry<Family>) result=result&entry.value;
519 template<instruction_feature E>
inline constexpr auto family_features=known_features<family_of<E>>;
520 inline constexpr auto arm_features=known_features<arm>;
521 inline constexpr auto x86_features=known_features<x86>;
522 inline constexpr auto wasm_features=known_features<wasm>;
523 template<architecture Family>
inline constexpr isa<Family> invalid_features=[] {
525 constexpr auto index=feature_traits<Family>::count;
526 result.flags[index/64]|=std::uint64_t{1}<<(index%64);
530 template<isa<> A>
inline constexpr isa<> source_isa=[]()
consteval {
531 static_assert(A<=known_features<target_arch>,
"source target contains an unregistered ISA feature");
539 constexpr auto family=detail::arch_family_v<A>;
543 for(
auto const & entry:detail::feature_registry<family>)
544 if(bits.
has(entry.value)) bits=bits&entry.implies;
545 }
while(previous!=bits);
549 inline constexpr isa<> scalar{};
550 inline constexpr auto avx2=
feature_closure(x86_feature::avx2&x86_feature::fma);
551 inline constexpr auto avx512=
feature_closure(avx2&x86_feature::avx512f&x86_feature::avx512dq&x86_feature::avx512bw&x86_feature::avx512vl);
552 inline constexpr auto avx512_bf16=
feature_closure(avx512&x86_feature::avx512bf16);
553 inline constexpr auto avx512_fp16=
feature_closure(avx512&x86_feature::avx512fp16);
555 inline constexpr auto neon_fp16=
feature_closure(arm_feature::neon_fp16);
556 inline constexpr auto neon_bf16=
feature_closure(arm_feature::neon_bf16);
560 template<architecture Family=target_arch>
562 if(text.empty())
return {};
564 while(!text.empty()) {
565 auto comma=text.find(
',');
566 auto token=text.substr(0,comma);
568 for(
auto const & entry:detail::feature_registry<Family>)
if(entry.targetable && token.compare(entry.spelling)==0) {
569 bits=bits&entry.value&entry.target_implies; found=
true;
break;
571 if(!found)
return detail::invalid_features<Family>;
572 if(comma==std::string_view::npos)
break;
573 text.remove_prefix(comma+1);
574 if(text.empty())
return detail::invalid_features<Family>;
581 std::uint64_t missing_xcr0=0;
582 bool invalid_features=
false;
583 bool missing_xcr0_observation=
false;
586 return missing_features==
isa<Family>{} && !missing_xcr0 && !invalid_features && !missing_xcr0_observation;
591 constexpr char const *
reason() const noexcept {
592 if(invalid_features)
return "invalid ISA features";
593 for(
auto const & entry:detail::feature_registry<Family>)
594 if(missing_features.has(entry.value))
return entry.spelling.data();
595 if(missing_xcr0_observation)
return "XCR0 unavailable";
596 if(missing_xcr0 & (1ull<<1))
return "XMM state unavailable";
597 if(missing_xcr0 & (1ull<<2))
return "YMM state unavailable";
598 if(missing_xcr0 & (1ull<<5))
return "opmask state unavailable";
599 if(missing_xcr0 & (1ull<<6))
return "ZMM high state unavailable";
600 if(missing_xcr0 & (1ull<<7))
return "high ZMM registers unavailable";
601 return admitted() ?
"admitted" :
"ISA requirements unavailable";
606 template<
class C>
concept x86_observation=
requires(C
const & c) {
607 c.max_basic_leaf; c.leaf1_ecx; c.leaf1_edx; c.leaf7_ebx;
608 c.max_leaf7_subleaf; c.leaf7_1_eax; c.leaf7_edx;
610 template<
class C>
concept arm_observation=
requires(C
const & c) {
611 c.baseline_observed; c.fp; c.asimd;
612 c.fp16_observed; c.scalar_fp16; c.vector_fp16;
613 c.bf16_observed; c.bf16;
615 template<
class C,architecture Family>
concept normalized_features=
requires(C
const & c) {
616 { c.present } -> std::same_as<isa<Family>
const &>;
617 { c.observed } -> std::same_as<isa<Family>
const &>;
619 template<
class C>
concept has_feature_observation=
requires(C
const & c) { c.present; c.observed; };
620 template<architecture Family>
struct feature_observation {
623 template<x86_observation C>
624 constexpr feature_observation<x86> decode_x86_features(C
const & cpu)
noexcept {
625 feature_observation<x86> result;
626 for(
auto const & entry:feature_registry<x86>) {
627 std::uint32_t word=0;
629 switch(entry.location) {
630 case feature_register::leaf1_ecx: observed=cpu.max_basic_leaf>=1; word=cpu.leaf1_ecx;
break;
631 case feature_register::leaf1_edx: observed=cpu.max_basic_leaf>=1; word=cpu.leaf1_edx;
break;
632 case feature_register::leaf7_ebx: observed=cpu.max_basic_leaf>=7; word=cpu.leaf7_ebx;
break;
633 case feature_register::leaf7_ecx:
634 if constexpr(
requires { cpu.leaf7_ecx; }) {
635 observed=cpu.max_basic_leaf>=7; word=cpu.leaf7_ecx;
638 case feature_register::leaf7_edx: observed=cpu.max_basic_leaf>=7; word=cpu.leaf7_edx;
break;
639 case feature_register::leaf7_1_eax:
640 observed=cpu.max_basic_leaf>=7 && cpu.max_leaf7_subleaf>=1; word=cpu.leaf7_1_eax;
break;
641 case feature_register::leaf7_1_edx:
642 if constexpr(
requires { cpu.leaf7_1_edx; }) {
643 observed=cpu.max_basic_leaf>=7 && cpu.max_leaf7_subleaf>=1; word=cpu.leaf7_1_edx;
646 case feature_register::extended1_ecx:
647 if constexpr(
requires { cpu.max_extended_leaf; cpu.extended1_ecx; }) {
648 observed=cpu.max_extended_leaf>=0x80000001u; word=cpu.extended1_ecx;
651 case feature_register::arm:
case feature_register::wasm:
continue;
654 result.observed.set(f,observed);
655 result.present.set(f,observed && (word&(std::uint32_t{1}<<entry.bit)));
659 template<arm_observation C>
660 constexpr feature_observation<arm> decode_arm_features(C
const & cpu)
noexcept {
661 feature_observation<arm> result;
662 result.observed.set(arm_feature::neon,cpu.baseline_observed);
663 result.present.set(arm_feature::neon,cpu.baseline_observed && cpu.fp && cpu.asimd);
664 result.observed.set(arm_feature::neon_fp16,cpu.fp16_observed);
665 result.present.set(arm_feature::neon_fp16,cpu.fp16_observed && cpu.scalar_fp16 && cpu.vector_fp16);
666 result.observed.set(arm_feature::neon_bf16,cpu.bf16_observed);
667 result.present.set(arm_feature::neon_bf16,cpu.bf16_observed && cpu.bf16);
668 if constexpr(
requires { cpu.ebf16_observed; cpu.ebf16; }) {
669 result.observed.set(arm_feature::ebf16,cpu.ebf16_observed);
670 result.present.set(arm_feature::ebf16,cpu.ebf16_observed && cpu.ebf16);
672 constexpr auto baseline=arm_feature::neon&arm_feature::neon_fp16&arm_feature::neon_bf16&arm_feature::ebf16;
673 if constexpr(
requires { cpu.extra_observed; cpu.extra_features; })
674 for(
auto const & entry:feature_registry<arm>) {
675 if(baseline.has(entry.value))
continue;
677 auto observed=cpu.extra_observed.has(f);
678 result.observed.set(f,observed);
679 result.present.set(f,observed && cpu.extra_features.has(f));
683 template<architecture Family>
685 isa_admission<Family> result;
686 auto known=known_features<Family>;
687 result.invalid_features=!present.valid() || !observed.valid() || !(bits<=known);
688 auto available=intersection(present,observed);
689 result.missing_features=intersection(bits,known);
690 for(std::size_t i=0;i<available.flags.size();++i)
691 result.missing_features.flags[i]&=~available.flags[i];
694 constexpr isa_admission<x86> classify_x86_features(isa<x86> present,
695 isa<x86> observed,std::uint64_t xcr0,
bool readable,isa<x86> bits)
noexcept {
696 auto result=classify_features(present,observed,bits);
697 if(bits.has(x86_feature::avx)) {
698 result.missing_xcr0_observation=!readable;
699 result.missing_xcr0=(bits.has(x86_feature::avx512f)?0xe6ull:0x6ull)&~(readable?xcr0:0ull);
708 template<
class C>
requires detail::normalized_features<C,x86> &&
requires(C
const & c) { c.xcr0; c.xcr0_observed; }
710 return detail::classify_x86_features(cpu.present,cpu.observed,cpu.xcr0,cpu.xcr0_observed,
711 feature_closure(requested&minimum));
716 template<
class C>
requires detail::normalized_features<C,arm>
718 return detail::classify_features(cpu.present,cpu.observed,feature_closure(requested&minimum));
723 template<
class C>
requires detail::normalized_features<C,wasm>
725 return detail::classify_features(runtime.present,runtime.observed,feature_closure(requested&minimum));
730 template<detail::x86_observation C>
requires (!detail::has_feature_observation<C>) &&
requires(C
const & c) { c.xcr0; c.xcr0_observed; }
732 auto features=detail::decode_x86_features(cpu);
733 bool readable=cpu.max_basic_leaf>=1 && (cpu.leaf1_ecx&(1u<<26)) &&
734 (cpu.leaf1_ecx&(1u<<27)) && cpu.xcr0_observed;
735 return detail::classify_x86_features(features.present,features.observed,cpu.xcr0,readable,
736 feature_closure(requested&minimum));
741 template<detail::arm_observation C>
requires (!detail::has_feature_observation<C>)
743 auto features=detail::decode_arm_features(cpu);
744 return detail::classify_features(features.present,features.observed,feature_closure(requested&minimum));
757 template<detail::instruction_feature E>
target_entry(E)->target_entry<detail::family_of<E>>;
759 template<arch A,arch B>
requires detail::same_arch<A,B>
761 template<
auto... Entries>
struct isa_list {};
763 template<
auto... A,
auto... B>
764 constexpr isa_list<A...,B...>
operator+(isa_list<A...>,isa_list<B...>)
noexcept {
return {}; }
767 template<auto E,
int I>
struct abi_match {
768 static constexpr auto entry=[] {
769 if constexpr(arch<std::remove_cv_t<
decltype(E)>>)
return target_entry<arch_family_v<std::remove_cv_t<
decltype(E)>>>{E};
772 static constexpr auto architecture=entry.architecture;
773 static constexpr auto minimum=entry.minimum;
774 static_assert(architecture<=known_features<
decltype(architecture)::family>,
"ABI policy contains an unregistered ISA feature");
775 static_assert(minimum<=known_features<
decltype(minimum)::family>,
"ABI policy minimum contains an unregistered ISA feature");
776 static constexpr auto required_features=
feature_closure(architecture&minimum);
777 static constexpr bool matched=
true;
778 static constexpr int index=I;
780 template<
isa A,
int I,
auto... Entries>
struct abi_lookup_impl {
781 static constexpr bool matched=
false;
782 static constexpr int index=-1;
783 static constexpr decltype(A) architecture{},minimum{},required_features{};
785 template<
isa A,
int I,
auto E,
auto... Rest>
786 struct abi_lookup_impl<A,I,E,Rest...> : std::conditional_t<
787 abi_match<E,I>::required_features<=A,
788 abi_match<E,I>,abi_lookup_impl<A,I+1,Rest...>> {};
792 template<isa A,class List> struct abi_lookup;
793 template<isa A,auto... Entries>
794 requires ((decltype(detail::abi_match<Entries,0>::architecture)::family==decltype(A)::family) && ...)
795 struct abi_lookup<A,isa_list<Entries...>> : detail::abi_lookup_impl<A,0,Entries...> {};
798 template<class C> concept capability_observation=
799 normalized_features<C,x86> || normalized_features<C,arm> || normalized_features<C,wasm> ||
800 x86_observation<C> || arm_observation<C>;
801 template<capability_observation C> inline constexpr architecture observation_family=[] {
802 if constexpr(normalized_features<C,x86>) return x86;
803 else if constexpr(normalized_features<C,arm>) return arm;
804 else if constexpr(normalized_features<C,wasm>) return wasm;
805 else if constexpr(x86_observation<C>) return x86;
810 template<auto... Entries,detail::capability_observation C,class F>
811 requires ((decltype(detail::abi_match<Entries,0>::architecture)::family==detail::observation_family<C>) && ...)
812 constexpr bool with_isa(isa_list<Entries...>,C const & cpu,F && callback,isa<detail::observation_family<C>> minimum={}) {
814 auto try_entry=[&]<auto E>() {
815 using entry=detail::abi_match<E,0>;
816 if(!selected && classify_isa(cpu,entry::architecture,minimum&entry::minimum).admitted()) {
818 std::forward<F>(callback).template operator()<entry::architecture>();
821 (try_entry.template operator()<Entries>(),...);
A feature enum or an ISA value from one architecture family.
constexpr std::uint32_t crc32(std::uint32_t accumulator, std::uint8_t value) noexcept
Update the CRC32 accumulator with exactly 8 input bits.
constexpr simd< std::uint64_t, 2, Arch > pmull(std::uint64_t a, std::uint64_t b) noexcept
std::string_view const type
typename mask_traits< std::remove_cvref_t< T > >::type mask
constexpr std::uint16_t lzcnt(std::uint16_t value) noexcept
constexpr std::uint16_t popcnt(std::uint16_t value) noexcept
constexpr simd< std::uint64_t, 2, Arch > vpclmulqdq(simd< std::uint64_t, 2, Arch > a, simd< std::uint64_t, 2, Arch > b) noexcept
Multiply selected halves of one 128-bit lane using PCLMUL and AVX.
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr std::size_t arm_feature_count
Number of named ARM feature values.
constexpr architecture arm
Select ARM feature metadata independently of the compilation target.
constexpr architecture x86
Select x86 feature metadata independently of the compilation target.
void operator>=(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
void operator>(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
x86_feature
Architecture-specific instruction features, represented by local bit indices.
architecture
Instruction-set families; an ISA value belongs to exactly one family.
target_entry(isa< Family >, isa< Family >={}) -> target_entry< Family >
Deduce a source record from same-family ISA values.
constexpr auto feature_closure(A input) noexcept
constexpr architecture wasm
Select WebAssembly feature metadata independently of the compilation target.
constexpr simd< fp16, 32, Arch > fma(simd< fp16, 32, Arch > a, simd< fp16, 32, Arch > b, simd< fp16, 32, Arch > c) noexcept
void operator<=(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
constexpr std::size_t x86_feature_count
Number of named x86 feature values.
constexpr isa_admission< x86 > classify_isa(C const &cpu, isa< x86 > requested, isa< x86 > minimum={}) noexcept
@ unknown
Any other vendor string.
constexpr int target
First matching requirement, with every later choice checked for shadowing.
void operator&(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
void operator+(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
constexpr isa< Family > target_features(std::string_view text) noexcept
wasm_feature
WebAssembly validation capabilities, using local bit indices.
void operator<(simd< T, N, A >, simd< U, M, B >)=delete
Reject mixed architectures and mismatched short-vector widths before native conversions can participa...
constexpr std::size_t wasm_feature_count
Number of named Wasm feature values.
arm_feature
Independently observable ARM instruction features, using local bit indices.
isa(E) -> isa< detail::family_of< E > >
Deduce the family from a feature enum; explicit isa<> always names the host family.
bool neon
Read or update the neon feature bit through flags.
constexpr bool has(isa other) const noexcept
True when every bit in a same-family requirement is present.
typename detail::feature_traits< arm >::type feature_type
Feature enum accepted by this ISA family.
static constexpr auto feature_count
Number of registered features in this family.
bool lse
Read or update the lse feature bit through flags.
bool i8mm
Read or update the i8mm feature bit through flags.
bool fp16fml
Read or update the fp16fml feature bit through flags.
std::array< std::uint64_t,(feature_count+1+63)/64 > flags
Public structural bits; invalid enum values and padding fail validation.
constexpr bool valid() const noexcept
True when no invalid marker or unregistered padding bit is set.
static constexpr architecture family
Architecture family, independent of the compiler target.
bool neon_bf16
Read or update the neon_bf16 feature bit through flags.
bool sha1
Read or update the sha1 feature bit through flags.
constexpr bool get(feature_type f) const noexcept
Read one feature; out-of-range values are absent.
bool pauth
Read or update the pauth feature bit through flags.
constexpr bool has(feature_type f) const noexcept
True when the feature is present.
bool aes
Read or update the aes feature bit through flags.
constexpr void set(feature_type f, bool value) noexcept
bool sha3
Read or update the sha3 feature bit through flags.
bool jsconv
Read or update the jsconv feature bit through flags.
constexpr isa() noexcept=default
Construct the empty requirement set.
bool crc
Read or update the crc feature bit through flags.
bool sha512
Read or update the sha512 feature bit through flags.
bool rcpc
Read or update the rcpc feature bit through flags.
bool sm4
Read or update the sm4 feature bit through flags.
bool sha2
Read or update the sha2 feature bit through flags.
bool neon_fp16
Read or update the neon_fp16 feature bit through flags.
bool complxnum
Read or update the complxnum feature bit through flags.
bool pmull
Read or update the pmull feature bit through flags.
bool dotprod
Read or update the dotprod feature bit through flags.
constexpr bool operator==(isa const &) const =default
Compare every stored bit, including invalid requirements.
bool rdm
Read or update the rdm feature bit through flags.
constexpr void set(feature_type f, bool value) noexcept
constexpr isa() noexcept=default
Construct the empty requirement set.
bool simd128
Read or update the simd128 feature bit through flags.
constexpr bool has(feature_type f) const noexcept
True when the feature is present.
constexpr bool operator==(isa const &) const =default
Compare every stored bit, including invalid requirements.
std::array< std::uint64_t,(feature_count+1+63)/64 > flags
Public structural bits; invalid enum values and padding fail validation.
constexpr bool valid() const noexcept
True when no invalid marker or unregistered padding bit is set.
static constexpr architecture family
Architecture family, independent of the compiler target.
typename detail::feature_traits< wasm >::type feature_type
Feature enum accepted by this ISA family.
static constexpr auto feature_count
Number of registered features in this family.
constexpr bool get(feature_type f) const noexcept
Read one feature; out-of-range values are absent.
bool relaxed_simd
Read or update the relaxed_simd feature bit through flags.
constexpr bool has(isa other) const noexcept
True when every bit in a same-family requirement is present.
bool lzcnt
Read or update the lzcnt feature bit through flags.
bool sse42
Read or update the sse42 feature bit through flags.
bool sha512
Read or update the sha512 feature bit through flags.
bool avx512bw
Read or update the avx512bw feature bit through flags.
bool bmi1
Read or update the bmi1 feature bit through flags.
bool avx512dq
Read or update the avx512dq feature bit through flags.
typename detail::feature_traits< x86 >::type feature_type
Feature enum accepted by this ISA family.
bool crc32
Read or update the crc32 feature bit through flags.
bool sse2
Read or update the sse2 feature bit through flags.
bool avx512f
Read or update the avx512f feature bit through flags.
bool sse
Read or update the sse feature bit through flags.
bool f16c
Read or update the f16c feature bit through flags.
static constexpr auto feature_count
Number of registered features in this family.
bool avx512bitalg
Read or update the avx512bitalg feature bit through flags.
bool ssse3
Read or update the ssse3 feature bit through flags.
bool sm3
Read or update the sm3 feature bit through flags.
bool avxifma
Read or update the avxifma feature bit through flags.
bool avx512fp16
Read or update the avx512fp16 feature bit through flags.
bool popcnt
Read or update the popcnt feature bit through flags.
bool avxneconvert
Read or update the avxneconvert feature bit through flags.
bool adx
Read or update the adx feature bit through flags.
bool avx
Read or update the avx feature bit through flags.
bool sha
Read or update the sha feature bit through flags.
bool avx512vl
Read or update the avx512vl feature bit through flags.
bool avx512bf16
Read or update the avx512bf16 feature bit through flags.
bool avxvnniint8
Read or update the avxvnniint8 feature bit through flags.
bool avx512ifma
Read or update the avx512ifma feature bit through flags.
bool fma
Read or update the fma feature bit through flags.
bool avx512vbmi2
Read or update the avx512vbmi2 feature bit through flags.
bool avx2
Read or update the avx2 feature bit through flags.
constexpr bool operator==(isa const &) const =default
Compare every stored bit, including invalid requirements.
constexpr isa() noexcept=default
Construct the empty requirement set.
bool aes
Read or update the aes feature bit through flags.
constexpr bool valid() const noexcept
True when no invalid marker or unregistered padding bit is set.
bool avxvnniint16
Read or update the avxvnniint16 feature bit through flags.
bool cx16
Read or update the cx16 feature bit through flags.
bool sm4
Read or update the sm4 feature bit through flags.
bool mmx
Read or update the mmx feature bit through flags.
constexpr bool has(feature_type f) const noexcept
True when the feature is present.
bool vpclmulqdq
Read or update the vpclmulqdq feature bit through flags.
constexpr bool has(isa other) const noexcept
True when every bit in a same-family requirement is present.
bool sse41
Read or update the sse41 feature bit through flags.
std::array< std::uint64_t,(feature_count+1+63)/64 > flags
Public structural bits; invalid enum values and padding fail validation.
bool avx512vnni
Read or update the avx512vnni feature bit through flags.
bool gfni
Read or update the gfni feature bit through flags.
static constexpr architecture family
Architecture family, independent of the compiler target.
bool mwaitx
Read or update the mwaitx feature bit through flags.
bool sahf
Read or update the sahf feature bit through flags.
constexpr bool get(feature_type f) const noexcept
Read one feature; out-of-range values are absent.
bool avx512vbmi
Read or update the avx512vbmi feature bit through flags.
bool avx512vpopcntdq
Read or update the avx512vpopcntdq feature bit through flags.
constexpr void set(feature_type f, bool value) noexcept
bool waitpkg
Read or update the waitpkg feature bit through flags.
bool pclmul
Read or update the pclmul feature bit through flags.
bool vaes
Read or update the vaes feature bit through flags.
bool avx512cd
Read or update the avx512cd feature bit through flags.
bool sse3
Read or update the sse3 feature bit through flags.
bool avxvnni
Read or update the avxvnni feature bit through flags.
bool movbe
Read or update the movbe feature bit through flags.
bool bmi2
Read or update the bmi2 feature bit through flags.
Admission result for requirements from one architecture family.
constexpr char const * reason() const noexcept
constexpr bool admitted() const noexcept
True only when every requested feature and OS state component is present.
A structural set of one family's features, without prerequisite closure.
static constexpr auto feature_count
constexpr bool has(feature_type f) const noexcept
True when the feature is present.
constexpr isa() noexcept=default
Construct the empty requirement set.
static constexpr architecture family
constexpr bool operator==(isa const &) const =default
Compare every stored bit, including invalid requirements.
std::array< std::uint64_t,(feature_count+1+63)/64 > flags
constexpr void set(feature_type f, bool value) noexcept
typename detail::feature_traits< Family >::type feature_type
constexpr bool has(isa other) const noexcept
True when every bit in a same-family requirement is present.
constexpr bool get(feature_type f) const noexcept
Read one feature; out-of-range values are absent.
constexpr bool valid() const noexcept
True when no invalid marker or unregistered padding bit is set.
waiter using the MONITORX/MWAITX instruction feature
A source variant retains its requested ISA and inherited compiler minimum.
constexpr bool operator==(target_entry const &) const =default
Compare the requested ISA and inherited compiler minimum exactly.