public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* regrename speedup
@ 2009-10-17 14:46 Bernd Schmidt
  2009-10-22 13:09 ` Eric Botcazou
  2009-11-12 18:32 ` Bernd Schmidt
  0 siblings, 2 replies; 23+ messages in thread
From: Bernd Schmidt @ 2009-10-17 14:46 UTC (permalink / raw)
  To: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 982 bytes --]

PR38582 has a testcase which spends a lot of time in the register
renamer.  One problem here is quite obvious: we always scan a whole
chain to find its end so that we can insert a new element.  The patch
below fixes this by rearranging the data structures a little.

The testcase still consumes a lot of time in the renamer, but the
problem is shifted to merge_overlapping_regs which probably needs rewriting.

Bootstrapped & regression tested on i686-linux (failures match those
seen in gcc-testresults), which is however meaningless as
-frename-registers isn't tested.  I've compiled a large number of files
both with and without this patch with -frename-registers, and output was
identical in all cases.

OK?


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-speed.diff --]
[-- Type: text/plain, Size: 14117 bytes --]


	PR rtl/38582
	* regrename.c (struct du_head): New structure; some elements moved
	from...
	(struct du_chain): ... this one.
	(open_chains, closed_chains): Now of type struct du_head *.
	(do_replace): Accept du_head argument, not du_chain.  All callers
	changed.  Modified code to match new data structures.
	(build_def_use): Return a list of du_head structures.  Modified code
	to match new data structures.
	(dump_def_use_chain): Accept du_head argument, not du_chain.  All
	callers changed.  Modified code to match new data structures.
	(merge_overlapping_regs): Accept du_head argument, not du_chain.  All
	callers changed.  Modified code to match new data structures.
	(scan_rtx_reg): Change type of this_regno and this_nregs to unsigned.
	Allocate a du_head structure as well as a du_chain when creating a
	new chain.  Modified other code to match new data structures.

Index: regrename.c
===================================================================
--- regrename.c	(revision 152847)
+++ regrename.c	(working copy)
@@ -40,15 +40,35 @@
 #include "tree-pass.h"
 #include "df.h"
 
+/* We keep linked lists of DU_HEAD structures, each of which describes
+   a chain of occurrences of a reg.  */
+struct du_head
+{
+  /* The next chain.  */
+  struct du_head *next_chain;
+  /* The first and last elements of this chain.  */
+  struct du_chain *first, *last;
+  /* Describes the register being tracked.  */
+  unsigned regno, nregs;
+  /* Nonzero if the chain crosses a call.  */
+  unsigned int need_caller_save_reg:1;
+  /* Nonzero if the chain is finished.  */
+  unsigned int terminated:1;
+};
+
+/* This struct describes a single occurrence of a register.  */
 struct du_chain
 {
-  struct du_chain *next_chain;
+  /* Links to the next occurrence of the register.  */
   struct du_chain *next_use;
 
+  /* The insn where the register appears.  */
   rtx insn;
+  /* The location inside the insn.  */
   rtx *loc;
+  /* The register class required by the insn at this location.  */
   ENUM_BITFIELD(reg_class) cl : 16;
-  unsigned int need_caller_save_reg:1;
+  /* Nonzero if the register is subject to earlyclobber.  */
   unsigned int earlyclobber:1;
 };
 
@@ -79,19 +99,19 @@ static const char * const scan_actions_n
 
 static struct obstack rename_obstack;
 
-static void do_replace (struct du_chain *, int);
+static void do_replace (struct du_head *, int);
 static void scan_rtx_reg (rtx, rtx *, enum reg_class,
 			  enum scan_actions, enum op_type, int);
 static void scan_rtx_address (rtx, rtx *, enum reg_class,
 			      enum scan_actions, enum machine_mode);
 static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
 		      enum op_type, int);
-static struct du_chain *build_def_use (basic_block);
-static void dump_def_use_chain (struct du_chain *);
+static struct du_head *build_def_use (basic_block);
+static void dump_def_use_chain (struct du_head *);
 static void note_sets (rtx, const_rtx, void *);
 static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
 static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
-				    struct du_chain *);
+				    struct du_head *);
 
 /* Called through note_stores.  Find sets of registers, and
    record them in *DATA (which is actually a HARD_REG_SET *).  */
@@ -127,14 +147,14 @@ clear_dead_regs (HARD_REG_SET *pset, enu
       }
 }
 
-/* For a def-use chain CHAIN in basic block B, find which registers overlap
+/* For a def-use chain HEAD in basic block B, find which registers overlap
    its lifetime and set the corresponding bits in *PSET.  */
 
 static void
 merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_chain *chain)
+			struct du_head *head)
 {
-  struct du_chain *t = chain;
+  struct du_chain *t;
   rtx insn;
   HARD_REG_SET live;
   df_ref *def_rec;
@@ -146,6 +166,8 @@ merge_overlapping_regs (basic_block b, H
       if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
 	SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
     }
+
+  t = head->first;
   insn = BB_HEAD (b);
   while (t)
     {
@@ -159,7 +181,7 @@ merge_overlapping_regs (basic_block b, H
 	      note_stores (PATTERN (insn), note_sets, (void *) &live);
 	      /* Only record currently live regs if we are inside the
 		 reg's live range.  */
-	      if (t != chain)
+	      if (t != head->first)
 		IOR_HARD_REG_SET (*pset, live);
 	      clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
 	    }
@@ -200,7 +222,7 @@ regrename_optimize (void)
 
   FOR_EACH_BB (bb)
     {
-      struct du_chain *all_chains = 0;
+      struct du_head *all_chains = 0;
       HARD_REG_SET unavailable;
       HARD_REG_SET regs_seen;
 
@@ -229,13 +251,13 @@ regrename_optimize (void)
 	{
 	  int new_reg, best_new_reg;
 	  int n_uses;
-	  struct du_chain *this_du = all_chains;
+	  struct du_head *this_head = all_chains;
 	  struct du_chain *tmp;
 	  HARD_REG_SET this_unavailable;
-	  int reg = REGNO (*this_du->loc);
+	  int reg = this_head->regno;
 	  int i;
 
-	  all_chains = this_du->next_chain;
+	  all_chains = this_head->next_chain;
 
 	  best_new_reg = reg;
 
@@ -262,7 +284,7 @@ regrename_optimize (void)
 	  /* Count number of uses, and narrow the set of registers we can
 	     use for renaming.  */
 	  n_uses = 0;
-	  for (tmp = this_du; tmp; tmp = tmp->next_use)
+	  for (tmp = this_head->first; tmp; tmp = tmp->next_use)
 	    {
 	      if (DEBUG_INSN_P (tmp->insn))
 		continue;
@@ -274,16 +296,17 @@ regrename_optimize (void)
 	  if (n_uses < 2)
 	    continue;
 
-	  if (this_du->need_caller_save_reg)
+	  if (this_head->need_caller_save_reg)
 	    IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
 
-	  merge_overlapping_regs (bb, &this_unavailable, this_du);
+	  merge_overlapping_regs (bb, &this_unavailable, this_head);
 
 	  /* Now potential_regs is a reasonable approximation, let's
 	     have a closer look at each register still in there.  */
 	  for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++)
 	    {
-	      int nregs = hard_regno_nregs[new_reg][GET_MODE (*this_du->loc)];
+	      enum machine_mode mode = GET_MODE (*this_head->first->loc);
+	      int nregs = hard_regno_nregs[new_reg][mode];
 
 	      for (i = nregs - 1; i >= 0; --i)
 	        if (TEST_HARD_REG_BIT (this_unavailable, new_reg + i)
@@ -308,10 +331,10 @@ regrename_optimize (void)
 
 	      /* See whether it accepts all modes that occur in
 		 definition and uses.  */
-	      for (tmp = this_du; tmp; tmp = tmp->next_use)
+	      for (tmp = this_head->first; tmp; tmp = tmp->next_use)
 		if ((! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc))
 		     && ! DEBUG_INSN_P (tmp->insn))
-		    || (tmp->need_caller_save_reg
+		    || (this_head->need_caller_save_reg
 			&& ! (HARD_REGNO_CALL_PART_CLOBBERED
 			      (reg, GET_MODE (*tmp->loc)))
 			&& (HARD_REGNO_CALL_PART_CLOBBERED
@@ -327,8 +350,8 @@ regrename_optimize (void)
 	  if (dump_file)
 	    {
 	      fprintf (dump_file, "Register %s in insn %d",
-		       reg_names[reg], INSN_UID (this_du->insn));
-	      if (this_du->need_caller_save_reg)
+		       reg_names[reg], INSN_UID (this_head->first->insn));
+	      if (this_head->need_caller_save_reg)
 		fprintf (dump_file, " crosses a call");
 	    }
 
@@ -343,7 +366,7 @@ regrename_optimize (void)
 	  if (dump_file)
 	    fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
 
-	  do_replace (this_du, best_new_reg);
+	  do_replace (this_head, best_new_reg);
 	  tick[best_new_reg] = ++this_tick;
 	  df_set_regs_ever_live (best_new_reg, true);
 	}
@@ -360,16 +383,17 @@ regrename_optimize (void)
 }
 
 static void
-do_replace (struct du_chain *chain, int reg)
+do_replace (struct du_head *head, int reg)
 {
-  unsigned int base_regno = REGNO (*chain->loc);
+  struct du_chain *chain;
+  unsigned int base_regno = head->regno;
 
-  gcc_assert (! DEBUG_INSN_P (chain->insn));
+  gcc_assert (! DEBUG_INSN_P (head->first->insn));
 
-  while (chain)
+  for (chain = head->first; chain; chain = chain->next_use)
     {
       unsigned int regno = ORIGINAL_REGNO (*chain->loc);
-      struct reg_attrs * attr = REG_ATTRS (*chain->loc);
+      struct reg_attrs *attr = REG_ATTRS (*chain->loc);
       int reg_ptr = REG_POINTER (*chain->loc);
 
       if (DEBUG_INSN_P (chain->insn) && REGNO (*chain->loc) != base_regno)
@@ -399,37 +423,42 @@ do_replace (struct du_chain *chain, int 
 	}
 
       df_insn_rescan (chain->insn);
-      chain = chain->next_use;
     }
 }
 
 
-static struct du_chain *open_chains;
-static struct du_chain *closed_chains;
+static struct du_head *open_chains;
+static struct du_head *closed_chains;
 
 static void
 scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
 	      enum scan_actions action, enum op_type type, int earlyclobber)
 {
-  struct du_chain **p;
+  struct du_head **p;
   rtx x = *loc;
   enum machine_mode mode = GET_MODE (x);
-  int this_regno = REGNO (x);
-  int this_nregs = hard_regno_nregs[this_regno][mode];
+  unsigned this_regno = REGNO (x);
+  unsigned this_nregs = hard_regno_nregs[this_regno][mode];
 
   if (action == mark_write)
     {
       if (type == OP_OUT)
 	{
+	  struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
 	  struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+	  head->next_chain = open_chains;
+	  open_chains = head;
+	  head->first = head->last = this_du;
+	  head->regno = this_regno;
+	  head->nregs = this_nregs;
+	  head->need_caller_save_reg = 0;
+	  head->terminated = 0;
+
 	  this_du->next_use = 0;
-	  this_du->next_chain = open_chains;
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
-	  this_du->need_caller_save_reg = 0;
 	  this_du->earlyclobber = earlyclobber;
-	  open_chains = this_du;
 	}
       return;
     }
@@ -439,8 +468,8 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 
   for (p = &open_chains; *p;)
     {
-      struct du_chain *this_du = *p;
-
+      struct du_head *head = *p;
+      
       /* Check if the chain has been terminated if it has then skip to
 	 the next chain.
 
@@ -448,18 +477,17 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	 the chain in Step 3, but are trying to hide in-out operands
 	 from terminate_write in Step 5.  */
 
-      if (*this_du->loc == cc0_rtx)
-	p = &this_du->next_chain;
+      if (head->terminated)
+	p = &head->next_chain;
       else
 	{
-	  int regno = REGNO (*this_du->loc);
-	  int nregs = hard_regno_nregs[regno][GET_MODE (*this_du->loc)];
-	  int exact_match = (regno == this_regno && nregs == this_nregs);
+	  int exact_match = (head->regno == this_regno
+			     && head->nregs == this_nregs);
 
-	  if (regno + nregs <= this_regno
-	      || this_regno + this_nregs <= regno)
+	  if (head->regno + head->nregs <= this_regno
+	      || this_regno + this_nregs <= head->regno)
 	    {
-	      p = &this_du->next_chain;
+	      p = &head->next_chain;
 	      continue;
 	    }
 
@@ -473,37 +501,36 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 		 be replaced with, terminate the chain.  */
 	      if (cl != NO_REGS)
 		{
+		  struct du_chain *this_du;
 		  this_du = XOBNEW (&rename_obstack, struct du_chain);
 		  this_du->next_use = 0;
-		  this_du->next_chain = (*p)->next_chain;
 		  this_du->loc = loc;
 		  this_du->insn = insn;
 		  this_du->cl = cl;
-		  this_du->need_caller_save_reg = 0;
-		  while (*p)
-		    p = &(*p)->next_use;
-		  *p = this_du;
+		  head->last->next_use = this_du;
+		  head->last = this_du;
 		  return;
 		}
 	    }
 
 	  if (action != terminate_overlapping_read || ! exact_match)
 	    {
-	      struct du_chain *next = this_du->next_chain;
+	      struct du_head *next = head->next_chain;
 
 	      /* Whether the terminated chain can be used for renaming
 	         depends on the action and this being an exact match.
 	         In either case, we remove this element from open_chains.  */
 
+	      head->terminated = 1;
 	      if ((action == terminate_dead || action == terminate_write)
 		  && exact_match)
 		{
-		  this_du->next_chain = closed_chains;
-		  closed_chains = this_du;
+		  head->next_chain = closed_chains;
+		  closed_chains = head;
 		  if (dump_file)
 		    fprintf (dump_file,
 			     "Closing chain %s at insn %d (%s)\n",
-			     reg_names[REGNO (*this_du->loc)], INSN_UID (insn),
+			     reg_names[head->regno], INSN_UID (insn),
 			     scan_actions_name[(int) action]);
 		}
 	      else
@@ -511,13 +538,13 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 		  if (dump_file)
 		    fprintf (dump_file,
 			     "Discarding chain %s at insn %d (%s)\n",
-			     reg_names[REGNO (*this_du->loc)], INSN_UID (insn),
+			     reg_names[head->regno], INSN_UID (insn),
 			     scan_actions_name[(int) action]);
 		}
 	      *p = next;
 	    }
 	  else
-	    p = &this_du->next_chain;
+	    p = &head->next_chain;
 	}
     }
 }
@@ -760,7 +787,7 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 
 /* Build def/use chain.  */
 
-static struct du_chain *
+static struct du_head *
 build_def_use (basic_block bb)
 {
   rtx insn;
@@ -914,7 +941,7 @@ build_def_use (basic_block bb)
 	     requires a caller-saved reg.  */
 	  if (CALL_P (insn))
 	    {
-	      struct du_chain *p;
+	      struct du_head *p;
 	      for (p = open_chains; p; p = p->next_chain)
 		p->need_caller_save_reg = 1;
 	    }
@@ -1014,14 +1041,13 @@ build_def_use (basic_block bb)
    printed in reverse order as that's how we build them.  */
 
 static void
-dump_def_use_chain (struct du_chain *chains)
+dump_def_use_chain (struct du_head *head)
 {
-  while (chains)
+  while (head)
     {
-      struct du_chain *this_du = chains;
-      int r = REGNO (*this_du->loc);
-      int nregs = hard_regno_nregs[r][GET_MODE (*this_du->loc)];
-      fprintf (dump_file, "Register %s (%d):", reg_names[r], nregs);
+      struct du_chain *this_du = head->first;
+      fprintf (dump_file, "Register %s (%d):",
+	       reg_names[head->regno], head->nregs);
       while (this_du)
 	{
 	  fprintf (dump_file, " %d [%s]", INSN_UID (this_du->insn),
@@ -1029,7 +1055,7 @@ dump_def_use_chain (struct du_chain *cha
 	  this_du = this_du->next_use;
 	}
       fprintf (dump_file, "\n");
-      chains = chains->next_chain;
+      head = head->next_chain;
     }
 }
 

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-10-17 14:46 regrename speedup Bernd Schmidt
@ 2009-10-22 13:09 ` Eric Botcazou
  2009-11-12 18:31   ` Bernd Schmidt
  2009-11-12 18:32 ` Bernd Schmidt
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-10-22 13:09 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

>	PR rtl/38582

PR rtl-opt (or PR rtl-optimization).

>	* regrename.c (struct du_head): New structure; some elements moved
>	from...
>	(struct du_chain): ... this one.
>	(open_chains, closed_chains): Now of type struct du_head *.
>	(do_replace): Accept du_head argument, not du_chain.  All callers
>	changed.  Modified code to match new data structures.
>	(build_def_use): Return a list of du_head structures.  Modified code
>	to match new data structures.
>	(dump_def_use_chain): Accept du_head argument, not du_chain.  All
>	callers changed.  Modified code to match new data structures.
>	(merge_overlapping_regs): Accept du_head argument, not du_chain.  All
>	callers changed.  Modified code to match new data structures.
>	(scan_rtx_reg): Change type of this_regno and this_nregs to unsigned.
>	Allocate a du_head structure as well as a du_chain when creating a
>	new chain.  Modified other code to match new data structures.

OK with the trailing spaces removed in

@@ -439,8 +468,8 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 
   for (p = &open_chains; *p;)
     {
-      struct du_chain *this_du = *p;
-
+      struct du_head *head = *p;
+      
       /* Check if the chain has been terminated if it has then skip to
 	 the next chain.
 
-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-10-22 13:09 ` Eric Botcazou
@ 2009-11-12 18:31   ` Bernd Schmidt
  2009-11-28  1:39     ` H.J. Lu
  0 siblings, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-12 18:31 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

Eric Botcazou wrote:
>> 	PR rtl/38582
> 
> PR rtl-opt (or PR rtl-optimization).
> 
>> 	* regrename.c (struct du_head): New structure; some elements moved
>> 	from...
>> 	(struct du_chain): ... this one.
>> 	(open_chains, closed_chains): Now of type struct du_head *.
>> 	(do_replace): Accept du_head argument, not du_chain.  All callers
>> 	changed.  Modified code to match new data structures.
>> 	(build_def_use): Return a list of du_head structures.  Modified code
>> 	to match new data structures.
>> 	(dump_def_use_chain): Accept du_head argument, not du_chain.  All
>> 	callers changed.  Modified code to match new data structures.
>> 	(merge_overlapping_regs): Accept du_head argument, not du_chain.  All
>> 	callers changed.  Modified code to match new data structures.
>> 	(scan_rtx_reg): Change type of this_regno and this_nregs to unsigned.
>> 	Allocate a du_head structure as well as a du_chain when creating a
>> 	new chain.  Modified other code to match new data structures.
> 
> OK with the trailing spaces removed in
[...]

Thanks, committed now (vacation got in the way).  I'll post a followup
patch as well which takes care of the remaining performance problem.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-10-17 14:46 regrename speedup Bernd Schmidt
  2009-10-22 13:09 ` Eric Botcazou
@ 2009-11-12 18:32 ` Bernd Schmidt
  2009-11-19 19:28   ` Eric Botcazou
  1 sibling, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-12 18:32 UTC (permalink / raw)
  To: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1339 bytes --]

I wrote:

> The testcase (PR38582) still consumes a lot of time in the renamer,
> but the problem is shifted to merge_overlapping_regs which probably
> needs rewriting.

The following patch cures the problem entirely.  The initial scanning
phase of the renamer is rewritten to keep track of conflicts between
chains, as well as recording lifetimes of hard registers.  This removes
the need for merge_overlapping_regs to rescan the basic block every time.

For some cases involving multiword hard registers, I've made it give up,
since the effort of tracking the individual hard regs gets too hairy if
there is non-exact overlap.  This does not seem to happen often enough
to worry about, at least on x86.  On the plus side, the conflicts
information gathered by the new code is slightly less conservative, as
we no longer treat all outputs as earlyclobbers for the last insn in a
chain when determining conflicts.

I've bootstrapped and regression tested this patch on i686-linux with
another small patch to force flag_rename_registers to 1.

Ok (and for which stage)?


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-speed3.diff --]
[-- Type: text/plain, Size: 32483 bytes --]

	* regrename.c (struct du_head): New members id, conflicts,
	hard_conflicts and cannot_rename.
	(enum scan_actions): Remove terminate_all_read and
	terminate_overlapping_read; add mark_all_read.
	(scan_actions_name): Likewise.
	(du_head_p): New typedef.  Define a vector type for it.
	(id_to_chain): New static variable.
	(note_sets, clear_dead_regs): Delete functions.
	(free_chain_data): New function.
	(merge_overlapping_regs): Simply walk the conflicts bitmap.
	Remove argument B, all callers changed.
	(regrename_optimize): Allocate id_to_chain.  Ignore chains that have
	the cannot_rename bit set.  Update regno and nregs of a renamed chain.
	Call free_chain_data when done.
	(do_replace): Remove death notes when the renamed reg is set in the
	last insn; add them if not.
	(mark_conflict, note_sets_clobbers): New static function.
	(fail_current_block, current_id, live_chains, live_hard_regs): New
	static variables.
	(scan_rtx_reg): Keep track of conflicts between chains, and between
	chains and hard regs.  Don't terminate chains when we find a read we
	can't handle, mark it unrenameable instead.  For terminate_write,
	terminate chains that are written with an exact match or a superset
	of registers.  Set fail_current_block if multi-word lifetimes are too
	complex to handle.
	(scan_rtx_address): Use mark_all_read instead of terminate_all_read.
	(hide_operands, restore_operands, record_out_operands): New functions,
	broken out of build_def_use.
	(build_def_use): Call them as necessary.  Initialize current_id,
	live_chains and live_hard_regs; free memory for them when done.
	Rearrange the steps so that earlyclobbers are noted before reads
	are processed.  Add new steps to keep track of hard register lifetimes
	outside insn operands.

Index: regrename.c
===================================================================
--- regrename.c	(revision 154123)
+++ regrename.c	(working copy)
@@ -50,10 +50,22 @@ struct du_head
   struct du_chain *first, *last;
   /* Describes the register being tracked.  */
   unsigned regno, nregs;
+
+  /* A unique id to be used as an index into the conflicts bitmaps.  */
+  unsigned id;
+  /* A bitmap to record conflicts with other chains.  */
+  bitmap_head conflicts;
+  /* Conflicts with untracked hard registers.  */
+  HARD_REG_SET hard_conflicts;
+
+  /* Nonzero if the chain is finished; zero if it is still open.  */
+  unsigned int terminated:1;
   /* Nonzero if the chain crosses a call.  */
   unsigned int need_caller_save_reg:1;
-  /* Nonzero if the chain is finished.  */
-  unsigned int terminated:1;
+  /* Nonzero if the register is used in a way that prevents renaming,
+     such as the SET_DEST of a CALL_INSN or an asm operand that used
+     to be a hard register.  */
+  unsigned int cannot_rename:1;
 };
 
 /* This struct describes a single occurrence of a register.  */
@@ -74,10 +86,9 @@ struct du_chain
 
 enum scan_actions
 {
-  terminate_all_read,
-  terminate_overlapping_read,
   terminate_write,
   terminate_dead,
+  mark_all_read,
   mark_read,
   mark_write,
   /* mark_access is for marking the destination regs in
@@ -88,10 +99,9 @@ enum scan_actions
 
 static const char * const scan_actions_name[] =
 {
-  "terminate_all_read",
-  "terminate_overlapping_read",
   "terminate_write",
   "terminate_dead",
+  "mark_all_read",
   "mark_read",
   "mark_write",
   "mark_access"
@@ -108,95 +118,40 @@ static void scan_rtx (rtx, rtx *, enum r
 		      enum op_type, int);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
-static void note_sets (rtx, const_rtx, void *);
-static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
-static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
-				    struct du_head *);
 
-/* Called through note_stores.  Find sets of registers, and
-   record them in *DATA (which is actually a HARD_REG_SET *).  */
+typedef struct du_head *du_head_p;
+DEF_VEC_P (du_head_p);
+DEF_VEC_ALLOC_P (du_head_p, heap);
+static VEC(du_head_p, heap) *id_to_chain;
 
 static void
-note_sets (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data)
+free_chain_data (void)
 {
-  HARD_REG_SET *pset = (HARD_REG_SET *) data;
-
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!REG_P (x))
-    return;
-  /* There must not be pseudos at this point.  */
-  gcc_assert (HARD_REGISTER_P (x));
-  add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
-}
-
-/* Clear all registers from *PSET for which a note of kind KIND can be found
-   in the list NOTES.  */
+  int i;
+  du_head_p ptr;
+  for (i = 0; VEC_iterate(du_head_p, id_to_chain, i, ptr); i++)
+    {
+      bitmap_clear (&ptr->conflicts);
+    }
 
-static void
-clear_dead_regs (HARD_REG_SET *pset, enum reg_note kind, rtx notes)
-{
-  rtx note;
-  for (note = notes; note; note = XEXP (note, 1))
-    if (REG_NOTE_KIND (note) == kind && REG_P (XEXP (note, 0)))
-      {
-	rtx reg = XEXP (note, 0);
-	/* There must not be pseudos at this point.  */
-	gcc_assert (HARD_REGISTER_P (reg));
-	remove_from_hard_reg_set (pset, GET_MODE (reg), REGNO (reg));
-      }
+  VEC_free (du_head_p, heap, id_to_chain);
 }
 
 /* For a def-use chain HEAD in basic block B, find which registers overlap
    its lifetime and set the corresponding bits in *PSET.  */
 
 static void
-merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_head *head)
+merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
 {
-  struct du_chain *t;
-  rtx insn;
-  HARD_REG_SET live;
-  df_ref *def_rec;
-
-  REG_SET_TO_HARD_REG_SET (live, df_get_live_in (b));
-  for (def_rec = df_get_artificial_defs (b->index); *def_rec; def_rec++)
-    {
-      df_ref def = *def_rec;
-      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
-	SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
-    }
-
-  t = head->first;
-  insn = BB_HEAD (b);
-  while (t)
+  bitmap_iterator bi;
+  unsigned i;
+  IOR_HARD_REG_SET (*pset, head->hard_conflicts);
+  EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
     {
-      /* Search forward until the next reference to the register to be
-	 renamed.  */
-      while (insn != t->insn)
-	{
-	  if (INSN_P (insn))
-	    {
-	      clear_dead_regs (&live, REG_DEAD, REG_NOTES (insn));
-	      note_stores (PATTERN (insn), note_sets, (void *) &live);
-	      /* Only record currently live regs if we are inside the
-		 reg's live range.  */
-	      if (t != head->first)
-		IOR_HARD_REG_SET (*pset, live);
-	      clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
-	    }
-	  insn = NEXT_INSN (insn);
-	}
-
-      IOR_HARD_REG_SET (*pset, live);
-
-      /* For the last reference, also merge in all registers set in the
-	 same insn.
-	 @@@ We only have take earlyclobbered sets into account.  */
-      if (! t->next_use)
-	note_stores (PATTERN (insn), note_sets, (void *) pset);
-
-      t = t->next_use;
+      du_head_p other = VEC_index (du_head_p, id_to_chain, i);
+      unsigned j = other->nregs;
+      while (j-- > 0)
+	SET_HARD_REG_BIT (*pset, other->regno + j);
     }
 }
 
@@ -226,6 +181,8 @@ regrename_optimize (void)
       HARD_REG_SET unavailable;
       HARD_REG_SET regs_seen;
 
+      id_to_chain = VEC_alloc (du_head_p, heap, 0);
+
       CLEAR_HARD_REG_SET (unavailable);
 
       if (dump_file)
@@ -249,7 +206,7 @@ regrename_optimize (void)
       CLEAR_HARD_REG_SET (regs_seen);
       while (all_chains)
 	{
-	  int new_reg, best_new_reg;
+	  int new_reg, best_new_reg, best_nregs;
 	  int n_uses;
 	  struct du_head *this_head = all_chains;
 	  struct du_chain *tmp;
@@ -259,6 +216,9 @@ regrename_optimize (void)
 
 	  all_chains = this_head->next_chain;
 
+	  if (this_head->cannot_rename)
+	    continue;
+	  
 	  best_new_reg = reg;
 
 #if 0 /* This just disables optimization opportunities.  */
@@ -299,7 +259,7 @@ regrename_optimize (void)
 	  if (this_head->need_caller_save_reg)
 	    IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
 
-	  merge_overlapping_regs (bb, &this_unavailable, this_head);
+	  merge_overlapping_regs (&this_unavailable, this_head);
 
 	  /* Now potential_regs is a reasonable approximation, let's
 	     have a closer look at each register still in there.  */
@@ -343,7 +303,10 @@ regrename_optimize (void)
 	      if (! tmp)
 		{
 		  if (tick[best_new_reg] > tick[new_reg])
-		    best_new_reg = new_reg;
+		    {
+		      best_new_reg = new_reg;
+		      best_nregs = nregs;
+		    }
 		}
 	    }
 
@@ -367,10 +330,13 @@ regrename_optimize (void)
 	    fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
 
 	  do_replace (this_head, best_new_reg);
+	  this_head->regno = best_new_reg;
+	  this_head->nregs = best_nregs;
 	  tick[best_new_reg] = ++this_tick;
 	  df_set_regs_ever_live (best_new_reg, true);
 	}
 
+      free_chain_data ();
       obstack_free (&rename_obstack, first_obj);
     }
 
@@ -387,6 +353,7 @@ do_replace (struct du_head *head, int re
 {
   struct du_chain *chain;
   unsigned int base_regno = head->regno;
+  bool found_note = false;
 
   gcc_assert (! DEBUG_INSN_P (head->first->insn));
 
@@ -410,26 +377,88 @@ do_replace (struct du_head *head, int re
 
 	  for (note = REG_NOTES (chain->insn); note; note = XEXP (note, 1))
 	    {
-	      if (REG_NOTE_KIND (note) == REG_DEAD 
-		  || REG_NOTE_KIND (note) == REG_UNUSED)
+	      enum reg_note kind = REG_NOTE_KIND (note);
+	      if (kind == REG_DEAD || kind == REG_UNUSED)
 		{
 		  rtx reg = XEXP (note, 0);
 		  gcc_assert (HARD_REGISTER_P (reg));
 		  
-		  if (REGNO (reg) == base_regno) 
-		    XEXP (note, 0) = *chain->loc;
+		  if (REGNO (reg) == base_regno)
+		    {
+		      found_note = true;
+		      if (kind == REG_DEAD
+			  && reg_set_p (*chain->loc, chain->insn))
+			remove_note (chain->insn, note);
+		      else
+			XEXP (note, 0) = *chain->loc;
+		      break;
+		    }
 		}
 	    }
 	}
 
       df_insn_rescan (chain->insn);
     }
+  if (!found_note)
+    {
+      /* If the chain's first insn is the same as the last, we should have
+	 found a REG_UNUSED note.  */
+      gcc_assert (head->first->insn != head->last->insn);
+      if (!reg_set_p (*head->last->loc, head->last->insn))
+	add_reg_note (head->last->insn, REG_DEAD, *head->last->loc);
+    }
+}
+
+
+/* Walk all chains starting with CHAINS and record that they conflict with
+   another chains whose id is ID.  */
+static void
+mark_conflict (struct du_head *chains, unsigned id)
+{
+  while (chains)
+    {
+      bitmap_set_bit (&chains->conflicts, id);
+      chains = chains->next_chain;
+    }
 }
 
+/* True if we found a register with a size mismatch that means we can't
+   track its lifetime accurately.  If so, we abort the current block
+   without renaming.  */
+static bool fail_current_block;
+
+/* The id to be given to the next opened chain.  */
+static unsigned current_id;
 
+/* List of currently open chains, and closed chains that can be renamed.  */
 static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
+/* Conflict bitmaps, tracking the live chains and the live hard registers.  */
+static bitmap_head live_chains;
+static HARD_REG_SET live_hard_regs;
+
+/* Called through note_stores.  DATA points to a rtx_code, either SET or
+   CLOBBER, which tells us which kind of rtx to look at.  If we have a
+   match, record the set register in live_hard_regs.  */
+
+static void
+note_sets_clobbers (rtx x, const_rtx set, void *data)
+{
+  enum rtx_code *pcode = (enum rtx_code *)data;
+  struct du_head *chain;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  if (!REG_P (x) || GET_CODE (set) != *pcode)
+    return;
+  /* There must not be pseudos at this point.  */
+  gcc_assert (HARD_REGISTER_P (x));
+  add_to_hard_reg_set (&live_hard_regs, GET_MODE (x), REGNO (x));
+  for (chain = open_chains; chain; chain = chain->next_chain)
+    add_to_hard_reg_set (&chain->hard_conflicts, GET_MODE (x), REGNO (x));
+}
+
 static void
 scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
 	      enum scan_actions action, enum op_type type, int earlyclobber)
@@ -446,19 +475,44 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	{
 	  struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
 	  struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+	  int nregs;
 	  head->next_chain = open_chains;
-	  open_chains = head;
 	  head->first = head->last = this_du;
 	  head->regno = this_regno;
 	  head->nregs = this_nregs;
 	  head->need_caller_save_reg = 0;
+	  head->cannot_rename = 0;
 	  head->terminated = 0;
 
+	  VEC_safe_push (du_head_p, heap, id_to_chain, head);
+	  head->id = current_id++;
+
+	  bitmap_initialize (&head->conflicts, &bitmap_default_obstack);
+	  bitmap_copy (&head->conflicts, &live_chains);
+	  mark_conflict (open_chains, head->id);
+
+	  /* Since we're tracking this as a chain now, remove it from the
+	     list of conflicting live hard registers.  */
+	  nregs = head->nregs;
+	  while (nregs-- > 0)
+	    CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+
+	  COPY_HARD_REG_SET (head->hard_conflicts, live_hard_regs);
+	  bitmap_set_bit (&live_chains, head->id);
+
+	  open_chains = head;
+
 	  this_du->next_use = 0;
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
 	  this_du->earlyclobber = earlyclobber;
+
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Creating chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
 	}
       return;
     }
@@ -469,82 +523,89 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
   for (p = &open_chains; *p;)
     {
       struct du_head *head = *p;
+      struct du_head *next = head->next_chain;
+      int exact_match = (head->regno == this_regno
+			 && head->nregs == this_nregs);
+      int superset = (this_regno <= head->regno
+		      && this_regno + this_nregs >= head->regno + head->nregs);
 
-      /* Check if the chain has been terminated if it has then skip to
-	 the next chain.
-
-	 This can happen when we've already appended the location to
-	 the chain in Step 3, but are trying to hide in-out operands
-	 from terminate_write in Step 5.  */
+      if (head->terminated
+	  || head->regno + head->nregs <= this_regno
+	  || this_regno + this_nregs <= head->regno)
+	{
+	  p = &head->next_chain;
+	  continue;
+	}
 
-      if (head->terminated)
-	p = &head->next_chain;
-      else
+      if (action == mark_read || action == mark_access)
 	{
-	  int exact_match = (head->regno == this_regno
-			     && head->nregs == this_nregs);
+	  /* ??? Class NO_REGS can happen if the md file makes use of
+	     EXTRA_CONSTRAINTS to match registers.  Which is arguably
+	     wrong, but there we are.  */
 
-	  if (head->regno + head->nregs <= this_regno
-	      || this_regno + this_nregs <= head->regno)
+	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
 	    {
-	      p = &head->next_chain;
-	      continue;
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+			 reg_names[head->regno], head->id, INSN_UID (insn),
+			 scan_actions_name[(int) action]);
+	      head->cannot_rename = 1;
 	    }
-
-	  if (action == mark_read || action == mark_access)
+	  else
 	    {
-	      gcc_assert (exact_match || DEBUG_INSN_P (insn));
-
-	      /* ??? Class NO_REGS can happen if the md file makes use of
-		 EXTRA_CONSTRAINTS to match registers.  Which is arguably
-		 wrong, but there we are.  Since we know not what this may
-		 be replaced with, terminate the chain.  */
-	      if (cl != NO_REGS)
-		{
-		  struct du_chain *this_du;
-		  this_du = XOBNEW (&rename_obstack, struct du_chain);
-		  this_du->next_use = 0;
-		  this_du->loc = loc;
-		  this_du->insn = insn;
-		  this_du->cl = cl;
-		  head->last->next_use = this_du;
-		  head->last = this_du;
-		  return;
-		}
+	      struct du_chain *this_du;
+	      this_du = XOBNEW (&rename_obstack, struct du_chain);
+	      this_du->next_use = 0;
+	      this_du->loc = loc;
+	      this_du->insn = insn;
+	      this_du->cl = cl;
+	      head->last->next_use = this_du;
+	      head->last = this_du;
 	    }
+	  /* There may be other chains that do not match exactly; ensure
+	     they all get marked unrenamable.  */
+	  p = &head->next_chain;
+	  continue;
+	}
 
-	  if (action != terminate_overlapping_read || ! exact_match)
-	    {
-	      struct du_head *next = head->next_chain;
-
-	      /* Whether the terminated chain can be used for renaming
-	         depends on the action and this being an exact match.
-	         In either case, we remove this element from open_chains.  */
+      /* Whether the terminated chain can be used for renaming
+	 depends on the action and this being an exact match.
+	 In either case, we remove this element from open_chains.  */
 
-	      head->terminated = 1;
-	      if ((action == terminate_dead || action == terminate_write)
-		  && exact_match)
-		{
-		  head->next_chain = closed_chains;
-		  closed_chains = head;
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Closing chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Discarding chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      *p = next;
-	    }
-	  else
-	    p = &head->next_chain;
+      if ((action == terminate_dead || action == terminate_write)
+	  && superset)
+	{
+	  head->terminated = 1;
+	  head->next_chain = closed_chains;
+	  closed_chains = head;
+	  bitmap_clear_bit (&live_chains, head->id);
+	  *p = next;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Closing chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	}
+      else if (action == terminate_dead || action == terminate_write)
+	{
+	  /* In this case, tracking liveness gets too hard.  Fail the
+	     entire basic block.  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Failing basic block due to unhandled overlap\n");
+	  fail_current_block = true;
+	  return;
+	}
+      else
+	{
+	  head->cannot_rename = 1;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	  p = &head->next_chain;
 	}
     }
 }
@@ -670,7 +731,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
 #ifndef AUTO_INC_DEC
       /* If the target doesn't claim to handle autoinc, this must be
 	 something special, like a stack push.  Kill this chain.  */
-      action = terminate_all_read;
+      action = mark_all_read;
 #endif
       break;
 
@@ -785,15 +846,119 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
     }
 }
 
+/* Hide operands of the current insn (of which there are N_OPS) by
+   substituting cc0 for them.
+   Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
+   If INOUT_AND_EC_ONLY is set, we only do this for OP_INOUT type operands
+   and earlyclobbers.  */
+
+static void
+hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
+	       bool inout_and_ec_only)
+{
+  int i;
+  int alt = which_alternative;
+  for (i = 0; i < n_ops; i++)
+    {
+      old_operands[i] = recog_data.operand[i];
+      /* Don't squash match_operator or match_parallel here, since
+	 we don't know that all of the contained registers are
+	 reachable by proper operands.  */
+      if (recog_data.constraints[i][0] == '\0')
+	continue;
+      if (!inout_and_ec_only || recog_data.operand_type[i] == OP_INOUT
+	  || recog_op_alt[i][alt].earlyclobber)
+	*recog_data.operand_loc[i] = cc0_rtx;
+    }
+  for (i = 0; i < recog_data.n_dups; i++)
+    {
+      int opn = recog_data.dup_num[i];
+      old_dups[i] = *recog_data.dup_loc[i];
+      if (!inout_and_ec_only || recog_data.operand_type[opn] == OP_INOUT
+	  || recog_op_alt[opn][alt].earlyclobber)
+	*recog_data.dup_loc[i] = cc0_rtx;
+    }
+}
+
+/* Undoes the substitution performed by hide_operands.  INSN is the insn we
+   are processing; the arguments are the same as in hide_operands.  */
+
+static void
+restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
+{
+  int i;
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i] = old_dups[i];
+  for (i = 0; i < n_ops; i++)
+    *recog_data.operand_loc[i] = old_operands[i];
+  if (recog_data.n_dups)
+    df_insn_rescan (insn);
+}
+
+/* For each output operands of INSN, call scan_rtx to create a new
+   open chain.  Do this only for normal or earlyclobber outputs,
+   depending on EARLYCLOBBER.  */
+
+static void
+record_out_operands (rtx insn, bool earlyclobber)
+{
+  int n_ops = recog_data.n_operands;
+  int alt = which_alternative;
+
+  int i;
+
+  for (i = 0; i < n_ops + recog_data.n_dups; i++)
+    {
+      int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+      rtx *loc = (i < n_ops
+		  ? recog_data.operand_loc[opn]
+		  : recog_data.dup_loc[i - n_ops]);
+      rtx op = *loc;
+      enum reg_class cl = recog_op_alt[opn][alt].cl;
+      struct du_head *prev_open = open_chains;
+
+      if (recog_data.operand_type[opn] == OP_OUT
+	  && recog_op_alt[opn][alt].earlyclobber == earlyclobber)
+	scan_rtx (insn, loc, cl, mark_write, OP_OUT, earlyclobber);
+
+      /* ??? Many targets have output constraints on the SET_DEST
+	 of a call insn, which is stupid, since these are certainly
+	 ABI defined hard registers.  For these, and for asm operands
+	 that originally referenced hard registers, we must record that
+	 the chain cannot be renamed.  */
+      if (CALL_P (insn)
+	  || (asm_noperands (PATTERN (insn)) > 0
+	      && REG_P (op)
+	      && REGNO (op) == ORIGINAL_REGNO (op)))
+	{
+	  if (prev_open != open_chains)
+	    open_chains->cannot_rename = 1;
+	}
+    }
+}
+
 /* Build def/use chain.  */
 
 static struct du_head *
 build_def_use (basic_block bb)
 {
   rtx insn;
+  df_ref *def_rec;
 
   open_chains = closed_chains = NULL;
 
+  fail_current_block = false;
+
+  current_id = 0;
+  bitmap_initialize (&live_chains, &bitmap_default_obstack);
+  REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
+  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
+    {
+      df_ref def = *def_rec;
+      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+	SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
+    }
+
   for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
     {
       if (NONDEBUG_INSN_P (insn))
@@ -805,22 +970,31 @@ build_def_use (basic_block bb)
 	  int i, icode;
 	  int alt;
 	  int predicated;
+	  enum rtx_code set_code = SET;
+	  enum rtx_code clobber_code = CLOBBER;
 
 	  /* Process the insn, determining its effect on the def-use
-	     chains.  We perform the following steps with the register
-	     references in the insn:
-	     (1) Any read that overlaps an open chain, but doesn't exactly
-	         match, causes that chain to be closed.  We can't deal
-	         with overlaps yet.
+	     chains and live hard registers.  We perform the following
+	     steps with the register references in the insn, simulating
+	     its effect:
+	     (1) Deal with earlyclobber operands and CLOBBERs of non-operands
+	         by creating chains and marking hard regs live.
 	     (2) Any read outside an operand causes any chain it overlaps
-	         with to be closed, since we can't replace it.
+	         with to be marked unrenamable.
 	     (3) Any read inside an operand is added if there's already
 	         an open chain for it.
 	     (4) For any REG_DEAD note we find, close open chains that
 	         overlap it.
-	     (5) For any write we find, close open chains that overlap it.
-	     (6) For any write we find in an operand, make a new chain.
-	     (7) For any REG_UNUSED, close any chains we just opened.  */
+	     (5) For any non-earlyclobber write we find, close open chains
+	         that overlap it.
+	     (6) For any non-earlyclobber write we find in an operand, make
+	         a new chain or mark the hard register as live.
+	     (7) For any REG_UNUSED, close any chains we just opened.
+
+	     We cannot deal with situations where we track a reg in one mode
+	     and see a reference in another mode; these will cause the chain
+	     to be marked unrenamable or even cause us to abort the entire
+	     basic block.  */
 
 	  icode = recog_memoized (insn);
 	  extract_insn (insn);
@@ -842,49 +1016,42 @@ build_def_use (basic_block bb)
 		recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
 	      if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
 	          || (predicated && recog_data.operand_type[i] == OP_OUT))
-		recog_data.operand_type[i] = OP_INOUT;
+		{
+		  recog_data.operand_type[i] = OP_INOUT;
+		  if (matches >= 0
+		      && (GET_MODE_SIZE (recog_data.operand_mode[i])
+			  != GET_MODE_SIZE (recog_data.operand_mode[matches])))
+		    fail_current_block = true;
+		}
 	    }
+	  if (fail_current_block)
+	    break;
 
-	  /* Step 1: Close chains for which we have overlapping reads.  */
-	  for (i = 0; i < n_ops; i++)
-	    scan_rtx (insn, recog_data.operand_loc[i],
-		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i], 0);
+	  /* Step 1a: Mark hard registers that are clobbered in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &clobber_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 2: Close chains for which we have reads outside operands.
+	  /* Step 1b: Begin new chains for earlyclobbered writes inside
+	     operands.  */
+	  record_out_operands (insn, true);
+
+	  /* Step 2: Mark chains for which we have reads outside operands
+	     as non-renamable.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      /* Don't squash match_operator or match_parallel here, since
-		 we don't know that all of the contained registers are
-		 reachable by proper operands.  */
-	      if (recog_data.constraints[i][0] == '\0')
-		continue;
-	      *recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      *recog_data.dup_loc[i] = cc0_rtx;
-	    }
+	  hide_operands (n_ops, old_operands, old_dups, false);
 
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, mark_all_read,
 		    OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
-	  if (recog_data.n_dups)
-	    df_insn_rescan (insn);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN, 0);
+		      NO_REGS, mark_all_read, OP_IN, 0);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -898,7 +1065,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+		  scan_rtx (insn, loc, NO_REGS, mark_all_read, OP_IN, 0);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -934,8 +1101,13 @@ build_def_use (basic_block bb)
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN, 0);
+	      }
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -948,67 +1120,24 @@ build_def_use (basic_block bb)
 
 	  /* Step 5: Close open chains that overlap writes.  Similar to
 	     step 2, we hide in-out operands, since we do not want to
-	     close these chains.  */
-
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      if (recog_data.operand_type[i] == OP_INOUT)
-		*recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      int opn = recog_data.dup_num[i];
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      if (recog_data.operand_type[opn] == OP_INOUT)
-		*recog_data.dup_loc[i] = cc0_rtx;
-	    }
+	     close these chains.  We also hide earlyclobber operands,
+	     since we've opened chains for them earlier and they couldn't
+	     possibly overlap any input operands anyway.  */
 
+	  hide_operands (n_ops, old_operands, old_dups, true);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
-
-	  /* Step 6: Begin new chains for writes inside operands.  */
-	  /* ??? Many targets have output constraints on the SET_DEST
-	     of a call insn, which is stupid, since these are certainly
-	     ABI defined hard registers.  Don't change calls at all.
-	     Similarly take special care for asm statement that originally
-	     referenced hard registers.  */
-	  if (asm_noperands (PATTERN (insn)) > 0)
-	    {
-	      for (i = 0; i < n_ops; i++)
-		if (recog_data.operand_type[i] == OP_OUT)
-		  {
-		    rtx *loc = recog_data.operand_loc[i];
-		    rtx op = *loc;
-		    enum reg_class cl = recog_op_alt[i][alt].cl;
-
-		    if (REG_P (op)
-			&& REGNO (op) == ORIGINAL_REGNO (op))
-		      continue;
-
-		    scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			      recog_op_alt[i][alt].earlyclobber);
-		  }
-	    }
-	  else if (!CALL_P (insn))
-	    for (i = 0; i < n_ops + recog_data.n_dups; i++)
-	      {
-		int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
-		rtx *loc = (i < n_ops
-			    ? recog_data.operand_loc[opn]
-			    : recog_data.dup_loc[i - n_ops]);
-		enum reg_class cl = recog_op_alt[opn][alt].cl;
+	  /* Step 6a: Mark hard registers that are set in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &set_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-		if (recog_data.operand_type[opn] == OP_OUT)
-		  scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			    recog_op_alt[opn][alt].earlyclobber);
-	      }
+	  /* Step 6b: Begin new chains for writes inside operands.  */
+	  record_out_operands (insn, false);
 
-	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
+	  /* Step 6c: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
@@ -1019,8 +1148,13 @@ build_def_use (basic_block bb)
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN, 0);
+	      }
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
@@ -1032,6 +1166,11 @@ build_def_use (basic_block bb)
 	break;
     }
 
+  bitmap_clear (&live_chains);
+
+  if (fail_current_block)
+    return NULL;
+
   /* Since we close every chain when we find a REG_DEAD note, anything that
      is still open lives past the basic block, so it can't be renamed.  */
   return closed_chains;

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-12 18:32 ` Bernd Schmidt
@ 2009-11-19 19:28   ` Eric Botcazou
  2009-11-20  1:36     ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-19 19:28 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

> The following patch cures the problem entirely.  The initial scanning
> phase of the renamer is rewritten to keep track of conflicts between
> chains, as well as recording lifetimes of hard registers.  This removes
> the need for merge_overlapping_regs to rescan the basic block every time.

What's the effect on the memory consumption, e.g. for the testcase of PR38582?

> I've bootstrapped and regression tested this patch on i686-linux with
> another small patch to force flag_rename_registers to 1.
>
> Ok (and for which stage)?

I think we should consider installing it now, given that the option is only 
activated on demand or through -funroll-loops and -fpeel-loops.

I have a request though: can you split the patch into 2 parts, the first one 
containing only the cleaning and refactoring work?  That is to say, primarily 
the hide_operands/restore_operands thing.  But could you also add the removal 
of the unused earlyclobber machinery?  Your patch won't use it either and the 
combined result is a little confusing.  Thanks in advance.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-19 19:28   ` Eric Botcazou
@ 2009-11-20  1:36     ` Bernd Schmidt
  2009-11-20  7:58       ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-20  1:36 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1614 bytes --]

Eric Botcazou wrote:
>> The following patch cures the problem entirely.  The initial scanning
>> phase of the renamer is rewritten to keep track of conflicts between
>> chains, as well as recording lifetimes of hard registers.  This removes
>> the need for merge_overlapping_regs to rescan the basic block every time.
> 
> What's the effect on the memory consumption, e.g. for the testcase of PR38582?

I would expect it to be reasonable.  Is the report from f951 meaningful?
 rename registers      :   3.28 ( 1%) usr   0.00 ( 0%) sys   3.31 ( 0%)
wall    8909 kB ( 1%) ggc

There's a constant upper limit to how big the bitmaps can get - no more
than FIRST_PSEUDO_REGISTER bits can be set in any of them.

> 
> I think we should consider installing it now, given that the option is only 
> activated on demand or through -funroll-loops and -fpeel-loops.
> 
> I have a request though: can you split the patch into 2 parts, the first one 
> containing only the cleaning and refactoring work?  That is to say, primarily 
> the hide_operands/restore_operands thing.

Done and attached.  Note that I didn't really test the intermediate step
very much yet.

>  But could you also add the removal 
> of the unused earlyclobber machinery?  Your patch won't use it either and the 
> combined result is a little confusing.

Not sure what you mean here?


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-cleanup.diff --]
[-- Type: text/plain, Size: 6595 bytes --]

Index: regrename.c
===================================================================
--- regrename.c.orig
+++ regrename.c
@@ -785,6 +785,92 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
     }
 }
 
+/* Hide operands of the current insn (of which there are N_OPS) by
+   substituting cc0 for them.
+   Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
+   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+
+static void
+hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
+	       bool inout_only)
+{
+  int i;
+  for (i = 0; i < n_ops; i++)
+    {
+      old_operands[i] = recog_data.operand[i];
+      /* Don't squash match_operator or match_parallel here, since
+	 we don't know that all of the contained registers are
+	 reachable by proper operands.  */
+      if (recog_data.constraints[i][0] == '\0')
+	continue;
+      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+	*recog_data.operand_loc[i] = cc0_rtx;
+    }
+  for (i = 0; i < recog_data.n_dups; i++)
+    {
+      int opn = recog_data.dup_num[i];
+      old_dups[i] = *recog_data.dup_loc[i];
+      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+	*recog_data.dup_loc[i] = cc0_rtx;
+    }
+}
+
+/* Undoes the substitution performed by hide_operands.  INSN is the insn we
+   are processing; the arguments are the same as in hide_operands.  */
+
+static void
+restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
+{
+  int i;
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i] = old_dups[i];
+  for (i = 0; i < n_ops; i++)
+    *recog_data.operand_loc[i] = old_operands[i];
+  if (recog_data.n_dups)
+    df_insn_rescan (insn);
+}
+
+/* For each output operands of INSN, call scan_rtx to create a new
+   open chain.  */
+
+static void
+record_out_operands (rtx insn)
+{
+  int n_ops = recog_data.n_operands;
+  int alt = which_alternative;
+
+  int i;
+
+  for (i = 0; i < n_ops + recog_data.n_dups; i++)
+    {
+      int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+      rtx *loc = (i < n_ops
+		  ? recog_data.operand_loc[opn]
+		  : recog_data.dup_loc[i - n_ops]);
+      rtx op = *loc;
+      enum reg_class cl = recog_op_alt[opn][alt].cl;
+      struct du_head *prev_open = open_chains;
+
+      if (recog_data.operand_type[opn] == OP_OUT)
+	scan_rtx (insn, loc, cl, mark_write, OP_OUT,
+		  recog_op_alt[opn][alt].earlyclobber);
+
+      /* ??? Many targets have output constraints on the SET_DEST
+	 of a call insn, which is stupid, since these are certainly
+	 ABI defined hard registers.  For these, and for asm operands
+	 that originally referenced hard registers, we must record that
+	 the chain cannot be renamed.  */
+      if (CALL_P (insn)
+	  || (asm_noperands (PATTERN (insn)) > 0
+	      && REG_P (op)
+	      && REGNO (op) == ORIGINAL_REGNO (op)))
+	{
+	  if (prev_open != open_chains)
+	    open_chains->first->cl = NO_REGS;
+	}
+    }
+}
+
 /* Build def/use chain.  */
 
 static struct du_head *
@@ -855,31 +941,10 @@ build_def_use (basic_block bb)
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      /* Don't squash match_operator or match_parallel here, since
-		 we don't know that all of the contained registers are
-		 reachable by proper operands.  */
-	      if (recog_data.constraints[i][0] == '\0')
-		continue;
-	      *recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      *recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
+	  hide_operands (n_ops, old_operands, old_dups, false);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
 		    OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
-	  if (recog_data.n_dups)
-	    df_insn_rescan (insn);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
@@ -950,63 +1015,12 @@ build_def_use (basic_block bb)
 	     step 2, we hide in-out operands, since we do not want to
 	     close these chains.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      if (recog_data.operand_type[i] == OP_INOUT)
-		*recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      int opn = recog_data.dup_num[i];
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      if (recog_data.operand_type[opn] == OP_INOUT)
-		*recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
+	  hide_operands (n_ops, old_operands, old_dups, true);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 6: Begin new chains for writes inside operands.  */
-	  /* ??? Many targets have output constraints on the SET_DEST
-	     of a call insn, which is stupid, since these are certainly
-	     ABI defined hard registers.  Don't change calls at all.
-	     Similarly take special care for asm statement that originally
-	     referenced hard registers.  */
-	  if (asm_noperands (PATTERN (insn)) > 0)
-	    {
-	      for (i = 0; i < n_ops; i++)
-		if (recog_data.operand_type[i] == OP_OUT)
-		  {
-		    rtx *loc = recog_data.operand_loc[i];
-		    rtx op = *loc;
-		    enum reg_class cl = recog_op_alt[i][alt].cl;
-
-		    if (REG_P (op)
-			&& REGNO (op) == ORIGINAL_REGNO (op))
-		      continue;
-
-		    scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			      recog_op_alt[i][alt].earlyclobber);
-		  }
-	    }
-	  else if (!CALL_P (insn))
-	    for (i = 0; i < n_ops + recog_data.n_dups; i++)
-	      {
-		int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
-		rtx *loc = (i < n_ops
-			    ? recog_data.operand_loc[opn]
-			    : recog_data.dup_loc[i - n_ops]);
-		enum reg_class cl = recog_op_alt[opn][alt].cl;
-
-		if (recog_data.operand_type[opn] == OP_OUT)
-		  scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			    recog_op_alt[opn][alt].earlyclobber);
-	      }
+	  record_out_operands (insn);
 
 	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */

[-- Attachment #3: rr-full.diff --]
[-- Type: text/plain, Size: 27721 bytes --]

Index: gcc/regrename.c
===================================================================
--- gcc.orig/regrename.c
+++ gcc/regrename.c
@@ -50,10 +50,22 @@ struct du_head
   struct du_chain *first, *last;
   /* Describes the register being tracked.  */
   unsigned regno, nregs;
+
+  /* A unique id to be used as an index into the conflicts bitmaps.  */
+  unsigned id;
+  /* A bitmap to record conflicts with other chains.  */
+  bitmap_head conflicts;
+  /* Conflicts with untracked hard registers.  */
+  HARD_REG_SET hard_conflicts;
+
+  /* Nonzero if the chain is finished; zero if it is still open.  */
+  unsigned int terminated:1;
   /* Nonzero if the chain crosses a call.  */
   unsigned int need_caller_save_reg:1;
-  /* Nonzero if the chain is finished.  */
-  unsigned int terminated:1;
+  /* Nonzero if the register is used in a way that prevents renaming,
+     such as the SET_DEST of a CALL_INSN or an asm operand that used
+     to be a hard register.  */
+  unsigned int cannot_rename:1;
 };
 
 /* This struct describes a single occurrence of a register.  */
@@ -74,10 +86,9 @@ struct du_chain
 
 enum scan_actions
 {
-  terminate_all_read,
-  terminate_overlapping_read,
   terminate_write,
   terminate_dead,
+  mark_all_read,
   mark_read,
   mark_write,
   /* mark_access is for marking the destination regs in
@@ -88,10 +99,9 @@ enum scan_actions
 
 static const char * const scan_actions_name[] =
 {
-  "terminate_all_read",
-  "terminate_overlapping_read",
   "terminate_write",
   "terminate_dead",
+  "mark_all_read",
   "mark_read",
   "mark_write",
   "mark_access"
@@ -108,95 +118,40 @@ static void scan_rtx (rtx, rtx *, enum r
 		      enum op_type, int);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
-static void note_sets (rtx, const_rtx, void *);
-static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
-static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
-				    struct du_head *);
 
-/* Called through note_stores.  Find sets of registers, and
-   record them in *DATA (which is actually a HARD_REG_SET *).  */
+typedef struct du_head *du_head_p;
+DEF_VEC_P (du_head_p);
+DEF_VEC_ALLOC_P (du_head_p, heap);
+static VEC(du_head_p, heap) *id_to_chain;
 
 static void
-note_sets (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data)
+free_chain_data (void)
 {
-  HARD_REG_SET *pset = (HARD_REG_SET *) data;
-
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!REG_P (x))
-    return;
-  /* There must not be pseudos at this point.  */
-  gcc_assert (HARD_REGISTER_P (x));
-  add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
-}
-
-/* Clear all registers from *PSET for which a note of kind KIND can be found
-   in the list NOTES.  */
+  int i;
+  du_head_p ptr;
+  for (i = 0; VEC_iterate(du_head_p, id_to_chain, i, ptr); i++)
+    {
+      bitmap_clear (&ptr->conflicts);
+    }
 
-static void
-clear_dead_regs (HARD_REG_SET *pset, enum reg_note kind, rtx notes)
-{
-  rtx note;
-  for (note = notes; note; note = XEXP (note, 1))
-    if (REG_NOTE_KIND (note) == kind && REG_P (XEXP (note, 0)))
-      {
-	rtx reg = XEXP (note, 0);
-	/* There must not be pseudos at this point.  */
-	gcc_assert (HARD_REGISTER_P (reg));
-	remove_from_hard_reg_set (pset, GET_MODE (reg), REGNO (reg));
-      }
+  VEC_free (du_head_p, heap, id_to_chain);
 }
 
 /* For a def-use chain HEAD in basic block B, find which registers overlap
    its lifetime and set the corresponding bits in *PSET.  */
 
 static void
-merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_head *head)
+merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
 {
-  struct du_chain *t;
-  rtx insn;
-  HARD_REG_SET live;
-  df_ref *def_rec;
-
-  REG_SET_TO_HARD_REG_SET (live, df_get_live_in (b));
-  for (def_rec = df_get_artificial_defs (b->index); *def_rec; def_rec++)
+  bitmap_iterator bi;
+  unsigned i;
+  IOR_HARD_REG_SET (*pset, head->hard_conflicts);
+  EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
     {
-      df_ref def = *def_rec;
-      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
-	SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
-    }
-
-  t = head->first;
-  insn = BB_HEAD (b);
-  while (t)
-    {
-      /* Search forward until the next reference to the register to be
-	 renamed.  */
-      while (insn != t->insn)
-	{
-	  if (INSN_P (insn))
-	    {
-	      clear_dead_regs (&live, REG_DEAD, REG_NOTES (insn));
-	      note_stores (PATTERN (insn), note_sets, (void *) &live);
-	      /* Only record currently live regs if we are inside the
-		 reg's live range.  */
-	      if (t != head->first)
-		IOR_HARD_REG_SET (*pset, live);
-	      clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
-	    }
-	  insn = NEXT_INSN (insn);
-	}
-
-      IOR_HARD_REG_SET (*pset, live);
-
-      /* For the last reference, also merge in all registers set in the
-	 same insn.
-	 @@@ We only have take earlyclobbered sets into account.  */
-      if (! t->next_use)
-	note_stores (PATTERN (insn), note_sets, (void *) pset);
-
-      t = t->next_use;
+      du_head_p other = VEC_index (du_head_p, id_to_chain, i);
+      unsigned j = other->nregs;
+      while (j-- > 0)
+	SET_HARD_REG_BIT (*pset, other->regno + j);
     }
 }
 
@@ -226,6 +181,8 @@ regrename_optimize (void)
       HARD_REG_SET unavailable;
       HARD_REG_SET regs_seen;
 
+      id_to_chain = VEC_alloc (du_head_p, heap, 0);
+
       CLEAR_HARD_REG_SET (unavailable);
 
       if (dump_file)
@@ -249,7 +206,7 @@ regrename_optimize (void)
       CLEAR_HARD_REG_SET (regs_seen);
       while (all_chains)
 	{
-	  int new_reg, best_new_reg;
+	  int new_reg, best_new_reg, best_nregs;
 	  int n_uses;
 	  struct du_head *this_head = all_chains;
 	  struct du_chain *tmp;
@@ -259,6 +216,9 @@ regrename_optimize (void)
 
 	  all_chains = this_head->next_chain;
 
+	  if (this_head->cannot_rename)
+	    continue;
+
 	  best_new_reg = reg;
 
 #if 0 /* This just disables optimization opportunities.  */
@@ -299,7 +259,7 @@ regrename_optimize (void)
 	  if (this_head->need_caller_save_reg)
 	    IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
 
-	  merge_overlapping_regs (bb, &this_unavailable, this_head);
+	  merge_overlapping_regs (&this_unavailable, this_head);
 
 	  /* Now potential_regs is a reasonable approximation, let's
 	     have a closer look at each register still in there.  */
@@ -343,7 +303,10 @@ regrename_optimize (void)
 	      if (! tmp)
 		{
 		  if (tick[best_new_reg] > tick[new_reg])
-		    best_new_reg = new_reg;
+		    {
+		      best_new_reg = new_reg;
+		      best_nregs = nregs;
+		    }
 		}
 	    }
 
@@ -367,10 +330,13 @@ regrename_optimize (void)
 	    fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
 
 	  do_replace (this_head, best_new_reg);
+	  this_head->regno = best_new_reg;
+	  this_head->nregs = best_nregs;
 	  tick[best_new_reg] = ++this_tick;
 	  df_set_regs_ever_live (best_new_reg, true);
 	}
 
+      free_chain_data ();
       obstack_free (&rename_obstack, first_obj);
     }
 
@@ -387,6 +353,7 @@ do_replace (struct du_head *head, int re
 {
   struct du_chain *chain;
   unsigned int base_regno = head->regno;
+  bool found_note = false;
 
   gcc_assert (! DEBUG_INSN_P (head->first->insn));
 
@@ -410,26 +377,88 @@ do_replace (struct du_head *head, int re
 
 	  for (note = REG_NOTES (chain->insn); note; note = XEXP (note, 1))
 	    {
-	      if (REG_NOTE_KIND (note) == REG_DEAD 
-		  || REG_NOTE_KIND (note) == REG_UNUSED)
+	      enum reg_note kind = REG_NOTE_KIND (note);
+	      if (kind == REG_DEAD || kind == REG_UNUSED)
 		{
 		  rtx reg = XEXP (note, 0);
 		  gcc_assert (HARD_REGISTER_P (reg));
 		  
-		  if (REGNO (reg) == base_regno) 
-		    XEXP (note, 0) = *chain->loc;
+		  if (REGNO (reg) == base_regno)
+		    {
+		      found_note = true;
+		      if (kind == REG_DEAD
+			  && reg_set_p (*chain->loc, chain->insn))
+			remove_note (chain->insn, note);
+		      else
+			XEXP (note, 0) = *chain->loc;
+		      break;
+		    }
 		}
 	    }
 	}
 
       df_insn_rescan (chain->insn);
     }
+  if (!found_note)
+    {
+      /* If the chain's first insn is the same as the last, we should have
+	 found a REG_UNUSED note.  */
+      gcc_assert (head->first->insn != head->last->insn);
+      if (!reg_set_p (*head->last->loc, head->last->insn))
+	add_reg_note (head->last->insn, REG_DEAD, *head->last->loc);
+    }
+}
+
+
+/* Walk all chains starting with CHAINS and record that they conflict with
+   another chains whose id is ID.  */
+static void
+mark_conflict (struct du_head *chains, unsigned id)
+{
+  while (chains)
+    {
+      bitmap_set_bit (&chains->conflicts, id);
+      chains = chains->next_chain;
+    }
 }
 
+/* True if we found a register with a size mismatch that means we can't
+   track its lifetime accurately.  If so, we abort the current block
+   without renaming.  */
+static bool fail_current_block;
+
+/* The id to be given to the next opened chain.  */
+static unsigned current_id;
 
+/* List of currently open chains, and closed chains that can be renamed.  */
 static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
+/* Conflict bitmaps, tracking the live chains and the live hard registers.  */
+static bitmap_head live_chains;
+static HARD_REG_SET live_hard_regs;
+
+/* Called through note_stores.  DATA points to a rtx_code, either SET or
+   CLOBBER, which tells us which kind of rtx to look at.  If we have a
+   match, record the set register in live_hard_regs.  */
+
+static void
+note_sets_clobbers (rtx x, const_rtx set, void *data)
+{
+  enum rtx_code *pcode = (enum rtx_code *)data;
+  struct du_head *chain;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  if (!REG_P (x) || GET_CODE (set) != *pcode)
+    return;
+  /* There must not be pseudos at this point.  */
+  gcc_assert (HARD_REGISTER_P (x));
+  add_to_hard_reg_set (&live_hard_regs, GET_MODE (x), REGNO (x));
+  for (chain = open_chains; chain; chain = chain->next_chain)
+    add_to_hard_reg_set (&chain->hard_conflicts, GET_MODE (x), REGNO (x));
+}
+
 static void
 scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
 	      enum scan_actions action, enum op_type type, int earlyclobber)
@@ -446,19 +475,44 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	{
 	  struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
 	  struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+	  int nregs;
 	  head->next_chain = open_chains;
-	  open_chains = head;
 	  head->first = head->last = this_du;
 	  head->regno = this_regno;
 	  head->nregs = this_nregs;
 	  head->need_caller_save_reg = 0;
+	  head->cannot_rename = 0;
 	  head->terminated = 0;
 
+	  VEC_safe_push (du_head_p, heap, id_to_chain, head);
+	  head->id = current_id++;
+
+	  bitmap_initialize (&head->conflicts, &bitmap_default_obstack);
+	  bitmap_copy (&head->conflicts, &live_chains);
+	  mark_conflict (open_chains, head->id);
+
+	  /* Since we're tracking this as a chain now, remove it from the
+	     list of conflicting live hard registers.  */
+	  nregs = head->nregs;
+	  while (nregs-- > 0)
+	    CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+
+	  COPY_HARD_REG_SET (head->hard_conflicts, live_hard_regs);
+	  bitmap_set_bit (&live_chains, head->id);
+
+	  open_chains = head;
+
 	  this_du->next_use = 0;
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
 	  this_du->earlyclobber = earlyclobber;
+
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Creating chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
 	}
       return;
     }
@@ -469,82 +523,89 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
   for (p = &open_chains; *p;)
     {
       struct du_head *head = *p;
+      struct du_head *next = head->next_chain;
+      int exact_match = (head->regno == this_regno
+			 && head->nregs == this_nregs);
+      int superset = (this_regno <= head->regno
+		      && this_regno + this_nregs >= head->regno + head->nregs);
+
+      if (head->terminated
+	  || head->regno + head->nregs <= this_regno
+	  || this_regno + this_nregs <= head->regno)
+	{
+	  p = &head->next_chain;
+	  continue;
+	}
 
-      /* Check if the chain has been terminated if it has then skip to
-	 the next chain.
-
-	 This can happen when we've already appended the location to
-	 the chain in Step 3, but are trying to hide in-out operands
-	 from terminate_write in Step 5.  */
-
-      if (head->terminated)
-	p = &head->next_chain;
-      else
+      if (action == mark_read || action == mark_access)
 	{
-	  int exact_match = (head->regno == this_regno
-			     && head->nregs == this_nregs);
+	  /* ??? Class NO_REGS can happen if the md file makes use of
+	     EXTRA_CONSTRAINTS to match registers.  Which is arguably
+	     wrong, but there we are.  */
 
-	  if (head->regno + head->nregs <= this_regno
-	      || this_regno + this_nregs <= head->regno)
+	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
 	    {
-	      p = &head->next_chain;
-	      continue;
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+			 reg_names[head->regno], head->id, INSN_UID (insn),
+			 scan_actions_name[(int) action]);
+	      head->cannot_rename = 1;
 	    }
-
-	  if (action == mark_read || action == mark_access)
+	  else
 	    {
-	      gcc_assert (exact_match || DEBUG_INSN_P (insn));
-
-	      /* ??? Class NO_REGS can happen if the md file makes use of
-		 EXTRA_CONSTRAINTS to match registers.  Which is arguably
-		 wrong, but there we are.  Since we know not what this may
-		 be replaced with, terminate the chain.  */
-	      if (cl != NO_REGS)
-		{
-		  struct du_chain *this_du;
-		  this_du = XOBNEW (&rename_obstack, struct du_chain);
-		  this_du->next_use = 0;
-		  this_du->loc = loc;
-		  this_du->insn = insn;
-		  this_du->cl = cl;
-		  head->last->next_use = this_du;
-		  head->last = this_du;
-		  return;
-		}
+	      struct du_chain *this_du;
+	      this_du = XOBNEW (&rename_obstack, struct du_chain);
+	      this_du->next_use = 0;
+	      this_du->loc = loc;
+	      this_du->insn = insn;
+	      this_du->cl = cl;
+	      head->last->next_use = this_du;
+	      head->last = this_du;
 	    }
+	  /* There may be other chains that do not match exactly; ensure
+	     they all get marked unrenamable.  */
+	  p = &head->next_chain;
+	  continue;
+	}
 
-	  if (action != terminate_overlapping_read || ! exact_match)
-	    {
-	      struct du_head *next = head->next_chain;
+      /* Whether the terminated chain can be used for renaming
+	 depends on the action and this being an exact match.
+	 In either case, we remove this element from open_chains.  */
 
-	      /* Whether the terminated chain can be used for renaming
-	         depends on the action and this being an exact match.
-	         In either case, we remove this element from open_chains.  */
-
-	      head->terminated = 1;
-	      if ((action == terminate_dead || action == terminate_write)
-		  && exact_match)
-		{
-		  head->next_chain = closed_chains;
-		  closed_chains = head;
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Closing chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Discarding chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      *p = next;
-	    }
-	  else
-	    p = &head->next_chain;
+      if ((action == terminate_dead || action == terminate_write)
+	  && superset)
+	{
+	  head->terminated = 1;
+	  head->next_chain = closed_chains;
+	  closed_chains = head;
+	  bitmap_clear_bit (&live_chains, head->id);
+	  *p = next;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Closing chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	}
+      else if (action == terminate_dead || action == terminate_write)
+	{
+	  /* In this case, tracking liveness gets too hard.  Fail the
+	     entire basic block.  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Failing basic block due to unhandled overlap\n");
+	  fail_current_block = true;
+	  return;
+	}
+      else
+	{
+	  head->cannot_rename = 1;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	  p = &head->next_chain;
 	}
     }
 }
@@ -670,7 +731,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
 #ifndef AUTO_INC_DEC
       /* If the target doesn't claim to handle autoinc, this must be
 	 something special, like a stack push.  Kill this chain.  */
-      action = terminate_all_read;
+      action = mark_all_read;
 #endif
       break;
 
@@ -788,13 +849,15 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 /* Hide operands of the current insn (of which there are N_OPS) by
    substituting cc0 for them.
    Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
-   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+   If INOUT_AND_EC_ONLY is set, we only do this for OP_INOUT type operands
+   and earlyclobbers.  */
 
 static void
 hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
-	       bool inout_only)
+	       bool inout_and_ec_only)
 {
   int i;
+  int alt = which_alternative;
   for (i = 0; i < n_ops; i++)
     {
       old_operands[i] = recog_data.operand[i];
@@ -803,14 +866,16 @@ hide_operands (int n_ops, rtx *old_opera
 	 reachable by proper operands.  */
       if (recog_data.constraints[i][0] == '\0')
 	continue;
-      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[i] == OP_INOUT
+	  || recog_op_alt[i][alt].earlyclobber)
 	*recog_data.operand_loc[i] = cc0_rtx;
     }
   for (i = 0; i < recog_data.n_dups; i++)
     {
       int opn = recog_data.dup_num[i];
       old_dups[i] = *recog_data.dup_loc[i];
-      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[opn] == OP_INOUT
+	  || recog_op_alt[opn][alt].earlyclobber)
 	*recog_data.dup_loc[i] = cc0_rtx;
     }
 }
@@ -831,10 +896,11 @@ restore_operands (rtx insn, int n_ops, r
 }
 
 /* For each output operands of INSN, call scan_rtx to create a new
-   open chain.  */
+   open chain.  Do this only for normal or earlyclobber outputs,
+   depending on EARLYCLOBBER.  */
 
 static void
-record_out_operands (rtx insn)
+record_out_operands (rtx insn, bool earlyclobber)
 {
   int n_ops = recog_data.n_operands;
   int alt = which_alternative;
@@ -851,9 +917,9 @@ record_out_operands (rtx insn)
       enum reg_class cl = recog_op_alt[opn][alt].cl;
       struct du_head *prev_open = open_chains;
 
-      if (recog_data.operand_type[opn] == OP_OUT)
-	scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-		  recog_op_alt[opn][alt].earlyclobber);
+      if (recog_data.operand_type[opn] == OP_OUT
+	  && recog_op_alt[opn][alt].earlyclobber == earlyclobber)
+	scan_rtx (insn, loc, cl, mark_write, OP_OUT, earlyclobber);
 
       /* ??? Many targets have output constraints on the SET_DEST
 	 of a call insn, which is stupid, since these are certainly
@@ -866,7 +932,7 @@ record_out_operands (rtx insn)
 	      && REGNO (op) == ORIGINAL_REGNO (op)))
 	{
 	  if (prev_open != open_chains)
-	    open_chains->first->cl = NO_REGS;
+	    open_chains->cannot_rename = 1;
 	}
     }
 }
@@ -877,9 +943,22 @@ static struct du_head *
 build_def_use (basic_block bb)
 {
   rtx insn;
+  df_ref *def_rec;
 
   open_chains = closed_chains = NULL;
 
+  fail_current_block = false;
+
+  current_id = 0;
+  bitmap_initialize (&live_chains, &bitmap_default_obstack);
+  REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
+  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
+    {
+      df_ref def = *def_rec;
+      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+	SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
+    }
+
   for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
     {
       if (NONDEBUG_INSN_P (insn))
@@ -891,22 +970,31 @@ build_def_use (basic_block bb)
 	  int i, icode;
 	  int alt;
 	  int predicated;
+	  enum rtx_code set_code = SET;
+	  enum rtx_code clobber_code = CLOBBER;
 
 	  /* Process the insn, determining its effect on the def-use
-	     chains.  We perform the following steps with the register
-	     references in the insn:
-	     (1) Any read that overlaps an open chain, but doesn't exactly
-	         match, causes that chain to be closed.  We can't deal
-	         with overlaps yet.
+	     chains and live hard registers.  We perform the following
+	     steps with the register references in the insn, simulating
+	     its effect:
+	     (1) Deal with earlyclobber operands and CLOBBERs of non-operands
+	         by creating chains and marking hard regs live.
 	     (2) Any read outside an operand causes any chain it overlaps
-	         with to be closed, since we can't replace it.
+	         with to be marked unrenamable.
 	     (3) Any read inside an operand is added if there's already
 	         an open chain for it.
 	     (4) For any REG_DEAD note we find, close open chains that
 	         overlap it.
-	     (5) For any write we find, close open chains that overlap it.
-	     (6) For any write we find in an operand, make a new chain.
-	     (7) For any REG_UNUSED, close any chains we just opened.  */
+	     (5) For any non-earlyclobber write we find, close open chains
+	         that overlap it.
+	     (6) For any non-earlyclobber write we find in an operand, make
+	         a new chain or mark the hard register as live.
+	     (7) For any REG_UNUSED, close any chains we just opened.
+
+	     We cannot deal with situations where we track a reg in one mode
+	     and see a reference in another mode; these will cause the chain
+	     to be marked unrenamable or even cause us to abort the entire
+	     basic block.  */
 
 	  icode = recog_memoized (insn);
 	  extract_insn (insn);
@@ -928,28 +1016,42 @@ build_def_use (basic_block bb)
 		recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
 	      if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
 	          || (predicated && recog_data.operand_type[i] == OP_OUT))
-		recog_data.operand_type[i] = OP_INOUT;
+		{
+		  recog_data.operand_type[i] = OP_INOUT;
+		  if (matches >= 0
+		      && (GET_MODE_SIZE (recog_data.operand_mode[i])
+			  != GET_MODE_SIZE (recog_data.operand_mode[matches])))
+		    fail_current_block = true;
+		}
 	    }
+	  if (fail_current_block)
+	    break;
+
+	  /* Step 1a: Mark hard registers that are clobbered in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &clobber_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 1: Close chains for which we have overlapping reads.  */
-	  for (i = 0; i < n_ops; i++)
-	    scan_rtx (insn, recog_data.operand_loc[i],
-		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i], 0);
+	  /* Step 1b: Begin new chains for earlyclobbered writes inside
+	     operands.  */
+	  record_out_operands (insn, true);
 
-	  /* Step 2: Close chains for which we have reads outside operands.
+	  /* Step 2: Mark chains for which we have reads outside operands
+	     as non-renamable.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, false);
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
+
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, mark_all_read,
 		    OP_IN, 0);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN, 0);
+		      NO_REGS, mark_all_read, OP_IN, 0);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -963,7 +1065,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+		  scan_rtx (insn, loc, NO_REGS, mark_all_read, OP_IN, 0);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -999,8 +1101,13 @@ build_def_use (basic_block bb)
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN, 0);
+	      }
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -1013,16 +1120,24 @@ build_def_use (basic_block bb)
 
 	  /* Step 5: Close open chains that overlap writes.  Similar to
 	     step 2, we hide in-out operands, since we do not want to
-	     close these chains.  */
+	     close these chains.  We also hide earlyclobber operands,
+	     since we've opened chains for them earlier and they couldn't
+	     possibly overlap any input operands anyway.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, true);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6: Begin new chains for writes inside operands.  */
-	  record_out_operands (insn);
+	  /* Step 6a: Mark hard registers that are set in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &set_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
+	  /* Step 6b: Begin new chains for writes inside operands.  */
+	  record_out_operands (insn, false);
+
+	  /* Step 6c: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
@@ -1033,8 +1148,13 @@ build_def_use (basic_block bb)
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN, 0);
+	      }
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
@@ -1046,6 +1166,11 @@ build_def_use (basic_block bb)
 	break;
     }
 
+  bitmap_clear (&live_chains);
+
+  if (fail_current_block)
+    return NULL;
+
   /* Since we close every chain when we find a REG_DEAD note, anything that
      is still open lives past the basic block, so it can't be renamed.  */
   return closed_chains;

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-20  1:36     ` Bernd Schmidt
@ 2009-11-20  7:58       ` Eric Botcazou
  2009-11-20 21:31         ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-20  7:58 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

> Not sure what you mean here?

Do a grep "earlyclobber" on the unmodified file.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-20  7:58       ` Eric Botcazou
@ 2009-11-20 21:31         ` Bernd Schmidt
  2009-11-22 22:26           ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-20 21:31 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

Eric Botcazou wrote:
>> Not sure what you mean here?
> 
> Do a grep "earlyclobber" on the unmodified file.

Still not sure what you mean.  I don't really want to change any
behaviour in a cleanup patch.

Is the split-up I posted OK?


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-20 21:31         ` Bernd Schmidt
@ 2009-11-22 22:26           ` Eric Botcazou
  2009-11-24 11:55             ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-22 22:26 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

> Still not sure what you mean.  I don't really want to change any
> behaviour in a cleanup patch.

Where is the "earlyclobber" field of "du_chain" used?

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-22 22:26           ` Eric Botcazou
@ 2009-11-24 11:55             ` Bernd Schmidt
  2009-11-24 12:30               ` Eric Botcazou
  2009-11-26 12:27               ` Eric Botcazou
  0 siblings, 2 replies; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-24 11:55 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 553 bytes --]

Eric Botcazou wrote:
>> Still not sure what you mean.  I don't really want to change any
>> behaviour in a cleanup patch.
> 
> Where is the "earlyclobber" field of "du_chain" used?

Ah.  That looks a little buggy actually in the old version.  Nevermind,
here's a new set of patches.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-cleanup2.diff --]
[-- Type: text/plain, Size: 13899 bytes --]

	* regrename.c (struct du_chain): Remove member earlyclobber.
	(scan_rtx_reg): Don't set it.  Remove argument earlyclobber,
	all callers changed.
	(scan_rtx): Remove argument earlyclobber, all callers changed.
	(hide_operands, restore_operands, record_out_operands): New functions,
	broken out of build_def_use.
	(build_def_use): Call them as necessary.

Index: regrename.c
===================================================================
--- regrename.c.orig
+++ regrename.c
@@ -68,8 +68,6 @@ struct du_chain
   rtx *loc;
   /* The register class required by the insn at this location.  */
   ENUM_BITFIELD(reg_class) cl : 16;
-  /* Nonzero if the register is subject to earlyclobber.  */
-  unsigned int earlyclobber:1;
 };
 
 enum scan_actions
@@ -101,11 +99,11 @@ static struct obstack rename_obstack;
 
 static void do_replace (struct du_head *, int);
 static void scan_rtx_reg (rtx, rtx *, enum reg_class,
-			  enum scan_actions, enum op_type, int);
+			  enum scan_actions, enum op_type);
 static void scan_rtx_address (rtx, rtx *, enum reg_class,
 			      enum scan_actions, enum machine_mode);
 static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
-		      enum op_type, int);
+		      enum op_type);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
 static void note_sets (rtx, const_rtx, void *);
@@ -431,8 +429,8 @@ static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
 static void
-scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
-	      enum scan_actions action, enum op_type type, int earlyclobber)
+scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+	      enum op_type type)
 {
   struct du_head **p;
   rtx x = *loc;
@@ -458,7 +456,6 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
-	  this_du->earlyclobber = earlyclobber;
 	}
       return;
     }
@@ -681,7 +678,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
       return;
 
     case REG:
-      scan_rtx_reg (insn, loc, cl, action, OP_IN, 0);
+      scan_rtx_reg (insn, loc, cl, action, OP_IN);
       return;
 
     default:
@@ -700,8 +697,8 @@ scan_rtx_address (rtx insn, rtx *loc, en
 }
 
 static void
-scan_rtx (rtx insn, rtx *loc, enum reg_class cl,
-	  enum scan_actions action, enum op_type type, int earlyclobber)
+scan_rtx (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+	  enum op_type type)
 {
   const char *fmt;
   rtx x = *loc;
@@ -723,7 +720,7 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
       return;
 
     case REG:
-      scan_rtx_reg (insn, loc, cl, action, type, earlyclobber);
+      scan_rtx_reg (insn, loc, cl, action, type);
       return;
 
     case MEM:
@@ -733,21 +730,21 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
       return;
 
     case SET:
-      scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN, 0);
+      scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN);
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
       return;
 
     case STRICT_LOW_PART:
-      scan_rtx (insn, &XEXP (x, 0), cl, action, OP_INOUT, earlyclobber);
+      scan_rtx (insn, &XEXP (x, 0), cl, action, OP_INOUT);
       return;
 
     case ZERO_EXTRACT:
     case SIGN_EXTRACT:
       scan_rtx (insn, &XEXP (x, 0), cl, action,
-		type == OP_IN ? OP_IN : OP_INOUT, earlyclobber);
-      scan_rtx (insn, &XEXP (x, 1), cl, action, OP_IN, 0);
-      scan_rtx (insn, &XEXP (x, 2), cl, action, OP_IN, 0);
+		type == OP_IN ? OP_IN : OP_INOUT);
+      scan_rtx (insn, &XEXP (x, 1), cl, action, OP_IN);
+      scan_rtx (insn, &XEXP (x, 2), cl, action, OP_IN);
       return;
 
     case POST_INC:
@@ -761,13 +758,13 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 
     case CLOBBER:
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
       return;
 
     case EXPR_LIST:
-      scan_rtx (insn, &XEXP (x, 0), cl, action, type, 0);
+      scan_rtx (insn, &XEXP (x, 0), cl, action, type);
       if (XEXP (x, 1))
-	scan_rtx (insn, &XEXP (x, 1), cl, action, type, 0);
+	scan_rtx (insn, &XEXP (x, 1), cl, action, type);
       return;
 
     default:
@@ -778,10 +775,95 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-	scan_rtx (insn, &XEXP (x, i), cl, action, type, 0);
+	scan_rtx (insn, &XEXP (x, i), cl, action, type);
       else if (fmt[i] == 'E')
 	for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-	  scan_rtx (insn, &XVECEXP (x, i, j), cl, action, type, 0);
+	  scan_rtx (insn, &XVECEXP (x, i, j), cl, action, type);
+    }
+}
+
+/* Hide operands of the current insn (of which there are N_OPS) by
+   substituting cc0 for them.
+   Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
+   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+
+static void
+hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
+	       bool inout_only)
+{
+  int i;
+  for (i = 0; i < n_ops; i++)
+    {
+      old_operands[i] = recog_data.operand[i];
+      /* Don't squash match_operator or match_parallel here, since
+	 we don't know that all of the contained registers are
+	 reachable by proper operands.  */
+      if (recog_data.constraints[i][0] == '\0')
+	continue;
+      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+	*recog_data.operand_loc[i] = cc0_rtx;
+    }
+  for (i = 0; i < recog_data.n_dups; i++)
+    {
+      int opn = recog_data.dup_num[i];
+      old_dups[i] = *recog_data.dup_loc[i];
+      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+	*recog_data.dup_loc[i] = cc0_rtx;
+    }
+}
+
+/* Undoes the substitution performed by hide_operands.  INSN is the insn we
+   are processing; the arguments are the same as in hide_operands.  */
+
+static void
+restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
+{
+  int i;
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i] = old_dups[i];
+  for (i = 0; i < n_ops; i++)
+    *recog_data.operand_loc[i] = old_operands[i];
+  if (recog_data.n_dups)
+    df_insn_rescan (insn);
+}
+
+/* For each output operands of INSN, call scan_rtx to create a new
+   open chain.  */
+
+static void
+record_out_operands (rtx insn)
+{
+  int n_ops = recog_data.n_operands;
+  int alt = which_alternative;
+
+  int i;
+
+  for (i = 0; i < n_ops + recog_data.n_dups; i++)
+    {
+      int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+      rtx *loc = (i < n_ops
+		  ? recog_data.operand_loc[opn]
+		  : recog_data.dup_loc[i - n_ops]);
+      rtx op = *loc;
+      enum reg_class cl = recog_op_alt[opn][alt].cl;
+      struct du_head *prev_open = open_chains;
+
+      if (recog_data.operand_type[opn] == OP_OUT)
+	scan_rtx (insn, loc, cl, mark_write, OP_OUT);
+
+      /* ??? Many targets have output constraints on the SET_DEST
+	 of a call insn, which is stupid, since these are certainly
+	 ABI defined hard registers.  For these, and for asm operands
+	 that originally referenced hard registers, we must record that
+	 the chain cannot be renamed.  */
+      if (CALL_P (insn)
+	  || (asm_noperands (PATTERN (insn)) > 0
+	      && REG_P (op)
+	      && REGNO (op) == ORIGINAL_REGNO (op)))
+	{
+	  if (prev_open != open_chains)
+	    open_chains->first->cl = NO_REGS;
+	}
     }
 }
 
@@ -849,42 +931,21 @@ build_def_use (basic_block bb)
 	  for (i = 0; i < n_ops; i++)
 	    scan_rtx (insn, recog_data.operand_loc[i],
 		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i], 0);
+		      recog_data.operand_type[i]);
 
 	  /* Step 2: Close chains for which we have reads outside operands.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      /* Don't squash match_operator or match_parallel here, since
-		 we don't know that all of the contained registers are
-		 reachable by proper operands.  */
-	      if (recog_data.constraints[i][0] == '\0')
-		continue;
-	      *recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      *recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
+	  hide_operands (n_ops, old_operands, old_dups, false);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
-		    OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
-	  if (recog_data.n_dups)
-	    df_insn_rescan (insn);
+		    OP_IN);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN, 0);
+		      NO_REGS, terminate_all_read, OP_IN);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -898,7 +959,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -920,7 +981,7 @@ build_def_use (basic_block bb)
 	      if (recog_op_alt[opn][alt].is_address)
 		scan_rtx_address (insn, loc, cl, mark_read, VOIDmode);
 	      else
-		scan_rtx (insn, loc, cl, mark_read, type, 0);
+		scan_rtx (insn, loc, cl, mark_read, type);
 	    }
 
 	  /* Step 3B: Record updates for regs in REG_INC notes, and
@@ -929,13 +990,13 @@ build_def_use (basic_block bb)
 	    if (REG_NOTE_KIND (note) == REG_INC
 		|| REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
 	      scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_read,
-			OP_INOUT, 0);
+			OP_INOUT);
 
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
 	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+			OP_IN);
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -950,83 +1011,32 @@ build_def_use (basic_block bb)
 	     step 2, we hide in-out operands, since we do not want to
 	     close these chains.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      if (recog_data.operand_type[i] == OP_INOUT)
-		*recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      int opn = recog_data.dup_num[i];
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      if (recog_data.operand_type[opn] == OP_INOUT)
-		*recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
+	  hide_operands (n_ops, old_operands, old_dups, true);
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 6: Begin new chains for writes inside operands.  */
-	  /* ??? Many targets have output constraints on the SET_DEST
-	     of a call insn, which is stupid, since these are certainly
-	     ABI defined hard registers.  Don't change calls at all.
-	     Similarly take special care for asm statement that originally
-	     referenced hard registers.  */
-	  if (asm_noperands (PATTERN (insn)) > 0)
-	    {
-	      for (i = 0; i < n_ops; i++)
-		if (recog_data.operand_type[i] == OP_OUT)
-		  {
-		    rtx *loc = recog_data.operand_loc[i];
-		    rtx op = *loc;
-		    enum reg_class cl = recog_op_alt[i][alt].cl;
-
-		    if (REG_P (op)
-			&& REGNO (op) == ORIGINAL_REGNO (op))
-		      continue;
-
-		    scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			      recog_op_alt[i][alt].earlyclobber);
-		  }
-	    }
-	  else if (!CALL_P (insn))
-	    for (i = 0; i < n_ops + recog_data.n_dups; i++)
-	      {
-		int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
-		rtx *loc = (i < n_ops
-			    ? recog_data.operand_loc[opn]
-			    : recog_data.dup_loc[i - n_ops]);
-		enum reg_class cl = recog_op_alt[opn][alt].cl;
-
-		if (recog_data.operand_type[opn] == OP_OUT)
-		  scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			    recog_op_alt[opn][alt].earlyclobber);
-	      }
+	  record_out_operands (insn);
 
 	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
 	      scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_access,
-			OP_INOUT, 0);
+			OP_INOUT);
 
 	  /* Step 7: Close chains for registers that were never
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
 	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+			OP_IN);
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
 	{
 	  scan_rtx (insn, &INSN_VAR_LOCATION_LOC (insn),
-		    ALL_REGS, mark_read, OP_IN, 0);
+		    ALL_REGS, mark_read, OP_IN);
 	}
       if (insn == BB_END (bb))
 	break;

[-- Attachment #3: rr-full2.diff --]
[-- Type: text/plain, Size: 29097 bytes --]

	* regrename.c (struct du_head): New members id, conflicts,
	hard_conflicts and cannot_rename.
	(enum scan_actions): Remove terminate_all_read and
	terminate_overlapping_read; add mark_all_read.
	(scan_actions_name): Likewise.
	(du_head_p): New typedef.  Define a vector type for it.
	(id_to_chain): New static variable.
	(note_sets, clear_dead_regs): Delete functions.
	(free_chain_data): New function.
	(merge_overlapping_regs): Simply walk the conflicts bitmap.
	Remove argument B, all callers changed.
	(regrename_optimize): Allocate id_to_chain.  Ignore chains that have
	the cannot_rename bit set.  Update regno and nregs of a renamed chain.
	Call free_chain_data when done.
	(do_replace): Remove death notes when the renamed reg is set in the
	last insn; add them if not.
	(mark_conflict, note_sets_clobbers): New static function.
	(fail_current_block, current_id, live_chains, live_hard_regs): New
	static variables.
	(scan_rtx_reg): Keep track of conflicts between chains, and between
	chains and hard regs.  Don't terminate chains when we find a read we
	can't handle, mark it unrenameable instead.  For terminate_write,
	terminate chains that are written with an exact match or a superset
	of registers.  Set fail_current_block if multi-word lifetimes are too
	complex to handle.
	(scan_rtx_address): Use mark_all_read instead of terminate_all_read.
	(build_def_use): Initialize current_id, live_chains and live_hard_regs;
	free memory for them when done.
	Rearrange the steps so that earlyclobbers are noted before reads
	are processed.  Add new steps to keep track of hard register lifetimes
	outside insn operands.

Index: gcc/regrename.c
===================================================================
--- gcc.orig/regrename.c
+++ gcc/regrename.c
@@ -50,10 +50,22 @@ struct du_head
   struct du_chain *first, *last;
   /* Describes the register being tracked.  */
   unsigned regno, nregs;
+
+  /* A unique id to be used as an index into the conflicts bitmaps.  */
+  unsigned id;
+  /* A bitmap to record conflicts with other chains.  */
+  bitmap_head conflicts;
+  /* Conflicts with untracked hard registers.  */
+  HARD_REG_SET hard_conflicts;
+
+  /* Nonzero if the chain is finished; zero if it is still open.  */
+  unsigned int terminated:1;
   /* Nonzero if the chain crosses a call.  */
   unsigned int need_caller_save_reg:1;
-  /* Nonzero if the chain is finished.  */
-  unsigned int terminated:1;
+  /* Nonzero if the register is used in a way that prevents renaming,
+     such as the SET_DEST of a CALL_INSN or an asm operand that used
+     to be a hard register.  */
+  unsigned int cannot_rename:1;
 };
 
 /* This struct describes a single occurrence of a register.  */
@@ -72,10 +84,9 @@ struct du_chain
 
 enum scan_actions
 {
-  terminate_all_read,
-  terminate_overlapping_read,
   terminate_write,
   terminate_dead,
+  mark_all_read,
   mark_read,
   mark_write,
   /* mark_access is for marking the destination regs in
@@ -86,10 +97,9 @@ enum scan_actions
 
 static const char * const scan_actions_name[] =
 {
-  "terminate_all_read",
-  "terminate_overlapping_read",
   "terminate_write",
   "terminate_dead",
+  "mark_all_read",
   "mark_read",
   "mark_write",
   "mark_access"
@@ -106,95 +116,40 @@ static void scan_rtx (rtx, rtx *, enum r
 		      enum op_type);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
-static void note_sets (rtx, const_rtx, void *);
-static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
-static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
-				    struct du_head *);
 
-/* Called through note_stores.  Find sets of registers, and
-   record them in *DATA (which is actually a HARD_REG_SET *).  */
+typedef struct du_head *du_head_p;
+DEF_VEC_P (du_head_p);
+DEF_VEC_ALLOC_P (du_head_p, heap);
+static VEC(du_head_p, heap) *id_to_chain;
 
 static void
-note_sets (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data)
+free_chain_data (void)
 {
-  HARD_REG_SET *pset = (HARD_REG_SET *) data;
-
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!REG_P (x))
-    return;
-  /* There must not be pseudos at this point.  */
-  gcc_assert (HARD_REGISTER_P (x));
-  add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
-}
-
-/* Clear all registers from *PSET for which a note of kind KIND can be found
-   in the list NOTES.  */
+  int i;
+  du_head_p ptr;
+  for (i = 0; VEC_iterate(du_head_p, id_to_chain, i, ptr); i++)
+    {
+      bitmap_clear (&ptr->conflicts);
+    }
 
-static void
-clear_dead_regs (HARD_REG_SET *pset, enum reg_note kind, rtx notes)
-{
-  rtx note;
-  for (note = notes; note; note = XEXP (note, 1))
-    if (REG_NOTE_KIND (note) == kind && REG_P (XEXP (note, 0)))
-      {
-	rtx reg = XEXP (note, 0);
-	/* There must not be pseudos at this point.  */
-	gcc_assert (HARD_REGISTER_P (reg));
-	remove_from_hard_reg_set (pset, GET_MODE (reg), REGNO (reg));
-      }
+  VEC_free (du_head_p, heap, id_to_chain);
 }
 
 /* For a def-use chain HEAD in basic block B, find which registers overlap
    its lifetime and set the corresponding bits in *PSET.  */
 
 static void
-merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_head *head)
+merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
 {
-  struct du_chain *t;
-  rtx insn;
-  HARD_REG_SET live;
-  df_ref *def_rec;
-
-  REG_SET_TO_HARD_REG_SET (live, df_get_live_in (b));
-  for (def_rec = df_get_artificial_defs (b->index); *def_rec; def_rec++)
+  bitmap_iterator bi;
+  unsigned i;
+  IOR_HARD_REG_SET (*pset, head->hard_conflicts);
+  EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
     {
-      df_ref def = *def_rec;
-      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
-	SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
-    }
-
-  t = head->first;
-  insn = BB_HEAD (b);
-  while (t)
-    {
-      /* Search forward until the next reference to the register to be
-	 renamed.  */
-      while (insn != t->insn)
-	{
-	  if (INSN_P (insn))
-	    {
-	      clear_dead_regs (&live, REG_DEAD, REG_NOTES (insn));
-	      note_stores (PATTERN (insn), note_sets, (void *) &live);
-	      /* Only record currently live regs if we are inside the
-		 reg's live range.  */
-	      if (t != head->first)
-		IOR_HARD_REG_SET (*pset, live);
-	      clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
-	    }
-	  insn = NEXT_INSN (insn);
-	}
-
-      IOR_HARD_REG_SET (*pset, live);
-
-      /* For the last reference, also merge in all registers set in the
-	 same insn.
-	 @@@ We only have take earlyclobbered sets into account.  */
-      if (! t->next_use)
-	note_stores (PATTERN (insn), note_sets, (void *) pset);
-
-      t = t->next_use;
+      du_head_p other = VEC_index (du_head_p, id_to_chain, i);
+      unsigned j = other->nregs;
+      while (j-- > 0)
+	SET_HARD_REG_BIT (*pset, other->regno + j);
     }
 }
 
@@ -224,6 +179,8 @@ regrename_optimize (void)
       HARD_REG_SET unavailable;
       HARD_REG_SET regs_seen;
 
+      id_to_chain = VEC_alloc (du_head_p, heap, 0);
+
       CLEAR_HARD_REG_SET (unavailable);
 
       if (dump_file)
@@ -247,7 +204,7 @@ regrename_optimize (void)
       CLEAR_HARD_REG_SET (regs_seen);
       while (all_chains)
 	{
-	  int new_reg, best_new_reg;
+	  int new_reg, best_new_reg, best_nregs;
 	  int n_uses;
 	  struct du_head *this_head = all_chains;
 	  struct du_chain *tmp;
@@ -257,6 +214,9 @@ regrename_optimize (void)
 
 	  all_chains = this_head->next_chain;
 
+	  if (this_head->cannot_rename)
+	    continue;
+
 	  best_new_reg = reg;
 
 #if 0 /* This just disables optimization opportunities.  */
@@ -297,7 +257,7 @@ regrename_optimize (void)
 	  if (this_head->need_caller_save_reg)
 	    IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
 
-	  merge_overlapping_regs (bb, &this_unavailable, this_head);
+	  merge_overlapping_regs (&this_unavailable, this_head);
 
 	  /* Now potential_regs is a reasonable approximation, let's
 	     have a closer look at each register still in there.  */
@@ -341,7 +301,10 @@ regrename_optimize (void)
 	      if (! tmp)
 		{
 		  if (tick[best_new_reg] > tick[new_reg])
-		    best_new_reg = new_reg;
+		    {
+		      best_new_reg = new_reg;
+		      best_nregs = nregs;
+		    }
 		}
 	    }
 
@@ -365,10 +328,13 @@ regrename_optimize (void)
 	    fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
 
 	  do_replace (this_head, best_new_reg);
+	  this_head->regno = best_new_reg;
+	  this_head->nregs = best_nregs;
 	  tick[best_new_reg] = ++this_tick;
 	  df_set_regs_ever_live (best_new_reg, true);
 	}
 
+      free_chain_data ();
       obstack_free (&rename_obstack, first_obj);
     }
 
@@ -385,6 +351,7 @@ do_replace (struct du_head *head, int re
 {
   struct du_chain *chain;
   unsigned int base_regno = head->regno;
+  bool found_note = false;
 
   gcc_assert (! DEBUG_INSN_P (head->first->insn));
 
@@ -408,26 +375,88 @@ do_replace (struct du_head *head, int re
 
 	  for (note = REG_NOTES (chain->insn); note; note = XEXP (note, 1))
 	    {
-	      if (REG_NOTE_KIND (note) == REG_DEAD 
-		  || REG_NOTE_KIND (note) == REG_UNUSED)
+	      enum reg_note kind = REG_NOTE_KIND (note);
+	      if (kind == REG_DEAD || kind == REG_UNUSED)
 		{
 		  rtx reg = XEXP (note, 0);
 		  gcc_assert (HARD_REGISTER_P (reg));
 		  
-		  if (REGNO (reg) == base_regno) 
-		    XEXP (note, 0) = *chain->loc;
+		  if (REGNO (reg) == base_regno)
+		    {
+		      found_note = true;
+		      if (kind == REG_DEAD
+			  && reg_set_p (*chain->loc, chain->insn))
+			remove_note (chain->insn, note);
+		      else
+			XEXP (note, 0) = *chain->loc;
+		      break;
+		    }
 		}
 	    }
 	}
 
       df_insn_rescan (chain->insn);
     }
+  if (!found_note)
+    {
+      /* If the chain's first insn is the same as the last, we should have
+	 found a REG_UNUSED note.  */
+      gcc_assert (head->first->insn != head->last->insn);
+      if (!reg_set_p (*head->last->loc, head->last->insn))
+	add_reg_note (head->last->insn, REG_DEAD, *head->last->loc);
+    }
+}
+
+
+/* Walk all chains starting with CHAINS and record that they conflict with
+   another chains whose id is ID.  */
+static void
+mark_conflict (struct du_head *chains, unsigned id)
+{
+  while (chains)
+    {
+      bitmap_set_bit (&chains->conflicts, id);
+      chains = chains->next_chain;
+    }
 }
 
+/* True if we found a register with a size mismatch that means we can't
+   track its lifetime accurately.  If so, we abort the current block
+   without renaming.  */
+static bool fail_current_block;
+
+/* The id to be given to the next opened chain.  */
+static unsigned current_id;
 
+/* List of currently open chains, and closed chains that can be renamed.  */
 static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
+/* Conflict bitmaps, tracking the live chains and the live hard registers.  */
+static bitmap_head live_chains;
+static HARD_REG_SET live_hard_regs;
+
+/* Called through note_stores.  DATA points to a rtx_code, either SET or
+   CLOBBER, which tells us which kind of rtx to look at.  If we have a
+   match, record the set register in live_hard_regs.  */
+
+static void
+note_sets_clobbers (rtx x, const_rtx set, void *data)
+{
+  enum rtx_code *pcode = (enum rtx_code *)data;
+  struct du_head *chain;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  if (!REG_P (x) || GET_CODE (set) != *pcode)
+    return;
+  /* There must not be pseudos at this point.  */
+  gcc_assert (HARD_REGISTER_P (x));
+  add_to_hard_reg_set (&live_hard_regs, GET_MODE (x), REGNO (x));
+  for (chain = open_chains; chain; chain = chain->next_chain)
+    add_to_hard_reg_set (&chain->hard_conflicts, GET_MODE (x), REGNO (x));
+}
+
 static void
 scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
 	      enum op_type type)
@@ -444,18 +473,45 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	{
 	  struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
 	  struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+	  int nregs;
+
 	  head->next_chain = open_chains;
 	  open_chains = head;
 	  head->first = head->last = this_du;
 	  head->regno = this_regno;
 	  head->nregs = this_nregs;
 	  head->need_caller_save_reg = 0;
+	  head->cannot_rename = 0;
 	  head->terminated = 0;
 
+	  VEC_safe_push (du_head_p, heap, id_to_chain, head);
+	  head->id = current_id++;
+
+	  bitmap_initialize (&head->conflicts, &bitmap_default_obstack);
+	  bitmap_copy (&head->conflicts, &live_chains);
+	  mark_conflict (open_chains, head->id);
+
+	  /* Since we're tracking this as a chain now, remove it from the
+	     list of conflicting live hard registers.  */
+	  nregs = head->nregs;
+	  while (nregs-- > 0)
+	    CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+
+	  COPY_HARD_REG_SET (head->hard_conflicts, live_hard_regs);
+	  bitmap_set_bit (&live_chains, head->id);
+
+	  open_chains = head;
+
 	  this_du->next_use = 0;
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
+
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Creating chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
 	}
       return;
     }
@@ -466,82 +522,89 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
   for (p = &open_chains; *p;)
     {
       struct du_head *head = *p;
+      struct du_head *next = head->next_chain;
+      int exact_match = (head->regno == this_regno
+			 && head->nregs == this_nregs);
+      int superset = (this_regno <= head->regno
+		      && this_regno + this_nregs >= head->regno + head->nregs);
+
+      if (head->terminated
+	  || head->regno + head->nregs <= this_regno
+	  || this_regno + this_nregs <= head->regno)
+	{
+	  p = &head->next_chain;
+	  continue;
+	}
 
-      /* Check if the chain has been terminated if it has then skip to
-	 the next chain.
-
-	 This can happen when we've already appended the location to
-	 the chain in Step 3, but are trying to hide in-out operands
-	 from terminate_write in Step 5.  */
-
-      if (head->terminated)
-	p = &head->next_chain;
-      else
+      if (action == mark_read || action == mark_access)
 	{
-	  int exact_match = (head->regno == this_regno
-			     && head->nregs == this_nregs);
+	  /* ??? Class NO_REGS can happen if the md file makes use of
+	     EXTRA_CONSTRAINTS to match registers.  Which is arguably
+	     wrong, but there we are.  */
 
-	  if (head->regno + head->nregs <= this_regno
-	      || this_regno + this_nregs <= head->regno)
+	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
 	    {
-	      p = &head->next_chain;
-	      continue;
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+			 reg_names[head->regno], head->id, INSN_UID (insn),
+			 scan_actions_name[(int) action]);
+	      head->cannot_rename = 1;
 	    }
-
-	  if (action == mark_read || action == mark_access)
+	  else
 	    {
-	      gcc_assert (exact_match || DEBUG_INSN_P (insn));
-
-	      /* ??? Class NO_REGS can happen if the md file makes use of
-		 EXTRA_CONSTRAINTS to match registers.  Which is arguably
-		 wrong, but there we are.  Since we know not what this may
-		 be replaced with, terminate the chain.  */
-	      if (cl != NO_REGS)
-		{
-		  struct du_chain *this_du;
-		  this_du = XOBNEW (&rename_obstack, struct du_chain);
-		  this_du->next_use = 0;
-		  this_du->loc = loc;
-		  this_du->insn = insn;
-		  this_du->cl = cl;
-		  head->last->next_use = this_du;
-		  head->last = this_du;
-		  return;
-		}
+	      struct du_chain *this_du;
+	      this_du = XOBNEW (&rename_obstack, struct du_chain);
+	      this_du->next_use = 0;
+	      this_du->loc = loc;
+	      this_du->insn = insn;
+	      this_du->cl = cl;
+	      head->last->next_use = this_du;
+	      head->last = this_du;
 	    }
+	  /* There may be other chains that do not match exactly; ensure
+	     they all get marked unrenamable.  */
+	  p = &head->next_chain;
+	  continue;
+	}
 
-	  if (action != terminate_overlapping_read || ! exact_match)
-	    {
-	      struct du_head *next = head->next_chain;
+      /* Whether the terminated chain can be used for renaming
+	 depends on the action and this being an exact match.
+	 In either case, we remove this element from open_chains.  */
 
-	      /* Whether the terminated chain can be used for renaming
-	         depends on the action and this being an exact match.
-	         In either case, we remove this element from open_chains.  */
-
-	      head->terminated = 1;
-	      if ((action == terminate_dead || action == terminate_write)
-		  && exact_match)
-		{
-		  head->next_chain = closed_chains;
-		  closed_chains = head;
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Closing chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Discarding chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      *p = next;
-	    }
-	  else
-	    p = &head->next_chain;
+      if ((action == terminate_dead || action == terminate_write)
+	  && superset)
+	{
+	  head->terminated = 1;
+	  head->next_chain = closed_chains;
+	  closed_chains = head;
+	  bitmap_clear_bit (&live_chains, head->id);
+	  *p = next;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Closing chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	}
+      else if (action == terminate_dead || action == terminate_write)
+	{
+	  /* In this case, tracking liveness gets too hard.  Fail the
+	     entire basic block.  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Failing basic block due to unhandled overlap\n");
+	  fail_current_block = true;
+	  return;
+	}
+      else
+	{
+	  head->cannot_rename = 1;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	  p = &head->next_chain;
 	}
     }
 }
@@ -667,7 +730,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
 #ifndef AUTO_INC_DEC
       /* If the target doesn't claim to handle autoinc, this must be
 	 something special, like a stack push.  Kill this chain.  */
-      action = terminate_all_read;
+      action = mark_all_read;
 #endif
       break;
 
@@ -785,13 +848,15 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 /* Hide operands of the current insn (of which there are N_OPS) by
    substituting cc0 for them.
    Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
-   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+   If INOUT_AND_EC_ONLY is set, we only do this for OP_INOUT type operands
+   and earlyclobbers.  */
 
 static void
 hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
-	       bool inout_only)
+	       bool inout_and_ec_only)
 {
   int i;
+  int alt = which_alternative;
   for (i = 0; i < n_ops; i++)
     {
       old_operands[i] = recog_data.operand[i];
@@ -800,14 +865,16 @@ hide_operands (int n_ops, rtx *old_opera
 	 reachable by proper operands.  */
       if (recog_data.constraints[i][0] == '\0')
 	continue;
-      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[i] == OP_INOUT
+	  || recog_op_alt[i][alt].earlyclobber)
 	*recog_data.operand_loc[i] = cc0_rtx;
     }
   for (i = 0; i < recog_data.n_dups; i++)
     {
       int opn = recog_data.dup_num[i];
       old_dups[i] = *recog_data.dup_loc[i];
-      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[opn] == OP_INOUT
+	  || recog_op_alt[opn][alt].earlyclobber)
 	*recog_data.dup_loc[i] = cc0_rtx;
     }
 }
@@ -828,10 +895,11 @@ restore_operands (rtx insn, int n_ops, r
 }
 
 /* For each output operands of INSN, call scan_rtx to create a new
-   open chain.  */
+   open chain.  Do this only for normal or earlyclobber outputs,
+   depending on EARLYCLOBBER.  */
 
 static void
-record_out_operands (rtx insn)
+record_out_operands (rtx insn, bool earlyclobber)
 {
   int n_ops = recog_data.n_operands;
   int alt = which_alternative;
@@ -848,7 +916,8 @@ record_out_operands (rtx insn)
       enum reg_class cl = recog_op_alt[opn][alt].cl;
       struct du_head *prev_open = open_chains;
 
-      if (recog_data.operand_type[opn] == OP_OUT)
+      if (recog_data.operand_type[opn] == OP_OUT
+	  && recog_op_alt[opn][alt].earlyclobber == earlyclobber)
 	scan_rtx (insn, loc, cl, mark_write, OP_OUT);
 
       /* ??? Many targets have output constraints on the SET_DEST
@@ -862,7 +931,7 @@ record_out_operands (rtx insn)
 	      && REGNO (op) == ORIGINAL_REGNO (op)))
 	{
 	  if (prev_open != open_chains)
-	    open_chains->first->cl = NO_REGS;
+	    open_chains->cannot_rename = 1;
 	}
     }
 }
@@ -873,9 +942,22 @@ static struct du_head *
 build_def_use (basic_block bb)
 {
   rtx insn;
+  df_ref *def_rec;
 
   open_chains = closed_chains = NULL;
 
+  fail_current_block = false;
+
+  current_id = 0;
+  bitmap_initialize (&live_chains, &bitmap_default_obstack);
+  REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
+  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
+    {
+      df_ref def = *def_rec;
+      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+	SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
+    }
+
   for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
     {
       if (NONDEBUG_INSN_P (insn))
@@ -887,22 +969,31 @@ build_def_use (basic_block bb)
 	  int i, icode;
 	  int alt;
 	  int predicated;
+	  enum rtx_code set_code = SET;
+	  enum rtx_code clobber_code = CLOBBER;
 
 	  /* Process the insn, determining its effect on the def-use
-	     chains.  We perform the following steps with the register
-	     references in the insn:
-	     (1) Any read that overlaps an open chain, but doesn't exactly
-	         match, causes that chain to be closed.  We can't deal
-	         with overlaps yet.
+	     chains and live hard registers.  We perform the following
+	     steps with the register references in the insn, simulating
+	     its effect:
+	     (1) Deal with earlyclobber operands and CLOBBERs of non-operands
+	         by creating chains and marking hard regs live.
 	     (2) Any read outside an operand causes any chain it overlaps
-	         with to be closed, since we can't replace it.
+	         with to be marked unrenamable.
 	     (3) Any read inside an operand is added if there's already
 	         an open chain for it.
 	     (4) For any REG_DEAD note we find, close open chains that
 	         overlap it.
-	     (5) For any write we find, close open chains that overlap it.
-	     (6) For any write we find in an operand, make a new chain.
-	     (7) For any REG_UNUSED, close any chains we just opened.  */
+	     (5) For any non-earlyclobber write we find, close open chains
+	         that overlap it.
+	     (6) For any non-earlyclobber write we find in an operand, make
+	         a new chain or mark the hard register as live.
+	     (7) For any REG_UNUSED, close any chains we just opened.
+
+	     We cannot deal with situations where we track a reg in one mode
+	     and see a reference in another mode; these will cause the chain
+	     to be marked unrenamable or even cause us to abort the entire
+	     basic block.  */
 
 	  icode = recog_memoized (insn);
 	  extract_insn (insn);
@@ -924,28 +1015,41 @@ build_def_use (basic_block bb)
 		recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
 	      if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
 	          || (predicated && recog_data.operand_type[i] == OP_OUT))
-		recog_data.operand_type[i] = OP_INOUT;
+		{
+		  recog_data.operand_type[i] = OP_INOUT;
+		  if (matches >= 0
+		      && (GET_MODE_SIZE (recog_data.operand_mode[i])
+			  != GET_MODE_SIZE (recog_data.operand_mode[matches])))
+		    fail_current_block = true;
+		}
 	    }
 
-	  /* Step 1: Close chains for which we have overlapping reads.  */
-	  for (i = 0; i < n_ops; i++)
-	    scan_rtx (insn, recog_data.operand_loc[i],
-		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i]);
+	  if (fail_current_block)
+	    break;
+
+	  /* Step 1a: Mark hard registers that are clobbered in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &clobber_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 2: Close chains for which we have reads outside operands.
+	  /* Step 1b: Begin new chains for earlyclobbered writes inside
+	     operands.  */
+	  record_out_operands (insn, true);
+
+	  /* Step 2: Mark chains for which we have reads outside operands
+	     as non-renamable.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, false);
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
-		    OP_IN);
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, mark_all_read, OP_IN);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN);
+		      NO_REGS, mark_all_read, OP_IN);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -959,7 +1063,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN);
+		  scan_rtx (insn, loc, NO_REGS, mark_all_read, OP_IN);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -995,8 +1099,13 @@ build_def_use (basic_block bb)
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN);
+	      }
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -1009,16 +1118,24 @@ build_def_use (basic_block bb)
 
 	  /* Step 5: Close open chains that overlap writes.  Similar to
 	     step 2, we hide in-out operands, since we do not want to
-	     close these chains.  */
+	     close these chains.  We also hide earlyclobber operands,
+	     since we've opened chains for them earlier and they couldn't
+	     possibly overlap any input operands anyway.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, true);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6: Begin new chains for writes inside operands.  */
-	  record_out_operands (insn);
+	  /* Step 6a: Mark hard registers that are set in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &set_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
+	  /* Step 6b: Begin new chains for writes inside operands.  */
+	  record_out_operands (insn, false);
+
+	  /* Step 6c: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
@@ -1029,8 +1146,13 @@ build_def_use (basic_block bb)
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN);
+	      }
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
@@ -1042,6 +1164,11 @@ build_def_use (basic_block bb)
 	break;
     }
 
+  bitmap_clear (&live_chains);
+
+  if (fail_current_block)
+    return NULL;
+
   /* Since we close every chain when we find a REG_DEAD note, anything that
      is still open lives past the basic block, so it can't be renamed.  */
   return closed_chains;

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-24 11:55             ` Bernd Schmidt
@ 2009-11-24 12:30               ` Eric Botcazou
  2009-11-26 12:27               ` Eric Botcazou
  1 sibling, 0 replies; 23+ messages in thread
From: Eric Botcazou @ 2009-11-24 12:30 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

> Ah.  That looks a little buggy actually in the old version.  Nevermind,
> here's a new set of patches.

Thanks.  rr-cleanup2.diff is OK for mainline modulo the following nits:

+/* Undoes the substitution performed by hide_operands.  INSN is the insn we
+   are processing; the arguments are the same as in hide_operands.  */

"Undo the"


+/* For each output operands of INSN, call scan_rtx to create a new
+   open chain.  */

"operand of"


+      struct du_head *prev_open = open_chains;
+
+      if (recog_data.operand_type[opn] == OP_OUT)
+	scan_rtx (insn, loc, cl, mark_write, OP_OUT);
+
+      /* ??? Many targets have output constraints on the SET_DEST
+	 of a call insn, which is stupid, since these are certainly
+	 ABI defined hard registers.  For these, and for asm operands
+	 that originally referenced hard registers, we must record that
+	 the chain cannot be renamed.  */
+      if (CALL_P (insn)
+	  || (asm_noperands (PATTERN (insn)) > 0
+	      && REG_P (op)
+	      && REGNO (op) == ORIGINAL_REGNO (op)))
+	{
+	  if (prev_open != open_chains)
+	    open_chains->first->cl = NO_REGS;
+	}

I'd rewrite it as

      struct du_head *prev_open;

      if (recog_data.operand_type[opn] != OP_OUT)
        continue;

      prev_open = open_chains;
      scan_rtx (insn, loc, cl, mark_write, OP_OUT);

to make things clearer.


I'll look into rr-full2.diff later today.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-24 11:55             ` Bernd Schmidt
  2009-11-24 12:30               ` Eric Botcazou
@ 2009-11-26 12:27               ` Eric Botcazou
  2009-11-26 16:50                 ` Bernd Schmidt
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-26 12:27 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

[Sorry for the delay]

> Ah.  That looks a little buggy actually in the old version.  Nevermind,
> here's a new set of patches.

rr-full2.diff is OK for mainline modulo the following nits:

+  int i;
+  du_head_p ptr;
+  for (i = 0; VEC_iterate(du_head_p, id_to_chain, i, ptr); i++)
+    {
+      bitmap_clear (&ptr->conflicts);
+    }

Superfluous parentheses.


 /* For a def-use chain HEAD in basic block B, find which registers overlap
    its lifetime and set the corresponding bits in *PSET.  */
 
 static void
-merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_head *head)
+merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)

"in basic block B" needs to be removed from the comment as well.


+/* Walk all chains starting with CHAINS and record that they conflict with
+   another chains whose id is ID.  */
+static void
+mark_conflict (struct du_head *chains, unsigned id)
+{
+  while (chains)
+    {
+      bitmap_set_bit (&chains->conflicts, id);
+      chains = chains->next_chain;
+    }
 }

Missing blank line after comment.  "Another chain whose".


+/* True if we found a register with a size mismatch that means we can't
+   track its lifetime accurately.  If so, we abort the current block
+   without renaming.  */
+static bool fail_current_block;

"size mismatch, which means that we can't"


+/* Conflict bitmaps, tracking the live chains and the live hard registers.  
*/
+static bitmap_head live_chains;
+static HARD_REG_SET live_hard_regs;

Rename "live_chains" into "open_chains_id" and make it clear in the comment 
that it's equivalent to open_chains.


+/* Called through note_stores.  DATA points to a rtx_code, either SET or
+   CLOBBER, which tells us which kind of rtx to look at.  If we have a
+   match, record the set register in live_hard_regs.  */

"... and in the hard_conflicts bitmap of the open chains".


+  enum rtx_code *pcode = (enum rtx_code *)data;
+  struct du_head *chain;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  if (!REG_P (x) || GET_CODE (set) != *pcode)
+    return;

   enum rtx_code pcode = *(enum rtx_code *)data;
   struct du_head *chain;

   if (GET_CODE (x) == SUBREG)
     x = SUBREG_REG (x);
   if (!REG_P (x) || GET_CODE (set) != pcode)
     return;

saves one '*'. :-)


+	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
 	    {
-	      p = &head->next_chain;
-	      continue;
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+			 reg_names[head->regno], head->id, INSN_UID (insn),
+			 scan_actions_name[(int) action]);
+	      head->cannot_rename = 1;
 	    }

"non-renamable" doesn't sound too English.  What about

	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
	    {
	      if (dump_file)
		fprintf (dump_file,
			 "Cannot rename chain %s (%d) at insn %d (%s)\n",
			 reg_names[head->regno], head->id, INSN_UID (insn),
			 scan_actions_name[(int) action]);
	      head->cannot_rename = 1;
	    }

?  This would fix the long line in the process.

Same for

+      else
+	{
+	  head->cannot_rename = 1;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Marking chain %s (%d) at insn %d (%s) as non-renamable\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	  p = &head->next_chain;
 	}


+	  /* Step 2: Mark chains for which we have reads outside operands
+	     as non-renamable.

All the other comments use "unrenamable" so I'd use it there too for the sake 
of consistency.  And ChangeLog has "unrenameable".


 	  /* Step 5: Close open chains that overlap writes.  Similar to
 	     step 2, we hide in-out operands, since we do not want to
-	     close these chains.  */
+	     close these chains.  We also hide earlyclobber operands,
+	     since we've opened chains for them earlier and they couldn't
+	     possibly overlap any input operands anyway.  */

"We also hide earlyclobber operands, since we've opened chains for them in 
step 1 and earlier chains they would overlap with must have been closed at 
the previous insn at the latest, as such operands cannot possibly overlap
with any input operands".


Please commit the 2 patches separately under PR rtl-opt/38582.  TIA.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-26 12:27               ` Eric Botcazou
@ 2009-11-26 16:50                 ` Bernd Schmidt
  2009-11-26 17:46                   ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-26 16:50 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

Eric Botcazou wrote:
> Rename "live_chains" into "open_chains_id" and make it clear in the comment 
> that it's equivalent to open_chains.

I'm making all the changes except this one unless you insist.  Giving a
bitmap a name that ends in "id" is confusing IMO - that sounds like a
number.  I'll add the requested comment that its contents match open_chains.

I'm also adding a small fix for DEBUG_INSNS with multiword hardregs
which showed up in a checking-enabled bootstrap.  We were adding the
same rtl location to a chain multiple times and aborting later when we
made a replacement of the first one with (clobber (const_int 0)), and
tried to take REGNO of that when encountering the second du_chain with
that location.

Retesting currently.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-26 16:50                 ` Bernd Schmidt
@ 2009-11-26 17:46                   ` Eric Botcazou
  2009-11-26 23:19                     ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-26 17:46 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches

> I'm making all the changes except this one unless you insist.  Giving a
> bitmap a name that ends in "id" is confusing IMO - that sounds like a
> number.

Fair enough.  But I think that you should replace "live" with "open" in the 
name to make it immediate that it's the same info under a different form, and 
of course add some prefix or suffix to distinguish them.

> I'm also adding a small fix for DEBUG_INSNS with multiword hardregs
> which showed up in a checking-enabled bootstrap.  We were adding the
> same rtl location to a chain multiple times and aborting later when we
> made a replacement of the first one with (clobber (const_int 0)), and
> tried to take REGNO of that when encountering the second du_chain with
> that location.

Ah, sure, DEBUG_INSNs. :-)

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-26 17:46                   ` Eric Botcazou
@ 2009-11-26 23:19                     ` Bernd Schmidt
  0 siblings, 0 replies; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-26 23:19 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1081 bytes --]

Eric Botcazou wrote:
>> I'm making all the changes except this one unless you insist.  Giving a
>> bitmap a name that ends in "id" is confusing IMO - that sounds like a
>> number.
> 
> Fair enough.  But I think that you should replace "live" with "open" in the 
> name to make it immediate that it's the same info under a different form, and 
> of course add some prefix or suffix to distinguish them.

Here's what I committed after testing (with flag_rename_registers forced
on).  I get
FAIL: gcc.target/i386/vperm-v4sf-1.c (test for excess errors)
FAIL: gcc.target/i386/vperm-v4si-2x.c (test for excess errors)

which appears unrelated and also shows up on gcc-testresults.

One more thing I added to the patch is an initialization of best_nregs
because I remembered that I'd seen compiler warnings for this.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-cleanup3.diff --]
[-- Type: text/plain, Size: 14308 bytes --]

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 154686)
+++ ChangeLog	(working copy)
@@ -1,3 +1,14 @@
+2009-11-26  Bernd Schmidt  <bernd.schmidt@analog.com>
+
+	PR rtl-opt/38582
+	* regrename.c (struct du_chain): Remove member earlyclobber.
+	(scan_rtx_reg): Don't set it.  Remove argument earlyclobber,
+	all callers changed.
+	(scan_rtx): Remove argument earlyclobber, all callers changed.
+	(hide_operands, restore_operands, record_out_operands): New functions,
+	broken out of build_def_use.
+	(build_def_use): Call them as necessary.
+
 2009-11-26  Richard Guenther  <rguenther@suse.de>
 
 	* tree-ssa-dce.c (nr_walks): New variable.
Index: regrename.c
===================================================================
--- regrename.c	(revision 154686)
+++ regrename.c	(working copy)
@@ -68,8 +68,6 @@ struct du_chain
   rtx *loc;
   /* The register class required by the insn at this location.  */
   ENUM_BITFIELD(reg_class) cl : 16;
-  /* Nonzero if the register is subject to earlyclobber.  */
-  unsigned int earlyclobber:1;
 };
 
 enum scan_actions
@@ -101,11 +99,11 @@ static struct obstack rename_obstack;
 
 static void do_replace (struct du_head *, int);
 static void scan_rtx_reg (rtx, rtx *, enum reg_class,
-			  enum scan_actions, enum op_type, int);
+			  enum scan_actions, enum op_type);
 static void scan_rtx_address (rtx, rtx *, enum reg_class,
 			      enum scan_actions, enum machine_mode);
 static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
-		      enum op_type, int);
+		      enum op_type);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
 static void note_sets (rtx, const_rtx, void *);
@@ -431,8 +429,8 @@ static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
 static void
-scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
-	      enum scan_actions action, enum op_type type, int earlyclobber)
+scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+	      enum op_type type)
 {
   struct du_head **p;
   rtx x = *loc;
@@ -458,7 +456,6 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
-	  this_du->earlyclobber = earlyclobber;
 	}
       return;
     }
@@ -681,7 +678,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
       return;
 
     case REG:
-      scan_rtx_reg (insn, loc, cl, action, OP_IN, 0);
+      scan_rtx_reg (insn, loc, cl, action, OP_IN);
       return;
 
     default:
@@ -700,8 +697,8 @@ scan_rtx_address (rtx insn, rtx *loc, en
 }
 
 static void
-scan_rtx (rtx insn, rtx *loc, enum reg_class cl,
-	  enum scan_actions action, enum op_type type, int earlyclobber)
+scan_rtx (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
+	  enum op_type type)
 {
   const char *fmt;
   rtx x = *loc;
@@ -723,7 +720,7 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
       return;
 
     case REG:
-      scan_rtx_reg (insn, loc, cl, action, type, earlyclobber);
+      scan_rtx_reg (insn, loc, cl, action, type);
       return;
 
     case MEM:
@@ -733,21 +730,21 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
       return;
 
     case SET:
-      scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN, 0);
+      scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN);
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
       return;
 
     case STRICT_LOW_PART:
-      scan_rtx (insn, &XEXP (x, 0), cl, action, OP_INOUT, earlyclobber);
+      scan_rtx (insn, &XEXP (x, 0), cl, action, OP_INOUT);
       return;
 
     case ZERO_EXTRACT:
     case SIGN_EXTRACT:
       scan_rtx (insn, &XEXP (x, 0), cl, action,
-		type == OP_IN ? OP_IN : OP_INOUT, earlyclobber);
-      scan_rtx (insn, &XEXP (x, 1), cl, action, OP_IN, 0);
-      scan_rtx (insn, &XEXP (x, 2), cl, action, OP_IN, 0);
+		type == OP_IN ? OP_IN : OP_INOUT);
+      scan_rtx (insn, &XEXP (x, 1), cl, action, OP_IN);
+      scan_rtx (insn, &XEXP (x, 2), cl, action, OP_IN);
       return;
 
     case POST_INC:
@@ -761,13 +758,13 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 
     case CLOBBER:
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
       return;
 
     case EXPR_LIST:
-      scan_rtx (insn, &XEXP (x, 0), cl, action, type, 0);
+      scan_rtx (insn, &XEXP (x, 0), cl, action, type);
       if (XEXP (x, 1))
-	scan_rtx (insn, &XEXP (x, 1), cl, action, type, 0);
+	scan_rtx (insn, &XEXP (x, 1), cl, action, type);
       return;
 
     default:
@@ -778,10 +775,99 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-	scan_rtx (insn, &XEXP (x, i), cl, action, type, 0);
+	scan_rtx (insn, &XEXP (x, i), cl, action, type);
       else if (fmt[i] == 'E')
 	for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-	  scan_rtx (insn, &XVECEXP (x, i, j), cl, action, type, 0);
+	  scan_rtx (insn, &XVECEXP (x, i, j), cl, action, type);
+    }
+}
+
+/* Hide operands of the current insn (of which there are N_OPS) by
+   substituting cc0 for them.
+   Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
+   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+
+static void
+hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
+	       bool inout_only)
+{
+  int i;
+  for (i = 0; i < n_ops; i++)
+    {
+      old_operands[i] = recog_data.operand[i];
+      /* Don't squash match_operator or match_parallel here, since
+	 we don't know that all of the contained registers are
+	 reachable by proper operands.  */
+      if (recog_data.constraints[i][0] == '\0')
+	continue;
+      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+	*recog_data.operand_loc[i] = cc0_rtx;
+    }
+  for (i = 0; i < recog_data.n_dups; i++)
+    {
+      int opn = recog_data.dup_num[i];
+      old_dups[i] = *recog_data.dup_loc[i];
+      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+	*recog_data.dup_loc[i] = cc0_rtx;
+    }
+}
+
+/* Undo the substitution performed by hide_operands.  INSN is the insn we
+   are processing; the arguments are the same as in hide_operands.  */
+
+static void
+restore_operands (rtx insn, int n_ops, rtx *old_operands, rtx *old_dups)
+{
+  int i;
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i] = old_dups[i];
+  for (i = 0; i < n_ops; i++)
+    *recog_data.operand_loc[i] = old_operands[i];
+  if (recog_data.n_dups)
+    df_insn_rescan (insn);
+}
+
+/* For each output operand of INSN, call scan_rtx to create a new
+   open chain.  */
+
+static void
+record_out_operands (rtx insn)
+{
+  int n_ops = recog_data.n_operands;
+  int alt = which_alternative;
+
+  int i;
+
+  for (i = 0; i < n_ops + recog_data.n_dups; i++)
+    {
+      int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+      rtx *loc = (i < n_ops
+		  ? recog_data.operand_loc[opn]
+		  : recog_data.dup_loc[i - n_ops]);
+      rtx op = *loc;
+      enum reg_class cl = recog_op_alt[opn][alt].cl;
+
+      struct du_head *prev_open;
+
+      if (recog_data.operand_type[opn] != OP_OUT)
+	continue;
+
+      prev_open = open_chains;
+      scan_rtx (insn, loc, cl, mark_write, OP_OUT);
+
+      /* ??? Many targets have output constraints on the SET_DEST
+	 of a call insn, which is stupid, since these are certainly
+	 ABI defined hard registers.  For these, and for asm operands
+	 that originally referenced hard registers, we must record that
+	 the chain cannot be renamed.  */
+      if (CALL_P (insn)
+	  || (asm_noperands (PATTERN (insn)) > 0
+	      && REG_P (op)
+	      && REGNO (op) == ORIGINAL_REGNO (op)))
+	{
+	  if (prev_open != open_chains)
+	    open_chains->first->cl = NO_REGS;
+	}
     }
 }
 
@@ -849,42 +935,21 @@ build_def_use (basic_block bb)
 	  for (i = 0; i < n_ops; i++)
 	    scan_rtx (insn, recog_data.operand_loc[i],
 		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i], 0);
+		      recog_data.operand_type[i]);
 
 	  /* Step 2: Close chains for which we have reads outside operands.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      /* Don't squash match_operator or match_parallel here, since
-		 we don't know that all of the contained registers are
-		 reachable by proper operands.  */
-	      if (recog_data.constraints[i][0] == '\0')
-		continue;
-	      *recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      *recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
+	  hide_operands (n_ops, old_operands, old_dups, false);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
-		    OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
-	  if (recog_data.n_dups)
-	    df_insn_rescan (insn);
+		    OP_IN);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN, 0);
+		      NO_REGS, terminate_all_read, OP_IN);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -898,7 +963,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -920,7 +985,7 @@ build_def_use (basic_block bb)
 	      if (recog_op_alt[opn][alt].is_address)
 		scan_rtx_address (insn, loc, cl, mark_read, VOIDmode);
 	      else
-		scan_rtx (insn, loc, cl, mark_read, type, 0);
+		scan_rtx (insn, loc, cl, mark_read, type);
 	    }
 
 	  /* Step 3B: Record updates for regs in REG_INC notes, and
@@ -929,13 +994,13 @@ build_def_use (basic_block bb)
 	    if (REG_NOTE_KIND (note) == REG_INC
 		|| REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
 	      scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_read,
-			OP_INOUT, 0);
+			OP_INOUT);
 
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
 	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+			OP_IN);
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -950,83 +1015,32 @@ build_def_use (basic_block bb)
 	     step 2, we hide in-out operands, since we do not want to
 	     close these chains.  */
 
-	  for (i = 0; i < n_ops; i++)
-	    {
-	      old_operands[i] = recog_data.operand[i];
-	      if (recog_data.operand_type[i] == OP_INOUT)
-		*recog_data.operand_loc[i] = cc0_rtx;
-	    }
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    {
-	      int opn = recog_data.dup_num[i];
-	      old_dups[i] = *recog_data.dup_loc[i];
-	      if (recog_data.operand_type[opn] == OP_INOUT)
-		*recog_data.dup_loc[i] = cc0_rtx;
-	    }
-
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
-
-	  for (i = 0; i < recog_data.n_dups; i++)
-	    *recog_data.dup_loc[i] = old_dups[i];
-	  for (i = 0; i < n_ops; i++)
-	    *recog_data.operand_loc[i] = old_operands[i];
+	  hide_operands (n_ops, old_operands, old_dups, true);
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 6: Begin new chains for writes inside operands.  */
-	  /* ??? Many targets have output constraints on the SET_DEST
-	     of a call insn, which is stupid, since these are certainly
-	     ABI defined hard registers.  Don't change calls at all.
-	     Similarly take special care for asm statement that originally
-	     referenced hard registers.  */
-	  if (asm_noperands (PATTERN (insn)) > 0)
-	    {
-	      for (i = 0; i < n_ops; i++)
-		if (recog_data.operand_type[i] == OP_OUT)
-		  {
-		    rtx *loc = recog_data.operand_loc[i];
-		    rtx op = *loc;
-		    enum reg_class cl = recog_op_alt[i][alt].cl;
-
-		    if (REG_P (op)
-			&& REGNO (op) == ORIGINAL_REGNO (op))
-		      continue;
-
-		    scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			      recog_op_alt[i][alt].earlyclobber);
-		  }
-	    }
-	  else if (!CALL_P (insn))
-	    for (i = 0; i < n_ops + recog_data.n_dups; i++)
-	      {
-		int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
-		rtx *loc = (i < n_ops
-			    ? recog_data.operand_loc[opn]
-			    : recog_data.dup_loc[i - n_ops]);
-		enum reg_class cl = recog_op_alt[opn][alt].cl;
-
-		if (recog_data.operand_type[opn] == OP_OUT)
-		  scan_rtx (insn, loc, cl, mark_write, OP_OUT,
-			    recog_op_alt[opn][alt].earlyclobber);
-	      }
+	  record_out_operands (insn);
 
 	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
 	      scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_access,
-			OP_INOUT, 0);
+			OP_INOUT);
 
 	  /* Step 7: Close chains for registers that were never
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
 	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN, 0);
+			OP_IN);
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
 	{
 	  scan_rtx (insn, &INSN_VAR_LOCATION_LOC (insn),
-		    ALL_REGS, mark_read, OP_IN, 0);
+		    ALL_REGS, mark_read, OP_IN);
 	}
       if (insn == BB_END (bb))
 	break;

[-- Attachment #3: rr-full3.diff --]
[-- Type: text/plain, Size: 29945 bytes --]

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 154687)
+++ ChangeLog	(working copy)
@@ -9,6 +9,38 @@
 	broken out of build_def_use.
 	(build_def_use): Call them as necessary.
 
+	* regrename.c (struct du_head): New members id, conflicts,
+	hard_conflicts and cannot_rename.
+	(enum scan_actions): Remove terminate_all_read and
+	terminate_overlapping_read; add mark_all_read.
+	(scan_actions_name): Likewise.
+	(du_head_p): New typedef.  Define a vector type for it.
+	(id_to_chain): New static variable.
+	(note_sets, clear_dead_regs): Delete functions.
+	(free_chain_data): New function.
+	(merge_overlapping_regs): Simply walk the conflicts bitmap.
+	Remove argument B, all callers changed.
+	(regrename_optimize): Allocate id_to_chain.  Ignore chains that have
+	the cannot_rename bit set.  Update regno and nregs of a renamed chain.
+	Call free_chain_data when done.
+	(do_replace): Remove death notes when the renamed reg is set in the
+	last insn; add them if not.
+	(mark_conflict, note_sets_clobbers): New static function.
+	(fail_current_block, current_id, open_chains_set, live_hard_regs): New
+	static variables.
+	(scan_rtx_reg): Keep track of conflicts between chains, and between
+	chains and hard regs.  Don't terminate chains when we find a read we
+	can't handle, mark it unrenameable instead.  For terminate_write,
+	terminate chains that are written with an exact match or a superset
+	of registers.  Set fail_current_block if multi-word lifetimes are too
+	complex to handle.
+	(scan_rtx_address): Use mark_all_read instead of terminate_all_read.
+	(build_def_use): Initialize current_id, live_chains and live_hard_regs;
+	free memory for them when done.
+	Rearrange the steps so that earlyclobbers are noted before reads
+	are processed.  Add new steps to keep track of hard register lifetimes
+	outside insn operands.
+
 2009-11-26  Richard Guenther  <rguenther@suse.de>
 
 	* tree-ssa-dce.c (nr_walks): New variable.
Index: regrename.c
===================================================================
--- regrename.c	(revision 154687)
+++ regrename.c	(working copy)
@@ -50,10 +50,22 @@ struct du_head
   struct du_chain *first, *last;
   /* Describes the register being tracked.  */
   unsigned regno, nregs;
+
+  /* A unique id to be used as an index into the conflicts bitmaps.  */
+  unsigned id;
+  /* A bitmap to record conflicts with other chains.  */
+  bitmap_head conflicts;
+  /* Conflicts with untracked hard registers.  */
+  HARD_REG_SET hard_conflicts;
+
+  /* Nonzero if the chain is finished; zero if it is still open.  */
+  unsigned int terminated:1;
   /* Nonzero if the chain crosses a call.  */
   unsigned int need_caller_save_reg:1;
-  /* Nonzero if the chain is finished.  */
-  unsigned int terminated:1;
+  /* Nonzero if the register is used in a way that prevents renaming,
+     such as the SET_DEST of a CALL_INSN or an asm operand that used
+     to be a hard register.  */
+  unsigned int cannot_rename:1;
 };
 
 /* This struct describes a single occurrence of a register.  */
@@ -72,10 +84,9 @@ struct du_chain
 
 enum scan_actions
 {
-  terminate_all_read,
-  terminate_overlapping_read,
   terminate_write,
   terminate_dead,
+  mark_all_read,
   mark_read,
   mark_write,
   /* mark_access is for marking the destination regs in
@@ -86,10 +97,9 @@ enum scan_actions
 
 static const char * const scan_actions_name[] =
 {
-  "terminate_all_read",
-  "terminate_overlapping_read",
   "terminate_write",
   "terminate_dead",
+  "mark_all_read",
   "mark_read",
   "mark_write",
   "mark_access"
@@ -106,95 +116,38 @@ static void scan_rtx (rtx, rtx *, enum r
 		      enum op_type);
 static struct du_head *build_def_use (basic_block);
 static void dump_def_use_chain (struct du_head *);
-static void note_sets (rtx, const_rtx, void *);
-static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
-static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
-				    struct du_head *);
 
-/* Called through note_stores.  Find sets of registers, and
-   record them in *DATA (which is actually a HARD_REG_SET *).  */
+typedef struct du_head *du_head_p;
+DEF_VEC_P (du_head_p);
+DEF_VEC_ALLOC_P (du_head_p, heap);
+static VEC(du_head_p, heap) *id_to_chain;
 
 static void
-note_sets (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data)
+free_chain_data (void)
 {
-  HARD_REG_SET *pset = (HARD_REG_SET *) data;
-
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!REG_P (x))
-    return;
-  /* There must not be pseudos at this point.  */
-  gcc_assert (HARD_REGISTER_P (x));
-  add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
-}
-
-/* Clear all registers from *PSET for which a note of kind KIND can be found
-   in the list NOTES.  */
+  int i;
+  du_head_p ptr;
+  for (i = 0; VEC_iterate(du_head_p, id_to_chain, i, ptr); i++)
+    bitmap_clear (&ptr->conflicts);
 
-static void
-clear_dead_regs (HARD_REG_SET *pset, enum reg_note kind, rtx notes)
-{
-  rtx note;
-  for (note = notes; note; note = XEXP (note, 1))
-    if (REG_NOTE_KIND (note) == kind && REG_P (XEXP (note, 0)))
-      {
-	rtx reg = XEXP (note, 0);
-	/* There must not be pseudos at this point.  */
-	gcc_assert (HARD_REGISTER_P (reg));
-	remove_from_hard_reg_set (pset, GET_MODE (reg), REGNO (reg));
-      }
+  VEC_free (du_head_p, heap, id_to_chain);
 }
 
-/* For a def-use chain HEAD in basic block B, find which registers overlap
-   its lifetime and set the corresponding bits in *PSET.  */
+/* For a def-use chain HEAD, find which registers overlap its lifetime and
+   set the corresponding bits in *PSET.  */
 
 static void
-merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
-			struct du_head *head)
+merge_overlapping_regs (HARD_REG_SET *pset, struct du_head *head)
 {
-  struct du_chain *t;
-  rtx insn;
-  HARD_REG_SET live;
-  df_ref *def_rec;
-
-  REG_SET_TO_HARD_REG_SET (live, df_get_live_in (b));
-  for (def_rec = df_get_artificial_defs (b->index); *def_rec; def_rec++)
-    {
-      df_ref def = *def_rec;
-      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
-	SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
-    }
-
-  t = head->first;
-  insn = BB_HEAD (b);
-  while (t)
+  bitmap_iterator bi;
+  unsigned i;
+  IOR_HARD_REG_SET (*pset, head->hard_conflicts);
+  EXECUTE_IF_SET_IN_BITMAP (&head->conflicts, 0, i, bi)
     {
-      /* Search forward until the next reference to the register to be
-	 renamed.  */
-      while (insn != t->insn)
-	{
-	  if (INSN_P (insn))
-	    {
-	      clear_dead_regs (&live, REG_DEAD, REG_NOTES (insn));
-	      note_stores (PATTERN (insn), note_sets, (void *) &live);
-	      /* Only record currently live regs if we are inside the
-		 reg's live range.  */
-	      if (t != head->first)
-		IOR_HARD_REG_SET (*pset, live);
-	      clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
-	    }
-	  insn = NEXT_INSN (insn);
-	}
-
-      IOR_HARD_REG_SET (*pset, live);
-
-      /* For the last reference, also merge in all registers set in the
-	 same insn.
-	 @@@ We only have take earlyclobbered sets into account.  */
-      if (! t->next_use)
-	note_stores (PATTERN (insn), note_sets, (void *) pset);
-
-      t = t->next_use;
+      du_head_p other = VEC_index (du_head_p, id_to_chain, i);
+      unsigned j = other->nregs;
+      while (j-- > 0)
+	SET_HARD_REG_BIT (*pset, other->regno + j);
     }
 }
 
@@ -224,6 +177,8 @@ regrename_optimize (void)
       HARD_REG_SET unavailable;
       HARD_REG_SET regs_seen;
 
+      id_to_chain = VEC_alloc (du_head_p, heap, 0);
+
       CLEAR_HARD_REG_SET (unavailable);
 
       if (dump_file)
@@ -247,7 +202,7 @@ regrename_optimize (void)
       CLEAR_HARD_REG_SET (regs_seen);
       while (all_chains)
 	{
-	  int new_reg, best_new_reg;
+	  int new_reg, best_new_reg, best_nregs;
 	  int n_uses;
 	  struct du_head *this_head = all_chains;
 	  struct du_chain *tmp;
@@ -257,7 +212,11 @@ regrename_optimize (void)
 
 	  all_chains = this_head->next_chain;
 
+	  if (this_head->cannot_rename)
+	    continue;
+
 	  best_new_reg = reg;
+	  best_nregs = this_head->nregs;
 
 #if 0 /* This just disables optimization opportunities.  */
 	  /* Only rename once we've seen the reg more than once.  */
@@ -297,7 +256,7 @@ regrename_optimize (void)
 	  if (this_head->need_caller_save_reg)
 	    IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
 
-	  merge_overlapping_regs (bb, &this_unavailable, this_head);
+	  merge_overlapping_regs (&this_unavailable, this_head);
 
 	  /* Now potential_regs is a reasonable approximation, let's
 	     have a closer look at each register still in there.  */
@@ -341,7 +300,10 @@ regrename_optimize (void)
 	      if (! tmp)
 		{
 		  if (tick[best_new_reg] > tick[new_reg])
-		    best_new_reg = new_reg;
+		    {
+		      best_new_reg = new_reg;
+		      best_nregs = nregs;
+		    }
 		}
 	    }
 
@@ -365,10 +327,13 @@ regrename_optimize (void)
 	    fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
 
 	  do_replace (this_head, best_new_reg);
+	  this_head->regno = best_new_reg;
+	  this_head->nregs = best_nregs;
 	  tick[best_new_reg] = ++this_tick;
 	  df_set_regs_ever_live (best_new_reg, true);
 	}
 
+      free_chain_data ();
       obstack_free (&rename_obstack, first_obj);
     }
 
@@ -385,6 +350,7 @@ do_replace (struct du_head *head, int re
 {
   struct du_chain *chain;
   unsigned int base_regno = head->regno;
+  bool found_note = false;
 
   gcc_assert (! DEBUG_INSN_P (head->first->insn));
 
@@ -408,26 +374,92 @@ do_replace (struct du_head *head, int re
 
 	  for (note = REG_NOTES (chain->insn); note; note = XEXP (note, 1))
 	    {
-	      if (REG_NOTE_KIND (note) == REG_DEAD
-		  || REG_NOTE_KIND (note) == REG_UNUSED)
+	      enum reg_note kind = REG_NOTE_KIND (note);
+	      if (kind == REG_DEAD || kind == REG_UNUSED)
 		{
 		  rtx reg = XEXP (note, 0);
 		  gcc_assert (HARD_REGISTER_P (reg));
 
 		  if (REGNO (reg) == base_regno)
-		    XEXP (note, 0) = *chain->loc;
+		    {
+		      found_note = true;
+		      if (kind == REG_DEAD
+			  && reg_set_p (*chain->loc, chain->insn))
+			remove_note (chain->insn, note);
+		      else
+			XEXP (note, 0) = *chain->loc;
+		      break;
+		    }
 		}
 	    }
 	}
 
       df_insn_rescan (chain->insn);
     }
+  if (!found_note)
+    {
+      /* If the chain's first insn is the same as the last, we should have
+	 found a REG_UNUSED note.  */
+      gcc_assert (head->first->insn != head->last->insn);
+      if (!reg_set_p (*head->last->loc, head->last->insn))
+	add_reg_note (head->last->insn, REG_DEAD, *head->last->loc);
+    }
 }
 
 
+/* Walk all chains starting with CHAINS and record that they conflict with
+   another chain whose id is ID.  */
+
+static void
+mark_conflict (struct du_head *chains, unsigned id)
+{
+  while (chains)
+    {
+      bitmap_set_bit (&chains->conflicts, id);
+      chains = chains->next_chain;
+    }
+}
+
+/* True if we found a register with a size mismatch, which means that we
+   can't track its lifetime accurately.  If so, we abort the current block
+   without renaming.  */
+static bool fail_current_block;
+
+/* The id to be given to the next opened chain.  */
+static unsigned current_id;
+
+/* List of currently open chains, and closed chains that can be renamed.  */
 static struct du_head *open_chains;
 static struct du_head *closed_chains;
 
+/* Conflict bitmaps, tracking the live chains and the live hard registers.
+   The bits set in open_chains_set always match the list found in
+   open_chains.  */
+static bitmap_head open_chains_set;
+static HARD_REG_SET live_hard_regs;
+
+/* Called through note_stores.  DATA points to a rtx_code, either SET or
+   CLOBBER, which tells us which kind of rtx to look at.  If we have a
+   match, record the set register in live_hard_regs and in the hard_conflicts
+   bitmap of open chains.  */
+
+static void
+note_sets_clobbers (rtx x, const_rtx set, void *data)
+{
+  enum rtx_code code = *(enum rtx_code *)data;
+  struct du_head *chain;
+
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+  if (!REG_P (x) || GET_CODE (set) != code)
+    return;
+  /* There must not be pseudos at this point.  */
+  gcc_assert (HARD_REGISTER_P (x));
+  add_to_hard_reg_set (&live_hard_regs, GET_MODE (x), REGNO (x));
+  for (chain = open_chains; chain; chain = chain->next_chain)
+    add_to_hard_reg_set (&chain->hard_conflicts, GET_MODE (x), REGNO (x));
+}
+
 static void
 scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl, enum scan_actions action,
 	      enum op_type type)
@@ -444,18 +476,45 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	{
 	  struct du_head *head = XOBNEW (&rename_obstack, struct du_head);
 	  struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+	  int nregs;
+
 	  head->next_chain = open_chains;
 	  open_chains = head;
 	  head->first = head->last = this_du;
 	  head->regno = this_regno;
 	  head->nregs = this_nregs;
 	  head->need_caller_save_reg = 0;
+	  head->cannot_rename = 0;
 	  head->terminated = 0;
 
+	  VEC_safe_push (du_head_p, heap, id_to_chain, head);
+	  head->id = current_id++;
+
+	  bitmap_initialize (&head->conflicts, &bitmap_default_obstack);
+	  bitmap_copy (&head->conflicts, &open_chains_set);
+	  mark_conflict (open_chains, head->id);
+
+	  /* Since we're tracking this as a chain now, remove it from the
+	     list of conflicting live hard registers.  */
+	  nregs = head->nregs;
+	  while (nregs-- > 0)
+	    CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+
+	  COPY_HARD_REG_SET (head->hard_conflicts, live_hard_regs);
+	  bitmap_set_bit (&open_chains_set, head->id);
+
+	  open_chains = head;
+
 	  this_du->next_use = 0;
 	  this_du->loc = loc;
 	  this_du->insn = insn;
 	  this_du->cl = cl;
+
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Creating chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
 	}
       return;
     }
@@ -466,82 +525,94 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
   for (p = &open_chains; *p;)
     {
       struct du_head *head = *p;
+      struct du_head *next = head->next_chain;
+      int exact_match = (head->regno == this_regno
+			 && head->nregs == this_nregs);
+      int superset = (this_regno <= head->regno
+		      && this_regno + this_nregs >= head->regno + head->nregs);
 
-      /* Check if the chain has been terminated if it has then skip to
-	 the next chain.
-
-	 This can happen when we've already appended the location to
-	 the chain in Step 3, but are trying to hide in-out operands
-	 from terminate_write in Step 5.  */
+      if (head->terminated
+	  || head->regno + head->nregs <= this_regno
+	  || this_regno + this_nregs <= head->regno)
+	{
+	  p = &head->next_chain;
+	  continue;
+	}
 
-      if (head->terminated)
-	p = &head->next_chain;
-      else
+      if (action == mark_read || action == mark_access)
 	{
-	  int exact_match = (head->regno == this_regno
-			     && head->nregs == this_nregs);
+	  /* ??? Class NO_REGS can happen if the md file makes use of
+	     EXTRA_CONSTRAINTS to match registers.  Which is arguably
+	     wrong, but there we are.  */
 
-	  if (head->regno + head->nregs <= this_regno
-	      || this_regno + this_nregs <= head->regno)
+	  if (cl == NO_REGS || (!exact_match && !DEBUG_INSN_P (insn)))
 	    {
-	      p = &head->next_chain;
-	      continue;
+	      if (dump_file)
+		fprintf (dump_file,
+			 "Cannot rename chain %s (%d) at insn %d (%s)\n",
+			 reg_names[head->regno], head->id, INSN_UID (insn),
+			 scan_actions_name[(int) action]);
+	      head->cannot_rename = 1;
 	    }
-
-	  if (action == mark_read || action == mark_access)
+	  else
 	    {
-	      gcc_assert (exact_match || DEBUG_INSN_P (insn));
+	      struct du_chain *this_du;
+	      this_du = XOBNEW (&rename_obstack, struct du_chain);
+	      this_du->next_use = 0;
+	      this_du->loc = loc;
+	      this_du->insn = insn;
+	      this_du->cl = cl;
+	      head->last->next_use = this_du;
+	      head->last = this_du;
 
-	      /* ??? Class NO_REGS can happen if the md file makes use of
-		 EXTRA_CONSTRAINTS to match registers.  Which is arguably
-		 wrong, but there we are.  Since we know not what this may
-		 be replaced with, terminate the chain.  */
-	      if (cl != NO_REGS)
-		{
-		  struct du_chain *this_du;
-		  this_du = XOBNEW (&rename_obstack, struct du_chain);
-		  this_du->next_use = 0;
-		  this_du->loc = loc;
-		  this_du->insn = insn;
-		  this_du->cl = cl;
-		  head->last->next_use = this_du;
-		  head->last = this_du;
-		  return;
-		}
 	    }
+	  /* Avoid adding the same location in a DEBUG_INSN multiple times,
+	     which could happen with non-exact overlap.  */
+	  if (DEBUG_INSN_P (insn))
+	    return;
+	  /* Otherwise, find any other chains that do not match exactly;
+	     ensure they all get marked unrenamable.  */
+	  p = &head->next_chain;
+	  continue;
+	}
 
-	  if (action != terminate_overlapping_read || ! exact_match)
-	    {
-	      struct du_head *next = head->next_chain;
-
-	      /* Whether the terminated chain can be used for renaming
-	         depends on the action and this being an exact match.
-	         In either case, we remove this element from open_chains.  */
+      /* Whether the terminated chain can be used for renaming
+	 depends on the action and this being an exact match.
+	 In either case, we remove this element from open_chains.  */
 
-	      head->terminated = 1;
-	      if ((action == terminate_dead || action == terminate_write)
-		  && exact_match)
-		{
-		  head->next_chain = closed_chains;
-		  closed_chains = head;
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Closing chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file,
-			     "Discarding chain %s at insn %d (%s)\n",
-			     reg_names[head->regno], INSN_UID (insn),
-			     scan_actions_name[(int) action]);
-		}
-	      *p = next;
-	    }
-	  else
-	    p = &head->next_chain;
+      if ((action == terminate_dead || action == terminate_write)
+	  && superset)
+	{
+	  head->terminated = 1;
+	  head->next_chain = closed_chains;
+	  closed_chains = head;
+	  bitmap_clear_bit (&open_chains_set, head->id);
+	  *p = next;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Closing chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	}
+      else if (action == terminate_dead || action == terminate_write)
+	{
+	  /* In this case, tracking liveness gets too hard.  Fail the
+	     entire basic block.  */
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Failing basic block due to unhandled overlap\n");
+	  fail_current_block = true;
+	  return;
+	}
+      else
+	{
+	  head->cannot_rename = 1;
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "Cannot rename chain %s (%d) at insn %d (%s)\n",
+		     reg_names[head->regno], head->id, INSN_UID (insn),
+		     scan_actions_name[(int) action]);
+	  p = &head->next_chain;
 	}
     }
 }
@@ -667,7 +738,7 @@ scan_rtx_address (rtx insn, rtx *loc, en
 #ifndef AUTO_INC_DEC
       /* If the target doesn't claim to handle autoinc, this must be
 	 something special, like a stack push.  Kill this chain.  */
-      action = terminate_all_read;
+      action = mark_all_read;
 #endif
       break;
 
@@ -785,13 +856,15 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 /* Hide operands of the current insn (of which there are N_OPS) by
    substituting cc0 for them.
    Previous values are stored in the OLD_OPERANDS and OLD_DUPS.
-   If INOUT_ONLY is set, we only do this for OP_INOUT type operands.  */
+   If INOUT_AND_EC_ONLY is set, we only do this for OP_INOUT type operands
+   and earlyclobbers.  */
 
 static void
 hide_operands (int n_ops, rtx *old_operands, rtx *old_dups,
-	       bool inout_only)
+	       bool inout_and_ec_only)
 {
   int i;
+  int alt = which_alternative;
   for (i = 0; i < n_ops; i++)
     {
       old_operands[i] = recog_data.operand[i];
@@ -800,14 +873,16 @@ hide_operands (int n_ops, rtx *old_opera
 	 reachable by proper operands.  */
       if (recog_data.constraints[i][0] == '\0')
 	continue;
-      if (!inout_only || recog_data.operand_type[i] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[i] == OP_INOUT
+	  || recog_op_alt[i][alt].earlyclobber)
 	*recog_data.operand_loc[i] = cc0_rtx;
     }
   for (i = 0; i < recog_data.n_dups; i++)
     {
       int opn = recog_data.dup_num[i];
       old_dups[i] = *recog_data.dup_loc[i];
-      if (!inout_only || recog_data.operand_type[opn] == OP_INOUT)
+      if (!inout_and_ec_only || recog_data.operand_type[opn] == OP_INOUT
+	  || recog_op_alt[opn][alt].earlyclobber)
 	*recog_data.dup_loc[i] = cc0_rtx;
     }
 }
@@ -828,10 +903,11 @@ restore_operands (rtx insn, int n_ops, r
 }
 
 /* For each output operand of INSN, call scan_rtx to create a new
-   open chain.  */
+   open chain.  Do this only for normal or earlyclobber outputs,
+   depending on EARLYCLOBBER.  */
 
 static void
-record_out_operands (rtx insn)
+record_out_operands (rtx insn, bool earlyclobber)
 {
   int n_ops = recog_data.n_operands;
   int alt = which_alternative;
@@ -849,7 +925,8 @@ record_out_operands (rtx insn)
 
       struct du_head *prev_open;
 
-      if (recog_data.operand_type[opn] != OP_OUT)
+      if (recog_data.operand_type[opn] != OP_OUT
+	  || recog_op_alt[opn][alt].earlyclobber != earlyclobber)
 	continue;
 
       prev_open = open_chains;
@@ -866,7 +943,7 @@ record_out_operands (rtx insn)
 	      && REGNO (op) == ORIGINAL_REGNO (op)))
 	{
 	  if (prev_open != open_chains)
-	    open_chains->first->cl = NO_REGS;
+	    open_chains->cannot_rename = 1;
 	}
     }
 }
@@ -877,9 +954,22 @@ static struct du_head *
 build_def_use (basic_block bb)
 {
   rtx insn;
+  df_ref *def_rec;
 
   open_chains = closed_chains = NULL;
 
+  fail_current_block = false;
+
+  current_id = 0;
+  bitmap_initialize (&open_chains_set, &bitmap_default_obstack);
+  REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
+  for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
+    {
+      df_ref def = *def_rec;
+      if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+	SET_HARD_REG_BIT (live_hard_regs, DF_REF_REGNO (def));
+    }
+
   for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
     {
       if (NONDEBUG_INSN_P (insn))
@@ -891,22 +981,31 @@ build_def_use (basic_block bb)
 	  int i, icode;
 	  int alt;
 	  int predicated;
+	  enum rtx_code set_code = SET;
+	  enum rtx_code clobber_code = CLOBBER;
 
 	  /* Process the insn, determining its effect on the def-use
-	     chains.  We perform the following steps with the register
-	     references in the insn:
-	     (1) Any read that overlaps an open chain, but doesn't exactly
-	         match, causes that chain to be closed.  We can't deal
-	         with overlaps yet.
+	     chains and live hard registers.  We perform the following
+	     steps with the register references in the insn, simulating
+	     its effect:
+	     (1) Deal with earlyclobber operands and CLOBBERs of non-operands
+	         by creating chains and marking hard regs live.
 	     (2) Any read outside an operand causes any chain it overlaps
-	         with to be closed, since we can't replace it.
+	         with to be marked unrenamable.
 	     (3) Any read inside an operand is added if there's already
 	         an open chain for it.
 	     (4) For any REG_DEAD note we find, close open chains that
 	         overlap it.
-	     (5) For any write we find, close open chains that overlap it.
-	     (6) For any write we find in an operand, make a new chain.
-	     (7) For any REG_UNUSED, close any chains we just opened.  */
+	     (5) For any non-earlyclobber write we find, close open chains
+	         that overlap it.
+	     (6) For any non-earlyclobber write we find in an operand, make
+	         a new chain or mark the hard register as live.
+	     (7) For any REG_UNUSED, close any chains we just opened.
+
+	     We cannot deal with situations where we track a reg in one mode
+	     and see a reference in another mode; these will cause the chain
+	     to be marked unrenamable or even cause us to abort the entire
+	     basic block.  */
 
 	  icode = recog_memoized (insn);
 	  extract_insn (insn);
@@ -928,28 +1027,41 @@ build_def_use (basic_block bb)
 		recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
 	      if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
 	          || (predicated && recog_data.operand_type[i] == OP_OUT))
-		recog_data.operand_type[i] = OP_INOUT;
+		{
+		  recog_data.operand_type[i] = OP_INOUT;
+		  if (matches >= 0
+		      && (GET_MODE_SIZE (recog_data.operand_mode[i])
+			  != GET_MODE_SIZE (recog_data.operand_mode[matches])))
+		    fail_current_block = true;
+		}
 	    }
 
-	  /* Step 1: Close chains for which we have overlapping reads.  */
-	  for (i = 0; i < n_ops; i++)
-	    scan_rtx (insn, recog_data.operand_loc[i],
-		      NO_REGS, terminate_overlapping_read,
-		      recog_data.operand_type[i]);
+	  if (fail_current_block)
+	    break;
 
-	  /* Step 2: Close chains for which we have reads outside operands.
+	  /* Step 1a: Mark hard registers that are clobbered in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &clobber_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
+
+	  /* Step 1b: Begin new chains for earlyclobbered writes inside
+	     operands.  */
+	  record_out_operands (insn, true);
+
+	  /* Step 2: Mark chains for which we have reads outside operands
+	     as unrenamable.
 	     We do this by munging all operands into CC0, and closing
 	     everything remaining.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, false);
-	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
-		    OP_IN);
+	  scan_rtx (insn, &PATTERN (insn), NO_REGS, mark_all_read, OP_IN);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
 	  /* Step 2B: Can't rename function call argument registers.  */
 	  if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
 	    scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
-		      NO_REGS, terminate_all_read, OP_IN);
+		      NO_REGS, mark_all_read, OP_IN);
 
 	  /* Step 2C: Can't rename asm operands that were originally
 	     hard registers.  */
@@ -963,7 +1075,7 @@ build_def_use (basic_block bb)
 		    && REGNO (op) == ORIGINAL_REGNO (op)
 		    && (recog_data.operand_type[i] == OP_IN
 			|| recog_data.operand_type[i] == OP_INOUT))
-		  scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN);
+		  scan_rtx (insn, loc, NO_REGS, mark_all_read, OP_IN);
 	      }
 
 	  /* Step 3: Append to chains for reads inside operands.  */
@@ -999,8 +1111,13 @@ build_def_use (basic_block bb)
 	  /* Step 4: Close chains for registers that die here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_DEAD)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN);
+	      }
 
 	  /* Step 4B: If this is a call, any chain live at this point
 	     requires a caller-saved reg.  */
@@ -1013,16 +1130,26 @@ build_def_use (basic_block bb)
 
 	  /* Step 5: Close open chains that overlap writes.  Similar to
 	     step 2, we hide in-out operands, since we do not want to
-	     close these chains.  */
+	     close these chains.  We also hide earlyclobber operands,
+	     since we've opened chains for them in step 1, and earlier
+	     chains they would overlap with must have been closed at
+	     the previous insn at the latest, as such operands cannot
+	     possibly overlap with any input operands.  */
 
 	  hide_operands (n_ops, old_operands, old_dups, true);
 	  scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN);
 	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6: Begin new chains for writes inside operands.  */
-	  record_out_operands (insn);
+	  /* Step 6a: Mark hard registers that are set in this insn,
+	     outside an operand, as live.  */
+	  hide_operands (n_ops, old_operands, old_dups, false);
+	  note_stores (PATTERN (insn), note_sets_clobbers, &set_code);
+	  restore_operands (insn, n_ops, old_operands, old_dups);
 
-	  /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
+	  /* Step 6b: Begin new chains for writes inside operands.  */
+	  record_out_operands (insn, false);
+
+	  /* Step 6c: Record destination regs in REG_FRAME_RELATED_EXPR
 	     notes for update.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
@@ -1033,8 +1160,13 @@ build_def_use (basic_block bb)
 	     really used here.  */
 	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
 	    if (REG_NOTE_KIND (note) == REG_UNUSED)
-	      scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
-			OP_IN);
+	      {
+		remove_from_hard_reg_set (&live_hard_regs,
+					  GET_MODE (XEXP (note, 0)),
+					  REGNO (XEXP (note, 0)));
+		scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+			  OP_IN);
+	      }
 	}
       else if (DEBUG_INSN_P (insn)
 	       && !VAR_LOC_UNKNOWN_P (INSN_VAR_LOCATION_LOC (insn)))
@@ -1046,6 +1178,11 @@ build_def_use (basic_block bb)
 	break;
     }
 
+  bitmap_clear (&open_chains_set);
+
+  if (fail_current_block)
+    return NULL;
+
   /* Since we close every chain when we find a REG_DEAD note, anything that
      is still open lives past the basic block, so it can't be renamed.  */
   return closed_chains;

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-12 18:31   ` Bernd Schmidt
@ 2009-11-28  1:39     ` H.J. Lu
  2009-11-28  8:55       ` Eric Botcazou
  2009-12-02 13:13       ` Bernd Schmidt
  0 siblings, 2 replies; 23+ messages in thread
From: H.J. Lu @ 2009-11-28  1:39 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: Eric Botcazou, gcc-patches

On Thu, Nov 12, 2009 at 10:25 AM, Bernd Schmidt <bernds_cb1@t-online.de> wrote:
> Eric Botcazou wrote:
>>>      PR rtl/38582
>>
>> PR rtl-opt (or PR rtl-optimization).
>>
>>>      * regrename.c (struct du_head): New structure; some elements moved
>>>      from...
>>>      (struct du_chain): ... this one.
>>>      (open_chains, closed_chains): Now of type struct du_head *.
>>>      (do_replace): Accept du_head argument, not du_chain.  All callers
>>>      changed.  Modified code to match new data structures.
>>>      (build_def_use): Return a list of du_head structures.  Modified code
>>>      to match new data structures.
>>>      (dump_def_use_chain): Accept du_head argument, not du_chain.  All
>>>      callers changed.  Modified code to match new data structures.
>>>      (merge_overlapping_regs): Accept du_head argument, not du_chain.  All
>>>      callers changed.  Modified code to match new data structures.
>>>      (scan_rtx_reg): Change type of this_regno and this_nregs to unsigned.
>>>      Allocate a du_head structure as well as a du_chain when creating a
>>>      new chain.  Modified other code to match new data structures.
>>
>> OK with the trailing spaces removed in
> [...]
>
> Thanks, committed now (vacation got in the way).  I'll post a followup
> patch as well which takes care of the remaining performance problem.
>
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42202


-- 
H.J.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-28  1:39     ` H.J. Lu
@ 2009-11-28  8:55       ` Eric Botcazou
  2009-11-30 11:55         ` Bernd Schmidt
  2009-12-02 13:13       ` Bernd Schmidt
  1 sibling, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-28  8:55 UTC (permalink / raw)
  To: H.J. Lu; +Cc: gcc-patches, Bernd Schmidt

> This caused:
>
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42202

No, it didn't, it's revision 154688.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-28  8:55       ` Eric Botcazou
@ 2009-11-30 11:55         ` Bernd Schmidt
  2009-11-30 12:19           ` Eric Botcazou
  0 siblings, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-30 11:55 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: H.J. Lu, gcc-patches

Eric Botcazou wrote:
>> This caused:
>>
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42202
> 
> No, it didn't, it's revision 154688.

Looks like it isn't handling register sets in COND_EXPRs correctly.
I'll think of a way to fix it.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-30 11:55         ` Bernd Schmidt
@ 2009-11-30 12:19           ` Eric Botcazou
  2009-11-30 13:27             ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-11-30 12:19 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches, H.J. Lu

> Looks like it isn't handling register sets in COND_EXPRs correctly.

Was this latent in the previous implementation?  The code does explicitly 
handle COND_EXECs in a few places.

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-30 12:19           ` Eric Botcazou
@ 2009-11-30 13:27             ` Bernd Schmidt
  0 siblings, 0 replies; 23+ messages in thread
From: Bernd Schmidt @ 2009-11-30 13:27 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches, H.J. Lu

Eric Botcazou wrote:
>> Looks like it isn't handling register sets in COND_EXPRs correctly.
> 
> Was this latent in the previous implementation?  The code does explicitly 
> handle COND_EXECs in a few places.
> 
Yes, but by unconditionally setting them to INOUT operands, we are
missing cases where the first SET makes a register live, so we don't
create a chain for it or mark a hard register as live.

So I'm thinking we should set these operands to INOUT only if it's
already being tracked.

Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-11-28  1:39     ` H.J. Lu
  2009-11-28  8:55       ` Eric Botcazou
@ 2009-12-02 13:13       ` Bernd Schmidt
  2009-12-02 18:22         ` Eric Botcazou
  1 sibling, 1 reply; 23+ messages in thread
From: Bernd Schmidt @ 2009-12-02 13:13 UTC (permalink / raw)
  To: H.J. Lu; +Cc: Eric Botcazou, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2835 bytes --]

H.J. Lu wrote:
> This caused:
> 
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42202

Here's a patch.  Bootstrapped and regression tested on ia64-linux (the
gcc60 machine), with flag_rename_registers forced to 1 (without this
patch the bootstrap fails under those conditions).  Since the machine is
so slow I didn't yet run a comparison with a clean tree, but the
failures I get are all seen in recent gcc-testresults postings prior to
my regrename patches.  The gfortran failures are gone.

FAIL: gcc.dg/builtin-apply4.c execution test
FAIL: gcc.dg/pr34668-1.c (internal compiler error)
FAIL: gcc.dg/pr34668-1.c (test for excess errors)
FAIL: gcc.dg/guality/pr41353-1.c  -O3 -fomit-frame-pointer  line 39 i == 12
FAIL: gcc.dg/guality/pr41353-1.c  -O3 -g  line 39 i == 12
FAIL: abi_check
FAIL: libmudflap.c/pass54-frag.c execution test
FAIL: libmudflap.c/pass54-frag.c execution test
FAIL: libmudflap.c/pass54-frag.c (-static) execution test
FAIL: libmudflap.c/pass54-frag.c (-static) execution test
FAIL: libmudflap.c/fail31-frag.c (-O3) crash test
FAIL: libmudflap.c/fail31-frag.c (-O3) output pattern test
FAIL: libmudflap.c/pass45-frag.c (-O3) execution test
FAIL: libmudflap.c/pass45-frag.c (-O3) output pattern test
FAIL: libmudflap.c/pass45-frag.c (-O3) execution test
FAIL: libmudflap.c/pass45-frag.c (-O3) output pattern test
FAIL: libmudflap.c++/pass41-frag.cxx execution test
FAIL: libmudflap.c++/pass41-frag.cxx (-static) execution test
FAIL: libmudflap.c++/pass41-frag.cxx ( -O) execution test
FAIL: libmudflap.c++/pass41-frag.cxx (-O2) execution test
FAIL: libmudflap.c++/pass41-frag.cxx (-O3) execution test
FAIL: libffi.call/cls_longdouble_va.c -O0 -W -Wall output pattern test,
is %.1f
FAIL: libffi.call/cls_longdouble_va.c -O2 output pattern test, is %.1f
FAIL: libffi.call/cls_longdouble_va.c -O3 output pattern test, is %.1f
FAIL: libffi.call/cls_longdouble_va.c -Os output pattern test, is %.1f
FAIL: libffi.call/cls_longdouble_va.c -O2 -fomit-frame-pointer output
pattern test, is %.1f
FAIL: getlocalvartable output
FAIL: Throw_3 -O3 output - source compiled test
FAIL: Throw_3 -O3 -findirect-dispatch output - source compiled test

The closest matches appear to be
http://gcc.gnu.org/ml/gcc-testresults/2009-11/msg02593.html
http://gcc.gnu.org/ml/gcc-testresults/2009-11/msg02570.html
http://gcc.gnu.org/ml/gcc-testresults/2009-11/msg02537.html

which are about as old as the tree I was testing.

Could install now, or could wait for the clean results (without this and
the previous regrename patch) which should take another 24 hours or so.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

[-- Attachment #2: rr-ia64.diff --]
[-- Type: text/plain, Size: 4706 bytes --]

	* regrename.c (live_in_chains): New variable.
	(verify_reg_tracked): New static function.
	(scan_rtx_reg): Update live_in_chains.
	(scan_rtx): Only promote sets in COND_EXEC to OP_INOUT if
	we're already tracking the reg.
	(build_def_use): Likewise.  Initialize live_in_chains.

Index: regrename.c
===================================================================
--- regrename.c	(revision 154688)
+++ regrename.c	(working copy)
@@ -438,6 +438,54 @@ static struct du_head *closed_chains;
 static bitmap_head open_chains_set;
 static HARD_REG_SET live_hard_regs;
 
+/* Record the registers being tracked in open_chains.  The intersection
+   between this and live_hard_regs is empty.  */
+static HARD_REG_SET live_in_chains;
+
+/* Return true if OP is a reg that is being tracked already in some form.
+   May set fail_current_block if it sees an unhandled case of overlap.  */
+
+static bool
+verify_reg_tracked (rtx op)
+{
+  unsigned regno, nregs;
+  bool all_live, all_dead;
+  if (!REG_P (op))
+    return false;
+
+  regno = REGNO (op);
+  nregs = hard_regno_nregs[regno][GET_MODE (op)];
+  all_live = all_dead = true;
+  while (nregs-- > 0)
+    if (TEST_HARD_REG_BIT (live_hard_regs, regno + nregs))
+      all_dead = false;
+    else
+      all_live = false;
+  if (!all_dead && !all_live)
+    {
+      fail_current_block = true;
+      return false;
+    }
+
+  if (all_live)
+    return true;
+
+  nregs = hard_regno_nregs[regno][GET_MODE (op)];
+  all_live = all_dead = true;
+  while (nregs-- > 0)
+    if (TEST_HARD_REG_BIT (live_in_chains, regno + nregs))
+      all_dead = false;
+    else
+      all_live = false;
+  if (!all_dead && !all_live)
+    {
+      fail_current_block = true;
+      return false;
+    }
+
+  return all_live;
+}
+
 /* Called through note_stores.  DATA points to a rtx_code, either SET or
    CLOBBER, which tells us which kind of rtx to look at.  If we have a
    match, record the set register in live_hard_regs and in the hard_conflicts
@@ -495,10 +543,14 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
 	  mark_conflict (open_chains, head->id);
 
 	  /* Since we're tracking this as a chain now, remove it from the
-	     list of conflicting live hard registers.  */
+	     list of conflicting live hard registers and track it in
+	     live_in_chains instead.  */
 	  nregs = head->nregs;
 	  while (nregs-- > 0)
-	    CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+	    {
+	      SET_HARD_REG_BIT (live_in_chains, head->regno + nregs);
+	      CLEAR_HARD_REG_BIT (live_hard_regs, head->regno + nregs);
+	    }
 
 	  COPY_HARD_REG_SET (head->hard_conflicts, live_hard_regs);
 	  bitmap_set_bit (&open_chains_set, head->id);
@@ -583,10 +635,17 @@ scan_rtx_reg (rtx insn, rtx *loc, enum r
       if ((action == terminate_dead || action == terminate_write)
 	  && superset)
 	{
+	  unsigned nregs;
+
 	  head->terminated = 1;
 	  head->next_chain = closed_chains;
 	  closed_chains = head;
 	  bitmap_clear_bit (&open_chains_set, head->id);
+
+	  nregs = head->nregs;
+	  while (nregs-- > 0)
+	    CLEAR_HARD_REG_BIT (live_in_chains, head->regno + nregs);
+
 	  *p = next;
 	  if (dump_file)
 	    fprintf (dump_file,
@@ -803,7 +862,8 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
     case SET:
       scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN);
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
+		(GET_CODE (PATTERN (insn)) == COND_EXEC
+		 && verify_reg_tracked (SET_DEST (x))) ? OP_INOUT : OP_OUT);
       return;
 
     case STRICT_LOW_PART:
@@ -829,7 +889,8 @@ scan_rtx (rtx insn, rtx *loc, enum reg_c
 
     case CLOBBER:
       scan_rtx (insn, &SET_DEST (x), cl, action,
-		GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT);
+		(GET_CODE (PATTERN (insn)) == COND_EXEC
+		 && verify_reg_tracked (SET_DEST (x))) ? OP_INOUT : OP_OUT);
       return;
 
     case EXPR_LIST:
@@ -962,6 +1023,7 @@ build_def_use (basic_block bb)
 
   current_id = 0;
   bitmap_initialize (&open_chains_set, &bitmap_default_obstack);
+  CLEAR_HARD_REG_SET (live_in_chains);
   REG_SET_TO_HARD_REG_SET (live_hard_regs, df_get_live_in (bb));
   for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++)
     {
@@ -1026,7 +1088,8 @@ build_def_use (basic_block bb)
 	      if (matches >= 0)
 		recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
 	      if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
-	          || (predicated && recog_data.operand_type[i] == OP_OUT))
+	          || (predicated && recog_data.operand_type[i] == OP_OUT
+		      && verify_reg_tracked (recog_data.operand[i])))
 		{
 		  recog_data.operand_type[i] = OP_INOUT;
 		  if (matches >= 0

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-12-02 13:13       ` Bernd Schmidt
@ 2009-12-02 18:22         ` Eric Botcazou
  2009-12-03 13:08           ` Bernd Schmidt
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Botcazou @ 2009-12-02 18:22 UTC (permalink / raw)
  To: Bernd Schmidt; +Cc: gcc-patches, H.J. Lu

> Could install now, or could wait for the clean results (without this and
> the previous regrename patch) which should take another 24 hours or so.

At your convenience, we aren't in a hurry.  Patch is OK if you update 

	  /* Simplify the code below by rewriting things to reflect
	     matching constraints.  Also promote OP_OUT to OP_INOUT
	     in predicated instructions.  */

in build_def_use, something like "... but only for register operands that are 
already tracked, so that we can create a chain when the first SET makes a 
register live".

-- 
Eric Botcazou

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: regrename speedup
  2009-12-02 18:22         ` Eric Botcazou
@ 2009-12-03 13:08           ` Bernd Schmidt
  0 siblings, 0 replies; 23+ messages in thread
From: Bernd Schmidt @ 2009-12-03 13:08 UTC (permalink / raw)
  To: Eric Botcazou; +Cc: gcc-patches, H.J. Lu

Eric Botcazou wrote:
>> Could install now, or could wait for the clean results (without this and
>> the previous regrename patch) which should take another 24 hours or so.
> 
> At your convenience, we aren't in a hurry.  Patch is OK if you update 
> 
> 	  /* Simplify the code below by rewriting things to reflect
> 	     matching constraints.  Also promote OP_OUT to OP_INOUT
> 	     in predicated instructions.  */
> 
> in build_def_use, something like "... but only for register operands that are 
> already tracked, so that we can create a chain when the first SET makes a 
> register live".

Committed now after retesting with a clean tree and getting the same set
of failures.


Bernd
-- 
This footer brought to you by insane German lawmakers.
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 40368
Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2009-12-03 12:59 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-17 14:46 regrename speedup Bernd Schmidt
2009-10-22 13:09 ` Eric Botcazou
2009-11-12 18:31   ` Bernd Schmidt
2009-11-28  1:39     ` H.J. Lu
2009-11-28  8:55       ` Eric Botcazou
2009-11-30 11:55         ` Bernd Schmidt
2009-11-30 12:19           ` Eric Botcazou
2009-11-30 13:27             ` Bernd Schmidt
2009-12-02 13:13       ` Bernd Schmidt
2009-12-02 18:22         ` Eric Botcazou
2009-12-03 13:08           ` Bernd Schmidt
2009-11-12 18:32 ` Bernd Schmidt
2009-11-19 19:28   ` Eric Botcazou
2009-11-20  1:36     ` Bernd Schmidt
2009-11-20  7:58       ` Eric Botcazou
2009-11-20 21:31         ` Bernd Schmidt
2009-11-22 22:26           ` Eric Botcazou
2009-11-24 11:55             ` Bernd Schmidt
2009-11-24 12:30               ` Eric Botcazou
2009-11-26 12:27               ` Eric Botcazou
2009-11-26 16:50                 ` Bernd Schmidt
2009-11-26 17:46                   ` Eric Botcazou
2009-11-26 23:19                     ` Bernd Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).