| @@ -72,6 +72,10 @@ enum { · 71 unmodified lines |
| 72 | OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ | 72 | OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ |
| 73 | OPT_SEG, | 73 | OPT_SEG, |
| 74 | OPT_ST, | 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 | OPT_IM8, | 79 | OPT_IM8, |
| 76 | OPT_IM8S, | 80 | OPT_IM8S, |
| 77 | OPT_IM16, | 81 | OPT_IM16, |
| @@ -120,10 +124,12 @@ enum { · 42 unmodified lines |
| 120 | #define OP_INDIR (1 << OPT_INDIR) | 124 | #define OP_INDIR (1 << OPT_INDIR) |
| 121 | #ifdef TCC_TARGET_X86_64 | 125 | #ifdef TCC_TARGET_X86_64 |
| 122 | # define OP_REG64 (1 << OPT_REG64) | 126 | # define OP_REG64 (1 << OPT_REG64) |
| | 127 | # define OP_REG8_LOW (1 << OPT_REG8_LOW) |
| 123 | # define OP_IM64 (1 << OPT_IM64) | 128 | # define OP_IM64 (1 << OPT_IM64) |
| 124 | # define OP_EA32 (OP_EA << 1) | 129 | # define OP_EA32 (OP_EA << 1) |
| 125 | #else | 130 | #else |
| 126 | # define OP_REG64 0 | 131 | # define OP_REG64 0 |
| | 132 | # define OP_REG8_LOW 0 |
| 127 | # define OP_IM64 0 | 133 | # define OP_IM64 0 |
| 128 | # define OP_EA32 0 | 134 | # define OP_EA32 0 |
| 129 | #endif | 135 | #endif |
| @@ -272,6 +278,39 @@ static inline int get_reg_shift(TCCState *s1) · 142 unmodified lines |
| 272 | return shift; | 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 | static int asm_parse_reg(int *type) | 314 | static int asm_parse_reg(int *type) |
| 276 | { | 315 | { |
| 277 | int reg = 0; | 316 | int reg = 0; |
| @@ -281,12 +320,17 @@ static int asm_parse_reg(int *type) · 3 unmodified lines |
| 281 | next(); | 320 | next(); |
| 282 | if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { | 321 | if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
| 283 | reg = tok - TOK_ASM_eax; | 322 | reg = tok - TOK_ASM_eax; |
| | 323 | *type = OP_REG32; |
| 284 | #ifdef TCC_TARGET_X86_64 | 324 | #ifdef TCC_TARGET_X86_64 |
| 285 | *type = OP_EA32; | | |
| 286 | } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { | 325 | } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { |
| 287 | reg = tok - TOK_ASM_rax; | 326 | reg = tok - TOK_ASM_rax; |
| | 327 | *type = OP_REG64; |
| 288 | } else if (tok == TOK_ASM_rip) { | 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 | #endif | 334 | #endif |
| 291 | } else { | 335 | } else { |
| 292 | error_32: | 336 | error_32: |
| @@ -345,6 +389,13 @@ static void parse_operand(TCCState *s1, Operand *op) · 52 unmodified lines |
| 345 | if (op->reg == 0) | 389 | if (op->reg == 0) |
| 346 | op->type |= OP_ST0; | 390 | op->type |= OP_ST0; |
| 347 | goto no_skip; | 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 | } else { | 399 | } else { |
| 349 | reg_error: | 400 | reg_error: |
| 350 | tcc_error("unknown register %%%s", get_tok_str(tok, &tokc)); | 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 | op->shift = get_reg_shift(s1); | 462 | op->shift = get_reg_shift(s1); |
| 412 | } | 463 | } |
| 413 | } | 464 | } |
| 414 | if (type & OP_EA32) | 465 | if (type & OP_REG32) |
| 415 | op->type |= OP_EA32; | 466 | op->type |= OP_EA32; |
| 416 | skip(')'); | 467 | skip(')'); |
| 417 | } | 468 | } |
| @@ -475,7 +526,7 @@ static inline int asm_modrm(int reg, Operand *op) · 57 unmodified lines |
| 475 | #endif | 526 | #endif |
| 476 | gen_expr32(&op->e); | 527 | gen_expr32(&op->e); |
| 477 | #ifdef TCC_TARGET_X86_64 | 528 | #ifdef TCC_TARGET_X86_64 |
| 478 | } else if (op->reg == 8) { | 529 | } else if (op->reg == -2) { |
| 479 | ExprValue *pe = &op->e; | 530 | ExprValue *pe = &op->e; |
| 480 | g(0x05 + (reg << 3)); | 531 | g(0x05 + (reg << 3)); |
| 481 | gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); | 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 | return 0; | 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 | static void maybe_print_stats (void) | 633 | static void maybe_print_stats (void) |
| 520 | { | 634 | { |
| 521 | static int already = 1; | 635 | static int already = 1; |
| @@ -558,13 +672,16 @@ static void maybe_print_stats (void) · 36 unmodified lines |
| 558 | ST_FUNC void asm_opcode(TCCState *s1, int opcode) | 672 | ST_FUNC void asm_opcode(TCCState *s1, int opcode) |
| 559 | { | 673 | { |
| 560 | const ASMInstr *pa; | 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 | int nb_ops, s; | 676 | int nb_ops, s; |
| 563 | Operand ops[MAX_OPERANDS], *pop; | 677 | Operand ops[MAX_OPERANDS], *pop; |
| 564 | int op_type[3]; /* decoded op type */ | 678 | int op_type[3]; /* decoded op type */ |
| 565 | int alltypes; /* OR of all operand types */ | 679 | int alltypes; /* OR of all operand types */ |
| 566 | int autosize; | 680 | int autosize; |
| 567 | int p66; | 681 | int p66; |
| | 682 | #ifdef TCC_TARGET_X86_64 |
| | 683 | int rex64; |
| | 684 | #endif |
| 568 | | 685 | |
| 569 | maybe_print_stats(); | 686 | maybe_print_stats(); |
| 570 | /* force synthetic ';' after prefix instruction, so we can handle */ | 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 | if (p66) | 892 | if (p66) |
| 776 | g(0x66); | 893 | g(0x66); |
| 777 | #ifdef TCC_TARGET_X86_64 | 894 | #ifdef TCC_TARGET_X86_64 |
| | 895 | rex64 = 0; |
| 778 | if (s == 3 || (alltypes & OP_REG64)) { | 896 | if (s == 3 || (alltypes & OP_REG64)) { |
| 779 | /* generate REX prefix */ | 897 | /* generate REX prefix */ |
| 780 | int default64 = 0; | 898 | int default64 = 0; |
| @@ -794,7 +912,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 13 unmodified lines |
| 794 | && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq | 912 | && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq |
| 795 | && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp)) | 913 | && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp)) |
| 796 | && !default64) | 914 | && !default64) |
| 797 | g(0x48); | 915 | rex64 = 1; |
| 798 | } | 916 | } |
| 799 | #endif | 917 | #endif |
| 800 | | 918 | |
| @@ -830,6 +948,50 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 29 unmodified lines |
| 830 | /* fpu arith case */ | 948 | /* fpu arith case */ |
| 831 | v += ((opcode - pa->sym) / 6) << 3; | 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 | if (pa->instr_type & OPC_REG) { | 995 | if (pa->instr_type & OPC_REG) { |
| 834 | /* mov $im, %reg case */ | 996 | /* mov $im, %reg case */ |
| 835 | if (v == 0xb0 && s >= 1) | 997 | if (v == 0xb0 && s >= 1) |
| @@ -881,8 +1043,6 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 45 unmodified lines |
| 881 | g(op1); | 1043 | g(op1); |
| 882 | g(v); | 1044 | g(v); |
| 883 | | 1045 | |
| 884 | /* search which operand will used for modrm */ | | |
| 885 | modrm_index = 0; | | |
| 886 | if (OPCT_IS(pa->instr_type, OPC_SHIFT)) { | 1046 | if (OPCT_IS(pa->instr_type, OPC_SHIFT)) { |
| 887 | reg = (opcode - pa->sym) / NBWLX; | 1047 | reg = (opcode - pa->sym) / NBWLX; |
| 888 | if (reg == 6) | 1048 | if (reg == 6) |
| @@ -897,40 +1057,10 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 8 unmodified lines |
| 897 | | 1057 | |
| 898 | pc = 0; | 1058 | pc = 0; |
| 899 | if (pa->instr_type & OPC_MODRM) { | 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 | /* if a register is used in another operand then it is | 1060 | /* if a register is used in another operand then it is |
| 925 | used instead of group */ | 1061 | used instead of group */ |
| 926 | for(i = 0;i < nb_ops; i++) { | 1062 | if (modreg_index >= 0) |
| 927 | v = op_type[i]; | 1063 | reg = ops[modreg_index].reg; |
| 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 | } | | |
| 934 | pc = asm_modrm(reg, &ops[modrm_index]); | 1064 | pc = asm_modrm(reg, &ops[modrm_index]); |
| 935 | } | 1065 | } |
| 936 | | 1066 | |