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)
