PR c++/68795: fix uninitialized close_paren_loc in cp_parser_postfix_expression
[gcc.git] / gcc / compare-elim.c
index 3fbe140b80c675b7fc6613cb780537050f1a1f8c..a5ce2bfcf2c023c7996a9f2f9b9069a974d648bd 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
 
@@ -35,7 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 
    (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
 
@@ -57,16 +57,15 @@ along with GCC; see the file COPYING3.  If not see
 #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
@@ -81,7 +80,7 @@ along with GCC; see the file COPYING3.  If not see
 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.  */
@@ -91,20 +90,23 @@ struct comparison_use
 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;
@@ -118,15 +120,13 @@ struct comparison
   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;
 
@@ -143,7 +143,6 @@ conforming_compare (rtx insn)
     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;
 
@@ -156,7 +155,7 @@ conforming_compare (rtx insn)
    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;
 
@@ -191,16 +190,16 @@ arithmetic_flags_clobber_p (rtx insn)
    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;
@@ -249,19 +248,59 @@ public:
   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);
@@ -291,7 +330,7 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
     {
       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;
 
@@ -302,36 +341,15 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
       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;
            }
@@ -341,7 +359,8 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
          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
@@ -360,7 +379,6 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
          /* 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.  */
@@ -394,8 +412,7 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
          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;
@@ -404,6 +421,13 @@ find_comparison_dom_walker::before_dom_children (basic_block bb)
            }
        }
     }
+
+  /* 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.  */
@@ -430,7 +454,7 @@ static rtx
 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;
 
@@ -462,8 +486,7 @@ maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
       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);
@@ -471,7 +494,7 @@ maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
                return NULL;
            }
        }
-      
+
       if (sel_mode != cmp->orig_mode)
        {
          flags = gen_rtx_REG (sel_mode, targetm.flags_regnum);
@@ -490,7 +513,8 @@ maybe_select_cc_mode (struct comparison *cmp, rtx a ATTRIBUTE_UNUSED,
 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)
@@ -522,7 +546,7 @@ try_eliminate_compare (struct comparison *cmp)
           | 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.  */
@@ -532,7 +556,7 @@ try_eliminate_compare (struct comparison *cmp)
        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;
 
@@ -589,14 +613,14 @@ try_eliminate_compare (struct comparison *cmp)
   /* 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);
 
@@ -643,15 +667,6 @@ execute_compare_elim_after_reload (void)
   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 =
@@ -659,15 +674,12 @@ 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
@@ -678,8 +690,18 @@ public:
   {}
 
   /* 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