OSDN Git Service

* gcc.target/mips/mips32-dspr2-type.c: New test.
[pf3gnuchains/gcc-fork.git] / gcc / recog.c
index 16bb280..5e1df5c 100644 (file)
@@ -1,6 +1,7 @@
 /* Subroutines used by or related to instruction recognition.
    Copyright (C) 1987, 1988, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998
-   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -31,6 +32,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, 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"
@@ -139,7 +141,7 @@ check_asm_operands (rtx x)
   operands = alloca (noperands * sizeof (rtx));
   constraints = alloca (noperands * sizeof (char *));
 
-  decode_asm_operands (x, operands, NULL, constraints, NULL);
+  decode_asm_operands (x, operands, NULL, constraints, NULL, NULL);
 
   for (i = 0; i < noperands; i++)
     {
@@ -237,45 +239,28 @@ validate_change (rtx object, rtx *loc, rtx new, int in_group)
     return apply_change_group ();
 }
 
+/* Keep X canonicalized if some changes have made it non-canonical; only
+   modifies the operands of X, not (for example) its code.  Simplifications
+   are not the job of this routine.
 
-/* Function to be passed to for_each_rtx to test whether a piece of
-   RTL contains any mem/v.  */
-static int
-volatile_mem_p (rtx *x, void *data ATTRIBUTE_UNUSED)
+   Return true if anything was changed.  */
+bool
+canonicalize_change_group (rtx insn, rtx x)
 {
-  return (MEM_P (*x) && MEM_VOLATILE_P (*x));
-}
-
-/* Same as validate_change, but doesn't support groups, and it accepts
-   volatile mems if they're already present in the original insn.  */
-
-int
-validate_change_maybe_volatile (rtx object, rtx *loc, rtx new)
-{
-  int result;
-
-  if (validate_change (object, loc, new, 0))
-    return 1;
-
-  if (volatile_ok
-      /* If there isn't a volatile MEM, there's nothing we can do.  */
-      || !for_each_rtx (&PATTERN (object), volatile_mem_p, 0)
-      /* Make sure we're not adding or removing volatile MEMs.  */
-      || for_each_rtx (loc, volatile_mem_p, 0)
-      || for_each_rtx (&new, volatile_mem_p, 0)
-      || !insn_invalid_p (object))
-    return 0;
-
-  volatile_ok = 1;
-
-  gcc_assert (!insn_invalid_p (object));
-
-  result = validate_change (object, loc, new, 0);
-
-  volatile_ok = 0;
-
-  return result;
+  if (COMMUTATIVE_P (x)
+      && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
+    {
+      /* Oops, the caller has made X no longer canonical.
+        Let's redo the changes in the correct order.  */
+      rtx tem = XEXP (x, 0);
+      validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
+      validate_change (insn, &XEXP (x, 1), tem, 1);
+      return true;
+    }
+  else
+    return false;
 }
+  
 
 /* This subroutine of apply_change_group verifies whether the changes to INSN
    were valid; i.e. whether INSN can still be recognized.  */
@@ -339,7 +324,7 @@ num_changes_pending (void)
 /* Tentatively apply the changes numbered NUM and up.
    Return 1 if all changes are valid, zero otherwise.  */
 
-static int
+int
 verify_changes (int num)
 {
   int i;
@@ -688,17 +673,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.  */
 
@@ -747,6 +721,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
@@ -1003,7 +1017,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;
 
@@ -1087,7 +1101,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;
 
@@ -1479,15 +1493,16 @@ asm_noperands (rtx body)
 
 const char *
 decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
-                    const char **constraints, enum machine_mode *modes)
+                    const char **constraints, enum machine_mode *modes,
+                    location_t *loc)
 {
   int i;
   int noperands;
-  const char *template = 0;
+  rtx asmop = 0;
 
   if (GET_CODE (body) == SET && GET_CODE (SET_SRC (body)) == ASM_OPERANDS)
     {
-      rtx asmop = SET_SRC (body);
+      asmop = SET_SRC (body);
       /* Single output operand: BODY is (set OUTPUT (asm_operands ....)).  */
 
       noperands = ASM_OPERANDS_INPUT_LENGTH (asmop) + 1;
@@ -1514,11 +1529,10 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
        constraints[0] = ASM_OPERANDS_OUTPUT_CONSTRAINT (asmop);
       if (modes)
        modes[0] = GET_MODE (SET_DEST (body));
-      template = ASM_OPERANDS_TEMPLATE (asmop);
     }
   else if (GET_CODE (body) == ASM_OPERANDS)
     {
-      rtx asmop = body;
+      asmop = body;
       /* No output operands: BODY is (asm_operands ....).  */
 
       noperands = ASM_OPERANDS_INPUT_LENGTH (asmop);
@@ -1536,17 +1550,18 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
          if (modes)
            modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
        }
-      template = ASM_OPERANDS_TEMPLATE (asmop);
     }
   else if (GET_CODE (body) == PARALLEL
           && GET_CODE (XVECEXP (body, 0, 0)) == SET
           && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) == ASM_OPERANDS)
     {
-      rtx asmop = SET_SRC (XVECEXP (body, 0, 0));
       int nparallel = XVECLEN (body, 0); /* Includes CLOBBERs.  */
-      int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+      int nin;
       int nout = 0;            /* Does not include CLOBBERs.  */
 
+      asmop = SET_SRC (XVECEXP (body, 0, 0));
+      nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+
       /* At least one output, plus some CLOBBERs.  */
 
       /* The outputs are in the SETs.
@@ -1578,16 +1593,16 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
          if (modes)
            modes[i + nout] = ASM_OPERANDS_INPUT_MODE (asmop, i);
        }
-
-      template = ASM_OPERANDS_TEMPLATE (asmop);
     }
   else if (GET_CODE (body) == PARALLEL
           && GET_CODE (XVECEXP (body, 0, 0)) == ASM_OPERANDS)
     {
       /* No outputs, but some CLOBBERs.  */
 
-      rtx asmop = XVECEXP (body, 0, 0);
-      int nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
+      int nin;
+
+      asmop = XVECEXP (body, 0, 0);
+      nin = ASM_OPERANDS_INPUT_LENGTH (asmop);
 
       for (i = 0; i < nin; i++)
        {
@@ -1601,10 +1616,19 @@ decode_asm_operands (rtx body, rtx *operands, rtx **operand_locs,
            modes[i] = ASM_OPERANDS_INPUT_MODE (asmop, i);
        }
 
-      template = ASM_OPERANDS_TEMPLATE (asmop);
     }
 
-  return template;
+  if (loc)
+    {
+#ifdef USE_MAPPED_LOCATION
+      *loc = ASM_OPERANDS_SOURCE_LOCATION (asmop);
+#else
+      loc->file = ASM_OPERANDS_SOURCE_FILE (asmop);
+      loc->line = ASM_OPERANDS_SOURCE_LINE (asmop);
+#endif
+    }
+
+  return ASM_OPERANDS_TEMPLATE (asmop);
 }
 
 /* Check if an asm_operand matches its constraints.
@@ -1970,8 +1994,17 @@ offsettable_address_p (int strictp, enum machine_mode mode, rtx y)
    because the amount of the increment depends on the mode.  */
 
 int
-mode_dependent_address_p (rtx addr ATTRIBUTE_UNUSED /* Maybe used in GO_IF_MODE_DEPENDENT_ADDRESS.  */)
-{
+mode_dependent_address_p (rtx addr)
+{
+  /* Auto-increment addressing with anything other than post_modify
+     or pre_modify always introduces a mode dependency.  Catch such
+     cases now instead of deferring to the target.  */
+  if (GET_CODE (addr) == PRE_INC
+      || GET_CODE (addr) == POST_INC
+      || GET_CODE (addr) == PRE_DEC
+      || GET_CODE (addr) == POST_DEC)
+    return 1;
+
   GO_IF_MODE_DEPENDENT_ADDRESS (addr, win);
   return 0;
   /* Label `win' might (not) be used via GO_IF_MODE_DEPENDENT_ADDRESS.  */
@@ -1991,6 +2024,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
@@ -2001,6 +2035,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)
@@ -2062,7 +2097,7 @@ extract_insn (rtx insn)
          decode_asm_operands (body, recog_data.operand,
                               recog_data.operand_loc,
                               recog_data.constraints,
-                              recog_data.operand_mode);
+                              recog_data.operand_mode, NULL);
          if (noperands > 0)
            {
              const char *p =  recog_data.constraints[0];
@@ -2205,7 +2240,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':
@@ -2226,7 +2261,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;
                    }
 
@@ -2673,6 +2709,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))
@@ -2818,7 +2858,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;
@@ -2848,6 +2888,7 @@ split_all_insns_noflow (void)
            split_insn (insn);
        }
     }
+  return 0;
 }
 \f
 #ifdef HAVE_peephole2
@@ -3031,8 +3072,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;
@@ -3104,8 +3145,18 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
              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
@@ -3335,47 +3386,92 @@ peephole2_optimize (FILE *dump_file ATTRIBUTE_UNUSED)
 /* Common predicates for use with define_bypass.  */
 
 /* True if the dependency between OUT_INSN and IN_INSN is on the store
-   data not the address operand(s) of the store.  IN_INSN must be
-   single_set.  OUT_INSN must be either a single_set or a PARALLEL with
-   SETs inside.  */
+   data not the address operand(s) of the store.  IN_INSN and OUT_INSN
+   must be either a single_set or a PARALLEL with SETs inside.  */
 
 int
 store_data_bypass_p (rtx out_insn, rtx in_insn)
 {
   rtx out_set, in_set;
+  rtx out_pat, in_pat;
+  rtx out_exp, in_exp;
+  int i, j;
 
   in_set = single_set (in_insn);
-  gcc_assert (in_set);
-
-  if (!MEM_P (SET_DEST (in_set)))
-    return false;
-
-  out_set = single_set (out_insn);
-  if (out_set)
+  if (in_set)
     {
-      if (reg_mentioned_p (SET_DEST (out_set), SET_DEST (in_set)))
+      if (!MEM_P (SET_DEST (in_set)))
        return false;
+
+      out_set = single_set (out_insn);
+      if (out_set)
+        {
+          if (reg_mentioned_p (SET_DEST (out_set), SET_DEST (in_set)))
+            return false;
+        }
+      else
+        {
+          out_pat = PATTERN (out_insn);
+
+         if (GET_CODE (out_pat) != PARALLEL)
+           return false;
+
+          for (i = 0; i < XVECLEN (out_pat, 0); i++)
+          {
+            out_exp = XVECEXP (out_pat, 0, i);
+
+            if (GET_CODE (out_exp) == CLOBBER)
+              continue;
+
+            gcc_assert (GET_CODE (out_exp) == SET);
+
+            if (reg_mentioned_p (SET_DEST (out_exp), SET_DEST (in_set)))
+              return false;
+          }
+      }
     }
   else
     {
-      rtx out_pat;
-      int i;
-
-      out_pat = PATTERN (out_insn);
-      gcc_assert (GET_CODE (out_pat) == PARALLEL);
+      in_pat = PATTERN (in_insn);
+      gcc_assert (GET_CODE (in_pat) == PARALLEL);
 
-      for (i = 0; i < XVECLEN (out_pat, 0); i++)
+      for (i = 0; i < XVECLEN (in_pat, 0); i++)
        {
-         rtx exp = XVECEXP (out_pat, 0, i);
+         in_exp = XVECEXP (in_pat, 0, i);
 
-         if (GET_CODE (exp) == CLOBBER)
+         if (GET_CODE (in_exp) == CLOBBER)
            continue;
 
-         gcc_assert (GET_CODE (exp) == SET);
+         gcc_assert (GET_CODE (in_exp) == SET);
 
-         if (reg_mentioned_p (SET_DEST (exp), SET_DEST (in_set)))
+         if (!MEM_P (SET_DEST (in_exp)))
            return false;
-       }
+
+          out_set = single_set (out_insn);
+          if (out_set)
+            {
+              if (reg_mentioned_p (SET_DEST (out_set), SET_DEST (in_exp)))
+                return false;
+            }
+          else
+            {
+              out_pat = PATTERN (out_insn);
+              gcc_assert (GET_CODE (out_pat) == PARALLEL);
+
+              for (j = 0; j < XVECLEN (out_pat, 0); j++)
+                {
+                  out_exp = XVECEXP (out_pat, 0, j);
+
+                  if (GET_CODE (out_exp) == CLOBBER)
+                    continue;
+
+                  gcc_assert (GET_CODE (out_exp) == SET);
+
+                  if (reg_mentioned_p (SET_DEST (out_exp), SET_DEST (in_exp)))
+                    return false;
+                }
+            }
+        }
     }
 
   return true;
@@ -3441,12 +3537,13 @@ gate_handle_peephole2 (void)
   return (optimize > 0 && flag_peephole2);
 }
 
-static void
+static unsigned int
 rest_of_handle_peephole2 (void)
 {
 #ifdef HAVE_peephole2
-  peephole2_optimize (dump_file);
+  peephole2_optimize ();
 #endif
+  return 0;
 }
 
 struct tree_opt_pass pass_peephole2 =
@@ -3466,10 +3563,11 @@ struct tree_opt_pass pass_peephole2 =
   'z'                                   /* letter */
 };
 
-static void
+static unsigned int
 rest_of_handle_split_all_insns (void)
 {
   split_all_insns (1);
+  return 0;
 }
 
 struct tree_opt_pass pass_split_all_insns =