1 /******************************************************/
2 /* X86 code generator */
5 /* number of available registers */
8 #define NB_REG_CLASSES 2
10 /* a register can belong to several classes */
11 #define REG_CLASS_INT 0x0001
12 #define REG_CLASS_FLOAT 0x0002
14 /* pretty names for the registers */
22 int reg_classes
[NB_REGS
] = {
23 REG_CLASS_INT
, /* eax */
24 REG_CLASS_INT
, /* ecx */
25 REG_CLASS_INT
, /* edx */
26 REG_CLASS_FLOAT
, /* st0 */
29 /* integer return register for functions */
30 #define FUNC_RET_REG 0
31 /* float return register for functions */
32 #define FUNC_RET_FREG 3
34 /* defined if function parameters must be evaluated in reverse order */
35 #define INVERT_FUNC_PARAMS
37 /* defined if structures are passed as pointers. Otherwise structures
38 are directly pushed on stack. */
39 //#define FUNC_STRUCT_PARAM_AS_PTR
41 /* function call context */
42 typedef struct GFuncContext
{
46 /******************************************************/
69 /* add a new relocation entry to symbol 's' */
70 void greloc(Sym
*s
, int addr
, int type
)
73 p
= malloc(sizeof(Reloc
));
78 p
->next
= (Reloc
*)s
->c
;
82 /* patch each relocation entry with value 'val' */
83 void greloc_patch(Sym
*s
, int val
)
92 *(int *)p
->addr
= val
;
95 *(int *)p
->addr
= val
- p
->addr
- 4;
105 /* output a symbol and patch all calls to it */
110 n
= *(int *)t
; /* next value */
111 *(int *)t
= a
- t
- 4;
121 /* psym is used to put an instruction with a data field which is a
122 reference to a symbol. It is in fact the same as oad ! */
125 /* instruction + 4 bytes data. Return the address of the data */
126 int oad(int c
, int s
)
135 /* output constant with relocation if 't & VT_FORWARD' is true */
136 void gen_addr32(int c
, int t
)
138 if (!(t
& VT_FORWARD
)) {
141 greloc((Sym
*)c
, ind
, RELOC_ADDR32
);
146 /* XXX: generate correct pointer for forward references to functions */
148 void load(int r
, int ft
, int fc
)
154 if (v
== VT_LLOCAL
) {
155 load(r
, VT_LOCAL
| VT_LVAL
, fc
);
158 if ((ft
& VT_BTYPE
) == VT_FLOAT
) {
161 } else if ((ft
& VT_BTYPE
) == VT_DOUBLE
) {
164 } else if ((ft
& VT_TYPE
) == VT_BYTE
)
165 o(0xbe0f); /* movsbl */
166 else if ((ft
& VT_TYPE
) == (VT_BYTE
| VT_UNSIGNED
))
167 o(0xb60f); /* movzbl */
168 else if ((ft
& VT_TYPE
) == VT_SHORT
)
169 o(0xbf0f); /* movswl */
170 else if ((ft
& VT_TYPE
) == (VT_SHORT
| VT_UNSIGNED
))
171 o(0xb70f); /* movzwl */
176 o(0x05 + r
* 8); /* 0xXX, r */
178 } else if (v
== VT_LOCAL
) {
179 oad(0x85 + r
* 8, fc
); /* xx(%ebp), r */
181 g(0x00 + r
* 8 + v
); /* (v), r */
185 o(0xb8 + r
); /* mov $xx, r */
187 } else if (v
== VT_LOCAL
) {
189 oad(0x85 + r
* 8, fc
); /* lea xxx(%ebp), r */
190 } else if (v
== VT_CMP
) {
191 oad(0xb8 + r
, 0); /* mov $0, r */
192 o(0x0f); /* setxx %br */
195 } else if (v
== VT_JMP
|| v
== VT_JMPI
) {
197 oad(0xb8 + r
, t
); /* mov $1, r */
198 oad(0xe9, 5); /* jmp after */
200 oad(0xb8 + r
, t
^ 1); /* mov $0, r */
203 o(0xc0 + r
+ v
* 8); /* mov v, r */
209 /* WARNING: r must not be allocated on the stack */
210 void store(r
, ft
, fc
)
214 fr
= ft
& VT_VALMASK
;
216 /* XXX: incorrect if reg to reg */
217 if (bt
== VT_FLOAT
) {
220 } else if (bt
== VT_DOUBLE
) {
231 if (fr
== VT_CONST
) {
232 o(0x05 + r
* 8); /* mov r,xxx */
234 } else if (fr
== VT_LOCAL
) {
235 oad(0x85 + r
* 8, fc
); /* mov r,xxx(%ebp) */
236 } else if (ft
& VT_LVAL
) {
237 g(fr
+ r
* 8); /* mov r, (fr) */
238 } else if (fr
!= r
) {
239 o(0xc0 + fr
+ r
* 8); /* mov r, fr */
243 /* start function call and return function call context */
244 void gfunc_start(GFuncContext
*c
)
249 /* push function parameter which is in (vt, vc) */
250 void gfunc_param(GFuncContext
*c
)
252 int size
, align
, ft
, fc
, r
;
254 if ((vt
& (VT_BTYPE
| VT_LVAL
)) == (VT_STRUCT
| VT_LVAL
)) {
255 size
= type_size(vt
, &align
);
256 /* align to stack align size */
257 size
= (size
+ 3) & ~3;
258 /* allocate the necessary size on stack */
259 oad(0xec81, size
); /* sub $xxx, %esp */
260 /* generate structure store */
261 r
= get_reg(REG_CLASS_INT
);
262 o(0x89); /* mov %esp, r */
271 c
->args_size
+= size
;
272 } else if ((vt
& VT_BTYPE
) == VT_DOUBLE
||
273 (vt
& VT_BTYPE
) == VT_FLOAT
) {
274 gv(); /* only one float register */
275 if ((vt
& VT_BTYPE
) == VT_FLOAT
)
279 oad(0xec81, size
); /* sub $xxx, %esp */
280 o(0x245cd9 + size
- 4); /* fstp[s|l] 0(%esp) */
284 /* simple type (currently always same size) */
285 /* XXX: implicit cast ? */
287 o(0x50 + r
); /* push r */
292 /* generate function call with address in (vt, vc) and free function
294 void gfunc_call(GFuncContext
*c
)
297 if ((vt
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
299 /* forward reference */
300 if (vt
& VT_FORWARD
) {
301 greloc((Sym
*)vc
, ind
+ 1, RELOC_REL32
);
304 oad(0xe8, vc
- ind
- 5);
307 /* otherwise, indirect call */
309 o(0xff); /* call *r */
313 oad(0xc481, c
->args_size
); /* add $xxx, %esp */
318 return psym(0xe9, t
);
321 /* generate a test. set 'inv' to invert test */
322 int gtst(int inv
, int t
)
327 /* fast case : can jump directly since flags are set */
329 t
= psym((vc
- 16) ^ inv
, t
);
330 } else if (v
== VT_JMP
|| v
== VT_JMPI
) {
331 /* && or || optimization */
332 if ((v
& 1) == inv
) {
333 /* insert vc jump list in t */
343 } else if ((vt
& (VT_VALMASK
| VT_LVAL
)) == VT_CONST
) {
344 /* constant jmp optimization */
345 if ((vc
!= 0) != inv
)
352 t
= psym(0x85 ^ inv
, t
);
357 /* generate an integer binary operation 'v = r op fr' instruction and
358 modifies (vt,vc) if needed */
359 void gen_opi(int op
, int r
, int fr
)
364 o(0xc0 + r
+ fr
* 8);
365 } else if (op
== '-') {
367 o(0xc0 + r
+ fr
* 8);
368 } else if (op
== '&') {
370 o(0xc0 + r
+ fr
* 8);
371 } else if (op
== '^') {
373 o(0xc0 + r
+ fr
* 8);
374 } else if (op
== '|') {
376 o(0xc0 + r
+ fr
* 8);
377 } else if (op
== '*') {
378 o(0xaf0f); /* imul fr, r */
379 o(0xc0 + fr
+ r
* 8);
380 } else if (op
== TOK_SHL
| op
== TOK_SHR
| op
== TOK_SAR
) {
386 o(0x87); /* xchg r, %ecx */
391 o(0xd3); /* shl/shr/sar %cl, r */
394 else if (op
== TOK_SHR
)
398 vt
= (vt
& VT_TYPE
) | r
;
399 } else if (op
== '/' | op
== TOK_UDIV
| op
== TOK_PDIV
|
400 op
== '%' | op
== TOK_UMOD
) {
401 save_reg(2); /* save edx */
402 t
= save_reg_forced(fr
); /* save fr and get op2 location */
403 move_reg(0, r
); /* op1 is %eax */
404 if (op
== TOK_UDIV
| op
== TOK_UMOD
) {
405 o(0xf7d231); /* xor %edx, %edx, div t(%ebp), %eax */
408 o(0xf799); /* cltd, idiv t(%ebp), %eax */
411 if (op
== '%' | op
== TOK_UMOD
)
415 vt
= (vt
& VT_TYPE
) | r
;
418 o(0xc0 + r
+ fr
* 8); /* cmp fr, r */
423 /* generate a floating point operation 'v = t1 op t2' instruction and
424 modifies (vt,vc) if needed */
425 /* NOTE: floats can only be lvalues */
428 int a
, ft
, fc
, swapped
, r
;
430 /* must put at least one value in the floating point register */
431 if ((vstack_ptr
[-4] & VT_LVAL
) &&
432 (vstack_ptr
[-2] & VT_LVAL
)) {
439 if (op
>= TOK_EQ
&& op
<= TOK_GT
) {
440 /* load on stack second operand */
441 load(REG_ST0
, vstack_ptr
[-2], vstack_ptr
[-1]);
442 if (op
== TOK_GE
|| op
== TOK_GT
)
443 o(0xc9d9); /* fxch %st(1) */
444 o(0xe9da); /* fucompp */
445 o(0xe0df); /* fnstsw %ax */
447 o(0x45e480); /* and $0x45, %ah */
448 o(0x40fC80); /* cmp $0x40, %ah */
449 } else if (op
== TOK_NE
) {
450 o(0x45e480); /* and $0x45, %ah */
451 o(0x40f480); /* xor $0x40, %ah */
453 } else if (op
== TOK_GE
|| op
== TOK_LE
) {
454 o(0x05c4f6); /* test $0x05, %ah */
457 o(0x45c4f6); /* test $0x45, %ah */
460 vstack_ptr
[-4] = (vstack_ptr
[-4] & VT_TYPE
) | VT_CMP
;
463 /* swap the stack if needed so that t1 is the register and t2 is
464 the memory reference */
466 if (vstack_ptr
[-4] & VT_LVAL
) {
492 if ((ft
& VT_BTYPE
) == VT_DOUBLE
)
501 } else if (r
== VT_LOCAL
) {
511 /* convert integers to floating point (float or double) */
514 if ((vt
& (VT_BTYPE
| VT_UNSIGNED
)) == (VT_INT
| VT_UNSIGNED
)) {
515 /* unsigned int to float/double */
516 o(0x6a); /* push $0 */
518 o(0x50 + (vt
& VT_VALMASK
)); /* push r */
519 o(0x242cdf); /* fildll (%esp) */
520 o(0x08c483); /* add $8, %esp */
522 /* int to float/double */
523 o(0x50 + (vt
& VT_VALMASK
)); /* push r */
524 o(0x2404db); /* fildl (%esp) */
525 o(0x04c483); /* add $4, %esp */
529 /* end of X86 code generator */
530 /*************************************************************/