This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[bib][rfc] flow.c cleanup


Hello,

I am working on cleaning up flow.c a bit; in first place, I would like
to split out the unrelated stuff (log links creation, pre/post modify
transformation, ...) as mentioned in TODO in comments.

The attached patch is first part of this attempt; it splits
propagate_insn_info into parts -- the first where it just
gathers info about insn (sets, uses, etc. -- in extract_insn_info),
and then several passes using this information doing all the rest
(computing liveness, creating death notes, ...), with emphasis on that
one function should only perform one of those roles so that it should
be easy to decompose into several functions.

As a test of the idea, log links creation is completely split out.

The whole patch was developed (and tested) so that there is absolutely
no change in functionality -- the resulting code should be byte to byte
identical to previous one.

I have a few questions:
-- is this approach acceptable? Any comments?
-- would this patch be accepted into bib? It certainly fixes no
   regressions (I would consider it wrong if it did :-) ), but it
   also should not change anything and keeping patch of this size on
   separate branch would be unpleasant.

Zdenek

Index: Makefile.in
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.939.2.20
diff -c -3 -p -r1.939.2.20 Makefile.in
*** Makefile.in	11 Dec 2002 23:03:23 -0000	1.939.2.20
--- Makefile.in	12 Dec 2002 02:10:12 -0000
*************** rtl.o : rtl.c $(CONFIG_H) $(SYSTEM_H) co
*** 1419,1425 ****
  print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
      hard-reg-set.h $(BASIC_BLOCK_H) real.h
  rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h $(RTL_H) \
!    hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) real.h flags.h
  
  errors.o : errors.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(GTM_H) errors.h
  	$(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
--- 1419,1426 ----
  print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \
      hard-reg-set.h $(BASIC_BLOCK_H) real.h
  rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) toplev.h $(RTL_H) \
!    hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) real.h flags.h \
!    regs.h function.h
  
  errors.o : errors.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(GTM_H) errors.h
  	$(CC) -c $(ALL_CFLAGS) -DGENERATOR_FILE $(ALL_CPPFLAGS) $(INCLUDES) $< $(OUTPUT_OPTION)
Index: basic-block.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/basic-block.h,v
retrieving revision 1.158.4.3
diff -c -3 -p -r1.158.4.3 basic-block.h
*** basic-block.h	1 Oct 2002 17:31:36 -0000	1.158.4.3
--- basic-block.h	12 Dec 2002 02:10:12 -0000
*************** enum update_life_extent
*** 638,644 ****
  #define PROP_AUTOINC		64	/* Create autoinc mem references.  */
  #define PROP_EQUAL_NOTES	128	/* Take into account REG_EQUAL notes.  */
  #define PROP_SCAN_DEAD_STORES	256	/* Scan for dead code.  */
! #define PROP_FINAL		(PROP_DEATH_NOTES | PROP_LOG_LINKS  \
  				 | PROP_REG_INFO | PROP_KILL_DEAD_CODE  \
  				 | PROP_SCAN_DEAD_CODE | PROP_AUTOINC \
  				 | PROP_ALLOW_CFG_CHANGES \
--- 638,644 ----
  #define PROP_AUTOINC		64	/* Create autoinc mem references.  */
  #define PROP_EQUAL_NOTES	128	/* Take into account REG_EQUAL notes.  */
  #define PROP_SCAN_DEAD_STORES	256	/* Scan for dead code.  */
! #define PROP_FINAL		(PROP_DEATH_NOTES \
  				 | PROP_REG_INFO | PROP_KILL_DEAD_CODE  \
  				 | PROP_SCAN_DEAD_CODE | PROP_AUTOINC \
  				 | PROP_ALLOW_CFG_CHANGES \
*************** extern int propagate_block	PARAMS ((basi
*** 676,685 ****
  					 int));
  
  struct propagate_block_info;
! extern rtx propagate_one_insn	PARAMS ((struct propagate_block_info *, rtx));
  extern struct propagate_block_info *init_propagate_block_info
    PARAMS ((basic_block, regset, regset, regset, int));
  extern void free_propagate_block_info PARAMS ((struct propagate_block_info *));
  
  /* In lcm.c */
  extern struct edge_list *pre_edge_lcm	PARAMS ((FILE *, int, sbitmap *,
--- 676,687 ----
  					 int));
  
  struct propagate_block_info;
! extern rtx propagate_one_insn	PARAMS ((struct propagate_block_info *, rtx,
! 					 struct insn_extract *));
  extern struct propagate_block_info *init_propagate_block_info
    PARAMS ((basic_block, regset, regset, regset, int));
  extern void free_propagate_block_info PARAMS ((struct propagate_block_info *));
+ extern void create_log_links	PARAMS ((sbitmap));
  
  /* In lcm.c */
  extern struct edge_list *pre_edge_lcm	PARAMS ((FILE *, int, sbitmap *,
Index: cfgcleanup.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/cfgcleanup.c,v
retrieving revision 1.63.4.5
diff -c -3 -p -r1.63.4.5 cfgcleanup.c
*** cfgcleanup.c	1 Dec 2002 05:41:43 -0000	1.63.4.5
--- cfgcleanup.c	12 Dec 2002 02:10:12 -0000
*************** cleanup_cfg (mode)
*** 1796,1803 ****
  	  if (!update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
  						 PROP_DEATH_NOTES
  						 | PROP_SCAN_DEAD_CODE
! 						 | PROP_KILL_DEAD_CODE
! 						 | PROP_LOG_LINKS))
  	    break;
  	}
        else if (!(mode & (CLEANUP_NO_INSN_DEL | CLEANUP_PRE_SIBCALL))
--- 1796,1802 ----
  	  if (!update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
  						 PROP_DEATH_NOTES
  						 | PROP_SCAN_DEAD_CODE
! 						 | PROP_KILL_DEAD_CODE));
  	    break;
  	}
        else if (!(mode & (CLEANUP_NO_INSN_DEL | CLEANUP_PRE_SIBCALL))
Index: flow.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/flow.c,v
retrieving revision 1.536.4.5
diff -c -3 -p -r1.536.4.5 flow.c
*** flow.c	1 Dec 2002 05:41:58 -0000	1.536.4.5
--- flow.c	12 Dec 2002 02:10:13 -0000
*************** static void calculate_global_regs_live	P
*** 298,311 ****
  static void propagate_block_delete_insn PARAMS ((rtx));
  static rtx propagate_block_delete_libcall PARAMS ((rtx, rtx));
  static int insn_dead_p			PARAMS ((struct propagate_block_info *,
! 						 rtx, int, rtx));
  static int libcall_dead_p		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx));
- static void mark_set_regs		PARAMS ((struct propagate_block_info *,
- 						 rtx, rtx));
- static void mark_set_1			PARAMS ((struct propagate_block_info *,
- 						 enum rtx_code, rtx, rtx,
- 						 rtx, int));
  static int find_regno_partial		PARAMS ((rtx *, void *));
  
  #ifdef HAVE_conditional_execution
--- 298,307 ----
  static void propagate_block_delete_insn PARAMS ((rtx));
  static rtx propagate_block_delete_libcall PARAMS ((rtx, rtx));
  static int insn_dead_p			PARAMS ((struct propagate_block_info *,
! 						 struct insn_extract *,
! 						 int, rtx));
  static int libcall_dead_p		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx));
  static int find_regno_partial		PARAMS ((rtx *, void *));
  
  #ifdef HAVE_conditional_execution
*************** static rtx not_reg_cond			PARAMS ((rtx))
*** 321,338 ****
  static rtx and_reg_cond			PARAMS ((rtx, rtx, int));
  #endif
  #ifdef AUTO_INC_DEC
! static void attempt_auto_inc		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx, rtx, rtx, rtx));
! static void find_auto_inc		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx));
  static int try_pre_increment_1		PARAMS ((struct propagate_block_info *,
  						 rtx));
  static int try_pre_increment		PARAMS ((rtx, rtx, HOST_WIDE_INT));
  #endif
- static void mark_used_reg		PARAMS ((struct propagate_block_info *,
- 						 rtx, rtx, rtx));
- static void mark_used_regs		PARAMS ((struct propagate_block_info *,
- 						 rtx, rtx, rtx));
  void dump_flow_info			PARAMS ((FILE *));
  void debug_flow_info			PARAMS ((void));
  static void add_to_mem_set_list		PARAMS ((struct propagate_block_info *,
--- 317,332 ----
  static rtx and_reg_cond			PARAMS ((rtx, rtx, int));
  #endif
  #ifdef AUTO_INC_DEC
! static int attempt_auto_inc		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx, rtx, rtx, rtx));
! static int find_auto_inc		PARAMS ((struct propagate_block_info *,
  						 rtx, rtx));
  static int try_pre_increment_1		PARAMS ((struct propagate_block_info *,
  						 rtx));
  static int try_pre_increment		PARAMS ((rtx, rtx, HOST_WIDE_INT));
+ static int autoinc_transform		PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *, rtx));
  #endif
  void dump_flow_info			PARAMS ((FILE *));
  void debug_flow_info			PARAMS ((void));
  static void add_to_mem_set_list		PARAMS ((struct propagate_block_info *,
*************** static int invalidate_mems_from_autoinc 
*** 341,346 ****
--- 335,366 ----
  static void invalidate_mems_from_set	PARAMS ((struct propagate_block_info *,
  						 rtx));
  static void clear_log_links		PARAMS ((sbitmap));
+ static rtx kill_dead_insn		PARAMS ((struct propagate_block_info *,
+ 					      	 rtx, int, rtx));
+ static void set_up_log_links		PARAMS ((rtx,
+ 						 struct propagate_block_info *,
+ 					   	 struct insn_extract *));
+ static void update_next_use_sets	PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *));
+ static void update_next_use_uses	PARAMS ((rtx,
+ 						 struct propagate_block_info *,
+ 						 struct insn_extract *));
+ static void update_live_by_sets		PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *));
+ static void update_live_by_uses		PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *));
+ static void update_mems_by_sets		PARAMS ((struct propagate_block_info *,
+ 					    	 struct insn_extract *));
+ static void update_mems_by_uses		PARAMS ((struct propagate_block_info *,
+ 					    	 struct insn_extract *));
+ static void create_unused_notes		PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *, rtx));
+ static void create_dead_notes		PARAMS ((struct propagate_block_info *,
+ 						 struct insn_extract *, rtx));
+ static void count_register_info_for_sets PARAMS ((struct propagate_block_info *,
+ 						  struct insn_extract *));
+ static void count_register_info_for_uses PARAMS ((struct propagate_block_info *,
+ 						  struct insn_extract *));
  
  
  void
*************** life_analysis (f, file, flags)
*** 441,447 ****
  #endif
  
    if (! optimize)
!     flags &= ~(PROP_LOG_LINKS | PROP_AUTOINC | PROP_ALLOW_CFG_CHANGES);
  
    /* The post-reload life analysis have (on a global basis) the same
       registers live as was computed by reload itself.  elimination
--- 461,467 ----
  #endif
  
    if (! optimize)
!     flags &= ~(PROP_AUTOINC | PROP_ALLOW_CFG_CHANGES);
  
    /* The post-reload life analysis have (on a global basis) the same
       registers live as was computed by reload itself.  elimination
*************** update_life_info (blocks, extent, prop_f
*** 697,706 ****
  	count_or_remove_death_notes (blocks, 1);
      }
  
-   /* Clear log links in case we are asked to (re)compute them.  */
-   if (prop_flags & PROP_LOG_LINKS)
-     clear_log_links (blocks);
- 
    if (blocks)
      {
        EXECUTE_IF_SET_IN_SBITMAP (blocks, 0, i,
--- 717,722 ----
*************** propagate_block_delete_libcall ( insn, n
*** 1597,1893 ****
    return before;
  }
  
! /* Update the life-status of regs for one insn.  Return the previous insn.  */
! 
! rtx
! propagate_one_insn (pbi, insn)
       struct propagate_block_info *pbi;
       rtx insn;
  {
    rtx prev = PREV_INSN (insn);
-   int flags = pbi->flags;
-   int insn_is_dead = 0;
-   int libcall_is_dead = 0;
-   rtx note;
-   int i;
  
!   if (! INSN_P (insn))
!     return prev;
  
!   note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
!   if (flags & PROP_SCAN_DEAD_CODE)
      {
!       insn_is_dead = insn_dead_p (pbi, PATTERN (insn), 0, REG_NOTES (insn));
!       libcall_is_dead = (insn_is_dead && note != 0
! 			 && libcall_dead_p (pbi, note, insn));
!     }
  
!   /* If an instruction consists of just dead store(s) on final pass,
!      delete it.  */
!   if ((flags & PROP_KILL_DEAD_CODE) && insn_is_dead)
!     {
!       /* If we're trying to delete a prologue or epilogue instruction
! 	 that isn't flagged as possibly being dead, something is wrong.
! 	 But if we are keeping the stack pointer depressed, we might well
! 	 be deleting insns that are used to compute the amount to update
! 	 it by, so they are fine.  */
!       if (reload_completed
! 	  && !(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
! 		&& (TYPE_RETURNS_STACK_DEPRESSED
! 		    (TREE_TYPE (current_function_decl))))
! 	  && (((HAVE_epilogue || HAVE_prologue)
! 	       && prologue_epilogue_contains (insn))
! 	      || (HAVE_sibcall_epilogue
! 		  && sibcall_epilogue_contains (insn)))
! 	  && find_reg_note (insn, REG_MAYBE_DEAD, NULL_RTX) == 0)
! 	fatal_insn ("Attempt to delete prologue/epilogue insn:", insn);
! 
!       /* Record sets.  Do this even for dead instructions, since they
! 	 would have killed the values if they hadn't been deleted.  */
!       mark_set_regs (pbi, PATTERN (insn), insn);
! 
!       /* CC0 is now known to be dead.  Either this insn used it,
! 	 in which case it doesn't anymore, or clobbered it,
! 	 so the next insn can't use it.  */
!       pbi->cc0_live = 0;
  
!       if (libcall_is_dead)
! 	prev = propagate_block_delete_libcall ( insn, note);
!       else
  	{
  
! 	/* If INSN contains a RETVAL note and is dead, but the libcall
! 	   as a whole is not dead, then we want to remove INSN, but
! 	   not the whole libcall sequence.
! 
! 	   However, we need to also remove the dangling REG_LIBCALL	
! 	   note so that we do not have mis-matched LIBCALL/RETVAL
! 	   notes.  In theory we could find a new location for the
! 	   REG_RETVAL note, but it hardly seems worth the effort. 
! 
! 	   NOTE at this point will be the RETVAL note if it exists.  */
! 	  if (note)
! 	    {
! 	      rtx libcall_note;
! 	 
! 	      libcall_note
! 		= find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX);
! 	      remove_note (XEXP (note, 0), libcall_note);
! 	    }
! 
! 	  /* Similarly if INSN contains a LIBCALL note, remove the
! 	     dnagling REG_RETVAL note.  */
! 	  note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
! 	  if (note)
! 	    {
! 	      rtx retval_note;
  
! 	      retval_note
! 		= find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX);
! 	      remove_note (XEXP (note, 0), retval_note);
! 	    }
  
! 	  /* Now delete INSN.  */
! 	  propagate_block_delete_insn (insn);
  	}
  
!       return prev;
      }
  
!   /* See if this is an increment or decrement that can be merged into
!      a following memory address.  */
! #ifdef AUTO_INC_DEC
!   {
!     rtx x = single_set (insn);
! 
!     /* Does this instruction increment or decrement a register?  */
!     if ((flags & PROP_AUTOINC)
! 	&& x != 0
! 	&& GET_CODE (SET_DEST (x)) == REG
! 	&& (GET_CODE (SET_SRC (x)) == PLUS
! 	    || GET_CODE (SET_SRC (x)) == MINUS)
! 	&& XEXP (SET_SRC (x), 0) == SET_DEST (x)
! 	&& GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
! 	/* Ok, look for a following memory ref we can combine with.
! 	   If one is found, change the memory ref to a PRE_INC
! 	   or PRE_DEC, cancel this insn, and return 1.
! 	   Return 0 if nothing has been done.  */
! 	&& try_pre_increment_1 (pbi, insn))
!       return prev;
!   }
! #endif /* AUTO_INC_DEC */
  
!   CLEAR_REG_SET (pbi->new_set);
  
!   /* If this is not the final pass, and this insn is copying the value of
!      a library call and it's dead, don't scan the insns that perform the
!      library call, so that the call's arguments are not marked live.  */
!   if (libcall_is_dead)
      {
!       /* Record the death of the dest reg.  */
!       mark_set_regs (pbi, PATTERN (insn), insn);
  
!       insn = XEXP (note, 0);
!       return PREV_INSN (insn);
      }
!   else if (GET_CODE (PATTERN (insn)) == SET
! 	   && SET_DEST (PATTERN (insn)) == stack_pointer_rtx
! 	   && GET_CODE (SET_SRC (PATTERN (insn))) == PLUS
! 	   && XEXP (SET_SRC (PATTERN (insn)), 0) == stack_pointer_rtx
! 	   && GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 1)) == CONST_INT)
!     /* We have an insn to pop a constant amount off the stack.
!        (Such insns use PLUS regardless of the direction of the stack,
!        and any insn to adjust the stack by a constant is always a pop.)
!        These insns, if not dead stores, have no effect on life, though
!        they do have an effect on the memory stores we are tracking.  */
!     invalidate_mems_from_set (pbi, stack_pointer_rtx);
!   else
!     {
!       rtx note;
!       /* Any regs live at the time of a call instruction must not go
! 	 in a register clobbered by calls.  Find all regs now live and
! 	 record this for them.  */
! 
!       if (GET_CODE (insn) == CALL_INSN && (flags & PROP_REG_INFO))
! 	EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
! 				   { REG_N_CALLS_CROSSED (i)++; });
! 
!       /* Record sets.  Do this even for dead instructions, since they
! 	 would have killed the values if they hadn't been deleted.  */
!       mark_set_regs (pbi, PATTERN (insn), insn);
  
!       if (GET_CODE (insn) == CALL_INSN)
! 	{
! 	  int i;
! 	  rtx note, cond;
  
! 	  cond = NULL_RTX;
! 	  if (GET_CODE (PATTERN (insn)) == COND_EXEC)
! 	    cond = COND_EXEC_TEST (PATTERN (insn));
  
! 	  /* Non-constant calls clobber memory, constant calls do not
! 	     clobber memory, though they may clobber outgoing arguments
! 	     on the stack.  */
! 	  if (! CONST_OR_PURE_CALL_P (insn))
! 	    {
! 	      free_EXPR_LIST_list (&pbi->mem_set_list);
! 	      pbi->mem_set_list_len = 0;
! 	    }
! 	  else
! 	    invalidate_mems_from_set (pbi, stack_pointer_rtx);
  
! 	  /* There may be extra registers to be clobbered.  */
! 	  for (note = CALL_INSN_FUNCTION_USAGE (insn);
! 	       note;
! 	       note = XEXP (note, 1))
! 	    if (GET_CODE (XEXP (note, 0)) == CLOBBER)
! 	      mark_set_1 (pbi, CLOBBER, XEXP (XEXP (note, 0), 0),
! 			  cond, insn, pbi->flags);
  
! 	  /* Calls change all call-used and global registers.  */
! 	  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
! 	    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
  	      {
! 		/* We do not want REG_UNUSED notes for these registers.  */
! 		mark_set_1 (pbi, CLOBBER, regno_reg_rtx[i], cond, insn,
! 			    pbi->flags & ~(PROP_DEATH_NOTES | PROP_REG_INFO));
  	      }
! 	}
  
!       /* If an insn doesn't use CC0, it becomes dead since we assume
! 	 that every insn clobbers it.  So show it dead here;
! 	 mark_used_regs will set it live if it is referenced.  */
!       pbi->cc0_live = 0;
! 
!       /* Record uses.  */
!       if (! insn_is_dead)
! 	mark_used_regs (pbi, PATTERN (insn), NULL_RTX, insn);
!       if ((flags & PROP_EQUAL_NOTES)
! 	  && ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX))
! 	      || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX))))
! 	mark_used_regs (pbi, XEXP (note, 0), NULL_RTX, insn);
  
!       /* Sometimes we may have inserted something before INSN (such as a move)
! 	 when we make an auto-inc.  So ensure we will scan those insns.  */
! #ifdef AUTO_INC_DEC
!       prev = PREV_INSN (insn);
  #endif
  
!       if (! insn_is_dead && GET_CODE (insn) == CALL_INSN)
  	{
! 	  int i;
! 	  rtx note, cond;
  
! 	  cond = NULL_RTX;
! 	  if (GET_CODE (PATTERN (insn)) == COND_EXEC)
! 	    cond = COND_EXEC_TEST (PATTERN (insn));
! 
! 	  /* Calls use their arguments.  */
! 	  for (note = CALL_INSN_FUNCTION_USAGE (insn);
! 	       note;
! 	       note = XEXP (note, 1))
! 	    if (GET_CODE (XEXP (note, 0)) == USE)
! 	      mark_used_regs (pbi, XEXP (XEXP (note, 0), 0),
! 			      cond, insn);
  
! 	  /* The stack ptr is used (honorarily) by a CALL insn.  */
! 	  SET_REGNO_REG_SET (pbi->reg_live, STACK_POINTER_REGNUM);
  
! 	  /* Calls may also reference any of the global registers,
! 	     so they are made live.  */
! 	  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
! 	    if (global_regs[i])
! 	      mark_used_reg (pbi, regno_reg_rtx[i], cond, insn);
  	}
      }
- 
-   /* On final pass, update counts of how many insns in which each reg
-      is live.  */
-   if (flags & PROP_REG_INFO)
-     EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
- 			       { REG_LIVE_LENGTH (i)++; });
- 
-   return prev;
  }
  
! /* Initialize a propagate_block_info struct for public consumption.
!    Note that the structure itself is opaque to this file, but that
!    the user can use the regsets provided here.  */
! 
! struct propagate_block_info *
! init_propagate_block_info (bb, live, local_set, cond_local_set, flags)
!      basic_block bb;
!      regset live, local_set, cond_local_set;
!      int flags;
  {
!   struct propagate_block_info *pbi = xmalloc (sizeof (*pbi));
! 
!   pbi->bb = bb;
!   pbi->reg_live = live;
!   pbi->mem_set_list = NULL_RTX;
!   pbi->mem_set_list_len = 0;
!   pbi->local_set = local_set;
!   pbi->cond_local_set = cond_local_set;
!   pbi->cc0_live = 0;
!   pbi->flags = flags;
  
!   if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
!     pbi->reg_next_use = (rtx *) xcalloc (max_reg_num (), sizeof (rtx));
    else
!     pbi->reg_next_use = NULL;
  
!   pbi->new_set = BITMAP_XMALLOC ();
  
! #ifdef HAVE_conditional_execution
!   pbi->reg_cond_dead = splay_tree_new (splay_tree_compare_ints, NULL,
! 				       free_reg_cond_life_info);
!   pbi->reg_cond_reg = BITMAP_XMALLOC ();
  
!   /* If this block ends in a conditional branch, for each register live
!      from one side of the branch and not the other, record the register
!      as conditionally dead.  */
!   if (GET_CODE (bb->end) == JUMP_INSN
!       && any_condjump_p (bb->end))
      {
        regset_head diff_head;
        regset diff = INITIALIZE_REG_SET (diff_head);
--- 1613,2655 ----
    return before;
  }
  
! /* Kill the dead INSN; return the previous one.  */
! static rtx
! kill_dead_insn (pbi, insn, libcall_is_dead, note)
       struct propagate_block_info *pbi;
       rtx insn;
+      int libcall_is_dead;
+      rtx note;
  {
    rtx prev = PREV_INSN (insn);
  
!   /* If we're trying to delete a prologue or epilogue instruction
!      that isn't flagged as possibly being dead, something is wrong.
!      But if we are keeping the stack pointer depressed, we might well
!      be deleting insns that are used to compute the amount to update
!      it by, so they are fine.  */
!   if (reload_completed
!       && !(TREE_CODE (TREE_TYPE (current_function_decl)) == FUNCTION_TYPE
! 	   && (TYPE_RETURNS_STACK_DEPRESSED
! 	       (TREE_TYPE (current_function_decl))))
!       && (((HAVE_epilogue || HAVE_prologue)
! 	   && prologue_epilogue_contains (insn))
! 	  || (HAVE_sibcall_epilogue
! 	      && sibcall_epilogue_contains (insn)))
!       && find_reg_note (insn, REG_MAYBE_DEAD, NULL_RTX) == 0)
!     fatal_insn ("Attempt to delete prologue/epilogue insn:", insn);
! 
!   /* CC0 is now known to be dead.  Either this insn used it,
!      in which case it doesn't anymore, or clobbered it,
!      so the next insn can't use it.  */
!   pbi->cc0_live = 0;
  
!   if (libcall_is_dead)
!     prev = propagate_block_delete_libcall ( insn, note);
!   else
      {
!       /* If INSN contains a RETVAL note and is dead, but the libcall
! 	 as a whole is not dead, then we want to remove INSN, but
! 	 not the whole libcall sequence.
  
! 	 However, we need to also remove the dangling REG_LIBCALL	
! 	 note so that we do not have mis-matched LIBCALL/RETVAL
! 	 notes.  In theory we could find a new location for the
! 	 REG_RETVAL note, but it hardly seems worth the effort. 
  
! 	 NOTE at this point will be the RETVAL note if it exists.  */
!       if (note)
  	{
+ 	  rtx libcall_note;
  
! 	  libcall_note
!   		  = find_reg_note (XEXP (note, 0), REG_LIBCALL, NULL_RTX);
! 	  remove_note (XEXP (note, 0), libcall_note);
! 	}
  
!       /* Similarly if INSN contains a LIBCALL note, remove the
! 	 dnagling REG_RETVAL note.  */
!       note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
!       if (note)
! 	{
! 	  rtx retval_note;
  
! 	  retval_note
!   		  = find_reg_note (XEXP (note, 0), REG_RETVAL, NULL_RTX);
! 	  remove_note (XEXP (note, 0), retval_note);
  	}
  
!       /* Now delete INSN.  */
!       propagate_block_delete_insn (insn);
      }
  
!   return prev;
! }
  
! /* Set up log links to INSN.  */
! static void
! set_up_log_links (insn, pbi, insn_info)
!      rtx insn;
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, freg, reg;
!   rtx y;
  
!   for (regs = insn_info->registers_set; regs; regs = XEXP (regs, 1))
      {
!       reg = XEXP (regs, 0);
!       reg = XEXP (reg, 1);  /* Skip flags.  */
!       reg = XEXP (reg, 1);  /* Skip cond.  */
!       freg = XEXP (reg, 0);
!       y = pbi->reg_next_use[REGNO (freg)];
! 
!       /* Do not make a log link for frame pointer.  */
!       if (REGNO (freg) == FRAME_POINTER_REGNUM
! 	  && (! reload_completed || frame_pointer_needed))
! 	continue;
! #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
!       if (REGNO (freg) == HARD_FRAME_POINTER_REGNUM
! 	  && (! reload_completed || frame_pointer_needed))
! 	continue;
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
!       if (REGNO (freg) == ARG_POINTER_REGNUM && fixed_regs[REGNO (freg)])
! 	continue;
! #endif
  
!       /* Make a logical link from the next following insn
! 	 that uses this register, back to this insn.
! 	 The following insns have already been processed.
! 
! 	 We don't build a LOG_LINK for hard registers containing
! 	 in ASM_OPERANDs.  If these registers get replaced,
! 	 we might wind up changing the semantics of the insn,
! 	 even if reload can make what appear to be valid
! 	 assignments later.  */
!       if (!y || BLOCK_FOR_INSN (y) != BLOCK_FOR_INSN (insn))
! 	continue;
! 
!       if (REGNO (freg) < FIRST_PSEUDO_REGISTER
! 	  && asm_noperands (PATTERN (y)) >= 0)
! 	continue;
!       
!       LOG_LINKS (y) = alloc_INSN_LIST (insn, LOG_LINKS (y));
      }
! }
  
! /* Update PBI->reg_next_use by sets and clobbers.  */
! static void
! update_next_use_sets (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, reg;
!   rtx lists[2];
!   int i, j, regno_first, regno_last;
! 
!   lists[0] = insn_info->registers_set;
!   lists[1] = insn_info->registers_altered;
! 
!   /* Remove set or clobbered registers.  */
!   for (i = 0; i < 2; i++)
!     for (regs = lists[i]; regs; regs = XEXP (regs, 1))
!       {
! 	reg = XEXP (regs, 0);
! 	reg = XEXP (reg, 1);  /* Skip flags.  */
! 	reg = XEXP (reg, 1);  /* Skip cond.  */
! 	reg = XEXP (reg, 1);  /* Skip reg.  */
! 	regno_first = INTVAL (XEXP (reg, 0));
! 	reg = XEXP (reg, 1);
! 	regno_last = INTVAL (XEXP (reg, 0));
  
! 	for (j = regno_first; j <= regno_last; j++)
! 	  pbi->reg_next_use[j] = NULL_RTX;
!       }
! }
  
! /* Update PBI->reg_next_use by uses.  */
! static void
! update_next_use_uses (insn, pbi, insn_info)
!      rtx insn;
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, freg, reg;
!   int flags;
  
!   /* Add used registers.  */
!   for (regs = insn_info->registers_used; regs; regs = XEXP (regs, 1))
!     {
!       reg = XEXP (regs, 0);
!       flags = INTVAL (XEXP (reg, 0));
!       reg = XEXP (reg, 1);
!       reg = XEXP (reg, 1);  /* Skip cond.  */
!       reg = XEXP (reg, 1);  /* Skip reg.  */
!       freg = XEXP (reg, 0);
  
!       if (flags & IIF_CALL_STACK_USAGE)
! 	continue;
!       pbi->reg_next_use[REGNO (freg)] = insn;
!     }
! }
! 
! /* Update live status by sets in the insn.  */
! static void
! update_live_by_sets (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, act, freg, cond;
!   rtx lists[2];
!   int i, j, regno_first, regno_last;
! 
!   lists[0] = insn_info->registers_set;
!   lists[1] = insn_info->registers_altered;
! 
!   /* If an insn doesn't use CC0, it becomes dead since we assume
!      that every insn clobbers it.  So show it dead here;
!      mark_used_regs will set it live if it is referenced.  */
!   pbi->cc0_live = 0;
! 
!   /* Remove set or clobbered registers.  */
!   for (i = 0; i < 2; i++)
!     for (regs = lists[i]; regs; regs = XEXP (regs, 1))
!       {
! 	act = XEXP (regs, 0);
! 	act = XEXP (act, 1)  /* Skip flags.  */;
! 	cond = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	freg = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	regno_first = INTVAL (XEXP (act, 0));
! 	act = XEXP (act, 1);
! 	regno_last = INTVAL (XEXP (act, 0));
! 
!       	if (REGNO (freg) == FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! 	  continue;
! #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
! 	if (REGNO (freg) == HARD_FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! 	  continue;
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! 	if (REGNO (freg) == ARG_POINTER_REGNUM && fixed_regs[REGNO (freg)])
! 	  continue;
! #endif
! 	for (j = regno_first; j <= regno_last; j++)
! 	  {
! 	    if (pbi->local_set)
  	      {
! 	      	/* Order of the set operation matters here since both
! 	  	   sets may be the same.  */
! 	    	CLEAR_REGNO_REG_SET (pbi->cond_local_set, j);
! 		if (cond != NULL_RTX
! 		    && ! REGNO_REG_SET_P (pbi->local_set, j))
! 		  SET_REGNO_REG_SET (pbi->cond_local_set, j);
! 		else
! 		  SET_REGNO_REG_SET (pbi->local_set, j);
  	      }
! 	    if (i == 0)   /* registers_set  */
! 	      SET_REGNO_REG_SET (pbi->new_set, j);
  
! 	    /* The stack pointer is never dead.  Well, not strictly true,
! 	       but it's very difficult to tell from here.  Hopefully
! 	       combine_stack_adjustments will fix up the most egregious
! 	       errors.  */
! 	    if (REGNO (freg) == STACK_POINTER_REGNUM)
! 	      continue;
  
! #ifdef HAVE_conditional_execution
! 	    /* Consider conditional death.  */
! 	    if (! mark_regno_cond_dead (pbi, j, cond))
! 	      continue;
! #endif
! 
! 	    /* The register is dead now.  */
! 	    CLEAR_REGNO_REG_SET (pbi->reg_live, j);
! 	  }
!       }
! }
! 
! /* Update live status by uses in the insn.  */
! static void
! update_live_by_uses (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, act, freg, cond;
!   int j, regno_first, regno_last;
! #ifdef HAVE_conditional_execution
!   int this_was_live;
!   splay_tree_node node;
!   struct reg_cond_life_info *rcli;
!   rtx ncond;
! #endif
! 
! #ifdef HAVE_cc0
!   if (insn_info->uses_cc0)
!     pbi->cc0_live = 1;
  #endif
+     
+   /* Add used registers.  */
+   for (regs = insn_info->registers_used; regs; regs = XEXP (regs, 1))
+     {
+       act = XEXP (regs, 0);
+       act = XEXP (act, 1)  /* Skip flags.  */;
+       cond = XEXP (act, 0);
+       act = XEXP (act, 1);
+       freg = XEXP (act, 0);
+       act = XEXP (act, 1);
+       regno_first = INTVAL (XEXP (act, 0));
+       act = XEXP (act, 1);
+       regno_last = INTVAL (XEXP (act, 0));
  
!       /* Mark the register as being live.  */
!       for (j = regno_first; j <= regno_last; j++)
  	{
! #ifdef HAVE_conditional_execution
! 	  this_was_live = REGNO_REG_SET_P (pbi->reg_live, j);
! #endif
! 	  SET_REGNO_REG_SET (pbi->reg_live, j);
! 
! #ifdef HAVE_conditional_execution
! 	  if (cond == NULL_RTX)
! 	    {
! 	      /* Not a conditional use.  */
! 	      if (this_was_live)
! 		{
! 		  /* The register may have been conditionally live previously,
! 		     but is now unconditionally live.  Remove it from the
! 		     conditionally dead list, so that a conditional set won't
! 		     cause us to think it dead.  */
! 		  splay_tree_remove (pbi->reg_cond_dead, j);
! 		}
! 	      continue;
! 	    }
  
! 	  /* A conditional use. Record that fact; if it is later
!     	     conditionally set, we'll know to kill the register.  */
! 	  if (this_was_live)
!     	    {
! 	      node = splay_tree_lookup (pbi->reg_cond_dead, j);
! 	      if (node == NULL)
! 		{
! 		  /* The register was unconditionally live previously.
! 		     No need to do anything.  */
! 		}
! 	      else
! 		{
! 		  /* The register was conditionally live previously.
! 		     Subtract the new life cond from the old death cond.  */
! 		  rcli = (struct reg_cond_life_info *) node->value;
! 		  ncond = rcli->condition;
! 		  ncond = and_reg_cond (ncond, not_reg_cond (cond), 1);
  
! 		  /* If the register is now unconditionally live,
! 		     remove the entry in the splay_tree.  */
! 		  if (ncond == const0_rtx)
! 		    splay_tree_remove (pbi->reg_cond_dead, j);
! 		  else
! 		    {
! 		      rcli->condition = ncond;
! 		      SET_REGNO_REG_SET (pbi->reg_cond_reg,
! 					 REGNO (XEXP (cond, 0)));
! 		    }
! 		}
! 	    }
! 	  else
! 	    {
! 	      /* The register was not previously live at all.  Record
! 		 the condition under which it is still dead.  */
! 	      rcli = (struct reg_cond_life_info *) xmalloc (sizeof (*rcli));
! 	      rcli->condition = not_reg_cond (cond);
! 	      rcli->stores = const0_rtx;
! 	      rcli->orig_condition = const0_rtx;
! 	      splay_tree_insert (pbi->reg_cond_dead, j,
! 				 (splay_tree_value) rcli);
  
! 	      SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0)));
! 	    }
! #endif
  	}
      }
  }
  
! /* Update traced mems by sets in the insn.  */
! static void
! update_mems_by_sets (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
  {
!   rtx regs, reg, freg, cond, mem;
!   rtx lists[2];
!   int i;
  
!   if (insn_info->all_mems_altered)
!     {
!       /* All MEMs are clobbered by a non-constant call.  */
!       free_EXPR_LIST_list (&pbi->mem_set_list);
!       pbi->mem_set_list_len = 0;
!     }
    else
!     {
!       if (insn_info->contains_impure_call)
! 	{
! 	  /* The call must be const or pure; constant calls do not
! 	     clobber memory, though they may clobber outgoing arguments
! 	     on the stack.  */
! 	  invalidate_mems_from_set (pbi, stack_pointer_rtx);
! 	}
!       
!       lists[0] = insn_info->registers_set;
!       lists[1] = insn_info->registers_altered;
! 
!       /* Invalidate MEMs using set or clobbered registers.  */
!       for (i = 0; i < 2; i++)
! 	for (regs = lists[i]; regs; regs = XEXP (regs, 1))
! 	  {
! 	    reg = XEXP (regs, 0);
! 	    reg = XEXP (reg, 1);  /* Skip flags.  */
! 	    cond = XEXP (reg, 0);
! 	    reg = XEXP (reg, 1);
! 	    freg = XEXP (reg, 0);
  
! 	    invalidate_mems_from_set (pbi, freg);
! 	  }
!     }
!   
!   /* Record new MEMs.  */
!   for (regs = insn_info->mems_set; regs; regs = XEXP (regs, 1))
!     {
!       reg = XEXP (regs, 0);
!       cond = XEXP (reg, 0);
!       mem = XEXP (reg, 1);
!       
!       /* If the memory reference had embedded side effects (autoincrement
! 	 address modes.  Then we may need to kill some entries on the
! 	 memory set list.  */
!       for_each_rtx (&mem, invalidate_mems_from_autoinc, pbi);
  
!       if (! side_effects_p (mem)
! 	  /* ??? With more effort we could track conditional memory life.  */
! 	  && ! cond)
! 	add_to_mem_set_list (pbi, canon_rtx (mem));
!     }
! }
  
! /* Update traced mems by uses in the insn.  */
! static void
! update_mems_by_uses (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, reg, mem, cond;
! 
!   for (regs = insn_info->mems_used; regs; regs = XEXP (regs, 1))
!     {
!       reg = XEXP (regs, 0);
!       cond = XEXP (reg, 0);
!       mem = XEXP (reg, 1);
! 
!       /* Invalidate the data for the last MEM stored, but only if MEM is
! 	 something that can be stored into.  */
!       if (GET_CODE (XEXP (mem, 0)) == SYMBOL_REF
! 	  && CONSTANT_POOL_ADDRESS_P (XEXP (mem, 0)))
! 	/* Needn't clear the memory set list.  */
! 	;
!       else
! 	{
! 	  rtx temp = pbi->mem_set_list;
! 	  rtx prev = NULL_RTX;
! 	  rtx next;
! 
! 	  while (temp)
! 	    {
! 	      next = XEXP (temp, 1);
! 	      if (anti_dependence (XEXP (temp, 0), mem))
! 		{
! 		  /* Splice temp out of the list.  */
! 		  if (prev)
! 		    XEXP (prev, 1) = next;
! 		  else
! 	    	    pbi->mem_set_list = next;
!     		  free_EXPR_LIST_node (temp);
! 		  pbi->mem_set_list_len--;
! 		}
! 	      else
! 		prev = temp;
! 	      temp = next;
! 	    }
! 	}
! 
!       /* If the memory reference had embedded side effects (autoincrement
! 	 address modes.  Then we may need to kill some entries on the
! 	 memory set list.  */
!       for_each_rtx (&mem, invalidate_mems_from_autoinc, pbi);
!     }
! }
! 
! /* Create REG_UNUSED notes for sets in the INSN.  */
! static void
! create_unused_notes (pbi, insn_info, insn)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
!      rtx insn;
! {
!   rtx regs, freg, cond, act;
!   rtx lists[3];
!   int i;
!   int all_dead, flags;
!   int j, regno_first, regno_last;
! 
!   lists[0] = insn_info->scratches;
!   lists[1] = insn_info->registers_set;
!   lists[2] = insn_info->registers_altered;
! 
!   /* Add notes for dead registers.  */
!   for (i = 2; i >= 0; i--)
!     for (regs = lists[i]; regs; regs = XEXP (regs, 1))
!       {
! 	act = XEXP (regs, 0);
! 	flags = INTVAL (XEXP (act, 0));
! 	act = XEXP (act, 1);
! 	cond = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	freg = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	regno_first = INTVAL (XEXP (act, 0));
!     	act = XEXP (act, 1);
! 	regno_last = INTVAL (XEXP (act, 0));
! 
! 	/* We do not want to add REG_UNUSED notes for global registers
! 	   (implicitly) clobbered by a call.  */
! 	if (flags & IIF_CALL_CLOBBERED_GLOBAL)
! 	  continue;
! 
! 	all_dead = 1;
! 	for (j = regno_first; j <= regno_last; j++)
! 	  if (REGNO_REG_SET_P (pbi->reg_live, j))
! 	    {
!       	      all_dead = 0;
! 	      break;
! 	    }
! 
! 	/* Mark the dead store.  */
! 	if (all_dead)
! 	  {
! 	    REG_NOTES (insn) = alloc_EXPR_LIST (REG_UNUSED,
! 	  					freg,
! 	  					REG_NOTES (insn));
! 	  }
! 	else
! 	  {
! 	    /* This is a case where we have a multi-word hard register
!   	       and some, but not all, of the words of the register are
! 	       needed in subsequent insns.  Write REG_UNUSED notes
! 	       for those parts that were not needed.  This case should
! 	       be rare.  */
! 	    for (j = regno_first; j <= regno_last; j++)
! 	      {
! 		if (REGNO_REG_SET_P (pbi->reg_live, j))
! 		  continue;
! 
! 	      	REG_NOTES (insn) = alloc_EXPR_LIST (REG_UNUSED,
!       						    regno_reg_rtx[j],
! 						    REG_NOTES (insn));
! 	      }
! 	  }
!       }
! }
! 
! /* Create REG_DEAD notes for uses in the INSN.  */
! static void
! create_dead_notes (pbi, insn_info, insn)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
!      rtx insn;
! {
!   rtx regs, freg, act;
!   int all_live, all_dead, all_set, alive, set;
!   int j, regno_first, regno_last;
! 
!   /* Add notes for dead registers.  */
!   for (regs = insn_info->registers_used; regs; regs = XEXP (regs, 1))
!     {
!       act = XEXP (regs, 0);
!       act = XEXP (act, 1);  /* Skip flags.  */
!       act = XEXP (act, 1);  /* Skip cond.  */
!       freg = XEXP (act, 0);
!       act = XEXP (act, 1);
!       regno_first = INTVAL (XEXP (act, 0));
!       act = XEXP (act, 1);
!       regno_last = INTVAL (XEXP (act, 0));
! 
!       all_live = all_dead = 1;
!       all_set = 1;
!       for (j = regno_first; j <= regno_last; j++)
! 	{
! 	  alive = REGNO_REG_SET_P (pbi->reg_live, j);
! 	  set = REGNO_REG_SET_P (pbi->new_set, j);
! 
! 	  /* Find out if any of this register is live after this instruction.  */
! 	  all_live &= alive;
! 	  all_dead &= ! alive;
! 	  
!       	  /* Find out if all of the register was set by this insn.  */
! 	  all_set &= set;
!       
!       	  /* Check for the case where the register dying partially
! 	     overlaps the register set by this insn.  */
! 	  all_dead &= !set;
! 	}
! 
!       /* Record and count the insns in which a reg dies.  If it is used in
! 	 this insn and was dead below the insn then it dies in this insn.
! 	 If it was set in this insn, we do not make a REG_DEAD note;
! 	 likewise if we already made such a note.  */
!       if (all_live || all_set)
! 	continue;
! 
!       /* If none of the words in X is needed, make a REG_DEAD note.
! 	 Otherwise, we must make partial REG_DEAD notes.  */
!       if (all_dead)
! 	{
! 	  if (! find_regno_note (insn, REG_DEAD, regno_first))
! 	    REG_NOTES (insn)
! 		    = alloc_EXPR_LIST (REG_DEAD, freg, REG_NOTES (insn));
! 
! 	  continue;
! 	}
! 	
!       /* Don't make a REG_DEAD note for a part of a register
! 	 that is set in the insn.  */
!       for (j = regno_first; j <= regno_last; j++)
! 	if (! REGNO_REG_SET_P (pbi->reg_live, j)
! 	    && ! dead_or_set_regno_p (insn, j))
! 	  REG_NOTES (insn)
! 		  = alloc_EXPR_LIST (REG_DEAD,
! 				     regno_reg_rtx[j],
! 				     REG_NOTES (insn));
!     }
! }
! 
! /* Counts register info for sets in insn.  */
! static void
! count_register_info_for_sets (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, freg, cond, act;
!   rtx lists[2];
!   int i;
!   int all_dead, flags;
!   int j, regno_first, regno_last;
! 
!   lists[0] = insn_info->registers_set;
!   lists[1] = insn_info->registers_altered;
! 
!   /* Count the info.  */
!   for (i = 1; i >= 0; i--)
!     for (regs = lists[i]; regs; regs = XEXP (regs, 1))
!       {
! 	act = XEXP (regs, 0);
! 	flags = INTVAL (XEXP (act, 0));
! 	act = XEXP (act, 1);
! 	cond = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	freg = XEXP (act, 0);
! 	act = XEXP (act, 1);
! 	regno_first = INTVAL (XEXP (act, 0));
! 	act = XEXP (act, 1);
! 	regno_last = INTVAL (XEXP (act, 0));
! 
!       	if (regno_first == FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! 	  continue;
! #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
! 	if (regno_first == HARD_FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! 	  continue;
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! 	if (regno_first == ARG_POINTER_REGNUM && fixed_regs[regno_first])
! 	  continue;
! #endif
! 
! 	/* We do not want to track global registers (implicitly) clobbered
! 	   by a call.  */
! 	if (flags & IIF_CALL_CLOBBERED_GLOBAL)
! 	  continue;
! 
! 	all_dead = 1;
! 
! 	for (j = regno_first; j <= regno_last; j++)
! 	  {
! 	    if (REGNO_REG_SET_P (pbi->reg_live, j))
! 	      {
! 		all_dead = 0;
! 		break;
! 	      }
! 	  }
! 
! 	for (j = regno_first; j <= regno_last; j++)
! 	  {
! 	    /* Count (weighted) references, stores, etc.  This counts a
!   	       register twice if it is modified, but that is correct.  */
! 	    REG_N_SETS (j) += 1;
! 	    REG_N_REFS (j) += 1;
! 	    REG_FREQ (j) += REG_FREQ_FROM_BB (pbi->bb);
! 
! 	    /* The insns where a reg is live are normally counted
! 	       elsewhere, but we want the count to include the insn
! 	       where the reg is set, and the normal counting mechanism
! 	       would not count it.  */
! 	    REG_LIVE_LENGTH (j) += 1;
! 	      
! 	    /* If this is a hard reg, record this function uses the reg.  */
! 	    if (regno_first < FIRST_PSEUDO_REGISTER)
! 	      regs_ever_live[j] = 1;
! 	  }
! 
! 	if (regno_first >= FIRST_PSEUDO_REGISTER)
! 	  {
!     	    /* Keep track of which basic blocks each reg appears in.  */
! 	    if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
! 	      REG_BASIC_BLOCK (regno_first) = pbi->bb->index;
! 	    else if (REG_BASIC_BLOCK (regno_first) != pbi->bb->index)
! 	      REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
! 	  }
! 
! 	if (all_dead)
! 	  REG_N_DEATHS (regno_first) += 1;
!       }
! }
! 
! /* Counts register info for uses in insn.  */
! static void
! count_register_info_for_uses (pbi, insn_info)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
! {
!   rtx regs, freg, act;
!   int set, alive, all_live, all_dead, all_set, flags;
!   regset already_done;
!   int j, regno_first, regno_last;
! 
!   already_done = BITMAP_XMALLOC ();
!   CLEAR_REG_SET (already_done);
!   
!   /* Count the info.  */
!   for (regs = insn_info->registers_used; regs; regs = XEXP (regs, 1))
!     {
!       act = XEXP (regs, 0);
!       flags = INTVAL (XEXP (act, 0));
!       act = XEXP (act, 1);
!       act = XEXP (act, 1);  /* Skip cond.  */
!       freg = XEXP (act, 0);
!       act = XEXP (act, 1);
!       regno_first = INTVAL (XEXP (act, 0));
!       act = XEXP (act, 1);
!       regno_last = INTVAL (XEXP (act, 0));
! 
!       /* Do not count stats for this one.  */
!       if (flags & IIF_CALL_STACK_USAGE)
! 	continue;
! 
!       /* Count register deaths.  */
!       if (!REGNO_REG_SET_P (already_done, regno_first))
! 	{
! 	  SET_REGNO_REG_SET (already_done, regno_first);
! 	  all_live = all_dead = 1;
! 	  all_set = 1;
! 
! 	  for (j = regno_first; j <= regno_last; j++)
! 	    {
! 	      /* Find out if any of this register is live after this
! 		 instruction.  */
! 	      alive = REGNO_REG_SET_P (pbi->reg_live, j);
! 	      all_live &= alive;
! 	      all_dead &= ! alive;
! 	  
! 	      set = REGNO_REG_SET_P (pbi->new_set, j);
! 	      /* Find out if all of the register was set by this insn.  */
! 	      all_set &= set;
!       
! 	      /* Check for the case where the register dying partially
! 		 overlaps the register set by this insn.  */
! 	      all_dead &= !set;
! 	    }
! 
! 	  if (!all_live && !all_set && all_dead)
! 	    REG_N_DEATHS (regno_first)++;
! 	}
! 
!       if (regno_first < FIRST_PSEUDO_REGISTER)
! 	{
! 	  /* If this is a register we are going to try to eliminate,
! 	     don't mark it live here.  If we are successful in
! 	     eliminating it, it need not be live unless it is used for
! 	     pseudos, in which case it will have been set live when it
! 	     was allocated to the pseudos.  If the register will not
! 	     be eliminated, reload will set it live at that point.
! 
! 	     Otherwise, record that this function uses this register.  */
! 	  /* ??? The PPC backend tries to "eliminate" on the pic
! 	     register to itself.  This should be fixed.  In the mean
! 	     time, hack around it.  */
! 
! 	  if (TEST_HARD_REG_BIT (elim_reg_set, regno_first)
! 	      && (regno_first == FRAME_POINTER_REGNUM
!        		  || regno_first == ARG_POINTER_REGNUM))
! 	    continue;
! 
! 	  for (j = regno_first; j <= regno_last; j++)
! 	    regs_ever_live[j] = 1;
! 	  continue;
! 	}
! 
!       /* Keep track of which basic block each reg appears in.  */
! 
!       if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
! 	REG_BASIC_BLOCK (regno_first) = pbi->bb->index;
!       else if (REG_BASIC_BLOCK (regno_first) != pbi->bb->index)
! 	REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
! 
!       /* Count (weighted) number of uses of each reg.  */
!       REG_FREQ (regno_first) += REG_FREQ_FROM_BB (pbi->bb);
!       REG_N_REFS (regno_first)++;
!     }
! 
!   BITMAP_XFREE (already_done);
! }
! 
! #ifdef AUTO_INC_DEC
! /* Attempt to convert MEMs into autoincs.  */
! static int
! autoinc_transform (pbi, insn_info, insn)
!      struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
!      rtx insn;
! {
!   rtx mems, mem, act;
!   rtx lists[2];
!   int i;
!   int changed = 0;
! 
!   lists[0] = insn_info->mems_set;
!   lists[1] = insn_info->mems_used;
! 
!   /* Count the info.  */
!   for (i = 0; i < 2; i++)
!     for (mems = lists[i]; mems; mems = XEXP (mems, 1))
!       {
! 	act = XEXP (mems, 0);
! 	mem = XEXP (act, 1);
! 
! 	changed |= find_auto_inc (pbi, mem, insn);
!       }
! 
!   return changed;
! }
! #endif /* AUTO_INC_DEC */
! 
! /* Update the life-status of regs for one insn.  Return the previous insn.  */
! rtx
! propagate_one_insn (pbi, insn, insn_info)
!      struct propagate_block_info *pbi;
!      rtx insn;
!      struct insn_extract *insn_info;
! {
!   rtx prev = PREV_INSN (insn);
!   int flags = pbi->flags;
!   int insn_is_dead = 0;
!   int libcall_is_dead = 0;
!   struct insn_extract own_insn_info;
!   rtx note;
!   int i;
! 
!   if (! INSN_P (insn))
!     return prev;
! 
!   /* See if this is an increment or decrement that can be merged into
!      a following memory address.  */
! #ifdef AUTO_INC_DEC
!   {
!     rtx x = single_set (insn);
! 
!     /* Does this instruction increment or decrement a register?  */
!     if ((flags & PROP_AUTOINC)
! 	&& x != 0
! 	&& GET_CODE (SET_DEST (x)) == REG
! 	&& (GET_CODE (SET_SRC (x)) == PLUS
! 	    || GET_CODE (SET_SRC (x)) == MINUS)
! 	&& XEXP (SET_SRC (x), 0) == SET_DEST (x)
! 	&& GET_CODE (XEXP (SET_SRC (x), 1)) == CONST_INT
! 	/* Ok, look for a following memory ref we can combine with.
! 	   If one is found, change the memory ref to a PRE_INC
! 	   or PRE_DEC, cancel this insn, and return 1.
! 	   Return 0 if nothing has been done.  */
! 	&& try_pre_increment_1 (pbi, insn))
!       return prev;
!   }
! #endif /* AUTO_INC_DEC */
! 
!   if (!insn_info)
!     {
!       /* Extract the information about definitions, uses etc from insn.  */
!       insn_info = &own_insn_info;
!       extract_insn_info (insn_info, insn, flags & PROP_EQUAL_NOTES);
!     }
! 
!   if (flags & PROP_AUTOINC)
!     {
!       /* Update reg_next_use.  */
!       update_next_use_sets (pbi, insn_info);
!     }
!   
!   /* If this is a call to `setjmp' et al, warn if any
!      non-volatile datum is live.  */
!   if ((flags & PROP_REG_INFO)
!       && GET_CODE (insn) == CALL_INSN
!       && find_reg_note (insn, REG_SETJMP, NULL))
!     IOR_REG_SET (regs_live_at_setjmp, pbi->reg_live);
! 
!   note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
!   if (flags & PROP_SCAN_DEAD_CODE)
!     {
!       insn_is_dead = insn_dead_p (pbi, insn_info, 0, REG_NOTES (insn));
!       libcall_is_dead = (insn_is_dead && note != 0
! 			 && libcall_dead_p (pbi, note, insn));
!     }
! 
!   CLEAR_REG_SET (pbi->new_set);
! 
!   /* Any regs live at the time of a call instruction must not go
!      in a register clobbered by calls.  Find all regs now live and
!      record this for them.  */
! 
!   if (!insn_is_dead
!       && GET_CODE (insn) == CALL_INSN && (flags & PROP_REG_INFO))
!     EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
! 			       { REG_N_CALLS_CROSSED (i)++; });
! 
!   /* Create REG_UNUSED notes.  */
!   if (flags & PROP_DEATH_NOTES)
!     create_unused_notes (pbi, insn_info, insn);
! 
!   /* Count the register info.  */
!   if (flags & PROP_REG_INFO)
!     count_register_info_for_sets (pbi, insn_info);
! 
!   /* Update liveness info.  */
!   update_live_by_sets (pbi, insn_info);
! 
!   /* If this set is a MEM, then it kills any aliased writes.
!      If this set is a REG, then it kills any MEMs which use the reg.  */
!   if (optimize && (flags & PROP_SCAN_DEAD_STORES))
!     update_mems_by_sets (pbi, insn_info);
! 
!   /* If an instruction consists of just dead store(s) on final pass,
!      delete it.  */
!   if ((flags & PROP_KILL_DEAD_CODE) && insn_is_dead)
!     return kill_dead_insn (pbi, insn, libcall_is_dead, note);
! 
!   /* If this is not the final pass, and this insn is copying the value of
!      a library call and it's dead, don't scan the insns that perform the
!      library call, so that the call's arguments are not marked live.  */
!   if (libcall_is_dead)
!     {
!       insn = XEXP (note, 0);
!       return PREV_INSN (insn);
!     }
!   
!   if (! insn_is_dead)
!     {
! #ifdef AUTO_INC_DEC
!       if (flags & PROP_AUTOINC)
! 	{
! 	  if (autoinc_transform (pbi, insn_info, insn))
! 	    {
! 	      /* The insn has changed, rescan it.  */
! 	      extract_insn_info (insn_info, insn, flags & PROP_EQUAL_NOTES);
! 
! 	      /* Sometimes we may have inserted something before INSN (such as a move)
! 		 when we make an auto-inc.  So ensure we will scan those insns.  */
! 	      prev = PREV_INSN (insn);
! 	    }
! 	}
! #endif
! 
!       /* Count the register info.  */
!       if (flags & PROP_REG_INFO)
! 	count_register_info_for_uses (pbi, insn_info);
! 
!       if (flags & PROP_DEATH_NOTES)
! 	create_dead_notes (pbi, insn_info, insn);
! 
!       /* Update liveness info.  */
!       update_live_by_uses (pbi, insn_info);
! 
!       if (flags & PROP_AUTOINC)
! 	{
! 	  /* Update reg_next_use.  */
! 	  update_next_use_uses (insn, pbi, insn_info);
! 	}
!     }
! 
!   /* Update list of tracked MEMs.  */
!   if (optimize && (flags & PROP_SCAN_DEAD_STORES))
!     update_mems_by_uses (pbi, insn_info);
! 
!   /* On final pass, update counts of how many insns in which each reg
!      is live.  */
!   if (flags & PROP_REG_INFO)
!     EXECUTE_IF_SET_IN_REG_SET (pbi->reg_live, 0, i,
! 			       { REG_LIVE_LENGTH (i)++; });
! 
!   return prev;
! }
! 
! /* Initialize a propagate_block_info struct for public consumption.
!    Note that the structure itself is opaque to this file, but that
!    the user can use the regsets provided here.  */
! 
! struct propagate_block_info *
! init_propagate_block_info (bb, live, local_set, cond_local_set, flags)
!      basic_block bb;
!      regset live, local_set, cond_local_set;
!      int flags;
! {
!   struct propagate_block_info *pbi = xmalloc (sizeof (*pbi));
! 
!   pbi->bb = bb;
!   pbi->reg_live = live;
!   pbi->mem_set_list = NULL_RTX;
!   pbi->mem_set_list_len = 0;
!   pbi->local_set = local_set;
!   pbi->cond_local_set = cond_local_set;
!   pbi->cc0_live = 0;
!   pbi->flags = flags;
! 
!   if (flags & PROP_AUTOINC)
!     pbi->reg_next_use = (rtx *) xcalloc (max_reg_num (), sizeof (rtx));
!   else
!     pbi->reg_next_use = NULL;
! 
!   pbi->new_set = BITMAP_XMALLOC ();
! 
! #ifdef HAVE_conditional_execution
!   pbi->reg_cond_dead = splay_tree_new (splay_tree_compare_ints, NULL,
! 				       free_reg_cond_life_info);
!   pbi->reg_cond_reg = BITMAP_XMALLOC ();
! 
!   /* If this block ends in a conditional branch, for each register live
!      from one side of the branch and not the other, record the register
!      as conditionally dead.  */
!   if (GET_CODE (bb->end) == JUMP_INSN
!       && any_condjump_p (bb->end))
      {
        regset_head diff_head;
        regset diff = INITIALIZE_REG_SET (diff_head);
*************** propagate_block (bb, live, local_set, co
*** 2082,2095 ****
    changed = 0;
    for (insn = bb->end;; insn = prev)
      {
!       /* If this is a call to `setjmp' et al, warn if any
! 	 non-volatile datum is live.  */
!       if ((flags & PROP_REG_INFO)
! 	  && GET_CODE (insn) == CALL_INSN
! 	  && find_reg_note (insn, REG_SETJMP, NULL))
! 	IOR_REG_SET (regs_live_at_setjmp, pbi->reg_live);
! 
!       prev = propagate_one_insn (pbi, insn);
        changed |= NEXT_INSN (prev) != insn;
  
        if (insn == bb->head)
--- 2844,2850 ----
    changed = 0;
    for (insn = bb->end;; insn = prev)
      {
!       prev = propagate_one_insn (pbi, insn, NULL);
        changed |= NEXT_INSN (prev) != insn;
  
        if (insn == bb->head)
*************** propagate_block (bb, live, local_set, co
*** 2111,2128 ****
     pertaining to the insn.  */
  
  static int
! insn_dead_p (pbi, x, call_ok, notes)
       struct propagate_block_info *pbi;
!      rtx x;
       int call_ok;
       rtx notes ATTRIBUTE_UNUSED;
  {
!   enum rtx_code code = GET_CODE (x);
  
    /* Don't eliminate insns that may trap.  */
!   if (flag_non_call_exceptions && may_trap_p (x))
      return 0;
  
  #ifdef AUTO_INC_DEC
    /* As flow is invoked after combine, we must take existing AUTO_INC
       expressions into account.  */
--- 2866,2921 ----
     pertaining to the insn.  */
  
  static int
! insn_dead_p (pbi, insn_info, call_ok, notes)
       struct propagate_block_info *pbi;
!      struct insn_extract *insn_info;
       int call_ok;
       rtx notes ATTRIBUTE_UNUSED;
  {
!   rtx reg, expr, canon_r, temp, r;
!   int regno;
  
    /* Don't eliminate insns that may trap.  */
!   if (flag_non_call_exceptions && insn_info->may_trap)
!     return 0;
! 
!   /* Do not elliminate calls.  */
!   if (!call_ok && insn_info->contains_call)
!     return 0;
! 
!   /* Don't eliminate loads from volatile memory or volatile asms.  */
!   if (insn_info->contains_volatile_ref)
      return 0;
  
+   /* We generally should not elliminate something else than sets.  */
+   if (insn_info->not_set)
+     {
+       if (!insn_info->registers_altered)
+ 	return 0;
+ 
+       /* A CLOBBER of a pseudo-register that is dead serves no purpose.  That
+ 	 is not necessarily true for hard registers.  */
+       for (expr = insn_info->registers_altered; expr; expr = XEXP (expr, 1))
+ 	{
+ 	  int flags;
+ 
+ 	  reg = XEXP (expr, 0);
+ 	  flags = INTVAL (XEXP (reg, 0));
+ 	  reg = XEXP (reg, 1);
+ 	  reg = XEXP (reg, 1);  /* Skip cond.  */
+ 	  reg = XEXP (reg, 0);
+ 
+ 	  if (!flags & IIF_TOPLEV_CLOBBER)
+ 	    return 0;
+ 	  if (GET_CODE (reg) != REG)
+ 	    return 0;
+ 	  if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+ 	      || REGNO_REG_SET_P (pbi->reg_live, REGNO (reg)))
+ 	    return 0;
+ 	}
+       return 1;
+     }
+ 
  #ifdef AUTO_INC_DEC
    /* As flow is invoked after combine, we must take existing AUTO_INC
       expressions into account.  */
*************** insn_dead_p (pbi, x, call_ok, notes)
*** 2140,2295 ****
      }
  #endif
  
-   /* If setting something that's a reg or part of one,
-      see if that register's altered value will be live.  */
- 
-   if (code == SET)
-     {
-       rtx r = SET_DEST (x);
- 
  #ifdef HAVE_cc0
!       if (GET_CODE (r) == CC0)
! 	return ! pbi->cc0_live;
  #endif
  
!       /* A SET that is a subroutine call cannot be dead.  */
!       if (GET_CODE (SET_SRC (x)) == CALL)
! 	{
! 	  if (! call_ok)
! 	    return 0;
! 	}
! 
!       /* Don't eliminate loads from volatile memory or volatile asms.  */
!       else if (volatile_refs_p (SET_SRC (x)))
  	return 0;
  
!       if (GET_CODE (r) == MEM)
! 	{
! 	  rtx temp, canon_r;
! 
! 	  if (MEM_VOLATILE_P (r) || GET_MODE (r) == BLKmode)
! 	    return 0;
! 
! 	  canon_r = canon_rtx (r);
! 
! 	  /* Walk the set of memory locations we are currently tracking
! 	     and see if one is an identical match to this memory location.
! 	     If so, this memory write is dead (remember, we're walking
! 	     backwards from the end of the block to the start).  Since
! 	     rtx_equal_p does not check the alias set or flags, we also
! 	     must have the potential for them to conflict (anti_dependence).  */
! 	  for (temp = pbi->mem_set_list; temp != 0; temp = XEXP (temp, 1))
! 	    if (anti_dependence (r, XEXP (temp, 0)))
! 	      {
! 		rtx mem = XEXP (temp, 0);
  
! 		if (rtx_equal_p (XEXP (canon_r, 0), XEXP (mem, 0))
! 		    && (GET_MODE_SIZE (GET_MODE (canon_r))
! 			<= GET_MODE_SIZE (GET_MODE (mem))))
! 		  return 1;
  
  #ifdef AUTO_INC_DEC
! 		/* Check if memory reference matches an auto increment. Only
! 		   post increment/decrement or modify are valid.  */
! 		if (GET_MODE (mem) == GET_MODE (r)
! 		    && (GET_CODE (XEXP (mem, 0)) == POST_DEC
! 			|| GET_CODE (XEXP (mem, 0)) == POST_INC
! 			|| GET_CODE (XEXP (mem, 0)) == POST_MODIFY)
! 		    && GET_MODE (XEXP (mem, 0)) == GET_MODE (r)
! 		    && rtx_equal_p (XEXP (XEXP (mem, 0), 0), XEXP (r, 0)))
! 		  return 1;
  #endif
! 	      }
! 	}
!       else
! 	{
! 	  while (GET_CODE (r) == SUBREG
! 		 || GET_CODE (r) == STRICT_LOW_PART
! 		 || GET_CODE (r) == ZERO_EXTRACT)
! 	    r = XEXP (r, 0);
  
! 	  if (GET_CODE (r) == REG)
! 	    {
! 	      int regno = REGNO (r);
  
! 	      /* Obvious.  */
! 	      if (REGNO_REG_SET_P (pbi->reg_live, regno))
! 		return 0;
! 
! 	      /* If this is a hard register, verify that subsequent
! 		 words are not needed.  */
! 	      if (regno < FIRST_PSEUDO_REGISTER)
! 		{
! 		  int n = HARD_REGNO_NREGS (regno, GET_MODE (r));
  
! 		  while (--n > 0)
! 		    if (REGNO_REG_SET_P (pbi->reg_live, regno+n))
! 		      return 0;
! 		}
  
! 	      /* Don't delete insns to set global regs.  */
! 	      if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
! 		return 0;
! 
! 	      /* Make sure insns to set the stack pointer aren't deleted.  */
! 	      if (regno == STACK_POINTER_REGNUM)
! 		return 0;
! 
! 	      /* ??? These bits might be redundant with the force live bits
! 		 in calculate_global_regs_live.  We would delete from
! 		 sequential sets; whether this actually affects real code
! 		 for anything but the stack pointer I don't know.  */
! 	      /* Make sure insns to set the frame pointer aren't deleted.  */
! 	      if (regno == FRAME_POINTER_REGNUM
! 		  && (! reload_completed || frame_pointer_needed))
! 		return 0;
  #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
! 	      if (regno == HARD_FRAME_POINTER_REGNUM
! 		  && (! reload_completed || frame_pointer_needed))
! 		return 0;
  #endif
  
  #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! 	      /* Make sure insns to set arg pointer are never deleted
! 		 (if the arg pointer isn't fixed, there will be a USE
! 		 for it, so we can treat it normally).  */
! 	      if (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
! 		return 0;
  #endif
  
! 	      /* Otherwise, the set is dead.  */
! 	      return 1;
! 	    }
! 	}
!     }
! 
!   /* If performing several activities, insn is dead if each activity
!      is individually dead.  Also, CLOBBERs and USEs can be ignored; a
!      CLOBBER or USE that's inside a PARALLEL doesn't make the insn
!      worth keeping.  */
!   else if (code == PARALLEL)
!     {
!       int i = XVECLEN (x, 0);
! 
!       for (i--; i >= 0; i--)
! 	if (GET_CODE (XVECEXP (x, 0, i)) != CLOBBER
! 	    && GET_CODE (XVECEXP (x, 0, i)) != USE
! 	    && ! insn_dead_p (pbi, XVECEXP (x, 0, i), call_ok, NULL_RTX))
! 	  return 0;
  
!       return 1;
      }
  
!   /* A CLOBBER of a pseudo-register that is dead serves no purpose.  That
!      is not necessarily true for hard registers.  */
!   else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == REG
! 	   && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER
! 	   && ! REGNO_REG_SET_P (pbi->reg_live, REGNO (XEXP (x, 0))))
!     return 1;
! 
!   /* We do not check other CLOBBER or USE here.  An insn consisting of just
!      a CLOBBER or just a USE should not be deleted.  */
!   return 0;
  }
  
  /* If INSN is the last insn in a libcall, and assuming INSN is dead,
--- 2933,3040 ----
      }
  #endif
  
  #ifdef HAVE_cc0
!   /* If it sets live cc0, it is not dead.  */
!   if (insn_info->sets_cc0 && pbi->cc0_live)
!     return 0;
  #endif
  
!   /* Check memory sets.  */
!   for (reg = insn_info->mems_set; reg; reg = XEXP (reg, 1))
!     {
!       expr = XEXP (XEXP (reg, 0), 1);
!       if (MEM_VOLATILE_P (expr) || GET_MODE (expr) == BLKmode)
  	return 0;
+       canon_r = canon_rtx (expr);
  
!       /* Walk the set of memory locations we are currently tracking
! 	 and see if one is an identical match to this memory location.
! 	 If so, this memory write is dead (remember, we're walking
! 	 backwards from the end of the block to the start).  Since
! 	 rtx_equal_p does not check the alias set or flags, we also
! 	 must have the potential for them to conflict (anti_dependence).  */
!       for (temp = pbi->mem_set_list; temp != 0; temp = XEXP (temp, 1))
! 	if (anti_dependence (expr, XEXP (temp, 0)))
! 	  {
! 	    rtx mem = XEXP (temp, 0);
  
! 	    if (rtx_equal_p (XEXP (canon_r, 0), XEXP (mem, 0))
! 		&& (GET_MODE_SIZE (GET_MODE (canon_r))
! 		    <= GET_MODE_SIZE (GET_MODE (mem))))
! 	      goto next;
  
  #ifdef AUTO_INC_DEC
! 	    /* Check if memory reference matches an auto increment. Only
! 	       post increment/decrement or modify are valid.  */
! 	    if (GET_MODE (mem) == GET_MODE (expr)
! 		&& (GET_CODE (XEXP (mem, 0)) == POST_DEC
! 		    || GET_CODE (XEXP (mem, 0)) == POST_INC
! 		    || GET_CODE (XEXP (mem, 0)) == POST_MODIFY)
! 		&& GET_MODE (XEXP (mem, 0)) == GET_MODE (expr)
! 		&& rtx_equal_p (XEXP (XEXP (mem, 0), 0), XEXP (expr, 0)))
! 	      goto next;
  #endif
! 	  }
  
!       /* The set is not dead.  */
!       return 0;
! next:;
!     }
  
!   /* If setting something that's a reg or part of one,
!      see if that register's altered value will be live.  */
!   for (reg = insn_info->registers_set; reg; reg = XEXP (reg, 1))
!     {
!       expr = XEXP (reg, 0);
!       expr = XEXP (expr, 1);  /* Skip flags.  */
!       expr = XEXP (expr, 1);  /* Skip cond.  */
! 
!       regno = REGNO (XEXP (expr, 0));
!       
!       /* Don't delete insns to set global regs.  */
!       if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
! 	return 0;
  
!       /* Make sure insns to set the stack pointer aren't deleted.  */
!       if (regno == STACK_POINTER_REGNUM)
! 	return 0;
  
!       /* ??? These bits might be redundant with the force live bits
! 	 in calculate_global_regs_live.  We would delete from
! 	 sequential sets; whether this actually affects real code
! 	 for anything but the stack pointer I don't know.  */
!       /* Make sure insns to set the frame pointer aren't deleted.  */
!       if (regno == FRAME_POINTER_REGNUM
! 	  && (! reload_completed || frame_pointer_needed))
! 	return 0;
  #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
!       if (regno == HARD_FRAME_POINTER_REGNUM
! 	  && (! reload_completed || frame_pointer_needed))
! 	return 0;
  #endif
  
  #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
!       /* Make sure insns to set arg pointer are never deleted
! 	 (if the arg pointer isn't fixed, there will be a USE
! 	 for it, so we can treat it normally).  */
!       if (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
! 	return 0;
  #endif
  
!       /* Check whether register (or subsequent regs in case of
! 	 hard regs) is not live.  */
!       for (r = expr; r; r = XEXP (r, 1))
! 	{
! 	  regno = REGNO (XEXP (r, 0));
  
! 	  /* Obvious.  */
! 	  if (REGNO_REG_SET_P (pbi->reg_live, regno))
! 	    return 0;
! 	}
      }
  
!   /* If we got here, the instruction is dead.  */
!   return 1;
  }
  
  /* If INSN is the last insn in a libcall, and assuming INSN is dead,
*************** libcall_dead_p (pbi, note, insn)
*** 2313,2318 ****
--- 3058,3064 ----
       rtx note;
       rtx insn;
  {
+   struct insn_extract call_info;
    rtx x = single_set (insn);
  
    if (x)
*************** libcall_dead_p (pbi, note, insn)
*** 2322,2329 ****
        if (GET_CODE (r) == REG)
  	{
  	  rtx call = XEXP (note, 0);
- 	  rtx call_pat;
- 	  int i;
  
  	  /* Find the call insn.  */
  	  while (call != insn && GET_CODE (call) != CALL_INSN)
--- 3068,3073 ----
*************** libcall_dead_p (pbi, note, insn)
*** 2334,2359 ****
  	  if (call == insn)
  	    return 0;
  
! 	  /* See if the hard reg holding the value is dead.
! 	     If this is a PARALLEL, find the call within it.  */
! 	  call_pat = PATTERN (call);
! 	  if (GET_CODE (call_pat) == PARALLEL)
! 	    {
! 	      for (i = XVECLEN (call_pat, 0) - 1; i >= 0; i--)
! 		if (GET_CODE (XVECEXP (call_pat, 0, i)) == SET
! 		    && GET_CODE (SET_SRC (XVECEXP (call_pat, 0, i))) == CALL)
! 		  break;
! 
! 	      /* This may be a library call that is returning a value
! 		 via invisible pointer.  Do nothing special, since
! 		 ordinary death handling can understand these insns.  */
! 	      if (i < 0)
! 		return 0;
! 
! 	      call_pat = XVECEXP (call_pat, 0, i);
! 	    }
! 
! 	  return insn_dead_p (pbi, call_pat, 1, REG_NOTES (call));
  	}
      }
    return 1;
--- 3078,3086 ----
  	  if (call == insn)
  	    return 0;
  
! 	  /* Check that the call is dead.  */
! 	  extract_insn_info (&call_info, call, 0);
! 	  return insn_dead_p (pbi, &call_info, 1, REG_NOTES (call));
  	}
      }
    return 1;
*************** invalidate_mems_from_set (pbi, exp)
*** 2484,2880 ****
  	  else
  	    pbi->mem_set_list = next;
  	  free_EXPR_LIST_node (temp);
! 	  pbi->mem_set_list_len--;
! 	}
!       else
! 	prev = temp;
!       temp = next;
!     }
! }
! 
! /* Process the registers that are set within X.  Their bits are set to
!    1 in the regset DEAD, because they are dead prior to this insn.
! 
!    If INSN is nonzero, it is the insn being processed.
! 
!    FLAGS is the set of operations to perform.  */
! 
! static void
! mark_set_regs (pbi, x, insn)
!      struct propagate_block_info *pbi;
!      rtx x, insn;
! {
!   rtx cond = NULL_RTX;
!   rtx link;
!   enum rtx_code code;
! 
!   if (insn)
!     for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
!       {
! 	if (REG_NOTE_KIND (link) == REG_INC)
! 	  mark_set_1 (pbi, SET, XEXP (link, 0),
! 		      (GET_CODE (x) == COND_EXEC
! 		       ? COND_EXEC_TEST (x) : NULL_RTX),
! 		      insn, pbi->flags);
!       }
!  retry:
!   switch (code = GET_CODE (x))
!     {
!     case SET:
!     case CLOBBER:
!       mark_set_1 (pbi, code, SET_DEST (x), cond, insn, pbi->flags);
!       return;
! 
!     case COND_EXEC:
!       cond = COND_EXEC_TEST (x);
!       x = COND_EXEC_CODE (x);
!       goto retry;
! 
!     case PARALLEL:
!       {
! 	int i;
! 
! 	for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
! 	  {
! 	    rtx sub = XVECEXP (x, 0, i);
! 	    switch (code = GET_CODE (sub))
! 	      {
! 	      case COND_EXEC:
! 		if (cond != NULL_RTX)
! 		  abort ();
! 
! 		cond = COND_EXEC_TEST (sub);
! 		sub = COND_EXEC_CODE (sub);
! 		if (GET_CODE (sub) != SET && GET_CODE (sub) != CLOBBER)
! 		  break;
! 		/* Fall through.  */
! 
! 	      case SET:
! 	      case CLOBBER:
! 		mark_set_1 (pbi, code, SET_DEST (sub), cond, insn, pbi->flags);
! 		break;
! 
! 	      default:
! 		break;
! 	      }
! 	  }
! 	break;
!       }
! 
!     default:
!       break;
!     }
! }
! 
! /* Process a single set, which appears in INSN.  REG (which may not
!    actually be a REG, it may also be a SUBREG, PARALLEL, etc.) is
!    being set using the CODE (which may be SET, CLOBBER, or COND_EXEC).
!    If the set is conditional (because it appear in a COND_EXEC), COND
!    will be the condition.  */
! 
! static void
! mark_set_1 (pbi, code, reg, cond, insn, flags)
!      struct propagate_block_info *pbi;
!      enum rtx_code code;
!      rtx reg, cond, insn;
!      int flags;
! {
!   int regno_first = -1, regno_last = -1;
!   unsigned long not_dead = 0;
!   int i;
! 
!   /* Modifying just one hardware register of a multi-reg value or just a
!      byte field of a register does not mean the value from before this insn
!      is now dead.  Of course, if it was dead after it's unused now.  */
! 
!   switch (GET_CODE (reg))
!     {
!     case PARALLEL:
!       /* Some targets place small structures in registers for return values of
! 	 functions.  We have to detect this case specially here to get correct
! 	 flow information.  */
!       for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
! 	if (XEXP (XVECEXP (reg, 0, i), 0) != 0)
! 	  mark_set_1 (pbi, code, XEXP (XVECEXP (reg, 0, i), 0), cond, insn,
! 		      flags);
!       return;
! 
!     case ZERO_EXTRACT:
!     case SIGN_EXTRACT:
!     case STRICT_LOW_PART:
!       /* ??? Assumes STRICT_LOW_PART not used on multi-word registers.  */
!       do
! 	reg = XEXP (reg, 0);
!       while (GET_CODE (reg) == SUBREG
! 	     || GET_CODE (reg) == ZERO_EXTRACT
! 	     || GET_CODE (reg) == SIGN_EXTRACT
! 	     || GET_CODE (reg) == STRICT_LOW_PART);
!       if (GET_CODE (reg) == MEM)
! 	break;
!       not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live, REGNO (reg));
!       /* Fall through.  */
! 
!     case REG:
!       regno_last = regno_first = REGNO (reg);
!       if (regno_first < FIRST_PSEUDO_REGISTER)
! 	regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
!       break;
! 
!     case SUBREG:
!       if (GET_CODE (SUBREG_REG (reg)) == REG)
! 	{
! 	  enum machine_mode outer_mode = GET_MODE (reg);
! 	  enum machine_mode inner_mode = GET_MODE (SUBREG_REG (reg));
! 
! 	  /* Identify the range of registers affected.  This is moderately
! 	     tricky for hard registers.  See alter_subreg.  */
! 
! 	  regno_last = regno_first = REGNO (SUBREG_REG (reg));
! 	  if (regno_first < FIRST_PSEUDO_REGISTER)
! 	    {
! 	      regno_first += subreg_regno_offset (regno_first, inner_mode,
! 						  SUBREG_BYTE (reg),
! 						  outer_mode);
! 	      regno_last = (regno_first
! 			    + HARD_REGNO_NREGS (regno_first, outer_mode) - 1);
! 
! 	      /* Since we've just adjusted the register number ranges, make
! 		 sure REG matches.  Otherwise some_was_live will be clear
! 		 when it shouldn't have been, and we'll create incorrect
! 		 REG_UNUSED notes.  */
! 	      reg = gen_rtx_REG (outer_mode, regno_first);
! 	    }
! 	  else
! 	    {
! 	      /* If the number of words in the subreg is less than the number
! 		 of words in the full register, we have a well-defined partial
! 		 set.  Otherwise the high bits are undefined.
! 
! 		 This is only really applicable to pseudos, since we just took
! 		 care of multi-word hard registers.  */
! 	      if (((GET_MODE_SIZE (outer_mode)
! 		    + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
! 		  < ((GET_MODE_SIZE (inner_mode)
! 		      + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
! 		not_dead = (unsigned long) REGNO_REG_SET_P (pbi->reg_live,
! 							    regno_first);
! 
! 	      reg = SUBREG_REG (reg);
! 	    }
! 	}
!       else
! 	reg = SUBREG_REG (reg);
!       break;
! 
!     default:
!       break;
!     }
! 
!   /* If this set is a MEM, then it kills any aliased writes.
!      If this set is a REG, then it kills any MEMs which use the reg.  */
!   if (optimize && (flags & PROP_SCAN_DEAD_STORES))
!     {
!       if (GET_CODE (reg) == REG)
! 	invalidate_mems_from_set (pbi, reg);
! 
!       /* If the memory reference had embedded side effects (autoincrement
! 	 address modes.  Then we may need to kill some entries on the
! 	 memory set list.  */
!       if (insn && GET_CODE (reg) == MEM)
! 	for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi);
! 
!       if (GET_CODE (reg) == MEM && ! side_effects_p (reg)
! 	  /* ??? With more effort we could track conditional memory life.  */
! 	  && ! cond)
! 	add_to_mem_set_list (pbi, canon_rtx (reg));
!     }
! 
!   if (GET_CODE (reg) == REG
!       && ! (regno_first == FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
!       && ! (regno_first == HARD_FRAME_POINTER_REGNUM
! 	    && (! reload_completed || frame_pointer_needed))
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
!       && ! (regno_first == ARG_POINTER_REGNUM && fixed_regs[regno_first])
! #endif
!       )
!     {
!       int some_was_live = 0, some_was_dead = 0;
! 
!       for (i = regno_first; i <= regno_last; ++i)
! 	{
! 	  int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i);
! 	  if (pbi->local_set)
! 	    {
! 	      /* Order of the set operation matters here since both
! 		 sets may be the same.  */
! 	      CLEAR_REGNO_REG_SET (pbi->cond_local_set, i);
! 	      if (cond != NULL_RTX
! 		  && ! REGNO_REG_SET_P (pbi->local_set, i))
! 		SET_REGNO_REG_SET (pbi->cond_local_set, i);
! 	      else
! 		SET_REGNO_REG_SET (pbi->local_set, i);
! 	    }
! 	  if (code != CLOBBER)
! 	    SET_REGNO_REG_SET (pbi->new_set, i);
! 
! 	  some_was_live |= needed_regno;
! 	  some_was_dead |= ! needed_regno;
! 	}
! 
! #ifdef HAVE_conditional_execution
!       /* Consider conditional death in deciding that the register needs
! 	 a death note.  */
!       if (some_was_live && ! not_dead
! 	  /* The stack pointer is never dead.  Well, not strictly true,
! 	     but it's very difficult to tell from here.  Hopefully
! 	     combine_stack_adjustments will fix up the most egregious
! 	     errors.  */
! 	  && regno_first != STACK_POINTER_REGNUM)
! 	{
! 	  for (i = regno_first; i <= regno_last; ++i)
! 	    if (! mark_regno_cond_dead (pbi, i, cond))
! 	      not_dead |= ((unsigned long) 1) << (i - regno_first);
! 	}
! #endif
! 
!       /* Additional data to record if this is the final pass.  */
!       if (flags & (PROP_LOG_LINKS | PROP_REG_INFO
! 		   | PROP_DEATH_NOTES | PROP_AUTOINC))
! 	{
! 	  rtx y;
! 	  int blocknum = pbi->bb->index;
! 
! 	  y = NULL_RTX;
! 	  if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
! 	    {
! 	      y = pbi->reg_next_use[regno_first];
! 
! 	      /* The next use is no longer next, since a store intervenes.  */
! 	      for (i = regno_first; i <= regno_last; ++i)
! 		pbi->reg_next_use[i] = 0;
! 	    }
! 
! 	  if (flags & PROP_REG_INFO)
! 	    {
! 	      for (i = regno_first; i <= regno_last; ++i)
! 		{
! 		  /* Count (weighted) references, stores, etc.  This counts a
! 		     register twice if it is modified, but that is correct.  */
! 		  REG_N_SETS (i) += 1;
! 		  REG_N_REFS (i) += 1;
! 		  REG_FREQ (i) += REG_FREQ_FROM_BB (pbi->bb);
! 
! 	          /* The insns where a reg is live are normally counted
! 		     elsewhere, but we want the count to include the insn
! 		     where the reg is set, and the normal counting mechanism
! 		     would not count it.  */
! 		  REG_LIVE_LENGTH (i) += 1;
! 		}
! 
! 	      /* If this is a hard reg, record this function uses the reg.  */
! 	      if (regno_first < FIRST_PSEUDO_REGISTER)
! 		{
! 		  for (i = regno_first; i <= regno_last; i++)
! 		    regs_ever_live[i] = 1;
! 		}
! 	      else
! 		{
! 		  /* Keep track of which basic blocks each reg appears in.  */
! 		  if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
! 		    REG_BASIC_BLOCK (regno_first) = blocknum;
! 		  else if (REG_BASIC_BLOCK (regno_first) != blocknum)
! 		    REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
! 		}
! 	    }
! 
! 	  if (! some_was_dead)
! 	    {
! 	      if (flags & PROP_LOG_LINKS)
! 		{
! 		  /* Make a logical link from the next following insn
! 		     that uses this register, back to this insn.
! 		     The following insns have already been processed.
! 
! 		     We don't build a LOG_LINK for hard registers containing
! 		     in ASM_OPERANDs.  If these registers get replaced,
! 		     we might wind up changing the semantics of the insn,
! 		     even if reload can make what appear to be valid
! 		     assignments later.  */
! 		  if (y && (BLOCK_NUM (y) == blocknum)
! 		      && (regno_first >= FIRST_PSEUDO_REGISTER
! 			  || asm_noperands (PATTERN (y)) < 0))
! 		    LOG_LINKS (y) = alloc_INSN_LIST (insn, LOG_LINKS (y));
! 		}
! 	    }
! 	  else if (not_dead)
! 	    ;
! 	  else if (! some_was_live)
! 	    {
! 	      if (flags & PROP_REG_INFO)
! 		REG_N_DEATHS (regno_first) += 1;
! 
! 	      if (flags & PROP_DEATH_NOTES)
! 		{
! 		  /* Note that dead stores have already been deleted
! 		     when possible.  If we get here, we have found a
! 		     dead store that cannot be eliminated (because the
! 		     same insn does something useful).  Indicate this
! 		     by marking the reg being set as dying here.  */
! 		  REG_NOTES (insn)
! 		    = alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
! 		}
! 	    }
! 	  else
! 	    {
! 	      if (flags & PROP_DEATH_NOTES)
! 		{
! 		  /* This is a case where we have a multi-word hard register
! 		     and some, but not all, of the words of the register are
! 		     needed in subsequent insns.  Write REG_UNUSED notes
! 		     for those parts that were not needed.  This case should
! 		     be rare.  */
! 
! 		  for (i = regno_first; i <= regno_last; ++i)
! 		    if (! REGNO_REG_SET_P (pbi->reg_live, i))
! 		      REG_NOTES (insn)
! 			= alloc_EXPR_LIST (REG_UNUSED,
! 					   regno_reg_rtx[i],
! 					   REG_NOTES (insn));
! 		}
! 	    }
! 	}
! 
!       /* Mark the register as being dead.  */
!       if (some_was_live
! 	  /* The stack pointer is never dead.  Well, not strictly true,
! 	     but it's very difficult to tell from here.  Hopefully
! 	     combine_stack_adjustments will fix up the most egregious
! 	     errors.  */
! 	  && regno_first != STACK_POINTER_REGNUM)
! 	{
! 	  for (i = regno_first; i <= regno_last; ++i)
! 	    if (!(not_dead & (((unsigned long) 1) << (i - regno_first))))
! 	      CLEAR_REGNO_REG_SET (pbi->reg_live, i);
! 	}
!     }
!   else if (GET_CODE (reg) == REG)
!     {
!       if (flags & (PROP_LOG_LINKS | PROP_AUTOINC))
! 	pbi->reg_next_use[regno_first] = 0;
!     }
! 
!   /* If this is the last pass and this is a SCRATCH, show it will be dying
!      here and count it.  */
!   else if (GET_CODE (reg) == SCRATCH)
!     {
!       if (flags & PROP_DEATH_NOTES)
! 	REG_NOTES (insn)
! 	  = alloc_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn));
      }
  }
  
  #ifdef HAVE_conditional_execution
  /* Mark REGNO conditionally dead.
--- 3211,3224 ----
  	  else
  	    pbi->mem_set_list = next;
  	  free_EXPR_LIST_node (temp);
! 	  pbi->mem_set_list_len--;
! 	}
!       else
! 	prev = temp;
!       temp = next;
      }
  }
+ 
  
  #ifdef HAVE_conditional_execution
  /* Mark REGNO conditionally dead.
*************** elim_reg_cond (x, regno)
*** 3311,3317 ****
     that has a single set whose source is a PLUS of INCR_REG and something
     else.  */
  
! static void
  attempt_auto_inc (pbi, inc, insn, mem, incr, incr_reg)
       struct propagate_block_info *pbi;
       rtx inc, insn, mem, incr, incr_reg;
--- 3655,3661 ----
     that has a single set whose source is a PLUS of INCR_REG and something
     else.  */
  
! static int
  attempt_auto_inc (pbi, inc, insn, mem, incr, incr_reg)
       struct propagate_block_info *pbi;
       rtx inc, insn, mem, incr, incr_reg;
*************** attempt_auto_inc (pbi, inc, insn, mem, i
*** 3324,3330 ****
  
    /* Make sure this reg appears only once in this insn.  */
    if (count_occurrences (PATTERN (insn), incr_reg, 1) != 1)
!     return;
  
    if (dead_or_set_p (incr, incr_reg)
        /* Mustn't autoinc an eliminable register.  */
--- 3668,3674 ----
  
    /* Make sure this reg appears only once in this insn.  */
    if (count_occurrences (PATTERN (insn), incr_reg, 1) != 1)
!     return 0;
  
    if (dead_or_set_p (incr, incr_reg)
        /* Mustn't autoinc an eliminable register.  */
*************** attempt_auto_inc (pbi, inc, insn, mem, i
*** 3335,3341 ****
  	 we can't, we are done.  Otherwise, we will do any
  	 needed updates below.  */
        if (! validate_change (insn, &XEXP (mem, 0), inc, 0))
! 	return;
      }
    else if (GET_CODE (q) == REG
  	   /* PREV_INSN used here to check the semi-open interval
--- 3679,3685 ----
  	 we can't, we are done.  Otherwise, we will do any
  	 needed updates below.  */
        if (! validate_change (insn, &XEXP (mem, 0), inc, 0))
! 	return 0;
      }
    else if (GET_CODE (q) == REG
  	   /* PREV_INSN used here to check the semi-open interval
*************** attempt_auto_inc (pbi, inc, insn, mem, i
*** 3367,3373 ****
        validate_change (insn, &XEXP (mem, 0), inc, 1);
        validate_change (incr, &XEXP (y, opnum), q, 1);
        if (! apply_change_group ())
! 	return;
  
        /* We now know we'll be doing this change, so emit the
  	 new insn(s) and do the updates.  */
--- 3711,3717 ----
        validate_change (insn, &XEXP (mem, 0), inc, 1);
        validate_change (incr, &XEXP (y, opnum), q, 1);
        if (! apply_change_group ())
! 	return 0;
  
        /* We now know we'll be doing this change, so emit the
  	 new insn(s) and do the updates.  */
*************** attempt_auto_inc (pbi, inc, insn, mem, i
*** 3406,3412 ****
        clear_reg_alias_info (q);
      }
    else
!     return;
  
    /* If we haven't returned, it means we were able to make the
       auto-inc, so update the status.  First, record that this insn
--- 3750,3756 ----
        clear_reg_alias_info (q);
      }
    else
!     return 0;
  
    /* If we haven't returned, it means we were able to make the
       auto-inc, so update the status.  First, record that this insn
*************** attempt_auto_inc (pbi, inc, insn, mem, i
*** 3450,3461 ****
  	 even though it isn't a SET in rtl.  */
        REG_N_SETS (regno)++;
      }
  }
  
  /* X is a MEM found in INSN.  See if we can convert it into an auto-increment
     reference.  */
  
! static void
  find_auto_inc (pbi, x, insn)
       struct propagate_block_info *pbi;
       rtx x;
--- 3794,3807 ----
  	 even though it isn't a SET in rtl.  */
        REG_N_SETS (regno)++;
      }
+ 
+   return 1;
  }
  
  /* X is a MEM found in INSN.  See if we can convert it into an auto-increment
     reference.  */
  
! static int
  find_auto_inc (pbi, x, insn)
       struct propagate_block_info *pbi;
       rtx x;
*************** find_auto_inc (pbi, x, insn)
*** 3466,3474 ****
    rtx set, y, incr, inc_val;
    int regno;
    int size = GET_MODE_SIZE (GET_MODE (x));
  
    if (GET_CODE (insn) == JUMP_INSN)
!     return;
  
    /* Here we detect use of an index register which might be good for
       postincrement, postdecrement, preincrement, or predecrement.  */
--- 3812,3821 ----
    rtx set, y, incr, inc_val;
    int regno;
    int size = GET_MODE_SIZE (GET_MODE (x));
+   int changed = 0;
  
    if (GET_CODE (insn) == JUMP_INSN)
!     return 0;
  
    /* Here we detect use of an index register which might be good for
       postincrement, postdecrement, preincrement, or predecrement.  */
*************** find_auto_inc (pbi, x, insn)
*** 3477,3529 ****
      offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
  
    if (GET_CODE (addr) != REG)
!     return;
  
    regno = REGNO (addr);
  
    /* Is the next use an increment that might make auto-increment? */
    incr = pbi->reg_next_use[regno];
    if (incr == 0 || BLOCK_NUM (incr) != BLOCK_NUM (insn))
!     return;
    set = single_set (incr);
    if (set == 0 || GET_CODE (set) != SET)
!     return;
    y = SET_SRC (set);
  
    if (GET_CODE (y) != PLUS)
!     return;
  
    if (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) == REGNO (addr))
      inc_val = XEXP (y, 1);
    else if (REG_P (XEXP (y, 1)) && REGNO (XEXP (y, 1)) == REGNO (addr))
      inc_val = XEXP (y, 0);
    else
!     return;
  
    if (GET_CODE (inc_val) == CONST_INT)
      {
        if (HAVE_POST_INCREMENT
  	  && (INTVAL (inc_val) == size && offset == 0))
! 	attempt_auto_inc (pbi, gen_rtx_POST_INC (Pmode, addr), insn, x,
! 			  incr, addr);
        else if (HAVE_POST_DECREMENT
  	       && (INTVAL (inc_val) == -size && offset == 0))
! 	attempt_auto_inc (pbi, gen_rtx_POST_DEC (Pmode, addr), insn, x,
! 			  incr, addr);
        else if (HAVE_PRE_INCREMENT
  	       && (INTVAL (inc_val) == size && offset == size))
! 	attempt_auto_inc (pbi, gen_rtx_PRE_INC (Pmode, addr), insn, x,
! 			  incr, addr);
        else if (HAVE_PRE_DECREMENT
  	       && (INTVAL (inc_val) == -size && offset == -size))
! 	attempt_auto_inc (pbi, gen_rtx_PRE_DEC (Pmode, addr), insn, x,
! 			  incr, addr);
        else if (HAVE_POST_MODIFY_DISP && offset == 0)
! 	attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
! 						    gen_rtx_PLUS (Pmode,
! 								  addr,
! 								  inc_val)),
! 			  insn, x, incr, addr);
      }
    else if (GET_CODE (inc_val) == REG
  	   && ! reg_set_between_p (inc_val, PREV_INSN (insn),
--- 3824,3877 ----
      offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
  
    if (GET_CODE (addr) != REG)
!     return 0;
  
    regno = REGNO (addr);
  
    /* Is the next use an increment that might make auto-increment? */
    incr = pbi->reg_next_use[regno];
    if (incr == 0 || BLOCK_NUM (incr) != BLOCK_NUM (insn))
!     return 0;
    set = single_set (incr);
    if (set == 0 || GET_CODE (set) != SET)
!     return 0;
    y = SET_SRC (set);
  
    if (GET_CODE (y) != PLUS)
!     return 0;
  
    if (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) == REGNO (addr))
      inc_val = XEXP (y, 1);
    else if (REG_P (XEXP (y, 1)) && REGNO (XEXP (y, 1)) == REGNO (addr))
      inc_val = XEXP (y, 0);
    else
!     return 0;
  
    if (GET_CODE (inc_val) == CONST_INT)
      {
        if (HAVE_POST_INCREMENT
  	  && (INTVAL (inc_val) == size && offset == 0))
! 	changed = attempt_auto_inc (pbi, gen_rtx_POST_INC (Pmode, addr),
! 				    insn, x, incr, addr);
        else if (HAVE_POST_DECREMENT
  	       && (INTVAL (inc_val) == -size && offset == 0))
! 	changed = attempt_auto_inc (pbi, gen_rtx_POST_DEC (Pmode, addr),
! 				    insn, x, incr, addr);
        else if (HAVE_PRE_INCREMENT
  	       && (INTVAL (inc_val) == size && offset == size))
! 	changed = attempt_auto_inc (pbi, gen_rtx_PRE_INC (Pmode, addr),
! 				    insn, x, incr, addr);
        else if (HAVE_PRE_DECREMENT
  	       && (INTVAL (inc_val) == -size && offset == -size))
! 	changed = attempt_auto_inc (pbi, gen_rtx_PRE_DEC (Pmode, addr),
! 				    insn, x, incr, addr);
        else if (HAVE_POST_MODIFY_DISP && offset == 0)
! 	changed = attempt_auto_inc (pbi,
! 				    gen_rtx_POST_MODIFY (Pmode, addr,
! 					gen_rtx_PLUS (Pmode,
! 						      addr,
! 						      inc_val)),
! 				    insn, x, incr, addr);
      }
    else if (GET_CODE (inc_val) == REG
  	   && ! reg_set_between_p (inc_val, PREV_INSN (insn),
*************** find_auto_inc (pbi, x, insn)
*** 3531,4014 ****
  
      {
        if (HAVE_POST_MODIFY_REG && offset == 0)
! 	attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
! 						    gen_rtx_PLUS (Pmode,
! 								  addr,
! 								  inc_val)),
! 			  insn, x, incr, addr);
!     }
! }
! 
! #endif /* AUTO_INC_DEC */
! 
! static void
! mark_used_reg (pbi, reg, cond, insn)
!      struct propagate_block_info *pbi;
!      rtx reg;
!      rtx cond ATTRIBUTE_UNUSED;
!      rtx insn;
! {
!   unsigned int regno_first, regno_last, i;
!   int some_was_live, some_was_dead, some_not_set;
! 
!   regno_last = regno_first = REGNO (reg);
!   if (regno_first < FIRST_PSEUDO_REGISTER)
!     regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
! 
!   /* Find out if any of this register is live after this instruction.  */
!   some_was_live = some_was_dead = 0;
!   for (i = regno_first; i <= regno_last; ++i)
!     {
!       int needed_regno = REGNO_REG_SET_P (pbi->reg_live, i);
!       some_was_live |= needed_regno;
!       some_was_dead |= ! needed_regno;
!     }
! 
!   /* Find out if any of the register was set this insn.  */
!   some_not_set = 0;
!   for (i = regno_first; i <= regno_last; ++i)
!     some_not_set |= ! REGNO_REG_SET_P (pbi->new_set, i);
! 
!   if (pbi->flags & (PROP_LOG_LINKS | PROP_AUTOINC))
!     {
!       /* Record where each reg is used, so when the reg is set we know
! 	 the next insn that uses it.  */
!       pbi->reg_next_use[regno_first] = insn;
!     }
! 
!   if (pbi->flags & PROP_REG_INFO)
!     {
!       if (regno_first < FIRST_PSEUDO_REGISTER)
! 	{
! 	  /* If this is a register we are going to try to eliminate,
! 	     don't mark it live here.  If we are successful in
! 	     eliminating it, it need not be live unless it is used for
! 	     pseudos, in which case it will have been set live when it
! 	     was allocated to the pseudos.  If the register will not
! 	     be eliminated, reload will set it live at that point.
! 
! 	     Otherwise, record that this function uses this register.  */
! 	  /* ??? The PPC backend tries to "eliminate" on the pic
! 	     register to itself.  This should be fixed.  In the mean
! 	     time, hack around it.  */
! 
! 	  if (! (TEST_HARD_REG_BIT (elim_reg_set, regno_first)
! 	         && (regno_first == FRAME_POINTER_REGNUM
! 		     || regno_first == ARG_POINTER_REGNUM)))
! 	    for (i = regno_first; i <= regno_last; ++i)
! 	      regs_ever_live[i] = 1;
! 	}
!       else
! 	{
! 	  /* Keep track of which basic block each reg appears in.  */
! 
! 	  int blocknum = pbi->bb->index;
! 	  if (REG_BASIC_BLOCK (regno_first) == REG_BLOCK_UNKNOWN)
! 	    REG_BASIC_BLOCK (regno_first) = blocknum;
! 	  else if (REG_BASIC_BLOCK (regno_first) != blocknum)
! 	    REG_BASIC_BLOCK (regno_first) = REG_BLOCK_GLOBAL;
! 
! 	  /* Count (weighted) number of uses of each reg.  */
! 	  REG_FREQ (regno_first) += REG_FREQ_FROM_BB (pbi->bb);
! 	  REG_N_REFS (regno_first)++;
! 	}
!     }
! 
!   /* Record and count the insns in which a reg dies.  If it is used in
!      this insn and was dead below the insn then it dies in this insn.
!      If it was set in this insn, we do not make a REG_DEAD note;
!      likewise if we already made such a note.  */
!   if ((pbi->flags & (PROP_DEATH_NOTES | PROP_REG_INFO))
!       && some_was_dead
!       && some_not_set)
!     {
!       /* Check for the case where the register dying partially
! 	 overlaps the register set by this insn.  */
!       if (regno_first != regno_last)
! 	for (i = regno_first; i <= regno_last; ++i)
! 	  some_was_live |= REGNO_REG_SET_P (pbi->new_set, i);
! 
!       /* If none of the words in X is needed, make a REG_DEAD note.
! 	 Otherwise, we must make partial REG_DEAD notes.  */
!       if (! some_was_live)
! 	{
! 	  if ((pbi->flags & PROP_DEATH_NOTES)
! 	      && ! find_regno_note (insn, REG_DEAD, regno_first))
! 	    REG_NOTES (insn)
! 	      = alloc_EXPR_LIST (REG_DEAD, reg, REG_NOTES (insn));
! 
! 	  if (pbi->flags & PROP_REG_INFO)
! 	    REG_N_DEATHS (regno_first)++;
! 	}
!       else
! 	{
! 	  /* Don't make a REG_DEAD note for a part of a register
! 	     that is set in the insn.  */
! 	  for (i = regno_first; i <= regno_last; ++i)
! 	    if (! REGNO_REG_SET_P (pbi->reg_live, i)
! 		&& ! dead_or_set_regno_p (insn, i))
! 	      REG_NOTES (insn)
! 		= alloc_EXPR_LIST (REG_DEAD,
! 				   regno_reg_rtx[i],
! 				   REG_NOTES (insn));
! 	}
!     }
! 
!   /* Mark the register as being live.  */
!   for (i = regno_first; i <= regno_last; ++i)
!     {
! #ifdef HAVE_conditional_execution
!       int this_was_live = REGNO_REG_SET_P (pbi->reg_live, i);
! #endif
! 
!       SET_REGNO_REG_SET (pbi->reg_live, i);
! 
! #ifdef HAVE_conditional_execution
!       /* If this is a conditional use, record that fact.  If it is later
! 	 conditionally set, we'll know to kill the register.  */
!       if (cond != NULL_RTX)
! 	{
! 	  splay_tree_node node;
! 	  struct reg_cond_life_info *rcli;
! 	  rtx ncond;
! 
! 	  if (this_was_live)
! 	    {
! 	      node = splay_tree_lookup (pbi->reg_cond_dead, i);
! 	      if (node == NULL)
! 		{
! 		  /* The register was unconditionally live previously.
! 		     No need to do anything.  */
! 		}
! 	      else
! 		{
! 		  /* The register was conditionally live previously.
! 		     Subtract the new life cond from the old death cond.  */
! 		  rcli = (struct reg_cond_life_info *) node->value;
! 		  ncond = rcli->condition;
! 		  ncond = and_reg_cond (ncond, not_reg_cond (cond), 1);
! 
! 		  /* If the register is now unconditionally live,
! 		     remove the entry in the splay_tree.  */
! 		  if (ncond == const0_rtx)
! 		    splay_tree_remove (pbi->reg_cond_dead, i);
! 		  else
! 		    {
! 		      rcli->condition = ncond;
! 		      SET_REGNO_REG_SET (pbi->reg_cond_reg,
! 					 REGNO (XEXP (cond, 0)));
! 		    }
! 		}
! 	    }
! 	  else
! 	    {
! 	      /* The register was not previously live at all.  Record
! 		 the condition under which it is still dead.  */
! 	      rcli = (struct reg_cond_life_info *) xmalloc (sizeof (*rcli));
! 	      rcli->condition = not_reg_cond (cond);
! 	      rcli->stores = const0_rtx;
! 	      rcli->orig_condition = const0_rtx;
! 	      splay_tree_insert (pbi->reg_cond_dead, i,
! 				 (splay_tree_value) rcli);
! 
! 	      SET_REGNO_REG_SET (pbi->reg_cond_reg, REGNO (XEXP (cond, 0)));
! 	    }
! 	}
!       else if (this_was_live)
! 	{
! 	  /* The register may have been conditionally live previously, but
! 	     is now unconditionally live.  Remove it from the conditionally
! 	     dead list, so that a conditional set won't cause us to think
! 	     it dead.  */
! 	  splay_tree_remove (pbi->reg_cond_dead, i);
! 	}
! #endif
!     }
! }
! 
! /* Scan expression X and store a 1-bit in NEW_LIVE for each reg it uses.
!    This is done assuming the registers needed from X are those that
!    have 1-bits in PBI->REG_LIVE.
! 
!    INSN is the containing instruction.  If INSN is dead, this function
!    is not called.  */
! 
! static void
! mark_used_regs (pbi, x, cond, insn)
!      struct propagate_block_info *pbi;
!      rtx x, cond, insn;
! {
!   RTX_CODE code;
!   int regno;
!   int flags = pbi->flags;
! 
!  retry:
!   if (!x)
!     return;
!   code = GET_CODE (x);
!   switch (code)
!     {
!     case LABEL_REF:
!     case SYMBOL_REF:
!     case CONST_INT:
!     case CONST:
!     case CONST_DOUBLE:
!     case CONST_VECTOR:
!     case PC:
!     case ADDR_VEC:
!     case ADDR_DIFF_VEC:
!       return;
! 
! #ifdef HAVE_cc0
!     case CC0:
!       pbi->cc0_live = 1;
!       return;
! #endif
! 
!     case CLOBBER:
!       /* If we are clobbering a MEM, mark any registers inside the address
! 	 as being used.  */
!       if (GET_CODE (XEXP (x, 0)) == MEM)
! 	mark_used_regs (pbi, XEXP (XEXP (x, 0), 0), cond, insn);
!       return;
! 
!     case MEM:
!       /* Don't bother watching stores to mems if this is not the
! 	 final pass.  We'll not be deleting dead stores this round.  */
!       if (optimize && (flags & PROP_SCAN_DEAD_STORES))
! 	{
! 	  /* Invalidate the data for the last MEM stored, but only if MEM is
! 	     something that can be stored into.  */
! 	  if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
! 	      && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
! 	    /* Needn't clear the memory set list.  */
! 	    ;
! 	  else
! 	    {
! 	      rtx temp = pbi->mem_set_list;
! 	      rtx prev = NULL_RTX;
! 	      rtx next;
! 
! 	      while (temp)
! 		{
! 		  next = XEXP (temp, 1);
! 		  if (anti_dependence (XEXP (temp, 0), x))
! 		    {
! 		      /* Splice temp out of the list.  */
! 		      if (prev)
! 			XEXP (prev, 1) = next;
! 		      else
! 			pbi->mem_set_list = next;
! 		      free_EXPR_LIST_node (temp);
! 		      pbi->mem_set_list_len--;
! 		    }
! 		  else
! 		    prev = temp;
! 		  temp = next;
! 		}
! 	    }
! 
! 	  /* If the memory reference had embedded side effects (autoincrement
! 	     address modes.  Then we may need to kill some entries on the
! 	     memory set list.  */
! 	  if (insn)
! 	    for_each_rtx (&PATTERN (insn), invalidate_mems_from_autoinc, pbi);
! 	}
! 
! #ifdef AUTO_INC_DEC
!       if (flags & PROP_AUTOINC)
! 	find_auto_inc (pbi, x, insn);
! #endif
!       break;
! 
!     case SUBREG:
! #ifdef CANNOT_CHANGE_MODE_CLASS
!       if (GET_CODE (SUBREG_REG (x)) == REG
! 	  && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
! 	SET_REGNO_REG_SET (&subregs_of_mode[GET_MODE (x)],
! 			   REGNO (SUBREG_REG (x)));
! #endif
! 
!       /* While we're here, optimize this case.  */
!       x = SUBREG_REG (x);
!       if (GET_CODE (x) != REG)
! 	goto retry;
!       /* Fall through.  */
! 
!     case REG:
!       /* See a register other than being set => mark it as needed.  */
!       mark_used_reg (pbi, x, cond, insn);
!       return;
! 
!     case SET:
!       {
! 	rtx testreg = SET_DEST (x);
! 	int mark_dest = 0;
! 
! 	/* If storing into MEM, don't show it as being used.  But do
! 	   show the address as being used.  */
! 	if (GET_CODE (testreg) == MEM)
! 	  {
! #ifdef AUTO_INC_DEC
! 	    if (flags & PROP_AUTOINC)
! 	      find_auto_inc (pbi, testreg, insn);
! #endif
! 	    mark_used_regs (pbi, XEXP (testreg, 0), cond, insn);
! 	    mark_used_regs (pbi, SET_SRC (x), cond, insn);
! 	    return;
! 	  }
! 
! 	/* Storing in STRICT_LOW_PART is like storing in a reg
! 	   in that this SET might be dead, so ignore it in TESTREG.
! 	   but in some other ways it is like using the reg.
! 
! 	   Storing in a SUBREG or a bit field is like storing the entire
! 	   register in that if the register's value is not used
! 	   then this SET is not needed.  */
! 	while (GET_CODE (testreg) == STRICT_LOW_PART
! 	       || GET_CODE (testreg) == ZERO_EXTRACT
! 	       || GET_CODE (testreg) == SIGN_EXTRACT
! 	       || GET_CODE (testreg) == SUBREG)
! 	  {
! #ifdef CANNOT_CHANGE_MODE_CLASS
! 	    if (GET_CODE (testreg) == SUBREG
! 		&& GET_CODE (SUBREG_REG (testreg)) == REG
! 		&& REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER)
! 	      SET_REGNO_REG_SET (&subregs_of_mode[GET_MODE (testreg)],
! 				 REGNO (SUBREG_REG (testreg)));
! #endif
! 
! 	    /* Modifying a single register in an alternate mode
! 	       does not use any of the old value.  But these other
! 	       ways of storing in a register do use the old value.  */
! 	    if (GET_CODE (testreg) == SUBREG
! 		&& !((REG_BYTES (SUBREG_REG (testreg))
! 		      + UNITS_PER_WORD - 1) / UNITS_PER_WORD
! 		     > (REG_BYTES (testreg)
! 			+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))
! 	      ;
! 	    else
! 	      mark_dest = 1;
! 
! 	    testreg = XEXP (testreg, 0);
! 	  }
! 
! 	/* If this is a store into a register or group of registers,
! 	   recursively scan the value being stored.  */
! 
! 	if ((GET_CODE (testreg) == PARALLEL
! 	     && GET_MODE (testreg) == BLKmode)
! 	    || (GET_CODE (testreg) == REG
! 		&& (regno = REGNO (testreg),
! 		    ! (regno == FRAME_POINTER_REGNUM
! 		       && (! reload_completed || frame_pointer_needed)))
! #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
! 		&& ! (regno == HARD_FRAME_POINTER_REGNUM
! 		      && (! reload_completed || frame_pointer_needed))
! #endif
! #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
! 		&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
! #endif
! 		))
! 	  {
! 	    if (mark_dest)
! 	      mark_used_regs (pbi, SET_DEST (x), cond, insn);
! 	    mark_used_regs (pbi, SET_SRC (x), cond, insn);
! 	    return;
! 	  }
!       }
!       break;
! 
!     case ASM_OPERANDS:
!     case UNSPEC_VOLATILE:
!     case TRAP_IF:
!     case ASM_INPUT:
!       {
! 	/* Traditional and volatile asm instructions must be considered to use
! 	   and clobber all hard registers, all pseudo-registers and all of
! 	   memory.  So must TRAP_IF and UNSPEC_VOLATILE operations.
! 
! 	   Consider for instance a volatile asm that changes the fpu rounding
! 	   mode.  An insn should not be moved across this even if it only uses
! 	   pseudo-regs because it might give an incorrectly rounded result.
! 
! 	   ?!? Unfortunately, marking all hard registers as live causes massive
! 	   problems for the register allocator and marking all pseudos as live
! 	   creates mountains of uninitialized variable warnings.
! 
! 	   So for now, just clear the memory set list and mark any regs
! 	   we can find in ASM_OPERANDS as used.  */
! 	if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
! 	  {
! 	    free_EXPR_LIST_list (&pbi->mem_set_list);
! 	    pbi->mem_set_list_len = 0;
! 	  }
! 
! 	/* For all ASM_OPERANDS, we must traverse the vector of input operands.
! 	   We can not just fall through here since then we would be confused
! 	   by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
! 	   traditional asms unlike their normal usage.  */
! 	if (code == ASM_OPERANDS)
! 	  {
! 	    int j;
! 
! 	    for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
! 	      mark_used_regs (pbi, ASM_OPERANDS_INPUT (x, j), cond, insn);
! 	  }
! 	break;
!       }
! 
!     case COND_EXEC:
!       if (cond != NULL_RTX)
! 	abort ();
! 
!       mark_used_regs (pbi, COND_EXEC_TEST (x), NULL_RTX, insn);
! 
!       cond = COND_EXEC_TEST (x);
!       x = COND_EXEC_CODE (x);
!       goto retry;
! 
!     case PHI:
!       /* We _do_not_ want to scan operands of phi nodes.  Operands of
! 	 a phi function are evaluated only when control reaches this
! 	 block along a particular edge.  Therefore, regs that appear
! 	 as arguments to phi should not be added to the global live at
! 	 start.  */
!       return;
! 
!     default:
!       break;
      }
  
!   /* Recursively scan the operands of this expression.  */
! 
!   {
!     const char * const fmt = GET_RTX_FORMAT (code);
!     int i;
! 
!     for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
!       {
! 	if (fmt[i] == 'e')
! 	  {
! 	    /* Tail recursive case: save a function call level.  */
! 	    if (i == 0)
! 	      {
! 		x = XEXP (x, 0);
! 		goto retry;
! 	      }
! 	    mark_used_regs (pbi, XEXP (x, i), cond, insn);
! 	  }
! 	else if (fmt[i] == 'E')
! 	  {
! 	    int j;
! 	    for (j = 0; j < XVECLEN (x, i); j++)
! 	      mark_used_regs (pbi, XVECEXP (x, i, j), cond, insn);
! 	  }
!       }
!   }
  }
- 
- #ifdef AUTO_INC_DEC
  
  static int
  try_pre_increment_1 (pbi, insn)
--- 3879,3894 ----
  
      {
        if (HAVE_POST_MODIFY_REG && offset == 0)
! 	changed = attempt_auto_inc (pbi,
! 				    gen_rtx_POST_MODIFY (Pmode, addr,
! 					    gen_rtx_PLUS (Pmode,
! 							  addr,
! 							  inc_val)),
! 				    insn, x, incr, addr);
      }
  
!   return changed;
  }
  
  static int
  try_pre_increment_1 (pbi, insn)
*************** reg_set_to_hard_reg_set (to, from)
*** 4377,4380 ****
--- 4257,4301 ----
  	 return;
         SET_HARD_REG_BIT (*to, i);
       });
+ }
+ 
+ void
+ create_log_links (blocks)
+      sbitmap blocks;
+ {
+   struct propagate_block_info pbi;
+   basic_block bb;
+   rtx insn;
+   struct insn_extract insn_info;
+ 
+   /* Clear the old log links.  */
+   clear_log_links (blocks);
+ 
+   /* Allocate the structures used.  */
+   pbi.reg_next_use = (rtx *) xcalloc (max_reg_num (), sizeof (rtx));
+ 
+   /* Pass through the insns and establish log links.  */
+   FOR_EACH_BB (bb)
+     {
+       if (blocks && ! TEST_BIT (blocks, bb->index))
+ 	continue;
+       
+       for (insn = bb->end; insn != PREV_INSN (bb->head); insn = PREV_INSN (insn))
+ 	{
+ 	  if (!INSN_P (insn))
+ 	    continue;
+ 
+ 	  /* Extract the information about definitions, uses etc from insn.  */
+ 	  extract_insn_info (&insn_info, insn, 0);
+ 	  
+ 	  /* Set up log links.  */
+ 	  set_up_log_links (insn, &pbi, &insn_info);
+ 
+ 	  /* Update reg_next_use.  */
+ 	  update_next_use_sets (&pbi, &insn_info);
+ 	  update_next_use_uses (insn, &pbi, &insn_info);
+ 	}
+     }
+ 
+   free (pbi.reg_next_use);
  }
Index: ifcvt.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/ifcvt.c,v
retrieving revision 1.103.2.3
diff -c -3 -p -r1.103.2.3 ifcvt.c
*** ifcvt.c	1 Dec 2002 05:42:08 -0000	1.103.2.3
--- ifcvt.c	12 Dec 2002 02:10:13 -0000
*************** dead_or_predicable (test_bb, merge_bb, o
*** 2961,2967 ****
  
        for (insn = jump; ; insn = prev)
  	{
! 	  prev = propagate_one_insn (pbi, insn);
  	  if (insn == earliest)
  	    break;
  	}
--- 2961,2967 ----
  
        for (insn = jump; ; insn = prev)
  	{
! 	  prev = propagate_one_insn (pbi, insn, NULL);
  	  if (insn == earliest)
  	    break;
  	}
Index: ra.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/ra.c,v
retrieving revision 1.4.4.1
diff -c -3 -p -r1.4.4.1 ra.c
*** ra.c	1 Dec 2002 05:42:13 -0000	1.4.4.1
--- ra.c	12 Dec 2002 02:10:13 -0000
*************** reg_alloc ()
*** 859,865 ****
    if ((debug_new_regalloc & DUMP_LAST_FLOW) == 0)
      rtl_dump_file = NULL;
    life_analysis (get_insns (), rtl_dump_file,
! 		 PROP_DEATH_NOTES | PROP_LOG_LINKS  | PROP_REG_INFO);
    cleanup_cfg (CLEANUP_EXPENSIVE);
    recompute_reg_usage (get_insns (), TRUE);
    if (rtl_dump_file)
--- 859,865 ----
    if ((debug_new_regalloc & DUMP_LAST_FLOW) == 0)
      rtl_dump_file = NULL;
    life_analysis (get_insns (), rtl_dump_file,
! 		 PROP_DEATH_NOTES | PROP_REG_INFO);
    cleanup_cfg (CLEANUP_EXPENSIVE);
    recompute_reg_usage (get_insns (), TRUE);
    if (rtl_dump_file)
Index: recog.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/recog.c,v
retrieving revision 1.164.4.5
diff -c -3 -p -r1.164.4.5 recog.c
*** recog.c	3 Dec 2002 17:34:59 -0000	1.164.4.5
--- recog.c	12 Dec 2002 02:10:13 -0000
*************** peephole2_optimize (dump_file)
*** 3137,3143 ****
  	      if (--peep2_current < 0)
  		peep2_current = MAX_INSNS_PER_PEEP2;
  	      peep2_insn_data[peep2_current].insn = insn;
! 	      propagate_one_insn (pbi, insn);
  	      COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
  
  	      /* Match the peephole.  */
--- 3137,3143 ----
  	      if (--peep2_current < 0)
  		peep2_current = MAX_INSNS_PER_PEEP2;
  	      peep2_insn_data[peep2_current].insn = insn;
! 	      propagate_one_insn (pbi, insn, NULL);
  	      COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
  
  	      /* Match the peephole.  */
*************** peephole2_optimize (dump_file)
*** 3299,3305 ****
  			  if (--i < 0)
  			    i = MAX_INSNS_PER_PEEP2;
  			  peep2_insn_data[i].insn = x;
! 			  propagate_one_insn (pbi, x);
  			  COPY_REG_SET (peep2_insn_data[i].live_before, live);
  			}
  		      x = PREV_INSN (x);
--- 3299,3305 ----
  			  if (--i < 0)
  			    i = MAX_INSNS_PER_PEEP2;
  			  peep2_insn_data[i].insn = x;
! 			  propagate_one_insn (pbi, x, NULL);
  			  COPY_REG_SET (peep2_insn_data[i].live_before, live);
  			}
  		      x = PREV_INSN (x);
Index: rtl.h
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/rtl.h,v
retrieving revision 1.367.4.8
diff -c -3 -p -r1.367.4.8 rtl.h
*** rtl.h	3 Dec 2002 17:35:00 -0000	1.367.4.8
--- rtl.h	12 Dec 2002 02:10:13 -0000
*************** extern rtx set_unique_reg_note		PARAMS (
*** 1570,1575 ****
--- 1570,1609 ----
  
  /* Functions in rtlanal.c */
  
+ /* Information about insn.  */
+ struct insn_extract
+ {
+   /* Definitions:  */
+   rtx registers_set;		/* List of set registers.  */
+   rtx mems_set;			/* List of set memory references.  */
+   int all_mems_altered;		/* By a function call.  */
+   rtx registers_altered;	/* List of registers altered (by call or
+ 				   embedded clobber).  */
+   rtx scratches;		/* Scratches.  */
+   int sets_cc0;			/* Sets cc0?  */
+ 
+   /* Uses:  */
+   rtx registers_used;		/* List of register uses.  */
+   rtx mems_used;		/* List of used memory references.  */
+   int uses_cc0;			/* Uses cc0?  */
+ 
+   /* Special effects:  */
+   int may_trap;			/* May trap?  */
+   int contains_call;		/* Contains any call?  */
+   int contains_impure_call;	/* Contains impure call?  */
+   int contains_volatile_ref;	/* Contains load from volatile memory or
+ 				   volatile asms?  */
+   int not_set;			/* Something else than set.  */
+ };
+ 
+ /* Placed to register lists to specify details about their origin.  */
+ enum insn_extract_flags
+ {
+   IIF_TOPLEV_CLOBBER = 1,		/* Comes from a toplevel CLOBBER.  */
+   IIF_CALL_CLOBBERED_GLOBAL = 2,	/* A global register clobbered by call.  */
+   IIF_CALL_STACK_USAGE = 4		/* A stack pointer used by CALL.  */
+ };
+ 
  /* Single set is implemented as macro for performance reasons.  */
  #define single_set(I) (INSN_P (I) \
  		       ? (GET_CODE (PATTERN (I)) == SET \
*************** extern int insns_safe_to_move_p         
*** 1647,1652 ****
--- 1681,1688 ----
  extern int loc_mentioned_in_p		PARAMS ((rtx *, rtx));
  extern rtx find_first_parameter_load	PARAMS ((rtx, rtx));
  extern bool keep_with_call_p		PARAMS ((rtx));
+ void extract_insn_info			PARAMS ((struct insn_extract *,
+ 						 rtx, int));
  
  /* flow.c */
  
Index: rtlanal.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/rtlanal.c,v
retrieving revision 1.138.4.5
diff -c -3 -p -r1.138.4.5 rtlanal.c
*** rtlanal.c	1 Dec 2002 05:42:17 -0000	1.138.4.5
--- rtlanal.c	12 Dec 2002 02:10:13 -0000
*************** Software Foundation, 59 Temple Place - S
*** 33,38 ****
--- 33,40 ----
  #include "flags.h"
  #include "basic-block.h"
  #include "real.h"
+ #include "function.h"
+ #include "regs.h"
  
  /* Forward declarations */
  static int global_reg_mentioned_p_1 PARAMS ((rtx *, void *));
*************** static int computed_jump_p_1	PARAMS ((rt
*** 42,47 ****
--- 44,58 ----
  static void parms_set 		PARAMS ((rtx, rtx, void *));
  static bool hoist_test_store		PARAMS ((rtx, rtx, regset));
  static void hoist_update_store		PARAMS ((rtx, rtx *, rtx, rtx));
+ static void record_set_regs		PARAMS ((struct insn_extract *,
+ 						 rtx, rtx, int));
+ static void record_set_1		PARAMS ((struct insn_extract *,
+ 						 enum rtx_code,
+ 						 rtx, rtx, rtx, int));
+ static void record_used_regs		PARAMS ((struct insn_extract *,
+ 						 rtx, rtx, rtx, int));
+ static void record_used_reg		PARAMS ((struct insn_extract *,
+ 						 rtx, rtx, int));
  
  /* Bit flags that specify the machine subtype we are compiling for.
     Bits are tested using macros TARGET_... defined in the tm.h file
*************** hoist_insn_to_edge (insn, e, val, new)
*** 3632,3634 ****
--- 3643,4259 ----
    end_sequence ();
    return new_insn;
  }
+ 
+ /* Extract various informations about INSN into INSN_INFO.  */
+ void
+ extract_insn_info (insn_info, insn, flags)
+      struct insn_extract *insn_info;
+      rtx insn;
+      int flags;
+ {
+   rtx note, cond;
+   int i;
+   int iif = 0;
+   rtx temp = NULL_RTX;
+ 
+   insn_info->registers_set = NULL_RTX;
+   insn_info->mems_set = NULL_RTX;
+   insn_info->all_mems_altered = 0;
+   insn_info->registers_altered = NULL_RTX;
+   insn_info->scratches = NULL_RTX;
+   insn_info->sets_cc0 = 0;
+ 
+   /* Uses:  */
+   insn_info->registers_used = NULL_RTX;
+   insn_info->mems_used = NULL_RTX;
+   insn_info->uses_cc0 = 0;
+ 
+   /* Special effects:  */
+   insn_info->may_trap = 0;
+   insn_info->contains_call = 0;
+   insn_info->contains_impure_call = 0;
+   insn_info->contains_volatile_ref = 0;
+   insn_info->not_set = 0;
+ 
+   if (! INSN_P (insn))
+     {
+       insn_info->not_set = 1;
+       return;
+     }
+  
+   insn_info->may_trap = may_trap_p (PATTERN (insn));
+   insn_info->contains_volatile_ref = volatile_refs_p (PATTERN (insn));
+   switch (GET_CODE (insn))
+     {
+     case INSN:
+     case CALL_INSN:
+       break;
+     case CLOBBER:
+       iif |= IIF_TOPLEV_CLOBBER;
+       /* Fallthru.  */
+     default:
+       insn_info->not_set = 1;
+     }
+   switch (GET_CODE (PATTERN (insn)))
+     {
+     case SET:
+     case CALL:
+     case PARALLEL:
+       break;
+     default:
+       insn_info->not_set = 1;
+     }
+ 
+   /* Any regs live at the time of a call instruction must not go
+      in a register clobbered by calls.  Find all regs now live and
+      record this for them.  */
+ 
+   /* Record sets.  */
+   record_set_regs (insn_info, PATTERN (insn), insn, iif);
+ 
+   if (GET_CODE (insn) == CALL_INSN)
+     {
+       insn_info->contains_call = 1;
+ 
+       cond = NULL_RTX;
+       if (GET_CODE (PATTERN (insn)) == COND_EXEC)
+ 	cond = COND_EXEC_TEST (PATTERN (insn));
+ 
+       /* Non-constant calls clobber memory, constant calls do not
+ 	 clobber memory, though they may clobber outgoing arguments
+ 	 on the stack.  */
+       if (! CONST_OR_PURE_CALL_P (insn))
+ 	{
+ 	  insn_info->all_mems_altered = 1;
+ 	  insn_info->contains_impure_call = 1;
+ 	}
+ 
+       /* There may be extra registers to be clobbered.  */
+       for (note = CALL_INSN_FUNCTION_USAGE (insn);
+ 	   note;
+ 	   note = XEXP (note, 1))
+ 	if (GET_CODE (XEXP (note, 0)) == CLOBBER)
+ 	  record_set_1 (insn_info, CLOBBER, XEXP (XEXP (note, 0), 0),
+ 			cond, insn, iif);
+ 
+       /* Calls change all call-used and global registers.  */
+       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ 	if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
+ 	  record_set_1 (insn_info, CLOBBER, regno_reg_rtx[i], cond, insn,
+ 			iif | IIF_CALL_CLOBBERED_GLOBAL);
+     }
+ 
+   /* Record uses.  */
+   record_used_regs (insn_info, PATTERN (insn), NULL_RTX, insn, iif);
+   if ((flags & PROP_EQUAL_NOTES)
+       && ((note = find_reg_note (insn, REG_EQUAL, NULL_RTX))
+ 	  || (note = find_reg_note (insn, REG_EQUIV, NULL_RTX))))
+     record_used_regs (insn_info, XEXP (note, 0), NULL_RTX, insn, iif);
+ 
+   if (GET_CODE (insn) == CALL_INSN)
+     {
+       cond = NULL_RTX;
+       if (GET_CODE (PATTERN (insn)) == COND_EXEC)
+ 	cond = COND_EXEC_TEST (PATTERN (insn));
+ 
+       /* Calls use their arguments.  */
+       for (note = CALL_INSN_FUNCTION_USAGE (insn);
+ 	   note;
+ 	   note = XEXP (note, 1))
+ 	if (GET_CODE (XEXP (note, 0)) == USE)
+ 	  record_used_regs (insn_info, XEXP (XEXP (note, 0), 0),
+ 			    cond, insn, iif);
+ 
+       /* The stack ptr is used (honorarily) by a CALL insn.  */
+       record_used_regs (insn_info, stack_pointer_rtx, cond, insn,
+ 			iif | IIF_CALL_STACK_USAGE);
+ 
+       /* Calls may also reference any of the global registers,
+ 	 so they are made live.  */
+       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ 	if (global_regs[i])
+ 	  record_used_reg (insn_info, regno_reg_rtx[i], cond, iif);
+     }
+   /* For debugging purposes only; if we want dumps to be identical,
+      we must ensure that these lists are in right order. Still not
+      perfect, as we split the data into several lists, but good enough.  */
+ 
+   temp = insn_info->registers_set;
+   insn_info->registers_set = NULL_RTX;
+   for (; temp; temp = XEXP (temp, 1))
+     insn_info->registers_set =
+       alloc_EXPR_LIST (0, XEXP (temp, 0), insn_info->registers_set);
+     
+   temp = insn_info->registers_used;
+   insn_info->registers_used = NULL_RTX;
+   for (; temp; temp = XEXP (temp, 1))
+     insn_info->registers_used =
+       alloc_EXPR_LIST (0, XEXP (temp, 0), insn_info->registers_used);
+     
+   temp = insn_info->registers_altered;
+   insn_info->registers_altered = NULL_RTX;
+   for (; temp; temp = XEXP (temp, 1))
+     insn_info->registers_altered =
+       alloc_EXPR_LIST (0, XEXP (temp, 0), insn_info->registers_altered);
+ }
+ 
+ /* Record the registers that are set within X
+ 
+    If INSN is nonzero, it is the insn being processed.  */
+ 
+ static void
+ record_set_regs (insn_info, x, insn, iif)
+      struct insn_extract *insn_info;
+      rtx x, insn;
+      int iif;
+ {
+   rtx cond = NULL_RTX;
+   rtx link;
+   enum rtx_code code;
+ 
+   if (insn)
+     for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+       {
+ 	if (REG_NOTE_KIND (link) == REG_INC)
+ 	  record_set_1 (insn_info, SET, XEXP (link, 0),
+ 			(GET_CODE (x) == COND_EXEC
+ 			 ? COND_EXEC_TEST (x) : NULL_RTX),
+ 			insn, iif);
+       }
+ retry:
+   switch (code = GET_CODE (x))
+     {
+     case SET:
+     case CLOBBER:
+       record_set_1 (insn_info, code, SET_DEST (x), cond, insn, iif);
+       return;
+ 
+     case COND_EXEC:
+       cond = COND_EXEC_TEST (x);
+       x = COND_EXEC_CODE (x);
+       goto retry;
+ 
+     case PARALLEL:
+       {
+ 	int i;
+ 
+ 	for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ 	  {
+ 	    rtx sub = XVECEXP (x, 0, i);
+ 	    switch (code = GET_CODE (sub))
+ 	      {
+ 	      case COND_EXEC:
+ 		if (cond != NULL_RTX)
+ 		  abort ();
+ 
+ 		cond = COND_EXEC_TEST (sub);
+ 		sub = COND_EXEC_CODE (sub);
+ 		if (GET_CODE (sub) != SET && GET_CODE (sub) != CLOBBER)
+ 		  break;
+ 		/* Fall through.  */
+ 
+ 	      case SET:
+ 	      case CLOBBER:
+ 		record_set_1 (insn_info, code, SET_DEST (sub), cond, insn, iif);
+ 		break;
+ 
+ 	      default:
+ 		break;
+ 	      }
+ 	  }
+ 	break;
+       }
+ 
+     default:
+       break;
+     }
+ }
+ 
+ /* Process a single set, which appears in INSN.  REG (which may not
+    actually be a REG, it may also be a SUBREG, PARALLEL, etc.) is
+    being set using the CODE (which may be SET, CLOBBER, or COND_EXEC).
+    If the set is conditional (because it appear in a COND_EXEC), COND
+    will be the condition.  IIF are the flags containing some special
+    info about usage of the REG in the INSN.  */
+ 
+ static void
+ record_set_1 (insn_info, code, reg, cond, insn, iif)
+      struct insn_extract *insn_info;
+      enum rtx_code code;
+      rtx reg, cond, insn;
+      int iif;
+ {
+   int regno_first = -1, regno_last = -2;
+   int i;
+   rtx reg_list;
+ 
+   /* Modifying just one hardware register of a multi-reg value or just a
+      byte field of a register does not mean the value from before this insn
+      is now dead.  Of course, if it was dead after it's unused now.  */
+ 
+   switch (GET_CODE (reg))
+     {
+     case PARALLEL:
+       /* Some targets place small structures in registers for return values of
+ 	 functions.  We have to detect this case specially here to get correct
+ 	 flow information.  */
+       for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
+ 	if (XEXP (XVECEXP (reg, 0, i), 0) != 0)
+ 	  record_set_1 (insn_info, code, XEXP (XVECEXP (reg, 0, i), 0),
+ 			cond, insn, iif);
+       return;
+ 
+     case ZERO_EXTRACT:
+     case SIGN_EXTRACT:
+     case STRICT_LOW_PART:
+       /* ??? Assumes STRICT_LOW_PART not used on multi-word registers.  */
+       do
+ 	reg = XEXP (reg, 0);
+       while (GET_CODE (reg) == SUBREG
+ 	     || GET_CODE (reg) == ZERO_EXTRACT
+ 	     || GET_CODE (reg) == SIGN_EXTRACT
+ 	     || GET_CODE (reg) == STRICT_LOW_PART);
+       if (GET_CODE (reg) == MEM)
+ 	break;
+       /* Fall through.  */
+ 
+     case REG:
+       regno_last = regno_first = REGNO (reg);
+       if (regno_first < FIRST_PSEUDO_REGISTER)
+ 	regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
+       break;
+ 
+     case SUBREG:
+       if (GET_CODE (SUBREG_REG (reg)) == REG)
+ 	{
+ 	  enum machine_mode outer_mode = GET_MODE (reg);
+ 	  enum machine_mode inner_mode = GET_MODE (SUBREG_REG (reg));
+ 
+ 	  /* Identify the range of registers affected.  This is moderately
+ 	     tricky for hard registers.  See alter_subreg.  */
+ 
+ 	  regno_last = regno_first = REGNO (SUBREG_REG (reg));
+ 	  if (regno_first < FIRST_PSEUDO_REGISTER)
+ 	    {
+ 	      regno_first += subreg_regno_offset (regno_first, inner_mode,
+ 						  SUBREG_BYTE (reg),
+ 						  outer_mode);
+ 	      regno_last = (regno_first
+ 			    + HARD_REGNO_NREGS (regno_first, outer_mode) - 1);
+ 
+ 	      /* Since we've just adjusted the register number ranges, make
+ 		 sure REG matches.  Otherwise some_was_live will be clear
+ 		 when it shouldn't have been, and we'll create incorrect
+ 		 REG_UNUSED notes.  */
+ 	      reg = gen_rtx_REG (outer_mode, regno_first);
+ 	    }
+ 	  else
+ 	    reg = SUBREG_REG (reg);
+ 	}
+       else
+ 	reg = SUBREG_REG (reg);
+       break;
+ 
+     case CC0:
+       insn_info->sets_cc0 = 1;
+       break;
+ 
+     default:
+       break;
+     }
+ 
+   switch (GET_CODE (reg))
+     {
+     case REG:
+       reg_list = NULL_RTX;
+ 
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (regno_last), reg_list);
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (regno_first), reg_list);
+       reg_list = alloc_EXPR_LIST (0, reg, reg_list);
+       reg_list = alloc_EXPR_LIST (0, cond, reg_list);
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (iif), reg_list);
+ 
+       if (code == SET)
+ 	insn_info->registers_set =
+ 	  alloc_EXPR_LIST (0, reg_list,
+ 			   insn_info->registers_set);
+       else
+ 	insn_info->registers_altered =
+ 	  alloc_EXPR_LIST (0, reg_list,
+ 			   insn_info->registers_altered);
+       break;
+ 
+     case MEM:
+       insn_info->mems_set = 
+ 	      alloc_EXPR_LIST (0,
+ 			       alloc_EXPR_LIST (0, cond, reg),
+ 			       insn_info->mems_set);
+       break;
+  
+     case SCRATCH:
+       reg_list = NULL_RTX;
+ 
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (-1), reg_list);  /* regno_last */
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (-2), reg_list);  /* regno_first */
+       reg_list = alloc_EXPR_LIST (0, reg, reg_list);
+       reg_list = alloc_EXPR_LIST (0, cond, reg_list);
+       reg_list = alloc_EXPR_LIST (0, GEN_INT (iif), reg_list);
+       insn_info->scratches =
+ 	      alloc_EXPR_LIST (0, reg_list, insn_info->scratches);
+       break;
+ 
+     default:;
+     }
+ }
+ 
+ /* Record usage of REG inside INSN_INFO.  */
+ 
+ static void
+ record_used_reg (insn_info, reg, cond, iif)
+      struct insn_extract *insn_info;
+      rtx reg;
+      rtx cond;
+      int iif;
+ {
+   int regno_first, regno_last;
+   rtx reg_list = NULL_RTX;
+ 
+   regno_last = regno_first = REGNO (reg);
+   if (regno_first < FIRST_PSEUDO_REGISTER)
+     regno_last += HARD_REGNO_NREGS (regno_first, GET_MODE (reg)) - 1;
+ 
+   reg_list = alloc_EXPR_LIST (0, GEN_INT (regno_last), reg_list);
+   reg_list = alloc_EXPR_LIST (0, GEN_INT (regno_first), reg_list);
+   reg_list = alloc_EXPR_LIST (0, reg, reg_list);
+   reg_list = alloc_EXPR_LIST (0, cond, reg_list);
+   reg_list = alloc_EXPR_LIST (0, GEN_INT (iif), reg_list);
+   insn_info->registers_used = 
+     alloc_EXPR_LIST (0, reg_list, insn_info->registers_used);
+ }
+ 
+ /* Record registers used in INSN inside INSN_INFO.  */
+ 
+ static void
+ record_used_regs (insn_info, x, cond, insn, iif)
+      struct insn_extract *insn_info;
+      rtx x, cond, insn;
+      int iif;
+ {
+   RTX_CODE code;
+   int regno;
+   const char *fmt;
+   int i;
+ 
+  retry:
+   if (!x)
+     return;
+   code = GET_CODE (x);
+   switch (code)
+     {
+     case LABEL_REF:
+     case SYMBOL_REF:
+     case CONST_INT:
+     case CONST:
+     case CONST_DOUBLE:
+     case CONST_VECTOR:
+     case PC:
+     case ADDR_VEC:
+     case ADDR_DIFF_VEC:
+       return;
+ 
+ #ifdef HAVE_cc0
+     case CC0:
+       insn_info->uses_cc0 = 1;
+       return;
+ #endif
+ 
+     case CLOBBER:
+       /* If we are clobbering a MEM, mark any registers inside the address
+ 	 as being used.  */
+       if (GET_CODE (XEXP (x, 0)) == MEM)
+ 	record_used_regs (insn_info, XEXP (XEXP (x, 0), 0), cond, insn, iif);
+       return;
+ 
+     case MEM:
+       insn_info->mems_used = 
+ 	      alloc_EXPR_LIST (0,
+ 			       alloc_EXPR_LIST (0, cond, x),
+ 			       insn_info->mems_used);
+       break;
+ 
+     case SUBREG:
+ #ifdef CANNOT_CHANGE_MODE_CLASS
+       if (GET_CODE (SUBREG_REG (x)) == REG
+ 	  && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
+ 	SET_REGNO_REG_SET (&subregs_of_mode[GET_MODE (x)],
+ 			   REGNO (SUBREG_REG (x)));
+ #endif
+ 
+       /* While we're here, optimize this case.  */
+       x = SUBREG_REG (x);
+       if (GET_CODE (x) != REG)
+ 	goto retry;
+       /* Fall through.  */
+ 
+     case REG:
+       /* See a register other than being set => mark it as needed.  */
+       record_used_reg (insn_info, x, cond, iif);
+       return;
+ 
+     case SET:
+       {
+ 	rtx testreg = SET_DEST (x);
+ 	int mark_dest = 0;
+ 
+ 	/* If storing into MEM, don't show it as being used.  But do
+ 	   show the address as being used.  */
+ 	if (GET_CODE (testreg) == MEM)
+ 	  {
+ 	    record_used_regs (insn_info, XEXP (testreg, 0), cond, insn, iif);
+ 	    record_used_regs (insn_info, SET_SRC (x), cond, insn, iif);
+ 	    return;
+ 	  }
+ 
+ 	/* Storing in STRICT_LOW_PART is like storing in a reg
+ 	   in that this SET might be dead, so ignore it in TESTREG.
+ 	   but in some other ways it is like using the reg.
+ 
+ 	   Storing in a SUBREG or a bit field is like storing the entire
+ 	   register in that if the register's value is not used
+ 	   then this SET is not needed.  */
+ 	while (GET_CODE (testreg) == STRICT_LOW_PART
+ 	       || GET_CODE (testreg) == ZERO_EXTRACT
+ 	       || GET_CODE (testreg) == SIGN_EXTRACT
+ 	       || GET_CODE (testreg) == SUBREG)
+ 	  {
+ #ifdef CANNOT_CHANGE_MODE_CLASS
+ 	    if (GET_CODE (testreg) == SUBREG
+ 		&& GET_CODE (SUBREG_REG (testreg)) == REG
+ 		&& REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER)
+ 	      SET_REGNO_REG_SET (&subregs_of_mode[GET_MODE (testreg)],
+ 				 REGNO (SUBREG_REG (testreg)));
+ #endif
+ 
+ 	    /* Modifying a single register in an alternate mode
+ 	       does not use any of the old value.  But these other
+ 	       ways of storing in a register do use the old value.  */
+ 	    if (GET_CODE (testreg) == SUBREG
+ 		&& !((REG_BYTES (SUBREG_REG (testreg))
+ 		      + UNITS_PER_WORD - 1) / UNITS_PER_WORD
+ 		     > (REG_BYTES (testreg)
+ 			+ UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+ 	      ;
+ 	    else
+ 	      mark_dest = 1;
+ 
+ 	    testreg = XEXP (testreg, 0);
+ 	  }
+ 
+ 	/* If this is a store into a register or group of registers,
+ 	   recursively scan the value being stored.  */
+ 
+ 	if ((GET_CODE (testreg) == PARALLEL
+ 	     && GET_MODE (testreg) == BLKmode)
+ 	    || (GET_CODE (testreg) == REG
+ 		&& (regno = REGNO (testreg),
+ 		    ! (regno == FRAME_POINTER_REGNUM
+ 		       && (! reload_completed || frame_pointer_needed)))
+ #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ 		&& ! (regno == HARD_FRAME_POINTER_REGNUM
+ 		      && (! reload_completed || frame_pointer_needed))
+ #endif
+ #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ 		&& ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
+ #endif
+ 		))
+ 	  {
+ 	    if (mark_dest)
+ 	      record_used_regs (insn_info, SET_DEST (x), cond, insn, iif);
+ 	    record_used_regs (insn_info, SET_SRC (x), cond, insn, iif);
+ 	    return;
+ 	  }
+       }
+       break;
+ 
+     case ASM_OPERANDS:
+     case UNSPEC_VOLATILE:
+     case TRAP_IF:
+     case ASM_INPUT:
+       {
+ 	/* Traditional and volatile asm instructions must be considered to use
+ 	   and clobber all hard registers, all pseudo-registers and all of
+ 	   memory.  So must TRAP_IF and UNSPEC_VOLATILE operations.
+ 
+ 	   Consider for instance a volatile asm that changes the fpu rounding
+ 	   mode.  An insn should not be moved across this even if it only uses
+ 	   pseudo-regs because it might give an incorrectly rounded result.
+ 
+ 	   ?!? Unfortunately, marking all hard registers as live causes massive
+ 	   problems for the register allocator and marking all pseudos as live
+ 	   creates mountains of uninitialized variable warnings.
+ 
+ 	   So for now, just clear the memory set list and mark any regs
+ 	   we can find in ASM_OPERANDS as used.  */
+ 	if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
+ 	  insn_info->all_mems_altered = 1;
+ 
+ 	/* For all ASM_OPERANDS, we must traverse the vector of input operands.
+ 	   We can not just fall through here since then we would be confused
+ 	   by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
+ 	   traditional asms unlike their normal usage.  */
+ 	if (code == ASM_OPERANDS)
+ 	  {
+ 	    int j;
+ 
+ 	    for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
+ 	      record_used_regs (insn_info, ASM_OPERANDS_INPUT (x, j),
+ 				cond, insn, iif);
+ 	  }
+ 	break;
+       }
+ 
+     case COND_EXEC:
+       if (cond != NULL_RTX)
+ 	abort ();
+ 
+       record_used_regs (insn_info, COND_EXEC_TEST (x), NULL_RTX, insn, iif);
+ 
+       cond = COND_EXEC_TEST (x);
+       x = COND_EXEC_CODE (x);
+       goto retry;
+ 
+     case PHI:
+       /* We _do_not_ want to scan operands of phi nodes.  Operands of
+ 	 a phi function are evaluated only when control reaches this
+ 	 block along a particular edge.  Therefore, regs that appear
+ 	 as arguments to phi should not be added to the global live at
+ 	 start.  */
+       return;
+ 
+     default:
+       break;
+     }
+ 
+   /* Recursively scan the operands of this expression.  */
+   fmt = GET_RTX_FORMAT (code);
+   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+     {
+       if (fmt[i] == 'e')
+ 	{
+ 	  /* Tail recursive case: save a function call level.  */
+ 	  if (i == 0)
+ 	    {
+ 	      x = XEXP (x, 0);
+ 	      goto retry;
+ 	    }
+ 	  record_used_regs (insn_info, XEXP (x, i), cond, insn, iif);
+ 	}
+       else if (fmt[i] == 'E')
+ 	{
+ 	  int j;
+ 	  for (j = 0; j < XVECLEN (x, i); j++)
+ 	    record_used_regs (insn_info, XVECEXP (x, i, j), cond, insn, iif);
+ 	}
+     }
+ }
+ 
Index: toplev.c
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/toplev.c,v
retrieving revision 1.668.4.12
diff -c -3 -p -r1.668.4.12 toplev.c
*** toplev.c	3 Dec 2002 17:35:02 -0000	1.668.4.12
--- toplev.c	12 Dec 2002 02:10:13 -0000
*************** rest_of_compilation (decl)
*** 3098,3104 ****
  	  /* Insns were inserted, so things might look a bit different.  */
  	  insns = get_insns ();
  	  update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
! 					    PROP_LOG_LINKS | PROP_REG_INFO
  					    | PROP_DEATH_NOTES);
  	}
      }
--- 3098,3104 ----
  	  /* Insns were inserted, so things might look a bit different.  */
  	  insns = get_insns ();
  	  update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
! 					    PROP_REG_INFO
  					    | PROP_DEATH_NOTES);
  	}
      }
*************** rest_of_compilation (decl)
*** 3116,3121 ****
--- 3116,3122 ----
        timevar_push (TV_COMBINE);
        open_dump_file (DFI_combine, decl);
  
+       create_log_links (NULL);
        rebuild_jump_labels_after_combine
  	= combine_instructions (insns, max_reg_num ());
  
*************** rest_of_compilation (decl)
*** 3159,3164 ****
--- 3160,3166 ----
        timevar_push (TV_REGMOVE);
        open_dump_file (DFI_regmove, decl);
  
+       create_log_links (NULL);
        regmove_optimize (insns, max_reg_num (), rtl_dump_file);
  
        cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]