From: Kyrylo Tkachov <[email protected]>
simplify_count_zeroes accepts table elements of up to 64 bits, but
computes the value for a zero input in int. A table entry outside the
int range is truncated: with a long long table whose entry for a zero
input is 0x100000005, the recognized count returns 5 for a zero input.
For a CLZ table the value is also subtracted from 31 or 63 in int,
which overflows for a long long entry of 0x80000000.
Do not replace the table lookup in these cases. An entry that is only
truncated to a type that is not wider than int, such as 0xffffffff in
an unsigned int CTZ table, is still handled.
Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
gcc/ChangeLog:
* tree-ssa-forwprop.cc (simplify_count_zeroes): Fail if the
value at zero does not survive the computation in int.
gcc/testsuite/ChangeLog:
* gcc.dg/torture/clz-ctz-table-2.c: New test.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
.../gcc.dg/torture/clz-ctz-table-2.c | 64 +++++++++++++++++++
gcc/tree-ssa-forwprop.cc | 11 ++++
2 files changed, 75 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/torture/clz-ctz-table-2.c
diff --git a/gcc/testsuite/gcc.dg/torture/clz-ctz-table-2.c
b/gcc/testsuite/gcc.dg/torture/clz-ctz-table-2.c
new file mode 100644
index 00000000000..7921b6cb7c5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/clz-ctz-table-2.c
@@ -0,0 +1,64 @@
+/* { dg-do run } */
+/* { dg-require-effective-target int32plus } */
+
+/* The table entries for a zero input do not survive the computation of
+ the recognized counts in int. */
+
+static const long long tab[64] = {
+ 0x100000005LL, 0, 0, 0, 3, 0, 0, 20, 0, 4, 0, 11, 7, 0, 0, 21,
+ 30, 1, 0, 5, 28, 0, 12, 0, 17, 8, 0, 14, 0, 0, 0, 22,
+ 31, 0, 2, 19, 0, 10, 6, 0, 29, 0, 27, 0, 16, 13, 0, 0,
+ 0, 18, 9, 0, 0, 26, 15, 0, 0, 0, 25, 0, 0, 24, 23, 0 };
+
+__attribute__((noipa)) long long
+f (unsigned v)
+{
+ return tab[((unsigned) ((v & -v) * 0x2265b1f5U)) >> 26];
+}
+
+/* The CLZ table entry fits int only after it is subtracted from 31. */
+
+static const long long tab2[64] = {
+ 0x80000000LL, 29, 0, 6, 0, 12, 0, 0, 2, 0, 5, 0, 1, 0, 0, 0,
+ 0, 31, 0, 0, 25, 0, 16, 26, 0, 22, 0, 17, 8, 27, 0, 0,
+ 0, 23, 14, 20, 0, 18, 0, 9, 0, 28, 0, 11, 0, 4, 0, 0,
+ 0, 30, 24, 15, 0, 21, 7, 0, 0, 13, 19, 0, 0, 0, 10, 3 };
+
+__attribute__((noipa)) long long
+g (unsigned x)
+{
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return tab2[(x * 0xbb968a43U) >> 26];
+}
+
+/* The unsigned entry does not fit int, but the conversion back to
+ unsigned restores it. */
+
+static const unsigned tab3[64] = {
+ 0xffffffffU, 0, 0, 0, 3, 0, 0, 20, 0, 4, 0, 11, 7, 0, 0, 21,
+ 30, 1, 0, 5, 28, 0, 12, 0, 17, 8, 0, 14, 0, 0, 0, 22,
+ 31, 0, 2, 19, 0, 10, 6, 0, 29, 0, 27, 0, 16, 13, 0, 0,
+ 0, 18, 9, 0, 0, 26, 15, 0, 0, 0, 25, 0, 0, 24, 23, 0 };
+
+__attribute__((noipa)) unsigned
+h (unsigned v)
+{
+ return tab3[((unsigned) ((v & -v) * 0x2265b1f5U)) >> 26];
+}
+
+int
+main (void)
+{
+ if (f (0) != 0x100000005LL || f (1) != 0 || f (0x80000000U) != 31)
+ __builtin_abort ();
+ if (g (0) != 0x80000000LL || g (1) != 0 || g (0x80000000U) != 31
+ || g (0x1234) != 12)
+ __builtin_abort ();
+ if (h (0) != 0xffffffffU || h (1) != 0 || h (0x80000000U) != 31)
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index fba4387f503..4698839c62e 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -3528,6 +3528,10 @@ simplify_count_zeroes (gimple_stmt_iterator *gsi)
ctz_val = 32;
zero_ok = CLZ_DEFINED_VALUE_AT_ZERO (SCALAR_INT_TYPE_MODE (input_type),
ctz_val) == 2;
+ /* The sequence below computes the value at zero in int, as
+ INPUT_BITS - 1 minus the value that it selects for zero. */
+ if (!wi::fits_to_tree_p (zero_val, integer_type_node))
+ return false;
zero_val = input_bits - 1 - zero_val;
}
int nargs = 2;
@@ -3546,6 +3550,13 @@ simplify_count_zeroes (gimple_stmt_iterator *gsi)
nargs = 1;
}
+ /* The sequence below computes the value at zero in int and converts it
+ to TYPE, which keeps it for a CTZ if TYPE is not wider than int. */
+ if (!wi::fits_to_tree_p (zero_val, integer_type_node)
+ && (fn == IFN_CLZ
+ || TYPE_PRECISION (type) > TYPE_PRECISION (integer_type_node)))
+ return false;
+
gimple_seq seq = NULL;
gimple *g;
gcall *call = gimple_build_call_internal (fn, nargs, res_ops[0],
--
2.54.0 (Apple Git-157)