/* * The instruction set level. See simd.h. The level is picked once, before * the first terminal exists, so every call in a process uses the same * kernels. */ #include "term.h" #include "simd.h" #include #include #include int term_isa_level; static atomic_int isa_state; static const char *const level_names[] = { "base", "avx2", "avx512" }; #if defined(TERM_ISA_RUNTIME) /* The AVX-512 kernels also need VBMI and VBMI2. Every CPU with VBMI except * Cannon Lake has VBMI2. */ static int cpu_level(void) { __builtin_cpu_init(); if (__builtin_cpu_supports("x86-64-v4") && __builtin_cpu_supports("avx512vbmi") && __builtin_cpu_supports("avx512vbmi2")) return TERM_ISA_AVX512; if (__builtin_cpu_supports("x86-64-v3")) return TERM_ISA_AVX2; return TERM_ISA_BASE; } #else static int cpu_level(void) { return TERM_ISA_FLOOR; } #endif static int pick_level(void) { int level = cpu_level(); /* TERM_HEADLESS_ISA can only lower the level, never below the floor the * compiler flags fix. */ const char *want = getenv("TERM_HEADLESS_ISA"); for (int i = TERM_ISA_FLOOR; want && i < level; i++) if (!strcmp(want, level_names[i])) level = i; return level; } /* The first caller picks the level. A second caller waits until it is set. */ void term_isa_init(void) { if (atomic_load_explicit(&isa_state, memory_order_acquire) == 2) return; int expected = 0; if (atomic_compare_exchange_strong(&isa_state, &expected, 1)) { term_isa_level = pick_level(); atomic_store_explicit(&isa_state, 2, memory_order_release); return; } while (atomic_load_explicit(&isa_state, memory_order_acquire) != 2) term_cpu_relax(); } const char *term_isa(void) { term_isa_init(); #if defined(__aarch64__) return "neon"; #else int level = TERM_ISA_FLOOR; if (term_use_avx2()) level = TERM_ISA_AVX2; if (term_use_avx512()) level = TERM_ISA_AVX512; return level_names[level]; #endif }