|
|
@ -253,10 +253,10 @@ static const uint16_t op0_codes[] = { |
|
|
|
#endif |
|
|
|
}; |
|
|
|
|
|
|
|
static inline int get_reg_shift(TCCState *S) |
|
|
|
static inline int get_reg_shift(TCCState *s1) |
|
|
|
{ |
|
|
|
int shift, v; |
|
|
|
v = asm_int_expr(S); |
|
|
|
v = asm_int_expr(s1); |
|
|
|
switch(v) { |
|
|
|
case 1: |
|
|
|
shift = 0; |
|
|
@ -271,7 +271,7 @@ static inline int get_reg_shift(TCCState *S) |
|
|
|
shift = 3; |
|
|
|
break; |
|
|
|
default: |
|
|
|
expect(S, "1, 2, 4 or 8 constant"); |
|
|
|
expect("1, 2, 4 or 8 constant"); |
|
|
|
shift = 0; |
|
|
|
break; |
|
|
|
} |
|
|
@ -279,11 +279,11 @@ static inline int get_reg_shift(TCCState *S) |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
static int asm_parse_numeric_reg(TCCState *S, int t, unsigned int *type) |
|
|
|
static int asm_parse_numeric_reg(int t, unsigned int *type) |
|
|
|
{ |
|
|
|
int reg = -1; |
|
|
|
if (t >= TOK_IDENT && t < S->tccpp_tok_ident) { |
|
|
|
const char *s = S->tccpp_table_ident[t - 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') { |
|
|
@ -318,51 +318,51 @@ static int asm_parse_numeric_reg(TCCState *S, int t, unsigned int *type) |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
static int asm_parse_reg(TCCState* S, unsigned int *type) |
|
|
|
static int asm_parse_reg(unsigned int *type) |
|
|
|
{ |
|
|
|
int reg = 0; |
|
|
|
*type = 0; |
|
|
|
if (S->tccpp_tok != '%') |
|
|
|
if (tok != '%') |
|
|
|
goto error_32; |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok >= TOK_ASM_eax && S->tccpp_tok <= TOK_ASM_edi) { |
|
|
|
reg = S->tccpp_tok - TOK_ASM_eax; |
|
|
|
next(); |
|
|
|
if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { |
|
|
|
reg = tok - TOK_ASM_eax; |
|
|
|
*type = OP_REG32; |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
} else if (S->tccpp_tok >= TOK_ASM_rax && S->tccpp_tok <= TOK_ASM_rdi) { |
|
|
|
reg = S->tccpp_tok - TOK_ASM_rax; |
|
|
|
} else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { |
|
|
|
reg = tok - TOK_ASM_rax; |
|
|
|
*type = OP_REG64; |
|
|
|
} else if (S->tccpp_tok == TOK_ASM_rip) { |
|
|
|
} else if (tok == TOK_ASM_rip) { |
|
|
|
reg = -2; /* Probably should use different escape code. */ |
|
|
|
*type = OP_REG64; |
|
|
|
} else if ((reg = asm_parse_numeric_reg(S, S->tccpp_tok, type)) >= 0 |
|
|
|
} else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0 |
|
|
|
&& (*type == OP_REG32 || *type == OP_REG64)) { |
|
|
|
; |
|
|
|
#endif |
|
|
|
} else { |
|
|
|
error_32: |
|
|
|
expect(S, "register"); |
|
|
|
expect("register"); |
|
|
|
} |
|
|
|
next(S); |
|
|
|
next(); |
|
|
|
return reg; |
|
|
|
} |
|
|
|
|
|
|
|
static void parse_operand(TCCState *S, Operand *op) |
|
|
|
static void parse_operand(TCCState *s1, Operand *op) |
|
|
|
{ |
|
|
|
ExprValue e; |
|
|
|
int reg, indir; |
|
|
|
const char *p; |
|
|
|
|
|
|
|
indir = 0; |
|
|
|
if (S->tccpp_tok == '*') { |
|
|
|
next(S); |
|
|
|
if (tok == '*') { |
|
|
|
next(); |
|
|
|
indir = OP_INDIR; |
|
|
|
} |
|
|
|
|
|
|
|
if (S->tccpp_tok == '%') { |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok >= TOK_ASM_al && S->tccpp_tok <= TOK_ASM_db7) { |
|
|
|
reg = S->tccpp_tok - TOK_ASM_al; |
|
|
|
if (tok == '%') { |
|
|
|
next(); |
|
|
|
if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { |
|
|
|
reg = tok - TOK_ASM_al; |
|
|
|
op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ |
|
|
|
op->reg = reg & 7; |
|
|
|
if ((op->type & OP_REG) && op->reg == TREG_XAX) |
|
|
@ -371,48 +371,48 @@ static void parse_operand(TCCState *S, Operand *op) |
|
|
|
op->type |= OP_CL; |
|
|
|
else if (op->type == OP_REG16 && op->reg == TREG_XDX) |
|
|
|
op->type |= OP_DX; |
|
|
|
} else if (S->tccpp_tok >= TOK_ASM_dr0 && S->tccpp_tok <= TOK_ASM_dr7) { |
|
|
|
} else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { |
|
|
|
op->type = OP_DB; |
|
|
|
op->reg = S->tccpp_tok - TOK_ASM_dr0; |
|
|
|
} else if (S->tccpp_tok >= TOK_ASM_es && S->tccpp_tok <= TOK_ASM_gs) { |
|
|
|
op->reg = tok - TOK_ASM_dr0; |
|
|
|
} else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { |
|
|
|
op->type = OP_SEG; |
|
|
|
op->reg = S->tccpp_tok - TOK_ASM_es; |
|
|
|
} else if (S->tccpp_tok == TOK_ASM_st) { |
|
|
|
op->reg = tok - TOK_ASM_es; |
|
|
|
} else if (tok == TOK_ASM_st) { |
|
|
|
op->type = OP_ST; |
|
|
|
op->reg = 0; |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok == '(') { |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok != TOK_PPNUM) |
|
|
|
next(); |
|
|
|
if (tok == '(') { |
|
|
|
next(); |
|
|
|
if (tok != TOK_PPNUM) |
|
|
|
goto reg_error; |
|
|
|
p = S->tccpp_tokc.str.data; |
|
|
|
p = tokc.str.data; |
|
|
|
reg = p[0] - '0'; |
|
|
|
if ((unsigned)reg >= 8 || p[1] != '\0') |
|
|
|
goto reg_error; |
|
|
|
op->reg = reg; |
|
|
|
next(S); |
|
|
|
skip(S, ')'); |
|
|
|
next(); |
|
|
|
skip(')'); |
|
|
|
} |
|
|
|
if (op->reg == 0) |
|
|
|
op->type |= OP_ST0; |
|
|
|
goto no_skip; |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
} else if (S->tccpp_tok >= TOK_ASM_spl && S->tccpp_tok <= TOK_ASM_dil) { |
|
|
|
} else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) { |
|
|
|
op->type = OP_REG8 | OP_REG8_LOW; |
|
|
|
op->reg = 4 + S->tccpp_tok - TOK_ASM_spl; |
|
|
|
} else if ((op->reg = asm_parse_numeric_reg(S, S->tccpp_tok, &op->type)) >= 0) { |
|
|
|
op->reg = 4 + tok - TOK_ASM_spl; |
|
|
|
} else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) { |
|
|
|
; |
|
|
|
#endif |
|
|
|
} else { |
|
|
|
reg_error: |
|
|
|
tcc_error(S, "unknown register %%%s", get_tok_str(S, S->tccpp_tok, &S->tccpp_tokc)); |
|
|
|
tcc_error("unknown register %%%s", get_tok_str(tok, &tokc)); |
|
|
|
} |
|
|
|
next(S); |
|
|
|
next(); |
|
|
|
no_skip: ; |
|
|
|
} else if (S->tccpp_tok == '$') { |
|
|
|
} else if (tok == '$') { |
|
|
|
/* constant value */ |
|
|
|
next(S); |
|
|
|
asm_expr(S, &e); |
|
|
|
next(); |
|
|
|
asm_expr(s1, &e); |
|
|
|
op->type = OP_IM32; |
|
|
|
op->e = e; |
|
|
|
if (!op->e.sym) { |
|
|
@ -433,45 +433,45 @@ static void parse_operand(TCCState *S, Operand *op) |
|
|
|
op->reg = -1; |
|
|
|
op->reg2 = -1; |
|
|
|
op->shift = 0; |
|
|
|
if (S->tccpp_tok != '(') { |
|
|
|
asm_expr(S, &e); |
|
|
|
if (tok != '(') { |
|
|
|
asm_expr(s1, &e); |
|
|
|
op->e = e; |
|
|
|
} else { |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok == '%') { |
|
|
|
unget_tok(S, '('); |
|
|
|
next(); |
|
|
|
if (tok == '%') { |
|
|
|
unget_tok('('); |
|
|
|
op->e.v = 0; |
|
|
|
op->e.sym = NULL; |
|
|
|
} else { |
|
|
|
/* bracketed offset expression */ |
|
|
|
asm_expr(S, &e); |
|
|
|
if (S->tccpp_tok != ')') |
|
|
|
expect(S, ")"); |
|
|
|
next(S); |
|
|
|
asm_expr(s1, &e); |
|
|
|
if (tok != ')') |
|
|
|
expect(")"); |
|
|
|
next(); |
|
|
|
op->e.v = e.v; |
|
|
|
op->e.sym = e.sym; |
|
|
|
} |
|
|
|
op->e.pcrel = 0; |
|
|
|
} |
|
|
|
if (S->tccpp_tok == '(') { |
|
|
|
if (tok == '(') { |
|
|
|
unsigned int type = 0; |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok != ',') { |
|
|
|
op->reg = asm_parse_reg(S, &type); |
|
|
|
next(); |
|
|
|
if (tok != ',') { |
|
|
|
op->reg = asm_parse_reg(&type); |
|
|
|
} |
|
|
|
if (S->tccpp_tok == ',') { |
|
|
|
next(S); |
|
|
|
if (S->tccpp_tok != ',') { |
|
|
|
op->reg2 = asm_parse_reg(S, &type); |
|
|
|
if (tok == ',') { |
|
|
|
next(); |
|
|
|
if (tok != ',') { |
|
|
|
op->reg2 = asm_parse_reg(&type); |
|
|
|
} |
|
|
|
if (S->tccpp_tok == ',') { |
|
|
|
next(S); |
|
|
|
op->shift = get_reg_shift(S); |
|
|
|
if (tok == ',') { |
|
|
|
next(); |
|
|
|
op->shift = get_reg_shift(s1); |
|
|
|
} |
|
|
|
} |
|
|
|
if (type & OP_REG32) |
|
|
|
op->type |= OP_EA32; |
|
|
|
skip(S, ')'); |
|
|
|
skip(')'); |
|
|
|
} |
|
|
|
if (op->reg == -1 && op->reg2 == -1) |
|
|
|
op->type |= OP_ADDR; |
|
|
@ -480,65 +480,65 @@ static void parse_operand(TCCState *S, Operand *op) |
|
|
|
} |
|
|
|
|
|
|
|
/* XXX: unify with C code output ? */ |
|
|
|
ST_FUNC void gen_expr32(TCCState* S, ExprValue *pe) |
|
|
|
ST_FUNC void gen_expr32(ExprValue *pe) |
|
|
|
{ |
|
|
|
if (pe->pcrel) |
|
|
|
/* If PC-relative, always set VT_SYM, even without symbol,
|
|
|
|
so as to force a relocation to be emitted. */ |
|
|
|
gen_addrpc32(S, VT_SYM, pe->sym, pe->v); |
|
|
|
gen_addrpc32(VT_SYM, pe->sym, pe->v); |
|
|
|
else |
|
|
|
gen_addr32(S, pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
ST_FUNC void gen_expr64(TCCState* S, ExprValue *pe) |
|
|
|
ST_FUNC void gen_expr64(ExprValue *pe) |
|
|
|
{ |
|
|
|
gen_addr64(S, pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
/* XXX: unify with C code output ? */ |
|
|
|
static void gen_disp32(TCCState* S, ExprValue *pe) |
|
|
|
static void gen_disp32(ExprValue *pe) |
|
|
|
{ |
|
|
|
Sym *sym = pe->sym; |
|
|
|
ElfSym *esym = elfsym(S, sym); |
|
|
|
ElfSym *esym = elfsym(sym); |
|
|
|
if (esym && esym->st_shndx == cur_text_section->sh_num) { |
|
|
|
/* same section: we can output an absolute value. Note
|
|
|
|
that the TCC compiler behaves differently here because |
|
|
|
it always outputs a relocation to ease (future) code |
|
|
|
elimination in the linker */ |
|
|
|
gen_le32(S, pe->v + esym->st_value - S->tccgen_ind - 4); |
|
|
|
gen_le32(pe->v + esym->st_value - ind - 4); |
|
|
|
} else { |
|
|
|
if (sym && sym->type.t == VT_VOID) { |
|
|
|
sym->type.t = VT_FUNC; |
|
|
|
sym->type.ref = NULL; |
|
|
|
} |
|
|
|
gen_addrpc32(S, VT_SYM, sym, pe->v); |
|
|
|
gen_addrpc32(VT_SYM, sym, pe->v); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* generate the modrm operand */ |
|
|
|
static inline int asm_modrm(TCCState* S, int reg, Operand *op) |
|
|
|
static inline int asm_modrm(int reg, Operand *op) |
|
|
|
{ |
|
|
|
int mod, reg1, reg2, sib_reg1; |
|
|
|
|
|
|
|
if (op->type & (OP_REG | OP_MMX | OP_SSE)) { |
|
|
|
g(S, 0xc0 + (reg << 3) + op->reg); |
|
|
|
g(0xc0 + (reg << 3) + op->reg); |
|
|
|
} else if (op->reg == -1 && op->reg2 == -1) { |
|
|
|
/* displacement only */ |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
g(S, 0x04 + (reg << 3)); |
|
|
|
g(S, 0x25); |
|
|
|
g(0x04 + (reg << 3)); |
|
|
|
g(0x25); |
|
|
|
#else |
|
|
|
g(S, 0x05 + (reg << 3)); |
|
|
|
g(0x05 + (reg << 3)); |
|
|
|
#endif |
|
|
|
gen_expr32(S, &op->e); |
|
|
|
gen_expr32(&op->e); |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
} else if (op->reg == -2) { |
|
|
|
ExprValue *pe = &op->e; |
|
|
|
g(S, 0x05 + (reg << 3)); |
|
|
|
gen_addrpc32(S, pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
return S->tccgen_ind; |
|
|
|
g(0x05 + (reg << 3)); |
|
|
|
gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); |
|
|
|
return ind; |
|
|
|
#endif |
|
|
|
} else { |
|
|
|
sib_reg1 = op->reg; |
|
|
@ -557,19 +557,19 @@ static inline int asm_modrm(TCCState* S, int reg, Operand *op) |
|
|
|
reg1 = op->reg; |
|
|
|
if (op->reg2 != -1) |
|
|
|
reg1 = 4; |
|
|
|
g(S, mod + (reg << 3) + reg1); |
|
|
|
g(mod + (reg << 3) + reg1); |
|
|
|
if (reg1 == 4) { |
|
|
|
/* add sib byte */ |
|
|
|
reg2 = op->reg2; |
|
|
|
if (reg2 == -1) |
|
|
|
reg2 = 4; /* indicate no index */ |
|
|
|
g(S, (op->shift << 6) + (reg2 << 3) + sib_reg1); |
|
|
|
g((op->shift << 6) + (reg2 << 3) + sib_reg1); |
|
|
|
} |
|
|
|
/* add offset */ |
|
|
|
if (mod == 0x40) { |
|
|
|
g(S, op->e.v); |
|
|
|
g(op->e.v); |
|
|
|
} else if (mod == 0x80 || op->reg == -1) { |
|
|
|
gen_expr32(S, &op->e); |
|
|
|
gen_expr32(&op->e); |
|
|
|
} |
|
|
|
} |
|
|
|
return 0; |
|
|
@ -581,7 +581,7 @@ static inline int asm_modrm(TCCState* S, int reg, Operand *op) |
|
|
|
#define REX_X 0x42 |
|
|
|
#define REX_B 0x41 |
|
|
|
|
|
|
|
static void asm_rex(TCCState* S, int width64, Operand *ops, int nb_ops, int *op_type, |
|
|
|
static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type, |
|
|
|
int regi, int rmi) |
|
|
|
{ |
|
|
|
unsigned char rex = width64 ? 0x48 : 0; |
|
|
@ -631,9 +631,9 @@ static void asm_rex(TCCState* S, int width64, Operand *ops, int nb_ops, int *op_ |
|
|
|
} |
|
|
|
if (rex) { |
|
|
|
if (saw_high_8bit) |
|
|
|
tcc_error(S, "can't encode register %%%ch when REX prefix is required", |
|
|
|
tcc_error("can't encode register %%%ch when REX prefix is required", |
|
|
|
"acdb"[saw_high_8bit-4]); |
|
|
|
g(S, rex); |
|
|
|
g(rex); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
@ -679,7 +679,7 @@ static void maybe_print_stats (void) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ST_FUNC void asm_opcode(TCCState *S, int opcode) |
|
|
|
ST_FUNC void asm_opcode(TCCState *s1, int opcode) |
|
|
|
{ |
|
|
|
const ASMInstr *pa; |
|
|
|
int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc; |
|
|
@ -697,7 +697,7 @@ ST_FUNC void asm_opcode(TCCState *S, int opcode) |
|
|
|
/* force synthetic ';' after prefix instruction, so we can handle */ |
|
|
|
/* one-line things like "rep stosb" instead of only "rep\nstosb" */ |
|
|
|
if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) |
|
|
|
unget_tok(S, ';'); |
|
|
|
unget_tok(';'); |
|
|
|
|
|
|
|
/* get operands */ |
|
|
|
pop = ops; |
|
|
@ -705,27 +705,27 @@ ST_FUNC void asm_opcode(TCCState *S, int opcode) |
|
|
|
seg_prefix = 0; |
|
|
|
alltypes = 0; |
|
|
|
for(;;) { |
|
|
|
if (S->tccpp_tok == ';' || S->tccpp_tok == TOK_LINEFEED) |
|
|
|
if (tok == ';' || tok == TOK_LINEFEED) |
|
|
|
break; |
|
|
|
if (nb_ops >= MAX_OPERANDS) { |
|
|
|
tcc_error(S, "incorrect number of operands"); |
|
|
|
tcc_error("incorrect number of operands"); |
|
|
|
} |
|
|
|
parse_operand(S, pop); |
|
|
|
if (S->tccpp_tok == ':') { |
|
|
|
parse_operand(s1, pop); |
|
|
|
if (tok == ':') { |
|
|
|
if (pop->type != OP_SEG || seg_prefix) |
|
|
|
tcc_error(S, "incorrect prefix"); |
|
|
|
tcc_error("incorrect prefix"); |
|
|
|
seg_prefix = segment_prefixes[pop->reg]; |
|
|
|
next(S); |
|
|
|
parse_operand(S, pop); |
|
|
|
next(); |
|
|
|
parse_operand(s1, pop); |
|
|
|
if (!(pop->type & OP_EA)) { |
|
|
|
tcc_error(S, "segment prefix must be followed by memory reference"); |
|
|
|
tcc_error("segment prefix must be followed by memory reference"); |
|
|
|
} |
|
|
|
} |
|
|
|
pop++; |
|
|
|
nb_ops++; |
|
|
|
if (S->tccpp_tok != ',') |
|
|
|
if (tok != ',') |
|
|
|
break; |
|
|
|
next(S); |
|
|
|
next(); |
|
|
|
} |
|
|
|
|
|
|
|
s = 0; /* avoid warning */ |
|
|
@ -842,23 +842,23 @@ again: |
|
|
|
int b; |
|
|
|
b = op0_codes[opcode - TOK_ASM_first]; |
|
|
|
if (b & 0xff00) |
|
|
|
g(S, b >> 8); |
|
|
|
g(S, b); |
|
|
|
g(b >> 8); |
|
|
|
g(b); |
|
|
|
return; |
|
|
|
} else if (opcode <= TOK_ASM_alllast) { |
|
|
|
tcc_error(S, "bad operand with opcode '%s'", |
|
|
|
get_tok_str(S, opcode, NULL)); |
|
|
|
tcc_error("bad operand with opcode '%s'", |
|
|
|
get_tok_str(opcode, NULL)); |
|
|
|
} else { |
|
|
|
/* Special case for cmovcc, we accept size suffixes but ignore
|
|
|
|
them, but we don't want them to blow up our tables. */ |
|
|
|
TokenSym *ts = S->tccpp_table_ident[opcode - TOK_IDENT]; |
|
|
|
TokenSym *ts = table_ident[opcode - TOK_IDENT]; |
|
|
|
if (ts->len >= 6 |
|
|
|
&& strchr("wlq", ts->str[ts->len-1]) |
|
|
|
&& !memcmp(ts->str, "cmov", 4)) { |
|
|
|
opcode = tok_alloc(S, ts->str, ts->len-1)->tok; |
|
|
|
opcode = tok_alloc(ts->str, ts->len-1)->tok; |
|
|
|
goto again; |
|
|
|
} |
|
|
|
tcc_error(S, "unknown opcode '%s'", ts->str); |
|
|
|
tcc_error("unknown opcode '%s'", ts->str); |
|
|
|
} |
|
|
|
} |
|
|
|
/* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ |
|
|
@ -886,7 +886,7 @@ again: |
|
|
|
(ops[0].type & OP_EA)) |
|
|
|
s = NBWLX - 2; |
|
|
|
else |
|
|
|
tcc_error(S, "cannot infer opcode suffix"); |
|
|
|
tcc_error("cannot infer opcode suffix"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -894,7 +894,7 @@ again: |
|
|
|
/* Generate addr32 prefix if needed */ |
|
|
|
for(i = 0; i < nb_ops; i++) { |
|
|
|
if (ops[i].type & OP_EA32) { |
|
|
|
g(S, 0x67); |
|
|
|
g(0x67); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -913,7 +913,7 @@ again: |
|
|
|
p66 = 1; |
|
|
|
} |
|
|
|
if (p66) |
|
|
|
g(S, 0x66); |
|
|
|
g(0x66); |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
rex64 = 0; |
|
|
|
if (pa->instr_type & OPC_48) |
|
|
@ -943,9 +943,9 @@ again: |
|
|
|
|
|
|
|
/* now generates the operation */ |
|
|
|
if (OPCT_IS(pa->instr_type, OPC_FWAIT)) |
|
|
|
g(S, 0x9b); |
|
|
|
g(0x9b); |
|
|
|
if (seg_prefix) |
|
|
|
g(S, seg_prefix); |
|
|
|
g(seg_prefix); |
|
|
|
|
|
|
|
v = pa->opcode; |
|
|
|
if (pa->instr_type & OPC_0F) |
|
|
@ -998,7 +998,7 @@ again: |
|
|
|
goto modrm_found; |
|
|
|
} |
|
|
|
#ifdef ASM_DEBUG |
|
|
|
tcc_error(S, "bad op table"); |
|
|
|
tcc_error("bad op table"); |
|
|
|
#endif |
|
|
|
modrm_found: |
|
|
|
modrm_index = i; |
|
|
@ -1014,7 +1014,7 @@ again: |
|
|
|
} |
|
|
|
} |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
asm_rex (S, rex64, ops, nb_ops, op_type, modreg_index, modrm_index); |
|
|
|
asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (pa->instr_type & OPC_REG) { |
|
|
@ -1035,10 +1035,10 @@ again: |
|
|
|
int jmp_disp; |
|
|
|
|
|
|
|
/* see if we can really generate the jump with a byte offset */ |
|
|
|
esym = elfsym(S, ops[0].e.sym); |
|
|
|
esym = elfsym(ops[0].e.sym); |
|
|
|
if (!esym || esym->st_shndx != cur_text_section->sh_num) |
|
|
|
goto no_short_jump; |
|
|
|
jmp_disp = ops[0].e.v + esym->st_value - S->tccgen_ind - 2 - (v >= 0xff); |
|
|
|
jmp_disp = ops[0].e.v + esym->st_value - ind - 2 - (v >= 0xff); |
|
|
|
if (jmp_disp == (int8_t)jmp_disp) { |
|
|
|
/* OK to generate jump */ |
|
|
|
ops[0].e.sym = 0; |
|
|
@ -1053,18 +1053,18 @@ again: |
|
|
|
else if (v == 0x70) /* jcc */ |
|
|
|
v += 0x0f10; |
|
|
|
else |
|
|
|
tcc_error(S, "invalid displacement"); |
|
|
|
tcc_error("invalid displacement"); |
|
|
|
} |
|
|
|
} |
|
|
|
if (OPCT_IS(pa->instr_type, OPC_TEST)) |
|
|
|
v += test_bits[opcode - pa->sym]; |
|
|
|
op1 = v >> 16; |
|
|
|
if (op1) |
|
|
|
g(S, op1); |
|
|
|
g(op1); |
|
|
|
op1 = (v >> 8) & 0xff; |
|
|
|
if (op1) |
|
|
|
g(S, op1); |
|
|
|
g(S, v); |
|
|
|
g(op1); |
|
|
|
g(v); |
|
|
|
|
|
|
|
if (OPCT_IS(pa->instr_type, OPC_SHIFT)) { |
|
|
|
reg = (opcode - pa->sym) / NBWLX; |
|
|
@ -1084,7 +1084,7 @@ again: |
|
|
|
used instead of group */ |
|
|
|
if (modreg_index >= 0) |
|
|
|
reg = ops[modreg_index].reg; |
|
|
|
pc = asm_modrm(S, reg, &ops[modrm_index]); |
|
|
|
pc = asm_modrm(reg, &ops[modrm_index]); |
|
|
|
} |
|
|
|
|
|
|
|
/* emit constants */ |
|
|
@ -1092,10 +1092,10 @@ again: |
|
|
|
if (!(pa->instr_type & OPC_0F) |
|
|
|
&& (pa->opcode == 0x9a || pa->opcode == 0xea)) { |
|
|
|
/* ljmp or lcall kludge */ |
|
|
|
gen_expr32(S, &ops[1].e); |
|
|
|
gen_expr32(&ops[1].e); |
|
|
|
if (ops[0].e.sym) |
|
|
|
tcc_error(S, "cannot relocate"); |
|
|
|
gen_le16(S, ops[0].e.v); |
|
|
|
tcc_error("cannot relocate"); |
|
|
|
gen_le16(ops[0].e.v); |
|
|
|
return; |
|
|
|
} |
|
|
|
#endif |
|
|
@ -1116,32 +1116,32 @@ again: |
|
|
|
} |
|
|
|
|
|
|
|
if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym) |
|
|
|
tcc_error(S, "cannot relocate"); |
|
|
|
tcc_error("cannot relocate"); |
|
|
|
|
|
|
|
if (v & (OP_IM8 | OP_IM8S)) { |
|
|
|
g(S, ops[i].e.v); |
|
|
|
g(ops[i].e.v); |
|
|
|
} else if (v & OP_IM16) { |
|
|
|
gen_le16(S, ops[i].e.v); |
|
|
|
gen_le16(ops[i].e.v); |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
} else if (v & OP_IM64) { |
|
|
|
gen_expr64(S, &ops[i].e); |
|
|
|
gen_expr64(&ops[i].e); |
|
|
|
#endif |
|
|
|
} else if (pa->op_type[i] == OPT_DISP || pa->op_type[i] == OPT_DISP8) { |
|
|
|
gen_disp32(S, &ops[i].e); |
|
|
|
gen_disp32(&ops[i].e); |
|
|
|
} else { |
|
|
|
gen_expr32(S, &ops[i].e); |
|
|
|
gen_expr32(&ops[i].e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* after immediate operands, adjust pc-relative address */ |
|
|
|
if (pc) |
|
|
|
add32le(cur_text_section->data + pc - 4, pc - S->tccgen_ind); |
|
|
|
add32le(cur_text_section->data + pc - 4, pc - ind); |
|
|
|
} |
|
|
|
|
|
|
|
/* return the constraint priority (we allocate first the lowest
|
|
|
|
numbered constraints) */ |
|
|
|
static inline int constraint_priority(TCCState* S, const char *str) |
|
|
|
static inline int constraint_priority(const char *str) |
|
|
|
{ |
|
|
|
int priority, c, pr; |
|
|
|
|
|
|
@ -1182,7 +1182,7 @@ static inline int constraint_priority(TCCState* S, const char *str) |
|
|
|
pr = 4; |
|
|
|
break; |
|
|
|
default: |
|
|
|
tcc_error(S, "unknown constraint '%c'", c); |
|
|
|
tcc_error("unknown constraint '%c'", c); |
|
|
|
pr = 0; |
|
|
|
} |
|
|
|
if (pr > priority) |
|
|
@ -1200,19 +1200,19 @@ static const char *skip_constraint_modifiers(const char *p) |
|
|
|
|
|
|
|
/* If T (a token) is of the form "%reg" returns the register
|
|
|
|
number and type, otherwise return -1. */ |
|
|
|
ST_FUNC int asm_parse_regvar (TCCState* S, int t) |
|
|
|
ST_FUNC int asm_parse_regvar (int t) |
|
|
|
{ |
|
|
|
const char *s; |
|
|
|
Operand op; |
|
|
|
if (t < TOK_IDENT || (t & SYM_FIELD)) |
|
|
|
return -1; |
|
|
|
s = S->tccpp_table_ident[t - TOK_IDENT]->str; |
|
|
|
s = table_ident[t - TOK_IDENT]->str; |
|
|
|
if (s[0] != '%') |
|
|
|
return -1; |
|
|
|
t = tok_alloc_const(S, s + 1); |
|
|
|
unget_tok(S, t); |
|
|
|
unget_tok(S, '%'); |
|
|
|
parse_operand(S, &op); |
|
|
|
t = tok_alloc_const(s + 1); |
|
|
|
unget_tok(t); |
|
|
|
unget_tok('%'); |
|
|
|
parse_operand(tcc_state, &op); |
|
|
|
/* Accept only integer regs for now. */ |
|
|
|
if (op.type & OP_REG) |
|
|
|
return op.reg; |
|
|
@ -1225,7 +1225,7 @@ ST_FUNC int asm_parse_regvar (TCCState* S, int t) |
|
|
|
|
|
|
|
#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) |
|
|
|
|
|
|
|
ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
ST_FUNC void asm_compute_constraints(ASMOperand *operands, |
|
|
|
int nb_operands, int nb_outputs, |
|
|
|
const uint8_t *clobber_regs, |
|
|
|
int *pout_reg) |
|
|
@ -1253,13 +1253,13 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
str = skip_constraint_modifiers(str); |
|
|
|
if (isnum(*str) || *str == '[') { |
|
|
|
/* this is a reference to another constraint */ |
|
|
|
k = find_constraint(S, operands, nb_operands, str, NULL); |
|
|
|
k = find_constraint(operands, nb_operands, str, NULL); |
|
|
|
if ((unsigned)k >= i || i < nb_outputs) |
|
|
|
tcc_error(S, "invalid reference in constraint %d ('%s')", |
|
|
|
tcc_error("invalid reference in constraint %d ('%s')", |
|
|
|
i, str); |
|
|
|
op->ref_index = k; |
|
|
|
if (operands[k].input_index >= 0) |
|
|
|
tcc_error(S, "cannot reference twice the same operand"); |
|
|
|
tcc_error("cannot reference twice the same operand"); |
|
|
|
operands[k].input_index = i; |
|
|
|
op->priority = 5; |
|
|
|
} else if ((op->vt->r & VT_VALMASK) == VT_LOCAL |
|
|
@ -1268,7 +1268,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
op->priority = 1; |
|
|
|
op->reg = reg; |
|
|
|
} else { |
|
|
|
op->priority = constraint_priority(S, str); |
|
|
|
op->priority = constraint_priority(str); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1316,7 +1316,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
} |
|
|
|
if (op->reg >= 0) { |
|
|
|
if (is_reg_allocated(op->reg)) |
|
|
|
tcc_error(S, "asm regvar requests register that's taken already"); |
|
|
|
tcc_error("asm regvar requests register that's taken already"); |
|
|
|
reg = op->reg; |
|
|
|
goto reg_found; |
|
|
|
} |
|
|
@ -1330,7 +1330,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
/* FALL THRU */ |
|
|
|
case '&': |
|
|
|
if (j >= nb_outputs) |
|
|
|
tcc_error(S, "'%c' modifier can only be applied to outputs", c); |
|
|
|
tcc_error("'%c' modifier can only be applied to outputs", c); |
|
|
|
reg_mask = REG_IN_MASK | REG_OUT_MASK; |
|
|
|
goto try_next; |
|
|
|
case 'A': |
|
|
@ -1424,7 +1424,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
tcc_error(S, "asm constraint %d ('%s') could not be satisfied", |
|
|
|
tcc_error("asm constraint %d ('%s') could not be satisfied", |
|
|
|
j, op->constraint); |
|
|
|
break; |
|
|
|
} |
|
|
@ -1447,7 +1447,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
if (!(regs_allocated[reg] & REG_OUT_MASK)) |
|
|
|
goto reg_found2; |
|
|
|
} |
|
|
|
tcc_error(S, "could not find free output register for reloading"); |
|
|
|
tcc_error("could not find free output register for reloading"); |
|
|
|
reg_found2: |
|
|
|
*pout_reg = reg; |
|
|
|
break; |
|
|
@ -1461,7 +1461,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
op = &operands[j]; |
|
|
|
printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", |
|
|
|
j, |
|
|
|
op->id ? get_tok_str(S, op->id, NULL) : "", |
|
|
|
op->id ? get_tok_str(op->id, NULL) : "", |
|
|
|
op->constraint, |
|
|
|
op->vt->r, |
|
|
|
op->reg); |
|
|
@ -1471,7 +1471,7 @@ ST_FUNC void asm_compute_constraints(TCCState* S, ASMOperand *operands, |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
ST_FUNC void subst_asm_operand(TCCState* S, CString *add_str, |
|
|
|
ST_FUNC void subst_asm_operand(CString *add_str, |
|
|
|
SValue *sv, int modifier) |
|
|
|
{ |
|
|
|
int r, reg, size, val; |
|
|
@ -1481,33 +1481,33 @@ ST_FUNC void subst_asm_operand(TCCState* S, CString *add_str, |
|
|
|
if ((r & VT_VALMASK) == VT_CONST) { |
|
|
|
if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' && |
|
|
|
modifier != 'P') |
|
|
|
cstr_ccat(S, add_str, '$'); |
|
|
|
cstr_ccat(add_str, '$'); |
|
|
|
if (r & VT_SYM) { |
|
|
|
const char *name = get_tok_str(S, sv->sym->v, NULL); |
|
|
|
const char *name = get_tok_str(sv->sym->v, NULL); |
|
|
|
if (sv->sym->v >= SYM_FIRST_ANOM) { |
|
|
|
/* In case of anonymous symbols ("L.42", used
|
|
|
|
for static data labels) we can't find them |
|
|
|
in the C symbol table when later looking up |
|
|
|
this name. So enter them now into the asm label |
|
|
|
list when we still know the symbol. */ |
|
|
|
get_asm_sym(S, tok_alloc_const(S, name), sv->sym); |
|
|
|
get_asm_sym(tok_alloc_const(name), sv->sym); |
|
|
|
} |
|
|
|
if (S->leading_underscore) |
|
|
|
cstr_ccat(S, add_str, '_'); |
|
|
|
cstr_cat(S, add_str, name, -1); |
|
|
|
if (tcc_state->leading_underscore) |
|
|
|
cstr_ccat(add_str, '_'); |
|
|
|
cstr_cat(add_str, name, -1); |
|
|
|
if ((uint32_t)sv->c.i == 0) |
|
|
|
goto no_offset; |
|
|
|
cstr_ccat(S, add_str, '+'); |
|
|
|
cstr_ccat(add_str, '+'); |
|
|
|
} |
|
|
|
val = sv->c.i; |
|
|
|
if (modifier == 'n') |
|
|
|
val = -val; |
|
|
|
snprintf(buf, sizeof(buf), "%d", (int)sv->c.i); |
|
|
|
cstr_cat(S, add_str, buf, -1); |
|
|
|
cstr_cat(add_str, buf, -1); |
|
|
|
no_offset:; |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
|
if (r & VT_LVAL) |
|
|
|
cstr_cat(S, add_str, "(%rip)", -1); |
|
|
|
cstr_cat(add_str, "(%rip)", -1); |
|
|
|
#endif |
|
|
|
} else if ((r & VT_VALMASK) == VT_LOCAL) { |
|
|
|
#ifdef TCC_TARGET_X86_64 |
|
|
@ -1515,24 +1515,24 @@ ST_FUNC void subst_asm_operand(TCCState* S, CString *add_str, |
|
|
|
#else |
|
|
|
snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i); |
|
|
|
#endif |
|
|
|
cstr_cat(S, add_str, buf, -1); |
|
|
|
cstr_cat(add_str, buf, -1); |
|
|
|
} else if (r & VT_LVAL) { |
|
|
|
reg = r & VT_VALMASK; |
|
|
|
if (reg >= VT_CONST) |
|
|
|
tcc_internal_error(S, ""); |
|
|
|
tcc_internal_error(""); |
|
|