//------------------------------------------------------------------------------
// GB_compiler.h: handle compiler variations
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2023, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

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

#ifndef GB_COMPILER_H
#define GB_COMPILER_H

#define GB_COMPILER_NVCC    0
#define GB_COMPILER_ICX     0
#define GB_COMPILER_ICC     0
#define GB_COMPILER_CLANG   0
#define GB_COMPILER_GCC     0
#define GB_COMPILER_MSC     0
#define GB_COMPILER_XLC     0

//------------------------------------------------------------------------------
// determine which compiler is in use
//------------------------------------------------------------------------------

#if defined ( __NVCC__ )

    // NVIDIA nvcc compiler
    #undef  GB_COMPILER_NVCC
    #define GB_COMPILER_NVCC    1

    #define GB_COMPILER_MAJOR __CUDACC_VER_MAJOR__
    #define GB_COMPILER_MINOR __CUDACC_VER_MINOR__
    #define GB_COMPILER_SUB   __CUDACC_VER_BUILD__
    #define GB_COMPILER_NAME  "nvcc"

#elif defined ( __INTEL_CLANG_COMPILER )

    // Intel icx compiler
    #undef  GB_COMPILER_ICX
    #define GB_COMPILER_ICX     1

    #define GB_COMPILER_MAJOR __INTEL_CLANG_COMPILER
    #define GB_COMPILER_MINOR 0
    #define GB_COMPILER_SUB   0
    #define GB_COMPILER_NAME  __VERSION__

#elif defined ( __INTEL_COMPILER )

    // Intel icc compiler
    #undef  GB_COMPILER_ICC
    #define GB_COMPILER_ICC     1

    #define GB_COMPILER_MAJOR __INTEL_COMPILER
    #define GB_COMPILER_MINOR __INTEL_COMPILER_UPDATE
    #define GB_COMPILER_SUB   0
    #define GB_COMPILER_NAME  __VERSION__

#elif defined ( __clang__ )

    // clang
    #undef  GB_COMPILER_CLANG
    #define GB_COMPILER_CLANG   1

    #define GB_COMPILER_MAJOR __clang_major__
    #define GB_COMPILER_MINOR __clang_minor__
    #define GB_COMPILER_SUB   __clang_patchlevel__
    #define GB_COMPILER_NAME  "clang " __clang_version__

#elif defined ( __xlC__ )

    // xlc
    #undef  GB_COMPILER_XLC
    #define GB_COMPILER_XLC     1

    #define GB_COMPILER_MAJOR ( __xlC__ / 256 )
    #define GB_COMPILER_MINOR ( __xlC__ - 256 * GB_COMPILER_MAJOR)
    #define GB_COMPILER_SUB   0
    #define GB_COMPILER_NAME  "IBM xlc " GB_XSTR (__xlC__)

#elif defined ( __GNUC__ )

    // gcc
    #undef  GB_COMPILER_GCC
    #define GB_COMPILER_GCC     1

    #define GB_COMPILER_MAJOR __GNUC__
    #define GB_COMPILER_MINOR __GNUC_MINOR__
    #define GB_COMPILER_SUB   __GNUC_PATCHLEVEL__
    #define GB_COMPILER_NAME  "GNU gcc " GB_XSTR (__GNUC__) "." \
        GB_XSTR (__GNUC_MINOR__) "." GB_XSTR (__GNUC_PATCHLEVEL__)

#elif defined ( _MSC_VER )

    // Microsoft Visual Studio (cl compiler)
    #undef  GB_COMPILER_MSC
    #define GB_COMPILER_MSC     1

    #define GB_COMPILER_MAJOR ( _MSC_VER / 100 )
    #define GB_COMPILER_MINOR ( _MSC_VER - 100 * GB_COMPILER_MAJOR)
    #define GB_COMPILER_SUB   0
    #define GB_COMPILER_NAME  "Microsoft Visual Studio " GB_XSTR (_MSC_VER)

#else

    // other compiler
    #define GB_COMPILER_MAJOR 0
    #define GB_COMPILER_MINOR 0
    #define GB_COMPILER_SUB   0
    #define GB_COMPILER_NAME  "other C compiler"

#endif

//------------------------------------------------------------------------------
// Workaround for compiler bug in Microsoft Visual Studio 2019
//------------------------------------------------------------------------------

// The GB_COMPILER_MSC_2019_OR_NEWER flag disables the FIRST_FC32 and
// SECOND_FC32 binary operators for the MS Visual Studio 2019 or newer compilers
// (MSC versions 19.20 or newer).  It's possible that the compiler bug will be
// fixed in later versions of the MSC.  In that case, an upper version bound
// should be added to this macro.

#define GB_COMPILER_MSC_2019_OR_NEWER ( GB_COMPILER_MSC \
    && (GB_COMPILER_MAJOR == 19) && (GB_COMPILER_MINOR >= 20))

//------------------------------------------------------------------------------
// malloc.h: required include file for Microsoft Visual Studio
//------------------------------------------------------------------------------

#if GB_COMPILER_MSC
    #include <malloc.h>
#endif

//------------------------------------------------------------------------------
// OpenMP pragmas and tasks
//------------------------------------------------------------------------------

// GB_PRAGMA(x) becomes "#pragma x", but the way to do this depends on the
// compiler:

#if GB_COMPILER_MSC

    // MS Visual Studio is not ANSI C11 compliant, and uses __pragma:
    #define GB_PRAGMA(x) __pragma (x)
    // no #pragma omp simd is available in MS Visual Studio
    #define GB_PRAGMA_SIMD
    #define GB_PRAGMA_SIMD_REDUCTION(op,s)

#else

    // ANSI C11 compilers use _Pragma:
    #define GB_PRAGMA(x) _Pragma (#x)
    // create two kinds of SIMD pragmas:
    // GB_PRAGMA_SIMD becomes "#pragma omp simd"
    // GB_PRAGMA_SIMD_REDUCTION (+,cij) becomes
    // "#pragma omp simd reduction(+:cij)"
    #define GB_PRAGMA_SIMD GB_PRAGMA (omp simd)
    #define GB_PRAGMA_SIMD_REDUCTION(op,s) GB_PRAGMA (omp simd reduction(op:s))

#endif

// by default, enable the #pragma omp simd statements.  This can be #undef'd
// later, for methods that must disable it (see GB_generic.h).
#define GB_PRAGMA_SIMD_VECTORIZE GB_PRAGMA_SIMD

//------------------------------------------------------------------------------
// variable-length arrays
//------------------------------------------------------------------------------

// If variable-length arrays are not supported, user-defined types are limited
// in size to 128 bytes or less.  Many of the type-generic routines allocate
// workspace for a single scalar of variable size, using a statement:
//
//      GB_void aij [xsize] ;
//
// To support non-variable-length arrays in ANSI C95 or earlier, this is used:
//
//      GB_void aij [GB_VLA(xsize)] ;
//
// GB_VLA(xsize) is either defined as xsize (for ANSI C99 or later), or a fixed
// size of 128, in which case user-defined types
// are limited to a max of 128 bytes.

#if GB_COMPILER_NVCC

    // NVIDIA nvcc compiler for host or device code
    #define GB_HAS_VLA 1

#elif GB_COMPILER_MSC

    // Microsoft Visual Studio does not support variable-length arrays.
    #define GB_HAS_VLA  0

#elif defined ( __cplusplus )

    #define GB_HAS_VLA  1

#elif (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))

    // ANSI C99 and later
    #define GB_HAS_VLA  1

#else

    // ANSI C95 and earlier
    #define GB_HAS_VLA  0

#endif

#ifdef PGI_COMPILER_BUG
    // If GraphBLAS is compiled with -DPGI_COMPILER_BUG, then a workaround is
    // enabled for a bug in the PGI compiler.  The compiler does not correctly
    // handle automatic arrays of variable size.
    #undef  GB_HAS_VLA
    #define GB_HAS_VLA  0
#endif


#if ( GB_HAS_VLA )

    // variable-length arrays are allowed
    #define GB_VLA(s) s

#else

    // variable-length arrays are not allowed
    #define GB_VLA_MAXSIZE 128
    #define GB_VLA(s) GB_VLA_MAXSIZE

#endif

//------------------------------------------------------------------------------
// determine the target architecture
//------------------------------------------------------------------------------

#if !defined ( GBX86 )

    #if ( defined (_M_X64) || defined (__x86_64__)) && \
        ! ( defined (__CLR_VER) || defined (__pnacl__) )
    // the target architecture is x86_64, and not a virtual machine
    #define GBX86 1
    #else
    #define GBX86 0
    #endif

#endif

//------------------------------------------------------------------------------
// AVX2 and AVX512F support for the x86_64 architecture
//------------------------------------------------------------------------------

// gcc 7.5.0 cannot compile code with __attribute__ ((target ("avx512f"))), or
// avx2 (it triggers a bug in the compiler), but those targets are fine with
// gcc 9.3.0 or later.  It might be OK on gcc 8.x but I haven't tested this.

#if GBX86
    #if GB_COMPILER_GCC
        #if __GNUC__ >= 9
            // enable avx512f on gcc 9.x and later
            #define GB_COMPILER_SUPPORTS_AVX512F 1
            #define GB_COMPILER_SUPPORTS_AVX2 1
        #else
            // disable avx2 and avx512f on gcc 8.x and earlier
            #define GB_COMPILER_SUPPORTS_AVX512F 0
            #define GB_COMPILER_SUPPORTS_AVX2 0
        #endif
    #elif GB_COMPILER_ICX || GB_COMPILER_ICC || GB_COMPILER_CLANG
        // all these compilers can handle AVX512F and AVX2 on x86
        #define GB_COMPILER_SUPPORTS_AVX512F 1
        #define GB_COMPILER_SUPPORTS_AVX2 1
    #else
        // unsure if xlc can handle AVX, but it is not likely to be used on
        // the x86 anyway.  cpu_features is disabled for MS Visual Studio.
        #define GB_COMPILER_SUPPORTS_AVX512F 0
        #define GB_COMPILER_SUPPORTS_AVX2 0
    #endif
#else
    // non-X86_64 architecture
    #define GB_COMPILER_SUPPORTS_AVX512F 0
    #define GB_COMPILER_SUPPORTS_AVX2 0
#endif

// prefix for function with target avx512f
#if GB_COMPILER_SUPPORTS_AVX512F
    #if (defined (_WIN64) || defined (_WIN32)) && \
        (GB_COMPILER_ICC || GB_COMPILER_ICX)
        // the Intel compilers on Windows support this feature:
        #define GB_TARGET_AVX512F __declspec (target ("avx512f"))
    #else
        #define GB_TARGET_AVX512F __attribute__ ((target ("avx512f")))
    #endif
#else
    #define GB_TARGET_AVX512F
#endif

// prefix for function with target avx2
#if GB_COMPILER_SUPPORTS_AVX2
    #if (defined (_WIN64) || defined (_WIN32)) && \
        (GB_COMPILER_ICC || GB_COMPILER_ICX)
        // the Intel compilers on Windows support this feature:
        #define GB_TARGET_AVX2 __declspec (target ("avx2"))
    #else
        #define GB_TARGET_AVX2 __attribute__ ((target ("avx2")))
    #endif
#else
    #define GB_TARGET_AVX2
#endif

//------------------------------------------------------------------------------
// disable Google's cpu_featgures on some compilers
//------------------------------------------------------------------------------

#if defined (_WIN32) || GB_COMPILER_NVCC

    // entirely disable cpu_features for MS Visual Studio, nvcc, and MinGW
    #undef  GBNCPUFEAT
    #define GBNCPUFEAT 1

#endif

#endif

