46#ifndef SIMDutf_INTERNAL_ISADETECTION_H
47#define SIMDutf_INTERNAL_ISADETECTION_H
53#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || \
62#include "simdutf/portability.h"
65#if SIMDUTF_IS_RISCV64 && defined(__linux__)
68struct simdutf_riscv_hwprobe {
72 #define simdutf_riscv_hwprobe(...) syscall(258, __VA_ARGS__)
73 #define SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0 4
74 #define SIMDUTF_RISCV_HWPROBE_IMA_V (1 << 2)
75 #define SIMDUTF_RISCV_HWPROBE_EXT_ZVBB (1 << 17)
78#if defined(__loongarch__) && defined(__linux__)
84#if defined(__aarch64__) && defined(__linux__)
87#if (defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) && \
88 defined(_WIN32) && !defined(_WINDOWS_)
92extern "C" __declspec(dllimport)
int __stdcall IsProcessorFeaturePresent(
93 unsigned long ProcessorFeature);
116 AVX512VBMI2 = 0x10000,
117 AVX512VPOPCNTDQ = 0x2000,
126#if defined(__PPC64__)
128static inline uint32_t detect_supported_architectures() {
129 return instruction_set::ALTIVEC;
132#elif SIMDUTF_IS_RISCV64
134static inline uint32_t detect_supported_architectures() {
135 uint32_t host_isa = instruction_set::DEFAULT;
137 host_isa |= instruction_set::RVV;
140 host_isa |= instruction_set::ZVBB;
142 #if defined(__linux__)
143 simdutf_riscv_hwprobe probes[] = {{SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0, 0}};
144 long ret = simdutf_riscv_hwprobe(&probes,
sizeof probes /
sizeof *probes, 0,
147 uint64_t extensions = probes[0].value;
148 if (extensions & SIMDUTF_RISCV_HWPROBE_IMA_V)
149 host_isa |= instruction_set::RVV;
150 if (extensions & SIMDUTF_RISCV_HWPROBE_EXT_ZVBB)
151 host_isa |= instruction_set::ZVBB;
154 #if defined(RUN_IN_SPIKE_SIMULATOR)
156 host_isa |= instruction_set::RVV;
161#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
163 #if defined(__linux__)
172 #define HWCAP_SVE (1 << 22)
175 #define HWCAP2_SVE2 (1 << 1)
181 #ifndef PF_ARM_SVE_INSTRUCTIONS_AVAILABLE
182 #define PF_ARM_SVE_INSTRUCTIONS_AVAILABLE 46
184 #ifndef PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE
185 #define PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE 47
189static inline uint32_t detect_supported_architectures() {
191 uint32_t host_isa = instruction_set::NEON;
192 #if defined(__linux__)
193 unsigned long hwcap = getauxval(AT_HWCAP);
194 unsigned long hwcap2 = getauxval(AT_HWCAP2);
195 if (hwcap & HWCAP_SVE) {
196 host_isa |= instruction_set::SVE;
201 if (hwcap2 & HWCAP2_SVE2) {
202 host_isa |= instruction_set::SVE2;
205 #elif defined(_WIN32)
206 if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)) {
207 host_isa |= instruction_set::SVE;
209 if (IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)) {
210 host_isa |= instruction_set::SVE2;
219#elif defined(__x86_64__) || defined(_M_AMD64)
226constexpr uint32_t pclmulqdq = uint32_t(1)
228constexpr uint32_t sse42 = uint32_t(1)
230constexpr uint32_t osxsave =
231 (uint32_t(1) << 26) |
237constexpr uint32_t bmi1 = uint32_t(1) << 3;
238constexpr uint32_t avx2 = uint32_t(1) << 5;
239constexpr uint32_t bmi2 = uint32_t(1) << 8;
240constexpr uint32_t avx512f = uint32_t(1) << 16;
241constexpr uint32_t avx512dq = uint32_t(1) << 17;
242constexpr uint32_t avx512ifma = uint32_t(1) << 21;
243constexpr uint32_t avx512cd = uint32_t(1) << 28;
244constexpr uint32_t avx512bw = uint32_t(1) << 30;
245constexpr uint32_t avx512vl = uint32_t(1) << 31;
249constexpr uint32_t avx512vbmi = uint32_t(1) << 1;
250constexpr uint32_t avx512vbmi2 = uint32_t(1) << 6;
251constexpr uint32_t avx512vnni = uint32_t(1) << 11;
252constexpr uint32_t avx512bitalg = uint32_t(1) << 12;
253constexpr uint32_t avx512vpopcnt = uint32_t(1) << 14;
256constexpr uint32_t avx512vp2intersect = uint32_t(1) << 8;
259constexpr uint64_t avx256_saved = uint64_t(1) << 2;
260constexpr uint64_t avx512_saved =
266static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
268 #if defined(_MSC_VER)
270 __cpuidex(cpu_info, *eax, *ecx);
275 #elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) || \
277 uint32_t level = *eax;
278 __get_cpuid(level, eax, ebx, ecx, edx);
280 uint32_t a = *eax, b, c = *ecx, d;
281 asm volatile(
"cpuid\n\t" :
"+a"(a),
"=b"(b),
"+c"(c),
"=d"(d));
289static inline uint64_t xgetbv() {
290 #if defined(_MSC_VER)
292 #elif defined(__FILC__)
295 uint32_t xcr0_lo, xcr0_hi;
296 asm volatile(
"xgetbv\n\t" :
"=a"(xcr0_lo),
"=d"(xcr0_hi) :
"c"(0));
297 return xcr0_lo | ((uint64_t)xcr0_hi << 32);
301static inline uint32_t detect_supported_architectures() {
306 uint32_t host_isa = 0x0;
310 cpuid(&eax, &ebx, &ecx, &edx);
312 if (ecx & cpuid_bit::sse42) {
313 host_isa |= instruction_set::SSE42;
316 if (ecx & cpuid_bit::pclmulqdq) {
317 host_isa |= instruction_set::PCLMULQDQ;
320 if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
325 uint64_t xcr0 = xgetbv();
327 if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
333 cpuid(&eax, &ebx, &ecx, &edx);
334 if (ebx & cpuid_bit::ebx::avx2) {
335 host_isa |= instruction_set::AVX2;
337 if (ebx & cpuid_bit::ebx::bmi1) {
338 host_isa |= instruction_set::BMI1;
340 if (ebx & cpuid_bit::ebx::bmi2) {
341 host_isa |= instruction_set::BMI2;
343 if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) ==
344 cpuid_bit::xcr0_bit::avx512_saved)) {
347 if (ebx & cpuid_bit::ebx::avx512f) {
348 host_isa |= instruction_set::AVX512F;
350 if (ebx & cpuid_bit::ebx::avx512bw) {
351 host_isa |= instruction_set::AVX512BW;
353 if (ebx & cpuid_bit::ebx::avx512cd) {
354 host_isa |= instruction_set::AVX512CD;
356 if (ebx & cpuid_bit::ebx::avx512dq) {
357 host_isa |= instruction_set::AVX512DQ;
359 if (ebx & cpuid_bit::ebx::avx512vl) {
360 host_isa |= instruction_set::AVX512VL;
362 if (ecx & cpuid_bit::ecx::avx512vbmi2) {
363 host_isa |= instruction_set::AVX512VBMI2;
365 if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
366 host_isa |= instruction_set::AVX512VPOPCNTDQ;
370#elif defined(__loongarch__)
372static inline uint32_t detect_supported_architectures() {
373 uint32_t host_isa = instruction_set::DEFAULT;
374 #if defined(__linux__)
376 hwcap = getauxval(AT_HWCAP);
377 if (hwcap & HWCAP_LOONGARCH_LSX) {
378 host_isa |= instruction_set::LSX;
380 if (hwcap & HWCAP_LOONGARCH_LASX) {
381 host_isa |= instruction_set::LASX;
389static inline uint32_t detect_supported_architectures() {
390 return instruction_set::DEFAULT;
helpers placed in namespace detail are not a part of the public API