OSDN Git Service

PR middle-end/26983
[pf3gnuchains/gcc-fork.git] / gcc / recog.c
index 4dfdca1..cd1cb7d 100644 (file)
@@ -16,8 +16,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 
 #include "config.h"
@@ -31,6 +31,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "hard-reg-set.h"
 #include "recog.h"
 #include "regs.h"
+#include "addresses.h"
 #include "expr.h"
 #include "function.h"
 #include "flags.h"
@@ -39,6 +40,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "basic-block.h"
 #include "output.h"
 #include "reload.h"
+#include "timevar.h"
+#include "tree-pass.h"
 
 #ifndef STACK_PUSH_CODE
 #ifdef STACK_GROWS_DOWNWARD
@@ -235,6 +238,7 @@ validate_change (rtx object, rtx *loc, rtx new, int in_group)
     return apply_change_group ();
 }
 
+
 /* This subroutine of apply_change_group verifies whether the changes to INSN
    were valid; i.e. whether INSN can still be recognized.  */
 
@@ -294,11 +298,11 @@ num_changes_pending (void)
   return num_changes;
 }
 
-/* Apply a group of changes previously issued with `validate_change'.
+/* Tentatively apply the changes numbered NUM and up.
    Return 1 if all changes are valid, zero otherwise.  */
 
 int
-apply_change_group (void)
+verify_changes (int num)
 {
   int i;
   rtx last_validated = NULL_RTX;
@@ -312,7 +316,7 @@ apply_change_group (void)
      we also require that the operands meet the constraints for
      the insn.  */
 
-  for (i = 0; i < num_changes; i++)
+  for (i = num; i < num_changes; i++)
     {
       rtx object = changes[i].object;
 
@@ -376,17 +380,38 @@ apply_change_group (void)
       last_validated = object;
     }
 
-  if (i == num_changes)
-    {
-      basic_block bb;
+  return (i == num_changes);
+}
+
+/* A group of changes has previously been issued with validate_change and
+   verified with verify_changes.  Update the BB_DIRTY flags of the affected
+   blocks, and clear num_changes.  */
 
-      for (i = 0; i < num_changes; i++)
-       if (changes[i].object
-           && INSN_P (changes[i].object)
-           && (bb = BLOCK_FOR_INSN (changes[i].object)))
-         bb->flags |= BB_DIRTY;
+void
+confirm_change_group (void)
+{
+  int i;
+  basic_block bb;
+
+  for (i = 0; i < num_changes; i++)
+    if (changes[i].object
+       && INSN_P (changes[i].object)
+       && (bb = BLOCK_FOR_INSN (changes[i].object)))
+      bb->flags |= BB_DIRTY;
 
-      num_changes = 0;
+  num_changes = 0;
+}
+
+/* Apply a group of changes previously issued with `validate_change'.
+   If all changes are valid, call confirm_change_group and return 1,
+   otherwise, call cancel_changes and return 0.  */
+
+int
+apply_change_group (void)
+{
+  if (verify_changes (0))
+    {
+      confirm_change_group ();
       return 1;
     }
   else
@@ -396,6 +421,7 @@ apply_change_group (void)
     }
 }
 
+
 /* Return the number of changes so far in the current group.  */
 
 int
@@ -624,17 +650,6 @@ validate_replace_rtx_1 (rtx *loc, rtx from, rtx to, rtx object)
     }
 }
 
-/* Try replacing every occurrence of FROM in subexpression LOC of INSN
-   with TO.  After all changes have been made, validate by seeing
-   if INSN is still valid.  */
-
-int
-validate_replace_rtx_subexp (rtx from, rtx to, rtx insn, rtx *loc)
-{
-  validate_replace_rtx_1 (loc, from, to, insn);
-  return apply_change_group ();
-}
-
 /* Try replacing every occurrence of FROM in INSN with TO.  After all
    changes have been made, validate by seeing if INSN is still valid.  */
 
@@ -683,6 +698,46 @@ validate_replace_src_group (rtx from, rtx to, rtx insn)
   d.insn = insn;
   note_uses (&PATTERN (insn), validate_replace_src_1, &d);
 }
+
+/* Try simplify INSN.
+   Invoke simplify_rtx () on every SET_SRC and SET_DEST inside the INSN's
+   pattern and return true if something was simplified.  */
+
+bool
+validate_simplify_insn (rtx insn)
+{
+  int i;
+  rtx pat = NULL;
+  rtx newpat = NULL;
+
+  pat = PATTERN (insn);
+
+  if (GET_CODE (pat) == SET)
+    {
+      newpat = simplify_rtx (SET_SRC (pat));
+      if (newpat && !rtx_equal_p (SET_SRC (pat), newpat))
+       validate_change (insn, &SET_SRC (pat), newpat, 1);
+      newpat = simplify_rtx (SET_DEST (pat));
+      if (newpat && !rtx_equal_p (SET_DEST (pat), newpat))
+       validate_change (insn, &SET_DEST (pat), newpat, 1);
+    }
+  else if (GET_CODE (pat) == PARALLEL)
+    for (i = 0; i < XVECLEN (pat, 0); i++)
+      {
+       rtx s = XVECEXP (pat, 0, i);
+
+       if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
+         {
+           newpat = simplify_rtx (SET_SRC (s));
+           if (newpat && !rtx_equal_p (SET_SRC (s), newpat))
+             validate_change (insn, &SET_SRC (s), newpat, 1);
+           newpat = simplify_rtx (SET_DEST (s));
+           if (newpat && !rtx_equal_p (SET_DEST (s), newpat))
+             validate_change (insn, &SET_DEST (s), newpat, 1);
+         }
+      }
+  return ((num_changes_pending () > 0) && (apply_change_group () > 0));
+}
 \f
 #ifdef HAVE_cc0
 /* Return 1 if the insn using CC0 set by INSN does not contain
@@ -939,7 +994,7 @@ general_operand (rtx op, enum machine_mode mode)
 
       /* FLOAT_MODE subregs can't be paradoxical.  Combine will occasionally
         create such rtl, and we must reject it.  */
-      if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
+      if (SCALAR_FLOAT_MODE_P (GET_MODE (op))
          && GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
        return 0;
 
@@ -1023,7 +1078,7 @@ register_operand (rtx op, enum machine_mode mode)
 
       /* FLOAT_MODE subregs can't be paradoxical.  Combine will occasionally
         create such rtl, and we must reject it.  */
-      if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
+      if (SCALAR_FLOAT_MODE_P (GET_MODE (op))
          && GET_MODE_SIZE (GET_MODE (op)) > GET_MODE_SIZE (GET_MODE (sub)))
        return 0;
 
@@ -1927,6 +1982,7 @@ extract_insn_cached (rtx insn)
   extract_insn (insn);
   recog_data.insn = insn;
 }
+
 /* Do cached extract_insn, constrain_operands and complain about failures.
    Used by insn_attrtab.  */
 void
@@ -1937,6 +1993,7 @@ extract_constrain_insn_cached (rtx insn)
       && !constrain_operands (reload_completed))
     fatal_insn_not_found (insn);
 }
+
 /* Do cached constrain_operands and complain about failures.  */
 int
 constrain_operands_cached (int strict)
@@ -2141,7 +2198,7 @@ preprocess_constraints (void)
                case 'p':
                  op_alt[j].is_address = 1;
                  op_alt[j].cl = reg_class_subunion[(int) op_alt[j].cl]
-                   [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+                     [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
                  break;
 
                case 'g':
@@ -2162,7 +2219,8 @@ preprocess_constraints (void)
                      op_alt[j].cl
                        = (reg_class_subunion
                           [(int) op_alt[j].cl]
-                          [(int) MODE_BASE_REG_CLASS (VOIDmode)]);
+                          [(int) base_reg_class (VOIDmode, ADDRESS,
+                                                 SCRATCH)]);
                      break;
                    }
 
@@ -2233,6 +2291,7 @@ constrain_operands (int strict)
 
   do
     {
+      int seen_earlyclobber_at = -1;
       int opno;
       int lose = 0;
       funny_match_index = 0;
@@ -2295,6 +2354,8 @@ constrain_operands (int strict)
 
              case '&':
                earlyclobber[opno] = 1;
+               if (seen_earlyclobber_at < 0)
+                 seen_earlyclobber_at = opno;
                break;
 
              case '0':  case '1':  case '2':  case '3':  case '4':
@@ -2362,16 +2423,22 @@ constrain_operands (int strict)
                break;
 
                /* No need to check general_operand again;
-                  it was done in insn-recog.c.  */
+                  it was done in insn-recog.c.  Well, except that reload
+                  doesn't check the validity of its replacements, but
+                  that should only matter when there's a bug.  */
              case 'g':
                /* Anything goes unless it is a REG and really has a hard reg
                   but the hard reg is not in the class GENERAL_REGS.  */
-               if (strict < 0
-                   || GENERAL_REGS == ALL_REGS
-                   || !REG_P (op)
-                   || (reload_in_progress
-                       && REGNO (op) >= FIRST_PSEUDO_REGISTER)
-                   || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
+               if (REG_P (op))
+                 {
+                   if (strict < 0
+                       || GENERAL_REGS == ALL_REGS
+                       || (reload_in_progress
+                           && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+                       || reg_fits_class_p (op, GENERAL_REGS, offset, mode))
+                     win = 1;
+                 }
+               else if (strict < 0 || general_operand (op, mode))
                  win = 1;
                break;
 
@@ -2543,8 +2610,10 @@ constrain_operands (int strict)
          /* See if any earlyclobber operand conflicts with some other
             operand.  */
 
-         if (strict > 0)
-           for (eopno = 0; eopno < recog_data.n_operands; eopno++)
+         if (strict > 0  && seen_earlyclobber_at >= 0)
+           for (eopno = seen_earlyclobber_at;
+                eopno < recog_data.n_operands;
+                eopno++)
              /* Ignore earlyclobber operands now in memory,
                 because we would often report failure when we have
                 two memory operands, one of which was formerly a REG.  */
@@ -2598,6 +2667,10 @@ reg_fits_class_p (rtx operand, enum reg_class cl, int offset,
                  enum machine_mode mode)
 {
   int regno = REGNO (operand);
+
+  if (cl == NO_REGS)
+    return 0;
+
   if (regno < FIRST_PSEUDO_REGISTER
       && TEST_HARD_REG_BIT (reg_class_contents[(int) cl],
                            regno + offset))
@@ -2743,7 +2816,7 @@ split_all_insns (int upd_life)
 /* Same as split_all_insns, but do not expect CFG to be available.
    Used by machine dependent reorg passes.  */
 
-void
+unsigned int
 split_all_insns_noflow (void)
 {
   rtx next, insn;
@@ -2773,6 +2846,7 @@ split_all_insns_noflow (void)
            split_insn (insn);
        }
     }
+  return 0;
 }
 \f
 #ifdef HAVE_peephole2
@@ -2784,6 +2858,8 @@ struct peep2_insn_data
 
 static struct peep2_insn_data peep2_insn_data[MAX_INSNS_PER_PEEP2 + 1];
 static int peep2_current;
+/* The number of instructions available to match a peep2.  */
+int peep2_current_count;
 
 /* A non-insn marker indicating the last insn of the block.
    The live_before regset for this element is correct, indicating
@@ -2797,14 +2873,12 @@ static int peep2_current;
 rtx
 peep2_next_insn (int n)
 {
-  gcc_assert (n < MAX_INSNS_PER_PEEP2 + 1);
+  gcc_assert (n <= peep2_current_count);
 
   n += peep2_current;
   if (n >= MAX_INSNS_PER_PEEP2 + 1)
     n -= MAX_INSNS_PER_PEEP2 + 1;
 
-  if (peep2_insn_data[n].insn == PEEP2_EOB)
-    return NULL_RTX;
   return peep2_insn_data[n].insn;
 }
 
@@ -2956,8 +3030,8 @@ peep2_find_free_register (int from, int to, const char *class_str,
 
 /* Perform the peephole2 optimization pass.  */
 
-void
-peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
+static void
+peephole2_optimize (void)
 {
   rtx insn, prev;
   regset live;
@@ -2993,13 +3067,14 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
       /* Indicate that all slots except the last holds invalid data.  */
       for (i = 0; i < MAX_INSNS_PER_PEEP2; ++i)
        peep2_insn_data[i].insn = NULL_RTX;
+      peep2_current_count = 0;
 
       /* Indicate that the last slot contains live_after data.  */
       peep2_insn_data[MAX_INSNS_PER_PEEP2].insn = PEEP2_EOB;
       peep2_current = MAX_INSNS_PER_PEEP2;
 
       /* Start up propagation.  */
-      COPY_REG_SET (live, bb->global_live_at_end);
+      COPY_REG_SET (live, bb->il.rtl->global_live_at_end);
       COPY_REG_SET (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live);
 
 #ifdef HAVE_conditional_execution
@@ -3021,12 +3096,25 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
              /* Record this insn.  */
              if (--peep2_current < 0)
                peep2_current = MAX_INSNS_PER_PEEP2;
+             if (peep2_current_count < MAX_INSNS_PER_PEEP2
+                 && peep2_insn_data[peep2_current].insn == NULL_RTX)
+               peep2_current_count++;
              peep2_insn_data[peep2_current].insn = insn;
              propagate_one_insn (pbi, insn);
              COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
 
-             /* Match the peephole.  */
-             try = peephole2_insns (PATTERN (insn), insn, &match_len);
+             if (RTX_FRAME_RELATED_P (insn))
+               {
+                 /* If an insn has RTX_FRAME_RELATED_P set, peephole
+                    substitution would lose the
+                    REG_FRAME_RELATED_EXPR that is attached.  */
+                 peep2_current_count = 0;
+                 try = NULL;
+               }
+             else
+               /* Match the peephole.  */
+               try = peephole2_insns (PATTERN (insn), insn, &match_len);
+
              if (try != NULL)
                {
                  /* If we are splitting a CALL_INSN, look for the CALL_INSN
@@ -3065,7 +3153,6 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
                          {
                          case REG_NORETURN:
                          case REG_SETJMP:
-                         case REG_ALWAYS_RETURN:
                            REG_NOTES (new_insn)
                              = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
                                                   XEXP (note, 0),
@@ -3166,6 +3253,7 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
                  for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
                    peep2_insn_data[i].insn = NULL_RTX;
                  peep2_insn_data[peep2_current].insn = PEEP2_EOB;
+                 peep2_current_count = 0;
 #else
                  /* Back up lifetime information past the end of the
                     newly created sequence.  */
@@ -3181,6 +3269,9 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
                        {
                          if (--i < 0)
                            i = MAX_INSNS_PER_PEEP2;
+                         if (peep2_current_count < MAX_INSNS_PER_PEEP2
+                             && peep2_insn_data[i].insn == NULL_RTX)
+                           peep2_current_count++;
                          peep2_insn_data[i].insn = x;
                          propagate_one_insn (pbi, x);
                          COPY_REG_SET (peep2_insn_data[i].live_before, live);
@@ -3212,7 +3303,7 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
 
       /* Some peepholes can decide the don't need one or more of their
         inputs.  If this happens, local life update is not enough.  */
-      EXECUTE_IF_AND_COMPL_IN_BITMAP (bb->global_live_at_start, live,
+      EXECUTE_IF_AND_COMPL_IN_BITMAP (bb->il.rtl->global_live_at_start, live,
                                      0, j, rsi)
        {
          do_global_life_update = true;
@@ -3352,3 +3443,124 @@ if_test_bypass_p (rtx out_insn, rtx in_insn)
 
   return true;
 }
+\f
+static bool
+gate_handle_peephole2 (void)
+{
+  return (optimize > 0 && flag_peephole2);
+}
+
+static unsigned int
+rest_of_handle_peephole2 (void)
+{
+#ifdef HAVE_peephole2
+  peephole2_optimize ();
+#endif
+  return 0;
+}
+
+struct tree_opt_pass pass_peephole2 =
+{
+  "peephole2",                          /* name */
+  gate_handle_peephole2,                /* gate */
+  rest_of_handle_peephole2,             /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_PEEPHOLE2,                         /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+  'z'                                   /* letter */
+};
+
+static unsigned int
+rest_of_handle_split_all_insns (void)
+{
+  split_all_insns (1);
+  return 0;
+}
+
+struct tree_opt_pass pass_split_all_insns =
+{
+  "split1",                             /* name */
+  NULL,                                 /* gate */
+  rest_of_handle_split_all_insns,       /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  0,                                    /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+  0                                     /* letter */
+};
+
+/* The placement of the splitting that we do for shorten_branches
+   depends on whether regstack is used by the target or not.  */
+static bool
+gate_do_final_split (void)
+{
+#if defined (HAVE_ATTR_length) && !defined (STACK_REGS)
+  return 1;
+#else
+  return 0;
+#endif 
+}
+
+struct tree_opt_pass pass_split_for_shorten_branches =
+{
+  "split3",                             /* name */
+  gate_do_final_split,                  /* gate */
+  split_all_insns_noflow,               /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_SHORTEN_BRANCH,                    /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+  0                                     /* letter */
+};
+
+
+static bool
+gate_handle_split_before_regstack (void)
+{
+#if defined (HAVE_ATTR_length) && defined (STACK_REGS)
+  /* If flow2 creates new instructions which need splitting
+     and scheduling after reload is not done, they might not be
+     split until final which doesn't allow splitting
+     if HAVE_ATTR_length.  */
+# ifdef INSN_SCHEDULING
+  return (optimize && !flag_schedule_insns_after_reload);
+# else
+  return (optimize);
+# endif
+#else
+  return 0;
+#endif
+}
+
+struct tree_opt_pass pass_split_before_regstack =
+{
+  "split2",                             /* name */
+  gate_handle_split_before_regstack,    /* gate */
+  rest_of_handle_split_all_insns,       /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_SHORTEN_BRANCH,                    /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func,                       /* todo_flags_finish */
+  0                                     /* letter */
+};