Loop patch update (Was: Re: Autoincrement examples)

Joern Rennecke amylaar@cygnus.co.uk
Fri Dec 10 16:59:00 GMT 1999


I've noticed that all the intresting examples seem to get pessimized by
loop so that no autoincrement optimization is applicable any more.

So I've updated my loop patches for the current egcs mainline:

Thu Sep  2 23:50:58 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* loop.h (struct induction): New member did_derive.
	* loop.c (strength_reduce, record_giv): Clear did_derive when
	creating giv.
	(recombine_givs): Set did_derive when deriving from a giv.
	(strength_reduce): Don't apply post-increment auto_inc_opt
	if did_derive is set.

Wed May 12 22:19:02 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* loop.h (reg_dead_after_loop): Declare.
	* unroll.c (reg_dead_after_loop): Don't declare.  No longer static.
	* loop.c (recombine_givs): If a giv is used outside the loop, use
	reg_dead_after_loop to find out if it matters.

Wed May 12 20:27:39 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* loop.c (strength_reduce): When doing biv->giv conversion, update
	reg note of NEXT->insn.  When converting to a DEST_REG giv, undo
	all changes that tried to make DEST_ADDR givs.
	* combine.c (validate_subst): Use SUBST for all substitutions.

Wed May 12 19:51:51 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* loop.c (strength_reduce): Check if a biv can be detected by a
	REG_EQUAL note attached to the biv insn.

Wed May  5 21:15:40 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* loop.h (struct induction): New members autoinc_pred, autoinc_succ,
	preinc, combine_start_limit, combine_end_limit.
	* loop.c (last_recorded_giv, last_recorded_addr_giv):
	New static variables.
	(strength_reduce): Set new fields in struct induction for givs.
	Initialize last_recorded_giv.
	(find_mem_givs): Set mem_mode before calling record_giv.
	(record_giv): Set new fields in struct induction.  Keep track of
	last_recorded_giv.
	(combine_givs_p): Supress combinations that would foil giv-giv
	autoincrement opportunities.
	(combine_givs): Propagate autoinc_{pred,succ} settings into
	combine_{start,end}_limit.
	(recombine_givs): Allow to derive givs from a non-eliminable biv.

Index: combine.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/combine.c,v
retrieving revision 1.101
diff -p -r1.101 combine.c
*** combine.c	1999/12/09 10:46:10	1.101
--- combine.c	1999/12/11 00:42:37
*************** static int combinable_i3pat	PROTO((rtx, 
*** 361,366 ****
--- 361,367 ----
  static int contains_muldiv	PROTO((rtx));
  static rtx try_combine		PROTO((rtx, rtx, rtx));
  static void undo_all		PROTO((void));
+ static void undo_last		PROTO((void));
  static void undo_commit		PROTO((void));
  static rtx *find_split_point	PROTO((rtx *, rtx));
  static rtx subst		PROTO((rtx, rtx, rtx, int, int));
*************** combine_instructions (f, nregs)
*** 738,743 ****
--- 739,745 ----
    total_successes += combine_successes;
  
    nonzero_sign_valid = 0;
+   reg_last_set_value = 0;
  
    /* Make recognizer allow volatile MEMs again.  */
    init_recog ();
*************** try_combine (i3, i2, i1)
*** 2695,2700 ****
--- 2697,2728 ----
    else
      return newi2pat ? i2 : i3;
  }
+ 
+ /* Undo all the modifications recorded in undobuf after previous_undos.  */
+ 
+ static void
+ undo_last ()
+ {
+   struct undo *undo, *next;
+ 
+   for (undo = undobuf.undos; undo != undobuf.previous_undos; undo = next)
+     {
+       next = undo->next;
+       if (undo->is_int)
+ 	*undo->where.i = undo->old_contents.i;
+       else
+ 	*undo->where.r = undo->old_contents.r;
+ 
+       undo->next = undobuf.frees;
+       undobuf.frees = undo;
+     }
+ 
+   undobuf.undos = undobuf.previous_undos;
+ 
+   /* Clear this here, so that subsequent get_last_value calls are not
+      affected.  */
+   subst_prev_insn = NULL_RTX;
+ }
  
  /* Undo all the modifications recorded in undobuf.  */
  
*************** nonzero_bits (x, mode)
*** 7790,7795 ****
--- 7818,7827 ----
  	}
  #endif
  
+       /* If called from loop, the reg_last_* arrays are not set.  */
+       if (! reg_last_set_value)
+ 	return nonzero;
+ 
        /* If X is a register whose nonzero bits value is current, use it.
  	 Otherwise, if X is a register whose value we can find, use that
  	 value.  Otherwise, use the previously-computed global nonzero bits
*************** num_sign_bit_copies (x, mode)
*** 8188,8193 ****
--- 8220,8229 ----
  	return GET_MODE_BITSIZE (Pmode) - GET_MODE_BITSIZE (ptr_mode) + 1;
  #endif
  
+       /* If called from loop, the reg_last_* arrays are not set.  */
+       if (! reg_last_set_value)
+ 	return 1;
+ 
        if (reg_last_set_value[REGNO (x)] != 0
  	  && reg_last_set_mode[REGNO (x)] == mode
  	  && (reg_last_set_label[REGNO (x)] == label_tick
*************** get_last_value (x)
*** 11217,11223 ****
        && (value = get_last_value (SUBREG_REG (x))) != 0)
      return gen_lowpart_for_combine (GET_MODE (x), value);
  
!   if (GET_CODE (x) != REG)
      return 0;
  
    regno = REGNO (x);
--- 11253,11260 ----
        && (value = get_last_value (SUBREG_REG (x))) != 0)
      return gen_lowpart_for_combine (GET_MODE (x), value);
  
!   /* If called from loop, the reg_last_* arrays are not set.  */
!   if (GET_CODE (x) != REG || ! reg_last_set_value)
      return 0;
  
    regno = REGNO (x);
*************** insn_cuid (insn)
*** 12371,12376 ****
--- 12408,12495 ----
      abort ();
  
    return INSN_CUID (insn);
+ }
+ 
+ void
+ validate_subst_start ()
+ {
+   undobuf.undos = 0;
+ 
+   /* Save the current high-water-mark so we can free storage if we didn't
+      accept this set of combinations.  */
+   undobuf.storage = (char *) oballoc (0);
+ }
+ 
+ void
+ validate_subst_undo ()
+ {
+   undo_all ();
+ }
+ 
+ /* Replace FROM with to throughout in INSN, and make simplifications.
+    Return nonzero for success.  */
+ int
+ validate_subst (insn, from, to)
+      rtx insn, from, to;
+ {
+   rtx pat, new_pat;
+   int i;
+ 
+   pat = PATTERN (insn);
+ 
+   /* If from is not mentioned in PAT, we don't need to grind it through
+      subst.  This can safe some time, and also avoids suprious failures
+      when a simplification is not recognized as a valid insn.  */
+   if (! reg_mentioned_p (from, pat))
+     {
+       /* But we still have to make sure a REG_EQUAL note gets updated.  */
+       rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ 
+       if (note)
+ 	SUBST (XEXP (note, 0), subst (XEXP (note, 0), from, to, 0, 1));
+       return 1;
+     }
+ 
+   /* We have to set previous_undos to prevent gen_rtx_combine from re-using
+      some piece of shared rtl.  */
+   undobuf.previous_undos = undobuf.undos;
+ 
+   subst_insn = insn;
+ 
+   new_pat = subst (pat, from, to, 0, 1);
+ 
+   /* If PAT is a PARALLEL, check to see if it contains the CLOBBER
+      we use to indicate that something didn't match.  If we find such a
+      thing, force rejection.
+      This is the same test as in recog_for_combine; we can't use the that
+      function here because it tries to use data flow information.  */
+   if (GET_CODE (pat) == PARALLEL)
+     for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+       if (GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER
+ 	  && XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx)
+     {
+       undo_last ();
+       return 0;
+     }
+ 
+   /* Change INSN to a nop so that validate_change is forced to re-recognize.  */
+   PATTERN (insn) = const0_rtx;
+   if (validate_change (insn, &PATTERN (insn), new_pat, 0))
+     {
+       rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
+ 
+       PATTERN (insn) = pat;
+       SUBST ( PATTERN (insn), new_pat);
+       if (note)
+ 	SUBST (XEXP (note, 0), subst (XEXP (note, 0), from, to, 0, 1));
+       return 1;
+     }
+   else
+     {
+       PATTERN (insn) = pat;
+       undo_last ();
+       return 0;
+     }
  }
  
  void
Index: loop.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/loop.c,v
retrieving revision 1.206
diff -p -r1.206 loop.c
*** loop.c	1999/12/10 15:27:55	1.206
--- loop.c	1999/12/11 00:42:40
*************** struct movable
*** 254,260 ****
  		   that the reg is live outside the range from where it is set
  		   to the following label.  */
    unsigned int done : 1;	/* 1 inhibits further processing of this */
!   
    unsigned int partial : 1;	/* 1 means this reg is used for zero-extending.
  				   In particular, moving it does not make it
  				   invariant.  */
--- 254,260 ----
  		   that the reg is live outside the range from where it is set
  		   to the following label.  */
    unsigned int done : 1;	/* 1 inhibits further processing of this */
! 
    unsigned int partial : 1;	/* 1 means this reg is used for zero-extending.
  				   In particular, moving it does not make it
  				   invariant.  */
*************** static rtx express_from_1 PROTO((rtx, rt
*** 324,331 ****
  static rtx combine_givs_p PROTO((struct induction *, struct induction *));
  static void combine_givs PROTO((struct iv_class *));
  struct recombine_givs_stats;
! static int find_life_end PROTO((rtx, struct recombine_givs_stats *, rtx, rtx));
! static void recombine_givs PROTO((struct iv_class *, rtx, rtx, int));
  static int product_cheap_p PROTO((rtx, rtx));
  static int maybe_eliminate_biv PROTO((struct iv_class *, rtx, rtx, int, int, int));
  static int maybe_eliminate_biv_1 PROTO((rtx, rtx, struct iv_class *, int, rtx));
--- 324,335 ----
  static rtx combine_givs_p PROTO((struct induction *, struct induction *));
  static void combine_givs PROTO((struct iv_class *));
  struct recombine_givs_stats;
! static void find_giv_uses PROTO((rtx,  struct recombine_givs_stats *, rtx,
! 				 rtx));
! static void note_giv_use PROTO((struct induction *, rtx, int,
! 				struct recombine_givs_stats *));
! static int cmp_giv_by_value_and_insn PROTO((struct induction **, struct induction **));
! static void recombine_givs PROTO((struct iv_class *, rtx, rtx, rtx, rtx, int));
  static int product_cheap_p PROTO((rtx, rtx));
  static int maybe_eliminate_biv PROTO((struct iv_class *, rtx, rtx, int, int, int));
  static int maybe_eliminate_biv_1 PROTO((rtx, rtx, struct iv_class *, int, rtx));
*************** scan_loop (loop_start, end, loop_cont, u
*** 753,759 ****
    /* Count number of times each reg is set during this loop.
       Set VARRAY_CHAR (may_not_optimize, I) if it is not safe to move out
       the setting of register I.  Set VARRAY_RTX (reg_single_usage, I).  */
!   
    /* Allocate extra space for REGS that might be created by
       load_mems.  We allocate a little extra slop as well, in the hopes
       that even after the moving of movables creates some new registers
--- 757,763 ----
    /* Count number of times each reg is set during this loop.
       Set VARRAY_CHAR (may_not_optimize, I) if it is not safe to move out
       the setting of register I.  Set VARRAY_RTX (reg_single_usage, I).  */
! 
    /* Allocate extra space for REGS that might be created by
       load_mems.  We allocate a little extra slop as well, in the hopes
       that even after the moving of movables creates some new registers
*************** record_excess_regs (in_this, not_in_this
*** 1222,1228 ****
  	  && ! reg_mentioned_p (in_this, not_in_this))
  	*output = gen_rtx_EXPR_LIST (VOIDmode, in_this, *output);
        return;
!       
      default:
        break;
      }
--- 1226,1232 ----
  	  && ! reg_mentioned_p (in_this, not_in_this))
  	*output = gen_rtx_EXPR_LIST (VOIDmode, in_this, *output);
        return;
! 
      default:
        break;
      }
*************** replace_call_address (x, reg, addr)
*** 2296,2302 ****
  	abort ();
        XEXP (x, 0) = addr;
        return;
!       
      default:
        break;
      }
--- 2300,2306 ----
  	abort ();
        XEXP (x, 0) = addr;
        return;
! 
      default:
        break;
      }
*************** count_nonfixed_reads (x)
*** 2347,2353 ****
      case MEM:
        return ((invariant_p (XEXP (x, 0)) != 1)
  	      + count_nonfixed_reads (XEXP (x, 0)));
!       
      default:
        break;
      }
--- 2351,2357 ----
      case MEM:
        return ((invariant_p (XEXP (x, 0)) != 1)
  	      + count_nonfixed_reads (XEXP (x, 0)));
! 
      default:
        break;
      }
*************** invariant_p (x)
*** 3341,3347 ****
        if (MEM_VOLATILE_P (x))
  	return 0;
        break;
!       
      default:
        break;
      }
--- 3345,3351 ----
        if (MEM_VOLATILE_P (x))
  	return 0;
        break;
! 
      default:
        break;
      }
*************** static rtx note_insn;
*** 3710,3715 ****
--- 3714,3726 ----
  
  static rtx addr_placeholder;
  
+ /* The last giv we have seen since we passed a CODE_LABEL.  Used to
+    find places where auto-increment is useful to generate a DEST_ADDR
+    giv from a giv with the same mult_val but different add_val.
+    We must suppress some giv combinations to allow these auto_increments
+    to be formed.  */
+ static struct induction *last_recorded_giv, *last_recorded_addr_giv;
+ 
  /* ??? Unfinished optimizations, and possible future optimizations,
     for the strength reduction code.  */
  
*************** static rtx addr_placeholder;
*** 3744,3750 ****
     This does not cause a problem here, because the added registers cannot be
     givs outside of their loop, and hence will never be reconsidered.
     But scan_loop must check regnos to make sure they are in bounds. 
!    
     SCAN_START is the first instruction in the loop, as the loop would
     actually be executed.  END is the NOTE_INSN_LOOP_END.  LOOP_TOP is
     the first instruction in the loop, as it is layed out in the
--- 3755,3761 ----
     This does not cause a problem here, because the added registers cannot be
     givs outside of their loop, and hence will never be reconsidered.
     But scan_loop must check regnos to make sure they are in bounds. 
! 
     SCAN_START is the first instruction in the loop, as the loop would
     actually be executed.  END is the NOTE_INSN_LOOP_END.  LOOP_TOP is
     the first instruction in the loop, as it is layed out in the
*************** strength_reduce (scan_start, end, loop_t
*** 3838,3847 ****
  	      && REG_IV_TYPE (REGNO (dest_reg)) != NOT_BASIC_INDUCT)
  	    {
  	      int multi_insn_incr = 0;
  
! 	      if (basic_induction_var (SET_SRC (set), GET_MODE (SET_SRC (set)),
! 				       dest_reg, p, &inc_val, &mult_val,
! 				       &location, &multi_insn_incr))
  		{
  		  /* It is a possible basic induction variable.
  		     Create and initialize an induction structure for it.  */
--- 3849,3863 ----
  	      && REG_IV_TYPE (REGNO (dest_reg)) != NOT_BASIC_INDUCT)
  	    {
  	      int multi_insn_incr = 0;
+ 	      enum machine_mode mode = GET_MODE (SET_SRC (set));
+ 	      rtx note = find_reg_note (p, REG_EQUAL, 0);
  
! 	      if (basic_induction_var (SET_SRC (set), mode, dest_reg, p,
! 		  &inc_val, &mult_val, &location, &multi_insn_incr)
! 		  || (note
! 		      && basic_induction_var (XEXP (note, 0), mode, dest_reg, p,
! 					      &inc_val, &mult_val, &location,
! 					      &multi_insn_incr)))
  		{
  		  /* It is a possible basic induction variable.
  		     Create and initialize an induction structure for it.  */
*************** strength_reduce (scan_start, end, loop_t
*** 4180,4185 ****
--- 4196,4207 ----
  
  	      if (loop_dump_stream)
  		fprintf (loop_dump_stream, "is giv of biv %d\n", bl2->regno);
+ 
+ 	      /* If the changed insn carries a REG_EQUAL note, update it.  */
+ 	      note = find_reg_note (bl->biv->insn, REG_EQUAL, NULL_RTX);
+ 	      if (note)
+ 		XEXP (note, 0) = copy_rtx (src);
+ 
  	      /* Let this giv be discovered by the generic code.  */
  	      REG_IV_TYPE (bl->regno) = UNKNOWN_INDUCT;
  	      reg_biv_class[bl->regno] = NULL_PTR;
*************** strength_reduce (scan_start, end, loop_t
*** 4315,4321 ****
  	  for (vp = &bl->biv, next = *vp; v = next, next = v->next_iv;)
  	    {
  	      HOST_WIDE_INT offset;
! 	      rtx set, add_val, old_reg, dest_reg, last_use_insn, note;
  	      int old_regno, new_regno;
  
  	      if (! v->always_executed
--- 4337,4343 ----
  	  for (vp = &bl->biv, next = *vp; v = next, next = v->next_iv;)
  	    {
  	      HOST_WIDE_INT offset;
! 	      rtx set, src, add_val, old_reg, dest_reg, last_use_insn, note;
  	      int old_regno, new_regno;
  
  	      if (! v->always_executed
*************** strength_reduce (scan_start, end, loop_t
*** 4337,4344 ****
  	      add_val = plus_constant (next->add_val, offset);
  	      old_reg = v->dest_reg;
  	      dest_reg = gen_reg_rtx (v->mode);
!     
! 	      /* Unlike reg_iv_type / reg_iv_info, the other three arrays
  		 have been allocated with some slop space, so we may not
  		 actually need to reallocate them.  If we do, the following
  		 if statement will be executed just once in this loop.  */
--- 4359,4368 ----
  	      add_val = plus_constant (next->add_val, offset);
  	      old_reg = v->dest_reg;
  	      dest_reg = gen_reg_rtx (v->mode);
! 	      old_regno = REGNO (old_reg);
! 	      new_regno = REGNO (dest_reg);
! 
! 	      /* Unlike reg_iv_type / reg_iv_info, the other four arrays
  		 have been allocated with some slop space, so we may not
  		 actually need to reallocate them.  If we do, the following
  		 if statement will be executed just once in this loop.  */
*************** strength_reduce (scan_start, end, loop_t
*** 4350,4395 ****
  		  VARRAY_GROW (may_not_optimize, nregs);
  		  VARRAY_GROW (reg_single_usage, nregs);
  		}
!     
  	      if (! validate_change (next->insn, next->location, add_val, 0))
  		{
  		  vp = &v->next_iv;
  		  continue;
  		}
- 
- 	      /* Here we can try to eliminate the increment by combining
- 		 it into the uses.  */
- 
- 	      /* Set last_use_insn so that we can check against it.  */
  
! 	      for (last_use_insn = v->insn, p = NEXT_INSN (v->insn);
! 		   p != next->insn;
! 		   p = next_insn_in_loop (p, scan_start, end, loop_top))
  		{
  		  if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
  		    continue;
! 		  if (reg_mentioned_p (old_reg, PATTERN (p)))
! 		    {
! 		      last_use_insn = p;
! 		    }
  		}
! 
! 	      /* If we can't get the LUIDs for the insns, we can't
! 		 calculate the lifetime.  This is likely from unrolling
! 		 of an inner loop, so there is little point in making this
! 		 a DEST_REG giv anyways.  */
! 	      if (INSN_UID (v->insn) >= max_uid_for_loop
! 		  || INSN_UID (last_use_insn) >= max_uid_for_loop
! 		  || ! validate_change (v->insn, &SET_DEST (set), dest_reg, 0))
  		{
  		  /* Change the increment at NEXT back to what it was.  */
  		  if (! validate_change (next->insn, next->location,
  		      next->add_val, 0))
  		    abort ();
  		  vp = &v->next_iv;
  		  continue;
  		}
  	      next->add_val = add_val;
  	      v->dest_reg = dest_reg;
  	      v->giv_type = DEST_REG;
  	      v->location = &SET_SRC (set);
--- 4374,4465 ----
  		  VARRAY_GROW (may_not_optimize, nregs);
  		  VARRAY_GROW (reg_single_usage, nregs);
  		}
! 	      VARRAY_CHAR (may_not_optimize, new_regno) = 0;
! 
  	      if (! validate_change (next->insn, next->location, add_val, 0))
  		{
  		  vp = &v->next_iv;
  		  continue;
  		}
  
! 	      src = SET_SRC (set);
! 	      /* Try to replace all uses of OLD_REG with SRC.  This will
! 		 mostly win when it generates / changes address givs, but it
! 		 might also change some DEST_REG givs or create the odd
! 		 PEA on an 68k.  */
! 	      last_use_insn = NULL_RTX;
! 	      validate_subst_start ();
! 	      for (p = NEXT_INSN (v->insn); p != next->insn; p = NEXT_INSN (p))
  		{
+ 		  rtx newpat;
+ 
  		  if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
  		    continue;
! 		  if (! validate_subst (p, old_reg, src))
! 		    last_use_insn = p;
  		}
! 	      /* If some uses remain, we'd like to make this a DEST_REG
! 		 giv.  However, after loop unrolling, V->INSN or LAST_USE_INSN
! 		 might have no valid luid.  We need these not only for
! 		 calculating the lifetime now, but also in recombine_givs when
! 		 doing giv derivation, to find givs with non-overlapping
! 		 lifetimes.  So if we don't have LUIDs available, or if we
! 		 can't calculate the giv, leave the biv increment alone.  */
! 	      if (last_use_insn
! 		  && (INSN_UID (v->insn) >= max_uid_for_loop
! 		      || INSN_UID (last_use_insn) >= max_uid_for_loop
! 		      || ! validate_change (v->insn, &SET_DEST (set),
! 					    dest_reg, 0)))
  		{
  		  /* Change the increment at NEXT back to what it was.  */
  		  if (! validate_change (next->insn, next->location,
  		      next->add_val, 0))
  		    abort ();
+ 
+ 		  /* Undo all the substitutions made by validate_subst above,
+ 		     since the biv does hold the incremented value after
+ 		     all.  */
+ 		  validate_subst_undo ();
+ 
  		  vp = &v->next_iv;
  		  continue;
  		}
+ 
+ 	      /* If we have to make a DEST_REG giv, undo all the
+ 		 substitutions made by validate_subst above, since we are
+ 		 going to replace the biv by a DEST_REG giv.  We must do this
+ 		 before allocating anything more on obstack, e.g. with
+ 		 copy_rtx.  */
+ 	      if (last_use_insn)
+ 		validate_subst_undo ();
+ 
+ 	      /* If next_insn has a REG_EQUAL note that mentiones OLD_REG,
+ 		 it must be replaced.  */
+ 	      note = find_reg_note (next->insn, REG_EQUAL, NULL_RTX);
+ 	      if (note && reg_mentioned_p (old_reg, XEXP (note, 0)))
+ 		XEXP (note, 0) = copy_rtx (SET_SRC (single_set (next->insn)));
+ 
+ 	      /* Remove the increment from the list of biv increments.  */
+ 	      *vp = next;
+ 	      bl->biv_count--;
+ 	      VARRAY_INT (set_in_loop, old_regno)--;
+ 	      VARRAY_INT (n_times_set, old_regno)--;
  	      next->add_val = add_val;
+ 
+ 	      if (! last_use_insn)
+ 		{
+ 		  if (loop_dump_stream)
+ 		    fprintf (loop_dump_stream,
+ 			     "Increment %d of biv %d eliminated.\n\n",
+ 			 INSN_UID (v->insn), old_regno);
+ 		  PUT_CODE (v->insn, NOTE);
+ 		  NOTE_LINE_NUMBER (v->insn) = NOTE_INSN_DELETED;
+ 		  NOTE_SOURCE_FILE (v->insn) = 0;
+ 		  VARRAY_INT (set_in_loop, new_regno) = 0;
+ 		  VARRAY_INT (n_times_set, new_regno) = 0;
+ 		  continue;
+ 		}
+ 
  	      v->dest_reg = dest_reg;
  	      v->giv_type = DEST_REG;
  	      v->location = &SET_SRC (set);
*************** strength_reduce (scan_start, end, loop_t
*** 4406,4443 ****
  	      v->unrolled = 0;
  	      v->shared = 0;
  	      v->derived_from = 0;
  	      v->always_computable = 1;
  	      v->always_executed = 1;
  	      v->replaceable = 1;
  	      v->no_const_addval = 0;
!     
! 	      old_regno = REGNO (old_reg);
! 	      new_regno = REGNO (dest_reg);
! 	      VARRAY_INT (set_in_loop, old_regno)--;
  	      VARRAY_INT (set_in_loop, new_regno) = 1;
- 	      VARRAY_INT (n_times_set, old_regno)--;
  	      VARRAY_INT (n_times_set, new_regno) = 1;
! 	      VARRAY_CHAR (may_not_optimize, new_regno) = 0;
!     
  	      REG_IV_TYPE (new_regno) = GENERAL_INDUCT;
  	      REG_IV_INFO (new_regno) = v;
- 
- 	      /* If next_insn has a REG_EQUAL note that mentiones OLD_REG,
- 		 it must be replaced.  */
- 	      note = find_reg_note (next->insn, REG_EQUAL, NULL_RTX);
- 	      if (note && reg_mentioned_p (old_reg, XEXP (note, 0)))
- 		XEXP (note, 0) = copy_rtx (SET_SRC (single_set (next->insn)));
  
! 	      /* Remove the increment from the list of biv increments,
! 		 and record it as a giv.  */
! 	      *vp = next;
! 	      bl->biv_count--;
  	      v->next_iv = bl->giv;
  	      bl->giv = v;
  	      bl->giv_count++;
  	      v->benefit = rtx_cost (SET_SRC (set), SET);
  	      bl->total_benefit += v->benefit;
!     
  	      /* Now replace the biv with DEST_REG in all insns between
  		 the replaced increment and the next increment, and
  		 remember the last insn that needed a replacement.  */
--- 4476,4506 ----
  	      v->unrolled = 0;
  	      v->shared = 0;
  	      v->derived_from = 0;
+ 	      v->did_derive = 0;
+ 	      v->combine_start_limit = 0;
+ 	      v->combine_end_limit = 0;
  	      v->always_computable = 1;
  	      v->always_executed = 1;
  	      v->replaceable = 1;
  	      v->no_const_addval = 0;
! 	      v->autoinc_pred = 0;
! 	      v->autoinc_succ = 0;
! 	      v->preinc = 0;
! 	      v->leading_combined = 0;
! 
  	      VARRAY_INT (set_in_loop, new_regno) = 1;
  	      VARRAY_INT (n_times_set, new_regno) = 1;
! 
  	      REG_IV_TYPE (new_regno) = GENERAL_INDUCT;
  	      REG_IV_INFO (new_regno) = v;
  
! 	      /* Record V as a giv.  */
  	      v->next_iv = bl->giv;
  	      bl->giv = v;
  	      bl->giv_count++;
  	      v->benefit = rtx_cost (SET_SRC (set), SET);
  	      bl->total_benefit += v->benefit;
! 
  	      /* Now replace the biv with DEST_REG in all insns between
  		 the replaced increment and the next increment, and
  		 remember the last insn that needed a replacement.  */
*************** strength_reduce (scan_start, end, loop_t
*** 4446,4452 ****
  		   p = next_insn_in_loop (p, scan_start, end, loop_top))
  		{
  		  rtx note;
!     
  		  if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
  		    continue;
  		  if (reg_mentioned_p (old_reg, PATTERN (p)))
--- 4509,4515 ----
  		   p = next_insn_in_loop (p, scan_start, end, loop_top))
  		{
  		  rtx note;
! 
  		  if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
  		    continue;
  		  if (reg_mentioned_p (old_reg, PATTERN (p)))
*************** strength_reduce (scan_start, end, loop_t
*** 4462,4468 ****
  			  = replace_rtx (XEXP (note, 0), old_reg, dest_reg);
  		    }
  		}
!     
  	      v->last_use = last_use_insn;
  	      v->lifetime = INSN_LUID (v->insn) - INSN_LUID (last_use_insn);
  	      /* If the lifetime is zero, it means that this register is really
--- 4525,4531 ----
  			  = replace_rtx (XEXP (note, 0), old_reg, dest_reg);
  		    }
  		}
! 
  	      v->last_use = last_use_insn;
  	      v->lifetime = INSN_LUID (v->insn) - INSN_LUID (last_use_insn);
  	      /* If the lifetime is zero, it means that this register is really
*************** strength_reduce (scan_start, end, loop_t
*** 4488,4493 ****
--- 4551,4557 ----
    not_every_iteration = 0;
    loop_depth = 0;
    maybe_multiple = 0;
+   last_recorded_giv = last_recorded_addr_giv = 0;
    p = scan_start;
    while (1)
      {
*************** strength_reduce (scan_start, end, loop_t
*** 4680,4685 ****
--- 4744,4752 ----
  	  && no_labels_between_p (p, loop_end)
  	  && loop_insn_first_p (p, loop_cont))
  	not_every_iteration = 0;
+ 
+       if (GET_CODE (p) == CODE_LABEL)
+ 	last_recorded_giv = last_recorded_addr_giv = 0;
      }
  
    /* Try to calculate and save the number of loop iterations.  This is
*************** strength_reduce (scan_start, end, loop_t
*** 4905,4911 ****
  
        /* Now that we know which givs will be reduced, try to rearrange the
           combinations to reduce register pressure.
!          recombine_givs calls find_life_end, which needs reg_iv_type and
  	 reg_iv_info to be valid for all pseudos.  We do the necessary
  	 reallocation here since it allows to check if there are still
  	 more bivs to process.  */
--- 4972,4978 ----
  
        /* Now that we know which givs will be reduced, try to rearrange the
           combinations to reduce register pressure.
!          recombine_givs calls find_giv_uses, which needs reg_iv_type and
  	 reg_iv_info to be valid for all pseudos.  We do the necessary
  	 reallocation here since it allows to check if there are still
  	 more bivs to process.  */
*************** strength_reduce (scan_start, end, loop_t
*** 4920,4926 ****
  	  VARRAY_GROW (reg_iv_type, nregs);
  	  VARRAY_GROW (reg_iv_info, nregs);
  	}
!       recombine_givs (bl, loop_start, loop_end, unroll_p);
  
        /* Reduce each giv that we decided to reduce.  */
  
--- 4987,4993 ----
  	  VARRAY_GROW (reg_iv_type, nregs);
  	  VARRAY_GROW (reg_iv_info, nregs);
  	}
!       recombine_givs (bl, scan_start, loop_start, loop_end, loop_top, unroll_p);
  
        /* Reduce each giv that we decided to reduce.  */
  
*************** strength_reduce (scan_start, end, loop_t
*** 4931,4979 ****
  	    {
  	      int auto_inc_opt = 0;
  
! 	      /* If the code for derived givs immediately below has already
  		 allocated a new_reg, we must keep it.  */
  	      if (! v->new_reg)
  		v->new_reg = gen_reg_rtx (v->mode);
  
  	      if (v->derived_from)
! 		{
! 		  struct induction *d = v->derived_from;
! 
! 		  /* In case d->dest_reg is not replaceable, we have
! 		     to replace it in v->insn now.  */
! 		  if (! d->new_reg)
! 		    d->new_reg = gen_reg_rtx (d->mode);
! 		  PATTERN (v->insn)
! 		    = replace_rtx (PATTERN (v->insn), d->dest_reg, d->new_reg);
! 		  PATTERN (v->insn)
! 		    = replace_rtx (PATTERN (v->insn), v->dest_reg, v->new_reg);
! 		  /* For each place where the biv is incremented, add an
! 		     insn to set the new, reduced reg for the giv.
! 		     We used to do this only for biv_count != 1, but
! 		     this fails when there is a giv after a single biv
! 		     increment, e.g. when the last giv was expressed as
! 		     pre-decrement.  */
! 		  for (tv = bl->biv; tv; tv = tv->next_iv)
! 		    {
! 		      /* We always emit reduced giv increments before the
! 			 biv increment when bl->biv_count != 1.  So by
! 			 emitting the add insns for derived givs after the
! 			 biv increment, they pick up the updated value of
! 			 the reduced giv.
! 			 If the reduced giv is processed with
! 			 auto_inc_opt == 1, then it is incremented earlier
! 			 than the biv, hence we'll still pick up the right
! 			 value.
! 			 If it's processed with auto_inc_opt == -1,
! 			 that implies that the biv increment is before the
! 			 first reduced giv's use.  The derived giv's lifetime
! 			 is after the reduced giv's lifetime, hence in this
! 			 case, the biv increment doesn't matter.  */
! 		      emit_insn_after (copy_rtx (PATTERN (v->insn)), tv->insn);
! 		    }
! 		  continue;
! 		}
  
  #ifdef AUTO_INC_DEC
  	      /* If the target has auto-increment addressing modes, and
--- 4998,5010 ----
  	    {
  	      int auto_inc_opt = 0;
  
! 	      /* If the code for derived givs in recombine_givs has already
  		 allocated a new_reg, we must keep it.  */
  	      if (! v->new_reg)
  		v->new_reg = gen_reg_rtx (v->mode);
  
  	      if (v->derived_from)
! 		continue;
  
  #ifdef AUTO_INC_DEC
  	      /* If the target has auto-increment addressing modes, and
*************** strength_reduce (scan_start, end, loop_t
*** 5032,5037 ****
--- 5063,5073 ----
  		  else
  		    auto_inc_opt = 1;
  
+ 		  /* We can't put an insn after v->insn if v was used to
+ 		     derive other givs in recombine_givs.  */
+ 		  if (auto_inc_opt == 1 && v->did_derive)
+ 		    auto_inc_opt = 0;
+ 
  #ifdef HAVE_cc0
  		  {
  		    rtx prev;
*************** strength_reduce (scan_start, end, loop_t
*** 5065,5070 ****
--- 5101,5115 ----
  		  else
  		    insert_before = v->insn;
  
+ 		  /* If the biv was recognized from a REG_EQUAL note, we
+ 		     can have the special case that the giv is used in the
+ 		     biv increment.  Then the giv increment must be put
+ 		     after the biv increment, which is typically actually
+ 		     a copy of the giv into the biv.  */
+ 		  if (reg_overlap_mentioned_p (v->dest_reg,
+ 					       SET_SRC (single_set (tv->insn))))
+ 		    insert_before = NEXT_INSN (tv->insn);
+ 
  		  if (tv->mult_val == const1_rtx)
  		    emit_iv_add_mult (tv->add_val, v->mult_val,
  				      v->new_reg, v->new_reg, insert_before);
*************** strength_reduce (scan_start, end, loop_t
*** 5311,5317 ****
        if (unrolled_insn_copies < 0)
  	unrolled_insn_copies = 0;
      }
!   
    /* Unroll loops from within strength reduction so that we can use the
       induction variable information that strength_reduce has already
       collected.  Always unroll loops that would be as small or smaller
--- 5356,5362 ----
        if (unrolled_insn_copies < 0)
  	unrolled_insn_copies = 0;
      }
! 
    /* Unroll loops from within strength reduction so that we can use the
       induction variable information that strength_reduce has already
       collected.  Always unroll loops that would be as small or smaller
*************** find_mem_givs (x, insn, not_every_iterat
*** 5436,5446 ****
  	    struct induction *v
  	      = (struct induction *) oballoc (sizeof (struct induction));
  
  	    record_giv (v, insn, src_reg, addr_placeholder, mult_val,
  			add_val, benefit, DEST_ADDR, not_every_iteration,
  			maybe_multiple, &XEXP (x, 0), loop_start, loop_end);
- 
- 	    v->mem_mode = GET_MODE (x);
  	  }
        }
        return;
--- 5481,5490 ----
  	    struct induction *v
  	      = (struct induction *) oballoc (sizeof (struct induction));
  
+ 	    v->mem_mode = GET_MODE (x);
  	    record_giv (v, insn, src_reg, addr_placeholder, mult_val,
  			add_val, benefit, DEST_ADDR, not_every_iteration,
  			maybe_multiple, &XEXP (x, 0), loop_start, loop_end);
  	  }
        }
        return;
*************** record_giv (v, insn, src_reg, dest_reg, 
*** 5622,5629 ****
--- 5666,5680 ----
    v->auto_inc_opt = 0;
    v->unrolled = 0;
    v->shared = 0;
+   v->autoinc_pred = 0;
+   v->autoinc_succ = 0;
+   v->preinc = 0;
+   v->leading_combined = 0;
    v->derived_from = 0;
+   v->did_derive = 0;
    v->last_use = 0;
+   v->combine_start_limit = 0;
+   v->combine_end_limit = 0;
  
    /* The v->always_computable field is used in update_giv_derive, to
       determine whether a giv can be used to derive another giv.  For a
*************** record_giv (v, insn, src_reg, dest_reg, 
*** 5774,5779 ****
--- 5825,5894 ----
        }
    }
  
+ #ifdef AUTO_INC_DEC
+   if (last_recorded_addr_giv
+       && last_recorded_addr_giv->src_reg == src_reg
+       && rtx_equal_p (last_recorded_addr_giv->mult_val, mult_val)
+       && GET_CODE (add_val) == CONST_INT)
+     {
+       /* Check if changing the previous giv to post-increment would allow
+ 	 to generate the value of the current giv.  */
+       if (! last_recorded_addr_giv->preinc
+ 	  && ((HAVE_POST_INCREMENT
+ 	       && (INTVAL (add_val) - INTVAL (last_recorded_addr_giv->add_val)
+ 		   == GET_MODE_SIZE (last_recorded_addr_giv->mem_mode)))
+ 	      || (HAVE_POST_DECREMENT
+ 		  && ((INTVAL (add_val)
+ 		       - INTVAL (last_recorded_addr_giv->add_val))
+ 		      == -GET_MODE_SIZE (last_recorded_addr_giv->mem_mode))))
+ 	  && ! (combine_givs_p (last_recorded_addr_giv, v)
+ 		|| combine_givs_p (v, last_recorded_addr_giv)))
+ 	{
+ 	  last_recorded_addr_giv->autoinc_pred = 1;
+ 	  v->autoinc_succ = 1;
+ 	  /* Record only one autoinc opportunity for LAST_RECORDED_ADDR_GIV.  */
+ 	  last_recorded_addr_giv = 0;
+ 	}
+       else if ((HAVE_PRE_INCREMENT
+ 	   && type == DEST_ADDR
+ 	   && (INTVAL (add_val) - INTVAL (last_recorded_addr_giv->add_val)
+ 	       == GET_MODE_SIZE (v->mem_mode)))
+ 	  || (HAVE_PRE_DECREMENT
+ 	      && type == DEST_ADDR
+ 	      && (INTVAL (add_val) - INTVAL (last_recorded_addr_giv->add_val)
+ 		  == -GET_MODE_SIZE (v->mem_mode))))
+ 	{
+ 	  struct induction *succ = v;
+ 
+ 	  if (last_recorded_giv->giv_type == DEST_REG
+ 	      && rtx_equal_p (last_recorded_giv->add_val, v->add_val)
+ 	      && rtx_equal_p (last_recorded_giv->mult_val, v->mult_val))
+ 	    succ = last_recorded_giv;
+ 	  last_recorded_addr_giv->autoinc_pred = 1;
+ 	  succ->autoinc_succ = 1;
+ 	  v->preinc = 1;
+ 	  /* Record only one autoinc opportunity for LAST_RECORDED_ADDR_GIV.  */
+ 	  last_recorded_addr_giv = 0;
+ 	}
+     }
+ 
+   last_recorded_giv = v;
+ 
+   /* Only record DEST_ADDR givs as such for following auto_increment tests
+      if we can use them at all.  */
+   if (type == DEST_ADDR
+       && GET_CODE (add_val) == CONST_INT
+       /* And only if it's likely to be useful.  The typical case uses a
+ 	 structure, sub-array or several array members each iteration, so
+ 	 we should see an increment that is larger than the individual
+ 	 access size.  */
+       && (GET_CODE (reg_biv_class[REGNO (src_reg)]->biv->add_val) != CONST_INT
+ 	  || (abs (INTVAL (v->mult_val)
+ 		   * INTVAL (reg_biv_class[REGNO (src_reg)]->biv->add_val))
+ 	      > GET_MODE_SIZE (v->mem_mode))))
+     last_recorded_addr_giv = v;
+ #endif
+ 
    if (loop_dump_stream)
      {
        if (type == DEST_REG)
*************** check_final_value (v, loop_start, loop_e
*** 5915,5921 ****
  		last_giv_use = p;
  	    }
  	}
!       
        /* Now that the lifetime of the giv is known, check for branches
  	 from within the lifetime to outside the lifetime if it is still
  	 replaceable.  */
--- 6030,6036 ----
  		last_giv_use = p;
  	    }
  	}
! 
        /* Now that the lifetime of the giv is known, check for branches
  	 from within the lifetime to outside the lifetime if it is still
  	 replaceable.  */
*************** general_induction_var (x, src_reg, add_v
*** 6281,6289 ****
--- 6396,6406 ----
    rtx orig_x = x;
    char *storage;
  
+ #if 0 /* Invariants are useful to derive other givs from.  */
    /* If this is an invariant, forget it, it isn't a giv.  */
    if (invariant_p (x) == 1)
      return 0;
+ #endif
  
    /* See if the expression could be a giv and get its form.
       Mark our place on the obstack in case we don't find a giv.  */
*************** simplify_giv_expr (x, benefit)
*** 6636,6646 ****
  	    *benefit += v->benefit;
  	    if (v->cant_derive)
  	      return 0;
- 
- 	    tem = gen_rtx_PLUS (mode, gen_rtx_MULT (mode,
- 						    v->src_reg, v->mult_val),
- 				v->add_val);
  
  	    if (v->derive_adjustment)
  	      tem = gen_rtx_MINUS (mode, tem, v->derive_adjustment);
  	    return simplify_giv_expr (tem, benefit);
--- 6753,6765 ----
  	    *benefit += v->benefit;
  	    if (v->cant_derive)
  	      return 0;
  
+ 	    if (v->mult_val != const0_rtx)
+ 	      tem = gen_rtx_PLUS (mode, gen_rtx_MULT (mode,
+ 						      v->src_reg, v->mult_val),
+ 				  v->add_val);
+ 	    else
+ 	      tem = v->add_val;
  	    if (v->derive_adjustment)
  	      tem = gen_rtx_MINUS (mode, tem, v->derive_adjustment);
  	    return simplify_giv_expr (tem, benefit);
*************** express_from (g1, g2)
*** 7076,7085 ****
  	  mult = gen_rtx_PLUS (g2->mode, mult, XEXP (add, 0));
  	  add = tem;
  	}
!       
        return gen_rtx_PLUS (g2->mode, mult, add);
      }
!   
  }
  
  /* Return an rtx, if any, that expresses giv G2 as a function of the register
--- 7195,7204 ----
  	  mult = gen_rtx_PLUS (g2->mode, mult, XEXP (add, 0));
  	  add = tem;
  	}
! 
        return gen_rtx_PLUS (g2->mode, mult, add);
      }
! 
  }
  
  /* Return an rtx, if any, that expresses giv G2 as a function of the register
*************** combine_givs_p (g1, g2)
*** 7101,7106 ****
--- 7220,7241 ----
    if (tem == g1->dest_reg
        && (g1->giv_type == DEST_REG || g2->giv_type == DEST_ADDR))
      {
+       /* Don't combine if this would prevent an autoinc opportunity.
+ 	 We only do this check in if both givs are the same; if they
+ 	 can be combined even though they are different, then it is likely
+ 	 that the putative autoinc-pair can be combined without an
+ 	 autoincrement, too.  We don't want to prevent that combination.  */
+ #ifdef AUTO_INC_DEC
+       if ((g1->combine_start_limit
+ 	   && loop_insn_first_p (g2->insn, g1->combine_start_limit))
+ 	  || (g1->combine_end_limit
+ 	      && loop_insn_first_p (g1->combine_end_limit, g2->insn))
+ 	  || (g2->combine_start_limit
+ 	      && loop_insn_first_p (g1->insn, g2->combine_start_limit))
+ 	  || (g2->combine_end_limit
+ 	      && loop_insn_first_p (g2->combine_end_limit, g1->insn)))
+ 	return 0;
+ #endif
        return g1->dest_reg;
      }
  
*************** cmp_combine_givs_stats (xp, yp)
*** 7151,7156 ****
--- 7286,7312 ----
    return d;
  }
  
+ static int
+ cmp_giv_by_value_and_insn (xp, yp)
+      struct induction **xp, **yp;
+ {
+   struct induction *x = *xp, *y = *yp;
+   HOST_WIDE_INT d;
+ 
+   d = (int) GET_CODE (x->mult_val) - (int) GET_CODE (y->mult_val);
+   if (! d && GET_CODE (x->mult_val) == CONST_INT)
+     d = INTVAL (x->mult_val) - INTVAL (y->mult_val);
+   if (! d)
+     d = (int) GET_CODE (x->add_val) - (int) GET_CODE (y->add_val);
+   if (! d && GET_CODE (x->add_val) == CONST_INT)
+     d = INTVAL (x->add_val) - INTVAL (y->add_val);
+   if (d)
+     return d < 0 ? -1 : 1;
+   if (x->insn == y->insn)
+     return xp - yp;
+   return loop_insn_first_p (x->insn, y->insn) ? -1 : 1;
+ }
+ 
  /* Check all pairs of givs for iv_class BL and see if any can be combined with
     any other.  If so, point SAME to the giv combined with and set NEW_REG to
     be an expression (in terms of the other giv's DEST_REG) equivalent to the
*************** combine_givs (bl)
*** 7176,7186 ****
--- 7332,7393 ----
  
    giv_array
      = (struct induction **) alloca (giv_count * sizeof (struct induction *));
+ 
+ #ifdef AUTO_INC_DEC
+   /* Order givs by mult_val / add_val / position in insn stream.  */
    i = 0;
    for (g1 = bl->giv; g1; g1 = g1->next_iv)
      if (!g1->ignore)
        giv_array[i++] = g1;
  
+   qsort (giv_array, giv_count, sizeof(*giv_array), cmp_giv_by_value_and_insn);
+ 
+   /* Go through givs forward in insn stream order, set combine_start_limit
+      for giv that flag autoinc_succ or follow one with matching
+      add_val/mult_val that flags it.  */
+   g1 = NULL_PTR;
+   for (i = 0; i < giv_count; i++)
+     {
+       g2 = giv_array[i];
+       if (g2->autoinc_succ)
+ 	g1 = g2;
+       else if (! g1)
+ 	continue;
+       else if (! rtx_equal_p (g1->mult_val, g2->mult_val)
+ 	       || ! rtx_equal_p (g1->add_val, g2->add_val))
+ 	{
+ 	  g1 = 0;
+ 	  continue;
+ 	}
+       g2->combine_start_limit = g1->insn;
+     }
+ 
+   /* Go through givs backward in insn stream order, set combine_end_limit
+      for giv that flag autoinc_pred or follow one with matching
+      add_val/mult_val that flags it.  */
+   g1 = NULL_PTR;
+   for (i = giv_count - 1; i >= 0; i--)
+     {
+       g2 = giv_array[i];
+       if (g2->autoinc_pred)
+ 	g1 = g2;
+       else if (! g1)
+ 	continue;
+       else if (! rtx_equal_p (g1->mult_val, g2->mult_val)
+ 	       || ! rtx_equal_p (g1->add_val, g2->add_val))
+ 	{
+ 	  g1 = 0;
+ 	  continue;
+ 	}
+       g2->combine_end_limit = g1->insn;
+     }
+ #endif /* AUTO_INC_DEC */
+ 
+   i = 0;
+   for (g1 = bl->giv; g1; g1 = g1->next_iv)
+     if (!g1->ignore)
+       giv_array[i++] = g1;
+ 
    stats = (struct combine_givs_stats *) xcalloc (giv_count, sizeof (*stats));
    can_combine = (rtx *) xcalloc (giv_count, giv_count * sizeof(rtx));
  
*************** struct recombine_givs_stats
*** 7322,7327 ****
--- 7529,7537 ----
  {
    int giv_number;
    int start_luid, end_luid;
+   rtx start_insn; /* First insn in loop order in which the giv (including
+ 		     combinations) is used; Initialized to NULL_RTX; set
+ 		     to a NOTE when invalid.  */
  };
  
  /* Used below as comparison function for qsort.  We want a ascending luid
*************** cmp_recombine_givs_stats (xp, yp)
*** 7344,7356 ****
    return d;
  }
  
! /* Scan X, which is a part of INSN, for the end of life of a giv.  Also
!    look for the start of life of a giv where the start has not been seen
!    yet to unlock the search for the end of its life.
!    Only consider givs that belong to BIV.
!    Return the total number of lifetime ends that have been found.  */
! static int
! find_life_end (x, stats, insn, biv)
       rtx x, insn, biv;
       struct recombine_givs_stats *stats;
  {
--- 7554,7605 ----
    return d;
  }
  
! /* The last label we encountered while scanning forward for giv uses.
!    Is initialized to SCAN_START (not necessarily a label) in recombine_givs.  */
! static rtx loop_last_label;
! 
! /* V, a giv, is used in INSN.
!    FROM_COMBINED is set if the use comes (possibly) from a combined giv.
!    It must not be set if there are no combined givs for this giv, since
!    this can confuse giv derivation to move the giv insn to the wrong place.
!    Update start_insn / end_luid in STATS accordingly.  */
! static void
! note_giv_use (v, insn, from_combined, stats)
!      struct induction *v;
!      rtx insn;
!      int from_combined;
!      struct recombine_givs_stats *stats;
! {
!   if (stats[v->ix].start_insn)
!     {
!       if (loop_insn_first_p (stats[v->ix].start_insn, loop_last_label)
! 	  && (loop_insn_first_p (loop_last_label, insn)
! 	      || loop_insn_first_p (insn, stats[v->ix].start_insn)))
! 	stats[v->ix].start_insn = loop_number_loop_starts[0];
!     }
!   else
!     {
!       rtx p;
! 
!       stats[v->ix].start_insn = insn;
!       if (from_combined)
! 	v->leading_combined = 1;
! 
!       /* Update start_luid now so that we won't loose this information it
! 	 when we invalidate start_insn.  */
!       for (p = insn; INSN_UID (p) >= max_uid_for_loop; )
! 	p = PREV_INSN (p);
!       stats[v->ix].start_luid = INSN_LUID (p);
!     }
!   while (INSN_UID (insn) >= max_uid_for_loop)
!     insn = NEXT_INSN (insn);
!   stats[v->ix].end_luid = INSN_LUID (insn);
! }
! 
! /* Scan X, which is a part of INSN, for uses of givs.
!    Only consider givs that belong to BIV.  */
! static void
! find_giv_uses (x, stats, insn, biv)
       rtx x, insn, biv;
       struct recombine_givs_stats *stats;
  {
*************** find_life_end (x, stats, insn, biv)
*** 7372,7419 ****
  
  	    if (REG_IV_TYPE (regno) == GENERAL_INDUCT
  		&& ! v->ignore
! 		&& v->src_reg == biv
! 		&& stats[v->ix].end_luid <= 0)
  	      {
! 		/* If we see a 0 here for end_luid, it means that we have
! 		   scanned the entire loop without finding any use at all.
! 		   We must not predicate this code on a start_luid match
! 		   since that would make the test fail for givs that have
! 		   been hoisted out of inner loops.  */
! 		if (stats[v->ix].end_luid == 0)
  		  {
! 		    stats[v->ix].end_luid = stats[v->ix].start_luid;
! 		    return 1 + find_life_end (SET_SRC (x), stats, insn, biv);
  		  }
- 		else if (stats[v->ix].start_luid == INSN_LUID (insn))
- 		  stats[v->ix].end_luid = 0;
  	      }
- 	    return find_life_end (SET_SRC (x), stats, insn, biv);
  	  }
  	break;
        }
      case REG:
        {
  	int regno = REGNO (x);
! 	struct induction *v = REG_IV_INFO (regno);
! 
! 	if (REG_IV_TYPE (regno) == GENERAL_INDUCT
! 	    && ! v->ignore
! 	    && v->src_reg == biv
! 	    && stats[v->ix].end_luid == 0)
  	  {
! 	    while (INSN_UID (insn) >= max_uid_for_loop)
! 	      insn = NEXT_INSN (insn);
! 	    stats[v->ix].end_luid = INSN_LUID (insn);
! 	    return 1;
  	  }
! 	return 0;
        }
      case LABEL_REF:
      case CONST_DOUBLE:
      case CONST_INT:
      case CONST:
!       return 0;
      default:
        break;
      }
--- 7621,7696 ----
  
  	    if (REG_IV_TYPE (regno) == GENERAL_INDUCT
  		&& ! v->ignore
! 		&& v->src_reg == biv)
! 	      {
! 		/* Since we are setting a non-ignored general induction
! 		   variable, this insn will be changed or go away, hence
! 		   we don't have to consider uses in the SET_SRC.  */
! 		return;
! 	      }
! 	    find_giv_uses (SET_SRC (x), stats, insn, biv);
! 	    return;
! 	  }
! 	break;
!       }
!     /* If this is a reduced DEST_ADDR giv, the original address doesn't
!        count; but if the giv has been combined with another one, we must
!        count the use there.  */
!     case MEM:
!       {
! 	rtx src_reg;
! 	rtx add_val;
! 	rtx mult_val;
! 	int benefit;
! 	struct induction *v;
! 
! 	if (general_induction_var (XEXP (x, 0), &src_reg, &add_val,
! 				   &mult_val, 1, &benefit)
! 	    && src_reg == biv)
! 	  {
! 	    for (v = reg_biv_class[REGNO (biv)]->giv; v; v = v->next_iv)
  	      {
! 		if (v->location == &XEXP (x, 0))
  		  {
! 		    int from_combined = 0;
! 
! 		    if (v->same)
! 		      {
! 			v = v->same;
! 			from_combined = 1;
! 		      }
! 		    if (v->ignore)
! 		      break;
! 		    note_giv_use (v, insn, from_combined, stats);
! 		    return;
  		  }
  	      }
  	  }
  	break;
        }
      case REG:
        {
  	int regno = REGNO (x);
! 	if (REG_IV_TYPE (regno) == GENERAL_INDUCT)
  	  {
! 	    struct induction *v = REG_IV_INFO (regno);
! 	    int from_combined = 0;
! 
! 	    if (v->same)
! 	      {
! 		v = v->same;
! 		from_combined = 1;
! 	      }
! 	    if (! v->ignore && v->src_reg == biv)
! 	      note_giv_use (v, insn, from_combined, stats);
  	  }
! 	return;
        }
      case LABEL_REF:
      case CONST_DOUBLE:
      case CONST_INT:
      case CONST:
!       return;
      default:
        break;
      }
*************** find_life_end (x, stats, insn, biv)
*** 7422,7434 ****
    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
      {
        if (fmt[i] == 'e')
! 	retval += find_life_end (XEXP (x, i), stats, insn, biv);
  
        else if (fmt[i] == 'E')
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
! 	  retval += find_life_end (XVECEXP (x, i, j), stats, insn, biv);
      }
!   return retval;
  }
  
  /* For each giv that has been combined with another, look if
--- 7699,7711 ----
    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
      {
        if (fmt[i] == 'e')
! 	find_giv_uses (XEXP (x, i), stats, insn, biv);
  
        else if (fmt[i] == 'E')
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
! 	  find_giv_uses (XVECEXP (x, i, j), stats, insn, biv);
      }
!   return;
  }
  
  /* For each giv that has been combined with another, look if
*************** find_life_end (x, stats, insn, biv)
*** 7436,7451 ****
     This tends to shorten giv lifetimes, and helps the next step:
     try to derive givs from other givs.  */
  static void
! recombine_givs (bl, loop_start, loop_end, unroll_p)
       struct iv_class *bl;
!      rtx loop_start, loop_end;
       int unroll_p;
  {
    struct induction *v, **giv_array, *last_giv;
    struct recombine_givs_stats *stats;
    int giv_count;
    int i, rescan;
!   int ends_need_computing;
  
    for (giv_count = 0, v = bl->giv; v; v = v->next_iv)
      {
--- 7713,7732 ----
     This tends to shorten giv lifetimes, and helps the next step:
     try to derive givs from other givs.  */
  static void
! recombine_givs (bl, scan_start, loop_start, loop_end, loop_top, unroll_p)
       struct iv_class *bl;
!      rtx scan_start, loop_start, loop_end, loop_top;
       int unroll_p;
  {
    struct induction *v, **giv_array, *last_giv;
    struct recombine_givs_stats *stats;
    int giv_count;
    int i, rescan;
!   int n_giv_live_after_loop;
!   struct induction **giv_live_after_loop;
!   rtx biv_use_start, biv_use_end;
!   struct induction *biv_giv;
!   int life_start, life_end;
  
    for (giv_count = 0, v = bl->giv; v; v = v->next_iv)
      {
*************** recombine_givs (bl, loop_start, loop_end
*** 7456,7469 ****
      = (struct induction **) xmalloc (giv_count * sizeof (struct induction *));
    stats = (struct recombine_givs_stats *) xmalloc (giv_count * sizeof *stats);
  
!   /* Initialize stats and set up the ix field for each giv in stats to name
!      the corresponding index into stats.  */
!   for (i = 0, v = bl->giv; v; v = v->next_iv)
      {
        rtx p;
  
        if (v->ignore)
! 	continue;
        giv_array[i] = v;
        stats[i].giv_number = i;
        /* If this giv has been hoisted out of an inner loop, use the luid of
--- 7737,7757 ----
      = (struct induction **) xmalloc (giv_count * sizeof (struct induction *));
    stats = (struct recombine_givs_stats *) xmalloc (giv_count * sizeof *stats);
  
!   /* Initialize stats, and clear the live_after_loop fields.
!      Also note where the biv is used by unreduced givs.  */
!   for (i = 0, biv_use_start = biv_use_end = 0, v = bl->giv; v; v = v->next_iv)
      {
        rtx p;
  
        if (v->ignore)
! 	{
! 	  if (! biv_use_start || loop_insn_first_p (v->insn, biv_use_start))
! 	    biv_use_start = v->insn;
! 	  if (! biv_use_end || loop_insn_first_p (biv_use_end, v->insn))
! 	    biv_use_end = v->insn;
! 	  continue;
! 	}
!       v->live_after_loop = 0;
        giv_array[i] = v;
        stats[i].giv_number = i;
        /* If this giv has been hoisted out of an inner loop, use the luid of
*************** recombine_givs (bl, loop_start, loop_end
*** 7471,7476 ****
--- 7759,7765 ----
        for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
  	p = PREV_INSN (p);
        stats[i].start_luid = INSN_LUID (p);
+       stats[i].start_insn = NULL_RTX;
        i++;
      }
  
*************** recombine_givs (bl, loop_start, loop_end
*** 7525,7654 ****
  	last_giv = v;
      }
  
!   ends_need_computing = 0;
!   /* For each DEST_REG giv, compute lifetime starts, and try to compute
!      lifetime ends from regscan info.  */
!   for (i = giv_count - 1; i >= 0; i--)
      {
!       v = giv_array[stats[i].giv_number];
!       if (v->ignore)
  	continue;
!       if (v->giv_type == DEST_ADDR)
! 	{
! 	  /* Loop unrolling of an inner loop can even create new DEST_REG
! 	     givs.  */
! 	  rtx p;
! 	  for (p = v->insn; INSN_UID (p) >= max_uid_for_loop; )
! 	    p = PREV_INSN (p);
! 	  stats[i].start_luid = stats[i].end_luid = INSN_LUID (p);
! 	  if (p != v->insn)
! 	    stats[i].end_luid++;
! 	}
!       else /* v->giv_type == DEST_REG */
! 	{
! 	  if (v->last_use)
! 	    {
! 	      stats[i].start_luid = INSN_LUID (v->insn);
! 	      stats[i].end_luid = INSN_LUID (v->last_use);
! 	    }
! 	  else if (INSN_UID (v->insn) >= max_uid_for_loop)
! 	    {
! 	      rtx p;
! 	      /* This insn has been created by loop optimization on an inner
! 		 loop.  We don't have a proper start_luid that will match
! 		 when we see the first set.  But we do know that there will
! 		 be no use before the set, so we can set end_luid to 0 so that
! 		 we'll start looking for the last use right away.  */
! 	      for (p = PREV_INSN (v->insn); INSN_UID (p) >= max_uid_for_loop; )
! 		p = PREV_INSN (p);
! 	      stats[i].start_luid = INSN_LUID (p);
! 	      stats[i].end_luid = 0;
! 	      ends_need_computing++;
! 	    }
! 	  else
! 	    {
! 	      int regno = REGNO (v->dest_reg);
! 	      int count = VARRAY_INT (n_times_set, regno) - 1;
! 	      rtx p = v->insn;
! 
! 	      /* Find the first insn that sets the giv, so that we can verify
! 		 if this giv's lifetime wraps around the loop.  We also need
! 		 the luid of the first setting insn in order to detect the
! 		 last use properly.  */
! 	      while (count)
! 		{
! 		  p = prev_nonnote_insn (p);
! 		  if (reg_set_p (v->dest_reg, p))
! 		  count--;
! 		}
  
! 	      stats[i].start_luid = INSN_LUID (p);
! 	      if (stats[i].start_luid > uid_luid[REGNO_FIRST_UID (regno)])
! 		{
! 		  stats[i].end_luid = -1;
! 		  ends_need_computing++;
! 		}
! 	      else
! 		{
! 		  stats[i].end_luid = uid_luid[REGNO_LAST_UID (regno)];
! 		  if (stats[i].end_luid > INSN_LUID (loop_end))
! 		    {
! 		      stats[i].end_luid = -1;
! 		      ends_need_computing++;
! 		    }
! 		}
! 	    }
! 	}
!     }
  
!   /* If the regscan information was unconclusive for one or more DEST_REG
!      givs, scan the all insn in the loop to find out lifetime ends.  */
!   if (ends_need_computing)
!     {
!       rtx biv = bl->biv->src_reg;
!       rtx p = loop_end;
! 
!       do
! 	{
! 	  if (p == loop_start)
! 	    p = loop_end;
! 	  p = PREV_INSN (p);
! 	  if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
! 	    continue;
! 	  ends_need_computing -= find_life_end (PATTERN (p), stats, p, biv);
  	}
-       while (ends_need_computing);
      }
  
!   /* Set start_luid back to the last insn that sets the giv.  This allows
!      more combinations.  */
!   for (i = giv_count - 1; i >= 0; i--)
!     {
!       v = giv_array[stats[i].giv_number];
!       if (v->ignore)
! 	continue;
!       if (INSN_UID (v->insn) < max_uid_for_loop)
! 	stats[i].start_luid = INSN_LUID (v->insn);
!     }
  
!   /* Now adjust lifetime ends by taking combined givs into account.  */
    for (i = giv_count - 1; i >= 0; i--)
      {
-       unsigned luid;
-       int j;
- 
        v = giv_array[stats[i].giv_number];
!       if (v->ignore)
  	continue;
!       if (v->same && ! v->same->ignore)
! 	{
! 	  j = v->same->ix;
! 	  luid = stats[i].start_luid;
! 	  /* Use unsigned arithmetic to model loop wrap-around.  */
! 	  if (luid - stats[j].start_luid
! 	      > (unsigned) stats[j].end_luid - stats[j].start_luid)
! 	    stats[j].end_luid = luid;
! 	}
      }
  
    qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
--- 7814,7913 ----
  	last_giv = v;
      }
  
!   /* Set up the giv_live_after_loop array.  */
!   n_giv_live_after_loop = 0;
!   giv_live_after_loop = NULL_PTR;
!   for (v = bl->giv; v; v = v->next_iv)
      {
!       struct induction *same;
! 
!       if (v->giv_type != DEST_REG || v->last_use)
  	continue;
!       if ((uid_luid[REGNO_FIRST_UID (REGNO (v->dest_reg))]
! 	   > INSN_LUID (loop_start))
! 	  && (uid_luid[REGNO_LAST_UID (REGNO (v->dest_reg))]
! 	      < INSN_LUID (loop_end)))
! 	continue;
  
!       /* Sometimes the register is immediately overwritten after the loop.
!          This happens particularily in the second loop pass, when we see
!          the results of strength reduction in the first pass.  */
!       if (flag_expensive_optimizations
! 	  && reg_dead_after_loop (v->dest_reg, loop_start, loop_end))
! 	continue;
  
!       same = v->same ? v->same : v;
!       if (! same->ignore
! 	  && ! same->live_after_loop)
! 	{
! 	  same->live_after_loop = 1;
! 	  if (! giv_live_after_loop)
! 	    giv_live_after_loop
! 	      = (struct induction **) alloca (sizeof (struct induction *)
! 					      * giv_count);
! 	  giv_live_after_loop[n_giv_live_after_loop++] = same;
  	}
      }
  
!   /* Scan all the insns in the loop to find out lifetime starts and ends.  */
!   {
!     rtx biv = bl->biv->src_reg;
!     rtx p = loop_end;
!     for (loop_last_label = scan_start, p = scan_start; p; 
! 	 p = next_insn_in_loop (p, scan_start, loop_end, loop_top))
!       {
! 	if (GET_CODE (p) == CODE_LABEL)
! 	  loop_last_label = p;
! 	else if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
! 	  {
! 	    find_giv_uses (PATTERN (p), stats, p, biv);
! 	    /* If this is a jump, we have to consider uses outside the loop.  */
! 	    if (GET_CODE (p) == JUMP_INSN && GET_CODE (PATTERN (p)) != RETURN)
! 	      {
! 		int is_loop_exit = 1;
! 		rtx label;
! 
! 		if (condjump_p (p) || condjump_in_parallel_p (p))
! 		  {
! 		    label = XEXP (condjump_label (p), 0);
! 		    /* If the destination is within the loop, and this
! 		       is not a conditional branch at the loop end, this
! 		       is not a loop exit.  */
! 		    if (loop_insn_first_p (loop_start, label)
! 			&& loop_insn_first_p (label, loop_end)
! 			&& (simplejump_p (p)
! 			    /* Shortcut for forward branches - by definition,
! 			       they can't be the end of the loop  */
! 			    || loop_insn_first_p (p, label)
! 			    || ! no_labels_between_p (p, loop_end)))
! 		      is_loop_exit = 0;
! 		  }
! 
! 		if (is_loop_exit)
! 		  {
! 		    for (i = n_giv_live_after_loop -1; i >= 0; i--)
! 		      /* We don't have recorded which givs are life after the
! 			 loop only because their giv register is life, or
! 			 (also) because a combined giv is life after the loop,
! 			 so just pretend it is the latter if any other givs
! 			 have been combined with this one.  */
! 		      note_giv_use (giv_live_after_loop[i], p,
! 				    giv_live_after_loop[i]->combined_with,
! 				    stats);
! 		  }
! 	      }
! 	  }
!       }
!   }
  
!   /* Ignore givs that are not used at all.  */
    for (i = giv_count - 1; i >= 0; i--)
      {
        v = giv_array[stats[i].giv_number];
!       if (v->ignore || v->same)
  	continue;
!       if (! stats[i].start_insn)
! 	v->ignore = 1;
      }
  
    qsort (stats, giv_count, sizeof(*stats), cmp_recombine_givs_stats);
*************** recombine_givs (bl, loop_start, loop_end
*** 7664,7680 ****
       When we are finished with the current LAST_GIV (i.e. the inner loop
       terminates), we start again with rescan, which then becomes the new
       LAST_GIV.  */
    for (i = giv_count - 1; i >= 0; i = rescan)
      {
!       int life_start, life_end;
  
!       for (last_giv = 0, rescan = -1; i >= 0; i--)
  	{
  	  rtx sum;
  
  	  v = giv_array[stats[i].giv_number];
! 	  if (v->giv_type != DEST_REG || v->derived_from || v->same)
  	    continue;
  	  if (! last_giv)
  	    {
  	      /* Don't use a giv that's likely to be dead to derive
--- 7923,7964 ----
       When we are finished with the current LAST_GIV (i.e. the inner loop
       terminates), we start again with rescan, which then becomes the new
       LAST_GIV.  */
+ 
+   /* The biv is also a giv, of sorts.  If it can't be eliminated, we
+      might as well consider to derive givs from it.  */
+   if (biv_use_start && bl->biv_count == 1)
+     {
+       biv_giv = (struct induction *) oballoc (sizeof *biv_giv);
+       biv_giv->add_val = const0_rtx;
+       biv_giv->mult_val = const1_rtx;
+       biv_giv->dest_reg = biv_giv->new_reg = regno_reg_rtx[bl->regno];
+       biv_giv->insn = bl->biv->insn;  /* Used for debugging dump.  */
+       last_giv = biv_giv;
+       while (INSN_UID (biv_use_start) >= max_uid_for_loop)
+ 	biv_use_start = PREV_INSN (biv_use_start);
+       life_start = INSN_LUID (biv_use_start);
+       while (INSN_UID (biv_use_end) >= max_uid_for_loop)
+ 	biv_use_end = NEXT_INSN (biv_use_end);
+       life_end = INSN_LUID (biv_use_end);
+     }
+   else
+     last_giv = 0;
+ 
    for (i = giv_count - 1; i >= 0; i = rescan)
      {
!       rtx add_insn, trial_add_insn = NULL_RTX;
  
!       for (rescan = -1; i >= 0; i--)
  	{
  	  rtx sum;
  
  	  v = giv_array[stats[i].giv_number];
! 	  if (v->derived_from || v->same || v->ignore)
  	    continue;
+ 
+ 	  if (! v->new_reg)
+ 	    v->new_reg = gen_reg_rtx (v->mode);
+ 
  	  if (! last_giv)
  	    {
  	      /* Don't use a giv that's likely to be dead to derive
*************** recombine_givs (bl, loop_start, loop_end
*** 7687,7704 ****
  		}
  	      continue;
  	    }
  	  /* Use unsigned arithmetic to model loop wrap around.  */
  	  if (((unsigned) stats[i].start_luid - life_start
  	       >= (unsigned) life_end - life_start)
  	      && ((unsigned) stats[i].end_luid - life_start
  		  > (unsigned) life_end - life_start)
- 	      /*  Check that the giv insn we're about to use for deriving
- 		  precedes all uses of that giv.  Note that initializing the
- 		  derived giv would defeat the purpose of reducing register
- 		  pressure.
- 		  ??? We could arrange to move the insn.  */
- 	      && ((unsigned) stats[i].end_luid - INSN_LUID (loop_start)
-                   > (unsigned) stats[i].start_luid - INSN_LUID (loop_start))
  	      && rtx_equal_p (last_giv->mult_val, v->mult_val)
  	      /* ??? Could handle libcalls, but would need more logic.  */
  	      && ! find_reg_note (v->insn, REG_RETVAL, NULL_RTX)
--- 7971,7998 ----
  		}
  	      continue;
  	    }
+ 
+ 	  /* ??? We would save some time by setting up add_insn only
+ 	     immediately before it is going to be used, but that would
+ 	     make the multi-line conditional below even harder to read.  */
+ 	  if (v->giv_type == DEST_REG)
+ 	    add_insn = v->insn;
+ 	  else
+ 	    {
+ 	      if (! trial_add_insn)
+ 		{
+ 		  trial_add_insn = make_insn_raw (NULL_RTX);
+ 		  PREV_INSN (trial_add_insn) = NULL_RTX;
+ 		  NEXT_INSN (trial_add_insn) = NULL_RTX;
+ 		}
+ 	      add_insn = trial_add_insn;
+ 	    }
+ 
  	  /* Use unsigned arithmetic to model loop wrap around.  */
  	  if (((unsigned) stats[i].start_luid - life_start
  	       >= (unsigned) life_end - life_start)
  	      && ((unsigned) stats[i].end_luid - life_start
  		  > (unsigned) life_end - life_start)
  	      && rtx_equal_p (last_giv->mult_val, v->mult_val)
  	      /* ??? Could handle libcalls, but would need more logic.  */
  	      && ! find_reg_note (v->insn, REG_RETVAL, NULL_RTX)
*************** recombine_givs (bl, loop_start, loop_end
*** 7708,7737 ****
  		 don't have this detailed control flow information.
  		 N.B. since last_giv will be reduced, it is valid
  		 anywhere in the loop, so we don't need to check the
! 		 validity of last_giv.
! 		 We rely here on the fact that v->always_executed implies that
! 		 there is no jump to someplace else in the loop before the
! 		 giv insn, and hence any insn that is executed before the
! 		 giv insn in the loop will have a lower luid.  */
! 	      && (v->always_executed || ! v->combined_with)
  	      && (sum = express_from (last_giv, v))
  	      /* Make sure we don't make the add more expensive.  ADD_COST
  		 doesn't take different costs of registers and constants into
  		 account, so compare the cost of the actual SET_SRCs.  */
! 	      && (rtx_cost (sum, SET)
! 		  <= rtx_cost (SET_SRC (single_set (v->insn)), SET))
  	      /* ??? unroll can't understand anything but reg + const_int
  		 sums.  It would be cleaner to fix unroll.  */
  	      && ((GET_CODE (sum) == PLUS
  		   && GET_CODE (XEXP (sum, 0)) == REG
  		   && GET_CODE (XEXP (sum, 1)) == CONST_INT)
  		  || ! unroll_p)
! 	      && validate_change (v->insn, &PATTERN (v->insn),
! 				  gen_rtx_SET (VOIDmode, v->dest_reg, sum), 0))
  	    {
  	      v->derived_from = last_giv;
  	      life_end = stats[i].end_luid;
  
  	      if (loop_dump_stream)
  		{
  		  fprintf (loop_dump_stream,
--- 8002,8088 ----
  		 don't have this detailed control flow information.
  		 N.B. since last_giv will be reduced, it is valid
  		 anywhere in the loop, so we don't need to check the
! 		 validity of last_giv.  */
! 	      && (GET_CODE (stats[i].start_insn) != NOTE
! 		  || ! v->combined_with
! 		  /* We rely here on the fact that v->always_executed implies
! 		     that there is no jump to someplace else in the loop before
! 		     the giv insn, and hence any insn that is executed before
! 		     the giv insn in the loop will have a lower luid.  */
! 		  || (v->giv_type == DEST_REG
! 		      && v->always_executed
! 		      && ! v->leading_combined
! 		      /*  Check that the giv insn we're about to use for
! 			  deriving precedes all uses of that giv.  Note that
! 			  initializing the derived giv would defeat the purpose
! 			  of reducing register pressure.  */
! 		      && ((unsigned) stats[i].end_luid - INSN_LUID (scan_start)
! 			  > ((unsigned) stats[i].start_luid
! 			     - INSN_LUID (scan_start)))))
! 	      /* If we are deriving from the biv, this must be before the biv
! 		 increment.  */
! 	      && (last_giv != biv_giv
! 		  || loop_insn_first_p ((v->leading_combined
! 					 ? stats[i].start_insn : v->insn),
! 					bl->biv->insn))
  	      && (sum = express_from (last_giv, v))
  	      /* Make sure we don't make the add more expensive.  ADD_COST
  		 doesn't take different costs of registers and constants into
  		 account, so compare the cost of the actual SET_SRCs.  */
! 	      && (v->giv_type != DEST_REG
! 		  || (rtx_cost (sum, SET)
! 		      <= rtx_cost (SET_SRC (single_set (v->insn)), SET)))
  	      /* ??? unroll can't understand anything but reg + const_int
  		 sums.  It would be cleaner to fix unroll.  */
  	      && ((GET_CODE (sum) == PLUS
  		   && GET_CODE (XEXP (sum, 0)) == REG
  		   && GET_CODE (XEXP (sum, 1)) == CONST_INT)
  		  || ! unroll_p)
! 	      && validate_change (add_insn, &PATTERN (add_insn),
! 				  gen_rtx_SET (VOIDmode, v->new_reg, sum), 0))
  	    {
+ 	      struct induction *tv;
+ 
+ 	      last_giv->did_derive = 1;
  	      v->derived_from = last_giv;
  	      life_end = stats[i].end_luid;
+ 	      if (v->giv_type == DEST_ADDR)
+ 		{
+ 		  trial_add_insn = NULL_RTX;
+ 		  reorder_insns (add_insn, add_insn,
+ 				 PREV_INSN (stats[i].start_insn));
+ 		}
+ 	      /* Check if we want / have to move this giv.  */
+ 	      else if (v->leading_combined)
+ 		{
+ 		  rtx insert_after = PREV_INSN (stats[i].start_insn);
+ 		  rtx prev = PREV_INSN (v->insn);
+ 		  rtx next = NEXT_INSN (v->insn);
  
+ #ifdef HAVE_cc0
+ 		  if (GET_RTX_CLASS (GET_CODE (insert_after)) == 'i'
+ 		      && sets_cc0_p (PATTERN (insert_after)))
+ 		    insert_after = PREV_INSN (insert_after);
+ #endif
+ 		  if (v->insn == insert_after
+ 		      || prev == insert_after)
+ 		    ; /* do nothing */
+ 		  else if (loop_insn_first_p (v->insn, insert_after))
+ 		    {
+ 		      reorder_insns (v->insn, v->insn, insert_after);
+ 		      while (INSN_UID (prev) >= max_uid_for_loop)
+ 			prev = PREV_INSN (prev);
+ 		      compute_luids (next, v->insn, INSN_LUID (prev));
+ 		    }
+ 		  else
+ 		    {
+ 		      reorder_insns (v->insn, v->insn, insert_after);
+ 		      while (INSN_UID (insert_after) >= max_uid_for_loop)
+ 			insert_after = PREV_INSN (insert_after);
+ 		      compute_luids (v->insn, prev, INSN_LUID (insert_after));
+ 		    }
+ 		}
+ 
  	      if (loop_dump_stream)
  		{
  		  fprintf (loop_dump_stream,
*************** recombine_givs (bl, loop_start, loop_end
*** 7740,7749 ****
--- 8091,8147 ----
  		  print_rtl (loop_dump_stream, sum);
  		  putc ('\n', loop_dump_stream);
  		}
+ 
+ 	      /* In case LAST_GIV->dest_reg is not replaceable, we have
+                  to replace it in ADD_INSN now.  */
+                   PATTERN (add_insn)
+                     = replace_rtx (PATTERN (add_insn), last_giv->dest_reg,
+ 				   last_giv->new_reg);
+ 
+ 	      /* For each place where the biv is incremented, add an
+ 		 insn to set the new, reduced reg for the giv.
+ 		 We used to do this only for biv_count != 1, but
+ 		 this fails when there is a giv after a single biv
+ 		 increment, e.g. when the last giv was expressed as
+ 		 pre-decrement.
+ 		 We do this here (rather than at giv derivation time) because
+ 		 we want to copy ADD_INSN - which is not the same as V->insn
+ 		 for DEST_ADDR givs - and to exploit the lifetime
+ 		 information we have.  */
+ 	      for (tv = bl->biv; tv; tv = tv->next_iv)
+ 		{
+ 		  /* If the biv increment precedes ADD_INSN, we can ignore it.
+ 		     Only handle the most common case here.  */
+ 		  if (loop_insn_first_p (tv->insn, add_insn)
+ 		      && (loop_insn_first_p (scan_start, tv->insn)
+ 			  || loop_insn_first_p (add_insn, scan_start)))
+ 		    continue;
+ 		  /* Likewise if the biv increment is after the last giv use.
+ 		     Only handle the most common case here.  */
+ 		  if (INSN_UID (tv->insn) < max_uid_for_loop
+ 		      && stats[i].end_luid < INSN_LUID (tv->insn)
+ 		      && INSN_LUID (scan_start) < stats[i].end_luid)
+ 		    continue;
+ 
+ 		  /* We always emit reduced giv increments before the biv
+ 		     increment when bl->biv_count != 1.  So by emitting
+ 		     the add insns for derived givs after the biv increment,
+ 		     they pick up the updated value of the reduced giv.
+ 		     If the reduced giv is processed with auto_inc_opt == 1,
+ 		     then it is incremented earlier than the biv, hence we'll
+ 		     still pick up the right value.
+ 		     If it's processed with auto_inc_opt == -1,
+ 		     that implies that the biv increment is before the
+ 		     first reduced giv's use.  The derived giv's lifetime
+ 		     is after the reduced giv's lifetime, hence in this
+ 		     case, the biv increment doesn't matter.  */
+ 		  emit_insn_after (copy_rtx (PATTERN (add_insn)), tv->insn);
+ 		}
  	    }
  	  else if (rescan < 0)
  	    rescan = i;
  	}
+       last_giv = 0;
      }
  
    /* Clean up.  */
*************** load_mems_and_recount_loop_regs_set (sca
*** 9691,9697 ****
    int nregs = max_reg_num ();
  
    load_mems (scan_start, end, loop_top, start);
!   
    /* Recalculate set_in_loop and friends since load_mems may have
       created new registers.  */
    if (max_reg_num () > nregs)
--- 10089,10095 ----
    int nregs = max_reg_num ();
  
    load_mems (scan_start, end, loop_top, start);
! 
    /* Recalculate set_in_loop and friends since load_mems may have
       created new registers.  */
    if (max_reg_num () > nregs)
*************** load_mems_and_recount_loop_regs_set (sca
*** 9724,9730 ****
  	  VARRAY_CHAR (may_not_optimize, i) = 1;
  	  VARRAY_INT (set_in_loop, i) = 1;
  	}
!       
  #ifdef AVOID_CCMODE_COPIES
        /* Don't try to move insns which set CC registers if we should not
  	 create CCmode register copies.  */
--- 10122,10128 ----
  	  VARRAY_CHAR (may_not_optimize, i) = 1;
  	  VARRAY_INT (set_in_loop, i) = 1;
  	}
! 
  #ifdef AVOID_CCMODE_COPIES
        /* Don't try to move insns which set CC registers if we should not
  	 create CCmode register copies.  */
*************** replace_label (x, data)
*** 10176,10182 ****
  
    if (XEXP (l, 0) != old_label)
      return 0;
!   
    XEXP (l, 0) = new_label;
    ++LABEL_NUSES (new_label);
    --LABEL_NUSES (old_label);
--- 10574,10580 ----
  
    if (XEXP (l, 0) != old_label)
      return 0;
! 
    XEXP (l, 0) = new_label;
    ++LABEL_NUSES (new_label);
    --LABEL_NUSES (old_label);
Index: loop.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/loop.h,v
retrieving revision 1.20
diff -p -r1.20 loop.h
*** loop.h	1999/12/08 03:22:33	1.20
--- loop.h	1999/12/11 00:42:40
*************** struct induction
*** 102,107 ****
--- 102,124 ----
    unsigned shared : 1;
    unsigned no_const_addval : 1; /* 1 if add_val does not contain a const. */
    unsigned multi_insn_incr : 1;	/* 1 if multiple insns updated the biv.  */
+ 
+   /* giv-giv autoinc in the following means that that a DEST_ADDR giv
+      can be formed from a preceding giv with the same mult_val but
+      different add_val by using auto-increment.  */
+   unsigned autoinc_pred : 1;	/* 1 if predecessor in a giv-giv autoinc.  */
+   unsigned autoinc_succ : 1;	/* 1 if successor in a giv-giv autoinc.  */
+   unsigned preinc : 1;		/* 1 if considered for pre-increment in
+ 				   giv-giv autoinc.  */
+   unsigned live_after_loop : 1; /* Used inside recombine_givs to keep track
+ 				   of which givs have already been included
+ 				   in an array of givs live after the loop.  */
+   unsigned leading_combined : 1;/* In recombine_givs, set if this giv has been
+ 				   combined with one or more other givs that
+ 				   precede the giv insn of this giv.
+ 				   Giv derivation then requires to move the
+ 				   giv insn before the first use.  */
+   unsigned did_derive : 1;	/* Set in recombine_givs.  */
    int lifetime;			/* Length of life of this giv */
    rtx derive_adjustment;	/* If nonzero, is an adjustment to be
  				   subtracted from add_val when this giv
*************** struct induction
*** 128,133 ****
--- 145,152 ----
  				   that doesn't have this field set.  */
    rtx last_use;			/* For a giv made from a biv increment, this is
  				   a substitute for the lifetime information. */
+   rtx combine_start_limit;
+   rtx combine_end_limit;
  };
  
  /* A `struct iv_class' is created for each biv.  */
*************** void emit_unrolled_add PROTO((rtx, rtx, 
*** 257,262 ****
--- 276,282 ----
  int back_branch_in_range_p PROTO((rtx, rtx, rtx));
  
  int loop_insn_first_p PROTO((rtx, rtx));
+ int reg_dead_after_loop PROTO((rtx, rtx, rtx));
  
  /* Forward declarations for non-static functions declared in stmt.c.  */
  void find_loop_tree_blocks PROTO((void));
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/rtl.h,v
retrieving revision 1.158
diff -p -r1.158 rtl.h
*** rtl.h	1999/12/04 03:00:03	1.158
--- rtl.h	1999/12/11 00:42:42
*************** extern void add_clobbers		PROTO ((rtx, i
*** 1470,1475 ****
--- 1470,1478 ----
  extern void combine_instructions	PROTO ((rtx, int));
  extern int extended_count		PROTO ((rtx, enum machine_mode, int));
  extern rtx remove_death			PROTO ((int, rtx));
+ extern int validate_subst		PROTO((rtx, rtx, rtx));
+ extern void validate_subst_start	PROTO((void));
+ extern void validate_subst_undo		PROTO((void));
  #ifdef BUFSIZ
  extern void dump_combine_stats		PROTO ((FILE *));
  extern void dump_combine_total_stats	PROTO ((FILE *));
Index: unroll.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/unroll.c,v
retrieving revision 1.79
diff -p -r1.79 unroll.c
*** unroll.c	1999/11/29 10:51:09	1.79
--- unroll.c	1999/12/11 00:42:43
*************** static int find_splittable_regs PROTO((e
*** 205,211 ****
  				       unsigned HOST_WIDE_INT));
  static int find_splittable_givs PROTO((struct iv_class *, enum unroll_types,
  				       rtx, rtx, rtx, int));
- static int reg_dead_after_loop PROTO((rtx, rtx, rtx));
  static rtx fold_rtx_mult_add PROTO((rtx, rtx, rtx, enum machine_mode));
  static int verify_addresses PROTO((struct induction *, rtx, int));
  static rtx remap_split_bivs PROTO((rtx));
--- 205,210 ----
*************** find_splittable_givs (bl, unroll_type, l
*** 3221,3227 ****
  /* ?? Could be made more intelligent in the handling of jumps, so that
     it can search past if statements and other similar structures.  */
  
! static int
  reg_dead_after_loop (reg, loop_start, loop_end)
       rtx reg, loop_start, loop_end;
  {
--- 3220,3226 ----
  /* ?? Could be made more intelligent in the handling of jumps, so that
     it can search past if statements and other similar structures.  */
  
! int
  reg_dead_after_loop (reg, loop_start, loop_end)
       rtx reg, loop_start, loop_end;
  {


More information about the Gcc mailing list