diff mbox series

array-bounds: Fix up ICE on overaligned variables [PR99109]

Message ID 20210217101234.GL4020736@tucnak
State New
Headers show
Series array-bounds: Fix up ICE on overaligned variables [PR99109] | expand

Commit Message

Jakub Jelinek Feb. 17, 2021, 10:12 a.m. UTC
Hi!

check_mem_ref builds artificial arrays for variables that don't have
array type.
The C standard says:
"For the purposes of these operators, a pointer to an object that is not an element of an
array behaves the same as a pointer to the first element of an array of length one with the
type of the object as its element type."
so it isn't completely wrong and does simplify the function.
But, layout_type can fail if the size of the element type is not a multiple
of its alignment (i.e. overaligned types) and we then ICE because of that.

The following patch uses TYPE_MAIN_VARIANT in those cases instead, but only
for the types that need it, as for the diagnostics it is better to use the
typedef names etc. that were really used in the source if possible.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2021-02-17  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/99109
	* gimple-array-bounds.cc (overaligned_type_p): New function.
	(array_bounds_checker::check_mem_ref): For overaligned types use
	TYPE_MAIN_VARIANT of reftype as element type of a new array.

	* g++.dg/warn/Warray-bounds-17.C: New test.


	Jakub

Comments

Martin Sebor Feb. 17, 2021, 8:38 p.m. UTC | #1
On 2/17/21 3:12 AM, Jakub Jelinek via Gcc-patches wrote:
> Hi!
> 
> check_mem_ref builds artificial arrays for variables that don't have
> array type.
> The C standard says:
> "For the purposes of these operators, a pointer to an object that is not an element of an
> array behaves the same as a pointer to the first element of an array of length one with the
> type of the object as its element type."
> so it isn't completely wrong and does simplify the function.
> But, layout_type can fail if the size of the element type is not a multiple
> of its alignment (i.e. overaligned types) and we then ICE because of that.
> 
> The following patch uses TYPE_MAIN_VARIANT in those cases instead, but only
> for the types that need it, as for the diagnostics it is better to use the
> typedef names etc. that were really used in the source if possible.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2021-02-17  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/99109
> 	* gimple-array-bounds.cc (overaligned_type_p): New function.
> 	(array_bounds_checker::check_mem_ref): For overaligned types use
> 	TYPE_MAIN_VARIANT of reftype as element type of a new array.
> 
> 	* g++.dg/warn/Warray-bounds-17.C: New test.
> 
> --- gcc/gimple-array-bounds.cc.jj	2021-01-04 10:25:37.471249246 +0100
> +++ gcc/gimple-array-bounds.cc	2021-02-16 17:00:41.961750114 +0100
> @@ -386,6 +386,21 @@ build_zero_elt_array_type (tree eltype)
>     return arrtype;
>   }
>   
> +/* Return true if it is not possible to build an array with element type
> +   ELTYPE, because either ELTYPE has alignment larger than its size
> +   or its size is not a multiple of its alignment.  */
> +
> +static bool
> +overaligned_type_p (tree eltype)
> +{
> +  return (TYPE_SIZE_UNIT (eltype)
> +	  && TREE_CODE (TYPE_SIZE_UNIT (eltype)) == INTEGER_CST
> +	  && !integer_zerop (TYPE_SIZE_UNIT (eltype))
> +	  && TYPE_ALIGN_UNIT (eltype) > 1
> +	  && wi::zext (wi::to_wide (TYPE_SIZE_UNIT (eltype)),
> +		       ffs_hwi (TYPE_ALIGN_UNIT (eltype)) - 1) != 0);
> +}
> +
>   /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
>      references to string constants.  If VRP can determine that the array
>      subscript is a constant, check if it is outside valid range.
> @@ -553,6 +568,8 @@ array_bounds_checker::check_mem_ref (loc
>         reftype = TREE_TYPE (TREE_TYPE (arg));
>         if (TREE_CODE (reftype) == ARRAY_TYPE)
>   	reftype = TREE_TYPE (reftype);
> +      else if (overaligned_type_p (reftype))
> +	reftype = TYPE_MAIN_VARIANT (reftype);
>         if (tree refsize = TYPE_SIZE_UNIT (reftype))
>   	if (TREE_CODE (refsize) == INTEGER_CST)
>   	  eltsize = wi::to_offset (refsize);
> @@ -675,7 +692,11 @@ array_bounds_checker::check_mem_ref (loc
>         /* Treat a reference to a non-array object as one to an array
>   	 of a single element.  */
>         if (TREE_CODE (reftype) != ARRAY_TYPE)
> -	reftype = build_array_type_nelts (reftype, 1);
> +	{
> +	  if (overaligned_type_p (reftype))
> +	    reftype = TYPE_MAIN_VARIANT (reftype);
> +	  reftype = build_array_type_nelts (reftype, 1);
> +	}

Rather than complicating the logic in the caller (which is already
long and hard to follow) I'd suggest to consider changing
the build_zero_elt_array_type() helper to handle both kinds of arrays
(i.e., zero-length and otherwise), and strip the excess alignment
from reftype in it.  That will simplify the function.

I recall considering this (using a single helper) but not why I didn't
do it to begin with.

Martin

>   
>         /* Extract the element type out of MEM_REF and use its size
>   	 to compute the index to print in the diagnostic; arrays
> --- gcc/testsuite/g++.dg/warn/Warray-bounds-17.C.jj	2021-02-16 17:24:14.178813304 +0100
> +++ gcc/testsuite/g++.dg/warn/Warray-bounds-17.C	2021-02-16 17:23:35.305251062 +0100
> @@ -0,0 +1,15 @@
> +// PR middle-end/99109
> +// { dg-do compile }
> +// { dg-options "-O2 -Warray-bounds" }
> +
> +typedef int A __attribute__((aligned (64)));
> +void foo (int *);
> +
> +void
> +bar (void)
> +{
> +  A b;			// { dg-message "while referencing" }
> +  int *p = &b;
> +  int *x = (p - 1);	// { dg-warning "outside array bounds" }
> +  foo (x);
> +}
> 
> 	Jakub
>
Jakub Jelinek Feb. 17, 2021, 8:56 p.m. UTC | #2
On Wed, Feb 17, 2021 at 01:38:56PM -0700, Martin Sebor wrote:
> > -	reftype = build_array_type_nelts (reftype, 1);
> > +	{
> > +	  if (overaligned_type_p (reftype))
> > +	    reftype = TYPE_MAIN_VARIANT (reftype);
> > +	  reftype = build_array_type_nelts (reftype, 1);
> > +	}
> 
> Rather than complicating the logic in the caller (which is already
> long and hard to follow) I'd suggest to consider changing
> the build_zero_elt_array_type() helper to handle both kinds of arrays
> (i.e., zero-length and otherwise), and strip the excess alignment
> from reftype in it.  That will simplify the function.

That will mean the overaligned type checking will need to be done
even for the case where reftype was originally an ARRAY_TYPE and the code
just picks up an element type out of it (in that case it is known that it
will succeed).
And there is a question how to name it, build_array_type_nelts is already
taken and it might be confusing what this almost like build_array_type_nelts
but with extras differs from the tree.c function.

Anyway, can change it if you can suggest a good name...

	Jakub
Martin Sebor Feb. 17, 2021, 9:38 p.m. UTC | #3
On 2/17/21 1:56 PM, Jakub Jelinek wrote:
> On Wed, Feb 17, 2021 at 01:38:56PM -0700, Martin Sebor wrote:
>>> -	reftype = build_array_type_nelts (reftype, 1);
>>> +	{
>>> +	  if (overaligned_type_p (reftype))
>>> +	    reftype = TYPE_MAIN_VARIANT (reftype);
>>> +	  reftype = build_array_type_nelts (reftype, 1);
>>> +	}
>>
>> Rather than complicating the logic in the caller (which is already
>> long and hard to follow) I'd suggest to consider changing
>> the build_zero_elt_array_type() helper to handle both kinds of arrays
>> (i.e., zero-length and otherwise), and strip the excess alignment
>> from reftype in it.  That will simplify the function.
> 
> That will mean the overaligned type checking will need to be done
> even for the case where reftype was originally an ARRAY_TYPE and the code
> just picks up an element type out of it (in that case it is known that it
> will succeed).
> And there is a question how to name it, build_array_type_nelts is already
> taken and it might be confusing what this almost like build_array_type_nelts
> but with extras differs from the tree.c function.
> 
> Anyway, can change it if you can suggest a good name...

How does build_printable_array_type sound?

Also, would using TYPE_MAIN_VARIANT whenever TYPE_USER_ALIGN is set
be a simpler solution?  (It might not be as refined as the test in
your patch but I don't think we'd be giving up too much by
the simplification.)

Martin
Jakub Jelinek Feb. 17, 2021, 9:47 p.m. UTC | #4
On Wed, Feb 17, 2021 at 02:38:04PM -0700, Martin Sebor wrote:
> How does build_printable_array_type sound?

I'll go with that.

> Also, would using TYPE_MAIN_VARIANT whenever TYPE_USER_ALIGN is set
> be a simpler solution?  (It might not be as refined as the test in
> your patch but I don't think we'd be giving up too much by
> the simplification.)

That seems like a bad idea.  TYPE_USER_ALIGN is set quite often,
but in most cases the type size is a multiple of the alignment.

	Jakub
diff mbox series

Patch

--- gcc/gimple-array-bounds.cc.jj	2021-01-04 10:25:37.471249246 +0100
+++ gcc/gimple-array-bounds.cc	2021-02-16 17:00:41.961750114 +0100
@@ -386,6 +386,21 @@  build_zero_elt_array_type (tree eltype)
   return arrtype;
 }
 
+/* Return true if it is not possible to build an array with element type
+   ELTYPE, because either ELTYPE has alignment larger than its size
+   or its size is not a multiple of its alignment.  */
+
+static bool
+overaligned_type_p (tree eltype)
+{
+  return (TYPE_SIZE_UNIT (eltype)
+	  && TREE_CODE (TYPE_SIZE_UNIT (eltype)) == INTEGER_CST
+	  && !integer_zerop (TYPE_SIZE_UNIT (eltype))
+	  && TYPE_ALIGN_UNIT (eltype) > 1
+	  && wi::zext (wi::to_wide (TYPE_SIZE_UNIT (eltype)),
+		       ffs_hwi (TYPE_ALIGN_UNIT (eltype)) - 1) != 0);
+}
+
 /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
    references to string constants.  If VRP can determine that the array
    subscript is a constant, check if it is outside valid range.
@@ -553,6 +568,8 @@  array_bounds_checker::check_mem_ref (loc
       reftype = TREE_TYPE (TREE_TYPE (arg));
       if (TREE_CODE (reftype) == ARRAY_TYPE)
 	reftype = TREE_TYPE (reftype);
+      else if (overaligned_type_p (reftype))
+	reftype = TYPE_MAIN_VARIANT (reftype);
       if (tree refsize = TYPE_SIZE_UNIT (reftype))
 	if (TREE_CODE (refsize) == INTEGER_CST)
 	  eltsize = wi::to_offset (refsize);
@@ -675,7 +692,11 @@  array_bounds_checker::check_mem_ref (loc
       /* Treat a reference to a non-array object as one to an array
 	 of a single element.  */
       if (TREE_CODE (reftype) != ARRAY_TYPE)
-	reftype = build_array_type_nelts (reftype, 1);
+	{
+	  if (overaligned_type_p (reftype))
+	    reftype = TYPE_MAIN_VARIANT (reftype);
+	  reftype = build_array_type_nelts (reftype, 1);
+	}
 
       /* Extract the element type out of MEM_REF and use its size
 	 to compute the index to print in the diagnostic; arrays
--- gcc/testsuite/g++.dg/warn/Warray-bounds-17.C.jj	2021-02-16 17:24:14.178813304 +0100
+++ gcc/testsuite/g++.dg/warn/Warray-bounds-17.C	2021-02-16 17:23:35.305251062 +0100
@@ -0,0 +1,15 @@ 
+// PR middle-end/99109
+// { dg-do compile }
+// { dg-options "-O2 -Warray-bounds" }
+
+typedef int A __attribute__((aligned (64)));
+void foo (int *);
+
+void
+bar (void)
+{
+  A b;			// { dg-message "while referencing" }
+  int *p = &b;
+  int *x = (p - 1);	// { dg-warning "outside array bounds" }
+  foo (x);
+}