/* Post-reload compare elimination.
- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2010-2016 Free Software Foundation, Inc.
This file is part of GCC.
(1) All comparison patterns are represented as
- [(set (reg:CC) (compare:CC (reg) (immediate)))]
+ [(set (reg:CC) (compare:CC (reg) (reg_or_immediate)))]
(2) All insn patterns that modify the flags are represented as
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
+#include "df.h"
#include "tm_p.h"
#include "insn-config.h"
#include "recog.h"
-#include "flags.h"
-#include "basic-block.h"
+#include "cfgrtl.h"
#include "tree-pass.h"
-#include "target.h"
-#include "df.h"
#include "domwalk.h"
\f
struct comparison_use
{
/* The instruction in which the result of the compare is used. */
- rtx insn;
+ rtx_insn *insn;
/* The location of the flags register within the use. */
rtx *loc;
/* The comparison code applied against the flags register. */
struct comparison
{
/* The comparison instruction. */
- rtx insn;
+ rtx_insn *insn;
/* The insn prior to the comparison insn that clobbers the flags. */
- rtx prev_clobber;
+ rtx_insn *prev_clobber;
/* The two values being compared. These will be either REGs or
constants. */
rtx in_a, in_b;
+ /* The REG_EH_REGION of the comparison. */
+ rtx eh_note;
+
/* Information about how this comparison is used. */
struct comparison_use uses[MAX_CMP_USE];
/* The original CC_MODE for this comparison. */
- enum machine_mode orig_mode;
+ machine_mode orig_mode;
/* The number of uses identified for this comparison. */
unsigned short n_uses;
bool inputs_valid;
};
-typedef struct comparison *comparison_struct_p;
-
-static vec<comparison_struct_p> all_compares;
+static vec<comparison *> all_compares;
/* Look for a "conforming" comparison, as defined above. If valid, return
the rtx for the COMPARE itself. */
static rtx
-conforming_compare (rtx insn)
+conforming_compare (rtx_insn *insn)
{
rtx set, src, dest;
return NULL;
if (REG_P (XEXP (src, 0))
- && REG_P (XEXP (src, 0))
&& (REG_P (XEXP (src, 1)) || CONSTANT_P (XEXP (src, 1))))
return src;
correct. The term "arithmetic" may be somewhat misleading... */
static bool
-arithmetic_flags_clobber_p (rtx insn)
+arithmetic_flags_clobber_p (rtx_insn *insn)
{
rtx pat, x;
it in CMP; otherwise indicate that we've missed a use. */
static void
-find_flags_uses_in_insn (struct comparison *cmp, rtx insn)
+find_flags_uses_in_insn (struct comparison *cmp, rtx_insn *insn)
{
- df_ref *use_rec, use;
+ df_ref use;
/* If we've already lost track of uses, don't bother collecting more. */
if (cmp->missing_uses)
return;
/* Find a USE of the flags register. */
- for (use_rec = DF_INSN_USES (insn); (use = *use_rec) != NULL; use_rec++)
+ FOR_EACH_INSN_USE (use, insn)
if (DF_REF_REGNO (use) == targetm.flags_regnum)
{
rtx x, *loc;
find_comparison_dom_walker (cdi_direction direction)
: dom_walker (direction) {}
- virtual void before_dom_children (basic_block);
+ virtual edge before_dom_children (basic_block);
};
+/* Return true if conforming COMPARE with EH_NOTE is redundant with comparison
+ CMP and can thus be eliminated. */
+
+static bool
+can_eliminate_compare (rtx compare, rtx eh_note, struct comparison *cmp)
+{
+ /* Take care that it's in the same EH region. */
+ if (cfun->can_throw_non_call_exceptions
+ && !rtx_equal_p (eh_note, cmp->eh_note))
+ return false;
+
+ /* Make sure the compare is redundant with the previous. */
+ if (!rtx_equal_p (XEXP (compare, 0), cmp->in_a)
+ || !rtx_equal_p (XEXP (compare, 1), cmp->in_b))
+ return false;
+
+ /* New mode must be compatible with the previous compare mode. */
+ enum machine_mode new_mode
+ = targetm.cc_modes_compatible (GET_MODE (compare), cmp->orig_mode);
+
+ if (new_mode == VOIDmode)
+ return false;
+
+ if (cmp->orig_mode != new_mode)
+ {
+ /* Generate new comparison for substitution. */
+ rtx flags = gen_rtx_REG (new_mode, targetm.flags_regnum);
+ rtx x = gen_rtx_COMPARE (new_mode, cmp->in_a, cmp->in_b);
+ x = gen_rtx_SET (flags, x);
+
+ if (!validate_change (cmp->insn, &PATTERN (cmp->insn), x, false))
+ return false;
+
+ cmp->orig_mode = new_mode;
+ }
+
+ return true;
+}
+
/* Identify comparison instructions within BB. If the flags from the last
compare in the BB is live at the end of the block, install the compare
in BB->AUX. Called via dom_walker.walk (). */
-void
+edge
find_comparison_dom_walker::before_dom_children (basic_block bb)
{
struct comparison *last_cmp;
- rtx insn, next, last_clobber;
+ rtx_insn *insn, *next, *last_clobber;
bool last_cmp_valid;
+ bool need_purge = false;
bitmap killed;
killed = BITMAP_ALLOC (NULL);
{
rtx src;
- next = (insn == BB_END (bb) ? NULL_RTX : NEXT_INSN (insn));
+ next = (insn == BB_END (bb) ? NULL : NEXT_INSN (insn));
if (!NONDEBUG_INSN_P (insn))
continue;
src = conforming_compare (insn);
if (src)
{
- enum machine_mode src_mode = GET_MODE (src);
+ rtx eh_note = NULL;
- /* Eliminate a compare that's redundant with the previous. */
- if (last_cmp_valid
- && rtx_equal_p (last_cmp->in_a, XEXP (src, 0))
- && rtx_equal_p (last_cmp->in_b, XEXP (src, 1)))
- {
- rtx flags, x;
- enum machine_mode new_mode
- = targetm.cc_modes_compatible (last_cmp->orig_mode, src_mode);
-
- /* New mode is incompatible with the previous compare mode. */
- if (new_mode == VOIDmode)
- continue;
-
- if (new_mode != last_cmp->orig_mode)
- {
- flags = gen_rtx_REG (src_mode, targetm.flags_regnum);
-
- /* Generate new comparison for substitution. */
- x = gen_rtx_COMPARE (new_mode, XEXP (src, 0), XEXP (src, 1));
- x = gen_rtx_SET (VOIDmode, flags, x);
-
- if (!validate_change (last_cmp->insn,
- &PATTERN (last_cmp->insn), x, false))
- continue;
-
- last_cmp->orig_mode = new_mode;
- }
+ if (cfun->can_throw_non_call_exceptions)
+ eh_note = find_reg_note (insn, REG_EH_REGION, NULL);
+ if (last_cmp_valid && can_eliminate_compare (src, eh_note, last_cmp))
+ {
+ if (eh_note)
+ need_purge = true;
delete_insn (insn);
continue;
}
last_cmp->prev_clobber = last_clobber;
last_cmp->in_a = XEXP (src, 0);
last_cmp->in_b = XEXP (src, 1);
- last_cmp->orig_mode = src_mode;
+ last_cmp->eh_note = eh_note;
+ last_cmp->orig_mode = GET_MODE (src);
all_compares.safe_push (last_cmp);
/* It's unusual, but be prepared for comparison patterns that
/* In either case, the previous compare is no longer valid. */
last_cmp = NULL;
last_cmp_valid = false;
- continue;
}
/* Notice if this instruction uses the flags register. */
FOR_EACH_EDGE (e, ei, bb->succs)
{
basic_block dest = e->dest;
- if (bitmap_bit_p (df_get_live_in (bb),
- targetm.flags_regnum)
+ if (bitmap_bit_p (df_get_live_in (bb), targetm.flags_regnum)
&& !single_pred_p (dest))
{
last_cmp->missing_uses = true;
}
}
}
+
+ /* If we deleted a compare with a REG_EH_REGION note, we may need to
+ remove EH edges. */
+ if (need_purge)
+ purge_dead_edges (bb);
+
+ return NULL;
}
/* Find all comparisons in the function. */
maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
rtx b ATTRIBUTE_UNUSED)
{
- enum machine_mode sel_mode;
+ machine_mode sel_mode;
const int n = cmp->n_uses;
rtx flags = NULL;
sel_mode = SELECT_CC_MODE (cmp->uses[0].code, a, b);
for (i = 1; i < n; ++i)
{
- enum machine_mode new_mode;
- new_mode = SELECT_CC_MODE (cmp->uses[i].code, a, b);
+ machine_mode new_mode = SELECT_CC_MODE (cmp->uses[i].code, a, b);
if (new_mode != sel_mode)
{
sel_mode = targetm.cc_modes_compatible (sel_mode, new_mode);
return NULL;
}
}
-
+
if (sel_mode != cmp->orig_mode)
{
flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
static bool
try_eliminate_compare (struct comparison *cmp)
{
- rtx x, insn, bb_head, flags, in_a, cmp_src;
+ rtx_insn *insn, *bb_head;
+ rtx x, flags, in_a, cmp_src;
/* We must have found an interesting "clobber" preceding the compare. */
if (cmp->prev_clobber == NULL)
| DF_REF_MUST_CLOBBER | DF_REF_SIGN_EXTRACT
| DF_REF_ZERO_EXTRACT | DF_REF_STRICT_LOW_PART
| DF_REF_PRE_POST_MODIFY);
- df_ref *def_rec, def;
+ df_ref def;
/* Note that the BB_HEAD is always either a note or a label, but in
any case it means that IN_A is defined outside the block. */
continue;
/* Find a possible def of IN_A in INSN. */
- for (def_rec = DF_INSN_DEFS (insn); (def = *def_rec) != NULL; def_rec++)
+ FOR_EACH_INSN_DEF (def, insn)
if (DF_REF_REGNO (def) == REGNO (in_a))
break;
/* Generate a new comparison for installation in the setter. */
x = copy_rtx (cmp_src);
x = gen_rtx_COMPARE (GET_MODE (flags), x, cmp->in_b);
- x = gen_rtx_SET (VOIDmode, flags, x);
+ x = gen_rtx_SET (flags, x);
/* Succeed if the new instruction is valid. Note that we may have started
a change group within maybe_select_cc_mode, therefore we must continue. */
validate_change (insn, &XVECEXP (PATTERN (insn), 0, 1), x, true);
if (!apply_change_group ())
return false;
-
+
/* Success. Delete the compare insn... */
delete_insn (cmp->insn);
return 0;
}
-static bool
-gate_compare_elim_after_reload (void)
-{
- /* Setting this target hook value is how a backend indicates the need. */
- if (targetm.flags_regnum == INVALID_REGNUM)
- return false;
- return flag_compare_elim_after_reload;
-}
-
namespace {
const pass_data pass_data_compare_elim_after_reload =
RTL_PASS, /* type */
"cmpelim", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
TV_NONE, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- ( TODO_df_finish | TODO_df_verify
- | TODO_verify_rtl_sharing ), /* todo_flags_finish */
+ ( TODO_df_finish | TODO_df_verify ), /* todo_flags_finish */
};
class pass_compare_elim_after_reload : public rtl_opt_pass
{}
/* opt_pass methods: */
- bool gate () { return gate_compare_elim_after_reload (); }
- unsigned int execute () { return execute_compare_elim_after_reload (); }
+ virtual bool gate (function *)
+ {
+ /* Setting this target hook value is how a backend indicates the need. */
+ if (targetm.flags_regnum == INVALID_REGNUM)
+ return false;
+ return flag_compare_elim_after_reload;
+ }
+
+ virtual unsigned int execute (function *)
+ {
+ return execute_compare_elim_after_reload ();
+ }
}; // class pass_compare_elim_after_reload