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