x86_64-asm: =m operand fixes
The problem was with tcctest.c:
unsigned set;
__asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc");
when with tcc compiled with the HAVE_SELINUX option, run with
tcc -run, it would use large addresses far beyond the 32bits
range when tcc did not use the pc-relative mode for accessing
'set' in global data memory. In fact the assembler did not
know about %rip at all.
Changes:
- memory operands use (%rax) not (%eax)
- conversion from VT_LLOCAL: use type VT_PTR
- support 'k' modifier
- support %rip register
- support X(%rip) pc-relative addresses
The test in tcctest.c is from Michael Matz.
This commit is contained in:
75
i386-asm.c
75
i386-asm.c
@ -276,6 +276,8 @@ static int asm_parse_reg(int *type)
|
||||
*type = OP_EA32;
|
||||
} else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
|
||||
reg = tok - TOK_ASM_rax;
|
||||
} else if (tok == TOK_ASM_rip) {
|
||||
reg = 8;
|
||||
#endif
|
||||
} else {
|
||||
error_32:
|
||||
@ -444,7 +446,7 @@ static void gen_disp32(ExprValue *pe)
|
||||
}
|
||||
|
||||
/* generate the modrm operand */
|
||||
static inline void asm_modrm(int reg, Operand *op)
|
||||
static inline int asm_modrm(int reg, Operand *op)
|
||||
{
|
||||
int mod, reg1, reg2, sib_reg1;
|
||||
|
||||
@ -459,6 +461,13 @@ static inline void asm_modrm(int reg, Operand *op)
|
||||
g(0x05 + (reg << 3));
|
||||
#endif
|
||||
gen_expr32(&op->e);
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
} else if (op->reg == 8) {
|
||||
ExprValue *pe = &op->e;
|
||||
g(0x05 + (reg << 3));
|
||||
gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
|
||||
return ind;
|
||||
#endif
|
||||
} else {
|
||||
sib_reg1 = op->reg;
|
||||
/* fist compute displacement encoding */
|
||||
@ -491,12 +500,13 @@ static inline void asm_modrm(int reg, Operand *op)
|
||||
gen_expr32(&op->e);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||
{
|
||||
const ASMInstr *pa;
|
||||
int i, modrm_index, reg, v, op1, seg_prefix;
|
||||
int i, modrm_index, reg, v, op1, seg_prefix, pc;
|
||||
int nb_ops, s;
|
||||
Operand ops[MAX_OPERANDS], *pop;
|
||||
int op_type[3]; /* decoded op type */
|
||||
@ -802,6 +812,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||
} else {
|
||||
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
|
||||
}
|
||||
|
||||
pc = 0;
|
||||
if (pa->instr_type & OPC_MODRM) {
|
||||
/* first look for an ea operand */
|
||||
for(i = 0;i < nb_ops; i++) {
|
||||
@ -828,8 +840,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asm_modrm(reg, &ops[modrm_index]);
|
||||
pc = asm_modrm(reg, &ops[modrm_index]);
|
||||
}
|
||||
|
||||
/* emit constants */
|
||||
@ -858,30 +869,29 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
|
||||
else
|
||||
v = OP_IM64;
|
||||
}
|
||||
|
||||
if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
|
||||
tcc_error("cannot relocate");
|
||||
|
||||
if (v & (OP_IM8 | OP_IM8S)) {
|
||||
if (ops[i].e.sym)
|
||||
goto error_relocate;
|
||||
g(ops[i].e.v);
|
||||
} else if (v & OP_IM16) {
|
||||
if (ops[i].e.sym)
|
||||
error_relocate:
|
||||
tcc_error("cannot relocate");
|
||||
else
|
||||
gen_le16(ops[i].e.v);
|
||||
} else {
|
||||
if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
|
||||
gen_disp32(&ops[i].e);
|
||||
} else {
|
||||
gen_le16(ops[i].e.v);
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
if (v & OP_IM64)
|
||||
gen_expr64(&ops[i].e);
|
||||
else
|
||||
} else if (v & OP_IM64) {
|
||||
gen_expr64(&ops[i].e);
|
||||
#endif
|
||||
gen_expr32(&ops[i].e);
|
||||
}
|
||||
} else if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
|
||||
gen_disp32(&ops[i].e);
|
||||
} else {
|
||||
gen_expr32(&ops[i].e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* after immediate operands, adjust pc-relative address */
|
||||
if (pc)
|
||||
*(int*)(text_section->data + pc - 4) -= ind - pc;
|
||||
}
|
||||
|
||||
/* return the constraint priority (we allocate first the lowest
|
||||
@ -1189,17 +1199,20 @@ ST_FUNC void subst_asm_operand(CString *add_str,
|
||||
cstr_ccat(add_str, '$');
|
||||
if (r & VT_SYM) {
|
||||
cstr_cat(add_str, get_tok_str(sv->sym->v, NULL), -1);
|
||||
if ((uint32_t)sv->c.i != 0) {
|
||||
cstr_ccat(add_str, '+');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if ((uint32_t)sv->c.i == 0)
|
||||
goto no_offset;
|
||||
cstr_ccat(add_str, '+');
|
||||
}
|
||||
val = sv->c.i;
|
||||
if (modifier == 'n')
|
||||
val = -val;
|
||||
snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
|
||||
cstr_cat(add_str, buf, -1);
|
||||
no_offset:;
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
if (r & VT_LVAL)
|
||||
cstr_cat(add_str, "(%rip)", -1);
|
||||
#endif
|
||||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
|
||||
@ -1212,7 +1225,12 @@ ST_FUNC void subst_asm_operand(CString *add_str,
|
||||
if (reg >= VT_CONST)
|
||||
tcc_error("internal compiler error");
|
||||
snprintf(buf, sizeof(buf), "(%%%s)",
|
||||
get_tok_str(TOK_ASM_eax + reg, NULL));
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
get_tok_str(TOK_ASM_rax + reg, NULL)
|
||||
#else
|
||||
get_tok_str(TOK_ASM_eax + reg, NULL)
|
||||
#endif
|
||||
);
|
||||
cstr_cat(add_str, buf, -1);
|
||||
} else {
|
||||
/* register case */
|
||||
@ -1244,6 +1262,8 @@ ST_FUNC void subst_asm_operand(CString *add_str,
|
||||
size = -1;
|
||||
} else if (modifier == 'w') {
|
||||
size = 2;
|
||||
} else if (modifier == 'k') {
|
||||
size = 4;
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
} else if (modifier == 'q') {
|
||||
size = 8;
|
||||
@ -1311,7 +1331,8 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
|
||||
output cases) */
|
||||
SValue sv;
|
||||
sv = *op->vt;
|
||||
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
|
||||
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
|
||||
sv.type.t = VT_PTR;
|
||||
load(op->reg, &sv);
|
||||
} else if (i >= nb_outputs || op->is_rw) {
|
||||
/* load value in register */
|
||||
|
||||
Reference in New Issue
Block a user