simplify-rtx.c (simplify_subreg): Fix combining of paradoxical subregs.
authorJan Hubicka <jh@suse.cz>
Mon, 4 Jun 2001 18:44:57 +0000 (20:44 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Mon, 4 Jun 2001 18:44:57 +0000 (18:44 +0000)
* simplify-rtx.c (simplify_subreg): Fix combining of
paradoxical subregs.

From-SVN: r42868

gcc/ChangeLog
gcc/simplify-rtx.c

index 05421810ee0871c39c7933efa081e5425bc3a519..38d6aaf5d5bb40f8beb5e708df44b89028219898 100644 (file)
@@ -1,3 +1,8 @@
+Mon Jun  4 20:44:25 CEST 2001  Jan Hubicka  <jh@suse.cz>
+
+       * simplify-rtx.c (simplify_subreg): Fix combining of
+       paradoxical subregs.
+
 Mon Jun  4 20:15:25 CEST 2001  Jan Hubicka  <jh@suse.cz>
 
        * rtlanal.c (rtx_unsable_p): ADDRESSOF is stable.
index 574513f59357da8f5927df15c3af8834d0c00334..97d6f6bc709e3510b9e2dfb594368b3e893ecbb3 100644 (file)
@@ -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)),