diff --git a/Changelog b/Changelog index a830c89..9cf673d 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,7 @@ version 0.9.24: +- Import 409,410: ARM EABI by Daniel Glöckner + - Some in-between fixes: TCC -E no longer hangs with macro calls involving newlines. (next_nomacro1 now advances the read-pointer with TOK_LINEFEED) diff --git a/Makefile b/Makefile index dc01d46..65f9fd9 100644 --- a/Makefile +++ b/Makefile @@ -158,7 +158,7 @@ c67-tcc$(EXESUF): tcc.c c67-gen.c tccelf.c tccasm.c tcctok.h libtcc.h tcccoff.c $(CC) $(CFLAGS) -DTCC_TARGET_C67 -o $@ $< $(LIBS) arm-tcc$(EXESUF): tcc.c arm-gen.c tccelf.c tccasm.c tcctok.h libtcc.h - $(CC) $(CFLAGS) -DTCC_TARGET_ARM -o $@ $< $(LIBS) + $(CC) $(CFLAGS) -DTCC_TARGET_ARM -DTCC_ARM_EABI -o $@ $< $(LIBS) i386-win32-tcc$(EXESUF): tcc.c i386-gen.c tccelf.c tccasm.c i386-asm.c tcctok.h libtcc.h i386-asm.h tccpe.c $(CC) $(CFLAGS) -DTCC_TARGET_PE -o $@ $< $(LIBS) diff --git a/arm-gen.c b/arm-gen.c index f657331..189f362 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -20,8 +20,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef TCC_ARM_EABI +#define TCC_ARM_VFP +#endif + + /* number of available registers */ +#ifdef TCC_ARM_VFP +#define NB_REGS 13 +#else #define NB_REGS 9 +#endif /* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does @@ -37,6 +46,12 @@ #define RC_F1 0x0100 #define RC_F2 0x0200 #define RC_F3 0x0400 +#ifdef TCC_ARM_VFP +#define RC_F4 0x0800 +#define RC_F5 0x1000 +#define RC_F6 0x2000 +#define RC_F7 0x4000 +#endif #define RC_IRET RC_R0 /* function return: integer register */ #define RC_LRET RC_R1 /* function return: second integer register */ #define RC_FRET RC_F0 /* function return: float register */ @@ -52,6 +67,12 @@ enum { TREG_F1, TREG_F2, TREG_F3, +#ifdef TCC_ARM_VFP + TREG_F4, + TREG_F5, + TREG_F6, + TREG_F7, +#endif }; int reg_classes[NB_REGS] = { @@ -64,6 +85,12 @@ int reg_classes[NB_REGS] = { /* f1 */ RC_FLOAT | RC_F1, /* f2 */ RC_FLOAT | RC_F2, /* f3 */ RC_FLOAT | RC_F3, +#ifdef TCC_ARM_VFP + /* d4/s8 */ RC_FLOAT | RC_F4, +/* d5/s10 */ RC_FLOAT | RC_F5, +/* d6/s12 */ RC_FLOAT | RC_F6, +/* d7/s14 */ RC_FLOAT | RC_F7, +#endif }; static int two2mask(int a,int b) { @@ -74,6 +101,10 @@ static int regmask(int r) { return reg_classes[r]&~(RC_INT|RC_FLOAT); } +#ifdef TCC_ARM_VFP +#define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0) +#endif + /* return registers for function */ #define REG_IRET TREG_R0 /* single word int return register */ #define REG_LRET TREG_R1 /* second word return register (for long long) */ @@ -86,12 +117,28 @@ static int regmask(int r) { are directly pushed on stack. */ //#define FUNC_STRUCT_PARAM_AS_PTR +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) +static CType float_type, double_type, func_float_type, func_double_type; +#endif + /* pointer size, in bytes */ #define PTR_SIZE 4 /* long double size and alignment, in bytes */ +#ifdef TCC_ARM_VFP #define LDOUBLE_SIZE 8 +#endif + +#ifndef LDOUBLE_SIZE +#define LDOUBLE_SIZE 8 +#endif + +#ifdef TCC_ARM_EABI +#define LDOUBLE_ALIGN 8 +#else #define LDOUBLE_ALIGN 4 +#endif + /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 8 @@ -112,6 +159,7 @@ static int regmask(int r) { /******************************************************/ static unsigned long func_sub_sp_offset,last_itod_magic; +static int leaffunc; void o(unsigned long i) { @@ -287,19 +335,28 @@ void gsym(int t) gsym_addr(t, ind); } +#ifdef TCC_ARM_VFP +static unsigned long vfpr(int r) +{ + if(rTREG_F7) + error("compiler error! register %i is no vfp register",r); + return r-5; +} +#else static unsigned long fpr(int r) { if(rTREG_F3) - error("compiler error! register %i is no fp register\n",r); + error("compiler error! register %i is no fpa register",r); return r-5; } +#endif static unsigned long intr(int r) { if(r==4) return 12; if((r<0 || r>4) && r!=14) - error("compiler error! register %i is no int register\n",r); + error("compiler error! register %i is no int register",r); return r; } @@ -335,28 +392,32 @@ static unsigned long mapcc(int cc) switch(cc) { case TOK_ULT: - return 0x30000000; + return 0x30000000; /* CC/LO */ case TOK_UGE: - return 0x20000000; + return 0x20000000; /* CS/HS */ case TOK_EQ: - return 0x00000000; + return 0x00000000; /* EQ */ case TOK_NE: - return 0x10000000; + return 0x10000000; /* NE */ case TOK_ULE: - return 0x90000000; + return 0x90000000; /* LS */ case TOK_UGT: - return 0x80000000; + return 0x80000000; /* HI */ + case TOK_Nset: + return 0x40000000; /* MI */ + case TOK_Nclear: + return 0x50000000; /* PL */ case TOK_LT: - return 0xB0000000; + return 0xB0000000; /* LT */ case TOK_GE: - return 0xA0000000; + return 0xA0000000; /* GE */ case TOK_LE: - return 0xD0000000; + return 0xD0000000; /* LE */ case TOK_GT: - return 0xC0000000; + return 0xC0000000; /* GT */ } error("unexpected condition code"); - return 0xE0000000; + return 0xE0000000; /* AL */ } static int negcc(int cc) @@ -375,6 +436,10 @@ static int negcc(int cc) return TOK_UGT; case TOK_UGT: return TOK_ULE; + case TOK_Nset: + return TOK_Nclear; + case TOK_Nclear: + return TOK_Nset; case TOK_LT: return TOK_GE; case TOK_GE: @@ -432,6 +497,14 @@ void load(int r, SValue *sv) if(v == VT_LOCAL) { if(is_float(ft)) { calcaddr(&base,&fc,&sign,1020,2); +#ifdef TCC_ARM_VFP + op=0xED100A00; /* flds */ + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x100; /* flds -> fldd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else op=0xED100100; if(!sign) op|=0x800000; @@ -445,7 +518,9 @@ void load(int r, SValue *sv) op|=0x400000; #endif o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); - } else if((ft & VT_TYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_SHORT) { +#endif + } else if((ft & (VT_BTYPE|VT_UNSIGNED)) == VT_BYTE + || (ft & VT_BTYPE) == VT_SHORT) { calcaddr(&base,&fc,&sign,255,0); op=0xE1500090; if ((ft & VT_BTYPE) == VT_SHORT) @@ -504,7 +579,11 @@ void load(int r, SValue *sv) return; } else if (v < VT_CONST) { if(is_float(ft)) +#ifdef TCC_ARM_VFP + o(0xEEB00A40|(vfpr(r)<<12)|vfpr(v)|T2CPR(ft)); /* fcpyX */ +#else o(0xEE008180|(fpr(r)<<12)|fpr(v)); +#endif else o(0xE1A00000|(intr(r)<<12)|intr(v)); return; @@ -550,6 +629,14 @@ void store(int r, SValue *sv) 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) + op|=0x100; /* fsts -> fstd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else op=0xED000100; if(!sign) op|=0x800000; @@ -563,6 +650,7 @@ void store(int r, SValue *sv) op|=0x400000; #endif o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); +#endif return; } else if((ft & VT_BTYPE) == VT_SHORT) { calcaddr(&base,&fc,&sign,255,0); @@ -635,19 +723,44 @@ 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 + if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT + && type_size(&vtop[-nb_args].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); + vtop->type.t = VT_LLONG; + args_size = 0; + for(i = nb_args + 1 ; i-- ;) { + size = type_size(&vtop[-i].type, &align); + if(args_size & (align-1)) { + vpushi(0); + vtop->type.t = VT_VOID; /* padding */ + vrott(i+2); + args_size += 4; + ++nb_args; + } + args_size += (size + 3) & -4; + } + vtop--; +#endif args_size = 0; for(i = nb_args ; i-- && args_size < 16 ;) { - if ((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT) { + switch(vtop[-i].type.t & VT_BTYPE) { + case VT_STRUCT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_LDOUBLE: size = type_size(&vtop[-i].type, &align); - size = (size + 3) & ~3; + size = (size + 3) & -4; args_size += size; - } else if ((vtop[-i].type.t & VT_BTYPE) == VT_FLOAT) - args_size += 4; - else if ((vtop[-i].type.t & VT_BTYPE) == VT_DOUBLE) - args_size += 8; - else if ((vtop[-i].type.t & VT_BTYPE) == VT_LDOUBLE) - args_size += LDOUBLE_SIZE; - else { + break; + default: plan[nb_args-1-i][0]=args_size/4; args_size += 4; if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { @@ -662,7 +775,7 @@ void gfunc_call(int nb_args) if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); /* align to stack align size */ - size = (size + 3) & ~3; + size = (size + 3) & -4; /* allocate the necessary size on stack */ gadd_sp(-size); /* generate structure store */ @@ -674,6 +787,16 @@ void gfunc_call(int nb_args) vtop--; args_size += size; } else if (is_float(vtop->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); +#else r=fpr(gv(RC_FLOAT))<<12; if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) size = 4; @@ -688,6 +811,7 @@ void gfunc_call(int nb_args) r|=0x8000; o(0xED2D0100|r|(size>>2)); +#endif vtop--; args_size += size; } else { @@ -718,7 +842,14 @@ void gfunc_call(int nb_args) s=regmask(plan[nb_args-i-1][0]); todo&=~(1<type.t == VT_VOID) { + if(s == RC_INT) + o(0xE24DD004); /* sub sp,sp,#4 */ + vtop--; + } else +#endif + if(s == RC_INT) { r = gv(s); o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ vtop--; @@ -733,7 +864,7 @@ void gfunc_call(int nb_args) gv(plan2[i]); vrott(keep); } - save_regs(keep); /* save used temporary registers */ +save_regs(keep); /* save used temporary registers */ keep++; if(args_size) { int n; @@ -758,7 +889,26 @@ void gfunc_call(int nb_args) gcall_or_jmp(0); if (args_size) gadd_sp(args_size); +#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; + } +#ifdef TCC_ARM_VFP + else if(is_float(vtop->type.ref->type.t)) { + if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { + o(0xEE000A10); /* fmsr s0,r0 */ + } else { + o(0xEE000B10); /* fmdlr d0,r0 */ + o(0xEE201B10); /* fmdhr d0,r1 */ + } + } +#endif +#endif vtop-=keep; + leaffunc = 0; } /* generate function prolog of type 't' */ @@ -770,17 +920,18 @@ void gfunc_prolog(CType *func_type) sym = func_type->ref; func_vt = sym->type; - n=0; - addr=12; - if((func_vt.t & VT_BTYPE) == VT_STRUCT) { + n = 0; + addr = 0; + if((func_vt.t & VT_BTYPE) == VT_STRUCT + && type_size(&func_vt,&align) > 4) + { func_vc = addr; addr += 4; n++; } for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { size = type_size(&sym2->type, &align); - size = (size + 3) & ~3; - n+=size/4; + n += (size + 3) / 4; } o(0xE1A0C00D); /* mov ip,sp */ if(func_type->ref->c == FUNC_ELLIPSIS) @@ -788,40 +939,63 @@ void gfunc_prolog(CType *func_type) if(n) { if(n>4) n=4; +#ifdef TCC_ARM_EABI + n=(n+1)&-2; +#endif o(0xE92D0000|((1<next)) { CType *type; type = &sym->type; - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); size = type_size(type, &align); - size = (size + 3) & ~3; + size = (size + 3) & -4; +#ifdef TCC_ARM_EABI + addr = (addr + align - 1) & -align; +#endif + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); addr += size; } last_itod_magic=0; - loc = 0; + leaffunc = 1; + loc = -12; } /* generate function epilog */ void gfunc_epilog(void) { unsigned long x; - o(0xE89BA800); /* restore fp, sp, pc */ - if(loc) { - x=stuff_const(0xE24DD000, (-loc + 3) & -4); /* sub sp,sp,# */ + int diff; +#ifdef TCC_ARM_EABI + if(is_float(func_vt.t)) { + if((func_vt.t & VT_BTYPE) == VT_FLOAT) + o(0xEE100A10); /* fmrs r0, s0 */ + else { + o(0xEE100B10); /* fmrdl r0, d0 */ + o(0xEE301B10); /* fmrdh r1, d0 */ + } + } +#endif + o(0xE91BA800); /* restore fp, sp, pc */ + diff = (-loc + 3) & -4; +#ifdef TCC_ARM_EABI + if(!leaffunc) + diff = (diff + 7) & -8; +#endif + if(diff > 12) { + x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ if(x) *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = x; else { unsigned long addr; addr=ind; o(0xE59FC004); /* ldr ip,[pc+4] */ - o(0xE04DD00C); /* sub sp,sp,ip */ + o(0xE04BD00C); /* sub sp,fp,ip */ o(0xE1A0F00E); /* mov pc,lr */ - o((-loc + 3) & -4); + o(diff); *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); } } @@ -879,7 +1053,12 @@ int gtst(int inv, int t) } else { if (is_float(vtop->type.t)) { r=gv(RC_FLOAT); - o(0xEE90F118|fpr(r)<<16); +#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); @@ -1058,6 +1237,110 @@ done: } } +#ifdef TCC_ARM_VFP +static int is_zero(int i) +{ + if((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop[i].type.t == VT_FLOAT) + return (vtop[i].c.f == 0.f); + else if (vtop[i].type.t == VT_DOUBLE) + return (vtop[i].c.d == 0.0); + return (vtop[i].c.ld == 0.l); +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + * two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + unsigned long x; + int fneg=0,r; + x=0xEE000A00|T2CPR(vtop->type.t); + switch(op) { + case '+': + if(is_zero(-1)) + vswap(); + if(is_zero(0)) { + vtop--; + return; + } + x|=0x300000; + break; + case '-': + x|=0x300040; + if(is_zero(0)) { + vtop--; + return; + } + if(is_zero(-1)) { + x|=0x810000; /* fsubX -> fnegX */ + vswap(); + vtop--; + fneg=1; + } + break; + case '*': + x|=0x200000; + break; + case '/': + x|=0x800000; + break; + default: + if(op < TOK_ULT && op > TOK_GT) { + error("unknown fp op %x!",op); + return; + } + if(is_zero(-1)) { + vswap(); + switch(op) { + case TOK_LT: op=TOK_GT; break; + case TOK_GE: op=TOK_ULE; break; + case TOK_LE: op=TOK_GE; break; + case TOK_GT: op=TOK_ULT; break; + } + } + x|=0xB40040; /* fcmpX */ + if(op!=TOK_EQ && op!=TOK_NE) + x|=0x80; /* fcmpX -> fcmpeX */ + if(is_zero(0)) { + vtop--; + o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */ + } else { + x|=vfpr(gv(RC_FLOAT)); + vswap(); + o(x|(vfpr(gv(RC_FLOAT))<<12)); + vtop--; + } + o(0xEEF1FA10); /* fmstat */ + + switch(op) { + case TOK_LE: op=TOK_ULE; break; + case TOK_LT: op=TOK_ULT; break; + case TOK_UGE: op=TOK_GE; break; + case TOK_UGT: op=TOK_GT; break; + } + + vtop->r = VT_CMP; + vtop->c.i = op; + return; + } + r=gv(RC_FLOAT); + x|=vfpr(r); + r=regmask(r); + if(!fneg) { + int r2; + vswap(); + r2=gv(RC_FLOAT); + x|=vfpr(r2)<<16; + r|=regmask(r2); + } + vtop->r=get_reg_ex(RC_FLOAT,r); + if(!fneg) + vtop--; + o(x|(vfpr(vtop->r)<<12)); +} + +#else static int is_fconst() { long double f; @@ -1193,24 +1476,20 @@ void gen_opf(int op) default: if(op >= TOK_ULT && op <= TOK_GT) { x|=0xd0f110; // cmfe +/* bug (intention?) in Linux FPU emulator + doesn't set carry if equal */ switch(op) { case TOK_ULT: case TOK_UGE: case TOK_ULE: case TOK_UGT: - fputs("unsigned comparision on floats?\n",stderr); + error("unsigned comparision on floats?"); break; case TOK_LT: - op=TOK_ULT; - break; - case TOK_GE: - op=TOK_UGE; + op=TOK_Nset; break; case TOK_LE: - op=TOK_ULE; - break; - case TOK_GT: - op=TOK_UGT; + op=TOK_ULE; /* correct in unordered case only if AC bit in FPSR set */ break; case TOK_EQ: case TOK_NE: @@ -1221,26 +1500,20 @@ void gen_opf(int op) c2=c1; vswap(); switch(op) { - case TOK_ULT: - op=TOK_UGT; + case TOK_Nset: + op=TOK_GT; break; - case TOK_UGE: + case TOK_GE: op=TOK_ULE; break; case TOK_ULE: - op=TOK_UGE; + op=TOK_GE; break; - case TOK_UGT: - op=TOK_ULT; + case TOK_GT: + op=TOK_Nset; break; } } -// bug (intention?) in Linux FPU emulator -// doesn't set carry if equal - if(op==TOK_ULT) - op=TOK_LT; - else if(op==TOK_UGE) - op=TOK_GE; vswap(); r=fpr(gv(RC_FLOAT)); vswap(); @@ -1254,7 +1527,7 @@ void gen_opf(int op) vtop[-1].r = VT_CMP; vtop[-1].c.i = op; } else { - error("unknown fp op %x!\n",op); + error("unknown fp op %x!",op); return; } } @@ -1270,6 +1543,7 @@ void gen_opf(int op) vtop--; o(x|(r<<16)|(c1<<12)|r2); } +#endif /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' and 'long long' cases. */ @@ -1279,6 +1553,14 @@ void gen_cvt_itof(int t) bt=vtop->type.t & VT_BTYPE; if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { r=intr(gv(RC_INT)); +#ifdef TCC_ARM_VFP + r2=vfpr(vtop->r=get_reg(RC_FLOAT)); + o(0xEE000A10|(r<<12)|(r2<<16)); /* fmsr */ + r2<<=12; + if(!(vtop->type.t & VT_UNSIGNED)) + r2|=0x80; /* fuitoX -> fsituX */ + o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/ +#else r2=fpr(vtop->r=get_reg(RC_FLOAT)); o(0xEE000190|(r2<<16)|(r<<12)); if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { @@ -1300,14 +1582,30 @@ void gen_cvt_itof(int t) } o(0xBE000180|(r2<<16)|(r2<<12)|r); } +#endif return; } else if(bt == VT_LLONG) { int func; + CType *func_type = &func_old_type; +#ifdef TCC_ARM_VFP +#ifdef TCC_ARM_EABI + func_type = &func_double_type; +#endif + if((t & VT_BTYPE) == VT_FLOAT) { +#ifdef TCC_ARM_EABI + func_type = &func_float_type; +#endif + if(vtop->type.t & VT_UNSIGNED) + func=TOK___ulltof; + else + func=TOK___slltof; + } else +#endif if(vtop->type.t & VT_UNSIGNED) func=TOK___ulltold; else func=TOK___slltold; - vpush_global_sym(&func_old_type, func); + vpush_global_sym(func_type, func); vswap(); gfunc_call(1); vpushi(0); @@ -1325,6 +1623,14 @@ void gen_cvt_ftoi(int t) t&=VT_BTYPE; r2=vtop->type.t & VT_BTYPE; if(t==VT_INT) { +#ifdef TCC_ARM_VFP + r=vfpr(gv(RC_FLOAT)); + u=u?0:0x10000; + o(0xEEBC0A40|(r<<12)|r|T2CPR(r2)); /* ftoXiY */ + r2=intr(vtop->r=get_reg(RC_INT)); + o(0xEE100A10|(r<<16)|(r2<<12)); + return; +#else if(u) { if(r2 == VT_FLOAT) func=TOK___fixunssfsi; @@ -1342,6 +1648,7 @@ void gen_cvt_ftoi(int t) o(0xEE100170|(r2<<12)|r); return; } +#endif } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 if(r2 == VT_FLOAT) func=TOK___fixsfdi; @@ -1370,8 +1677,15 @@ void gen_cvt_ftoi(int t) /* convert from one floating point type to another */ void gen_cvt_ftof(int t) { - /* all we have to do on i386 and ARM is to put the float in a register */ +#ifdef TCC_ARM_VFP + if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) { + int r=vfpr(gv(RC_FLOAT)); + o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t)); + } +#else + /* all we have to do on i386 and FPA ARM is to put the float in a register */ gv(RC_FLOAT); +#endif } /* computed goto support */ diff --git a/elf.h b/elf.h index cb254b1..39e2d88 100644 --- a/elf.h +++ b/elf.h @@ -290,6 +290,9 @@ typedef struct #define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ #define SHT_HIOS 0x6fffffff /* End OS-specific type */ #define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_ARM_EXIDX 0x70000001 /* Exception Index table */ +#define SHT_ARM_PREEMPTMAP 0x70000002 /* dynamic linking pre-emption map */ +#define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attrs */ #define SHT_HIPROC 0x7fffffff /* End of processor-specific */ #define SHT_LOUSER 0x80000000 /* Start of application-specific */ #define SHT_HIUSER 0x8fffffff /* End of application-specific */ @@ -1589,10 +1592,13 @@ typedef Elf32_Addr Elf32_Conflict; #define R_ARM_GLOB_DAT 21 /* Create GOT entry */ #define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ #define R_ARM_RELATIVE 23 /* Adjust by program base */ -#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_GOTOFF32 24 /* 32 bit offset to GOT */ +#define R_ARM_BASE_PREL 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT_BREL 26 /* 32 bit GOT entry */ #define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_PREL31 42 #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ diff --git a/tcc.c b/tcc.c index 68e2915..36195d1 100644 --- a/tcc.c +++ b/tcc.c @@ -577,6 +577,8 @@ struct TCCState { #define TOK_NE 0x95 #define TOK_ULE 0x96 #define TOK_UGT 0x97 +#define TOK_Nset 0x98 +#define TOK_Nclear 0x99 #define TOK_LT 0x9c #define TOK_GE 0x9d #define TOK_LE 0x9e @@ -4827,6 +4829,9 @@ int gv(int rc) Sym *sym; int *ptr; unsigned long offset; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + CValue check; +#endif /* XXX: unify with initializers handling ? */ /* CPUs usually cannot use float constants, so we store them @@ -4842,6 +4847,13 @@ int gv(int rc) #endif ptr = section_ptr_add(data_section, size); size = size >> 2; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + check.d = 1; + if(check.tab[0]) + for(i=0;ic.tab[size-1-i]; + else +#endif for(i=0;ic.tab[i]; sym = get_sym_ref(&vtop->type, data_section, offset, size << 2); @@ -6022,6 +6034,12 @@ static int type_size(CType *type, int *a) } else if (bt == VT_DOUBLE || bt == VT_LLONG) { #ifdef TCC_TARGET_I386 *a = 4; +#elif defined(TCC_TARGET_ARM) +#ifdef TCC_ARM_EABI + *a = 8; +#else + *a = 4; +#endif #else *a = 8; #endif @@ -6337,6 +6355,13 @@ void vstore(void) if (!nocode_wanted) { size = type_size(&vtop->type, &align); +#ifdef TCC_ARM_EABI + if(!(align & 7)) + vpush_global_sym(&func_old_type, TOK_memcpy8); + else if(!(align & 3)) + vpush_global_sym(&func_old_type, TOK_memcpy4); + else +#endif vpush_global_sym(&func_old_type, TOK_memcpy); /* destination */ @@ -8113,6 +8138,27 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, CType type; /* if returning structure, must copy it to implicit first pointer arg location */ +#ifdef TCC_ARM_EABI + int align, size; + size = type_size(&func_vt,&align); + if(size <= 4) + { + if((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & 3)) + && (align & 3)) + { + int addr; + loc = (loc - size) & -4; + addr = loc; + type = func_vt; + vset(&type, VT_LOCAL | VT_LVAL, addr); + vswap(); + vstore(); + vset(&int_type, VT_LOCAL | VT_LVAL, addr); + } + vtop->type = int_type; + gv(RC_IRET); + } else { +#endif type = func_vt; mk_pointer(&type); vset(&type, VT_LOCAL | VT_LVAL, func_vc); @@ -8120,6 +8166,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, vswap(); /* copy structure value to pointer */ vstore(); +#ifdef TCC_ARM_EABI + } +#endif } else if (is_float(func_vt.t)) { gv(RC_FRET); } else { @@ -9360,6 +9409,16 @@ 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); +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + float_type.t = VT_FLOAT; + double_type.t = VT_DOUBLE; + + func_float_type.t = VT_FUNC; + 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); +#endif + #if 0 /* define 'void *alloca(unsigned int)' builtin function */ { diff --git a/tccelf.c b/tccelf.c index 73f84cc..a3c9aee 100644 --- a/tccelf.c +++ b/tccelf.c @@ -532,6 +532,8 @@ static void relocate_section(TCCState *s1, Section *s) break; #elif defined(TCC_TARGET_ARM) case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: case R_ARM_PLT32: { int x; @@ -548,13 +550,27 @@ static void relocate_section(TCCState *s1, Section *s) (*(int *)ptr) |= x; } break; + case R_ARM_PREL31: + { + int x; + x = (*(int *)ptr) & 0x7fffffff; + (*(int *)ptr) &= 0x80000000; + x = (x * 2) / 2; + x += val - addr; + if((x^(x>>1))&0x40000000) + error("can't relocate value at %x",addr); + (*(int *)ptr) |= x & 0x7fffffff; + } case R_ARM_ABS32: *(int *)ptr += val; break; - case R_ARM_GOTPC: + case R_ARM_BASE_PREL: *(int *)ptr += s1->got->sh_addr - addr; break; - case R_ARM_GOT32: + case R_ARM_GOTOFF32: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_ARM_GOT_BREL: /* we load the got offset */ *(int *)ptr += s1->got_offsets[sym_index]; break; @@ -856,17 +872,17 @@ static void build_got_entries(TCCState *s1) } break; #elif defined(TCC_TARGET_ARM) - case R_ARM_GOT32: - case R_ARM_GOTOFF: - case R_ARM_GOTPC: + case R_ARM_GOT_BREL: + case R_ARM_GOTOFF32: + case R_ARM_BASE_PREL: case R_ARM_PLT32: if (!s1->got) build_got(s1); - if (type == R_ARM_GOT32 || type == R_ARM_PLT32) { + if (type == R_ARM_GOT_BREL || type == R_ARM_PLT32) { sym_index = ELF32_R_SYM(rel->r_info); sym = &((Elf32_Sym *)symtab_section->data)[sym_index]; /* look at the symbol got offset. If none, then add one */ - if (type == R_ARM_GOT32) + if (type == R_ARM_GOT_BREL) reloc_type = R_ARM_GLOB_DAT; else reloc_type = R_ARM_JUMP_SLOT; @@ -1081,8 +1097,12 @@ static void tcc_add_linker_symbols(TCCState *s1) #ifdef __FreeBSD__ static char elf_interp[] = "/usr/libexec/ld-elf.so.1"; #else +#ifdef TCC_ARM_EABI +static char elf_interp[] = "/lib/ld-linux.so.3"; +#else static char elf_interp[] = "/lib/ld-linux.so.2"; #endif +#endif static void tcc_output_binary(TCCState *s1, FILE *f, const int *section_order) @@ -1661,7 +1681,12 @@ int tcc_output_file(TCCState *s1, const char *filename) ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; #endif #ifdef TCC_TARGET_ARM +#ifdef TCC_ARM_EABI + ehdr.e_ident[EI_OSABI] = 0; + ehdr.e_flags = 4 << 24; +#else ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM; +#endif #endif switch(file_type) { default: @@ -1833,6 +1858,9 @@ static int tcc_load_object_file(TCCState *s1, /* ignore sections types we do not handle */ if (sh->sh_type != SHT_PROGBITS && sh->sh_type != SHT_REL && +#ifdef TCC_ARM_EABI + sh->sh_type != SHT_ARM_EXIDX && +#endif sh->sh_type != SHT_NOBITS) continue; if (sh->sh_addralign < 1) diff --git a/tcctok.h b/tcctok.h index dab5942..e019476 100644 --- a/tcctok.h +++ b/tcctok.h @@ -121,17 +121,36 @@ #endif /* builtin functions or variables */ +#ifdef TCC_ARM_EABI + DEF(TOK_memcpy, "__aeabi_memcpy") + DEF(TOK_memcpy4, "__aeabi_memcpy4") + DEF(TOK_memcpy8, "__aeabi_memcpy8") + DEF(TOK_memset, "__aeabi_memset") +#else DEF(TOK_memcpy, "memcpy") DEF(TOK_memset, "memset") +#endif DEF(TOK___divdi3, "__divdi3") DEF(TOK___moddi3, "__moddi3") DEF(TOK___udivdi3, "__udivdi3") DEF(TOK___umoddi3, "__umoddi3") #if defined(TCC_TARGET_ARM) - DEF(TOK___divsi3, "__divsi3") DEF(TOK___modsi3, "__modsi3") - DEF(TOK___udivsi3, "__udivsi3") DEF(TOK___umodsi3, "__umodsi3") +#ifdef TCC_ARM_EABI + DEF(TOK___divsi3, "__aeabi_idiv") + DEF(TOK___udivsi3, "__aeabi_uidiv") + DEF(TOK___sardi3, "__aeabi_lasr") + DEF(TOK___shrdi3, "__aeabi_llsr") + DEF(TOK___shldi3, "__aeabi_llsl") + DEF(TOK___slltof, "__aeabi_l2f") + DEF(TOK___slltold, "__aeabi_l2d") + DEF(TOK___fixsfdi, "__aeabi_f2lz") + DEF(TOK___fixdfdi, "__aeabi_d2lz") + DEF(TOK___fixxfdi, "__aeabi_d2lz") +#else + DEF(TOK___divsi3, "__divsi3") + DEF(TOK___udivsi3, "__udivsi3") DEF(TOK___sardi3, "__ashrdi3") DEF(TOK___shrdi3, "__lshrdi3") DEF(TOK___shldi3, "__ashldi3") @@ -142,6 +161,7 @@ DEF(TOK___fixsfdi, "__fixsfdi") DEF(TOK___fixdfdi, "__fixdfdi") DEF(TOK___fixxfdi, "__fixxfdi") +#endif #elif defined(TCC_TARGET_C67) DEF(TOK__divi, "_divi") DEF(TOK__divu, "_divu") @@ -160,12 +180,21 @@ #endif DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control") DEF(TOK___tcc_fpu_control, "__tcc_fpu_control") +#ifdef TCC_ARM_EABI + DEF(TOK___ulltof, "__aeabi_ul2f") + DEF(TOK___ulltod, "__aeabi_ul2d") + DEF(TOK___ulltold, "__aeabi_ul2d") + DEF(TOK___fixunssfdi, "__aeabi_f2ulz") + DEF(TOK___fixunsdfdi, "__aeabi_d2ulz") + DEF(TOK___fixunsxfdi, "__aeabi_d2ulz") +#else DEF(TOK___ulltof, "__ulltof") DEF(TOK___ulltod, "__ulltod") DEF(TOK___ulltold, "__ulltold") DEF(TOK___fixunssfdi, "__fixunssfdi") DEF(TOK___fixunsdfdi, "__fixunsdfdi") DEF(TOK___fixunsxfdi, "__fixunsxfdi") +#endif DEF(TOK___chkstk, "__chkstk") /* bound checking symbols */