rtl.h (validate_subreg): Declare.
authorRichard Henderson <rth@redhat.com>
Wed, 24 Nov 2004 00:09:03 +0000 (16:09 -0800)
committerRichard Henderson <rth@gcc.gnu.org>
Wed, 24 Nov 2004 00:09:03 +0000 (16:09 -0800)
        * rtl.h (validate_subreg): Declare.
        * emit-rtl.c (validate_subreg): New.
        (gen_rtx_SUBREG): Use it.
        * simplify-rtx.c (simplify_subreg): Likewise.
        (simplify_gen_subreg): Likewise.  Remove duplicate asserts.
        * expr.c (emit_move_insn_1): Tidy complex move code.  Use memory
        fallback whenever gen_realpart/gen_imagpart would not be able to
        create SUBREGs.

From-SVN: r91126

gcc/ChangeLog
gcc/emit-rtl.c
gcc/expr.c
gcc/rtl.h
gcc/simplify-rtx.c

index 8a31513ada1a06076cdaf92c590d2bcdfd0185b1..87fdf4f7cbd8870611d2adf71e8b65019f07101c 100644 (file)
@@ -1,3 +1,14 @@
+2004-11-23  Richard Henderson  <rth@redhat.com>
+
+       * rtl.h (validate_subreg): Declare.
+       * emit-rtl.c (validate_subreg): New.
+       (gen_rtx_SUBREG): Use it.
+       * simplify-rtx.c (simplify_subreg): Likewise.
+       (simplify_gen_subreg): Likewise.  Remove duplicate asserts.
+       * expr.c (emit_move_insn_1): Tidy complex move code.  Use memory
+       fallback whenever gen_realpart/gen_imagpart would not be able to
+       create SUBREGs.
+
 2004-11-23  Richard Henderson  <rth@redhat.com>
 
        * expmed.c (extract_bit_field): Use simplify_gen_subreg instead of
index c24ba25586516076787ca788594ab55404149ce6..98dec5f9c8d293a94da27e25b793d73afc7b2c8c 100644 (file)
@@ -607,20 +607,98 @@ gen_const_mem (enum machine_mode mode, rtx addr)
   return mem;
 }
 
-rtx
-gen_rtx_SUBREG (enum machine_mode mode, rtx reg, int offset)
+/* We want to create (subreg:OMODE (obj:IMODE) OFFSET).  Return true if
+   this construct would be valid, and false otherwise.  */
+
+bool
+validate_subreg (enum machine_mode omode, enum machine_mode imode,
+                rtx reg, unsigned int offset)
 {
-  /* This is the most common failure type.
-     Catch it early so we can see who does it.  */
-  gcc_assert (!(offset % GET_MODE_SIZE (mode)));
+  unsigned int isize = GET_MODE_SIZE (imode);
+  unsigned int osize = GET_MODE_SIZE (omode);
 
-  /* This check isn't usable right now because combine will
-     throw arbitrary crap like a CALL into a SUBREG in
-     gen_lowpart_for_combine so we must just eat it.  */
-#if 0
-  /* Check for this too.  */
-  gcc_assert (offset < GET_MODE_SIZE (GET_MODE (reg)));
+  /* All subregs must be aligned.  */
+  if (offset % osize != 0)
+    return false;
+
+  /* The subreg offset cannot be outside the inner object.  */
+  if (offset >= isize)
+    return false;
+
+  /* ??? This should not be here.  Temporarily continue to allow word_mode
+     subregs of anything.  The most common offender is (subreg:SI (reg:DF)).
+     Generally, backends are doing something sketchy but it'll take time to
+     fix them all.  */
+  if (omode == word_mode)
+    ;
+  /* ??? Similarly, e.g. with (subreg:DF (reg:TI)).  Though store_bit_field
+     is the culprit here, and not the backends.  */
+  else if (osize >= UNITS_PER_WORD && isize >= osize)
+    ;
+  /* Allow component subregs of complex and vector.  Though given the below
+     extraction rules, it's not always clear what that means.  */
+  else if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
+          && GET_MODE_INNER (imode) == omode)
+    ;
+  /* ??? x86 sse code makes heavy use of *paradoxical* vector subregs,
+     i.e. (subreg:V4SF (reg:SF) 0).  This surely isn't the cleanest way to
+     represent this.  It's questionable if this ought to be represented at
+     all -- why can't this all be hidden in post-reload splitters that make
+     arbitrarily mode changes to the registers themselves.  */
+  else if (VECTOR_MODE_P (omode) && GET_MODE_INNER (omode) == imode)
+    ;
+  /* Subregs involving floating point modes are not allowed to
+     change size.  Therefore (subreg:DI (reg:DF) 0) is fine, but
+     (subreg:SI (reg:DF) 0) isn't.  */
+  else if (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))
+    {
+      if (isize != osize)
+       return false;
+    }
+
+  /* Paradoxical subregs must have offset zero.  */
+  if (osize > isize)
+    return offset == 0;
+
+  /* This is a normal subreg.  Verify that the offset is representable.  */
+
+  /* For hard registers, we already have most of these rules collected in
+     subreg_offset_representable_p.  */
+  if (reg && REG_P (reg) && HARD_REGISTER_P (reg))
+    {
+      unsigned int regno = REGNO (reg);
+
+#ifdef CANNOT_CHANGE_MODE_CLASS
+      if ((COMPLEX_MODE_P (imode) || VECTOR_MODE_P (imode))
+         && GET_MODE_INNER (imode) == omode)
+       ;
+      else if (REG_CANNOT_CHANGE_MODE_P (regno, imode, omode))
+       return false;
 #endif
+
+      return subreg_offset_representable_p (regno, imode, offset, omode);
+    }
+
+  /* For pseudo registers, we want most of the same checks.  Namely:
+     If the register no larger than a word, the subreg must be lowpart.
+     If the register is larger than a word, the subreg must be the lowpart
+     of a subword.  A subreg does *not* perform arbitrary bit extraction.
+     Given that we've already checked mode/offset alignment, we only have
+     to check subword subregs here.  */
+  if (osize < UNITS_PER_WORD)
+    {
+      enum machine_mode wmode = isize > UNITS_PER_WORD ? word_mode : imode;
+      unsigned int low_off = subreg_lowpart_offset (omode, wmode);
+      if (offset % UNITS_PER_WORD != low_off)
+       return false;
+    }
+  return true;
+}
+
+rtx
+gen_rtx_SUBREG (enum machine_mode mode, rtx reg, int offset)
+{
+  gcc_assert (validate_subreg (mode, GET_MODE (reg), reg, offset));
   return gen_rtx_raw_SUBREG (mode, reg, offset);
 }
 
index f94993085cfdf5ca5a4c25a4aa00b7e1efb56ef4..fb0c73ecd09ee6c1d0e989bfe157ee9654bcdbcc 100644 (file)
@@ -2647,7 +2647,6 @@ emit_move_insn_1 (rtx x, rtx y)
 {
   enum machine_mode mode = GET_MODE (x);
   enum machine_mode submode;
-  enum mode_class class = GET_MODE_CLASS (mode);
 
   gcc_assert ((unsigned int) mode < (unsigned int) MAX_MACHINE_MODE);
 
@@ -2656,20 +2655,21 @@ emit_move_insn_1 (rtx x, rtx y)
       emit_insn (GEN_FCN (mov_optab->handlers[(int) mode].insn_code) (x, y));
 
   /* Expand complex moves by moving real part and imag part, if possible.  */
-  else if ((class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
+  else if (COMPLEX_MODE_P (mode)
           && BLKmode != (submode = GET_MODE_INNER (mode))
           && (mov_optab->handlers[(int) submode].insn_code
               != CODE_FOR_nothing))
     {
+      unsigned int modesize = GET_MODE_SIZE (mode);
+      unsigned int submodesize = GET_MODE_SIZE (submode);
+
       /* Don't split destination if it is a stack push.  */
-      int stack = push_operand (x, GET_MODE (x));
+      int stack = push_operand (x, mode);
 
 #ifdef PUSH_ROUNDING
       /* In case we output to the stack, but the size is smaller than the
         machine can push exactly, we need to use move instructions.  */
-      if (stack
-         && (PUSH_ROUNDING (GET_MODE_SIZE (submode))
-             != GET_MODE_SIZE (submode)))
+      if (stack && PUSH_ROUNDING (submodesize) != submodesize)
        {
          rtx temp;
          HOST_WIDE_INT offset1, offset2;
@@ -2683,9 +2683,7 @@ emit_move_insn_1 (rtx x, rtx y)
                               add_optab,
 #endif
                               stack_pointer_rtx,
-                              GEN_INT
-                                (PUSH_ROUNDING
-                                 (GET_MODE_SIZE (GET_MODE (x)))),
+                              GEN_INT (PUSH_ROUNDING (modesize)),
                               stack_pointer_rtx, 0, OPTAB_LIB_WIDEN);
 
          if (temp != stack_pointer_rtx)
@@ -2693,11 +2691,10 @@ emit_move_insn_1 (rtx x, rtx y)
 
 #ifdef STACK_GROWS_DOWNWARD
          offset1 = 0;
-         offset2 = GET_MODE_SIZE (submode);
+         offset2 = submodesize;
 #else
-         offset1 = -PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)));
-         offset2 = (-PUSH_ROUNDING (GET_MODE_SIZE (GET_MODE (x)))
-                    + GET_MODE_SIZE (submode));
+         offset1 = -PUSH_ROUNDING (modesize);
+         offset2 = -PUSH_ROUNDING (modesize) + submodesize;
 #endif
 
          emit_move_insn (change_address (x, submode,
@@ -2748,42 +2745,32 @@ emit_move_insn_1 (rtx x, rtx y)
             memory and reload.  FIXME, we should see about using extract and
             insert on integer registers, but complex short and complex char
             variables should be rarely used.  */
-         if (GET_MODE_BITSIZE (mode) < 2 * BITS_PER_WORD
-             && (reload_in_progress | reload_completed) == 0)
+         if ((reload_in_progress | reload_completed) == 0
+             && (!validate_subreg (submode, mode, NULL, submodesize)
+                 || !validate_subreg (submode, mode, NULL, 0)))
            {
-             int packed_dest_p
-               = (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER);
-             int packed_src_p
-               = (REG_P (y) && REGNO (y) < FIRST_PSEUDO_REGISTER);
-
-             if (packed_dest_p || packed_src_p)
+             if (REG_P (x) || REG_P (y))
                {
-                 enum mode_class reg_class = ((class == MODE_COMPLEX_FLOAT)
-                                              ? MODE_FLOAT : MODE_INT);
-
+                 rtx mem, cmem;
                  enum machine_mode reg_mode
-                   = mode_for_size (GET_MODE_BITSIZE (mode), reg_class, 1);
+                   = mode_for_size (GET_MODE_BITSIZE (mode), MODE_INT, 1);
+
+                 gcc_assert (reg_mode != BLKmode);
+
+                 mem = assign_stack_temp (reg_mode, modesize, 0);
+                 cmem = adjust_address (mem, mode, 0);
 
-                 if (reg_mode != BLKmode)
+                 if (REG_P (x))
+                   {
+                     rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
+                     emit_move_insn_1 (cmem, y);
+                     return emit_move_insn_1 (sreg, mem);
+                   }
+                 else
                    {
-                     rtx mem = assign_stack_temp (reg_mode,
-                                                  GET_MODE_SIZE (mode), 0);
-                     rtx cmem = adjust_address (mem, mode, 0);
-
-                     if (packed_dest_p)
-                       {
-                         rtx sreg = gen_rtx_SUBREG (reg_mode, x, 0);
-
-                         emit_move_insn_1 (cmem, y);
-                         return emit_move_insn_1 (sreg, mem);
-                       }
-                     else
-                       {
-                         rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
-
-                         emit_move_insn_1 (mem, sreg);
-                         return emit_move_insn_1 (x, cmem);
-                       }
+                     rtx sreg = gen_rtx_SUBREG (reg_mode, y, 0);
+                     emit_move_insn_1 (mem, sreg);
+                     return emit_move_insn_1 (x, cmem);
                    }
                }
            }
index 978c92dd7e5420a2d9eb079dd1b441179ef16d4b..edb2913ac85704c5b66ab8a0ef58f864eb2cd792 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1964,6 +1964,8 @@ extern rtx delete_insn_and_edges (rtx);
 extern void delete_insn_chain_and_edges (rtx, rtx);
 extern rtx gen_lowpart_SUBREG (enum machine_mode, rtx);
 extern rtx gen_const_mem (enum machine_mode, rtx);
+extern bool validate_subreg (enum machine_mode, enum machine_mode,
+                            rtx, unsigned int);
 
 /* In combine.c */
 extern int combine_instructions (rtx, unsigned int);
index 8ecd8fc55281135766e0017da518ce9512619051..52c0cffb87bb718ea18a944971044b6f1b9eede3 100644 (file)
@@ -3642,12 +3642,14 @@ simplify_subreg (enum machine_mode outermode, rtx op,
        }
 
       /* Recurse for further possible simplifications.  */
-      newx = simplify_subreg (outermode, SUBREG_REG (op),
-                            GET_MODE (SUBREG_REG (op)),
-                            final_offset);
+      newx = simplify_subreg (outermode, SUBREG_REG (op), innermostmode,
+                             final_offset);
       if (newx)
        return newx;
-      return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
+      if (validate_subreg (outermode, innermostmode,
+                          SUBREG_REG (op), final_offset))
+        return gen_rtx_SUBREG (outermode, SUBREG_REG (op), final_offset);
+      return NULL_RTX;
     }
 
   /* SUBREG of a hard register => just change the register number
@@ -3725,9 +3727,9 @@ simplify_subreg (enum machine_mode outermode, rtx op,
       res = simplify_subreg (outermode, part, GET_MODE (part), final_offset);
       if (res)
        return res;
-      /* We can at least simplify it by referring directly to the
-        relevant part.  */
-      return gen_rtx_SUBREG (outermode, part, final_offset);
+      if (validate_subreg (outermode, GET_MODE (part), part, final_offset))
+        return gen_rtx_SUBREG (outermode, part, final_offset);
+      return NULL_RTX;
     }
 
   /* Optimize SUBREG truncations of zero and sign extended values.  */
@@ -3775,17 +3777,6 @@ simplify_gen_subreg (enum machine_mode outermode, rtx op,
                     enum machine_mode innermode, unsigned int byte)
 {
   rtx newx;
-  /* Little bit of sanity checking.  */
-  gcc_assert (innermode != VOIDmode);
-  gcc_assert (outermode != VOIDmode);
-  gcc_assert (innermode != BLKmode);
-  gcc_assert (outermode != BLKmode);
-
-  gcc_assert (GET_MODE (op) == innermode
-             || GET_MODE (op) == VOIDmode);
-
-  gcc_assert ((byte % GET_MODE_SIZE (outermode)) == 0);
-  gcc_assert (byte < GET_MODE_SIZE (innermode));
 
   newx = simplify_subreg (outermode, op, innermode, byte);
   if (newx)
@@ -3795,8 +3786,12 @@ simplify_gen_subreg (enum machine_mode outermode, rtx op,
       || (REG_P (op) && REGNO (op) < FIRST_PSEUDO_REGISTER))
     return NULL_RTX;
 
-  return gen_rtx_SUBREG (outermode, op, byte);
+  if (validate_subreg (outermode, innermode, op, byte))
+    return gen_rtx_SUBREG (outermode, op, byte);
+
+  return NULL_RTX;
 }
+
 /* Simplify X, an rtx expression.
 
    Return the simplified expression or NULL if no simplifications