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]

Re: RFA: improvement to if-conversion


I've finally found some time to finish the iinitial coding for if-conversion / cross-jump merge.

> > I think full register liveness information would be harder to keep
> > up-to-date as if conversion progresses.
>
> I think that global_live_at_start/end are kept up-to-date.  Leastwise,
> I'd be interested to know how badly they're off.  We re-run life info
> at the end, but that's to get death notes inserted properly.

It turns out that it's actually much worse. We don't have any life info in the first
if-conversion pass at all. A lot of loop optimizations only make sense after
if-conversion has been properly performed, so I don't think only relying on the
second if-conversion pass is much good. I suppose that means that I'll have to
run flow before if-conversion then.




Index: basic-block.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/basic-block.h,v
retrieving revision 1.233
diff -p -r1.233 basic-block.h
*** basic-block.h	13 Dec 2004 20:44:31 -0000	1.233
--- basic-block.h	12 Jan 2005 20:43:41 -0000
*************** enum update_life_extent
*** 679,684 ****
--- 679,691 ----
  #define CLEANUP_CFGLAYOUT	128	/* Do cleanup in cfglayout mode.  */
  #define CLEANUP_LOG_LINKS	256	/* Update log links.  */
  
+ /* The following are ORed in on top of the CLEANUp* flags in calls to
+    struct_equiv_block_eq.  */
+ #define STRUCT_EQUIV_START	512	 /* Initializes the search range.  */
+ #define STRUCT_EQUIV_RERUN	1024	/* Rerun to find register use in
+ 					   found equivalence.  */
+ #define STRUCT_EQUIV_FINAL	2048	/* Make any changes necessary to get
+ 					   actual equivalence.  */
  extern void life_analysis (FILE *, int);
  extern int update_life_info (sbitmap, enum update_life_extent, int);
  extern int update_life_info_in_dirty_blocks (enum update_life_extent, int);
*************** extern void break_superblocks (void);
*** 832,837 ****
--- 839,928 ----
  extern void check_bb_profile (basic_block, FILE *);
  extern void update_bb_profile_for_threading (basic_block, int, gcov_type, edge);
  
+ /* In cfgcleanup.c */
+ 
  #include "cfghooks.h"
  
+ /* Constants used to size arrays in struct equiv_info.  When these limits
+    are exceeded, struct_equiv returns zero.
+    The maximum number of pseudo registers that are different in the two blocks,
+    but appear in equivalent places and are dead at the end (or where one of
+    a pair is dead at the end).  */
+ #define STRUCT_EQUIV_MAX_LOCAL 16
+ /* The maximum number of references to an input register that struct_equiv
+    can handle.  */
+ #define STRUCT_EQUIV_MAX_INPUT 9
+ 
+ struct struct_equiv_checkpoint
+ {
+   int ninsns, version, local_count, input_count;
+   rtx x_start, y_start;
+   bool input_valid;
+ };
+ 
+ /* A struct equiv_info is used to pass information to struct_equiv and
+    to gather state while two basic blocks are checked for structural
+    equivalence.
+    X_BLOCK and Y_BLOCK are the two block being compared.
+    X_INPUT and Y_INPUT are used by struct_equiv to record a register that
+    is used as an input parameter, i.e. where different registers are used
+    as sources.  This is only used for a register that is live at the end
+    of the blocks, or in some identical code at the end of the blocks;
+    Inputs that are dead at the end go into X_LOCAL / Y_LOCAL.
+    NEED_FULL_BLOCK is set when the entire basic blocks have to match, as
+    in if-conversion.  It is cleared for cross-jumping.
+    If INPUT_REG is set, it is replaced in X_BLOCK for the input.
+    X_LOCAL and Y_LOCAL are used to gather register numbers of register pairs
+    that are local to X_BLOCK and Y_BLOCK, with LOCAL_COUNT being the index
+    to the next free entry.
+    DYING_INPUTS is set to the number of local registers that turn out
+    to be inputs to the (possibly partial) block.
+    LOCAL_RVALUE is nonzero if the corresponding X_LOCAL / Y_LOCAL entry
+    was a source operand (including STRICT_LOW_PART) for the last invocation
+    of struct_equiv mentioning it, zero if it was a destination-only operand.
+    Since we are scanning backwards, this means the register is input/local
+    for the (partial) block scanned so far.
+    COMMON_LIVE keeps track of the registers which are currently live
+    (as we scan backwards from the end) and have the same numbers in both
+    blocks.  N.B. a register that is in common_live is unsuitable to become
+    a local reg.
+    Likewise, LOCAL_LIVE keeps track of registers that are local to one of
+    the blocks; these registers must not be accepted as identical when
+    encountered in both blocks.
+    X_END and Y_END are the last insns in X_BLOCK and Y_BLOCK, respectively,
+    that are being compared.  A final jump insn will not be included.
+    After struct_equiv_merge_block has run, X_START and Y_START are set to
+    first real instructions of the - possibly partial - matched blocks.  */
+ /* INPUT_COST is the cost that adding an extra input to the matched blocks
+    is supposed to have, nad is taken into account when considering if the
+    matched sequence should be extended backwards.  input_cost < 0 means
+    don't accept any inputs at all.  */
+    
+ struct equiv_info
+ {
+   basic_block x_block, y_block;
+   rtx x_input, y_input, input_reg;
+   int input_cost;
+   int dying_inputs;
+   regset common_live;
+   regset local_live;
+   bitmap equiv_used;
+   rtx x_end, y_end;
+   int ninsns, version, local_count, input_count;
+   rtx x_start, y_start;
+   bool input_valid;
+   struct struct_equiv_checkpoint checkpoint[2];
+   int mode;
+   bool need_rerun, live_update;
+   bool need_full_block;
+   bool local_rvalue[STRUCT_EQUIV_MAX_LOCAL];
+   rtx *input_use[STRUCT_EQUIV_MAX_INPUT];
+   rtx x_local[STRUCT_EQUIV_MAX_LOCAL], y_local[STRUCT_EQUIV_MAX_LOCAL];
+ };
+ 
+ bool struct_equiv (rtx *xp, rtx y, int rvalue, struct equiv_info *info);
+ bool struct_equiv_set (rtx x, rtx y, struct equiv_info *info);
+ int struct_equiv_block_eq (int mode, struct equiv_info *info);
+ void struct_equiv_merge_block (struct equiv_info *info);
+ 
  #endif /* GCC_BASIC_BLOCK_H */
Index: cfgcleanup.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cfgcleanup.c,v
retrieving revision 1.138
diff -p -r1.138 cfgcleanup.c
*** cfgcleanup.c	29 Nov 2004 20:46:10 -0000	1.138
--- cfgcleanup.c	12 Jan 2005 20:43:42 -0000
*************** Software Foundation, 59 Temple Place - S
*** 50,55 ****
--- 50,57 ----
  #include "target.h"
  #include "cfglayout.h"
  #include "emit-rtl.h"
+ #include "reload.h"
+ #include "expr.h"
  
  /* cleanup_cfg maintains following flags for each basic block.  */
  
*************** static bool first_pass;
*** 74,80 ****
  static bool try_crossjump_to_edge (int, edge, edge);
  static bool try_crossjump_bb (int, basic_block);
  static bool outgoing_edges_match (int, basic_block, basic_block);
- static int flow_find_cross_jump (int, basic_block, basic_block, rtx *, rtx *);
  static bool insns_match_p (int, rtx, rtx);
  
  static void merge_blocks_move_predecessor_nojumps (basic_block, basic_block);
--- 76,81 ----
*************** merge_memattrs (rtx x, rtx y)
*** 995,1000 ****
--- 996,1783 ----
    return;
  }
  
+ /* In SET, assign the bit for the register number of REG the value VALUE.
+    If REG is a hard register, do so for all its consituent registers.
+    Return the number of registers that have become included (as a positive
+    number) or excluded (as a negative number).  */
+ static int
+ assign_reg_reg_set (regset set, rtx reg, int value)
+ {
+   unsigned regno = REGNO (reg);
+   int nregs, i, old;
+ 
+   if (regno >= FIRST_PSEUDO_REGISTER)
+     {
+       if (reload_completed)
+ 	regno = reg_renumber[regno];
+       else
+ 	{
+ 	  if ((value != 0) == REGNO_REG_SET_P (set, regno))
+ 	    return 0;
+ 	  if (value)
+ 	    SET_REGNO_REG_SET (set, regno);
+ 	  else
+ 	    CLEAR_REGNO_REG_SET (set, regno);
+ 	  return value ? 1 : -1;
+ 	}
+     }
+   old = 0;
+   for (i = nregs = hard_regno_nregs[regno][GET_MODE (reg)]; --i >= 0; regno++)
+     {
+       old += REGNO_REG_SET_P (set, regno);
+       if (value)
+ 	SET_REGNO_REG_SET (set, regno);
+       else
+ 	CLEAR_REGNO_REG_SET (set, regno);
+     }
+   return value ? nregs - old : -old;
+ }
+ 
+ /* Check if *xp is equivalent to Y.  RVALUE indicates if the processed
+    piece of rtl is used as a destination, in which case we can't have
+    different registers being an input.  Returns nonzero if the two blocks
+    have been identified as equivalent, zero otherwise.
+    RVALUE == 0: destination
+    RVALUE == 1: source
+    RVALUE == -1: source, ignore SET_DEST of SET / clobber.  */
+ bool
+ struct_equiv (rtx *xp, rtx y, int rvalue, struct equiv_info *info)
+ {
+   rtx x = *xp;
+   enum rtx_code code;
+   int length;
+   const char *format;
+   int i;
+ 
+   if (!y || !x)
+     return x == y;
+   if (code != REG && x == y)
+     return true;
+   code = GET_CODE (y);
+   if (reload_completed
+       && (code == REG || (code == SUBREG && REG_P (SUBREG_REG (y))))
+       && rtx_renumbered_equal_p (x, y))
+     {
+       int regno = true_regnum (x);
+       int nregs = hard_regno_nregs[regno][GET_MODE (x)];
+       int i;
+ 
+       for (i = nregs; --i>= 0; regno++)
+ 	if (REGNO_REG_SET_P (info->local_live, regno))
+ 	  return false;
+ 
+       /* Update liveness information.  */
+       if (info->live_update
+ 	  && assign_reg_reg_set (info->common_live, x, rvalue))
+ 	info->version++;
+ 
+       return (rvalue || !info->input_valid
+ 	      || (!reg_overlap_mentioned_for_reload_p (x, info->x_input)
+ 		  && !reg_overlap_mentioned_for_reload_p (x, info->y_input)));
+     }
+   if (GET_CODE (x) != code
+       || GET_MODE (x) != GET_MODE (y))
+     return false;
+   /* ??? could extend to allow CONST_INT inputs.  */
+   switch (code)
+     {
+     case REG:
+       {
+ 	unsigned x_regno, y_regno;
+ 	int x_common_live, y_common_live;
+ 
+ 	if (reload_completed)
+ 	  x_regno = true_regnum (x), y_regno = true_regnum (y);
+ 	else
+ 	  x_regno = REGNO (x), y_regno = REGNO (y);
+ 
+ 	/* If the register is a locally live one in one block, the
+ 	   corresponding one must be locally live in the other, too, and
+ 	   match of identical regnos doesn't apply.  */
+ 	if (REGNO_REG_SET_P (info->local_live, x_regno))
+ 	  {
+ 	    if (!REGNO_REG_SET_P (info->local_live, y_regno))
+ 	      return false;
+ 	  }
+ 	else if (REGNO_REG_SET_P (info->local_live, y_regno))
+ 	  return false;
+ 	else if (x_regno == y_regno)
+ 	  {
+ 	    if (!rvalue && info->input_valid
+ 		&& (REGNO (info->x_input) == x_regno
+ 		    || REGNO (info->y_input) == y_regno))
+ 	      return false;
+ 
+ 	    /* Update liveness information.  */
+ 	    if (info->live_update
+ 		&& assign_reg_reg_set (info->common_live, x, rvalue))
+ 	      info->version++;
+ 
+ 	    return true;
+ 	  }
+ 
+ 	x_common_live = REGNO_REG_SET_P (info->common_live, x_regno);
+ 	y_common_live = REGNO_REG_SET_P (info->common_live, y_regno);
+ 	if (x_common_live != y_common_live)
+ 	  return false;
+ 	else if (x_common_live)
+ 	  {
+ 	    if (! rvalue || info->input_cost < 0 || no_new_pseudos)
+ 	      return false;
+ 	    /* If info->live_update is not set, we are processing notes.
+ 	       We then allow a match with x_input / y_input found in a
+ 	       previous pass.  */
+ 	    if (info->live_update && !info->input_valid)
+ 	      {
+ 		info->input_valid = true;
+ 		info->x_input = x;
+ 		info->y_input = y;
+ 		info->input_count += optimize_size ? 2 : 1;
+ 		if (info->input_reg)
+ 		  validate_change (info->x_start, xp, info->input_reg, 1);
+ 	      }
+ 	    else if ((info->live_update ? ! info->input_valid : ! info->x_input)
+ 		     || ! rtx_equal_p (x, info->x_input)
+ 		     || ! rtx_equal_p (y, info->y_input))
+ 	      return false;
+ 	    else if (info->input_reg)
+ 	      validate_change (info->x_start, xp, info->input_reg, 1);
+ 	  }
+ 	else
+ 	  {
+ 	    int x_nregs = (x_regno >= FIRST_PSEUDO_REGISTER
+ 			   ? 1 : hard_regno_nregs[x_regno][GET_MODE (x)]);
+ 	    int y_nregs = (y_regno >= FIRST_PSEUDO_REGISTER
+ 			   ? 1 : hard_regno_nregs[y_regno][GET_MODE (y)]);
+ 	    int size = GET_MODE_SIZE (GET_MODE (x));
+ 	    enum machine_mode x_mode = GET_MODE (x);
+ 	    unsigned x_regno_i, y_regno_i;
+ 	    int x_nregs_i, y_nregs_i, size_i;
+ 	    int change;
+ 
+ 	    /* This might be a register local to each block.  See if we have
+ 	       it already registerd.  */
+ 	    for (i = info->local_count - 1; i >= 0; i--)
+ 	      {
+ 		x_regno_i = REGNO (info->x_local[i]);
+ 		x_nregs_i = (x_regno_i >= FIRST_PSEUDO_REGISTER
+ 			     ? 1 : hard_regno_nregs[x_regno_i][GET_MODE (x)]);
+ 		y_regno_i = REGNO (info->x_local[i]);
+ 		y_nregs_i = (y_regno_i >= FIRST_PSEUDO_REGISTER
+ 			     ? 1 : hard_regno_nregs[y_regno_i][GET_MODE (y)]);
+ 		size_i = GET_MODE_SIZE (GET_MODE (info->x_local[i]));
+ 
+ 		/* If we have a new pair of registers that is wider than an
+ 		   old pair and enclosing it with matching offsets,
+ 		   remove the old pair.  If we find a matching, wider, old
+ 		   pair, use the old one.  If the width is the same, use the
+ 		   old one if the modes match, but the new if they don't.
+ 		   We don't want to get too fancy with subreg_regno_offset
+ 		   here, so we just test two straightforwad cases each.  */
+ 		if (info->live_update
+ 		    && (x_mode != GET_MODE (info->x_local[i])
+ 			? size >= size_i : size > size_i))
+ 		  {
+ 		    /* If the new pair is fully enclosing a matching
+ 		       existing pair, remove the old one.  N.B. because
+ 		       we are removing one entry here, the check below
+ 		       if we have space for a new entry will succeed.  */
+ 		    if ((x_regno <= x_regno_i
+ 			 && x_regno + x_nregs >= x_regno_i + x_nregs_i
+ 			 && x_nregs == y_nregs && x_nregs_i == y_nregs_i
+ 			 && x_regno - x_regno_i == y_regno - y_regno_i)
+ 			|| (x_regno == x_regno_i && y_regno == y_regno_i
+ 			    && x_nregs >= x_nregs_i && y_nregs >= y_nregs_i))
+ 		      {
+ 			info->x_local[i] = info->x_local[info->local_count-1];
+ 			info->y_local[i] = info->y_local[info->local_count-1];
+ 			info->local_count--;
+ 			continue;
+ 		      }
+ 		  }
+ 		else
+ 		  {
+ 
+ 		    /* If the new pair is fully enclosed within a matching
+ 		       existing pair, succeed.  */
+ 		    if (x_regno >= x_regno_i
+ 			&& x_regno + x_nregs <= x_regno_i + x_nregs_i
+ 			&& x_nregs == y_nregs && x_nregs_i == y_nregs_i
+ 			&& x_regno - x_regno_i == y_regno - y_regno_i)
+ 		      break;
+ 		    if (x_regno == x_regno_i && y_regno == y_regno_i
+ 			&& x_nregs <= x_nregs_i && y_nregs <= y_nregs_i)
+ 		      break;
+ 		}
+ 
+ 		/* Any other overlap causes a match failure.  */
+ 		if (x_regno + x_nregs > x_regno_i
+ 		    && x_regno_i + x_nregs_i > x_regno)
+ 		  return false;
+ 		if (y_regno + y_nregs > y_regno_i
+ 		    && y_regno_i + y_nregs_i > y_regno)
+ 		  return false;
+ 	      }
+ 	    if (i < 0)
+ 	      {
+ 		/* Not found.  Create a new entry if possible.  */
+ 		if (!info->live_update
+ 		    || info->local_count >= STRUCT_EQUIV_MAX_LOCAL)
+ 		  return false;
+ 		/* Make sure we enter this pair in its renumbered form.  */
+ 		if (x_regno != REGNO (x))
+ 		  x = gen_rtx_REG (GET_MODE (x), x_regno);
+ 		if (y_regno != REGNO (y))
+ 		  y = gen_rtx_REG (GET_MODE (y), y_regno);
+ 		info->x_local[info->local_count] = x;
+ 		info->y_local[info->local_count] = y;
+ 		info->local_count++;
+ 		info->version++;
+ 	      }
+ 	    assign_reg_reg_set (info->local_live, x, rvalue);
+ 	    change = assign_reg_reg_set (info->local_live, y, rvalue);
+ 	    info->input_count += change;
+ 	    if (change)
+ 	      info->version++;
+ 	  }
+ 	return true;
+       }
+     case SET:
+       return ((rvalue < 0
+ 	       || struct_equiv (&SET_DEST (x), SET_DEST (y), 0, info))
+ 	      && struct_equiv (&SET_SRC (x), SET_SRC (y), 1, info));
+     case CLOBBER:
+       return rvalue < 0 || struct_equiv (&XEXP (x, 0), XEXP (y, 0), 0, info);
+     case MEM:
+       return struct_equiv (&XEXP (x, 0), XEXP (y, 0), 1, info);
+     case POST_INC: case POST_DEC: case PRE_INC: case PRE_DEC:
+     case POST_MODIFY: case PRE_MODIFY:
+     case STRICT_LOW_PART:
+       return (struct_equiv (&XEXP (x, 0), XEXP (y, 0), 0, info)
+ 	      && struct_equiv (&XEXP (x, 0), XEXP (y, 0), 1, info));
+     case SIGN_EXTEND: case ZERO_EXTEND:
+       return ((rvalue || struct_equiv (&XEXP (x, 0), XEXP (y, 0), 0, info))
+ 	      && struct_equiv (&XEXP (x, 0), XEXP (y, 0), 1, info));
+     case PARALLEL:
+       {
+ 	int j;
+ 
+ 	if (!struct_equiv_set (x, y, info))
+ 	  return false;
+ 	for (j = 0; j < XVECLEN (x, 0); ++j)
+ 	  if (! struct_equiv (&XVECEXP (x, 0, j), XVECEXP (y, 0, j), -1, info))
+ 	    return false;
+ 	return true;
+       }
+     case LABEL_REF:
+       /* We can't assume nonlocal labels have their following insns yet.  */
+       if (LABEL_REF_NONLOCAL_P (x) || LABEL_REF_NONLOCAL_P (y))
+ 	return XEXP (x, 0) == XEXP (y, 0);
+ 
+       /* Two label-refs are equivalent if they point at labels
+ 	 in the same position in the instruction stream.  */
+       return (next_real_insn (XEXP (x, 0))
+ 	      == next_real_insn (XEXP (y, 0)));
+     case SYMBOL_REF:
+       return XSTR (x, 0) == XSTR (y, 0);
+     /* Some rtl is guaranteed to be shared, or unique;  If we didn't match
+        EQ equality above, they aren't the same.  */
+     case CONST_INT:
+     case CODE_LABEL:
+       return false;
+     default:
+       break;
+     }
+ 
+   /* For commutative operations, the RTX match if the operands match in any
+      order.  */
+   if (targetm.commutative_p (x, UNKNOWN))
+     return ((struct_equiv (&XEXP (x, 0), XEXP (y, 0), rvalue, info)
+ 	     && struct_equiv (&XEXP (x, 1), XEXP (y, 1), rvalue, info))
+ 	    || (struct_equiv (&XEXP (x, 0), XEXP (y, 1), rvalue, info)
+ 		&& struct_equiv (&XEXP (x, 1), XEXP (y, 0), rvalue, info)));
+ 
+   /* Process subexpressions - this is similar to rtx_equal_p.  */
+   length = GET_RTX_LENGTH (code);
+   format = GET_RTX_FORMAT (code);
+ 
+   for (i = 0; i < length; ++i)
+     {
+       switch (format[i])
+         {
+ 	case 'w':
+ 	  if (XWINT (x, i) != XWINT (y, i))
+ 	    return false;
+ 	  break;
+ 	case 'n':
+ 	case 'i':
+ 	  if (XINT (x, i) != XINT (y, i))
+ 	    return false;
+ 	  break;
+         case 'V':
+         case 'E':
+ 	  if (XVECLEN (x, i) != XVECLEN (y, i))
+ 	    return 0;
+           if (XVEC (x, i) != 0)
+             {
+               int j;
+               for (j = 0; j < XVECLEN (x, i); ++j)
+                 {
+                   if (! struct_equiv (&XVECEXP (x, i, j), XVECEXP (y, i, j),
+ 				      rvalue, info))
+ 		    return false;
+                 }
+             }
+           break;
+         case 'e':
+           if (! struct_equiv (&XEXP (x, i), XEXP (y, i), rvalue, info))
+ 	    return false;
+           break;
+ 	case 'S':
+ 	case 's':
+ 	  if ((XSTR (x, i) || XSTR (y, i))
+ 	      && (! XSTR (x, i) || ! XSTR (y, i)
+ 		  || strcmp (XSTR (x, i), XSTR (y, i))))
+ 	    return false;
+ 	  break;
+         case 'u':
+ 	  /* These are just backpointers, so they don't matter.  */
+ 	  break;
+         case '0':
+         case 't':
+ 	  break;
+ 	  /* It is believed that rtx's at this level will never
+ 	     contain anything but integers and other rtx's,
+ 	     except for within LABEL_REFs and SYMBOL_REFs.  */
+ 	default:
+ 	  gcc_unreachable ();
+ 	}
+     }
+   return true;
+ }
+ 
+ /* Do only the struct_equiv SET_DEST processing for SETs and CLOBBERs.  */
+ bool
+ struct_equiv_set (rtx x, rtx y, struct equiv_info *info)
+ {
+   if (GET_CODE (x) == SET || GET_CODE (x) == CLOBBER)
+     return struct_equiv (&XEXP (x, 0), XEXP (y, 0), 0, info);
+   else if (GET_CODE (x) == PARALLEL)
+     {
+       int j;
+ 
+       if (XVECLEN (x, 0) != XVECLEN (y, 0))
+ 	return 0;
+       for (j = 0; j < XVECLEN (x, 0); ++j)
+ 	{
+ 	  rtx ye = XVECEXP (y, 0, j);
+ 	  if ((GET_CODE (ye) == SET || GET_CODE (ye) == CLOBBER)
+ 	      && ! struct_equiv (&XVECEXP (x, 0, j), ye, 0, info))
+ 	    return false;
+ 	}
+     }
+   return true;
+ }
+ 
+ static void
+ struct_equiv_make_checkpoint (int n, struct equiv_info *info)
+ {
+   struct struct_equiv_checkpoint *p = &info->checkpoint[n];
+ 
+   p->ninsns = info->ninsns;
+   p->version = info->version;
+   p->local_count = info->local_count;
+   p->input_count = info->input_count;
+   p->x_start = info->x_start;
+   p->y_start = info->y_start;
+   p->input_valid = info->input_valid;
+ }
+ 
+ static void
+ struct_equiv_improve_checkpoint (int n, struct equiv_info *info)
+ {
+   struct struct_equiv_checkpoint *p = &info->checkpoint[n];
+ 
+ #ifdef HAVE_cc0
+   if (reg_mentioned_p (cc0_rtx, info->x_start) && !sets_cc0_p (info->x_start))
+     return;
+ #endif
+   if (info->input_cost >= 0
+       ? (COSTS_N_INSNS(info->ninsns - p->ninsns)
+ 	 > info->input_cost * (info->input_count - p->input_count))
+       : info->ninsns > p->ninsns && !info->input_count)
+     struct_equiv_make_checkpoint (n, info);
+ }
+ 
+ static void
+ struct_equiv_restore_checkpoint (int n, struct equiv_info *info)
+ {
+   struct struct_equiv_checkpoint *p = &info->checkpoint[n];
+ 
+   info->ninsns = p->ninsns;
+   info->x_start = p->x_start;
+   info->y_start = p->y_start;
+   info->input_count = p->input_count;
+   info->input_valid = p->input_valid;
+   while (info->local_count > p->local_count)
+     {
+       info->local_count--;
+       info->version--;
+       if (REGNO_REG_SET_P (info->local_live,
+ 			   REGNO (info->x_local[info->local_count])))
+ 	{
+ 	  assign_reg_reg_set (info->local_live,
+ 			      info->x_local[info->local_count], 0);
+ 	  assign_reg_reg_set (info->local_live,
+ 			      info->y_local[info->local_count], 0);
+ 	  info->version--;
+ 	}
+     }
+   if (info->version != p->version)
+     info->need_rerun = true;
+ }
+ 
+ static bool
+ struct_equiv_death (rtx i1 ATTRIBUTE_UNUSED, rtx i2 ATTRIBUTE_UNUSED,
+ 		    struct equiv_info *info ATTRIBUTE_UNUSED)
+ {
+ #ifdef STACK_REGS
+   /* If cross_jump_death_matters is not 0, the insn's mode
+      indicates whether or not the insn contains any stack-like regs.  */
+     
+   if (INSN_P (i1)
+       && (info->mode & CLEANUP_POST_REGSTACK) && stack_regs_mentioned (i1))
+     {
+       /* If register stack conversion has already been done, then
+ 	 death notes must also be compared before it is certain that
+ 	 the two instruction streams match.  */
+     
+       rtx note;
+       HARD_REG_SET i1_regset, i2_regset;
+     
+       CLEAR_HARD_REG_SET (i1_regset);
+       CLEAR_HARD_REG_SET (i2_regset);
+ 
+       for (note = REG_NOTES (i1); note; note = XEXP (note, 1))
+ 	if (REG_NOTE_KIND (note) == REG_DEAD
+ 	    && STACK_REG_P (XEXP (note, 0)))
+ 	  SET_HARD_REG_BIT (i1_regset, REGNO (XEXP (note, 0)));
+ 
+       for (note = REG_NOTES (i2); note; note = XEXP (note, 1))
+ 	if (REG_NOTE_KIND (note) == REG_DEAD
+ 	    && STACK_REG_P (XEXP (note, 0)))
+ 	  {
+ 	    int regno = REGNO (XEXP (note, 0));
+ 	    int i;
+ 
+ 	    for (i = info->local_count - 1; i >= 0; i--)
+ 	      if (regno == REGNO (info->y_local[i]))
+ 		{
+ 		  regno = REGNO (info->x_local[i]);
+ 		  break;
+ 		}
+ 	    SET_HARD_REG_BIT (i2_regset, regno);
+ 	  }
+ 
+       GO_IF_HARD_REG_EQUAL (i1_regset, i2_regset, done);
+ 
+       return false;
+ 
+     done:
+       ;
+     }
+ #endif
+   return true;
+ }
+ 
+ /* Return number of matched insns.  */
+ /* This function must be called up to three times for a successful cross-jump
+    match:
+    first to find out which instructions do match.  While trying to match
+    another instruction that doesn't match, we destroy information in info
+    about the actual inputs.  So if there have been any before the last
+    match attempt, we need to callthis function again to recompute the
+    actual inputs up to the actual start of the matching sequence.
+    When we are then satisfied that the cross-jump is worth while, we
+    call this function a third time to make any changes needed to make the
+    sequences match: apply equivalences, remove non-matching
+    notes and merge memory attributes.  */
+ /* if mode contains STRUCT_EQUIV_FINAL, also change x to contain input_reg.  */
+ int
+ struct_equiv_block_eq (int mode, struct equiv_info *info)
+ {
+   rtx x_stop, y_stop;
+   rtx xi, yi;
+   int i;
+ 
+   if (! REG_SET_EQUAL_P (info->x_block->global_live_at_end,
+ 			 info->y_block->global_live_at_end))
+     return 0;
+   info->mode = mode;
+   if (mode & STRUCT_EQUIV_START)
+     {
+       x_stop = BB_HEAD (info->x_block);
+       y_stop = BB_HEAD (info->y_block);
+       if (!x_stop || !y_stop)
+ 	return 0;
+       info->x_input = info->y_input = NULL_RTX;
+       info->equiv_used = ALLOC_REG_SET (&reg_obstack);
+       info->input_reg = NULL_RTX;
+     }
+   else
+     {
+       x_stop = info->x_start;
+       y_stop = info->y_start;
+       if (info->input_valid && ! info->input_reg)
+ 	info->input_reg = gen_reg_rtx (GET_MODE (info->x_input));
+     }
+   info->ninsns = info->version = info->local_count = info->input_count = 0;
+   info->x_start = info->y_start = NULL_RTX;
+   info->need_rerun = false;
+   info->live_update = true;
+   info->input_valid = false;
+   info->common_live = ALLOC_REG_SET (&reg_obstack);
+   info->local_live = ALLOC_REG_SET (&reg_obstack);
+   COPY_REG_SET (info->common_live, info->x_block->global_live_at_end);
+   struct_equiv_make_checkpoint (0, info);
+ 
+   /* Skip simple jumps at the end of the blocks.  Complex jumps still
+      need to be compared for equivalence, which we'll do below.  */
+ 
+   xi = BB_END (info->x_block);
+   if (onlyjump_p (xi)
+       || (returnjump_p (xi) && !side_effects_p (PATTERN (xi))))
+     {
+       info->x_start = xi;
+       xi = PREV_INSN (xi);
+     }
+ 
+   yi = BB_END (info->y_block);
+   if (onlyjump_p (yi)
+       || (returnjump_p (yi) && !side_effects_p (PATTERN (yi))))
+     {
+       info->y_start = yi;
+       /* Count everything except for unconditional jump as insn.  */
+       /* ??? Is it right to count unconditional jumps with a clobber?
+          Should we count conditional returns?  */
+       if (!simplejump_p (yi) && !returnjump_p (yi) && info->x_start)
+ 	info->ninsns++;
+       yi = PREV_INSN (yi);
+     }
+ 
+   struct_equiv_improve_checkpoint (0, info);
+   info->x_end = xi;
+   info->y_end = yi;
+   for (;;)
+     {
+       int rvalue_change_start;
+ 
+       /* Ignore notes.  */
+       while (!INSN_P (xi) && xi != x_stop)
+ 	xi = PREV_INSN (xi);
+ 
+       while (!INSN_P (yi) && yi != y_stop)
+ 	yi = PREV_INSN (yi);
+ 
+       if (GET_CODE (xi) != GET_CODE (yi))
+ 	break;
+ 
+       info->x_start = xi;
+       info->y_start = yi;
+ 
+       /* If this is a CALL_INSN, compare register usage information.
+ 	 If we don't check this on stack register machines, the two
+ 	 CALL_INSNs might be merged leaving reg-stack.c with mismatching
+ 	 numbers of stack registers in the same basic block.
+ 	 If we don't check this on machines with delay slots, a delay slot may
+ 	 be filled that clobbers a parameter expected by the subroutine.
+ 
+ 	 ??? We take the simple route for now and assume that if they're
+ 	 equal, they were constructed identically.  */
+ 
+       if (CALL_P (xi))
+ 	{
+ 	  if (SIBLING_CALL_P (xi) != SIBLING_CALL_P (yi)
+ 	      || ! struct_equiv_set (PATTERN (xi), PATTERN (yi), info)
+ 	      || ! struct_equiv_set (CALL_INSN_FUNCTION_USAGE (xi),
+ 				     CALL_INSN_FUNCTION_USAGE (yi), info)
+ 	      || ! struct_equiv (&CALL_INSN_FUNCTION_USAGE (xi),
+ 				 CALL_INSN_FUNCTION_USAGE (yi), -1, info))
+ 	    break;
+ 	}
+       else if (INSN_P (xi))
+ 	{
+ 	  if (! struct_equiv_set (PATTERN (xi), PATTERN (yi), info))
+ 	    break;
+ 	}
+       rvalue_change_start = num_validated_changes ();
+       struct_equiv_make_checkpoint (1, info);
+       /* Check struct_equiv_death *after* the inputs have been processed,
+ 	 so that local inputs will already have been set up.  */
+       if (INSN_P (xi)
+ 	  && (bitmap_bit_p (info->equiv_used, info->ninsns)
+ 	      || ! struct_equiv (&PATTERN (xi), PATTERN (yi), -1, info)
+ 	      || ! struct_equiv_death (xi, yi, info)
+ 	      || ! verify_changes (0)))
+ 	{
+ 	  cancel_changes (rvalue_change_start);
+ 	  struct_equiv_restore_checkpoint (1, info);
+ 	  /* Do not do EQUIV substitution after reload.  First, we're undoing
+ 	     the work of reload_cse.  Second, we may be undoing the work of
+ 	     the post- reload splitting pass.  */
+ 	  /* ??? Possibly add a new phase switch variable that can be used by
+ 	     targets to disallow the troublesome insns after splitting.  */
+ 	  if (!reload_completed)
+ 	    {
+ 	      /* The following code helps take care of G++ cleanups.  */
+ 	      rtx equiv1 = find_reg_equal_equiv_note (xi);
+ 	      rtx equiv2 = find_reg_equal_equiv_note (yi);
+ 
+ 	      if (equiv1 && equiv2
+ 		  /* If the equivalences are not to a constant, they may
+ 		     reference pseudos that no longer exist, so we can't
+ 		     use them.  */
+ 		  && (! reload_completed
+ 		      || (CONSTANT_P (XEXP (equiv1, 0))
+ 			  && rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))))
+ 		{
+ 		  rtx s1 = single_set (xi);
+ 		  rtx s2 = single_set (yi);
+ 
+ 		  if (s1 != 0 && s2 != 0)
+ 		    {
+ 		      validate_change (xi, &SET_SRC (s1), XEXP (equiv1, 0), 1);
+ 		      validate_change (yi, &SET_SRC (s2), XEXP (equiv2, 0), 1);
+ 		      /* Only inspecting the new SET_SRC is not good enough,
+ 			 because there may also be bare USEs in a single_set
+ 			 PARALLEL.  */
+ 		      if (!struct_equiv (&PATTERN (xi), PATTERN (yi), -1, info)
+ 			  || !struct_equiv_death (xi, yi, info)
+ 			  || !verify_changes (0))
+ 			{
+ 			  cancel_changes (0);
+ 			  break;
+ 			}
+ 		      else if (info->mode & STRUCT_EQUIV_FINAL)
+ 			/* Success, commit now.  */
+ 			confirm_change_group ();
+ 		      else
+ 			/* Success, but put old code back for now, since we do
+ 			   not know yet if we will want to make any change.  */
+ 			cancel_changes (0);
+ 		      /* Mark this insn so that we'll use the equivalence in
+ 			 all subsequent passes.  */
+ 		      bitmap_set_bit (info->equiv_used, info->ninsns);
+ 		    }
+ 		}
+ 	    }
+ 	  else
+ 	    break;
+ 	}
+       if (INSN_P (xi))
+ 	{
+ 	  if (info->mode & STRUCT_EQUIV_FINAL)
+ 	    {
+ 	      rtx equiv1, equiv2;
+ 
+ 	      merge_memattrs (xi, yi);
+ 
+ 	      /* If the merged insns have different REG_EQUAL notes, then
+ 		 remove them.  */
+ 	      info->live_update = 0;
+ 	      equiv1 = find_reg_equal_equiv_note (xi);
+ 	      equiv2 = find_reg_equal_equiv_note (yi);
+ 	      if (equiv1 && !equiv2)
+ 		remove_note (xi, equiv1);
+ 	      else if (!equiv1 && equiv2)
+ 		remove_note (yi, equiv2);
+ 	      else if (equiv1 && equiv2
+ 		       && !struct_equiv (&XEXP (equiv1, 0), XEXP (equiv2, 0),
+ 					 1, info))
+ 		{
+ 		  remove_note (xi, equiv1);
+ 		  remove_note (yi, equiv2);
+ 		}
+ 	      info->live_update = 1;
+ 	    }
+ 	  info->ninsns++;
+ 	  info->x_start = xi;
+ 	  info->y_start = yi;
+ 	  struct_equiv_improve_checkpoint (0, info);
+ 	}
+       if (xi == x_stop || yi == y_stop)
+ 	{
+ 	  /* If we reached the start of at least one of the blocks, but
+ 	     checkpoint[0] hasn't been advanced back to the first valid insn
+ 	     yet, represent the increased benefit of completing the block
+ 	     as an increased instruction count.  */
+ 	  if (info->checkpoint[0].x_start != info->x_start
+ 	      && (xi == BB_HEAD (info->x_block)
+ 		  || yi == BB_HEAD (info->y_block)))
+ 	    {
+ 	      info->ninsns++;
+ 	      struct_equiv_improve_checkpoint (0, info);
+ 	      info->ninsns--;
+ 	    }
+ 	  break;
+ 	}
+       xi = PREV_INSN (xi);
+       yi = PREV_INSN (yi);
+     }
+ 
+   /* Restore to checkpoint[0] to get the sequence with the best known-so-far
+      cost-benefit difference.  */
+   struct_equiv_restore_checkpoint (0, info);
+ 
+   /* Include preceding notes and labels in the cross-jump / if-conversion.
+      One, this may bring us to the head of the blocks.
+      Two, it keeps line number notes as matched as may be.  */
+   if (info->ninsns)
+     {
+       xi = info->x_start;
+       yi = info->y_start;
+       while (xi != x_stop && !INSN_P (PREV_INSN (xi)))
+ 	xi = PREV_INSN (xi);
+ 
+       while (yi != y_stop && !INSN_P (PREV_INSN (yi)))
+ 	yi = PREV_INSN (yi);
+ 
+       info->x_start = xi;
+       info->y_start = yi;
+     }
+ 
+   if (info->input_valid && info->input_reg == NULL_RTX)
+     info->need_rerun = true;
+   if (!info->input_valid)
+     {
+       info->x_input = 0;
+       info->y_input = 0;
+     }
+   if (!info->need_rerun)
+     {
+       info->dying_inputs = 0;
+       for (i = info->local_count-1; i >=0; i--)
+ 	{
+ 	  rtx x = info->x_local[i];
+ 	  unsigned regno = REGNO (x);
+ 	  int nregs = (regno >= FIRST_PSEUDO_REGISTER
+ 		       ? 1 : hard_regno_nregs[regno][GET_MODE (x)]);
+ 
+ 	  for (info->local_rvalue[i] = false; nregs >= 0; regno++, --nregs)
+ 	    if (REGNO_REG_SET_P (info->local_live, regno))
+ 	      {
+ 		info->dying_inputs++;
+ 		info->local_rvalue[i] = true;
+ 		break;
+ 	      }
+ 	}
+     }
+ 
+   if (info->need_full_block
+       && (info->x_start != x_stop || info->y_start != y_stop))
+     return 0;
+   return info->ninsns;
+ }
  
  /* Return true if I1 and I2 are equivalent and thus can be crossjumped.  */
  
*************** insns_match_p (int mode ATTRIBUTE_UNUSED
*** 1104,1222 ****
    return false;
  }
  
- /* Look through the insns at the end of BB1 and BB2 and find the longest
-    sequence that are equivalent.  Store the first insns for that sequence
-    in *F1 and *F2 and return the sequence length.
- 
-    To simplify callers of this function, if the blocks match exactly,
-    store the head of the blocks in *F1 and *F2.  */
- 
- static int
- flow_find_cross_jump (int mode ATTRIBUTE_UNUSED, basic_block bb1,
- 		      basic_block bb2, rtx *f1, rtx *f2)
- {
-   rtx i1, i2, last1, last2, afterlast1, afterlast2;
-   int ninsns = 0;
- 
-   /* Skip simple jumps at the end of the blocks.  Complex jumps still
-      need to be compared for equivalence, which we'll do below.  */
- 
-   i1 = BB_END (bb1);
-   last1 = afterlast1 = last2 = afterlast2 = NULL_RTX;
-   if (onlyjump_p (i1)
-       || (returnjump_p (i1) && !side_effects_p (PATTERN (i1))))
-     {
-       last1 = i1;
-       i1 = PREV_INSN (i1);
-     }
- 
-   i2 = BB_END (bb2);
-   if (onlyjump_p (i2)
-       || (returnjump_p (i2) && !side_effects_p (PATTERN (i2))))
-     {
-       last2 = i2;
-       /* Count everything except for unconditional jump as insn.  */
-       if (!simplejump_p (i2) && !returnjump_p (i2) && last1)
- 	ninsns++;
-       i2 = PREV_INSN (i2);
-     }
- 
-   while (true)
-     {
-       /* Ignore notes.  */
-       while (!INSN_P (i1) && i1 != BB_HEAD (bb1))
- 	i1 = PREV_INSN (i1);
- 
-       while (!INSN_P (i2) && i2 != BB_HEAD (bb2))
- 	i2 = PREV_INSN (i2);
- 
-       if (i1 == BB_HEAD (bb1) || i2 == BB_HEAD (bb2))
- 	break;
- 
-       if (!insns_match_p (mode, i1, i2))
- 	break;
- 
-       merge_memattrs (i1, i2);
- 
-       /* Don't begin a cross-jump with a NOTE insn.  */
-       if (INSN_P (i1))
- 	{
- 	  /* If the merged insns have different REG_EQUAL notes, then
- 	     remove them.  */
- 	  rtx equiv1 = find_reg_equal_equiv_note (i1);
- 	  rtx equiv2 = find_reg_equal_equiv_note (i2);
- 
- 	  if (equiv1 && !equiv2)
- 	    remove_note (i1, equiv1);
- 	  else if (!equiv1 && equiv2)
- 	    remove_note (i2, equiv2);
- 	  else if (equiv1 && equiv2
- 		   && !rtx_equal_p (XEXP (equiv1, 0), XEXP (equiv2, 0)))
- 	    {
- 	      remove_note (i1, equiv1);
- 	      remove_note (i2, equiv2);
- 	    }
- 
- 	  afterlast1 = last1, afterlast2 = last2;
- 	  last1 = i1, last2 = i2;
- 	  ninsns++;
- 	}
- 
-       i1 = PREV_INSN (i1);
-       i2 = PREV_INSN (i2);
-     }
- 
- #ifdef HAVE_cc0
-   /* Don't allow the insn after a compare to be shared by
-      cross-jumping unless the compare is also shared.  */
-   if (ninsns && reg_mentioned_p (cc0_rtx, last1) && ! sets_cc0_p (last1))
-     last1 = afterlast1, last2 = afterlast2, ninsns--;
- #endif
- 
-   /* Include preceding notes and labels in the cross-jump.  One,
-      this may bring us to the head of the blocks as requested above.
-      Two, it keeps line number notes as matched as may be.  */
-   if (ninsns)
-     {
-       while (last1 != BB_HEAD (bb1) && !INSN_P (PREV_INSN (last1)))
- 	last1 = PREV_INSN (last1);
- 
-       if (last1 != BB_HEAD (bb1) && LABEL_P (PREV_INSN (last1)))
- 	last1 = PREV_INSN (last1);
- 
-       while (last2 != BB_HEAD (bb2) && !INSN_P (PREV_INSN (last2)))
- 	last2 = PREV_INSN (last2);
- 
-       if (last2 != BB_HEAD (bb2) && LABEL_P (PREV_INSN (last2)))
- 	last2 = PREV_INSN (last2);
- 
-       *f1 = last1;
-       *f2 = last2;
-     }
- 
-   return ninsns;
- }
- 
  /* Return true iff outgoing edges of BB1 and BB2 match, together with
     the branch instruction.  This means that if we commonize the control
     flow before end of the basic block, the semantic remains unchanged.
--- 1887,1892 ----
*************** outgoing_edges_match (int mode, basic_bl
*** 1495,1508 ****
  static bool
  try_crossjump_to_edge (int mode, edge e1, edge e2)
  {
!   int nmatch;
    basic_block src1 = e1->src, src2 = e2->src;
    basic_block redirect_to, redirect_from, to_remove;
-   rtx newpos1, newpos2;
    edge s;
    edge_iterator ei;
! 
!   newpos1 = newpos2 = NULL_RTX;
  
    /* If we have partitioned hot/cold basic blocks, it is a bad idea
       to try this optimization. 
--- 2165,2176 ----
  static bool
  try_crossjump_to_edge (int mode, edge e1, edge e2)
  {
!   int nmatch, i;
    basic_block src1 = e1->src, src2 = e2->src;
    basic_block redirect_to, redirect_from, to_remove;
    edge s;
    edge_iterator ei;
!   struct equiv_info info;
  
    /* If we have partitioned hot/cold basic blocks, it is a bad idea
       to try this optimization. 
*************** try_crossjump_to_edge (int mode, edge e1
*** 1553,1567 ****
      return false;
  
    /* ... and part the second.  */
!   nmatch = flow_find_cross_jump (mode, src1, src2, &newpos1, &newpos2);
  
    /* Don't proceed with the crossjump unless we found a sufficient number
       of matching instructions or the 'from' block was totally matched
       (such that its predecessors will hopefully be redirected and the
       block removed).  */
!   if ((nmatch < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
!       && (newpos1 != BB_HEAD (src1)))
      return false;
  
    /* Here we know that the insns in the end of SRC1 which are common with SRC2
       will be deleted.
--- 2221,2258 ----
      return false;
  
    /* ... and part the second.  */
!   info.x_block = src2;
!   info.y_block = src1;
!   info.need_full_block = false;
!   info.input_cost = optimize_size ? COSTS_N_INSNS (1) : -1;
!   nmatch = struct_equiv_block_eq (STRUCT_EQUIV_START | mode, &info);
  
    /* Don't proceed with the crossjump unless we found a sufficient number
       of matching instructions or the 'from' block was totally matched
       (such that its predecessors will hopefully be redirected and the
       block removed).  */
!   if ((nmatch -info.input_count < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
!       && (info.y_start != BB_HEAD (src1)))
      return false;
+   while (info.need_rerun)
+     {
+       nmatch = struct_equiv_block_eq (STRUCT_EQUIV_RERUN | mode, &info);
+       if ((nmatch -info.input_count < PARAM_VALUE (PARAM_MIN_CROSSJUMP_INSNS))
+ 	   && (info.y_start != BB_HEAD (src1)))
+ 	return false;
+     }
+   struct_equiv_block_eq (STRUCT_EQUIV_FINAL | mode, &info);
+   if (info.input_reg)
+     {
+       emit_insn_before (gen_move_insn (info.input_reg, info.x_input),
+ 			info.x_start);
+       emit_insn_before (gen_move_insn (info.input_reg, info.y_input),
+ 			info.y_start);
+     }
+   for (i = info.local_count; --i >= 0; )
+     if (info.local_rvalue[i])
+       emit_insn_before (gen_move_insn (info.x_local[i], info.y_local[i]),
+ 			info.y_start);
  
    /* Here we know that the insns in the end of SRC1 which are common with SRC2
       will be deleted.
*************** try_crossjump_to_edge (int mode, edge e1
*** 1595,1608 ****
      }
  
    /* Avoid splitting if possible.  */
!   if (newpos2 == BB_HEAD (src2))
      redirect_to = src2;
    else
      {
        if (dump_file)
  	fprintf (dump_file, "Splitting bb %i before %i insns\n",
  		 src2->index, nmatch);
!       redirect_to = split_block (src2, PREV_INSN (newpos2))->dest;
      }
  
    if (dump_file)
--- 2286,2299 ----
      }
  
    /* Avoid splitting if possible.  */
!   if (info.x_start == BB_HEAD (src2))
      redirect_to = src2;
    else
      {
        if (dump_file)
  	fprintf (dump_file, "Splitting bb %i before %i insns\n",
  		 src2->index, nmatch);
!       redirect_to = split_block (src2, PREV_INSN (info.x_start))->dest;
      }
  
    if (dump_file)
*************** try_crossjump_to_edge (int mode, edge e1
*** 1673,1685 ****
    /* Edit SRC1 to go to REDIRECT_TO at NEWPOS1.  */
  
    /* Skip possible basic block header.  */
!   if (LABEL_P (newpos1))
!     newpos1 = NEXT_INSN (newpos1);
  
!   if (NOTE_P (newpos1))
!     newpos1 = NEXT_INSN (newpos1);
  
!   redirect_from = split_block (src1, PREV_INSN (newpos1))->src;
    to_remove = EDGE_SUCC (redirect_from, 0)->dest;
  
    redirect_edge_and_branch_force (EDGE_SUCC (redirect_from, 0), redirect_to);
--- 2364,2376 ----
    /* Edit SRC1 to go to REDIRECT_TO at NEWPOS1.  */
  
    /* Skip possible basic block header.  */
!   if (LABEL_P (info.y_start))
!     info.y_start = NEXT_INSN (info.y_start);
  
!   if (NOTE_P (info.y_start))
!     info.y_start = NEXT_INSN (info.y_start);
  
!   redirect_from = split_block (src1, PREV_INSN (info.y_start))->src;
    to_remove = EDGE_SUCC (redirect_from, 0)->dest;
  
    redirect_edge_and_branch_force (EDGE_SUCC (redirect_from, 0), redirect_to);
Index: hooks.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/hooks.c,v
retrieving revision 1.36
diff -p -r1.36 hooks.c
*** hooks.c	7 Oct 2004 04:00:55 -0000	1.36
--- hooks.c	12 Jan 2005 20:43:42 -0000
*************** hook_bool_rtx_false (rtx a ATTRIBUTE_UNU
*** 184,189 ****
--- 184,195 ----
  }
  
  bool
+ hook_bool_rtx_int_false (rtx a ATTRIBUTE_UNUSED, int code ATTRIBUTE_UNUSED)
+ {
+   return false;
+ }
+ 
+ bool
  hook_bool_uintp_uintp_false (unsigned int *a ATTRIBUTE_UNUSED,
  			     unsigned int *b ATTRIBUTE_UNUSED)
  {
Index: ifcvt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ifcvt.c,v
retrieving revision 1.174
diff -p -r1.174 ifcvt.c
*** ifcvt.c	18 Dec 2004 15:26:32 -0000	1.174
--- ifcvt.c	12 Jan 2005 20:43:42 -0000
*************** static rtx noce_get_alt_condition (struc
*** 646,651 ****
--- 646,653 ----
  static int noce_try_minmax (struct noce_if_info *);
  static int noce_try_abs (struct noce_if_info *);
  static int noce_try_sign_mask (struct noce_if_info *);
+ static bool noce_try_complex_cmove (struct ce_if_block *, struct noce_if_info *,
+ 				    rtx, rtx);
  
  /* Helper function for noce_try_store_flag*.  */
  
*************** noce_process_if_block (struct ce_if_bloc
*** 1946,1952 ****
    if (! insn_a
        || insn_a != last_active_insn (then_bb, FALSE)
        || (set_a = single_set (insn_a)) == NULL_RTX)
!     return FALSE;
  
    x = SET_DEST (set_a);
    a = SET_SRC (set_a);
--- 1948,1956 ----
    if (! insn_a
        || insn_a != last_active_insn (then_bb, FALSE)
        || (set_a = single_set (insn_a)) == NULL_RTX)
!     return ((else_bb && HAVE_conditional_move)
! 	    ? noce_try_complex_cmove (ce_info, &if_info, jump, cond)
! 	    : FALSE);
  
    x = SET_DEST (set_a);
    a = SET_SRC (set_a);
*************** find_if_block (struct ce_if_block * ce_i
*** 2663,2668 ****
--- 2667,2740 ----
    return process_if_block (ce_info);
  }
  
+ /* Try to use a cmove where if end else blocks are structurally equivalent
+    blocks that differ only by an input register, and possible some local
+    registers.  */
+ static bool
+ noce_try_complex_cmove (struct ce_if_block * ce_info,
+ 			struct noce_if_info *if_info, rtx jump, rtx cond)
+ {
+   basic_block then_bb = ce_info->then_bb;	/* THEN */
+   basic_block else_bb = ce_info->else_bb;	/* ELSE or NULL */
+   rtx yi;
+   struct equiv_info info;
+   int i;
+   rtx temp;
+   rtx next;
+   int n_matched;
+ 
+   if_info->cond = cond;
+   if_info->jump = jump;
+   info.x_block = else_bb;
+   info.y_block = then_bb;
+   info.need_full_block = true;
+   info.input_cost = 0;
+   n_matched = struct_equiv_block_eq (STRUCT_EQUIV_START, &info);
+   if (! n_matched)
+     return false;
+   while (info.need_rerun)
+     {
+       n_matched = struct_equiv_block_eq (STRUCT_EQUIV_RERUN, &info);
+       if (! n_matched)
+ 	return false;
+     }
+   /* We want exactly one input.
+      ??? We might allow more inputs if BRANCH_COST is high, or no inputs
+      as a means of commoning basically identical code.  */
+   if (info.input_valid && ! info.dying_inputs)
+     {
+       temp = info.input_reg;
+       if_info->a = info.y_input;
+       if_info->b = info.x_input;
+     }
+   else
+     {
+       if (info.dying_inputs != 1)
+ 	return false;
+       for (i = info.local_count-1; ! info.local_rvalue[i]; ) i--;
+       temp = info.x_local[i];
+       if_info->a = info.y_local[i];
+       if_info->b = info.x_local[i];
+     }
+   if_info->x = temp;
+   if (! noce_try_cmove (if_info))
+     return false;
+   struct_equiv_block_eq (STRUCT_EQUIV_FINAL, &info);
+   if (flag_expensive_optimizations)
+     cond_exec_changed_p = TRUE;
+   for (yi = BB_HEAD (then_bb); BB_HEAD (then_bb) != BB_END (then_bb); yi = next)
+     {
+       next = NEXT_INSN (yi);
+       if (INSN_P (yi))
+ 	next = delete_insn (yi);
+       if (yi == BB_END (then_bb) || PREV_INSN (yi) == BB_END (then_bb))
+ 	break;
+     }
+   delete_insn (jump);
+   merge_if_block (ce_info);
+   return true;
+ }
+ 
  /* Convert a branch over a trap, or a branch
     to a trap, into a conditional trap.  */
  
Index: recog.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/recog.c,v
retrieving revision 1.215
diff -p -r1.215 recog.c
*** recog.c	25 Nov 2004 09:30:04 -0000	1.215
--- recog.c	12 Jan 2005 20:43:42 -0000
*************** num_changes_pending (void)
*** 294,304 ****
    return num_changes;
  }
  
! /* Apply a group of changes previously issued with `validate_change'.
     Return 1 if all changes are valid, zero otherwise.  */
  
  int
! apply_change_group (void)
  {
    int i;
    rtx last_validated = NULL_RTX;
--- 294,304 ----
    return num_changes;
  }
  
! /* Tentatively apply the changes numbered NUM and up.
     Return 1 if all changes are valid, zero otherwise.  */
  
  int
! verify_changes (int num)
  {
    int i;
    rtx last_validated = NULL_RTX;
*************** apply_change_group (void)
*** 312,318 ****
       we also require that the operands meet the constraints for
       the insn.  */
  
!   for (i = 0; i < num_changes; i++)
      {
        rtx object = changes[i].object;
  
--- 312,318 ----
       we also require that the operands meet the constraints for
       the insn.  */
  
!   for (i = num; i < num_changes; i++)
      {
        rtx object = changes[i].object;
  
*************** apply_change_group (void)
*** 376,392 ****
        last_validated = object;
      }
  
!   if (i == num_changes)
!     {
!       basic_block bb;
  
!       for (i = 0; i < num_changes; i++)
! 	if (changes[i].object
! 	    && INSN_P (changes[i].object)
! 	    && (bb = BLOCK_FOR_INSN (changes[i].object)))
! 	  bb->flags |= BB_DIRTY;
  
!       num_changes = 0;
        return 1;
      }
    else
--- 376,413 ----
        last_validated = object;
      }
  
!   return (i == num_changes);
! }
  
! /* A group of changes has previously been issued with validate_change and
!    verified with verify_changes.  Update the BB_DIRTY flags of the affected
!    blocks, and clear num_changes.  */
  
! void
! confirm_change_group (void)
! {
!   int i;
!   basic_block bb;
! 
!   for (i = 0; i < num_changes; i++)
!     if (changes[i].object
! 	&& INSN_P (changes[i].object)
! 	&& (bb = BLOCK_FOR_INSN (changes[i].object)))
!       bb->flags |= BB_DIRTY;
! 
!   num_changes = 0;
! }
! 
! /* Apply a group of changes previously issued with `validate_change'.
!    If all changes are valid, call confirm_change_group and return 1,
!    otherwise, call cancel_changes and return 0.  */
! 
! int
! apply_change_group (void)
! {
!   if (verify_changes (0))
!     {
!       confirm_change_group ();
        return 1;
      }
    else
*************** apply_change_group (void)
*** 396,401 ****
--- 417,423 ----
      }
  }
  
+ 
  /* Return the number of changes so far in the current group.  */
  
  int
Index: recog.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/recog.h,v
retrieving revision 1.52
diff -p -r1.52 recog.h
*** recog.h	24 Nov 2004 18:22:27 -0000	1.52
--- recog.h	12 Jan 2005 20:43:42 -0000
*************** extern int check_asm_operands (rtx);
*** 75,80 ****
--- 75,82 ----
  extern int asm_operand_ok (rtx, const char *);
  extern int validate_change (rtx, rtx *, rtx, int);
  extern int insn_invalid_p (rtx);
+ extern int verify_changes (int);
+ extern void confirm_change_group (void);
  extern int apply_change_group (void);
  extern int num_validated_changes (void);
  extern void cancel_changes (int);
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.109
diff -p -r1.109 target-def.h
*** target-def.h	9 Dec 2004 11:06:16 -0000	1.109
--- target-def.h	12 Jan 2005 20:43:42 -0000
*************** Foundation, 59 Temple Place - Suite 330,
*** 337,342 ****
--- 337,343 ----
  #define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED hook_bool_bool_false
  #define TARGET_CANNOT_FORCE_CONST_MEM hook_bool_rtx_false
  #define TARGET_CANNOT_COPY_INSN_P NULL
+ #define TARGET_COMMUTATIVE_P hook_bool_rtx_commutative_p
  #define TARGET_DELEGITIMIZE_ADDRESS hook_rtx_rtx_identity
  #define TARGET_FUNCTION_OK_FOR_SIBCALL hook_bool_tree_tree_false
  #define TARGET_COMP_TYPE_ATTRIBUTES hook_int_tree_tree_1
*************** Foundation, 59 Temple Place - Suite 330,
*** 500,505 ****
--- 501,507 ----
    TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED,	\
    TARGET_CANNOT_FORCE_CONST_MEM,		\
    TARGET_CANNOT_COPY_INSN_P,			\
+   TARGET_COMMUTATIVE_P,				\
    TARGET_DELEGITIMIZE_ADDRESS,			\
    TARGET_FUNCTION_OK_FOR_SIBCALL,		\
    TARGET_IN_SMALL_DATA_P,			\
Index: target.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target.h,v
retrieving revision 1.121
diff -p -r1.121 target.h
*** target.h	9 Dec 2004 11:06:16 -0000	1.121
--- target.h	12 Jan 2005 20:43:43 -0000
*************** struct gcc_target
*** 376,381 ****
--- 376,384 ----
    /* True if the insn X cannot be duplicated.  */
    bool (* cannot_copy_insn_p) (rtx);
  
+   /* True if X is considered to be commutative.  */
+   bool (* commutative_p) (rtx, int);
+ 
    /* Given an address RTX, undo the effects of LEGITIMIZE_ADDRESS.  */
    rtx (* delegitimize_address) (rtx);
  
Index: targhooks.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/targhooks.c,v
retrieving revision 2.34
diff -p -r2.34 targhooks.c
*** targhooks.c	1 Oct 2004 05:08:57 -0000	2.34
--- targhooks.c	12 Jan 2005 20:43:43 -0000
*************** hook_bool_CUMULATIVE_ARGS_mode_tree_bool
*** 285,287 ****
--- 285,293 ----
  {
    return true;
  }
+ 
+ bool
+ hook_bool_rtx_commutative_p (rtx x, int outer_code ATTRIBUTE_UNUSED)
+ {
+   return COMMUTATIVE_P (x);
+ }
Index: targhooks.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/targhooks.h,v
retrieving revision 2.23
diff -p -r2.23 targhooks.h
*** targhooks.h	24 Sep 2004 15:13:53 -0000	2.23
--- targhooks.h	12 Jan 2005 20:43:43 -0000
*************** extern bool hook_bool_CUMULATIVE_ARGS_mo
*** 58,60 ****
--- 58,61 ----
    (CUMULATIVE_ARGS *, enum machine_mode, tree, bool);
  extern bool hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
    (CUMULATIVE_ARGS *, enum machine_mode, tree, bool);
+ extern bool hook_bool_rtx_commutative_p (rtx, int);
Index: config/pa/pa.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/pa/pa.c,v
retrieving revision 1.280
diff -p -r1.280 pa.c
*** config/pa/pa.c	15 Dec 2004 05:10:56 -0000	1.280
--- config/pa/pa.c	12 Jan 2005 20:43:43 -0000
*************** static size_t n_deferred_plabels = 0;
*** 236,241 ****
--- 236,244 ----
  #undef TARGET_FUNCTION_OK_FOR_SIBCALL
  #define TARGET_FUNCTION_OK_FOR_SIBCALL pa_function_ok_for_sibcall
  
+ #undef TARGET_COMMUTATIVE_P
+ #define TARGET_COMMUTATIVE_P pa_commutative_p
+ 
  #undef TARGET_ASM_OUTPUT_MI_THUNK
  #define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk
  #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
*************** pa_function_ok_for_sibcall (tree decl, t
*** 8113,8118 ****
--- 8116,8132 ----
  	  && !TREE_PUBLIC (decl));
  }
  
+ /* ??? Addition is not commutative on the PA due to the weird implicit
+    space register selection rules for memory addresses.  Therefore, we
+    don't consider a + b == b + a, as this might be inside a MEM.  */
+ bool
+ pa_commutative_p
+ {
+   return (COMMUTATIVE_P (x)
+ 	  && ((outer_code != UNKNOWN && outer_code != MEM)
+ 	      || GET_CODE (x) != PLUS));
+ }
+ 
  /* Returns 1 if the 6 operands specified in OPERANDS are suitable for
     use in fmpyadd instructions.  */
  int
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.406
diff -p -r1.406 tm.texi
*** doc/tm.texi	20 Dec 2004 02:08:57 -0000	1.406
--- doc/tm.texi	12 Jan 2005 20:43:45 -0000
*************** filling of delay slots can result in bra
*** 9439,9444 ****
--- 9439,9451 ----
  may in turn cause a branch offset to overflow.
  @end defmac
  
+ @deftypefn {Target Hook} bool TARGET_COMMUTATIVE_P (rtx @var{x}, @var{outer_code})
+ This target hook returns @code{true} if {x} is considered to be commutative.
+ Usually, this is just COMMUTATIVE_P (@var{x}), but the pa doesn't consider
+ PLUS to be commutative inside a MEM.  @var{outer_code} is the rtx code
+ of the enclosing rtl, if known, otherwise it is UNKNOWN.
+ @end deftypefn
+ 
  @defmac ALLOCATE_INITIAL_VALUE (@var{hard_reg})
  
  When the initial value of a hard register has been copied in a pseudo

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