aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog1
-rw-r--r--arm-gen.c131
-rw-r--r--libtcc.c23
-rw-r--r--tcc-doc.texi3
-rw-r--r--tcc.c2
-rw-r--r--tcc.h21
-rw-r--r--tccelf.c2
7 files changed, 113 insertions, 70 deletions
diff --git a/Changelog b/Changelog
index 52f8a10..9a497cf 100644
--- a/Changelog
+++ b/Changelog
@@ -18,6 +18,7 @@ Features:
- improved variable length array support (James Lyon)
- add the possibility to use noname functions by ordinal (YX Hao)
- add a install-strip target to install tcc (Thomas Preud'homme)
+- add runtime selection of float ABI on ARM (Thomas Preud'homme)
Platforms:
- support Debian GNU/kfreeBSD 64bit userspace (Thomas Preud'homme)
diff --git a/arm-gen.c b/arm-gen.c
index 05bccb0..bc24f70 100644
--- a/arm-gen.c
+++ b/arm-gen.c
@@ -141,6 +141,13 @@ enum {
#define ELF_START_ADDR 0x00008000
#define ELF_PAGE_SIZE 0x1000
+enum float_abi {
+ ARM_SOFTFP_FLOAT,
+ ARM_HARD_FLOAT,
+};
+
+enum float_abi float_abi;
+
/******************************************************/
#else /* ! TARGET_DEFS_ONLY */
/******************************************************/
@@ -169,7 +176,7 @@ static int leaffunc;
#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
static CType float_type, double_type, func_float_type, func_double_type;
-ST_FUNC void arm_init_types(void)
+ST_FUNC void arm_init(struct TCCState *s)
{
float_type.t = VT_FLOAT;
double_type.t = VT_DOUBLE;
@@ -177,12 +184,14 @@ ST_FUNC void arm_init_types(void)
func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
func_double_type.t = VT_FUNC;
func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
+
+ float_abi = s->float_abi;
}
#else
#define func_float_type func_old_type
#define func_double_type func_old_type
#define func_ldouble_type func_old_type
-ST_FUNC void arm_init_types(void) {}
+ST_FUNC void arm_init(void) {}
#endif
static int two2mask(int a,int b) {
@@ -195,6 +204,16 @@ static int regmask(int r) {
/******************************************************/
+#ifdef TCC_ARM_EABI
+char *default_elfinterp(struct TCCState *s)
+{
+ if (s->float_abi == ARM_HARD_FLOAT)
+ return "/lib/ld-linux-armhf.so.3";
+ else
+ return "/lib/ld-linux.so.3";
+}
+#endif
+
void o(uint32_t i)
{
/* this is a good place to start adding big-endian support*/
@@ -841,22 +860,19 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) {
#ifdef TCC_ARM_EABI
int size, align;
size = type_size(vt, &align);
-#ifdef TCC_ARM_HARDFLOAT
- if (!variadic && (is_float(vt->t) || is_hgen_float_aggr(vt))) {
+ if (float_abi == ARM_HARD_FLOAT && !variadic &&
+ (is_float(vt->t) || is_hgen_float_aggr(vt))) {
*ret_align = 8;
ret->ref = NULL;
ret->t = VT_DOUBLE;
return (size + 7) >> 3;
- } else
-#endif
- if (size > 4) {
- return 0;
- } else {
+ } else if (size <= 4) {
*ret_align = 4;
ret->ref = NULL;
ret->t = VT_INT;
return 1;
- }
+ } else
+ return 0;
#else
return 0;
#endif
@@ -1171,9 +1187,11 @@ void gfunc_call(int nb_args)
int todo;
struct plan plan;
-#ifdef TCC_ARM_HARDFLOAT
- variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS);
- corefloat = variadic || floats_in_core_regs(&vtop[-nb_args]);
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT) {
+ variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS);
+ corefloat = variadic || floats_in_core_regs(&vtop[-nb_args]);
+ }
#endif
/* cannot let cpu flags if other instruction are generated. Also avoid leaving
VT_JMP anywhere except on the top of the stack because it would complicate
@@ -1199,9 +1217,9 @@ void gfunc_call(int nb_args)
gcall_or_jmp(0);
if (args_size)
gadd_sp(args_size); /* pop all parameters passed on the stack */
-#ifdef TCC_ARM_EABI
-#ifdef TCC_ARM_VFP
- if(corefloat && is_float(vtop->type.ref->type.t)) {
+#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
+ if(float_abi == ARM_SOFTFP_FLOAT && corefloat &&
+ is_float(vtop->type.ref->type.t)) {
if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) {
o(0xEE000A10); /*vmov s0, r0 */
} else {
@@ -1210,7 +1228,6 @@ void gfunc_call(int nb_args)
}
}
#endif
-#endif
vtop -= nb_args + 1; /* Pop all params and fct address from value stack */
leaffunc = 0; /* we are calling a function, so we aren't in a leaf function */
}
@@ -1220,9 +1237,8 @@ void gfunc_prolog(CType *func_type)
{
Sym *sym,*sym2;
int n, nf, size, align, struct_ret = 0;
-#ifdef TCC_ARM_HARDFLOAT
+ int addr, pn, sn; /* pn=core, sn=stack */
struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
-#endif
CType ret_type;
sym = func_type->ref;
@@ -1237,11 +1253,11 @@ void gfunc_prolog(CType *func_type)
struct_ret = 1;
func_vc = 12; /* Offset from fp of the place to store the result */
}
- for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) {
+ for(sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) {
size = type_size(&sym2->type, &align);
-#ifdef TCC_ARM_HARDFLOAT
- if (!func_var && (is_float(sym2->type.t)
- || is_hgen_float_aggr(&sym2->type))) {
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT && !func_var &&
+ (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) {
int tmpnf = assign_vfpreg(&avregs, align, size);
tmpnf += (size + 3) / 4;
nf = (tmpnf > nf) ? tmpnf : nf;
@@ -1270,50 +1286,49 @@ void gfunc_prolog(CType *func_type)
o(0xE92D5800); /* save fp, ip, lr */
o(0xE1A0B00D); /* mov fp, sp */
func_sub_sp_offset = ind;
- o(0xE1A00000); /* nop, leave space for stack adjustment in epilogue */
- {
- int addr, pn = struct_ret, sn = 0; /* pn=core, sn=stack */
+ o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */
-#ifdef TCC_ARM_HARDFLOAT
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT) {
func_vc += nf * 4;
avregs = AVAIL_REGS_INITIALIZER;
+ }
#endif
- while ((sym = sym->next)) {
- CType *type;
- type = &sym->type;
- size = type_size(type, &align);
- size = (size + 3) >> 2;
- align = (align + 3) & ~3;
-#ifdef TCC_ARM_HARDFLOAT
- if (!func_var && (is_float(sym->type.t)
- || is_hgen_float_aggr(&sym->type))) {
- int fpn = assign_vfpreg(&avregs, align, size << 2);
- if (fpn >= 0) {
- addr = fpn * 4;
- } else
- goto from_stack;
- } else
+ pn = struct_ret, sn = 0;
+ while ((sym = sym->next)) {
+ CType *type;
+ type = &sym->type;
+ size = type_size(type, &align);
+ size = (size + 3) >> 2;
+ align = (align + 3) & ~3;
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT && !func_var && (is_float(sym->type.t)
+ || is_hgen_float_aggr(&sym->type))) {
+ int fpn = assign_vfpreg(&avregs, align, size << 2);
+ if (fpn >= 0)
+ addr = fpn * 4;
+ else
+ goto from_stack;
+ } else
#endif
- if (pn < 4) {
+ if (pn < 4) {
#ifdef TCC_ARM_EABI
pn = (pn + (align-1)/4) & -(align/4);
#endif
- addr = (nf + pn) * 4;
- pn += size;
- if (!sn && pn > 4)
- sn = (pn - 4);
- } else {
-#ifdef TCC_ARM_HARDFLOAT
+ addr = (nf + pn) * 4;
+ pn += size;
+ if (!sn && pn > 4)
+ sn = (pn - 4);
+ } else {
from_stack:
-#endif
#ifdef TCC_ARM_EABI
sn = (sn + (align-1)/4) & -(align/4);
#endif
- addr = (n + nf + sn) * 4;
- sn += size;
- }
- sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr+12);
+ addr = (n + nf + sn) * 4;
+ sn += size;
}
+ sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t),
+ addr + 12);
}
last_itod_magic=0;
leaffunc = 1;
@@ -1327,12 +1342,8 @@ void gfunc_epilog(void)
int diff;
/* Copy float return value to core register if base standard is used and
float computation is made with VFP */
-#ifdef TCC_ARM_EABI
- if (
-#ifdef TCC_ARM_HARDFLOAT
- func_var &&
-#endif
- is_float(func_vt.t)) {
+#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
+ if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) {
if((func_vt.t & VT_BTYPE) == VT_FLOAT)
o(0xEE100A10); /* fmrs r0, s0 */
else {
diff --git a/libtcc.c b/libtcc.c
index 154a266..127806f 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -757,7 +757,7 @@ static int tcc_compile(TCCState *s1)
func_old_type.t = VT_FUNC;
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
#ifdef TCC_TARGET_ARM
- arm_init_types();
+ arm_init(s1);
#endif
#if 0
@@ -945,9 +945,12 @@ LIBTCCAPI TCCState *tcc_new(void)
tcc_define_symbol(s, "__ARMEL__", NULL);
#if defined(TCC_ARM_EABI)
tcc_define_symbol(s, "__ARM_EABI__", NULL);
+#endif
#if defined(TCC_ARM_HARDFLOAT)
+ s->float_abi = ARM_HARD_FLOAT;
tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
-#endif
+#else
+ s->float_abi = ARM_SOFTFP_FLOAT;
#endif
#endif
@@ -1628,6 +1631,7 @@ enum {
TCC_OPTION_b,
TCC_OPTION_g,
TCC_OPTION_c,
+ TCC_OPTION_float_abi,
TCC_OPTION_static,
TCC_OPTION_shared,
TCC_OPTION_soname,
@@ -1680,6 +1684,9 @@ static const TCCOption tcc_options[] = {
#endif
{ "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "c", TCC_OPTION_c, 0 },
+#ifdef TCC_TARGET_ARM
+ { "mfloat-abi", TCC_OPTION_float_abi, TCC_OPTION_HAS_ARG },
+#endif
{ "static", TCC_OPTION_static, 0 },
{ "shared", TCC_OPTION_shared, 0 },
{ "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
@@ -1817,6 +1824,18 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv)
case TCC_OPTION_c:
s->output_type = TCC_OUTPUT_OBJ;
break;
+#ifdef TCC_TARGET_ARM
+ case TCC_OPTION_float_abi:
+ /* tcc doesn't support soft float yet */
+ if (!strcmp(optarg, "softfp")) {
+ s->float_abi = ARM_SOFTFP_FLOAT;
+ tcc_undefine_symbol(s, "__ARM_PCS_VFP");
+ } else if (!strcmp(optarg, "hard"))
+ s->float_abi = ARM_HARD_FLOAT;
+ else
+ tcc_error("unsupported float abi '%s'", optarg);
+ break;
+#endif
case TCC_OPTION_static:
s->static_link = 1;
break;
diff --git a/tcc-doc.texi b/tcc-doc.texi
index 540b4b6..e8832f6 100644
--- a/tcc-doc.texi
+++ b/tcc-doc.texi
@@ -176,6 +176,9 @@ In a script, it gives the following header:
#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11
@end example
+@item -mfloat-abi (ARM only)
+Select the float ABI. Possible values: @code{softfp} and @code{hard}
+
@item -dumpversion
Print only the compiler version and nothing else.
diff --git a/tcc.c b/tcc.c
index 58f9007..9b5ca2e 100644
--- a/tcc.c
+++ b/tcc.c
@@ -224,7 +224,7 @@ static void display_info(TCCState *s, int what)
print_paths("crt", s->crt_paths, s->nb_crt_paths);
print_paths("libraries", s->library_paths, s->nb_library_paths);
print_paths("include", s->sysinclude_paths, s->nb_sysinclude_paths);
- printf("elfinterp:\n %s\n", CONFIG_TCC_ELFINTERP);
+ printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s));
break;
}
}
diff --git a/tcc.h b/tcc.h
index f3d868f..73285ae 100644
--- a/tcc.h
+++ b/tcc.h
@@ -221,21 +221,24 @@
# endif
# elif defined __GNU__
# define CONFIG_TCC_ELFINTERP "/lib/ld.so"
-# elif defined TCC_ARM_HARDFLOAT
-# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-armhf.so.3"
-# elif defined TCC_ARM_EABI
-# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.3"
# elif defined(TCC_TARGET_X86_64)
# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
# elif defined(TCC_UCLIBC)
# define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0"
# elif defined(TCC_TARGET_PE)
# define CONFIG_TCC_ELFINTERP "-"
-# else
+# elif !defined(TCC_ARM_EABI)
# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
# endif
#endif
+/* var elf_interp dans *-gen.c */
+#ifdef CONFIG_TCC_ELFINTERP
+# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
+#else
+# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
+#endif
+
/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
#define TCC_LIBGCC USE_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
@@ -561,6 +564,9 @@ struct TCCState {
/* compile with built-in memory and bounds checker */
int do_bounds_check;
#endif
+#ifdef TCC_TARGET_ARM
+ enum float_abi float_abi; /* float ABI of the generated code*/
+#endif
addr_t text_addr; /* address of text section */
int has_text_addr;
@@ -1329,7 +1335,10 @@ ST_FUNC void gen_opl(int op);
/* ------------ arm-gen.c ------------ */
#ifdef TCC_TARGET_ARM
-ST_FUNC void arm_init_types(void);
+#ifdef TCC_ARM_EABI
+ST_FUNC char *default_elfinterp(struct TCCState *s);
+#endif
+ST_FUNC void arm_init(struct TCCState *s);
ST_FUNC uint32_t encbranch(int pos, int addr, int fail);
ST_FUNC void gen_cvt_itof1(int t);
#endif
diff --git a/tccelf.c b/tccelf.c
index aa3daac..43a8086 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -1592,7 +1592,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
/* allow override the dynamic loader */
const char *elfint = getenv("LD_SO");
if (elfint == NULL)
- elfint = CONFIG_TCC_ELFINTERP;
+ elfint = DEFAULT_ELFINTERP(s1);
/* add interpreter section only if executable */
interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
interp->sh_addralign = 1;