aarch64: Fix folding of degenerate svwhilele case [PR117045]

The svwhilele folder mishandled the degenerate case in which
the second argument is the maximum integer.  In that case,
the result is all-true regardless of the first parameter:

  If the second scalar operand is equal to the maximum signed integer
  value then a condition which includes an equality test can never fail
  and the result will be an all-true predicate.

This is because the conceptual "increment the first operand
by 1 after each element" is done modulo the range of the operand.
The GCC code was instead treating it as infinite precision.
whilele_5.c even had a test for the incorrect behaviour.

The easiest fix seemed to be to handle that case specially before
doing constant folding.  This also copes with variable first operands.

gcc/
	PR target/116999
	PR target/117045
	* config/aarch64/aarch64-sve-builtins-base.cc
	(svwhilelx_impl::fold): Check for WHILELTs of the minimum value
	and WHILELEs of the maximum value.  Fold them to all-false and
	all-true respectively.

gcc/testsuite/
	PR target/116999
	PR target/117045
	* gcc.target/aarch64/sve/acle/general/whilele_5.c: Fix bogus
	expected result.
	* gcc.target/aarch64/sve/acle/general/whilele_11.c: New test.
	* gcc.target/aarch64/sve/acle/general/whilele_12.c: Likewise.
This commit is contained in:
Richard Sandiford 2024-10-14 09:52:44 +01:00
parent 9f549d216c
commit 50e7c51b0a
4 changed files with 76 additions and 2 deletions

View file

@ -2945,7 +2945,9 @@ public:
: while_comparison (unspec_for_sint, unspec_for_uint), m_eq_p (eq_p)
{}
/* Try to fold a call by treating its arguments as constants of type T. */
/* Try to fold a call by treating its arguments as constants of type T.
We have already filtered out the degenerate cases of X .LT. MIN
and X .LE. MAX. */
template<typename T>
gimple *
fold_type (gimple_folder &f) const
@ -3001,6 +3003,13 @@ public:
if (f.vectors_per_tuple () > 1)
return nullptr;
/* Filter out cases where the condition is always true or always false. */
tree arg1 = gimple_call_arg (f.call, 1);
if (!m_eq_p && operand_equal_p (arg1, TYPE_MIN_VALUE (TREE_TYPE (arg1))))
return f.fold_to_pfalse ();
if (m_eq_p && operand_equal_p (arg1, TYPE_MAX_VALUE (TREE_TYPE (arg1))))
return f.fold_to_ptrue ();
if (f.type_suffix (1).unsigned_p)
return fold_type<poly_uint64> (f);
else

View file

@ -0,0 +1,31 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
#include <arm_sve.h>
#include <limits.h>
svbool_t
f1 (volatile int32_t *ptr)
{
return svwhilelt_b8_s32 (*ptr, INT32_MIN);
}
svbool_t
f2 (volatile uint32_t *ptr)
{
return svwhilelt_b16_u32 (*ptr, 0);
}
svbool_t
f3 (volatile int64_t *ptr)
{
return svwhilelt_b32_s64 (*ptr, INT64_MIN);
}
svbool_t
f4 (volatile uint64_t *ptr)
{
return svwhilelt_b64_u64 (*ptr, 0);
}
/* { dg-final { scan-assembler-times {\tpfalse\tp[0-9]+\.b\n} 4 } } */

View file

@ -0,0 +1,34 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
#include <arm_sve.h>
#include <limits.h>
svbool_t
f1 (volatile int32_t *ptr)
{
return svwhilele_b8_s32 (*ptr, INT32_MAX);
}
svbool_t
f2 (volatile uint32_t *ptr)
{
return svwhilele_b16_u32 (*ptr, UINT32_MAX);
}
svbool_t
f3 (volatile int64_t *ptr)
{
return svwhilele_b32_s64 (*ptr, INT64_MAX);
}
svbool_t
f4 (volatile uint64_t *ptr)
{
return svwhilele_b64_u64 (*ptr, UINT64_MAX);
}
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.b(?:, all)\n} } } */
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.h(?:, all)\n} } } */
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.s(?:, all)\n} } } */
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.d(?:, all)\n} } } */

View file

@ -28,7 +28,7 @@ test3 (svbool_t *ptr)
*ptr = svwhilele_b16_s32 (0x7ffffffb, 0x7fffffff);
}
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.h, vl5\n} } } */
/* { dg-final { scan-assembler {\tptrue\tp[0-9]+\.h(?:, all)\n} } } */
void
test4 (svbool_t *ptr)