History

x86-64-asm: Support high registers %r8 - %r15

This requires correctly handling the REX prefix. As bonus we now also support the four 8bit registers spl,bpl,sil,dil, which are decoded as ah,ch,dh,bh in non-long-mode (and require a REX prefix as well).

Michael Matz committed 10 years ago

Commit 4cb70471 parent 87658263 files · +215 −43Browse files

Unified · Split

3 files
  1. i386-asm.c +170 −40
  2. i386-tok.h +8 −0
  3. tests/asmtest.S +37 −3
i386-asm.c+170−40
@@ -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_64125#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#else130#else
126# define OP_REG64 0131# define OP_REG64 0
132# define OP_REG8_LOW 0
127# define OP_IM64 0133# define OP_IM64 0
128# define OP_EA32 0134# define OP_EA32 0
129#endif135#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
282static 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
275static int asm_parse_reg(int *type)314static 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_64324#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#endif334#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#endif526#endif
476 gen_expr32(&op->e);527 gen_expr32(&op->e);
477#ifdef TCC_TARGET_X86_64528#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
576static 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
519static void maybe_print_stats (void)633static 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
558ST_FUNC void asm_opcode(TCCState *s1, int opcode)672ST_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_64894#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_popq912 && 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#endif917#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 is1060 /* 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
i386-tok.h+8−0
@@ -93,6 +93,14 @@ · 92 unmodified lines
93 DEF_ASM(st)93 DEF_ASM(st)
94 DEF_ASM(rip)94 DEF_ASM(rip)
95 95
96#ifdef TCC_TARGET_X86_64
97 /* The four low parts of sp/bp/si/di that exist only on
98 x86-64 (encoding aliased to ah,ch,dh,dh when not using REX). */
99 DEF_ASM(spl)
100 DEF_ASM(bpl)
101 DEF_ASM(sil)
102 DEF_ASM(dil)
103#endif
96 /* generic two operands */104 /* generic two operands */
97 DEF_BWLX(mov)105 DEF_BWLX(mov)
98 106
tests/asmtest.S+37−3
@@ -76,6 +76,22 @@ movq %dr6, %rax · 75 unmodified lines
76movl %fs, %ecx76movl %fs, %ecx
77movl %ebx, %fs77movl %ebx, %fs
78 78
79#ifdef __x86_64__
80movq %r8, %r9
81movq %r10, %r11
82movq %r12, %r13
83movq %r14, %r15
84movq %rax, %r9
85movq %r15, %rsi
86inc %r9b
87dec %r10w
88not %r11d
89negq %r12
90decb %r13b
91incw %r14w
92notl %r15d
93#endif
94
79 movsbl 0x1000, %eax95 movsbl 0x1000, %eax
80 movsbw 0x1000, %ax96 movsbw 0x1000, %ax
81 movswl 0x1000, %eax97 movswl 0x1000, %eax
@@ -184,6 +200,24 @@ addl $0x123, (%ebp) · 102 unmodified lines
184addl $0x123, (%esp)200addl $0x123, (%esp)
185cmpl $0x123, (%esp)201cmpl $0x123, (%esp)
186 202
203#ifdef __x86_64__
204xor %bl,%ah
205xor %bl,%r8b
206xor %r9b,%bl
207xor %sil,%cl
208add %eax,(%r8d)
209add %ebx,(%r9)
210add %edx,(%r10d,%r11d)
211add %ecx,(%r12,%r13)
212add %esi,(%r14,%r15,4)
213add %edi,0x1000(%rbx,%r12,8)
214add %r11,0x1000(%ebp,%r9d,8)
215movb $12, %ah
216movb $13, %bpl
217movb $14, %dil
218movb $15, %r12b
219#endif
220
187add %eax, (%ebx)221add %eax, (%ebx)
188add (%ebx), %eax222add (%ebx), %eax
189 223
@@ -796,10 +830,10 @@ nop · 606 unmodified lines
796 movd %rdi, %xmm2830 movd %rdi, %xmm2
797 movd (%rbx), %mm3831 movd (%rbx), %mm3
798 movd (%rbx), %xmm3832 movd (%rbx), %xmm3
799 movd %mm1, %rsi833 movd %mm1, %r12
800 movd %xmm2, %rdi834 movd %xmm2, %rdi
801 movd %mm3, (%rdx)835 movd %mm3, (%r8)
802 movd %xmm3, (%rdx)836 movd %xmm3, (%r13)
803#endif837#endif
804 838
805 movq (%ebp), %mm1839 movq (%ebp), %mm1

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t