match: Fix `!a?b:c` and `a?~t:t` patterns for signed 1 bit types [PR114666]

Message ID 20240411084240.3358337-1-quic_apinski@quicinc.com
State Accepted, archived
Headers
Series match: Fix `!a?b:c` and `a?~t:t` patterns for signed 1 bit types [PR114666] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed

Commit Message

Andrew Pinski April 11, 2024, 8:42 a.m. UTC
  The issue here is that the `a?~t:t` pattern assumed (maybe correctly) that a
here was always going to be a unsigned boolean type. This fixes the problem
in both patterns to cast the operand to boolean type first.

I should note that VRP seems to be keep on wanting to produce `a == 0?1:-2`
from `((int)a) ^ 1` is a bit odd and partly is the cause of the issue and there
seems to be some disconnect on what should be the canonical form. That will be
something to look at for GCC 15.

Bootstrapped and tested on x86_64-linux-gnu with no regressions.

	PR tree-optimization/114666

gcc/ChangeLog:

	* match.pd (`!a?b:c`): Cast `a` to boolean type for cond for
	gimple.
	(`a?~t:t`): Cast `a` to boolean type before casting it
	to the type.

gcc/testsuite/ChangeLog:

	* gcc.c-torture/execute/bitfld-signed1-1.c: New test.

Signed-off-by: Andrew Pinski <quic_apinski@quicinc.com>
---
 gcc/match.pd                                        | 10 +++++++---
 .../gcc.c-torture/execute/bitfld-signed1-1.c        | 13 +++++++++++++
 2 files changed, 20 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
  

Comments

Richard Biener April 11, 2024, 9:31 a.m. UTC | #1
On Thu, Apr 11, 2024 at 10:43 AM Andrew Pinski <quic_apinski@quicinc.com> wrote:
>
> The issue here is that the `a?~t:t` pattern assumed (maybe correctly) that a
> here was always going to be a unsigned boolean type. This fixes the problem
> in both patterns to cast the operand to boolean type first.
>
> I should note that VRP seems to be keep on wanting to produce `a == 0?1:-2`
> from `((int)a) ^ 1` is a bit odd and partly is the cause of the issue and there
> seems to be some disconnect on what should be the canonical form. That will be
> something to look at for GCC 15.
>
> Bootstrapped and tested on x86_64-linux-gnu with no regressions.
>
>         PR tree-optimization/114666
>
> gcc/ChangeLog:
>
>         * match.pd (`!a?b:c`): Cast `a` to boolean type for cond for
>         gimple.
>         (`a?~t:t`): Cast `a` to boolean type before casting it
>         to the type.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.c-torture/execute/bitfld-signed1-1.c: New test.
>
> Signed-off-by: Andrew Pinski <quic_apinski@quicinc.com>
> ---
>  gcc/match.pd                                        | 10 +++++++---
>  .../gcc.c-torture/execute/bitfld-signed1-1.c        | 13 +++++++++++++
>  2 files changed, 20 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 15a1e7350d4..ffc928b656a 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -5895,7 +5895,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>   /* !A ? B : C -> A ? C : B.  */
>   (simplify
>    (cnd (logical_inverted_value truth_valued_p@0) @1 @2)
> -  (cnd @0 @2 @1)))
> +  /* For gimple, make sure the operand to COND is a boolean type,
> +     truth_valued_p will match 1bit integers too. */
> +  (if (GIMPLE && cnd == COND_EXPR)
> +   (cnd (convert:boolean_type_node @0) @2 @1)
> +   (cnd @0 @2 @1))))

This looks "wrong" for GENERIC still?

But this is not really part of the fix but deciding we should not have
signed:1 as
cond operand?  I'll note that truth_valued_p allows signed:1.

Maybe as minimal surgery add a TYPE_UNSIGNED (TREE_TPE (@0)) check here
instead?

>  /* abs/negative simplifications moved from fold_cond_expr_with_comparison.
>
> @@ -7099,8 +7103,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>         && (!wascmp || TYPE_PRECISION (type) == 1))
>     (if ((!TYPE_UNSIGNED (type) && TREE_CODE (type) == BOOLEAN_TYPE)
>         || TYPE_PRECISION (type) == 1)
> -    (bit_xor (convert:type @0) @2)
> -    (bit_xor (negate (convert:type @0)) @2)))))
> +    (bit_xor (convert:type (convert:boolean_type_node @0)) @2)
> +    (bit_xor (negate (convert:type (convert:boolean_type_node @0))) @2)))))
>  #endif

This looks OK, but then testing TYPE_UNSIGNED (TREE_TYPE (@0)) might be
better?

Does this all just go downhill from what VRP creates?  That is, would
IL checking
have had a chance detecting it if we say signed:1 are not valid as condition?

That said, the latter pattern definitely needs guarding/adjustment, I'm not sure
the former is wrong?  Semantically [VEC_]COND_EXPR is op0 != 0 ? ... : ...

Richard.

>  /* Simplify pointer equality compares using PTA.  */
> diff --git a/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> new file mode 100644
> index 00000000000..b0ff120ea51
> --- /dev/null
> +++ b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> @@ -0,0 +1,13 @@
> +/* PR tree-optimization/114666 */
> +/* We used to miscompile this to be always aborting
> +   due to the use of the signed 1bit into the COND_EXPR. */
> +
> +struct {
> +  signed a : 1;
> +} b = {-1};
> +char c;
> +int main()
> +{
> +  if ((b.a ^ 1UL) < 3)
> +    __builtin_abort();
> +}
> --
> 2.43.0
>
  
Andrew Pinski April 11, 2024, 11:25 p.m. UTC | #2
> -----Original Message-----
> From: Richard Biener <richard.guenther@gmail.com>
> Sent: Thursday, April 11, 2024 2:31 AM
> To: Andrew Pinski (QUIC) <quic_apinski@quicinc.com>
> Cc: gcc-patches@gcc.gnu.org
> Subject: Re: [PATCH] match: Fix `!a?b:c` and `a?~t:t` patterns for signed 1 bit
> types [PR114666]
> 
> On Thu, Apr 11, 2024 at 10:43 AM Andrew Pinski
> <quic_apinski@quicinc.com> wrote:
> >
> > The issue here is that the `a?~t:t` pattern assumed (maybe correctly)
> > that a here was always going to be a unsigned boolean type. This fixes
> > the problem in both patterns to cast the operand to boolean type first.
> >
> > I should note that VRP seems to be keep on wanting to produce `a ==
> > 0?1:-2` from `((int)a) ^ 1` is a bit odd and partly is the cause of
> > the issue and there seems to be some disconnect on what should be the
> > canonical form. That will be something to look at for GCC 15.
> >
> > Bootstrapped and tested on x86_64-linux-gnu with no regressions.
> >
> >         PR tree-optimization/114666
> >
> > gcc/ChangeLog:
> >
> >         * match.pd (`!a?b:c`): Cast `a` to boolean type for cond for
> >         gimple.
> >         (`a?~t:t`): Cast `a` to boolean type before casting it
> >         to the type.
> >
> > gcc/testsuite/ChangeLog:
> >
> >         * gcc.c-torture/execute/bitfld-signed1-1.c: New test.
> >
> > Signed-off-by: Andrew Pinski <quic_apinski@quicinc.com>
> > ---
> >  gcc/match.pd                                        | 10 +++++++---
> >  .../gcc.c-torture/execute/bitfld-signed1-1.c        | 13 +++++++++++++
> >  2 files changed, 20 insertions(+), 3 deletions(-)  create mode 100644
> > gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> >
> > diff --git a/gcc/match.pd b/gcc/match.pd index
> > 15a1e7350d4..ffc928b656a 100644
> > --- a/gcc/match.pd
> > +++ b/gcc/match.pd
> > @@ -5895,7 +5895,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> >   /* !A ? B : C -> A ? C : B.  */
> >   (simplify
> >    (cnd (logical_inverted_value truth_valued_p@0) @1 @2)
> > -  (cnd @0 @2 @1)))
> > +  /* For gimple, make sure the operand to COND is a boolean type,
> > +     truth_valued_p will match 1bit integers too. */  (if (GIMPLE &&
> > + cnd == COND_EXPR)
> > +   (cnd (convert:boolean_type_node @0) @2 @1)
> > +   (cnd @0 @2 @1))))
> 
> This looks "wrong" for GENERIC still?

I tired without the GIMPLE check and ran into the testcase gcc.dg/torture/builtins-isinf-sign-1.c failing. Because the extra convert was blocking seeing both sides of an equal was the same (I didn't look into it further than that). So I decided to limit it to GIMPLE only.

> But this is not really part of the fix but deciding we should not have
> signed:1 as
> cond operand?  I'll note that truth_valued_p allows signed:1.
> 
> Maybe as minimal surgery add a TYPE_UNSIGNED (TREE_TPE (@0)) check here
> instead?

That might work, let me try.

> 
> >  /* abs/negative simplifications moved from
> fold_cond_expr_with_comparison.
> >
> > @@ -7099,8 +7103,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> >         && (!wascmp || TYPE_PRECISION (type) == 1))
> >     (if ((!TYPE_UNSIGNED (type) && TREE_CODE (type) == BOOLEAN_TYPE)
> >         || TYPE_PRECISION (type) == 1)
> > -    (bit_xor (convert:type @0) @2)
> > -    (bit_xor (negate (convert:type @0)) @2)))))
> > +    (bit_xor (convert:type (convert:boolean_type_node @0)) @2)
> > +    (bit_xor (negate (convert:type (convert:boolean_type_node @0)))
> > + @2)))))
> >  #endif
> 
> This looks OK, but then testing TYPE_UNSIGNED (TREE_TYPE (@0)) might be
> better?
> 

Let me do that just like the other pattern.

> Does this all just go downhill from what VRP creates?  That is, would IL
> checking have had a chance detecting it if we say signed:1 are not valid as
> condition?

Yes. So what VRP produces in the testcase is:
`_2 == 0 ? 1 : -2u` (where _2 is the signed 1bit integer).
Now maybe the COND_EXPR should be the canonical form for constants (but that is for a different patch I think, I added it to the list of things I should look into for GCC 15).

> 
> That said, the latter pattern definitely needs guarding/adjustment, I'm not
> sure the former is wrong?  Semantically [VEC_]COND_EXPR is op0 != 0 ? ... : ...

I forgot to mention that to fix the bug only one of the 2 hunks are needed.

> 
> Richard.
> 
> >  /* Simplify pointer equality compares using PTA.  */ diff --git
> > a/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > new file mode 100644
> > index 00000000000..b0ff120ea51
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > @@ -0,0 +1,13 @@
> > +/* PR tree-optimization/114666 */
> > +/* We used to miscompile this to be always aborting
> > +   due to the use of the signed 1bit into the COND_EXPR. */
> > +
> > +struct {
> > +  signed a : 1;
> > +} b = {-1};
> > +char c;
> > +int main()
> > +{
> > +  if ((b.a ^ 1UL) < 3)
> > +    __builtin_abort();
> > +}
> > --
> > 2.43.0
> >
  
Richard Biener April 12, 2024, 7:28 a.m. UTC | #3
On Fri, Apr 12, 2024 at 1:25 AM Andrew Pinski (QUIC)
<quic_apinski@quicinc.com> wrote:
>
> > -----Original Message-----
> > From: Richard Biener <richard.guenther@gmail.com>
> > Sent: Thursday, April 11, 2024 2:31 AM
> > To: Andrew Pinski (QUIC) <quic_apinski@quicinc.com>
> > Cc: gcc-patches@gcc.gnu.org
> > Subject: Re: [PATCH] match: Fix `!a?b:c` and `a?~t:t` patterns for signed 1 bit
> > types [PR114666]
> >
> > On Thu, Apr 11, 2024 at 10:43 AM Andrew Pinski
> > <quic_apinski@quicinc.com> wrote:
> > >
> > > The issue here is that the `a?~t:t` pattern assumed (maybe correctly)
> > > that a here was always going to be a unsigned boolean type. This fixes
> > > the problem in both patterns to cast the operand to boolean type first.
> > >
> > > I should note that VRP seems to be keep on wanting to produce `a ==
> > > 0?1:-2` from `((int)a) ^ 1` is a bit odd and partly is the cause of
> > > the issue and there seems to be some disconnect on what should be the
> > > canonical form. That will be something to look at for GCC 15.
> > >
> > > Bootstrapped and tested on x86_64-linux-gnu with no regressions.
> > >
> > >         PR tree-optimization/114666
> > >
> > > gcc/ChangeLog:
> > >
> > >         * match.pd (`!a?b:c`): Cast `a` to boolean type for cond for
> > >         gimple.
> > >         (`a?~t:t`): Cast `a` to boolean type before casting it
> > >         to the type.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > >         * gcc.c-torture/execute/bitfld-signed1-1.c: New test.
> > >
> > > Signed-off-by: Andrew Pinski <quic_apinski@quicinc.com>
> > > ---
> > >  gcc/match.pd                                        | 10 +++++++---
> > >  .../gcc.c-torture/execute/bitfld-signed1-1.c        | 13 +++++++++++++
> > >  2 files changed, 20 insertions(+), 3 deletions(-)  create mode 100644
> > > gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > >
> > > diff --git a/gcc/match.pd b/gcc/match.pd index
> > > 15a1e7350d4..ffc928b656a 100644
> > > --- a/gcc/match.pd
> > > +++ b/gcc/match.pd
> > > @@ -5895,7 +5895,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> > >   /* !A ? B : C -> A ? C : B.  */
> > >   (simplify
> > >    (cnd (logical_inverted_value truth_valued_p@0) @1 @2)
> > > -  (cnd @0 @2 @1)))
> > > +  /* For gimple, make sure the operand to COND is a boolean type,
> > > +     truth_valued_p will match 1bit integers too. */  (if (GIMPLE &&
> > > + cnd == COND_EXPR)
> > > +   (cnd (convert:boolean_type_node @0) @2 @1)
> > > +   (cnd @0 @2 @1))))
> >
> > This looks "wrong" for GENERIC still?
>
> I tired without the GIMPLE check and ran into the testcase gcc.dg/torture/builtins-isinf-sign-1.c failing. Because the extra convert was blocking seeing both sides of an equal was the same (I didn't look into it further than that). So I decided to limit it to GIMPLE only.
>
> > But this is not really part of the fix but deciding we should not have
> > signed:1 as
> > cond operand?  I'll note that truth_valued_p allows signed:1.
> >
> > Maybe as minimal surgery add a TYPE_UNSIGNED (TREE_TPE (@0)) check here
> > instead?
>
> That might work, let me try.
>
> >
> > >  /* abs/negative simplifications moved from
> > fold_cond_expr_with_comparison.
> > >
> > > @@ -7099,8 +7103,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> > >         && (!wascmp || TYPE_PRECISION (type) == 1))
> > >     (if ((!TYPE_UNSIGNED (type) && TREE_CODE (type) == BOOLEAN_TYPE)
> > >         || TYPE_PRECISION (type) == 1)
> > > -    (bit_xor (convert:type @0) @2)
> > > -    (bit_xor (negate (convert:type @0)) @2)))))
> > > +    (bit_xor (convert:type (convert:boolean_type_node @0)) @2)
> > > +    (bit_xor (negate (convert:type (convert:boolean_type_node @0)))
> > > + @2)))))
> > >  #endif
> >
> > This looks OK, but then testing TYPE_UNSIGNED (TREE_TYPE (@0)) might be
> > better?
> >
>
> Let me do that just like the other pattern.
>
> > Does this all just go downhill from what VRP creates?  That is, would IL
> > checking have had a chance detecting it if we say signed:1 are not valid as
> > condition?
>
> Yes. So what VRP produces in the testcase is:
> `_2 == 0 ? 1 : -2u` (where _2 is the signed 1bit integer).
> Now maybe the COND_EXPR should be the canonical form for constants (but that is for a different patch I think, I added it to the list of things I should look into for GCC 15).

Ah OK, so the !A ? B : C -> A ? C : B transform turns the
"proper" conditional into an improper one (if we want to restrict it).
And then the other pattern matches doing the wrong transform.

> >
> > That said, the latter pattern definitely needs guarding/adjustment, I'm not
> > sure the former is wrong?  Semantically [VEC_]COND_EXPR is op0 != 0 ? ... : ...
>
> I forgot to mention that to fix the bug only one of the 2 hunks are needed.
>
> >
> > Richard.
> >
> > >  /* Simplify pointer equality compares using PTA.  */ diff --git
> > > a/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > > b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > > new file mode 100644
> > > index 00000000000..b0ff120ea51
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
> > > @@ -0,0 +1,13 @@
> > > +/* PR tree-optimization/114666 */
> > > +/* We used to miscompile this to be always aborting
> > > +   due to the use of the signed 1bit into the COND_EXPR. */
> > > +
> > > +struct {
> > > +  signed a : 1;
> > > +} b = {-1};
> > > +char c;
> > > +int main()
> > > +{
> > > +  if ((b.a ^ 1UL) < 3)
> > > +    __builtin_abort();
> > > +}
> > > --
> > > 2.43.0
> > >
  

Patch

diff --git a/gcc/match.pd b/gcc/match.pd
index 15a1e7350d4..ffc928b656a 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5895,7 +5895,11 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  /* !A ? B : C -> A ? C : B.  */
  (simplify
   (cnd (logical_inverted_value truth_valued_p@0) @1 @2)
-  (cnd @0 @2 @1)))
+  /* For gimple, make sure the operand to COND is a boolean type,
+     truth_valued_p will match 1bit integers too. */
+  (if (GIMPLE && cnd == COND_EXPR)
+   (cnd (convert:boolean_type_node @0) @2 @1)
+   (cnd @0 @2 @1))))
 
 /* abs/negative simplifications moved from fold_cond_expr_with_comparison.
 
@@ -7099,8 +7103,8 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        && (!wascmp || TYPE_PRECISION (type) == 1))
    (if ((!TYPE_UNSIGNED (type) && TREE_CODE (type) == BOOLEAN_TYPE)
 	|| TYPE_PRECISION (type) == 1)
-    (bit_xor (convert:type @0) @2)
-    (bit_xor (negate (convert:type @0)) @2)))))
+    (bit_xor (convert:type (convert:boolean_type_node @0)) @2)
+    (bit_xor (negate (convert:type (convert:boolean_type_node @0))) @2)))))
 #endif
 
 /* Simplify pointer equality compares using PTA.  */
diff --git a/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
new file mode 100644
index 00000000000..b0ff120ea51
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/bitfld-signed1-1.c
@@ -0,0 +1,13 @@ 
+/* PR tree-optimization/114666 */
+/* We used to miscompile this to be always aborting
+   due to the use of the signed 1bit into the COND_EXPR. */
+
+struct {
+  signed a : 1;
+} b = {-1};
+char c;
+int main()
+{
+  if ((b.a ^ 1UL) < 3)
+    __builtin_abort();
+}