OSDN Git Service

* config/mips/iris5.h (UNALIGNED_INT_ASM_OP,
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index 44cfc3b..b2123c1 100644 (file)
@@ -113,12 +113,7 @@ simplify_gen_binary (code, mode, op0, op1)
 
   /* Put complex operands first and constants second if commutative.  */
   if (GET_RTX_CLASS (code) == 'c'
-      && ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
-         || (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
-             && GET_RTX_CLASS (GET_CODE (op1)) != 'o')
-         || (GET_CODE (op0) == SUBREG
-             && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
-             && GET_RTX_CLASS (GET_CODE (op1)) != 'o')))
+      && swap_commutative_operands_p (op0, op1))
     tem = op0, op0 = op1, op1 = tem;
 
   /* If this simplifies, do it.  */
@@ -194,12 +189,7 @@ simplify_gen_relational (code, mode, cmp_mode, op0, op1)
     return tem;
 
   /* Put complex operands first and constants second.  */
-  if ((CONSTANT_P (op0) && GET_CODE (op1) != CONST_INT)
-      || (GET_RTX_CLASS (GET_CODE (op0)) == 'o'
-         && GET_RTX_CLASS (GET_CODE (op1)) != 'o')
-      || (GET_CODE (op0) == SUBREG
-         && GET_RTX_CLASS (GET_CODE (SUBREG_REG (op0))) == 'o'
-         && GET_RTX_CLASS (GET_CODE (op1)) != 'o'))
+  if (swap_commutative_operands_p (op0, op1))
     tem = op0, op0 = op1, op1 = tem, code = swap_condition (code);
 
   return gen_rtx_fmt_ee (code, mode, op0, op1);
@@ -259,13 +249,18 @@ simplify_replace_rtx (x, old, new)
                              simplify_replace_rtx (XEXP (x, 2), old, new));
 
     case 'x':
-      /* The only case we try to handle is a lowpart SUBREG of a single-word
-        CONST_INT.  */
-      if (code == SUBREG && subreg_lowpart_p (x) && old == SUBREG_REG (x)
-         && GET_CODE (new) == CONST_INT
-         && GET_MODE_SIZE (GET_MODE (old)) <= UNITS_PER_WORD)
-       return GEN_INT (INTVAL (new) & GET_MODE_MASK (mode));
-
+      /* The only case we try to handle is a SUBREG.  */
+      if (code == SUBREG)
+       {
+         rtx exp;
+         exp = simplify_gen_subreg (GET_MODE (x),
+                                    simplify_replace_rtx (SUBREG_REG (x),
+                                                          old, new),
+                                    GET_MODE (SUBREG_REG (x)),
+                                    SUBREG_BYTE (x));
+         if (exp)
+          x = exp;
+       }
       return x;
 
     default:
@@ -1822,8 +1817,7 @@ simplify_relational_operation (code, mode, op0, op1)
     return 0;
 
   /* Make sure the constant is second.  */
-  if ((CONSTANT_P (op0) && ! CONSTANT_P (op1))
-      || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT))
+  if (swap_commutative_operands_p (op0, op1))
     {
       tem = op0, op0 = op1, op1 = tem;
       code = swap_condition (code);
@@ -2207,6 +2201,9 @@ simplify_subreg (outermode, op, innermode, byte)
       || byte >= GET_MODE_SIZE (innermode))
     abort ();
 
+  if (outermode == innermode && !byte)
+    return op;
+
   /* Attempt to simplify constant to non-SUBREG expression.  */
   if (CONSTANT_P (op))
     {
@@ -2218,7 +2215,7 @@ simplify_subreg (outermode, op, innermode, byte)
         Later it we should move all simplification code here and rewrite
         GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends
         using SIMPLIFY_SUBREG.  */
-      if (subreg_lowpart_parts_p (outermode, innermode, byte))
+      if (subreg_lowpart_offset (outermode, innermode) == byte)
        {
          rtx new = gen_lowpart_if_possible (outermode, op);
          if (new)
@@ -2230,9 +2227,9 @@ simplify_subreg (outermode, op, innermode, byte)
          && GET_MODE_SIZE (innermode) > UNITS_PER_WORD
          && GET_MODE_CLASS (outermode) == MODE_INT)
        {
-         rtx new = operand_subword (op,
-                                    (byte / UNITS_PER_WORD),
-                                    0, innermode);
+         rtx new = constant_subword (op,
+                                     (byte / UNITS_PER_WORD),
+                                     innermode);
          if (new)
            return new;
        }
@@ -2300,35 +2297,61 @@ simplify_subreg (outermode, op, innermode, byte)
   if (GET_CODE (op) == SUBREG)
     {
       enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
-      unsigned int final_offset = byte + SUBREG_BYTE (op);
+      int final_offset = byte + SUBREG_BYTE (op);
       rtx new;
 
       if (outermode == innermostmode
          && byte == 0 && SUBREG_BYTE (op) == 0)
        return SUBREG_REG (op);
 
-      if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
-         && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode)
-         && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (innermostmode))
+      /* The SUBREG_BYTE represents offset, as if the value were stored
+        in memory.  Irritating exception is paradoxical subreg, where
+        we define SUBREG_BYTE to be 0.  On big endian machines, this
+        value should be negative.  For a moment, undo this exception. */
+      if (byte == 0 && GET_MODE_SIZE (innermode) < GET_MODE_SIZE (outermode))
+       {
+         int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
+         if (WORDS_BIG_ENDIAN)
+           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           final_offset += difference % UNITS_PER_WORD;
+       }
+      if (SUBREG_BYTE (op) == 0
+         && GET_MODE_SIZE (innermostmode) < GET_MODE_SIZE (innermode))
+       {
+         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (innermode));
+         if (WORDS_BIG_ENDIAN)
+           final_offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           final_offset += difference % UNITS_PER_WORD;
+       }
+
+      /* See whether resulting subreg will be paradoxical.  */
+      if (GET_MODE_SIZE (innermostmode) > GET_MODE_SIZE (outermode))
        {
-         /* Inner SUBREG is paradoxical, outer is not.  On big endian
-            we have to special case this.  */
-         if (SUBREG_BYTE (op))
-           abort(); /* Can a paradoxical subreg have nonzero offset? */
-         if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
-           final_offset = (byte - GET_MODE_SIZE (innermode)
-                           + GET_MODE_SIZE (innermostmode));
-         else if (WORDS_BIG_ENDIAN)
-           final_offset = ((final_offset % UNITS_PER_WORD)
-                           + ((byte - GET_MODE_SIZE (innermode)
-                               + GET_MODE_SIZE (innermostmode))
-                              * UNITS_PER_WORD) / UNITS_PER_WORD);
+         /* In nonparadoxical subregs we can't handle negative offsets.  */
+         if (final_offset < 0)
+           return NULL_RTX;
+         /* Bail out in case resulting subreg would be incorrect.  */
+         if (final_offset % GET_MODE_SIZE (outermode)
+             || final_offset >= GET_MODE_SIZE (innermostmode))
+           return NULL;
+       }
+      else
+       {
+         int offset = 0;
+         int difference = (GET_MODE_SIZE (innermostmode) - GET_MODE_SIZE (outermode));
+
+         /* In paradoxical subreg, see if we are still looking on lower part.
+            If so, our SUBREG_BYTE will be 0.  */
+         if (WORDS_BIG_ENDIAN)
+           offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
+         if (BYTES_BIG_ENDIAN)
+           offset += difference % UNITS_PER_WORD;
+         if (offset == final_offset)
+           final_offset = 0;
          else
-           final_offset = (((final_offset * UNITS_PER_WORD)
-                            / UNITS_PER_WORD)
-                           + ((byte - GET_MODE_SIZE (innermode)
-                               + GET_MODE_SIZE (innermostmode))
-                              % UNITS_PER_WORD));
+           return NULL;
        }
 
       /* Recurse for futher possible simplifications.  */
@@ -2345,12 +2368,24 @@ simplify_subreg (outermode, op, innermode, byte)
      suppress this simplification.  If the hard register is the stack,
      frame, or argument pointer, leave this as a SUBREG.  */
 
-  if (REG_P (op) == REG
+  if (REG_P (op)
+      && (! REG_FUNCTION_VALUE_P (op)
+         || ! rtx_equal_function_value_matters)
+#ifdef CLASS_CANNOT_CHANGE_MODE
+      && ! (CLASS_CANNOT_CHANGE_MODE_P (outermode, innermode)
+           && GET_MODE_CLASS (innermode) != MODE_COMPLEX_INT
+           && GET_MODE_CLASS (innermode) != MODE_COMPLEX_FLOAT
+           && (TEST_HARD_REG_BIT
+               (reg_class_contents[(int) CLASS_CANNOT_CHANGE_MODE],
+                REGNO (op))))
+#endif
       && REGNO (op) < FIRST_PSEUDO_REGISTER
-      && REGNO (op) != FRAME_POINTER_REGNUM
+      && ((reload_completed && !frame_pointer_needed)
+         || (REGNO (op) != FRAME_POINTER_REGNUM
 #if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
-      && REGNO (op) != HARD_FRAME_POINTER_REGNUM
+             && REGNO (op) != HARD_FRAME_POINTER_REGNUM
 #endif
+            ))
 #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
       && REGNO (op) != ARG_POINTER_REGNUM
 #endif
@@ -2359,7 +2394,11 @@ simplify_subreg (outermode, op, innermode, byte)
       int final_regno = subreg_hard_regno (gen_rtx_SUBREG (outermode, op, byte),
                                           0);
 
-      if (HARD_REGNO_MODE_OK (final_regno, outermode))
+      /* ??? We do allow it if the current REG is not valid for
+        its mode.  This is a kludge to work around how float/complex
+        arguments are passed on 32-bit Sparc and should be fixed.  */
+      if (HARD_REGNO_MODE_OK (final_regno, outermode)
+         || ! HARD_REGNO_MODE_OK (REGNO (op), innermode))
        return gen_rtx_REG (outermode, final_regno);
     }
 
@@ -2370,7 +2409,11 @@ simplify_subreg (outermode, op, innermode, byte)
 
   if (GET_CODE (op) == MEM
       && ! mode_dependent_address_p (XEXP (op, 0))
-      && ! MEM_VOLATILE_P (op)
+      /* Allow splitting of volatile memory references in case we don't
+         have instruction to move the whole thing.  */
+      && (! MEM_VOLATILE_P (op)
+         || (mov_optab->handlers[(int) innermode].insn_code
+             == CODE_FOR_nothing))
       && GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op)))
     {
       rtx new;
@@ -2379,8 +2422,52 @@ simplify_subreg (outermode, op, innermode, byte)
       MEM_COPY_ATTRIBUTES (new, op);
       return new;
     }
+
+  /* Handle complex values represented as CONCAT
+     of real and imaginary part.  */
+  if (GET_CODE (op) == CONCAT)
+    {
+      int is_realpart = byte < GET_MODE_UNIT_SIZE (innermode);
+      rtx part = is_realpart ? XEXP (op, 0) : XEXP (op, 1);
+      unsigned int final_offset;
+
+      final_offset = byte % (GET_MODE_UNIT_SIZE (innermode));
+      return simplify_subreg (outermode, part, GET_MODE (part), final_offset);
+    }
+
   return NULL_RTX;
 }
+/* Make a SUBREG operation or equivalent if it folds.  */
+
+rtx
+simplify_gen_subreg (outermode, op, innermode, byte)
+     rtx op;
+     unsigned int byte;
+     enum machine_mode outermode, innermode;
+{
+  rtx new;
+  /* Little bit of sanity checking.  */
+  if (innermode == VOIDmode || outermode == VOIDmode
+      || innermode == BLKmode || outermode == BLKmode)
+    abort ();
+
+  if (GET_MODE (op) != innermode
+      && GET_MODE (op) != VOIDmode)
+    abort ();
+
+  if (byte % GET_MODE_SIZE (outermode)
+      || byte >= GET_MODE_SIZE (innermode))
+    abort ();
+
+  new = simplify_subreg (outermode, op, innermode, byte);
+  if (new)
+    return new;
+
+  if (GET_CODE (op) == SUBREG || GET_MODE (op) == VOIDmode)
+    return NULL_RTX;
+
+  return gen_rtx_SUBREG (outermode, op, byte);
+}
 /* Simplify X, an rtx expression.
 
    Return the simplified expression or NULL if no simplifications
@@ -2450,6 +2537,13 @@ simplify_rtx (x)
                                             ? GET_MODE (XEXP (x, 0))
                                             : GET_MODE (XEXP (x, 1))),
                                            XEXP (x, 0), XEXP (x, 1));
+    case 'x':
+      /* The only case we try to handle is a SUBREG.  */
+      if (code == SUBREG)
+        return simplify_gen_subreg (mode, SUBREG_REG (x),
+                                   GET_MODE (SUBREG_REG (x)),
+                                   SUBREG_BYTE (x));
+      return NULL;
     default:
       return NULL;
     }