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
7272 OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */
7373 OPT_SEG,
7474 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
7579 OPT_IM8,
7680 OPT_IM8S,
7781 OPT_IM16,
@@ -120,10 +124,12 @@ enum { · 42 unmodified lines
120124 #define OP_INDIR (1 << OPT_INDIR)
121125 #ifdef TCC_TARGET_X86_64
122126 # define OP_REG64 (1 << OPT_REG64)
127+# define OP_REG8_LOW (1 << OPT_REG8_LOW)
123128 # define OP_IM64 (1 << OPT_IM64)
124129 # define OP_EA32 (OP_EA << 1)
125130 #else
126131 # define OP_REG64 0
132+# define OP_REG8_LOW 0
127133 # define OP_IM64 0
128134 # define OP_EA32 0
129135 #endif
@@ -272,6 +278,39 @@ static inline int get_reg_shift(TCCState *s1) · 142 unmodified lines
272278 return shift;
273279 }
274280
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+
275314 static int asm_parse_reg(int *type)
276315 {
277316 int reg = 0;
@@ -281,12 +320,17 @@ static int asm_parse_reg(int *type) · 3 unmodified lines
281320 next();
282321 if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
283322 reg = tok - TOK_ASM_eax;
323+ *type = OP_REG32;
284324 #ifdef TCC_TARGET_X86_64
285− *type = OP_EA32;
286325 } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
287326 reg = tok - TOK_ASM_rax;
327+ *type = OP_REG64;
288328 } 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+ ;
290334 #endif
291335 } else {
292336 error_32:
@@ -345,6 +389,13 @@ static void parse_operand(TCCState *s1, Operand *op) · 52 unmodified lines
345389 if (op->reg == 0)
346390 op->type |= OP_ST0;
347391 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
348399 } else {
349400 reg_error:
350401 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
411462 op->shift = get_reg_shift(s1);
412463 }
413464 }
414− if (type & OP_EA32)
465+ if (type & OP_REG32)
415466 op->type |= OP_EA32;
416467 skip(')');
417468 }
@@ -475,7 +526,7 @@ static inline int asm_modrm(int reg, Operand *op) · 57 unmodified lines
475526 #endif
476527 gen_expr32(&op->e);
477528 #ifdef TCC_TARGET_X86_64
478− } else if (op->reg == 8) {
529+ } else if (op->reg == -2) {
479530 ExprValue *pe = &op->e;
480531 g(0x05 + (reg << 3));
481532 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
516567 return 0;
517568 }
518569
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+
519633 static void maybe_print_stats (void)
520634 {
521635 static int already = 1;
@@ -558,13 +672,16 @@ static void maybe_print_stats (void) · 36 unmodified lines
558672 ST_FUNC void asm_opcode(TCCState *s1, int opcode)
559673 {
560674 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;
562676 int nb_ops, s;
563677 Operand ops[MAX_OPERANDS], *pop;
564678 int op_type[3]; /* decoded op type */
565679 int alltypes; /* OR of all operand types */
566680 int autosize;
567681 int p66;
682+#ifdef TCC_TARGET_X86_64
683+ int rex64;
684+#endif
568685
569686 maybe_print_stats();
570687 /* 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
775892 if (p66)
776893 g(0x66);
777894 #ifdef TCC_TARGET_X86_64
895+ rex64 = 0;
778896 if (s == 3 || (alltypes & OP_REG64)) {
779897 /* generate REX prefix */
780898 int default64 = 0;
@@ -794,7 +912,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 13 unmodified lines
794912 && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq
795913 && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp))
796914 && !default64)
797− g(0x48);
915+ rex64 = 1;
798916 }
799917 #endif
800918
@@ -830,6 +948,50 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 29 unmodified lines
830948 /* fpu arith case */
831949 v += ((opcode - pa->sym) / 6) << 3;
832950 }
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+
833995 if (pa->instr_type & OPC_REG) {
834996 /* mov $im, %reg case */
835997 if (v == 0xb0 && s >= 1)
@@ -881,8 +1043,6 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 45 unmodified lines
8811043 g(op1);
8821044 g(v);
8831045
884− /* search which operand will used for modrm */
885− modrm_index = 0;
8861046 if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
8871047 reg = (opcode - pa->sym) / NBWLX;
8881048 if (reg == 6)
@@ -897,40 +1057,10 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 8 unmodified lines
8971057
8981058 pc = 0;
8991059 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;
9241060 /* if a register is used in another operand then it is
9251061 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;
9341064 pc = asm_modrm(reg, &ops[modrm_index]);
9351065 }
9361066
i386-tok.h+8−0
@@ -93,6 +93,14 @@ · 92 unmodified lines
9393 DEF_ASM(st)
9494 DEF_ASM(rip)
9595
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
96104 /* generic two operands */
97105 DEF_BWLX(mov)
98106
tests/asmtest.S+37−3
@@ -76,6 +76,22 @@ movq %dr6, %rax · 75 unmodified lines
7676 movl %fs, %ecx
7777 movl %ebx, %fs
7878
79+#ifdef __x86_64__
80+movq %r8, %r9
81+movq %r10, %r11
82+movq %r12, %r13
83+movq %r14, %r15
84+movq %rax, %r9
85+movq %r15, %rsi
86+inc %r9b
87+dec %r10w
88+not %r11d
89+negq %r12
90+decb %r13b
91+incw %r14w
92+notl %r15d
93+#endif
94+
7995 movsbl 0x1000, %eax
8096 movsbw 0x1000, %ax
8197 movswl 0x1000, %eax
@@ -184,6 +200,24 @@ addl $0x123, (%ebp) · 102 unmodified lines
184200 addl $0x123, (%esp)
185201 cmpl $0x123, (%esp)
186202
203+#ifdef __x86_64__
204+xor %bl,%ah
205+xor %bl,%r8b
206+xor %r9b,%bl
207+xor %sil,%cl
208+add %eax,(%r8d)
209+add %ebx,(%r9)
210+add %edx,(%r10d,%r11d)
211+add %ecx,(%r12,%r13)
212+add %esi,(%r14,%r15,4)
213+add %edi,0x1000(%rbx,%r12,8)
214+add %r11,0x1000(%ebp,%r9d,8)
215+movb $12, %ah
216+movb $13, %bpl
217+movb $14, %dil
218+movb $15, %r12b
219+#endif
220+
187221 add %eax, (%ebx)
188222 add (%ebx), %eax
189223
@@ -796,10 +830,10 @@ nop · 606 unmodified lines
796830 movd %rdi, %xmm2
797831 movd (%rbx), %mm3
798832 movd (%rbx), %xmm3
799− movd %mm1, %rsi
833+ movd %mm1, %r12
800834 movd %xmm2, %rdi
801− movd %mm3, (%rdx)
802− movd %xmm3, (%rdx)
835+ movd %mm3, (%r8)
836+ movd %xmm3, (%r13)
803837 #endif
804838
805839 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