use register classes (will allow better and simpler code gen - fixed long double...
[tinycc.git] / i386-gen.c
blob11f489840df18aa8f0585a19119b02d7b0bad993
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 /* a register can belong to several classes */
25 #define RC_INT 0x0001 /* generic integer register */
26 #define RC_FLOAT 0x0002 /* generic float register */
27 #define RC_IRET 0x0004 /* function returned integer register */
28 #define RC_FRET 0x0008 /* function returned float register */
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 /* eax */ RC_INT | RC_IRET,
40 /* ecx */ RC_INT,
41 /* edx */ RC_INT,
42 /* st0 */ RC_FLOAT | RC_FRET,
45 /* return registers for function */
46 #define REG_IRET REG_EAX
47 #define REG_FRET REG_ST0
49 /* defined if function parameters must be evaluated in reverse order */
50 #define INVERT_FUNC_PARAMS
52 /* defined if structures are passed as pointers. Otherwise structures
53 are directly pushed on stack. */
54 //#define FUNC_STRUCT_PARAM_AS_PTR
56 /* pointer size, in bytes */
57 #define PTR_SIZE 4
59 /* long double size and alignment, in bytes */
60 #define LDOUBLE_SIZE 12
61 #define LDOUBLE_ALIGN 4
63 /* function call context */
64 typedef struct GFuncContext {
65 int args_size;
66 } GFuncContext;
68 /******************************************************/
70 void g(int c)
72 *(char *)ind++ = c;
75 void o(int c)
77 while (c) {
78 g(c);
79 c = c / 256;
83 void gen_le32(int c)
85 g(c);
86 g(c >> 8);
87 g(c >> 16);
88 g(c >> 24);
91 /* patch relocation entry with value 'val' */
92 void greloc_patch1(Reloc *p, int val)
94 switch(p->type) {
95 case RELOC_ADDR32:
96 *(int *)p->addr = val;
97 break;
98 case RELOC_REL32:
99 *(int *)p->addr = val - p->addr - 4;
100 break;
104 /* output a symbol and patch all calls to it */
105 void gsym_addr(t, a)
107 int n;
108 while (t) {
109 n = *(int *)t; /* next value */
110 *(int *)t = a - t - 4;
111 t = n;
115 void gsym(t)
117 gsym_addr(t, ind);
120 /* psym is used to put an instruction with a data field which is a
121 reference to a symbol. It is in fact the same as oad ! */
122 #define psym oad
124 /* instruction + 4 bytes data. Return the address of the data */
125 int oad(int c, int s)
127 o(c);
128 *(int *)ind = s;
129 s = ind;
130 ind = ind + 4;
131 return s;
134 /* output constant with relocation if 'r & VT_FORWARD' is true */
135 void gen_addr32(int r, int c)
137 if (!(r & VT_FORWARD)) {
138 gen_le32(c);
139 } else {
140 greloc((Sym *)c, ind, RELOC_ADDR32);
141 gen_le32(0);
145 /* generate a modrm reference. 'op_reg' contains the addtionnal 3
146 opcode bits */
147 void gen_modrm(int op_reg, int r, int c)
149 op_reg = op_reg << 3;
150 if ((r & VT_VALMASK) == VT_CONST) {
151 /* constant memory reference */
152 o(0x05 | op_reg);
153 gen_addr32(r, c);
154 } else if ((r & VT_VALMASK) == VT_LOCAL) {
155 /* currently, we use only ebp as base */
156 if (c == (char)c) {
157 /* short reference */
158 o(0x45 | op_reg);
159 g(c);
160 } else {
161 oad(0x85 | op_reg, c);
163 } else {
164 g(0x00 | op_reg | (r & VT_VALMASK));
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 fr = 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 */
207 gen_modrm(r, fr, fc);
208 } else {
209 if (v == VT_CONST) {
210 o(0xb8 + r); /* mov $xx, r */
211 gen_addr32(fr, fc);
212 } else if (v == VT_LOCAL) {
213 o(0x8d); /* lea xxx(%ebp), r */
214 gen_modrm(r, VT_LOCAL, fc);
215 } else if (v == VT_CMP) {
216 oad(0xb8 + r, 0); /* mov $0, r */
217 o(0x0f); /* setxx %br */
218 o(fc);
219 o(0xc0 + r);
220 } else if (v == VT_JMP || v == VT_JMPI) {
221 t = v & 1;
222 oad(0xb8 + r, t); /* mov $1, r */
223 oad(0xe9, 5); /* jmp after */
224 gsym(fc);
225 oad(0xb8 + r, t ^ 1); /* mov $0, r */
226 } else if (v != r) {
227 o(0x89);
228 o(0xc0 + r + v * 8); /* mov v, r */
233 /* store register 'r' in lvalue 'v' */
234 void store(int r, SValue *v)
236 int fr, bt, ft, fc;
238 ft = v->t;
239 fc = v->c.ul;
240 fr = v->r & VT_VALMASK;
241 bt = ft & VT_BTYPE;
242 /* XXX: incorrect if float reg to reg */
243 if (bt == VT_FLOAT) {
244 o(0xd9); /* fsts */
245 r = 2;
246 } else if (bt == VT_DOUBLE) {
247 o(0xdd); /* fstpl */
248 r = 2;
249 } else if (bt == VT_LDOUBLE) {
250 o(0xc0d9); /* fld %st(0) */
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 fr == VT_LOCAL ||
263 (v->r & VT_LVAL)) {
264 gen_modrm(r, v->r, fc);
265 } else if (fr != r) {
266 o(0xc0 + fr + r * 8); /* mov r, fr */
270 /* start function call and return function call context */
271 void gfunc_start(GFuncContext *c)
273 c->args_size = 0;
276 /* push function parameter which is in (vtop->t, vtop->c). Stack entry
277 is then popped. */
278 void gfunc_param(GFuncContext *c)
280 int size, align, r;
282 if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
283 size = type_size(vtop->t, &align);
284 /* align to stack align size */
285 size = (size + 3) & ~3;
286 /* allocate the necessary size on stack */
287 oad(0xec81, size); /* sub $xxx, %esp */
288 /* generate structure store */
289 r = get_reg(RC_INT);
290 o(0x89); /* mov %esp, r */
291 o(0xe0 + r);
292 vset(VT_INT, r, 0);
293 vswap();
294 vstore();
295 c->args_size += size;
296 } else if (is_float(vtop->t)) {
297 gv(RC_FLOAT); /* only one float register */
298 if ((vtop->t & VT_BTYPE) == VT_FLOAT)
299 size = 4;
300 else if ((vtop->t & VT_BTYPE) == VT_DOUBLE)
301 size = 8;
302 else
303 size = 12;
304 oad(0xec81, size); /* sub $xxx, %esp */
305 if (size == 12)
306 o(0x7cdb);
307 else
308 o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
309 g(0x24);
310 g(0x00);
311 c->args_size += size;
312 } else {
313 /* simple type (currently always same size) */
314 /* XXX: implicit cast ? */
315 r = gv(RC_INT);
316 o(0x50 + r); /* push r */
317 c->args_size += 4;
319 vtop--;
322 /* generate function call with address in (vtop->t, vtop->c) and free function
323 context. Stack entry is popped */
324 void gfunc_call(GFuncContext *c)
326 int r;
327 if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
328 /* constant case */
329 /* forward reference */
330 if (vtop->r & VT_FORWARD) {
331 greloc(vtop->c.sym, ind + 1, RELOC_REL32);
332 oad(0xe8, 0);
333 } else {
334 oad(0xe8, vtop->c.ul - ind - 5);
336 } else {
337 /* otherwise, indirect call */
338 r = gv(RC_INT);
339 o(0xff); /* call *r */
340 o(0xd0 + r);
342 if (c->args_size)
343 oad(0xc481, c->args_size); /* add $xxx, %esp */
344 vtop--;
347 int gjmp(int t)
349 return psym(0xe9, t);
352 /* generate a test. set 'inv' to invert test. Stack entry is popped */
353 int gtst(int inv, int t)
355 int v, *p;
356 v = vtop->r & VT_VALMASK;
357 if (v == VT_CMP) {
358 /* fast case : can jump directly since flags are set */
359 g(0x0f);
360 t = psym((vtop->c.i - 16) ^ inv, t);
361 } else if (v == VT_JMP || v == VT_JMPI) {
362 /* && or || optimization */
363 if ((v & 1) == inv) {
364 /* insert vtop->c jump list in t */
365 p = &vtop->c.i;
366 while (*p != 0)
367 p = (int *)*p;
368 *p = t;
369 t = vtop->c.i;
370 } else {
371 t = gjmp(t);
372 gsym(vtop->c.i);
374 } else {
375 if (is_float(vtop->t)) {
376 vpushi(0);
377 gen_op(TOK_NE);
379 if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) {
380 /* constant jmp optimization */
381 if ((vtop->c.i != 0) != inv)
382 t = gjmp(t);
383 } else {
384 v = gv(RC_INT);
385 o(0x85);
386 o(0xc0 + v * 9);
387 g(0x0f);
388 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(RC_INT);
402 vswap();
403 fr = gv(RC_INT);
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->r = 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 = REG_EDX;
457 else
458 r = REG_EAX;
459 vtop->r = r;
460 } else {
461 vtop--;
462 o(0x39);
463 o(0xc0 + r + fr * 8); /* cmp fr, r */
464 vset(VT_INT, 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;
475 /* convert constants to memory references */
476 if ((vtop[-1].r & (VT_CONST | VT_LVAL)) == VT_CONST) {
477 vswap();
478 gv(RC_FLOAT);
479 vswap();
481 if ((vtop[0].r & (VT_CONST | VT_LVAL)) == VT_CONST)
482 gv(RC_FLOAT);
484 /* must put at least one value in the floating point register */
485 if ((vtop[-1].r & VT_LVAL) &&
486 (vtop[0].r & VT_LVAL)) {
487 vswap();
488 gv(RC_FLOAT);
489 vswap();
491 if (op >= TOK_EQ && op <= TOK_GT) {
492 /* load on stack second operand */
493 load(REG_ST0, vtop);
494 if (op == TOK_GE || op == TOK_GT)
495 o(0xc9d9); /* fxch %st(1) */
496 o(0xe9da); /* fucompp */
497 o(0xe0df); /* fnstsw %ax */
498 if (op == TOK_EQ) {
499 o(0x45e480); /* and $0x45, %ah */
500 o(0x40fC80); /* cmp $0x40, %ah */
501 } else if (op == TOK_NE) {
502 o(0x45e480); /* and $0x45, %ah */
503 o(0x40f480); /* xor $0x40, %ah */
504 op = TOK_NE;
505 } else if (op == TOK_GE || op == TOK_LE) {
506 o(0x05c4f6); /* test $0x05, %ah */
507 op = TOK_EQ;
508 } else {
509 o(0x45c4f6); /* test $0x45, %ah */
510 op = TOK_EQ;
512 vtop--;
513 vtop->r = VT_CMP;
514 vtop->c.i = op;
515 } else {
516 swapped = 0;
517 /* swap the stack if needed so that t1 is the register and t2 is
518 the memory reference */
519 if (vtop[-1].r & VT_LVAL) {
520 vswap();
521 swapped = 1;
523 /* no memory reference possible for long double operations */
524 if ((vtop->t & VT_BTYPE) == VT_LDOUBLE) {
525 load(REG_ST0, vtop);
526 swapped = !swapped;
529 switch(op) {
530 default:
531 case '+':
532 a = 0;
533 break;
534 case '-':
535 a = 4;
536 if (swapped)
537 a++;
538 break;
539 case '*':
540 a = 1;
541 break;
542 case '/':
543 a = 6;
544 if (swapped)
545 a++;
546 break;
548 ft = vtop->t;
549 fc = vtop->c.ul;
550 if ((ft & VT_BTYPE) == VT_LDOUBLE) {
551 o(0xde); /* fxxxp %st, %st(1) */
552 o(0xc1 + (a << 3));
553 } else {
554 if ((ft & VT_BTYPE) == VT_DOUBLE)
555 o(0xdc);
556 else
557 o(0xd8);
558 gen_modrm(a, vtop->r, fc);
560 vtop--;
564 /* convert integers to fp 't' type */
565 void gen_cvt_itof(int t)
567 gv(RC_INT);
568 if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) {
569 /* unsigned int to float/double/long double */
570 o(0x6a); /* push $0 */
571 g(0x00);
572 o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
573 o(0x242cdf); /* fildll (%esp) */
574 o(0x08c483); /* add $8, %esp */
575 } else {
576 /* int to float/double/long double */
577 o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
578 o(0x2404db); /* fildl (%esp) */
579 o(0x04c483); /* add $4, %esp */
581 vtop->r = REG_ST0;
584 /* FPU control word for rounding to nearest mode */
585 /* XXX: should move that into tcc lib support code ! */
586 static unsigned short __tcc_fpu_control = 0x137f;
587 /* FPU control word for round to zero mode for int convertion */
588 static unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
590 /* convert fp to int 't' type */
591 /* XXX: handle long long case */
592 void gen_cvt_ftoi(int t)
594 int r, size;
596 gv(RC_FLOAT);
597 if (t == VT_INT | VT_UNSIGNED &&
598 t == VT_LLONG | VT_UNSIGNED &&
599 t == VT_LLONG)
600 size = 8;
601 else
602 size = 4;
604 r = get_reg(RC_INT);
605 oad(0x2dd9, (int)&__tcc_int_fpu_control); /* ldcw xxx */
606 oad(0xec81, size); /* sub $xxx, %esp */
607 if (size == 4)
608 o(0x1cdb); /* fistpl */
609 else
610 o(0x3cdb); /* fistpll */
611 o(0x24);
612 oad(0x2dd9, (int)&__tcc_fpu_control); /* ldcw xxx */
613 o(0x58 + r); /* pop r */
614 if (size == 8)
615 o(0x04c483); /* add $4, %esp */
616 vtop->r = r;
619 /* convert from one floating point type to another */
620 void gen_cvt_ftof(int t)
622 /* all we have to do on i386 is to put the float in a register */
623 gv(RC_FLOAT);
626 /* pop stack value */
627 void vpop(void)
629 /* for x86, we need to pop the FP stack */
630 if ((vtop->r & VT_VALMASK) == REG_ST0) {
631 o(0xd9dd); /* fstp %st(1) */
633 vtop--;
638 /* end of X86 code generator */
639 /*************************************************************/