#include "bio/bio.h"

static uint32_t g_bio_cpu_features = 0;

#if defined(_MSC_VER)
#include <intrin.h>
#endif

static void cpuid_leaf(uint32_t leaf, uint32_t subleaf, uint32_t out[4]) {
#if defined(_MSC_VER)
    int regs[4];
    __cpuidex(regs, (int)leaf, (int)subleaf);
    out[0] = (uint32_t)regs[0];
    out[1] = (uint32_t)regs[1];
    out[2] = (uint32_t)regs[2];
    out[3] = (uint32_t)regs[3];
#elif defined(__GNUC__) || defined(__clang__)
#if defined(__i386__) || defined(__x86_64__)
    uint32_t a = 0, b = 0, c = 0, d = 0;
    __asm__ volatile("cpuid"
                     : "=a"(a), "=b"(b), "=c"(c), "=d"(d)
                     : "a"(leaf), "c"(subleaf));
    out[0] = a;
    out[1] = b;
    out[2] = c;
    out[3] = d;
#else
    (void)leaf;
    (void)subleaf;
    out[0] = out[1] = out[2] = out[3] = 0;
#endif
#else
    (void)leaf;
    (void)subleaf;
    out[0] = out[1] = out[2] = out[3] = 0;
#endif
}

void bio_runtime_detect_features(void) {
    uint32_t r[4] = {0, 0, 0, 0};
    uint32_t max_leaf = 0;
    g_bio_cpu_features = 0;

#if defined(__aarch64__) || defined(_M_ARM64)
    g_bio_cpu_features |= BIO_CPU_NEON;
#endif

    cpuid_leaf(0u, 0u, r);
    max_leaf = r[0];
    if (max_leaf >= 1u) {
        cpuid_leaf(1u, 0u, r);
        if ((r[3] & (1u << 26)) != 0u) {
            g_bio_cpu_features |= BIO_CPU_SSE2;
        }
        if ((r[2] & (1u << 20)) != 0u) {
            g_bio_cpu_features |= BIO_CPU_SSE42;
        }
        if ((r[2] & (1u << 28)) != 0u) {
            g_bio_cpu_features |= BIO_CPU_AVX;
        }
    }
    if (max_leaf >= 7u) {
        cpuid_leaf(7u, 0u, r);
        if ((r[1] & (1u << 5)) != 0u) {
            g_bio_cpu_features |= BIO_CPU_AVX2;
        }
        if ((r[1] & (1u << 16)) != 0u) {
            g_bio_cpu_features |= BIO_CPU_AVX512F;
        }
    }
}

uint32_t bio_v1_cpu_features(void) {
    return g_bio_cpu_features;
}
