Rev 647 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
145 | halyavin | 1 | /* |
2 | * X86 code generator for TCC |
||
3 | * |
||
4 | * Copyright (c) 2001-2004 Fabrice Bellard |
||
5 | * |
||
6 | * This library is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public |
||
8 | * License as published by the Free Software Foundation; either |
||
9 | * version 2 of the License, or (at your option) any later version. |
||
10 | * |
||
11 | * This library 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 GNU |
||
14 | * Lesser General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU Lesser General Public |
||
17 | * License along with this library; if not, write to the Free Software |
||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
19 | */ |
||
20 | |||
21 | /* number of available registers */ |
||
22 | #define NB_REGS 4 |
||
23 | |||
24 | /* a register can belong to several classes. The classes must be |
||
25 | sorted from more general to more precise (see gv2() code which does |
||
26 | assumptions on it). */ |
||
27 | #define RC_INT 0x0001 /* generic integer register */ |
||
28 | #define RC_FLOAT 0x0002 /* generic float register */ |
||
29 | #define RC_EAX 0x0004 |
||
30 | #define RC_ST0 0x0008 |
||
31 | #define RC_ECX 0x0010 |
||
32 | #define RC_EDX 0x0020 |
||
33 | #define RC_IRET RC_EAX /* function return: integer register */ |
||
34 | #define RC_LRET RC_EDX /* function return: second integer register */ |
||
35 | #define RC_FRET RC_ST0 /* function return: float register */ |
||
36 | |||
37 | /* pretty names for the registers */ |
||
38 | enum { |
||
39 | TREG_EAX = 0, |
||
40 | TREG_ECX, |
||
41 | TREG_EDX, |
||
42 | TREG_ST0, |
||
43 | }; |
||
44 | |||
45 | int reg_classes[NB_REGS] = { |
||
46 | /* eax */ RC_INT | RC_EAX, |
||
47 | /* ecx */ RC_INT | RC_ECX, |
||
48 | /* edx */ RC_INT | RC_EDX, |
||
49 | /* st0 */ RC_FLOAT | RC_ST0, |
||
50 | }; |
||
51 | |||
52 | /* return registers for function */ |
||
53 | #define REG_IRET TREG_EAX /* single word int return register */ |
||
54 | #define REG_LRET TREG_EDX /* second word return register (for long long) */ |
||
55 | #define REG_FRET TREG_ST0 /* float return register */ |
||
56 | |||
57 | /* defined if function parameters must be evaluated in reverse order */ |
||
58 | #define INVERT_FUNC_PARAMS |
||
59 | |||
60 | /* defined if structures are passed as pointers. Otherwise structures |
||
61 | are directly pushed on stack. */ |
||
62 | //#define FUNC_STRUCT_PARAM_AS_PTR |
||
63 | |||
64 | /* pointer size, in bytes */ |
||
65 | #define PTR_SIZE 4 |
||
66 | |||
67 | /* long double size and alignment, in bytes */ |
||
68 | #define LDOUBLE_SIZE 12 |
||
69 | #define LDOUBLE_ALIGN 4 |
||
70 | /* maximum alignment (for aligned attribute support) */ |
||
71 | #define MAX_ALIGN 8 |
||
72 | |||
73 | /******************************************************/ |
||
74 | /* ELF defines */ |
||
75 | |||
76 | #define EM_TCC_TARGET EM_386 |
||
77 | |||
78 | /* relocation type for 32 bit data relocation */ |
||
79 | #define R_DATA_32 R_386_32 |
||
80 | #define R_JMP_SLOT R_386_JMP_SLOT |
||
81 | #define R_COPY R_386_COPY |
||
82 | |||
83 | #define ELF_START_ADDR 0x08048000 |
||
84 | #define ELF_PAGE_SIZE 0x1000 |
||
85 | |||
86 | /******************************************************/ |
||
87 | |||
88 | static unsigned long func_sub_sp_offset; |
||
89 | static unsigned long func_bound_offset; |
||
90 | static int func_ret_sub; |
||
91 | |||
92 | /* XXX: make it faster ? */ |
||
93 | void g(int c) |
||
94 | { |
||
95 | int ind1; |
||
96 | ind1 = ind + 1; |
||
97 | if (ind1 > cur_text_section->data_allocated) |
||
98 | section_realloc(cur_text_section, ind1); |
||
99 | cur_text_section->data[ind] = c; |
||
100 | ind = ind1; |
||
101 | } |
||
102 | |||
103 | void o(unsigned int c) |
||
104 | { |
||
105 | while (c) { |
||
106 | g(c); |
||
107 | c = c >> 8; |
||
108 | } |
||
109 | } |
||
110 | |||
111 | void gen_le32(int c) |
||
112 | { |
||
113 | g(c); |
||
114 | g(c >> 8); |
||
115 | g(c >> 16); |
||
116 | g(c >> 24); |
||
117 | } |
||
118 | |||
119 | /* output a symbol and patch all calls to it */ |
||
120 | void gsym_addr(int t, int a) |
||
121 | { |
||
122 | int n, *ptr; |
||
123 | while (t) { |
||
124 | ptr = (int *)(cur_text_section->data + t); |
||
125 | n = *ptr; /* next value */ |
||
126 | *ptr = a - t - 4; |
||
127 | t = n; |
||
128 | } |
||
129 | } |
||
130 | |||
131 | void gsym(int t) |
||
132 | { |
||
133 | gsym_addr(t, ind); |
||
134 | } |
||
135 | |||
136 | /* psym is used to put an instruction with a data field which is a |
||
137 | reference to a symbol. It is in fact the same as oad ! */ |
||
138 | #define psym oad |
||
139 | |||
140 | /* instruction + 4 bytes data. Return the address of the data */ |
||
141 | static int oad(int c, int s) |
||
142 | { |
||
143 | int ind1; |
||
144 | |||
145 | o(c); |
||
146 | ind1 = ind + 4; |
||
147 | if (ind1 > cur_text_section->data_allocated) |
||
148 | section_realloc(cur_text_section, ind1); |
||
149 | *(int *)(cur_text_section->data + ind) = s; |
||
150 | s = ind; |
||
151 | ind = ind1; |
||
152 | return s; |
||
153 | } |
||
154 | |||
155 | /* output constant with relocation if 'r & VT_SYM' is true */ |
||
156 | static void gen_addr32(int r, Sym *sym, int c) |
||
157 | { |
||
158 | if (r & VT_SYM) |
||
159 | greloc(cur_text_section, sym, ind, R_386_32); |
||
160 | gen_le32(c); |
||
161 | } |
||
162 | |||
163 | /* generate a modrm reference. 'op_reg' contains the addtionnal 3 |
||
164 | opcode bits */ |
||
165 | static void gen_modrm(int op_reg, int r, Sym *sym, int c) |
||
166 | { |
||
167 | op_reg = op_reg << 3; |
||
168 | if ((r & VT_VALMASK) == VT_CONST) { |
||
169 | /* constant memory reference */ |
||
170 | o(0x05 | op_reg); |
||
171 | gen_addr32(r, sym, c); |
||
172 | } else if ((r & VT_VALMASK) == VT_LOCAL) { |
||
173 | /* currently, we use only ebp as base */ |
||
174 | if (c == (char)c) { |
||
175 | /* short reference */ |
||
176 | o(0x45 | op_reg); |
||
177 | g(c); |
||
178 | } else { |
||
179 | oad(0x85 | op_reg, c); |
||
180 | } |
||
181 | } else { |
||
182 | g(0x00 | op_reg | (r & VT_VALMASK)); |
||
183 | } |
||
184 | } |
||
185 | |||
186 | |||
187 | /* load 'r' from value 'sv' */ |
||
188 | void load(int r, SValue *sv) |
||
189 | { |
||
190 | int v, t, ft, fc, fr; |
||
191 | SValue v1; |
||
192 | |||
193 | fr = sv->r; |
||
194 | ft = sv->type.t; |
||
195 | fc = sv->c.ul; |
||
196 | |||
197 | v = fr & VT_VALMASK; |
||
198 | if (fr & VT_LVAL) { |
||
199 | if (v == VT_LLOCAL) { |
||
200 | v1.type.t = VT_INT; |
||
201 | v1.r = VT_LOCAL | VT_LVAL; |
||
202 | v1.c.ul = fc; |
||
203 | load(r, &v1); |
||
204 | fr = r; |
||
205 | } |
||
206 | if ((ft & VT_BTYPE) == VT_FLOAT) { |
||
207 | o(0xd9); /* flds */ |
||
208 | r = 0; |
||
209 | } else if ((ft & VT_BTYPE) == VT_DOUBLE) { |
||
210 | o(0xdd); /* fldl */ |
||
211 | r = 0; |
||
212 | } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { |
||
213 | o(0xdb); /* fldt */ |
||
214 | r = 5; |
||
215 | } else if ((ft & VT_TYPE) == VT_BYTE) { |
||
216 | o(0xbe0f); /* movsbl */ |
||
217 | } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { |
||
218 | o(0xb60f); /* movzbl */ |
||
219 | } else if ((ft & VT_TYPE) == VT_SHORT) { |
||
220 | o(0xbf0f); /* movswl */ |
||
221 | } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { |
||
222 | o(0xb70f); /* movzwl */ |
||
223 | } else { |
||
224 | o(0x8b); /* movl */ |
||
225 | } |
||
226 | gen_modrm(r, fr, sv->sym, fc); |
||
227 | } else { |
||
228 | if (v == VT_CONST) { |
||
229 | o(0xb8 + r); /* mov $xx, r */ |
||
230 | gen_addr32(fr, sv->sym, fc); |
||
231 | } else if (v == VT_LOCAL) { |
||
232 | o(0x8d); /* lea xxx(%ebp), r */ |
||
233 | gen_modrm(r, VT_LOCAL, sv->sym, fc); |
||
234 | } else if (v == VT_CMP) { |
||
235 | oad(0xb8 + r, 0); /* mov $0, r */ |
||
236 | o(0x0f); /* setxx %br */ |
||
237 | o(fc); |
||
238 | o(0xc0 + r); |
||
239 | } else if (v == VT_JMP || v == VT_JMPI) { |
||
240 | t = v & 1; |
||
241 | oad(0xb8 + r, t); /* mov $1, r */ |
||
242 | o(0x05eb); /* jmp after */ |
||
243 | gsym(fc); |
||
244 | oad(0xb8 + r, t ^ 1); /* mov $0, r */ |
||
245 | } else if (v != r) { |
||
246 | o(0x89); |
||
247 | o(0xc0 + r + v * 8); /* mov v, r */ |
||
248 | } |
||
249 | } |
||
250 | } |
||
251 | |||
252 | /* store register 'r' in lvalue 'v' */ |
||
253 | void store(int r, SValue *v) |
||
254 | { |
||
255 | int fr, bt, ft, fc; |
||
256 | |||
257 | ft = v->type.t; |
||
258 | fc = v->c.ul; |
||
259 | fr = v->r & VT_VALMASK; |
||
260 | bt = ft & VT_BTYPE; |
||
261 | /* XXX: incorrect if float reg to reg */ |
||
262 | if (bt == VT_FLOAT) { |
||
263 | o(0xd9); /* fsts */ |
||
264 | r = 2; |
||
265 | } else if (bt == VT_DOUBLE) { |
||
266 | o(0xdd); /* fstpl */ |
||
267 | r = 2; |
||
268 | } else if (bt == VT_LDOUBLE) { |
||
269 | o(0xc0d9); /* fld %st(0) */ |
||
270 | o(0xdb); /* fstpt */ |
||
271 | r = 7; |
||
272 | } else { |
||
273 | if (bt == VT_SHORT) |
||
274 | o(0x66); |
||
275 | if (bt == VT_BYTE || bt == VT_BOOL) |
||
276 | o(0x88); |
||
277 | else |
||
278 | o(0x89); |
||
279 | } |
||
280 | if (fr == VT_CONST || |
||
281 | fr == VT_LOCAL || |
||
282 | (v->r & VT_LVAL)) { |
||
283 | gen_modrm(r, v->r, v->sym, fc); |
||
284 | } else if (fr != r) { |
||
285 | o(0xc0 + fr + r * 8); /* mov r, fr */ |
||
286 | } |
||
287 | } |
||
288 | |||
289 | static void gadd_sp(int val) |
||
290 | { |
||
291 | if (val == (char)val) { |
||
292 | o(0xc483); |
||
293 | g(val); |
||
294 | } else { |
||
295 | oad(0xc481, val); /* add $xxx, %esp */ |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /* 'is_jmp' is '1' if it is a jump */ |
||
300 | static void gcall_or_jmp(int is_jmp) |
||
301 | { |
||
302 | int r; |
||
303 | if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
||
304 | /* constant case */ |
||
305 | if (vtop->r & VT_SYM) { |
||
306 | /* relocation case */ |
||
307 | greloc(cur_text_section, vtop->sym, |
||
308 | ind + 1, R_386_PC32); |
||
309 | } else { |
||
310 | /* put an empty PC32 relocation */ |
||
311 | put_elf_reloc(symtab_section, cur_text_section, |
||
312 | ind + 1, R_386_PC32, 0); |
||
313 | } |
||
314 | oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ |
||
315 | } else { |
||
316 | /* otherwise, indirect call */ |
||
317 | r = gv(RC_INT); |
||
318 | o(0xff); /* call/jmp *r */ |
||
319 | o(0xd0 + r + (is_jmp << 4)); |
||
320 | } |
||
321 | } |
||
322 | |||
323 | static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; |
||
324 | |||
325 | /* Generate function call. The function address is pushed first, then |
||
326 | all the parameters in call order. This functions pops all the |
||
327 | parameters and the function address. */ |
||
328 | void gfunc_call(int nb_args) |
||
329 | { |
||
330 | int size, align, r, args_size, i, func_call; |
||
331 | Sym *func_sym; |
||
332 | |||
333 | args_size = 0; |
||
334 | for(i = 0;i < nb_args; i++) { |
||
335 | if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { |
||
336 | size = type_size(&vtop->type, &align); |
||
337 | /* align to stack align size */ |
||
338 | size = (size + 3) & ~3; |
||
339 | /* allocate the necessary size on stack */ |
||
340 | oad(0xec81, size); /* sub $xxx, %esp */ |
||
341 | /* generate structure store */ |
||
342 | r = get_reg(RC_INT); |
||
343 | o(0x89); /* mov %esp, r */ |
||
344 | o(0xe0 + r); |
||
345 | vset(&vtop->type, r | VT_LVAL, 0); |
||
346 | vswap(); |
||
347 | vstore(); |
||
348 | args_size += size; |
||
349 | } else if (is_float(vtop->type.t)) { |
||
350 | gv(RC_FLOAT); /* only one float register */ |
||
351 | if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) |
||
352 | size = 4; |
||
353 | else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) |
||
354 | size = 8; |
||
355 | else |
||
356 | size = 12; |
||
357 | oad(0xec81, size); /* sub $xxx, %esp */ |
||
358 | if (size == 12) |
||
359 | o(0x7cdb); |
||
360 | else |
||
361 | o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ |
||
362 | g(0x24); |
||
363 | g(0x00); |
||
364 | args_size += size; |
||
365 | } else { |
||
366 | /* simple type (currently always same size) */ |
||
367 | /* XXX: implicit cast ? */ |
||
368 | r = gv(RC_INT); |
||
369 | if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
||
370 | size = 8; |
||
371 | o(0x50 + vtop->r2); /* push r */ |
||
372 | } else { |
||
373 | size = 4; |
||
374 | } |
||
375 | o(0x50 + r); /* push r */ |
||
376 | args_size += size; |
||
377 | } |
||
378 | vtop--; |
||
379 | } |
||
380 | save_regs(0); /* save used temporary registers */ |
||
381 | func_sym = vtop->type.ref; |
||
382 | func_call = func_sym->r; |
||
383 | /* fast call case */ |
||
384 | if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { |
||
385 | int fastcall_nb_regs; |
||
386 | fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; |
||
387 | for(i = 0;i < fastcall_nb_regs; i++) { |
||
388 | if (args_size <= 0) |
||
389 | break; |
||
390 | o(0x58 + fastcall_regs[i]); /* pop r */ |
||
391 | /* XXX: incorrect for struct/floats */ |
||
392 | args_size -= 4; |
||
393 | } |
||
394 | } |
||
395 | gcall_or_jmp(0); |
||
396 | if (args_size && func_sym->r != FUNC_STDCALL) |
||
397 | gadd_sp(args_size); |
||
398 | vtop--; |
||
399 | } |
||
400 | |||
401 | #ifdef TCC_TARGET_PE |
||
402 | #define FUNC_PROLOG_SIZE 10 |
||
403 | #else |
||
404 | #define FUNC_PROLOG_SIZE 9 |
||
405 | #endif |
||
406 | |||
407 | /* generate function prolog of type 't' */ |
||
408 | void gfunc_prolog(CType *func_type) |
||
409 | { |
||
410 | int addr, align, size, func_call, fastcall_nb_regs; |
||
411 | int param_index, param_addr; |
||
412 | Sym *sym; |
||
413 | CType *type; |
||
414 | |||
415 | sym = func_type->ref; |
||
416 | func_call = sym->r; |
||
417 | addr = 8; |
||
418 | loc = 0; |
||
419 | if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { |
||
420 | fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; |
||
421 | } else { |
||
422 | fastcall_nb_regs = 0; |
||
423 | } |
||
424 | param_index = 0; |
||
425 | |||
426 | ind += FUNC_PROLOG_SIZE; |
||
427 | func_sub_sp_offset = ind; |
||
428 | /* if the function returns a structure, then add an |
||
429 | implicit pointer parameter */ |
||
430 | func_vt = sym->type; |
||
431 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
||
432 | /* XXX: fastcall case ? */ |
||
433 | func_vc = addr; |
||
434 | addr += 4; |
||
435 | param_index++; |
||
436 | } |
||
437 | /* define parameters */ |
||
438 | while ((sym = sym->next) != NULL) { |
||
439 | type = &sym->type; |
||
440 | size = type_size(type, &align); |
||
441 | size = (size + 3) & ~3; |
||
442 | #ifdef FUNC_STRUCT_PARAM_AS_PTR |
||
443 | /* structs are passed as pointer */ |
||
444 | if ((type->t & VT_BTYPE) == VT_STRUCT) { |
||
445 | size = 4; |
||
446 | } |
||
447 | #endif |
||
448 | if (param_index < fastcall_nb_regs) { |
||
449 | /* save FASTCALL register */ |
||
450 | loc -= 4; |
||
451 | o(0x89); /* movl */ |
||
452 | gen_modrm(fastcall_regs[param_index], VT_LOCAL, NULL, loc); |
||
453 | param_addr = loc; |
||
454 | } else { |
||
455 | param_addr = addr; |
||
456 | addr += size; |
||
457 | } |
||
458 | sym_push(sym->v & ~SYM_FIELD, type, |
||
459 | VT_LOCAL | VT_LVAL, param_addr); |
||
460 | param_index++; |
||
461 | } |
||
462 | func_ret_sub = 0; |
||
463 | /* pascal type call ? */ |
||
464 | if (func_call == FUNC_STDCALL) |
||
465 | func_ret_sub = addr - 8; |
||
466 | |||
467 | /* leave some room for bound checking code */ |
||
468 | if (do_bounds_check) { |
||
469 | oad(0xb8, 0); /* lbound section pointer */ |
||
470 | oad(0xb8, 0); /* call to function */ |
||
471 | func_bound_offset = lbounds_section->data_offset; |
||
472 | } |
||
473 | } |
||
474 | |||
475 | /* generate function epilog */ |
||
476 | void gfunc_epilog(void) |
||
477 | { |
||
478 | int v, saved_ind; |
||
479 | |||
480 | #ifdef CONFIG_TCC_BCHECK |
||
481 | if (do_bounds_check && func_bound_offset != lbounds_section->data_offset) { |
||
482 | int saved_ind; |
||
483 | int *bounds_ptr; |
||
484 | Sym *sym, *sym_data; |
||
485 | /* add end of table info */ |
||
486 | bounds_ptr = section_ptr_add(lbounds_section, sizeof(int)); |
||
487 | *bounds_ptr = 0; |
||
488 | /* generate bound local allocation */ |
||
489 | saved_ind = ind; |
||
490 | ind = func_sub_sp_offset; |
||
491 | sym_data = get_sym_ref(&char_pointer_type, lbounds_section, |
||
492 | func_bound_offset, lbounds_section->data_offset); |
||
493 | greloc(cur_text_section, sym_data, |
||
494 | ind + 1, R_386_32); |
||
495 | oad(0xb8, 0); /* mov %eax, xxx */ |
||
496 | sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0); |
||
497 | greloc(cur_text_section, sym, |
||
498 | ind + 1, R_386_PC32); |
||
499 | oad(0xe8, -4); |
||
500 | ind = saved_ind; |
||
501 | /* generate bound check local freeing */ |
||
502 | o(0x5250); /* save returned value, if any */ |
||
503 | greloc(cur_text_section, sym_data, |
||
504 | ind + 1, R_386_32); |
||
505 | oad(0xb8, 0); /* mov %eax, xxx */ |
||
506 | sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0); |
||
507 | greloc(cur_text_section, sym, |
||
508 | ind + 1, R_386_PC32); |
||
509 | oad(0xe8, -4); |
||
510 | o(0x585a); /* restore returned value, if any */ |
||
511 | } |
||
512 | #endif |
||
513 | o(0xc9); /* leave */ |
||
514 | if (func_ret_sub == 0) { |
||
515 | o(0xc3); /* ret */ |
||
516 | } else { |
||
517 | o(0xc2); /* ret n */ |
||
518 | g(func_ret_sub); |
||
519 | g(func_ret_sub >> 8); |
||
520 | } |
||
521 | /* align local size to word & save local variables */ |
||
522 | |||
523 | v = (-loc + 3) & -4; |
||
524 | saved_ind = ind; |
||
525 | ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; |
||
526 | #ifdef TCC_TARGET_PE |
||
527 | if (v >= 4096) { |
||
528 | Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); |
||
529 | oad(0xb8, v); /* mov stacksize, %eax */ |
||
530 | oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ |
||
531 | greloc(cur_text_section, sym, ind-4, R_386_PC32); |
||
532 | } else |
||
533 | #endif |
||
534 | { |
||
535 | o(0xe58955); /* push %ebp, mov %esp, %ebp */ |
||
536 | o(0xec81); /* sub esp, stacksize */ |
||
537 | gen_le32(v); |
||
538 | #if FUNC_PROLOG_SIZE == 10 |
||
539 | o(0x90); /* adjust to FUNC_PROLOG_SIZE */ |
||
540 | #endif |
||
541 | } |
||
542 | ind = saved_ind; |
||
543 | } |
||
544 | |||
545 | /* generate a jump to a label */ |
||
546 | int gjmp(int t) |
||
547 | { |
||
548 | return psym(0xe9, t); |
||
549 | } |
||
550 | |||
551 | /* generate a jump to a fixed address */ |
||
552 | void gjmp_addr(int a) |
||
553 | { |
||
554 | int r; |
||
555 | r = a - ind - 2; |
||
556 | if (r == (char)r) { |
||
557 | g(0xeb); |
||
558 | g(r); |
||
559 | } else { |
||
560 | oad(0xe9, a - ind - 5); |
||
561 | } |
||
562 | } |
||
563 | |||
564 | /* generate a test. set 'inv' to invert test. Stack entry is popped */ |
||
565 | int gtst(int inv, int t) |
||
566 | { |
||
567 | int v, *p; |
||
568 | |||
569 | v = vtop->r & VT_VALMASK; |
||
570 | if (v == VT_CMP) { |
||
571 | /* fast case : can jump directly since flags are set */ |
||
572 | g(0x0f); |
||
573 | t = psym((vtop->c.i - 16) ^ inv, t); |
||
574 | } else if (v == VT_JMP || v == VT_JMPI) { |
||
575 | /* && or || optimization */ |
||
576 | if ((v & 1) == inv) { |
||
577 | /* insert vtop->c jump list in t */ |
||
578 | p = &vtop->c.i; |
||
579 | while (*p != 0) |
||
580 | p = (int *)(cur_text_section->data + *p); |
||
581 | *p = t; |
||
582 | t = vtop->c.i; |
||
583 | } else { |
||
584 | t = gjmp(t); |
||
585 | gsym(vtop->c.i); |
||
586 | } |
||
587 | } else { |
||
588 | if (is_float(vtop->type.t)) { |
||
589 | vpushi(0); |
||
590 | gen_op(TOK_NE); |
||
591 | } |
||
592 | if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
593 | /* constant jmp optimization */ |
||
594 | if ((vtop->c.i != 0) != inv) |
||
595 | t = gjmp(t); |
||
596 | } else { |
||
597 | v = gv(RC_INT); |
||
598 | o(0x85); |
||
599 | o(0xc0 + v * 9); |
||
600 | g(0x0f); |
||
601 | t = psym(0x85 ^ inv, t); |
||
602 | } |
||
603 | } |
||
604 | vtop--; |
||
605 | return t; |
||
606 | } |
||
607 | |||
608 | /* generate an integer binary operation */ |
||
609 | void gen_opi(int op) |
||
610 | { |
||
611 | int r, fr, opc, c; |
||
612 | |||
613 | switch(op) { |
||
614 | case '+': |
||
615 | case TOK_ADDC1: /* add with carry generation */ |
||
616 | opc = 0; |
||
617 | gen_op8: |
||
618 | if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
619 | /* constant case */ |
||
620 | vswap(); |
||
621 | r = gv(RC_INT); |
||
622 | vswap(); |
||
623 | c = vtop->c.i; |
||
624 | if (c == (char)c) { |
||
625 | /* XXX: generate inc and dec for smaller code ? */ |
||
626 | o(0x83); |
||
627 | o(0xc0 | (opc << 3) | r); |
||
628 | g(c); |
||
629 | } else { |
||
630 | o(0x81); |
||
631 | oad(0xc0 | (opc << 3) | r, c); |
||
632 | } |
||
633 | } else { |
||
634 | gv2(RC_INT, RC_INT); |
||
635 | r = vtop[-1].r; |
||
636 | fr = vtop[0].r; |
||
637 | o((opc << 3) | 0x01); |
||
638 | o(0xc0 + r + fr * 8); |
||
639 | } |
||
640 | vtop--; |
||
641 | if (op >= TOK_ULT && op <= TOK_GT) { |
||
642 | vtop->r = VT_CMP; |
||
643 | vtop->c.i = op; |
||
644 | } |
||
645 | break; |
||
646 | case '-': |
||
647 | case TOK_SUBC1: /* sub with carry generation */ |
||
648 | opc = 5; |
||
649 | goto gen_op8; |
||
650 | case TOK_ADDC2: /* add with carry use */ |
||
651 | opc = 2; |
||
652 | goto gen_op8; |
||
653 | case TOK_SUBC2: /* sub with carry use */ |
||
654 | opc = 3; |
||
655 | goto gen_op8; |
||
656 | case '&': |
||
657 | opc = 4; |
||
658 | goto gen_op8; |
||
659 | case '^': |
||
660 | opc = 6; |
||
661 | goto gen_op8; |
||
662 | case '|': |
||
663 | opc = 1; |
||
664 | goto gen_op8; |
||
665 | case '*': |
||
666 | gv2(RC_INT, RC_INT); |
||
667 | r = vtop[-1].r; |
||
668 | fr = vtop[0].r; |
||
669 | vtop--; |
||
670 | o(0xaf0f); /* imul fr, r */ |
||
671 | o(0xc0 + fr + r * 8); |
||
672 | break; |
||
673 | case TOK_SHL: |
||
674 | opc = 4; |
||
675 | goto gen_shift; |
||
676 | case TOK_SHR: |
||
677 | opc = 5; |
||
678 | goto gen_shift; |
||
679 | case TOK_SAR: |
||
680 | opc = 7; |
||
681 | gen_shift: |
||
682 | opc = 0xc0 | (opc << 3); |
||
683 | if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { |
||
684 | /* constant case */ |
||
685 | vswap(); |
||
686 | r = gv(RC_INT); |
||
687 | vswap(); |
||
688 | c = vtop->c.i & 0x1f; |
||
689 | o(0xc1); /* shl/shr/sar $xxx, r */ |
||
690 | o(opc | r); |
||
691 | g(c); |
||
692 | } else { |
||
693 | /* we generate the shift in ecx */ |
||
694 | gv2(RC_INT, RC_ECX); |
||
695 | r = vtop[-1].r; |
||
696 | o(0xd3); /* shl/shr/sar %cl, r */ |
||
697 | o(opc | r); |
||
698 | } |
||
699 | vtop--; |
||
700 | break; |
||
701 | case '/': |
||
702 | case TOK_UDIV: |
||
703 | case TOK_PDIV: |
||
704 | case '%': |
||
705 | case TOK_UMOD: |
||
706 | case TOK_UMULL: |
||
707 | /* first operand must be in eax */ |
||
708 | /* XXX: need better constraint for second operand */ |
||
709 | gv2(RC_EAX, RC_ECX); |
||
710 | r = vtop[-1].r; |
||
711 | fr = vtop[0].r; |
||
712 | vtop--; |
||
713 | save_reg(TREG_EDX); |
||
714 | if (op == TOK_UMULL) { |
||
715 | o(0xf7); /* mul fr */ |
||
716 | o(0xe0 + fr); |
||
717 | vtop->r2 = TREG_EDX; |
||
718 | r = TREG_EAX; |
||
719 | } else { |
||
720 | if (op == TOK_UDIV || op == TOK_UMOD) { |
||
721 | o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ |
||
722 | o(0xf0 + fr); |
||
723 | } else { |
||
724 | o(0xf799); /* cltd, idiv fr, %eax */ |
||
725 | o(0xf8 + fr); |
||
726 | } |
||
727 | if (op == '%' || op == TOK_UMOD) |
||
728 | r = TREG_EDX; |
||
729 | else |
||
730 | r = TREG_EAX; |
||
731 | } |
||
732 | vtop->r = r; |
||
733 | break; |
||
734 | default: |
||
735 | opc = 7; |
||
736 | goto gen_op8; |
||
737 | } |
||
738 | } |
||
739 | |||
740 | /* generate a floating point operation 'v = t1 op t2' instruction. The |
||
741 | two operands are guaranted to have the same floating point type */ |
||
742 | /* XXX: need to use ST1 too */ |
||
743 | void gen_opf(int op) |
||
744 | { |
||
745 | int a, ft, fc, swapped, r; |
||
746 | |||
747 | /* convert constants to memory references */ |
||
748 | if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { |
||
749 | vswap(); |
||
750 | gv(RC_FLOAT); |
||
751 | vswap(); |
||
752 | } |
||
753 | if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) |
||
754 | gv(RC_FLOAT); |
||
755 | |||
756 | /* must put at least one value in the floating point register */ |
||
757 | if ((vtop[-1].r & VT_LVAL) && |
||
758 | (vtop[0].r & VT_LVAL)) { |
||
759 | vswap(); |
||
760 | gv(RC_FLOAT); |
||
761 | vswap(); |
||
762 | } |
||
763 | swapped = 0; |
||
764 | /* swap the stack if needed so that t1 is the register and t2 is |
||
765 | the memory reference */ |
||
766 | if (vtop[-1].r & VT_LVAL) { |
||
767 | vswap(); |
||
768 | swapped = 1; |
||
769 | } |
||
770 | if (op >= TOK_ULT && op <= TOK_GT) { |
||
771 | /* load on stack second operand */ |
||
772 | load(TREG_ST0, vtop); |
||
773 | save_reg(TREG_EAX); /* eax is used by FP comparison code */ |
||
774 | if (op == TOK_GE || op == TOK_GT) |
||
775 | swapped = !swapped; |
||
776 | else if (op == TOK_EQ || op == TOK_NE) |
||
777 | swapped = 0; |
||
778 | if (swapped) |
||
779 | o(0xc9d9); /* fxch %st(1) */ |
||
780 | o(0xe9da); /* fucompp */ |
||
781 | o(0xe0df); /* fnstsw %ax */ |
||
782 | if (op == TOK_EQ) { |
||
783 | o(0x45e480); /* and $0x45, %ah */ |
||
784 | o(0x40fC80); /* cmp $0x40, %ah */ |
||
785 | } else if (op == TOK_NE) { |
||
786 | o(0x45e480); /* and $0x45, %ah */ |
||
787 | o(0x40f480); /* xor $0x40, %ah */ |
||
788 | op = TOK_NE; |
||
789 | } else if (op == TOK_GE || op == TOK_LE) { |
||
790 | o(0x05c4f6); /* test $0x05, %ah */ |
||
791 | op = TOK_EQ; |
||
792 | } else { |
||
793 | o(0x45c4f6); /* test $0x45, %ah */ |
||
794 | op = TOK_EQ; |
||
795 | } |
||
796 | vtop--; |
||
797 | vtop->r = VT_CMP; |
||
798 | vtop->c.i = op; |
||
799 | } else { |
||
800 | /* no memory reference possible for long double operations */ |
||
801 | if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { |
||
802 | load(TREG_ST0, vtop); |
||
803 | swapped = !swapped; |
||
804 | } |
||
805 | |||
806 | switch(op) { |
||
807 | default: |
||
808 | case '+': |
||
809 | a = 0; |
||
810 | break; |
||
811 | case '-': |
||
812 | a = 4; |
||
813 | if (swapped) |
||
814 | a++; |
||
815 | break; |
||
816 | case '*': |
||
817 | a = 1; |
||
818 | break; |
||
819 | case '/': |
||
820 | a = 6; |
||
821 | if (swapped) |
||
822 | a++; |
||
823 | break; |
||
824 | } |
||
825 | ft = vtop->type.t; |
||
826 | fc = vtop->c.ul; |
||
827 | if ((ft & VT_BTYPE) == VT_LDOUBLE) { |
||
828 | o(0xde); /* fxxxp %st, %st(1) */ |
||
829 | o(0xc1 + (a << 3)); |
||
830 | } else { |
||
831 | /* if saved lvalue, then we must reload it */ |
||
832 | r = vtop->r; |
||
833 | if ((r & VT_VALMASK) == VT_LLOCAL) { |
||
834 | SValue v1; |
||
835 | r = get_reg(RC_INT); |
||
836 | v1.type.t = VT_INT; |
||
837 | v1.r = VT_LOCAL | VT_LVAL; |
||
838 | v1.c.ul = fc; |
||
839 | load(r, &v1); |
||
840 | fc = 0; |
||
841 | } |
||
842 | |||
843 | if ((ft & VT_BTYPE) == VT_DOUBLE) |
||
844 | o(0xdc); |
||
845 | else |
||
846 | o(0xd8); |
||
847 | gen_modrm(a, r, vtop->sym, fc); |
||
848 | } |
||
849 | vtop--; |
||
850 | } |
||
851 | } |
||
852 | |||
853 | /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' |
||
854 | and 'long long' cases. */ |
||
855 | void gen_cvt_itof(int t) |
||
856 | { |
||
857 | save_reg(TREG_ST0); |
||
858 | gv(RC_INT); |
||
859 | if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { |
||
860 | /* signed long long to float/double/long double (unsigned case |
||
861 | is handled generically) */ |
||
862 | o(0x50 + vtop->r2); /* push r2 */ |
||
863 | o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
||
864 | o(0x242cdf); /* fildll (%esp) */ |
||
865 | o(0x08c483); /* add $8, %esp */ |
||
866 | } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == |
||
867 | (VT_INT | VT_UNSIGNED)) { |
||
868 | /* unsigned int to float/double/long double */ |
||
869 | o(0x6a); /* push $0 */ |
||
870 | g(0x00); |
||
871 | o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
||
872 | o(0x242cdf); /* fildll (%esp) */ |
||
873 | o(0x08c483); /* add $8, %esp */ |
||
874 | } else { |
||
875 | /* int to float/double/long double */ |
||
876 | o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ |
||
877 | o(0x2404db); /* fildl (%esp) */ |
||
878 | o(0x04c483); /* add $4, %esp */ |
||
879 | } |
||
880 | vtop->r = TREG_ST0; |
||
881 | } |
||
882 | |||
883 | /* convert fp to int 't' type */ |
||
884 | /* XXX: handle long long case */ |
||
885 | void gen_cvt_ftoi(int t) |
||
886 | { |
||
887 | int r, r2, size; |
||
888 | Sym *sym; |
||
889 | CType ushort_type; |
||
890 | |||
891 | ushort_type.t = VT_SHORT | VT_UNSIGNED; |
||
892 | |||
893 | gv(RC_FLOAT); |
||
894 | if (t != VT_INT) |
||
895 | size = 8; |
||
896 | else |
||
897 | size = 4; |
||
898 | |||
899 | o(0x2dd9); /* ldcw xxx */ |
||
900 | sym = external_global_sym(TOK___tcc_int_fpu_control, |
||
901 | &ushort_type, VT_LVAL); |
||
902 | greloc(cur_text_section, sym, |
||
903 | ind, R_386_32); |
||
904 | gen_le32(0); |
||
905 | |||
906 | oad(0xec81, size); /* sub $xxx, %esp */ |
||
907 | if (size == 4) |
||
908 | o(0x1cdb); /* fistpl */ |
||
909 | else |
||
910 | o(0x3cdf); /* fistpll */ |
||
911 | o(0x24); |
||
912 | o(0x2dd9); /* ldcw xxx */ |
||
913 | sym = external_global_sym(TOK___tcc_fpu_control, |
||
914 | &ushort_type, VT_LVAL); |
||
915 | greloc(cur_text_section, sym, |
||
916 | ind, R_386_32); |
||
917 | gen_le32(0); |
||
918 | |||
919 | r = get_reg(RC_INT); |
||
920 | o(0x58 + r); /* pop r */ |
||
921 | if (size == 8) { |
||
922 | if (t == VT_LLONG) { |
||
923 | vtop->r = r; /* mark reg as used */ |
||
924 | r2 = get_reg(RC_INT); |
||
925 | o(0x58 + r2); /* pop r2 */ |
||
926 | vtop->r2 = r2; |
||
927 | } else { |
||
928 | o(0x04c483); /* add $4, %esp */ |
||
929 | } |
||
930 | } |
||
931 | vtop->r = r; |
||
932 | } |
||
933 | |||
934 | /* convert from one floating point type to another */ |
||
935 | void gen_cvt_ftof(int t) |
||
936 | { |
||
937 | /* all we have to do on i386 is to put the float in a register */ |
||
938 | gv(RC_FLOAT); |
||
939 | } |
||
940 | |||
941 | /* computed goto support */ |
||
942 | void ggoto(void) |
||
943 | { |
||
944 | gcall_or_jmp(1); |
||
945 | vtop--; |
||
946 | } |
||
947 | |||
948 | /* bound check support functions */ |
||
949 | #ifdef CONFIG_TCC_BCHECK |
||
950 | |||
951 | /* generate a bounded pointer addition */ |
||
952 | void gen_bounded_ptr_add(void) |
||
953 | { |
||
954 | Sym *sym; |
||
955 | |||
956 | /* prepare fast i386 function call (args in eax and edx) */ |
||
957 | gv2(RC_EAX, RC_EDX); |
||
958 | /* save all temporary registers */ |
||
959 | vtop -= 2; |
||
960 | save_regs(0); |
||
961 | /* do a fast function call */ |
||
962 | sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0); |
||
963 | greloc(cur_text_section, sym, |
||
964 | ind + 1, R_386_PC32); |
||
965 | oad(0xe8, -4); |
||
966 | /* returned pointer is in eax */ |
||
967 | vtop++; |
||
968 | vtop->r = TREG_EAX | VT_BOUNDED; |
||
969 | /* address of bounding function call point */ |
||
970 | vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); |
||
971 | } |
||
972 | |||
973 | /* patch pointer addition in vtop so that pointer dereferencing is |
||
974 | also tested */ |
||
975 | void gen_bounded_ptr_deref(void) |
||
976 | { |
||
977 | int func; |
||
978 | int size, align; |
||
979 | Elf32_Rel *rel; |
||
980 | Sym *sym; |
||
981 | |||
982 | size = 0; |
||
983 | /* XXX: put that code in generic part of tcc */ |
||
984 | if (!is_float(vtop->type.t)) { |
||
985 | if (vtop->r & VT_LVAL_BYTE) |
||
986 | size = 1; |
||
987 | else if (vtop->r & VT_LVAL_SHORT) |
||
988 | size = 2; |
||
989 | } |
||
990 | if (!size) |
||
991 | size = type_size(&vtop->type, &align); |
||
992 | switch(size) { |
||
993 | case 1: func = TOK___bound_ptr_indir1; break; |
||
994 | case 2: func = TOK___bound_ptr_indir2; break; |
||
995 | case 4: func = TOK___bound_ptr_indir4; break; |
||
996 | case 8: func = TOK___bound_ptr_indir8; break; |
||
997 | case 12: func = TOK___bound_ptr_indir12; break; |
||
998 | case 16: func = TOK___bound_ptr_indir16; break; |
||
999 | default: |
||
1000 | error("unhandled size when derefencing bounded pointer"); |
||
1001 | func = 0; |
||
1002 | break; |
||
1003 | } |
||
1004 | |||
1005 | /* patch relocation */ |
||
1006 | /* XXX: find a better solution ? */ |
||
1007 | rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul); |
||
1008 | sym = external_global_sym(func, &func_old_type, 0); |
||
1009 | if (!sym->c) |
||
1010 | put_extern_sym(sym, NULL, 0, 0); |
||
1011 | rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); |
||
1012 | } |
||
1013 | #endif |
||
1014 | |||
1015 | /* end of X86 code generator */ |
||
1016 | /*************************************************************/><>=>><>=>><>><>><>>=>=>>=>>><>><> |
||
1017 |