/* TCC ARM runtime EABI
   Copyright (C) 2013 Thomas Preud'homme

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/

#ifdef __TINYC__
#define INT_MIN (-2147483647 - 1)
#define INT_MAX 2147483647
#define UINT_MAX 0xffffffff
#define LONG_MIN (-2147483647L - 1)
#define LONG_MAX 2147483647L
#define ULONG_MAX 0xffffffffUL
#define LLONG_MAX 9223372036854775807LL
#define LLONG_MIN (-9223372036854775807LL - 1)
#define ULLONG_MAX 0xffffffffffffffffULL
#else
#include <limits.h>
#endif

/* We rely on the little endianness and EABI calling convention for this to
   work */

typedef struct double_unsigned_struct {
    unsigned low;
    unsigned high;
} double_unsigned_struct;

typedef struct unsigned_int_struct {
    unsigned low;
    int high;
} unsigned_int_struct;

#define REGS_RETURN(name, type) \
    static void name ## _return(type ret) {}


/* Float helper functions */

#define FLOAT_EXP_BITS 8
#define FLOAT_FRAC_BITS 23

#define DOUBLE_EXP_BITS 11
#define DOUBLE_FRAC_BITS 52

#define ONE_EXP(type) ((1 << (type ## _EXP_BITS - 1)) - 1)

REGS_RETURN(unsigned_int_struct, unsigned_int_struct)
REGS_RETURN(double_unsigned_struct, double_unsigned_struct)

/* float -> integer: (sign) 1.fraction x 2^(exponent - exp_for_one) */


/* float to [unsigned] long long conversion */
#define DEFINE__AEABI_F2XLZ(name, with_sign)                                 \
void __aeabi_ ## name(unsigned val)                                          \
{                                                                            \
    int exp, high_shift, sign;                                               \
    double_unsigned_struct ret;                                              \
                                                                             \
    /* compute sign */                                                       \
    sign = val >> 31;                                                        \
                                                                             \
    /* compute real exponent */                                              \
    exp = val >> FLOAT_FRAC_BITS;                                            \
    exp &= (1 << FLOAT_EXP_BITS) - 1;                                        \
    exp -= ONE_EXP(FLOAT);                                                   \
                                                                             \
    /* undefined behavior if truncated value cannot be represented */        \
    if (with_sign) {                                                         \
        if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \
            return;                                                          \
    } else {                                                                 \
        if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */  \
            return;                                                          \
    }                                                                        \
                                                                             \
    val &= (1 << FLOAT_FRAC_BITS) - 1;                                       \
    if (exp >= 32) {                                                         \
        ret.high = 1 << (exp - 32);                                          \
        if (exp - 32 >= FLOAT_FRAC_BITS) {                                   \
            ret.high |= val << (exp - 32 - FLOAT_FRAC_BITS);                 \
            ret.low = 0;                                                     \
        } else {                                                             \
            high_shift = FLOAT_FRAC_BITS - (exp - 32);                       \
            ret.high |= val >> high_shift;                                   \
            ret.low = val << (32 - high_shift);                              \
        }                                                                    \
    } else {                                                                 \
        ret.high = 0;                                                        \
        ret.low = 1 << exp;                                                  \
        if (exp > FLOAT_FRAC_BITS)                                           \
            ret.low |= val << (exp - FLOAT_FRAC_BITS);                       \
        else                                                                 \
            ret.low |= val >> (FLOAT_FRAC_BITS - exp);                       \
    }                                                                        \
                                                                             \
    /* encode negative integer using 2's complement */                       \
    if (with_sign && sign) {                                                 \
        ret.low = ~ret.low;                                                  \
        ret.high = ~ret.high;                                                \
        if (ret.low == UINT_MAX) {                                           \
            ret.low = 0;                                                     \
            ret.high++;                                                      \
        } else                                                               \
            ret.low++;                                                       \
    }                                                                        \
                                                                             \
    double_unsigned_struct_return(ret);                                      \
}

/* float to unsigned long long conversion */
DEFINE__AEABI_F2XLZ(f2ulz, 0)

/* float to long long conversion */
DEFINE__AEABI_F2XLZ(f2lz, 1)

/* double to [unsigned] long long conversion */
#define DEFINE__AEABI_D2XLZ(name, with_sign)                                 \
void __aeabi_ ## name(double_unsigned_struct val)                            \
{                                                                            \
    int exp, high_shift, sign;                                               \
    double_unsigned_struct ret;                                              \
                                                                             \
    if ((val.high & ~0x80000000) == 0 && val.low == 0) {                     \
        ret.low = ret.high = 0;                                              \
        goto _ret_;                                                          \
    }                                                                        \
                                                                             \
    /* compute sign */                                                       \
    sign = val.high >> 31;                                                   \
                                                                             \
    /* compute real exponent */                                              \
    exp = (val.high >> (DOUBLE_FRAC_BITS - 32));                             \
    exp &= (1 << DOUBLE_EXP_BITS) - 1;                                       \
    exp -= ONE_EXP(DOUBLE);                                                  \
                                                                             \
    /* undefined behavior if truncated value cannot be represented */        \
    if (with_sign) {                                                         \
        if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \
            return;                                                          \
    } else {                                                                 \
        if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */  \
            return;                                                          \
    }                                                                        \
                                                                             \
    val.high &= (1 << (DOUBLE_FRAC_BITS - 32)) - 1;                          \
    if (exp >= 32) {                                                         \
        ret.high = 1 << (exp - 32);                                          \
        if (exp >= DOUBLE_FRAC_BITS) {                                       \
            high_shift = exp - DOUBLE_FRAC_BITS;                             \
            ret.high |= val.high << high_shift;                              \
            ret.high |= val.low >> (32 - high_shift);                        \
            ret.low = val.low << high_shift;                                 \
        } else {                                                             \
            high_shift = DOUBLE_FRAC_BITS - exp;                             \
            ret.high |= val.high >> high_shift;                              \
            ret.low = val.high << (32 - high_shift);                         \
            ret.low |= val.low >> high_shift;                                \
        }                                                                    \
    } else {                                                                 \
        ret.high = 0;                                                        \
        ret.low = 1 << exp;                                                  \
        if (exp > DOUBLE_FRAC_BITS - 32) {                                   \
            high_shift = exp - (DOUBLE_FRAC_BITS - 32);                      \
            ret.low |= val.high << high_shift;                               \
            ret.low |= val.low >> (32 - high_shift);                         \
        } else                                                               \
            ret.low |= val.high >> (DOUBLE_FRAC_BITS - 32 - exp);            \
    }                                                                        \
                                                                             \
    /* encode negative integer using 2's complement */                       \
    if (with_sign && sign) {                                                 \
        ret.low = ~ret.low;                                                  \
        ret.high = ~ret.high;                                                \
        if (ret.low == UINT_MAX) {                                           \
            ret.low = 0;                                                     \
            ret.high++;                                                      \
        } else                                                               \
            ret.low++;                                                       \
    }                                                                        \
                                                                             \
_ret_:                                                                       \
    double_unsigned_struct_return(ret);                                      \
}

/* double to unsigned long long conversion */
DEFINE__AEABI_D2XLZ(d2ulz, 0)

/* double to long long conversion */
DEFINE__AEABI_D2XLZ(d2lz, 1)

/* long long to float conversion */
#define DEFINE__AEABI_XL2F(name, with_sign)                             \
unsigned __aeabi_ ## name(unsigned long long v)                         \
{                                                                       \
    int s /* shift */, flb /* first lost bit */, sign = 0;              \
    unsigned p = 0 /* power */, ret;                                    \
    double_unsigned_struct val;                                         \
                                                                        \
    /* fraction in negative float is encoded in 1's complement */       \
    if (with_sign && (v & (1ULL << 63))) {                              \
        sign = 1;                                                       \
        v = ~v + 1;                                                     \
    }                                                                   \
    val.low = v;                                                        \
    val.high = v >> 32;                                                 \
    /* fill fraction bits */                                            \
    for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1);      \
    if (p) {                                                            \
        ret = val.high & (p - 1);                                       \
        if (s < FLOAT_FRAC_BITS) {                                      \
            ret <<= FLOAT_FRAC_BITS - s;                                \
            ret |= val.low >> (32 - (FLOAT_FRAC_BITS - s));             \
            flb = (val.low >> (32 - (FLOAT_FRAC_BITS - s - 1))) & 1;    \
        } else {                                                        \
            flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1;               \
            ret >>= s - FLOAT_FRAC_BITS;                                \
        }                                                               \
        s += 32;                                                        \
    } else {                                                            \
        for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1);   \
        if (p) {                                                        \
            ret = val.low & (p - 1);                                    \
            if (s <= FLOAT_FRAC_BITS) {                                 \
                ret <<= FLOAT_FRAC_BITS - s;                            \
                flb = 0;                                                \
	    } else {                                                    \
                flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1;           \
                ret >>= s - FLOAT_FRAC_BITS;                            \
	    }                                                           \
        } else                                                          \
            return 0;                                                   \
    }                                                                   \
    if (flb)                                                            \
        ret++;                                                          \
                                                                        \
    /* fill exponent bits */                                            \
    ret |= (s + ONE_EXP(FLOAT)) << FLOAT_FRAC_BITS;                     \
                                                                        \
    /* fill sign bit */                                                 \
    ret |= sign << 31;                                                  \
                                                                        \
    return ret;                                                         \
}

/* unsigned long long to float conversion */
DEFINE__AEABI_XL2F(ul2f, 0)

/* long long to float conversion */
DEFINE__AEABI_XL2F(l2f, 1)

/* long long to double conversion */
#define __AEABI_XL2D(name, with_sign)                                   \
void __aeabi_ ## name(unsigned long long v)                             \
{                                                                       \
    int s /* shift */, high_shift, sign = 0;                            \
    unsigned tmp, p = 0;                                                \
    double_unsigned_struct val, ret;                                    \
                                                                        \
    /* fraction in negative float is encoded in 1's complement */       \
    if (with_sign && (v & (1ULL << 63))) {                              \
        sign = 1;                                                       \
        v = ~v + 1;                                                     \
    }                                                                   \
    val.low = v;                                                        \
    val.high = v >> 32;                                                 \
                                                                        \
    /* fill fraction bits */                                            \
    for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1);      \
    if (p) {                                                            \
        tmp = val.high & (p - 1);                                       \
        if (s < DOUBLE_FRAC_BITS - 32) {                                \
            high_shift = DOUBLE_FRAC_BITS - 32 - s;                     \
            ret.high = tmp << high_shift;                               \
            ret.high |= val.low >> (32 - high_shift);                   \
            ret.low = val.low << high_shift;                            \
        } else {                                                        \
            high_shift = s - (DOUBLE_FRAC_BITS - 32);                   \
            ret.high = tmp >> high_shift;                               \
            ret.low = tmp << (32 - high_shift);                         \
            ret.low |= val.low >> high_shift;                           \
            if ((val.low >> (high_shift - 1)) & 1) {                    \
                if (ret.low == UINT_MAX) {                              \
                    ret.high++;                                         \
                    ret.low = 0;                                        \
		} else                                                  \
                    ret.low++;                                          \
            }                                                           \
        }                                                               \
        s += 32;                                                        \
    } else {                                                            \
        for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1);   \
        if (p) {                                                        \
            tmp = val.low & (p - 1);                                    \
            if (s <= DOUBLE_FRAC_BITS - 32) {                           \
                high_shift = DOUBLE_FRAC_BITS - 32 - s;                 \
                ret.high = tmp << high_shift;                           \
                ret.low = 0;                                            \
	    } else {                                                    \
                high_shift = s - (DOUBLE_FRAC_BITS - 32);               \
                ret.high = tmp >> high_shift;                           \
                ret.low = tmp << (32 - high_shift);                     \
            }                                                           \
        } else {                                                        \
            ret.high = ret.low = 0;                                     \
            goto _ret_;                                                 \
        }                                                               \
    }                                                                   \
                                                                        \
    /* fill exponent bits */                                            \
    ret.high |= (s + ONE_EXP(DOUBLE)) << (DOUBLE_FRAC_BITS - 32);       \
                                                                        \
    /* fill sign bit */                                                 \
    ret.high |= sign << 31;                                             \
                                                                        \
_ret_:                                                                  \
    double_unsigned_struct_return(ret);                                 \
}

/* unsigned long long to double conversion */
__AEABI_XL2D(ul2d, 0)

/* long long to double conversion */
__AEABI_XL2D(l2d, 1)

#if 1
/* Long long helper functions */

/* TODO: add error in case of den == 0 (see §4.3.1 and §4.3.2) */

#define define_aeabi_xdivmod_signed_type(basetype, type) \
typedef struct type {                                    \
    basetype quot;                                       \
    unsigned basetype rem;                               \
} type

#define define_aeabi_xdivmod_unsigned_type(basetype, type) \
typedef struct type {                                      \
    basetype quot;                                         \
    basetype rem;                                          \
} type

#define AEABI_UXDIVMOD(name,type, rettype, typemacro)                     \
static inline rettype aeabi_ ## name (type num, type den)                 \
{                                                                         \
    rettype ret;                                                          \
    type quot = 0;                                                        \
                                                                          \
    /* Increase quotient while it is less than numerator */               \
    while (num >= den) {                                                  \
        type q = 1;                                                       \
                                                                          \
        /* Find closest power of two */                                   \
        while ((q << 1) * den <= num && q * den <= typemacro ## _MAX / 2) \
            q <<= 1;                                                      \
                                                                          \
        /* Compute difference between current quotient and numerator */   \
        num -= q * den;                                                   \
        quot += q;                                                        \
    }                                                                     \
    ret.quot = quot;                                                      \
    ret.rem = num;                                                        \
    return ret;                                                           \
}

#define __AEABI_XDIVMOD(name, type, uiname, rettype, urettype, typemacro)     \
void __aeabi_ ## name(type numerator, type denominator)                       \
{                                                                             \
    unsigned type num, den;                                                   \
    urettype uxdiv_ret;                                                       \
    rettype ret;                                                              \
                                                                              \
    if (numerator >= 0)                                                       \
      num = numerator;                                                        \
    else                                                                      \
      num = 0 - numerator;                                                    \
    if (denominator >= 0)                                                     \
      den = denominator;                                                      \
    else                                                                      \
      den = 0 - denominator;                                                  \
    uxdiv_ret = aeabi_ ## uiname(num, den);                                   \
    /* signs differ */                                                        \
    if ((numerator & typemacro ## _MIN) != (denominator & typemacro ## _MIN)) \
        ret.quot = 0 - uxdiv_ret.quot;                                        \
    else                                                                      \
        ret.quot = uxdiv_ret.quot;                                            \
    if (numerator < 0)                                                        \
        ret.rem = 0 - uxdiv_ret.rem;                                          \
    else                                                                      \
        ret.rem = uxdiv_ret.rem;                                              \
                                                                              \
    rettype ## _return(ret);                                                  \
}

define_aeabi_xdivmod_signed_type(long long, lldiv_t);
define_aeabi_xdivmod_unsigned_type(unsigned long long, ulldiv_t);
define_aeabi_xdivmod_signed_type(int, idiv_t);
define_aeabi_xdivmod_unsigned_type(unsigned, uidiv_t);

REGS_RETURN(lldiv_t, lldiv_t)
REGS_RETURN(ulldiv_t, ulldiv_t)
REGS_RETURN(idiv_t, idiv_t)
REGS_RETURN(uidiv_t, uidiv_t)

AEABI_UXDIVMOD(uldivmod, unsigned long long, ulldiv_t, ULLONG)

__AEABI_XDIVMOD(ldivmod, long long, uldivmod, lldiv_t, ulldiv_t, LLONG)

void __aeabi_uldivmod(unsigned long long num, unsigned long long den)
{
    ulldiv_t_return(aeabi_uldivmod(num, den));
}

void __aeabi_llsl(double_unsigned_struct val, int shift)
{
    double_unsigned_struct ret;

    if (shift >= 32) {
        val.high = val.low;
        val.low = 0;
        shift -= 32;
    }
    if (shift > 0) {
        ret.low = val.low << shift;
        ret.high = (val.high << shift) | (val.low >> (32 - shift));
        double_unsigned_struct_return(ret);
	return;
    }
    double_unsigned_struct_return(val);
}

#define aeabi_lsr(val, shift, fill, type)                          \
    type ## _struct ret;                                           \
                                                                   \
    if (shift >= 32) {                                             \
        val.low = val.high;                                        \
        val.high = fill;                                           \
        shift -= 32;                                               \
    }                                                              \
    if (shift > 0) {                                               \
        ret.high = val.high >> shift;                              \
        ret.low = (val.high << (32 - shift)) | (val.low >> shift); \
        type ## _struct_return(ret);                               \
	return;                                                    \
    }                                                              \
    type ## _struct_return(val);

void __aeabi_llsr(double_unsigned_struct val, int shift)
{
    aeabi_lsr(val, shift, 0, double_unsigned);
}

void __aeabi_lasr(unsigned_int_struct val, int shift)
{
    aeabi_lsr(val, shift, val.high >> 31, unsigned_int);
}


/* Integer division functions */

#if 0 /* very slow */
AEABI_UXDIVMOD(uidivmod, unsigned, uidiv_t, UINT)

int __aeabi_idiv(int numerator, int denominator)
{
    unsigned num, den;
    uidiv_t ret;

    if (numerator >= 0)
        num = numerator;
    else
        num = 0 - numerator;
    if (denominator >= 0)
        den = denominator;
    else
        den = 0 - denominator;
    ret = aeabi_uidivmod(num, den);
    if ((numerator & INT_MIN) != (denominator & INT_MIN)) /* signs differ */
        ret.quot *= -1;
    return ret.quot;
}

unsigned __aeabi_uidiv(unsigned num, unsigned den)
{
    return aeabi_uidivmod(num, den).quot;
}

__AEABI_XDIVMOD(idivmod, int, uidivmod, idiv_t, uidiv_t, INT)

void __aeabi_uidivmod(unsigned num, unsigned den)
{
    uidiv_t_return(aeabi_uidivmod(num, den));
}
#else
# define UIDIVMOD_ASM 1
#endif

/* Some targets do not have all eabi calls (OpenBSD) */
typedef __SIZE_TYPE__ size_t;
extern void *memcpy(void *dest, const void *src, size_t n);
extern void *memmove(void *dest, const void *src, size_t n);
extern void *memset(void *s, int c, size_t n);

void *
__aeabi_memcpy (void *dest, const void *src, size_t n)
{
    return memcpy (dest, src, n);
}

void *
__aeabi_memmove (void *dest, const void *src, size_t n)
{
    return memmove (dest, src, n);
}

void *
__aeabi_memmove4 (void *dest, const void *src, size_t n)
{
    return memmove (dest, src, n);
}

void *
__aeabi_memmove8 (void *dest, const void *src, size_t n)
{
    return memmove (dest, src, n);
}

void *
__aeabi_memset (void *s, size_t n, int c)
{
    return memset (s, c, n);
}

/* ***************************************************************** */
#if UIDIVMOD_ASM
#include <config.h>
__asm__(
   "\n  .global __aeabi_idiv, __aeabi_idivmod"
   "\n  .global __aeabi_uidiv, __aeabi_uidivmod"
#if __ARM_FEATURE_IDIV
   "\n__aeabi_idiv:"
   "\n__aeabi_idivmod:"
   "\n  mov     r2, r0"
   "\n  sdiv    r0, r0, r1"
   "\n  mls     r1, r1, r0, r2"
   "\n  bx      lr"

   "\n__aeabi_uidiv:"
   "\n__aeabi_uidivmod:"
   "\n  mov     r2, r0"
   "\n  udiv    r0, r0, r1"
   "\n  mls     r1, r1, r0, r2"
   "\n  bx      lr"
#else
/* Runtime ABI for the ARM Cortex-M0
 * idivmod.S: signed 32 bit division (quotient and remainder)
 *
 * Copyright (c) 2012 Jörg Mische <bobbl@gmx.de>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */
   "\n__aeabi_idiv:"
   "\n__aeabi_idivmod:"
   "\n  cmp     r0, #0"
   "\n  bge     .Lnumerator_pos"
   "\n  rsb     r0, r0, #0" // num = -num
   "\n  cmp     r1, #0"
   "\n  bge     .Lboth_neg"
   "\n  rsb     r1, r1, #0" // den = -den
   "\n  push    {lr}"
   "\n  bl      __aeabi_uidivmod"
   "\n  rsb     r1, r1, #0" // rem = -rem
   "\n  pop     {pc}"
   "\n.Lboth_neg:"
   "\n  push    {lr}"
   "\n  bl      __aeabi_uidivmod"
   "\n  rsb     r0, r0, #0" // quot = -quot
   "\n  rsb     r1, r1, #0" // rem = -rem
   "\n  pop     {pc}"
   "\n.Ldenom_neg:"
   "\n  rsb     r1, r1, #0" // den = -den
   "\n  push    {lr}"
   "\n  bl      __aeabi_uidivmod"
   "\n  rsb     r0, r0, #0" // quot = -quot
   "\n  pop     {pc}"
   "\n.Lnumerator_pos:"
   "\n  cmp     r1, #0"
   "\n  blt     .Ldenom_neg"

   // Divide r0 by r1 and return the quotient in r0 and the remainder in r1
   "\n__aeabi_uidiv:"
   "\n__aeabi_uidivmod:"
   // Shift left the denominator until it is greater than the numerator
   "\n  mov     r2, #1"	    // counter
   "\n  mov     r3, #0"	    // result
   "\n  cmp     r0, r1"
   "\n  bls     .Lsub_loop"
   "\n  adds    r1, #0"	    // dont shift if denominator would overflow
   "\n  bmi     .Lsub_loop"
   "\n  beq     .Luidiv0"
   "\n.Ldenom_shift_loop:"
   "\n  lsl     r2, #1"
   "\n  lsls    r1, #1"
   "\n  bmi     .Lsub_loop"
   "\n  cmp     r0, r1"
   "\n  bhi     .Ldenom_shift_loop"
   "\n.Lsub_loop:"
   "\n  cmp     r0, r1"     // if (num >= den)...
   "\n  subcs   r0, r1"     // numerator -= denom
   "\n  orrcs   r3, r2"     // result(r3) |= bitmask(r2)
   "\n  lsr     r1, #1"	    // denom(r1) >>= 1
   "\n  lsrs    r2, #1"	    // bitmask(r2) >>= 1
   "\n  bne     .Lsub_loop"
   "\n  mov     r1, r0"	    // remainder(r1) = numerator(r0)
   "\n  mov     r0, r3"	    // quotient(r0) = result(r3)
   "\n  bx      lr"
   "\n.Luidiv0:"            // XXX: division by zero
   "\n  mov     r0, #0"
   "\n  bx      lr"
#endif
);
#endif /* UIDIVMOD_ASM */
/* ***************************************************************** */
#endif
