| @@ -72,6 +72,10 @@ enum { · 71 unmodified lines |
| 72 | 72 | OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ |
| 73 | 73 | OPT_SEG, |
| 74 | 74 | OPT_ST, |
| 75 | +#ifdef TCC_TARGET_X86_64 |
| 76 | + OPT_REG8_LOW, /* %spl,%bpl,%sil,%dil, encoded like ah,ch,dh,bh, but |
| 77 | + with REX prefix, not used in insn templates */ |
| 78 | +#endif |
| 75 | 79 | OPT_IM8, |
| 76 | 80 | OPT_IM8S, |
| 77 | 81 | OPT_IM16, |
| @@ -120,10 +124,12 @@ enum { · 42 unmodified lines |
| 120 | 124 | #define OP_INDIR (1 << OPT_INDIR) |
| 121 | 125 | #ifdef TCC_TARGET_X86_64 |
| 122 | 126 | # define OP_REG64 (1 << OPT_REG64) |
| 127 | +# define OP_REG8_LOW (1 << OPT_REG8_LOW) |
| 123 | 128 | # define OP_IM64 (1 << OPT_IM64) |
| 124 | 129 | # define OP_EA32 (OP_EA << 1) |
| 125 | 130 | #else |
| 126 | 131 | # define OP_REG64 0 |
| 132 | +# define OP_REG8_LOW 0 |
| 127 | 133 | # define OP_IM64 0 |
| 128 | 134 | # define OP_EA32 0 |
| 129 | 135 | #endif |
| @@ -272,6 +278,39 @@ static inline int get_reg_shift(TCCState *s1) · 142 unmodified lines |
| 272 | 278 | return shift; |
| 273 | 279 | } |
| 274 | 280 | |
| 281 | +#ifdef TCC_TARGET_X86_64 |
| 282 | +static int asm_parse_high_reg(int *type) |
| 283 | +{ |
| 284 | + int reg = -1; |
| 285 | + if (tok >= TOK_IDENT && tok < tok_ident) { |
| 286 | + const char *s = table_ident[tok - TOK_IDENT]->str; |
| 287 | + char c; |
| 288 | + if (*s++ != 'r') |
| 289 | + return -1; |
| 290 | + /* Don't allow leading '0'. */ |
| 291 | + if ((c = *s++) >= '1' && c <= '9') |
| 292 | + reg = c - '0'; |
| 293 | + else |
| 294 | + return -1; |
| 295 | + if ((c = *s) >= '0' && c <= '5') |
| 296 | + s++, reg = reg * 10 + c - '0'; |
| 297 | + if (reg > 15) |
| 298 | + return -1; |
| 299 | + if ((c = *s) == 0) |
| 300 | + *type = OP_REG64; |
| 301 | + else if (c == 'b' && !s[1]) |
| 302 | + *type = OP_REG8; |
| 303 | + else if (c == 'w' && !s[1]) |
| 304 | + *type = OP_REG16; |
| 305 | + else if (c == 'd' && !s[1]) |
| 306 | + *type = OP_REG32; |
| 307 | + else |
| 308 | + return -1; |
| 309 | + } |
| 310 | + return reg; |
| 311 | +} |
| 312 | +#endif |
| 313 | + |
| 275 | 314 | static int asm_parse_reg(int *type) |
| 276 | 315 | { |
| 277 | 316 | int reg = 0; |
| @@ -281,12 +320,17 @@ static int asm_parse_reg(int *type) · 3 unmodified lines |
| 281 | 320 | next(); |
| 282 | 321 | if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
| 283 | 322 | reg = tok - TOK_ASM_eax; |
| 323 | + *type = OP_REG32; |
| 284 | 324 | #ifdef TCC_TARGET_X86_64 |
| 285 | | − *type = OP_EA32; |
| 286 | 325 | } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { |
| 287 | 326 | reg = tok - TOK_ASM_rax; |
| 327 | + *type = OP_REG64; |
| 288 | 328 | } else if (tok == TOK_ASM_rip) { |
| 289 | | − reg = 8; |
| 329 | + reg = -2; /* Probably should use different escape code. */ |
| 330 | + *type = OP_REG64; |
| 331 | + } else if ((reg = asm_parse_high_reg(type)) >= 0 |
| 332 | + && (*type == OP_REG32 || *type == OP_REG64)) { |
| 333 | + ; |
| 290 | 334 | #endif |
| 291 | 335 | } else { |
| 292 | 336 | error_32: |
| @@ -345,6 +389,13 @@ static void parse_operand(TCCState *s1, Operand *op) · 52 unmodified lines |
| 345 | 389 | if (op->reg == 0) |
| 346 | 390 | op->type |= OP_ST0; |
| 347 | 391 | goto no_skip; |
| 392 | +#ifdef TCC_TARGET_X86_64 |
| 393 | + } else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) { |
| 394 | + op->type = OP_REG8 | OP_REG8_LOW; |
| 395 | + op->reg = 4 + tok - TOK_ASM_spl; |
| 396 | + } else if ((op->reg = asm_parse_high_reg(&op->type)) >= 0) { |
| 397 | + ; |
| 398 | +#endif |
| 348 | 399 | } else { |
| 349 | 400 | reg_error: |
| 350 | 401 | tcc_error("unknown register %%%s", get_tok_str(tok, &tokc)); |
| @@ -411,7 +462,7 @@ static void parse_operand(TCCState *s1, Operand *op) · 60 unmodified lines |
| 411 | 462 | op->shift = get_reg_shift(s1); |
| 412 | 463 | } |
| 413 | 464 | } |
| 414 | | − if (type & OP_EA32) |
| 465 | + if (type & OP_REG32) |
| 415 | 466 | op->type |= OP_EA32; |
| 416 | 467 | skip(')'); |
| 417 | 468 | } |
| @@ -475,7 +526,7 @@ static inline int asm_modrm(int reg, Operand *op) · 57 unmodified lines |
| 475 | 526 | #endif |
| 476 | 527 | gen_expr32(&op->e); |
| 477 | 528 | #ifdef TCC_TARGET_X86_64 |
| 478 | | − } else if (op->reg == 8) { |
| 529 | + } else if (op->reg == -2) { |
| 479 | 530 | ExprValue *pe = &op->e; |
| 480 | 531 | g(0x05 + (reg << 3)); |
| 481 | 532 | gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
| @@ -516,6 +567,69 @@ static inline int asm_modrm(int reg, Operand *op) · 34 unmodified lines |
| 516 | 567 | return 0; |
| 517 | 568 | } |
| 518 | 569 | |
| 570 | +#ifdef TCC_TARGET_X86_64 |
| 571 | +#define REX_W 0x48 |
| 572 | +#define REX_R 0x44 |
| 573 | +#define REX_X 0x42 |
| 574 | +#define REX_B 0x41 |
| 575 | + |
| 576 | +static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type, |
| 577 | + int regi, int rmi) |
| 578 | +{ |
| 579 | + unsigned char rex = width64 ? 0x48 : 0; |
| 580 | + int saw_high_8bit = 0; |
| 581 | + int i; |
| 582 | + if (rmi == -1) { |
| 583 | + /* No mod/rm byte, but we might have a register op nevertheless |
| 584 | + (we will add it to the opcode later). */ |
| 585 | + for(i = 0; i < nb_ops; i++) { |
| 586 | + if (op_type[i] & (OP_REG | OP_ST)) { |
| 587 | + if (ops[i].reg >= 8) { |
| 588 | + rex |= REX_B; |
| 589 | + ops[i].reg -= 8; |
| 590 | + } else if (ops[i].type & OP_REG8_LOW) |
| 591 | + rex |= 0x40; |
| 592 | + else if (ops[i].type & OP_REG8 && ops[i].reg >= 4) |
| 593 | + /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */ |
| 594 | + saw_high_8bit = ops[i].reg; |
| 595 | + break; |
| 596 | + } |
| 597 | + } |
| 598 | + } else { |
| 599 | + if (regi != -1) { |
| 600 | + if (ops[regi].reg >= 8) { |
| 601 | + rex |= REX_R; |
| 602 | + ops[regi].reg -= 8; |
| 603 | + } else if (ops[regi].type & OP_REG8_LOW) |
| 604 | + rex |= 0x40; |
| 605 | + else if (ops[regi].type & OP_REG8 && ops[regi].reg >= 4) |
| 606 | + /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */ |
| 607 | + saw_high_8bit = ops[regi].reg; |
| 608 | + } |
| 609 | + if (ops[rmi].type & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_EA)) { |
| 610 | + if (ops[rmi].reg >= 8) { |
| 611 | + rex |= REX_B; |
| 612 | + ops[rmi].reg -= 8; |
| 613 | + } else if (ops[rmi].type & OP_REG8_LOW) |
| 614 | + rex |= 0x40; |
| 615 | + else if (ops[rmi].type & OP_REG8 && ops[rmi].reg >= 4) |
| 616 | + /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */ |
| 617 | + saw_high_8bit = ops[rmi].reg; |
| 618 | + } |
| 619 | + if (ops[rmi].type & OP_EA && ops[rmi].reg2 >= 8) { |
| 620 | + rex |= REX_X; |
| 621 | + ops[rmi].reg2 -= 8; |
| 622 | + } |
| 623 | + } |
| 624 | + if (rex) { |
| 625 | + if (saw_high_8bit) |
| 626 | + tcc_error("can't encode register %%%ch when REX prefix is required", |
| 627 | + "acdb"[saw_high_8bit-4]); |
| 628 | + g(rex); |
| 629 | + } |
| 630 | +} |
| 631 | +#endif |
| 632 | + |
| 519 | 633 | static void maybe_print_stats (void) |
| 520 | 634 | { |
| 521 | 635 | static int already = 1; |
| @@ -558,13 +672,16 @@ static void maybe_print_stats (void) · 36 unmodified lines |
| 558 | 672 | ST_FUNC void asm_opcode(TCCState *s1, int opcode) |
| 559 | 673 | { |
| 560 | 674 | const ASMInstr *pa; |
| 561 | | − int i, modrm_index, reg, v, op1, seg_prefix, pc; |
| 675 | + int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc; |
| 562 | 676 | int nb_ops, s; |
| 563 | 677 | Operand ops[MAX_OPERANDS], *pop; |
| 564 | 678 | int op_type[3]; /* decoded op type */ |
| 565 | 679 | int alltypes; /* OR of all operand types */ |
| 566 | 680 | int autosize; |
| 567 | 681 | int p66; |
| 682 | +#ifdef TCC_TARGET_X86_64 |
| 683 | + int rex64; |
| 684 | +#endif |
| 568 | 685 | |
| 569 | 686 | maybe_print_stats(); |
| 570 | 687 | /* force synthetic ';' after prefix instruction, so we can handle */ |
| @@ -775,6 +892,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 204 unmodified lines |
| 775 | 892 | if (p66) |
| 776 | 893 | g(0x66); |
| 777 | 894 | #ifdef TCC_TARGET_X86_64 |
| 895 | + rex64 = 0; |
| 778 | 896 | if (s == 3 || (alltypes & OP_REG64)) { |
| 779 | 897 | /* generate REX prefix */ |
| 780 | 898 | int default64 = 0; |
| @@ -794,7 +912,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 13 unmodified lines |
| 794 | 912 | && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq |
| 795 | 913 | && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp)) |
| 796 | 914 | && !default64) |
| 797 | | − g(0x48); |
| 915 | + rex64 = 1; |
| 798 | 916 | } |
| 799 | 917 | #endif |
| 800 | 918 | |
| @@ -830,6 +948,50 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 29 unmodified lines |
| 830 | 948 | /* fpu arith case */ |
| 831 | 949 | v += ((opcode - pa->sym) / 6) << 3; |
| 832 | 950 | } |
| 951 | + |
| 952 | + /* search which operand will be used for modrm */ |
| 953 | + modrm_index = -1; |
| 954 | + modreg_index = -1; |
| 955 | + if (pa->instr_type & OPC_MODRM) { |
| 956 | + if (!nb_ops) { |
| 957 | + /* A modrm opcode without operands is a special case (e.g. mfence). |
| 958 | + It has a group and acts as if there's an register operand 0 |
| 959 | + (ax). */ |
| 960 | + i = 0; |
| 961 | + ops[i].type = OP_REG; |
| 962 | + ops[i].reg = 0; |
| 963 | + goto modrm_found; |
| 964 | + } |
| 965 | + /* first look for an ea operand */ |
| 966 | + for(i = 0;i < nb_ops; i++) { |
| 967 | + if (op_type[i] & OP_EA) |
| 968 | + goto modrm_found; |
| 969 | + } |
| 970 | + /* then if not found, a register or indirection (shift instructions) */ |
| 971 | + for(i = 0;i < nb_ops; i++) { |
| 972 | + if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) |
| 973 | + goto modrm_found; |
| 974 | + } |
| 975 | +#ifdef ASM_DEBUG |
| 976 | + tcc_error("bad op table"); |
| 977 | +#endif |
| 978 | + modrm_found: |
| 979 | + modrm_index = i; |
| 980 | + /* if a register is used in another operand then it is |
| 981 | + used instead of group */ |
| 982 | + for(i = 0;i < nb_ops; i++) { |
| 983 | + int t = op_type[i]; |
| 984 | + if (i != modrm_index && |
| 985 | + (t & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { |
| 986 | + modreg_index = i; |
| 987 | + break; |
| 988 | + } |
| 989 | + } |
| 990 | + } |
| 991 | +#ifdef TCC_TARGET_X86_64 |
| 992 | + asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index); |
| 993 | +#endif |
| 994 | + |
| 833 | 995 | if (pa->instr_type & OPC_REG) { |
| 834 | 996 | /* mov $im, %reg case */ |
| 835 | 997 | if (v == 0xb0 && s >= 1) |
| @@ -881,8 +1043,6 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 45 unmodified lines |
| 881 | 1043 | g(op1); |
| 882 | 1044 | g(v); |
| 883 | 1045 | |
| 884 | | − /* search which operand will used for modrm */ |
| 885 | | − modrm_index = 0; |
| 886 | 1046 | if (OPCT_IS(pa->instr_type, OPC_SHIFT)) { |
| 887 | 1047 | reg = (opcode - pa->sym) / NBWLX; |
| 888 | 1048 | if (reg == 6) |
| @@ -897,40 +1057,10 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 8 unmodified lines |
| 897 | 1057 | |
| 898 | 1058 | pc = 0; |
| 899 | 1059 | if (pa->instr_type & OPC_MODRM) { |
| 900 | | − if (!nb_ops) { |
| 901 | | − /* A modrm opcode without operands is a special case (e.g. mfence). |
| 902 | | − It has a group and acts as if there's an register operand 0 |
| 903 | | − (ax). */ |
| 904 | | − i = 0; |
| 905 | | − ops[i].type = OP_REG; |
| 906 | | − ops[i].reg = 0; |
| 907 | | − goto modrm_found; |
| 908 | | − } |
| 909 | | − /* first look for an ea operand */ |
| 910 | | − for(i = 0;i < nb_ops; i++) { |
| 911 | | − if (op_type[i] & OP_EA) |
| 912 | | − goto modrm_found; |
| 913 | | − } |
| 914 | | − /* then if not found, a register or indirection (shift instructions) */ |
| 915 | | − for(i = 0;i < nb_ops; i++) { |
| 916 | | − if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) |
| 917 | | − goto modrm_found; |
| 918 | | − } |
| 919 | | −#ifdef ASM_DEBUG |
| 920 | | − tcc_error("bad op table"); |
| 921 | | −#endif |
| 922 | | − modrm_found: |
| 923 | | − modrm_index = i; |
| 924 | 1060 | /* if a register is used in another operand then it is |
| 925 | 1061 | used instead of group */ |
| 926 | | − for(i = 0;i < nb_ops; i++) { |
| 927 | | − v = op_type[i]; |
| 928 | | − if (i != modrm_index && |
| 929 | | − (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { |
| 930 | | − reg = ops[i].reg; |
| 931 | | − break; |
| 932 | | − } |
| 933 | | − } |
| 1062 | + if (modreg_index >= 0) |
| 1063 | + reg = ops[modreg_index].reg; |
| 934 | 1064 | pc = asm_modrm(reg, &ops[modrm_index]); |
| 935 | 1065 | } |
| 936 | 1066 | |