OSDN Git Service

* loop.h (precondition_loop_p): Added new mode argument.
authorm.hayes <m.hayes@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 25 Nov 1998 21:32:27 +0000 (21:32 +0000)
committerm.hayes <m.hayes@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 25 Nov 1998 21:32:27 +0000 (21:32 +0000)
* unroll.c (precondition_loop_p): Likewise.
(approx_final_value): Function deleted and subsumed
  into loop_iterations.
(loop_find_equiv_value): New function.
(loop_iterations): Use loop_find_equiv_value to find increments
too large to be immediate constants.  Also use it to find terms
common to initial and final iteration values that can be removed.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@23885 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/loop.h
gcc/unroll.c

index e295f1c..08456ee 100644 (file)
@@ -1,3 +1,15 @@
+Thu Nov 26 18:26:21 1998  Michael Hayes  <m.hayes@elec.canterbury.ac.nz>
+
+       * loop.h (precondition_loop_p): Added new mode argument.
+       * unroll.c (precondition_loop_p): Likewise.
+       (approx_final_value): Function deleted and subsumed
+       into loop_iterations.
+       (loop_find_equiv_value): New function.
+       (loop_iterations): Use loop_find_equiv_value to find increments
+       too large to be immediate constants.  Also use it to find terms
+       common to initial and final iteration values that can be removed.
+
+
 Thu Nov 26 18:05:04 1998  Michael Hayes  <m.hayes@elec.canterbury.ac.nz>
 
        * loop.h (struct loop_info): Define new structure.
index f747e46..c47fa41 100644 (file)
@@ -215,7 +215,8 @@ void unroll_loop PROTO((rtx, int, rtx, rtx, struct loop_info *, int));
 rtx biv_total_increment PROTO((struct iv_class *, rtx, rtx));
 unsigned HOST_WIDE_INT loop_iterations PROTO((rtx, rtx, struct loop_info *));
 int precondition_loop_p PROTO((rtx, struct loop_info *, 
-                              rtx *, rtx *, rtx *));
+                              rtx *, rtx *, rtx *, 
+                              enum machine_mode *mode));
 rtx final_biv_value PROTO((struct iv_class *, rtx, rtx,
                           unsigned HOST_WIDE_INT));
 rtx final_giv_value PROTO((struct induction *, rtx, rtx,
index 5f9f2c7..0e616f5 100644 (file)
@@ -195,7 +195,6 @@ static void final_reg_note_copy PROTO((rtx, struct inline_remap *));
 static void copy_loop_body PROTO((rtx, rtx, struct inline_remap *, rtx, int,
                                  enum unroll_types, rtx, rtx, rtx, rtx));
 void iteration_info PROTO((rtx, rtx *, rtx *, rtx, rtx));
-static rtx approx_final_value PROTO((enum rtx_code, rtx, int *, int *));
 static int find_splittable_regs PROTO((enum unroll_types, rtx, rtx, rtx, int,
                                       unsigned HOST_WIDE_INT));
 static int find_splittable_givs PROTO((struct iv_class *, enum unroll_types,
@@ -849,12 +848,13 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
   if (unroll_type == UNROLL_NAIVE && ! splitting_not_safe && strength_reduce_p)
     {
       rtx initial_value, final_value, increment;
+      enum machine_mode mode;
 
       if (precondition_loop_p (loop_start, loop_info,
-                              &initial_value, &final_value, &increment))
+                              &initial_value, &final_value, &increment,
+                              &mode))
        {
          register rtx diff ;
-         enum machine_mode mode;
          rtx *labels;
          int abs_inc, neg_inc;
 
@@ -886,21 +886,6 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
 
          start_sequence ();
 
-         /* Decide what mode to do these calculations in.  Choose the larger
-            of final_value's mode and initial_value's mode, or a full-word if
-            both are constants.  */
-         mode = GET_MODE (final_value);
-         if (mode == VOIDmode)
-           {
-             mode = GET_MODE (initial_value);
-             if (mode == VOIDmode)
-               mode = word_mode;
-           }
-         else if (mode != GET_MODE (initial_value)
-                  && (GET_MODE_SIZE (mode)
-                      < GET_MODE_SIZE (GET_MODE (initial_value))))
-           mode = GET_MODE (initial_value);
-
          /* Calculate the difference between the final and initial values.
             Final value may be a (plus (reg x) (const_int 1)) rtx.
             Let the following cse pass simplify this if initial value is
@@ -1314,10 +1299,11 @@ unroll_loop (loop_end, insn_count, loop_start, end_insert_before,
 
 int
 precondition_loop_p (loop_start, loop_info,
-                    initial_value, final_value, increment)
+                    initial_value, final_value, increment, mode)
      rtx loop_start;
      struct loop_info *loop_info;
      rtx *initial_value, *final_value, *increment;
+     enum machine_mode *mode;
 {
 
   if (loop_info->n_iterations > 0)
@@ -1325,6 +1311,7 @@ precondition_loop_p (loop_start, loop_info,
       *initial_value = const0_rtx;
       *increment = const1_rtx;
       *final_value = GEN_INT (loop_info->n_iterations);
+      *mode = word_mode;
 
       if (loop_dump_stream)
        {
@@ -1430,6 +1417,21 @@ precondition_loop_p (loop_start, loop_info,
   *increment = loop_info->increment;
   *final_value = loop_info->final_value;
 
+  /* Decide what mode to do these calculations in.  Choose the larger
+     of final_value's mode and initial_value's mode, or a full-word if
+     both are constants.  */
+  *mode = GET_MODE (*final_value);
+  if (*mode == VOIDmode)
+    {
+      *mode = GET_MODE (*initial_value);
+      if (*mode == VOIDmode)
+       *mode = word_mode;
+    }
+  else if (*mode != GET_MODE (*initial_value)
+          && (GET_MODE_SIZE (*mode)
+              < GET_MODE_SIZE (GET_MODE (*initial_value))))
+    *mode = GET_MODE (*initial_value);
+
   /* Success! */
   if (loop_dump_stream)
     fprintf (loop_dump_stream, "Preconditioning: Successful.\n");
@@ -2421,60 +2423,6 @@ iteration_info (iteration_var, initial_value, increment, loop_start, loop_end)
     }
 }
 
-/* Calculate the approximate final value of the iteration variable
-   which has an loop exit test with code COMPARISON_CODE and comparison value
-   of COMPARISON_VALUE.  Also returns an indication of whether the comparison
-   was signed or unsigned, and the direction of the comparison.  This info is
-   needed to calculate the number of loop iterations.  */
-
-static rtx
-approx_final_value (comparison_code, comparison_value, unsigned_p, compare_dir)
-     enum rtx_code comparison_code;
-     rtx comparison_value;
-     int *unsigned_p;
-     int *compare_dir;
-{
-  /* Calculate the final value of the induction variable.
-     The exact final value depends on the branch operator, and increment sign.
-     This is only an approximate value.  It will be wrong if the iteration
-     variable is not incremented by one each time through the loop, and
-     approx final value - start value % increment != 0.  */
-
-  *unsigned_p = 0;
-  switch (comparison_code)
-    {
-    case LEU:
-      *unsigned_p = 1;
-    case LE:
-      *compare_dir = 1;
-      return plus_constant (comparison_value, 1);
-    case GEU:
-      *unsigned_p = 1;
-    case GE:
-      *compare_dir = -1;
-      return plus_constant (comparison_value, -1);
-    case EQ:
-      /* Can not calculate a final value for this case.  */
-      *compare_dir = 0;
-      return 0;
-    case LTU:
-      *unsigned_p = 1;
-    case LT:
-      *compare_dir = 1;
-      return comparison_value;
-      break;
-    case GTU:
-      *unsigned_p = 1;
-    case GT:
-      *compare_dir = -1;
-      return comparison_value;
-    case NE:
-      *compare_dir = 0;
-      return comparison_value;
-    default:
-      abort ();
-    }
-}
 
 /* For each biv and giv, determine whether it can be safely split into
    a different variable for each unrolled copy of the loop body.  If it
@@ -3398,6 +3346,51 @@ final_giv_value (v, loop_start, loop_end, n_iterations)
 }
 
 
+/* Look back before LOOP_START for then insn that sets REG and return
+   the equivalent constant if there is a REG_EQUAL note otherwise just
+   the SET_SRC of REG.  */
+
+static rtx
+loop_find_equiv_value (loop_start, reg)
+     rtx loop_start;
+     rtx reg;
+{
+  rtx insn, set;
+  rtx ret;
+  
+  ret = reg;
+  for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn))
+    {
+      if (GET_CODE (insn) == CODE_LABEL)
+       break;
+      
+      else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
+              && reg_set_p (reg, insn))
+       {
+         /* We found the last insn before the loop that sets the register.
+            If it sets the entire register, and has a REG_EQUAL note,
+            then use the value of the REG_EQUAL note.  */
+         if ((set = single_set (insn))
+                 && (SET_DEST (set) == reg))
+           {
+             rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+             
+             /* Only use the REG_EQUAL note if it is a constant.
+                Other things, divide in particular, will cause
+                problems later if we use them.  */
+             if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST
+                 && CONSTANT_P (XEXP (note, 0)))
+               ret = XEXP (note, 0);
+             else
+               ret = SET_SRC (set);
+           }
+         break;
+       }
+    }
+  return ret;
+}
+
+
 /* Calculate the number of loop iterations.  Returns the exact number of loop
    iterations if it can be calculated, otherwise returns zero.  */
 
@@ -3409,23 +3402,28 @@ loop_iterations (loop_start, loop_end, loop_info)
   rtx comparison, comparison_value;
   rtx iteration_var, initial_value, increment, final_value;
   enum rtx_code comparison_code;
-  HOST_WIDE_INT i;
+  HOST_WIDE_INT abs_inc;
+  unsigned HOST_WIDE_INT abs_diff;
+  int off_by_one;
   int increment_dir;
-  int unsigned_compare, compare_dir, final_larger;
-  unsigned long tempu;
+  int unsigned_p, compare_dir, final_larger;
   rtx last_loop_insn;
 
-  /* First find the iteration variable.  If the last insn is a conditional
-     branch, and the insn before tests a register value, make that the
-     iteration variable.  */
-  
+  loop_info->n_iterations = 0;
   loop_info->initial_value = 0;
-  loop_info->increment = 0;
+  loop_info->initial_equiv_value = 0;
+  loop_info->comparison_value = 0;
   loop_info->final_value = 0;
+  loop_info->final_equiv_value = 0;
+  loop_info->increment = 0;
   loop_info->iteration_var = 0;
-  loop_info->unroll_number = 2;
+  loop_info->unroll_number = 1;
 
-  /* We used to use pren_nonnote_insn here, but that fails because it might
+  /* First find the iteration variable.  If the last insn is a conditional
+     branch, and the insn before tests a register value, make that the
+     iteration variable.  */
+  
+  /* We used to use prev_nonnote_insn here, but that fails because it might
      accidentally get the branch for a contained loop if the branch for this
      loop was deleted.  We can only trust branches immediately before the
      loop_end.  */
@@ -3436,7 +3434,7 @@ loop_iterations (loop_start, loop_end, loop_info)
     {
       if (loop_dump_stream)
        fprintf (loop_dump_stream,
-                "Loop unrolling: No final conditional branch found.\n");
+                "Loop iterations: No final conditional branch found.\n");
       return 0;
     }
 
@@ -3451,7 +3449,7 @@ loop_iterations (loop_start, loop_end, loop_info)
     {
       if (loop_dump_stream)
        fprintf (loop_dump_stream,
-                "Loop unrolling: Comparison not against register.\n");
+                "Loop iterations: Comparison not against register.\n");
       return 0;
     }
 
@@ -3467,102 +3465,201 @@ loop_iterations (loop_start, loop_end, loop_info)
     /* iteration_info already printed a message.  */
     return 0;
 
+  unsigned_p = 0;
+  off_by_one = 0;
+  switch (comparison_code)
+    {
+    case LEU:
+      unsigned_p = 1;
+    case LE:
+      compare_dir = 1;
+      off_by_one = 1;
+      break;
+    case GEU:
+      unsigned_p = 1;
+    case GE:
+      compare_dir = -1;
+      off_by_one = -1;
+      break;
+    case EQ:
+      /* Cannot determine loop iterations with this case.  */
+      compare_dir = 0;
+      break;
+    case LTU:
+      unsigned_p = 1;
+    case LT:
+      compare_dir = 1;
+      break;
+    case GTU:
+      unsigned_p = 1;
+    case GT:
+      compare_dir = -1;
+    case NE:
+      compare_dir = 0;
+      break;
+    default:
+      abort ();
+    }
+
   /* If the comparison value is an invariant register, then try to find
      its value from the insns before the start of the loop.  */
 
+  final_value = comparison_value;
   if (GET_CODE (comparison_value) == REG && invariant_p (comparison_value))
     {
-      rtx insn, set;
-    
-      for (insn = PREV_INSN (loop_start); insn ; insn = PREV_INSN (insn))
-       {
-         if (GET_CODE (insn) == CODE_LABEL)
-           break;
-
-         else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
-                  && reg_set_p (comparison_value, insn))
-           {
-             /* We found the last insn before the loop that sets the register.
-                If it sets the entire register, and has a REG_EQUAL note,
-                then use the value of the REG_EQUAL note.  */
-             if ((set = single_set (insn))
-                 && (SET_DEST (set) == comparison_value))
-               {
-                 rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-
-                 /* Only use the REG_EQUAL note if it is a constant.
-                    Other things, divide in particular, will cause
-                    problems later if we use them.  */
-                 if (note && GET_CODE (XEXP (note, 0)) != EXPR_LIST
-                     && CONSTANT_P (XEXP (note, 0)))
-                   comparison_value = XEXP (note, 0);
-               }
-             break;
-           }
-       }
-    }
-
-  final_value = approx_final_value (comparison_code, comparison_value,
-                                   &unsigned_compare, &compare_dir);
+      final_value = loop_find_equiv_value (loop_start, comparison_value);
+      /* If we don't get an invariant final value, we are better
+        off with the original register.  */
+      if (!invariant_p (final_value))
+       final_value = comparison_value;
+    }
+
+  /* Calculate the approximate final value of the induction variable
+     (on the last successful iteration).  The exact final value
+     depends on the branch operator, and increment sign.  It will be
+     wrong if the iteration variable is not incremented by one each
+     time through the loop and (comparison_value + off_by_one -
+     initial_value) % increment != 0.
+     ??? Note that the final_value may overflow and thus final_larger
+     will be bogus.  A potentially infinite loop will be classified
+     as immediate, e.g. for (i = 0x7ffffff0; i <= 0x7fffffff; i++)  */
+  if (off_by_one)
+    final_value = plus_constant (final_value, off_by_one);
 
   /* Save the calculated values describing this loop's bounds, in case
      precondition_loop_p will need them later.  These values can not be
      recalculated inside precondition_loop_p because strength reduction
-     optimizations may obscure the loop's structure.  */
+     optimizations may obscure the loop's structure.  
 
-  loop_info->iteration_var = iteration_var;
+     These values are only required by precondition_loop_p and insert_bct
+     whenever the number of iterations cannot be computed at compile time.
+     Only the difference between final_value and initial_value is
+     important.  Note that final_value is only approximate.  */
   loop_info->initial_value = initial_value;
+  loop_info->comparison_value = comparison_value;
+  loop_info->final_value = plus_constant (comparison_value, off_by_one);
   loop_info->increment = increment;
-  loop_info->final_value = final_value;
+  loop_info->iteration_var = iteration_var;
   loop_info->comparison_code = comparison_code;
 
+  if (REG_P (initial_value))
+    {
+      rtx temp = final_value;
+
+      /* initial_value = reg1, final_value = reg2 + const, where reg1
+        != reg2.  Try to find what reg1 is equivalent to.  Hopefully
+        it will either be reg2 or reg2 plus a constant.  */
+      if (GET_CODE (temp) == PLUS)
+       temp = XEXP (temp, 0);
+      if (REG_P (temp) && REGNO (temp) != REGNO (initial_value))
+       initial_value = loop_find_equiv_value (loop_start, initial_value);
+    }
+
+  /* If have initial_value = reg + const1 and final_value = reg +
+     const2, then replace initial_value with const1 and final_value
+     with const2.  This should be safe since we are protected by the
+     initial comparison before entering the loop.  */
+  if ((GET_CODE (initial_value) == REG || GET_CODE (initial_value) == PLUS)
+      && (GET_CODE (final_value) == REG || GET_CODE (final_value) == PLUS))
+    {
+      rtx init_op0;
+      rtx fini_op0;
+      rtx init_op1;
+      rtx fini_op1;
+
+      if (GET_CODE (initial_value) == PLUS)
+       init_op1 = XEXP (initial_value, 1), init_op0 = XEXP (initial_value, 0);
+      else
+       init_op1 = const0_rtx, init_op0 = initial_value;
+
+      if (GET_CODE (final_value) == PLUS)
+       fini_op1 = XEXP (final_value, 1), fini_op0 = XEXP (final_value, 0);
+      else
+       fini_op1 = const0_rtx, fini_op0 = final_value;
+
+      /* Remove register common factor if present.  */
+      if (REG_P (init_op0) && init_op0 == fini_op0)
+       {
+         initial_value = init_op1;
+         final_value = fini_op1;
+       }
+      else if (REG_P (init_op0) && init_op0 == fini_op1)
+       {
+         initial_value = init_op1;
+         final_value = fini_op0;
+       }
+      else if (REG_P (init_op1) && init_op1 == fini_op0)
+       {
+         initial_value = init_op0;
+         final_value = fini_op1;
+       }
+      else if (REG_P (init_op1) && init_op1 == fini_op1)
+       {
+         initial_value = init_op0;
+         final_value = fini_op0;
+       }
+    }
+  loop_info->initial_equiv_value = initial_value;
+  loop_info->final_equiv_value = final_value;
+  
   if (increment == 0)
     {
       if (loop_dump_stream)
        fprintf (loop_dump_stream,
-                "Loop unrolling: Increment value can't be calculated.\n");
+                "Loop iterations: Increment value can't be calculated.\n");
       return 0;
     }
-  else if (GET_CODE (increment) != CONST_INT)
+
+  if (GET_CODE (increment) != CONST_INT)
     {
-      if (loop_dump_stream)
-       fprintf (loop_dump_stream,
-                "Loop unrolling: Increment value not constant.\n");
-      return 0;
+      increment = loop_find_equiv_value (loop_start, increment);
+
+      if (GET_CODE (increment) != CONST_INT)
+       {
+         if (loop_dump_stream)
+           {
+             fprintf (loop_dump_stream,
+                      "Loop iterations: Increment value not constant ");
+             print_rtl (loop_dump_stream, increment);
+             fprintf (loop_dump_stream, ".\n");
+           }
+         return 0;
+       }
+      loop_info->increment = increment;
     }
-  else if (GET_CODE (initial_value) != CONST_INT)
+
+  if (GET_CODE (initial_value) != CONST_INT)
     {
       if (loop_dump_stream)
-       fprintf (loop_dump_stream,
-                "Loop unrolling: Initial value not constant.\n");
+       {
+         fprintf (loop_dump_stream,
+                  "Loop iterations: Initial value not constant ");
+         print_rtl (loop_dump_stream, initial_value);
+         fprintf (loop_dump_stream, ".\n");
+       }
       return 0;
     }
-  else if (final_value == 0)
+  else if (comparison_code == EQ)
     {
       if (loop_dump_stream)
        fprintf (loop_dump_stream,
-                "Loop unrolling: EQ comparison loop.\n");
+                "Loop iterations: EQ comparison loop.\n");
       return 0;
     }
   else if (GET_CODE (final_value) != CONST_INT)
     {
       if (loop_dump_stream)
-       fprintf (loop_dump_stream,
-                "Loop unrolling: Final value not constant.\n");
+       {
+         fprintf (loop_dump_stream,
+                  "Loop iterations: Final value not constant ");
+         print_rtl (loop_dump_stream, final_value);
+         fprintf (loop_dump_stream, ".\n");
+       }
       return 0;
     }
 
-  /* ?? Final value and initial value do not have to be constants.
-     Only their difference has to be constant.  When the iteration variable
-     is an array address, the final value and initial value might both
-     be addresses with the same base but different constant offsets.
-     Final value must be invariant for this to work.
-
-     To do this, need some way to find the values of registers which are
-     invariant.  */
-
   /* Final_larger is 1 if final larger, 0 if they are equal, otherwise -1.  */
-  if (unsigned_compare)
+  if (unsigned_p)
     final_larger
       = ((unsigned HOST_WIDE_INT) INTVAL (final_value)
         > (unsigned HOST_WIDE_INT) INTVAL (initial_value))
@@ -3614,35 +3711,40 @@ loop_iterations (loop_start, loop_end, loop_info)
     {
       if (loop_dump_stream)
        fprintf (loop_dump_stream,
-                "Loop unrolling: Not normal loop.\n");
+                "Loop iterations: Not normal loop.\n");
       return 0;
     }
 
   /* Calculate the number of iterations, final_value is only an approximation,
-     so correct for that.  Note that tempu and loop_info->n_iterations are
+     so correct for that.  Note that abs_diff and n_iterations are
      unsigned, because they can be as large as 2^n - 1.  */
 
-  i = INTVAL (increment);
-  if (i > 0)
-    tempu = INTVAL (final_value) - INTVAL (initial_value);
-  else if (i < 0)
+  abs_inc = INTVAL (increment);
+  if (abs_inc > 0)
+    abs_diff = INTVAL (final_value) - INTVAL (initial_value);
+  else if (abs_inc < 0)
     {
-      tempu = INTVAL (initial_value) - INTVAL (final_value);
-      i = -i;
+      abs_diff = INTVAL (initial_value) - INTVAL (final_value);
+      abs_inc = -abs_inc;
     }
   else
     abort ();
 
-  /* For NE tests, make sure that the iteration variable won't miss the
-     final value.  If tempu mod i is not zero, then the iteration variable
-     will overflow before the loop exits, and we can not calculate the
-     number of iterations.  */
-  if (compare_dir == 0 && (tempu % i) != 0)
+  /* For NE tests, make sure that the iteration variable won't miss
+     the final value.  If abs_diff mod abs_incr is not zero, then the
+     iteration variable will overflow before the loop exits, and we
+     can not calculate the number of iterations.  */
+  if (compare_dir == 0 && (abs_diff % abs_inc) != 0)
     return 0;
 
-  return tempu / i + ((tempu % i) != 0);
+  /* Note that the number of iterations could be calculated using
+     (abs_diff + abs_inc - 1) / abs_inc, provided care was taken to
+     handle potential overflow of the summation.  */
+  loop_info->n_iterations = abs_diff / abs_inc + ((abs_diff % abs_inc) != 0);
+  return loop_info->n_iterations;
 }
 
+
 /* Replace uses of split bivs with their split pseudo register.  This is
    for original instructions which remain after loop unrolling without
    copying.  */