//          Copyright Naoki Shibata 2010 - 2017.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <stdint.h>
#include <assert.h>

#include "misc.h"

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

#if (defined(__GNUC__) || defined(__CLANG__)) && !defined(__INTEL_COMPILER)
#define CONST const
#else
#define CONST
#endif

#define IMPORT_IS_EXPORT
#include "sleef.h"

static int cpuSupportsSSE4_1() {
  static int ret = -1;
  if (ret == -1) {
    int32_t reg[4];
    Sleef_x86CpuID(reg, 1, 0);
    ret = (reg[2] & (1 << 19)) != 0;
  }
  return ret;
}

static int cpuSupportsAVX2() {
  static int ret = -1;
  if (ret == -1) {
    int32_t reg[4];
    Sleef_x86CpuID(reg, 7, 0);
    ret = (reg[1] & (1 << 5)) != 0;
  }
  return ret;
}

static int cpuSupportsFMA() {
  static int ret = -1;
  if (ret == -1) {
    int32_t reg[4];
    Sleef_x86CpuID(reg, 1, 0);
    ret = (reg[2] & (1 << 12)) != 0;
  }
  return ret;
}

#define SUBST_IF_SSE4(funcsse4) if (cpuSupportsSSE4_1()) p = funcsse4;

#ifdef ENABLE_AVX2
#define SUBST_IF_AVX2(funcavx2) if (cpuSupportsAVX2() && cpuSupportsFMA()) p = funcavx2;
#else
#define SUBST_IF_AVX2(funcavx2)
#endif

/*
 * DISPATCH_R_X, DISPATCH_R_X_Y and DISPATCH_R_X_Y_Z are the macro for
 * defining dispatchers. R, X, Y and Z represent the data types of
 * return value, first argument, second argument and third argument,
 * respectively. vf, vi, i and p correspond to vector FP, vector
 * integer, scalar integer and scalar pointer types, respectively.
 *
 * The arguments for the macros are as follows:
 *   fptype   : FP type name
 *   funcname : Fundamental function name
 *   pfn      : Name of pointer of the function to the dispatcher
 *   dfn      : Name of the dispatcher function
 *   funcsse2 : Name of the SSE2 function
 *   funcsse4 : Name of the SSE4 function
 *   funcavx2 : Name of the AVX2 function
 */

#define DISPATCH_vf_vf(fptype, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST fptype (*pfn)(fptype arg0);				\
  static CONST fptype dfn(fptype arg0) {				\
    fptype CONST (*p)(fptype arg0) = funcsse2;				\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST fptype (*pfn)(fptype arg0) = dfn;			\
  EXPORT CONST fptype funcName(fptype arg0) { return (*pfn)(arg0); }

#define DISPATCH_vf_vf_vf(fptype, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST fptype (*pfn)(fptype arg0, fptype arg1);			\
  static CONST fptype dfn(fptype arg0, fptype arg1) {			\
    fptype CONST (*p)(fptype arg0, fptype arg1) = funcsse2;		\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1);						\
  }									\
  static CONST fptype (*pfn)(fptype arg0, fptype arg1) = dfn;		\
  EXPORT CONST fptype funcName(fptype arg0, fptype arg1) { return (*pfn)(arg0, arg1); }

#define DISPATCH_vf2_vf(fptype, fptype2, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST fptype2 (*pfn)(fptype arg0);				\
  static CONST fptype2 dfn(fptype arg0) {				\
    fptype2 CONST (*p)(fptype arg0) = funcsse2;				\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST fptype2 (*pfn)(fptype arg0) = dfn;			\
  EXPORT CONST fptype2 funcName(fptype arg0) { return (*pfn)(arg0); }

#define DISPATCH_vf_vf_vi(fptype, itype, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST fptype (*pfn)(fptype arg0, itype arg1);			\
  static CONST fptype dfn(fptype arg0, itype arg1) {			\
    fptype CONST (*p)(fptype arg0, itype arg1) = funcsse2;		\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1);						\
  }									\
  static CONST fptype (*pfn)(fptype arg0, itype arg1) = dfn;		\
  EXPORT CONST fptype funcName(fptype arg0, itype arg1) { return (*pfn)(arg0, arg1); }

#define DISPATCH_vi_vf(fptype, itype, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST itype (*pfn)(fptype arg0);				\
  static CONST itype dfn(fptype arg0) {					\
    itype CONST (*p)(fptype arg0) = funcsse2;				\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST itype (*pfn)(fptype arg0) = dfn;				\
  EXPORT CONST itype funcName(fptype arg0) { return (*pfn)(arg0); }

#define DISPATCH_vf_vf_vf_vf(fptype, funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST fptype (*pfn)(fptype arg0, fptype arg1, fptype arg2);	\
  static CONST fptype dfn(fptype arg0, fptype arg1, fptype arg2) {	\
    fptype CONST (*p)(fptype arg0, fptype arg1, fptype arg2) = funcsse2; \
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1, arg2);					\
  }									\
  static CONST fptype (*pfn)(fptype arg0, fptype arg1, fptype arg2) = dfn; \
  EXPORT CONST fptype funcName(fptype arg0, fptype arg1, fptype arg2) { return (*pfn)(arg0, arg1, arg2); }

#define DISPATCH_i_i(funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST int (*pfn)(int arg0);					\
  static CONST int dfn(int arg0) {					\
    int CONST (*p)(int) = funcsse2;					\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST int (*pfn)(int arg0) = dfn;				\
  EXPORT CONST int funcName(int arg0) { return (*pfn)(arg0); }

#define DISPATCH_p_i(funcName, pfn, dfn, funcsse2, funcsse4, funcavx2) \
  static CONST void *(*pfn)(int arg0);					\
  static CONST void *dfn(int arg0) {					\
    CONST void *(*p)(int) = funcsse2;					\
    SUBST_IF_SSE4(funcsse4);						\
    SUBST_IF_AVX2(funcavx2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST void *(*pfn)(int arg0) = dfn;				\
  EXPORT CONST void *funcName(int arg0) { return (*pfn)(arg0); }

//
