aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Preud'homme <robotux@celest.fr>2013-04-07 17:02:57 +0200
committerThomas Preud'homme <robotux@celest.fr>2013-12-11 10:15:30 +0800
commitf2dbcf7594887ddfdec646ab2a85f4e2358ec209 (patch)
tree228e60ee5f0d9d20380c5e727c7a26e53e971a27
parent389c25c4b9006b16138094b110e4c50b78432905 (diff)
downloadtinycc-f2dbcf7594887ddfdec646ab2a85f4e2358ec209.tar.gz
tinycc-f2dbcf7594887ddfdec646ab2a85f4e2358ec209.tar.bz2
Add ARM aeabi functions needed to run tcctest
Add implementation for float / integer conversion functions: __aeabi_d2lz, __aeabi_d2ulz, __aeabi_f2lz, __aeabi_f2ulz, __aeabi_l2d, __aeabi_l2f, __aeabi_ul2d, __aeabi_ul2f Add implementation for long long helper functions: __aeabi_ldivmod, __aeabi_uldivmod, __aeabi_llsl, __aeabi_llsr, __aeabi_lasr Add implementation for integer division functions: __aeabi_uidiv, __aeabi_uidivmod, __aeabi_idiv, __aeabi_idivmod,
-rw-r--r--lib/Makefile2
-rw-r--r--lib/armeabi.c441
-rw-r--r--tests/tcctest.c67
3 files changed, 509 insertions, 1 deletions
diff --git a/lib/Makefile b/lib/Makefile
index dfd01c3..a8a2b5d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -45,7 +45,7 @@ cross : TCC = $(TOP)/$(TARGET)-tcc$(EXESUF)
I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BCHECK_O)
X86_64_O = libtcc1.o alloca86_64.o
-ARM_O = libtcc1.o
+ARM_O = libtcc1.o armeabi.o
WIN32_O = $(I386_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o
WIN64_O = $(X86_64_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o
diff --git a/lib/armeabi.c b/lib/armeabi.c
new file mode 100644
index 0000000..c00ace6
--- /dev/null
+++ b/lib/armeabi.c
@@ -0,0 +1,441 @@
+#include<limits.h>
+
+/* 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) \
+ 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__AEABIT_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__AEABIT_F2XLZ(f2ulz, 0)
+
+/* float to long long conversion */
+DEFINE__AEABIT_F2XLZ(f2lz, 1)
+
+/* double to [unsigned] long long conversion */
+#define DEFINE__AEABIT_D2XLZ(name, with_sign) \
+void __aeabi_ ## name(double_unsigned_struct val) \
+{ \
+ int exp, high_shift, sign; \
+ double_unsigned_struct 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++; \
+ } \
+ \
+ double_unsigned_struct_return(ret); \
+}
+
+/* double to unsigned long long conversion */
+DEFINE__AEABIT_D2XLZ(d2ulz, 0)
+
+/* double to long long conversion */
+DEFINE__AEABIT_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 */, 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 & (1 << 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)); \
+ } else \
+ 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; \
+ else \
+ ret >>= s - FLOAT_FRAC_BITS; \
+ } else \
+ return 0; \
+ } \
+ \
+ /* 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, 0)
+
+/* long long to double conversion */
+#define __AEABI_XL2D(name, with_sign) \
+void __aeabi_ ## name(unsigned long long v) \
+{ \
+ int s, 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; \
+ } \
+ 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; \
+ double_unsigned_struct_return(ret); \
+ } \
+ } \
+ \
+ /* fill exponent bits */ \
+ ret.high |= (s + ONE_EXP(DOUBLE)) << (DOUBLE_FRAC_BITS - 32); \
+ \
+ /* fill sign bit */ \
+ ret.high |= sign << 31; \
+ \
+ double_unsigned_struct_return(ret); \
+}
+
+/* unsigned long long to double conversion */
+__AEABI_XL2D(ul2d, 0)
+
+/* long long to double conversion */
+__AEABI_XL2D(l2d, 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; \
+ \
+ num = numerator & typemacro ## _MAX; \
+ den = denominator & typemacro ## _MAX; \
+ uxdiv_ret = aeabi_ ## uiname(num, den); \
+ /* signs differ */ \
+ if ((numerator & typemacro ## _MIN) != (denominator & typemacro ## _MIN)) \
+ ret.quot = uxdiv_ret.quot * -1; \
+ else \
+ ret.quot = uxdiv_ret.quot; \
+ if (numerator & typemacro ## _MIN) \
+ ret.rem = uxdiv_ret.rem * -1; \
+ 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, ULONG)
+
+__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 */
+
+AEABI_UXDIVMOD(uidivmod, unsigned, uidiv_t, UINT)
+
+int __aeabi_idiv(int numerator, int denominator)
+{
+ unsigned num, den;
+ uidiv_t ret;
+
+ num = numerator & INT_MAX;
+ den = denominator & INT_MAX;
+ 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));
+}
diff --git a/tests/tcctest.c b/tests/tcctest.c
index c5a3e73..eb284f0 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -59,6 +59,7 @@
#include "tcclib.h"
+void intdiv_test();
void string_test();
void expr_test();
void macro_test();
@@ -167,6 +168,71 @@ int qq(int x)
#define wq_spin_lock spin_lock
#define TEST2() wq_spin_lock(a)
+#define UINT_MAX ((unsigned) -1)
+
+void intdiv_test(void)
+{
+ printf("18/21=%u\n", 18/21);
+ printf("18%21=%u\n", 18%21);
+ printf("41/21=%u\n", 41/21);
+ printf("41%21=%u\n", 41%21);
+ printf("42/21=%u\n", 42/21);
+ printf("42%21=%u\n", 42%21);
+ printf("43/21=%u\n", 43/21);
+ printf("43%21=%u\n", 43%21);
+ printf("126/21=%u\n", 126/21);
+ printf("12%/21=%u\n", 126%21);
+ printf("131/21=%u\n", 131/21);
+ printf("131%21=%u\n", 131%21);
+ printf("(UINT_MAX/2+3)/2=%u\n", (UINT_MAX/2+3)/2);
+ printf("(UINT_MAX/2+3)%2=%u\n", (UINT_MAX/2+3)%2);
+
+ printf("18/-21=%u\n", 18/-21);
+ printf("18%-21=%u\n", 18%-21);
+ printf("41/-21=%u\n", 41/-21);
+ printf("41%-21=%u\n", 41%-21);
+ printf("42/-21=%u\n", 42/-21);
+ printf("42%-21=%u\n", 42%-21);
+ printf("43/-21=%u\n", 43/-21);
+ printf("43%-21=%u\n", 43%-21);
+ printf("126/-21=%u\n", 126/-21);
+ printf("12%/-21=%u\n", 126%-21);
+ printf("131/-21=%u\n", 131/-21);
+ printf("131%-21=%u\n", 131%-21);
+ printf("(UINT_MAX/2+3)/-2=%u\n", (UINT_MAX/2+3)/-2);
+ printf("(UINT_MAX/2+3)%-2=%u\n", (UINT_MAX/2+3)%-2);
+
+ printf("-18/21=%u\n", -18/21);
+ printf("-18%21=%u\n", -18%21);
+ printf("-41/21=%u\n", -41/21);
+ printf("-41%21=%u\n", -41%21);
+ printf("-42/21=%u\n", -42/21);
+ printf("-42%21=%u\n", -42%21);
+ printf("-43/21=%u\n", -43/21);
+ printf("-43%21=%u\n", -43%21);
+ printf("-126/21=%u\n", -126/21);
+ printf("-12%/21=%u\n", -126%21);
+ printf("-131/21=%u\n", -131/21);
+ printf("-131%21=%u\n", -131%21);
+ printf("-(UINT_MAX/2+3)/2=%u\n", (0-(UINT_MAX/2+3))/2);
+ printf("-(UINT_MAX/2+3)%2=%u\n", (0-(UINT_MAX/2+3))%2);
+
+ printf("-18/-21=%u\n", -18/-21);
+ printf("-18%-21=%u\n", -18%-21);
+ printf("-41/-21=%u\n", -41/-21);
+ printf("-41%-21=%u\n", -41%-21);
+ printf("-42/-21=%u\n", -42/-21);
+ printf("-42%-21=%u\n", -42%-21);
+ printf("-43/-21=%u\n", -43/-21);
+ printf("-43%-21=%u\n", -43%-21);
+ printf("-126/-21=%u\n", -126/-21);
+ printf("-12%/-21=%u\n", -126%-21);
+ printf("-131/-21=%u\n", -131/-21);
+ printf("-131%-21=%u\n", -131%-21);
+ printf("-(UINT_MAX/2+3)/-2=%u\n", (0-(UINT_MAX/2+3))/-2);
+ printf("-(UINT_MAX/2+3)%-2=%u\n", (0-(UINT_MAX/2+3))%-2);
+}
+
void macro_test(void)
{
printf("macro:\n");
@@ -619,6 +685,7 @@ int main(int argc, char **argv)
math_cmp_test();
callsave_test();
builtin_frame_address_test();
+ intdiv_test();
return 0;
}