new value stack system to handle the futur types (long long and floats)
[tinycc.git] / i386-gen.c
blob163180b5a1db7ca69b6252581ddcc41628436fff
1 /*
2 * X86 code generator for TCC
3 *
4 * Copyright (c) 2001 Fabrice Bellard
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* number of available registers */
22 #define NB_REGS 4
24 #define NB_REG_CLASSES 2
26 /* a register can belong to several classes */
27 #define REG_CLASS_INT 0x0001
28 #define REG_CLASS_FLOAT 0x0002
30 /* pretty names for the registers */
31 enum {
32 REG_EAX = 0,
33 REG_ECX,
34 REG_EDX,
35 REG_ST0,
38 int reg_classes[NB_REGS] = {
39 REG_CLASS_INT, /* eax */
40 REG_CLASS_INT, /* ecx */
41 REG_CLASS_INT, /* edx */
42 REG_CLASS_FLOAT, /* st0 */
45 /* integer return register for functions */
46 #define FUNC_RET_REG 0
47 /* float return register for functions */
48 #define FUNC_RET_FREG 3
50 /* defined if function parameters must be evaluated in reverse order */
51 #define INVERT_FUNC_PARAMS
53 /* defined if structures are passed as pointers. Otherwise structures
54 are directly pushed on stack. */
55 //#define FUNC_STRUCT_PARAM_AS_PTR
57 /* pointer size, in bytes */
58 #define PTR_SIZE 4
60 /* long double size and alignment, in bytes */
61 #define LDOUBLE_SIZE 12
62 #define LDOUBLE_ALIGN 4
64 /* function call context */
65 typedef struct GFuncContext {
66 int args_size;
67 } GFuncContext;
69 /******************************************************/
71 void g(int c)
73 *(char *)ind++ = c;
76 void o(int c)
78 while (c) {
79 g(c);
80 c = c / 256;
84 void gen_le32(int c)
86 g(c);
87 g(c >> 8);
88 g(c >> 16);
89 g(c >> 24);
92 /* add a new relocation entry to symbol 's' */
93 void greloc(Sym *s, int addr, int type)
95 Reloc *p;
96 p = malloc(sizeof(Reloc));
97 if (!p)
98 error("memory full");
99 p->type = type;
100 p->addr = addr;
101 p->next = (Reloc *)s->c;
102 s->c = (int)p;
105 /* patch each relocation entry with value 'val' */
106 void greloc_patch(Sym *s, int val)
108 Reloc *p, *p1;
110 p = (Reloc *)s->c;
111 while (p != NULL) {
112 p1 = p->next;
113 switch(p->type) {
114 case RELOC_ADDR32:
115 *(int *)p->addr = val;
116 break;
117 case RELOC_REL32:
118 *(int *)p->addr = val - p->addr - 4;
119 break;
121 free(p);
122 p = p1;
124 s->c = val;
125 s->t &= ~VT_FORWARD;
128 /* output a symbol and patch all calls to it */
129 void gsym_addr(t, a)
131 int n;
132 while (t) {
133 n = *(int *)t; /* next value */
134 *(int *)t = a - t - 4;
135 t = n;
139 void gsym(t)
141 gsym_addr(t, ind);
144 /* psym is used to put an instruction with a data field which is a
145 reference to a symbol. It is in fact the same as oad ! */
146 #define psym oad
148 /* instruction + 4 bytes data. Return the address of the data */
149 int oad(int c, int s)
151 o(c);
152 *(int *)ind = s;
153 s = ind;
154 ind = ind + 4;
155 return s;
158 /* output constant with relocation if 't & VT_FORWARD' is true */
159 void gen_addr32(int c, int t)
161 if (!(t & VT_FORWARD)) {
162 gen_le32(c);
163 } else {
164 greloc((Sym *)c, ind, RELOC_ADDR32);
165 gen_le32(0);
169 /* XXX: generate correct pointer for forward references to functions */
170 /* r = (ft, fc) */
171 void load(int r, int ft, int fc)
173 int v, t;
175 v = ft & VT_VALMASK;
176 if (ft & VT_LVAL) {
177 if (v == VT_LLOCAL) {
178 load(r, VT_LOCAL | VT_LVAL, fc);
179 v = r;
181 if ((ft & VT_BTYPE) == VT_FLOAT) {
182 o(0xd9); /* flds */
183 r = 0;
184 } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
185 o(0xdd); /* fldl */
186 r = 0;
187 } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
188 o(0xdb); /* fldt */
189 r = 5;
190 } else if ((ft & VT_TYPE) == VT_BYTE)
191 o(0xbe0f); /* movsbl */
192 else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
193 o(0xb60f); /* movzbl */
194 else if ((ft & VT_TYPE) == VT_SHORT)
195 o(0xbf0f); /* movswl */
196 else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
197 o(0xb70f); /* movzwl */
198 else
199 o(0x8b); /* movl */
201 if (v == VT_CONST) {
202 o(0x05 + r * 8); /* 0xXX, r */
203 gen_addr32(fc, ft);
204 } else if (v == VT_LOCAL) {
205 oad(0x85 + r * 8, fc); /* xx(%ebp), r */
206 } else {
207 g(0x00 + r * 8 + v); /* (v), r */
209 } else {
210 if (v == VT_CONST) {
211 o(0xb8 + r); /* mov $xx, r */
212 gen_addr32(fc, ft);
213 } else if (v == VT_LOCAL) {
214 o(0x8d);
215 oad(0x85 + r * 8, fc); /* lea xxx(%ebp), r */
216 } else if (v == VT_CMP) {
217 oad(0xb8 + r, 0); /* mov $0, r */
218 o(0x0f); /* setxx %br */
219 o(fc);
220 o(0xc0 + r);
221 } else if (v == VT_JMP || v == VT_JMPI) {
222 t = v & 1;
223 oad(0xb8 + r, t); /* mov $1, r */
224 oad(0xe9, 5); /* jmp after */
225 gsym(fc);
226 oad(0xb8 + r, t ^ 1); /* mov $0, r */
227 } else if (v != r) {
228 o(0x89);
229 o(0xc0 + r + v * 8); /* mov v, r */
234 /* (ft, fc) = r */
235 /* WARNING: r must not be allocated on the stack */
236 void store(r, ft, fc)
238 int fr, bt;
240 fr = ft & VT_VALMASK;
241 bt = ft & VT_BTYPE;
242 /* XXX: incorrect if reg to reg */
243 /* XXX: should not flush float stack */
244 if (bt == VT_FLOAT) {
245 o(0xd9); /* fstps */
246 r = 3;
247 } else if (bt == VT_DOUBLE) {
248 o(0xdd); /* fstpl */
249 r = 3;
250 } else if (bt == VT_LDOUBLE) {
251 o(0xdb); /* fstpt */
252 r = 7;
253 } else {
254 if (bt == VT_SHORT)
255 o(0x66);
256 if (bt == VT_BYTE)
257 o(0x88);
258 else
259 o(0x89);
261 if (fr == VT_CONST) {
262 o(0x05 + r * 8); /* mov r,xxx */
263 gen_addr32(fc, ft);
264 } else if (fr == VT_LOCAL) {
265 oad(0x85 + r * 8, fc); /* mov r,xxx(%ebp) */
266 } else if (ft & VT_LVAL) {
267 g(fr + r * 8); /* mov r, (fr) */
268 } else if (fr != r) {
269 o(0xc0 + fr + r * 8); /* mov r, fr */
273 /* start function call and return function call context */
274 void gfunc_start(GFuncContext *c)
276 c->args_size = 0;
279 /* push function parameter which is in (vtop->t, vtop->c). Stack entry
280 is then popped. */
281 void gfunc_param(GFuncContext *c)
283 int size, align, r;
285 if ((vtop->t & (VT_BTYPE | VT_LVAL)) == (VT_STRUCT | VT_LVAL)) {
286 size = type_size(vtop->t, &align);
287 /* align to stack align size */
288 size = (size + 3) & ~3;
289 /* allocate the necessary size on stack */
290 oad(0xec81, size); /* sub $xxx, %esp */
291 /* generate structure store */
292 r = get_reg(REG_CLASS_INT);
293 o(0x89); /* mov %esp, r */
294 o(0xe0 + r);
295 vset(VT_INT | r, 0);
296 vswap();
297 vstore();
298 c->args_size += size;
299 } else if ((vtop->t & VT_BTYPE) == VT_LDOUBLE ||
300 (vtop->t & VT_BTYPE) == VT_DOUBLE ||
301 (vtop->t & VT_BTYPE) == VT_FLOAT) {
302 gv(); /* only one float register */
303 if ((vtop->t & VT_BTYPE) == VT_FLOAT)
304 size = 4;
305 else if ((vtop->t & VT_BTYPE) == VT_DOUBLE)
306 size = 8;
307 else
308 size = 12;
309 oad(0xec81, size); /* sub $xxx, %esp */
310 if (size == 12)
311 o(0x7cdb);
312 else
313 o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
314 g(0x24);
315 g(0x00);
316 c->args_size += size;
317 } else {
318 /* simple type (currently always same size) */
319 /* XXX: implicit cast ? */
320 r = gv();
321 o(0x50 + r); /* push r */
322 c->args_size += 4;
324 vtop--;
327 /* generate function call with address in (vtop->t, vtop->c) and free function
328 context. Stack entry is popped */
329 void gfunc_call(GFuncContext *c)
331 int r;
332 if ((vtop->t & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
333 /* constant case */
334 /* forward reference */
335 if (vtop->t & VT_FORWARD) {
336 greloc((Sym *)vtop->c, ind + 1, RELOC_REL32);
337 oad(0xe8, 0);
338 } else {
339 oad(0xe8, vtop->c - ind - 5);
341 } else {
342 /* otherwise, indirect call */
343 r = gv();
344 o(0xff); /* call *r */
345 o(0xd0 + r);
347 if (c->args_size)
348 oad(0xc481, c->args_size); /* add $xxx, %esp */
349 vtop--;
352 int gjmp(int t)
354 return psym(0xe9, t);
357 /* generate a test. set 'inv' to invert test. Stack entry is popped */
358 int gtst(int inv, int t)
360 int v, *p;
361 v = vtop->t & VT_VALMASK;
362 if (v == VT_CMP) {
363 /* fast case : can jump directly since flags are set */
364 g(0x0f);
365 t = psym((vtop->c - 16) ^ inv, t);
366 } else if (v == VT_JMP || v == VT_JMPI) {
367 /* && or || optimization */
368 if ((v & 1) == inv) {
369 /* insert vtop->c jump list in t */
370 p = &vtop->c;
371 while (*p != 0)
372 p = (int *)*p;
373 *p = t;
374 t = vtop->c;
375 } else {
376 t = gjmp(t);
377 gsym(vtop->c);
379 } else if ((vtop->t & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
380 /* constant jmp optimization */
381 if ((vtop->c != 0) != inv)
382 t = gjmp(t);
383 } else {
384 /* XXX: floats */
385 v = gv();
386 o(0x85);
387 o(0xc0 + v * 9);
388 g(0x0f);
389 t = psym(0x85 ^ inv, t);
391 vtop--;
392 return t;
395 /* generate an integer binary operation */
396 void gen_opi(int op)
398 int t, r, fr;
400 vswap();
401 r = gv();
402 vswap();
403 fr = gv();
404 vtop--;
406 if (op == '+') {
407 o(0x01);
408 o(0xc0 + r + fr * 8);
409 } else if (op == '-') {
410 o(0x29);
411 o(0xc0 + r + fr * 8);
412 } else if (op == '&') {
413 o(0x21);
414 o(0xc0 + r + fr * 8);
415 } else if (op == '^') {
416 o(0x31);
417 o(0xc0 + r + fr * 8);
418 } else if (op == '|') {
419 o(0x09);
420 o(0xc0 + r + fr * 8);
421 } else if (op == '*') {
422 o(0xaf0f); /* imul fr, r */
423 o(0xc0 + fr + r * 8);
424 } else if (op == TOK_SHL | op == TOK_SHR | op == TOK_SAR) {
425 /* op2 is %ecx */
426 if (fr != 1) {
427 if (r == 1) {
428 r = fr;
429 fr = 1;
430 o(0x87); /* xchg r, %ecx */
431 o(0xc1 + r * 8);
432 } else
433 move_reg(1, fr);
435 o(0xd3); /* shl/shr/sar %cl, r */
436 if (op == TOK_SHL)
437 o(0xe0 + r);
438 else if (op == TOK_SHR)
439 o(0xe8 + r);
440 else
441 o(0xf8 + r);
442 vtop->t = (vtop->t & VT_TYPE) | r;
443 } else if (op == '/' | op == TOK_UDIV | op == TOK_PDIV |
444 op == '%' | op == TOK_UMOD) {
445 save_reg(2); /* save edx */
446 t = save_reg_forced(fr); /* save fr and get op2 location */
447 move_reg(0, r); /* op1 is %eax */
448 if (op == TOK_UDIV | op == TOK_UMOD) {
449 o(0xf7d231); /* xor %edx, %edx, div t(%ebp), %eax */
450 oad(0xb5, t);
451 } else {
452 o(0xf799); /* cltd, idiv t(%ebp), %eax */
453 oad(0xbd, t);
455 if (op == '%' | op == TOK_UMOD)
456 r = 2;
457 else
458 r = 0;
459 vtop->t = (vtop->t & VT_TYPE) | r;
460 } else {
461 vtop--;
462 o(0x39);
463 o(0xc0 + r + fr * 8); /* cmp fr, r */
464 vset(VT_CMP, op);
468 /* generate a floating point operation 'v = t1 op t2' instruction. The
469 two operands are guaranted to have the same floating point type */
470 /* NOTE: currently floats can only be lvalues */
471 void gen_opf(int op)
473 int a, ft, fc, swapped, r;
475 /* must put at least one value in the floating point register */
476 if ((vtop[-1].t & VT_LVAL) &&
477 (vtop[0].t & VT_LVAL)) {
478 vswap();
479 gv();
480 vswap();
482 if (op >= TOK_EQ && op <= TOK_GT) {
483 /* load on stack second operand */
484 load(REG_ST0, vtop->t, vtop->c);
485 if (op == TOK_GE || op == TOK_GT)
486 o(0xc9d9); /* fxch %st(1) */
487 o(0xe9da); /* fucompp */
488 o(0xe0df); /* fnstsw %ax */
489 if (op == TOK_EQ) {
490 o(0x45e480); /* and $0x45, %ah */
491 o(0x40fC80); /* cmp $0x40, %ah */
492 } else if (op == TOK_NE) {
493 o(0x45e480); /* and $0x45, %ah */
494 o(0x40f480); /* xor $0x40, %ah */
495 op = TOK_NE;
496 } else if (op == TOK_GE || op == TOK_LE) {
497 o(0x05c4f6); /* test $0x05, %ah */
498 op = TOK_EQ;
499 } else {
500 o(0x45c4f6); /* test $0x45, %ah */
501 op = TOK_EQ;
503 vtop--;
504 vtop->t = (vtop->t & VT_TYPE) | VT_CMP;
505 vtop->c = op;
506 } else {
507 /* swap the stack if needed so that t1 is the register and t2 is
508 the memory reference */
509 swapped = 0;
510 if (vtop[-1].t & VT_LVAL) {
511 vswap();
512 swapped = 1;
515 switch(op) {
516 default:
517 case '+':
518 a = 0;
519 break;
520 case '-':
521 a = 0x20;
522 if (swapped)
523 a += 8;
524 break;
525 case '*':
526 a = 0x08;
527 break;
528 case '/':
529 a = 0x30;
530 if (swapped)
531 a += 8;
532 break;
534 ft = vtop->t;
535 fc = vtop->c;
536 if ((ft & VT_BTYPE) == VT_DOUBLE)
537 o(0xdc);
538 else
539 o(0xd8);
541 r = ft & VT_VALMASK;
542 if (r == VT_CONST) {
543 o(0x05 + a);
544 gen_addr32(fc, ft);
545 } else if (r == VT_LOCAL) {
546 oad(0x85 + a, fc);
547 } else {
548 g(0x00 + a + r);
550 vtop--;
554 /* convert integers to floating point 't' type (float/double/long
555 double) */
556 void gen_cvtf(int t)
558 if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) {
559 /* unsigned int to float/double/long double */
560 o(0x6a); /* push $0 */
561 g(0x00);
562 o(0x50 + (vtop->t & VT_VALMASK)); /* push r */
563 o(0x242cdf); /* fildll (%esp) */
564 o(0x08c483); /* add $8, %esp */
565 } else {
566 /* int to float/double/long double */
567 o(0x50 + (vtop->t & VT_VALMASK)); /* push r */
568 o(0x2404db); /* fildl (%esp) */
569 o(0x04c483); /* add $4, %esp */
573 /* end of X86 code generator */
574 /*************************************************************/