fixed 'no base' modrm generation - better asm constraints handling

This commit is contained in:
bellard 2004-10-18 00:13:39 +00:00
parent 62f99adb71
commit d65463809c

View File

@ -364,7 +364,7 @@ static void gen_le16(int v)
/* generate the modrm operand */ /* generate the modrm operand */
static inline void asm_modrm(int reg, Operand *op) static inline void asm_modrm(int reg, Operand *op)
{ {
int mod, reg1, reg2; int mod, reg1, reg2, sib_reg1;
if (op->type & (OP_REG | OP_MMX | OP_SSE)) { if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
g(0xc0 + (reg << 3) + op->reg); g(0xc0 + (reg << 3) + op->reg);
@ -373,8 +373,12 @@ static inline void asm_modrm(int reg, Operand *op)
g(0x05 + (reg << 3)); g(0x05 + (reg << 3));
gen_expr32(&op->e); gen_expr32(&op->e);
} else { } else {
sib_reg1 = op->reg;
/* fist compute displacement encoding */ /* fist compute displacement encoding */
if (op->e.v == 0 && !op->e.sym && op->reg != 5) { if (sib_reg1 == -1) {
sib_reg1 = 5;
mod = 0x00;
} else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
mod = 0x00; mod = 0x00;
} else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
mod = 0x40; mod = 0x40;
@ -391,13 +395,13 @@ static inline void asm_modrm(int reg, Operand *op)
reg2 = op->reg2; reg2 = op->reg2;
if (reg2 == -1) if (reg2 == -1)
reg2 = 4; /* indicate no index */ reg2 = 4; /* indicate no index */
g((op->shift << 6) + (reg2 << 3) + op->reg); g((op->shift << 6) + (reg2 << 3) + sib_reg1);
} }
/* add offset */ /* add offset */
if (mod == 0x40) { if (mod == 0x40) {
g(op->e.v); g(op->e.v);
} else if (mod == 0x80) { } else if (mod == 0x80 || op->reg == -1) {
gen_expr32(&op->e); gen_expr32(&op->e);
} }
} }
@ -517,7 +521,11 @@ static void asm_opcode(TCCState *s1, int opcode)
s = reg_to_size[ops[i].type & OP_REG]; s = reg_to_size[ops[i].type & OP_REG];
} }
if (s == 3) { if (s == 3) {
error("cannot infer opcode suffix"); if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
(ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
s = 2;
else
error("cannot infer opcode suffix");
} }
} }
@ -752,50 +760,56 @@ static const char *skip_constraint_modifiers(const char *p)
return p; return p;
} }
static void asm_compute_constraints(uint8_t *regs_allocated, #define REG_OUT_MASK 0x01
ASMOperand *operands, #define REG_IN_MASK 0x02
int nb_operands1, int nb_outputs,
int is_output, #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
uint8_t *input_regs_allocated)
static void asm_compute_constraints(ASMOperand *operands,
int nb_operands, int nb_outputs,
const uint8_t *clobber_regs,
int *pout_reg)
{ {
ASMOperand *op; ASMOperand *op;
int sorted_op[MAX_ASM_OPERANDS]; int sorted_op[MAX_ASM_OPERANDS];
int i, j, k, p1, p2, tmp, reg, c, base, nb_operands; int i, j, k, p1, p2, tmp, reg, c, reg_mask;
const char *str; const char *str;
uint8_t regs_allocated[NB_ASM_REGS];
if (is_output) { /* init fields */
base = 0; for(i=0;i<nb_operands;i++) {
nb_operands = nb_outputs; op = &operands[i];
} else { op->input_index = -1;
base = nb_outputs; op->ref_index = -1;
nb_operands = nb_operands1 - nb_outputs; op->reg = -1;
op->is_memory = 0;
op->is_rw = 0;
} }
/* compute constraint priority and evaluate references to output /* compute constraint priority and evaluate references to output
constraints if input constraints */ constraints if input constraints */
for(i=0;i<nb_operands;i++) { for(i=0;i<nb_operands;i++) {
j = base + i; op = &operands[i];
op = &operands[j];
str = op->constraint; str = op->constraint;
op->ref_index = -1;
op->reg = -1;
str = skip_constraint_modifiers(str); str = skip_constraint_modifiers(str);
if (!is_output && (isnum(*str) || *str == '[')) { if (isnum(*str) || *str == '[') {
/* this is a reference to another constraint */ /* this is a reference to another constraint */
k = find_constraint(operands, nb_operands1, str, NULL); k = find_constraint(operands, nb_operands, str, NULL);
if ((unsigned)k >= j) if ((unsigned)k >= i || i < nb_outputs)
error("invalid reference in constraint %d ('%s')", error("invalid reference in constraint %d ('%s')",
j, str); i, str);
op->ref_index = k; op->ref_index = k;
str = operands[k].constraint; if (operands[k].input_index >= 0)
str = skip_constraint_modifiers(str); error("cannot reference twice the same operand");
operands[k].input_index = i;
op->priority = 5;
} else {
op->priority = constraint_priority(str);
} }
op->priority = constraint_priority(str);
} }
/* sort operands according to their priority */ /* sort operands according to their priority */
for(i=0;i<nb_operands;i++) for(i=0;i<nb_operands;i++)
sorted_op[i] = base + i; sorted_op[i] = i;
for(i=0;i<nb_operands - 1;i++) { for(i=0;i<nb_operands - 1;i++) {
for(j=i+1;j<nb_operands;j++) { for(j=i+1;j<nb_operands;j++) {
p1 = operands[sorted_op[i]].priority; p1 = operands[sorted_op[i]].priority;
@ -808,32 +822,55 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
} }
} }
memset(regs_allocated, 0, NB_ASM_REGS); for(i = 0;i < NB_ASM_REGS; i++) {
regs_allocated[4] = 1; /* esp cannot be used */ if (clobber_regs[i])
regs_allocated[5] = 1; /* ebp cannot be used yet */ regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
else
regs_allocated[i] = 0;
}
/* esp cannot be used */
regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
/* ebp cannot be used yet */
regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
/* allocate registers and generate corresponding asm moves */ /* allocate registers and generate corresponding asm moves */
for(i=0;i<nb_operands;i++) { for(i=0;i<nb_operands;i++) {
j = sorted_op[i]; j = sorted_op[i];
op = &operands[j]; op = &operands[j];
str = op->constraint; str = op->constraint;
/* no need to allocate references */
if (op->ref_index >= 0) { if (op->ref_index >= 0)
str = operands[op->ref_index].constraint; continue;
/* select if register is used for output, input or both */
if (op->input_index >= 0) {
reg_mask = REG_IN_MASK | REG_OUT_MASK;
} else if (j < nb_outputs) {
reg_mask = REG_OUT_MASK;
} else {
reg_mask = REG_IN_MASK;
} }
str = skip_constraint_modifiers(str);
try_next: try_next:
c = *str++; c = *str++;
switch(c) { switch(c) {
case '=':
goto try_next;
case '+':
op->is_rw = 1;
/* FALL THRU */
case '&':
if (j >= nb_outputs)
error("'%c' modifier can only be applied to outputs", c);
reg_mask = REG_IN_MASK | REG_OUT_MASK;
goto try_next;
case 'A': case 'A':
/* allocate both eax and edx */ /* allocate both eax and edx */
if (regs_allocated[TREG_EAX] || regs_allocated[TREG_EDX]) if (is_reg_allocated(TREG_EAX) ||
is_reg_allocated(TREG_EDX))
goto try_next; goto try_next;
op->is_llong = 1; op->is_llong = 1;
op->reg = TREG_EAX; op->reg = TREG_EAX;
regs_allocated[TREG_EAX] = 1; regs_allocated[TREG_EAX] |= reg_mask;
regs_allocated[TREG_EDX] = 1; regs_allocated[TREG_EDX] |= reg_mask;
break; break;
case 'a': case 'a':
reg = TREG_EAX; reg = TREG_EAX;
@ -853,20 +890,20 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
case 'D': case 'D':
reg = 7; reg = 7;
alloc_reg: alloc_reg:
if (regs_allocated[reg]) if (is_reg_allocated(reg))
goto try_next; goto try_next;
goto reg_found; goto reg_found;
case 'q': case 'q':
/* eax, ebx, ecx or edx */ /* eax, ebx, ecx or edx */
for(reg = 0; reg < 4; reg++) { for(reg = 0; reg < 4; reg++) {
if (!regs_allocated[reg]) if (!is_reg_allocated(reg))
goto reg_found; goto reg_found;
} }
goto try_next; goto try_next;
case 'r': case 'r':
/* any general register */ /* any general register */
for(reg = 0; reg < 8; reg++) { for(reg = 0; reg < 8; reg++) {
if (!regs_allocated[reg]) if (!is_reg_allocated(reg))
goto reg_found; goto reg_found;
} }
goto try_next; goto try_next;
@ -874,7 +911,7 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
/* now we can reload in the register */ /* now we can reload in the register */
op->is_llong = 0; op->is_llong = 0;
op->reg = reg; op->reg = reg;
regs_allocated[reg] = 1; regs_allocated[reg] |= reg_mask;
break; break;
case 'i': case 'i':
if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
@ -888,25 +925,27 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
break; break;
case 'm': case 'm':
case 'g': case 'g':
/* nothing special to do because the operand is /* nothing special to do because the operand is already in
already in memory */ memory, except if the pointer itself is stored in a
memory variable (VT_LLOCAL case) */
/* XXX: fix constant case */ /* XXX: fix constant case */
if (is_output) { /* if it is a reference to a memory zone, it must lie
/* if it is a reference to a memory zone, it must lie in a register, so we reserve the register in the
in a register, so we reserve the register in the input registers and a load will be generated
input registers and a load will be generated later */
later */ if (j < nb_outputs || c == 'm') {
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
/* any general register */ /* any general register */
for(reg = 0; reg < 8; reg++) { for(reg = 0; reg < 8; reg++) {
if (!input_regs_allocated[reg]) if (!(regs_allocated[reg] & REG_IN_MASK))
goto reg_found1; goto reg_found1;
} }
goto try_next; goto try_next;
reg_found1: reg_found1:
/* now we can reload in the register */ /* now we can reload in the register */
input_regs_allocated[reg] = 1; regs_allocated[reg] |= REG_IN_MASK;
op->reg = reg; op->reg = reg;
op->is_memory = 1;
} }
} }
break; break;
@ -915,14 +954,34 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
j, op->constraint); j, op->constraint);
break; break;
} }
/* if a reference is present for that operand, we assign it too */
if (op->input_index >= 0) {
operands[op->input_index].reg = op->reg;
operands[op->input_index].is_llong = op->is_llong;
}
} }
/* compute out_reg. It is used to store outputs registers to memory
locations references by pointers (VT_LLOCAL case) */
*pout_reg = -1;
for(i=0;i<nb_operands;i++) {
op = &operands[i];
if (op->reg >= 0 &&
(op->vt->r & VT_VALMASK) == VT_LLOCAL &&
!op->is_memory) {
for(reg = 0; reg < 8; reg++) {
if (!(regs_allocated[reg] & REG_OUT_MASK))
goto reg_found2;
}
error("could not find free output register for reloading");
reg_found2:
*pout_reg = reg;
break;
}
}
/* print sorted constraints */ /* print sorted constraints */
#ifdef ASM_DEBUG #ifdef ASM_DEBUG
if (is_output)
printf("outputs=\n");
else
printf("inputs=\n");
for(i=0;i<nb_operands;i++) { for(i=0;i<nb_operands;i++) {
j = sorted_op[i]; j = sorted_op[i];
op = &operands[j]; op = &operands[j];
@ -933,6 +992,8 @@ static void asm_compute_constraints(uint8_t *regs_allocated,
op->vt->r, op->vt->r,
op->reg); op->reg);
} }
if (*pout_reg >= 0)
printf("out_reg=%d\n", *pout_reg);
#endif #endif
} }
@ -1019,7 +1080,8 @@ static void subst_asm_operand(CString *add_str,
/* generate prolog and epilog code for asm statment */ /* generate prolog and epilog code for asm statment */
static void asm_gen_code(ASMOperand *operands, int nb_operands, static void asm_gen_code(ASMOperand *operands, int nb_operands,
int nb_outputs, int is_output, int nb_outputs, int is_output,
uint8_t *clobber_regs) uint8_t *clobber_regs,
int out_reg)
{ {
uint8_t regs_allocated[NB_ASM_REGS]; uint8_t regs_allocated[NB_ASM_REGS];
ASMOperand *op; ASMOperand *op;
@ -1042,39 +1104,52 @@ static void asm_gen_code(ASMOperand *operands, int nb_operands,
} }
/* generate load code */ /* generate load code */
for(i = nb_outputs ; i < nb_operands; i++) { for(i = 0; i < nb_operands; i++) {
op = &operands[i]; op = &operands[i];
if (op->reg >= 0) { if (op->reg >= 0) {
load(op->reg, op->vt); if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
if (op->is_llong) { op->is_memory) {
/* memory reference case (for both input and
output cases) */
SValue sv; SValue sv;
sv = *op->vt; sv = *op->vt;
sv.c.ul += 4; sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
load(TREG_EDX, &sv); load(op->reg, &sv);
} else if (i >= nb_outputs || op->is_rw) {
/* load value in register */
load(op->reg, op->vt);
if (op->is_llong) {
SValue sv;
sv = *op->vt;
sv.c.ul += 4;
load(TREG_EDX, &sv);
}
} }
} }
} }
/* generate load code for output memory references */
for(i = 0 ; i < nb_outputs; i++) {
op = &operands[i];
if (op->reg >= 0 && ((op->vt->r & VT_VALMASK) == VT_LLOCAL)) {
SValue sv;
sv = *op->vt;
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
load(op->reg, &sv);
}
}
} else { } else {
/* generate save code */ /* generate save code */
for(i = 0 ; i < nb_outputs; i++) { for(i = 0 ; i < nb_outputs; i++) {
op = &operands[i]; op = &operands[i];
if (op->reg >= 0 && ((op->vt->r & VT_VALMASK) != VT_LLOCAL)) { if (op->reg >= 0) {
store(op->reg, op->vt); if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
if (op->is_llong) { if (!op->is_memory) {
SValue sv; SValue sv;
sv = *op->vt; sv = *op->vt;
sv.c.ul += 4; sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
store(TREG_EDX, &sv); load(out_reg, &sv);
sv.r = (sv.r & ~VT_VALMASK) | out_reg;
store(op->reg, &sv);
}
} else {
store(op->reg, op->vt);
if (op->is_llong) {
SValue sv;
sv = *op->vt;
sv.c.ul += 4;
store(TREG_EDX, &sv);
}
} }
} }
} }