Rev 647 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
145 | halyavin | 1 | /* |
2 | * ARMv4 code generator for TCC |
||
3 | * |
||
6429 | siemargl | 4 | * Copyright (c) 2003 Daniel Glöckner |
5 | * Copyright (c) 2012 Thomas Preud'homme |
||
6 | * |
||
145 | halyavin | 7 | * Based on i386-gen.c by Fabrice Bellard |
8 | * |
||
9 | * This library is free software; you can redistribute it and/or |
||
10 | * modify it under the terms of the GNU Lesser General Public |
||
11 | * License as published by the Free Software Foundation; either |
||
12 | * version 2 of the License, or (at your option) any later version. |
||
13 | * |
||
14 | * This library is distributed in the hope that it will be useful, |
||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
17 | * Lesser General Public License for more details. |
||
18 | * |
||
19 | * You should have received a copy of the GNU Lesser General Public |
||
20 | * License along with this library; if not, write to the Free Software |
||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
22 | */ |
||
23 | |||
6429 | siemargl | 24 | #ifdef TARGET_DEFS_ONLY |
25 | |||
26 | #if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP) |
||
27 | #error "Currently TinyCC only supports float computation with VFP instructions" |
||
28 | #endif |
||
29 | |||
145 | halyavin | 30 | /* number of available registers */ |
6429 | siemargl | 31 | #ifdef TCC_ARM_VFP |
32 | #define NB_REGS 13 |
||
33 | #else |
||
145 | halyavin | 34 | #define NB_REGS 9 |
6429 | siemargl | 35 | #endif |
145 | halyavin | 36 | |
6429 | siemargl | 37 | #ifndef TCC_ARM_VERSION |
38 | # define TCC_ARM_VERSION 5 |
||
39 | #endif |
||
40 | |||
145 | halyavin | 41 | /* a register can belong to several classes. The classes must be |
42 | sorted from more general to more precise (see gv2() code which does |
||
43 | assumptions on it). */ |
||
44 | #define RC_INT 0x0001 /* generic integer register */ |
||
45 | #define RC_FLOAT 0x0002 /* generic float register */ |
||
46 | #define RC_R0 0x0004 |
||
6429 | siemargl | 47 | #define RC_R1 0x0008 |
145 | halyavin | 48 | #define RC_R2 0x0010 |
49 | #define RC_R3 0x0020 |
||
50 | #define RC_R12 0x0040 |
||
51 | #define RC_F0 0x0080 |
||
52 | #define RC_F1 0x0100 |
||
53 | #define RC_F2 0x0200 |
||
54 | #define RC_F3 0x0400 |
||
6429 | siemargl | 55 | #ifdef TCC_ARM_VFP |
56 | #define RC_F4 0x0800 |
||
57 | #define RC_F5 0x1000 |
||
58 | #define RC_F6 0x2000 |
||
59 | #define RC_F7 0x4000 |
||
60 | #endif |
||
145 | halyavin | 61 | #define RC_IRET RC_R0 /* function return: integer register */ |
62 | #define RC_LRET RC_R1 /* function return: second integer register */ |
||
63 | #define RC_FRET RC_F0 /* function return: float register */ |
||
64 | |||
65 | /* pretty names for the registers */ |
||
66 | enum { |
||
67 | TREG_R0 = 0, |
||
68 | TREG_R1, |
||
69 | TREG_R2, |
||
70 | TREG_R3, |
||
71 | TREG_R12, |
||
72 | TREG_F0, |
||
73 | TREG_F1, |
||
74 | TREG_F2, |
||
75 | TREG_F3, |
||
6429 | siemargl | 76 | #ifdef TCC_ARM_VFP |
77 | TREG_F4, |
||
78 | TREG_F5, |
||
79 | TREG_F6, |
||
80 | TREG_F7, |
||
81 | #endif |
||
145 | halyavin | 82 | }; |
83 | |||
6429 | siemargl | 84 | #ifdef TCC_ARM_VFP |
85 | #define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0) |
||
86 | #endif |
||
145 | halyavin | 87 | |
88 | /* return registers for function */ |
||
89 | #define REG_IRET TREG_R0 /* single word int return register */ |
||
90 | #define REG_LRET TREG_R1 /* second word return register (for long long) */ |
||
91 | #define REG_FRET TREG_F0 /* float return register */ |
||
92 | |||
6429 | siemargl | 93 | #ifdef TCC_ARM_EABI |
94 | #define TOK___divdi3 TOK___aeabi_ldivmod |
||
95 | #define TOK___moddi3 TOK___aeabi_ldivmod |
||
96 | #define TOK___udivdi3 TOK___aeabi_uldivmod |
||
97 | #define TOK___umoddi3 TOK___aeabi_uldivmod |
||
98 | #endif |
||
99 | |||
145 | halyavin | 100 | /* defined if function parameters must be evaluated in reverse order */ |
101 | #define INVERT_FUNC_PARAMS |
||
102 | |||
103 | /* defined if structures are passed as pointers. Otherwise structures |
||
104 | are directly pushed on stack. */ |
||
6429 | siemargl | 105 | /* #define FUNC_STRUCT_PARAM_AS_PTR */ |
145 | halyavin | 106 | |
107 | /* pointer size, in bytes */ |
||
108 | #define PTR_SIZE 4 |
||
109 | |||
110 | /* long double size and alignment, in bytes */ |
||
6429 | siemargl | 111 | #ifdef TCC_ARM_VFP |
145 | halyavin | 112 | #define LDOUBLE_SIZE 8 |
6429 | siemargl | 113 | #endif |
114 | |||
115 | #ifndef LDOUBLE_SIZE |
||
116 | #define LDOUBLE_SIZE 8 |
||
117 | #endif |
||
118 | |||
119 | #ifdef TCC_ARM_EABI |
||
120 | #define LDOUBLE_ALIGN 8 |
||
121 | #else |
||
145 | halyavin | 122 | #define LDOUBLE_ALIGN 4 |
6429 | siemargl | 123 | #endif |
124 | |||
145 | halyavin | 125 | /* maximum alignment (for aligned attribute support) */ |
126 | #define MAX_ALIGN 8 |
||
127 | |||
128 | #define CHAR_IS_UNSIGNED |
||
129 | |||
130 | /******************************************************/ |
||
131 | /* ELF defines */ |
||
132 | |||
133 | #define EM_TCC_TARGET EM_ARM |
||
134 | |||
135 | /* relocation type for 32 bit data relocation */ |
||
136 | #define R_DATA_32 R_ARM_ABS32 |
||
6429 | siemargl | 137 | #define R_DATA_PTR R_ARM_ABS32 |
145 | halyavin | 138 | #define R_JMP_SLOT R_ARM_JUMP_SLOT |
139 | #define R_COPY R_ARM_COPY |
||
140 | |||
141 | #define ELF_START_ADDR 0x00008000 |
||
142 | #define ELF_PAGE_SIZE 0x1000 |
||
143 | |||
6429 | siemargl | 144 | enum float_abi { |
145 | ARM_SOFTFP_FLOAT, |
||
146 | ARM_HARD_FLOAT, |
||
147 | }; |
||
148 | |||
145 | halyavin | 149 | /******************************************************/ |
6429 | siemargl | 150 | #else /* ! TARGET_DEFS_ONLY */ |
151 | /******************************************************/ |
||
152 | #include "tcc.h" |
||
145 | halyavin | 153 | |
6429 | siemargl | 154 | enum float_abi float_abi; |
155 | |||
156 | ST_DATA const int reg_classes[NB_REGS] = { |
||
157 | /* r0 */ RC_INT | RC_R0, |
||
158 | /* r1 */ RC_INT | RC_R1, |
||
159 | /* r2 */ RC_INT | RC_R2, |
||
160 | /* r3 */ RC_INT | RC_R3, |
||
161 | /* r12 */ RC_INT | RC_R12, |
||
162 | /* f0 */ RC_FLOAT | RC_F0, |
||
163 | /* f1 */ RC_FLOAT | RC_F1, |
||
164 | /* f2 */ RC_FLOAT | RC_F2, |
||
165 | /* f3 */ RC_FLOAT | RC_F3, |
||
166 | #ifdef TCC_ARM_VFP |
||
167 | /* d4/s8 */ RC_FLOAT | RC_F4, |
||
168 | /* d5/s10 */ RC_FLOAT | RC_F5, |
||
169 | /* d6/s12 */ RC_FLOAT | RC_F6, |
||
170 | /* d7/s14 */ RC_FLOAT | RC_F7, |
||
171 | #endif |
||
172 | }; |
||
173 | |||
174 | static int func_sub_sp_offset, last_itod_magic; |
||
175 | static int leaffunc; |
||
176 | |||
177 | #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) |
||
178 | static CType float_type, double_type, func_float_type, func_double_type; |
||
179 | ST_FUNC void arm_init(struct TCCState *s) |
||
145 | halyavin | 180 | { |
6429 | siemargl | 181 | float_type.t = VT_FLOAT; |
182 | double_type.t = VT_DOUBLE; |
||
183 | func_float_type.t = VT_FUNC; |
||
184 | func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD); |
||
185 | func_double_type.t = VT_FUNC; |
||
186 | func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); |
||
187 | |||
188 | float_abi = s->float_abi; |
||
189 | #ifndef TCC_ARM_HARDFLOAT |
||
190 | tcc_warning("soft float ABI currently not supported: default to softfp"); |
||
191 | #endif |
||
192 | } |
||
193 | #else |
||
194 | #define func_float_type func_old_type |
||
195 | #define func_double_type func_old_type |
||
196 | #define func_ldouble_type func_old_type |
||
197 | ST_FUNC void arm_init(struct TCCState *s) |
||
198 | { |
||
199 | #if !defined (TCC_ARM_VFP) |
||
200 | tcc_warning("Support for FPA is deprecated and will be removed in next" |
||
201 | " release"); |
||
202 | #endif |
||
203 | #if !defined (TCC_ARM_EABI) |
||
204 | tcc_warning("Support for OABI is deprecated and will be removed in next" |
||
205 | " release"); |
||
206 | #endif |
||
207 | } |
||
208 | #endif |
||
209 | |||
210 | static int two2mask(int a,int b) { |
||
211 | return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT); |
||
212 | } |
||
213 | |||
214 | static int regmask(int r) { |
||
215 | return reg_classes[r]&~(RC_INT|RC_FLOAT); |
||
216 | } |
||
217 | |||
218 | /******************************************************/ |
||
219 | |||
220 | #if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP) |
||
221 | char *default_elfinterp(struct TCCState *s) |
||
222 | { |
||
223 | if (s->float_abi == ARM_HARD_FLOAT) |
||
224 | return "/lib/ld-linux-armhf.so.3"; |
||
225 | else |
||
226 | return "/lib/ld-linux.so.3"; |
||
227 | } |
||
228 | #endif |
||
229 | |||
230 | void o(uint32_t i) |
||
231 | { |
||
145 | halyavin | 232 | /* this is a good place to start adding big-endian support*/ |
233 | int ind1; |
||
234 | |||
235 | ind1 = ind + 4; |
||
236 | if (!cur_text_section) |
||
6429 | siemargl | 237 | tcc_error("compiler error! This happens f.ex. if the compiler\n" |
145 | halyavin | 238 | "can't evaluate constant expressions outside of a function."); |
239 | if (ind1 > cur_text_section->data_allocated) |
||
240 | section_realloc(cur_text_section, ind1); |
||
241 | cur_text_section->data[ind++] = i&255; |
||
242 | i>>=8; |
||
243 | cur_text_section->data[ind++] = i&255; |
||
6429 | siemargl | 244 | i>>=8; |
145 | halyavin | 245 | cur_text_section->data[ind++] = i&255; |
246 | i>>=8; |
||
247 | cur_text_section->data[ind++] = i; |
||
248 | } |
||
249 | |||
6429 | siemargl | 250 | static uint32_t stuff_const(uint32_t op, uint32_t c) |
145 | halyavin | 251 | { |
252 | int try_neg=0; |
||
6429 | siemargl | 253 | uint32_t nc = 0, negop = 0; |
145 | halyavin | 254 | |
255 | switch(op&0x1F00000) |
||
256 | { |
||
257 | case 0x800000: //add |
||
258 | case 0x400000: //sub |
||
259 | try_neg=1; |
||
260 | negop=op^0xC00000; |
||
261 | nc=-c; |
||
262 | break; |
||
263 | case 0x1A00000: //mov |
||
264 | case 0x1E00000: //mvn |
||
265 | try_neg=1; |
||
266 | negop=op^0x400000; |
||
267 | nc=~c; |
||
268 | break; |
||
269 | case 0x200000: //xor |
||
270 | if(c==~0) |
||
271 | return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000; |
||
272 | break; |
||
273 | case 0x0: //and |
||
274 | if(c==~0) |
||
275 | return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000; |
||
276 | case 0x1C00000: //bic |
||
277 | try_neg=1; |
||
278 | negop=op^0x1C00000; |
||
279 | nc=~c; |
||
280 | break; |
||
281 | case 0x1800000: //orr |
||
282 | if(c==~0) |
||
283 | return (op&0xFFF0FFFF)|0x1E00000; |
||
284 | break; |
||
285 | } |
||
286 | do { |
||
6429 | siemargl | 287 | uint32_t m; |
145 | halyavin | 288 | int i; |
289 | if(c<256) /* catch undefined <<32 */ |
||
290 | return op|c; |
||
291 | for(i=2;i<32;i+=2) { |
||
292 | m=(0xff>>i)|(0xff<<(32-i)); |
||
293 | if(!(c&~m)) |
||
294 | return op|(i<<7)|(c<>(32-i)); |
||
295 | } |
||
296 | op=negop; |
||
297 | c=nc; |
||
298 | } while(try_neg--); |
||
299 | return 0; |
||
300 | } |
||
301 | |||
302 | |||
303 | //only add,sub |
||
6429 | siemargl | 304 | void stuff_const_harder(uint32_t op, uint32_t v) { |
305 | uint32_t x; |
||
145 | halyavin | 306 | x=stuff_const(op,v); |
307 | if(x) |
||
308 | o(x); |
||
309 | else { |
||
6429 | siemargl | 310 | uint32_t a[16], nv, no, o2, n2; |
145 | halyavin | 311 | int i,j,k; |
312 | a[0]=0xff; |
||
313 | o2=(op&0xfff0ffff)|((op&0xf000)<<4);; |
||
314 | for(i=1;i<16;i++) |
||
315 | a[i]=(a[i-1]>>2)|(a[i-1]<<30); |
||
316 | for(i=0;i<12;i++) |
||
6429 | siemargl | 317 | for(j=i<4?i+12:15;j>=i+4;j--) |
145 | halyavin | 318 | if((v&(a[i]|a[j]))==v) { |
319 | o(stuff_const(op,v&a[i])); |
||
320 | o(stuff_const(o2,v&a[j])); |
||
321 | return; |
||
322 | } |
||
323 | no=op^0xC00000; |
||
324 | n2=o2^0xC00000; |
||
325 | nv=-v; |
||
326 | for(i=0;i<12;i++) |
||
6429 | siemargl | 327 | for(j=i<4?i+12:15;j>=i+4;j--) |
145 | halyavin | 328 | if((nv&(a[i]|a[j]))==nv) { |
329 | o(stuff_const(no,nv&a[i])); |
||
330 | o(stuff_const(n2,nv&a[j])); |
||
331 | return; |
||
332 | } |
||
333 | for(i=0;i<8;i++) |
||
6429 | siemargl | 334 | for(j=i+4;j<12;j++) |
335 | for(k=i<4?i+12:15;k>=j+4;k--) |
||
145 | halyavin | 336 | if((v&(a[i]|a[j]|a[k]))==v) { |
337 | o(stuff_const(op,v&a[i])); |
||
338 | o(stuff_const(o2,v&a[j])); |
||
339 | o(stuff_const(o2,v&a[k])); |
||
340 | return; |
||
341 | } |
||
342 | no=op^0xC00000; |
||
343 | nv=-v; |
||
344 | for(i=0;i<8;i++) |
||
6429 | siemargl | 345 | for(j=i+4;j<12;j++) |
346 | for(k=i<4?i+12:15;k>=j+4;k--) |
||
145 | halyavin | 347 | if((nv&(a[i]|a[j]|a[k]))==nv) { |
348 | o(stuff_const(no,nv&a[i])); |
||
349 | o(stuff_const(n2,nv&a[j])); |
||
350 | o(stuff_const(n2,nv&a[k])); |
||
351 | return; |
||
352 | } |
||
353 | o(stuff_const(op,v&a[0])); |
||
354 | o(stuff_const(o2,v&a[4])); |
||
355 | o(stuff_const(o2,v&a[8])); |
||
356 | o(stuff_const(o2,v&a[12])); |
||
357 | } |
||
358 | } |
||
359 | |||
6429 | siemargl | 360 | ST_FUNC uint32_t encbranch(int pos, int addr, int fail) |
145 | halyavin | 361 | { |
362 | addr-=pos+8; |
||
363 | addr/=4; |
||
364 | if(addr>=0x1000000 || addr<-0x1000000) { |
||
365 | if(fail) |
||
6429 | siemargl | 366 | tcc_error("FIXME: function bigger than 32MB"); |
145 | halyavin | 367 | return 0; |
368 | } |
||
369 | return 0x0A000000|(addr&0xffffff); |
||
370 | } |
||
371 | |||
372 | int decbranch(int pos) |
||
373 | { |
||
374 | int x; |
||
6429 | siemargl | 375 | x=*(uint32_t *)(cur_text_section->data + pos); |
145 | halyavin | 376 | x&=0x00ffffff; |
377 | if(x&0x800000) |
||
378 | x-=0x1000000; |
||
379 | return x*4+pos+8; |
||
380 | } |
||
381 | |||
382 | /* output a symbol and patch all calls to it */ |
||
383 | void gsym_addr(int t, int a) |
||
384 | { |
||
6429 | siemargl | 385 | uint32_t *x; |
145 | halyavin | 386 | int lt; |
387 | while(t) { |
||
6429 | siemargl | 388 | x=(uint32_t *)(cur_text_section->data + t); |
145 | halyavin | 389 | t=decbranch(lt=t); |
390 | if(a==lt+4) |
||
391 | *x=0xE1A00000; // nop |
||
392 | else { |
||
393 | *x &= 0xff000000; |
||
394 | *x |= encbranch(lt,a,1); |
||
395 | } |
||
396 | } |
||
397 | } |
||
398 | |||
399 | void gsym(int t) |
||
400 | { |
||
401 | gsym_addr(t, ind); |
||
402 | } |
||
403 | |||
6429 | siemargl | 404 | #ifdef TCC_ARM_VFP |
405 | static uint32_t vfpr(int r) |
||
145 | halyavin | 406 | { |
6429 | siemargl | 407 | if(r |
408 | tcc_error("compiler error! register %i is no vfp register",r); |
||
409 | return r-5; |
||
410 | } |
||
411 | #else |
||
412 | static uint32_t fpr(int r) |
||
413 | { |
||
145 | halyavin | 414 | if(r |
6429 | siemargl | 415 | tcc_error("compiler error! register %i is no fpa register",r); |
145 | halyavin | 416 | return r-5; |
417 | } |
||
6429 | siemargl | 418 | #endif |
145 | halyavin | 419 | |
6429 | siemargl | 420 | static uint32_t intr(int r) |
145 | halyavin | 421 | { |
422 | if(r==4) |
||
423 | return 12; |
||
424 | if((r<0 || r>4) && r!=14) |
||
6429 | siemargl | 425 | tcc_error("compiler error! register %i is no int register",r); |
145 | halyavin | 426 | return r; |
427 | } |
||
428 | |||
6429 | siemargl | 429 | static void calcaddr(uint32_t *base, int *off, int *sgn, int maxoff, unsigned shift) |
145 | halyavin | 430 | { |
431 | if(*off>maxoff || *off&((1< |
||
6429 | siemargl | 432 | uint32_t x, y; |
145 | halyavin | 433 | x=0xE280E000; |
434 | if(*sgn) |
||
435 | x=0xE240E000; |
||
436 | x|=(*base)<<16; |
||
437 | *base=14; // lr |
||
438 | y=stuff_const(x,*off&~maxoff); |
||
439 | if(y) { |
||
440 | o(y); |
||
441 | *off&=maxoff; |
||
442 | return; |
||
443 | } |
||
444 | y=stuff_const(x,(*off+maxoff)&~maxoff); |
||
445 | if(y) { |
||
446 | o(y); |
||
447 | *sgn=!*sgn; |
||
448 | *off=((*off+maxoff)&~maxoff)-*off; |
||
449 | return; |
||
450 | } |
||
451 | stuff_const_harder(x,*off&~maxoff); |
||
452 | *off&=maxoff; |
||
453 | } |
||
454 | } |
||
455 | |||
6429 | siemargl | 456 | static uint32_t mapcc(int cc) |
145 | halyavin | 457 | { |
458 | switch(cc) |
||
459 | { |
||
460 | case TOK_ULT: |
||
6429 | siemargl | 461 | return 0x30000000; /* CC/LO */ |
145 | halyavin | 462 | case TOK_UGE: |
6429 | siemargl | 463 | return 0x20000000; /* CS/HS */ |
145 | halyavin | 464 | case TOK_EQ: |
6429 | siemargl | 465 | return 0x00000000; /* EQ */ |
145 | halyavin | 466 | case TOK_NE: |
6429 | siemargl | 467 | return 0x10000000; /* NE */ |
145 | halyavin | 468 | case TOK_ULE: |
6429 | siemargl | 469 | return 0x90000000; /* LS */ |
145 | halyavin | 470 | case TOK_UGT: |
6429 | siemargl | 471 | return 0x80000000; /* HI */ |
472 | case TOK_Nset: |
||
473 | return 0x40000000; /* MI */ |
||
474 | case TOK_Nclear: |
||
475 | return 0x50000000; /* PL */ |
||
145 | halyavin | 476 | case TOK_LT: |
6429 | siemargl | 477 | return 0xB0000000; /* LT */ |
145 | halyavin | 478 | case TOK_GE: |
6429 | siemargl | 479 | return 0xA0000000; /* GE */ |
145 | halyavin | 480 | case TOK_LE: |
6429 | siemargl | 481 | return 0xD0000000; /* LE */ |
145 | halyavin | 482 | case TOK_GT: |
6429 | siemargl | 483 | return 0xC0000000; /* GT */ |
145 | halyavin | 484 | } |
6429 | siemargl | 485 | tcc_error("unexpected condition code"); |
486 | return 0xE0000000; /* AL */ |
||
145 | halyavin | 487 | } |
488 | |||
489 | static int negcc(int cc) |
||
490 | { |
||
491 | switch(cc) |
||
492 | { |
||
493 | case TOK_ULT: |
||
494 | return TOK_UGE; |
||
495 | case TOK_UGE: |
||
496 | return TOK_ULT; |
||
497 | case TOK_EQ: |
||
498 | return TOK_NE; |
||
499 | case TOK_NE: |
||
500 | return TOK_EQ; |
||
501 | case TOK_ULE: |
||
502 | return TOK_UGT; |
||
503 | case TOK_UGT: |
||
504 | return TOK_ULE; |
||
6429 | siemargl | 505 | case TOK_Nset: |
506 | return TOK_Nclear; |
||
507 | case TOK_Nclear: |
||
508 | return TOK_Nset; |
||
145 | halyavin | 509 | case TOK_LT: |
510 | return TOK_GE; |
||
511 | case TOK_GE: |
||
512 | return TOK_LT; |
||
513 | case TOK_LE: |
||
514 | return TOK_GT; |
||
515 | case TOK_GT: |
||
516 | return TOK_LE; |
||
517 | } |
||
6429 | siemargl | 518 | tcc_error("unexpected condition code"); |
145 | halyavin | 519 | return TOK_NE; |
520 | } |
||
521 | |||
522 | /* load 'r' from value 'sv' */ |
||
523 | void load(int r, SValue *sv) |
||
524 | { |
||
525 | int v, ft, fc, fr, sign; |
||
6429 | siemargl | 526 | uint32_t op; |
145 | halyavin | 527 | SValue v1; |
528 | |||
529 | fr = sv->r; |
||
530 | ft = sv->type.t; |
||
6429 | siemargl | 531 | fc = sv->c.i; |
145 | halyavin | 532 | |
533 | if(fc>=0) |
||
534 | sign=0; |
||
535 | else { |
||
536 | sign=1; |
||
537 | fc=-fc; |
||
538 | } |
||
6429 | siemargl | 539 | |
145 | halyavin | 540 | v = fr & VT_VALMASK; |
541 | if (fr & VT_LVAL) { |
||
6429 | siemargl | 542 | uint32_t base = 0xB; // fp |
145 | halyavin | 543 | if(v == VT_LLOCAL) { |
544 | v1.type.t = VT_PTR; |
||
545 | v1.r = VT_LOCAL | VT_LVAL; |
||
6429 | siemargl | 546 | v1.c.i = sv->c.i; |
145 | halyavin | 547 | load(base=14 /* lr */, &v1); |
548 | fc=sign=0; |
||
549 | v=VT_LOCAL; |
||
550 | } else if(v == VT_CONST) { |
||
551 | v1.type.t = VT_PTR; |
||
552 | v1.r = fr&~VT_LVAL; |
||
6429 | siemargl | 553 | v1.c.i = sv->c.i; |
145 | halyavin | 554 | v1.sym=sv->sym; |
555 | load(base=14, &v1); |
||
556 | fc=sign=0; |
||
557 | v=VT_LOCAL; |
||
558 | } else if(v < VT_CONST) { |
||
559 | base=intr(v); |
||
560 | fc=sign=0; |
||
561 | v=VT_LOCAL; |
||
562 | } |
||
563 | if(v == VT_LOCAL) { |
||
564 | if(is_float(ft)) { |
||
565 | calcaddr(&base,&fc,&sign,1020,2); |
||
6429 | siemargl | 566 | #ifdef TCC_ARM_VFP |
567 | op=0xED100A00; /* flds */ |
||
568 | if(!sign) |
||
569 | op|=0x800000; |
||
570 | if ((ft & VT_BTYPE) != VT_FLOAT) |
||
571 | op|=0x100; /* flds -> fldd */ |
||
572 | o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); |
||
573 | #else |
||
145 | halyavin | 574 | op=0xED100100; |
575 | if(!sign) |
||
576 | op|=0x800000; |
||
577 | #if LDOUBLE_SIZE == 8 |
||
578 | if ((ft & VT_BTYPE) != VT_FLOAT) |
||
579 | op|=0x8000; |
||
580 | #else |
||
581 | if ((ft & VT_BTYPE) == VT_DOUBLE) |
||
582 | op|=0x8000; |
||
583 | else if ((ft & VT_BTYPE) == VT_LDOUBLE) |
||
584 | op|=0x400000; |
||
585 | #endif |
||
586 | o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); |
||
6429 | siemargl | 587 | #endif |
588 | } else if((ft & (VT_BTYPE|VT_UNSIGNED)) == VT_BYTE |
||
589 | || (ft & VT_BTYPE) == VT_SHORT) { |
||
145 | halyavin | 590 | calcaddr(&base,&fc,&sign,255,0); |
591 | op=0xE1500090; |
||
592 | if ((ft & VT_BTYPE) == VT_SHORT) |
||
593 | op|=0x20; |
||
594 | if ((ft & VT_UNSIGNED) == 0) |
||
595 | op|=0x40; |
||
596 | if(!sign) |
||
597 | op|=0x800000; |
||
598 | o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); |
||
599 | } else { |
||
600 | calcaddr(&base,&fc,&sign,4095,0); |
||
601 | op=0xE5100000; |
||
602 | if(!sign) |
||
603 | op|=0x800000; |
||
6429 | siemargl | 604 | if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) |
145 | halyavin | 605 | op|=0x400000; |
606 | o(op|(intr(r)<<12)|fc|(base<<16)); |
||
607 | } |
||
608 | return; |
||
609 | } |
||
610 | } else { |
||
611 | if (v == VT_CONST) { |
||
6429 | siemargl | 612 | op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.i); |
145 | halyavin | 613 | if (fr & VT_SYM || !op) { |
614 | o(0xE59F0000|(intr(r)<<12)); |
||
615 | o(0xEA000000); |
||
616 | if(fr & VT_SYM) |
||
617 | greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); |
||
6429 | siemargl | 618 | o(sv->c.i); |
145 | halyavin | 619 | } else |
620 | o(op); |
||
621 | return; |
||
622 | } else if (v == VT_LOCAL) { |
||
6429 | siemargl | 623 | op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.i); |
145 | halyavin | 624 | if (fr & VT_SYM || !op) { |
625 | o(0xE59F0000|(intr(r)<<12)); |
||
626 | o(0xEA000000); |
||
627 | if(fr & VT_SYM) // needed ? |
||
628 | greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); |
||
6429 | siemargl | 629 | o(sv->c.i); |
145 | halyavin | 630 | o(0xE08B0000|(intr(r)<<12)|intr(r)); |
631 | } else |
||
632 | o(op); |
||
633 | return; |
||
634 | } else if(v == VT_CMP) { |
||
6429 | siemargl | 635 | o(mapcc(sv->c.i)|0x3A00001|(intr(r)<<12)); |
636 | o(mapcc(negcc(sv->c.i))|0x3A00000|(intr(r)<<12)); |
||
145 | halyavin | 637 | return; |
638 | } else if (v == VT_JMP || v == VT_JMPI) { |
||
639 | int t; |
||
640 | t = v & 1; |
||
641 | o(0xE3A00000|(intr(r)<<12)|t); |
||
642 | o(0xEA000000); |
||
6429 | siemargl | 643 | gsym(sv->c.i); |
145 | halyavin | 644 | o(0xE3A00000|(intr(r)<<12)|(t^1)); |
645 | return; |
||
646 | } else if (v < VT_CONST) { |
||
647 | if(is_float(ft)) |
||
6429 | siemargl | 648 | #ifdef TCC_ARM_VFP |
649 | o(0xEEB00A40|(vfpr(r)<<12)|vfpr(v)|T2CPR(ft)); /* fcpyX */ |
||
650 | #else |
||
145 | halyavin | 651 | o(0xEE008180|(fpr(r)<<12)|fpr(v)); |
6429 | siemargl | 652 | #endif |
145 | halyavin | 653 | else |
654 | o(0xE1A00000|(intr(r)<<12)|intr(v)); |
||
655 | return; |
||
656 | } |
||
657 | } |
||
6429 | siemargl | 658 | tcc_error("load unimplemented!"); |
145 | halyavin | 659 | } |
660 | |||
661 | /* store register 'r' in lvalue 'v' */ |
||
662 | void store(int r, SValue *sv) |
||
663 | { |
||
664 | SValue v1; |
||
665 | int v, ft, fc, fr, sign; |
||
6429 | siemargl | 666 | uint32_t op; |
145 | halyavin | 667 | |
668 | fr = sv->r; |
||
669 | ft = sv->type.t; |
||
6429 | siemargl | 670 | fc = sv->c.i; |
145 | halyavin | 671 | |
672 | if(fc>=0) |
||
673 | sign=0; |
||
674 | else { |
||
675 | sign=1; |
||
676 | fc=-fc; |
||
677 | } |
||
6429 | siemargl | 678 | |
679 | v = fr & VT_VALMASK; |
||
145 | halyavin | 680 | if (fr & VT_LVAL || fr == VT_LOCAL) { |
6429 | siemargl | 681 | uint32_t base = 0xb; |
145 | halyavin | 682 | if(v < VT_CONST) { |
683 | base=intr(v); |
||
684 | v=VT_LOCAL; |
||
685 | fc=sign=0; |
||
686 | } else if(v == VT_CONST) { |
||
687 | v1.type.t = ft; |
||
688 | v1.r = fr&~VT_LVAL; |
||
6429 | siemargl | 689 | v1.c.i = sv->c.i; |
145 | halyavin | 690 | v1.sym=sv->sym; |
691 | load(base=14, &v1); |
||
692 | fc=sign=0; |
||
6429 | siemargl | 693 | v=VT_LOCAL; |
145 | halyavin | 694 | } |
695 | if(v == VT_LOCAL) { |
||
696 | if(is_float(ft)) { |
||
697 | calcaddr(&base,&fc,&sign,1020,2); |
||
6429 | siemargl | 698 | #ifdef TCC_ARM_VFP |
699 | op=0xED000A00; /* fsts */ |
||
700 | if(!sign) |
||
701 | op|=0x800000; |
||
702 | if ((ft & VT_BTYPE) != VT_FLOAT) |
||
703 | op|=0x100; /* fsts -> fstd */ |
||
704 | o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); |
||
705 | #else |
||
145 | halyavin | 706 | op=0xED000100; |
707 | if(!sign) |
||
708 | op|=0x800000; |
||
709 | #if LDOUBLE_SIZE == 8 |
||
710 | if ((ft & VT_BTYPE) != VT_FLOAT) |
||
711 | op|=0x8000; |
||
712 | #else |
||
713 | if ((ft & VT_BTYPE) == VT_DOUBLE) |
||
714 | op|=0x8000; |
||
715 | if ((ft & VT_BTYPE) == VT_LDOUBLE) |
||
716 | op|=0x400000; |
||
717 | #endif |
||
718 | o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); |
||
6429 | siemargl | 719 | #endif |
145 | halyavin | 720 | return; |
721 | } else if((ft & VT_BTYPE) == VT_SHORT) { |
||
722 | calcaddr(&base,&fc,&sign,255,0); |
||
723 | op=0xE14000B0; |
||
724 | if(!sign) |
||
725 | op|=0x800000; |
||
726 | o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); |
||
727 | } else { |
||
728 | calcaddr(&base,&fc,&sign,4095,0); |
||
729 | op=0xE5000000; |
||
730 | if(!sign) |
||
731 | op|=0x800000; |
||
6429 | siemargl | 732 | if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL) |
145 | halyavin | 733 | op|=0x400000; |
734 | o(op|(intr(r)<<12)|fc|(base<<16)); |
||
735 | } |
||
736 | return; |
||
737 | } |
||
738 | } |
||
6429 | siemargl | 739 | tcc_error("store unimplemented"); |
145 | halyavin | 740 | } |
741 | |||
742 | static void gadd_sp(int val) |
||
743 | { |
||
744 | stuff_const_harder(0xE28DD000,val); |
||
745 | } |
||
746 | |||
747 | /* 'is_jmp' is '1' if it is a jump */ |
||
748 | static void gcall_or_jmp(int is_jmp) |
||
749 | { |
||
750 | int r; |
||
751 | if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
||
6429 | siemargl | 752 | uint32_t x; |
145 | halyavin | 753 | /* constant case */ |
6429 | siemargl | 754 | x=encbranch(ind,ind+vtop->c.i,0); |
145 | halyavin | 755 | if(x) { |
756 | if (vtop->r & VT_SYM) { |
||
757 | /* relocation case */ |
||
758 | greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); |
||
759 | } else |
||
760 | put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, 0); |
||
761 | o(x|(is_jmp?0xE0000000:0xE1000000)); |
||
762 | } else { |
||
763 | if(!is_jmp) |
||
764 | o(0xE28FE004); // add lr,pc,#4 |
||
765 | o(0xE51FF004); // ldr pc,[pc,#-4] |
||
766 | if (vtop->r & VT_SYM) |
||
767 | greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); |
||
6429 | siemargl | 768 | o(vtop->c.i); |
145 | halyavin | 769 | } |
770 | } else { |
||
771 | /* otherwise, indirect call */ |
||
772 | r = gv(RC_INT); |
||
773 | if(!is_jmp) |
||
774 | o(0xE1A0E00F); // mov lr,pc |
||
775 | o(0xE1A0F000|intr(r)); // mov pc,r |
||
776 | } |
||
777 | } |
||
778 | |||
6429 | siemargl | 779 | /* Return whether a structure is an homogeneous float aggregate or not. |
780 | The answer is true if all the elements of the structure are of the same |
||
781 | primitive float type and there is less than 4 elements. |
||
782 | |||
783 | type: the type corresponding to the structure to be tested */ |
||
784 | static int is_hgen_float_aggr(CType *type) |
||
145 | halyavin | 785 | { |
6429 | siemargl | 786 | if ((type->t & VT_BTYPE) == VT_STRUCT) { |
787 | struct Sym *ref; |
||
788 | int btype, nb_fields = 0; |
||
145 | halyavin | 789 | |
6429 | siemargl | 790 | ref = type->ref->next; |
791 | btype = ref->type.t & VT_BTYPE; |
||
792 | if (btype == VT_FLOAT || btype == VT_DOUBLE) { |
||
793 | for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++); |
||
794 | return !ref && nb_fields <= 4; |
||
145 | halyavin | 795 | } |
796 | } |
||
6429 | siemargl | 797 | return 0; |
798 | } |
||
145 | halyavin | 799 | |
6429 | siemargl | 800 | struct avail_regs { |
801 | signed char avail[3]; /* 3 holes max with only float and double alignments */ |
||
802 | int first_hole; /* first available hole */ |
||
803 | int last_hole; /* last available hole (none if equal to first_hole) */ |
||
804 | int first_free_reg; /* next free register in the sequence, hole excluded */ |
||
805 | }; |
||
806 | |||
807 | #define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 } |
||
808 | |||
809 | /* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC |
||
810 | param) according to the rules described in the procedure call standard for |
||
811 | the ARM architecture (AAPCS). If found, the registers are assigned to this |
||
812 | VFP CPRC parameter. Registers are allocated in sequence unless a hole exists |
||
813 | and the parameter is a single float. |
||
814 | |||
815 | avregs: opaque structure to keep track of available VFP co-processor regs |
||
816 | align: alignment contraints for the param, as returned by type_size() |
||
817 | size: size of the parameter, as returned by type_size() */ |
||
818 | int assign_vfpreg(struct avail_regs *avregs, int align, int size) |
||
819 | { |
||
820 | int first_reg = 0; |
||
821 | |||
822 | if (avregs->first_free_reg == -1) |
||
823 | return -1; |
||
824 | if (align >> 3) { /* double alignment */ |
||
825 | first_reg = avregs->first_free_reg; |
||
826 | /* alignment contraint not respected so use next reg and record hole */ |
||
827 | if (first_reg & 1) |
||
828 | avregs->avail[avregs->last_hole++] = first_reg++; |
||
829 | } else { /* no special alignment (float or array of float) */ |
||
830 | /* if single float and a hole is available, assign the param to it */ |
||
831 | if (size == 4 && avregs->first_hole != avregs->last_hole) |
||
832 | return avregs->avail[avregs->first_hole++]; |
||
833 | else |
||
834 | first_reg = avregs->first_free_reg; |
||
835 | } |
||
836 | if (first_reg + size / 4 <= 16) { |
||
837 | avregs->first_free_reg = first_reg + size / 4; |
||
838 | return first_reg; |
||
839 | } |
||
840 | avregs->first_free_reg = -1; |
||
841 | return -1; |
||
842 | } |
||
843 | |||
844 | /* Returns whether all params need to be passed in core registers or not. |
||
845 | This is the case for function part of the runtime ABI. */ |
||
846 | int floats_in_core_regs(SValue *sval) |
||
847 | { |
||
848 | if (!sval->sym) |
||
849 | return 0; |
||
850 | |||
851 | switch (sval->sym->v) { |
||
852 | case TOK___floatundisf: |
||
853 | case TOK___floatundidf: |
||
854 | case TOK___fixunssfdi: |
||
855 | case TOK___fixunsdfdi: |
||
856 | #ifndef TCC_ARM_VFP |
||
857 | case TOK___fixunsxfdi: |
||
858 | #endif |
||
859 | case TOK___floatdisf: |
||
860 | case TOK___floatdidf: |
||
861 | case TOK___fixsfdi: |
||
862 | case TOK___fixdfdi: |
||
863 | return 1; |
||
864 | |||
865 | default: |
||
866 | return 0; |
||
867 | } |
||
868 | } |
||
869 | |||
870 | /* Return the number of registers needed to return the struct, or 0 if |
||
871 | returning via struct pointer. */ |
||
872 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { |
||
873 | #ifdef TCC_ARM_EABI |
||
874 | int size, align; |
||
875 | size = type_size(vt, &align); |
||
876 | if (float_abi == ARM_HARD_FLOAT && !variadic && |
||
877 | (is_float(vt->t) || is_hgen_float_aggr(vt))) { |
||
878 | *ret_align = 8; |
||
879 | *regsize = 8; |
||
880 | ret->ref = NULL; |
||
881 | ret->t = VT_DOUBLE; |
||
882 | return (size + 7) >> 3; |
||
883 | } else if (size <= 4) { |
||
884 | *ret_align = 4; |
||
885 | *regsize = 4; |
||
886 | ret->ref = NULL; |
||
887 | ret->t = VT_INT; |
||
888 | return 1; |
||
889 | } else |
||
890 | return 0; |
||
891 | #else |
||
892 | return 0; |
||
893 | #endif |
||
894 | } |
||
895 | |||
896 | /* Parameters are classified according to how they are copied to their final |
||
897 | destination for the function call. Because the copying is performed class |
||
898 | after class according to the order in the union below, it is important that |
||
899 | some constraints about the order of the members of this union are respected: |
||
900 | - CORE_STRUCT_CLASS must come after STACK_CLASS; |
||
901 | - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and |
||
902 | VFP_STRUCT_CLASS; |
||
903 | - VFP_STRUCT_CLASS must come after VFP_CLASS. |
||
904 | See the comment for the main loop in copy_params() for the reason. */ |
||
905 | enum reg_class { |
||
906 | STACK_CLASS = 0, |
||
907 | CORE_STRUCT_CLASS, |
||
908 | VFP_CLASS, |
||
909 | VFP_STRUCT_CLASS, |
||
910 | CORE_CLASS, |
||
911 | NB_CLASSES |
||
912 | }; |
||
913 | |||
914 | struct param_plan { |
||
915 | int start; /* first reg or addr used depending on the class */ |
||
916 | int end; /* last reg used or next free addr depending on the class */ |
||
917 | SValue *sval; /* pointer to SValue on the value stack */ |
||
918 | struct param_plan *prev; /* previous element in this class */ |
||
919 | }; |
||
920 | |||
921 | struct plan { |
||
922 | struct param_plan *pplans; /* array of all the param plans */ |
||
923 | struct param_plan *clsplans[NB_CLASSES]; /* per class lists of param plans */ |
||
924 | }; |
||
925 | |||
926 | #define add_param_plan(plan,pplan,class) \ |
||
927 | do { \ |
||
928 | pplan.prev = plan->clsplans[class]; \ |
||
929 | plan->pplans[plan ## _nb] = pplan; \ |
||
930 | plan->clsplans[class] = &plan->pplans[plan ## _nb++]; \ |
||
931 | } while(0) |
||
932 | |||
933 | /* Assign parameters to registers and stack with alignment according to the |
||
934 | rules in the procedure call standard for the ARM architecture (AAPCS). |
||
935 | The overall assignment is recorded in an array of per parameter structures |
||
936 | called parameter plans. The parameter plans are also further organized in a |
||
937 | number of linked lists, one per class of parameter (see the comment for the |
||
938 | definition of union reg_class). |
||
939 | |||
940 | nb_args: number of parameters of the function for which a call is generated |
||
941 | float_abi: float ABI in use for this function call |
||
942 | plan: the structure where the overall assignment is recorded |
||
943 | todo: a bitmap that record which core registers hold a parameter |
||
944 | |||
945 | Returns the amount of stack space needed for parameter passing |
||
946 | |||
947 | Note: this function allocated an array in plan->pplans with tcc_malloc. It |
||
948 | is the responsibility of the caller to free this array once used (ie not |
||
949 | before copy_params). */ |
||
950 | static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) |
||
951 | { |
||
952 | int i, size, align; |
||
953 | int ncrn /* next core register number */, nsaa /* next stacked argument address*/; |
||
954 | int plan_nb = 0; |
||
955 | struct param_plan pplan; |
||
956 | struct avail_regs avregs = AVAIL_REGS_INITIALIZER; |
||
957 | |||
958 | ncrn = nsaa = 0; |
||
959 | *todo = 0; |
||
960 | plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans)); |
||
961 | memset(plan->clsplans, 0, sizeof(plan->clsplans)); |
||
962 | for(i = nb_args; i-- ;) { |
||
963 | int j, start_vfpreg = 0; |
||
964 | CType type = vtop[-i].type; |
||
965 | type.t &= ~VT_ARRAY; |
||
966 | size = type_size(&type, &align); |
||
967 | size = (size + 3) & ~3; |
||
968 | align = (align + 3) & ~3; |
||
969 | switch(vtop[-i].type.t & VT_BTYPE) { |
||
970 | case VT_STRUCT: |
||
971 | case VT_FLOAT: |
||
972 | case VT_DOUBLE: |
||
973 | case VT_LDOUBLE: |
||
974 | if (float_abi == ARM_HARD_FLOAT) { |
||
975 | int is_hfa = 0; /* Homogeneous float aggregate */ |
||
976 | |||
977 | if (is_float(vtop[-i].type.t) |
||
978 | || (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) { |
||
979 | int end_vfpreg; |
||
980 | |||
981 | start_vfpreg = assign_vfpreg(&avregs, align, size); |
||
982 | end_vfpreg = start_vfpreg + ((size - 1) >> 2); |
||
983 | if (start_vfpreg >= 0) { |
||
984 | pplan = (struct param_plan) {start_vfpreg, end_vfpreg, &vtop[-i]}; |
||
985 | if (is_hfa) |
||
986 | add_param_plan(plan, pplan, VFP_STRUCT_CLASS); |
||
987 | else |
||
988 | add_param_plan(plan, pplan, VFP_CLASS); |
||
989 | continue; |
||
990 | } else |
||
991 | break; |
||
992 | } |
||
145 | halyavin | 993 | } |
6429 | siemargl | 994 | ncrn = (ncrn + (align-1)/4) & ~((align/4) - 1); |
995 | if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) { |
||
996 | /* The parameter is allocated both in core register and on stack. As |
||
997 | * such, it can be of either class: it would either be the last of |
||
998 | * CORE_STRUCT_CLASS or the first of STACK_CLASS. */ |
||
999 | for (j = ncrn; j < 4 && j < ncrn + size / 4; j++) |
||
1000 | *todo|=(1< |
||
1001 | pplan = (struct param_plan) {ncrn, j, &vtop[-i]}; |
||
1002 | add_param_plan(plan, pplan, CORE_STRUCT_CLASS); |
||
1003 | ncrn += size/4; |
||
1004 | if (ncrn > 4) |
||
1005 | nsaa = (ncrn - 4) * 4; |
||
145 | halyavin | 1006 | } else { |
6429 | siemargl | 1007 | ncrn = 4; |
1008 | break; |
||
145 | halyavin | 1009 | } |
6429 | siemargl | 1010 | continue; |
1011 | default: |
||
1012 | if (ncrn < 4) { |
||
1013 | int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; |
||
1014 | |||
1015 | if (is_long) { |
||
1016 | ncrn = (ncrn + 1) & -2; |
||
1017 | if (ncrn == 4) |
||
1018 | break; |
||
1019 | } |
||
1020 | pplan = (struct param_plan) {ncrn, ncrn, &vtop[-i]}; |
||
1021 | ncrn++; |
||
1022 | if (is_long) |
||
1023 | pplan.end = ncrn++; |
||
1024 | add_param_plan(plan, pplan, CORE_CLASS); |
||
1025 | continue; |
||
1026 | } |
||
145 | halyavin | 1027 | } |
6429 | siemargl | 1028 | nsaa = (nsaa + (align - 1)) & ~(align - 1); |
1029 | pplan = (struct param_plan) {nsaa, nsaa + size, &vtop[-i]}; |
||
1030 | add_param_plan(plan, pplan, STACK_CLASS); |
||
1031 | nsaa += size; /* size already rounded up before */ |
||
145 | halyavin | 1032 | } |
6429 | siemargl | 1033 | return nsaa; |
1034 | } |
||
1035 | |||
1036 | #undef add_param_plan |
||
1037 | |||
1038 | /* Copy parameters to their final destination (core reg, VFP reg or stack) for |
||
1039 | function call. |
||
1040 | |||
1041 | nb_args: number of parameters the function take |
||
1042 | plan: the overall assignment plan for parameters |
||
1043 | todo: a bitmap indicating what core reg will hold a parameter |
||
1044 | |||
1045 | Returns the number of SValue added by this function on the value stack */ |
||
1046 | static int copy_params(int nb_args, struct plan *plan, int todo) |
||
1047 | { |
||
1048 | int size, align, r, i, nb_extra_sval = 0; |
||
1049 | struct param_plan *pplan; |
||
1050 | |||
1051 | /* Several constraints require parameters to be copied in a specific order: |
||
1052 | - structures are copied to the stack before being loaded in a reg; |
||
1053 | - floats loaded to an odd numbered VFP reg are first copied to the |
||
1054 | preceding even numbered VFP reg and then moved to the next VFP reg. |
||
1055 | |||
1056 | It is thus important that: |
||
1057 | - structures assigned to core regs must be copied after parameters |
||
1058 | assigned to the stack but before structures assigned to VFP regs because |
||
1059 | a structure can lie partly in core registers and partly on the stack; |
||
1060 | - parameters assigned to the stack and all structures be copied before |
||
1061 | parameters assigned to a core reg since copying a parameter to the stack |
||
1062 | require using a core reg; |
||
1063 | - parameters assigned to VFP regs be copied before structures assigned to |
||
1064 | VFP regs as the copy might use an even numbered VFP reg that already |
||
1065 | holds part of a structure. */ |
||
1066 | for(i = 0; i < NB_CLASSES; i++) { |
||
1067 | for(pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) { |
||
1068 | vpushv(pplan->sval); |
||
1069 | pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */ |
||
1070 | switch(i) { |
||
1071 | case STACK_CLASS: |
||
1072 | case CORE_STRUCT_CLASS: |
||
1073 | case VFP_STRUCT_CLASS: |
||
1074 | if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) { |
||
1075 | int padding = 0; |
||
1076 | size = type_size(&pplan->sval->type, &align); |
||
1077 | /* align to stack align size */ |
||
1078 | size = (size + 3) & ~3; |
||
1079 | if (i == STACK_CLASS && pplan->prev) |
||
1080 | padding = pplan->start - pplan->prev->end; |
||
1081 | size += padding; /* Add padding if any */ |
||
1082 | /* allocate the necessary size on stack */ |
||
1083 | gadd_sp(-size); |
||
1084 | /* generate structure store */ |
||
1085 | r = get_reg(RC_INT); |
||
1086 | o(0xE28D0000|(intr(r)<<12)|padding); /* add r, sp, padding */ |
||
1087 | vset(&vtop->type, r | VT_LVAL, 0); |
||
1088 | vswap(); |
||
1089 | vstore(); /* memcpy to current sp + potential padding */ |
||
1090 | |||
1091 | /* Homogeneous float aggregate are loaded to VFP registers |
||
1092 | immediately since there is no way of loading data in multiple |
||
1093 | non consecutive VFP registers as what is done for other |
||
1094 | structures (see the use of todo). */ |
||
1095 | if (i == VFP_STRUCT_CLASS) { |
||
1096 | int first = pplan->start, nb = pplan->end - first + 1; |
||
1097 | /* vpop.32 {pplan->start, ..., pplan->end} */ |
||
1098 | o(0xECBD0A00|(first&1)<<22|(first>>1)<<12|nb); |
||
1099 | /* No need to write the register used to a SValue since VFP regs |
||
1100 | cannot be used for gcall_or_jmp */ |
||
1101 | } |
||
1102 | } else { |
||
1103 | if (is_float(pplan->sval->type.t)) { |
||
1104 | #ifdef TCC_ARM_VFP |
||
1105 | r = vfpr(gv(RC_FLOAT)) << 12; |
||
1106 | if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) |
||
1107 | size = 4; |
||
1108 | else { |
||
1109 | size = 8; |
||
1110 | r |= 0x101; /* vpush.32 -> vpush.64 */ |
||
1111 | } |
||
1112 | o(0xED2D0A01 + r); /* vpush */ |
||
1113 | #else |
||
1114 | r = fpr(gv(RC_FLOAT)) << 12; |
||
1115 | if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT) |
||
1116 | size = 4; |
||
1117 | else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE) |
||
1118 | size = 8; |
||
1119 | else |
||
1120 | size = LDOUBLE_SIZE; |
||
1121 | |||
1122 | if (size == 12) |
||
1123 | r |= 0x400000; |
||
1124 | else if(size == 8) |
||
1125 | r|=0x8000; |
||
1126 | |||
1127 | o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */ |
||
1128 | #endif |
||
1129 | } else { |
||
1130 | /* simple type (currently always same size) */ |
||
1131 | /* XXX: implicit cast ? */ |
||
1132 | size=4; |
||
1133 | if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { |
||
1134 | lexpand_nr(); |
||
1135 | size = 8; |
||
1136 | r = gv(RC_INT); |
||
1137 | o(0xE52D0004|(intr(r)<<12)); /* push r */ |
||
1138 | vtop--; |
||
1139 | } |
||
1140 | r = gv(RC_INT); |
||
1141 | o(0xE52D0004|(intr(r)<<12)); /* push r */ |
||
1142 | } |
||
1143 | if (i == STACK_CLASS && pplan->prev) |
||
1144 | gadd_sp(pplan->prev->end - pplan->start); /* Add padding if any */ |
||
1145 | } |
||
1146 | break; |
||
1147 | |||
1148 | case VFP_CLASS: |
||
1149 | gv(regmask(TREG_F0 + (pplan->start >> 1))); |
||
1150 | if (pplan->start & 1) { /* Must be in upper part of double register */ |
||
1151 | o(0xEEF00A40|((pplan->start>>1)<<12)|(pplan->start>>1)); /* vmov.f32 s(n+1), sn */ |
||
1152 | vtop->r = VT_CONST; /* avoid being saved on stack by gv for next float */ |
||
1153 | } |
||
1154 | break; |
||
1155 | |||
1156 | case CORE_CLASS: |
||
1157 | if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) { |
||
1158 | lexpand_nr(); |
||
1159 | gv(regmask(pplan->end)); |
||
1160 | pplan->sval->r2 = vtop->r; |
||
1161 | vtop--; |
||
1162 | } |
||
1163 | gv(regmask(pplan->start)); |
||
1164 | /* Mark register as used so that gcall_or_jmp use another one |
||
1165 | (regs >=4 are free as never used to pass parameters) */ |
||
1166 | pplan->sval->r = vtop->r; |
||
1167 | break; |
||
1168 | } |
||
1169 | vtop--; |
||
1170 | } |
||
145 | halyavin | 1171 | } |
6429 | siemargl | 1172 | |
1173 | /* Manually free remaining registers since next parameters are loaded |
||
1174 | * manually, without the help of gv(int). */ |
||
1175 | save_regs(nb_args); |
||
1176 | |||
1177 | if(todo) { |
||
1178 | o(0xE8BD0000|todo); /* pop {todo} */ |
||
1179 | for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { |
||
1180 | int r; |
||
1181 | pplan->sval->r = pplan->start; |
||
1182 | /* An SValue can only pin 2 registers at best (r and r2) but a structure |
||
1183 | can occupy more than 2 registers. Thus, we need to push on the value |
||
1184 | stack some fake parameter to have on SValue for each registers used |
||
1185 | by a structure (r2 is not used). */ |
||
1186 | for (r = pplan->start + 1; r <= pplan->end; r++) { |
||
1187 | if (todo & (1 << r)) { |
||
1188 | nb_extra_sval++; |
||
1189 | vpushi(0); |
||
1190 | vtop->r = r; |
||
1191 | } |
||
1192 | } |
||
145 | halyavin | 1193 | } |
1194 | } |
||
6429 | siemargl | 1195 | return nb_extra_sval; |
1196 | } |
||
1197 | |||
1198 | /* Generate function call. The function address is pushed first, then |
||
1199 | all the parameters in call order. This functions pops all the |
||
1200 | parameters and the function address. */ |
||
1201 | void gfunc_call(int nb_args) |
||
1202 | { |
||
1203 | int r, args_size; |
||
1204 | int def_float_abi = float_abi; |
||
1205 | int todo; |
||
1206 | struct plan plan; |
||
1207 | |||
1208 | #ifdef TCC_ARM_EABI |
||
1209 | int variadic; |
||
1210 | |||
1211 | if (float_abi == ARM_HARD_FLOAT) { |
||
1212 | variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); |
||
1213 | if (variadic || floats_in_core_regs(&vtop[-nb_args])) |
||
1214 | float_abi = ARM_SOFTFP_FLOAT; |
||
1215 | } |
||
1216 | #endif |
||
1217 | /* cannot let cpu flags if other instruction are generated. Also avoid leaving |
||
1218 | VT_JMP anywhere except on the top of the stack because it would complicate |
||
1219 | the code generator. */ |
||
1220 | r = vtop->r & VT_VALMASK; |
||
1221 | if (r == VT_CMP || (r & ~1) == VT_JMP) |
||
1222 | gv(RC_INT); |
||
1223 | |||
1224 | args_size = assign_regs(nb_args, float_abi, &plan, &todo); |
||
1225 | |||
1226 | #ifdef TCC_ARM_EABI |
||
1227 | if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ |
||
1228 | args_size = (args_size + 7) & ~7; |
||
1229 | o(0xE24DD004); /* sub sp, sp, #4 */ |
||
1230 | } |
||
1231 | #endif |
||
1232 | |||
1233 | nb_args += copy_params(nb_args, &plan, todo); |
||
1234 | tcc_free(plan.pplans); |
||
1235 | |||
1236 | /* Move fct SValue on top as required by gcall_or_jmp */ |
||
1237 | vrotb(nb_args + 1); |
||
145 | halyavin | 1238 | gcall_or_jmp(0); |
1239 | if (args_size) |
||
6429 | siemargl | 1240 | gadd_sp(args_size); /* pop all parameters passed on the stack */ |
1241 | #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) |
||
1242 | if(float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) { |
||
1243 | if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { |
||
1244 | o(0xEE000A10); /*vmov s0, r0 */ |
||
1245 | } else { |
||
1246 | o(0xEE000B10); /* vmov.32 d0[0], r0 */ |
||
1247 | o(0xEE201B10); /* vmov.32 d0[1], r1 */ |
||
1248 | } |
||
1249 | } |
||
1250 | #endif |
||
1251 | vtop -= nb_args + 1; /* Pop all params and fct address from value stack */ |
||
1252 | leaffunc = 0; /* we are calling a function, so we aren't in a leaf function */ |
||
1253 | float_abi = def_float_abi; |
||
145 | halyavin | 1254 | } |
1255 | |||
1256 | /* generate function prolog of type 't' */ |
||
1257 | void gfunc_prolog(CType *func_type) |
||
1258 | { |
||
1259 | Sym *sym,*sym2; |
||
6429 | siemargl | 1260 | int n, nf, size, align, rs, struct_ret = 0; |
1261 | int addr, pn, sn; /* pn=core, sn=stack */ |
||
1262 | CType ret_type; |
||
145 | halyavin | 1263 | |
6429 | siemargl | 1264 | #ifdef TCC_ARM_EABI |
1265 | struct avail_regs avregs = AVAIL_REGS_INITIALIZER; |
||
1266 | #endif |
||
1267 | |||
145 | halyavin | 1268 | sym = func_type->ref; |
1269 | func_vt = sym->type; |
||
6429 | siemargl | 1270 | func_var = (func_type->ref->c == FUNC_ELLIPSIS); |
1271 | |||
1272 | n = nf = 0; |
||
1273 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT && |
||
1274 | !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs)) |
||
1275 | { |
||
145 | halyavin | 1276 | n++; |
6429 | siemargl | 1277 | struct_ret = 1; |
1278 | func_vc = 12; /* Offset from fp of the place to store the result */ |
||
145 | halyavin | 1279 | } |
6429 | siemargl | 1280 | for(sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) { |
145 | halyavin | 1281 | size = type_size(&sym2->type, &align); |
6429 | siemargl | 1282 | #ifdef TCC_ARM_EABI |
1283 | if (float_abi == ARM_HARD_FLOAT && !func_var && |
||
1284 | (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { |
||
1285 | int tmpnf = assign_vfpreg(&avregs, align, size); |
||
1286 | tmpnf += (size + 3) / 4; |
||
1287 | nf = (tmpnf > nf) ? tmpnf : nf; |
||
1288 | } else |
||
1289 | #endif |
||
1290 | if (n < 4) |
||
1291 | n += (size + 3) / 4; |
||
145 | halyavin | 1292 | } |
1293 | o(0xE1A0C00D); /* mov ip,sp */ |
||
6429 | siemargl | 1294 | if (func_var) |
145 | halyavin | 1295 | n=4; |
6429 | siemargl | 1296 | if (n) { |
145 | halyavin | 1297 | if(n>4) |
1298 | n=4; |
||
6429 | siemargl | 1299 | #ifdef TCC_ARM_EABI |
1300 | n=(n+1)&-2; |
||
1301 | #endif |
||
145 | halyavin | 1302 | o(0xE92D0000|((1< |
1303 | } |
||
6429 | siemargl | 1304 | if (nf) { |
1305 | if (nf>16) |
||
1306 | nf=16; |
||
1307 | nf=(nf+1)&-2; /* nf => HARDFLOAT => EABI */ |
||
1308 | o(0xED2D0A00|nf); /* save s0-s15 on stack if needed */ |
||
1309 | } |
||
1310 | o(0xE92D5800); /* save fp, ip, lr */ |
||
1311 | o(0xE1A0B00D); /* mov fp, sp */ |
||
145 | halyavin | 1312 | func_sub_sp_offset = ind; |
6429 | siemargl | 1313 | o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */ |
1314 | |||
1315 | #ifdef TCC_ARM_EABI |
||
1316 | if (float_abi == ARM_HARD_FLOAT) { |
||
1317 | func_vc += nf * 4; |
||
1318 | avregs = AVAIL_REGS_INITIALIZER; |
||
1319 | } |
||
1320 | #endif |
||
1321 | pn = struct_ret, sn = 0; |
||
145 | halyavin | 1322 | while ((sym = sym->next)) { |
1323 | CType *type; |
||
1324 | type = &sym->type; |
||
1325 | size = type_size(type, &align); |
||
6429 | siemargl | 1326 | size = (size + 3) >> 2; |
1327 | align = (align + 3) & ~3; |
||
1328 | #ifdef TCC_ARM_EABI |
||
1329 | if (float_abi == ARM_HARD_FLOAT && !func_var && (is_float(sym->type.t) |
||
1330 | || is_hgen_float_aggr(&sym->type))) { |
||
1331 | int fpn = assign_vfpreg(&avregs, align, size << 2); |
||
1332 | if (fpn >= 0) |
||
1333 | addr = fpn * 4; |
||
1334 | else |
||
1335 | goto from_stack; |
||
1336 | } else |
||
1337 | #endif |
||
1338 | if (pn < 4) { |
||
1339 | #ifdef TCC_ARM_EABI |
||
1340 | pn = (pn + (align-1)/4) & -(align/4); |
||
1341 | #endif |
||
1342 | addr = (nf + pn) * 4; |
||
1343 | pn += size; |
||
1344 | if (!sn && pn > 4) |
||
1345 | sn = (pn - 4); |
||
1346 | } else { |
||
1347 | #ifdef TCC_ARM_EABI |
||
1348 | from_stack: |
||
1349 | sn = (sn + (align-1)/4) & -(align/4); |
||
1350 | #endif |
||
1351 | addr = (n + nf + sn) * 4; |
||
1352 | sn += size; |
||
1353 | } |
||
1354 | sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), |
||
1355 | addr + 12); |
||
145 | halyavin | 1356 | } |
1357 | last_itod_magic=0; |
||
6429 | siemargl | 1358 | leaffunc = 1; |
145 | halyavin | 1359 | loc = 0; |
1360 | } |
||
1361 | |||
1362 | /* generate function epilog */ |
||
1363 | void gfunc_epilog(void) |
||
1364 | { |
||
6429 | siemargl | 1365 | uint32_t x; |
1366 | int diff; |
||
1367 | /* Copy float return value to core register if base standard is used and |
||
1368 | float computation is made with VFP */ |
||
1369 | #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) |
||
1370 | if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) { |
||
1371 | if((func_vt.t & VT_BTYPE) == VT_FLOAT) |
||
1372 | o(0xEE100A10); /* fmrs r0, s0 */ |
||
1373 | else { |
||
1374 | o(0xEE100B10); /* fmrdl r0, d0 */ |
||
1375 | o(0xEE301B10); /* fmrdh r1, d0 */ |
||
1376 | } |
||
1377 | } |
||
1378 | #endif |
||
145 | halyavin | 1379 | o(0xE89BA800); /* restore fp, sp, pc */ |
6429 | siemargl | 1380 | diff = (-loc + 3) & -4; |
1381 | #ifdef TCC_ARM_EABI |
||
1382 | if(!leaffunc) |
||
1383 | diff = ((diff + 11) & -8) - 4; |
||
1384 | #endif |
||
1385 | if(diff > 0) { |
||
1386 | x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ |
||
145 | halyavin | 1387 | if(x) |
6429 | siemargl | 1388 | *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = x; |
145 | halyavin | 1389 | else { |
6429 | siemargl | 1390 | int addr; |
145 | halyavin | 1391 | addr=ind; |
1392 | o(0xE59FC004); /* ldr ip,[pc+4] */ |
||
6429 | siemargl | 1393 | o(0xE04BD00C); /* sub sp,fp,ip */ |
145 | halyavin | 1394 | o(0xE1A0F00E); /* mov pc,lr */ |
6429 | siemargl | 1395 | o(diff); |
1396 | *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); |
||
145 | halyavin | 1397 | } |
1398 | } |
||
1399 | } |
||
1400 | |||
1401 | /* generate a jump to a label */ |
||
1402 | int gjmp(int t) |
||
1403 | { |
||
1404 | int r; |
||
1405 | r=ind; |
||
1406 | o(0xE0000000|encbranch(r,t,1)); |
||
1407 | return r; |
||
1408 | } |
||
1409 | |||
1410 | /* generate a jump to a fixed address */ |
||
1411 | void gjmp_addr(int a) |
||
1412 | { |
||
1413 | gjmp(a); |
||
1414 | } |
||
1415 | |||
1416 | /* generate a test. set 'inv' to invert test. Stack entry is popped */ |
||
1417 | int gtst(int inv, int t) |
||
1418 | { |
||
1419 | int v, r; |
||
6429 | siemargl | 1420 | uint32_t op; |
145 | halyavin | 1421 | v = vtop->r & VT_VALMASK; |
1422 | r=ind; |
||
1423 | if (v == VT_CMP) { |
||
1424 | op=mapcc(inv?negcc(vtop->c.i):vtop->c.i); |
||
1425 | op|=encbranch(r,t,1); |
||
1426 | o(op); |
||
1427 | t=r; |
||
1428 | } else if (v == VT_JMP || v == VT_JMPI) { |
||
1429 | if ((v & 1) == inv) { |
||
1430 | if(!vtop->c.i) |
||
1431 | vtop->c.i=t; |
||
1432 | else { |
||
6429 | siemargl | 1433 | uint32_t *x; |
145 | halyavin | 1434 | int p,lp; |
1435 | if(t) { |
||
1436 | p = vtop->c.i; |
||
1437 | do { |
||
1438 | p = decbranch(lp=p); |
||
1439 | } while(p); |
||
6429 | siemargl | 1440 | x = (uint32_t *)(cur_text_section->data + lp); |
145 | halyavin | 1441 | *x &= 0xff000000; |
1442 | *x |= encbranch(lp,t,1); |
||
1443 | } |
||
1444 | t = vtop->c.i; |
||
1445 | } |
||
1446 | } else { |
||
1447 | t = gjmp(t); |
||
1448 | gsym(vtop->c.i); |
||
1449 | } |
||
1450 | } |
||
1451 | vtop--; |
||
1452 | return t; |
||
1453 | } |
||
1454 | |||
1455 | /* generate an integer binary operation */ |
||
1456 | void gen_opi(int op) |
||
1457 | { |
||
1458 | int c, func = 0; |
||
6429 | siemargl | 1459 | uint32_t opc = 0, r, fr; |
1460 | unsigned short retreg = REG_IRET; |
||
145 | halyavin | 1461 | |
1462 | c=0; |
||
1463 | switch(op) { |
||
1464 | case '+': |
||
1465 | opc = 0x8; |
||
1466 | c=1; |
||
1467 | break; |
||
1468 | case TOK_ADDC1: /* add with carry generation */ |
||
1469 | opc = 0x9; |
||
1470 | c=1; |
||
1471 | break; |
||
1472 | case '-': |
||
1473 | opc = 0x4; |
||
1474 | c=1; |
||
1475 | break; |
||
1476 | case TOK_SUBC1: /* sub with carry generation */ |
||
1477 | opc = 0x5; |
||
1478 | c=1; |
||
1479 | break; |
||
1480 | case TOK_ADDC2: /* add with carry use */ |
||
1481 | opc = 0xA; |
||
1482 | c=1; |
||
1483 | break; |
||
1484 | case TOK_SUBC2: /* sub with carry use */ |
||
1485 | opc = 0xC; |
||
1486 | c=1; |
||
1487 | break; |
||
1488 | case '&': |
||
1489 | opc = 0x0; |
||
1490 | c=1; |
||
1491 | break; |
||
1492 | case '^': |
||
1493 | opc = 0x2; |
||
1494 | c=1; |
||
1495 | break; |
||
1496 | case '|': |
||
1497 | opc = 0x18; |
||
1498 | c=1; |
||
1499 | break; |
||
1500 | case '*': |
||
1501 | gv2(RC_INT, RC_INT); |
||
1502 | r = vtop[-1].r; |
||
1503 | fr = vtop[0].r; |
||
1504 | vtop--; |
||
1505 | o(0xE0000090|(intr(r)<<16)|(intr(r)<<8)|intr(fr)); |
||
1506 | return; |
||
1507 | case TOK_SHL: |
||
1508 | opc = 0; |
||
1509 | c=2; |
||
1510 | break; |
||
1511 | case TOK_SHR: |
||
1512 | opc = 1; |
||
1513 | c=2; |
||
1514 | break; |
||
1515 | case TOK_SAR: |
||
1516 | opc = 2; |
||
1517 | c=2; |
||
1518 | break; |
||
1519 | case '/': |
||
1520 | case TOK_PDIV: |
||
1521 | func=TOK___divsi3; |
||
1522 | c=3; |
||
1523 | break; |
||
1524 | case TOK_UDIV: |
||
1525 | func=TOK___udivsi3; |
||
1526 | c=3; |
||
1527 | break; |
||
1528 | case '%': |
||
6429 | siemargl | 1529 | #ifdef TCC_ARM_EABI |
1530 | func=TOK___aeabi_idivmod; |
||
1531 | retreg=REG_LRET; |
||
1532 | #else |
||
145 | halyavin | 1533 | func=TOK___modsi3; |
6429 | siemargl | 1534 | #endif |
145 | halyavin | 1535 | c=3; |
1536 | break; |
||
1537 | case TOK_UMOD: |
||
6429 | siemargl | 1538 | #ifdef TCC_ARM_EABI |
1539 | func=TOK___aeabi_uidivmod; |
||
1540 | retreg=REG_LRET; |
||
1541 | #else |
||
145 | halyavin | 1542 | func=TOK___umodsi3; |
6429 | siemargl | 1543 | #endif |
145 | halyavin | 1544 | c=3; |
1545 | break; |
||
1546 | case TOK_UMULL: |
||
1547 | gv2(RC_INT, RC_INT); |
||
1548 | r=intr(vtop[-1].r2=get_reg(RC_INT)); |
||
1549 | c=vtop[-1].r; |
||
1550 | vtop[-1].r=get_reg_ex(RC_INT,regmask(c)); |
||
1551 | vtop--; |
||
1552 | o(0xE0800090|(r<<16)|(intr(vtop->r)<<12)|(intr(c)<<8)|intr(vtop[1].r)); |
||
1553 | return; |
||
1554 | default: |
||
1555 | opc = 0x15; |
||
1556 | c=1; |
||
1557 | break; |
||
1558 | } |
||
1559 | switch(c) { |
||
1560 | case 1: |
||
1561 | if((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
1562 | if(opc == 4 || opc == 5 || opc == 0xc) { |
||
1563 | vswap(); |
||
1564 | opc|=2; // sub -> rsb |
||
1565 | } |
||
1566 | } |
||
1567 | if ((vtop->r & VT_VALMASK) == VT_CMP || |
||
1568 | (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) |
||
1569 | gv(RC_INT); |
||
1570 | vswap(); |
||
1571 | c=intr(gv(RC_INT)); |
||
1572 | vswap(); |
||
1573 | opc=0xE0000000|(opc<<20)|(c<<16); |
||
1574 | if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
6429 | siemargl | 1575 | uint32_t x; |
145 | halyavin | 1576 | x=stuff_const(opc|0x2000000,vtop->c.i); |
1577 | if(x) { |
||
1578 | r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); |
||
1579 | o(x|(r<<12)); |
||
1580 | goto done; |
||
1581 | } |
||
1582 | } |
||
1583 | fr=intr(gv(RC_INT)); |
||
1584 | r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); |
||
1585 | o(opc|(r<<12)|fr); |
||
1586 | done: |
||
1587 | vtop--; |
||
1588 | if (op >= TOK_ULT && op <= TOK_GT) { |
||
1589 | vtop->r = VT_CMP; |
||
1590 | vtop->c.i = op; |
||
1591 | } |
||
1592 | break; |
||
1593 | case 2: |
||
1594 | opc=0xE1A00000|(opc<<5); |
||
1595 | if ((vtop->r & VT_VALMASK) == VT_CMP || |
||
1596 | (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) |
||
1597 | gv(RC_INT); |
||
1598 | vswap(); |
||
1599 | r=intr(gv(RC_INT)); |
||
1600 | vswap(); |
||
1601 | opc|=r; |
||
1602 | if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
1603 | fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); |
||
1604 | c = vtop->c.i & 0x1f; |
||
1605 | o(opc|(c<<7)|(fr<<12)); |
||
1606 | } else { |
||
1607 | fr=intr(gv(RC_INT)); |
||
1608 | c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); |
||
1609 | o(opc|(c<<12)|(fr<<8)|0x10); |
||
1610 | } |
||
1611 | vtop--; |
||
1612 | break; |
||
1613 | case 3: |
||
1614 | vpush_global_sym(&func_old_type, func); |
||
1615 | vrott(3); |
||
1616 | gfunc_call(2); |
||
1617 | vpushi(0); |
||
6429 | siemargl | 1618 | vtop->r = retreg; |
145 | halyavin | 1619 | break; |
1620 | default: |
||
6429 | siemargl | 1621 | tcc_error("gen_opi %i unimplemented!",op); |
145 | halyavin | 1622 | } |
1623 | } |
||
1624 | |||
6429 | siemargl | 1625 | #ifdef TCC_ARM_VFP |
1626 | static int is_zero(int i) |
||
145 | halyavin | 1627 | { |
6429 | siemargl | 1628 | if((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) |
1629 | return 0; |
||
1630 | if (vtop[i].type.t == VT_FLOAT) |
||
1631 | return (vtop[i].c.f == 0.f); |
||
1632 | else if (vtop[i].type.t == VT_DOUBLE) |
||
1633 | return (vtop[i].c.d == 0.0); |
||
1634 | return (vtop[i].c.ld == 0.l); |
||
1635 | } |
||
1636 | |||
1637 | /* generate a floating point operation 'v = t1 op t2' instruction. The |
||
1638 | * two operands are guaranted to have the same floating point type */ |
||
1639 | void gen_opf(int op) |
||
1640 | { |
||
1641 | uint32_t x; |
||
1642 | int fneg=0,r; |
||
1643 | x=0xEE000A00|T2CPR(vtop->type.t); |
||
1644 | switch(op) { |
||
1645 | case '+': |
||
1646 | if(is_zero(-1)) |
||
1647 | vswap(); |
||
1648 | if(is_zero(0)) { |
||
1649 | vtop--; |
||
1650 | return; |
||
1651 | } |
||
1652 | x|=0x300000; |
||
1653 | break; |
||
1654 | case '-': |
||
1655 | x|=0x300040; |
||
1656 | if(is_zero(0)) { |
||
1657 | vtop--; |
||
1658 | return; |
||
1659 | } |
||
1660 | if(is_zero(-1)) { |
||
1661 | x|=0x810000; /* fsubX -> fnegX */ |
||
1662 | vswap(); |
||
1663 | vtop--; |
||
1664 | fneg=1; |
||
1665 | } |
||
1666 | break; |
||
1667 | case '*': |
||
1668 | x|=0x200000; |
||
1669 | break; |
||
1670 | case '/': |
||
1671 | x|=0x800000; |
||
1672 | break; |
||
1673 | default: |
||
1674 | if(op < TOK_ULT || op > TOK_GT) { |
||
1675 | tcc_error("unknown fp op %x!",op); |
||
1676 | return; |
||
1677 | } |
||
1678 | if(is_zero(-1)) { |
||
1679 | vswap(); |
||
1680 | switch(op) { |
||
1681 | case TOK_LT: op=TOK_GT; break; |
||
1682 | case TOK_GE: op=TOK_ULE; break; |
||
1683 | case TOK_LE: op=TOK_GE; break; |
||
1684 | case TOK_GT: op=TOK_ULT; break; |
||
1685 | } |
||
1686 | } |
||
1687 | x|=0xB40040; /* fcmpX */ |
||
1688 | if(op!=TOK_EQ && op!=TOK_NE) |
||
1689 | x|=0x80; /* fcmpX -> fcmpeX */ |
||
1690 | if(is_zero(0)) { |
||
1691 | vtop--; |
||
1692 | o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */ |
||
1693 | } else { |
||
1694 | x|=vfpr(gv(RC_FLOAT)); |
||
1695 | vswap(); |
||
1696 | o(x|(vfpr(gv(RC_FLOAT))<<12)); |
||
1697 | vtop--; |
||
1698 | } |
||
1699 | o(0xEEF1FA10); /* fmstat */ |
||
1700 | |||
1701 | switch(op) { |
||
1702 | case TOK_LE: op=TOK_ULE; break; |
||
1703 | case TOK_LT: op=TOK_ULT; break; |
||
1704 | case TOK_UGE: op=TOK_GE; break; |
||
1705 | case TOK_UGT: op=TOK_GT; break; |
||
1706 | } |
||
1707 | |||
1708 | vtop->r = VT_CMP; |
||
1709 | vtop->c.i = op; |
||
1710 | return; |
||
1711 | } |
||
1712 | r=gv(RC_FLOAT); |
||
1713 | x|=vfpr(r); |
||
1714 | r=regmask(r); |
||
1715 | if(!fneg) { |
||
1716 | int r2; |
||
1717 | vswap(); |
||
1718 | r2=gv(RC_FLOAT); |
||
1719 | x|=vfpr(r2)<<16; |
||
1720 | r|=regmask(r2); |
||
1721 | } |
||
1722 | vtop->r=get_reg_ex(RC_FLOAT,r); |
||
1723 | if(!fneg) |
||
1724 | vtop--; |
||
1725 | o(x|(vfpr(vtop->r)<<12)); |
||
1726 | } |
||
1727 | |||
1728 | #else |
||
1729 | static uint32_t is_fconst() |
||
1730 | { |
||
145 | halyavin | 1731 | long double f; |
6429 | siemargl | 1732 | uint32_t r; |
145 | halyavin | 1733 | if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) |
1734 | return 0; |
||
1735 | if (vtop->type.t == VT_FLOAT) |
||
1736 | f = vtop->c.f; |
||
1737 | else if (vtop->type.t == VT_DOUBLE) |
||
1738 | f = vtop->c.d; |
||
1739 | else |
||
1740 | f = vtop->c.ld; |
||
1741 | if(!ieee_finite(f)) |
||
1742 | return 0; |
||
1743 | r=0x8; |
||
1744 | if(f<0.0) { |
||
1745 | r=0x18; |
||
1746 | f=-f; |
||
1747 | } |
||
1748 | if(f==0.0) |
||
1749 | return r; |
||
1750 | if(f==1.0) |
||
1751 | return r|1; |
||
1752 | if(f==2.0) |
||
1753 | return r|2; |
||
1754 | if(f==3.0) |
||
1755 | return r|3; |
||
1756 | if(f==4.0) |
||
1757 | return r|4; |
||
1758 | if(f==5.0) |
||
1759 | return r|5; |
||
1760 | if(f==0.5) |
||
1761 | return r|6; |
||
1762 | if(f==10.0) |
||
1763 | return r|7; |
||
1764 | return 0; |
||
1765 | } |
||
1766 | |||
1767 | /* generate a floating point operation 'v = t1 op t2' instruction. The |
||
1768 | two operands are guaranted to have the same floating point type */ |
||
1769 | void gen_opf(int op) |
||
1770 | { |
||
6429 | siemargl | 1771 | uint32_t x, r, r2, c1, c2; |
145 | halyavin | 1772 | //fputs("gen_opf\n",stderr); |
1773 | vswap(); |
||
1774 | c1 = is_fconst(); |
||
1775 | vswap(); |
||
1776 | c2 = is_fconst(); |
||
1777 | x=0xEE000100; |
||
1778 | #if LDOUBLE_SIZE == 8 |
||
1779 | if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) |
||
1780 | x|=0x80; |
||
1781 | #else |
||
1782 | if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) |
||
1783 | x|=0x80; |
||
1784 | else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) |
||
1785 | x|=0x80000; |
||
1786 | #endif |
||
1787 | switch(op) |
||
1788 | { |
||
1789 | case '+': |
||
1790 | if(!c2) { |
||
1791 | vswap(); |
||
1792 | c2=c1; |
||
1793 | } |
||
1794 | vswap(); |
||
1795 | r=fpr(gv(RC_FLOAT)); |
||
1796 | vswap(); |
||
1797 | if(c2) { |
||
1798 | if(c2>0xf) |
||
1799 | x|=0x200000; // suf |
||
1800 | r2=c2&0xf; |
||
1801 | } else { |
||
1802 | r2=fpr(gv(RC_FLOAT)); |
||
1803 | } |
||
1804 | break; |
||
1805 | case '-': |
||
1806 | if(c2) { |
||
1807 | if(c2<=0xf) |
||
1808 | x|=0x200000; // suf |
||
1809 | r2=c2&0xf; |
||
1810 | vswap(); |
||
1811 | r=fpr(gv(RC_FLOAT)); |
||
1812 | vswap(); |
||
1813 | } else if(c1 && c1<=0xf) { |
||
1814 | x|=0x300000; // rsf |
||
1815 | r2=c1; |
||
1816 | r=fpr(gv(RC_FLOAT)); |
||
1817 | vswap(); |
||
1818 | } else { |
||
1819 | x|=0x200000; // suf |
||
1820 | vswap(); |
||
1821 | r=fpr(gv(RC_FLOAT)); |
||
1822 | vswap(); |
||
1823 | r2=fpr(gv(RC_FLOAT)); |
||
1824 | } |
||
1825 | break; |
||
1826 | case '*': |
||
1827 | if(!c2 || c2>0xf) { |
||
1828 | vswap(); |
||
1829 | c2=c1; |
||
1830 | } |
||
1831 | vswap(); |
||
1832 | r=fpr(gv(RC_FLOAT)); |
||
1833 | vswap(); |
||
1834 | if(c2 && c2<=0xf) |
||
1835 | r2=c2; |
||
1836 | else |
||
1837 | r2=fpr(gv(RC_FLOAT)); |
||
1838 | x|=0x100000; // muf |
||
1839 | break; |
||
1840 | case '/': |
||
1841 | if(c2 && c2<=0xf) { |
||
1842 | x|=0x400000; // dvf |
||
1843 | r2=c2; |
||
1844 | vswap(); |
||
1845 | r=fpr(gv(RC_FLOAT)); |
||
1846 | vswap(); |
||
1847 | } else if(c1 && c1<=0xf) { |
||
1848 | x|=0x500000; // rdf |
||
1849 | r2=c1; |
||
1850 | r=fpr(gv(RC_FLOAT)); |
||
1851 | vswap(); |
||
1852 | } else { |
||
1853 | x|=0x400000; // dvf |
||
1854 | vswap(); |
||
1855 | r=fpr(gv(RC_FLOAT)); |
||
1856 | vswap(); |
||
1857 | r2=fpr(gv(RC_FLOAT)); |
||
6429 | siemargl | 1858 | } |
145 | halyavin | 1859 | break; |
1860 | default: |
||
1861 | if(op >= TOK_ULT && op <= TOK_GT) { |
||
1862 | x|=0xd0f110; // cmfe |
||
6429 | siemargl | 1863 | /* bug (intention?) in Linux FPU emulator |
1864 | doesn't set carry if equal */ |
||
145 | halyavin | 1865 | switch(op) { |
1866 | case TOK_ULT: |
||
1867 | case TOK_UGE: |
||
1868 | case TOK_ULE: |
||
1869 | case TOK_UGT: |
||
6429 | siemargl | 1870 | tcc_error("unsigned comparison on floats?"); |
145 | halyavin | 1871 | break; |
1872 | case TOK_LT: |
||
6429 | siemargl | 1873 | op=TOK_Nset; |
145 | halyavin | 1874 | break; |
1875 | case TOK_LE: |
||
6429 | siemargl | 1876 | op=TOK_ULE; /* correct in unordered case only if AC bit in FPSR set */ |
145 | halyavin | 1877 | break; |
1878 | case TOK_EQ: |
||
1879 | case TOK_NE: |
||
1880 | x&=~0x400000; // cmfe -> cmf |
||
1881 | break; |
||
1882 | } |
||
1883 | if(c1 && !c2) { |
||
1884 | c2=c1; |
||
1885 | vswap(); |
||
1886 | switch(op) { |
||
6429 | siemargl | 1887 | case TOK_Nset: |
1888 | op=TOK_GT; |
||
145 | halyavin | 1889 | break; |
6429 | siemargl | 1890 | case TOK_GE: |
145 | halyavin | 1891 | op=TOK_ULE; |
1892 | break; |
||
1893 | case TOK_ULE: |
||
6429 | siemargl | 1894 | op=TOK_GE; |
145 | halyavin | 1895 | break; |
6429 | siemargl | 1896 | case TOK_GT: |
1897 | op=TOK_Nset; |
||
145 | halyavin | 1898 | break; |
1899 | } |
||
1900 | } |
||
1901 | vswap(); |
||
1902 | r=fpr(gv(RC_FLOAT)); |
||
1903 | vswap(); |
||
1904 | if(c2) { |
||
1905 | if(c2>0xf) |
||
1906 | x|=0x200000; |
||
1907 | r2=c2&0xf; |
||
1908 | } else { |
||
1909 | r2=fpr(gv(RC_FLOAT)); |
||
1910 | } |
||
1911 | vtop[-1].r = VT_CMP; |
||
1912 | vtop[-1].c.i = op; |
||
1913 | } else { |
||
6429 | siemargl | 1914 | tcc_error("unknown fp op %x!",op); |
145 | halyavin | 1915 | return; |
1916 | } |
||
1917 | } |
||
1918 | if(vtop[-1].r == VT_CMP) |
||
1919 | c1=15; |
||
1920 | else { |
||
1921 | c1=vtop->r; |
||
1922 | if(r2&0x8) |
||
1923 | c1=vtop[-1].r; |
||
1924 | vtop[-1].r=get_reg_ex(RC_FLOAT,two2mask(vtop[-1].r,c1)); |
||
1925 | c1=fpr(vtop[-1].r); |
||
1926 | } |
||
1927 | vtop--; |
||
1928 | o(x|(r<<16)|(c1<<12)|r2); |
||
1929 | } |
||
6429 | siemargl | 1930 | #endif |
145 | halyavin | 1931 | |
1932 | /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' |
||
1933 | and 'long long' cases. */ |
||
6429 | siemargl | 1934 | ST_FUNC void gen_cvt_itof1(int t) |
145 | halyavin | 1935 | { |
6429 | siemargl | 1936 | uint32_t r, r2; |
1937 | int bt; |
||
145 | halyavin | 1938 | bt=vtop->type.t & VT_BTYPE; |
1939 | if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { |
||
6429 | siemargl | 1940 | #ifndef TCC_ARM_VFP |
1941 | uint32_t dsize = 0; |
||
1942 | #endif |
||
145 | halyavin | 1943 | r=intr(gv(RC_INT)); |
6429 | siemargl | 1944 | #ifdef TCC_ARM_VFP |
1945 | r2=vfpr(vtop->r=get_reg(RC_FLOAT)); |
||
1946 | o(0xEE000A10|(r<<12)|(r2<<16)); /* fmsr */ |
||
1947 | r2|=r2<<12; |
||
1948 | if(!(vtop->type.t & VT_UNSIGNED)) |
||
1949 | r2|=0x80; /* fuitoX -> fsituX */ |
||
1950 | o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/ |
||
1951 | #else |
||
145 | halyavin | 1952 | r2=fpr(vtop->r=get_reg(RC_FLOAT)); |
6429 | siemargl | 1953 | if((t & VT_BTYPE) != VT_FLOAT) |
1954 | dsize=0x80; /* flts -> fltd */ |
||
1955 | o(0xEE000110|dsize|(r2<<16)|(r<<12)); /* flts */ |
||
145 | halyavin | 1956 | if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { |
6429 | siemargl | 1957 | uint32_t off = 0; |
1958 | o(0xE3500000|(r<<12)); /* cmp */ |
||
145 | halyavin | 1959 | r=fpr(get_reg(RC_FLOAT)); |
1960 | if(last_itod_magic) { |
||
1961 | off=ind+8-last_itod_magic; |
||
1962 | off/=4; |
||
1963 | if(off>255) |
||
1964 | off=0; |
||
1965 | } |
||
6429 | siemargl | 1966 | o(0xBD1F0100|(r<<12)|off); /* ldflts */ |
145 | halyavin | 1967 | if(!off) { |
6429 | siemargl | 1968 | o(0xEA000000); /* b */ |
145 | halyavin | 1969 | last_itod_magic=ind; |
6429 | siemargl | 1970 | o(0x4F800000); /* 4294967296.0f */ |
145 | halyavin | 1971 | } |
6429 | siemargl | 1972 | o(0xBE000100|dsize|(r2<<16)|(r2<<12)|r); /* adflt */ |
145 | halyavin | 1973 | } |
6429 | siemargl | 1974 | #endif |
145 | halyavin | 1975 | return; |
1976 | } else if(bt == VT_LLONG) { |
||
1977 | int func; |
||
6429 | siemargl | 1978 | CType *func_type = 0; |
1979 | if((t & VT_BTYPE) == VT_FLOAT) { |
||
1980 | func_type = &func_float_type; |
||
1981 | if(vtop->type.t & VT_UNSIGNED) |
||
1982 | func=TOK___floatundisf; |
||
1983 | else |
||
1984 | func=TOK___floatdisf; |
||
1985 | #if LDOUBLE_SIZE != 8 |
||
1986 | } else if((t & VT_BTYPE) == VT_LDOUBLE) { |
||
1987 | func_type = &func_ldouble_type; |
||
1988 | if(vtop->type.t & VT_UNSIGNED) |
||
1989 | func=TOK___floatundixf; |
||
1990 | else |
||
1991 | func=TOK___floatdixf; |
||
1992 | } else if((t & VT_BTYPE) == VT_DOUBLE) { |
||
1993 | #else |
||
1994 | } else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) { |
||
1995 | #endif |
||
1996 | func_type = &func_double_type; |
||
1997 | if(vtop->type.t & VT_UNSIGNED) |
||
1998 | func=TOK___floatundidf; |
||
1999 | else |
||
2000 | func=TOK___floatdidf; |
||
2001 | } |
||
2002 | if(func_type) { |
||
2003 | vpush_global_sym(func_type, func); |
||
2004 | vswap(); |
||
2005 | gfunc_call(1); |
||
2006 | vpushi(0); |
||
2007 | vtop->r=TREG_F0; |
||
2008 | return; |
||
2009 | } |
||
145 | halyavin | 2010 | } |
6429 | siemargl | 2011 | tcc_error("unimplemented gen_cvt_itof %x!",vtop->type.t); |
145 | halyavin | 2012 | } |
2013 | |||
2014 | /* convert fp to int 't' type */ |
||
2015 | void gen_cvt_ftoi(int t) |
||
2016 | { |
||
6429 | siemargl | 2017 | uint32_t r, r2; |
2018 | int u, func = 0; |
||
145 | halyavin | 2019 | u=t&VT_UNSIGNED; |
2020 | t&=VT_BTYPE; |
||
2021 | r2=vtop->type.t & VT_BTYPE; |
||
2022 | if(t==VT_INT) { |
||
6429 | siemargl | 2023 | #ifdef TCC_ARM_VFP |
2024 | r=vfpr(gv(RC_FLOAT)); |
||
2025 | u=u?0:0x10000; |
||
2026 | o(0xEEBC0AC0|(r<<12)|r|T2CPR(r2)|u); /* ftoXizY */ |
||
2027 | r2=intr(vtop->r=get_reg(RC_INT)); |
||
2028 | o(0xEE100A10|(r<<16)|(r2<<12)); |
||
2029 | return; |
||
2030 | #else |
||
145 | halyavin | 2031 | if(u) { |
2032 | if(r2 == VT_FLOAT) |
||
2033 | func=TOK___fixunssfsi; |
||
6429 | siemargl | 2034 | #if LDOUBLE_SIZE != 8 |
2035 | else if(r2 == VT_LDOUBLE) |
||
2036 | func=TOK___fixunsxfsi; |
||
145 | halyavin | 2037 | else if(r2 == VT_DOUBLE) |
2038 | #else |
||
6429 | siemargl | 2039 | else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) |
145 | halyavin | 2040 | #endif |
6429 | siemargl | 2041 | func=TOK___fixunsdfsi; |
145 | halyavin | 2042 | } else { |
2043 | r=fpr(gv(RC_FLOAT)); |
||
2044 | r2=intr(vtop->r=get_reg(RC_INT)); |
||
2045 | o(0xEE100170|(r2<<12)|r); |
||
6429 | siemargl | 2046 | return; |
145 | halyavin | 2047 | } |
6429 | siemargl | 2048 | #endif |
145 | halyavin | 2049 | } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 |
2050 | if(r2 == VT_FLOAT) |
||
2051 | func=TOK___fixsfdi; |
||
6429 | siemargl | 2052 | #if LDOUBLE_SIZE != 8 |
2053 | else if(r2 == VT_LDOUBLE) |
||
2054 | func=TOK___fixxfdi; |
||
145 | halyavin | 2055 | else if(r2 == VT_DOUBLE) |
2056 | #else |
||
6429 | siemargl | 2057 | else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) |
145 | halyavin | 2058 | #endif |
6429 | siemargl | 2059 | func=TOK___fixdfdi; |
2060 | } |
||
145 | halyavin | 2061 | if(func) { |
2062 | vpush_global_sym(&func_old_type, func); |
||
2063 | vswap(); |
||
2064 | gfunc_call(1); |
||
2065 | vpushi(0); |
||
2066 | if(t == VT_LLONG) |
||
2067 | vtop->r2 = REG_LRET; |
||
2068 | vtop->r = REG_IRET; |
||
2069 | return; |
||
2070 | } |
||
6429 | siemargl | 2071 | tcc_error("unimplemented gen_cvt_ftoi!"); |
145 | halyavin | 2072 | } |
2073 | |||
2074 | /* convert from one floating point type to another */ |
||
2075 | void gen_cvt_ftof(int t) |
||
2076 | { |
||
6429 | siemargl | 2077 | #ifdef TCC_ARM_VFP |
2078 | if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) { |
||
2079 | uint32_t r = vfpr(gv(RC_FLOAT)); |
||
2080 | o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t)); |
||
2081 | } |
||
2082 | #else |
||
2083 | /* all we have to do on i386 and FPA ARM is to put the float in a register */ |
||
145 | halyavin | 2084 | gv(RC_FLOAT); |
6429 | siemargl | 2085 | #endif |
145 | halyavin | 2086 | } |
2087 | |||
2088 | /* computed goto support */ |
||
2089 | void ggoto(void) |
||
2090 | { |
||
2091 | gcall_or_jmp(1); |
||
2092 | vtop--; |
||
2093 | } |
||
2094 | |||
6429 | siemargl | 2095 | /* Save the stack pointer onto the stack and return the location of its address */ |
2096 | ST_FUNC void gen_vla_sp_save(int addr) { |
||
2097 | tcc_error("variable length arrays unsupported for this target"); |
||
2098 | } |
||
2099 | |||
2100 | /* Restore the SP from a location on the stack */ |
||
2101 | ST_FUNC void gen_vla_sp_restore(int addr) { |
||
2102 | tcc_error("variable length arrays unsupported for this target"); |
||
2103 | } |
||
2104 | |||
2105 | /* Subtract from the stack pointer, and push the resulting value onto the stack */ |
||
2106 | ST_FUNC void gen_vla_alloc(CType *type, int align) { |
||
2107 | tcc_error("variable length arrays unsupported for this target"); |
||
2108 | } |
||
2109 | |||
145 | halyavin | 2110 | /* end of ARM code generator */ |
2111 | /*************************************************************/ |
||
6429 | siemargl | 2112 | #endif |
2113 | /*************************************************************/12)|r|T2CPR(vtop-><12)|r|T2CPR(vtop->12)|r); |