PR c++/68795: fix uninitialized close_paren_loc in cp_parser_postfix_expression
[gcc.git] / gcc / compare-elim.c
index b021e8daf88ea69fbcee79099a381e0cfeedb20a..a5ce2bfcf2c023c7996a9f2f9b9069a974d648bd 100644 (file)
@@ -1,6 +1,5 @@
 /* Post-reload compare elimination.
-   Copyright (C) 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 2010-2016 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -36,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
 
@@ -58,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
@@ -82,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.  */
@@ -92,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;
@@ -119,17 +120,13 @@ struct comparison
   bool inputs_valid;
 };
   
-typedef struct comparison *comparison_struct_p;
-DEF_VEC_P(comparison_struct_p);
-DEF_VEC_ALLOC_P(comparison_struct_p, heap);
-
-static VEC(comparison_struct_p, heap) *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;
 
@@ -146,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;
 
@@ -159,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;
 
@@ -194,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;
@@ -246,17 +242,65 @@ find_flags_uses_in_insn (struct comparison *cmp, rtx insn)
   cmp->missing_uses = true;
 }
 
+class find_comparison_dom_walker : public dom_walker
+{
+public:
+  find_comparison_dom_walker (cdi_direction direction)
+    : dom_walker (direction) {}
+
+  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 walk_dominators_tree.  */
+   in BB->AUX.  Called via dom_walker.walk ().  */
 
-static void
-find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
-                       basic_block bb)
+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);
@@ -286,7 +330,7 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
     {
       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;
 
@@ -297,36 +341,15 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
       src = conforming_compare (insn);
       if (src)
        {
-         enum machine_mode src_mode = GET_MODE (src);
-
-         /* 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);
+         rtx eh_note = NULL;
 
-             /* 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;
            }
@@ -336,8 +359,9 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
          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;
-         VEC_safe_push (comparison_struct_p, heap, all_compares, last_cmp);
+         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
             also clobber an input, or perhaps a scratch.  */
@@ -355,7 +379,6 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
          /* 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.  */
@@ -389,8 +412,7 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
          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;
@@ -399,6 +421,13 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
            }
        }
     }
+
+  /* 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.  */
@@ -406,17 +435,10 @@ find_comparisons_in_bb (struct dom_walk_data *data ATTRIBUTE_UNUSED,
 static void
 find_comparisons (void)
 {
-  struct dom_walk_data data;
-
-  memset (&data, 0, sizeof(data));
-  data.dom_direction = CDI_DOMINATORS;
-  data.before_dom_children = find_comparisons_in_bb;
-
   calculate_dominance_info (CDI_DOMINATORS);
 
-  init_walk_dominator_tree (&data);
-  walk_dominator_tree (&data, ENTRY_BLOCK_PTR);
-  fini_walk_dominator_tree (&data);
+  find_comparison_dom_walker (CDI_DOMINATORS)
+    .walk (cfun->cfg->x_entry_block_ptr);
 
   clear_aux_for_blocks ();
   free_dominance_info (CDI_DOMINATORS);
@@ -432,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;
 
@@ -464,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);
@@ -473,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);
@@ -492,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)
@@ -524,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.  */
@@ -534,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;
 
@@ -591,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);
 
@@ -623,56 +645,70 @@ execute_compare_elim_after_reload (void)
 {
   df_analyze ();
 
-  gcc_checking_assert (all_compares == NULL);
+  gcc_checking_assert (!all_compares.exists ());
 
   /* Locate all comparisons and their uses, and eliminate duplicates.  */
   find_comparisons ();
-  if (all_compares)
+  if (all_compares.exists ())
     {
       struct comparison *cmp;
       size_t i;
 
       /* Eliminate comparisons that are redundant with flags computation.  */
-      FOR_EACH_VEC_ELT (comparison_struct_p, all_compares, i, cmp)
+      FOR_EACH_VEC_ELT (all_compares, i, cmp)
        {
          try_eliminate_compare (cmp);
          XDELETE (cmp);
        }
 
-      VEC_free (comparison_struct_p, heap, all_compares);
-      all_compares = NULL;
+      all_compares.release ();
     }
 
   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 {
 
-struct rtl_opt_pass pass_compare_elim_after_reload =
+const pass_data pass_data_compare_elim_after_reload =
 {
- {
-  RTL_PASS,
-  "cmpelim",                           /* name */
-  gate_compare_elim_after_reload,      /* gate */
-  execute_compare_elim_after_reload,   /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  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_ggc_collect                   /* todo_flags_finish */
- }
+  RTL_PASS, /* type */
+  "cmpelim", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_df_finish | TODO_df_verify ), /* todo_flags_finish */
 };
+
+class pass_compare_elim_after_reload : public rtl_opt_pass
+{
+public:
+  pass_compare_elim_after_reload (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_compare_elim_after_reload, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  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
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_compare_elim_after_reload (gcc::context *ctxt)
+{
+  return new pass_compare_elim_after_reload (ctxt);
+}