diff --git a/i386-asm.c b/i386-asm.c index a62a4cf..3612627 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -21,9 +21,7 @@ #include "tcc.h" -/* #define NB_ASM_REGS 8 */ #define MAX_OPERANDS 3 -#define NB_SAVED_REGS 3 #define TOK_ASM_first TOK_ASM_clc #define TOK_ASM_last TOK_ASM_emms @@ -279,11 +277,11 @@ static inline int get_reg_shift(TCCState *s1) } #ifdef TCC_TARGET_X86_64 -static int asm_parse_numeric_reg(int *type) +static int asm_parse_numeric_reg(int t, int *type) { int reg = -1; - if (tok >= TOK_IDENT && tok < tok_ident) { - const char *s = table_ident[tok - TOK_IDENT]->str; + if (t >= TOK_IDENT && t < tok_ident) { + const char *s = table_ident[t - TOK_IDENT]->str; char c; *type = OP_REG64; if (*s == 'c') { @@ -335,7 +333,7 @@ static int asm_parse_reg(int *type) } else if (tok == TOK_ASM_rip) { reg = -2; /* Probably should use different escape code. */ *type = OP_REG64; - } else if ((reg = asm_parse_numeric_reg(type)) >= 0 + } else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0 && (*type == OP_REG32 || *type == OP_REG64)) { ; #endif @@ -400,7 +398,7 @@ static void parse_operand(TCCState *s1, Operand *op) } else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) { op->type = OP_REG8 | OP_REG8_LOW; op->reg = 4 + tok - TOK_ASM_spl; - } else if ((op->reg = asm_parse_numeric_reg(&op->type)) >= 0) { + } else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) { ; #endif } else { @@ -1586,7 +1584,18 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, uint8_t regs_allocated[NB_ASM_REGS]; ASMOperand *op; int i, reg; - static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; + + /* Strictly speaking %Xbp and %Xsp should be included in the + call-preserved registers, but currently it doesn't matter. */ +#ifdef TCC_TARGET_X86_64 +#ifdef TCC_TARGET_PE + static uint8_t reg_saved[] = { 3, 6, 7, 12, 13, 14, 15 }; +#else + static uint8_t reg_saved[] = { 3, 12, 13, 14, 15 }; +#endif +#else + static uint8_t reg_saved[] = { 3, 6, 7 }; +#endif /* mark all used registers */ memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); @@ -1597,9 +1606,11 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, } if (!is_output) { /* generate reg save code */ - for(i = 0; i < NB_SAVED_REGS; i++) { + for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) { reg = reg_saved[i]; if (regs_allocated[reg]) { + if (reg >= 8) + g(0x41), reg-=8; g(0x50 + reg); } } @@ -1658,9 +1669,11 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, } } /* generate reg restore code */ - for(i = NB_SAVED_REGS - 1; i >= 0; i--) { + for(i = sizeof(reg_saved)/sizeof(reg_saved[0]) - 1; i >= 0; i--) { reg = reg_saved[i]; if (regs_allocated[reg]) { + if (reg >= 8) + g(0x41), reg-=8; g(0x58 + reg); } } @@ -1671,6 +1684,9 @@ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) { int reg; TokenSym *ts; +#ifdef TCC_TARGET_X86_64 + int type; +#endif if (!strcmp(str, "memory") || !strcmp(str, "cc") || @@ -1685,16 +1701,11 @@ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) #ifdef TCC_TARGET_X86_64 } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { reg -= TOK_ASM_rax; - } else if (1 && str[0] == 'r' && - (((str[1] == '8' || str[1] == '9') && str[2] == 0) || - (str[1] == '1' && str[2] >= '0' && str[2] <= '5' && - str[3] == 0))) { - /* Do nothing for now. We can't parse the high registers. */ - goto end; + } else if ((reg = asm_parse_numeric_reg(reg, &type)) >= 0) { + ; #endif } else { tcc_error("invalid clobber register '%s'", str); } clobber_regs[reg] = 1; -end:; } diff --git a/tests/tcctest.c b/tests/tcctest.c index d4a6a5a..5124a4f 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2925,6 +2925,29 @@ void fancy_copy2 (unsigned *in, unsigned *out) asm volatile ("mov %0,(%1)" : : "r" (*in), "r" (out) : "memory"); } +#ifdef __x86_64__ +void clobber_r12(void) +{ + asm volatile("mov $1, %%r12" ::: "r12"); +} +#endif + +void test_high_clobbers(void) +{ +#ifdef __x86_64__ + register long val asm("r12"); + long val2; + /* This tests if asm clobbers correctly save/restore callee saved + registers if they are clobbered and if it's the high 8 x86-64 + registers. This is fragile for GCC as the constraints do not + correctly capture the data flow, but good enough for us. */ + asm volatile("mov $0x4542, %%r12" : "=r" (val):: "memory"); + clobber_r12(); + asm volatile("mov %%r12, %0" : "=r" (val2) : "r" (val): "memory"); + printf("asmhc: 0x%x\n", val2); +#endif +} + void asm_test(void) { char buf[128]; @@ -3004,6 +3027,7 @@ void asm_test(void) printf ("fancycpy2(%d)=%d\n", val, val2); asm volatile ("mov $0x4243, %%esi" : "=r" (regvar)); printf ("regvar=%x\n", regvar); + test_high_clobbers(); return; label1: goto label2; diff --git a/x86_64-gen.c b/x86_64-gen.c index 35ec1ab..408bb91 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -24,7 +24,7 @@ /* number of available registers */ #define NB_REGS 25 -#define NB_ASM_REGS 8 +#define NB_ASM_REGS 16 /* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does