?? arm-gen.c
字號:
/* * ARMv4 code generator for TCC * * Copyright (c) 2003 Daniel Gl鯿kner * * Based on i386-gen.c by Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* number of available registers */#define NB_REGS 9/* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does assumptions on it). */#define RC_INT 0x0001 /* generic integer register */#define RC_FLOAT 0x0002 /* generic float register */#define RC_R0 0x0004#define RC_R1 0x0008 #define RC_R2 0x0010#define RC_R3 0x0020#define RC_R12 0x0040#define RC_F0 0x0080#define RC_F1 0x0100#define RC_F2 0x0200#define RC_F3 0x0400#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 *//* pretty names for the registers */enum { TREG_R0 = 0, TREG_R1, TREG_R2, TREG_R3, TREG_R12, TREG_F0, TREG_F1, TREG_F2, TREG_F3,};int reg_classes[NB_REGS] = { /* r0 */ RC_INT | RC_R0, /* r1 */ RC_INT | RC_R1, /* r2 */ RC_INT | RC_R2, /* r3 */ RC_INT | RC_R3, /* r12 */ RC_INT | RC_R12, /* f0 */ RC_FLOAT | RC_F0, /* f1 */ RC_FLOAT | RC_F1, /* f2 */ RC_FLOAT | RC_F2, /* f3 */ RC_FLOAT | RC_F3,};static int two2mask(int a,int b) { return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT);}static int regmask(int r) { return reg_classes[r]&~(RC_INT|RC_FLOAT);}/* 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) */#define REG_FRET TREG_F0 /* float return register *//* defined if function parameters must be evaluated in reverse order */#define INVERT_FUNC_PARAMS/* defined if structures are passed as pointers. Otherwise structures are directly pushed on stack. *///#define FUNC_STRUCT_PARAM_AS_PTR/* pointer size, in bytes */#define PTR_SIZE 4/* long double size and alignment, in bytes */#define LDOUBLE_SIZE 8#define LDOUBLE_ALIGN 4/* maximum alignment (for aligned attribute support) */#define MAX_ALIGN 8#define CHAR_IS_UNSIGNED/******************************************************//* ELF defines */#define EM_TCC_TARGET EM_ARM/* relocation type for 32 bit data relocation */#define R_DATA_32 R_ARM_ABS32#define R_JMP_SLOT R_ARM_JUMP_SLOT#define R_COPY R_ARM_COPY#define ELF_START_ADDR 0x00008000#define ELF_PAGE_SIZE 0x1000/******************************************************/static unsigned long func_sub_sp_offset,last_itod_magic;void o(unsigned long i){ /* this is a good place to start adding big-endian support*/ int ind1; ind1 = ind + 4; if (!cur_text_section) error("compiler error! This happens f.ex. if the compiler\n" "can't evaluate constant expressions outside of a function."); if (ind1 > cur_text_section->data_allocated) section_realloc(cur_text_section, ind1); cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i;}static unsigned long stuff_const(unsigned long op,unsigned long c){ int try_neg=0; unsigned long nc = 0,negop = 0; switch(op&0x1F00000) { case 0x800000: //add case 0x400000: //sub try_neg=1; negop=op^0xC00000; nc=-c; break; case 0x1A00000: //mov case 0x1E00000: //mvn try_neg=1; negop=op^0x400000; nc=~c; break; case 0x200000: //xor if(c==~0) return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000; break; case 0x0: //and if(c==~0) return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000; case 0x1C00000: //bic try_neg=1; negop=op^0x1C00000; nc=~c; break; case 0x1800000: //orr if(c==~0) return (op&0xFFF0FFFF)|0x1E00000; break; } do { unsigned long m; int i; if(c<256) /* catch undefined <<32 */ return op|c; for(i=2;i<32;i+=2) { m=(0xff>>i)|(0xff<<(32-i)); if(!(c&~m)) return op|(i<<7)|(c<<i)|(c>>(32-i)); } op=negop; c=nc; } while(try_neg--); return 0;}//only add,subvoid stuff_const_harder(unsigned long op,unsigned long v) { unsigned long x; x=stuff_const(op,v); if(x) o(x); else { unsigned long a[16],nv,no,o2,n2; int i,j,k; a[0]=0xff; o2=(op&0xfff0ffff)|((op&0xf000)<<4);; for(i=1;i<16;i++) a[i]=(a[i-1]>>2)|(a[i-1]<<30); for(i=0;i<12;i++) for(j=i+4;i<13+i;i++) if((v&(a[i]|a[j]))==v) { o(stuff_const(op,v&a[i])); o(stuff_const(o2,v&a[j])); return; } no=op^0xC00000; n2=o2^0xC00000; nv=-v; for(i=0;i<12;i++) for(j=i+4;i<13+i;i++) if((nv&(a[i]|a[j]))==nv) { o(stuff_const(no,nv&a[i])); o(stuff_const(n2,nv&a[j])); return; } for(i=0;i<8;i++) for(j=i+4;i<12;i++) for(k=j+4;k<13+i;i++) if((v&(a[i]|a[j]|a[k]))==v) { o(stuff_const(op,v&a[i])); o(stuff_const(o2,v&a[j])); o(stuff_const(o2,v&a[k])); return; } no=op^0xC00000; nv=-v; for(i=0;i<8;i++) for(j=i+4;i<12;i++) for(k=j+4;k<13+i;i++) if((nv&(a[i]|a[j]|a[k]))==nv) { o(stuff_const(no,nv&a[i])); o(stuff_const(n2,nv&a[j])); o(stuff_const(n2,nv&a[k])); return; } o(stuff_const(op,v&a[0])); o(stuff_const(o2,v&a[4])); o(stuff_const(o2,v&a[8])); o(stuff_const(o2,v&a[12])); }}unsigned long encbranch(int pos,int addr,int fail){ addr-=pos+8; addr/=4; if(addr>=0x1000000 || addr<-0x1000000) { if(fail) error("FIXME: function bigger than 32MB"); return 0; } return 0x0A000000|(addr&0xffffff);}int decbranch(int pos){ int x; x=*(int *)(cur_text_section->data + pos); x&=0x00ffffff; if(x&0x800000) x-=0x1000000; return x*4+pos+8;}/* output a symbol and patch all calls to it */void gsym_addr(int t, int a){ unsigned long *x; int lt; while(t) { x=(unsigned long *)(cur_text_section->data + t); t=decbranch(lt=t); if(a==lt+4) *x=0xE1A00000; // nop else { *x &= 0xff000000; *x |= encbranch(lt,a,1); } }}void gsym(int t){ gsym_addr(t, ind);}static unsigned long fpr(int r){ if(r<TREG_F0 || r>TREG_F3) error("compiler error! register %i is no fp register\n",r); return r-5;}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); return r;}static void calcaddr(unsigned long *base,int *off,int *sgn,int maxoff,unsigned shift){ if(*off>maxoff || *off&((1<<shift)-1)) { unsigned long x,y; x=0xE280E000; if(*sgn) x=0xE240E000; x|=(*base)<<16; *base=14; // lr y=stuff_const(x,*off&~maxoff); if(y) { o(y); *off&=maxoff; return; } y=stuff_const(x,(*off+maxoff)&~maxoff); if(y) { o(y); *sgn=!*sgn; *off=((*off+maxoff)&~maxoff)-*off; return; } stuff_const_harder(x,*off&~maxoff); *off&=maxoff; }}static unsigned long mapcc(int cc){ switch(cc) { case TOK_ULT: return 0x30000000; case TOK_UGE: return 0x20000000; case TOK_EQ: return 0x00000000; case TOK_NE: return 0x10000000; case TOK_ULE: return 0x90000000; case TOK_UGT: return 0x80000000; case TOK_LT: return 0xB0000000; case TOK_GE: return 0xA0000000; case TOK_LE: return 0xD0000000; case TOK_GT: return 0xC0000000; } error("unexpected condition code"); return 0xE0000000;}static int negcc(int cc){ switch(cc) { case TOK_ULT: return TOK_UGE; case TOK_UGE: return TOK_ULT; case TOK_EQ: return TOK_NE; case TOK_NE: return TOK_EQ; case TOK_ULE: return TOK_UGT; case TOK_UGT: return TOK_ULE; case TOK_LT: return TOK_GE; case TOK_GE: return TOK_LT; case TOK_LE: return TOK_GT; case TOK_GT: return TOK_LE; } error("unexpected condition code"); return TOK_NE;}/* load 'r' from value 'sv' */void load(int r, SValue *sv){ int v, ft, fc, fr, sign; unsigned long op; SValue v1; fr = sv->r; ft = sv->type.t; fc = sv->c.ul; if(fc>=0) sign=0; else { sign=1; fc=-fc; } v = fr & VT_VALMASK; if (fr & VT_LVAL) { unsigned long base=0xB; // fp if(v == VT_LLOCAL) { v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = sv->c.ul; load(base=14 /* lr */, &v1); fc=sign=0; v=VT_LOCAL; } else if(v == VT_CONST) { v1.type.t = VT_PTR; v1.r = fr&~VT_LVAL; v1.c.ul = sv->c.ul; v1.sym=sv->sym; load(base=14, &v1); fc=sign=0; v=VT_LOCAL; } else if(v < VT_CONST) { base=intr(v); fc=sign=0; v=VT_LOCAL; } if(v == VT_LOCAL) { if(is_float(ft)) { calcaddr(&base,&fc,&sign,1020,2); op=0xED100100; if(!sign) op|=0x800000;#if LDOUBLE_SIZE == 8 if ((ft & VT_BTYPE) != VT_FLOAT) op|=0x8000;#else if ((ft & VT_BTYPE) == VT_DOUBLE) op|=0x8000; else if ((ft & VT_BTYPE) == VT_LDOUBLE) op|=0x400000;#endif o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); } else if((ft & VT_TYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_SHORT) { calcaddr(&base,&fc,&sign,255,0); op=0xE1500090; if ((ft & VT_BTYPE) == VT_SHORT) op|=0x20; if ((ft & VT_UNSIGNED) == 0) op|=0x40; if(!sign) op|=0x800000; o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); } else { calcaddr(&base,&fc,&sign,4095,0); op=0xE5100000; if(!sign) op|=0x800000; if ((ft & VT_BTYPE) == VT_BYTE) op|=0x400000; o(op|(intr(r)<<12)|fc|(base<<16)); } return; } } else { if (v == VT_CONST) { op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.ul); if (fr & VT_SYM || !op) { o(0xE59F0000|(intr(r)<<12)); o(0xEA000000); if(fr & VT_SYM) greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); o(sv->c.ul); } else o(op); return; } else if (v == VT_LOCAL) { op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.ul); if (fr & VT_SYM || !op) { o(0xE59F0000|(intr(r)<<12)); o(0xEA000000); if(fr & VT_SYM) // needed ? greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); o(sv->c.ul); o(0xE08B0000|(intr(r)<<12)|intr(r)); } else o(op); return; } else if(v == VT_CMP) { o(mapcc(sv->c.ul)|0x3A00001|(intr(r)<<12)); o(mapcc(negcc(sv->c.ul))|0x3A00000|(intr(r)<<12)); return; } else if (v == VT_JMP || v == VT_JMPI) { int t; t = v & 1; o(0xE3A00000|(intr(r)<<12)|t); o(0xEA000000); gsym(sv->c.ul); o(0xE3A00000|(intr(r)<<12)|(t^1)); return; } else if (v < VT_CONST) { if(is_float(ft)) o(0xEE008180|(fpr(r)<<12)|fpr(v)); else o(0xE1A00000|(intr(r)<<12)|intr(v)); return; } } error("load unimplemented!");}/* store register 'r' in lvalue 'v' */void store(int r, SValue *sv){ SValue v1; int v, ft, fc, fr, sign; unsigned long op; fr = sv->r; ft = sv->type.t; fc = sv->c.ul; if(fc>=0) sign=0; else { sign=1; fc=-fc; } v = fr & VT_VALMASK; if (fr & VT_LVAL || fr == VT_LOCAL) { unsigned long base=0xb; if(v < VT_CONST) { base=intr(v); v=VT_LOCAL; fc=sign=0; } else if(v == VT_CONST) { v1.type.t = ft; v1.r = fr&~VT_LVAL; v1.c.ul = sv->c.ul; v1.sym=sv->sym; load(base=14, &v1); fc=sign=0; v=VT_LOCAL; } if(v == VT_LOCAL) { if(is_float(ft)) { calcaddr(&base,&fc,&sign,1020,2); op=0xED000100; if(!sign) op|=0x800000;#if LDOUBLE_SIZE == 8 if ((ft & VT_BTYPE) != VT_FLOAT) op|=0x8000;#else if ((ft & VT_BTYPE) == VT_DOUBLE) op|=0x8000; if ((ft & VT_BTYPE) == VT_LDOUBLE) op|=0x400000;#endif o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); return; } else if((ft & VT_BTYPE) == VT_SHORT) { calcaddr(&base,&fc,&sign,255,0); op=0xE14000B0; if(!sign) op|=0x800000; o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); } else { calcaddr(&base,&fc,&sign,4095,0); op=0xE5000000; if(!sign) op|=0x800000; if ((ft & VT_BTYPE) == VT_BYTE) op|=0x400000; o(op|(intr(r)<<12)|fc|(base<<16)); } return; } } error("store unimplemented");}static void gadd_sp(int val){ stuff_const_harder(0xE28DD000,val);}/* 'is_jmp' is '1' if it is a jump */static void gcall_or_jmp(int is_jmp){ int r; if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { unsigned long x; /* constant case */ x=encbranch(ind,ind+vtop->c.ul,0); if(x) { if (vtop->r & VT_SYM) { /* relocation case */ greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); } else put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, 0); o(x|(is_jmp?0xE0000000:0xE1000000)); } else { if(!is_jmp) o(0xE28FE004); // add lr,pc,#4 o(0xE51FF004); // ldr pc,[pc,#-4] if (vtop->r & VT_SYM) greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); o(vtop->c.ul); } } else { /* otherwise, indirect call */ r = gv(RC_INT); if(!is_jmp) o(0xE1A0E00F); // mov lr,pc o(0xE1A0F000|intr(r)); // mov pc,r }}/* 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 size, align, r, args_size, i; Sym *func_sym; signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; int todo=0xf, keep, plan2[4]={0,0,0,0}; r = vtop->r & VT_VALMASK; if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); args_size = 0; for(i = nb_args ; i-- && args_size < 16 ;) { if ((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop[-i].type, &align); size = (size + 3) & ~3; 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 { plan[nb_args-1-i][0]=args_size/4; args_size += 4; if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { plan[nb_args-1-i][1]=args_size/4; args_size += 4; } } } args_size = keep = 0; for(i = 0;i < nb_args; i++) { vnrott(keep+1); if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); /* align to stack align size */ size = (size + 3) & ~3; /* 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)) { 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)); vtop--; args_size += size; } else { int s;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -