# dlib C++ Library - simd_check.h

    // Copyright (C) 2013  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SIMd_CHECK_Hh_
    #define DLIB_SIMd_CHECK_Hh_

    #include <array>
    #include <iostream>

    //#define DLIB_DO_NOT_USE_SIMD

    // figure out which SIMD instructions we can use.
    #ifndef DLIB_DO_NOT_USE_SIMD
        #if defined(_MSC_VER)
            #ifdef __AVX__
                #ifndef DLIB_HAVE_SSE2
                    #define DLIB_HAVE_SSE2
                #endif
                #ifndef DLIB_HAVE_SSE3
                    #define DLIB_HAVE_SSE3
                #endif
                #ifndef DLIB_HAVE_SSE41
                    #define DLIB_HAVE_SSE41
                #endif
                #ifndef DLIB_HAVE_AVX
                    #define DLIB_HAVE_AVX
                #endif
            #endif
            #if (defined( _M_X64) || defined(_M_IX86_FP) && _M_IX86_FP >= 2) && !defined(DLIB_HAVE_SSE2)
                #define DLIB_HAVE_SSE2
            #endif
        #else
            #ifdef __SSE2__
                #ifndef DLIB_HAVE_SSE2
                    #define DLIB_HAVE_SSE2
                #endif
            #endif
            #ifdef __SSSE3__
                #ifndef DLIB_HAVE_SSE3
                    #define DLIB_HAVE_SSE3
                #endif
            #endif
            #ifdef __SSE4_1__
                #ifndef DLIB_HAVE_SSE41
                    #define DLIB_HAVE_SSE41
                #endif
            #endif
            #ifdef __AVX__
                #ifndef DLIB_HAVE_AVX
                    #define DLIB_HAVE_AVX
                #endif
            #endif
            #ifdef __AVX2__
                #ifndef DLIB_HAVE_AVX2
                    #define DLIB_HAVE_AVX2
                #endif
            #endif
            #ifdef __ALTIVEC__
                #ifndef DLIB_HAVE_ALTIVEC
                    #define DLIB_HAVE_ALTIVEC
                #endif
            #endif
            #ifdef __VSX__
                #ifndef DLIB_HAVE_VSX
                    #define DLIB_HAVE_VSX
                #endif
            #endif
            #ifdef __VEC__ // __VEC__ = 10206
                #ifndef DLIB_HAVE_POWER_VEC	// vector and vec_ intrinsics
                    #define DLIB_HAVE_POWER_VEC
                #endif
            #endif
            #ifdef __ARM_NEON
                #ifndef DLIB_HAVE_NEON
                    #define DLIB_HAVE_NEON
                #endif
            #endif
        #endif
    #endif

    // ----------------------------------------------------------------------------------------

    #ifdef DLIB_HAVE_ALTIVEC
    #include <altivec.h>
    #endif

    #ifdef DLIB_HAVE_SSE2
        #include <xmmintrin.h>
        #include <emmintrin.h>
        #include <mmintrin.h>
    #endif
    #ifdef DLIB_HAVE_SSE3
        #include <pmmintrin.h> // SSE3
        #include <tmmintrin.h>
    #endif
    #ifdef DLIB_HAVE_SSE41
        #include <smmintrin.h> // SSE4
    #endif
    #ifdef DLIB_HAVE_AVX
        #include <immintrin.h> // AVX
    #endif
    #ifdef DLIB_HAVE_AVX2
        #include <immintrin.h> // AVX
    //    #include <avx2intrin.h>
    #endif
    #ifdef DLIB_HAVE_NEON
        #include <arm_neon.h> // ARM NEON
    #endif

    // ----------------------------------------------------------------------------------------
    // Define functions to check, at runtime, what instructions are available

    #if defined(_MSC_VER) && (defined(_M_I86) || defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) )
        #include <intrin.h>

        inline std::array<unsigned int,4> cpuid(int function_id)
        {
            std::array<unsigned int,4> info;
            // Load EAX, EBX, ECX, EDX into info
            __cpuid((int*)info.data(), function_id);
            return info;
        }

    #elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__i686__) || defined(__amd64__) || defined(__x86_64__))
        #include <cpuid.h>

        inline std::array<unsigned int,4> cpuid(int function_id)
        {
            std::array<unsigned int,4> info;
            // Load EAX, EBX, ECX, EDX into info
            __cpuid_count(function_id, 0, info[0], info[1], info[2], info[3]);
            return info;
        }

    #else

        inline std::array<unsigned int,4> cpuid(int)
        {
            return std::array<unsigned int,4>{};
        }

    #endif

        inline bool cpu_has_sse2_instructions()   { return 0!=(cpuid(1)[3]&(1<<26)); }
        inline bool cpu_has_sse3_instructions()   { return 0!=(cpuid(1)[2]&(1<<0));  }
        inline bool cpu_has_sse41_instructions()  { return 0!=(cpuid(1)[2]&(1<<19)); }
        inline bool cpu_has_sse42_instructions()  { return 0!=(cpuid(1)[2]&(1<<20)); }
        inline bool cpu_has_avx_instructions()    { return 0!=(cpuid(1)[2]&(1<<28)); }
        inline bool cpu_has_avx2_instructions()   { return 0!=(cpuid(7)[1]&(1<<5));  }
        inline bool cpu_has_avx512_instructions() { return 0!=(cpuid(7)[1]&(1<<16)); }

        inline void warn_about_unavailable_but_used_cpu_instructions()
        {
    #if defined(DLIB_HAVE_AVX2)
            if (!cpu_has_avx2_instructions())
                std::cerr << "Dlib was compiled to use AVX2 instructions, but these aren't available on your machine." << std::endl;
    #elif defined(DLIB_HAVE_AVX)
            if (!cpu_has_avx_instructions())
                std::cerr << "Dlib was compiled to use AVX instructions, but these aren't available on your machine." << std::endl;
    #elif defined(DLIB_HAVE_SSE41)
            if (!cpu_has_sse41_instructions())
                std::cerr << "Dlib was compiled to use SSE41 instructions, but these aren't available on your machine." << std::endl;
    #elif defined(DLIB_HAVE_SSE3)
            if (!cpu_has_sse3_instructions())
                std::cerr << "Dlib was compiled to use SSE3 instructions, but these aren't available on your machine." << std::endl;
    #elif defined(DLIB_HAVE_SSE2)
            if (!cpu_has_sse2_instructions())
                std::cerr << "Dlib was compiled to use SSE2 instructions, but these aren't available on your machine." << std::endl;
    #endif
        }

    // ----------------------------------------------------------------------------------------

    #endif // DLIB_SIMd_CHECK_Hh_