//   Copyright Naoki Shibata and contributors 2010 - 2021.
// 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)

#if (defined (__GNUC__) || defined (__clang__) || defined(__INTEL_COMPILER)) && !defined(_MSC_VER)
#define CONST __attribute__((const))
#else
#define CONST
#endif

#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
static jmp_buf sigjmp;
#define SETJMP(x) setjmp(x)
#define LONGJMP longjmp
#else
static sigjmp_buf sigjmp;
#define SETJMP(x) sigsetjmp(x, 1)
#define LONGJMP siglongjmp
#endif

static void sighandler(int signum) {
  LONGJMP(sigjmp, 1);
}

static int cpuSupportsExt(void (*tryExt)()) {
  static int cache = -1;
  if (cache != -1) return cache;

  void (*org);
  org = signal(SIGILL, sighandler);

  if (SETJMP(sigjmp) == 0) {
    (*tryExt)();
    cache = 1;
  } else {
    cache = 0;
  }

  signal(SIGILL, org);
  return cache;
}

#ifndef VECALIAS_vf_vf
#define VECALIAS_vf_vf(fptype, funcNameS, funcName, veclen)
#define VECALIAS_vf_vf_vf(fptype, funcNameS, funcName, veclen)
#define VECALIAS_vf_vf_vf_vf(fptype, funcNameS, funcName, veclen)
#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
 *   veclen       : Vector length
 *   funcnameS    : First scalar function name
 *   funcnameS2   : Second scalar function name
 *   funcname     : Fundamental function name
 *   pfn          : Name of pointer of the function to the dispatcher
 *   dfn          : Name of the dispatcher function
 *   funcExt0     : Name of the function for vector extension 0
 *   funcExt1     : Name of the function for vector extension 1
 *   funcExt2     : Name of the function for vector extension 2
 */

#define DISPATCH_vf_vf(fptype, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0);			\
  static CONST VECTOR_CC fptype dfn(fptype arg0) {			\
    fptype CONST VECTOR_CC (*p)(fptype arg0) = funcExt0;		\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0) = dfn;		\
  EXPORT CONST VECTOR_CC fptype funcName(fptype arg0) { return (*pfn)(arg0); } \
  VECALIAS_vf_vf(fptype, funcNameS, funcName, veclen)			\
  VECALIAS_vf_vf(fptype, funcNameS2, funcName, veclen)

#define DISPATCH_vf_vf_vf(fptype, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, fptype arg1);	\
  static CONST VECTOR_CC fptype dfn(fptype arg0, fptype arg1) {		\
    fptype CONST VECTOR_CC (*p)(fptype arg0, fptype arg1) = funcExt0;	\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1);						\
  }									\
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, fptype arg1) = dfn; \
  EXPORT CONST VECTOR_CC fptype funcName(fptype arg0, fptype arg1) { return (*pfn)(arg0, arg1); } \
  VECALIAS_vf_vf_vf(fptype, funcNameS, funcName, veclen)		\
  VECALIAS_vf_vf_vf(fptype, funcNameS2, funcName, veclen)

#define DISPATCH_vf2_vf(fptype, fptype2, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC fptype2 (*pfn)(fptype arg0);			\
  static CONST VECTOR_CC fptype2 dfn(fptype arg0) {			\
    fptype2 CONST VECTOR_CC (*p)(fptype arg0) = funcExt0;		\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST VECTOR_CC fptype2 (*pfn)(fptype arg0) = dfn;		\
  EXPORT CONST VECTOR_CC fptype2 funcName(fptype arg0) { return (*pfn)(arg0); }

#define DISPATCH_vf_vf_vi(fptype, itype, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, itype arg1);	\
  static CONST VECTOR_CC fptype dfn(fptype arg0, itype arg1) {		\
    fptype CONST VECTOR_CC (*p)(fptype arg0, itype arg1) = funcExt0;	\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1);						\
  }									\
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, itype arg1) = dfn;	\
  EXPORT CONST VECTOR_CC fptype funcName(fptype arg0, itype arg1) { return (*pfn)(arg0, arg1); }

#define DISPATCH_vi_vf(fptype, itype, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC itype (*pfn)(fptype arg0);			\
  static CONST VECTOR_CC itype dfn(fptype arg0) {			\
    itype CONST VECTOR_CC (*p)(fptype arg0) = funcExt0;			\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST VECTOR_CC itype (*pfn)(fptype arg0) = dfn;		\
  EXPORT CONST VECTOR_CC itype funcName(fptype arg0) { return (*pfn)(arg0); }

#define DISPATCH_vf_vf_vf_vf(fptype, veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, fptype arg1, fptype arg2); \
  static CONST VECTOR_CC fptype dfn(fptype arg0, fptype arg1, fptype arg2) { \
    fptype CONST VECTOR_CC (*p)(fptype arg0, fptype arg1, fptype arg2) = funcExt0; \
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0, arg1, arg2);					\
  }									\
  static CONST VECTOR_CC fptype (*pfn)(fptype arg0, fptype arg1, fptype arg2) = dfn; \
  EXPORT CONST VECTOR_CC fptype funcName(fptype arg0, fptype arg1, fptype arg2) { return (*pfn)(arg0, arg1, arg2); } \
  VECALIAS_vf_vf_vf_vf(fptype, funcNameS, funcName, veclen)		\
  VECALIAS_vf_vf_vf_vf(fptype, funcNameS2, funcName, veclen)

#define DISPATCH_i_i(veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST int (*pfn)(int arg0);					\
  static CONST int dfn(int arg0) {					\
    int CONST (*p)(int) = funcExt0;					\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    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(veclen, funcNameS, funcNameS2, funcName, pfn, dfn, funcExt0, funcExt1, funcExt2) \
  static CONST void *(*pfn)(int arg0);					\
  static CONST void *dfn(int arg0) {					\
    CONST void *(*p)(int) = funcExt0;					\
    SUBST_IF_EXT1(funcExt1);						\
    SUBST_IF_EXT2(funcExt2);						\
    pfn = p;								\
    return (*pfn)(arg0);						\
  }									\
  static CONST void *(*pfn)(int arg0) = dfn;				\
  EXPORT CONST void *funcName(int arg0) { return (*pfn)(arg0); }

//

