/* Post-reload compare elimination.
- Copyright (C) 2010-2017 Free Software Foundation, Inc.
+ Copyright (C) 2010-2020 Free Software Foundation, Inc.
This file is part of GCC.
/* The insn prior to the comparison insn that clobbers the flags. */
rtx_insn *prev_clobber;
+ /* The insn prior to the comparison insn that sets in_a REG. */
+ rtx_insn *in_a_setter;
+
/* The two values being compared. These will be either REGs or
constants. */
rtx in_a, in_b;
/* True if its inputs are still valid at the end of the block. */
bool inputs_valid;
+
+ /* Whether IN_A is wrapped in a NOT before being compared. */
+ bool not_in_a;
};
static vec<comparison *> all_compares;
+/* Return whether X is a NOT unary expression. */
+
+static bool
+is_not (rtx x)
+{
+ return GET_CODE (x) == NOT;
+}
+
+/* Strip a NOT unary expression around X, if any. */
+
+static rtx
+strip_not (rtx x)
+{
+ if (is_not (x))
+ return XEXP (x, 0);
+
+ return x;
+}
+
/* Look for a "conforming" comparison, as defined above. If valid, return
the rtx for the COMPARE itself. */
if (!REG_P (dest) || REGNO (dest) != targetm.flags_regnum)
return NULL;
- if (!REG_P (XEXP (src, 0)))
+ if (!REG_P (strip_not (XEXP (src, 0))))
return NULL;
if (CONSTANT_P (XEXP (src, 1)) || REG_P (XEXP (src, 1)))
return false;
/* Make sure the compare is redundant with the previous. */
- if (!rtx_equal_p (XEXP (compare, 0), cmp->in_a)
+ if (!rtx_equal_p (strip_not (XEXP (compare, 0)), cmp->in_a)
|| !rtx_equal_p (XEXP (compare, 1), cmp->in_b))
return false;
+ if (is_not (XEXP (compare, 0)) != cmp->not_in_a)
+ return false;
+
/* New mode must be compatible with the previous compare mode. */
machine_mode new_mode
= targetm.cc_modes_compatible (GET_MODE (compare), cmp->orig_mode);
edge
find_comparison_dom_walker::before_dom_children (basic_block bb)
{
- struct comparison *last_cmp;
- rtx_insn *insn, *next, *last_clobber;
- bool last_cmp_valid;
+ rtx_insn *insn, *next;
bool need_purge = false;
- bitmap killed;
-
- killed = BITMAP_ALLOC (NULL);
+ rtx_insn *last_setter[FIRST_PSEUDO_REGISTER];
/* The last comparison that was made. Will be reset to NULL
once the flags are clobbered. */
- last_cmp = NULL;
+ struct comparison *last_cmp = NULL;
/* True iff the last comparison has not been clobbered, nor
have its inputs. Used to eliminate duplicate compares. */
- last_cmp_valid = false;
+ bool last_cmp_valid = false;
/* The last insn that clobbered the flags, if that insn is of
a form that may be valid for eliminating a following compare.
To be reset to NULL once the flags are set otherwise. */
- last_clobber = NULL;
+ rtx_insn *last_clobber = NULL;
/* Propagate the last live comparison throughout the extended basic block. */
if (single_pred_p (bb))
last_cmp_valid = last_cmp->inputs_valid;
}
+ memset (last_setter, 0, sizeof (last_setter));
for (insn = BB_HEAD (bb); insn; insn = next)
{
rtx src;
if (!NONDEBUG_INSN_P (insn))
continue;
- /* Compute the set of registers modified by this instruction. */
- bitmap_clear (killed);
- df_simulate_find_defs (insn, killed);
-
src = conforming_compare (insn);
if (src)
{
last_cmp = XCNEW (struct comparison);
last_cmp->insn = insn;
last_cmp->prev_clobber = last_clobber;
- last_cmp->in_a = XEXP (src, 0);
+ last_cmp->in_a = strip_not (XEXP (src, 0));
last_cmp->in_b = XEXP (src, 1);
+ last_cmp->not_in_a = is_not (XEXP (src, 0));
last_cmp->eh_note = eh_note;
last_cmp->orig_mode = GET_MODE (src);
+ if (last_cmp->in_b == const0_rtx
+ && last_setter[REGNO (last_cmp->in_a)])
+ {
+ rtx set = single_set (last_setter[REGNO (last_cmp->in_a)]);
+ if (set && rtx_equal_p (SET_DEST (set), last_cmp->in_a))
+ last_cmp->in_a_setter = last_setter[REGNO (last_cmp->in_a)];
+ }
all_compares.safe_push (last_cmp);
/* It's unusual, but be prepared for comparison patterns that
find_flags_uses_in_insn (last_cmp, insn);
/* Notice if this instruction kills the flags register. */
- if (bitmap_bit_p (killed, targetm.flags_regnum))
- {
- /* See if this insn could be the "clobber" that eliminates
- a future comparison. */
- last_clobber = (arithmetic_flags_clobber_p (insn) ? insn : NULL);
-
- /* In either case, the previous compare is no longer valid. */
- last_cmp = NULL;
- last_cmp_valid = false;
- }
+ df_ref def;
+ FOR_EACH_INSN_DEF (def, insn)
+ if (DF_REF_REGNO (def) == targetm.flags_regnum)
+ {
+ /* See if this insn could be the "clobber" that eliminates
+ a future comparison. */
+ last_clobber = (arithmetic_flags_clobber_p (insn)
+ ? insn : NULL);
+
+ /* In either case, the previous compare is no longer valid. */
+ last_cmp = NULL;
+ last_cmp_valid = false;
+ break;
+ }
}
- /* Notice if any of the inputs to the comparison have changed. */
- if (last_cmp_valid
- && (bitmap_bit_p (killed, REGNO (last_cmp->in_a))
- || (REG_P (last_cmp->in_b)
- && bitmap_bit_p (killed, REGNO (last_cmp->in_b)))))
- last_cmp_valid = false;
+ /* Notice if any of the inputs to the comparison have changed
+ and remember last insn that sets each register. */
+ df_ref def;
+ FOR_EACH_INSN_DEF (def, insn)
+ {
+ if (last_cmp_valid
+ && (DF_REF_REGNO (def) == REGNO (last_cmp->in_a)
+ || (REG_P (last_cmp->in_b)
+ && DF_REF_REGNO (def) == REGNO (last_cmp->in_b))))
+ last_cmp_valid = false;
+ last_setter[DF_REF_REGNO (def)] = insn;
+ }
}
- BITMAP_FREE (killed);
-
/* Remember the live comparison for subsequent members of
the extended basic block. */
if (last_cmp)
static rtx
try_validate_parallel (rtx set_a, rtx set_b)
{
- rtx par
- = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set_a, set_b));
+ rtx par = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set_a, set_b));
+ rtx_insn *insn = make_insn_raw (par);
- rtx_insn *insn;
- insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, par, 0, -1, 0);
+ if (insn_invalid_p (insn, false))
+ {
+ crtl->emit.x_cur_insn_uid--;
+ return NULL_RTX;
+ }
- return recog_memoized (insn) > 0 ? par : NULL_RTX;
+ SET_PREV_INSN (insn) = NULL_RTX;
+ SET_NEXT_INSN (insn) = NULL_RTX;
+ INSN_LOCATION (insn) = 0;
+ return insn;
}
/* For a comparison instruction described by CMP check if it compares a
<instructions that don't read the condition register>
I2: CC := CMP R1 0
I2 can be merged with I1 into:
- I1: { R1 := R2 + R3 ; CC := CMP (R2 + R3) 0 }
+ I1: { CC := CMP (R2 + R3) 0 ; R1 := R2 + R3 }
This catches cases where R1 is used between I1 and I2 and therefore
combine and other RTL optimisations will not try to propagate it into
I2. Return true if we succeeded in merging CMP. */
{
rtx_insn *cmp_insn = cmp->insn;
- if (!REG_P (cmp->in_a) || cmp->in_b != const0_rtx)
+ if (cmp->in_b != const0_rtx || cmp->in_a_setter == NULL)
return false;
rtx in_a = cmp->in_a;
df_ref use;
if (!use)
return false;
- /* Validate the data flow information before attempting to
- find the instruction that defines in_a. */
-
- struct df_link *ref_chain;
- ref_chain = DF_REF_CHAIN (use);
- if (!ref_chain || !ref_chain->ref
- || !DF_REF_INSN_INFO (ref_chain->ref) || ref_chain->next != NULL)
- return false;
-
- rtx_insn *def_insn = DF_REF_INSN (ref_chain->ref);
- /* We found the insn that defines in_a. Only consider the cases where
- it is in the same block as the comparison. */
- if (BLOCK_FOR_INSN (cmp_insn) != BLOCK_FOR_INSN (def_insn))
- return false;
-
+ rtx_insn *def_insn = cmp->in_a_setter;
rtx set = single_set (def_insn);
if (!set)
return false;
return false;
rtx src = SET_SRC (set);
+
+ /* If the source uses addressing modes with side effects, we can't
+ do the merge because we'd end up with a PARALLEL that has two
+ instances of that side effect in it. */
+ if (side_effects_p (src))
+ return false;
+
rtx flags = maybe_select_cc_mode (cmp, src, CONST0_RTX (GET_MODE (src)));
if (!flags)
{
static bool
try_eliminate_compare (struct comparison *cmp)
{
- rtx flags, in_a, in_b, cmp_src;
+ rtx flags, in_a, in_b, cmp_a, cmp_b;
if (try_merge_compare (cmp))
return true;
rtx x = XVECEXP (PATTERN (insn), 0, 0);
if (rtx_equal_p (SET_DEST (x), in_a))
- cmp_src = SET_SRC (x);
+ cmp_a = SET_SRC (x);
/* Also check operations with implicit extensions, e.g.:
[(set (reg:DI)
&& (GET_CODE (SET_SRC (x)) == ZERO_EXTEND
|| GET_CODE (SET_SRC (x)) == SIGN_EXTEND)
&& GET_MODE (XEXP (SET_SRC (x), 0)) == GET_MODE (in_a))
- cmp_src = XEXP (SET_SRC (x), 0);
+ cmp_a = XEXP (SET_SRC (x), 0);
/* Also check fully redundant comparisons, e.g.:
[(set (reg:SI)
&& GET_CODE (SET_SRC (x)) == MINUS
&& rtx_equal_p (XEXP (SET_SRC (x), 0), in_a)
&& rtx_equal_p (XEXP (SET_SRC (x), 1), in_b))
- cmp_src = in_a;
+ cmp_a = in_a;
+
+ else
+ return false;
+
+ /* If the source uses addressing modes with side effects, we can't
+ do the merge because we'd end up with a PARALLEL that has two
+ instances of that side effect in it. */
+ if (side_effects_p (cmp_a))
+ return false;
+ if (in_a == in_b)
+ cmp_b = cmp_a;
+ else if (rtx_equal_p (SET_DEST (x), in_b))
+ cmp_b = SET_SRC (x);
else
+ cmp_b = in_b;
+ if (side_effects_p (cmp_b))
return false;
/* Determine if we ought to use a different CC_MODE here. */
- flags = maybe_select_cc_mode (cmp, cmp_src, in_b);
+ flags = maybe_select_cc_mode (cmp, cmp_a, cmp_b);
if (flags == NULL)
flags = gen_rtx_REG (cmp->orig_mode, targetm.flags_regnum);
/* Generate a new comparison for installation in the setter. */
- rtx y = copy_rtx (cmp_src);
- y = gen_rtx_COMPARE (GET_MODE (flags), y, in_b);
+ rtx y = cmp->not_in_a
+ ? gen_rtx_NOT (GET_MODE (cmp_a), copy_rtx (cmp_a))
+ : copy_rtx (cmp_a);
+ y = gen_rtx_COMPARE (GET_MODE (flags), y, copy_rtx (cmp_b));
y = gen_rtx_SET (flags, y);
/* Canonicalize instruction to:
static unsigned int
execute_compare_elim_after_reload (void)
{
- df_chain_add_problem (DF_UD_CHAIN + DF_DU_CHAIN);
df_analyze ();
gcc_checking_assert (!all_compares.exists ());