OSDN Git Service

* config/mips/iris5.h (UNALIGNED_INT_ASM_OP,
[pf3gnuchains/gcc-fork.git] / gcc / simplify-rtx.c
index 621bd90..b2123c1 100644 (file)
@@ -2215,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)
@@ -2227,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;
        }
@@ -2297,41 +2297,63 @@ 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))
        {
-         /* 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);
+         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))
+       {
+         /* 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;
        }
 
-      /* Bail out in case resulting subreg would be incorrect.  */
-      if (final_offset % GET_MODE_SIZE (outermode)
-         || final_offset >= GET_MODE_SIZE (innermostmode))
-       return NULL;
       /* Recurse for futher possible simplifications.  */
       new = simplify_subreg (outermode, SUBREG_REG (op),
                             GET_MODE (SUBREG_REG (op)),
@@ -2347,11 +2369,23 @@ simplify_subreg (outermode, op, innermode, byte)
      frame, or argument pointer, leave this as a SUBREG.  */
 
   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
@@ -2360,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);
     }
 
@@ -2371,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;
@@ -2385,11 +2427,11 @@ simplify_subreg (outermode, op, innermode, byte)
      of real and imaginary part.  */
   if (GET_CODE (op) == CONCAT)
     {
-      int is_realpart = byte < GET_MODE_UNIT_SIZE (innermode) / 2;
+      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) / 2);
+      final_offset = byte % (GET_MODE_UNIT_SIZE (innermode));
       return simplify_subreg (outermode, part, GET_MODE (part), final_offset);
     }