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


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

Re: REG_NO_CONFLICT vs lower-subreg


Bernd Schmidt <bernds_cb1@t-online.de> writes:

> It would be nice to eliminate REG_NO_CONFLICT altogether, but a quick
> experiment with the i386 port showed that this idea is a non-starter
> for now (i386 still has insns operating on DImode, hence in some
> functions not all DImode registers get lowered, and lack of
> no_conflict blocks causes poor register allocation when that happens).
> 
> I suppose we could add a target macro to let individual ports turn off
> REG_NO_CONFLICT generation?  Any other ideas?

I have done the work needed to eliminate REG_NO_CONFLICT, but I've
stalled on it, mostly due to other work, partly because the patch
could usefully take advantage of the pending dataflow changes (instead
of using REG_SUBREG_DEAD notes).

For reference I've appended my current patch.  This includes changes
to the i386 backend as well as the generic changes to track subreg
liveness.  This patch does not itself eliminate REG_NO_CONFLICT, but
after implementing this patch REG_NO_CONFLICT blocks should become
useless.

Ian

Index: gcc/reload.c
===================================================================
--- gcc/reload.c	(revision 123904)
+++ gcc/reload.c	(working copy)
@@ -1513,71 +1513,118 @@ push_reload (rtx in, rtx out, rtx *inloc
   if (rld[i].reg_rtx == 0 && in != 0 && hard_regs_live_known)
     {
       rtx note;
-      int regno;
       enum machine_mode rel_mode = inmode;
 
       if (out && GET_MODE_SIZE (outmode) > GET_MODE_SIZE (inmode))
 	rel_mode = outmode;
 
       for (note = REG_NOTES (this_insn); note; note = XEXP (note, 1))
-	if (REG_NOTE_KIND (note) == REG_DEAD
-	    && REG_P (XEXP (note, 0))
-	    && (regno = REGNO (XEXP (note, 0))) < FIRST_PSEUDO_REGISTER
-	    && reg_mentioned_p (XEXP (note, 0), in)
-	    /* Check that we don't use a hardreg for an uninitialized
-	       pseudo.  See also find_dummy_reload().  */
-	    && (ORIGINAL_REGNO (XEXP (note, 0)) < FIRST_PSEUDO_REGISTER
-		|| ! bitmap_bit_p (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
-				   ORIGINAL_REGNO (XEXP (note, 0))))
-	    && ! refers_to_regno_for_reload_p (regno,
-					       (regno
-						+ hard_regno_nregs[regno]
-								  [rel_mode]),
-					       PATTERN (this_insn), inloc)
+	{
+	  int regno, byte;
+	  enum machine_mode reg_mode;
+	  bool found;
+
+	  if (REG_NOTE_KIND (note) != REG_DEAD
+	      || !REG_P (XEXP (note, 0)))
+	    continue;
+
+	  regno = REGNO (XEXP (note, 0));
+	  if (!HARD_REGISTER_NUM_P (regno))
+	    continue;
+
+	  reg_mode = GET_MODE (XEXP (note, 0));
+	  if (GET_MODE_SIZE (rel_mode) > GET_MODE_SIZE (reg_mode))
+	    continue;
+
+	  /* Check that we don't use a hardreg for an uninitialized
+	     pseudo.  See also find_dummy_reload.  */
+	  if (!HARD_REGISTER_NUM_P (ORIGINAL_REGNO (XEXP (note, 0)))
+	      && bitmap_bit_p (ENTRY_BLOCK_PTR->il.rtl->global_live_at_end,
+			       ORIGINAL_REGNO (XEXP (note, 0))))
+	    continue;
+
+	  found = false;
+
+	  /* Check each REL_MODE sized chunk of the register being
+	     killed.  */
+	  for (byte = 0;
+	       byte < GET_MODE_SIZE (reg_mode);
+	       byte += GET_MODE_SIZE (rel_mode))
+	    {
+	      int subregno;
+	      unsigned int nregs, offs;
+
+	      if (byte == 0)
+		subregno = regno;
+	      else
+		{
+		  /* subreg_offset_representable_p should probably
+		     handle this case, but as of this writing it gets
+		     an assertion failure.  */
+		  if (GET_MODE_SIZE (reg_mode) % GET_MODE_SIZE (rel_mode) != 0)
+		    break;
+
+		  if (!subreg_offset_representable_p (regno, reg_mode, byte,
+						      rel_mode))
+		    continue;
+		  subregno = subreg_regno_offset (regno, reg_mode, byte,
+						  rel_mode);
+		}
+
+	      if (!HARD_REGNO_MODE_OK (subregno, inmode)
+		  || !HARD_REGNO_MODE_OK (subregno, outmode))
+		continue;
+
+	      nregs = hard_regno_nregs[subregno][rel_mode];
+	      if (!refers_to_regno_for_reload_p (subregno, subregno + nregs,
+						 in, NULL)
+		  || refers_to_regno_for_reload_p (subregno, subregno + nregs,
+						   PATTERN (this_insn), inloc))
+		continue;
+
 	    /* If this is also an output reload, IN cannot be used as
 	       the reload register if it is set in this insn unless IN
 	       is also OUT.  */
-	    && (out == 0 || in == out
-		|| ! hard_reg_set_here_p (regno,
-					  (regno
-					   + hard_regno_nregs[regno]
-							     [rel_mode]),
+	      if (out != NULL_RTX
+		  && in != out
+		  && hard_reg_set_here_p (subregno, subregno + nregs,
 					  PATTERN (this_insn)))
-	    /* ??? Why is this code so different from the previous?
-	       Is there any simple coherent way to describe the two together?
-	       What's going on here.  */
-	    && (in != out
-		|| (GET_CODE (in) == SUBREG
-		    && (((GET_MODE_SIZE (GET_MODE (in)) + (UNITS_PER_WORD - 1))
-			 / UNITS_PER_WORD)
-			== ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
-			     + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD))))
-	    /* Make sure the operand fits in the reg that dies.  */
-	    && (GET_MODE_SIZE (rel_mode)
-		<= GET_MODE_SIZE (GET_MODE (XEXP (note, 0))))
-	    && HARD_REGNO_MODE_OK (regno, inmode)
-	    && HARD_REGNO_MODE_OK (regno, outmode))
-	  {
-	    unsigned int offs;
-	    unsigned int nregs = MAX (hard_regno_nregs[regno][inmode],
-				      hard_regno_nregs[regno][outmode]);
-
-	    for (offs = 0; offs < nregs; offs++)
-	      if (fixed_regs[regno + offs]
-		  || ! TEST_HARD_REG_BIT (reg_class_contents[(int) class],
-					  regno + offs))
-		break;
+		continue;
 
-	    if (offs == nregs
-		&& (! (refers_to_regno_for_reload_p
-		       (regno, (regno + hard_regno_nregs[regno][inmode]),
-				in, (rtx *)0))
-		    || can_reload_into (in, regno, inmode)))
-	      {
-		rld[i].reg_rtx = gen_rtx_REG (rel_mode, regno);
-		break;
-	      }
-	  }
+	      /* ??? Why is this code so different from the previous?
+		 Is there any simple coherent way to describe the two
+		 together?  What's going on here.  */
+	      if (in == out
+		  && (GET_CODE (in) != SUBREG
+		      || (((GET_MODE_SIZE (GET_MODE (in))
+			    + (UNITS_PER_WORD - 1))
+			   / UNITS_PER_WORD)
+			  != ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
+			       + (UNITS_PER_WORD - 1))
+			      / UNITS_PER_WORD))))
+		continue;
+
+	      for (offs = 0; offs < nregs; offs++)
+		if (fixed_regs[subregno + offs]
+		    || !TEST_HARD_REG_BIT (reg_class_contents[(int) class],
+					   subregno + offs))
+		  break;
+
+	      if (offs == nregs
+		  && (!refers_to_regno_for_reload_p
+		      (subregno, subregno + hard_regno_nregs[regno][inmode],
+		       in, NULL)
+		      || can_reload_into (in, subregno, inmode)))
+		{
+		  rld[i].reg_rtx = gen_rtx_REG (rel_mode, subregno);
+		  found = true;
+		  break;
+		}
+	    }
+
+	  if (found)
+	    break;
+	}
     }
 
   if (out)
Index: gcc/global.c
===================================================================
--- gcc/global.c	(revision 123904)
+++ gcc/global.c	(working copy)
@@ -183,6 +183,10 @@ static int allocno_row_words;
  (conflicts[(I) * allocno_row_words + (unsigned) (J) / INT_BITS]	\
   & ((INT_TYPE) 1 << ((unsigned) (J) % INT_BITS)))
 
+#define CLEAR_CONFLICT(I, J) \
+ (conflicts[(I) * allocno_row_words + (J) / INT_BITS]	\
+  &= ~((INT_TYPE) 1 << ((J) % INT_BITS)))
+
 /* For any allocno set in ALLOCNO_SET, set ALLOCNO to that allocno,
    and execute CODE.  */
 #define EXECUTE_IF_SET_IN_ALLOCNO_SET(ALLOCNO_SET, ALLOCNO, CODE)	\
@@ -300,7 +304,7 @@ static void mirror_conflicts (void);
 static void expand_preferences (void);
 static void prune_preferences (void);
 static void find_reg (int, HARD_REG_SET, int, int, int);
-static void record_one_conflict (int);
+static void record_one_conflict (int, int);
 static void record_conflicts (int *, int);
 static void mark_reg_store (rtx, rtx, void *);
 static void mark_reg_clobber (rtx, rtx, void *);
@@ -776,7 +780,7 @@ global_conflicts (void)
 		unsigned int regno = EH_RETURN_DATA_REGNO (i);
 		if (regno == INVALID_REGNUM)
 		  break;
-		record_one_conflict (regno);
+		record_one_conflict (regno, -1);
 	      }
 	  }
 #endif
@@ -802,7 +806,7 @@ global_conflicts (void)
 					       allocno[ax].no_stack_reg = 1;
 					     });
 	      for (ax = FIRST_STACK_REG; ax <= LAST_STACK_REG; ax++)
-		record_one_conflict (ax);
+		record_one_conflict (ax, -1);
 #endif
 
 	      /* No need to record conflicts for call clobbered regs if we have
@@ -811,7 +815,7 @@ global_conflicts (void)
 	      if (! current_function_has_nonlocal_label)
 		for (ax = 0; ax < FIRST_PSEUDO_REGISTER; ax++)
 		  if (call_used_regs [ax])
-		    record_one_conflict (ax);
+		    record_one_conflict (ax, -1);
 	    }
 	}
       }
@@ -852,7 +856,7 @@ global_conflicts (void)
 	      /* Mark any registers clobbered by INSN as live,
 		 so they conflict with the inputs.  */
 
-	      note_stores (PATTERN (insn), mark_reg_clobber, NULL);
+	      note_stores (PATTERN (insn), mark_reg_clobber, insn);
 
 	      /* Mark any registers dead after INSN as dead now.  */
 
@@ -865,12 +869,12 @@ global_conflicts (void)
 		 Clobbers are processed again, so they conflict with
 		 the registers that are set.  */
 
-	      note_stores (PATTERN (insn), mark_reg_store, NULL);
+	      note_stores (PATTERN (insn), mark_reg_store, insn);
 
 #ifdef AUTO_INC_DEC
 	      for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
 		if (REG_NOTE_KIND (link) == REG_INC)
-		  mark_reg_store (XEXP (link, 0), NULL_RTX, NULL);
+		  mark_reg_store (XEXP (link, 0), NULL_RTX, insn);
 #endif
 
 	      /* If INSN has multiple outputs, then any reg that dies here
@@ -1434,19 +1438,32 @@ retry_global_alloc (int regno, HARD_REG_
    and everything currently live.
    REGNO must not be a pseudo reg that was allocated
    by local_alloc; such numbers must be translated through
-   reg_renumber before calling here.  */
+   reg_renumber before calling here.
+   If NO_CONFLICT_REGNO is not -1, it is a register for which we
+   should not record a conflict.  */
 
 static void
-record_one_conflict (int regno)
+record_one_conflict (int regno, int no_conflict_regno)
 {
+  int no_conflict_allocno;
   int j;
 
+  no_conflict_allocno = -1;
+  if (no_conflict_regno != -1)
+    {
+      if (no_conflict_regno >= FIRST_PSEUDO_REGISTER)
+	no_conflict_allocno = reg_allocno[no_conflict_regno];
+      if (reg_renumber[no_conflict_regno] >= 0)
+	no_conflict_regno = reg_renumber[no_conflict_regno];
+    }
+
   if (regno < FIRST_PSEUDO_REGISTER)
     /* When a hard register becomes live,
        record conflicts with live pseudo regs.  */
     EXECUTE_IF_SET_IN_ALLOCNO_SET (allocnos_live, j,
       {
-	SET_HARD_REG_BIT (allocno[j].hard_reg_conflicts, regno);
+	if (j != no_conflict_allocno)
+	  SET_HARD_REG_BIT (allocno[j].hard_reg_conflicts, regno);
       });
   else
     /* When a pseudo-register becomes live,
@@ -1455,10 +1472,24 @@ record_one_conflict (int regno)
     {
       int ialloc = reg_allocno[regno];
       int ialloc_prod = ialloc * allocno_row_words;
+      bool clear;
+
+      if (no_conflict_regno == -1 || !HARD_REGISTER_NUM_P (no_conflict_regno))
+	IOR_HARD_REG_SET (allocno[ialloc].hard_reg_conflicts, hard_regs_live);
+      else
+	{
+	  HARD_REG_SET live;
 
-      IOR_HARD_REG_SET (allocno[ialloc].hard_reg_conflicts, hard_regs_live);
+	  COPY_HARD_REG_SET (live, hard_regs_live);
+	  CLEAR_HARD_REG_BIT (live, no_conflict_regno);
+	  IOR_HARD_REG_SET (allocno[ialloc].hard_reg_conflicts, live);
+	}
+      clear = (no_conflict_allocno != -1
+	       && !CONFLICTP (ialloc, no_conflict_allocno));
       for (j = allocno_row_words - 1; j >= 0; j--)
 	conflicts[ialloc_prod + j] |= allocnos_live[j];
+      if (clear)
+	CLEAR_CONFLICT (ialloc, no_conflict_allocno);
     }
 }
 
@@ -1526,9 +1557,11 @@ mirror_conflicts (void)
    a REG_INC note was found for it).  */
 
 static void
-mark_reg_store (rtx reg, rtx setter, void *data ATTRIBUTE_UNUSED)
+mark_reg_store (rtx reg, rtx setter, void *data)
 {
+  rtx insn = (rtx) data;
   int regno;
+  int no_conflict_regno;
 
   if (GET_CODE (reg) == SUBREG)
     reg = SUBREG_REG (reg);
@@ -1541,6 +1574,19 @@ mark_reg_store (rtx reg, rtx setter, voi
   if (setter && GET_CODE (setter) != CLOBBER)
     set_preference (reg, SET_SRC (setter));
 
+  /* If the source is a subreg which dies in this set, then this set
+     does not itself introduce a conflict with the source.  */
+  no_conflict_regno = -1;
+  if (setter
+      && GET_CODE (setter) != CLOBBER
+      && GET_CODE (SET_SRC (setter)) == SUBREG)
+    {
+      rtx note = find_reg_note (insn, REG_SUBREG_DEAD, NULL_RTX);
+
+      if (note && rtx_equal_p (SET_SRC (setter), XEXP (note, 0)))
+	no_conflict_regno = REGNO (SUBREG_REG (SET_SRC (setter)));
+    }
+
   regno = REGNO (reg);
 
   /* Either this is one of the max_allocno pseudo regs not allocated,
@@ -1550,7 +1596,7 @@ mark_reg_store (rtx reg, rtx setter, voi
       if (reg_allocno[regno] >= 0)
 	{
 	  SET_ALLOCNO_LIVE (reg_allocno[regno]);
-	  record_one_conflict (regno);
+	  record_one_conflict (regno, no_conflict_regno);
 	}
     }
 
@@ -1563,7 +1609,7 @@ mark_reg_store (rtx reg, rtx setter, voi
       int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
       while (regno < last)
 	{
-	  record_one_conflict (regno);
+	  record_one_conflict (regno, no_conflict_regno);
 	  SET_HARD_REG_BIT (hard_regs_live, regno);
 	  regno++;
 	}
@@ -1600,7 +1646,7 @@ mark_reg_conflicts (rtx reg)
   if (regno >= FIRST_PSEUDO_REGISTER)
     {
       if (reg_allocno[regno] >= 0)
-	record_one_conflict (regno);
+	record_one_conflict (regno, -1);
     }
 
   if (reg_renumber[regno] >= 0)
@@ -1612,7 +1658,7 @@ mark_reg_conflicts (rtx reg)
       int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
       while (regno < last)
 	{
-	  record_one_conflict (regno);
+	  record_one_conflict (regno, -1);
 	  regno++;
 	}
     }
@@ -1670,7 +1716,7 @@ mark_reg_live_nc (int regno, enum machin
 
 /* Try to set a preference for an allocno to a hard register.
    We are passed DEST and SRC which are the operands of a SET.  It is known
-   that SRC is a register.  If SRC or the first operand of SRC is a register,
+   that DEST is a register.  If SRC or the first operand of SRC is a register,
    try to set a preference.  If one of the two is a hard register and the other
    is a pseudo-register, mark the preference.
 
Index: gcc/local-alloc.c
===================================================================
--- gcc/local-alloc.c	(revision 123904)
+++ gcc/local-alloc.c	(working copy)
@@ -1855,6 +1855,16 @@ combine_regs (rtx usedreg, rtx setreg, i
   int usize, ssize;
   int sqty;
 
+  /* If we are setting a complete register from a subreg which dies
+     here, then we can tie the complete register to the subreg.  */
+  if (GET_CODE (usedreg) == SUBREG && GET_CODE (setreg) != SUBREG)
+    {
+      rtx note = find_reg_note (insn, REG_SUBREG_DEAD, NULL_RTX);
+
+      if (note && rtx_equal_p (usedreg, XEXP (note, 0)))
+	already_dead = 1;
+    }
+
   /* Determine the numbers and sizes of registers being used.  If a subreg
      is present that does not change the entire register, don't consider
      this a copy insn.  */
@@ -1930,8 +1940,8 @@ combine_regs (rtx usedreg, rtx setreg, i
      more than once.  In either event, we can't do anything with it.  */
   if ((ureg >= FIRST_PSEUDO_REGISTER && reg_qty[ureg] < 0)
       /* Do not combine registers unless one fits within the other.  */
-      || (offset > 0 && usize + offset > ssize)
-      || (offset < 0 && usize + offset < ssize)
+      || (offset > 0 && ssize + offset > usize)
+      || (offset < 0 && ssize + offset < usize)
       /* Do not combine with a smaller already-assigned object
 	 if that smaller object is already combined with something bigger.  */
       || (ssize > usize && ureg >= FIRST_PSEUDO_REGISTER
@@ -2516,6 +2526,276 @@ requires_inout (const char *p)
   return num_matching_alts;
 }
 
+/* This is used to pass data to mark_subregs via for_each_rtx.  */
+
+struct mark_subregs_data
+{
+  /* The register number we are looking for.  */
+  unsigned int regno;
+  /* The address of the bitmask we are setting.  */
+  unsigned HOST_WIDE_INT *subreg_bitmask;
+  /* Whether we set any elements in subregs_bitmask.  */
+  bool any;
+};
+
+/* This is called via for_each_rtx.  DATA points to a struct
+   mark_subregs_data.  If we find the DATA->REGNO unadorned, we return
+   1.  If we find DATA->REGNO within a SUBREG, we mark the appropriate
+   bits in DATA->SUBREG_BITMASK, where each bit represents a single
+   subreg of size UNITS_PER_WORD.  We set DATA->ANY if we set any of
+   the bits in DATA->SUBREG_BITMASK.  */
+
+static int
+mark_subregs (rtx *px, void *data)
+{
+  rtx x = *px;
+  struct mark_subregs_data *pmsd = (struct mark_subregs_data *) data;
+
+  if (REG_P (x) && REGNO (x) == pmsd->regno)
+    return 1;
+  else if (GET_CODE (x) == SUBREG
+	   && REG_P (SUBREG_REG (x))
+	   && REGNO (SUBREG_REG (x)) == pmsd->regno)
+    {
+      int byte;
+
+      if (SUBREG_BYTE (x) % UNITS_PER_WORD != 0)
+	return 1;
+
+      for (byte = 0;
+	   byte < GET_MODE_SIZE (GET_MODE (x));
+	   byte += UNITS_PER_WORD)
+	{
+	  int bit;
+
+	  bit = (SUBREG_BYTE (x) + byte) / UNITS_PER_WORD;
+	  if (bit >= HOST_BITS_PER_WIDE_INT)
+	    return 1;
+
+	  *pmsd->subreg_bitmask |= (unsigned HOST_WIDE_INT) 1 << bit;
+	}
+
+      pmsd->any = true;
+
+      return -1;
+    }
+  else if (GET_CODE (x) == SET)
+    {
+      if (for_each_rtx (&SET_SRC (x), mark_subregs, data) != 0)
+	return 1;
+
+      /* Ignore sets of a register.  We know that the register we care
+	 about dies in this insn, and we don't want to get confused by
+	 an irrelevant SET.  */
+      if (REG_P (SET_DEST (x))
+	  || (GET_CODE (SET_DEST (x)) == SUBREG
+	      && REG_P (SUBREG_REG (SET_DEST (x)))))
+	return -1;
+
+      if (for_each_rtx (&SET_DEST (x), mark_subregs, data) != 0)
+	return 1;
+    }
+  else if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC
+	   && GET_CODE (XEXP (x, 0)) == SUBREG
+	   && REG_P (SUBREG_REG (XEXP (x, 0)))
+	   && REGNO (SUBREG_REG (XEXP (x, 0))) == pmsd->regno)
+    return 1;
+
+  return 0;
+}
+
+/* This is used to pass data to track_subregs via for_each_rtx.  */
+
+struct track_subregs_data
+{
+  /* The insn we are looking at.  */
+  rtx insn;
+  /* The current subreg usage information.  */
+  unsigned HOST_WIDE_INT *subreg_usage;
+};
+
+/* This is called via for_each_rtx.  DATA points to a struct
+   track_subregs_data.
+
+   For each SUBREG we see that is not set by DATA->INSN, look to see
+   if we are currently tracking changes to it (subreg_usage[regno] !=
+   0).  If we are, it means that the register dies in a following
+   insn, and the bits set in subreg_usage[regno] are the bits which
+   are live at that insn.  If we are using words from this subreg
+   which are not set in subreg_usage[regno], then we know that this is
+   the last insn in which they are used, so they are dead.  Add a
+   REG_SUBREG_DEAD note for those words, and mark the appropriate bits
+   in subreg_usage[regno].
+
+   For each SUBREG which is set, clear the bits in subreg_usage, to
+   indicate that they are not live before this insn.
+
+   For each REG which is used or set outside of a SUBREG, clear the
+   corresponding entry in subreg_usage, to indicate that we can no
+   longer do anything useful with this register.  */
+
+static int
+track_subregs (rtx *px, void *data)
+{
+  rtx x = *px;
+  struct track_subregs_data *ptsd = (struct track_subregs_data *) data;
+
+  if (GET_CODE (x) == SUBREG
+      && REG_P (SUBREG_REG (x))
+      && !HARD_REGISTER_P (SUBREG_REG (x)))
+    {
+      int regno, byte;
+      bool dies;
+
+      regno = REGNO (SUBREG_REG (x));
+      if (ptsd->subreg_usage[regno] == 0)
+	return -1;
+
+      if (SUBREG_BYTE (x) % UNITS_PER_WORD != 0)
+	{
+	  ptsd->subreg_usage[regno] = 0;
+	  return -1;
+	}
+
+      dies = false;
+      for (byte = 0;
+	   byte < GET_MODE_SIZE (GET_MODE (x));
+	   byte += UNITS_PER_WORD)
+	{
+	  int bit;
+
+	  bit = (SUBREG_BYTE (x) + byte) / UNITS_PER_WORD;
+	  if (bit >= HOST_BITS_PER_WIDE_INT)
+	    {
+	      ptsd->subreg_usage[regno] = 0;
+	      return -1;
+	    }
+
+	  if ((ptsd->subreg_usage[regno]
+	       & ((unsigned HOST_WIDE_INT) 1 << bit))
+	      != 0)
+	    dies = true;
+	  else
+	    ptsd->subreg_usage[regno] |= (unsigned HOST_WIDE_INT) 1 << bit;
+	}
+
+      if (!dies)
+	{
+	  /* This register dies in a later insn, and this subreg is
+	     live now, but this subreg is not live at the time that
+	     the register dies.  Add a SUBREG_DEAD note.  */
+	  set_unique_reg_note (ptsd->insn, REG_SUBREG_DEAD, copy_rtx (x));
+	}
+
+      return -1;
+    }
+  else if (GET_CODE (x) == REG)
+    {
+      ptsd->subreg_usage[REGNO (x)] = 0;
+      return -1;
+    }
+  else if (GET_CODE (x) == SET)
+    {
+      /* If we see a SET of a SUBREG of a REG, stop tracking that
+	 register.  For a set of a SUBREG we could in some cases keep
+	 tracking it, but we have to be careful about how we handle
+	 the SET_SRC.  */
+      if (GET_CODE (SET_DEST (x)) == SUBREG
+	  && REG_P (SUBREG_REG (SET_DEST (x))))
+	ptsd->subreg_usage[REGNO (SUBREG_REG (SET_DEST (x)))] = 0;
+      else
+	for_each_rtx (&SET_DEST (x), track_subregs, data);
+
+      for_each_rtx (&SET_SRC (x), track_subregs, data);
+
+      return -1;
+    }
+  else if (GET_RTX_CLASS (GET_CODE (x)) == RTX_AUTOINC
+	   && GET_CODE (XEXP (x, 0)) == SUBREG
+	   && REG_P (SUBREG_REG (XEXP (x, 0))))
+    {
+      ptsd->subreg_usage[REGNO (XEXP (x, 0))] = 0;
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Add REG_SUBREG_DEAD notes.  We do this by walking backward through
+   basic blocks.  When we see a REG_DEAD note that refers to a SUBREG
+   of a pseudo-register, where the register is larger than
+   UNITS_PER_WORD, we see if we find any earlier reference to other
+   word sized SUBREGs of that register.  If we do, we add a
+   REG_SUBREG_DEAD note.  We only do this within basic blocks because
+   we don't expect it to be useful across basic blocks; the
+   lower-subreg pass should have already caught most of those cases
+   anyhow.  We use the REG_SUBREG_DEAD notes primarily to get good
+   conflict information when moving two word_mode registers into a
+   double word_mode register, and vice-versa.  */
+
+static void
+add_subreg_death_notes (void)
+{
+  int cregs;
+  unsigned HOST_WIDE_INT *subreg_usage;
+  basic_block bb;
+
+  /* SUBREG_DEAD notes are only needed for optimizing register
+     allocation, they are not needed for correctness.  */
+  if (!optimize)
+    return;
+
+  cregs = max_reg_num ();
+  subreg_usage = XNEWVEC (unsigned HOST_WIDE_INT, cregs);
+
+  FOR_EACH_BB (bb)
+    {
+      bool any;
+      rtx insn;
+
+      memset (subreg_usage, 0, cregs * sizeof (*subreg_usage));
+      any = false;
+
+      FOR_BB_INSNS_REVERSE (bb, insn)
+	{
+	  rtx note;
+
+	  if (!INSN_P (insn))
+	    continue;
+
+	  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+	    {
+	      if (REG_NOTE_KIND (note) == REG_DEAD
+		  && !HARD_REGISTER_P (XEXP (note, 0))
+		  && (GET_MODE_SIZE (GET_MODE (XEXP (note, 0)))
+		      > UNITS_PER_WORD))
+		{
+		  struct mark_subregs_data msd;
+
+		  msd.regno = REGNO (XEXP (note, 0));
+		  msd.subreg_bitmask = &subreg_usage[msd.regno];
+		  msd.any = false;
+		  if (for_each_rtx (&PATTERN (insn), mark_subregs, &msd) != 0)
+		    subreg_usage[msd.regno] = 0;
+		  else if (msd.any)
+		    any = true;
+		}
+	    }
+
+	  if (any)
+	    {
+	      struct track_subregs_data tsd;
+
+	      tsd.insn = insn;
+	      tsd.subreg_usage = subreg_usage;
+	      for_each_rtx (&PATTERN (insn), track_subregs, &tsd);
+	    }
+	}
+    }
+
+  free (subreg_usage);
+}
+
 void
 dump_local_alloc (FILE *file)
 {
@@ -2549,6 +2829,7 @@ rest_of_handle_local_alloc (void)
   allocate_initial_values (reg_equiv_memory_loc);
 
   regclass (get_insns (), max_reg_num ());
+  add_subreg_death_notes ();
   rebuild_notes = local_alloc ();
 
   /* Local allocation may have turned an indirect jump into a direct
Index: gcc/config/i386/i386.md
===================================================================
--- gcc/config/i386/i386.md	(revision 123904)
+++ gcc/config/i386/i386.md	(working copy)
@@ -3398,7 +3398,7 @@
 (define_split
   [(set (match_operand:DI 0 "memory_operand" "")
      (zero_extend:DI (match_dup 0)))]
-  "TARGET_64BIT"
+  ""
   [(set (match_dup 4) (const_int 0))]
   "split_di (&operands[0], 1, &operands[3], &operands[4]);")
 
@@ -3413,13 +3413,15 @@
 
 (define_split
   [(set (match_operand:DI 0 "nonimmediate_operand" "")
-	(zero_extend:DI (match_operand:SI 1 "general_operand" "")))
+	(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && reload_completed
-   && !SSE_REG_P (operands[0]) && !MMX_REG_P (operands[0])"
-  [(set (match_dup 3) (match_dup 1))
-   (set (match_dup 4) (const_int 0))]
-  "split_di (&operands[0], 1, &operands[3], &operands[4]);")
+  "!TARGET_64BIT
+   && (!MEM_P (operands[0]) || !MEM_P (operands[1]))
+   && (!reload_completed
+       || (!MMX_REG_P (operands[0]) && !SSE_REG_P (operands[0])))"
+  [(set (match_dup 0) (match_dup 1))
+   (set (match_dup 2) (const_int 0))]
+  "split_di (&operands[0], 1, &operands[0], &operands[2]);")
 
 (define_insn "zero_extendhidi2"
   [(set (match_operand:DI 0 "register_operand" "=r")
@@ -4795,7 +4797,40 @@
 	(plus:DI (match_operand:DI 1 "nonimmediate_operand" "")
 		 (match_operand:DI 2 "general_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && reload_completed"
+  "!TARGET_64BIT"
+  [(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
+	      (set (match_dup 3)
+		   (plus:SI (plus:SI (match_dup 4) (match_dup 5))
+			    (truncate:SI
+			     (lshiftrt:DI
+			      (plus:DI
+			       (zero_extend:DI (match_dup 1))
+			       (zero_extend:DI (match_dup 2)))
+			      (const_int 32)))))
+	      (clobber (reg:CC FLAGS_REG))])]
+{
+  split_di (operands + 0, 1, operands + 0, operands + 3);
+  split_di (operands + 1, 1, operands + 1, operands + 4);
+  split_di (operands + 2, 1, operands + 2, operands + 5);
+})
+
+(define_insn_and_split "*adddi3_2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r,rm,r")
+	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0")
+		 (match_operand:SI 2 "general_operand" "ri,rm,ri,rm")))
+   (set (match_operand:SI 3 "nonimmediate_operand" "=rm,r,rm,r")
+	(plus:SI (plus:SI (match_operand:SI 4 "general_operand" "3,3,ri,rm")
+			  (match_operand:SI 5 "general_operand" "ri,rm,3,3"))
+		 (truncate:SI
+		  (lshiftrt:DI
+		   (plus:DI
+		    (zero_extend:DI (match_dup 1))
+		    (zero_extend:DI (match_dup 2)))
+		   (const_int 32)))))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT"
+  "#"
+  "&& reload_completed"
   [(parallel [(set (reg:CC FLAGS_REG) (unspec:CC [(match_dup 1) (match_dup 2)]
 					  UNSPEC_ADD_CARRY))
 	      (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))])
@@ -4804,9 +4839,16 @@
 				     (match_dup 4))
 			    (match_dup 5)))
 	      (clobber (reg:CC FLAGS_REG))])]
-  "split_di (operands+0, 1, operands+0, operands+3);
-   split_di (operands+1, 1, operands+1, operands+4);
-   split_di (operands+2, 1, operands+2, operands+5);")
+{
+  /* We can only have one pair of commutative operands in an insn, so
+     we commute operands 4 and 5 manually.  */
+  if (rtx_equal_p (operands[5], operands[3]))
+    {
+      rtx tmp = operands[4];
+      operands[4] = operands[5];
+      operands[5] = tmp;
+    }
+})
 
 (define_insn "adddi3_carry_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r")
@@ -6525,7 +6567,41 @@
 	(minus:DI (match_operand:DI 1 "nonimmediate_operand" "")
 		  (match_operand:DI 2 "general_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && reload_completed"
+  "!TARGET_64BIT"
+  [(parallel [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))
+	      (set (match_dup 3)
+		   (plus:SI (minus:SI (match_dup 4) (match_dup 5))
+			    (truncate:SI
+			     (ashiftrt:DI
+			      (minus:DI
+			       (sign_extend:DI (match_dup 1))
+			       (sign_extend:DI (match_dup 2)))
+			      (const_int 32)))))
+	      (clobber (reg:CC FLAGS_REG))])]
+{
+  split_di (operands + 0, 1, operands + 0, operands + 3);
+  split_di (operands + 1, 1, operands + 1, operands + 4);
+  split_di (operands + 2, 1, operands + 2, operands + 5);
+})
+
+(define_insn_and_split "*subdi3_2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
+	(minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
+		  (match_operand:SI 2 "general_operand" "ri,rm")))
+   (set (match_operand:SI 3 "nonimmediate_operand" "=rm,r")
+	(plus:SI
+	 (minus:SI (match_operand:SI 4 "nonimmediate_operand" "3,3")
+		   (match_operand:SI 5 "general_operand" "ri,rm"))
+	 (truncate:SI
+	  (ashiftrt:DI
+	   (minus:DI
+	    (sign_extend:DI (match_dup 1))
+	    (sign_extend:DI (match_dup 2)))
+	   (const_int 32)))))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT"
+  "#"
+  "&& reload_completed"
   [(parallel [(set (reg:CC FLAGS_REG) (compare:CC (match_dup 1) (match_dup 2)))
 	      (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))])
    (parallel [(set (match_dup 3)
@@ -6533,9 +6609,7 @@
 			     (plus:SI (ltu:SI (reg:CC FLAGS_REG) (const_int 0))
 				      (match_dup 5))))
 	      (clobber (reg:CC FLAGS_REG))])]
-  "split_di (operands+0, 1, operands+0, operands+3);
-   split_di (operands+1, 1, operands+1, operands+4);
-   split_di (operands+2, 1, operands+2, operands+5);")
+  "")
 
 (define_insn "subdi3_carry_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r")
@@ -9429,7 +9503,27 @@
   [(set (match_operand:DI 0 "nonimmediate_operand" "")
 	(neg:DI (match_operand:DI 1 "general_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && reload_completed"
+  "!TARGET_64BIT"
+  [(parallel [(set (match_dup 0) (neg:SI (match_dup 2)))
+	      (set (match_dup 1)
+		   (not:SI (minus:SI (match_dup 3)
+				     (eq:SI (match_dup 2) (const_int 0)))))
+	      (clobber (reg:CC FLAGS_REG))])]
+{
+  split_di (operands + 1, 1, operands + 2, operands + 3);
+  split_di (operands + 0, 1, operands + 0, operands + 1);
+})
+
+(define_insn_and_split "*negdi2_2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
+	(neg:SI (match_operand:SI 2 "nonimmediate_operand" "0")))
+   (set (match_operand:SI 1 "nonimmediate_operand" "=rm")
+	(not:SI (minus:SI (match_operand:SI 3 "nonimmediate_operand" "1")
+			  (eq:SI (match_dup 2) (const_int 0)))))
+   (clobber (reg:CC FLAGS_REG))]
+  "!TARGET_64BIT"
+  "#"
+  "&& reload_completed"
   [(parallel
     [(set (reg:CCZ FLAGS_REG)
 	  (compare:CCZ (neg:SI (match_dup 2)) (const_int 0)))
@@ -9444,8 +9538,7 @@
     [(set (match_dup 1)
 	  (neg:SI (match_dup 1)))
      (clobber (reg:CC FLAGS_REG))])]
-  "split_di (operands+1, 1, operands+2, operands+3);
-   split_di (operands+0, 1, operands+0, operands+1);")
+  "")
 
 (define_insn "*negdi2_1_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
@@ -10383,7 +10476,14 @@
 	(ashift:DI (match_operand:DI 1 "ashldi_input_operand" "")
 		   (match_operand:QI 2 "nonmemory_operand" "")))]
   ""
-  "ix86_expand_binary_operator (ASHIFT, DImode, operands); DONE;")
+{
+  if (!TARGET_64BIT
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) >= 32)
+    FAIL;
+  ix86_expand_binary_operator (ASHIFT, DImode, operands);
+  DONE;
+})
 
 (define_insn "*ashldi3_1_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=rm,r")
@@ -10555,8 +10655,7 @@
 	(ashift:DI (match_operand:DI 1 "nonmemory_operand" "")
 		   (match_operand:QI 2 "nonmemory_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && ((optimize > 0 && flag_peephole2)
-		     ? flow2_completed : reload_completed)"
+  "!TARGET_64BIT"
   [(const_int 0)]
   "ix86_split_ashl (operands, NULL_RTX, DImode); DONE;")
 
@@ -11344,7 +11443,14 @@
 	(ashiftrt:DI (match_operand:DI 1 "shiftdi_operand" "")
 		     (match_operand:QI 2 "nonmemory_operand" "")))]
   ""
-  "ix86_expand_binary_operator (ASHIFTRT, DImode, operands); DONE;")
+{
+  if (!TARGET_64BIT
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) >= 32)
+    FAIL;
+  ix86_expand_binary_operator (ASHIFTRT, DImode, operands);
+  DONE;
+})
 
 (define_insn "*ashrdi3_63_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=*d,rm")
@@ -12023,7 +12129,14 @@
 	(lshiftrt:DI (match_operand:DI 1 "shiftdi_operand" "")
 		     (match_operand:QI 2 "nonmemory_operand" "")))]
   ""
-  "ix86_expand_binary_operator (LSHIFTRT, DImode, operands); DONE;")
+{
+  if (!TARGET_64BIT
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) >= 32)
+    FAIL;
+  ix86_expand_binary_operator (LSHIFTRT, DImode, operands);
+  DONE;
+})
 
 (define_insn "*lshrdi3_1_one_bit_rex64"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
@@ -12148,8 +12261,7 @@
 	(lshiftrt:DI (match_operand:DI 1 "register_operand" "")
 		     (match_operand:QI 2 "nonmemory_operand" "")))
    (clobber (reg:CC FLAGS_REG))]
-  "!TARGET_64BIT && ((optimize > 0 && flag_peephole2)
-		     ? flow2_completed : reload_completed)"
+  "!TARGET_64BIT"
   [(const_int 0)]
   "ix86_split_lshr (operands, NULL_RTX, DImode); DONE;")
 
Index: gcc/config/i386/i386.c
===================================================================
--- gcc/config/i386/i386.c	(revision 123904)
+++ gcc/config/i386/i386.c	(working copy)
@@ -9476,7 +9476,7 @@ ix86_expand_clear (rtx dest)
   rtx tmp;
 
   /* We play register width games, which are only valid after reload.  */
-  gcc_assert (reload_completed);
+  gcc_assert (reload_completed || GET_MODE_SIZE (GET_MODE (dest)) >= 4);
 
   /* Avoid HImode and its attendant prefix byte.  */
   if (GET_MODE_SIZE (GET_MODE (dest)) < 4)
@@ -13288,10 +13288,27 @@ ix86_split_ashl (rtx *operands, rtx scra
 	  rtx x;
 
 	  if (TARGET_PARTIAL_REG_STALL && !optimize_size)
-	    x = gen_rtx_ZERO_EXTEND (mode == DImode ? SImode : DImode, operands[2]);
+	    {
+	      x = gen_rtx_ZERO_EXTEND (mode == DImode ? SImode : DImode,
+				       operands[2]);
+	      x = gen_rtx_SET (VOIDmode, high[0], x);
+	      if (reload_completed
+		  && (!TARGET_ZERO_EXTEND_WITH_AND || optimize_size))
+		emit_insn (x);
+	      else
+		{
+		  rtx y;
+
+		  y = gen_rtx_CLOBBER (VOIDmode,
+				       gen_rtx_REG (CCmode, FLAGS_REG));
+		  emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x, y)));
+		}
+	    }
 	  else
-	    x = gen_lowpart (mode == DImode ? SImode : DImode, operands[2]);
-	  emit_insn (gen_rtx_SET (VOIDmode, high[0], x));
+	    {
+	      x = gen_lowpart (mode == DImode ? SImode : DImode, operands[2]);
+	      emit_insn (gen_rtx_SET (VOIDmode, high[0], x));
+	    }
 
 	  emit_insn ((mode == DImode
 		      ? gen_lshrsi3
@@ -19137,6 +19154,15 @@ ix86_modes_tieable_p (enum machine_mode 
       && ix86_tieable_integer_mode_p (mode2))
     return true;
 
+  /* If MODE1 is an integer mode bigger than a word, and MODE2 is an
+     integer mode word-sized or larger, then we can just pick up the
+     relevant pices.  */
+  if (VALID_INT_MODE_P (mode1)
+      && GET_MODE_SIZE (mode1) > UNITS_PER_WORD
+      && VALID_INT_MODE_P (mode2)
+      && GET_MODE_SIZE (mode2) >= UNITS_PER_WORD)
+    return true;
+
   /* MODE2 being XFmode implies fp stack or general regs, which means we
      can tie any smaller floating point modes to it.  Note that we do not
      tie this with TFmode.  */
Index: gcc/reg-notes.def
===================================================================
--- gcc/reg-notes.def	(revision 123904)
+++ gcc/reg-notes.def	(working copy)
@@ -164,3 +164,7 @@ REG_NOTE (CROSSING_JUMP)
 /* This kind of note is generated at each to `setjmp', and similar
    functions that can return twice.  */
 REG_NOTE (SETJMP)
+
+/* The argument is a SUBREG of a pseudo-register which dies in this
+   insn.  Used during the register allocation pass.  */
+REG_NOTE (SUBREG_DEAD)

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