From fad2288b4b1e63fce8550d70f99bcc16e54bf539 Mon Sep 17 00:00:00 2001
From: Richard Sandiford <richard.sandiford@linaro.org>
Date: Wed, 3 Jan 2018 07:17:33 +0000
Subject: [PATCH] poly_int: REGMODE_NATURAL_SIZE

This patch makes target-independent code that uses REGMODE_NATURAL_SIZE
treat it as a poly_int rather than a constant.

2018-01-03  Richard Sandiford  <richard.sandiford@linaro.org>
	    Alan Hayward  <alan.hayward@arm.com>
	    David Sherwood  <david.sherwood@arm.com>

gcc/
	* combine.c (can_change_dest_mode): Handle polynomial
	REGMODE_NATURAL_SIZE.
	* expmed.c (store_bit_field_1): Likewise.
	* expr.c (store_constructor): Likewise.
	* emit-rtl.c (validate_subreg): Operate on polynomial mode sizes
	and polynomial REGMODE_NATURAL_SIZE.
	(gen_lowpart_common): Likewise.
	* reginfo.c (record_subregs_of_mode): Likewise.
	* rtlanal.c (read_modify_subreg_p): Likewise.

Co-Authored-By: Alan Hayward <alan.hayward@arm.com>
Co-Authored-By: David Sherwood <david.sherwood@arm.com>

From-SVN: r256149
---
 gcc/ChangeLog  | 14 +++++++++++++
 gcc/combine.c  |  4 ++--
 gcc/emit-rtl.c | 53 +++++++++++++++++++++++++++++++++-----------------
 gcc/expmed.c   |  2 +-
 gcc/expr.c     |  4 ++--
 gcc/reginfo.c  | 12 ++++++++----
 gcc/rtlanal.c  | 12 +++++++-----
 7 files changed, 69 insertions(+), 32 deletions(-)

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b215b4f72780..5cb1c23a070f 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,17 @@
+2018-01-03  Richard Sandiford  <richard.sandiford@linaro.org>
+	    Alan Hayward  <alan.hayward@arm.com>
+	    David Sherwood  <david.sherwood@arm.com>
+
+	* combine.c (can_change_dest_mode): Handle polynomial
+	REGMODE_NATURAL_SIZE.
+	* expmed.c (store_bit_field_1): Likewise.
+	* expr.c (store_constructor): Likewise.
+	* emit-rtl.c (validate_subreg): Operate on polynomial mode sizes
+	and polynomial REGMODE_NATURAL_SIZE.
+	(gen_lowpart_common): Likewise.
+	* reginfo.c (record_subregs_of_mode): Likewise.
+	* rtlanal.c (read_modify_subreg_p): Likewise.
+
 2018-01-03  Richard Sandiford  <richard.sandiford@linaro.org>
 	    Alan Hayward  <alan.hayward@arm.com>
 	    David Sherwood  <david.sherwood@arm.com>
diff --git a/gcc/combine.c b/gcc/combine.c
index 31e6a4f68254..eb02f295c816 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -2477,8 +2477,8 @@ can_change_dest_mode (rtx x, int added_sets, machine_mode mode)
 
   /* Don't change between modes with different underlying register sizes,
      since this could lead to invalid subregs.  */
-  if (REGMODE_NATURAL_SIZE (mode)
-      != REGMODE_NATURAL_SIZE (GET_MODE (x)))
+  if (maybe_ne (REGMODE_NATURAL_SIZE (mode),
+		REGMODE_NATURAL_SIZE (GET_MODE (x))))
     return false;
 
   regno = REGNO (x);
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index e282b3d13cdc..f2d4940fb4f3 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -894,8 +894,13 @@ bool
 validate_subreg (machine_mode omode, machine_mode imode,
 		 const_rtx reg, poly_uint64 offset)
 {
-  unsigned int isize = GET_MODE_SIZE (imode);
-  unsigned int osize = GET_MODE_SIZE (omode);
+  poly_uint64 isize = GET_MODE_SIZE (imode);
+  poly_uint64 osize = GET_MODE_SIZE (omode);
+
+  /* The sizes must be ordered, so that we know whether the subreg
+     is partial, paradoxical or complete.  */
+  if (!ordered_p (isize, osize))
+    return false;
 
   /* All subregs must be aligned.  */
   if (!multiple_p (offset, osize))
@@ -905,7 +910,7 @@ validate_subreg (machine_mode omode, machine_mode imode,
   if (maybe_ge (offset, isize))
     return false;
 
-  unsigned int regsize = REGMODE_NATURAL_SIZE (imode);
+  poly_uint64 regsize = REGMODE_NATURAL_SIZE (imode);
 
   /* ??? This should not be here.  Temporarily continue to allow word_mode
      subregs of anything.  The most common offender is (subreg:SI (reg:DF)).
@@ -915,7 +920,7 @@ validate_subreg (machine_mode omode, machine_mode imode,
     ;
   /* ??? Similarly, e.g. with (subreg:DF (reg:TI)).  Though store_bit_field
      is the culprit here, and not the backends.  */
-  else if (osize >= regsize && isize >= osize)
+  else if (known_ge (osize, regsize) && known_ge (isize, osize))
     ;
   /* Allow component subregs of complex and vector.  Though given the below
      extraction rules, it's not always clear what that means.  */
@@ -934,7 +939,7 @@ validate_subreg (machine_mode omode, machine_mode imode,
      (subreg:SI (reg:DF) 0) isn't.  */
   else if (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))
     {
-      if (! (isize == osize
+      if (! (known_eq (isize, osize)
 	     /* LRA can use subreg to store a floating point value in
 		an integer mode.  Although the floating point and the
 		integer modes need the same number of hard registers,
@@ -946,7 +951,7 @@ validate_subreg (machine_mode omode, machine_mode imode,
     }
 
   /* Paradoxical subregs must have offset zero.  */
-  if (osize > isize)
+  if (maybe_gt (osize, isize))
     return known_eq (offset, 0U);
 
   /* This is a normal subreg.  Verify that the offset is representable.  */
@@ -966,6 +971,12 @@ validate_subreg (machine_mode omode, machine_mode imode,
       return subreg_offset_representable_p (regno, imode, offset, omode);
     }
 
+  /* The outer size must be ordered wrt the register size, otherwise
+     we wouldn't know at compile time how many registers the outer
+     mode occupies.  */
+  if (!ordered_p (osize, regsize))
+    return false;
+
   /* For pseudo registers, we want most of the same checks.  Namely:
 
      Assume that the pseudo register will be allocated to hard registers
@@ -976,10 +987,12 @@ validate_subreg (machine_mode omode, machine_mode imode,
 
      Given that we've already checked the mode and offset alignment,
      we only have to check subblock subregs here.  */
-  if (osize < regsize
+  if (maybe_lt (osize, regsize)
       && ! (lra_in_progress && (FLOAT_MODE_P (imode) || FLOAT_MODE_P (omode))))
     {
-      poly_uint64 block_size = MIN (isize, regsize);
+      /* It is invalid for the target to pick a register size for a mode
+	 that isn't ordered wrt to the size of that mode.  */
+      poly_uint64 block_size = ordered_min (isize, regsize);
       unsigned int start_reg;
       poly_uint64 offset_within_reg;
       if (!can_div_trunc_p (offset, block_size, &start_reg, &offset_within_reg)
@@ -1518,39 +1531,43 @@ maybe_set_max_label_num (rtx_code_label *x)
 rtx
 gen_lowpart_common (machine_mode mode, rtx x)
 {
-  int msize = GET_MODE_SIZE (mode);
-  int xsize;
+  poly_uint64 msize = GET_MODE_SIZE (mode);
   machine_mode innermode;
 
   /* Unfortunately, this routine doesn't take a parameter for the mode of X,
      so we have to make one up.  Yuk.  */
   innermode = GET_MODE (x);
   if (CONST_INT_P (x)
-      && msize * BITS_PER_UNIT <= HOST_BITS_PER_WIDE_INT)
+      && known_le (msize * BITS_PER_UNIT,
+		   (unsigned HOST_WIDE_INT) HOST_BITS_PER_WIDE_INT))
     innermode = int_mode_for_size (HOST_BITS_PER_WIDE_INT, 0).require ();
   else if (innermode == VOIDmode)
     innermode = int_mode_for_size (HOST_BITS_PER_DOUBLE_INT, 0).require ();
 
-  xsize = GET_MODE_SIZE (innermode);
-
   gcc_assert (innermode != VOIDmode && innermode != BLKmode);
 
   if (innermode == mode)
     return x;
 
+  /* The size of the outer and inner modes must be ordered.  */
+  poly_uint64 xsize = GET_MODE_SIZE (innermode);
+  if (!ordered_p (msize, xsize))
+    return 0;
+
   if (SCALAR_FLOAT_MODE_P (mode))
     {
       /* Don't allow paradoxical FLOAT_MODE subregs.  */
-      if (msize > xsize)
+      if (maybe_gt (msize, xsize))
 	return 0;
     }
   else
     {
       /* MODE must occupy no more of the underlying registers than X.  */
-      unsigned int regsize = REGMODE_NATURAL_SIZE (innermode);
-      unsigned int mregs = CEIL (msize, regsize);
-      unsigned int xregs = CEIL (xsize, regsize);
-      if (mregs > xregs)
+      poly_uint64 regsize = REGMODE_NATURAL_SIZE (innermode);
+      unsigned int mregs, xregs;
+      if (!can_div_away_from_zero_p (msize, regsize, &mregs)
+	  || !can_div_away_from_zero_p (xsize, regsize, &xregs)
+	  || mregs > xregs)
 	return 0;
     }
 
diff --git a/gcc/expmed.c b/gcc/expmed.c
index 55bb353cd759..b48248ff722f 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -779,7 +779,7 @@ store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, poly_uint64 bitnum,
 	 In the latter case, use subreg on the rhs side, not lhs.  */
       rtx sub;
       HOST_WIDE_INT regnum;
-      HOST_WIDE_INT regsize = REGMODE_NATURAL_SIZE (GET_MODE (op0));
+      poly_uint64 regsize = REGMODE_NATURAL_SIZE (GET_MODE (op0));
       if (known_eq (bitnum, 0U)
 	  && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (op0))))
 	{
diff --git a/gcc/expr.c b/gcc/expr.c
index 275199982e3a..ab49c559a2b0 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -6279,8 +6279,8 @@ store_constructor (tree exp, rtx target, int cleared, poly_int64 size,
 	   a constant.  But if more than one register is involved,
 	   this probably loses.  */
 	else if (REG_P (target) && TREE_STATIC (exp)
-		 && (GET_MODE_SIZE (GET_MODE (target))
-		     <= REGMODE_NATURAL_SIZE (GET_MODE (target))))
+		 && known_le (GET_MODE_SIZE (GET_MODE (target)),
+			      REGMODE_NATURAL_SIZE (GET_MODE (target))))
 	  {
 	    emit_move_insn (target, CONST0_RTX (GET_MODE (target)));
 	    cleared = 1;
diff --git a/gcc/reginfo.c b/gcc/reginfo.c
index 3d1c2ed51664..90d25c891d88 100644
--- a/gcc/reginfo.c
+++ b/gcc/reginfo.c
@@ -1294,10 +1294,14 @@ record_subregs_of_mode (rtx subreg, bool partial_def)
 	 subregs will be invalid.
 
 	 This relies on the fact that we've already been passed
-	 SUBREG with PARTIAL_DEF set to false.  */
-      unsigned int size = MAX (REGMODE_NATURAL_SIZE (shape.inner_mode),
-			       GET_MODE_SIZE (shape.outer_mode));
-      gcc_checking_assert (size < GET_MODE_SIZE (shape.inner_mode));
+	 SUBREG with PARTIAL_DEF set to false.
+
+	 The size of the outer mode must ordered wrt the size of the
+	 inner mode's registers, since otherwise we wouldn't know at
+	 compile time how many registers the outer mode occupies.  */
+      poly_uint64 size = MAX (REGMODE_NATURAL_SIZE (shape.inner_mode),
+			      GET_MODE_SIZE (shape.outer_mode));
+      gcc_checking_assert (known_lt (size, GET_MODE_SIZE (shape.inner_mode)));
       if (known_ge (shape.offset, size))
 	shape.offset -= size;
       else
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c
index e75ff103fcbf..78836edfc85d 100644
--- a/gcc/rtlanal.c
+++ b/gcc/rtlanal.c
@@ -1395,13 +1395,15 @@ modified_in_p (const_rtx x, const_rtx insn)
 bool
 read_modify_subreg_p (const_rtx x)
 {
-  unsigned int isize, osize;
   if (GET_CODE (x) != SUBREG)
     return false;
-  isize = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
-  osize = GET_MODE_SIZE (GET_MODE (x));
-  return isize > osize
-	 && isize > REGMODE_NATURAL_SIZE (GET_MODE (SUBREG_REG (x)));
+  poly_uint64 isize = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
+  poly_uint64 osize = GET_MODE_SIZE (GET_MODE (x));
+  poly_uint64 regsize = REGMODE_NATURAL_SIZE (GET_MODE (SUBREG_REG (x)));
+  /* The inner and outer modes of a subreg must be ordered, so that we
+     can tell whether they're paradoxical or partial.  */
+  gcc_checking_assert (ordered_p (isize, osize));
+  return (maybe_gt (isize, osize) && maybe_gt (isize, regsize));
 }
 
 /* Helper function for set_of.  */
-- 
GitLab