]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/compare-elim.c
PR fortran/95090 - ICE: identifier overflow
[thirdparty/gcc.git] / gcc / compare-elim.c
index 794a452f98bc81a2fd12bb667c7ff936b22f7e0e..85f3e34407413332162e0118de26bba5b96ce4b3 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
 
@@ -97,6 +97,9 @@ struct comparison
   /* 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;
@@ -120,10 +123,32 @@ struct comparison
 
   /* 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.  */
 
@@ -144,7 +169,7 @@ conforming_compare (rtx_insn *insn)
   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)))
@@ -275,10 +300,13 @@ can_eliminate_compare (rtx compare, rtx eh_note, struct comparison *cmp)
     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);
@@ -309,26 +337,22 @@ can_eliminate_compare (rtx compare, rtx eh_note, struct comparison *cmp)
 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))
@@ -338,6 +362,7 @@ find_comparison_dom_walker::before_dom_children (basic_block 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;
@@ -346,10 +371,6 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
       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)
        {
@@ -369,10 +390,18 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
          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
@@ -388,28 +417,36 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
            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)
@@ -625,13 +662,19 @@ can_merge_compare_into_arith (rtx_insn *cmp_insn, rtx_insn *arith_insn)
 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
@@ -643,7 +686,7 @@ try_validate_parallel (rtx set_a, rtx set_b)
    <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.  */
@@ -653,7 +696,7 @@ try_merge_compare (struct comparison *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;
@@ -664,21 +707,7 @@ try_merge_compare (struct comparison *cmp)
   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;
@@ -687,6 +716,13 @@ try_merge_compare (struct comparison *cmp)
     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)
     {
@@ -724,11 +760,12 @@ try_merge_compare (struct comparison *cmp)
 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;
 
+  /* We must have found an interesting "clobber" preceding the compare.  */
   if (cmp->prev_clobber == NULL)
     return false;
 
@@ -775,7 +812,7 @@ try_eliminate_compare (struct comparison *cmp)
 
   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)
@@ -789,7 +826,7 @@ try_eliminate_compare (struct comparison *cmp)
           && (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)
@@ -800,19 +837,36 @@ try_eliminate_compare (struct comparison *cmp)
           && 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:
@@ -854,7 +908,6 @@ try_eliminate_compare (struct comparison *cmp)
 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 ());