2 * X86 code generator for TCC
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 */
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 */
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 */
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
{
69 /******************************************************/
92 /* add a new relocation entry to symbol 's' */
93 void greloc(Sym
*s
, int addr
, int type
)
96 p
= malloc(sizeof(Reloc
));
101 p
->next
= (Reloc
*)s
->c
;
105 /* patch each relocation entry with value 'val' */
106 void greloc_patch(Sym
*s
, int val
)
115 *(int *)p
->addr
= val
;
118 *(int *)p
->addr
= val
- p
->addr
- 4;
128 /* output a symbol and patch all calls to it */
133 n
= *(int *)t
; /* next value */
134 *(int *)t
= a
- t
- 4;
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 ! */
148 /* instruction + 4 bytes data. Return the address of the data */
149 int oad(int c
, int 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
)) {
164 greloc((Sym
*)c
, ind
, RELOC_ADDR32
);
169 /* XXX: generate correct pointer for forward references to functions */
171 void load(int r
, int ft
, int fc
)
177 if (v
== VT_LLOCAL
) {
178 load(r
, VT_LOCAL
| VT_LVAL
, fc
);
181 if ((ft
& VT_BTYPE
) == VT_FLOAT
) {
184 } else if ((ft
& VT_BTYPE
) == VT_DOUBLE
) {
187 } else if ((ft
& VT_BTYPE
) == VT_LDOUBLE
) {
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 */
202 o(0x05 + r
* 8); /* 0xXX, r */
204 } else if (v
== VT_LOCAL
) {
205 oad(0x85 + r
* 8, fc
); /* xx(%ebp), r */
207 g(0x00 + r
* 8 + v
); /* (v), r */
211 o(0xb8 + r
); /* mov $xx, r */
213 } else if (v
== VT_LOCAL
) {
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 */
221 } else if (v
== VT_JMP
|| v
== VT_JMPI
) {
223 oad(0xb8 + r
, t
); /* mov $1, r */
224 oad(0xe9, 5); /* jmp after */
226 oad(0xb8 + r
, t
^ 1); /* mov $0, r */
229 o(0xc0 + r
+ v
* 8); /* mov v, r */
235 /* WARNING: r must not be allocated on the stack */
236 void store(r
, ft
, fc
)
240 fr
= ft
& VT_VALMASK
;
242 /* XXX: incorrect if reg to reg */
243 /* XXX: should not flush float stack */
244 if (bt
== VT_FLOAT
) {
247 } else if (bt
== VT_DOUBLE
) {
250 } else if (bt
== VT_LDOUBLE
) {
261 if (fr
== VT_CONST
) {
262 o(0x05 + r
* 8); /* mov r,xxx */
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
)
279 /* push function parameter which is in (vtop->t, vtop->c). Stack entry
281 void gfunc_param(GFuncContext
*c
)
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 */
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
)
305 else if ((vtop
->t
& VT_BTYPE
) == VT_DOUBLE
)
309 oad(0xec81, size
); /* sub $xxx, %esp */
313 o(0x5cd9 + size
- 4); /* fstp[s|l] 0(%esp) */
316 c
->args_size
+= size
;
318 /* simple type (currently always same size) */
319 /* XXX: implicit cast ? */
321 o(0x50 + r
); /* push r */
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
)
332 if ((vtop
->t
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
334 /* forward reference */
335 if (vtop
->t
& VT_FORWARD
) {
336 greloc((Sym
*)vtop
->c
, ind
+ 1, RELOC_REL32
);
339 oad(0xe8, vtop
->c
- ind
- 5);
342 /* otherwise, indirect call */
344 o(0xff); /* call *r */
348 oad(0xc481, c
->args_size
); /* add $xxx, %esp */
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
)
361 v
= vtop
->t
& VT_VALMASK
;
363 /* fast case : can jump directly since flags are set */
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 */
379 } else if ((vtop
->t
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
380 /* constant jmp optimization */
381 if ((vtop
->c
!= 0) != inv
)
389 t
= psym(0x85 ^ inv
, t
);
395 /* generate an integer binary operation */
408 o(0xc0 + r
+ fr
* 8);
409 } else if (op
== '-') {
411 o(0xc0 + r
+ fr
* 8);
412 } else if (op
== '&') {
414 o(0xc0 + r
+ fr
* 8);
415 } else if (op
== '^') {
417 o(0xc0 + r
+ fr
* 8);
418 } else if (op
== '|') {
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
) {
430 o(0x87); /* xchg r, %ecx */
435 o(0xd3); /* shl/shr/sar %cl, r */
438 else if (op
== TOK_SHR
)
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 */
452 o(0xf799); /* cltd, idiv t(%ebp), %eax */
455 if (op
== '%' | op
== TOK_UMOD
)
459 vtop
->t
= (vtop
->t
& VT_TYPE
) | r
;
463 o(0xc0 + r
+ fr
* 8); /* cmp fr, r */
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 */
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
)) {
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 */
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 */
496 } else if (op
== TOK_GE
|| op
== TOK_LE
) {
497 o(0x05c4f6); /* test $0x05, %ah */
500 o(0x45c4f6); /* test $0x45, %ah */
504 vtop
->t
= (vtop
->t
& VT_TYPE
) | VT_CMP
;
507 /* swap the stack if needed so that t1 is the register and t2 is
508 the memory reference */
510 if (vtop
[-1].t
& VT_LVAL
) {
536 if ((ft
& VT_BTYPE
) == VT_DOUBLE
)
545 } else if (r
== VT_LOCAL
) {
554 /* convert integers to floating point 't' type (float/double/long
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 */
562 o(0x50 + (vtop
->t
& VT_VALMASK
)); /* push r */
563 o(0x242cdf); /* fildll (%esp) */
564 o(0x08c483); /* add $8, %esp */
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 /*************************************************************/