OSDN Git Service

[pf3gnuchains/gcc-fork.git] / gcc / recog.c
index bde6602..19bc3b2 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines used by or related to instruction recognition.
-   Copyright (C) 1987, 1988, 1991, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1987, 1988, 91-97, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,12 +15,13 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
+#include "system.h"
 #include "rtl.h"
-#include <stdio.h>
 #include "insn-config.h"
 #include "insn-attr.h"
 #include "insn-flags.h"
@@ -30,6 +31,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "hard-reg-set.h"
 #include "flags.h"
 #include "real.h"
+#include "toplev.h"
 
 #ifndef STACK_PUSH_CODE
 #ifdef STACK_GROWS_DOWNWARD
@@ -39,11 +41,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #endif
 #endif
 
-/* Import from final.c: */
-extern rtx alter_subreg ();
-
-int strict_memory_address_p ();
-int memory_address_p ();
+static void validate_replace_rtx_1     PROTO((rtx *, rtx, rtx, rtx));
+static rtx *find_single_use_1          PROTO((rtx, rtx *));
+static rtx *find_constant_term_loc     PROTO((rtx *));
+static int insn_invalid_p              PROTO((rtx));
 
 /* Nonzero means allow operands to be volatile.
    This should be 0 if you are generating rtl, such as if you are calling
@@ -55,6 +56,50 @@ int memory_address_p ();
 
 int volatile_ok;
 
+/* The next variables are set up by extract_insn.  The first four of them
+   are also set up during insn_extract.  */
+
+/* Indexed by N, gives value of operand N.  */
+rtx recog_operand[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives location where operand N was found.  */
+rtx *recog_operand_loc[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives location where the Nth duplicate-appearance of
+   an operand was found.  This is something that matched MATCH_DUP.  */
+rtx *recog_dup_loc[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the operand number that was duplicated in the
+   Nth duplicate-appearance of an operand.  */
+char recog_dup_num[MAX_RECOG_OPERANDS];
+
+/* The number of operands of the insn.  */
+int recog_n_operands;
+
+/* The number of MATCH_DUPs in the insn.  */
+int recog_n_dups;
+
+/* The number of alternatives in the constraints for the insn.  */
+int recog_n_alternatives;
+
+/* Indexed by N, gives the mode of operand N.  */
+enum machine_mode recog_operand_mode[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the constraint string for operand N.  */
+char *recog_constraints[MAX_RECOG_OPERANDS];
+
+/* Indexed by N, gives the type (in, out, inout) for operand N.  */
+enum op_type recog_op_type[MAX_RECOG_OPERANDS];
+
+#ifndef REGISTER_CONSTRAINTS
+/* Indexed by N, nonzero if operand N should be an address.  */
+char recog_operand_address_p[MAX_RECOG_OPERANDS];
+#endif
+
+/* Contains a vector of operand_alternative structures for every operand.
+   Set up by preprocess_constraints.  */
+struct operand_alternative recog_op_alt[MAX_RECOG_OPERANDS][MAX_RECOG_ALTERNATIVES];
+
 /* On return from `constrain_operands', indicate which alternative
    was satisfied.  */
 
@@ -84,7 +129,7 @@ init_recog ()
 
 /* Try recognizing the instruction INSN,
    and return the code number that results.
-   Remeber the code so that repeated calls do not
+   Remember the code so that repeated calls do not
    need to spend the time for actual rerecognition.
 
    This function is the normal interface to instruction recognition.
@@ -126,19 +171,18 @@ check_asm_operands (x)
   return 1;
 }
 \f
-/* Static data for the next two routines.
-
-   The maximum number of changes supported is defined as the maximum
-   number of operands times 5.  This allows for repeated substitutions
-   inside complex indexed address, or, alternatively, changes in up
-   to 5 insns.  */
+/* Static data for the next two routines.  */
 
-#define MAX_CHANGE_LOCS        (MAX_RECOG_OPERANDS * 5)
+typedef struct change_t
+{
+  rtx object;
+  int old_code;
+  rtx *loc;
+  rtx old;
+} change_t;
 
-static rtx change_objects[MAX_CHANGE_LOCS];
-static int change_old_codes[MAX_CHANGE_LOCS];
-static rtx *change_locs[MAX_CHANGE_LOCS];
-static rtx change_olds[MAX_CHANGE_LOCS];
+static change_t *changes;
+static int changes_allocated;
 
 static int num_changes = 0;
 
@@ -172,22 +216,35 @@ validate_change (object, loc, new, in_group)
   if (old == new || rtx_equal_p (old, new))
     return 1;
 
-  if (num_changes >= MAX_CHANGE_LOCS
-      || (in_group == 0 && num_changes != 0))
+  if (in_group == 0 && num_changes != 0)
     abort ();
 
   *loc = new;
 
   /* Save the information describing this change.  */
-  change_objects[num_changes] = object;
-  change_locs[num_changes] = loc;
-  change_olds[num_changes] = old;
+  if (num_changes >= changes_allocated)
+    {
+      if (changes_allocated == 0)
+       /* This value allows for repeated substitutions inside complex
+          indexed addresses, or changes in up to 5 insns.  */
+       changes_allocated = MAX_RECOG_OPERANDS * 5;
+      else
+       changes_allocated *= 2;
+
+      changes = 
+       (change_t*) xrealloc (changes, 
+                             sizeof (change_t) * changes_allocated); 
+    }
+  
+  changes[num_changes].object = object;
+  changes[num_changes].loc = loc;
+  changes[num_changes].old = old;
 
   if (object && GET_CODE (object) != MEM)
     {
       /* Set INSN_CODE to force rerecognition of insn.  Save old code in
         case invalid.  */
-      change_old_codes[num_changes] = INSN_CODE (object);
+      changes[num_changes].old_code = INSN_CODE (object);
       INSN_CODE (object) = -1;
     }
 
@@ -202,6 +259,33 @@ validate_change (object, loc, new, 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.  */
+
+static int
+insn_invalid_p (insn)
+     rtx insn;
+{
+  int icode = recog_memoized (insn);
+  int is_asm = icode < 0 && asm_noperands (PATTERN (insn)) >= 0;
+
+  if (is_asm && ! check_asm_operands (PATTERN (insn)))
+    return 1;
+  if (! is_asm && icode < 0)
+    return 1;
+
+  /* After reload, verify that all constraints are satisfied.  */
+  if (reload_completed)
+    {
+      extract_insn (insn);
+
+      if (! constrain_operands (1))
+       return 1;
+    }
+
+  return 0;
+}
+
 /* Apply a group of changes previously issued with `validate_change'.
    Return 1 if all changes are valid, zero otherwise.  */
 
@@ -217,12 +301,11 @@ apply_change_group ()
      given a MEM and it still is a valid address, or if this is in insn
      and it is recognized.  In the latter case, if reload has completed,
      we also require that the operands meet the constraints for
-     the insn.  We do not allow modifying an ASM_OPERANDS after reload
-     has completed because verifying the constraints is too difficult.  */
+     the insn.  */
 
   for (i = 0; i < num_changes; i++)
     {
-      rtx object = change_objects[i];
+      rtx object = changes[i].object;
 
       if (object == 0)
        continue;
@@ -232,13 +315,7 @@ apply_change_group ()
          if (! memory_address_p (GET_MODE (object), XEXP (object, 0)))
            break;
        }
-      else if ((recog_memoized (object) < 0
-               && (asm_noperands (PATTERN (object)) < 0
-                   || ! check_asm_operands (PATTERN (object))
-                   || reload_completed))
-              || (reload_completed
-                  && (insn_extract (object),
-                      ! constrain_operands (INSN_CODE (object), 1))))
+      else if (insn_invalid_p (object))
        {
          rtx pat = PATTERN (object);
 
@@ -259,8 +336,8 @@ apply_change_group ()
                 {
                   int j;
 
-                  newpat = gen_rtx (PARALLEL, VOIDmode, 
-                                    gen_rtvec (XVECLEN (pat, 0) - 1));
+                  newpat = gen_rtx_PARALLEL (VOIDmode, 
+                                             gen_rtvec (XVECLEN (pat, 0) - 1));
                   for (j = 0; j < XVECLEN (newpat, 0); j++)
                     XVECEXP (newpat, 0, j) = XVECEXP (pat, 0, j);
                 }
@@ -317,9 +394,9 @@ cancel_changes (num)
      they were made.  */
   for (i = num_changes - 1; i >= num; i--)
     {
-      *change_locs[i] = change_olds[i];
-      if (change_objects[i] && GET_CODE (change_objects[i]) != MEM)
-       INSN_CODE (change_objects[i]) = change_old_codes[i];
+      *changes[i].loc = changes[i].old;
+      if (changes[i].object && GET_CODE (changes[i].object) != MEM)
+       INSN_CODE (changes[i].object) = changes[i].old_code;
     }
   num_changes = num;
 }
@@ -364,24 +441,38 @@ validate_replace_rtx_1 (loc, from, to, object)
       if (prev_changes != num_changes && CONSTANT_P (XEXP (x, 0)))
        {
          validate_change (object, loc,
-                          gen_rtx (GET_RTX_CLASS (code) == 'c' ? code
-                                   : swap_condition (code),
-                                   GET_MODE (x), XEXP (x, 1), XEXP (x, 0)),
+                          gen_rtx_fmt_ee (GET_RTX_CLASS (code) == 'c' ? code
+                                          : swap_condition (code),
+                                          GET_MODE (x), XEXP (x, 1),
+                                          XEXP (x, 0)),
                           1);
          x = *loc;
          code = GET_CODE (x);
        }
     }
 
+  /* Note that if CODE's RTX_CLASS is "c" or "<" we will have already
+     done the substitution, otherwise we won't.  */
+
   switch (code)
     {
     case PLUS:
-      /* If we have have a PLUS whose second operand is now a CONST_INT, use
+      /* If we have a PLUS whose second operand is now a CONST_INT, use
         plus_constant to try to simplify it.  */
       if (GET_CODE (XEXP (x, 1)) == CONST_INT && XEXP (x, 1) == to)
-       validate_change (object, loc, 
-                        plus_constant (XEXP (x, 0), INTVAL (XEXP (x, 1))), 1);
+       validate_change (object, loc, plus_constant (XEXP (x, 0), INTVAL (to)),
+                        1);
       return;
+
+    case MINUS:
+      if (GET_CODE (to) == CONST_INT && XEXP (x, 1) == from)
+       {
+         validate_change (object, loc,
+                          plus_constant (XEXP (x, 0), - INTVAL (to)),
+                          1);
+         return;
+       }
+      break;
       
     case ZERO_EXTEND:
     case SIGN_EXTEND:
@@ -399,7 +490,7 @@ validate_replace_rtx_1 (loc, from, to, object)
          rtx new = simplify_unary_operation (code, GET_MODE (x), to,
                                              GET_MODE (from));
          if (new == 0)
-           new = gen_rtx (CLOBBER, GET_MODE (x), const0_rtx);
+           new = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx);
 
          validate_change (object, loc, new, 1);
          return;
@@ -423,13 +514,12 @@ validate_replace_rtx_1 (loc, from, to, object)
          enum machine_mode mode = GET_MODE (x);
          rtx new;
 
-#if BYTES_BIG_ENDIAN
-         offset += (MIN (UNITS_PER_WORD,
-                         GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
-                    - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
-#endif
+         if (BYTES_BIG_ENDIAN)
+           offset += (MIN (UNITS_PER_WORD,
+                           GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+                      - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
 
-         new = gen_rtx (MEM, mode, plus_constant (XEXP (to, 0), offset));
+         new = gen_rtx_MEM (mode, plus_constant (XEXP (to, 0), offset));
          MEM_VOLATILE_P (new) = MEM_VOLATILE_P (to);
          RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (to);
          MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (to);
@@ -453,16 +543,23 @@ validate_replace_rtx_1 (loc, from, to, object)
        {
          enum machine_mode wanted_mode = VOIDmode;
          enum machine_mode is_mode = GET_MODE (to);
-         int width = INTVAL (XEXP (x, 1));
          int pos = INTVAL (XEXP (x, 2));
 
 #ifdef HAVE_extzv
          if (code == ZERO_EXTRACT)
-           wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+           {
+             wanted_mode = insn_operand_mode[(int) CODE_FOR_extzv][1];
+             if (wanted_mode == VOIDmode)
+               wanted_mode = word_mode;
+           }
 #endif
 #ifdef HAVE_extv
          if (code == SIGN_EXTRACT)
-           wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+           {
+             wanted_mode = insn_operand_mode[(int) CODE_FOR_extv][1];
+             if (wanted_mode == VOIDmode)
+               wanted_mode = word_mode;
+           }
 #endif
 
          /* If we have a narrower mode, we can do something.  */
@@ -474,15 +571,14 @@ validate_replace_rtx_1 (loc, from, to, object)
 
                  /* If the bytes and bits are counted differently, we
                     must adjust the offset.  */
-#if BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN
-             offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
-                       - offset);
-#endif
+             if (BYTES_BIG_ENDIAN != BITS_BIG_ENDIAN)
+               offset = (GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (wanted_mode)
+                         - offset);
 
              pos %= GET_MODE_BITSIZE (wanted_mode);
 
-             newmem = gen_rtx (MEM, wanted_mode,
-                               plus_constant (XEXP (to, 0), offset));
+             newmem = gen_rtx_MEM (wanted_mode,
+                                   plus_constant (XEXP (to, 0), offset));
              RTX_UNCHANGING_P (newmem) = RTX_UNCHANGING_P (to);
              MEM_VOLATILE_P (newmem) = MEM_VOLATILE_P (to);
              MEM_IN_STRUCT_P (newmem) = MEM_IN_STRUCT_P (to);
@@ -493,16 +589,24 @@ validate_replace_rtx_1 (loc, from, to, object)
        }
 
       break;
+      
+    default:
+      break;
     }
       
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+  /* For commutative or comparison operations we've already performed
+     replacements.  Don't try to perform them again.  */
+  if (GET_RTX_CLASS (code) != '<' && GET_RTX_CLASS (code) != 'c')
     {
-      if (fmt[i] == 'e')
-       validate_replace_rtx_1 (&XEXP (x, i), from, to, object);
-      else if (fmt[i] == 'E')
-       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object);
+      fmt = GET_RTX_FORMAT (code);
+      for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+        {
+          if (fmt[i] == 'e')
+            validate_replace_rtx_1 (&XEXP (x, i), from, to, object);
+          else if (fmt[i] == 'E')
+            for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+              validate_replace_rtx_1 (&XVECEXP (x, i, j), from, to, object);
+        }
     }
 }
 
@@ -516,6 +620,35 @@ validate_replace_rtx (from, to, insn)
   validate_replace_rtx_1 (&PATTERN (insn), 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.  */
+
+void
+validate_replace_rtx_group (from, to, insn)
+     rtx from, to, insn;
+{
+  validate_replace_rtx_1 (&PATTERN (insn), from, to, insn);
+}
+
+/* Try replacing every occurrence of FROM in INSN with TO, avoiding
+   SET_DESTs.  After all changes have been made, validate by seeing if
+   INSN is still valid.  */
+
+int
+validate_replace_src (from, to, insn)
+     rtx from, to, insn;
+{
+  if ((GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
+      || GET_CODE (PATTERN (insn)) != SET)
+    abort ();
+
+  validate_replace_rtx_1 (&SET_SRC (PATTERN (insn)), from, to, insn);
+  if (GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
+    validate_replace_rtx_1 (&XEXP (SET_DEST (PATTERN (insn)), 0),
+                           from, to, insn);
+  return apply_change_group ();
+}
 \f
 #ifdef HAVE_cc0
 /* Return 1 if the insn using CC0 set by INSN does not contain
@@ -615,6 +748,9 @@ find_single_use_1 (dest, loc)
     case MEM:
     case SUBREG:
       return find_single_use_1 (dest, &XEXP (x, 0));
+      
+    default:
+      break;
     }
 
   /* If it wasn't one of the common cases above, check each expression and
@@ -761,7 +897,8 @@ general_operand (op, mode)
   /* Don't accept CONST_INT or anything similar
      if the caller wants something floating.  */
   if (GET_MODE (op) == VOIDmode && mode != VOIDmode
-      && GET_MODE_CLASS (mode) != MODE_INT)
+      && GET_MODE_CLASS (mode) != MODE_INT
+      && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
     return 0;
 
   if (CONSTANT_P (op))
@@ -806,10 +943,18 @@ general_operand (op, mode)
       register rtx y = XEXP (op, 0);
       if (! volatile_ok && MEM_VOLATILE_P (op))
        return 0;
+      if (GET_CODE (y) == ADDRESSOF)
+       return 1;
       /* Use the mem's mode, since it will be reloaded thus.  */
       mode = GET_MODE (op);
       GO_IF_LEGITIMATE_ADDRESS (mode, y, win);
     }
+
+  /* Pretend this is an operand for now; we'll run force_operand
+     on its replacement in fixup_var_refs_1.  */
+  if (code == ADDRESSOF)
+    return 1;
+
   return 0;
 
  win:
@@ -864,6 +1009,19 @@ register_operand (op, mode)
         reg went on the stack.)  */
       if (! reload_completed && GET_CODE (SUBREG_REG (op)) == MEM)
        return general_operand (op, mode);
+
+#ifdef CLASS_CANNOT_CHANGE_SIZE
+      if (GET_CODE (SUBREG_REG (op)) == REG
+         && REGNO (SUBREG_REG (op)) < FIRST_PSEUDO_REGISTER
+         && TEST_HARD_REG_BIT (reg_class_contents[(int) CLASS_CANNOT_CHANGE_SIZE],
+                               REGNO (SUBREG_REG (op)))
+         && (GET_MODE_SIZE (mode)
+             != GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))))
+         && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_INT
+         && GET_MODE_CLASS (GET_MODE (SUBREG_REG (op))) != MODE_COMPLEX_FLOAT)
+       return 0;
+#endif
+
       op = SUBREG_REG (op);
     }
 
@@ -901,7 +1059,8 @@ immediate_operand (op, mode)
   /* Don't accept CONST_INT or anything similar
      if the caller wants something floating.  */
   if (GET_MODE (op) == VOIDmode && mode != VOIDmode
-      && GET_MODE_CLASS (mode) != MODE_INT)
+      && GET_MODE_CLASS (mode) != MODE_INT
+      && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
     return 0;
 
   return (CONSTANT_P (op)
@@ -918,7 +1077,7 @@ immediate_operand (op, mode)
 int
 const_int_operand (op, mode)
      register rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   return GET_CODE (op) == CONST_INT;
 }
@@ -934,7 +1093,8 @@ const_double_operand (op, mode)
   /* Don't accept CONST_INT or anything similar
      if the caller wants something floating.  */
   if (GET_MODE (op) == VOIDmode && mode != VOIDmode
-      && GET_MODE_CLASS (mode) != MODE_INT)
+      && GET_MODE_CLASS (mode) != MODE_INT
+      && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
     return 0;
 
   return ((GET_CODE (op) == CONST_DOUBLE || GET_CODE (op) == CONST_INT)
@@ -964,7 +1124,8 @@ nonmemory_operand (op, mode)
       /* Don't accept CONST_INT or anything similar
         if the caller wants something floating.  */
       if (GET_MODE (op) == VOIDmode && mode != VOIDmode
-         && GET_MODE_CLASS (mode) != MODE_INT)
+         && GET_MODE_CLASS (mode) != MODE_INT
+         && GET_MODE_CLASS (mode) != MODE_PARTIAL_INT)
        return 0;
 
       return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode)
@@ -1029,6 +1190,9 @@ memory_address_p (mode, addr)
      enum machine_mode mode;
      register rtx addr;
 {
+  if (GET_CODE (addr) == ADDRESSOF)
+    return 1;
+  
   GO_IF_LEGITIMATE_ADDRESS (mode, addr, win);
   return 0;
 
@@ -1079,10 +1243,12 @@ indirect_operand (op, mode)
       register int offset = SUBREG_WORD (op) * UNITS_PER_WORD;
       rtx inner = SUBREG_REG (op);
 
-#if BYTES_BIG_ENDIAN
-      offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
-                - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
-#endif
+      if (BYTES_BIG_ENDIAN)
+       offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (op)))
+                  - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (inner))));
+
+      if (mode != VOIDmode && GET_MODE (op) != mode)
+       return 0;
 
       /* The only way that we can have a general_operand as the resulting
         address is if OFFSET is zero and the address already is an operand
@@ -1529,7 +1695,7 @@ adj_offsettable_operand (op, offset)
 
       if (CONSTANT_ADDRESS_P (y))
        {
-         new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+         new = gen_rtx_MEM (GET_MODE (op), plus_constant_for_output (y, offset));
          RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
          return new;
        }
@@ -1549,18 +1715,213 @@ adj_offsettable_operand (op, offset)
            }
        }
 
-      new = gen_rtx (MEM, GET_MODE (op), plus_constant_for_output (y, offset));
+      new = gen_rtx_MEM (GET_MODE (op), plus_constant_for_output (y, offset));
       RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (op);
       return new;
     }
   abort ();
 }
 \f
+/* Analyze INSN and compute the variables recog_n_operands, recog_n_dups,
+   recog_n_alternatives, recog_operand, recog_operand_loc, recog_constraints,
+   recog_operand_mode, recog_dup_loc and recog_dup_num.
+   If REGISTER_CONSTRAINTS is not defined, also compute
+   recog_operand_address_p.  */
+void
+extract_insn (insn)
+     rtx insn;
+{
+  int i;
+  int icode;
+  int noperands;
+  rtx body = PATTERN (insn);
+
+  recog_n_operands = 0;
+  recog_n_alternatives = 0;
+  recog_n_dups = 0;
+
+  switch (GET_CODE (body))
+    {
+    case USE:
+    case CLOBBER:
+    case ASM_INPUT:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+      return;
+
+    case SET:
+    case PARALLEL:
+    case ASM_OPERANDS:
+      recog_n_operands = noperands = asm_noperands (body);
+      if (noperands >= 0)
+       {
+         /* This insn is an `asm' with operands.  */
+
+         /* expand_asm_operands makes sure there aren't too many operands.  */
+         if (noperands > MAX_RECOG_OPERANDS)
+           abort ();
+
+         /* Now get the operand values and constraints out of the insn.  */
+         decode_asm_operands (body, recog_operand, recog_operand_loc,
+                              recog_constraints, recog_operand_mode);
+         if (noperands > 0)
+           {
+             char *p =  recog_constraints[0];
+             recog_n_alternatives = 1;
+             while (*p)
+               recog_n_alternatives += (*p++ == ',');
+           }
+#ifndef REGISTER_CONSTRAINTS
+         bzero (recog_operand_address_p, sizeof recog_operand_address_p);
+#endif
+         break;
+       }
+
+      /* FALLTHROUGH */
+
+    default:
+      /* Ordinary insn: recognize it, get the operands via insn_extract
+        and get the constraints.  */
+
+      icode = recog_memoized (insn);
+      if (icode < 0)
+       fatal_insn_not_found (insn);
+
+      recog_n_operands = noperands = insn_n_operands[icode];
+      recog_n_alternatives = insn_n_alternatives[icode];
+      recog_n_dups = insn_n_dups[icode];
+
+      insn_extract (insn);
+
+      for (i = 0; i < noperands; i++)
+       {
 #ifdef REGISTER_CONSTRAINTS
+         recog_constraints[i] = insn_operand_constraint[icode][i];
+#else
+         recog_operand_address_p[i] = insn_operand_address_p[icode][i];
+#endif
+         recog_operand_mode[i] = insn_operand_mode[icode][i];
+       }
+    }
+  for (i = 0; i < noperands; i++)
+    recog_op_type[i] = (recog_constraints[i][0] == '=' ? OP_OUT
+                       : recog_constraints[i][0] == '+' ? OP_INOUT
+                       : OP_IN);
 
-/* Check the operands of an insn (found in recog_operands)
-   against the insn's operand constraints (found via INSN_CODE_NUM)
+  if (recog_n_alternatives > MAX_RECOG_ALTERNATIVES)
+    abort ();
+}
+
+/* After calling extract_insn, you can use this function to extract some
+   information from the constraint strings into a more usable form.
+   The collected data is stored in recog_op_alt.  */
+void
+preprocess_constraints ()
+{
+  int i;
+
+  for (i = 0; i < recog_n_operands; i++)
+    {
+      int j;
+      struct operand_alternative *op_alt;
+      char *p = recog_constraints[i];
+
+      op_alt = recog_op_alt[i];
+
+      for (j = 0; j < recog_n_alternatives; j++)
+       {
+         op_alt[j].class = NO_REGS;
+         op_alt[j].constraint = p;
+         op_alt[j].matches = -1;
+         op_alt[j].matched = -1;
+
+         if (*p == '\0' || *p == ',')
+           {
+             op_alt[j].anything_ok = 1;
+             continue;
+           }
+
+         for (;;)
+           {
+             char c = *p++;
+             if (c == '#')
+               do
+                 c = *p++;
+               while (c != ',' && c != '\0');
+             if (c == ',' || c == '\0')
+               break;
+
+             switch (c)
+               {
+               case '=': case '+': case '*': case '%':
+               case 'E': case 'F': case 'G': case 'H':
+               case 's': case 'i': case 'n':
+               case 'I': case 'J': case 'K': case 'L':
+               case 'M': case 'N': case 'O': case 'P':
+#ifdef EXTRA_CONSTRAINT
+               case 'Q': case 'R': case 'S': case 'T': case 'U':
+#endif
+                 /* These don't say anything we care about.  */
+                 break;
+
+               case '?':
+                 op_alt[j].reject += 6;
+                 break;
+               case '!':
+                 op_alt[j].reject += 600;
+                 break;
+               case '&':
+                 op_alt[j].earlyclobber = 1;
+                 break;                  
+
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                 op_alt[j].matches = c - '0';
+                 op_alt[op_alt[j].matches].matched = i;
+                 break;
+
+               case 'm':
+                 op_alt[j].memory_ok = 1;
+                 break;
+               case '<':
+                 op_alt[j].decmem_ok = 1;
+                 break;
+               case '>':
+                 op_alt[j].incmem_ok = 1;
+                 break;
+               case 'V':
+                 op_alt[j].nonoffmem_ok = 1;
+                 break;
+               case 'o':
+                 op_alt[j].offmem_ok = 1;
+                 break;
+               case 'X':
+                 op_alt[j].anything_ok = 1;
+                 break;
+
+               case 'p':
+                 op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) BASE_REG_CLASS];
+                 break;
+
+               case 'g': case 'r':
+                 op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) GENERAL_REGS];
+                 break;
+
+               default:
+                 op_alt[j].class = reg_class_subunion[(int) op_alt[j].class][(int) REG_CLASS_FROM_LETTER (c)];
+                 break;
+               }
+           }
+       }
+    }
+}
+#ifdef REGISTER_CONSTRAINTS
+
+/* Check the operands of an insn against the insn's operand constraints
    and return 1 if they are valid.
+   The information about the insn's operands, constraints, operand modes
+   etc. is obtained from the global variables set up by extract_insn.
 
    WHICH_ALTERNATIVE is set to a number which indicates which
    alternative of constraints was matched: 0 for the first alternative,
@@ -1590,40 +1951,35 @@ struct funny_match
 };
 
 int
-constrain_operands (insn_code_num, strict)
-     int insn_code_num;
+constrain_operands (strict)
      int strict;
 {
   char *constraints[MAX_RECOG_OPERANDS];
   int matching_operands[MAX_RECOG_OPERANDS];
-  enum op_type {OP_IN, OP_OUT, OP_INOUT} op_types[MAX_RECOG_OPERANDS];
   int earlyclobber[MAX_RECOG_OPERANDS];
   register int c;
-  int noperands = insn_n_operands[insn_code_num];
 
   struct funny_match funny_match[MAX_RECOG_OPERANDS];
   int funny_match_index;
-  int nalternatives = insn_n_alternatives[insn_code_num];
 
-  if (noperands == 0 || nalternatives == 0)
+  if (recog_n_operands == 0 || recog_n_alternatives == 0)
     return 1;
 
-  for (c = 0; c < noperands; c++)
+  for (c = 0; c < recog_n_operands; c++)
     {
-      constraints[c] = insn_operand_constraint[insn_code_num][c];
+      constraints[c] = recog_constraints[c];
       matching_operands[c] = -1;
-      op_types[c] = OP_IN;
     }
 
   which_alternative = 0;
 
-  while (which_alternative < nalternatives)
+  while (which_alternative < recog_n_alternatives)
     {
       register int opno;
       int lose = 0;
       funny_match_index = 0;
 
-      for (opno = 0; opno < noperands; opno++)
+      for (opno = 0; opno < recog_n_operands; opno++)
        {
          register rtx op = recog_operand[opno];
          enum machine_mode mode = GET_MODE (op);
@@ -1634,6 +1990,11 @@ constrain_operands (insn_code_num, strict)
 
          earlyclobber[opno] = 0;
 
+         /* A unary operator may be accepted by the predicate, but it
+            is irrelevant for matching constraints.  */
+         if (GET_RTX_CLASS (GET_CODE (op)) == '1')
+           op = XEXP (op, 0);
+
          if (GET_CODE (op) == SUBREG)
            {
              if (GET_CODE (SUBREG_REG (op)) == REG
@@ -1651,18 +2012,18 @@ constrain_operands (insn_code_num, strict)
            switch (c)
              {
              case '?':
-             case '#':
              case '!':
              case '*':
              case '%':
-               break;
-
              case '=':
-               op_types[opno] = OP_OUT;
+             case '+':
                break;
 
-             case '+':
-               op_types[opno] = OP_INOUT;
+             case '#':
+               /* Ignore rest of this alternative as far as
+                  constraint checking is concerned.  */
+               while (*p && *p != ',')
+                 p++;
                break;
 
              case '&':
@@ -1705,12 +2066,12 @@ constrain_operands (insn_code_num, strict)
 
              case 'p':
                /* p is used for address_operands.  When we are called by
-                  gen_input_reload, no one will have checked that the
-                  address is strictly valid, i.e., that all pseudos
-                  requiring hard regs have gotten them.  */
+                  gen_reload, no one will have checked that the address is
+                  strictly valid, i.e., that all pseudos requiring hard regs
+                  have gotten them.  */
                if (strict <= 0
-                   || (strict_memory_address_p
-                       (insn_operand_mode[insn_code_num][opno], op)))
+                   || (strict_memory_address_p (recog_operand_mode[opno],
+                                                op)))
                  win = 1;
                break;
 
@@ -1735,15 +2096,17 @@ constrain_operands (insn_code_num, strict)
                        && REGNO (op) >= FIRST_PSEUDO_REGISTER)
                    || (strict == 0 && GET_CODE (op) == SCRATCH)
                    || (GET_CODE (op) == REG
-                       && (GENERAL_REGS == ALL_REGS
+                       && ((GENERAL_REGS == ALL_REGS
+                            && REGNO (op) < FIRST_PSEUDO_REGISTER)
                            || reg_fits_class_p (op, GENERAL_REGS,
                                                 offset, mode))))
                  win = 1;
                break;
 
              case 'X':
-               /* This is used for a MATCH_SCRATCH in the cases when we
-                  don't actually need anything.  So anything goes any time. */
+               /* This is used for a MATCH_SCRATCH in the cases when
+                  we don't actually need anything.  So anything goes
+                  any time.  */
                win = 1;
                break;
 
@@ -1772,12 +2135,14 @@ constrain_operands (insn_code_num, strict)
                break;
 
              case 'E':
+#ifndef REAL_ARITHMETIC
                /* Match any CONST_DOUBLE, but only if
                   we can examine the bits of it reliably.  */
                if ((HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT
                     || HOST_BITS_PER_WIDE_INT != BITS_PER_WORD)
                    && GET_MODE (op) != VOIDmode && ! flag_pretend_float)
                  break;
+#endif
                if (GET_CODE (op) == CONST_DOUBLE)
                  win = 1;
                break;
@@ -1837,7 +2202,12 @@ constrain_operands (insn_code_num, strict)
 
              case 'V':
                if (GET_CODE (op) == MEM
-                   && ! offsettable_memref_p (op))
+                   && ((strict > 0 && ! offsettable_memref_p (op))
+                       || (strict < 0
+                           && !(CONSTANT_P (op) || GET_CODE (op) == MEM))
+                       || (reload_in_progress
+                           && !(GET_CODE (op) == REG
+                                && REGNO (op) >= FIRST_PSEUDO_REGISTER))))
                  win = 1;
                break;
 
@@ -1881,20 +2251,21 @@ constrain_operands (insn_code_num, strict)
             operand.  */
 
          if (strict > 0)
-           for (eopno = 0; eopno < noperands; eopno++)
+           for (eopno = 0; eopno < recog_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.  */
              if (earlyclobber[eopno]
                  && GET_CODE (recog_operand[eopno]) == REG)
-               for (opno = 0; opno < noperands; opno++)
+               for (opno = 0; opno < recog_n_operands; opno++)
                  if ((GET_CODE (recog_operand[opno]) == MEM
-                      || op_types[opno] != OP_OUT)
+                      || recog_op_type[opno] != OP_OUT)
                      && opno != eopno
-                     && constraints[opno] != 0
+                     /* Ignore things like match_operator operands.  */
+                     && *recog_constraints[opno] != 0
                      && ! (matching_operands[opno] == eopno
-                           && rtx_equal_p (recog_operand[opno],
-                                           recog_operand[eopno]))
+                           && operands_match_p (recog_operand[opno],
+                                                recog_operand[eopno]))
                      && ! safe_from_earlyclobber (recog_operand[opno],
                                                   recog_operand[eopno]))
                    lose = 1;
@@ -1917,13 +2288,13 @@ constrain_operands (insn_code_num, strict)
   /* If we are about to reject this, but we are not to test strictly,
      try a very loose test.  Only return failure if it fails also.  */
   if (strict == 0)
-    return constrain_operands (insn_code_num, -1);
+    return constrain_operands (-1);
   else
     return 0;
 }
 
 /* Return 1 iff OPERAND (assumed to be a REG rtx)
-   is a hard reg in class CLASS when its regno is offsetted by OFFSET
+   is a hard reg in class CLASS when its regno is offset by OFFSET
    and changed to mode MODE.
    If REG occupies multiple hard regs, all of them must be in CLASS.  */