From: Kyrylo Tkachov <[email protected]>

cfn_clz::fold_range computes the minimum of the result from the upper
bound of the argument, and cfn_ctz::fold_range computes the maximum of
the result from it.  This is wrong for a signed argument with negative
values, because a negative value has the sign bit set.  For

  _1 = .CLZ (x_4(D));
  _2 = _1 > 0;

with int x_4(D), evrp computes

  Global Exported: _1 = [irange] int [1, 31]

and folds _2 to 1, but .CLZ (-5) is 0.  For an argument in [-8, 4],
.CTZ gets [0, 2], but .CTZ (-8) is 3.  For an argument in [-8, 0], the
upper bound 0 makes .CLZ (x, 32) and .CTZ (x, 32) fold to 32.

Internal functions have no prototype, so .CLZ and .CTZ with a signed
argument are valid GIMPLE.  From C, only a redeclared builtin with a
signed argument triggers the bug, as in PR112949, which was closed as
invalid because that C code is undefined.  cfn_ffs casts the argument
to unsigned for the same reason (PR97325).

Use the nonzero bits of the argument instead of its upper bound.  Every
value of the argument is a submask of the nonzero bits, and the nonzero
bits are 0 only for [0, 0].  After the patch evrp computes

  Global Exported: _1 = [irange] int [0, 31]

and keeps the comparison.  The ranges of unsigned arguments do not
change.

Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.

Cc: Aldy Hernandez <[email protected]>
Cc: Andrew MacLeod <[email protected]>

gcc/ChangeLog:

        * gimple-range-op.cc (cfn_clz::fold_range): Use the nonzero bits
        of the argument instead of its upper bound.
        (cfn_ctz::fold_range): Likewise.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/clz-ctz-signed-1.c: New test.
        * gcc.target/aarch64/clz-ctz-signed-1.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/gimple-range-op.cc                        |  7 +-
 .../gcc.dg/tree-ssa/clz-ctz-signed-1.c        | 51 +++++++++++++
 .../gcc.target/aarch64/clz-ctz-signed-1.c     | 71 +++++++++++++++++++
 3 files changed, 126 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-signed-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/clz-ctz-signed-1.c

diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
index 20666ff0356..6c2d035ccc9 100644
--- a/gcc/gimple-range-op.cc
+++ b/gcc/gimple-range-op.cc
@@ -1027,8 +1027,9 @@ cfn_clz::fold_range (irange &r, tree type, const irange 
&lh,
     }
   if (mini == -2)
     return false;
-  // From clz of maximum we can compute result minimum.
-  wide_int max = lh.upper_bound ();
+  // Every value of the argument is a submask of the nonzero bits, so
+  // the clz of the nonzero bits is the minimum of the result.
+  wide_int max = lh.get_nonzero_bits ();
   int newmini = prec - 1 - wi::floor_log2 (max);
   if (max == 0)
     {
@@ -1091,7 +1092,7 @@ cfn_ctz::fold_range (irange &r, tree type, const irange 
&lh,
     }
   // If some high bits are known to be zero, we can decrease
   // the maximum.
-  wide_int max = lh.upper_bound ();
+  wide_int max = lh.get_nonzero_bits ();
   if (max == 0)
     {
       // Argument is [0, 0].  If CTZ_DEFINED_VALUE_AT_ZERO
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-signed-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-signed-1.c
new file mode 100644
index 00000000000..71617292e86
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-signed-1.c
@@ -0,0 +1,51 @@
+/* Ranges of .CLZ and .CTZ of a signed argument with negative values.  */
+/* { dg-do run } */
+/* { dg-require-effective-target clz_ifn } */
+/* { dg-require-effective-target ctz_ifn } */
+/* { dg-options "-O2 -fgimple" } */
+
+/* .CLZ (X) is 0 for a negative X.  */
+
+int __attribute__((noipa)) __GIMPLE (ssa,startwith("evrp"))
+clz1 (int x)
+{
+  int _1;
+  _Bool _2;
+  int _3;
+
+  __BB(2):
+  _1 = .CLZ (x_4(D));
+  _2 = _1 > 0;
+  _3 = _2 ? 100 : 7;
+  return _3;
+}
+
+/* .CTZ (-8) is 3, more than the floor_log2 of the maximum 4.  */
+
+int __attribute__((noipa)) __GIMPLE (ssa,startwith("evrp"))
+ctz1 (int x)
+{
+  int _1;
+  _Bool _2;
+  int _3;
+  int _5;
+  int _6;
+
+  __BB(2):
+  _5 = __MAX (x_4(D), _Literal (int) -8);
+  _6 = __MIN (_5, 4);
+  _1 = .CTZ (_6);
+  _2 = _1 <= 2;
+  _3 = _2 ? 100 : 7;
+  return _3;
+}
+
+int
+main (void)
+{
+  if (clz1 (-5) != 7)
+    __builtin_abort ();
+  if (ctz1 (-8) != 7)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/clz-ctz-signed-1.c 
b/gcc/testsuite/gcc.target/aarch64/clz-ctz-signed-1.c
new file mode 100644
index 00000000000..16b2a9b7b2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/clz-ctz-signed-1.c
@@ -0,0 +1,71 @@
+/* Ranges of .CLZ and .CTZ of a signed argument with negative values, with
+   the value at zero in the second argument.  32 is the value that AArch64
+   defines for SImode.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fgimple" } */
+
+/* .CLZ (X, 32) is 0 for a negative X.  */
+
+int __attribute__((noipa)) __GIMPLE (ssa,startwith("evrp"))
+clz1 (int x)
+{
+  int _1;
+  _Bool _2;
+  int _3;
+
+  __BB(2):
+  _1 = .CLZ (x_4(D), 32);
+  _2 = _1 > 0;
+  _3 = _2 ? 100 : 7;
+  return _3;
+}
+
+/* X >> 31 is -1 or 0, and .CLZ (-1, 32) is 0.  */
+
+int __attribute__((noipa)) __GIMPLE (ssa,startwith("evrp"))
+clz2 (int x)
+{
+  int _1;
+  _Bool _2;
+  int _3;
+  int _5;
+
+  __BB(2):
+  _5 = x_4(D) >> 31;
+  _1 = .CLZ (_5, 32);
+  _2 = _1 == 32;
+  _3 = _2 ? 100 : 7;
+  return _3;
+}
+
+/* .CTZ (-8, 32) is 3.  */
+
+int __attribute__((noipa)) __GIMPLE (ssa,startwith("evrp"))
+ctz1 (int x)
+{
+  int _1;
+  _Bool _2;
+  int _3;
+  int _5;
+  int _6;
+
+  __BB(2):
+  _5 = __MAX (x_4(D), _Literal (int) -8);
+  _6 = __MIN (_5, 0);
+  _1 = .CTZ (_6, 32);
+  _2 = _1 == 32;
+  _3 = _2 ? 100 : 7;
+  return _3;
+}
+
+int
+main (void)
+{
+  if (clz1 (-5) != 7)
+    __builtin_abort ();
+  if (clz2 (-5) != 7)
+    __builtin_abort ();
+  if (ctz1 (-8) != 7)
+    __builtin_abort ();
+  return 0;
+}
-- 
2.54.0 (Apple Git-157)

Reply via email to