/* Software floating-point emulation.
   Definitions for IEEE Quad Precision on the PowerPC.
   Copyright (C) 2016-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Michael Meissner (meissner@linux.vnet.ibm.com).

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   In addition to the permissions in the GNU Lesser General Public
   License, the Free Software Foundation gives you unlimited
   permission to link the compiled version of this file into
   combinations with other programs, and to distribute those
   combinations without any restriction coming from the use of this
   file.  (The Lesser General Public License restrictions do apply in
   other respects; for example, they cover modification of the file,
   and distribution when not linked into a combine executable.)

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

/* quad.h defines the TFtype type by:
   typedef float TFtype __attribute__ ((mode (TF)));

   This define forces it to use KFmode (aka, ieee 128-bit floating point).
   However, when the compiler's default is changed so that long double is IEEE
   128-bit floating point, we need to go back to using TFmode and TCmode.  */
#ifndef __LONG_DOUBLE_IEEE128__
#define TF KF

/* We also need TCtype to represent complex ieee 128-bit float for
   __mulkc3 and __divkc3.  */
typedef __complex float TCtype __attribute__ ((mode (KC)));

#else
typedef __complex float TCtype __attribute__ ((mode (TC)));
#endif

/* Force the use of the VSX instruction set.  */
#if defined(_ARCH_PPC) && (!defined(__VSX__) || !defined(__FLOAT128__))
#pragma GCC target ("vsx,float128")
#endif

#include <stddef.h>
#include <quad.h>

#define IBM128_TYPE	__ibm128

/* Add prototypes of the library functions created.  In case the appropriate
   int/long types are not declared in scope by the time quad.h is included,
   provide our own version.  */
typedef int	 SItype_ppc  __attribute__ ((__mode__ (__SI__)));
typedef int	 DItype_ppc  __attribute__ ((__mode__ (__DI__)));
typedef unsigned USItype_ppc __attribute__ ((__mode__ (__SI__)));
typedef unsigned UDItype_ppc __attribute__ ((__mode__ (__DI__)));

#ifdef _ARCH_PPC64
typedef int	 TItype_ppc  __attribute__ ((__mode__ (__TI__)));
typedef unsigned UTItype_ppc __attribute__ ((__mode__ (__TI__)));
#endif

/* Software emulation functions.  */
extern TFtype __addkf3_sw (TFtype, TFtype);
extern TFtype __subkf3_sw (TFtype, TFtype);
extern TFtype __mulkf3_sw (TFtype, TFtype);
extern TFtype __divkf3_sw (TFtype, TFtype);
extern TFtype __negkf2_sw (TFtype);
extern TFtype __powikf2_sw (TFtype, SItype_ppc);
extern CMPtype __eqkf2_sw (TFtype, TFtype);
extern CMPtype __gekf2_sw (TFtype, TFtype);
extern CMPtype __lekf2_sw (TFtype, TFtype);
extern CMPtype __unordkf2_sw (TFtype, TFtype);
extern TFtype __extendsfkf2_sw (float);
extern TFtype __extenddfkf2_sw (double);
extern float __trunckfsf2_sw (TFtype);
extern double __trunckfdf2_sw (TFtype);
extern SItype_ppc __fixkfsi_sw (TFtype);
extern DItype_ppc __fixkfdi_sw (TFtype);
extern USItype_ppc __fixunskfsi_sw (TFtype);
extern UDItype_ppc __fixunskfdi_sw (TFtype);
extern TFtype __floatsikf_sw (SItype_ppc);
extern TFtype __floatdikf_sw (DItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floattikf_sw (TItype_ppc);
#endif
extern TFtype __floatunsikf_sw (USItype_ppc);
extern TFtype __floatundikf_sw (UDItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floatuntikf_sw (UTItype_ppc);
extern TItype_ppc __fixkfti_sw (TFtype);
extern UTItype_ppc __fixunskfti_sw (TFtype);
#endif
extern IBM128_TYPE __extendkftf2_sw (TFtype);
extern TFtype __trunctfkf2_sw (IBM128_TYPE);
extern TCtype __mulkc3_sw (TFtype, TFtype, TFtype, TFtype);
extern TCtype __divkc3_sw (TFtype, TFtype, TFtype, TFtype);

#ifdef _ARCH_PPC64
extern TItype_ppc __fixkfti (TFtype);
extern UTItype_ppc __fixunskfti (TFtype);
extern TFtype __floattikf (TItype_ppc);
extern TFtype __floatuntikf (UTItype_ppc);
#endif

/* Functions using the ISA 3.0 hardware support.  If the code is compiled with
   -mcpu=power9, it will not use these functions, but if it was compiled with
   -mcpu=power7 or -mcpu=power8 and run on a ISA 3.0 system, it will use the
   hardware instruction.  */
extern TFtype __addkf3_hw (TFtype, TFtype);
extern TFtype __subkf3_hw (TFtype, TFtype);
extern TFtype __mulkf3_hw (TFtype, TFtype);
extern TFtype __divkf3_hw (TFtype, TFtype);
extern TFtype __negkf2_hw (TFtype);
extern TFtype __powikf2_hw (TFtype, SItype_ppc);
extern CMPtype __eqkf2_hw (TFtype, TFtype);
extern CMPtype __gekf2_hw (TFtype, TFtype);
extern CMPtype __lekf2_hw (TFtype, TFtype);
extern CMPtype __unordkf2_hw (TFtype, TFtype);
extern TFtype __extendsfkf2_hw (float);
extern TFtype __extenddfkf2_hw (double);
extern float __trunckfsf2_hw (TFtype);
extern double __trunckfdf2_hw (TFtype);
extern SItype_ppc __fixkfsi_hw (TFtype);
extern DItype_ppc __fixkfdi_hw (TFtype);
extern USItype_ppc __fixunskfsi_hw (TFtype);
extern UDItype_ppc __fixunskfdi_hw (TFtype);
extern TFtype __floatsikf_hw (SItype_ppc);
extern TFtype __floatdikf_hw (DItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floattikf_hw (TItype_ppc);
#endif
extern TFtype __floatunsikf_hw (USItype_ppc);
extern TFtype __floatundikf_hw (UDItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floatuntikf_hw (UTItype_ppc);
extern TItype_ppc __fixkfti_hw (TFtype);
extern UTItype_ppc __fixunskfti_hw (TFtype);
#endif
extern IBM128_TYPE __extendkftf2_hw (TFtype);
extern TFtype __trunctfkf2_hw (IBM128_TYPE);
extern TCtype __mulkc3_hw (TFtype, TFtype, TFtype, TFtype);
extern TCtype __divkc3_hw (TFtype, TFtype, TFtype, TFtype);

/* Ifunc function declarations, to automatically switch between software
   emulation and hardware support.  */
extern TFtype __addkf3 (TFtype, TFtype);
extern TFtype __subkf3 (TFtype, TFtype);
extern TFtype __mulkf3 (TFtype, TFtype);
extern TFtype __divkf3 (TFtype, TFtype);
extern TFtype __negkf2 (TFtype);
extern TFtype __powikf2 (TFtype, SItype_ppc);
extern CMPtype __eqkf2 (TFtype, TFtype);
extern CMPtype __nekf2 (TFtype, TFtype);
extern CMPtype __gekf2 (TFtype, TFtype);
extern CMPtype __gtkf2 (TFtype, TFtype);
extern CMPtype __lekf2 (TFtype, TFtype);
extern CMPtype __ltkf2 (TFtype, TFtype);
extern CMPtype __unordkf2 (TFtype, TFtype);
extern TFtype __extendsfkf2 (float);
extern TFtype __extenddfkf2 (double);
extern float __trunckfsf2 (TFtype);
extern double __trunckfdf2 (TFtype);
extern SItype_ppc __fixkfsi (TFtype);
extern DItype_ppc __fixkfdi (TFtype);
extern USItype_ppc __fixunskfsi (TFtype);
extern UDItype_ppc __fixunskfdi (TFtype);
extern TFtype __floatsikf (SItype_ppc);
extern TFtype __floatdikf (DItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floattikf (TItype_ppc);
#endif
extern TFtype __floatunsikf (USItype_ppc);
extern TFtype __floatundikf (UDItype_ppc);
#ifdef _ARCH_PPC64
extern TFtype __floatuntikf (UTItype_ppc);
extern TItype_ppc __fixkfti (TFtype);
extern UTItype_ppc __fixunskfti (TFtype);
#endif
extern IBM128_TYPE __extendkftf2 (TFtype);
extern TFtype __trunctfkf2 (IBM128_TYPE);

/* Complex __float128 built on __float128 interfaces.  */
extern TCtype __mulkc3 (TFtype, TFtype, TFtype, TFtype);
extern TCtype __divkc3 (TFtype, TFtype, TFtype, TFtype);

/* Convert IEEE 128-bit floating point to/from string.  We explicitly use
   _Float128 instead of TFmode because _strtokf and _strfromkf must be compiled
   with long double being IBM 128.  */
extern _Float128 __strtokf (const char *, char **);
extern int __strfromkf (char *restrict, size_t, const char *restrict,
			_Float128);

/* Implementation of conversions between __ibm128 and __float128, to allow the
   same code to be used on systems with IEEE 128-bit emulation and with IEEE
   128-bit hardware support.  */

union ibm128_union {
  IBM128_TYPE ibm128;
  double dbl[2];
};

#define CVT_FLOAT128_TO_IBM128(RESULT, VALUE)				\
{									\
  double __high, __low;							\
  TFtype __value = (VALUE);						\
  union ibm128_union u;							\
									\
  __high = (double) __value;						\
  if (__builtin_isnan (__high) || __builtin_isinf (__high))		\
    __low = 0.0;							\
									\
  else									\
    {									\
      double __high_temp;						\
									\
      __low = (double) (__value - (TFtype) __high);			\
      /* Renormalize low/high and move them into canonical IBM long	\
	 double form.  */						\
      __high_temp = __high + __low;					\
      __low = (__high - __high_temp) + __low;				\
      __high = __high_temp;						\
    }									\
									\
  u.dbl[0] = __high;							\
  u.dbl[1] = __low;							\
  RESULT = u.ibm128;							\
}

#define CVT_IBM128_TO_FLOAT128(RESULT, VALUE)				\
{									\
  union ibm128_union u;							\
  double __high, __low;							\
									\
  u.ibm128 = (VALUE);							\
  __high = u.dbl[0];							\
  __low = u.dbl[1];							\
									\
  /* Handle the special cases of NAN and infinity.  */			\
  if (__builtin_isnan (__high) || __builtin_isinf (__high))		\
    RESULT = (TFtype) __high;						\
									\
  /* If low is 0.0, there no need to do the add.  In addition,		\
     avoiding the add produces the correct sign if high is -0.0.  */	\
  else if (__low == 0.0)						\
    RESULT = (TFtype) __high;						\
									\
  else									\
    RESULT = ((TFtype) __high) + ((TFtype) __low);			\
}
