diff mbox

Fix wrong code on dynamic array slices

Message ID 13359827.9XQWfz1nEL@polaris
State New
Headers show

Commit Message

Eric Botcazou Sept. 16, 2015, 7:07 a.m. UTC
Hi,

the attached Ada testcase fails on x86/Linux (32-bit only) because the FRE
pass wrongly deletes stores as redundant:

Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[0]
{lb: 4294967292 sz: 4} = 100;
Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[1]
{lb: 4294967292 sz: 4} = 1;
Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[2]
{lb: 4294967292 sz: 4} = 2;
Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[3]
{lb: 4294967292 sz: 4} = 3;
Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4]
{lb: 4294967292 sz: 4} = 4;

While the last 4 stores happen to be redundant (however the compiler cannot
really determine it), the first one isn't and deleting it yields wrong code.

These stores are part of a larger group of stores:

VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967292]{lb: 4294967292 
sz: 4} = -4;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967293]{lb: 4294967292 
sz: 4} = -3;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967294]{lb: 4294967292 
sz: 4} = -2;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967295]{lb: 4294967292 
sz: 4} = -1;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[0]{lb: 4294967292 sz: 4} = 
100;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[1]{lb: 4294967292 sz: 4} = 
1;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[2]{lb: 4294967292 sz: 4} = 
2;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[3]{lb: 4294967292 sz: 4} = 
3;
VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4]{lb: 4294967292 sz: 4} = 
4;

and the first 4 ones are correctly detected as *not* redundant.  What changes 
between the two groups of stores is the index or, more precisely, the sign of
the difference between the index and the low bound (4294967292 aka -4); it's
positive for the first group and negative for the second group.  The latter
case fools ao_ref_init_from_vn_reference because it doesn't properly truncate
it to the precision of the index, like for example get_ref_base_and_extent,
so as to make it positive again in this case, which in turn fools FRE.

So the attached patch offset_int'ifies ao_ref_init_from_vn_reference the same
way get_ref_base_and_extent was offset_int'ified (in fact double_int'ified at 
the time), adding the missing truncation in the process.

Bootstrapped/regtested on x86_64-suse-linux, OK for the mainline?


2015-09-16  Eric Botcazou  <ebotcazou@adacore.com>

	* tree-ssa-sccvn.c (ao_ref_init_from_vn_reference): Use offset_int for
	offset and size computations instead of HOST_WIDE_INT.


2015-09-16  Eric Botcazou  <ebotcazou@adacore.com>

	* gnat.dg/opt49.adb: New test.

Comments

Richard Biener Sept. 16, 2015, 8:57 a.m. UTC | #1
Ok.

Thanks,
Richard.

On Wed, Sep 16, 2015 at 9:07 AM, Eric Botcazou <ebotcazou@adacore.com> wrote:
> Hi,
>
> the attached Ada testcase fails on x86/Linux (32-bit only) because the FRE
> pass wrongly deletes stores as redundant:
>
> Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[0]
> {lb: 4294967292 sz: 4} = 100;
> Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[1]
> {lb: 4294967292 sz: 4} = 1;
> Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[2]
> {lb: 4294967292 sz: 4} = 2;
> Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[3]
> {lb: 4294967292 sz: 4} = 3;
> Deleted redundant store VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4]
> {lb: 4294967292 sz: 4} = 4;
>
> While the last 4 stores happen to be redundant (however the compiler cannot
> really determine it), the first one isn't and deleting it yields wrong code.
>
> These stores are part of a larger group of stores:
>
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967292]{lb: 4294967292
> sz: 4} = -4;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967293]{lb: 4294967292
> sz: 4} = -3;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967294]{lb: 4294967292
> sz: 4} = -2;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4294967295]{lb: 4294967292
> sz: 4} = -1;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[0]{lb: 4294967292 sz: 4} =
> 100;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[1]{lb: 4294967292 sz: 4} =
> 1;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[2]{lb: 4294967292 sz: 4} =
> 2;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[3]{lb: 4294967292 sz: 4} =
> 3;
> VIEW_CONVERT_EXPR<integer[4294967292:4]>(*a.18_41)[4]{lb: 4294967292 sz: 4} =
> 4;
>
> and the first 4 ones are correctly detected as *not* redundant.  What changes
> between the two groups of stores is the index or, more precisely, the sign of
> the difference between the index and the low bound (4294967292 aka -4); it's
> positive for the first group and negative for the second group.  The latter
> case fools ao_ref_init_from_vn_reference because it doesn't properly truncate
> it to the precision of the index, like for example get_ref_base_and_extent,
> so as to make it positive again in this case, which in turn fools FRE.
>
> So the attached patch offset_int'ifies ao_ref_init_from_vn_reference the same
> way get_ref_base_and_extent was offset_int'ified (in fact double_int'ified at
> the time), adding the missing truncation in the process.
>
> Bootstrapped/regtested on x86_64-suse-linux, OK for the mainline?
>
>
> 2015-09-16  Eric Botcazou  <ebotcazou@adacore.com>
>
>         * tree-ssa-sccvn.c (ao_ref_init_from_vn_reference): Use offset_int for
>         offset and size computations instead of HOST_WIDE_INT.
>
>
> 2015-09-16  Eric Botcazou  <ebotcazou@adacore.com>
>
>         * gnat.dg/opt49.adb: New test.
>
> --
> Eric Botcazou
diff mbox

Patch

Index: tree-ssa-sccvn.c
===================================================================
--- tree-ssa-sccvn.c	(revision 227805)
+++ tree-ssa-sccvn.c	(working copy)
@@ -953,9 +953,9 @@  ao_ref_init_from_vn_reference (ao_ref *r
   unsigned i;
   tree base = NULL_TREE;
   tree *op0_p = &base;
-  HOST_WIDE_INT offset = 0;
-  HOST_WIDE_INT max_size;
-  HOST_WIDE_INT size = -1;
+  offset_int offset = 0;
+  offset_int max_size;
+  offset_int size = -1;
   tree size_tree = NULL_TREE;
   alias_set_type base_alias_set = -1;
 
@@ -971,15 +971,11 @@  ao_ref_init_from_vn_reference (ao_ref *r
       if (mode == BLKmode)
 	size_tree = TYPE_SIZE (type);
       else
-        size = GET_MODE_BITSIZE (mode);
-    }
-  if (size_tree != NULL_TREE)
-    {
-      if (!tree_fits_uhwi_p (size_tree))
-	size = -1;
-      else
-	size = tree_to_uhwi (size_tree);
+	size = int (GET_MODE_BITSIZE (mode));
     }
+  if (size_tree != NULL_TREE
+      && TREE_CODE (size_tree) == INTEGER_CST)
+    size = wi::to_offset (size_tree);
 
   /* Initially, maxsize is the same as the accessed element size.
      In the following it will only grow (or become -1).  */
@@ -1034,7 +1030,7 @@  ao_ref_init_from_vn_reference (ao_ref *r
 
 	/* And now the usual component-reference style ops.  */
 	case BIT_FIELD_REF:
-	  offset += tree_to_shwi (op->op1);
+	  offset += wi::to_offset (op->op1);
 	  break;
 
 	case COMPONENT_REF:
@@ -1043,15 +1039,16 @@  ao_ref_init_from_vn_reference (ao_ref *r
 	    /* We do not have a complete COMPONENT_REF tree here so we
 	       cannot use component_ref_field_offset.  Do the interesting
 	       parts manually.  */
+	    tree this_offset = DECL_FIELD_OFFSET (field);
 
-	    if (op->op1
-		|| !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field)))
+	    if (op->op1 || TREE_CODE (this_offset) != INTEGER_CST)
 	      max_size = -1;
 	    else
 	      {
-		offset += (tree_to_uhwi (DECL_FIELD_OFFSET (field))
-			   * BITS_PER_UNIT);
-		offset += TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
+		offset_int woffset = wi::lshift (wi::to_offset (this_offset),
+						 LOG2_BITS_PER_UNIT);
+		woffset += wi::to_offset (DECL_FIELD_BIT_OFFSET (field));
+		offset += woffset;
 	      }
 	    break;
 	  }
@@ -1059,17 +1056,18 @@  ao_ref_init_from_vn_reference (ao_ref *r
 	case ARRAY_RANGE_REF:
 	case ARRAY_REF:
 	  /* We recorded the lower bound and the element size.  */
-	  if (!tree_fits_shwi_p (op->op0)
-	      || !tree_fits_shwi_p (op->op1)
-	      || !tree_fits_shwi_p (op->op2))
+	  if (TREE_CODE (op->op0) != INTEGER_CST
+	      || TREE_CODE (op->op1) != INTEGER_CST
+	      || TREE_CODE (op->op2) != INTEGER_CST)
 	    max_size = -1;
 	  else
 	    {
-	      HOST_WIDE_INT hindex = tree_to_shwi (op->op0);
-	      hindex -= tree_to_shwi (op->op1);
-	      hindex *= tree_to_shwi (op->op2);
-	      hindex *= BITS_PER_UNIT;
-	      offset += hindex;
+	      offset_int woffset
+		= wi::sext (wi::to_offset (op->op0) - wi::to_offset (op->op1),
+			    TYPE_PRECISION (TREE_TYPE (op->op0)));
+	      woffset *= wi::to_offset (op->op2);
+	      woffset = wi::lshift (woffset, LOG2_BITS_PER_UNIT);
+	      offset += woffset;
 	    }
 	  break;
 
@@ -1102,9 +1100,6 @@  ao_ref_init_from_vn_reference (ao_ref *r
 
   ref->ref = NULL_TREE;
   ref->base = base;
-  ref->offset = offset;
-  ref->size = size;
-  ref->max_size = max_size;
   ref->ref_alias_set = set;
   if (base_alias_set != -1)
     ref->base_alias_set = base_alias_set;
@@ -1113,6 +1108,30 @@  ao_ref_init_from_vn_reference (ao_ref *r
   /* We discount volatiles from value-numbering elsewhere.  */
   ref->volatile_p = false;
 
+  if (!wi::fits_shwi_p (size) || wi::neg_p (size))
+    {
+      ref->offset = 0;
+      ref->size = -1;
+      ref->max_size = -1;
+      return true;
+    }
+
+  ref->size = size.to_shwi ();
+
+  if (!wi::fits_shwi_p (offset))
+    {
+      ref->offset = 0;
+      ref->max_size = -1;
+      return true;
+    }
+
+  ref->offset = offset.to_shwi ();
+
+  if (!wi::fits_shwi_p (max_size) || wi::neg_p (max_size))
+    ref->max_size = -1;
+  else
+    ref->max_size = max_size.to_shwi ();
+
   return true;
 }