From f6b50558fc46aeb8e981355009871b30e61de841 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Fri, 14 Jun 2013 16:18:16 +0200 Subject: Add support for load/store of _Bool value Add support for loading _Bool value in i386, x86_64 and arm as well as support for storing _Bool value on arm. --- arm-gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 250b1d9..eccfdd8 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -571,7 +571,7 @@ void load(int r, SValue *sv) op=0xE5100000; if(!sign) op|=0x800000; - if ((ft & VT_BTYPE) == VT_BYTE) + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) op|=0x400000; o(op|(intr(r)<<12)|fc|(base<<16)); } @@ -699,7 +699,7 @@ void store(int r, SValue *sv) op=0xE5000000; if(!sign) op|=0x800000; - if ((ft & VT_BTYPE) == VT_BYTE) + if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) op|=0x400000; o(op|(intr(r)<<12)|fc|(base<<16)); } -- cgit v1.3.1 From b7d017dec89984b8536139ec6053fc0255413c27 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 5 Nov 2013 17:50:30 +0800 Subject: Fix allocation of struct in registers on ARM Allocation of struct in core and/or VFP registers on ARM is made by manipulating the value stack to create 3 distinct zones: parameters allocated on stack, parameters of type struct allocated in core registers and parameters of type struct allocated in VFP registers. Parameters of primitive type can be in any zone. This commit change the order of the zones from stack, VFP, core to stack, core, VFP (from highest addresses to lowest ones) in order to correctly deal the situation when structures are allocated both in core and VFP registers. --- arm-gen.c | 90 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 44 insertions(+), 46 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index eccfdd8..c9d4e55 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -826,7 +826,7 @@ void gfunc_call(int nb_args) int size, align, r, args_size, i, ncrn, ncprn, argno, vfp_argno; signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; SValue *before_stack = NULL; /* SValue before first on stack argument */ - SValue *before_vfpreg_hfa = NULL; /* SValue before first in VFP reg hfa argument */ + SValue *before_creg = NULL; /* SValue before first argument of type struct in core register */ #ifdef TCC_ARM_HARDFLOAT struct avail_regs avregs = AVAIL_REGS_INITIALIZER; signed char vfp_plan[16]; @@ -865,12 +865,12 @@ void gfunc_call(int nb_args) (core or VFP) are free for the current argument, assign them to it, else allocate on stack with correct alignment. Whenever a structure is allocated in registers or on stack, it is always put on the stack at this stage. The - stack is divided in 3 zones. The zone are, from low addresses to high + stack is divided in 3 zones. The zone are, from high addresses to low addresses: structures to be loaded in core registers, structures to be loaded in VFP registers, argument allocated to stack. SValue's representing structures in the first zone are moved just after the SValue pointed by - before_vfpreg_hfa. SValue's representing structures in the second zone are - moved just after the SValue pointer by before_stack. */ + before_stack. SValue's representing structures in the second zone are + moved just after the SValue pointer by before_creg. */ for(i = nb_args; i-- ;) { int j, assigned_vfpreg = 0; size = type_size(&vtop[-i].type, &align); @@ -892,14 +892,15 @@ void gfunc_call(int nb_args) if (assigned_vfpreg >= 0) { vfp_plan[vfp_argno++]=TREG_F0 + assigned_vfpreg/2; if (hfa) { - /* before_stack can only have been set because all core registers - are assigned, so no need to care about before_vfpreg_hfa if - before_stack is set */ - if (before_stack) { - vrote(&vtop[-i], &vtop[-i] - before_stack); - before_stack++; - } else if (!before_vfpreg_hfa) - before_vfpreg_hfa = &vtop[-i-1]; + /* if before_creg is not set, it means that no parameter has been + * allocated in core register. This implied that no argument has + * been allocated on stack neither because a VFP was available for + * this parameter. */ + if (before_creg) { + /* before_creg already exists and we just update it */ + vrote(&vtop[-i], &vtop[-i] - before_creg); + before_creg++; + } for (j = assigned_vfpreg; j <= end_reg; j++) vfp_todo|=(1<4) - ncrn=4; - todo&=((1<r=i; - keep++; - nb_regs++; - } - } - args_size-=nb_regs*4; - } if(vfp_todo) { int nb_fregs=0; @@ -1129,6 +1109,24 @@ save_regs(keep); /* save used temporary registers */ args_size-=nb_fregs*4; } } + if(ncrn) { + int nb_regs=0; + if (ncrn>4) + ncrn=4; + todo&=((1<r=i; + keep++; + nb_regs++; + } + } + args_size-=nb_regs*4; + } vrotb(keep); gcall_or_jmp(0); if (args_size) -- cgit v1.3.1 From 0650ab01c8a86dcf5247c1621e47dac06c0fc30c Mon Sep 17 00:00:00 2001 From: Joseph Poirier Date: Fri, 8 Nov 2013 13:24:15 -0600 Subject: struct variable behind guard, proper macro check, and remove some whitespace. Wrap runtime_main as per its declaration in tcc.h. Fix preprocessor check for TCC_ARM_EABI macro definition. Signed-off-by: Joseph Poirier --- arm-gen.c | 34 ++++++++++++++-------------- libtcc.c | 76 ++++++++++++++++++++++++++++++++------------------------------- 2 files changed, 56 insertions(+), 54 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index c9d4e55..b9e622f 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1,6 +1,6 @@ /* * ARMv4 code generator for TCC - * + * * Copyright (c) 2003 Daniel Glöckner * Copyright (c) 2012 Thomas Preud'homme * @@ -46,7 +46,7 @@ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ #define RC_R0 0x0004 -#define RC_R1 0x0008 +#define RC_R1 0x0008 #define RC_R2 0x0010 #define RC_R3 0x0020 #define RC_R12 0x0040 @@ -211,7 +211,7 @@ void o(uint32_t i) cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i&255; - i>>=8; + i>>=8; cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i; @@ -506,7 +506,7 @@ void load(int r, SValue *sv) sign=1; fc=-fc; } - + v = fr & VT_VALMASK; if (fr & VT_LVAL) { uint32_t base = 0xB; // fp @@ -645,8 +645,8 @@ void store(int r, SValue *sv) sign=1; fc=-fc; } - - v = fr & VT_VALMASK; + + v = fr & VT_VALMASK; if (fr & VT_LVAL || fr == VT_LOCAL) { uint32_t base = 0xb; if(v < VT_CONST) { @@ -660,16 +660,16 @@ void store(int r, SValue *sv) v1.sym=sv->sym; load(base=14, &v1); fc=sign=0; - v=VT_LOCAL; + v=VT_LOCAL; } if(v == VT_LOCAL) { if(is_float(ft)) { calcaddr(&base,&fc,&sign,1020,2); #ifdef TCC_ARM_VFP op=0xED000A00; /* fsts */ - if(!sign) - op|=0x800000; - if ((ft & VT_BTYPE) != VT_FLOAT) + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) op|=0x100; /* fsts -> fstd */ o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); #else @@ -802,7 +802,7 @@ int assign_fpreg(struct avail_regs *avregs, int align, int size) /* Return 1 if this function returns via an sret pointer, 0 otherwise */ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { -#if TCC_ARM_EABI +#ifdef TCC_ARM_EABI int size, align; size = type_size(vt, &align); if (size > 4) { @@ -855,7 +855,7 @@ void gfunc_call(int nb_args) vtop[-nb_args+1]=tmp; --nb_args; } - + vpushi(0), nb_args++; vtop->type.t = VT_LLONG; #endif @@ -1023,7 +1023,7 @@ void gfunc_call(int nb_args) size = 8; else size = LDOUBLE_SIZE; - + if (size == 12) r|=0x400000; else if(size == 8) @@ -1362,7 +1362,7 @@ int gtst(int inv, int t) return gtst(inv, t); } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) + if ((vtop->c.i != 0) != inv) t = gjmp(t); } else { v = gv(RC_INT); @@ -1370,7 +1370,7 @@ int gtst(int inv, int t) vtop->r = VT_CMP; vtop->c.i = TOK_NE; return gtst(inv, t); - } + } } vtop--; return t; @@ -1628,7 +1628,7 @@ void gen_opf(int op) case TOK_UGE: op=TOK_GE; break; case TOK_UGT: op=TOK_GT; break; } - + vtop->r = VT_CMP; vtop->c.i = op; return; @@ -1779,7 +1779,7 @@ void gen_opf(int op) r=fpr(gv(RC_FLOAT)); vswap(); r2=fpr(gv(RC_FLOAT)); - } + } break; default: if(op >= TOK_ULT && op <= TOK_GT) { diff --git a/libtcc.c b/libtcc.c index f841eb0..aea81a3 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1,6 +1,6 @@ /* * TCC - Tiny C Compiler - * + * * Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -156,7 +156,7 @@ PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s) { int len; len = strlen(buf); - if (len < buf_size) + if (len < buf_size) pstrcpy(buf + len, buf_size - len, s); return buf; } @@ -275,7 +275,7 @@ ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data) { int nb, nb_alloc; void **pp; - + nb = *nb_ptr; pp = *ptab; /* every power of two we double array size */ @@ -371,7 +371,7 @@ ST_FUNC void section_realloc(Section *sec, unsigned long new_size) { unsigned long size; unsigned char *data; - + size = sec->data_allocated; if (size == 0) size = 1; @@ -414,7 +414,7 @@ ST_FUNC Section *find_section(TCCState *s1, const char *name) int i; for(i = 1; i < s1->nb_sections; i++) { sec = s1->sections[i]; - if (!strcmp(name, sec->name)) + if (!strcmp(name, sec->name)) return sec; } /* sections are created as PROGBITS */ @@ -423,7 +423,7 @@ ST_FUNC Section *find_section(TCCState *s1, const char *name) /* update sym->c so that it points to an external symbol in section 'section' with value 'value' */ -ST_FUNC void put_extern_sym2(Sym *sym, Section *section, +ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore) { @@ -434,7 +434,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, if (section == NULL) sh_num = SHN_UNDEF; - else if (section == SECTION_ABS) + else if (section == SECTION_ABS) sh_num = SHN_ABS; else sh_num = section->sh_num; @@ -468,13 +468,13 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, switch(sym->v) { #ifdef TCC_TARGET_PE /* XXX: we rely only on malloc hooks */ - case TOK_malloc: - case TOK_free: - case TOK_realloc: - case TOK_memalign: - case TOK_calloc: + case TOK_malloc: + case TOK_free: + case TOK_realloc: + case TOK_memalign: + case TOK_calloc: #endif - case TOK_memcpy: + case TOK_memcpy: case TOK_memmove: case TOK_memset: case TOK_strlen: @@ -527,7 +527,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, } } -ST_FUNC void put_extern_sym(Sym *sym, Section *section, +ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size) { put_extern_sym2(sym, section, value, size, 1); @@ -567,7 +567,7 @@ static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) { char buf[2048]; BufferedFile **pf, *f; - + buf[0] = '\0'; /* use upper file if inline ":asm:" or token ":paste:" */ for (f = file; f && f->filename[0] == ':'; f = f->prev) @@ -718,28 +718,28 @@ static int tcc_compile(TCCState *s1) cur_text_section = NULL; funcname = ""; - anon_sym = SYM_FIRST_ANOM; + anon_sym = SYM_FIRST_ANOM; /* file info: full path + filename */ section_sym = 0; /* avoid warning */ if (s1->do_debug) { - section_sym = put_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, + section_sym = put_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, text_section->sh_num, NULL); getcwd(buf, sizeof(buf)); #ifdef _WIN32 normalize_slashes(buf); #endif pstrcat(buf, sizeof(buf), "/"); - put_stabs_r(buf, N_SO, 0, 0, + put_stabs_r(buf, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); - put_stabs_r(file->filename, N_SO, 0, 0, + put_stabs_r(file->filename, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); } /* an elf symbol of type STT_FILE must be put so that STB_LOCAL symbols can be safely used */ - put_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, + put_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, SHN_ABS, file->filename); /* define some often used types */ @@ -794,7 +794,7 @@ static int tcc_compile(TCCState *s1) /* end of translation unit info */ if (s1->do_debug) { - put_stabs_r(NULL, N_SO, 0, 0, + put_stabs_r(NULL, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); } } @@ -803,7 +803,7 @@ static int tcc_compile(TCCState *s1) /* reset define stack, but leave -Dsymbols (may be incorrect if they are undefined) */ - free_defines(define_start); + free_defines(define_start); gen_inline_functions(); @@ -1004,13 +1004,13 @@ LIBTCCAPI TCCState *tcc_new(void) /* symbols are always generated for linking stage */ symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0, ".strtab", - ".hashtab", SHF_PRIVATE); + ".hashtab", SHF_PRIVATE); strtab_section = symtab_section->link; s->symtab = symtab_section; - + /* private symbol table for dynamic symbols */ s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE, - ".dynstrtab", + ".dynstrtab", ".dynhashtab", SHF_PRIVATE); s->alacarte_link = 1; s->nocommon = 1; @@ -1026,7 +1026,9 @@ LIBTCCAPI TCCState *tcc_new(void) #ifdef TCC_TARGET_I386 s->seg_size = 32; #endif +#ifdef TCC_IS_NATIVE s->runtime_main = "main"; +#endif return s; } @@ -1044,7 +1046,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) for(i = 0; i < s1->nb_priv_sections; i++) free_section(s1->priv_sections[i]); dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections); - + /* free any loaded DLLs */ #ifdef TCC_IS_NATIVE for ( i = 0; i < s1->nb_loaded_dlls; i++) { @@ -1053,7 +1055,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) dlclose(ref->handle); } #endif - + /* free loaded dlls array */ dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); @@ -1079,7 +1081,7 @@ LIBTCCAPI void tcc_delete(TCCState *s1) #ifdef TCC_IS_NATIVE # ifdef HAVE_SELINUX munmap (s1->write_mem, s1->mem_size); - munmap (s1->runtime_mem, s1->mem_size); + munmap (s1->runtime_mem, s1->mem_size); # else tcc_free(s1->runtime_mem); # endif @@ -1186,7 +1188,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) #endif ret = 0; } else { - ret = tcc_load_dll(s1, fd, filename, + ret = tcc_load_dll(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); } goto the_end; @@ -1318,9 +1320,9 @@ LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) /* define symbol */ tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL); /* create bounds sections */ - bounds_section = new_section(s, ".bounds", + bounds_section = new_section(s, ".bounds", SHT_PROGBITS, SHF_ALLOC); - lbounds_section = new_section(s, ".lbounds", + lbounds_section = new_section(s, ".lbounds", SHT_PROGBITS, SHF_ALLOC); } #endif @@ -1693,7 +1695,7 @@ static const TCCOption tcc_options[] = { { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, { "nostdinc", TCC_OPTION_nostdinc, 0 }, { "nostdlib", TCC_OPTION_nostdlib, 0 }, - { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, + { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "w", TCC_OPTION_w, 0 }, { "pipe", TCC_OPTION_pipe, 0}, @@ -1859,7 +1861,7 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv) goto unsupported_option; break; case TCC_OPTION_W: - if (tcc_set_warning(s, optarg, 1) < 0 && + if (tcc_set_warning(s, optarg, 1) < 0 && s->warn_unsupported) goto unsupported_option; break; @@ -1946,7 +1948,7 @@ PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time) tt = 0.001; if (total_bytes < 1) total_bytes = 1; - printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n", + printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n", tok_ident - TOK_IDENT, total_lines, total_bytes, tt, (int)(total_lines / tt), total_bytes / tt / 1000000.0); @@ -1955,7 +1957,7 @@ PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time) PUB_FUNC void tcc_set_environment(TCCState *s) { char * path; - + path = getenv("C_INCLUDE_PATH"); if(path != NULL) { tcc_add_include_path(s, path); -- cgit v1.3.1 From 1528a085408e7aa16d2009981215fc370842eb87 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Mon, 4 Feb 2013 20:02:38 +0100 Subject: Refactor and simplify gfunc_call() on arm --- arm-gen.c | 632 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 332 insertions(+), 300 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index b9e622f..7f870a2 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -746,8 +746,31 @@ static void gcall_or_jmp(int is_jmp) } } +/* Return 1 if this function returns via an sret pointer, 0 otherwise */ +ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { +#ifdef TCC_ARM_EABI + int size, align; + size = type_size(vt, &align); + if (size > 4) { + return 1; + } else { + *ret_align = 4; + ret->ref = NULL; + ret->t = VT_INT; + return 0; + } +#else + return 1; +#endif +} + #ifdef TCC_ARM_HARDFLOAT -static int is_float_hgen_aggr(CType *type) +/* Return whether a structure is an homogeneous float aggregate or not. + The answer is true if all the elements of the structure are of the same + primitive float type and there is less than 4 elements. + + type: the type corresponding to the structure to be tested */ +static int is_hgen_float_aggr(CType *type) { if ((type->t & VT_BTYPE) == VT_STRUCT) { struct Sym *ref; @@ -764,28 +787,36 @@ static int is_float_hgen_aggr(CType *type) } struct avail_regs { - /* worst case: f(float, double, 3 float struct, double, 3 float struct, double) */ - signed char avail[3]; - int first_hole; - int last_hole; - int first_free_reg; + signed char avail[3]; /* 3 holes max with only float and double alignments */ + int first_hole; /* first available hole */ + int last_hole; /* last available hole (none if equal to first_hole) */ + int first_free_reg; /* next free register in the sequence, hole excluded */ }; #define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 } -/* Assign a register for a CPRC param with correct size and alignment - * size and align are in bytes, as returned by type_size */ -int assign_fpreg(struct avail_regs *avregs, int align, int size) +/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC + param) according to the rules described in the procedure call standard for + the ARM architecture (AAPCS). If found, the registers are assigned to this + VFP CPRC parameter. Registers are allocated in sequence unless a hole exists + and the parameter is a single float. + + avregs: opaque structure to keep track of available VFP co-processor regs + align: alignment contraints for the param, as returned by type_size() + size: size of the parameter, as returned by type_size() */ +int assign_vfpreg(struct avail_regs *avregs, int align, int size) { int first_reg = 0; if (avregs->first_free_reg == -1) return -1; - if (align >> 3) { // alignment needed (base type: double) + if (align >> 3) { /* double alignment */ first_reg = avregs->first_free_reg; + /* alignment contraint not respected so use next reg and record hole */ if (first_reg & 1) avregs->avail[avregs->last_hole++] = first_reg++; - } else { + } else { /* no special alignment (float or array of float) */ + /* if single float and a hole is available, assign the param to it */ if (size == 4 && avregs->first_hole != avregs->last_hole) return avregs->avail[avregs->first_hole++]; else @@ -800,79 +831,76 @@ int assign_fpreg(struct avail_regs *avregs, int align, int size) } #endif -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { -#ifdef TCC_ARM_EABI - int size, align; - size = type_size(vt, &align); - if (size > 4) { - return 1; - } else { - *ret_align = 4; - ret->ref = NULL; - ret->t = VT_INT; - return 0; - } -#else - return 1; -#endif -} +/* Parameters are classified according to how they are copied to their final + destination for the function call. Because the copying is performed class + after class according to the order in the union below, it is important that + some constraints about the order of the members of this union are respected: + - CORE_STRUCT_CLASS must come after STACK_CLASS; + - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and + VFP_STRUCT_CLASS; + - VFP_STRUCT_CLASS must come after VFP_CLASS. + See the comment for the main loop in copy_params() for the reason. */ +enum reg_class { + STACK_CLASS = 0, + CORE_STRUCT_CLASS, + VFP_CLASS, + VFP_STRUCT_CLASS, + CORE_CLASS, + NB_CLASSES +}; -/* Generate function call. The function address is pushed first, then - all the parameters in call order. This functions pops all the - parameters and the function address. */ -void gfunc_call(int nb_args) +struct param_plan { + int start; /* first reg or addr used depending on the class */ + int end; /* last reg used or next free addr depending on the class */ + SValue *sval; /* pointer to SValue on the value stack */ + struct param_plan *prev; /* previous element in this class */ +}; + +struct plan { + struct param_plan *pplans; /* array of all the param plans */ + struct param_plan *clsplans[NB_CLASSES]; /* per class lists of param plans */ +}; + +#define add_param_plan(plan,pplan,class) \ + do { \ + pplan.prev = plan->clsplans[class]; \ + plan->pplans[plan ## _nb] = pplan; \ + plan->clsplans[class] = &plan->pplans[plan ## _nb++]; \ + } while(0) + +/* Assign parameters to registers and stack with alignment according to the + rules in the procedure call standard for the ARM architecture (AAPCS). + The overall assignment is recorded in an array of per parameter structures + called parameter plans. The parameter plans are also further organized in a + number of linked lists, one per class of parameter (see the comment for the + definition of union reg_class). + + nb_args: number of parameters of the function for which a call is generated + variadic: whether the function is a variadic function or not + plan: the structure where the overall assignment is recorded + todo: a bitmap that record which core registers hold a parameter + + Returns the amount of stack space needed for parameter passing + + Note: this function allocated an array in plan->pplans with tcc_malloc. It + is the responsability of the caller to free this array once used (ie not + before copy_params). */ +static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo) { - int size, align, r, args_size, i, ncrn, ncprn, argno, vfp_argno; - signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; - SValue *before_stack = NULL; /* SValue before first on stack argument */ - SValue *before_creg = NULL; /* SValue before first argument of type struct in core register */ + int i, size, align; + int ncrn /* next core register number */, nsaa /* next stacked argument address*/; + int plan_nb = 0; + struct param_plan pplan; #ifdef TCC_ARM_HARDFLOAT struct avail_regs avregs = AVAIL_REGS_INITIALIZER; - signed char vfp_plan[16]; - int plan2[4+16]; - int variadic; -#else - int plan2[4]={0,0,0,0}; #endif - int vfp_todo=0; - int todo=0, keep; - -#ifdef TCC_ARM_HARDFLOAT - memset(vfp_plan, -1, sizeof(vfp_plan)); - memset(plan2, 0, sizeof(plan2)); - variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); -#endif - r = vtop->r & VT_VALMASK; - if (r == VT_CMP || (r & ~1) == VT_JMP) - gv(RC_INT); -#ifdef TCC_ARM_EABI - if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT - && type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) { - SValue tmp; - tmp=vtop[-nb_args]; - vtop[-nb_args]=vtop[-nb_args+1]; - vtop[-nb_args+1]=tmp; - --nb_args; - } - vpushi(0), nb_args++; - vtop->type.t = VT_LLONG; -#endif - ncrn = ncprn = argno = vfp_argno = args_size = 0; - /* Assign argument to registers and stack with alignment. - If, considering alignment constraints, enough registers of the correct type - (core or VFP) are free for the current argument, assign them to it, else - allocate on stack with correct alignment. Whenever a structure is allocated - in registers or on stack, it is always put on the stack at this stage. The - stack is divided in 3 zones. The zone are, from high addresses to low - addresses: structures to be loaded in core registers, structures to be - loaded in VFP registers, argument allocated to stack. SValue's representing - structures in the first zone are moved just after the SValue pointed by - before_stack. SValue's representing structures in the second zone are - moved just after the SValue pointer by before_creg. */ + ncrn = nsaa = 0; + *todo = 0; + plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans)); + memset(plan->clsplans, 0, sizeof(plan->clsplans)); for(i = nb_args; i-- ;) { - int j, assigned_vfpreg = 0; + int j, start_vfpreg = 0; size = type_size(&vtop[-i].type, &align); switch(vtop[-i].type.t & VT_BTYPE) { case VT_STRUCT: @@ -881,262 +909,266 @@ void gfunc_call(int nb_args) case VT_LDOUBLE: #ifdef TCC_ARM_HARDFLOAT if (!variadic) { - int hfa = 0; /* Homogeneous float aggregate */ + int is_hfa = 0; /* Homogeneous float aggregate */ if (is_float(vtop[-i].type.t) - || (hfa = is_float_hgen_aggr(&vtop[-i].type))) { - int end_reg; - - assigned_vfpreg = assign_fpreg(&avregs, align, size); - end_reg = assigned_vfpreg + (size - 1) / 4; - if (assigned_vfpreg >= 0) { - vfp_plan[vfp_argno++]=TREG_F0 + assigned_vfpreg/2; - if (hfa) { - /* if before_creg is not set, it means that no parameter has been - * allocated in core register. This implied that no argument has - * been allocated on stack neither because a VFP was available for - * this parameter. */ - if (before_creg) { - /* before_creg already exists and we just update it */ - vrote(&vtop[-i], &vtop[-i] - before_creg); - before_creg++; - } - for (j = assigned_vfpreg; j <= end_reg; j++) - vfp_todo|=(1<> 2); + if (start_vfpreg >= 0) { + pplan = (struct param_plan) {start_vfpreg, end_vfpreg, &vtop[-i]}; + if (is_hfa) + add_param_plan(plan, pplan, VFP_STRUCT_CLASS); + else + add_param_plan(plan, pplan, VFP_CLASS); continue; - } else { - if (!hfa) - vfp_argno++; - if (!before_stack) - before_stack = &vtop[-i-1]; + } else break; - } } } #endif ncrn = (ncrn + (align-1)/4) & -(align/4); size = (size + 3) & -4; - if (ncrn + size/4 <= 4 || (ncrn < 4 && assigned_vfpreg != -1)) { - if (before_stack) { - vrote(&vtop[-i], &vtop[-i] - before_stack); - before_stack++; - /* before_stack can only have been set because all VFP registers are - * assigned, so no need to care about before_creg if before_stack is - * set since no more argument will be allocated in a VFP register. */ - } else if (!before_creg) - before_creg = &vtop[-i]; + if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { + /* The parameter is allocated both in core register and on stack. As + * such, it can be of either class: it would either be the last of + * CORE_STRUCT_CLASS or the first of STACK_CLASS. */ for (j = ncrn; j < 4 && j < ncrn + size / 4; j++) - todo|=(1< 4) { - args_size = (ncrn - 4) * 4; - if (!before_stack) - before_stack = &vtop[-i-1]; - } + *todo|=(1< 4) + nsaa = (ncrn - 4) * 4; } else { ncrn = 4; - /* No need to set before_creg since it has already been set when - * assigning argument to core registers */ - if (!before_stack) - before_stack = &vtop[-i-1]; break; } continue; default: -#ifdef TCC_ARM_EABI - if (!i) { - break; - } -#endif if (ncrn < 4) { int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; if (is_long) { ncrn = (ncrn + 1) & -2; - if (ncrn == 4) { - argno++; + if (ncrn == 4) break; - } - } - plan[argno++][0]=ncrn++; - if (is_long) { - plan[argno-1][1]=ncrn++; } + pplan = (struct param_plan) {ncrn, ncrn, &vtop[-i]}; + ncrn++; + if (is_long) + pplan.end = ncrn++; + add_param_plan(plan, pplan, CORE_CLASS); continue; } - argno++; } -#ifdef TCC_ARM_EABI - if(args_size & (align-1)) { - vpushi(0); - vtop->type.t = VT_VOID; /* padding */ - vrott(i+2); - args_size += 4; - nb_args++; - argno++; - } -#endif - args_size += (size + 3) & -4; + nsaa = (nsaa + (align - 1)) & ~(align - 1); + pplan = (struct param_plan) {nsaa, nsaa + size, &vtop[-i]}; + add_param_plan(plan, pplan, STACK_CLASS); + nsaa += size; /* size already rounded up before */ } + return nsaa; +} + +#undef add_param_plan + +/* Copy parameters to their final destination (core reg, VFP reg or stack) for + function call. + + nb_args: number of parameters the function take + plan: the overall assignment plan for parameters + todo: a bitmap indicating what core reg will hold a parameter */ +static void copy_params(int nb_args, struct plan *plan, int todo) +{ + int size, align, r, i; + struct param_plan *pplan; + + /* Put argument on stack (structure are put on stack no matter how they are + * passed via register or the stack). */ #ifdef TCC_ARM_EABI - vtop--, nb_args--; -#endif - args_size = keep = 0; - for(i = 0;i < nb_args; i++) { - vrotb(keep+1); - if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { - size = type_size(&vtop->type, &align); - /* align to stack align size */ - size = (size + 3) & -4; - /* allocate the necessary size on stack */ - gadd_sp(-size); - /* generate structure store */ - r = get_reg(RC_INT); - o(0xE1A0000D|(intr(r)<<12)); - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - vtop--; - args_size += size; - } else if (is_float(vtop->type.t)) { -#ifdef TCC_ARM_HARDFLOAT - if (!variadic && --vfp_argno<16 && vfp_plan[vfp_argno]!=-1) { - plan2[keep++]=vfp_plan[vfp_argno]; - continue; - } + if ((pplan = plan->clsplans[STACK_CLASS]) && pplan->end & 7) + o(0xE24DD004); /* sub sp, sp, #4 */ #endif + /* Several constraints require parameters to be copied in a specific order: + - structures are copied to the stack before being loaded in a reg; + - floats loaded to an odd numbered VFP reg are first copied to the + preceding even numbered VFP reg and then moved to the next VFP reg. + + It is thus important that: + - structures assigned to core regs must be copied after parameters + assigned to the stack but before structures assigned to VFP regs because + a structure can lie partly in core registers and partly on the stack; + - parameters assigned to the stack and all structures be copied before + parameters assigned to a core reg since copying a parameter to the stack + require using a core reg; + - parameters assigned to VFP regs be copied before structures assigned to + VFP regs as the copy might use an even numbered VFP reg that already + holds part of a structure. */ + for(i = 0; i < NB_CLASSES; i++) { + for(pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) { + vpushv(pplan->sval); + pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */ + switch(i) { + case STACK_CLASS: + case CORE_STRUCT_CLASS: + case VFP_STRUCT_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) { + size = type_size(&pplan->sval->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + if (i == STACK_CLASS && pplan->prev) + size += pplan->start - pplan->prev->end; /* Add padding if any */ + /* allocate the necessary size on stack */ + gadd_sp(-size); + /* generate structure store */ + r = get_reg(RC_INT); + o(0xE1A0000D|(intr(r)<<12)); /* mov r, sp */ + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); /* memcpy to current sp */ + /* Homogeneous float aggregate are loaded to VFP registers + immediately since there is no way of loading data in multiple + non consecutive VFP registers as what is done for other + structures (see the use of todo). */ + if (i == VFP_STRUCT_CLASS) { + int first = pplan->start, nb = pplan->end - first + 1; + /* vpop.32 {pplan->start, ..., pplan->end} */ + o(0xECBD0A00|(first&1)<<22|(first>>1)<<12|nb); + /* No need to write the register used to a SValue since VFP regs + cannot be used for gcall_or_jmp */ + } + } else { + if (is_float(pplan->sval->type.t)) { #ifdef TCC_ARM_VFP - r=vfpr(gv(RC_FLOAT))<<12; - size=4; - if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) - { - size=8; - r|=0x101; /* fstms -> fstmd */ - } - o(0xED2D0A01+r); + r = vfpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else { + size = 8; + r |= 0x101; /* vpush.32 -> vpush.64 */ + } + o(0xED2D0A01 + r); /* vpush */ #else - r=fpr(gv(RC_FLOAT))<<12; - if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) - size = 4; - else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - size = 8; - else - size = LDOUBLE_SIZE; - - if (size == 12) - r|=0x400000; - else if(size == 8) - r|=0x8000; - - o(0xED2D0100|r|(size>>2)); + r = fpr(gv(RC_FLOAT)) << 12; + if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = LDOUBLE_SIZE; + + if (size == 12) + r |= 0x400000; + else if(size == 8) + r|=0x8000; + + o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */ #endif - vtop--; - args_size += size; - } else { - int s; - /* simple type (currently always same size) */ - /* XXX: implicit cast ? */ - size=4; - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - lexpand_nr(); - s=-1; - if(--argno<4 && plan[argno][1]!=-1) - s=plan[argno][1]; - argno++; - size = 8; - if(s==-1) { - r = gv(RC_INT); - o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ - vtop--; - } else { - size=0; - plan2[keep]=s; - keep++; - vswap(); - } - } - s=-1; - if(--argno<4 && plan[argno][0]!=-1) - s=plan[argno][0]; -#ifdef TCC_ARM_EABI - if(vtop->type.t == VT_VOID) { - if(s == -1) - o(0xE24DD004); /* sub sp,sp,#4 */ - vtop--; - } else -#endif - if(s == -1) { - r = gv(RC_INT); - o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ - vtop--; - } else { - size=0; - plan2[keep]=s; - keep++; + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + size=4; + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand_nr(); + size = 8; + r = gv(RC_INT); + o(0xE52D0004|(intr(r)<<12)); /* push r */ + vtop--; + } + r = gv(RC_INT); + o(0xE52D0004|(intr(r)<<12)); /* push r */ + } + if (i == STACK_CLASS && pplan->prev) + gadd_sp(pplan->prev->end - pplan->start); /* Add padding if any */ + } + break; + + case VFP_CLASS: + gv(regmask(TREG_F0 + (pplan->start >> 1))); + if (pplan->start & 1) { /* Must be in upper part of double register */ + o(0xEEF00A40|((pplan->start>>1)<<12)|(pplan->start>>1)); /* vmov.f32 s(n+1), sn */ + vtop->r = VT_CONST; /* avoid being saved on stack by gv for next float */ + } + break; + + case CORE_CLASS: + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { + lexpand_nr(); + gv(regmask(pplan->end)); + pplan->sval->r2 = vtop->r; + vtop--; + } + gv(regmask(pplan->start)); + /* Mark register as used so that gcall_or_jmp use another one + (regs >=4 are free as never used to pass parameters) */ + pplan->sval->r = vtop->r; + break; } - args_size += size; - } - } - for(i = 0; i < keep; i++) { - vrotb(keep); - gv(regmask(plan2[i])); -#ifdef TCC_ARM_HARDFLOAT - /* arg is in s(2d+1): plan2[i] alignment occured (ex f,d,f) */ - if (i < keep - 1 && is_float(vtop->type.t) && (plan2[i] <= plan2[i + 1])) { - o(0xEEF00A40|(vfpr(plan2[i])<<12)|vfpr(plan2[i])); + vtop--; } -#endif } -save_regs(keep); /* save used temporary registers */ - keep++; - if(vfp_todo) { - int nb_fregs=0; - - for(i=0;i<16;i++) - if(vfp_todo&(1<>1)<<12|nb_fregs); - vpushi(0); - /* There might be 2 floats in a double VFP reg but that doesn't seem - to matter */ - if (!(i%2)) - vtop->r=TREG_F0+i/2; - keep++; - nb_fregs++; - } - if (nb_fregs) { - gadd_sp(nb_fregs*4); - args_size-=nb_fregs*4; + + /* Manually free remaining registers since next parameters are loaded + * manually, without the help of gv(int). */ + save_regs(nb_args); + + if(todo) { + o(0xE8BD0000|todo); /* pop {todo} */ + for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { + pplan->sval->r = pplan->start; + if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) + pplan->sval->r2 = pplan->end; } } - if(ncrn) { - int nb_regs=0; - if (ncrn>4) - ncrn=4; - todo&=((1<r=i; - keep++; - nb_regs++; - } - } - args_size-=nb_regs*4; +} + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +void gfunc_call(int nb_args) +{ + int align, r, args_size; + int variadic; + int todo; + struct plan plan; + + variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); + /* 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 + the code generator. */ + r = vtop->r & VT_VALMASK; + if (r == VT_CMP || (r & ~1) == VT_JMP) + gv(RC_INT); +#ifdef TCC_ARM_EABI + /* return type is a struct so caller of gfunc_call (unary(void) in tccgen.c) + assumed it had to be passed by a pointer. Since it's less than 4 bytes, we + can actually pass it directly in a register. */ + if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT + && type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) { + SValue tmp; + tmp=vtop[-nb_args]; + vtop[-nb_args]=vtop[-nb_args+1]; + vtop[-nb_args+1]=tmp; + --nb_args; } - vrotb(keep); +#endif + + args_size = assign_regs(nb_args, variadic, &plan, &todo); + copy_params(nb_args, &plan, todo); + tcc_free(plan.pplans); + + /* Move fct SValue on top as required by gcall_or_jmp */ + vrotb(nb_args + 1); gcall_or_jmp(0); if (args_size) - gadd_sp(args_size); + gadd_sp(args_size); /* pop all parameters passed on the stack */ #ifdef TCC_ARM_EABI if((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT - && type_size(&vtop->type.ref->type, &align) <= 4) - { - store(REG_IRET,vtop-keep); - ++keep; + && type_size(&vtop->type.ref->type, &align) <= 4) { + store(REG_IRET,vtop-nb_args-1); + nb_args++; } #ifdef TCC_ARM_VFP #ifdef TCC_ARM_HARDFLOAT @@ -1145,16 +1177,16 @@ save_regs(keep); /* save used temporary registers */ else if(is_float(vtop->type.ref->type.t)) { #endif if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { - o(0xEE000A10); /* fmsr s0,r0 */ + o(0xEE000A10); /*vmov s0, r0 */ } else { - o(0xEE000B10); /* fmdlr d0,r0 */ - o(0xEE201B10); /* fmdhr d0,r1 */ + o(0xEE000B10); /* vmov.32 d0[0], r0 */ + o(0xEE201B10); /* vmov.32 d0[1], r1 */ } } #endif #endif - vtop-=keep; - leaffunc = 0; + 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 */ } /* generate function prolog of type 't' */ @@ -1182,8 +1214,8 @@ void gfunc_prolog(CType *func_type) size = type_size(&sym2->type, &align); #ifdef TCC_ARM_HARDFLOAT if (!variadic && (is_float(sym2->type.t) - || is_float_hgen_aggr(&sym2->type))) { - int tmpnf = assign_fpreg(&avregs, align, size) + 1; + || is_hgen_float_aggr(&sym2->type))) { + int tmpnf = assign_vfpreg(&avregs, align, size) + 1; nf = (tmpnf > nf) ? tmpnf : nf; } else #endif @@ -1226,8 +1258,8 @@ void gfunc_prolog(CType *func_type) align = (align + 3) & ~3; #ifdef TCC_ARM_HARDFLOAT if (!variadic && (is_float(sym->type.t) - || is_float_hgen_aggr(&sym->type))) { - int fpn = assign_fpreg(&avregs, align, size << 2); + || is_hgen_float_aggr(&sym->type))) { + int fpn = assign_vfpreg(&avregs, align, size << 2); if (fpn >= 0) { addr = fpn * 4; } else -- cgit v1.3.1 From 0c40bc8982adfba427933260c6571bd29f50a649 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Sun, 17 Nov 2013 18:26:56 +0800 Subject: Correctly align and reclaim stack at function call * Correctly align stack in case of structure split between core registers and stack * Correctly reclaim stack space after function call in the case where the stack needed padding to be aligned at function call. --- arm-gen.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 7f870a2..ab7b0be 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -985,12 +985,6 @@ static void copy_params(int nb_args, struct plan *plan, int todo) int size, align, r, i; struct param_plan *pplan; - /* Put argument on stack (structure are put on stack no matter how they are - * passed via register or the stack). */ -#ifdef TCC_ARM_EABI - if ((pplan = plan->clsplans[STACK_CLASS]) && pplan->end & 7) - o(0xE24DD004); /* sub sp, sp, #4 */ -#endif /* Several constraints require parameters to be copied in a specific order: - structures are copied to the stack before being loaded in a reg; - floats loaded to an odd numbered VFP reg are first copied to the @@ -1156,6 +1150,14 @@ void gfunc_call(int nb_args) #endif args_size = assign_regs(nb_args, variadic, &plan, &todo); + +#ifdef TCC_ARM_EABI + if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ + args_size = (args_size + 7) & ~7; + o(0xE24DD004); /* sub sp, sp, #4 */ + } +#endif + copy_params(nb_args, &plan, todo); tcc_free(plan.pplans); -- cgit v1.3.1 From 41ce391c86df135609af33658414d4d452c5beb3 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Thu, 21 Nov 2013 21:09:44 +0800 Subject: Fix register corruption at function call on ARM Prior to this commit, params could use some registers that do not appear in the value stack. Therefore when generating function call, one of such register could be reused, leading to wrong parameter content. This happens when a structure is passed via core register, as only the first register would appear in the value stack. --- arm-gen.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index ab7b0be..488de76 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -979,10 +979,12 @@ static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo) nb_args: number of parameters the function take plan: the overall assignment plan for parameters - todo: a bitmap indicating what core reg will hold a parameter */ -static void copy_params(int nb_args, struct plan *plan, int todo) + todo: a bitmap indicating what core reg will hold a parameter + + Returns the number of SValue added by this function on the value stack */ +static int copy_params(int nb_args, struct plan *plan, int todo) { - int size, align, r, i; + int size, align, r, i, nb_extra_sval = 0; struct param_plan *pplan; /* Several constraints require parameters to be copied in a specific order: @@ -1111,11 +1113,19 @@ static void copy_params(int nb_args, struct plan *plan, int todo) if(todo) { o(0xE8BD0000|todo); /* pop {todo} */ for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { + int r; pplan->sval->r = pplan->start; - if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) - pplan->sval->r2 = pplan->end; + /* TODO: why adding fake param */ + for (r = pplan->start + 1; r <= pplan->end; r++) { + if (todo & (1 << r)) { + nb_extra_sval++; + vpushi(0); + vtop->r = r; + } + } } } + return nb_extra_sval; } /* Generate function call. The function address is pushed first, then @@ -1158,7 +1168,7 @@ void gfunc_call(int nb_args) } #endif - copy_params(nb_args, &plan, todo); + nb_args += copy_params(nb_args, &plan, todo); tcc_free(plan.pplans); /* Move fct SValue on top as required by gcall_or_jmp */ -- cgit v1.3.1 From c3e7c725b540c837e25093e2f488a4667f4d4ea0 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Thu, 21 Nov 2013 21:14:25 +0800 Subject: Fix counting of VFP regs in ARM's gfunc_prolog Fix in gfunc_prolog for ARM the counting of the highest numbered VFP float register used for parameter passing, rounded to 2. It can be computed from the range of VFP float register with the highest range start and adding the number of VFP float register occupied. This ensure that parameter of type struct that spans over more than 2 float registers are correctly taken into account. --- arm-gen.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 488de76..0fa2eb0 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1227,7 +1227,8 @@ void gfunc_prolog(CType *func_type) #ifdef TCC_ARM_HARDFLOAT if (!variadic && (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { - int tmpnf = assign_vfpreg(&avregs, align, size) + 1; + int tmpnf = assign_vfpreg(&avregs, align, size); + tmpnf += (size + 3) / 4; nf = (tmpnf > nf) ? tmpnf : nf; } else #endif -- cgit v1.3.1 From 63a84713eee3585f8e000a912f9c1799b13f09bd Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Fri, 22 Nov 2013 00:13:05 +0800 Subject: Correctly identify homogeneous float aggregate First related symbol of a structure justs indicate its size. This first member is the second related symbol. --- arm-gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 0fa2eb0..dd79c99 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -776,7 +776,7 @@ static int is_hgen_float_aggr(CType *type) struct Sym *ref; int btype, nb_fields = 0; - ref = type->ref; + ref = type->ref->next; btype = ref->type.t & VT_BTYPE; if (btype == VT_FLOAT || btype == VT_DOUBLE) { for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++); -- cgit v1.3.1 From d9d60a1ebd9ae24aafd2e042d5ad38f515583b7e Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Fri, 22 Nov 2013 00:15:34 +0800 Subject: Remove code in arm-gen.c for struct packing in reg Struct packing in register is now handled since commit 2bbfaf43 by tccgen.c proper. --- arm-gen.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index dd79c99..92b080d 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1133,7 +1133,7 @@ static int copy_params(int nb_args, struct plan *plan, int todo) parameters and the function address. */ void gfunc_call(int nb_args) { - int align, r, args_size; + int r, args_size; int variadic; int todo; struct plan plan; @@ -1145,19 +1145,6 @@ void gfunc_call(int nb_args) r = vtop->r & VT_VALMASK; if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); -#ifdef TCC_ARM_EABI - /* return type is a struct so caller of gfunc_call (unary(void) in tccgen.c) - assumed it had to be passed by a pointer. Since it's less than 4 bytes, we - can actually pass it directly in a register. */ - if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT - && type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) { - SValue tmp; - tmp=vtop[-nb_args]; - vtop[-nb_args]=vtop[-nb_args+1]; - vtop[-nb_args+1]=tmp; - --nb_args; - } -#endif args_size = assign_regs(nb_args, variadic, &plan, &todo); @@ -1177,16 +1164,11 @@ void gfunc_call(int nb_args) if (args_size) gadd_sp(args_size); /* pop all parameters passed on the stack */ #ifdef TCC_ARM_EABI - if((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT - && type_size(&vtop->type.ref->type, &align) <= 4) { - store(REG_IRET,vtop-nb_args-1); - nb_args++; - } #ifdef TCC_ARM_VFP #ifdef TCC_ARM_HARDFLOAT - else if(variadic && is_float(vtop->type.ref->type.t)) { + if(variadic && is_float(vtop->type.ref->type.t)) { #else - else if(is_float(vtop->type.ref->type.t)) { + rf(is_float(vtop->type.ref->type.t)) { #endif if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { o(0xEE000A10); /*vmov s0, r0 */ -- cgit v1.3.1 From dcec8673f21da86ae3dcf1ca3e9498127715b795 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Fri, 22 Nov 2013 09:27:15 +0800 Subject: Add support for struct > 4B returned via registers On ARM with hardfloat calling convention, structure containing 4 fields or less of the same float type are returned via float registers. This means that a structure can be returned in up to 4 double registers in a structure is composed of 4 doubles. This commit adds support for return of structures in several registers. --- arm-gen.c | 48 ++++++++++++++++++++++++++++++------------------ c67-gen.c | 5 +++-- i386-gen.c | 9 +++++---- tccgen.c | 46 +++++++++++++++++++++++++++++----------------- x86_64-gen.c | 18 ++++++++++-------- 5 files changed, 77 insertions(+), 49 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 92b080d..6d0acd8 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -746,24 +746,6 @@ static void gcall_or_jmp(int is_jmp) } } -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { -#ifdef TCC_ARM_EABI - int size, align; - size = type_size(vt, &align); - if (size > 4) { - return 1; - } else { - *ret_align = 4; - ret->ref = NULL; - ret->t = VT_INT; - return 0; - } -#else - return 1; -#endif -} - #ifdef TCC_ARM_HARDFLOAT /* Return whether a structure is an homogeneous float aggregate or not. The answer is true if all the elements of the structure are of the same @@ -831,6 +813,33 @@ int assign_vfpreg(struct avail_regs *avregs, int align, int size) } #endif +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ +ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { +#ifdef TCC_ARM_EABI + int size, align; + size = type_size(vt, &align); +#ifdef TCC_ARM_HARDFLOAT + if (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 { + *ret_align = 4; + ret->ref = NULL; + ret->t = VT_INT; + return 1; + } +#else + return 0; +#endif +} + /* Parameters are classified according to how they are copied to their final destination for the function call. Because the copying is performed class after class according to the order in the union below, it is important that @@ -1198,6 +1207,9 @@ void gfunc_prolog(CType *func_type) n = nf = 0; variadic = (func_type->ref->c == FUNC_ELLIPSIS); if((func_vt.t & VT_BTYPE) == VT_STRUCT +#ifdef TCC_ARM_HARDFLOAT + && (variadic || !is_hgen_float_aggr(&func_vt)) +#endif && type_size(&func_vt,&align) > 4) { n++; diff --git a/c67-gen.c b/c67-gen.c index 0d5e33f..1189dbb 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -1879,10 +1879,11 @@ static void gcall_or_jmp(int is_jmp) } } -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { *ret_align = 1; // Never have to re-align return values for x86-64 - return 1; + return 0; } /* generate function call with address in (vtop->t, vtop->c) and free function diff --git a/i386-gen.c b/i386-gen.c index 0a6d4d3..eaab2b7 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -374,7 +374,8 @@ static void gcall_or_jmp(int is_jmp) static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { #ifdef TCC_TARGET_PE @@ -383,11 +384,11 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) *ret_align = 1; // Never have to re-align return values for x86 size = type_size(vt, &align); if (size > 8) { - return 1; + return 0; } else if (size > 4) { ret->ref = NULL; ret->t = VT_LLONG; - return 0; + return 1; } else { ret->ref = NULL; ret->t = VT_INT; @@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) } #else *ret_align = 1; // Never have to re-align return values for x86 - return 1; + return 0; #endif } diff --git a/tccgen.c b/tccgen.c index bab4f7c..500e99e 100644 --- a/tccgen.c +++ b/tccgen.c @@ -3927,7 +3927,7 @@ ST_FUNC void unary(void) } else if (tok == '(') { SValue ret; Sym *sa; - int nb_args, sret; + int nb_args, sret, ret_align; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -3951,9 +3951,8 @@ ST_FUNC void unary(void) ret.r2 = VT_CONST; /* compute first implicit argument if a structure is returned */ if ((s->type.t & VT_BTYPE) == VT_STRUCT) { - int ret_align; sret = gfunc_sret(&s->type, &ret.type, &ret_align); - if (sret) { + if (!sret) { /* get some space for the returned structure */ size = type_size(&s->type, &align); loc = (loc - size) & -align; @@ -3966,11 +3965,11 @@ ST_FUNC void unary(void) nb_args++; } } else { - sret = 0; + sret = 1; ret.type = s->type; } - if (!sret) { + if (sret) { /* return in register */ if (is_float(ret.type.t)) { ret.r = reg_fret(ret.type.t); @@ -4010,18 +4009,23 @@ ST_FUNC void unary(void) vtop -= (nb_args + 1); } /* return value */ - vsetc(&ret.type, ret.r, &ret.c); - vtop->r2 = ret.r2; + for (r = ret.r + sret + !sret; r-- > ret.r;) { + vsetc(&ret.type, r, &ret.c); + vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */ + } /* handle packed struct return */ - if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) { - int addr; + if (((s->type.t & VT_BTYPE) == VT_STRUCT) && sret) { + int addr, offset; + size = type_size(&s->type, &align); loc = (loc - size) & -align; addr = loc; - vset(&ret.type, VT_LOCAL | VT_LVAL, addr); - vswap(); - vstore(); - vtop--; + for(offset = 0; offset < size; offset += ret_align) { + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); + vswap(); + vstore(); + vtop--; + } vset(&s->type, VT_LOCAL | VT_LVAL, addr); } } else { @@ -4593,7 +4597,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; int ret_align; - if (gfunc_sret(&func_vt, &ret_type, &ret_align)) { + if (!gfunc_sret(&func_vt, &ret_type, &ret_align)) { /* if returning structure, must copy it to implicit first pointer arg location */ type = func_vt; @@ -4605,7 +4609,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, vstore(); } else { /* returning structure packed into registers */ - int size, addr, align; + int r, size, addr, offset, align; size = type_size(&func_vt,&align); if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) && (align & (ret_align-1))) { @@ -4619,9 +4623,17 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, } vtop->type = ret_type; if (is_float(ret_type.t)) - gv(rc_fret(ret_type.t)); + r = rc_fret(ret_type.t); else - gv(RC_IRET); + r = RC_IRET; + /* We assume that when a structure is returned in multiple + registers, their classes are consecutive values of the + suite s(n) = 2^n */ + for (offset = 0; offset < size; offset += ret_align, r<<=1) { + gv(r); + vtop->c.i += ret_align; + vtop->r = VT_LOCAL | VT_LVAL; + } } } else if (is_float(func_vt.t)) { gv(rc_fret(func_vt.t)); diff --git a/x86_64-gen.c b/x86_64-gen.c index 690236e..0962056 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -656,7 +656,8 @@ void gen_offs_sp(int b, int r, int d) } } -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { int size, align; @@ -664,19 +665,19 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) size = type_size(vt, &align); ret->ref = NULL; if (size > 8) { - return 1; + return 0; } else if (size > 4) { ret->t = VT_LLONG; - return 0; + return 1; } else if (size > 2) { ret->t = VT_INT; - return 0; + return 1; } else if (size > 1) { ret->t = VT_SHORT; - return 0; + return 1; } else { ret->t = VT_BYTE; - return 0; + return 1; } } @@ -1056,11 +1057,12 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) { } } -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ +/* Return the number of registers needed to return the struct, or 0 if + returning via struct pointer. */ int gfunc_sret(CType *vt, CType *ret, int *ret_align) { int size, align, reg_count; *ret_align = 1; // Never have to re-align return values for x86-64 - return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) == x86_64_mode_memory); + return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); } #define REGN 6 -- cgit v1.3.1 From 48fc7466527d8b3409f313421e8aed559563bfdb Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Mon, 25 Nov 2013 10:51:39 +0800 Subject: Fix structure passing in ARM calling convention Fix the address on stack where a structure is copied when it is a parameter of a function call. This address must be computed from the stack pointer and a possible padding offset. --- arm-gen.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 6d0acd8..8b07708 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1020,19 +1020,22 @@ static int copy_params(int nb_args, struct plan *plan, int todo) case CORE_STRUCT_CLASS: case VFP_STRUCT_CLASS: if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) { + int padding = 0; size = type_size(&pplan->sval->type, &align); /* align to stack align size */ size = (size + 3) & ~3; if (i == STACK_CLASS && pplan->prev) - size += pplan->start - pplan->prev->end; /* Add padding if any */ + padding = pplan->start - pplan->prev->end; + size += padding; /* Add padding if any */ /* allocate the necessary size on stack */ gadd_sp(-size); /* generate structure store */ r = get_reg(RC_INT); - o(0xE1A0000D|(intr(r)<<12)); /* mov r, sp */ + o(0xE28D0000|(intr(r)<<12)|padding); /* add r, sp, padding */ vset(&vtop->type, r | VT_LVAL, 0); vswap(); - vstore(); /* memcpy to current sp */ + vstore(); /* memcpy to current sp + potential padding */ + /* Homogeneous float aggregate are loaded to VFP registers immediately since there is no way of loading data in multiple non consecutive VFP registers as what is done for other -- cgit v1.3.1 From 82b257c29cc24e561fd29a0374fcda73feb5d760 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Mon, 25 Nov 2013 11:00:51 +0800 Subject: Add comment to explain the code added by 41ce391c Add a comment in arm-gen.c to explain how commit 41ce391c86df135609af33658414d4d452c5beb3 solves the register corruption when passing a structure in a function call. --- arm-gen.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 8b07708..c22e98e 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1127,7 +1127,10 @@ static int copy_params(int nb_args, struct plan *plan, int todo) for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { int r; pplan->sval->r = pplan->start; - /* TODO: why adding fake param */ + /* An SValue can only pin 2 registers at best (r and r2) but a structure + can occupy more than 2 registers. Thus, we need to push on the value + stack some fake parameter to have on SValue for each registers used + by a structure (r2 is not used). */ for (r = pplan->start + 1; r <= pplan->end; r++) { if (todo & (1 << r)) { nb_extra_sval++; -- cgit v1.3.1 From 389c25c4b9006b16138094b110e4c50b78432905 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Wed, 11 Dec 2013 09:01:28 +0800 Subject: Support special calling convention for runtime ABI Add infrastructure to support special calling convention for runtime ABI function no matter what is the current calling convention. This involve 2 changes: - behave as per base standard in gfunc_call - move result back in VFP register in gen_cvt_itof1 --- arm-gen.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index c22e98e..0aa07b1 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -746,7 +746,6 @@ static void gcall_or_jmp(int is_jmp) } } -#ifdef TCC_ARM_HARDFLOAT /* Return whether a structure is an homogeneous float aggregate or not. The answer is true if all the elements of the structure are of the same primitive float type and there is less than 4 elements. @@ -811,7 +810,32 @@ int assign_vfpreg(struct avail_regs *avregs, int align, int size) avregs->first_free_reg = -1; return -1; } + +/* Returns whether all params need to be passed in core registers or not. + This is the case for function part of the runtime ABI. */ +int floats_in_core_regs(SValue *sval) +{ + if (!sval->sym) + return 0; + + switch (sval->sym->v) { + case TOK___floatundisf: + case TOK___floatundidf: + case TOK___fixunssfdi: + case TOK___fixunsdfdi: +#ifndef TCC_ARM_VFP + case TOK___fixunsxfdi: #endif + case TOK___floatdisf: + case TOK___floatdidf: + case TOK___fixsfdi: + case TOK___fixdfdi: + return 1; + + default: + return 0; + } +} /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ @@ -885,7 +909,7 @@ struct plan { definition of union reg_class). nb_args: number of parameters of the function for which a call is generated - variadic: whether the function is a variadic function or not + corefloat: whether to pass float via core registers or not plan: the structure where the overall assignment is recorded todo: a bitmap that record which core registers hold a parameter @@ -894,15 +918,13 @@ struct plan { Note: this function allocated an array in plan->pplans with tcc_malloc. It is the responsability of the caller to free this array once used (ie not before copy_params). */ -static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo) +static int assign_regs(int nb_args, int corefloat, struct plan *plan, int *todo) { int i, size, align; int ncrn /* next core register number */, nsaa /* next stacked argument address*/; int plan_nb = 0; struct param_plan pplan; -#ifdef TCC_ARM_HARDFLOAT struct avail_regs avregs = AVAIL_REGS_INITIALIZER; -#endif ncrn = nsaa = 0; *todo = 0; @@ -916,8 +938,7 @@ static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo) case VT_FLOAT: case VT_DOUBLE: case VT_LDOUBLE: -#ifdef TCC_ARM_HARDFLOAT - if (!variadic) { + if (!corefloat) { int is_hfa = 0; /* Homogeneous float aggregate */ if (is_float(vtop[-i].type.t) @@ -937,7 +958,6 @@ static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo) break; } } -#endif ncrn = (ncrn + (align-1)/4) & -(align/4); size = (size + 3) & -4; if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { @@ -1149,11 +1169,14 @@ static int copy_params(int nb_args, struct plan *plan, int todo) void gfunc_call(int nb_args) { int r, args_size; - int variadic; + int variadic, corefloat = 1; 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]); +#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 the code generator. */ @@ -1161,7 +1184,7 @@ void gfunc_call(int nb_args) if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); - args_size = assign_regs(nb_args, variadic, &plan, &todo); + args_size = assign_regs(nb_args, corefloat, &plan, &todo); #ifdef TCC_ARM_EABI if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ @@ -1180,11 +1203,7 @@ void gfunc_call(int nb_args) gadd_sp(args_size); /* pop all parameters passed on the stack */ #ifdef TCC_ARM_EABI #ifdef TCC_ARM_VFP -#ifdef TCC_ARM_HARDFLOAT - if(variadic && is_float(vtop->type.ref->type.t)) { -#else - rf(is_float(vtop->type.ref->type.t)) { -#endif + if(corefloat && is_float(vtop->type.ref->type.t)) { if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { o(0xEE000A10); /*vmov s0, r0 */ } else { -- cgit v1.3.1 From eda2c756edc4dca004ba217d5bf361235dd9de1f Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 31 Dec 2013 23:51:20 +0800 Subject: Move logic for if (int value) to tccgen.c Move the logic to do a test of an integer value (ex if (0)) out of arch-specific code to tccgen.c to avoid code duplication. This also fixes test of long long value which was only testing the bottom half of such values on 32 bits architectures. --- arm-gen.c | 25 +------------------------ c67-gen.c | 33 +-------------------------------- i386-gen.c | 19 +------------------ il-gen.c | 15 +-------------- tccgen.c | 44 ++++++++++++++++++++++++++++++++------------ x86_64-gen.c | 19 +------------------ 6 files changed, 37 insertions(+), 118 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 0aa07b1..eecb7d2 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1390,7 +1390,7 @@ int gtst(int inv, int t) op|=encbranch(r,t,1); o(op); t=r; - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ if ((v & 1) == inv) { if(!vtop->c.i) vtop->c.i=t; @@ -1412,29 +1412,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t)) { - r=gv(RC_FLOAT); -#ifdef TCC_ARM_VFP - o(0xEEB50A40|(vfpr(r)<<12)|T2CPR(vtop->type.t)); /* fcmpzX */ - o(0xEEF1FA10); /* fmstat */ -#else - o(0xEE90F118|(fpr(r)<<16)); -#endif - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - o(0xE3300000|(intr(v)<<16)); - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } } vtop--; return t; diff --git a/c67-gen.c b/c67-gen.c index 1189dbb..df4b5d3 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -2103,7 +2103,7 @@ int gtst(int inv, int t) C67_NOP(5); t = ind1; //return where we need to patch - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -2129,37 +2129,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t)) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - // I think we need to get the value on the stack - // into a register, test it, and generate a branch - // return the address of the branch, so it can be - // later patched - - v = gv(RC_INT); // get value into a reg - ind1 = ind; - C67_MVKL(C67_A0, t); //r=reg to load, constant - C67_MVKH(C67_A0, t); //r=reg to load, constant - - if (v != TREG_EAX && // check if not already in a conditional test reg - v != TREG_EDX && v != TREG_ST0 && v != C67_B2) { - C67_MV(v, C67_B2); - v = C67_B2; - } - - C67_IREG_B_REG(inv, v, C67_A0); // [!R] B.S2x A0 - C67_NOP(5); - t = ind1; //return where we need to patch - ind1 = ind; - } } vtop--; return t; diff --git a/i386-gen.c b/i386-gen.c index ebc0d14..2cb31ff 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -682,7 +682,7 @@ ST_FUNC int gtst(int inv, int t) /* fast case : can jump directly since flags are set */ g(0x0f); t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -695,23 +695,6 @@ ST_FUNC int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t) || - (vtop->type.t & VT_BTYPE) == VT_LLONG) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - o(0x85); - o(0xc0 + v * 9); - g(0x0f); - t = psym(0x85 ^ inv, t); - } } vtop--; return t; diff --git a/il-gen.c b/il-gen.c index 170f436..33f9f36 100644 --- a/il-gen.c +++ b/il-gen.c @@ -515,7 +515,7 @@ int gtst(int inv, int t) break; } t = out_opj(c, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -528,19 +528,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->t)) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - t = out_opj(IL_OP_BRTRUE - inv, t); - } } vtop--; return t; diff --git a/tccgen.c b/tccgen.c index e8f7f82..55b03e6 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1095,6 +1095,26 @@ static void gv_dup(void) } } +/* Generate value test + * + * Generate a test for any value (jump, comparison and integers) */ +int gvtst(int inv, int t) +{ + int v = vtop->r & VT_VALMASK; + if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + vtop--; + return t; + } + return gtst(inv, t); +} + #ifndef TCC_TARGET_X86_64 /* generate CPU independent (unsigned) long long operations */ static void gen_opl(int op) @@ -1293,13 +1313,13 @@ static void gen_opl(int op) b = 0; gen_op(op1); if (op1 != TOK_NE) { - a = gtst(1, 0); + a = gvtst(1, 0); } if (op != TOK_EQ) { /* generate non equal test */ /* XXX: NOT PORTABLE yet */ if (a == 0) { - b = gtst(0, 0); + b = gvtst(0, 0); } else { #if defined(TCC_TARGET_I386) b = psym(0x850f, 0); @@ -1324,7 +1344,7 @@ static void gen_opl(int op) else if (op1 == TOK_GE) op1 = TOK_UGE; gen_op(op1); - a = gtst(1, a); + a = gvtst(1, a); gsym(b); vseti(VT_JMPI, a); break; @@ -3665,7 +3685,7 @@ ST_FUNC void unary(void) vtop->c.i = vtop->c.i ^ 1; else { save_regs(1); - vseti(VT_JMP, gtst(1, 0)); + vseti(VT_JMP, gvtst(1, 0)); } break; case '~': @@ -4182,7 +4202,7 @@ static void expr_land(void) t = 0; save_regs(1); for(;;) { - t = gtst(1, t); + t = gvtst(1, t); if (tok != TOK_LAND) { vseti(VT_JMPI, t); break; @@ -4202,7 +4222,7 @@ static void expr_lor(void) t = 0; save_regs(1); for(;;) { - t = gtst(0, t); + t = gvtst(0, t); if (tok != TOK_LOR) { vseti(VT_JMP, t); break; @@ -4264,9 +4284,9 @@ static void expr_cond(void) } if (tok == ':' && gnu_ext) { gv_dup(); - tt = gtst(1, 0); + tt = gvtst(1, 0); } else { - tt = gtst(1, 0); + tt = gvtst(1, 0); gexpr(); } type1 = vtop->type; @@ -4512,7 +4532,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gexpr(); skip(')'); - a = gtst(1, 0); + a = gvtst(1, 0); block(bsym, csym, case_sym, def_sym, case_reg, 0); c = tok; if (c == TOK_ELSE) { @@ -4529,7 +4549,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gexpr(); skip(')'); - a = gtst(1, 0); + a = gvtst(1, 0); b = 0; block(&a, &b, case_sym, def_sym, case_reg, 0); gjmp_addr(d); @@ -4707,7 +4727,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, b = 0; if (tok != ';') { gexpr(); - a = gtst(1, 0); + a = gvtst(1, 0); } skip(';'); if (tok != ')') { @@ -4736,7 +4756,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gsym(b); gexpr(); - c = gtst(0, 0); + c = gvtst(0, 0); gsym_addr(c, d); skip(')'); gsym(a); diff --git a/x86_64-gen.c b/x86_64-gen.c index 1550c07..fe028d9 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -1582,7 +1582,7 @@ int gtst(int inv, int t) } g(0x0f); t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -1595,23 +1595,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t) || - (vtop->type.t & VT_BTYPE) == VT_LLONG) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - orex(0,v,v,0x85); - o(0xc0 + REG_VALUE(v) * 9); - g(0x0f); - t = psym(0x85 ^ inv, t); - } } vtop--; return t; -- cgit v1.3.1 From 8efaa711904b897f9a4821656ac10f980c5ae9fe Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Mon, 6 Jan 2014 22:27:39 +0800 Subject: Fix struct ret in variadic fct with ARM hardfloat The procedure calling standard for ARM architecture mandate the use of the base standard for variadic function. Therefore, hgen float aggregate must be returned via stack when greater than 4 bytes and via core registers else in case of variadic function. This patch improve gfunc_sret() to take into account whether the function is variadic or not and make use of gfunc_sret() return value to determine whether to pass a structure via stack in gfunc_prolog(). It also take advantage of knowing if a function is variadic or not move float result value from VFP register to core register in gfunc_epilog(). --- arm-gen.c | 34 ++++++++++++++++++---------------- c67-gen.c | 3 ++- i386-gen.c | 3 ++- il-gen.c | 1 + tcc.h | 3 ++- tccgen.c | 11 ++++++++--- x86_64-gen.c | 3 ++- 7 files changed, 35 insertions(+), 23 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index eecb7d2..9e6c638 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -839,12 +839,12 @@ int floats_in_core_regs(SValue *sval) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { +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 (is_float(vt->t) || is_hgen_float_aggr(vt)) { + if (!variadic && (is_float(vt->t) || is_hgen_float_aggr(vt))) { *ret_align = 8; ret->ref = NULL; ret->t = VT_DOUBLE; @@ -1221,21 +1221,19 @@ void gfunc_call(int nb_args) void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n,nf,size,align, variadic, struct_ret = 0; + int n, nf, size, align, struct_ret = 0; #ifdef TCC_ARM_HARDFLOAT struct avail_regs avregs = AVAIL_REGS_INITIALIZER; #endif + CType ret_type; sym = func_type->ref; func_vt = sym->type; + func_var = (func_type->ref->c == FUNC_ELLIPSIS); n = nf = 0; - variadic = (func_type->ref->c == FUNC_ELLIPSIS); - if((func_vt.t & VT_BTYPE) == VT_STRUCT -#ifdef TCC_ARM_HARDFLOAT - && (variadic || !is_hgen_float_aggr(&func_vt)) -#endif - && type_size(&func_vt,&align) > 4) + if ((func_vt.t & VT_BTYPE) == VT_STRUCT && + !gfunc_sret(&func_vt, func_var, &ret_type, &align)) { n++; struct_ret = 1; @@ -1244,7 +1242,7 @@ void gfunc_prolog(CType *func_type) for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) { size = type_size(&sym2->type, &align); #ifdef TCC_ARM_HARDFLOAT - if (!variadic && (is_float(sym2->type.t) + if (!func_var && (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { int tmpnf = assign_vfpreg(&avregs, align, size); tmpnf += (size + 3) / 4; @@ -1255,9 +1253,9 @@ void gfunc_prolog(CType *func_type) n += (size + 3) / 4; } o(0xE1A0C00D); /* mov ip,sp */ - if(variadic) + if (func_var) n=4; - if(n) { + if (n) { if(n>4) n=4; #ifdef TCC_ARM_EABI @@ -1289,7 +1287,7 @@ void gfunc_prolog(CType *func_type) size = (size + 3) >> 2; align = (align + 3) & ~3; #ifdef TCC_ARM_HARDFLOAT - if (!variadic && (is_float(sym->type.t) + 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) { @@ -1329,10 +1327,14 @@ void gfunc_epilog(void) { uint32_t x; 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 - /* Useless but harmless copy of the float result into main register(s) in case - of variadic function in the hardfloat variant */ - if(is_float(func_vt.t)) { + if ( +#ifdef TCC_ARM_HARDFLOAT + func_var && +#endif + is_float(func_vt.t)) { if((func_vt.t & VT_BTYPE) == VT_FLOAT) o(0xEE100A10); /* fmrs r0, s0 */ else { diff --git a/c67-gen.c b/c67-gen.c index df4b5d3..f2baea5 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -1881,7 +1881,7 @@ static void gcall_or_jmp(int is_jmp) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { *ret_align = 1; // Never have to re-align return values for x86-64 return 0; } @@ -1971,6 +1971,7 @@ void gfunc_prolog(CType * func_type) /* if the function returns a structure, then add an implicit pointer parameter */ func_vt = sym->type; + func_var = (sym->c == FUNC_ELLIPSIS); if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { func_vc = addr; addr += 4; diff --git a/i386-gen.c b/i386-gen.c index 4201ac2..2eb6922 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -376,7 +376,7 @@ static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { #ifdef TCC_TARGET_PE int size, align; @@ -527,6 +527,7 @@ ST_FUNC void gfunc_prolog(CType *func_type) /* if the function returns a structure, then add an implicit pointer parameter */ func_vt = sym->type; + func_var = (sym->c == FUNC_ELLIPSIS); #ifdef TCC_TARGET_PE size = type_size(&func_vt,&align); if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) { diff --git a/il-gen.c b/il-gen.c index 33f9f36..9e1ec64 100644 --- a/il-gen.c +++ b/il-gen.c @@ -441,6 +441,7 @@ void gfunc_prolog(int t) /* if the function returns a structure, then add an implicit pointer parameter */ func_vt = sym->t; + func_var = (sym->c == FUNC_ELLIPSIS); if ((func_vt & VT_BTYPE) == VT_STRUCT) { func_vc = addr; addr++; diff --git a/tcc.h b/tcc.h index 50642b7..21957e7 100644 --- a/tcc.h +++ b/tcc.h @@ -1176,6 +1176,7 @@ ST_DATA int const_wanted; /* true if constant wanted */ ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ ST_DATA CType func_vt; /* current function return type (used by return instruction) */ +ST_DATA int func_var; /* true if current function is variadic */ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ ST_DATA char *funcname; @@ -1288,7 +1289,7 @@ ST_FUNC void gsym_addr(int t, int a); ST_FUNC void gsym(int t); ST_FUNC void load(int r, SValue *sv); ST_FUNC void store(int r, SValue *v); -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *align); +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align); ST_FUNC void gfunc_call(int nb_args); ST_FUNC void gfunc_prolog(CType *func_type); ST_FUNC void gfunc_epilog(void); diff --git a/tccgen.c b/tccgen.c index 55b03e6..8355aae 100644 --- a/tccgen.c +++ b/tccgen.c @@ -66,6 +66,7 @@ ST_DATA int const_wanted; /* true if constant wanted */ ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ ST_DATA CType func_vt; /* current function return type (used by return instruction) */ +ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ ST_DATA char *funcname; @@ -3960,7 +3961,7 @@ ST_FUNC void unary(void) } else if (tok == '(') { SValue ret; Sym *sa; - int nb_args, ret_nregs, ret_align; + int nb_args, ret_nregs, ret_align, variadic; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -3984,7 +3985,9 @@ ST_FUNC void unary(void) ret.r2 = VT_CONST; /* compute first implicit argument if a structure is returned */ if ((s->type.t & VT_BTYPE) == VT_STRUCT) { - ret_nregs = gfunc_sret(&s->type, &ret.type, &ret_align); + variadic = (s->c == FUNC_ELLIPSIS); + ret_nregs = gfunc_sret(&s->type, variadic, &ret.type, + &ret_align); if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); @@ -4637,7 +4640,8 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; int ret_align, ret_nregs; - ret_nregs = gfunc_sret(&func_vt, &ret_type, &ret_align); + ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type, + &ret_align); if (0 == ret_nregs) { /* if returning structure, must copy it to implicit first pointer arg location */ @@ -5747,6 +5751,7 @@ static void gen_function(Sym *sym) cur_text_section = NULL; funcname = ""; /* for safety */ func_vt.t = VT_VOID; /* for safety */ + func_var = 0; /* for safety */ ind = 0; /* for safety */ nocode_wanted = saved_nocode_wanted; } diff --git a/x86_64-gen.c b/x86_64-gen.c index fe028d9..9aee875 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -658,7 +658,7 @@ void gen_offs_sp(int b, int r, int d) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { int size, align; *ret_align = 1; // Never have to re-align return values for x86-64 @@ -833,6 +833,7 @@ void gfunc_prolog(CType *func_type) /* if the function returns a structure, then add an implicit pointer parameter */ func_vt = sym->type; + func_var = (sym->c == FUNC_ELLIPSIS); size = gfunc_arg_size(&func_vt); if (size > 8) { gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); -- cgit v1.3.1 From 70a088af874076d8db5d7c2067afd8f2bcde0592 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 7 Jan 2014 16:04:12 +0800 Subject: Explicit that EABI only supports VFP for now --- Makefile | 2 +- arm-gen.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'arm-gen.c') diff --git a/Makefile b/Makefile index 9a4ae22..9225d82 100644 --- a/Makefile +++ b/Makefile @@ -169,7 +169,7 @@ $(C67_CROSS): DEFINES = -DTCC_TARGET_C67 $(ARM_FPA_CROSS): DEFINES = -DTCC_TARGET_ARM $(ARM_FPA_LD_CROSS)$(EXESUF): DEFINES = -DTCC_TARGET_ARM -DLDOUBLE_SIZE=12 $(ARM_VFP_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_VFP -$(ARM_EABI_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_EABI +$(ARM_EABI_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_EABI -DTCC_ARM_VFP $(I386_CROSS): $(I386_FILES) $(X64_CROSS): $(X86_64_FILES) diff --git a/arm-gen.c b/arm-gen.c index 9e6c638..05bccb0 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -23,10 +23,8 @@ #ifdef TARGET_DEFS_ONLY -#ifdef TCC_ARM_EABI -#ifndef TCC_ARM_VFP /* Avoid useless warning */ -#define TCC_ARM_VFP -#endif +#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP) +#error "Currently TinyCC only supports float computation with VFP instructions" #endif /* number of available registers */ -- cgit v1.3.1 From b6247d1f3c34e93e8603fddf5fc6da8dc6b81d00 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 7 Jan 2014 15:23:54 +0800 Subject: Add support for runtime selection of float ABI --- Changelog | 1 + arm-gen.c | 131 ++++++++++++++++++++++++++++++++--------------------------- libtcc.c | 23 ++++++++++- tcc-doc.texi | 3 ++ tcc.c | 2 +- tcc.h | 21 +++++++--- tccelf.c | 2 +- 7 files changed, 113 insertions(+), 70 deletions(-) (limited to 'arm-gen.c') 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 { @@ -1209,7 +1227,6 @@ void gfunc_call(int nb_args) o(0xEE201B10); /* vmov.32 d0[1], r1 */ } } -#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; -- cgit v1.3.1 From 9c6ddbfe903445763405d2c8bdec916c8a20f105 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sat, 11 Jan 2014 23:44:41 +0100 Subject: Fix compile on ARM non-eabi and non-vfp Adjust arm_init prototype to match declaration. --- arm-gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index bc24f70..9611dca 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -191,7 +191,7 @@ ST_FUNC void arm_init(struct TCCState *s) #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(void) {} +ST_FUNC void arm_init(struct TCCState *s) {} #endif static int two2mask(int a,int b) { -- cgit v1.3.1 From 5cbe03b9c47e676e045b4978c384087433bd6042 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Sat, 1 Feb 2014 12:29:51 +0800 Subject: Move result of itof double conv back to VFP reg EABI functions to convert an int to a double register take the integer value in core registers and also give the result in core registers. It is thus necessary to move the result back to VFP register after the function call. This only affected integer to double conversion because integer to float conversion used a VFP instruction to do the conversion and this obviously left the result in VFP register. Note that the behavior is left untouched for !EABI as the correct behavior in this case is unknown to the author of this patch. --- Changelog | 1 + arm-gen.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'arm-gen.c') diff --git a/Changelog b/Changelog index 9a497cf..20814c1 100644 --- a/Changelog +++ b/Changelog @@ -65,6 +65,7 @@ Bug fixes: - fix NaN comparison (Thomas Preud'homme) - use libtcc for static linking with runtime library (Thomas Preud'homme) - fix negation of 0.0 and -0.0 values (Thomas Preud'homme) +- fix integer to double conversion on ARM (Thomas Preud'homme) version 0.9.26: diff --git a/arm-gen.c b/arm-gen.c index 9611dca..c746e91 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1979,8 +1979,17 @@ ST_FUNC void gen_cvt_itof1(int t) vpush_global_sym(func_type, func); vswap(); gfunc_call(1); +#if defined(TCC_ARM_VFP) && defined(TCC_ARM_EABI) + r=get_reg(RC_FLOAT); + r2=vfpr(r); + o(0xEE000B10|(r2<<16)); /* vmov.32 dr2[0], r0 */ + o(0xEE201B10|(r2<<16)); /* vmov.32 dr2[1], r1 */ + vpushi(0); + vtop->r=r; +#else vpushi(0); vtop->r=TREG_F0; +#endif return; } } -- cgit v1.3.1 From 9fc57302f88a31ff7a42caf161ebcf7b07243a98 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Sun, 2 Feb 2014 20:29:24 +0800 Subject: Switch float abi to softfp for int <--> float conv This improves commit 5cbe03b9c47e676e045b4978c384087433bd6042 by avoiding a double transfer when the default float ABI is already softfp. It's also more clean by expliciting that the ABI is simply changed for runtime ABI functions. --- arm-gen.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index c746e91..4465043 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -923,7 +923,7 @@ struct plan { definition of union reg_class). nb_args: number of parameters of the function for which a call is generated - corefloat: whether to pass float via core registers or not + float_abi: float ABI in use for this function call plan: the structure where the overall assignment is recorded todo: a bitmap that record which core registers hold a parameter @@ -932,7 +932,7 @@ struct plan { Note: this function allocated an array in plan->pplans with tcc_malloc. It is the responsability of the caller to free this array once used (ie not before copy_params). */ -static int assign_regs(int nb_args, int corefloat, struct plan *plan, int *todo) +static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) { int i, size, align; int ncrn /* next core register number */, nsaa /* next stacked argument address*/; @@ -952,7 +952,7 @@ static int assign_regs(int nb_args, int corefloat, struct plan *plan, int *todo) case VT_FLOAT: case VT_DOUBLE: case VT_LDOUBLE: - if (!corefloat) { + if (float_abi == ARM_HARD_FLOAT) { int is_hfa = 0; /* Homogeneous float aggregate */ if (is_float(vtop[-i].type.t) @@ -1183,14 +1183,15 @@ static int copy_params(int nb_args, struct plan *plan, int todo) void gfunc_call(int nb_args) { int r, args_size; - int variadic, corefloat = 1; + int variadic, def_float_abi = float_abi; int todo; struct plan plan; #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]); + if (variadic || floats_in_core_regs(&vtop[-nb_args])) + float_abi = ARM_SOFTFP_FLOAT; } #endif /* cannot let cpu flags if other instruction are generated. Also avoid leaving @@ -1200,7 +1201,7 @@ void gfunc_call(int nb_args) if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); - args_size = assign_regs(nb_args, corefloat, &plan, &todo); + args_size = assign_regs(nb_args, float_abi, &plan, &todo); #ifdef TCC_ARM_EABI if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ @@ -1218,8 +1219,7 @@ void gfunc_call(int nb_args) if (args_size) gadd_sp(args_size); /* pop all parameters passed on the stack */ #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) - if(float_abi == ARM_SOFTFP_FLOAT && corefloat && - is_float(vtop->type.ref->type.t)) { + if(float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) { if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { o(0xEE000A10); /*vmov s0, r0 */ } else { @@ -1230,6 +1230,7 @@ void gfunc_call(int nb_args) #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 */ + float_abi = def_float_abi; } /* generate function prolog of type 't' */ @@ -1979,17 +1980,8 @@ ST_FUNC void gen_cvt_itof1(int t) vpush_global_sym(func_type, func); vswap(); gfunc_call(1); -#if defined(TCC_ARM_VFP) && defined(TCC_ARM_EABI) - r=get_reg(RC_FLOAT); - r2=vfpr(r); - o(0xEE000B10|(r2<<16)); /* vmov.32 dr2[0], r0 */ - o(0xEE201B10|(r2<<16)); /* vmov.32 dr2[1], r1 */ - vpushi(0); - vtop->r=r; -#else vpushi(0); vtop->r=TREG_F0; -#endif return; } } -- cgit v1.3.1 From 4760804dbac18646d9c3cbfaa88cee962f195cda Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Mon, 3 Feb 2014 11:13:42 +0800 Subject: Fix fct param passing of struct with size < 4 --- arm-gen.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 4465043..4c38cb9 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -947,6 +947,8 @@ static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) for(i = nb_args; i-- ;) { int j, start_vfpreg = 0; size = type_size(&vtop[-i].type, &align); + size = (size + 3) & ~3; + align = (align + 3) & ~3; switch(vtop[-i].type.t & VT_BTYPE) { case VT_STRUCT: case VT_FLOAT: @@ -972,8 +974,7 @@ static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) break; } } - ncrn = (ncrn + (align-1)/4) & -(align/4); - size = (size + 3) & -4; + ncrn = (ncrn + (align-1)/4) & ~((align/4) - 1); if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { /* The parameter is allocated both in core register and on stack. As * such, it can be of either class: it would either be the last of -- cgit v1.3.1 From c6017182f653fed44543ef81110cf3abbc34a08b Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Sun, 9 Feb 2014 23:15:33 +0800 Subject: Define float_eabi only in arm-gen.o --- arm-gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 4c38cb9..372c468 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -146,13 +146,13 @@ enum float_abi { ARM_HARD_FLOAT, }; -enum float_abi float_abi; - /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/ #include "tcc.h" +enum float_abi float_abi; + ST_DATA const int reg_classes[NB_REGS] = { /* r0 */ RC_INT | RC_R0, /* r1 */ RC_INT | RC_R1, -- cgit v1.3.1 From b8610f14b05932927bf0c5d4460cd2d1ec45b9a1 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 25 Mar 2014 19:48:33 +0800 Subject: Deprecate FPA and OABI support for ARM --- arm-gen.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 372c468..9aa093e 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -191,7 +191,17 @@ ST_FUNC void arm_init(struct TCCState *s) #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(struct TCCState *s) {} +ST_FUNC void arm_init(struct TCCState *s) +{ +#if !defined (TCC_ARM_VFP) + tcc_warning("Support for FPA is deprecated and will be removed in next" + " release"); +#endif +#if !defined (TCC_ARM_EABI) + tcc_warning("Support for OABI is deprecated and will be removed in next" + " release"); +#endif +} #endif static int two2mask(int a,int b) { -- cgit v1.3.1 From 6f6ed8acc795b8f3367b46eed7b78f4f0a4584ff Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 25 Mar 2014 19:54:04 +0800 Subject: Warn about soft float ABI not being supported For ARM target, tcc uses the soft float ABI when not asked to use hard float ABI. This means machine without a VFP co-processor generate code that they cannot run. This commit add a warning for such cases. --- arm-gen.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 9aa093e..1ee008f 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -186,6 +186,9 @@ ST_FUNC void arm_init(struct TCCState *s) func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); float_abi = s->float_abi; +#ifndef TCC_ARM_HARDFLOAT + tcc_warning("soft float ABI currently not supported: default to softfp"); +#endif } #else #define func_float_type func_old_type -- cgit v1.3.1 From 10750872419df9dc92421c4fd719f42e5561ee77 Mon Sep 17 00:00:00 2001 From: Daniel Glöckner Date: Sat, 29 Mar 2014 17:50:40 +0100 Subject: ARM: Fix passing arrays to varadic functions TinyCC miscompiled void g(int,...); void f(void) { char b[4000]; g(1, 2, 3, 4, b); } in two ways: 1. It didn't align the stack to 8 bytes before the call 2. It added sizeof(b) to the stack pointer after the call --- arm-gen.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index 1ee008f..a9c05fe 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -959,7 +959,9 @@ static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) memset(plan->clsplans, 0, sizeof(plan->clsplans)); for(i = nb_args; i-- ;) { int j, start_vfpreg = 0; - size = type_size(&vtop[-i].type, &align); + CType type = vtop[-i].type; + type.t &= ~VT_ARRAY; + size = type_size(&type, &align); size = (size + 3) & ~3; align = (align + 3) & ~3; switch(vtop[-i].type.t & VT_BTYPE) { -- cgit v1.3.1 From 3e9a7e9d69e3adb0e9ed65d11caf415e74458fe9 Mon Sep 17 00:00:00 2001 From: Vincent Lefevre Date: Mon, 7 Apr 2014 13:31:00 +0200 Subject: Corrected spelling mistakes in comments and strings --- arm-gen.c | 4 ++-- c67-gen.c | 4 ++-- i386-asm.c | 2 +- i386-tok.h | 2 +- include/stddef.h | 2 +- lib/bcheck.c | 2 +- tccasm.c | 2 +- tccgen.c | 4 ++-- tests/Makefile | 2 +- tests/tcctest.c | 2 +- tests/tests2/46_grep.c | 10 +++++----- 11 files changed, 18 insertions(+), 18 deletions(-) (limited to 'arm-gen.c') diff --git a/arm-gen.c b/arm-gen.c index a9c05fe..680a490 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -943,7 +943,7 @@ struct plan { Returns the amount of stack space needed for parameter passing Note: this function allocated an array in plan->pplans with tcc_malloc. It - is the responsability of the caller to free this array once used (ie not + is the responsibility of the caller to free this array once used (ie not before copy_params). */ static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) { @@ -1860,7 +1860,7 @@ void gen_opf(int op) case TOK_UGE: case TOK_ULE: case TOK_UGT: - tcc_error("unsigned comparision on floats?"); + tcc_error("unsigned comparison on floats?"); break; case TOK_LT: op=TOK_Nset; diff --git a/c67-gen.c b/c67-gen.c index 6d9068a..a26dfaa 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -245,8 +245,8 @@ void gsym(int t) } // these are regs that tcc doesn't really know about, -// but asign them unique values so the mapping routines -// can distinquish them +// but assign them unique values so the mapping routines +// can distinguish them #define C67_A0 105 #define C67_SP 106 diff --git a/i386-asm.c b/i386-asm.c index 8473d06..a524658 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -1382,7 +1382,7 @@ ST_FUNC void subst_asm_operand(CString *add_str, } } -/* generate prolog and epilog code for asm statment */ +/* generate prolog and epilog code for asm statement */ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, diff --git a/i386-tok.h b/i386-tok.h index d1e4bf3..6ba865d 100644 --- a/i386-tok.h +++ b/i386-tok.h @@ -191,7 +191,7 @@ DEF_FP(mul) DEF_ASM(fcom) - DEF_ASM(fcom_1) /* non existant op, just to have a regular table */ + DEF_ASM(fcom_1) /* non existent op, just to have a regular table */ DEF_FP1(com) DEF_FP(comp) diff --git a/include/stddef.h b/include/stddef.h index eaf0669..9e43de9 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -31,7 +31,7 @@ void *alloca(size_t size); by __need_wint_t, as otherwise stddef.h isn't allowed to define this type). Note that this must be outside the normal _STDDEF_H guard, so that it works even when we've included the file - already (without requring wint_t). Some other libs define _WINT_T + already (without requiring wint_t). Some other libs define _WINT_T if they've already provided that type, so we can use that as guard. TCC defines __WINT_TYPE__ for us. */ #if defined (__need_wint_t) diff --git a/lib/bcheck.c b/lib/bcheck.c index 968cdf4..76413ad 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -635,7 +635,7 @@ int __bound_delete_region(void *p) } /* return the size of the region starting at p, or EMPTY_SIZE if non - existant region. */ + existent region. */ static unsigned long get_region_size(void *p) { unsigned long addr = (unsigned long)p; diff --git a/tccasm.c b/tccasm.c index 9c77960..1c6a65d 100644 --- a/tccasm.c +++ b/tccasm.c @@ -232,7 +232,7 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) } else { goto cannot_relocate; } - pe->sym = NULL; /* same symbols can be substracted to NULL */ + pe->sym = NULL; /* same symbols can be subtracted to NULL */ } else { cannot_relocate: tcc_error("invalid operation with label"); diff --git a/tccgen.c b/tccgen.c index e6e0fe1..ec92797 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1579,7 +1579,7 @@ static inline int is_integer_btype(int bt) bt == VT_INT || bt == VT_LLONG); } -/* check types for comparison or substraction of pointers */ +/* check types for comparison or subtraction of pointers */ static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) { CType *type1, *type2, tmp_type1, tmp_type2; @@ -5574,7 +5574,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, if (sym->type.t & VT_EXTERN) { /* if the variable is extern, it was not allocated */ sym->type.t &= ~VT_EXTERN; - /* set array size if it was ommited in extern + /* set array size if it was omitted in extern declaration */ if ((sym->type.t & VT_ARRAY) && sym->type.ref->c < 0 && diff --git a/tests/Makefile b/tests/Makefile index 55bf29c..ee0eafe 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -158,7 +158,7 @@ btest: boundtest.c @for i in $(BOUNDS_OK); do \ echo ; echo --- boundtest $$i ---; \ if $(TCC) -b -run $< $$i ; then \ - echo succeded as expected; \ + echo succeeded as expected; \ else\ echo Failed positive test $$i ; exit 1 ; \ fi ;\ diff --git a/tests/tcctest.c b/tests/tcctest.c index c48c1bc..cc8ffd8 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2254,7 +2254,7 @@ void c99_vla_test(int size1, int size2) printf("%s\n", (sizeof tab1 == size1 * size2 * 2 * sizeof(int)) ? "PASSED" : "FAILED"); tab1_ptr = tab1; tab2_ptr = tab2; - printf("Test C99 VLA 2 (ptrs substract): "); + printf("Test C99 VLA 2 (ptrs subtract): "); printf("%s\n", (tab2 - tab1 == (tab2_ptr - tab1_ptr) / (sizeof(int) * 2)) ? "PASSED" : "FAILED"); printf("Test C99 VLA 3 (ptr add): "); printf("%s\n", &tab1[5][1] == (tab1_ptr + (5 * 2 + 1) * sizeof(int)) ? "PASSED" : "FAILED"); diff --git a/tests/tests2/46_grep.c b/tests/tests2/46_grep.c index 5f52220..27589a4 100644 --- a/tests/tests2/46_grep.c +++ b/tests/tests2/46_grep.c @@ -29,10 +29,10 @@ char *documentation[] = { "grep searches a file for a given pattern. Execute by", " grep [flags] regular_expression file_list\n", - "Flags are single characters preceeded by '-':", + "Flags are single characters preceded by '-':", " -c Only a count of matching lines is printed", " -f Print file name for matching lines switch, see below", - " -n Each line is preceeded by its line number", + " -n Each line is preceded by its line number", " -v Only print non-matching lines\n", "The file_list is a list of files (wildcards are acceptable on RSX modes).", "\nThe file name is normally printed if there is a file given.", @@ -54,10 +54,10 @@ char *patdoc[] = { "':n' \":n\" matches alphanumerics, \": \" matches spaces, tabs, and", "': ' other control characters, such as new-line.", "'*' An expression followed by an asterisk matches zero or more", - " occurrances of that expression: \"fo*\" matches \"f\", \"fo\"", + " occurrences of that expression: \"fo*\" matches \"f\", \"fo\"", " \"foo\", etc.", "'+' An expression followed by a plus sign matches one or more", - " occurrances of that expression: \"fo+\" matches \"fo\", etc.", + " occurrences of that expression: \"fo+\" matches \"fo\", etc.", "'-' An expression followed by a minus sign optionally matches", " the expression.", "'[]' A string enclosed in square brackets matches any character in", @@ -153,7 +153,7 @@ void compile(char *source) o == STAR || o == PLUS || o == MINUS) - badpat("Illegal occurrance op.", source, s); + badpat("Illegal occurrence op.", source, s); store(ENDPAT); store(ENDPAT); spp = pp; /* Save pattern end */ -- cgit v1.3.1