Rev 647 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
145 | halyavin | 1 | /* |
2 | * i386 specific functions for TCC assembler |
||
6429 | siemargl | 3 | * |
145 | halyavin | 4 | * Copyright (c) 2001, 2002 Fabrice Bellard |
6429 | siemargl | 5 | * Copyright (c) 2009 Frédéric Feret (x86_64 support) |
145 | halyavin | 6 | * |
7 | * This library is free software; you can redistribute it and/or |
||
8 | * modify it under the terms of the GNU Lesser General Public |
||
9 | * License as published by the Free Software Foundation; either |
||
10 | * version 2 of the License, or (at your option) any later version. |
||
11 | * |
||
12 | * This library is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
15 | * Lesser General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU Lesser General Public |
||
18 | * License along with this library; if not, write to the Free Software |
||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
20 | */ |
||
21 | |||
6429 | siemargl | 22 | #include "tcc.h" |
23 | |||
24 | /* #define NB_ASM_REGS 8 */ |
||
145 | halyavin | 25 | #define MAX_OPERANDS 3 |
6429 | siemargl | 26 | #define NB_SAVED_REGS 3 |
145 | halyavin | 27 | |
6429 | siemargl | 28 | #define TOK_ASM_first TOK_ASM_clc |
29 | #define TOK_ASM_last TOK_ASM_emms |
||
30 | #define TOK_ASM_alllast TOK_ASM_pxor |
||
31 | |||
32 | #define OPC_JMP 0x01 /* jmp operand */ |
||
33 | #define OPC_B 0x02 /* only used with OPC_WL */ |
||
34 | #define OPC_WL 0x04 /* accepts w, l or no suffix */ |
||
35 | #define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ |
||
36 | #define OPC_REG 0x08 /* register is added to opcode */ |
||
37 | #define OPC_MODRM 0x10 /* modrm encoding */ |
||
38 | #define OPC_FWAIT 0x20 /* add fwait opcode */ |
||
39 | #define OPC_TEST 0x40 /* test opcodes */ |
||
40 | #define OPC_SHIFT 0x80 /* shift opcodes */ |
||
145 | halyavin | 41 | #define OPC_D16 0x0100 /* generate data16 prefix */ |
42 | #define OPC_ARITH 0x0200 /* arithmetic opcodes */ |
||
43 | #define OPC_SHORTJMP 0x0400 /* short jmp operand */ |
||
44 | #define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ |
||
6429 | siemargl | 45 | #ifdef TCC_TARGET_X86_64 |
46 | # define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */ |
||
47 | # define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */ |
||
48 | # define OPC_WLX OPC_WLQ |
||
49 | #else |
||
50 | # define OPC_WLX OPC_WL |
||
51 | #endif |
||
52 | |||
145 | halyavin | 53 | #define OPC_GROUP_SHIFT 13 |
54 | |||
55 | /* in order to compress the operand type, we use specific operands and |
||
6429 | siemargl | 56 | we or only with EA */ |
57 | enum { |
||
58 | OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
59 | OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
60 | OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
61 | #ifdef TCC_TARGET_X86_64 |
||
62 | OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
63 | #endif |
||
64 | OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
65 | OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
66 | OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
67 | OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
68 | OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ |
||
69 | OPT_SEG, |
||
70 | OPT_ST, |
||
71 | OPT_IM8, |
||
72 | OPT_IM8S, |
||
73 | OPT_IM16, |
||
74 | OPT_IM32, |
||
75 | #ifdef TCC_TARGET_X86_64 |
||
76 | OPT_IM64, |
||
77 | #endif |
||
78 | OPT_EAX, /* %al, %ax, %eax or %rax register */ |
||
79 | OPT_ST0, /* %st(0) register */ |
||
80 | OPT_CL, /* %cl register */ |
||
81 | OPT_DX, /* %dx register */ |
||
82 | OPT_ADDR, /* OP_EA with only offset */ |
||
83 | OPT_INDIR, /* *(expr) */ |
||
84 | /* composite types */ |
||
85 | OPT_COMPOSITE_FIRST, |
||
86 | OPT_IM, /* IM8 | IM16 | IM32 | IM64 */ |
||
87 | OPT_REG, /* REG8 | REG16 | REG32 | REG64 */ |
||
88 | OPT_REGW, /* REG16 | REG32 | REG64 */ |
||
89 | OPT_IMW, /* IM16 | IM32 | IM64 */ |
||
90 | #ifdef TCC_TARGET_X86_64 |
||
91 | OPT_IMNO64, /* IM16 | IM32 */ |
||
92 | #endif |
||
93 | /* can be ored with any OPT_xxx */ |
||
94 | OPT_EA = 0x80 |
||
95 | }; |
||
145 | halyavin | 96 | |
97 | #define OP_REG8 (1 << OPT_REG8) |
||
98 | #define OP_REG16 (1 << OPT_REG16) |
||
99 | #define OP_REG32 (1 << OPT_REG32) |
||
100 | #define OP_MMX (1 << OPT_MMX) |
||
101 | #define OP_SSE (1 << OPT_SSE) |
||
102 | #define OP_CR (1 << OPT_CR) |
||
103 | #define OP_TR (1 << OPT_TR) |
||
104 | #define OP_DB (1 << OPT_DB) |
||
105 | #define OP_SEG (1 << OPT_SEG) |
||
106 | #define OP_ST (1 << OPT_ST) |
||
107 | #define OP_IM8 (1 << OPT_IM8) |
||
108 | #define OP_IM8S (1 << OPT_IM8S) |
||
109 | #define OP_IM16 (1 << OPT_IM16) |
||
110 | #define OP_IM32 (1 << OPT_IM32) |
||
111 | #define OP_EAX (1 << OPT_EAX) |
||
112 | #define OP_ST0 (1 << OPT_ST0) |
||
113 | #define OP_CL (1 << OPT_CL) |
||
114 | #define OP_DX (1 << OPT_DX) |
||
115 | #define OP_ADDR (1 << OPT_ADDR) |
||
116 | #define OP_INDIR (1 << OPT_INDIR) |
||
6429 | siemargl | 117 | #ifdef TCC_TARGET_X86_64 |
118 | # define OP_REG64 (1 << OPT_REG64) |
||
119 | # define OP_IM64 (1 << OPT_IM64) |
||
120 | #else |
||
121 | # define OP_REG64 0 |
||
122 | # define OP_IM64 0 |
||
123 | #endif |
||
145 | halyavin | 124 | |
125 | #define OP_EA 0x40000000 |
||
6429 | siemargl | 126 | #define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64) |
127 | |||
128 | #ifdef TCC_TARGET_X86_64 |
||
129 | # define OP_IM OP_IM64 |
||
130 | # define TREG_XAX TREG_RAX |
||
131 | # define TREG_XCX TREG_RCX |
||
132 | # define TREG_XDX TREG_RDX |
||
133 | #else |
||
134 | # define OP_IM OP_IM32 |
||
135 | # define TREG_XAX TREG_EAX |
||
136 | # define TREG_XCX TREG_ECX |
||
137 | # define TREG_XDX TREG_EDX |
||
138 | #endif |
||
139 | |||
140 | typedef struct ASMInstr { |
||
141 | uint16_t sym; |
||
142 | uint16_t opcode; |
||
143 | uint16_t instr_type; |
||
144 | uint8_t nb_ops; |
||
145 | uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ |
||
146 | } ASMInstr; |
||
147 | |||
148 | typedef struct Operand { |
||
149 | uint32_t type; |
||
145 | halyavin | 150 | int8_t reg; /* register, -1 if none */ |
151 | int8_t reg2; /* second register, -1 if none */ |
||
152 | uint8_t shift; |
||
153 | ExprValue e; |
||
154 | } Operand; |
||
155 | |||
6429 | siemargl | 156 | static const uint8_t reg_to_size[9] = { |
157 | /* |
||
145 | halyavin | 158 | [OP_REG8] = 0, |
159 | [OP_REG16] = 1, |
||
160 | [OP_REG32] = 2, |
||
6429 | siemargl | 161 | #ifdef TCC_TARGET_X86_64 |
162 | [OP_REG64] = 3, |
||
163 | #endif |
||
164 | */ |
||
165 | 0, 0, 1, 0, 2, 0, 0, 0, 3 |
||
145 | halyavin | 166 | }; |
167 | |||
168 | #define NB_TEST_OPCODES 30 |
||
169 | |||
170 | static const uint8_t test_bits[NB_TEST_OPCODES] = { |
||
171 | 0x00, /* o */ |
||
172 | 0x01, /* no */ |
||
173 | 0x02, /* b */ |
||
174 | 0x02, /* c */ |
||
175 | 0x02, /* nae */ |
||
176 | 0x03, /* nb */ |
||
177 | 0x03, /* nc */ |
||
178 | 0x03, /* ae */ |
||
179 | 0x04, /* e */ |
||
180 | 0x04, /* z */ |
||
181 | 0x05, /* ne */ |
||
182 | 0x05, /* nz */ |
||
183 | 0x06, /* be */ |
||
184 | 0x06, /* na */ |
||
185 | 0x07, /* nbe */ |
||
186 | 0x07, /* a */ |
||
187 | 0x08, /* s */ |
||
188 | 0x09, /* ns */ |
||
189 | 0x0a, /* p */ |
||
190 | 0x0a, /* pe */ |
||
191 | 0x0b, /* np */ |
||
192 | 0x0b, /* po */ |
||
193 | 0x0c, /* l */ |
||
194 | 0x0c, /* nge */ |
||
195 | 0x0d, /* nl */ |
||
196 | 0x0d, /* ge */ |
||
197 | 0x0e, /* le */ |
||
198 | 0x0e, /* ng */ |
||
199 | 0x0f, /* nle */ |
||
200 | 0x0f, /* g */ |
||
201 | }; |
||
202 | |||
6429 | siemargl | 203 | static const uint8_t segment_prefixes[] = { |
204 | 0x26, /* es */ |
||
205 | 0x2e, /* cs */ |
||
206 | 0x36, /* ss */ |
||
207 | 0x3e, /* ds */ |
||
208 | 0x64, /* fs */ |
||
209 | 0x65 /* gs */ |
||
210 | }; |
||
211 | |||
145 | halyavin | 212 | static const ASMInstr asm_instrs[] = { |
213 | #define ALT(x) x |
||
214 | #define DEF_ASM_OP0(name, opcode) |
||
215 | #define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, |
||
216 | #define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, |
||
217 | #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, |
||
218 | #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 }}, |
||
6429 | siemargl | 219 | #ifdef TCC_TARGET_X86_64 |
220 | # include "x86_64-asm.h" |
||
221 | #else |
||
222 | # include "i386-asm.h" |
||
223 | #endif |
||
145 | halyavin | 224 | /* last operation */ |
225 | { 0, }, |
||
226 | }; |
||
227 | |||
228 | static const uint16_t op0_codes[] = { |
||
229 | #define ALT(x) |
||
230 | #define DEF_ASM_OP0(x, opcode) opcode, |
||
231 | #define DEF_ASM_OP0L(name, opcode, group, instr_type) |
||
232 | #define DEF_ASM_OP1(name, opcode, group, instr_type, op0) |
||
233 | #define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) |
||
234 | #define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) |
||
6429 | siemargl | 235 | #ifdef TCC_TARGET_X86_64 |
236 | # include "x86_64-asm.h" |
||
237 | #else |
||
238 | # include "i386-asm.h" |
||
239 | #endif |
||
145 | halyavin | 240 | }; |
241 | |||
242 | static inline int get_reg_shift(TCCState *s1) |
||
243 | { |
||
244 | int shift, v; |
||
6429 | siemargl | 245 | #ifdef I386_ASM_16 |
246 | if (s1->seg_size == 16) |
||
247 | tcc_error("invalid effective address"); |
||
248 | #endif |
||
145 | halyavin | 249 | v = asm_int_expr(s1); |
250 | switch(v) { |
||
251 | case 1: |
||
252 | shift = 0; |
||
253 | break; |
||
254 | case 2: |
||
255 | shift = 1; |
||
256 | break; |
||
257 | case 4: |
||
258 | shift = 2; |
||
259 | break; |
||
260 | case 8: |
||
261 | shift = 3; |
||
262 | break; |
||
263 | default: |
||
264 | expect("1, 2, 4 or 8 constant"); |
||
265 | shift = 0; |
||
266 | break; |
||
267 | } |
||
268 | return shift; |
||
269 | } |
||
270 | |||
271 | static int asm_parse_reg(void) |
||
272 | { |
||
6429 | siemargl | 273 | int reg = 0; |
145 | halyavin | 274 | if (tok != '%') |
275 | goto error_32; |
||
276 | next(); |
||
277 | if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
||
278 | reg = tok - TOK_ASM_eax; |
||
6429 | siemargl | 279 | #ifdef TCC_TARGET_X86_64 |
280 | } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { |
||
281 | reg = tok - TOK_ASM_rax; |
||
282 | #endif |
||
283 | #ifdef I386_ASM_16 |
||
284 | } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) { |
||
285 | reg = tok - TOK_ASM_ax; |
||
286 | #endif |
||
145 | halyavin | 287 | } else { |
288 | error_32: |
||
6429 | siemargl | 289 | expect("register"); |
145 | halyavin | 290 | } |
6429 | siemargl | 291 | next(); |
292 | return reg; |
||
145 | halyavin | 293 | } |
294 | |||
295 | static void parse_operand(TCCState *s1, Operand *op) |
||
296 | { |
||
297 | ExprValue e; |
||
298 | int reg, indir; |
||
299 | const char *p; |
||
300 | |||
301 | indir = 0; |
||
302 | if (tok == '*') { |
||
303 | next(); |
||
304 | indir = OP_INDIR; |
||
305 | } |
||
306 | |||
307 | if (tok == '%') { |
||
308 | next(); |
||
309 | if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { |
||
310 | reg = tok - TOK_ASM_al; |
||
311 | op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ |
||
312 | op->reg = reg & 7; |
||
6429 | siemargl | 313 | if ((op->type & OP_REG) && op->reg == TREG_XAX) |
145 | halyavin | 314 | op->type |= OP_EAX; |
6429 | siemargl | 315 | else if (op->type == OP_REG8 && op->reg == TREG_XCX) |
145 | halyavin | 316 | op->type |= OP_CL; |
6429 | siemargl | 317 | else if (op->type == OP_REG16 && op->reg == TREG_XDX) |
145 | halyavin | 318 | op->type |= OP_DX; |
319 | } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { |
||
320 | op->type = OP_DB; |
||
321 | op->reg = tok - TOK_ASM_dr0; |
||
322 | } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { |
||
323 | op->type = OP_SEG; |
||
324 | op->reg = tok - TOK_ASM_es; |
||
325 | } else if (tok == TOK_ASM_st) { |
||
326 | op->type = OP_ST; |
||
327 | op->reg = 0; |
||
328 | next(); |
||
329 | if (tok == '(') { |
||
330 | next(); |
||
331 | if (tok != TOK_PPNUM) |
||
332 | goto reg_error; |
||
6429 | siemargl | 333 | p = tokc.str.data; |
145 | halyavin | 334 | reg = p[0] - '0'; |
335 | if ((unsigned)reg >= 8 || p[1] != '\0') |
||
336 | goto reg_error; |
||
337 | op->reg = reg; |
||
338 | next(); |
||
339 | skip(')'); |
||
340 | } |
||
341 | if (op->reg == 0) |
||
342 | op->type |= OP_ST0; |
||
343 | goto no_skip; |
||
344 | } else { |
||
345 | reg_error: |
||
6429 | siemargl | 346 | tcc_error("unknown register"); |
145 | halyavin | 347 | } |
348 | next(); |
||
349 | no_skip: ; |
||
350 | } else if (tok == '$') { |
||
351 | /* constant value */ |
||
352 | next(); |
||
353 | asm_expr(s1, &e); |
||
6429 | siemargl | 354 | op->type = OP_IM; |
145 | halyavin | 355 | op->e.v = e.v; |
356 | op->e.sym = e.sym; |
||
357 | if (!op->e.sym) { |
||
358 | if (op->e.v == (uint8_t)op->e.v) |
||
359 | op->type |= OP_IM8; |
||
360 | if (op->e.v == (int8_t)op->e.v) |
||
361 | op->type |= OP_IM8S; |
||
362 | if (op->e.v == (uint16_t)op->e.v) |
||
363 | op->type |= OP_IM16; |
||
6429 | siemargl | 364 | #ifdef TCC_TARGET_X86_64 |
365 | if (op->e.v == (uint32_t)op->e.v) |
||
366 | op->type |= OP_IM32; |
||
367 | #endif |
||
145 | halyavin | 368 | } |
369 | } else { |
||
370 | /* address(reg,reg2,shift) with all variants */ |
||
371 | op->type = OP_EA; |
||
372 | op->reg = -1; |
||
373 | op->reg2 = -1; |
||
374 | op->shift = 0; |
||
375 | if (tok != '(') { |
||
376 | asm_expr(s1, &e); |
||
377 | op->e.v = e.v; |
||
378 | op->e.sym = e.sym; |
||
379 | } else { |
||
6429 | siemargl | 380 | next(); |
381 | if (tok == '%') { |
||
382 | unget_tok('('); |
||
383 | op->e.v = 0; |
||
384 | op->e.sym = NULL; |
||
385 | } else { |
||
386 | /* bracketed offset expression */ |
||
387 | asm_expr(s1, &e); |
||
388 | if (tok != ')') |
||
389 | expect(")"); |
||
390 | next(); |
||
391 | op->e.v = e.v; |
||
392 | op->e.sym = e.sym; |
||
393 | } |
||
145 | halyavin | 394 | } |
395 | if (tok == '(') { |
||
396 | next(); |
||
397 | if (tok != ',') { |
||
398 | op->reg = asm_parse_reg(); |
||
399 | } |
||
400 | if (tok == ',') { |
||
401 | next(); |
||
402 | if (tok != ',') { |
||
403 | op->reg2 = asm_parse_reg(); |
||
6429 | siemargl | 404 | } |
405 | if (tok == ',') { |
||
406 | next(); |
||
407 | op->shift = get_reg_shift(s1); |
||
408 | } |
||
145 | halyavin | 409 | } |
410 | skip(')'); |
||
411 | } |
||
412 | if (op->reg == -1 && op->reg2 == -1) |
||
413 | op->type |= OP_ADDR; |
||
414 | } |
||
415 | op->type |= indir; |
||
416 | } |
||
417 | |||
418 | /* XXX: unify with C code output ? */ |
||
6429 | siemargl | 419 | ST_FUNC void gen_expr32(ExprValue *pe) |
145 | halyavin | 420 | { |
6429 | siemargl | 421 | gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
145 | halyavin | 422 | } |
423 | |||
6429 | siemargl | 424 | #ifdef TCC_TARGET_X86_64 |
425 | static void gen_expr64(ExprValue *pe) |
||
426 | { |
||
427 | gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
||
428 | } |
||
429 | #endif |
||
430 | |||
145 | halyavin | 431 | /* XXX: unify with C code output ? */ |
432 | static void gen_disp32(ExprValue *pe) |
||
433 | { |
||
6429 | siemargl | 434 | Sym *sym = pe->sym; |
435 | if (sym && sym->r == cur_text_section->sh_num) { |
||
436 | /* same section: we can output an absolute value. Note |
||
437 | that the TCC compiler behaves differently here because |
||
438 | it always outputs a relocation to ease (future) code |
||
439 | elimination in the linker */ |
||
440 | gen_le32(pe->v + sym->jnext - ind - 4); |
||
441 | } else { |
||
442 | if (sym && sym->type.t == VT_VOID) { |
||
443 | sym->type.t = VT_FUNC; |
||
444 | sym->type.ref = NULL; |
||
445 | } |
||
446 | gen_addrpc32(VT_SYM, sym, pe->v); |
||
447 | } |
||
448 | } |
||
449 | |||
450 | #ifdef I386_ASM_16 |
||
451 | static void gen_expr16(ExprValue *pe) |
||
452 | { |
||
453 | if (pe->sym) |
||
454 | greloc(cur_text_section, pe->sym, ind, R_386_16); |
||
455 | gen_le16(pe->v); |
||
456 | } |
||
457 | static void gen_disp16(ExprValue *pe) |
||
458 | { |
||
145 | halyavin | 459 | Sym *sym; |
460 | sym = pe->sym; |
||
461 | if (sym) { |
||
462 | if (sym->r == cur_text_section->sh_num) { |
||
463 | /* same section: we can output an absolute value. Note |
||
464 | that the TCC compiler behaves differently here because |
||
465 | it always outputs a relocation to ease (future) code |
||
466 | elimination in the linker */ |
||
6429 | siemargl | 467 | gen_le16(pe->v + sym->jnext - ind - 2); |
145 | halyavin | 468 | } else { |
6429 | siemargl | 469 | greloc(cur_text_section, sym, ind, R_386_PC16); |
470 | gen_le16(pe->v - 2); |
||
145 | halyavin | 471 | } |
472 | } else { |
||
473 | /* put an empty PC32 relocation */ |
||
6429 | siemargl | 474 | put_elf_reloc(symtab_section, cur_text_section, |
475 | ind, R_386_PC16, 0); |
||
476 | gen_le16(pe->v - 2); |
||
145 | halyavin | 477 | } |
478 | } |
||
6429 | siemargl | 479 | #endif |
145 | halyavin | 480 | |
481 | /* generate the modrm operand */ |
||
482 | static inline void asm_modrm(int reg, Operand *op) |
||
483 | { |
||
484 | int mod, reg1, reg2, sib_reg1; |
||
485 | |||
486 | if (op->type & (OP_REG | OP_MMX | OP_SSE)) { |
||
487 | g(0xc0 + (reg << 3) + op->reg); |
||
488 | } else if (op->reg == -1 && op->reg2 == -1) { |
||
489 | /* displacement only */ |
||
6429 | siemargl | 490 | #ifdef I386_ASM_16 |
491 | if (tcc_state->seg_size == 16) { |
||
492 | g(0x06 + (reg << 3)); |
||
493 | gen_expr16(&op->e); |
||
494 | } else if (tcc_state->seg_size == 32) |
||
495 | #endif |
||
496 | { |
||
497 | g(0x05 + (reg << 3)); |
||
498 | gen_expr32(&op->e); |
||
499 | } |
||
145 | halyavin | 500 | } else { |
501 | sib_reg1 = op->reg; |
||
502 | /* fist compute displacement encoding */ |
||
503 | if (sib_reg1 == -1) { |
||
504 | sib_reg1 = 5; |
||
505 | mod = 0x00; |
||
506 | } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { |
||
507 | mod = 0x00; |
||
508 | } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { |
||
509 | mod = 0x40; |
||
510 | } else { |
||
511 | mod = 0x80; |
||
512 | } |
||
513 | /* compute if sib byte needed */ |
||
514 | reg1 = op->reg; |
||
515 | if (op->reg2 != -1) |
||
516 | reg1 = 4; |
||
6429 | siemargl | 517 | #ifdef I386_ASM_16 |
518 | if (tcc_state->seg_size == 32) { |
||
519 | #endif |
||
145 | halyavin | 520 | g(mod + (reg << 3) + reg1); |
521 | if (reg1 == 4) { |
||
522 | /* add sib byte */ |
||
523 | reg2 = op->reg2; |
||
524 | if (reg2 == -1) |
||
525 | reg2 = 4; /* indicate no index */ |
||
526 | g((op->shift << 6) + (reg2 << 3) + sib_reg1); |
||
527 | } |
||
6429 | siemargl | 528 | #ifdef I386_ASM_16 |
529 | } else if (tcc_state->seg_size == 16) { |
||
530 | /* edi = 7, esi = 6 --> di = 5, si = 4 */ |
||
531 | if ((reg1 == 6) || (reg1 == 7)) { |
||
532 | reg1 -= 2; |
||
533 | /* ebx = 3 --> bx = 7 */ |
||
534 | } else if (reg1 == 3) { |
||
535 | reg1 = 7; |
||
536 | /* o32 = 5 --> o16 = 6 */ |
||
537 | } else if (reg1 == 5) { |
||
538 | reg1 = 6; |
||
539 | /* sib not valid in 16-bit mode */ |
||
540 | } else if (reg1 == 4) { |
||
541 | reg2 = op->reg2; |
||
542 | /* bp + si + offset */ |
||
543 | if ((sib_reg1 == 5) && (reg2 == 6)) { |
||
544 | reg1 = 2; |
||
545 | /* bp + di + offset */ |
||
546 | } else if ((sib_reg1 == 5) && (reg2 == 7)) { |
||
547 | reg1 = 3; |
||
548 | /* bx + si + offset */ |
||
549 | } else if ((sib_reg1 == 3) && (reg2 == 6)) { |
||
550 | reg1 = 0; |
||
551 | /* bx + di + offset */ |
||
552 | } else if ((sib_reg1 == 3) && (reg2 == 7)) { |
||
553 | reg1 = 1; |
||
554 | } else { |
||
555 | tcc_error("invalid effective address"); |
||
556 | } |
||
557 | if (op->e.v == 0) |
||
558 | mod = 0; |
||
559 | } else { |
||
560 | tcc_error("invalid register"); |
||
561 | } |
||
562 | g(mod + (reg << 3) + reg1); |
||
563 | } |
||
564 | #endif |
||
145 | halyavin | 565 | /* add offset */ |
566 | if (mod == 0x40) { |
||
567 | g(op->e.v); |
||
568 | } else if (mod == 0x80 || op->reg == -1) { |
||
6429 | siemargl | 569 | #ifdef I386_ASM_16 |
570 | if (tcc_state->seg_size == 16) |
||
571 | gen_expr16(&op->e); |
||
572 | else if (tcc_state->seg_size == 32) |
||
573 | #endif |
||
574 | gen_expr32(&op->e); |
||
145 | halyavin | 575 | } |
576 | } |
||
577 | } |
||
578 | |||
6429 | siemargl | 579 | ST_FUNC void asm_opcode(TCCState *s1, int opcode) |
145 | halyavin | 580 | { |
581 | const ASMInstr *pa; |
||
6429 | siemargl | 582 | int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; |
583 | int nb_ops, s; |
||
145 | halyavin | 584 | Operand ops[MAX_OPERANDS], *pop; |
585 | int op_type[3]; /* decoded op type */ |
||
6429 | siemargl | 586 | #ifdef I386_ASM_16 |
587 | static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0; |
||
588 | #endif |
||
145 | halyavin | 589 | |
6429 | siemargl | 590 | /* force synthetic ';' after prefix instruction, so we can handle */ |
591 | /* one-line things like "rep stosb" instead of only "rep\nstosb" */ |
||
592 | if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) |
||
593 | unget_tok(';'); |
||
594 | |||
145 | halyavin | 595 | /* get operands */ |
596 | pop = ops; |
||
597 | nb_ops = 0; |
||
6429 | siemargl | 598 | seg_prefix = 0; |
145 | halyavin | 599 | for(;;) { |
600 | if (tok == ';' || tok == TOK_LINEFEED) |
||
601 | break; |
||
602 | if (nb_ops >= MAX_OPERANDS) { |
||
6429 | siemargl | 603 | tcc_error("incorrect number of operands"); |
145 | halyavin | 604 | } |
605 | parse_operand(s1, pop); |
||
6429 | siemargl | 606 | if (tok == ':') { |
607 | if (pop->type != OP_SEG || seg_prefix) |
||
608 | tcc_error("incorrect prefix"); |
||
609 | seg_prefix = segment_prefixes[pop->reg]; |
||
610 | next(); |
||
611 | parse_operand(s1, pop); |
||
612 | #ifndef I386_ASM_16 |
||
613 | if (!(pop->type & OP_EA)) { |
||
614 | tcc_error("segment prefix must be followed by memory reference"); |
||
615 | } |
||
616 | #endif |
||
617 | } |
||
145 | halyavin | 618 | pop++; |
619 | nb_ops++; |
||
620 | if (tok != ',') |
||
621 | break; |
||
622 | next(); |
||
623 | } |
||
624 | |||
625 | is_short_jmp = 0; |
||
626 | s = 0; /* avoid warning */ |
||
6429 | siemargl | 627 | |
145 | halyavin | 628 | /* optimize matching by using a lookup table (no hashing is needed |
629 | !) */ |
||
630 | for(pa = asm_instrs; pa->sym != 0; pa++) { |
||
631 | s = 0; |
||
632 | if (pa->instr_type & OPC_FARITH) { |
||
633 | v = opcode - pa->sym; |
||
634 | if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) |
||
635 | continue; |
||
636 | } else if (pa->instr_type & OPC_ARITH) { |
||
6429 | siemargl | 637 | if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX)) |
145 | halyavin | 638 | continue; |
6429 | siemargl | 639 | s = (opcode - pa->sym) % NBWLX; |
145 | halyavin | 640 | } else if (pa->instr_type & OPC_SHIFT) { |
6429 | siemargl | 641 | if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) |
145 | halyavin | 642 | continue; |
6429 | siemargl | 643 | s = (opcode - pa->sym) % NBWLX; |
145 | halyavin | 644 | } else if (pa->instr_type & OPC_TEST) { |
645 | if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) |
||
646 | continue; |
||
647 | } else if (pa->instr_type & OPC_B) { |
||
6429 | siemargl | 648 | if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX)) |
145 | halyavin | 649 | continue; |
6429 | siemargl | 650 | s = opcode - pa->sym; |
651 | } else if (pa->instr_type & OPC_WLX) { |
||
652 | if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1)) |
||
145 | halyavin | 653 | continue; |
654 | s = opcode - pa->sym + 1; |
||
655 | } else { |
||
656 | if (pa->sym != opcode) |
||
657 | continue; |
||
658 | } |
||
659 | if (pa->nb_ops != nb_ops) |
||
660 | continue; |
||
661 | /* now decode and check each operand */ |
||
662 | for(i = 0; i < nb_ops; i++) { |
||
663 | int op1, op2; |
||
664 | op1 = pa->op_type[i]; |
||
665 | op2 = op1 & 0x1f; |
||
666 | switch(op2) { |
||
667 | case OPT_IM: |
||
6429 | siemargl | 668 | v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64; |
145 | halyavin | 669 | break; |
670 | case OPT_REG: |
||
6429 | siemargl | 671 | v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; |
145 | halyavin | 672 | break; |
673 | case OPT_REGW: |
||
6429 | siemargl | 674 | v = OP_REG16 | OP_REG32 | OP_REG64; |
145 | halyavin | 675 | break; |
676 | case OPT_IMW: |
||
6429 | siemargl | 677 | v = OP_IM16 | OP_IM32 | OP_IM64; |
678 | break; |
||
679 | #ifdef TCC_TARGET_X86_64 |
||
680 | case OPT_IMNO64: |
||
145 | halyavin | 681 | v = OP_IM16 | OP_IM32; |
682 | break; |
||
6429 | siemargl | 683 | #endif |
145 | halyavin | 684 | default: |
685 | v = 1 << op2; |
||
686 | break; |
||
687 | } |
||
688 | if (op1 & OPT_EA) |
||
689 | v |= OP_EA; |
||
690 | op_type[i] = v; |
||
691 | if ((ops[i].type & v) == 0) |
||
692 | goto next; |
||
693 | } |
||
694 | /* all is matching ! */ |
||
695 | break; |
||
696 | next: ; |
||
697 | } |
||
698 | if (pa->sym == 0) { |
||
6429 | siemargl | 699 | if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) { |
145 | halyavin | 700 | int b; |
6429 | siemargl | 701 | b = op0_codes[opcode - TOK_ASM_first]; |
702 | #ifdef I386_ASM_16 |
||
703 | if (opcode == TOK_ASM_o32) { |
||
704 | if (s1->seg_size == 32) |
||
705 | tcc_error("incorrect prefix"); |
||
706 | else |
||
707 | o32 = data32 = 1; |
||
708 | } else if (opcode == TOK_ASM_a32) { |
||
709 | if (s1->seg_size == 32) |
||
710 | tcc_error("incorrect prefix"); |
||
711 | else |
||
712 | a32 = addr32 = 1; |
||
713 | } |
||
714 | #endif |
||
145 | halyavin | 715 | if (b & 0xff00) |
716 | g(b >> 8); |
||
717 | g(b); |
||
718 | return; |
||
6429 | siemargl | 719 | } else if (opcode <= TOK_ASM_alllast) { |
720 | tcc_error("bad operand with opcode '%s'", |
||
721 | get_tok_str(opcode, NULL)); |
||
145 | halyavin | 722 | } else { |
6429 | siemargl | 723 | tcc_error("unknown opcode '%s'", |
145 | halyavin | 724 | get_tok_str(opcode, NULL)); |
725 | } |
||
726 | } |
||
727 | /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ |
||
6429 | siemargl | 728 | if (s == NBWLX-1) { |
729 | for(i = 0; s == NBWLX-1 && i < nb_ops; i++) { |
||
145 | halyavin | 730 | if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) |
731 | s = reg_to_size[ops[i].type & OP_REG]; |
||
732 | } |
||
6429 | siemargl | 733 | if (s == NBWLX-1) { |
734 | if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && |
||
735 | (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64))) |
||
145 | halyavin | 736 | s = 2; |
737 | else |
||
6429 | siemargl | 738 | tcc_error("cannot infer opcode suffix"); |
145 | halyavin | 739 | } |
740 | } |
||
741 | |||
6429 | siemargl | 742 | #ifdef I386_ASM_16 |
743 | for(i = 0; i < nb_ops; i++) { |
||
744 | if (ops[i].type & OP_REG32) { |
||
745 | if (s1->seg_size == 16) |
||
746 | o32 = 1; |
||
747 | } else if (!(ops[i].type & OP_REG32)) { |
||
748 | if (s1->seg_size == 32) |
||
749 | o32 = 1; |
||
750 | } |
||
751 | } |
||
752 | |||
753 | |||
754 | if (s == 1 || (pa->instr_type & OPC_D16)) { |
||
755 | if (s1->seg_size == 32) |
||
756 | o32 = 1; |
||
757 | } else if (s == 2) { |
||
758 | if (s1->seg_size == 16) { |
||
759 | if (!(pa->instr_type & OPC_D16)) |
||
760 | o32 = 1; |
||
761 | } |
||
762 | } |
||
763 | |||
764 | /* generate a16/a32 prefix if needed */ |
||
765 | if ((a32 == 1) && (addr32 == 0)) |
||
766 | g(0x67); |
||
767 | /* generate o16/o32 prefix if needed */ |
||
768 | if ((o32 == 1) && (data32 == 0)) |
||
769 | g(0x66); |
||
770 | |||
771 | addr32 = data32 = 0; |
||
772 | #else |
||
145 | halyavin | 773 | /* generate data16 prefix if needed */ |
774 | if (s == 1 || (pa->instr_type & OPC_D16)) |
||
6429 | siemargl | 775 | g(0x66); |
776 | #ifdef TCC_TARGET_X86_64 |
||
777 | else if (s == 3) { |
||
778 | /* generate REX prefix */ |
||
779 | if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop) |
||
780 | || !(ops[0].type & OP_REG64)) |
||
781 | g(0x48); |
||
782 | } |
||
783 | #endif |
||
784 | #endif |
||
785 | |||
145 | halyavin | 786 | /* now generates the operation */ |
787 | if (pa->instr_type & OPC_FWAIT) |
||
788 | g(0x9b); |
||
6429 | siemargl | 789 | if (seg_prefix) |
790 | g(seg_prefix); |
||
145 | halyavin | 791 | |
792 | v = pa->opcode; |
||
6429 | siemargl | 793 | if ((v == 0x69 || v == 0x6b) && nb_ops == 2) { |
145 | halyavin | 794 | /* kludge for imul $im, %reg */ |
795 | nb_ops = 3; |
||
796 | ops[2] = ops[1]; |
||
6429 | siemargl | 797 | op_type[2] = op_type[1]; |
145 | halyavin | 798 | } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { |
799 | v--; /* int $3 case */ |
||
800 | nb_ops = 0; |
||
801 | } else if ((v == 0x06 || v == 0x07)) { |
||
802 | if (ops[0].reg >= 4) { |
||
803 | /* push/pop %fs or %gs */ |
||
804 | v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); |
||
805 | } else { |
||
806 | v += ops[0].reg << 3; |
||
807 | } |
||
808 | nb_ops = 0; |
||
809 | } else if (v <= 0x05) { |
||
810 | /* arith case */ |
||
6429 | siemargl | 811 | v += ((opcode - TOK_ASM_addb) / NBWLX) << 3; |
145 | halyavin | 812 | } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { |
813 | /* fpu arith case */ |
||
814 | v += ((opcode - pa->sym) / 6) << 3; |
||
815 | } |
||
816 | if (pa->instr_type & OPC_REG) { |
||
817 | for(i = 0; i < nb_ops; i++) { |
||
818 | if (op_type[i] & (OP_REG | OP_ST)) { |
||
819 | v += ops[i].reg; |
||
820 | break; |
||
821 | } |
||
822 | } |
||
823 | /* mov $im, %reg case */ |
||
824 | if (pa->opcode == 0xb0 && s >= 1) |
||
825 | v += 7; |
||
826 | } |
||
827 | if (pa->instr_type & OPC_B) |
||
6429 | siemargl | 828 | v += s >= 1; |
145 | halyavin | 829 | if (pa->instr_type & OPC_TEST) |
6429 | siemargl | 830 | v += test_bits[opcode - pa->sym]; |
145 | halyavin | 831 | if (pa->instr_type & OPC_SHORTJMP) { |
832 | Sym *sym; |
||
833 | int jmp_disp; |
||
834 | |||
835 | /* see if we can really generate the jump with a byte offset */ |
||
836 | sym = ops[0].e.sym; |
||
837 | if (!sym) |
||
838 | goto no_short_jump; |
||
839 | if (sym->r != cur_text_section->sh_num) |
||
840 | goto no_short_jump; |
||
6429 | siemargl | 841 | jmp_disp = ops[0].e.v + sym->jnext - ind - 2; |
145 | halyavin | 842 | if (jmp_disp == (int8_t)jmp_disp) { |
843 | /* OK to generate jump */ |
||
844 | is_short_jmp = 1; |
||
845 | ops[0].e.v = jmp_disp; |
||
846 | } else { |
||
847 | no_short_jump: |
||
848 | if (pa->instr_type & OPC_JMP) { |
||
849 | /* long jump will be allowed. need to modify the |
||
850 | opcode slightly */ |
||
851 | if (v == 0xeb) |
||
852 | v = 0xe9; |
||
6429 | siemargl | 853 | else |
145 | halyavin | 854 | v += 0x0f10; |
855 | } else { |
||
6429 | siemargl | 856 | tcc_error("invalid displacement"); |
145 | halyavin | 857 | } |
858 | } |
||
859 | } |
||
860 | op1 = v >> 8; |
||
861 | if (op1) |
||
862 | g(op1); |
||
863 | g(v); |
||
6429 | siemargl | 864 | |
145 | halyavin | 865 | /* search which operand will used for modrm */ |
866 | modrm_index = 0; |
||
867 | if (pa->instr_type & OPC_SHIFT) { |
||
6429 | siemargl | 868 | reg = (opcode - pa->sym) / NBWLX; |
145 | halyavin | 869 | if (reg == 6) |
870 | reg = 7; |
||
871 | } else if (pa->instr_type & OPC_ARITH) { |
||
6429 | siemargl | 872 | reg = (opcode - pa->sym) / NBWLX; |
145 | halyavin | 873 | } else if (pa->instr_type & OPC_FARITH) { |
874 | reg = (opcode - pa->sym) / 6; |
||
875 | } else { |
||
876 | reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; |
||
877 | } |
||
878 | if (pa->instr_type & OPC_MODRM) { |
||
879 | /* first look for an ea operand */ |
||
880 | for(i = 0;i < nb_ops; i++) { |
||
881 | if (op_type[i] & OP_EA) |
||
882 | goto modrm_found; |
||
883 | } |
||
884 | /* then if not found, a register or indirection (shift instructions) */ |
||
885 | for(i = 0;i < nb_ops; i++) { |
||
886 | if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) |
||
887 | goto modrm_found; |
||
888 | } |
||
889 | #ifdef ASM_DEBUG |
||
6429 | siemargl | 890 | tcc_error("bad op table"); |
891 | #endif |
||
145 | halyavin | 892 | modrm_found: |
893 | modrm_index = i; |
||
894 | /* if a register is used in another operand then it is |
||
895 | used instead of group */ |
||
896 | for(i = 0;i < nb_ops; i++) { |
||
897 | v = op_type[i]; |
||
6429 | siemargl | 898 | if (i != modrm_index && |
145 | halyavin | 899 | (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { |
900 | reg = ops[i].reg; |
||
901 | break; |
||
902 | } |
||
903 | } |
||
904 | |||
905 | asm_modrm(reg, &ops[modrm_index]); |
||
906 | } |
||
907 | |||
908 | /* emit constants */ |
||
6429 | siemargl | 909 | #ifndef TCC_TARGET_X86_64 |
145 | halyavin | 910 | if (pa->opcode == 0x9a || pa->opcode == 0xea) { |
911 | /* ljmp or lcall kludge */ |
||
6429 | siemargl | 912 | #ifdef I386_ASM_16 |
913 | if (s1->seg_size == 16 && o32 == 0) |
||
914 | gen_expr16(&ops[1].e); |
||
915 | else |
||
916 | #endif |
||
917 | gen_expr32(&ops[1].e); |
||
145 | halyavin | 918 | if (ops[0].e.sym) |
6429 | siemargl | 919 | tcc_error("cannot relocate"); |
145 | halyavin | 920 | gen_le16(ops[0].e.v); |
6429 | siemargl | 921 | return; |
922 | } |
||
923 | #endif |
||
924 | for(i = 0;i < nb_ops; i++) { |
||
925 | v = op_type[i]; |
||
926 | if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) { |
||
927 | /* if multiple sizes are given it means we must look |
||
928 | at the op size */ |
||
929 | if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) { |
||
930 | if (s == 0) |
||
931 | v = OP_IM8; |
||
932 | else if (s == 1) |
||
933 | v = OP_IM16; |
||
934 | else if (s == 2 || (v & OP_IM64) == 0) |
||
935 | v = OP_IM32; |
||
936 | else |
||
937 | v = OP_IM64; |
||
938 | } |
||
939 | if (v & (OP_IM8 | OP_IM8S)) { |
||
940 | if (ops[i].e.sym) |
||
941 | goto error_relocate; |
||
942 | g(ops[i].e.v); |
||
943 | } else if (v & OP_IM16) { |
||
944 | #ifdef I386_ASM_16 |
||
945 | if (s1->seg_size == 16) |
||
946 | gen_expr16(&ops[i].e); |
||
947 | else |
||
948 | #endif |
||
949 | if (ops[i].e.sym) |
||
950 | error_relocate: |
||
951 | tcc_error("cannot relocate"); |
||
952 | else |
||
953 | gen_le16(ops[i].e.v); |
||
954 | } else { |
||
955 | if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { |
||
956 | if (is_short_jmp) |
||
957 | g(ops[i].e.v); |
||
958 | #ifdef I386_ASM_16 |
||
959 | else if (s1->seg_size == 16) |
||
960 | gen_disp16(&ops[i].e); |
||
961 | #endif |
||
145 | halyavin | 962 | else |
6429 | siemargl | 963 | gen_disp32(&ops[i].e); |
145 | halyavin | 964 | } else { |
6429 | siemargl | 965 | #ifdef I386_ASM_16 |
966 | if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32))) |
||
967 | gen_expr16(&ops[i].e); |
||
968 | else |
||
969 | #endif |
||
970 | #ifdef TCC_TARGET_X86_64 |
||
971 | if (v & OP_IM64) |
||
972 | gen_expr64(&ops[i].e); |
||
973 | else |
||
974 | #endif |
||
145 | halyavin | 975 | gen_expr32(&ops[i].e); |
976 | } |
||
977 | } |
||
6429 | siemargl | 978 | #ifdef I386_ASM_16 |
979 | } else if (v & (OP_REG16 | OP_REG32)) { |
||
980 | if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { |
||
981 | /* jmp $r */ |
||
982 | g(0xE0 + ops[i].reg); |
||
983 | } |
||
984 | #endif |
||
985 | #ifdef TCC_TARGET_X86_64 |
||
986 | } else if (v & (OP_REG32 | OP_REG64)) { |
||
987 | if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { |
||
988 | /* jmp $r */ |
||
989 | g(0xE0 + ops[i].reg); |
||
990 | } |
||
991 | #endif |
||
145 | halyavin | 992 | } |
993 | } |
||
6429 | siemargl | 994 | #ifdef I386_ASM_16 |
995 | a32 = o32 = 0; |
||
996 | #endif |
||
145 | halyavin | 997 | } |
998 | |||
999 | /* return the constraint priority (we allocate first the lowest |
||
1000 | numbered constraints) */ |
||
1001 | static inline int constraint_priority(const char *str) |
||
1002 | { |
||
1003 | int priority, c, pr; |
||
1004 | |||
1005 | /* we take the lowest priority */ |
||
1006 | priority = 0; |
||
1007 | for(;;) { |
||
1008 | c = *str; |
||
1009 | if (c == '\0') |
||
1010 | break; |
||
1011 | str++; |
||
1012 | switch(c) { |
||
1013 | case 'A': |
||
1014 | pr = 0; |
||
1015 | break; |
||
1016 | case 'a': |
||
1017 | case 'b': |
||
1018 | case 'c': |
||
1019 | case 'd': |
||
1020 | case 'S': |
||
1021 | case 'D': |
||
1022 | pr = 1; |
||
1023 | break; |
||
1024 | case 'q': |
||
1025 | pr = 2; |
||
1026 | break; |
||
1027 | case 'r': |
||
1028 | pr = 3; |
||
1029 | break; |
||
1030 | case 'N': |
||
1031 | case 'M': |
||
1032 | case 'I': |
||
1033 | case 'i': |
||
1034 | case 'm': |
||
1035 | case 'g': |
||
1036 | pr = 4; |
||
1037 | break; |
||
1038 | default: |
||
6429 | siemargl | 1039 | tcc_error("unknown constraint '%c'", c); |
145 | halyavin | 1040 | pr = 0; |
1041 | } |
||
1042 | if (pr > priority) |
||
1043 | priority = pr; |
||
1044 | } |
||
1045 | return priority; |
||
1046 | } |
||
1047 | |||
1048 | static const char *skip_constraint_modifiers(const char *p) |
||
1049 | { |
||
1050 | while (*p == '=' || *p == '&' || *p == '+' || *p == '%') |
||
1051 | p++; |
||
1052 | return p; |
||
1053 | } |
||
1054 | |||
1055 | #define REG_OUT_MASK 0x01 |
||
1056 | #define REG_IN_MASK 0x02 |
||
1057 | |||
1058 | #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) |
||
1059 | |||
6429 | siemargl | 1060 | ST_FUNC void asm_compute_constraints(ASMOperand *operands, |
1061 | int nb_operands, int nb_outputs, |
||
145 | halyavin | 1062 | const uint8_t *clobber_regs, |
1063 | int *pout_reg) |
||
1064 | { |
||
1065 | ASMOperand *op; |
||
1066 | int sorted_op[MAX_ASM_OPERANDS]; |
||
1067 | int i, j, k, p1, p2, tmp, reg, c, reg_mask; |
||
1068 | const char *str; |
||
1069 | uint8_t regs_allocated[NB_ASM_REGS]; |
||
6429 | siemargl | 1070 | |
145 | halyavin | 1071 | /* init fields */ |
1072 | for(i=0;i |
||
1073 | op = &operands[i]; |
||
1074 | op->input_index = -1; |
||
1075 | op->ref_index = -1; |
||
1076 | op->reg = -1; |
||
1077 | op->is_memory = 0; |
||
1078 | op->is_rw = 0; |
||
1079 | } |
||
1080 | /* compute constraint priority and evaluate references to output |
||
1081 | constraints if input constraints */ |
||
1082 | for(i=0;i |
||
1083 | op = &operands[i]; |
||
1084 | str = op->constraint; |
||
1085 | str = skip_constraint_modifiers(str); |
||
1086 | if (isnum(*str) || *str == '[') { |
||
1087 | /* this is a reference to another constraint */ |
||
1088 | k = find_constraint(operands, nb_operands, str, NULL); |
||
1089 | if ((unsigned)k >= i || i < nb_outputs) |
||
6429 | siemargl | 1090 | tcc_error("invalid reference in constraint %d ('%s')", |
145 | halyavin | 1091 | i, str); |
1092 | op->ref_index = k; |
||
1093 | if (operands[k].input_index >= 0) |
||
6429 | siemargl | 1094 | tcc_error("cannot reference twice the same operand"); |
145 | halyavin | 1095 | operands[k].input_index = i; |
1096 | op->priority = 5; |
||
1097 | } else { |
||
1098 | op->priority = constraint_priority(str); |
||
1099 | } |
||
1100 | } |
||
6429 | siemargl | 1101 | |
145 | halyavin | 1102 | /* sort operands according to their priority */ |
1103 | for(i=0;i |
||
1104 | sorted_op[i] = i; |
||
1105 | for(i=0;i |
||
1106 | for(j=i+1;j |
||
6429 | siemargl | 1107 | p1 = operands[sorted_op[i]].priority; |
145 | halyavin | 1108 | p2 = operands[sorted_op[j]].priority; |
1109 | if (p2 < p1) { |
||
1110 | tmp = sorted_op[i]; |
||
1111 | sorted_op[i] = sorted_op[j]; |
||
1112 | sorted_op[j] = tmp; |
||
1113 | } |
||
1114 | } |
||
1115 | } |
||
1116 | |||
1117 | for(i = 0;i < NB_ASM_REGS; i++) { |
||
1118 | if (clobber_regs[i]) |
||
1119 | regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; |
||
1120 | else |
||
1121 | regs_allocated[i] = 0; |
||
1122 | } |
||
1123 | /* esp cannot be used */ |
||
6429 | siemargl | 1124 | regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; |
145 | halyavin | 1125 | /* ebp cannot be used yet */ |
6429 | siemargl | 1126 | regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; |
145 | halyavin | 1127 | |
1128 | /* allocate registers and generate corresponding asm moves */ |
||
1129 | for(i=0;i |
||
1130 | j = sorted_op[i]; |
||
1131 | op = &operands[j]; |
||
1132 | str = op->constraint; |
||
1133 | /* no need to allocate references */ |
||
1134 | if (op->ref_index >= 0) |
||
1135 | continue; |
||
1136 | /* select if register is used for output, input or both */ |
||
1137 | if (op->input_index >= 0) { |
||
1138 | reg_mask = REG_IN_MASK | REG_OUT_MASK; |
||
1139 | } else if (j < nb_outputs) { |
||
1140 | reg_mask = REG_OUT_MASK; |
||
1141 | } else { |
||
1142 | reg_mask = REG_IN_MASK; |
||
1143 | } |
||
1144 | try_next: |
||
1145 | c = *str++; |
||
1146 | switch(c) { |
||
1147 | case '=': |
||
1148 | goto try_next; |
||
1149 | case '+': |
||
1150 | op->is_rw = 1; |
||
1151 | /* FALL THRU */ |
||
1152 | case '&': |
||
1153 | if (j >= nb_outputs) |
||
6429 | siemargl | 1154 | tcc_error("'%c' modifier can only be applied to outputs", c); |
145 | halyavin | 1155 | reg_mask = REG_IN_MASK | REG_OUT_MASK; |
1156 | goto try_next; |
||
1157 | case 'A': |
||
1158 | /* allocate both eax and edx */ |
||
6429 | siemargl | 1159 | if (is_reg_allocated(TREG_XAX) || |
1160 | is_reg_allocated(TREG_XDX)) |
||
145 | halyavin | 1161 | goto try_next; |
1162 | op->is_llong = 1; |
||
6429 | siemargl | 1163 | op->reg = TREG_XAX; |
1164 | regs_allocated[TREG_XAX] |= reg_mask; |
||
1165 | regs_allocated[TREG_XDX] |= reg_mask; |
||
145 | halyavin | 1166 | break; |
1167 | case 'a': |
||
6429 | siemargl | 1168 | reg = TREG_XAX; |
145 | halyavin | 1169 | goto alloc_reg; |
1170 | case 'b': |
||
1171 | reg = 3; |
||
1172 | goto alloc_reg; |
||
1173 | case 'c': |
||
6429 | siemargl | 1174 | reg = TREG_XCX; |
145 | halyavin | 1175 | goto alloc_reg; |
1176 | case 'd': |
||
6429 | siemargl | 1177 | reg = TREG_XDX; |
145 | halyavin | 1178 | goto alloc_reg; |
1179 | case 'S': |
||
1180 | reg = 6; |
||
1181 | goto alloc_reg; |
||
1182 | case 'D': |
||
1183 | reg = 7; |
||
1184 | alloc_reg: |
||
1185 | if (is_reg_allocated(reg)) |
||
1186 | goto try_next; |
||
1187 | goto reg_found; |
||
1188 | case 'q': |
||
1189 | /* eax, ebx, ecx or edx */ |
||
1190 | for(reg = 0; reg < 4; reg++) { |
||
1191 | if (!is_reg_allocated(reg)) |
||
1192 | goto reg_found; |
||
1193 | } |
||
1194 | goto try_next; |
||
1195 | case 'r': |
||
1196 | /* any general register */ |
||
1197 | for(reg = 0; reg < 8; reg++) { |
||
1198 | if (!is_reg_allocated(reg)) |
||
1199 | goto reg_found; |
||
1200 | } |
||
1201 | goto try_next; |
||
1202 | reg_found: |
||
1203 | /* now we can reload in the register */ |
||
1204 | op->is_llong = 0; |
||
1205 | op->reg = reg; |
||
1206 | regs_allocated[reg] |= reg_mask; |
||
1207 | break; |
||
1208 | case 'i': |
||
1209 | if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) |
||
1210 | goto try_next; |
||
1211 | break; |
||
1212 | case 'I': |
||
1213 | case 'N': |
||
1214 | case 'M': |
||
1215 | if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) |
||
1216 | goto try_next; |
||
1217 | break; |
||
1218 | case 'm': |
||
1219 | case 'g': |
||
1220 | /* nothing special to do because the operand is already in |
||
1221 | memory, except if the pointer itself is stored in a |
||
1222 | memory variable (VT_LLOCAL case) */ |
||
1223 | /* XXX: fix constant case */ |
||
1224 | /* if it is a reference to a memory zone, it must lie |
||
1225 | in a register, so we reserve the register in the |
||
1226 | input registers and a load will be generated |
||
1227 | later */ |
||
1228 | if (j < nb_outputs || c == 'm') { |
||
1229 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
||
1230 | /* any general register */ |
||
1231 | for(reg = 0; reg < 8; reg++) { |
||
1232 | if (!(regs_allocated[reg] & REG_IN_MASK)) |
||
1233 | goto reg_found1; |
||
1234 | } |
||
1235 | goto try_next; |
||
1236 | reg_found1: |
||
1237 | /* now we can reload in the register */ |
||
1238 | regs_allocated[reg] |= REG_IN_MASK; |
||
1239 | op->reg = reg; |
||
1240 | op->is_memory = 1; |
||
1241 | } |
||
1242 | } |
||
1243 | break; |
||
1244 | default: |
||
6429 | siemargl | 1245 | tcc_error("asm constraint %d ('%s') could not be satisfied", |
145 | halyavin | 1246 | j, op->constraint); |
1247 | break; |
||
1248 | } |
||
1249 | /* if a reference is present for that operand, we assign it too */ |
||
1250 | if (op->input_index >= 0) { |
||
1251 | operands[op->input_index].reg = op->reg; |
||
1252 | operands[op->input_index].is_llong = op->is_llong; |
||
1253 | } |
||
1254 | } |
||
6429 | siemargl | 1255 | |
145 | halyavin | 1256 | /* compute out_reg. It is used to store outputs registers to memory |
1257 | locations references by pointers (VT_LLOCAL case) */ |
||
1258 | *pout_reg = -1; |
||
1259 | for(i=0;i |
||
1260 | op = &operands[i]; |
||
6429 | siemargl | 1261 | if (op->reg >= 0 && |
145 | halyavin | 1262 | (op->vt->r & VT_VALMASK) == VT_LLOCAL && |
1263 | !op->is_memory) { |
||
1264 | for(reg = 0; reg < 8; reg++) { |
||
1265 | if (!(regs_allocated[reg] & REG_OUT_MASK)) |
||
1266 | goto reg_found2; |
||
1267 | } |
||
6429 | siemargl | 1268 | tcc_error("could not find free output register for reloading"); |
145 | halyavin | 1269 | reg_found2: |
1270 | *pout_reg = reg; |
||
1271 | break; |
||
1272 | } |
||
1273 | } |
||
6429 | siemargl | 1274 | |
145 | halyavin | 1275 | /* print sorted constraints */ |
1276 | #ifdef ASM_DEBUG |
||
1277 | for(i=0;i |
||
1278 | j = sorted_op[i]; |
||
1279 | op = &operands[j]; |
||
6429 | siemargl | 1280 | printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", |
1281 | j, |
||
1282 | op->id ? get_tok_str(op->id, NULL) : "", |
||
145 | halyavin | 1283 | op->constraint, |
1284 | op->vt->r, |
||
1285 | op->reg); |
||
1286 | } |
||
1287 | if (*pout_reg >= 0) |
||
1288 | printf("out_reg=%d\n", *pout_reg); |
||
1289 | #endif |
||
1290 | } |
||
1291 | |||
6429 | siemargl | 1292 | ST_FUNC void subst_asm_operand(CString *add_str, |
145 | halyavin | 1293 | SValue *sv, int modifier) |
1294 | { |
||
1295 | int r, reg, size, val; |
||
1296 | char buf[64]; |
||
1297 | |||
1298 | r = sv->r; |
||
1299 | if ((r & VT_VALMASK) == VT_CONST) { |
||
1300 | if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') |
||
1301 | cstr_ccat(add_str, '$'); |
||
1302 | if (r & VT_SYM) { |
||
6429 | siemargl | 1303 | cstr_cat(add_str, get_tok_str(sv->sym->v, NULL), -1); |
1304 | if ((uint32_t)sv->c.i != 0) { |
||
145 | halyavin | 1305 | cstr_ccat(add_str, '+'); |
1306 | } else { |
||
1307 | return; |
||
1308 | } |
||
1309 | } |
||
1310 | val = sv->c.i; |
||
1311 | if (modifier == 'n') |
||
1312 | val = -val; |
||
6429 | siemargl | 1313 | snprintf(buf, sizeof(buf), "%d", (int)sv->c.i); |
1314 | cstr_cat(add_str, buf, -1); |
||
145 | halyavin | 1315 | } else if ((r & VT_VALMASK) == VT_LOCAL) { |
6429 | siemargl | 1316 | snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i); |
1317 | cstr_cat(add_str, buf, -1); |
||
145 | halyavin | 1318 | } else if (r & VT_LVAL) { |
1319 | reg = r & VT_VALMASK; |
||
1320 | if (reg >= VT_CONST) |
||
6429 | siemargl | 1321 | tcc_error("internal compiler error"); |
1322 | snprintf(buf, sizeof(buf), "(%%%s)", |
||
145 | halyavin | 1323 | get_tok_str(TOK_ASM_eax + reg, NULL)); |
6429 | siemargl | 1324 | cstr_cat(add_str, buf, -1); |
145 | halyavin | 1325 | } else { |
1326 | /* register case */ |
||
1327 | reg = r & VT_VALMASK; |
||
1328 | if (reg >= VT_CONST) |
||
6429 | siemargl | 1329 | tcc_error("internal compiler error"); |
145 | halyavin | 1330 | |
1331 | /* choose register operand size */ |
||
1332 | if ((sv->type.t & VT_BTYPE) == VT_BYTE) |
||
1333 | size = 1; |
||
1334 | else if ((sv->type.t & VT_BTYPE) == VT_SHORT) |
||
1335 | size = 2; |
||
6429 | siemargl | 1336 | #ifdef TCC_TARGET_X86_64 |
1337 | else if ((sv->type.t & VT_BTYPE) == VT_LLONG) |
||
1338 | size = 8; |
||
1339 | #endif |
||
145 | halyavin | 1340 | else |
1341 | size = 4; |
||
1342 | if (size == 1 && reg >= 4) |
||
1343 | size = 4; |
||
1344 | |||
1345 | if (modifier == 'b') { |
||
1346 | if (reg >= 4) |
||
6429 | siemargl | 1347 | tcc_error("cannot use byte register"); |
145 | halyavin | 1348 | size = 1; |
1349 | } else if (modifier == 'h') { |
||
1350 | if (reg >= 4) |
||
6429 | siemargl | 1351 | tcc_error("cannot use byte register"); |
145 | halyavin | 1352 | size = -1; |
1353 | } else if (modifier == 'w') { |
||
1354 | size = 2; |
||
6429 | siemargl | 1355 | #ifdef TCC_TARGET_X86_64 |
1356 | } else if (modifier == 'q') { |
||
1357 | size = 8; |
||
1358 | #endif |
||
145 | halyavin | 1359 | } |
1360 | |||
1361 | switch(size) { |
||
1362 | case -1: |
||
1363 | reg = TOK_ASM_ah + reg; |
||
1364 | break; |
||
1365 | case 1: |
||
1366 | reg = TOK_ASM_al + reg; |
||
1367 | break; |
||
1368 | case 2: |
||
1369 | reg = TOK_ASM_ax + reg; |
||
1370 | break; |
||
1371 | default: |
||
1372 | reg = TOK_ASM_eax + reg; |
||
1373 | break; |
||
6429 | siemargl | 1374 | #ifdef TCC_TARGET_X86_64 |
1375 | case 8: |
||
1376 | reg = TOK_ASM_rax + reg; |
||
1377 | break; |
||
1378 | #endif |
||
145 | halyavin | 1379 | } |
1380 | snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); |
||
6429 | siemargl | 1381 | cstr_cat(add_str, buf, -1); |
145 | halyavin | 1382 | } |
1383 | } |
||
1384 | |||
6429 | siemargl | 1385 | /* generate prolog and epilog code for asm statement */ |
1386 | ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, |
||
145 | halyavin | 1387 | int nb_outputs, int is_output, |
1388 | uint8_t *clobber_regs, |
||
1389 | int out_reg) |
||
1390 | { |
||
1391 | uint8_t regs_allocated[NB_ASM_REGS]; |
||
1392 | ASMOperand *op; |
||
1393 | int i, reg; |
||
1394 | static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; |
||
1395 | |||
1396 | /* mark all used registers */ |
||
1397 | memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); |
||
1398 | for(i = 0; i < nb_operands;i++) { |
||
1399 | op = &operands[i]; |
||
1400 | if (op->reg >= 0) |
||
1401 | regs_allocated[op->reg] = 1; |
||
1402 | } |
||
1403 | if (!is_output) { |
||
1404 | /* generate reg save code */ |
||
1405 | for(i = 0; i < NB_SAVED_REGS; i++) { |
||
1406 | reg = reg_saved[i]; |
||
6429 | siemargl | 1407 | if (regs_allocated[reg]) { |
1408 | #ifdef I386_ASM_16 |
||
1409 | if (tcc_state->seg_size == 16) |
||
1410 | g(0x66); |
||
1411 | #endif |
||
145 | halyavin | 1412 | g(0x50 + reg); |
6429 | siemargl | 1413 | } |
145 | halyavin | 1414 | } |
1415 | |||
1416 | /* generate load code */ |
||
1417 | for(i = 0; i < nb_operands; i++) { |
||
1418 | op = &operands[i]; |
||
1419 | if (op->reg >= 0) { |
||
1420 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && |
||
1421 | op->is_memory) { |
||
1422 | /* memory reference case (for both input and |
||
1423 | output cases) */ |
||
1424 | SValue sv; |
||
1425 | sv = *op->vt; |
||
1426 | sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
||
1427 | load(op->reg, &sv); |
||
1428 | } else if (i >= nb_outputs || op->is_rw) { |
||
1429 | /* load value in register */ |
||
1430 | load(op->reg, op->vt); |
||
1431 | if (op->is_llong) { |
||
1432 | SValue sv; |
||
1433 | sv = *op->vt; |
||
6429 | siemargl | 1434 | sv.c.i += 4; |
1435 | load(TREG_XDX, &sv); |
||
145 | halyavin | 1436 | } |
1437 | } |
||
1438 | } |
||
1439 | } |
||
1440 | } else { |
||
1441 | /* generate save code */ |
||
1442 | for(i = 0 ; i < nb_outputs; i++) { |
||
1443 | op = &operands[i]; |
||
1444 | if (op->reg >= 0) { |
||
1445 | if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { |
||
1446 | if (!op->is_memory) { |
||
1447 | SValue sv; |
||
1448 | sv = *op->vt; |
||
1449 | sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; |
||
1450 | load(out_reg, &sv); |
||
1451 | |||
1452 | sv.r = (sv.r & ~VT_VALMASK) | out_reg; |
||
1453 | store(op->reg, &sv); |
||
1454 | } |
||
1455 | } else { |
||
1456 | store(op->reg, op->vt); |
||
1457 | if (op->is_llong) { |
||
1458 | SValue sv; |
||
1459 | sv = *op->vt; |
||
6429 | siemargl | 1460 | sv.c.i += 4; |
1461 | store(TREG_XDX, &sv); |
||
145 | halyavin | 1462 | } |
1463 | } |
||
1464 | } |
||
1465 | } |
||
1466 | /* generate reg restore code */ |
||
1467 | for(i = NB_SAVED_REGS - 1; i >= 0; i--) { |
||
1468 | reg = reg_saved[i]; |
||
6429 | siemargl | 1469 | if (regs_allocated[reg]) { |
1470 | #ifdef I386_ASM_16 |
||
1471 | if (tcc_state->seg_size == 16) |
||
1472 | g(0x66); |
||
1473 | #endif |
||
145 | halyavin | 1474 | g(0x58 + reg); |
6429 | siemargl | 1475 | } |
145 | halyavin | 1476 | } |
1477 | } |
||
1478 | } |
||
1479 | |||
6429 | siemargl | 1480 | ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) |
145 | halyavin | 1481 | { |
1482 | int reg; |
||
1483 | TokenSym *ts; |
||
1484 | |||
6429 | siemargl | 1485 | if (!strcmp(str, "memory") || |
145 | halyavin | 1486 | !strcmp(str, "cc")) |
1487 | return; |
||
1488 | ts = tok_alloc(str, strlen(str)); |
||
1489 | reg = ts->tok; |
||
1490 | if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { |
||
1491 | reg -= TOK_ASM_eax; |
||
1492 | } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { |
||
1493 | reg -= TOK_ASM_ax; |
||
6429 | siemargl | 1494 | #ifdef TCC_TARGET_X86_64 |
1495 | } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { |
||
1496 | reg -= TOK_ASM_rax; |
||
1497 | #endif |
||
145 | halyavin | 1498 | } else { |
6429 | siemargl | 1499 | tcc_error("invalid clobber register '%s'", str); |
145 | halyavin | 1500 | } |
1501 | clobber_regs[reg] = 1; |
||
1502 | }=>=>=>>>>>>>>>>>>> |