[PATCH][RFC] fix reload causing ICE in subreg_get_info on m68k (PR58369)

Mikael Pettersson mikpelinux@gmail.com
Fri Oct 18 20:40:00 GMT 2013


Jeff Law writes:
 > On 09/28/13 09:30, Mikael Pettersson wrote:
 > > This patch fixes PR58369, an ICE in subreg_get_info when compiling
 > > boost for m68k-linux.
 > >
 > > choose_reload_regs attempts to reload a DFmode (8-byte) reg, finds
 > > an XFmode (12-byte) reg in "last_reg", and calls subreg_regno_offset
 > > with these two modes and a subreg offset of zero.  However, this is
 > > not a correct lowpart subreg offset for big-endian and these two modes,
 > > so the lowpart subreg check in subreg_get_info fails, and the code
 > > continues to
 > >
 > >      gcc_assert ((GET_MODE_SIZE (xmode) % GET_MODE_SIZE (ymode)) == 0);
 > >
 > > which fails because (12 % 8) != 0.
 > >
 > > choose_reload_regs passes the constant zero, in all cases where the reg
 > > isn't already a subreg, as the subreg offset to subreg_regno_offset, even
 > > though lowpart subregs on big-endian targets require an explicit offset
 > > computation.  I think that is a bug.
 > >
 > > I believe other big-endian targets don't see this ICE because
 > > a) they define CANNOT_CHANGE_MODE_CLASS to reject differently-sized
 > >     modes in floating-point registers (which prevents this path in
 > >     choose_reload_regs), or
 > > b) their differently-sized modes are such that the size of a larger
 > >     mode is a whole multiple of the size of the smaller mode (which
 > >     allows the gcc_assert above to pass).
 > >
 > > This patch changes choose_reload_regs to call subreg_lowpart_offset
 > > to pass an endian-correct offset to subreg_regno_offset, except where
 > > the offset comes from a pre-existing subreg.
 > >
 > > [Defining CANNOT_CHANGE_MODE_CLASS appropriately for m68k also fixes
 > > the ICE, but I don't think the m68k backend really wants that, and I
 > > think it just papers over a generic bug.]
 > >
 > > Tested with trunk and 4.8 on {m68k,sparc64,powerpc64}-linux (big-endian),
 > > and on x86_64-linux/armv5tel-linux-gnueabi (little-endian).  No regressions.
 > >
 > > Comments?
 > > Is this Ok for trunk?
 > >
 > > gcc/
 > >
 > > 2013-09-28  Mikael Pettersson  <mikpelinux@gmail.com>
 > >
 > > 	PR rtl-optimization/58369
 > > 	* reload1.c (choose_reload_regs): Use subreg_lowpart_offset
 > > 	to pass endian-correct lowpart offset to subreg_regno_offset.
 > Thanks Mikael.  My only concern is the lack of adjustment when the value 
 > found was already a SUBREG.
 > 
 > ie, let's assume rld[r].in_reg was something like
 > (subreg:XF (reg:DF) 0)
 > 
 > and our target is (reg:DF)
 > 
 > In this case it seems to me we still want to compute the subreg offset, 
 > right?
 > 
 > jeff

Thanks Jeff.  Sorry about the delay, but it took me a while to work
out the details of the various cases (I was afraid of having to do
something like simplify_subreg, but the cases in reload are simpler),
and then to re-test on my various targets.

Let the reloadee be rld[r].in_reg, outermode be its mode, and innermode
be the mode of the hard reg in last_reg.

The trivial cases are when the reloadee is a plain REG or a SUBREG of a
hard reg.  For these, reload virtually forms a normal lowpart subreg of
last_reg, and subreg_lowpart_offset (outermode, innermode) computes the
endian-correct offset for subreg_regno_offset.  This is exactly what my
previous patch did.

In remaining cases the reloadee is a SUBREG of a pseudo.  Let outer_offset
be its BYTE, and middlemode be the mode of its REG.

Another simple case is when the reloadee is paradoxical.  Then outer_offset
is zero (by convention), and reload should just form a normal lowpart
subreg as in the trivial cases.  Even though the reloadee is paradoxical,
this subreg will fit thanks to the mode size check on lines 6546-6547.

If the reloadee is a normal lowpart SUBREG, then again reload should just
form a normal lowpart subreg as in the trivial cases.  (But see below.)

The tricky case is when the reloadee is a normal but not lowpart SUBREG.
We get the correct offset for reload's virtual subreg by adding outer_offset
to the lowpart offset of middlemode and innermode.  This works for both
big-endian and little-endian.  It also handles normal lowpart SUBREGs,
so we don't need to check for lowpart vs non-lowpart normal SUBREGs.

Tested with trunk and 4.8 on {m68k,sparc64,powerpc64,x86_64}-linux-gnu
and armv5tel-linux-gnueabi.  No regressions.

Ok for trunk?

gcc/

2013-10-18  Mikael Pettersson  <mikpelinux@gmail.com>

	PR rtl-optimization/58369
	* reload1.c (compute_reload_subreg_offset): Define.
	(choose_reload_regs): Use it to pass endian-correct
	offset to subreg_regno_offset.

--- gcc-4.9-20131006/gcc/reload1.c.~1~	2013-09-28 10:42:34.000000000 +0200
+++ gcc-4.9-20131006/gcc/reload1.c	2013-10-14 01:04:29.815104039 +0200
@@ -6363,6 +6363,34 @@ replaced_subreg (rtx x)
 }
 #endif
 
+/* Compute the offset to pass to subreg_regno_offset, for a pseudo of
+   mode OUTERMODE that is available in a hard reg of mode INNERMODE.
+   SUBREG is non-NULL if the pseudo is a subreg whose reg is a pseudo,
+   otherwise it is NULL.  */
+
+static int
+compute_reload_subreg_offset (enum machine_mode outermode, rtx subreg, enum machine_mode innermode)
+{
+  int outer_offset;
+  enum machine_mode middlemode;
+
+  if (! subreg)
+    return subreg_lowpart_offset (outermode, innermode);
+
+  outer_offset = SUBREG_BYTE (subreg);
+  middlemode = GET_MODE (SUBREG_REG (subreg));
+
+  /* If SUBREG is paradoxical then return the normal lowpart offset
+     for OUTERMODE and INNERMODE.  Our caller has already checked
+     that OUTERMODE fits in INNERMODE.  */
+  if (outer_offset == 0 && GET_MODE_SIZE (outermode) > GET_MODE_SIZE (middlemode))
+    return subreg_lowpart_offset (outermode, innermode);
+
+  /* SUBREG is normal, but may not be lowpart; return OUTER_OFFSET
+     plus the normal lowpart offset for MIDDLEMODE and INNERMODE.  */
+  return outer_offset + subreg_lowpart_offset (middlemode, innermode);
+}
+
 /* Assign hard reg targets for the pseudo-registers we must reload
    into hard regs for this insn.
    Also output the instructions to copy them in and out of the hard regs.
@@ -6500,6 +6528,7 @@ choose_reload_regs (struct insn_chain *c
 	      int byte = 0;
 	      int regno = -1;
 	      enum machine_mode mode = VOIDmode;
+	      rtx subreg = NULL_RTX;
 
 	      if (rld[r].in == 0)
 		;
@@ -6520,7 +6549,10 @@ choose_reload_regs (struct insn_chain *c
 		  if (regno < FIRST_PSEUDO_REGISTER)
 		    regno = subreg_regno (rld[r].in_reg);
 		  else
-		    byte = SUBREG_BYTE (rld[r].in_reg);
+		    {
+		      subreg = rld[r].in_reg;
+		      byte = SUBREG_BYTE (subreg);
+		    }
 		  mode = GET_MODE (rld[r].in_reg);
 		}
 #ifdef AUTO_INC_DEC
@@ -6558,6 +6590,7 @@ choose_reload_regs (struct insn_chain *c
 		  rtx last_reg = reg_last_reload_reg[regno];
 
 		  i = REGNO (last_reg);
+		  byte = compute_reload_subreg_offset (mode, subreg, GET_MODE (last_reg));
 		  i += subreg_regno_offset (i, GET_MODE (last_reg), byte, mode);
 		  last_class = REGNO_REG_CLASS (i);
 



More information about the Gcc-patches mailing list