Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
145 | halyavin | 1 | /* |
2 | * i386 specific functions for TCC assembler |
||
3 | * |
||
4 | * Copyright (c) 2001, 2002 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 | #define MAX_OPERANDS 3 |
||
22 | |||
23 | typedef struct ASMInstr { |
||
24 | uint16_t sym; |
||
25 | uint16_t opcode; |
||
26 | uint16_t instr_type; |
||
27 | #define OPC_JMP 0x01 /* jmp operand */ |
||
28 | #define OPC_B 0x02 /* only used zith OPC_WL */ |
||
29 | #define OPC_WL 0x04 /* accepts w, l or no suffix */ |
||
30 | #define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ |
||
31 | #define OPC_REG 0x08 /* register is added to opcode */ |
||
32 | #define OPC_MODRM 0x10 /* modrm encoding */ |
||
33 | #define OPC_FWAIT 0x20 /* add fwait opcode */ |
||
34 | #define OPC_TEST 0x40 /* test opcodes */ |
||
35 | #define OPC_SHIFT 0x80 /* shift opcodes */ |
||
36 | #define OPC_D16 0x0100 /* generate data16 prefix */ |
||
37 | #define OPC_ARITH 0x0200 /* arithmetic opcodes */ |
||
38 | #define OPC_SHORTJMP 0x0400 /* short jmp operand */ |
||
39 | #define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ |
||
40 | #define OPC_GROUP_SHIFT 13 |
||
41 | |||
42 | /* in order to compress the operand type, we use specific operands and |
||
43 | we or only with EA */ |
||
44 | #define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
45 | #define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
46 | #define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
47 | #define OPT_MMX 3 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
48 | #define OPT_SSE 4 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
49 | #define OPT_CR 5 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
50 | #define OPT_TR 6 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
51 | #define OPT_DB 7 /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
52 | #define OPT_SEG 8 |
||
53 | #define OPT_ST 9 |
||
54 | #define OPT_IM8 10 |
||
55 | #define OPT_IM8S 11 |
||
56 | #define OPT_IM16 12 |
||
57 | #define OPT_IM32 13 |
||
58 | #define OPT_EAX 14 /* %al, %ax or %eax register */ |
||
59 | #define OPT_ST0 15 /* %st(0) register */ |
||
60 | #define OPT_CL 16 /* %cl register */ |
||
61 | #define OPT_DX 17 /* %dx register */ |
||
62 | #define OPT_ADDR 18 /* OP_EA with only offset */ |
||
63 | #define OPT_INDIR 19 /* *(expr) */ |
||
64 | |||
65 | /* composite types */ |
||
66 | #define OPT_COMPOSITE_FIRST 20 |
||
67 | #define OPT_IM 20 /* IM8 | IM16 | IM32 */ |
||
68 | #define OPT_REG 21 /* REG8 | REG16 | REG32 */ |
||
69 | #define OPT_REGW 22 /* REG16 | REG32 */ |
||
70 | #define OPT_IMW 23 /* IM16 | IM32 */ |
||
71 | |||
72 | /* can be ored with any OPT_xxx */ |
||
73 | #define OPT_EA 0x80 |
||
74 | |||
75 | uint8_t nb_ops; |
||
76 | uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ |
||
77 | } ASMInstr; |
||
78 | |||
79 | typedef struct Operand { |
||
80 | uint32_t type; |
||
81 | #define OP_REG8 (1 << OPT_REG8) |
||
82 | #define OP_REG16 (1 << OPT_REG16) |
||
83 | #define OP_REG32 (1 << OPT_REG32) |
||
84 | #define OP_MMX (1 << OPT_MMX) |
||
85 | #define OP_SSE (1 << OPT_SSE) |
||
86 | #define OP_CR (1 << OPT_CR) |
||
87 | #define OP_TR (1 << OPT_TR) |
||
88 | #define OP_DB (1 << OPT_DB) |
||
89 | #define OP_SEG (1 << OPT_SEG) |
||
90 | #define OP_ST (1 << OPT_ST) |
||
91 | #define OP_IM8 (1 << OPT_IM8) |
||
92 | #define OP_IM8S (1 << OPT_IM8S) |
||
93 | #define OP_IM16 (1 << OPT_IM16) |
||
94 | #define OP_IM32 (1 << OPT_IM32) |
||
95 | #define OP_EAX (1 << OPT_EAX) |
||
96 | #define OP_ST0 (1 << OPT_ST0) |
||
97 | #define OP_CL (1 << OPT_CL) |
||
98 | #define OP_DX (1 << OPT_DX) |
||
99 | #define OP_ADDR (1 << OPT_ADDR) |
||
100 | #define OP_INDIR (1 << OPT_INDIR) |
||
101 | |||
102 | #define OP_EA 0x40000000 |
||
103 | #define OP_REG (OP_REG8 | OP_REG16 | OP_REG32) |
||
104 | #define OP_IM OP_IM32 |
||
105 | int8_t reg; /* register, -1 if none */ |
||
106 | int8_t reg2; /* second register, -1 if none */ |
||
107 | uint8_t shift; |
||
108 | ExprValue e; |
||
109 | } Operand; |
||
110 | |||
111 | static const uint8_t reg_to_size[5] = { |
||
112 | [OP_REG8] = 0, |
||
113 | [OP_REG16] = 1, |
||
114 | [OP_REG32] = 2, |
||
115 | }; |
||
116 | |||
117 | #define WORD_PREFIX_OPCODE 0x66 |
||
118 | |||
119 | #define NB_TEST_OPCODES 30 |
||
120 | |||
121 | static const uint8_t test_bits[NB_TEST_OPCODES] = { |
||
122 | 0x00, /* o */ |
||
123 | 0x01, /* no */ |
||
124 | 0x02, /* b */ |
||
125 | 0x02, /* c */ |
||
126 | 0x02, /* nae */ |
||
127 | 0x03, /* nb */ |
||
128 | 0x03, /* nc */ |
||
129 | 0x03, /* ae */ |
||
130 | 0x04, /* e */ |
||
131 | 0x04, /* z */ |
||
132 | 0x05, /* ne */ |
||
133 | 0x05, /* nz */ |
||
134 | 0x06, /* be */ |
||
135 | 0x06, /* na */ |
||
136 | 0x07, /* nbe */ |
||
137 | 0x07, /* a */ |
||
138 | 0x08, /* s */ |
||
139 | 0x09, /* ns */ |
||
140 | 0x0a, /* p */ |
||
141 | 0x0a, /* pe */ |
||
142 | 0x0b, /* np */ |
||
143 | 0x0b, /* po */ |
||
144 | 0x0c, /* l */ |
||
145 | 0x0c, /* nge */ |
||
146 | 0x0d, /* nl */ |
||
147 | 0x0d, /* ge */ |
||
148 | 0x0e, /* le */ |
||
149 | 0x0e, /* ng */ |
||
150 | 0x0f, /* nle */ |
||
151 | 0x0f, /* g */ |
||
152 | }; |
||
153 | |||
154 | static const ASMInstr asm_instrs[] = { |
||
155 | #define ALT(x) x |
||
156 | #define DEF_ASM_OP0(name, opcode) |
||
157 | #define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, |
||
158 | #define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, |
||
159 | #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, |
||
160 | #define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }}, |
||
161 | #include "i386-asm.h" |
||
162 | |||
163 | /* last operation */ |
||
164 | { 0, }, |
||
165 | }; |
||
166 | |||
167 | static const uint16_t op0_codes[] = { |
||
168 | #define ALT(x) |
||
169 | #define DEF_ASM_OP0(x, opcode) opcode, |
||
170 | #define DEF_ASM_OP0L(name, opcode, group, instr_type) |
||
171 | #define DEF_ASM_OP1(name, opcode, group, instr_type, op0) |
||
172 | #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) |
||
173 | #define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) |
||
174 | #include "i386-asm.h" |
||
175 | }; |
||
176 | |||
177 | static inline int get_reg_shift(TCCState *s1) |
||
178 | { |
||
179 | int shift, v; |
||
180 | |||
181 | v = asm_int_expr(s1); |
||
182 | switch(v) { |
||
183 | case 1: |
||
184 | shift = 0; |
||
185 | break; |
||
186 | case 2: |
||
187 | shift = 1; |
||
188 | break; |
||
189 | case 4: |
||
190 | shift = 2; |
||
191 | break; |
||
192 | case 8: |
||
193 | shift = 3; |
||
194 | break; |
||
195 | default: |
||
196 | expect("1, 2, 4 or 8 constant"); |
||
197 | shift = 0; |
||
198 | break; |
||
199 | } |
||
200 | return shift; |
||
201 | } |
||
202 | |||
203 | static int asm_parse_reg(void) |
||
204 | { |
||
205 | int reg; |
||
206 | if (tok != '%') |
||
207 | goto error_32; |
||
208 | next(); |
||
209 | if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
||
210 | reg = tok - TOK_ASM_eax; |
||
211 | next(); |
||
212 | return reg; |
||
213 | } else { |
||
214 | error_32: |
||
215 | expect("32 bit register"); |
||
216 | return 0; |
||
217 | } |
||
218 | } |
||
219 | |||
220 | static void parse_operand(TCCState *s1, Operand *op) |
||
221 | { |
||
222 | ExprValue e; |
||
223 | int reg, indir; |
||
224 | const char *p; |
||
225 | |||
226 | indir = 0; |
||
227 | if (tok == '*') { |
||
228 | next(); |
||
229 | indir = OP_INDIR; |
||
230 | } |
||
231 | |||
232 | if (tok == '%') { |
||
233 | next(); |
||
234 | if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { |
||
235 | reg = tok - TOK_ASM_al; |
||
236 | op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ |
||
237 | op->reg = reg & 7; |
||
238 | if ((op->type & OP_REG) && op->reg == TREG_EAX) |
||
239 | op->type |= OP_EAX; |
||
240 | else if (op->type == OP_REG8 && op->reg == TREG_ECX) |
||
241 | op->type |= OP_CL; |
||
242 | else if (op->type == OP_REG16 && op->reg == TREG_EDX) |
||
243 | op->type |= OP_DX; |
||
244 | } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { |
||
245 | op->type = OP_DB; |
||
246 | op->reg = tok - TOK_ASM_dr0; |
||
247 | } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { |
||
248 | op->type = OP_SEG; |
||
249 | op->reg = tok - TOK_ASM_es; |
||
250 | } else if (tok == TOK_ASM_st) { |
||
251 | op->type = OP_ST; |
||
252 | op->reg = 0; |
||
253 | next(); |
||
254 | if (tok == '(') { |
||
255 | next(); |
||
256 | if (tok != TOK_PPNUM) |
||
257 | goto reg_error; |
||
258 | p = tokc.cstr->data; |
||
259 | reg = p[0] - '0'; |
||
260 | if ((unsigned)reg >= 8 || p[1] != '\0') |
||
261 | goto reg_error; |
||
262 | op->reg = reg; |
||
263 | next(); |
||
264 | skip(')'); |
||
265 | } |
||
266 | if (op->reg == 0) |
||
267 | op->type |= OP_ST0; |
||
268 | goto no_skip; |
||
269 | } else { |
||
270 | reg_error: |
||
271 | error("unknown register"); |
||
272 | } |
||
273 | next(); |
||
274 | no_skip: ; |
||
275 | } else if (tok == '$') { |
||
276 | /* constant value */ |
||
277 | next(); |
||
278 | asm_expr(s1, &e); |
||
279 | op->type = OP_IM32; |
||
280 | op->e.v = e.v; |
||
281 | op->e.sym = e.sym; |
||
282 | if (!op->e.sym) { |
||
283 | if (op->e.v == (uint8_t)op->e.v) |
||
284 | op->type |= OP_IM8; |
||
285 | if (op->e.v == (int8_t)op->e.v) |
||
286 | op->type |= OP_IM8S; |
||
287 | if (op->e.v == (uint16_t)op->e.v) |
||
288 | op->type |= OP_IM16; |
||
289 | } |
||
290 | } else { |
||
291 | /* address(reg,reg2,shift) with all variants */ |
||
292 | op->type = OP_EA; |
||
293 | op->reg = -1; |
||
294 | op->reg2 = -1; |
||
295 | op->shift = 0; |
||
296 | if (tok != '(') { |
||
297 | asm_expr(s1, &e); |
||
298 | op->e.v = e.v; |
||
299 | op->e.sym = e.sym; |
||
300 | } else { |
||
301 | op->e.v = 0; |
||
302 | op->e.sym = NULL; |
||
303 | } |
||
304 | if (tok == '(') { |
||
305 | next(); |
||
306 | if (tok != ',') { |
||
307 | op->reg = asm_parse_reg(); |
||
308 | } |
||
309 | if (tok == ',') { |
||
310 | next(); |
||
311 | if (tok != ',') { |
||
312 | op->reg2 = asm_parse_reg(); |
||
313 | } |
||
314 | skip(','); |
||
315 | op->shift = get_reg_shift(s1); |
||
316 | } |
||
317 | skip(')'); |
||
318 | } |
||
319 | if (op->reg == -1 && op->reg2 == -1) |
||
320 | op->type |= OP_ADDR; |
||
321 | } |
||
322 | op->type |= indir; |
||
323 | } |
||
324 | |||
325 | /* XXX: unify with C code output ? */ |
||
326 | static void gen_expr32(ExprValue *pe) |
||
327 | { |
||
328 | if (pe->sym) |
||
329 | greloc(cur_text_section, pe->sym, ind, R_386_32); |
||
330 | gen_le32(pe->v); |
||
331 | } |
||
332 | |||
333 | /* XXX: unify with C code output ? */ |
||
334 | static void gen_disp32(ExprValue *pe) |
||
335 | { |
||
336 | Sym *sym; |
||
337 | sym = pe->sym; |
||
338 | if (sym) { |
||
339 | if (sym->r == cur_text_section->sh_num) { |
||
340 | /* same section: we can output an absolute value. Note |
||
341 | that the TCC compiler behaves differently here because |
||
342 | it always outputs a relocation to ease (future) code |
||
343 | elimination in the linker */ |
||
344 | gen_le32(pe->v + (long)sym->next - ind - 4); |
||
345 | } else { |
||
346 | greloc(cur_text_section, sym, ind, R_386_PC32); |
||
347 | gen_le32(pe->v - 4); |
||
348 | } |
||
349 | } else { |
||
350 | /* put an empty PC32 relocation */ |
||
351 | put_elf_reloc(symtab_section, cur_text_section, |
||
352 | ind, R_386_PC32, 0); |
||
353 | gen_le32(pe->v - 4); |
||
354 | } |
||
355 | } |
||
356 | |||
357 | |||
358 | static void gen_le16(int v) |
||
359 | { |
||
360 | g(v); |
||
361 | g(v >> 8); |
||
362 | } |
||
363 | |||
364 | /* generate the modrm operand */ |
||
365 | static inline void asm_modrm(int reg, Operand *op) |
||
366 | { |
||
367 | int mod, reg1, reg2, sib_reg1; |
||
368 | |||
369 | if (op->type & (OP_REG | OP_MMX | OP_SSE)) { |
||
370 | g(0xc0 + (reg << 3) + op->reg); |
||
371 | } else if (op->reg == -1 && op->reg2 == -1) { |
||
372 | /* displacement only */ |
||
373 | g(0x05 + (reg << 3)); |
||
374 | gen_expr32(&op->e); |
||
375 | } else { |
||
376 | sib_reg1 = op->reg; |
||
377 | /* fist compute displacement encoding */ |
||
378 | if (sib_reg1 == -1) { |
||
379 | sib_reg1 = 5; |
||
380 | mod = 0x00; |
||
381 | } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { |
||
382 | mod = 0x00; |
||
383 | } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { |
||
384 | mod = 0x40; |
||
385 | } else { |
||
386 | mod = 0x80; |
||
387 | } |
||
388 | /* compute if sib byte needed */ |
||
389 | reg1 = op->reg; |
||
390 | if (op->reg2 != -1) |
||
391 | reg1 = 4; |
||
392 | g(mod + (reg << 3) + reg1); |
||
393 | if (reg1 == 4) { |
||
394 | /* add sib byte */ |
||
395 | reg2 = op->reg2; |
||
396 | if (reg2 == -1) |
||
397 | reg2 = 4; /* indicate no index */ |
||
398 | g((op->shift << 6) + (reg2 << 3) + sib_reg1); |
||
399 | } |
||
400 | |||
401 | /* add offset */ |
||
402 | if (mod == 0x40) { |
||
403 | g(op->e.v); |
||
404 | } else if (mod == 0x80 || op->reg == -1) { |
||
405 | gen_expr32(&op->e); |
||
406 | } |
||
407 | } |
||
408 | } |
||
409 | |||
410 | static void asm_opcode(TCCState *s1, int opcode) |
||
411 | { |
||
412 | const ASMInstr *pa; |
||
413 | int i, modrm_index, reg, v, op1, is_short_jmp; |
||
414 | int nb_ops, s, ss; |
||
415 | Operand ops[MAX_OPERANDS], *pop; |
||
416 | int op_type[3]; /* decoded op type */ |
||
417 | |||
418 | /* get operands */ |
||
419 | pop = ops; |
||
420 | nb_ops = 0; |
||
421 | for(;;) { |
||
422 | if (tok == ';' || tok == TOK_LINEFEED) |
||
423 | break; |
||
424 | if (nb_ops >= MAX_OPERANDS) { |
||
425 | error("incorrect number of operands"); |
||
426 | } |
||
427 | parse_operand(s1, pop); |
||
428 | pop++; |
||
429 | nb_ops++; |
||
430 | if (tok != ',') |
||
431 | break; |
||
432 | next(); |
||
433 | } |
||
434 | |||
435 | is_short_jmp = 0; |
||
436 | s = 0; /* avoid warning */ |
||
437 | |||
438 | /* optimize matching by using a lookup table (no hashing is needed |
||
439 | !) */ |
||
440 | for(pa = asm_instrs; pa->sym != 0; pa++) { |
||
441 | s = 0; |
||
442 | if (pa->instr_type & OPC_FARITH) { |
||
443 | v = opcode - pa->sym; |
||
444 | if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) |
||
445 | continue; |
||
446 | } else if (pa->instr_type & OPC_ARITH) { |
||
447 | if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4)) |
||
448 | continue; |
||
449 | goto compute_size; |
||
450 | } else if (pa->instr_type & OPC_SHIFT) { |
||
451 | if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4)) |
||
452 | continue; |
||
453 | goto compute_size; |
||
454 | } else if (pa->instr_type & OPC_TEST) { |
||
455 | if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) |
||
456 | continue; |
||
457 | } else if (pa->instr_type & OPC_B) { |
||
458 | if (!(opcode >= pa->sym && opcode <= pa->sym + 3)) |
||
459 | continue; |
||
460 | compute_size: |
||
461 | s = (opcode - pa->sym) & 3; |
||
462 | } else if (pa->instr_type & OPC_WL) { |
||
463 | if (!(opcode >= pa->sym && opcode <= pa->sym + 2)) |
||
464 | continue; |
||
465 | s = opcode - pa->sym + 1; |
||
466 | } else { |
||
467 | if (pa->sym != opcode) |
||
468 | continue; |
||
469 | } |
||
470 | if (pa->nb_ops != nb_ops) |
||
471 | continue; |
||
472 | /* now decode and check each operand */ |
||
473 | for(i = 0; i < nb_ops; i++) { |
||
474 | int op1, op2; |
||
475 | op1 = pa->op_type[i]; |
||
476 | op2 = op1 & 0x1f; |
||
477 | switch(op2) { |
||
478 | case OPT_IM: |
||
479 | v = OP_IM8 | OP_IM16 | OP_IM32; |
||
480 | break; |
||
481 | case OPT_REG: |
||
482 | v = OP_REG8 | OP_REG16 | OP_REG32; |
||
483 | break; |
||
484 | case OPT_REGW: |
||
485 | v = OP_REG16 | OP_REG32; |
||
486 | break; |
||
487 | case OPT_IMW: |
||
488 | v = OP_IM16 | OP_IM32; |
||
489 | break; |
||
490 | default: |
||
491 | v = 1 << op2; |
||
492 | break; |
||
493 | } |
||
494 | if (op1 & OPT_EA) |
||
495 | v |= OP_EA; |
||
496 | op_type[i] = v; |
||
497 | if ((ops[i].type & v) == 0) |
||
498 | goto next; |
||
499 | } |
||
500 | /* all is matching ! */ |
||
501 | break; |
||
502 | next: ; |
||
503 | } |
||
504 | if (pa->sym == 0) { |
||
505 | if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) { |
||
506 | int b; |
||
507 | b = op0_codes[opcode - TOK_ASM_pusha]; |
||
508 | if (b & 0xff00) |
||
509 | g(b >> 8); |
||
510 | g(b); |
||
511 | return; |
||
512 | } else { |
||
513 | error("unknown opcode '%s'", |
||
514 | get_tok_str(opcode, NULL)); |
||
515 | } |
||
516 | } |
||
517 | /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ |
||
518 | if (s == 3) { |
||
519 | for(i = 0; s == 3 && i < nb_ops; i++) { |
||
520 | if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) |
||
521 | s = reg_to_size[ops[i].type & OP_REG]; |
||
522 | } |
||
523 | if (s == 3) { |
||
524 | if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && |
||
525 | (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32))) |
||
526 | s = 2; |
||
527 | else |
||
528 | error("cannot infer opcode suffix"); |
||
529 | } |
||
530 | } |
||
531 | |||
532 | /* generate data16 prefix if needed */ |
||
533 | ss = s; |
||
534 | if (s == 1 || (pa->instr_type & OPC_D16)) |
||
535 | g(WORD_PREFIX_OPCODE); |
||
536 | else if (s == 2) |
||
537 | s = 1; |
||
538 | /* now generates the operation */ |
||
539 | if (pa->instr_type & OPC_FWAIT) |
||
540 | g(0x9b); |
||
541 | |||
542 | v = pa->opcode; |
||
543 | if (v == 0x69 || v == 0x69) { |
||
544 | /* kludge for imul $im, %reg */ |
||
545 | nb_ops = 3; |
||
546 | ops[2] = ops[1]; |
||
547 | } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { |
||
548 | v--; /* int $3 case */ |
||
549 | nb_ops = 0; |
||
550 | } else if ((v == 0x06 || v == 0x07)) { |
||
551 | if (ops[0].reg >= 4) { |
||
552 | /* push/pop %fs or %gs */ |
||
553 | v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); |
||
554 | } else { |
||
555 | v += ops[0].reg << 3; |
||
556 | } |
||
557 | nb_ops = 0; |
||
558 | } else if (v <= 0x05) { |
||
559 | /* arith case */ |
||
560 | v += ((opcode - TOK_ASM_addb) >> 2) << 3; |
||
561 | } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { |
||
562 | /* fpu arith case */ |
||
563 | v += ((opcode - pa->sym) / 6) << 3; |
||
564 | } |
||
565 | if (pa->instr_type & OPC_REG) { |
||
566 | for(i = 0; i < nb_ops; i++) { |
||
567 | if (op_type[i] & (OP_REG | OP_ST)) { |
||
568 | v += ops[i].reg; |
||
569 | break; |
||
570 | } |
||
571 | } |
||
572 | /* mov $im, %reg case */ |
||
573 | if (pa->opcode == 0xb0 && s >= 1) |
||
574 | v += 7; |
||
575 | } |
||
576 | if (pa->instr_type & OPC_B) |
||
577 | v += s; |
||
578 | if (pa->instr_type & OPC_TEST) |
||
579 | v += test_bits[opcode - pa->sym]; |
||
580 | if (pa->instr_type & OPC_SHORTJMP) { |
||
581 | Sym *sym; |
||
582 | int jmp_disp; |
||
583 | |||
584 | /* see if we can really generate the jump with a byte offset */ |
||
585 | sym = ops[0].e.sym; |
||
586 | if (!sym) |
||
587 | goto no_short_jump; |
||
588 | if (sym->r != cur_text_section->sh_num) |
||
589 | goto no_short_jump; |
||
590 | jmp_disp = ops[0].e.v + (long)sym->next - ind - 2; |
||
591 | if (jmp_disp == (int8_t)jmp_disp) { |
||
592 | /* OK to generate jump */ |
||
593 | is_short_jmp = 1; |
||
594 | ops[0].e.v = jmp_disp; |
||
595 | } else { |
||
596 | no_short_jump: |
||
597 | if (pa->instr_type & OPC_JMP) { |
||
598 | /* long jump will be allowed. need to modify the |
||
599 | opcode slightly */ |
||
600 | if (v == 0xeb) |
||
601 | v = 0xe9; |
||
602 | else |
||
603 | v += 0x0f10; |
||
604 | } else { |
||
605 | error("invalid displacement"); |
||
606 | } |
||
607 | } |
||
608 | } |
||
609 | op1 = v >> 8; |
||
610 | if (op1) |
||
611 | g(op1); |
||
612 | g(v); |
||
613 | |||
614 | /* search which operand will used for modrm */ |
||
615 | modrm_index = 0; |
||
616 | if (pa->instr_type & OPC_SHIFT) { |
||
617 | reg = (opcode - pa->sym) >> 2; |
||
618 | if (reg == 6) |
||
619 | reg = 7; |
||
620 | } else if (pa->instr_type & OPC_ARITH) { |
||
621 | reg = (opcode - pa->sym) >> 2; |
||
622 | } else if (pa->instr_type & OPC_FARITH) { |
||
623 | reg = (opcode - pa->sym) / 6; |
||
624 | } else { |
||
625 | reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; |
||
626 | } |
||
627 | if (pa->instr_type & OPC_MODRM) { |
||
628 | /* first look for an ea operand */ |
||
629 | for(i = 0;i < nb_ops; i++) { |
||
630 | if (op_type[i] & OP_EA) |
||
631 | goto modrm_found; |
||
632 | } |
||
633 | /* then if not found, a register or indirection (shift instructions) */ |
||
634 | for(i = 0;i < nb_ops; i++) { |
||
635 | if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) |
||
636 | goto modrm_found; |
||
637 | } |
||
638 | #ifdef ASM_DEBUG |
||
639 | error("bad op table"); |
||
640 | #endif |
||
641 | modrm_found: |
||
642 | modrm_index = i; |
||
643 | /* if a register is used in another operand then it is |
||
644 | used instead of group */ |
||
645 | for(i = 0;i < nb_ops; i++) { |
||
646 | v = op_type[i]; |
||
647 | if (i != modrm_index && |
||
648 | (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { |
||
649 | reg = ops[i].reg; |
||
650 | break; |
||
651 | } |
||
652 | } |
||
653 | |||
654 | asm_modrm(reg, &ops[modrm_index]); |
||
655 | } |
||
656 | |||
657 | /* emit constants */ |
||
658 | if (pa->opcode == 0x9a || pa->opcode == 0xea) { |
||
659 | /* ljmp or lcall kludge */ |
||
660 | gen_expr32(&ops[1].e); |
||
661 | if (ops[0].e.sym) |
||
662 | error("cannot relocate"); |
||
663 | gen_le16(ops[0].e.v); |
||
664 | } else { |
||
665 | for(i = 0;i < nb_ops; i++) { |
||
666 | v = op_type[i]; |
||
667 | if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) { |
||
668 | /* if multiple sizes are given it means we must look |
||
669 | at the op size */ |
||
670 | if (v == (OP_IM8 | OP_IM16 | OP_IM32) || |
||
671 | v == (OP_IM16 | OP_IM32)) { |
||
672 | if (ss == 0) |
||
673 | v = OP_IM8; |
||
674 | else if (ss == 1) |
||
675 | v = OP_IM16; |
||
676 | else |
||
677 | v = OP_IM32; |
||
678 | } |
||
679 | if (v & (OP_IM8 | OP_IM8S)) { |
||
680 | if (ops[i].e.sym) |
||
681 | goto error_relocate; |
||
682 | g(ops[i].e.v); |
||
683 | } else if (v & OP_IM16) { |
||
684 | if (ops[i].e.sym) { |
||
685 | error_relocate: |
||
686 | error("cannot relocate"); |
||
687 | } |
||
688 | gen_le16(ops[i].e.v); |
||
689 | } else { |
||
690 | if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { |
||
691 | if (is_short_jmp) |
||
692 | g(ops[i].e.v); |
||
693 | else |
||
694 | gen_disp32(&ops[i].e); |
||
695 | } else { |
||
696 | gen_expr32(&ops[i].e); |
||
697 | } |
||
698 | } |
||
699 | } |
||
700 | } |
||
701 | } |
||
702 | } |
||
703 | |||
704 | #define NB_SAVED_REGS 3 |
||
705 | #define NB_ASM_REGS 8 |
||
706 | |||
707 | /* return the constraint priority (we allocate first the lowest |
||
708 | numbered constraints) */ |
||
709 | static inline int constraint_priority(const char *str) |
||
710 | { |
||
711 | int priority, c, pr; |
||
712 | |||
713 | /* we take the lowest priority */ |
||
714 | priority = 0; |
||
715 | for(;;) { |
||
716 | c = *str; |
||
717 | if (c == '\0') |
||
718 | break; |
||
719 | str++; |
||
720 | switch(c) { |
||
721 | case 'A': |
||
722 | pr = 0; |
||
723 | break; |
||
724 | case 'a': |
||
725 | case 'b': |
||
726 | case 'c': |
||
727 | case 'd': |
||
728 | case 'S': |
||
729 | case 'D': |
||
730 | pr = 1; |
||
731 | break; |
||
732 | case 'q': |
||
733 | pr = 2; |
||
734 | break; |
||
735 | case 'r': |
||
736 | pr = 3; |
||
737 | break; |
||
738 | case 'N': |
||
739 | case 'M': |
||
740 | case 'I': |
||
741 | case 'i': |
||
742 | case 'm': |
||
743 | case 'g': |
||
744 | pr = 4; |
||
745 | break; |
||
746 | default: |
||
747 | error("unknown constraint '%c'", c); |
||
748 | pr = 0; |
||
749 | } |
||
750 | if (pr > priority) |
||
751 | priority = pr; |
||
752 | } |
||
753 | return priority; |
||
754 | } |
||
755 | |||
756 | static const char *skip_constraint_modifiers(const char *p) |
||
757 | { |
||
758 | while (*p == '=' || *p == '&' || *p == '+' || *p == '%') |
||
759 | p++; |
||
760 | return p; |
||
761 | } |
||
762 | |||
763 | #define REG_OUT_MASK 0x01 |
||
764 | #define REG_IN_MASK 0x02 |
||
765 | |||
766 | #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) |
||
767 | |||
768 | static void asm_compute_constraints(ASMOperand *operands, |
||
769 | int nb_operands, int nb_outputs, |
||
770 | const uint8_t *clobber_regs, |
||
771 | int *pout_reg) |
||
772 | { |
||
773 | ASMOperand *op; |
||
774 | int sorted_op[MAX_ASM_OPERANDS]; |
||
775 | int i, j, k, p1, p2, tmp, reg, c, reg_mask; |
||
776 | const char *str; |
||
777 | uint8_t regs_allocated[NB_ASM_REGS]; |
||
778 | |||
779 | /* init fields */ |
||
780 | for(i=0;i |
||
781 | op = &operands[i]; |
||
782 | op->input_index = -1; |
||
783 | op->ref_index = -1; |
||
784 | op->reg = -1; |
||
785 | op->is_memory = 0; |
||
786 | op->is_rw = 0; |
||
787 | } |
||
788 | /* compute constraint priority and evaluate references to output |
||
789 | constraints if input constraints */ |
||
790 | for(i=0;i |
||
791 | op = &operands[i]; |
||
792 | str = op->constraint; |
||
793 | str = skip_constraint_modifiers(str); |
||
794 | if (isnum(*str) || *str == '[') { |
||
795 | /* this is a reference to another constraint */ |
||
796 | k = find_constraint(operands, nb_operands, str, NULL); |
||
797 | if ((unsigned)k >= i || i < nb_outputs) |
||
798 | error("invalid reference in constraint %d ('%s')", |
||
799 | i, str); |
||
800 | op->ref_index = k; |
||
801 | if (operands[k].input_index >= 0) |
||
802 | error("cannot reference twice the same operand"); |
||
803 | operands[k].input_index = i; |
||
804 | op->priority = 5; |
||
805 | } else { |
||
806 | op->priority = constraint_priority(str); |
||
807 | } |
||
808 | } |
||
809 | |||
810 | /* sort operands according to their priority */ |
||
811 | for(i=0;i |
||
812 | sorted_op[i] = i; |
||
813 | for(i=0;i |
||
814 | for(j=i+1;j |
||
815 | p1 = operands[sorted_op[i]].priority; |
||
816 | p2 = operands[sorted_op[j]].priority; |
||
817 | if (p2 < p1) { |
||
818 | tmp = sorted_op[i]; |
||
819 | sorted_op[i] = sorted_op[j]; |
||
820 | sorted_op[j] = tmp; |
||
821 | } |
||
822 | } |
||
823 | } |
||
824 | |||
825 | for(i = 0;i < NB_ASM_REGS; i++) { |
||
826 | if (clobber_regs[i]) |
||
827 | regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; |
||
828 | else |
||
829 | regs_allocated[i] = 0; |
||
830 | } |
||
831 | /* esp cannot be used */ |
||
832 | regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; |
||
833 | /* ebp cannot be used yet */ |
||
834 | regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; |
||
835 | |||
836 | /* allocate registers and generate corresponding asm moves */ |
||
837 | for(i=0;i |
||
838 | j = sorted_op[i]; |
||
839 | op = &operands[j]; |
||
840 | str = op->constraint; |
||
841 | /* no need to allocate references */ |
||
842 | if (op->ref_index >= 0) |
||
843 | continue; |
||
844 | /* select if register is used for output, input or both */ |
||
845 | if (op->input_index >= 0) { |
||
846 | reg_mask = REG_IN_MASK | REG_OUT_MASK; |
||
847 | } else if (j < nb_outputs) { |
||
848 | reg_mask = REG_OUT_MASK; |
||
849 | } else { |
||
850 | reg_mask = REG_IN_MASK; |
||
851 | } |
||
852 | try_next: |
||
853 | c = *str++; |
||
854 | switch(c) { |
||
855 | case '=': |
||
856 | goto try_next; |
||
857 | case '+': |
||
858 | op->is_rw = 1; |
||
859 | /* FALL THRU */ |
||
860 | case '&': |
||
861 | if (j >= nb_outputs) |
||
862 | error("'%c' modifier can only be applied to outputs", c); |
||
863 | reg_mask = REG_IN_MASK | REG_OUT_MASK; |
||
864 | goto try_next; |
||
865 | case 'A': |
||
866 | /* allocate both eax and edx */ |
||
867 | if (is_reg_allocated(TREG_EAX) || |
||
868 | is_reg_allocated(TREG_EDX)) |
||
869 | goto try_next; |
||
870 | op->is_llong = 1; |
||
871 | op->reg = TREG_EAX; |
||
872 | regs_allocated[TREG_EAX] |= reg_mask; |
||
873 | regs_allocated[TREG_EDX] |= reg_mask; |
||
874 | break; |
||
875 | case 'a': |
||
876 | reg = TREG_EAX; |
||
877 | goto alloc_reg; |
||
878 | case 'b': |
||
879 | reg = 3; |
||
880 | goto alloc_reg; |
||
881 | case 'c': |
||
882 | reg = TREG_ECX; |
||
883 | goto alloc_reg; |
||
884 | case 'd': |
||
885 | reg = TREG_EDX; |
||
886 | goto alloc_reg; |
||
887 | case 'S': |
||
888 | reg = 6; |
||
889 | goto alloc_reg; |
||
890 | case 'D': |
||
891 | reg = 7; |
||
892 | alloc_reg: |
||
893 | if (is_reg_allocated(reg)) |
||
894 | goto try_next; |
||
895 | goto reg_found; |
||
896 | case 'q': |
||
897 | /* eax, ebx, ecx or edx */ |
||
898 | for(reg = 0; reg < 4; reg++) { |
||
899 | if (!is_reg_allocated(reg)) |
||
900 | goto reg_found; |
||
901 | } |
||
902 | goto try_next; |
||
903 | case 'r': |
||
904 | /* any general register */ |
||
905 | for(reg = 0; reg < 8; reg++) { |
||
906 | if (!is_reg_allocated(reg)) |
||
907 | goto reg_found; |
||
908 | } |
||
909 | goto try_next; |
||
910 | reg_found: |
||
911 | /* now we can reload in the register */ |
||
912 | op->is_llong = 0; |
||
913 | op->reg = reg; |
||
914 | regs_allocated[reg] |= reg_mask; |
||
915 | break; |
||
916 | case 'i': |
||
917 | if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) |
||
918 | goto try_next; |
||
919 | break; |
||
920 | case 'I': |
||
921 | case 'N': |
||
922 | case 'M': |
||
923 | if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) |
||
924 | goto try_next; |
||
925 | break; |
||
926 | case 'm': |
||
927 | case 'g': |
||
928 | /* nothing special to do because the operand is already in |
||
929 | memory, except if the pointer itself is stored in a |
||
930 | memory variable (VT_LLOCAL case) */ |
||
931 | /* XXX: fix constant case */ |
||
932 | /* if it is a reference to a memory zone, it must lie |
||
933 | in a register, so we reserve the register in the |
||
934 | input registers and a load will be generated |
||
935 | later */ |
||
936 | if (j < nb_outputs || c == 'm') { |
||
937 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
||
938 | /* any general register */ |
||
939 | for(reg = 0; reg < 8; reg++) { |
||
940 | if (!(regs_allocated[reg] & REG_IN_MASK)) |
||
941 | goto reg_found1; |
||
942 | } |
||
943 | goto try_next; |
||
944 | reg_found1: |
||
945 | /* now we can reload in the register */ |
||
946 | regs_allocated[reg] |= REG_IN_MASK; |
||
947 | op->reg = reg; |
||
948 | op->is_memory = 1; |
||
949 | } |
||
950 | } |
||
951 | break; |
||
952 | default: |
||
953 | error("asm constraint %d ('%s') could not be satisfied", |
||
954 | j, op->constraint); |
||
955 | break; |
||
956 | } |
||
957 | /* if a reference is present for that operand, we assign it too */ |
||
958 | if (op->input_index >= 0) { |
||
959 | operands[op->input_index].reg = op->reg; |
||
960 | operands[op->input_index].is_llong = op->is_llong; |
||
961 | } |
||
962 | } |
||
963 | |||
964 | /* compute out_reg. It is used to store outputs registers to memory |
||
965 | locations references by pointers (VT_LLOCAL case) */ |
||
966 | *pout_reg = -1; |
||
967 | for(i=0;i |
||
968 | op = &operands[i]; |
||
969 | if (op->reg >= 0 && |
||
970 | (op->vt->r & VT_VALMASK) == VT_LLOCAL && |
||
971 | !op->is_memory) { |
||
972 | for(reg = 0; reg < 8; reg++) { |
||
973 | if (!(regs_allocated[reg] & REG_OUT_MASK)) |
||
974 | goto reg_found2; |
||
975 | } |
||
976 | error("could not find free output register for reloading"); |
||
977 | reg_found2: |
||
978 | *pout_reg = reg; |
||
979 | break; |
||
980 | } |
||
981 | } |
||
982 | |||
983 | /* print sorted constraints */ |
||
984 | #ifdef ASM_DEBUG |
||
985 | for(i=0;i |
||
986 | j = sorted_op[i]; |
||
987 | op = &operands[j]; |
||
988 | printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", |
||
989 | j, |
||
990 | op->id ? get_tok_str(op->id, NULL) : "", |
||
991 | op->constraint, |
||
992 | op->vt->r, |
||
993 | op->reg); |
||
994 | } |
||
995 | if (*pout_reg >= 0) |
||
996 | printf("out_reg=%d\n", *pout_reg); |
||
997 | #endif |
||
998 | } |
||
999 | |||
1000 | static void subst_asm_operand(CString *add_str, |
||
1001 | SValue *sv, int modifier) |
||
1002 | { |
||
1003 | int r, reg, size, val; |
||
1004 | char buf[64]; |
||
1005 | |||
1006 | r = sv->r; |
||
1007 | if ((r & VT_VALMASK) == VT_CONST) { |
||
1008 | if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') |
||
1009 | cstr_ccat(add_str, '$'); |
||
1010 | if (r & VT_SYM) { |
||
1011 | cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); |
||
1012 | if (sv->c.i != 0) { |
||
1013 | cstr_ccat(add_str, '+'); |
||
1014 | } else { |
||
1015 | return; |
||
1016 | } |
||
1017 | } |
||
1018 | val = sv->c.i; |
||
1019 | if (modifier == 'n') |
||
1020 | val = -val; |
||
1021 | snprintf(buf, sizeof(buf), "%d", sv->c.i); |
||
1022 | cstr_cat(add_str, buf); |
||
1023 | } else if ((r & VT_VALMASK) == VT_LOCAL) { |
||
1024 | snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); |
||
1025 | cstr_cat(add_str, buf); |
||
1026 | } else if (r & VT_LVAL) { |
||
1027 | reg = r & VT_VALMASK; |
||
1028 | if (reg >= VT_CONST) |
||
1029 | error("internal compiler error"); |
||
1030 | snprintf(buf, sizeof(buf), "(%%%s)", |
||
1031 | get_tok_str(TOK_ASM_eax + reg, NULL)); |
||
1032 | cstr_cat(add_str, buf); |
||
1033 | } else { |
||
1034 | /* register case */ |
||
1035 | reg = r & VT_VALMASK; |
||
1036 | if (reg >= VT_CONST) |
||
1037 | error("internal compiler error"); |
||
1038 | |||
1039 | /* choose register operand size */ |
||
1040 | if ((sv->type.t & VT_BTYPE) == VT_BYTE) |
||
1041 | size = 1; |
||
1042 | else if ((sv->type.t & VT_BTYPE) == VT_SHORT) |
||
1043 | size = 2; |
||
1044 | else |
||
1045 | size = 4; |
||
1046 | if (size == 1 && reg >= 4) |
||
1047 | size = 4; |
||
1048 | |||
1049 | if (modifier == 'b') { |
||
1050 | if (reg >= 4) |
||
1051 | error("cannot use byte register"); |
||
1052 | size = 1; |
||
1053 | } else if (modifier == 'h') { |
||
1054 | if (reg >= 4) |
||
1055 | error("cannot use byte register"); |
||
1056 | size = -1; |
||
1057 | } else if (modifier == 'w') { |
||
1058 | size = 2; |
||
1059 | } |
||
1060 | |||
1061 | switch(size) { |
||
1062 | case -1: |
||
1063 | reg = TOK_ASM_ah + reg; |
||
1064 | break; |
||
1065 | case 1: |
||
1066 | reg = TOK_ASM_al + reg; |
||
1067 | break; |
||
1068 | case 2: |
||
1069 | reg = TOK_ASM_ax + reg; |
||
1070 | break; |
||
1071 | default: |
||
1072 | reg = TOK_ASM_eax + reg; |
||
1073 | break; |
||
1074 | } |
||
1075 | snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); |
||
1076 | cstr_cat(add_str, buf); |
||
1077 | } |
||
1078 | } |
||
1079 | |||
1080 | /* generate prolog and epilog code for asm statment */ |
||
1081 | static void asm_gen_code(ASMOperand *operands, int nb_operands, |
||
1082 | int nb_outputs, int is_output, |
||
1083 | uint8_t *clobber_regs, |
||
1084 | int out_reg) |
||
1085 | { |
||
1086 | uint8_t regs_allocated[NB_ASM_REGS]; |
||
1087 | ASMOperand *op; |
||
1088 | int i, reg; |
||
1089 | static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; |
||
1090 | |||
1091 | /* mark all used registers */ |
||
1092 | memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); |
||
1093 | for(i = 0; i < nb_operands;i++) { |
||
1094 | op = &operands[i]; |
||
1095 | if (op->reg >= 0) |
||
1096 | regs_allocated[op->reg] = 1; |
||
1097 | } |
||
1098 | if (!is_output) { |
||
1099 | /* generate reg save code */ |
||
1100 | for(i = 0; i < NB_SAVED_REGS; i++) { |
||
1101 | reg = reg_saved[i]; |
||
1102 | if (regs_allocated[reg]) |
||
1103 | g(0x50 + reg); |
||
1104 | } |
||
1105 | |||
1106 | /* generate load code */ |
||
1107 | for(i = 0; i < nb_operands; i++) { |
||
1108 | op = &operands[i]; |
||
1109 | if (op->reg >= 0) { |
||
1110 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && |
||
1111 | op->is_memory) { |
||
1112 | /* memory reference case (for both input and |
||
1113 | output cases) */ |
||
1114 | SValue sv; |
||
1115 | sv = *op->vt; |
||
1116 | sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
||
1117 | load(op->reg, &sv); |
||
1118 | } else if (i >= nb_outputs || op->is_rw) { |
||
1119 | /* load value in register */ |
||
1120 | load(op->reg, op->vt); |
||
1121 | if (op->is_llong) { |
||
1122 | SValue sv; |
||
1123 | sv = *op->vt; |
||
1124 | sv.c.ul += 4; |
||
1125 | load(TREG_EDX, &sv); |
||
1126 | } |
||
1127 | } |
||
1128 | } |
||
1129 | } |
||
1130 | } else { |
||
1131 | /* generate save code */ |
||
1132 | for(i = 0 ; i < nb_outputs; i++) { |
||
1133 | op = &operands[i]; |
||
1134 | if (op->reg >= 0) { |
||
1135 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
||
1136 | if (!op->is_memory) { |
||
1137 | SValue sv; |
||
1138 | sv = *op->vt; |
||
1139 | sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
||
1140 | load(out_reg, &sv); |
||
1141 | |||
1142 | sv.r = (sv.r & ~VT_VALMASK) | out_reg; |
||
1143 | store(op->reg, &sv); |
||
1144 | } |
||
1145 | } else { |
||
1146 | store(op->reg, op->vt); |
||
1147 | if (op->is_llong) { |
||
1148 | SValue sv; |
||
1149 | sv = *op->vt; |
||
1150 | sv.c.ul += 4; |
||
1151 | store(TREG_EDX, &sv); |
||
1152 | } |
||
1153 | } |
||
1154 | } |
||
1155 | } |
||
1156 | /* generate reg restore code */ |
||
1157 | for(i = NB_SAVED_REGS - 1; i >= 0; i--) { |
||
1158 | reg = reg_saved[i]; |
||
1159 | if (regs_allocated[reg]) |
||
1160 | g(0x58 + reg); |
||
1161 | } |
||
1162 | } |
||
1163 | } |
||
1164 | |||
1165 | static void asm_clobber(uint8_t *clobber_regs, const char *str) |
||
1166 | { |
||
1167 | int reg; |
||
1168 | TokenSym *ts; |
||
1169 | |||
1170 | if (!strcmp(str, "memory") || |
||
1171 | !strcmp(str, "cc")) |
||
1172 | return; |
||
1173 | ts = tok_alloc(str, strlen(str)); |
||
1174 | reg = ts->tok; |
||
1175 | if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { |
||
1176 | reg -= TOK_ASM_eax; |
||
1177 | } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { |
||
1178 | reg -= TOK_ASM_ax; |
||
1179 | } else { |
||
1180 | error("invalid clobber register '%s'", str); |
||
1181 | } |
||
1182 | clobber_regs[reg] = 1; |
||
1183 | }=>=>>>>>>>>>>>>> |