Commit 0dd7a06d authored by Christoph Mallon's avatar Christoph Mallon
Browse files

x87: Simplify x87 code generation.

Do not store up to three registers for an instruction.
Instead only store the explicit register operand and whether the operands are permuted and/or the result is to be placed in the explicit register operand or st0.
parent aee4e102
......@@ -498,12 +498,9 @@ end_of_mods:
goto emit_AM;
} else {
assert(get_ia32_op_type(node) == ia32_Normal);
ia32_x87_attr_t const *const x87_attr = get_ia32_x87_attr_const(node);
arch_register_t const *const out = x87_attr->x87[2];
arch_register_t const * in = x87_attr->x87[1];
if (out == in)
in = x87_attr->x87[0];
be_emit_irprintf("%%%s, %%%s", in->name, out->name);
ia32_x87_attr_t const *const attr = get_ia32_x87_attr_const(node);
char const *const fmt = attr->res_in_reg ? "%%st, %%%s" : "%%%s, %%st";
be_emit_irprintf(fmt, attr->reg->name);
break;
}
......@@ -570,15 +567,12 @@ emit_AM:
case 'F':
if (*fmt == 'M') {
++fmt;
ia32_emit_x87_mode_suffix(node);
} else if (*fmt == 'P') {
++fmt;
ia32_x87_attr_t const *const attr = get_ia32_x87_attr_const(node);
if (attr->pop)
be_emit_char('p');
} else if (*fmt == 'R') {
++fmt;
/* NOTE: Work around a gas quirk for non-commutative operations if the
* destination register is not %st0. In this case r/non-r is swapped.
* %st0 = %st0 - %st1 -> fsub %st1, %st0 (as expected)
......@@ -590,24 +584,20 @@ emit_AM:
* right (r) side of the executed operation.
* - The placement of %st0 selects whether the result is written to
* %st0 (right) or the other register (left).
* This results in testing whether the left operand register is %st0
* instead of the expected test whether the output register equals the
* left operand register. */
ia32_x87_attr_t const *const attr = get_ia32_x87_attr_const(node);
if (get_ia32_op_type(node) == ia32_Normal ?
attr->x87[0] != &ia32_registers[REG_ST0] :
attr->attr.data.ins_permuted)
* This means that it is sufficient to test whether the operands are
* permuted. In particular it is not necessary to consider wether the
* result is to be placed into the explicit register operand. */
if (get_ia32_x87_attr_const(node)->attr.data.ins_permuted)
be_emit_char('r');
} else if (*fmt == 'X') {
++fmt;
ia32_emit_xmm_mode_suffix(node);
} else if ('0' <= *fmt && *fmt <= '2') {
const ia32_x87_attr_t *attr = get_ia32_x87_attr_const(node);
} else if (*fmt == '0') {
be_emit_char('%');
be_emit_string(attr->x87[*fmt++ - '0']->name);
be_emit_string(get_ia32_x87_attr_const(node)->reg->name);
} else {
goto unknown;
}
++fmt;
break;
case 'I':
......@@ -3250,38 +3240,30 @@ static void bemit_copybi(const ir_node *node)
static void bemit_fbinop(ir_node const *const node, unsigned const op_fwd, unsigned const op_rev)
{
ia32_x87_attr_t const *const attr = get_ia32_x87_attr_const(node);
arch_register_t const *const st0 = &ia32_registers[REG_ST0];
unsigned const op = attr->attr.data.ins_permuted ? op_rev : op_fwd;
if (get_ia32_op_type(node) == ia32_Normal) {
arch_register_t const *const out = attr->x87[2];
assert(out == attr->x87[0] || out == attr->x87[1]);
assert(!attr->attr.data.ins_permuted);
assert(!attr->pop || attr->res_in_reg);
unsigned char op0 = 0xD8;
if (out != st0) op0 |= 0x04;
if (attr->res_in_reg) op0 |= 0x04;
if (attr->pop) op0 |= 0x02;
bemit8(op0);
unsigned op = op_rev;
arch_register_t const *reg = attr->x87[0];
if (reg == st0) {
op = op_fwd;
reg = attr->x87[1];
}
bemit8(MOD_REG | ENC_REG(op) | ENC_RM(reg->index));
bemit8(MOD_REG | ENC_REG(op) | ENC_RM(attr->reg->index));
} else {
assert(attr->x87[2] == st0);
assert(!attr->reg);
assert(!attr->pop);
unsigned const size = get_mode_size_bits(get_ia32_ls_mode(node));
bemit8(size == 32 ? 0xD8 : 0xDC);
bemit_mod_am(attr->attr.data.ins_permuted ? op_rev : op_fwd, node);
bemit_mod_am(op, node);
}
}
static void bemit_fop_reg(ir_node const *const node, unsigned char const op0, unsigned char const op1)
{
bemit8(op0);
bemit8(op1 + get_ia32_x87_attr_const(node)->x87[0]->index);
bemit8(op1 + get_ia32_x87_attr_const(node)->reg->index);
}
static void bemit_fabs(const ir_node *node)
......@@ -3478,14 +3460,14 @@ static void bemit_fucomi(const ir_node *node)
{
const ia32_x87_attr_t *attr = get_ia32_x87_attr_const(node);
bemit8(attr->pop ? 0xDF : 0xDB); // fucom[p]i
bemit8(0xE8 + attr->x87[1]->index);
bemit8(0xE8 + attr->reg->index);
}
static void bemit_fucomfnstsw(const ir_node *node)
{
const ia32_x87_attr_t *attr = get_ia32_x87_attr_const(node);
bemit8(0xDD); // fucom[p]
bemit8((attr->pop ? 0xE8 : 0xE0) + attr->x87[1]->index);
bemit8((attr->pop ? 0xE8 : 0xE0) + attr->reg->index);
bemit_fnstsw();
}
......
......@@ -270,7 +270,8 @@ struct ia32_immediate_attr_t {
typedef struct ia32_x87_attr_t ia32_x87_attr_t;
struct ia32_x87_attr_t {
ia32_attr_t attr; /**< the generic attribute */
const arch_register_t *x87[3]; /**< register slots for x87 register */
arch_register_t const *reg; /**< The explicit register operand. */
bool res_in_reg; /**< True if the result is in the explicit register operand, %st0 otherwise. */
bool pop; /**< Emit a pop suffix. */
};
......
......@@ -2509,7 +2509,7 @@ femms => {
FucomFnstsw => {
reg_req => { },
emit => "fucom%FP %F1\n".
emit => "fucom%FP %F0\n".
"fnstsw %%ax",
attr_type => "ia32_x87_attr_t",
latency => 2,
......@@ -2525,7 +2525,7 @@ FucomppFnstsw => {
Fucomi => {
reg_req => { },
emit => 'fucom%FPi %F1',
emit => 'fucom%FPi %F0',
attr_type => "ia32_x87_attr_t",
latency => 1,
},
......
......@@ -395,12 +395,12 @@ static void x87_create_fxch(x87_state *state, ir_node *n, int pos)
ir_node *const block = get_nodes_block(n);
ir_node *const fxch = new_bd_ia32_fxch(NULL, block);
ia32_x87_attr_t *const attr = get_ia32_x87_attr(fxch);
attr->x87[0] = get_st_reg(pos);
attr->reg = get_st_reg(pos);
keep_alive(fxch);
sched_add_before(n, fxch);
DB((dbg, LEVEL_1, "<<< %s %s, %s\n", get_irn_opname(fxch), attr->x87[0]->name, get_st_reg(0)->name));
DB((dbg, LEVEL_1, "<<< %s %s\n", get_irn_opname(fxch), attr->reg->name));
}
/* -------------- x87 perm --------------- */
......@@ -539,12 +539,12 @@ static void x87_create_fpush(x87_state *state, ir_node *n, int pos, int const ou
ir_node *const fpush = new_bd_ia32_fpush(NULL, get_nodes_block(n));
ia32_x87_attr_t *const attr = get_ia32_x87_attr(fpush);
attr->x87[0] = get_st_reg(pos);
attr->reg = get_st_reg(pos);
keep_alive(fpush);
sched_add_before(n, fpush);
DB((dbg, LEVEL_1, "<<< %s %s, %s\n", get_irn_opname(fpush), attr->x87[0]->name, get_st_reg(0)->name));
DB((dbg, LEVEL_1, "<<< %s %s\n", get_irn_opname(fpush), attr->reg->name));
}
/**
......@@ -569,11 +569,11 @@ static ir_node *x87_create_fpop(x87_state *state, ir_node *n, int num)
else
fpop = new_bd_ia32_fpop(NULL, get_nodes_block(n));
attr = get_ia32_x87_attr(fpop);
attr->x87[0] = get_st_reg(0);
attr->reg = get_st_reg(0);
keep_alive(fpop);
sched_add_before(n, fpop);
DB((dbg, LEVEL_1, "<<< %s %s\n", get_irn_opname(fpop), attr->x87[0]->name));
DB((dbg, LEVEL_1, "<<< %s %s\n", get_irn_opname(fpop), attr->reg->name));
} while (--num > 0);
return fpop;
}
......@@ -734,10 +734,6 @@ static void vfp_dump_live(vfp_liveness live)
*/
static int sim_binop(x87_state *const state, ir_node *const n, ir_op *const op)
{
int op2_idx = 0, op1_idx;
int out_idx, do_pop = 0;
ia32_x87_attr_t *attr;
int permuted;
ir_node *patched_insn;
x87_simulator *sim = state->sim;
ir_node *op1 = get_irn_n(n, n_ia32_binary_left);
......@@ -756,17 +752,16 @@ static int sim_binop(x87_state *const state, ir_node *const n, ir_op *const op)
DB((dbg, LEVEL_1, "Stack before: "));
DEBUG_ONLY(x87_dump_stack(state);)
op1_idx = x87_on_stack(state, reg_index_1);
int op1_idx = x87_on_stack(state, reg_index_1);
assert(op1_idx >= 0);
op1_live_after = is_vfp_live(reg_index_1, live);
attr = get_ia32_x87_attr(n);
permuted = attr->attr.data.ins_permuted;
int op2_idx;
int out_idx;
bool pop = false;
int const out_reg_idx = out->index;
ia32_x87_attr_t *const attr = get_ia32_x87_attr(n);
if (reg_index_2 != REG_VFP_VFP_NOREG) {
assert(!permuted);
/* second operand is a vfp register */
op2_idx = x87_on_stack(state, reg_index_2);
assert(op2_idx >= 0);
......@@ -815,14 +810,14 @@ static int sim_binop(x87_state *const state, ir_node *const n, ir_op *const op)
out_idx = 0;
} else {
/* now do fxxxp (op = op X tos, pop) */
do_pop = 1;
out_idx = op1_idx;
pop = true;
}
} else if (op1_idx == 0) {
assert(op1_idx != op2_idx);
/* now do fxxxrp (op = tos X op, pop) */
do_pop = 1;
out_idx = op2_idx;
pop = true;
} else {
/* Bring the second on top. */
x87_create_fxch(state, n, op2_idx);
......@@ -837,7 +832,7 @@ static int sim_binop(x87_state *const state, ir_node *const n, ir_op *const op)
op2_idx = 0;
/* use fxxxp (op = op X tos, pop) */
out_idx = op1_idx;
do_pop = 1;
pop = true;
}
}
}
......@@ -847,38 +842,37 @@ static int sim_binop(x87_state *const state, ir_node *const n, ir_op *const op)
if (op1_live_after) {
/* first operand is live: push it here */
x87_create_fpush(state, n, op1_idx, out_reg_idx, op1);
op1_idx = 0;
} else {
/* first operand is dead: bring it to tos */
if (op1_idx != 0) {
if (op1_idx != 0)
x87_create_fxch(state, n, op1_idx);
op1_idx = 0;
}
}
/* use fxxx (tos = tos X mem) */
op1_idx = attr->attr.data.ins_permuted ? -1 : 0;
op2_idx = attr->attr.data.ins_permuted ? 0 : -1;
out_idx = 0;
}
assert(op1_idx == 0 || op2_idx == 0);
assert(out_idx == op1_idx || out_idx == op2_idx);
patched_insn = x87_patch_insn(n, op);
x87_set_st(state, out_reg_idx, patched_insn, out_idx);
if (do_pop) {
if (pop)
x87_pop(state);
}
/* patch the operation */
attr->pop = do_pop;
attr->x87[0] = op1_reg = get_st_reg(op1_idx);
if (reg_index_2 != REG_VFP_VFP_NOREG) {
attr->x87[1] = op2_reg = get_st_reg(op2_idx);
}
attr->x87[2] = out = get_st_reg(out_idx);
if (reg_index_2 != REG_VFP_VFP_NOREG) {
DB((dbg, LEVEL_1, "<<< %s %s, %s -> %s\n", get_irn_opname(n), op1_reg->name, op2_reg->name, out->name));
} else {
DB((dbg, LEVEL_1, "<<< %s %s, [AM] -> %s\n", get_irn_opname(n), op1_reg->name, out->name));
}
int const reg_idx = op1_idx != 0 ? op1_idx : op2_idx;
attr->reg = reg_idx >= 0 ? get_st_reg(reg_idx) : NULL;
attr->attr.data.ins_permuted = op1_idx != 0;
attr->res_in_reg = out_idx != 0;
attr->pop = pop;
DEBUG_ONLY(
char const *const l = op1_idx >= 0 ? get_st_reg(op1_idx)->name : "[AM]";
char const *const r = op2_idx >= 0 ? get_st_reg(op2_idx)->name : "[AM]";
char const *const o = get_st_reg(out_idx)->name;
DB((dbg, LEVEL_1, "<<< %s %s, %s -> %s\n", get_irn_opname(n), l, r, o));
);
return NO_NODE_ADDED;
}
......@@ -1178,8 +1172,6 @@ static int sim_FtstFnstsw(x87_state *state, ir_node *n)
*/
static int sim_Fucom(x87_state *state, ir_node *n)
{
int op1_idx;
int op2_idx = -1;
ia32_x87_attr_t *attr = get_ia32_x87_attr(n);
ir_op *dst;
x87_simulator *sim = state->sim;
......@@ -1190,16 +1182,17 @@ static int sim_Fucom(x87_state *state, ir_node *n)
int reg_index_1 = op1->index;
int reg_index_2 = op2->index;
unsigned live = vfp_live_args_after(sim, n, 0);
int pops = 0;
DB((dbg, LEVEL_1, ">>> %+F %s, %s\n", n, op1->name, op2->name));
DEBUG_ONLY(vfp_dump_live(live);)
DB((dbg, LEVEL_1, "Stack before: "));
DEBUG_ONLY(x87_dump_stack(state);)
op1_idx = x87_on_stack(state, reg_index_1);
int op1_idx = x87_on_stack(state, reg_index_1);
assert(op1_idx >= 0);
int op2_idx;
int pops = 0;
/* BEWARE: check for comp a,a cases, they might happen */
if (reg_index_2 != REG_VFP_VFP_NOREG) {
/* second operand is a vfp register */
......@@ -1316,21 +1309,16 @@ static int sim_Fucom(x87_state *state, ir_node *n)
}
} else {
/* second operand is an address mode */
if (is_vfp_live(reg_index_1, live)) {
/* first operand is live: bring it to TOS */
if (op1_idx != 0) {
x87_create_fxch(state, n, op1_idx);
op1_idx = 0;
}
} else {
/* first operand is dead: bring it to tos */
if (op1_idx != 0) {
if (op1_idx != 0)
x87_create_fxch(state, n, op1_idx);
op1_idx = 0;
}
/* Pop first operand, if it is dead. */
if (!is_vfp_live(reg_index_1, live))
pops = 1;
op1_idx = attr->attr.data.ins_permuted ? -1 : 0;
op2_idx = attr->attr.data.ins_permuted ? 0 : -1;
}
}
assert(op1_idx == 0 || op2_idx == 0);
/* patch the operation */
if (is_ia32_vFucomFnstsw(n)) {
......@@ -1348,26 +1336,17 @@ static int sim_Fucom(x87_state *state, ir_node *n)
}
x87_patch_insn(n, dst);
if (op1_idx != 0) {
int tmp = op1_idx;
op1_idx = op2_idx;
op2_idx = tmp;
attr->attr.data.ins_permuted ^= true;
}
op1 = get_st_reg(op1_idx);
attr->x87[0] = op1;
if (op2_idx >= 0) {
op2 = get_st_reg(op2_idx);
attr->x87[1] = op2;
}
int const reg_idx = op1_idx != 0 ? op1_idx : op2_idx;
attr->reg = reg_idx >= 0 ? get_st_reg(reg_idx) : NULL;
attr->attr.data.ins_permuted = op1_idx != 0;
attr->pop = pops != 0;
if (op2_idx >= 0) {
DB((dbg, LEVEL_1, "<<< %s %s, %s\n", get_irn_opname(n), op1->name, op2->name));
} else {
DB((dbg, LEVEL_1, "<<< %s %s, [AM]\n", get_irn_opname(n), op1->name));
}
DEBUG_ONLY(
char const *const l = op1_idx >= 0 ? get_st_reg(op1_idx)->name : "[AM]";
char const *const r = op2_idx >= 0 ? get_st_reg(op2_idx)->name : "[AM]";
DB((dbg, LEVEL_1, "<<< %s %s, %s\n", get_irn_opname(n), l, r));
);
return NO_NODE_ADDED;
}
......@@ -1486,7 +1465,7 @@ static ir_node *create_Copy(x87_state *state, ir_node *n)
x87_push(state, out->index, res);
ia32_x87_attr_t *const attr = get_ia32_x87_attr(res);
attr->x87[0] = get_st_reg(op1_idx);
attr->reg = get_st_reg(op1_idx);
}
arch_set_irn_register(res, out);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment