| @@ -0,0 +1,1293 @@ |
| | 1 | /* |
| | 2 | * ARMv4 code generator for TCC |
| | 3 | * |
| | 4 | * Copyright (c) 2003 Daniel Gl�ckner |
| | 5 | * |
| | 6 | * Based on i386-gen.c by Fabrice Bellard |
| | 7 | * |
| | 8 | * This library is free software; you can redistribute it and/or |
| | 9 | * modify it under the terms of the GNU Lesser General Public |
| | 10 | * License as published by the Free Software Foundation; either |
| | 11 | * version 2 of the License, or (at your option) any later version. |
| | 12 | * |
| | 13 | * This library is distributed in the hope that it will be useful, |
| | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | 16 | * Lesser General Public License for more details. |
| | 17 | * |
| | 18 | * You should have received a copy of the GNU Lesser General Public |
| | 19 | * License along with this library; if not, write to the Free Software |
| | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| | 21 | */ |
| | 22 | |
| | 23 | /* number of available registers */ |
| | 24 | #define NB_REGS 9 |
| | 25 | |
| | 26 | /* a register can belong to several classes. The classes must be |
| | 27 | sorted from more general to more precise (see gv2() code which does |
| | 28 | assumptions on it). */ |
| | 29 | #define RC_INT 0x0001 /* generic integer register */ |
| | 30 | #define RC_FLOAT 0x0002 /* generic float register */ |
| | 31 | #define RC_R0 0x0004 |
| | 32 | #define RC_R1 0x0008 |
| | 33 | #define RC_R2 0x0010 |
| | 34 | #define RC_R3 0x0020 |
| | 35 | #define RC_R12 0x0040 |
| | 36 | #define RC_F0 0x0080 |
| | 37 | #define RC_F1 0x0100 |
| | 38 | #define RC_F2 0x0200 |
| | 39 | #define RC_F3 0x0400 |
| | 40 | #define RC_IRET RC_R0 /* function return: integer register */ |
| | 41 | #define RC_LRET RC_R1 /* function return: second integer register */ |
| | 42 | #define RC_FRET RC_F0 /* function return: float register */ |
| | 43 | |
| | 44 | /* pretty names for the registers */ |
| | 45 | enum { |
| | 46 | TREG_R0 = 0, |
| | 47 | TREG_R1, |
| | 48 | TREG_R2, |
| | 49 | TREG_R3, |
| | 50 | TREG_R12, |
| | 51 | TREG_F0, |
| | 52 | TREG_F1, |
| | 53 | TREG_F2, |
| | 54 | TREG_F3, |
| | 55 | }; |
| | 56 | |
| | 57 | int reg_classes[NB_REGS] = { |
| | 58 | /* r0 */ RC_INT | RC_R0, |
| | 59 | /* r1 */ RC_INT | RC_R1, |
| | 60 | /* r2 */ RC_INT | RC_R2, |
| | 61 | /* r3 */ RC_INT | RC_R3, |
| | 62 | /* r12 */ RC_INT | RC_R12, |
| | 63 | /* f0 */ RC_FLOAT | RC_F0, |
| | 64 | /* f1 */ RC_FLOAT | RC_F1, |
| | 65 | /* f2 */ RC_FLOAT | RC_F2, |
| | 66 | /* f3 */ RC_FLOAT | RC_F3, |
| | 67 | }; |
| | 68 | |
| | 69 | static int two2mask(int a,int b) { |
| | 70 | return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT); |
| | 71 | } |
| | 72 | |
| | 73 | static int regmask(int r) { |
| | 74 | return reg_classes[r]&~(RC_INT|RC_FLOAT); |
| | 75 | } |
| | 76 | |
| | 77 | /* return registers for function */ |
| | 78 | #define REG_IRET TREG_R0 /* single word int return register */ |
| | 79 | #define REG_LRET TREG_R1 /* second word return register (for long long) */ |
| | 80 | #define REG_FRET TREG_F0 /* float return register */ |
| | 81 | |
| | 82 | /* defined if function parameters must be evaluated in reverse order */ |
| | 83 | #define INVERT_FUNC_PARAMS |
| | 84 | |
| | 85 | /* defined if structures are passed as pointers. Otherwise structures |
| | 86 | are directly pushed on stack. */ |
| | 87 | //#define FUNC_STRUCT_PARAM_AS_PTR |
| | 88 | |
| | 89 | /* pointer size, in bytes */ |
| | 90 | #define PTR_SIZE 4 |
| | 91 | |
| | 92 | /* long double size and alignment, in bytes */ |
| | 93 | #define LDOUBLE_SIZE 8 |
| | 94 | #define LDOUBLE_ALIGN 4 |
| | 95 | /* maximum alignment (for aligned attribute support) */ |
| | 96 | #define MAX_ALIGN 8 |
| | 97 | |
| | 98 | #define CHAR_IS_UNSIGNED |
| | 99 | |
| | 100 | /******************************************************/ |
| | 101 | /* ELF defines */ |
| | 102 | |
| | 103 | #define EM_TCC_TARGET EM_ARM |
| | 104 | |
| | 105 | /* relocation type for 32 bit data relocation */ |
| | 106 | #define R_DATA_32 R_ARM_ABS32 |
| | 107 | #define R_JMP_SLOT R_ARM_JUMP_SLOT |
| | 108 | #define R_COPY R_ARM_COPY |
| | 109 | |
| | 110 | #define ELF_START_ADDR 0x00008000 |
| | 111 | #define ELF_PAGE_SIZE 0x1000 |
| | 112 | |
| | 113 | /******************************************************/ |
| | 114 | static unsigned long func_sub_sp_offset,last_itod_magic; |
| | 115 | |
| | 116 | void o(unsigned long i) |
| | 117 | { |
| | 118 | /* this is a good place to start adding big-endian support*/ |
| | 119 | int ind1; |
| | 120 | |
| | 121 | ind1 = ind + 4; |
| | 122 | if (ind1 > cur_text_section->data_allocated) |
| | 123 | section_realloc(cur_text_section, ind1); |
| | 124 | cur_text_section->data[ind++] = i&255; |
| | 125 | i>>=8; |
| | 126 | cur_text_section->data[ind++] = i&255; |
| | 127 | i>>=8; |
| | 128 | cur_text_section->data[ind++] = i&255; |
| | 129 | i>>=8; |
| | 130 | cur_text_section->data[ind++] = i; |
| | 131 | } |
| | 132 | |
| | 133 | static unsigned long stuff_const(unsigned long op,unsigned long c) |
| | 134 | { |
| | 135 | int try_neg=0; |
| | 136 | unsigned long nc,negop; |
| | 137 | switch(op&0x1F00000) |
| | 138 | { |
| | 139 | case 0x800000: //add |
| | 140 | case 0x400000: //sub |
| | 141 | try_neg=1; |
| | 142 | negop=op^0xC00000; |
| | 143 | nc=-c; |
| | 144 | break; |
| | 145 | case 0x1A00000: //mov |
| | 146 | case 0x1E00000: //mvn |
| | 147 | try_neg=1; |
| | 148 | negop=op^0x400000; |
| | 149 | nc=~c; |
| | 150 | break; |
| | 151 | case 0x200000: //xor |
| | 152 | if(c==~0) |
| | 153 | return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000; |
| | 154 | break; |
| | 155 | case 0x0: //and |
| | 156 | if(c==~0) |
| | 157 | return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000; |
| | 158 | case 0x1C00000: //bic |
| | 159 | try_neg=1; |
| | 160 | negop=op^0x1C00000; |
| | 161 | nc=~c; |
| | 162 | break; |
| | 163 | case 0x1800000: //orr |
| | 164 | if(c==~0) |
| | 165 | return (op&0xFFF0FFFF)|0x1E00000; |
| | 166 | break; |
| | 167 | } |
| | 168 | do { |
| | 169 | unsigned long m; |
| | 170 | int i; |
| | 171 | if(c<256) /* catch undefined <<32 */ |
| | 172 | return op|c; |
| | 173 | for(i=2;i<32;i+=2) { |
| | 174 | m=(0xff>>i)|(0xff<<(32-i)); |
| | 175 | if(!(c&~m)) |
| | 176 | return op|(i<<7)|(c<<i)|(c>>(32-i)); |
| | 177 | } |
| | 178 | op=negop; |
| | 179 | c=nc; |
| | 180 | } while(try_neg--); |
| | 181 | return 0; |
| | 182 | } |
| | 183 | |
| | 184 | |
| | 185 | //only add,sub |
| | 186 | void stuff_const_harder(unsigned long op,unsigned long v) { |
| | 187 | unsigned long x; |
| | 188 | x=stuff_const(op,v); |
| | 189 | if(x) |
| | 190 | o(x); |
| | 191 | else { |
| | 192 | unsigned long a[16],nv,no,o2,n2; |
| | 193 | int i,j,k; |
| | 194 | a[0]=0xff; |
| | 195 | o2=(op&0xfff0ffff)|((op&0xf000)<<4);; |
| | 196 | for(i=1;i<16;i++) |
| | 197 | a[i]=(a[i-1]>>2)|(a[i-1]<<30); |
| | 198 | for(i=0;i<12;i++) |
| | 199 | for(j=i+4;i<13+i;i++) |
| | 200 | if((v&(a[i]|a[j]))==v) { |
| | 201 | o(stuff_const(op,v&a[i])); |
| | 202 | o(stuff_const(o2,v&a[j])); |
| | 203 | return; |
| | 204 | } |
| | 205 | no=op^0xC00000; |
| | 206 | n2=o2^0xC00000; |
| | 207 | nv=-v; |
| | 208 | for(i=0;i<12;i++) |
| | 209 | for(j=i+4;i<13+i;i++) |
| | 210 | if((nv&(a[i]|a[j]))==nv) { |
| | 211 | o(stuff_const(no,nv&a[i])); |
| | 212 | o(stuff_const(n2,nv&a[j])); |
| | 213 | return; |
| | 214 | } |
| | 215 | for(i=0;i<8;i++) |
| | 216 | for(j=i+4;i<12;i++) |
| | 217 | for(k=j+4;k<13+i;i++) |
| | 218 | if((v&(a[i]|a[j]|a[k]))==v) { |
| | 219 | o(stuff_const(op,v&a[i])); |
| | 220 | o(stuff_const(o2,v&a[j])); |
| | 221 | o(stuff_const(o2,v&a[k])); |
| | 222 | return; |
| | 223 | } |
| | 224 | no=op^0xC00000; |
| | 225 | nv=-v; |
| | 226 | for(i=0;i<8;i++) |
| | 227 | for(j=i+4;i<12;i++) |
| | 228 | for(k=j+4;k<13+i;i++) |
| | 229 | if((nv&(a[i]|a[j]|a[k]))==nv) { |
| | 230 | o(stuff_const(no,nv&a[i])); |
| | 231 | o(stuff_const(n2,nv&a[j])); |
| | 232 | o(stuff_const(n2,nv&a[k])); |
| | 233 | return; |
| | 234 | } |
| | 235 | o(stuff_const(op,v&a[0])); |
| | 236 | o(stuff_const(o2,v&a[4])); |
| | 237 | o(stuff_const(o2,v&a[8])); |
| | 238 | o(stuff_const(o2,v&a[12])); |
| | 239 | } |
| | 240 | } |
| | 241 | |
| | 242 | unsigned long encbranch(int pos,int addr,int fail) |
| | 243 | { |
| | 244 | addr-=pos+8; |
| | 245 | addr/=4; |
| | 246 | if(addr>=0x1000000 || addr<-0x1000000) { |
| | 247 | if(fail) |
| | 248 | error("FIXME: function bigger than 32MB"); |
| | 249 | return 0; |
| | 250 | } |
| | 251 | return 0x0A000000|(addr&0xffffff); |
| | 252 | } |
| | 253 | |
| | 254 | int decbranch(int pos) |
| | 255 | { |
| | 256 | int x; |
| | 257 | x=*(int *)(cur_text_section->data + pos); |
| | 258 | x&=0x00ffffff; |
| | 259 | if(x&0x800000) |
| | 260 | x-=0x1000000; |
| | 261 | return x*4+pos+8; |
| | 262 | } |
| | 263 | |
| | 264 | /* output a symbol and patch all calls to it */ |
| | 265 | void gsym_addr(int t, int a) |
| | 266 | { |
| | 267 | unsigned long *x; |
| | 268 | int lt; |
| | 269 | while(t) { |
| | 270 | x=(unsigned long *)(cur_text_section->data + t); |
| | 271 | t=decbranch(lt=t); |
| | 272 | if(a==lt+4) |
| | 273 | *x=0xE1A00000; // nop |
| | 274 | else { |
| | 275 | *x &= 0xff000000; |
| | 276 | *x |= encbranch(lt,a,1); |
| | 277 | } |
| | 278 | } |
| | 279 | } |
| | 280 | |
| | 281 | void gsym(int t) |
| | 282 | { |
| | 283 | gsym_addr(t, ind); |
| | 284 | } |
| | 285 | |
| | 286 | static unsigned long fpr(int r) |
| | 287 | { |
| | 288 | return r-5; |
| | 289 | } |
| | 290 | |
| | 291 | static unsigned long intr(int r) |
| | 292 | { |
| | 293 | return r==4?12:r; |
| | 294 | } |
| | 295 | |
| | 296 | static void calcaddr(unsigned long *base,int *off,int *sgn,int maxoff,unsigned shift) |
| | 297 | { |
| | 298 | if(*off>maxoff || *off&((1<<shift)-1)) { |
| | 299 | unsigned long x,y; |
| | 300 | x=0xE280E000; |
| | 301 | if(*sgn) |
| | 302 | x=0xE240E000; |
| | 303 | x|=(*base)<<16; |
| | 304 | *base=14; // lr |
| | 305 | y=stuff_const(x,*off&~maxoff); |
| | 306 | if(y) { |
| | 307 | o(y); |
| | 308 | *off&=maxoff; |
| | 309 | return; |
| | 310 | } |
| | 311 | y=stuff_const(x,(*off+maxoff)&~maxoff); |
| | 312 | if(y) { |
| | 313 | o(y); |
| | 314 | *sgn=!*sgn; |
| | 315 | *off=((*off+maxoff)&~maxoff)-*off; |
| | 316 | return; |
| | 317 | } |
| | 318 | stuff_const_harder(x,*off&~maxoff); |
| | 319 | *off&=maxoff; |
| | 320 | } |
| | 321 | } |
| | 322 | |
| | 323 | static unsigned long mapcc(int cc) |
| | 324 | { |
| | 325 | switch(cc) |
| | 326 | { |
| | 327 | case TOK_ULT: |
| | 328 | return 0x30000000; |
| | 329 | case TOK_UGE: |
| | 330 | return 0x20000000; |
| | 331 | case TOK_EQ: |
| | 332 | return 0x00000000; |
| | 333 | case TOK_NE: |
| | 334 | return 0x10000000; |
| | 335 | case TOK_ULE: |
| | 336 | return 0x90000000; |
| | 337 | case TOK_UGT: |
| | 338 | return 0x80000000; |
| | 339 | case TOK_LT: |
| | 340 | return 0xB0000000; |
| | 341 | case TOK_GE: |
| | 342 | return 0xA0000000; |
| | 343 | case TOK_LE: |
| | 344 | return 0xD0000000; |
| | 345 | case TOK_GT: |
| | 346 | return 0xC0000000; |
| | 347 | } |
| | 348 | error("unexpected condition code"); |
| | 349 | return 0xE0000000; |
| | 350 | } |
| | 351 | |
| | 352 | static int negcc(int cc) |
| | 353 | { |
| | 354 | switch(cc) |
| | 355 | { |
| | 356 | case TOK_ULT: |
| | 357 | return TOK_UGE; |
| | 358 | case TOK_UGE: |
| | 359 | return TOK_ULT; |
| | 360 | case TOK_EQ: |
| | 361 | return TOK_NE; |
| | 362 | case TOK_NE: |
| | 363 | return TOK_EQ; |
| | 364 | case TOK_ULE: |
| | 365 | return TOK_UGT; |
| | 366 | case TOK_UGT: |
| | 367 | return TOK_ULE; |
| | 368 | case TOK_LT: |
| | 369 | return TOK_GE; |
| | 370 | case TOK_GE: |
| | 371 | return TOK_LT; |
| | 372 | case TOK_LE: |
| | 373 | return TOK_GT; |
| | 374 | case TOK_GT: |
| | 375 | return TOK_LE; |
| | 376 | } |
| | 377 | error("unexpected condition code"); |
| | 378 | return TOK_NE; |
| | 379 | } |
| | 380 | |
| | 381 | /* load 'r' from value 'sv' */ |
| | 382 | void load(int r, SValue *sv) |
| | 383 | { |
| | 384 | int v, ft, fc, fr, sign; |
| | 385 | unsigned long op; |
| | 386 | SValue v1; |
| | 387 | |
| | 388 | fr = sv->r; |
| | 389 | ft = sv->type.t; |
| | 390 | fc = sv->c.ul; |
| | 391 | |
| | 392 | if(fc>=0) |
| | 393 | sign=0; |
| | 394 | else { |
| | 395 | sign=1; |
| | 396 | fc=-fc; |
| | 397 | } |
| | 398 | |
| | 399 | v = fr & VT_VALMASK; |
| | 400 | if (fr & VT_LVAL) { |
| | 401 | unsigned long base=0xB; // fp |
| | 402 | if(v == VT_LLOCAL) { |
| | 403 | v1.type.t = VT_PTR; |
| | 404 | v1.r = VT_LOCAL | VT_LVAL; |
| | 405 | v1.c.ul = sv->c.ul; |
| | 406 | load(base=14 /* lr */, &v1); |
| | 407 | fc=sign=0; |
| | 408 | v=VT_LOCAL; |
| | 409 | } else if(v == VT_CONST) { |
| | 410 | v1.type.t = VT_PTR; |
| | 411 | v1.r = fr&~VT_LVAL; |
| | 412 | v1.c.ul = sv->c.ul; |
| | 413 | v1.sym=sv->sym; |
| | 414 | load(base=14, &v1); |
| | 415 | fc=sign=0; |
| | 416 | v=VT_LOCAL; |
| | 417 | } else if(v < VT_CONST) { |
| | 418 | base=intr(v); |
| | 419 | fc=sign=0; |
| | 420 | v=VT_LOCAL; |
| | 421 | } |
| | 422 | if(v == VT_LOCAL) { |
| | 423 | if(is_float(ft)) { |
| | 424 | calcaddr(&base,&fc,&sign,1020,2); |
| | 425 | op=0xED100100; |
| | 426 | if(!sign) |
| | 427 | op|=0x800000; |
| | 428 | if ((ft & VT_BTYPE) != VT_FLOAT) |
| | 429 | op|=0x8000; |
| | 430 | o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); |
| | 431 | } else if((ft & VT_TYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_SHORT) { |
| | 432 | calcaddr(&base,&fc,&sign,255,0); |
| | 433 | op=0xE1500090; |
| | 434 | if ((ft & VT_BTYPE) == VT_SHORT) |
| | 435 | op|=0x20; |
| | 436 | if ((ft & VT_UNSIGNED) == 0) |
| | 437 | op|=0x40; |
| | 438 | if(!sign) |
| | 439 | op|=0x800000; |
| | 440 | o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); |
| | 441 | } else { |
| | 442 | calcaddr(&base,&fc,&sign,4095,0); |
| | 443 | op=0xE5100000; |
| | 444 | if(!sign) |
| | 445 | op|=0x800000; |
| | 446 | if ((ft & VT_BTYPE) == VT_BYTE) |
| | 447 | op|=0x400000; |
| | 448 | o(op|(intr(r)<<12)|fc|(base<<16)); |
| | 449 | } |
| | 450 | return; |
| | 451 | } |
| | 452 | } else { |
| | 453 | if (v == VT_CONST) { |
| | 454 | op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.ul); |
| | 455 | if (fr & VT_SYM || !op) { |
| | 456 | o(0xE59F0000|(intr(r)<<12)); |
| | 457 | o(0xEA000000); |
| | 458 | if(fr & VT_SYM) |
| | 459 | greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); |
| | 460 | o(sv->c.ul); |
| | 461 | } else |
| | 462 | o(op); |
| | 463 | return; |
| | 464 | } else if (v == VT_LOCAL) { |
| | 465 | op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.ul); |
| | 466 | if (fr & VT_SYM || !op) { |
| | 467 | o(0xE59F0000|(intr(r)<<12)); |
| | 468 | o(0xEA000000); |
| | 469 | if(fr & VT_SYM) // needed ? |
| | 470 | greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); |
| | 471 | o(sv->c.ul); |
| | 472 | o(0xE08B0000|(intr(r)<<12)|intr(r)); |
| | 473 | } else |
| | 474 | o(op); |
| | 475 | return; |
| | 476 | } else if(v == VT_CMP) { |
| | 477 | o(mapcc(sv->c.ul)|0x3A00001|(intr(r)<<12)); |
| | 478 | o(mapcc(negcc(sv->c.ul))|0x3A00000|(intr(r)<<12)); |
| | 479 | return; |
| | 480 | } else if (v == VT_JMP || v == VT_JMPI) { |
| | 481 | int t; |
| | 482 | t = v & 1; |
| | 483 | o(0xE3A00000|(intr(r)<<12)|t); |
| | 484 | o(0xEA000000); |
| | 485 | gsym(sv->c.ul); |
| | 486 | o(0xE3A00000|(intr(r)<<12)|(t^1)); |
| | 487 | return; |
| | 488 | } else if (v < VT_CONST) { |
| | 489 | if(is_float(ft)) |
| | 490 | o(0xEE008180|(fpr(r)<<12)|fpr(v)); |
| | 491 | else |
| | 492 | o(0xE1A00000|(intr(r)<<12)|intr(v)); |
| | 493 | return; |
| | 494 | } |
| | 495 | } |
| | 496 | error("load unimplemented!"); |
| | 497 | } |
| | 498 | |
| | 499 | /* store register 'r' in lvalue 'v' */ |
| | 500 | void store(int r, SValue *sv) |
| | 501 | { |
| | 502 | SValue v1; |
| | 503 | int v, ft, fc, fr, sign; |
| | 504 | unsigned long op; |
| | 505 | |
| | 506 | fr = sv->r; |
| | 507 | ft = sv->type.t; |
| | 508 | fc = sv->c.ul; |
| | 509 | |
| | 510 | if(fc>=0) |
| | 511 | sign=0; |
| | 512 | else { |
| | 513 | sign=1; |
| | 514 | fc=-fc; |
| | 515 | } |
| | 516 | |
| | 517 | v = fr & VT_VALMASK; |
| | 518 | if (fr & VT_LVAL || fr == VT_LOCAL) { |
| | 519 | unsigned long base=0xb; |
| | 520 | if(v < VT_CONST) { |
| | 521 | base=intr(v); |
| | 522 | v=VT_LOCAL; |
| | 523 | fc=sign=0; |
| | 524 | } else if(v == VT_CONST) { |
| | 525 | v1.type.t = ft; |
| | 526 | v1.r = fr&~VT_LVAL; |
| | 527 | v1.c.ul = sv->c.ul; |
| | 528 | v1.sym=sv->sym; |
| | 529 | load(base=14, &v1); |
| | 530 | fc=sign=0; |
| | 531 | v=VT_LOCAL; |
| | 532 | } |
| | 533 | if(v == VT_LOCAL) { |
| | 534 | if(is_float(ft)) { |
| | 535 | calcaddr(&base,&fc,&sign,1020,2); |
| | 536 | op=0xED000100; |
| | 537 | if(!sign) |
| | 538 | op|=0x800000; |
| | 539 | if ((ft & VT_BTYPE) != VT_FLOAT) |
| | 540 | op|=0x8000; |
| | 541 | o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); |
| | 542 | return; |
| | 543 | } else if((ft & VT_BTYPE) == VT_SHORT) { |
| | 544 | calcaddr(&base,&fc,&sign,255,0); |
| | 545 | op=0xE14000B0; |
| | 546 | if(!sign) |
| | 547 | op|=0x800000; |
| | 548 | o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); |
| | 549 | } else { |
| | 550 | calcaddr(&base,&fc,&sign,4095,0); |
| | 551 | op=0xE5000000; |
| | 552 | if(!sign) |
| | 553 | op|=0x800000; |
| | 554 | if ((ft & VT_BTYPE) == VT_BYTE) |
| | 555 | op|=0x400000; |
| | 556 | o(op|(intr(r)<<12)|fc|(base<<16)); |
| | 557 | } |
| | 558 | return; |
| | 559 | } |
| | 560 | } |
| | 561 | error("store unimplemented"); |
| | 562 | } |
| | 563 | |
| | 564 | static void gadd_sp(int val) |
| | 565 | { |
| | 566 | stuff_const_harder(0xE28DD000,val); |
| | 567 | } |
| | 568 | |
| | 569 | /* 'is_jmp' is '1' if it is a jump */ |
| | 570 | static void gcall_or_jmp(int is_jmp) |
| | 571 | { |
| | 572 | int r; |
| | 573 | if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
| | 574 | unsigned long x; |
| | 575 | /* constant case */ |
| | 576 | x=encbranch(ind,ind+vtop->c.ul,0); |
| | 577 | if(x) { |
| | 578 | if (vtop->r & VT_SYM) { |
| | 579 | /* relocation case */ |
| | 580 | greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); |
| | 581 | } else |
| | 582 | put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, 0); |
| | 583 | o(x|(is_jmp?0xE0000000:0xE1000000)); |
| | 584 | } else { |
| | 585 | if(!is_jmp) |
| | 586 | o(0xE28FE004); // add lr,pc,#4 |
| | 587 | o(0xE51FF004); // ldr pc,[pc,#-4] |
| | 588 | if (vtop->r & VT_SYM) |
| | 589 | greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); |
| | 590 | o(vtop->c.ul); |
| | 591 | } |
| | 592 | } else { |
| | 593 | /* otherwise, indirect call */ |
| | 594 | r = gv(RC_INT); |
| | 595 | if(!is_jmp) |
| | 596 | o(0xE1A0E00F); // mov lr,pc |
| | 597 | o(0xE1A0F000|intr(r)); // mov pc,r |
| | 598 | } |
| | 599 | } |
| | 600 | |
| | 601 | /* Generate function call. The function address is pushed first, then |
| | 602 | all the parameters in call order. This functions pops all the |
| | 603 | parameters and the function address. */ |
| | 604 | void gfunc_call(int nb_args) |
| | 605 | { |
| | 606 | int size, align, r, args_size, i; |
| | 607 | Sym *func_sym; |
| | 608 | |
| | 609 | args_size = 0; |
| | 610 | for(i = 0;i < nb_args; i++) { |
| | 611 | if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { |
| | 612 | size = type_size(&vtop->type, &align); |
| | 613 | /* align to stack align size */ |
| | 614 | size = (size + 3) & ~3; |
| | 615 | /* allocate the necessary size on stack */ |
| | 616 | gadd_sp(-size); |
| | 617 | /* generate structure store */ |
| | 618 | r = get_reg(RC_INT); |
| | 619 | o(0xE1A0000D|(intr(r)<<12)); |
| | 620 | vset(&vtop->type, r | VT_LVAL, 0); |
| | 621 | vswap(); |
| | 622 | vstore(); |
| | 623 | args_size += size; |
| | 624 | } else if (is_float(vtop->type.t)) { |
| | 625 | r=fpr(gv(RC_FLOAT))<<12; |
| | 626 | if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) |
| | 627 | size = 4; |
| | 628 | else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) |
| | 629 | size = 8; |
| | 630 | else |
| | 631 | size = LDOUBLE_SIZE; |
| | 632 | |
| | 633 | if (size == 12) |
| | 634 | r|=0x400000; |
| | 635 | else if(size == 8) |
| | 636 | r|=0x8000; |
| | 637 | |
| | 638 | o(0xED2D0100|r); |
| | 639 | args_size += size; |
| | 640 | } else { |
| | 641 | /* simple type (currently always same size) */ |
| | 642 | /* XXX: implicit cast ? */ |
| | 643 | r = gv(RC_INT); |
| | 644 | if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
| | 645 | size = 8; |
| | 646 | o(0xE52D0004|(intr(vtop->r2)<<12)); /* str r2,[sp,#-4]! */ |
| | 647 | } else { |
| | 648 | size = 4; |
| | 649 | } |
| | 650 | o(0xE52D0004|(intr(vtop->r)<<12)); /* str r,[sp,#-4]! */ |
| | 651 | args_size += size; |
| | 652 | } |
| | 653 | vtop--; |
| | 654 | } |
| | 655 | save_regs(0); /* save used temporary registers */ |
| | 656 | if(args_size) { |
| | 657 | int n; |
| | 658 | n=args_size/4; |
| | 659 | if(n>4) |
| | 660 | n=4; |
| | 661 | o(0xE8BD0000|((1<<n)-1)); |
| | 662 | args_size-=n*4; |
| | 663 | } |
| | 664 | func_sym = vtop->type.ref; |
| | 665 | gcall_or_jmp(0); |
| | 666 | if (args_size) |
| | 667 | gadd_sp(args_size); |
| | 668 | vtop--; |
| | 669 | } |
| | 670 | |
| | 671 | /* generate function prolog of type 't' */ |
| | 672 | void gfunc_prolog(CType *func_type) |
| | 673 | { |
| | 674 | Sym *sym,*sym2; |
| | 675 | int n,addr,size,align; |
| | 676 | |
| | 677 | sym = func_type->ref; |
| | 678 | func_vt = sym->type; |
| | 679 | |
| | 680 | n=0; |
| | 681 | addr=12; |
| | 682 | if((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| | 683 | func_vc = addr; |
| | 684 | addr += 4; |
| | 685 | n++; |
| | 686 | } |
| | 687 | for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { |
| | 688 | size = type_size(&sym2->type, &align); |
| | 689 | size = (size + 3) & ~3; |
| | 690 | n+=size/4; |
| | 691 | } |
| | 692 | o(0xE1A0C00D); /* mov ip,sp */ |
| | 693 | if(func_type->ref->c == FUNC_ELLIPSIS) |
| | 694 | n=4; |
| | 695 | if(n) { |
| | 696 | if(n>4) |
| | 697 | n=4; |
| | 698 | o(0xE92D0000|((1<<n)-1)); /* save r0-r4 on stack if needed */ |
| | 699 | } |
| | 700 | o(0xE92D5800); /* save fp, ip, lr*/ |
| | 701 | o(0xE1A0B00D); /* mov fp,sp */ |
| | 702 | func_sub_sp_offset = ind; |
| | 703 | o(0xE1A00000); /* nop, leave space for stack adjustment */ |
| | 704 | while ((sym = sym->next)) { |
| | 705 | CType *type; |
| | 706 | type = &sym->type; |
| | 707 | sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr); |
| | 708 | size = type_size(type, &align); |
| | 709 | size = (size + 3) & ~3; |
| | 710 | addr += size; |
| | 711 | } |
| | 712 | last_itod_magic=0; |
| | 713 | } |
| | 714 | |
| | 715 | /* generate function epilog */ |
| | 716 | void gfunc_epilog(void) |
| | 717 | { |
| | 718 | unsigned long x; |
| | 719 | o(0xE89BA800); /* restore fp, sp, pc */ |
| | 720 | if(loc) { |
| | 721 | x=stuff_const(0xE24DD000, (-loc + 3) & -4); /* sub sp,sp,# */ |
| | 722 | if(x) |
| | 723 | *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = x; |
| | 724 | else { |
| | 725 | unsigned long addr; |
| | 726 | addr=ind; |
| | 727 | o(0xE59FC004); /* ldr ip,[pc+4] */ |
| | 728 | o(0xE04DD00C); /* sub sp,sp,ip */ |
| | 729 | o(0xE1A0F00E); /* mov pc,lr */ |
| | 730 | o((-loc + 3) & -4); |
| | 731 | *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); |
| | 732 | } |
| | 733 | } |
| | 734 | } |
| | 735 | |
| | 736 | /* generate a jump to a label */ |
| | 737 | int gjmp(int t) |
| | 738 | { |
| | 739 | int r; |
| | 740 | r=ind; |
| | 741 | o(0xE0000000|encbranch(r,t,1)); |
| | 742 | return r; |
| | 743 | } |
| | 744 | |
| | 745 | /* generate a jump to a fixed address */ |
| | 746 | void gjmp_addr(int a) |
| | 747 | { |
| | 748 | gjmp(a); |
| | 749 | } |
| | 750 | |
| | 751 | /* generate a test. set 'inv' to invert test. Stack entry is popped */ |
| | 752 | int gtst(int inv, int t) |
| | 753 | { |
| | 754 | int v, r; |
| | 755 | unsigned long op; |
| | 756 | v = vtop->r & VT_VALMASK; |
| | 757 | r=ind; |
| | 758 | if (v == VT_CMP) { |
| | 759 | op=mapcc(inv?negcc(vtop->c.i):vtop->c.i); |
| | 760 | op|=encbranch(r,t,1); |
| | 761 | o(op); |
| | 762 | t=r; |
| | 763 | } else if (v == VT_JMP || v == VT_JMPI) { |
| | 764 | if ((v & 1) == inv) { |
| | 765 | if(!vtop->c.i) |
| | 766 | vtop->c.i=t; |
| | 767 | else { |
| | 768 | unsigned long *x; |
| | 769 | int p,lp; |
| | 770 | if(t) { |
| | 771 | p = vtop->c.i; |
| | 772 | do { |
| | 773 | p = decbranch(lp=p); |
| | 774 | } while(p); |
| | 775 | x = (unsigned long *)(cur_text_section->data + lp); |
| | 776 | *x &= 0xff000000; |
| | 777 | *x |= encbranch(lp,t,1); |
| | 778 | } |
| | 779 | t = vtop->c.i; |
| | 780 | } |
| | 781 | } else { |
| | 782 | t = gjmp(t); |
| | 783 | gsym(vtop->c.i); |
| | 784 | } |
| | 785 | } else { |
| | 786 | if (is_float(vtop->type.t)) { |
| | 787 | r=gv(RC_FLOAT); |
| | 788 | o(0xEE90F118|fpr(r)<<16); |
| | 789 | vtop->r = VT_CMP; |
| | 790 | vtop->c.i = TOK_NE; |
| | 791 | return gtst(inv, t); |
| | 792 | } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
| | 793 | /* constant jmp optimization */ |
| | 794 | if ((vtop->c.i != 0) != inv) |
| | 795 | t = gjmp(t); |
| | 796 | } else { |
| | 797 | v = gv(RC_INT); |
| | 798 | o(0xE3300000|(intr(v)<<16)); |
| | 799 | vtop->r = VT_CMP; |
| | 800 | vtop->c.i = TOK_NE; |
| | 801 | return gtst(inv, t); |
| | 802 | } |
| | 803 | } |
| | 804 | vtop--; |
| | 805 | return t; |
| | 806 | } |
| | 807 | |
| | 808 | /* generate an integer binary operation */ |
| | 809 | void gen_opi(int op) |
| | 810 | { |
| | 811 | int c, func; |
| | 812 | unsigned long opc,r,fr; |
| | 813 | |
| | 814 | c=0; |
| | 815 | switch(op) { |
| | 816 | case '+': |
| | 817 | opc = 0x8; |
| | 818 | c=1; |
| | 819 | break; |
| | 820 | case TOK_ADDC1: /* add with carry generation */ |
| | 821 | opc = 0x9; |
| | 822 | c=1; |
| | 823 | break; |
| | 824 | case '-': |
| | 825 | opc = 0x4; |
| | 826 | c=1; |
| | 827 | break; |
| | 828 | case TOK_SUBC1: /* sub with carry generation */ |
| | 829 | opc = 0x5; |
| | 830 | c=1; |
| | 831 | break; |
| | 832 | case TOK_ADDC2: /* add with carry use */ |
| | 833 | opc = 0xA; |
| | 834 | c=1; |
| | 835 | break; |
| | 836 | case TOK_SUBC2: /* sub with carry use */ |
| | 837 | opc = 0xC; |
| | 838 | c=1; |
| | 839 | break; |
| | 840 | case '&': |
| | 841 | opc = 0x0; |
| | 842 | c=1; |
| | 843 | break; |
| | 844 | case '^': |
| | 845 | opc = 0x2; |
| | 846 | c=1; |
| | 847 | break; |
| | 848 | case '|': |
| | 849 | opc = 0x18; |
| | 850 | c=1; |
| | 851 | break; |
| | 852 | case '*': |
| | 853 | gv2(RC_INT, RC_INT); |
| | 854 | r = vtop[-1].r; |
| | 855 | fr = vtop[0].r; |
| | 856 | vtop--; |
| | 857 | o(0xE0000090|(intr(r)<<16)|(intr(r)<<8)|intr(fr)); |
| | 858 | return; |
| | 859 | case TOK_SHL: |
| | 860 | opc = 0; |
| | 861 | c=2; |
| | 862 | break; |
| | 863 | case TOK_SHR: |
| | 864 | opc = 1; |
| | 865 | c=2; |
| | 866 | break; |
| | 867 | case TOK_SAR: |
| | 868 | opc = 2; |
| | 869 | c=2; |
| | 870 | break; |
| | 871 | case '/': |
| | 872 | case TOK_PDIV: |
| | 873 | func=TOK___divsi3; |
| | 874 | c=3; |
| | 875 | break; |
| | 876 | case TOK_UDIV: |
| | 877 | func=TOK___udivsi3; |
| | 878 | c=3; |
| | 879 | break; |
| | 880 | case '%': |
| | 881 | func=TOK___modsi3; |
| | 882 | c=3; |
| | 883 | break; |
| | 884 | case TOK_UMOD: |
| | 885 | func=TOK___umodsi3; |
| | 886 | c=3; |
| | 887 | break; |
| | 888 | case TOK_UMULL: |
| | 889 | gv2(RC_INT, RC_INT); |
| | 890 | r=intr(vtop[-1].r2=get_reg(RC_INT)); |
| | 891 | c=vtop[-1].r; |
| | 892 | vtop[-1].r=get_reg_ex(RC_INT,regmask(c)); |
| | 893 | vtop--; |
| | 894 | o(0xE0800090|(r<<16)|(intr(vtop->r)<<12)|(intr(c)<<8)|intr(vtop[1].r)); |
| | 895 | return; |
| | 896 | default: |
| | 897 | opc = 0x15; |
| | 898 | c=1; |
| | 899 | } |
| | 900 | switch(c) { |
| | 901 | case 1: |
| | 902 | if((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
| | 903 | if(opc == 4 || opc == 5 || opc == 0xc) { |
| | 904 | vswap(); |
| | 905 | opc|=2; // sub -> rsb |
| | 906 | } |
| | 907 | } |
| | 908 | vswap(); |
| | 909 | c=intr(gv(RC_INT)); |
| | 910 | vswap(); |
| | 911 | opc=0xE0000000|(opc<<20)|(c<<16); |
| | 912 | if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
| | 913 | unsigned long x; |
| | 914 | x=stuff_const(opc|0x2000000,vtop->c.i); |
| | 915 | if(x) { |
| | 916 | r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); |
| | 917 | o(x|(r<<12)); |
| | 918 | goto done; |
| | 919 | } |
| | 920 | } |
| | 921 | fr=intr(gv(RC_INT)); |
| | 922 | r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); |
| | 923 | o(opc|(r<<12)|fr); |
| | 924 | done: |
| | 925 | vtop--; |
| | 926 | if (op >= TOK_ULT && op <= TOK_GT) { |
| | 927 | vtop->r = VT_CMP; |
| | 928 | vtop->c.i = op; |
| | 929 | } |
| | 930 | break; |
| | 931 | case 2: |
| | 932 | opc=0xE1A00000|(opc<<5); |
| | 933 | vswap(); |
| | 934 | r=intr(gv(RC_INT)); |
| | 935 | vswap(); |
| | 936 | opc|=r; |
| | 937 | if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
| | 938 | fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); |
| | 939 | c = vtop->c.i & 0x1f; |
| | 940 | o(opc|(c<<7)|(fr<<12)); |
| | 941 | } else { |
| | 942 | fr=intr(gv(RC_INT)); |
| | 943 | c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); |
| | 944 | o(opc|(c<<12)|(fr<<8)|0x10); |
| | 945 | } |
| | 946 | vtop--; |
| | 947 | break; |
| | 948 | case 3: |
| | 949 | vpush_global_sym(&func_old_type, func); |
| | 950 | vrott(3); |
| | 951 | gfunc_call(2); |
| | 952 | vpushi(0); |
| | 953 | vtop->r = REG_IRET; |
| | 954 | break; |
| | 955 | default: |
| | 956 | error("gen_opi %i unimplemented!",op); |
| | 957 | } |
| | 958 | } |
| | 959 | |
| | 960 | static int is_fconst() |
| | 961 | { |
| | 962 | long double f; |
| | 963 | int r; |
| | 964 | if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) |
| | 965 | return 0; |
| | 966 | if (vtop->type.t == VT_FLOAT) |
| | 967 | f = vtop->c.f; |
| | 968 | else if (vtop->type.t == VT_DOUBLE) |
| | 969 | f = vtop->c.d; |
| | 970 | else |
| | 971 | f = vtop->c.ld; |
| | 972 | if(!ieee_finite(f)) |
| | 973 | return 0; |
| | 974 | r=0x8; |
| | 975 | if(f<0.0) { |
| | 976 | r=0x18; |
| | 977 | f=-f; |
| | 978 | } |
| | 979 | if(f==0.0) |
| | 980 | return r; |
| | 981 | if(f==1.0) |
| | 982 | return r|1; |
| | 983 | if(f==2.0) |
| | 984 | return r|2; |
| | 985 | if(f==3.0) |
| | 986 | return r|3; |
| | 987 | if(f==4.0) |
| | 988 | return r|4; |
| | 989 | if(f==5.0) |
| | 990 | return r|5; |
| | 991 | if(f==0.5) |
| | 992 | return r|6; |
| | 993 | if(f==10.0) |
| | 994 | return r|7; |
| | 995 | return 0; |
| | 996 | } |
| | 997 | |
| | 998 | /* generate a floating point operation 'v = t1 op t2' instruction. The |
| | 999 | two operands are guaranted to have the same floating point type */ |
| | 1000 | void gen_opf(int op) |
| | 1001 | { |
| | 1002 | unsigned long x; |
| | 1003 | int r,r2,c1,c2; |
| | 1004 | //fputs("gen_opf\n",stderr); |
| | 1005 | vswap(); |
| | 1006 | c1 = is_fconst(); |
| | 1007 | vswap(); |
| | 1008 | c2 = is_fconst(); |
| | 1009 | x=0xEE000180; |
| | 1010 | switch(op) |
| | 1011 | { |
| | 1012 | case '+': |
| | 1013 | if(!c2) { |
| | 1014 | vswap(); |
| | 1015 | c2=c1; |
| | 1016 | } |
| | 1017 | vswap(); |
| | 1018 | r=fpr(gv(RC_FLOAT)); |
| | 1019 | vswap(); |
| | 1020 | if(c2) { |
| | 1021 | if(c2>0xf) |
| | 1022 | x|=0x200000; // suf |
| | 1023 | r2=c2&0xf; |
| | 1024 | } else { |
| | 1025 | r2=fpr(gv(RC_FLOAT)); |
| | 1026 | } |
| | 1027 | break; |
| | 1028 | case '-': |
| | 1029 | if(c2) { |
| | 1030 | if(c2<=0xf) |
| | 1031 | x|=0x200000; // suf |
| | 1032 | r2=c2&0xf; |
| | 1033 | vswap(); |
| | 1034 | r=fpr(gv(RC_FLOAT)); |
| | 1035 | vswap(); |
| | 1036 | } else if(c1 && c1<=0xf) { |
| | 1037 | x|=0x300000; // rsf |
| | 1038 | r2=c1; |
| | 1039 | r=fpr(gv(RC_FLOAT)); |
| | 1040 | vswap(); |
| | 1041 | } else { |
| | 1042 | x|=0x200000; // suf |
| | 1043 | vswap(); |
| | 1044 | r=fpr(gv(RC_FLOAT)); |
| | 1045 | vswap(); |
| | 1046 | r2=fpr(gv(RC_FLOAT)); |
| | 1047 | } |
| | 1048 | break; |
| | 1049 | case '*': |
| | 1050 | if(!c2 || c2>0xf) { |
| | 1051 | vswap(); |
| | 1052 | c2=c1; |
| | 1053 | } |
| | 1054 | vswap(); |
| | 1055 | r=fpr(gv(RC_FLOAT)); |
| | 1056 | vswap(); |
| | 1057 | if(c2 && c2<=0xf) |
| | 1058 | r2=c2; |
| | 1059 | else |
| | 1060 | r2=fpr(gv(RC_FLOAT)); |
| | 1061 | x|=0x100000; // muf |
| | 1062 | break; |
| | 1063 | case '/': |
| | 1064 | if(c2 && c2<=0xf) { |
| | 1065 | x|=0x400000; // dvf |
| | 1066 | r2=c2; |
| | 1067 | vswap(); |
| | 1068 | r=fpr(gv(RC_FLOAT)); |
| | 1069 | vswap(); |
| | 1070 | } else if(c1 && c1<=0xf) { |
| | 1071 | x|=0x500000; // rdf |
| | 1072 | r2=c1; |
| | 1073 | r=fpr(gv(RC_FLOAT)); |
| | 1074 | vswap(); |
| | 1075 | } else { |
| | 1076 | x|=0x400000; // dvf |
| | 1077 | vswap(); |
| | 1078 | r=fpr(gv(RC_FLOAT)); |
| | 1079 | vswap(); |
| | 1080 | r2=fpr(gv(RC_FLOAT)); |
| | 1081 | } |
| | 1082 | break; |
| | 1083 | default: |
| | 1084 | if(op >= TOK_ULT && op <= TOK_GT) { |
| | 1085 | switch(op) { |
| | 1086 | case TOK_ULT: |
| | 1087 | case TOK_UGE: |
| | 1088 | case TOK_ULE: |
| | 1089 | case TOK_UGT: |
| | 1090 | fputs("unsigned comparision on floats?\n",stderr); |
| | 1091 | break; |
| | 1092 | case TOK_LT: |
| | 1093 | op=TOK_ULT; |
| | 1094 | break; |
| | 1095 | case TOK_GE: |
| | 1096 | op=TOK_UGE; |
| | 1097 | break; |
| | 1098 | case TOK_LE: |
| | 1099 | op=TOK_ULE; |
| | 1100 | break; |
| | 1101 | case TOK_GT: |
| | 1102 | op=TOK_UGT; |
| | 1103 | break; |
| | 1104 | } |
| | 1105 | x|=0x90f110; |
| | 1106 | if(c1 && !c2) { |
| | 1107 | c2=c1; |
| | 1108 | vswap(); |
| | 1109 | switch(op) { |
| | 1110 | case TOK_ULT: |
| | 1111 | op=TOK_UGT; |
| | 1112 | break; |
| | 1113 | case TOK_UGE: |
| | 1114 | op=TOK_ULE; |
| | 1115 | break; |
| | 1116 | case TOK_ULE: |
| | 1117 | op=TOK_UGE; |
| | 1118 | break; |
| | 1119 | case TOK_UGT: |
| | 1120 | op=TOK_ULT; |
| | 1121 | break; |
| | 1122 | } |
| | 1123 | } |
| | 1124 | vswap(); |
| | 1125 | r=fpr(gv(RC_FLOAT)); |
| | 1126 | vswap(); |
| | 1127 | if(c2) { |
| | 1128 | if(c2>0xf) |
| | 1129 | x|=0x200000; |
| | 1130 | r2=c2&0xf; |
| | 1131 | } else { |
| | 1132 | r2=fpr(gv(RC_FLOAT)); |
| | 1133 | } |
| | 1134 | vtop[-1].r = VT_CMP; |
| | 1135 | vtop[-1].c.i = op; |
| | 1136 | } else { |
| | 1137 | error("unknown fp op %x!\n",op); |
| | 1138 | return; |
| | 1139 | } |
| | 1140 | } |
| | 1141 | if(vtop[-1].r == VT_CMP) |
| | 1142 | c1=15; |
| | 1143 | else { |
| | 1144 | c1=vtop->r; |
| | 1145 | if(r2&0x8) |
| | 1146 | c1=vtop[-1].r; |
| | 1147 | vtop[-1].r=get_reg_ex(RC_FLOAT,two2mask(vtop[-1].r,c1)); |
| | 1148 | c1=fpr(vtop[-1].r); |
| | 1149 | } |
| | 1150 | vtop--; |
| | 1151 | o(x|(r<<16)|(c1<<12)|r2); |
| | 1152 | } |
| | 1153 | |
| | 1154 | /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' |
| | 1155 | and 'long long' cases. */ |
| | 1156 | void gen_cvt_itof(int t) |
| | 1157 | { |
| | 1158 | int r,r2,bt; |
| | 1159 | r=intr(gv(RC_INT)); |
| | 1160 | r2=fpr(vtop->r=get_reg(RC_FLOAT)); |
| | 1161 | bt=vtop->type.t & VT_BTYPE; |
| | 1162 | if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { |
| | 1163 | o(0xEE000190|(r2<<16)|(r<<12)); |
| | 1164 | if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { |
| | 1165 | unsigned int off=0; |
| | 1166 | o(0xE3500000|(r<<12)); |
| | 1167 | r=fpr(get_reg(RC_FLOAT)); |
| | 1168 | if(last_itod_magic) { |
| | 1169 | off=ind+8-last_itod_magic; |
| | 1170 | off/=4; |
| | 1171 | if(off>255) |
| | 1172 | off=0; |
| | 1173 | } |
| | 1174 | o(0xBD1F8100|(r<<12)|off); |
| | 1175 | if(!off) { |
| | 1176 | o(0xEA000001); |
| | 1177 | last_itod_magic=ind; |
| | 1178 | o(0x41F00000); |
| | 1179 | o(0); |
| | 1180 | } |
| | 1181 | o(0xBE000180|(r2<<16)|(r2<<12)|r); |
| | 1182 | } |
| | 1183 | return; |
| | 1184 | } else if(bt == VT_LLONG) { |
| | 1185 | int r3; |
| | 1186 | unsigned long x=0; |
| | 1187 | r3=intr(vtop->r2); |
| | 1188 | o(0xe52d0004|(r<<12)); //str r0, [sp, -#4]! |
| | 1189 | o(0xe3500000|(r3<<16)); //cmp r1, #0 |
| | 1190 | o(0xa3800102|(r3<<16)|(r3<<12)); //orrge r1, r1, #0x80000000 |
| | 1191 | o(0xe59f0014|(r<<12)); //ldr r0, [pc, #20] |
| | 1192 | o(0xe52d0004|(r3<<12)); //str r1, [sp, -#4]! |
| | 1193 | o(0xe52d0004|(r<<12)); //str r0, [sp, -#4]! |
| | 1194 | o(0xecfd0103|(r2<<12)); //ldfe f0, [sp], #12 |
| | 1195 | r=fpr(get_reg(RC_FLOAT)); |
| | 1196 | if((vtop->type.t & VT_UNSIGNED) != VT_UNSIGNED) |
| | 1197 | x=0xE0000000; |
| | 1198 | o(0xaddf0101|(r<<12)|x); //ldf(ge)e f1, [pc, #4] |
| | 1199 | o(0xae280100|(r2<<16)|(r2<<12)|r|x); //suf(ge)e f0, f0, f1 |
| | 1200 | o(0xea000002); //b pc+8 |
| | 1201 | o(0x403e);o(0x80000000);o(0); |
| | 1202 | if(x) |
| | 1203 | o(0xbe280100|(r2<<16)|(r2<<12)|r); //suflte f0, f0, f1 |
| | 1204 | vtop->r2=VT_CONST; |
| | 1205 | return; |
| | 1206 | } |
| | 1207 | error("unimplemented gen_cvt_itof %x!",vtop->type.t); |
| | 1208 | } |
| | 1209 | |
| | 1210 | /* convert fp to int 't' type */ |
| | 1211 | void gen_cvt_ftoi(int t) |
| | 1212 | { |
| | 1213 | int r,r2,u; |
| | 1214 | u=t&VT_UNSIGNED; |
| | 1215 | t&=VT_BTYPE; |
| | 1216 | r=fpr(gv(RC_FLOAT)); |
| | 1217 | r2=intr(vtop->r=get_reg(RC_INT)); |
| | 1218 | if(t==VT_INT) { |
| | 1219 | if(u) { |
| | 1220 | u=fpr(get_reg(RC_FLOAT)); |
| | 1221 | o(0xed9f0100|(u<<12)); |
| | 1222 | o(0xea000000); |
| | 1223 | o(0x4f000000); |
| | 1224 | o(0xee90f110|(r<<12)|u); |
| | 1225 | o(0x2e200180|(r<<16)|(r<<12)|u); |
| | 1226 | o(0x2e200180|(r<<16)|(r<<12)|u); |
| | 1227 | o(0xee100150|(r2<<12)|u); |
| | 1228 | } else |
| | 1229 | o(0xEE100170|(r<<12)|r); |
| | 1230 | return; |
| | 1231 | } else if(t==VT_LLONG) { |
| | 1232 | int Q,R; |
| | 1233 | Q=r2; |
| | 1234 | R=intr(vtop->r2=get_reg(RC_INT)); |
| | 1235 | if(Q<R) { |
| | 1236 | Q=R; |
| | 1237 | R=r2; |
| | 1238 | r2=vtop->r; |
| | 1239 | vtop->r=vtop->r2; |
| | 1240 | vtop->r2=r2; |
| | 1241 | } |
| | 1242 | if(!u) |
| | 1243 | r2=intr(get_reg(RC_INT)); |
| | 1244 | o(0xed6d0103|(r<<12)); |
| | 1245 | o(0xe0dde0b4); |
| | 1246 | o(0xe8bd0000|(1<<Q)|(1<<R)); |
| | 1247 | if(!u) |
| | 1248 | o(0xe20e0902|(r2<<12)); |
| | 1249 | o(0xe3cee902); |
| | 1250 | o(0xe24eec3f); |
| | 1251 | o(0xe25ee0ff); |
| | 1252 | if(!u) { |
| | 1253 | o(0xb3a00000|(R<<12)); |
| | 1254 | o(0xb3a00000|(Q<<12)); |
| | 1255 | } |
| | 1256 | o(0xba000000|(u?5:9)); |
| | 1257 | o(0xe25ee03f); |
| | 1258 | o(0xaa000000|(u?5:3)); |
| | 1259 | o(0xe1b000a0|(R<<12)|R); |
| | 1260 | o(0xe1a00060|(Q<<12)|Q); |
| | 1261 | o(0xe29ee001); |
| | 1262 | o(0x1afffffb); |
| | 1263 | if(u) { |
| | 1264 | o(0x13a00000|(R<<12)); |
| | 1265 | o(0x13a00000|(Q<<12)); |
| | 1266 | } else { |
| | 1267 | o(0xe3500000|(r2<<12)); |
| | 1268 | o(0x0a000001); |
| | 1269 | o(0xe2700000|(Q<<16)|(Q<<12)); |
| | 1270 | o(0xe2e00000|(R<<16)|(R<<12)); |
| | 1271 | } |
| | 1272 | return; |
| | 1273 | } |
| | 1274 | error("unimplemented gen_cvt_ftoi!"); |
| | 1275 | } |
| | 1276 | |
| | 1277 | /* convert from one floating point type to another */ |
| | 1278 | void gen_cvt_ftof(int t) |
| | 1279 | { |
| | 1280 | /* all we have to do on i386 and ARM is to put the float in a register */ |
| | 1281 | gv(RC_FLOAT); |
| | 1282 | } |
| | 1283 | |
| | 1284 | /* computed goto support */ |
| | 1285 | void ggoto(void) |
| | 1286 | { |
| | 1287 | gcall_or_jmp(1); |
| | 1288 | vtop--; |
| | 1289 | } |
| | 1290 | |
| | 1291 | /* end of ARM code generator */ |
| | 1292 | /*************************************************************/ |
| | 1293 | |