/* Copyright (C) 2013 Free Software Foundation, Inc.

   This file is part of GCC.

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

   GCC 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 General Public License
   for more details.

   Under Section 7 of GPL version 3, you are granted additional
   permissions described in the GCC Runtime Library Exception, version
   3.1, as published by the Free Software Foundation.

   You should have received a copy of the GNU General Public License and
   a copy of the GCC Runtime Library Exception along with this program;
   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   <http://www.gnu.org/licenses/>.  */


/* This file supplies implementations for some AVR-specific builtin
   functions so that code like the following works as expected:

   int (*f (void))(_Fract)
   {
       return __builtin_avr_countlsr;
   }

   In this specific case, the generated code is:

   f:
       ldi r24,lo8(gs(__countlsHI))
       ldi r25,hi8(gs(__countlsHI))
       ret
*/

/* Map fixed-point suffix to the corresponding fixed-point type.  */

typedef short _Fract fx_hr_t;
typedef _Fract fx_r_t;
typedef long _Fract fx_lr_t;
typedef long long _Fract fx_llr_t;

typedef unsigned short _Fract fx_uhr_t;
typedef unsigned _Fract fx_ur_t;
typedef unsigned long _Fract fx_ulr_t;
typedef unsigned long long _Fract fx_ullr_t;

typedef short _Accum fx_hk_t;
typedef _Accum fx_k_t;
typedef long _Accum fx_lk_t;
typedef long long _Accum fx_llk_t;

typedef unsigned short _Accum fx_uhk_t;
typedef unsigned _Accum fx_uk_t;
typedef unsigned long _Accum fx_ulk_t;
typedef unsigned long long _Accum fx_ullk_t;

/* Map fixed-point suffix to the corresponding natural integer type.  */

typedef char int_hr_t;
typedef int int_r_t;
typedef long int_lr_t;
typedef long long int_llr_t;

typedef unsigned char int_uhr_t;
typedef unsigned int int_ur_t;
typedef unsigned long int_ulr_t;
typedef unsigned long long int_ullr_t;

typedef int int_hk_t;
typedef long int_k_t;
typedef long long int_lk_t;
typedef long long int_llk_t;

typedef unsigned int int_uhk_t;
typedef unsigned long int_uk_t;
typedef unsigned long long int_ulk_t;
typedef unsigned long long int_ullk_t;

/* Map mode to the corresponding integer type.  */

typedef char int_qi_t;
typedef int int_hi_t;
typedef long int_si_t;
typedef long long int_di_t;

typedef unsigned char uint_qi_t;
typedef unsigned int uint_hi_t;
typedef unsigned long uint_si_t;
typedef unsigned long long uint_di_t;



/************************************************************************/

/* Supply implementations / symbols for __builtin_roundFX ASM_NAME.  */

#ifdef L_round

#define ROUND1(FX)                              \
  ROUND2 (FX)

#define ROUND2(FX)                                                      \
  extern fx_## FX ##_t __round## FX (fx_## FX ##_t x, int rpoint);      \
                                                                        \
  fx_## FX ##_t                                                         \
  __round## FX (fx_## FX ##_t x, int rpoint)                            \
  {                                                                     \
    return __builtin_avr_round ##FX (x, rpoint);                        \
  }

ROUND1(L_LABEL)

#endif /* L_round */



/*********************************************************************/

/* Implement some count-leading-redundant-sign-bits to be used with
   coundlsFX implementation.  */

#ifdef L__clrsbqi
extern int __clrsbqi2 (char x);

int
__clrsbqi2 (char x)
{
  int ret;

  if (x < 0)
    x = ~x;

  if (x == 0)
    return 8 * sizeof (x) -1;

  ret = __builtin_clz (x << 8);
  return ret - 1;
}
#endif /* L__clrsbqi */


#ifdef L__clrsbdi
extern int __clrsbdi2 (long long x);

int
__clrsbdi2 (long long x)
{
  int ret;

  if (x < 0LL)
    x = ~x;

  if (x == 0LL)
    return 8 * sizeof (x) -1;

  ret = __builtin_clzll ((unsigned long long) x);
  return ret - 1;
}
#endif /* L__clrsbdi */



/*********************************************************************/

/* Supply implementations / symbols for __builtin_avr_countlsFX.  */

/* Signed */

#ifdef L_countls

#define COUNTLS1(MM)                            \
  COUNTLS2 (MM)

#define COUNTLS2(MM)                                                    \
  extern int __countls## MM ##2 (int_## MM ##_t);                       \
  extern int __clrsb## MM ##2 (int_## MM ##_t);                         \
                                                                        \
  int                                                                   \
  __countls## MM ##2 (int_## MM ##_t x)                                 \
  {                                                                     \
    if (x == 0)                                                         \
      return __INT8_MAX__;                                              \
                                                                        \
    return __clrsb## MM ##2 (x);                                        \
  }

COUNTLS1(L_LABEL)

#endif /* L_countls */

/* Unsigned */

#ifdef L_countlsu

#define clz_qi2 __builtin_clz /* unused, avoid warning */
#define clz_hi2 __builtin_clz
#define clz_si2 __builtin_clzl
#define clz_di2 __builtin_clzll

#define COUNTLS1(MM)                            \
  COUNTLS2 (MM)

#define COUNTLS2(MM)                                                    \
  extern int __countlsu## MM ##2 (uint_## MM ##_t);                     \
                                                                        \
  int                                                                   \
  __countlsu## MM ##2 (uint_## MM ##_t x)                               \
  {                                                                     \
    if (x == 0)                                                         \
      return __INT8_MAX__;                                              \
                                                                        \
    if (sizeof (x) == 1)                                                \
      return clz_hi2 (x << 8);                                          \
    else                                                                \
      return clz_## MM ##2 (x);                                         \
  }

COUNTLS1(L_LABEL)

#endif /* L_countlsu */
