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