last patches to separate type and value handling
[tinycc.git] / i386-gen.c
blob8e7c2f46e51dc6854e81f106dbf68c429cb1d823
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->r &= ~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 'r & VT_FORWARD' is true */
159 void gen_addr32(int r, int c)
161 if (!(r & VT_FORWARD)) {
162 gen_le32(c);
163 } else {
164 greloc((Sym *)c, ind, RELOC_ADDR32);
165 gen_le32(0);
169 /* load 'r' from value 'sv' */
170 void load(int r, SValue *sv)
172 int v, t, ft, fc, fr;
173 SValue v1;
175 fr = sv->r;
176 ft = sv->t;
177 fc = sv->c.ul;
179 v = fr & VT_VALMASK;
180 if (fr & VT_LVAL) {
181 if (v == VT_LLOCAL) {
182 v1.t = VT_INT;
183 v1.r = VT_LOCAL | VT_LVAL;
184 v1.c.ul = fc;
185 load(r, &v1);
186 v = r;
188 if ((ft & VT_BTYPE) == VT_FLOAT) {
189 o(0xd9); /* flds */
190 r = 0;
191 } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
192 o(0xdd); /* fldl */
193 r = 0;
194 } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
195 o(0xdb); /* fldt */
196 r = 5;
197 } else if ((ft & VT_TYPE) == VT_BYTE)
198 o(0xbe0f); /* movsbl */
199 else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
200 o(0xb60f); /* movzbl */
201 else if ((ft & VT_TYPE) == VT_SHORT)
202 o(0xbf0f); /* movswl */
203 else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
204 o(0xb70f); /* movzwl */
205 else
206 o(0x8b); /* movl */
208 if (v == VT_CONST) {
209 o(0x05 + r * 8); /* 0xXX, r */
210 gen_addr32(fr, fc);
211 } else if (v == VT_LOCAL) {
212 oad(0x85 + r * 8, fc); /* xx(%ebp), r */
213 } else {
214 g(0x00 + r * 8 + v); /* (v), r */
216 } else {
217 if (v == VT_CONST) {
218 o(0xb8 + r); /* mov $xx, r */
219 gen_addr32(fr, fc);
220 } else if (v == VT_LOCAL) {
221 o(0x8d);
222 oad(0x85 + r * 8, fc); /* lea xxx(%ebp), r */
223 } else if (v == VT_CMP) {
224 oad(0xb8 + r, 0); /* mov $0, r */
225 o(0x0f); /* setxx %br */
226 o(fc);
227 o(0xc0 + r);
228 } else if (v == VT_JMP || v == VT_JMPI) {
229 t = v & 1;
230 oad(0xb8 + r, t); /* mov $1, r */
231 oad(0xe9, 5); /* jmp after */
232 gsym(fc);
233 oad(0xb8 + r, t ^ 1); /* mov $0, r */
234 } else if (v != r) {
235 o(0x89);
236 o(0xc0 + r + v * 8); /* mov v, r */
241 /* store register 'r' in lvalue 'v' */
242 void store(int r, SValue *v)
244 int fr, bt, ft, fc;
246 ft = v->t;
247 fc = v->c.ul;
248 fr = v->r & VT_VALMASK;
249 bt = ft & VT_BTYPE;
250 /* XXX: incorrect if reg to reg */
251 /* XXX: should not flush float stack */
252 if (bt == VT_FLOAT) {
253 o(0xd9); /* fsts */
254 r = 2;
255 } else if (bt == VT_DOUBLE) {
256 o(0xdd); /* fstpl */
257 r = 2;
258 } else if (bt == VT_LDOUBLE) {
259 o(0xc0d9); /* fld %st(0) */
260 o(0xdb); /* fstpt */
261 r = 7;
262 } else {
263 if (bt == VT_SHORT)
264 o(0x66);
265 if (bt == VT_BYTE)
266 o(0x88);
267 else
268 o(0x89);
270 if (fr == VT_CONST) {
271 o(0x05 + r * 8); /* mov r,xxx */
272 gen_addr32(v->r, fc);
273 } else if (fr == VT_LOCAL) {
274 oad(0x85 + r * 8, fc); /* mov r,xxx(%ebp) */
275 } else if (v->r & VT_LVAL) {
276 g(fr + r * 8); /* mov r, (fr) */
277 } else if (fr != r) {
278 o(0xc0 + fr + r * 8); /* mov r, fr */
282 /* start function call and return function call context */
283 void gfunc_start(GFuncContext *c)
285 c->args_size = 0;
288 /* push function parameter which is in (vtop->t, vtop->c). Stack entry
289 is then popped. */
290 void gfunc_param(GFuncContext *c)
292 int size, align, r;
294 if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
295 size = type_size(vtop->t, &align);
296 /* align to stack align size */
297 size = (size + 3) & ~3;
298 /* allocate the necessary size on stack */
299 oad(0xec81, size); /* sub $xxx, %esp */
300 /* generate structure store */
301 r = get_reg(REG_CLASS_INT);
302 o(0x89); /* mov %esp, r */
303 o(0xe0 + r);
304 vset(VT_INT, r, 0);
305 vswap();
306 vstore();
307 c->args_size += size;
308 } else if (is_float(vtop->t)) {
309 gv(); /* only one float register */
310 if ((vtop->t & VT_BTYPE) == VT_FLOAT)
311 size = 4;
312 else if ((vtop->t & VT_BTYPE) == VT_DOUBLE)
313 size = 8;
314 else
315 size = 12;
316 oad(0xec81, size); /* sub $xxx, %esp */
317 if (size == 12)
318 o(0x7cdb);
319 else
320 o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
321 g(0x24);
322 g(0x00);
323 c->args_size += size;
324 } else {
325 /* simple type (currently always same size) */
326 /* XXX: implicit cast ? */
327 r = gv();
328 o(0x50 + r); /* push r */
329 c->args_size += 4;
331 vtop--;
334 /* generate function call with address in (vtop->t, vtop->c) and free function
335 context. Stack entry is popped */
336 void gfunc_call(GFuncContext *c)
338 int r;
339 if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
340 /* constant case */
341 /* forward reference */
342 if (vtop->r & VT_FORWARD) {
343 greloc(vtop->c.sym, ind + 1, RELOC_REL32);
344 oad(0xe8, 0);
345 } else {
346 oad(0xe8, vtop->c.ul - ind - 5);
348 } else {
349 /* otherwise, indirect call */
350 r = gv();
351 o(0xff); /* call *r */
352 o(0xd0 + r);
354 if (c->args_size)
355 oad(0xc481, c->args_size); /* add $xxx, %esp */
356 vtop--;
359 int gjmp(int t)
361 return psym(0xe9, t);
364 /* generate a test. set 'inv' to invert test. Stack entry is popped */
365 int gtst(int inv, int t)
367 int v, *p;
368 v = vtop->r & VT_VALMASK;
369 if (v == VT_CMP) {
370 /* fast case : can jump directly since flags are set */
371 g(0x0f);
372 t = psym((vtop->c.i - 16) ^ inv, t);
373 } else if (v == VT_JMP || v == VT_JMPI) {
374 /* && or || optimization */
375 if ((v & 1) == inv) {
376 /* insert vtop->c jump list in t */
377 p = &vtop->c.i;
378 while (*p != 0)
379 p = (int *)*p;
380 *p = t;
381 t = vtop->c.i;
382 } else {
383 t = gjmp(t);
384 gsym(vtop->c.i);
386 } else if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
387 /* constant jmp optimization */
388 if ((vtop->c.i != 0) != inv)
389 t = gjmp(t);
390 } else {
391 /* XXX: floats */
392 v = gv();
393 o(0x85);
394 o(0xc0 + v * 9);
395 g(0x0f);
396 t = psym(0x85 ^ inv, t);
398 vtop--;
399 return t;
402 /* generate an integer binary operation */
403 void gen_opi(int op)
405 int t, r, fr;
407 vswap();
408 r = gv();
409 vswap();
410 fr = gv();
411 vtop--;
413 if (op == '+') {
414 o(0x01);
415 o(0xc0 + r + fr * 8);
416 } else if (op == '-') {
417 o(0x29);
418 o(0xc0 + r + fr * 8);
419 } else if (op == '&') {
420 o(0x21);
421 o(0xc0 + r + fr * 8);
422 } else if (op == '^') {
423 o(0x31);
424 o(0xc0 + r + fr * 8);
425 } else if (op == '|') {
426 o(0x09);
427 o(0xc0 + r + fr * 8);
428 } else if (op == '*') {
429 o(0xaf0f); /* imul fr, r */
430 o(0xc0 + fr + r * 8);
431 } else if (op == TOK_SHL | op == TOK_SHR | op == TOK_SAR) {
432 /* op2 is %ecx */
433 if (fr != 1) {
434 if (r == 1) {
435 r = fr;
436 fr = 1;
437 o(0x87); /* xchg r, %ecx */
438 o(0xc1 + r * 8);
439 } else
440 move_reg(1, fr);
442 o(0xd3); /* shl/shr/sar %cl, r */
443 if (op == TOK_SHL)
444 o(0xe0 + r);
445 else if (op == TOK_SHR)
446 o(0xe8 + r);
447 else
448 o(0xf8 + r);
449 vtop->r = r;
450 } else if (op == '/' | op == TOK_UDIV | op == TOK_PDIV |
451 op == '%' | op == TOK_UMOD) {
452 save_reg(2); /* save edx */
453 t = save_reg_forced(fr); /* save fr and get op2 location */
454 move_reg(0, r); /* op1 is %eax */
455 if (op == TOK_UDIV | op == TOK_UMOD) {
456 o(0xf7d231); /* xor %edx, %edx, div t(%ebp), %eax */
457 oad(0xb5, t);
458 } else {
459 o(0xf799); /* cltd, idiv t(%ebp), %eax */
460 oad(0xbd, t);
462 if (op == '%' | op == TOK_UMOD)
463 r = 2;
464 else
465 r = 0;
466 vtop->r = r;
467 } else {
468 vtop--;
469 o(0x39);
470 o(0xc0 + r + fr * 8); /* cmp fr, r */
471 vset(VT_INT, VT_CMP, op);
475 /* generate a floating point operation 'v = t1 op t2' instruction. The
476 two operands are guaranted to have the same floating point type */
477 /* NOTE: currently floats can only be lvalues */
478 void gen_opf(int op)
480 int a, ft, fc, swapped, r;
482 /* convert constants to memory references */
483 if ((vtop[-1].r & (VT_CONST | VT_LVAL)) == VT_CONST) {
484 vswap();
485 gv();
486 vswap();
488 if ((vtop[0].r & (VT_CONST | VT_LVAL)) == VT_CONST)
489 gv();
491 /* must put at least one value in the floating point register */
492 if ((vtop[-1].r & VT_LVAL) &&
493 (vtop[0].r & VT_LVAL)) {
494 vswap();
495 gv();
496 vswap();
498 if (op >= TOK_EQ && op <= TOK_GT) {
499 /* load on stack second operand */
500 load(REG_ST0, vtop);
501 if (op == TOK_GE || op == TOK_GT)
502 o(0xc9d9); /* fxch %st(1) */
503 o(0xe9da); /* fucompp */
504 o(0xe0df); /* fnstsw %ax */
505 if (op == TOK_EQ) {
506 o(0x45e480); /* and $0x45, %ah */
507 o(0x40fC80); /* cmp $0x40, %ah */
508 } else if (op == TOK_NE) {
509 o(0x45e480); /* and $0x45, %ah */
510 o(0x40f480); /* xor $0x40, %ah */
511 op = TOK_NE;
512 } else if (op == TOK_GE || op == TOK_LE) {
513 o(0x05c4f6); /* test $0x05, %ah */
514 op = TOK_EQ;
515 } else {
516 o(0x45c4f6); /* test $0x45, %ah */
517 op = TOK_EQ;
519 vtop--;
520 vtop->r = VT_CMP;
521 vtop->c.i = op;
522 } else {
523 /* swap the stack if needed so that t1 is the register and t2 is
524 the memory reference */
525 swapped = 0;
526 if (vtop[-1].r & VT_LVAL) {
527 vswap();
528 swapped = 1;
531 switch(op) {
532 default:
533 case '+':
534 a = 0;
535 break;
536 case '-':
537 a = 0x20;
538 if (swapped)
539 a += 8;
540 break;
541 case '*':
542 a = 0x08;
543 break;
544 case '/':
545 a = 0x30;
546 if (swapped)
547 a += 8;
548 break;
550 ft = vtop->t;
551 fc = vtop->c.ul;
552 if ((ft & VT_BTYPE) == VT_DOUBLE)
553 o(0xdc);
554 else
555 o(0xd8);
557 r = vtop->r & VT_VALMASK;
558 if (r == VT_CONST) {
559 o(0x05 + a);
560 gen_addr32(vtop->r, fc);
561 } else if (r == VT_LOCAL) {
562 oad(0x85 + a, fc);
563 } else {
564 g(0x00 + a + r);
566 vtop--;
570 /* convert integers to fp 't' type */
571 void gen_cvt_itof(int t)
573 gv();
574 if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) {
575 /* unsigned int to float/double/long double */
576 o(0x6a); /* push $0 */
577 g(0x00);
578 o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
579 o(0x242cdf); /* fildll (%esp) */
580 o(0x08c483); /* add $8, %esp */
581 } else {
582 /* int to float/double/long double */
583 o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
584 o(0x2404db); /* fildl (%esp) */
585 o(0x04c483); /* add $4, %esp */
587 vtop->r = REG_ST0;
590 /* FPU control word for rounding to nearest mode */
591 /* XXX: should move that into tcc lib support code ! */
592 static unsigned short __tcc_fpu_control = 0x137f;
593 /* FPU control word for round to zero mode for int convertion */
594 static unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
596 /* convert fp to int 't' type */
597 /* XXX: handle long long case */
598 void gen_cvt_ftoi(int t)
600 int r, size;
602 gv();
603 if (t == VT_INT | VT_UNSIGNED &&
604 t == VT_LLONG | VT_UNSIGNED &&
605 t == VT_LLONG)
606 size = 8;
607 else
608 size = 4;
610 r = get_reg(REG_CLASS_INT);
611 oad(0x2dd9, (int)&__tcc_int_fpu_control); /* ldcw xxx */
612 oad(0xec81, size); /* sub $xxx, %esp */
613 if (size == 4)
614 o(0x1cdb); /* fistpl */
615 else
616 o(0x3cdb); /* fistpll */
617 o(0x24);
618 oad(0x2dd9, (int)&__tcc_fpu_control); /* ldcw xxx */
619 o(0x58 + r); /* pop r */
620 if (size == 8)
621 o(0x04c483); /* add $4, %esp */
622 vtop->r = r;
625 /* convert from one floating point type to another */
626 void gen_cvt_ftof(int t)
628 /* all we have to do on i386 is to put the float in a register */
629 gv();
632 /* pop stack value */
633 void vpop(void)
635 /* for x86, we need to pop the FP stack */
636 if ((vtop->r & VT_VALMASK) == REG_ST0) {
637 o(0xd9dd); /* fstp %st(1) */
639 vtop--;
644 /* end of X86 code generator */
645 /*************************************************************/