2375 lines · 59.5 KB · C
RawDownload
/* *  ARMv4 code generator for TCC * *  Copyright (c) 2003 Daniel Glöckner *  Copyright (c) 2012 Thomas Preud'homme * *  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 */ #ifdef TARGET_DEFS_ONLY #if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP)#error "Currently TinyCC only supports float computation with VFP instructions"#endif /* number of available registers */#ifdef TCC_ARM_VFP#define NB_REGS            13#else#define NB_REGS             9#endif #ifndef CONFIG_TCC_CPUVER# define CONFIG_TCC_CPUVER 5#endif /* 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#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_IRE2    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,#ifdef TCC_ARM_VFP    TREG_F4,    TREG_F5,    TREG_F6,    TREG_F7,#endif    TREG_SP = 13,    TREG_LR,}; #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_IRE2 TREG_R1 /* second word return register (for long long) */#define REG_FRET TREG_F0 /* float return register */ #ifdef TCC_ARM_EABI#define TOK___divdi3 TOK___aeabi_ldivmod#define TOK___moddi3 TOK___aeabi_ldivmod#define TOK___udivdi3 TOK___aeabi_uldivmod#define TOK___umoddi3 TOK___aeabi_uldivmod#endif /* 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 */#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 #if LDOUBLE_SIZE == 8# define TCC_USING_DOUBLE_FOR_LDOUBLE 1#endif /* maximum alignment (for aligned attribute support) */#define MAX_ALIGN     8 #define CHAR_IS_UNSIGNED #define ARM_SOFTFP_FLOAT 0#define ARM_HARD_FLOAT 1 /******************************************************/#else /* ! TARGET_DEFS_ONLY *//******************************************************/#define USING_GLOBALS#include "tcc.h" ST_DATA const char * const target_machine_defs =    "__arm__\0"    "__arm\0"    "arm\0"    "__arm_elf__\0"    "__arm_elf\0"    "arm_elf\0"    "__ARM_ARCH_4__\0"    "__ARMEL__\0"    "__APCS_32__\0"#if defined TCC_ARM_EABI    "__ARM_EABI__\0"#endif    ; ST_DATA const 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,#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 float_abi;static int func_sub_sp_offset, last_itod_magic;static int leaffunc; #if defined(CONFIG_TCC_BCHECK)static addr_t func_bound_offset;static unsigned long func_bound_ind;ST_DATA int func_bound_add_epilog;#endif #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(struct TCCState *s){    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);     float_abi = s->float_abi;#ifndef TCC_ARM_HARDFLOAT// XXX: Works on OpenBSD// # warning "soft float ABI currently not supported: default to softfp"#endif}#else#define func_float_type func_old_type#define func_double_type func_old_type#define func_ldouble_type func_old_typeST_FUNC void arm_init(struct TCCState *s){#if 0#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}#endif #define CHECK_R(r) ((r) >= TREG_R0 && (r) <= TREG_LR) static int two2mask(int a,int b) {  if (!CHECK_R(a) || !CHECK_R(b))    tcc_error("compiler error! registers %i,%i is not valid",a,b);  return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT);} static int regmask(int r) {  if (!CHECK_R(r))    tcc_error("compiler error! register %i is not valid",r);  return reg_classes[r]&~(RC_INT|RC_FLOAT);} /******************************************************/ void o(uint32_t i){  /* this is a good place to start adding big-endian support*/  int ind1;  if (nocode_wanted)    return;  ind1 = ind + 4;  if (!cur_text_section)    tcc_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 uint32_t stuff_const(uint32_t op, uint32_t c){  int try_neg=0;  uint32_t 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 {    uint32_t m;    int i;    if(c<256) /* catch undefined <<32 */      return op|c;    for(i=2;i<32;i+=2) {      m=(0xffu>>i)|(0xffu<<(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(uint32_t op, uint32_t v) {  uint32_t x;  x=stuff_const(op,v);  if(x)    o(x);  else {    uint32_t 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+12:15;j>=i+4;j--)	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+12:15;j>=i+4;j--)	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;j<12;j++)	for(k=i<4?i+12:15;k>=j+4;k--)	  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;j<12;j++)	for(k=i<4?i+12:15;k>=j+4;k--)	  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]));  }} uint32_t encbranch(int pos, int addr, int fail){  addr-=pos+8;  addr/=4;  if(addr>=0x1000000 || addr<-0x1000000) {    if(fail)      tcc_error("FIXME: function bigger than 32MB");    return 0;  }  return 0x0A000000|(addr&0xffffff);} int decbranch(int pos){  int x;  x=*(uint32_t *)(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){  uint32_t *x;  int lt;  while(t) {    x=(uint32_t *)(cur_text_section->data + t);    t=decbranch(lt=t);    if(a==lt+4)      *x=0xE1A00000; // nop    else {      *x &= 0xff000000;      *x |= encbranch(lt,a,1);    }  }} #ifdef TCC_ARM_VFPstatic uint32_t vfpr(int r){  if(r<TREG_F0 || r>TREG_F7)    tcc_error("compiler error! register %i is no vfp register",r);  return r - TREG_F0;}#elsestatic uint32_t fpr(int r){  if(r<TREG_F0 || r>TREG_F3)    tcc_error("compiler error! register %i is no fpa register",r);  return r - TREG_F0;}#endif static uint32_t intr(int r){  if(r == TREG_R12)    return 12;  if(r >= TREG_R0 && r <= TREG_R3)    return r - TREG_R0;  if (!(r >= TREG_SP && r <= TREG_LR))    tcc_error("compiler error! register %i is no int register",r);  return r + (13 - TREG_SP);} static void calcaddr(uint32_t *base, int *off, int *sgn, int maxoff, unsigned shift){  if(*off>maxoff || *off&((1<<shift)-1)) {    uint32_t 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 uint32_t mapcc(int cc){  switch(cc)  {    case TOK_ULT:      return 0x30000000; /* CC/LO */    case TOK_UGE:      return 0x20000000; /* CS/HS */    case TOK_EQ:      return 0x00000000; /* EQ */    case TOK_NE:      return 0x10000000; /* NE */    case TOK_ULE:      return 0x90000000; /* LS */    case TOK_UGT:      return 0x80000000; /* HI */    case TOK_Nset:      return 0x40000000; /* MI */    case TOK_Nclear:      return 0x50000000; /* PL */    case TOK_LT:      return 0xB0000000; /* LT */    case TOK_GE:      return 0xA0000000; /* GE */    case TOK_LE:      return 0xD0000000; /* LE */    case TOK_GT:      return 0xC0000000; /* GT */  }  tcc_error("unexpected condition code");  return 0xE0000000; /* AL */} static int negcc(int cc){  switch(cc)  {

Lines 1–500 of 2375.