Commit a7fe14d4 authored by Andreas Fried's avatar Andreas Fried
Browse files

Add a callback to be_sched_fix_flags to optimize flag usage.

Implemented for now: When the flags of one Sub or Cmp node are needed, but
the flags of another one are currently alive, invert the condition code and
avoid rematerialization.
parent 2b4b670a
......@@ -125,7 +125,7 @@ static const arch_irn_ops_t amd64_irn_ops = {
static void amd64_before_ra(ir_graph *irg)
{
be_sched_fix_flags(irg, &amd64_reg_classes[CLASS_amd64_flags], NULL, NULL);
be_sched_fix_flags(irg, &amd64_reg_classes[CLASS_amd64_flags], NULL, NULL, NULL);
be_add_missing_keeps(irg);
}
......
......@@ -163,7 +163,7 @@ static void arm_emit(ir_graph *irg)
static void arm_before_ra(ir_graph *irg)
{
be_sched_fix_flags(irg, &arm_reg_classes[CLASS_arm_flags], NULL, NULL);
be_sched_fix_flags(irg, &arm_reg_classes[CLASS_arm_flags], NULL, NULL, NULL);
}
static ir_entity *divsi3;
......
......@@ -43,6 +43,7 @@ static const arch_register_class_t *flag_class;
static const arch_register_t *flags_reg;
static func_rematerialize remat;
static check_modifies_flags check_modify;
static try_replace_flags try_replace;
static bool changed;
static ir_node *default_remat(ir_node *node, ir_node *after)
......@@ -59,6 +60,15 @@ static bool default_check_modifies(const ir_node *node)
return arch_irn_is(node, modify_flags);
}
static bool default_try_replace(ir_node *consumers, ir_node *flags, ir_node *available, int pn)
{
(void)consumers;
(void)flags;
(void)available;
(void)pn;
return false;
}
/**
* tests whether we can legally move node node after node after
* (only works for nodes in same block)
......@@ -125,8 +135,16 @@ static void move_other_uses(ir_node *node, ir_node *copy)
}
}
static void rematerialize_or_move(ir_node *flags_needed, ir_node *node,
ir_node *flag_consumers, int pn)
/**
* Tries the following solutions in order:
* 1. Move flags_needed behind node
* 2. Modify flag_consumers to use available_flags instead of flags_needed
* 3. Rematerialize flags_needed behind node
*
* Returns true, if flag_consumers now use available_flags.
*/
static bool rematerialize_or_move(ir_node *flags_needed, ir_node *node,
ir_node *flag_consumers, int pn, ir_node *available_flags)
{
if (!is_Block(node)
&& get_nodes_block(flags_needed) == get_nodes_block(node)
......@@ -135,7 +153,13 @@ static void rematerialize_or_move(ir_node *flags_needed, ir_node *node,
sched_remove(flags_needed);
sched_add_after(node, flags_needed);
/* No need to update liveness, the node stays in the same block */
return;
return false;
}
/* Try to use the flags available at this point. */
if (available_flags != NULL &&
try_replace(flag_consumers, flags_needed, available_flags, pn)) {
return true;
}
changed = true;
......@@ -176,6 +200,8 @@ static void rematerialize_or_move(ir_node *flags_needed, ir_node *node,
}
}
}
return false;
}
/**
......@@ -213,7 +239,7 @@ static void fix_flags_walker(ir_node *block, void *env)
if (flags_needed != NULL && check_modify(test)) {
/* rematerialize */
rematerialize_or_move(flags_needed, node, flag_consumers, pn);
rematerialize_or_move(flags_needed, node, flag_consumers, pn, test);
flags_needed = NULL;
flag_consumers = NULL;
}
......@@ -238,9 +264,14 @@ static void fix_flags_walker(ir_node *block, void *env)
if (skip_Proj(new_flags_needed) != flags_needed) {
if (flags_needed != NULL) {
/* rematerialize node */
rematerialize_or_move(flags_needed, node, flag_consumers, pn);
flags_needed = NULL;
flag_consumers = NULL;
bool use_new_flags = rematerialize_or_move(
flags_needed, node, flag_consumers, pn, new_flags_needed);
/* We are only done with
* flag_consumers, if flags_needed has
* actually been rematerialized. */
if (!use_new_flags) {
flag_consumers = NULL;
}
}
flags_needed = new_flags_needed;
......@@ -261,7 +292,7 @@ static void fix_flags_walker(ir_node *block, void *env)
if (flags_needed != NULL) {
assert(get_nodes_block(flags_needed) != block);
rematerialize_or_move(flags_needed, place, flag_consumers, pn);
rematerialize_or_move(flags_needed, place, flag_consumers, pn, NULL);
flags_needed = NULL;
flag_consumers = NULL;
}
......@@ -272,17 +303,21 @@ static void fix_flags_walker(ir_node *block, void *env)
void be_sched_fix_flags(ir_graph *irg, const arch_register_class_t *flag_cls,
func_rematerialize remat_func,
check_modifies_flags check_modifies_flags_func)
check_modifies_flags check_modifies_flags_func,
try_replace_flags try_replace_flags_func)
{
flag_class = flag_cls;
flags_reg = &flag_class->regs[0];
remat = remat_func;
check_modify = check_modifies_flags_func;
try_replace = try_replace_flags_func;
changed = false;
if (remat == NULL)
remat = &default_remat;
if (check_modify == NULL)
check_modify = &default_check_modifies;
if (try_replace == NULL)
try_replace = &default_try_replace;
assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_DOMINANCE);
ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK |
......
......@@ -23,6 +23,12 @@ typedef ir_node * (*func_rematerialize) (ir_node *node, ir_node *after);
*/
typedef bool (*check_modifies_flags) (const ir_node *node);
/**
* Callback function that checks whether consumers can use the
* available flags instead of their original ones.
*/
typedef bool (*try_replace_flags) (ir_node *consumers, ir_node *flags_needed, ir_node *available_flags, int pn);
/**
* Walks the schedule and ensures that flags aren't destroyed between producer
* and consumer of flags. It does so by moving down/rematerialising of the
......@@ -32,6 +38,7 @@ typedef bool (*check_modifies_flags) (const ir_node *node);
*/
void be_sched_fix_flags(ir_graph *irg, const arch_register_class_t *flag_cls,
func_rematerialize remat_func,
check_modifies_flags check_modifies_flags_func);
check_modifies_flags check_modifies_flags_func,
try_replace_flags try_replace_flags_func);
#endif
......@@ -641,6 +641,45 @@ static ir_node *flags_remat(ir_node *node, ir_node *after)
return copy;
}
COMPILETIME_ASSERT((int)(n_ia32_Sub_minuend) == (int)(n_ia32_Cmp_left) &&
(int)(n_ia32_Sub_subtrahend) == (int)(n_ia32_Cmp_right),
Cmp_and_Sub_operand_numbers_equal);
static bool ia32_try_replace_flags(ir_node *consumers, ir_node *flags, ir_node *available, int pn)
{
if ((is_ia32_Sub(flags) || is_ia32_Cmp(flags)) &&
(is_ia32_Sub(available) || is_ia32_Cmp(available))) {
ir_node *flags_left = get_irn_n(flags, n_ia32_Cmp_left);
ir_node *flags_right = get_irn_n(flags, n_ia32_Cmp_right);
ir_node *avail_left = get_irn_n(available, n_ia32_Cmp_left);
ir_node *avail_right = get_irn_n(available, n_ia32_Cmp_right);
/* Assuming CSE would have found the more obvious case */
if (flags_left == avail_right && avail_left == flags_right) {
/* We can use available if we reverse the
* consumers' condition codes. */
ir_mode *flag_mode = ia32_reg_classes[CLASS_ia32_flags].mode;
const arch_register_t *flag_reg = &ia32_reg_classes[CLASS_ia32_flags].regs[0];
for (ir_node *c = consumers; c != NULL; c = get_irn_link(c)) {
x86_condition_code_t cc = get_ia32_condcode(c);
set_ia32_condcode(c, x86_invert_condition_code(cc));
foreach_irn_in(c, i, in) {
if (get_irn_mode(in) == flag_mode) {
ir_node *proj = new_r_Proj(available, flag_mode, pn);
arch_set_irn_register(proj, flag_reg);
set_irn_n(c, i, proj);
}
}
}
return true;
}
}
return false;
}
static void remat_simplifier(ir_node *block, void *env)
{
(void)env;
......@@ -726,7 +765,7 @@ static void ia32_before_ra(ir_graph *irg)
/* fixup flags */
be_sched_fix_flags(irg, &ia32_reg_classes[CLASS_ia32_flags],
&flags_remat, NULL);
&flags_remat, NULL, &ia32_try_replace_flags);
simplify_remat_nodes(irg);
be_add_missing_keeps(irg);
......
......@@ -224,9 +224,9 @@ static void sparc_before_ra(ir_graph *irg)
{
/* fixup flags register */
be_sched_fix_flags(irg, &sparc_reg_classes[CLASS_sparc_flags_class],
NULL, sparc_modifies_flags);
NULL, sparc_modifies_flags, NULL);
be_sched_fix_flags(irg, &sparc_reg_classes[CLASS_sparc_fpflags_class],
NULL, sparc_modifies_fp_flags);
NULL, sparc_modifies_fp_flags, NULL);
be_add_missing_keeps(irg);
}
......
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