SUBREG patch for ssa.c

Geoff Keating geoffk@cygnus.com
Tue Jun 20 17:06:00 GMT 2000


This changes ssa.c so that now, each pseudo is really only set by one
toplevel insn.  SETs that don't set all of their LHS are turned into a
SEQUENCE at the toplevel.  For instance, given

(insn (set (subreg foo) ...))

you used to get, if it worked at all,

(insn (set foo_1 foo_0))
(insn (set (subreg foo_1) ...))

and now you get

(insn (sequence [
  (set foo_1 foo_0)
  (set (subreg foo_1) ...)]))

In future, libcalls will probably also have to be wrapped in SEQUENCEs
like this.  It would be particularly good to optimise

(clobber foo)
(set (subreg foo 0) ...)
(set (subreg foo 1) ...)

into a single SEQUENCE insn rather than into the three insns you now
get; this will also avoid creating a 'fake' register which is never
actually initialised, only CLOBBERed.  With this patch, this can now
be done by changing only the pass that converts into SSA form, the one
that converts back will do the right thing.

This also removes some particularly bogus code that was treating
(subreg foo 0) and (subreg foo 1) as the same, as a side-effect.

Passes regression tests on powerpc-linux, the bootstrap is still
running.  It fixes all the outstanding -fssa problems on that target.

OK to commit (if bootstrap is OK)?

-- 
- Geoffrey Keating <geoffk@cygnus.com>

===File ~/patches/cygnus/ssa-subreg-1.patch=================
2000-06-20  Geoff Keating  <geoffk@cygnus.com>

	* ssa.c (struct rename_set_data): Change the name of field
	'set_dest' to 'old_reg'.  Add comments.
	(struct rename_context): Change the name of 'set_data' to
	'new_renames'.  Add new field 'done_renames'.
	(create_delayed_rename): New function.
	(apply_delayed_renames): New function.
	(rename_insn_1): Use the new functions.  Handle CLOBBERS.  Handle
	SUBREGs and similar by emitting a move.
	(new_registers_for_updates): Delete, functionality moved to
	apply_delayed_renames.
	(rename_block): Handle moves emitted by rename_insn_1 by putting
	them into a SEQUENCE with the original insn.  Add sanity checks
	and comments.
	(rename_equivalent_regs_in_insn): Don't handle SUBREGs specially.
	(rename_equivalent_regs): Expand SEQUENCEs out to individual insns.
	
Index: ssa.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/ssa.c,v
retrieving revision 1.13
diff -p -u -u -p -r1.13 ssa.c
--- ssa.c	2000/05/19 19:23:02	1.13
+++ ssa.c	2000/06/20 23:58:04
@@ -93,6 +93,8 @@ static unsigned int ssa_max_reg_num;
 
 /* Local function prototypes.  */
 
+struct rename_context;
+
 static inline rtx * phi_alternative
   PARAMS ((rtx, int));
 
@@ -114,6 +116,10 @@ static void insert_phi_node
   PARAMS ((int regno, int b));
 static void insert_phi_nodes
   PARAMS ((sbitmap *idfs, sbitmap *evals, int nregs));
+static void create_delayed_rename 
+  PARAMS ((struct rename_context *, rtx *));
+static void apply_delayed_renames 
+  PARAMS ((struct rename_context *));
 static int rename_insn_1 
   PARAMS ((rtx *ptr, void *data));
 static void rename_block 
@@ -521,10 +527,17 @@ insert_phi_nodes (idfs, evals, nregs)
 struct rename_set_data
 {
   struct rename_set_data *next;
+  /* This is the SET_DEST of the (first) SET that sets the REG.  */
   rtx *reg_loc;
-  rtx set_dest;
+  /* This is what used to be at *REG_LOC.  */
+  rtx old_reg;
+  /* This is the REG that will replace OLD_REG.  It's set only
+     when the rename data is moved onto the DONE_RENAMES queue.  */
   rtx new_reg;
+  /* This is what to restore ssa_rename_to[REGNO (old_reg)] to. 
+     It is usually the previous contents of ssa_rename_to[REGNO (old_reg)].  */
   rtx prev_reg;
+  /* This is the insn that contains all the SETs of the REG.  */
   rtx set_insn;
 };
 
@@ -532,13 +545,31 @@ struct rename_set_data
    renaming registers.  */
 struct rename_context
 {
-  struct rename_set_data *set_data;
+  struct rename_set_data *new_renames;
+  struct rename_set_data *done_renames;
   rtx current_insn;
 };
 
-static void new_registers_for_updates 
-  PARAMS ((struct rename_set_data *set_data,
-	   struct rename_set_data *old_set_data, rtx insn));
+/* Queue the rename of *REG_LOC.  */
+static void
+create_delayed_rename (c, reg_loc)
+     struct rename_context *c;
+     rtx *reg_loc;
+{
+  struct rename_set_data *r;
+  r = (struct rename_set_data *) xmalloc (sizeof(*r));
+  
+  if (GET_CODE (*reg_loc) != REG
+      || REGNO (*reg_loc) < FIRST_PSEUDO_REGISTER)
+    abort();
+
+  r->reg_loc = reg_loc;
+  r->old_reg = *reg_loc;
+  r->prev_reg = ssa_rename_to [REGNO (r->old_reg) - FIRST_PSEUDO_REGISTER];
+  r->set_insn = c->current_insn;
+  r->next = c->new_renames;
+  c->new_renames = r;
+}
 
 /* This is part of a rather ugly hack to allow the pre-ssa regno to be
    reused.  If, during processing, a register has not yet been touched,
@@ -549,6 +580,60 @@ static void new_registers_for_updates 
    already been reused.  */
 #define RENAME_NO_RTX  pc_rtx
 
+/* Move all the entries from NEW_RENAMES onto DONE_RENAMES by
+   applying all the renames on NEW_RENAMES.  */
+
+static void
+apply_delayed_renames (c)
+       struct rename_context *c;
+{
+  struct rename_set_data *r;
+  struct rename_set_data *last_r = NULL;
+  
+  for (r = c->new_renames; r != NULL; r = r->next)
+    {
+      int regno = REGNO (r->old_reg);
+      int new_regno;
+      
+      /* Failure here means that someone has a PARALLEL that sets
+	 a register twice (bad!).  */
+      if (ssa_rename_to [regno - FIRST_PSEUDO_REGISTER] != r->prev_reg)
+	abort();
+      /* Failure here means we have changed REG_LOC before applying
+	 the rename.  */
+      /* For the first set we come across, reuse the original regno.  */
+      if (r->prev_reg == NULL_RTX)
+	{
+	  r->new_reg = r->old_reg;
+	  /* We want to restore RENAME_NO_RTX rather than NULL_RTX. */
+	  r->prev_reg = RENAME_NO_RTX;
+	}
+      else
+	r->new_reg = gen_reg_rtx (GET_MODE (r->old_reg));
+      new_regno = REGNO (r->new_reg);
+      ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] = r->new_reg;
+
+      if (new_regno >= (int) ssa_definition->num_elements)
+	{
+	  int new_limit = new_regno * 5 / 4;
+	  ssa_definition = VARRAY_GROW (ssa_definition, new_limit);
+	  ssa_uses = VARRAY_GROW (ssa_uses, new_limit);
+	  ssa_rename_from = VARRAY_GROW (ssa_rename_from, new_limit);
+	}
+
+      VARRAY_RTX (ssa_definition, new_regno) = r->set_insn;
+      VARRAY_RTX (ssa_rename_from, new_regno) = r->old_reg;
+
+      last_r = r;
+    }
+  if (last_r != NULL)
+    {
+      last_r->next = c->done_renames;
+      c->done_renames = c->new_renames;
+      c->new_renames = NULL;
+    }
+}
+
 /* Part one of the first step of rename_block, called through for_each_rtx. 
    Mark pseudos that are set for later update.  Transform uses of pseudos.  */
 
@@ -559,47 +644,76 @@ rename_insn_1 (ptr, data)
 {
   rtx x = *ptr;
   struct rename_context *context = data;
-  struct rename_set_data **set_datap = &(context->set_data);
 
   if (x == NULL_RTX)
     return 0;
 
   switch (GET_CODE (x))
     {
+    case CLOBBER:
     case SET:
       {
 	rtx *destp = &SET_DEST (x);
 	rtx dest = SET_DEST (x);
 
-	/* Subregs at word 0 are interesting.  Subregs at word != 0 are
-	   presumed to be part of a contiguous multi-word set sequence.  */
-	while (GET_CODE (dest) == SUBREG
-	       && SUBREG_WORD (dest) == 0)
+	/* Some SETs also use the REG specified in their LHS.
+	   These can be detected by the presence of
+	   STRICT_LOW_PART, SUBREG, SIGN_EXTRACT, and ZERO_EXTRACT
+	   in the LHS.  Handle these by changing
+	   (set (subreg (reg foo)) ...)
+	   into
+	   (sequence [(set (reg foo_1) (reg foo))
+	              (set (subreg (reg foo_1)) ...)])  
+
+	   FIXME: Much of the time this is too much.  For many libcalls,
+	   paradoxical SUBREGs, etc., the input register is dead.  We should
+	   recognise this in rename_block or here and not make a false
+	   dependency.  */
+	   
+	if (GET_CODE (dest) == STRICT_LOW_PART
+	    || GET_CODE (dest) == SUBREG
+	    || GET_CODE (dest) == SIGN_EXTRACT
+	    || GET_CODE (dest) == ZERO_EXTRACT)
 	  {
-	    destp = &SUBREG_REG (dest);
-	    dest = SUBREG_REG (dest);
+	    rtx i, reg;
+	    reg = dest;
+	    
+	    while (GET_CODE (reg) == STRICT_LOW_PART
+		   || GET_CODE (reg) == SUBREG
+		   || GET_CODE (reg) == SIGN_EXTRACT
+		   || GET_CODE (reg) == ZERO_EXTRACT)
+		reg = XEXP (reg, 0);
+	    
+	    if (GET_CODE (reg) == REG
+		&& REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+	      {
+		/* Generate (set reg reg), and do renaming on it so
+		   that it becomes (set reg_1 reg_0), and we will
+		   replace reg with reg_1 in the SUBREG.  */
+
+		struct rename_set_data *saved_new_renames;
+		saved_new_renames = context->new_renames;
+		context->new_renames = NULL;
+		i = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
+		for_each_rtx (&i, rename_insn_1, data);
+		apply_delayed_renames (context);
+		context->new_renames = saved_new_renames;
+	      }
 	  }
-
-	if (GET_CODE (dest) == REG
-	    && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
+	else if (GET_CODE (dest) == REG
+		 && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
 	  {
 	    /* We found a genuine set of an interesting register.  Tag
 	       it so that we can create a new name for it after we finish
 	       processing this insn.  */
-
-	    struct rename_set_data *r;
-	    r = (struct rename_set_data *) xmalloc (sizeof(*r));
 
-	    r->reg_loc = destp;
-	    r->set_dest = SET_DEST (x);
-	    r->set_insn = context->current_insn;
-	    r->next = *set_datap;
-	    *set_datap = r;
+	    create_delayed_rename (context, destp);
 
 	    /* Since we do not wish to (directly) traverse the
 	       SET_DEST, recurse through for_each_rtx for the SET_SRC
 	       and return.  */
-	    for_each_rtx (&SET_SRC (x), rename_insn_1, data);
+	    if (GET_CODE (x) == SET)
+	      for_each_rtx (&SET_SRC (x), rename_insn_1, data);
 	    return -1;
 	  }
 
@@ -634,55 +748,6 @@ rename_insn_1 (ptr, data)
     }
 }
 
-/* Second part of the first step of rename_block.  The portion of the list
-   beginning at SET_DATA through OLD_SET_DATA contain the sets present in
-   INSN.  Update data structures accordingly.  */
-
-static void
-new_registers_for_updates (set_data, old_set_data, insn)
-     struct rename_set_data *set_data, *old_set_data;
-     rtx insn;
-{
-  while (set_data != old_set_data)
-    {
-      int regno, new_regno;
-      rtx old_reg, new_reg, prev_reg;
-
-      old_reg = *set_data->reg_loc;
-      regno = REGNO (*set_data->reg_loc);
-
-      /* For the first set we come across, reuse the original regno.  */
-      if (ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] == NULL_RTX)
-	{
-	  new_reg = old_reg;
-	  prev_reg = RENAME_NO_RTX;
-	}
-      else
-	{
-	  prev_reg = ssa_rename_to[regno - FIRST_PSEUDO_REGISTER];
-	  new_reg = gen_reg_rtx (GET_MODE (old_reg));
-	}
-
-      set_data->new_reg = new_reg;
-      set_data->prev_reg = prev_reg;
-      new_regno = REGNO (new_reg);
-      ssa_rename_to[regno - FIRST_PSEUDO_REGISTER] = new_reg;
-
-      if (new_regno >= (int) ssa_definition->num_elements)
-	{
-	  int new_limit = new_regno * 5 / 4;
-	  ssa_definition = VARRAY_GROW (ssa_definition, new_limit);
-	  ssa_uses = VARRAY_GROW (ssa_uses, new_limit);
-	  ssa_rename_from = VARRAY_GROW (ssa_rename_from, new_limit);
-	}
-
-      VARRAY_RTX (ssa_definition, new_regno) = insn;
-      VARRAY_RTX (ssa_rename_from, new_regno) = old_reg;
-
-      set_data = set_data->next;
-    }
-}
-
 static void
 rename_block (bb, idom)
      int bb;
@@ -705,14 +770,35 @@ rename_block (bb, idom)
       if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
 	{
 	  struct rename_context context;
-	  context.set_data = set_data;
+	  context.done_renames = set_data;
+	  context.new_renames = NULL;
 	  context.current_insn = insn;
 
+	  start_sequence ();
 	  for_each_rtx (&PATTERN (insn), rename_insn_1, &context);
 	  for_each_rtx (&REG_NOTES (insn), rename_insn_1, &context);
+
+	  /* Sometimes, we end up with a sequence of insns that
+	     SSA needs to treat as a single insn.  Wrap these in a
+	     SEQUENCE.  (Any notes now get attached to the SEQUENCE,
+	     not to the old version inner insn.)  */
+	  if (get_insns () != NULL_RTX)
+	    {
+	      rtx seq;
+	      int i;
+	      
+	      emit (PATTERN (insn));
+	      seq = gen_sequence ();
+	      /* We really want a SEQUENCE of SETs, not a SEQUENCE
+		 of INSNs.  */
+	      for (i = 0; i < XVECLEN (seq, 0); i++)
+		XVECEXP (seq, 0, i) = PATTERN (XVECEXP (seq, 0, i));
+	      PATTERN (insn) = seq;
+	    }
+	  end_sequence ();
 	  
-	  new_registers_for_updates (context.set_data, set_data, insn);
-	  set_data = context.set_data;
+	  apply_delayed_renames (&context);
+	  set_data = context.done_renames;
 	}
 
       next = NEXT_INSN (insn);
@@ -780,29 +866,18 @@ rename_block (bb, idom)
     if (idom[c] == bb)
       rename_block (c, idom);
 
-  /* Step Four: Update the sets to refer to their new register.  */
+  /* Step Four: Update the sets to refer to their new register,
+     and restore ssa_rename_to to its previous state.  */
 
   while (set_data)
     {
       struct rename_set_data *next;
       rtx old_reg = *set_data->reg_loc;
-
-      /* If the set is of a subreg only, copy the entire reg first so
-	 that unmodified bits are preserved.  Of course, we don't
-	 strictly have SSA any more, but that's the best we can do
-	 without a lot of hard work.  */
-
-      if (GET_CODE (set_data->set_dest) == SUBREG) 
-	{
-	  if (old_reg != set_data->new_reg)
-	    {
-	      rtx copy = gen_rtx_SET (GET_MODE (old_reg), 
-				      set_data->new_reg, old_reg);
-	      emit_insn_before (copy, set_data->set_insn);
-	    }
-	}
 
+      if (*set_data->reg_loc != set_data->old_reg)
+	abort();
       *set_data->reg_loc = set_data->new_reg;
+
       ssa_rename_to[REGNO (old_reg)-FIRST_PSEUDO_REGISTER]
 	= set_data->prev_reg;
 
@@ -1478,14 +1553,6 @@ coalesce_regs_in_copies (bb, p, conflict
       src = SET_SRC (pattern);
       dest = SET_DEST (pattern);
 
-      /* If src or dest are subregs, find the underlying reg.  */
-      while (GET_CODE (src) == SUBREG
-	     && SUBREG_WORD (src) != 0)
-	src = SUBREG_REG (src);
-      while (GET_CODE (dest) == SUBREG
-	     && SUBREG_WORD (dest) != 0)
-	dest = SUBREG_REG (dest);
-
       /* We're only looking for copies.  */
       if (GET_CODE (src) != REG || GET_CODE (dest) != REG)
 	continue;
@@ -1709,40 +1776,6 @@ rename_equivalent_regs_in_insn (ptr, dat
 
   switch (GET_CODE (x))
     {
-    case SET:
-      {
-	rtx *destp = &SET_DEST (x);
-	rtx dest = SET_DEST (x);
-
-	/* Subregs at word 0 are interesting.  Subregs at word != 0 are
-	   presumed to be part of a contiguous multi-word set sequence.  */
-	while (GET_CODE (dest) == SUBREG
-	       && SUBREG_WORD (dest) == 0)
-	  {
-	    destp = &SUBREG_REG (dest);
-	    dest = SUBREG_REG (dest);
-	  }
-
-	if (GET_CODE (dest) == REG
-	    && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
-	  {
-	    /* Got a pseudo; replace it.  */
-	    int regno = REGNO (dest);
-	    int new_regno = partition_find (reg_partition, regno);
-	    if (regno != new_regno)
-	      *destp = regno_reg_rtx[new_regno];
-
-	    for_each_rtx (&SET_SRC (x), 
-			  rename_equivalent_regs_in_insn, 
-			  data);
-	    return -1;
-	  }
-
-	/* Otherwise, this was not an interesting destination.  Continue
-	   on, marking uses as normal.  */
-	return 0;
-      }
-
     case REG:
       if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
 	{
@@ -1769,7 +1802,8 @@ rename_equivalent_regs_in_insn (ptr, dat
     }
 }
 
-/* Rename regs that are equivalent in REG_PARTITION.  */
+/* Rename regs that are equivalent in REG_PARTITION.  
+   Also collapse any SEQUENCE insns.  */
 
 static void
 rename_equivalent_regs (reg_partition)
@@ -1795,6 +1829,20 @@ rename_equivalent_regs (reg_partition)
 	      for_each_rtx (&REG_NOTES (insn), 
 			    rename_equivalent_regs_in_insn, 
 			    reg_partition);
+
+	      if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+		{
+		  rtx s = PATTERN (insn);
+		  int slen = XVECLEN (s, 0);
+		  int i;
+
+		  if (slen <= 1)
+		    abort();
+
+		  PATTERN (insn) = XVECEXP (s, 0, slen-1);
+		  for (i = 0; i < slen - 1; i++)
+		    emit_block_insn_before (XVECEXP (s, 0, i), insn, b);
+		}
 	    }
 
 	  next = NEXT_INSN (insn);
============================================================


More information about the Gcc-patches mailing list