Commit f44b7664 authored by Matthias Braun's avatar Matthias Braun
Browse files

- Reintroduced TestCmov and TestSet

- Cmov and Set can do AddressMode (for the comparison not for the result) now
- Rewrote should_be_same constraint handler and fixed a little bug in it

[r14879]
parent 1cf7acd7
......@@ -804,15 +804,6 @@ static int ia32_possible_memory_operand(const void *self, const ir_node *irn, un
return 1;
}
static void exchange_left_right(ir_node *node)
{
ir_node *tmp = get_irn_n(node, 3);
set_irn_n(node, 3, get_irn_n(node, 2));
set_irn_n(node, 2, tmp);
set_ia32_pncode(node, get_inversed_pnc(get_ia32_pncode(node)));
}
static void ia32_perform_memory_operand(const void *self, ir_node *irn, ir_node *spill, unsigned int i) {
const ia32_irn_ops_t *ops = self;
ia32_code_gen_t *cg = ops->cg;
......@@ -820,7 +811,7 @@ static void ia32_perform_memory_operand(const void *self, ir_node *irn, ir_node
assert(ia32_possible_memory_operand(self, irn, i) && "Cannot perform memory operand change");
if (i == 2) {
exchange_left_right(irn);
ia32_swap_left_right(irn);
}
set_ia32_op_type(irn, ia32_AddrModeS);
......@@ -835,7 +826,7 @@ static void ia32_perform_memory_operand(const void *self, ir_node *irn, ir_node
/* immediates are only allowed on the right side */
if(i == 2 && is_ia32_Immediate(get_irn_n(irn, 2))) {
exchange_left_right(irn);
ia32_swap_left_right(irn);
}
}
......
......@@ -186,7 +186,11 @@ int produces_result(const ir_node *node) {
!is_ia32_SwitchJmp(node) &&
!is_ia32_TestJmp(node) &&
!is_ia32_xCmpSet(node) &&
!is_ia32_xCondJmp(node);
!is_ia32_xCondJmp(node) &&
!is_ia32_CmpCMov(node) &&
!is_ia32_TestCMov(node) &&
!is_ia32_CmpSet(node) && /* this is correct, the Cmp has no result */
!is_ia32_TestSet(node);
}
static
......@@ -254,6 +258,14 @@ void ia32_emit_dest_register(ia32_emit_env_t *env, const ir_node *node, int pos)
be_emit_string(env, reg_name);
}
static void ia32_emit_register(ia32_emit_env_t *env, const arch_register_t *reg)
{
const char *reg_name = arch_register_get_name(reg);
be_emit_char(env, '%');
be_emit_string(env, reg_name);
}
void ia32_emit_x87_name(ia32_emit_env_t *env, const ir_node *node, int pos)
{
const ia32_x87_attr_t *attr = get_ia32_x87_attr_const(node);
......@@ -421,12 +433,12 @@ void ia32_emit_binop(ia32_emit_env_t *env, const ir_node *node) {
const arch_register_t *in;
const char *in_name;
in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2;
in = out ? ((out == in2) ? in1 : in2) : in2;
out = out ? out : in1;
in_name = arch_register_get_name(in);
if (is_ia32_emit_cl(node)) {
assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in) && "shift operation needs ecx");
assert(in == &ia32_gp_regs[REG_ECX]);
in_name = "cl";
}
......@@ -480,7 +492,7 @@ void ia32_emit_binop(ia32_emit_env_t *env, const ir_node *node) {
in_name = ia32_get_reg_name_for_mode(env, mode, in1);
if (is_ia32_emit_cl(node)) {
assert(REGS_ARE_EQUAL(&ia32_gp_regs[REG_ECX], in1) && "shift operation needs ecx");
assert(in1 == &ia32_gp_regs[REG_ECX]);
in_name = "cl";
}
......@@ -511,7 +523,7 @@ void ia32_emit_x87_binop(ia32_emit_env_t *env, const ir_node *node) {
const arch_register_t *out = x87_attr->x87[2];
const arch_register_t *in;
in = out ? (REGS_ARE_EQUAL(out, in2) ? in1 : in2) : in2;
in = out ? ((out == in2) ? in1 : in2) : in2;
out = out ? out : in1;
be_emit_char(env, '%');
......@@ -931,92 +943,66 @@ void emit_ia32_x87CondJmp(ia32_emit_env_t *env, const ir_node *node) {
finish_CondJmp(env, node, mode_E, pnc);
}
static
void emit_register_or_immediate(ia32_emit_env_t *env, const ir_node *node,
int pos)
{
ir_node *op = get_irn_n(node, pos);
if(is_ia32_Immediate(op)) {
emit_ia32_Immediate(env, op);
} else {
ia32_emit_source_register(env, node, pos);
}
}
static
int is_ia32_Immediate_0(const ir_node *node)
{
const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
return attr->offset == 0 && attr->symconst == NULL;
}
static
void CMov_emitter(ia32_emit_env_t *env, const ir_node *node)
{
long pnc = get_ia32_pncode(node);
const arch_register_t *in1, *in2, *out;
long pnc = get_ia32_pncode(node);
out = arch_get_irn_register(env->arch_env, node);
in1 = arch_get_irn_register(env->arch_env, get_irn_n(node, 2));
in2 = arch_get_irn_register(env->arch_env, get_irn_n(node, 3));
/* we have to emit the cmp first, because the destination register */
/* could be one of the compare registers */
if (is_ia32_CmpCMov(node)) {
long pncr = pnc & ~ia32_pn_Cmp_Unsigned;
ir_node *cmp_right = get_irn_n(node, 1);
if( (pncr == pn_Cmp_Eq || pncr == pn_Cmp_Lg)
&& is_ia32_Immediate(cmp_right)
&& is_ia32_Immediate_0(cmp_right)) {
be_emit_cstring(env, "\ttest ");
ia32_emit_source_register(env, node, 0);
be_emit_cstring(env, ", ");
ia32_emit_source_register(env, node, 0);
} else {
be_emit_cstring(env, "\tcmp ");
emit_register_or_immediate(env, node, 1);
be_emit_cstring(env, ", ");
ia32_emit_source_register(env, node, 0);
}
} else if (is_ia32_xCmpCMov(node)) {
if (is_ia32_xCmpCMov(node)) {
be_emit_cstring(env, "\tucomis");
ia32_emit_mode_suffix_mode(env, get_irn_mode(node));
be_emit_char(env, ' ');
ia32_emit_source_register(env, node, 1);
be_emit_cstring(env, ", ");
ia32_emit_source_register(env, node, 0);
in1 = arch_get_irn_register(env->arch_env, get_irn_n(node, 2));
in2 = arch_get_irn_register(env->arch_env, get_irn_n(node, 3));
} else {
assert(0 && "unsupported CMov");
if (is_ia32_CmpCMov(node)) {
be_emit_cstring(env, "\tcmp ");
} else {
assert(is_ia32_TestCMov(node));
be_emit_cstring(env, "\ttest ");
}
ia32_emit_binop(env, node);
in1 = arch_get_irn_register(env->arch_env, get_irn_n(node, 5));
in2 = arch_get_irn_register(env->arch_env, get_irn_n(node, 6));
}
be_emit_finish_line_gas(env, node);
if (REGS_ARE_EQUAL(out, in2)) {
if (out == in2) {
/* best case: default in == out -> do nothing */
} else if (REGS_ARE_EQUAL(out, in1)) {
ir_node *n = (ir_node*) node;
/* true in == out -> need complement compare and exchange true and default in */
ir_node *t = get_irn_n(n, 2);
set_irn_n(n, 2, get_irn_n(n, 3));
set_irn_n(n, 3, t);
} else if (out == in1) {
const arch_register_t *t;
/* true in == out -> need complement compare and exchange true and
* default in */
t = in1;
in1 = in2;
in2 = t;
pnc = get_negated_pnc(pnc, get_irn_mode(node));
} else {
/* out is different from in: need copy default -> out */
/* out is different from both ins: need copy default -> out */
be_emit_cstring(env, "\tmovl ");
ia32_emit_source_register(env, node, n_ia32_CmpCMov_val_false);
ia32_emit_register(env, in2);
be_emit_cstring(env, ", ");
ia32_emit_dest_register(env, node, 0);
ia32_emit_register(env, out);
be_emit_finish_line_gas(env, node);
}
be_emit_cstring(env, "\tcmov");
ia32_emit_cmp_suffix(env, pnc);
be_emit_cstring(env, "l ");
ia32_emit_source_register(env, node, n_ia32_CmpCMov_val_true);
ia32_emit_register(env, in1);
be_emit_cstring(env, ", ");
ia32_emit_dest_register(env, node, 0);
ia32_emit_register(env, out);
be_emit_finish_line_gas(env, node);
}
......@@ -1026,6 +1012,12 @@ void emit_ia32_CmpCMov(ia32_emit_env_t *env, const ir_node *node)
CMov_emitter(env, node);
}
static
void emit_ia32_TestCMov(ia32_emit_env_t *env, const ir_node *node)
{
CMov_emitter(env, node);
}
static
void emit_ia32_xCmpCMov(ia32_emit_env_t *env, const ir_node *node)
{
......@@ -1042,28 +1034,19 @@ void Set_emitter(ia32_emit_env_t *env, const ir_node *node)
out = arch_get_irn_register(env->arch_env, node);
reg8bit = ia32_get_mapped_reg_name(env->isa->regs_8bit, out);
if (is_ia32_CmpSet(node)) {
long pncr = pnc & ~ia32_pn_Cmp_Unsigned;
ir_node *cmp_right = get_irn_n(node, n_ia32_CmpSet_cmp_right);
if( (pncr == pn_Cmp_Eq || pncr == pn_Cmp_Lg)
&& is_ia32_Immediate(cmp_right)
&& is_ia32_Immediate_0(cmp_right)) {
be_emit_cstring(env, "\ttest ");
ia32_emit_source_register(env, node, n_ia32_CmpSet_cmp_left);
be_emit_cstring(env, ", ");
ia32_emit_source_register(env, node, n_ia32_CmpSet_cmp_left);
} else {
be_emit_cstring(env, "\tcmp ");
ia32_emit_binop(env, node);
}
} else if (is_ia32_xCmpSet(node)) {
if(is_ia32_xCmpSet(node)) {
be_emit_cstring(env, "\tucomis");
ia32_emit_mode_suffix_mode(env, get_irn_mode(get_irn_n(node, 2)));
be_emit_char(env, ' ');
ia32_emit_binop(env, node);
} else {
assert(0 && "unsupported Set");
if (is_ia32_CmpSet(node)) {
be_emit_cstring(env, "\tcmp ");
} else {
assert(is_ia32_TestSet(node));
be_emit_cstring(env, "\ttest ");
}
ia32_emit_binop(env, node);
}
be_emit_finish_line_gas(env, node);
......@@ -1084,6 +1067,11 @@ void emit_ia32_CmpSet(ia32_emit_env_t *env, const ir_node *node) {
Set_emitter(env, node);
}
static
void emit_ia32_TestSet(ia32_emit_env_t *env, const ir_node *node) {
Set_emitter(env, node);
}
static
void emit_ia32_xCmpSet(ia32_emit_env_t *env, const ir_node *node) {
Set_emitter(env, node);
......@@ -1680,8 +1668,8 @@ void emit_ia32_Conv_I2I(ia32_emit_env_t *env, const ir_node *node) {
in_reg = get_in_reg(env, node, 2);
out_reg = get_out_reg(env, node, 0);
if (REGS_ARE_EQUAL(in_reg, &ia32_gp_regs[REG_EAX]) &&
REGS_ARE_EQUAL(out_reg, in_reg) &&
if (in_reg == &ia32_gp_regs[REG_EAX] &&
out_reg == &ia32_gp_regs[REG_EAX] &&
signed_mode &&
smaller_bits == 16)
{
......@@ -1792,24 +1780,28 @@ void emit_be_SetSP(ia32_emit_env_t *env, const ir_node *node) {
static
void Copy_emitter(ia32_emit_env_t *env, const ir_node *node, const ir_node *op)
{
const arch_env_t *aenv = env->arch_env;
const arch_env_t *arch_env = env->arch_env;
const arch_register_t *in = arch_get_irn_register(arch_env, op);
const arch_register_t *out = arch_get_irn_register(arch_env, node);
ir_mode *mode;
if (REGS_ARE_EQUAL(arch_get_irn_register(aenv, node), arch_get_irn_register(aenv, op)) ||
arch_register_type_is(arch_get_irn_register(aenv, op), virtual))
if(in == out) {
return;
}
if(is_unknown_reg(in))
return;
mode = get_irn_mode(node);
if (mode == mode_E) {
be_emit_cstring(env, "\tmovsd ");
ia32_emit_source_register(env, node, 0);
ia32_emit_register(env, in);
be_emit_cstring(env, ", ");
ia32_emit_dest_register(env, node, 0);
ia32_emit_register(env, out);
} else {
be_emit_cstring(env, "\tmovl ");
ia32_emit_source_register(env, node, 0);
ia32_emit_register(env, in);
be_emit_cstring(env, ", ");
ia32_emit_dest_register(env, node, 0);
ia32_emit_register(env, out);
}
be_emit_finish_line_gas(env, node);
}
......@@ -1967,7 +1959,9 @@ void ia32_register_emitters(void) {
IA32_EMIT(CondJmp);
IA32_EMIT(TestJmp);
IA32_EMIT(CmpCMov);
IA32_EMIT(TestCMov);
IA32_EMIT(CmpSet);
IA32_EMIT(TestSet);
IA32_EMIT(SwitchJmp);
IA32_EMIT(CopyB);
IA32_EMIT(CopyB_i);
......
......@@ -32,6 +32,7 @@
#include "irgmod.h"
#include "irgwalk.h"
#include "iredges.h"
#include "irprintf.h"
#include "pdeq.h"
#include "error.h"
......@@ -80,7 +81,7 @@ static void ia32_transform_sub_to_neg_add(ir_node *irn, ia32_code_gen_t *cg) {
block = get_nodes_block(irn);
/* in case of sub and OUT == SRC2 we can transform the sequence into neg src2 -- add */
if (!REGS_ARE_EQUAL(out_reg, in2_reg))
if (out_reg != in2_reg)
return;
/* generate the neg src2 */
......@@ -169,7 +170,7 @@ static void ia32_transform_lea_to_add_or_shl(ir_node *irn, ia32_code_gen_t *cg)
index_reg = arch_get_irn_register(cg->arch_env, index);
out_reg = arch_get_irn_register(cg->arch_env, irn);
if (! REGS_ARE_EQUAL(out_reg, index_reg))
if (out_reg != index_reg)
return;
/* ok, we can transform it */
......@@ -205,7 +206,7 @@ static void ia32_transform_lea_to_add_or_shl(ir_node *irn, ia32_code_gen_t *cg)
case ia32_am_B:
case ia32_am_OB:
/* out register must be same as base register */
if (! REGS_ARE_EQUAL(out_reg, base_reg))
if (out_reg != base_reg)
return;
op1 = base;
......@@ -216,10 +217,10 @@ static void ia32_transform_lea_to_add_or_shl(ir_node *irn, ia32_code_gen_t *cg)
case ia32_am_BI:
assert(offs == 0);
/* out register must be same as one in register */
if (REGS_ARE_EQUAL(out_reg, base_reg)) {
if (out_reg == base_reg) {
op1 = base;
op2 = index;
} else if (REGS_ARE_EQUAL(out_reg, index_reg)) {
} else if (out_reg == index_reg) {
op1 = index;
op2 = base;
} else {
......@@ -254,11 +255,11 @@ static void ia32_transform_lea_to_add_or_shl(ir_node *irn, ia32_code_gen_t *cg)
}
static INLINE int need_constraint_copy(ir_node *irn) {
return ! is_ia32_Lea(irn) &&
return ! is_ia32_Lea(irn) &&
! is_ia32_Conv_I2I(irn) &&
! is_ia32_Conv_I2I8Bit(irn) &&
! is_ia32_CmpCMov(irn) &&
! is_ia32_CmpSet(irn);
! is_ia32_TestCMov(irn) &&
! is_ia32_CmpCMov(irn);
}
/**
......@@ -266,111 +267,176 @@ static INLINE int need_constraint_copy(ir_node *irn) {
* is not fulfilled.
* Transform Sub into Neg -- Add if IN2 == OUT
*/
static void ia32_finish_node(ir_node *irn, void *env) {
ia32_code_gen_t *cg = env;
static void assure_should_be_same_requirements(ia32_code_gen_t *cg,
ir_node *node)
{
ir_graph *irg = cg->irg;
const arch_env_t *arch_env = cg->arch_env;
const arch_register_req_t **reqs;
const arch_register_t *out_reg, *in_reg, *in2_reg;
const arch_register_t *out_reg, *in_reg;
int n_res, i;
ir_node *copy, *in_node, *block, *in2_node;
ir_node *in_node, *block;
ia32_op_type_t op_tp;
if (is_ia32_irn(irn)) {
/* AM Dest nodes don't produce any values */
op_tp = get_ia32_op_type(irn);
if (op_tp == ia32_AddrModeD)
goto end;
if(!is_ia32_irn(node))
return;
reqs = get_ia32_out_req_all(irn);
n_res = get_ia32_n_res(irn);
block = get_nodes_block(irn);
/* some nodes are just a bit less efficient, but need no fixing if the
* should be same requirement is not fulfilled */
if(!need_constraint_copy(node))
return;
/* check all OUT requirements, if there is a should_be_same */
if ((op_tp == ia32_Normal || op_tp == ia32_AddrModeS) && need_constraint_copy(irn))
{
for (i = 0; i < n_res; i++) {
if (arch_register_req_is(reqs[i], should_be_same)) {
int same_pos = reqs[i]->other_same;
/* get in and out register */
out_reg = get_ia32_out_reg(irn, i);
in_node = get_irn_n(irn, same_pos);
in_reg = arch_get_irn_register(cg->arch_env, in_node);
/* don't copy ignore nodes */
if (arch_irn_is(cg->arch_env, in_node, ignore) && is_Proj(in_node))
continue;
/* check if in and out register are equal */
if (! REGS_ARE_EQUAL(out_reg, in_reg)) {
/* in case of a commutative op: just exchange the in's */
/* beware: the current op could be everything, so test for ia32 */
/* commutativity first before getting the second in */
if (is_ia32_commutative(irn)) {
in2_node = get_irn_n(irn, same_pos ^ 1);
in2_reg = arch_get_irn_register(cg->arch_env, in2_node);
if (REGS_ARE_EQUAL(out_reg, in2_reg)) {
set_irn_n(irn, same_pos, in2_node);
set_irn_n(irn, same_pos ^ 1, in_node);
}
else
goto insert_copy;
}
else {
insert_copy:
DBG((dbg, LEVEL_1, "inserting copy for %+F in_pos %d\n", irn, same_pos));
/* create copy from in register */
copy = be_new_Copy(arch_register_get_class(in_reg), cg->irg, block, in_node);
DBG_OPT_2ADDRCPY(copy);
/* destination is the out register */
arch_set_irn_register(cg->arch_env, copy, out_reg);
/* insert copy before the node into the schedule */
sched_add_before(irn, copy);
/* set copy as in */
set_irn_n(irn, same_pos, copy);
}
}
}
reqs = get_ia32_out_req_all(node);
n_res = get_ia32_n_res(node);
block = get_nodes_block(node);
/* check all OUT requirements, if there is a should_be_same */
for (i = 0; i < n_res; i++) {
int i2, arity;
int same_pos;
ir_node *perm;
ir_node *in[2];
ir_node *perm_proj0;
ir_node *perm_proj1;
const arch_register_req_t *req = reqs[i];
const arch_register_class_t *class;
if (!arch_register_req_is(req, should_be_same))
continue;
same_pos = req->other_same;
/* get in and out register */
out_reg = get_ia32_out_reg(node, i);
in_node = get_irn_n(node, same_pos);
in_reg = arch_get_irn_register(arch_env, in_node);
/* requirement already fulfilled? */
if (in_reg == out_reg)
continue;
/* unknowns can be changed to any register we want on emitting */
if (is_unknown_reg(in_reg))
continue;
class = arch_register_get_class(in_reg);
assert(class == arch_register_get_class(out_reg));
/* check if any other input operands uses the out register */
arity = get_irn_arity(node);
ir_node *uses_out_reg = NULL;
int uses_out_reg_pos = -1;
for(i2 = 0; i2 < arity; ++i2) {
ir_node *in = get_irn_n(node, i2);
const arch_register_t *in_reg = arch_get_irn_register(arch_env, in);
if(in_reg != out_reg)
continue;
if(uses_out_reg != NULL && in != uses_out_reg) {
panic("invalid register allocation");
}
uses_out_reg = in;
if(uses_out_reg_pos >= 0)
uses_out_reg_pos = -1; /* multiple inputs... */
else
uses_out_reg_pos = i2;
}
/* check xCmp: try to avoid unordered cmp */
if ((is_ia32_xCmp(irn) || is_ia32_xCmpCMov(irn) || is_ia32_xCmpSet(irn)) &&
op_tp == ia32_Normal &&
! is_ia32_ImmConst(irn) && ! is_ia32_ImmSymConst(irn))
{
long pnc = get_ia32_pncode(irn);
/* noone else is using the out reg, we can simply copy it
* (the register can't be live since the operation will override it
* anyway) */
if(uses_out_reg == NULL) {
ir_node *copy = be_new_Copy(class, irg, block, in_node);
DBG_OPT_2ADDRCPY(copy);
if (pnc & pn_Cmp_Uo) {
ir_node *tmp;
int idx1 = 2, idx2 = 3;
/* destination is the out register */
arch_set_irn_register(arch_env, copy, out_reg);
if (is_ia32_xCmpCMov(irn)) {
idx1 = 0;
idx2 = 1;
}
/* insert copy before the node into the schedule */
sched_add_before(node, copy);
/* set copy as in */
set_irn_n(node, same_pos, copy);
DBG((dbg, LEVEL_1, "created copy %+F for should be same argument "
"at input %d of %+F\n", copy, same_pos, node));
continue;
}
/* for commutative nodes we can simply swap the left/right */
if(is_ia32_commutative(node) && uses_out_reg_pos == 3) {
ia32_swap_left_right(node);
DBG((dbg, LEVEL_1, "swapped left/right input of %+F to resolve "
"should be same constraint\n", node));
continue;
}
#ifdef DEBUG_libfirm
ir_fprintf(stderr, "Note: need perm to resolve should_be_same constraint at %+F (this is unsafe and should not happen in theory...)\n", node);
#endif
/* the out reg is used as node input: we need to permutate our input
* and the other (this is allowed, since the other node can't be live
* after! the operation as we will override the register. */
in[0] = in_node;
in[1] = uses_out_reg;
perm = be_new_Perm(class, irg, block, 2, in);
tmp = get_irn_n(irn, idx1);
set_irn_n(irn, idx1, get_irn_n(irn, idx2));
set_irn_n(irn, idx2, tmp);
perm_proj0 = new_r_Proj(irg, block, perm, get_irn_mode(in[0]), 0);
perm_proj1 = new_r_Proj(irg, block, perm, get_irn_mode(in[1]), 1);
set_ia32_pncode(irn, get_negated_pnc(pnc, mode_E));
arch_set_irn_register(arch_env, perm_proj0, out_reg);
arch_set_irn_register(arch_env, perm_proj1, in_reg);
sched_add_before(node, perm);
DBG((dbg, LEVEL_1, "created perm %+F for should be same argument "
"at input %d of %+F (need permutate with %+F)\n", perm, same_pos,
node, uses_out_reg));
/* use the perm results */
for(i2 = 0; i2 < arity; ++i2) {
ir_node *in = get_irn_n(node, i2);
if(in == in_node) {
set_irn_n<