From: Kyrylo Tkachov <[email protected]>
signed_integer_sat_trunc builds the constants of
(unsigned) X + NT_MAX + 1 > UNSIGNED_MAX ? saturate : (NT) X
at the precision of X, but the captured constants belong to the type of
the conversion, which the match never constrains. A variable shift
leaves the shift result in int and the comparison narrows it, so the two
precisions differ and wi::eq_p asserts:
during GIMPLE pass: vect
internal compiler error: in decompose, at wide-int.h:1049
gimple_signed_integer_sat_trunc
vect_recog_sat_trunc_pattern
Require the conversion to keep the precision of X. A narrowing
conversion compares only the low bits of X, so the expression is not a
saturating truncation of X. Nothing that used to be matched is lost,
because any case whose precisions differed hit the assert rather than
reaching a result. Also require the result to be narrower than X, which
is what makes the widths of the masks below valid.
Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill
gcc/ChangeLog:
PR tree-optimization/126982
* match-sat-alu.pd (signed_integer_sat_trunc): Require the
conversion to keep the precision of the operand, and the result
to be narrower than it.
gcc/testsuite/ChangeLog:
PR tree-optimization/126982
* gcc.dg/vect/pr126982.c: New test.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/match-sat-alu.pd | 7 +++-
gcc/testsuite/gcc.dg/vect/pr126982.c | 53 ++++++++++++++++++++++++++++
2 files changed, 59 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.dg/vect/pr126982.c
diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index c7333e8c211..7bbe2bbb26e 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -434,7 +434,12 @@ along with GCC; see the file COPYING3. If not see
(negate (nop_convert? (convert (lt @0 integer_zerop)))))
INTEGER_CST@3)
(convert @0))
- (if (!TYPE_UNSIGNED (TREE_TYPE (@0)) && TYPE_UNSIGNED (TREE_TYPE (@4)))
+ /* The comparison has to be the unsigned reinterpretation of X, and the
+ conversion has to narrow, otherwise the constants below do not have the
+ precision the comparison is carried out at. */
+ (if (!TYPE_UNSIGNED (TREE_TYPE (@0)) && TYPE_UNSIGNED (TREE_TYPE (@4))
+ && TYPE_PRECISION (TREE_TYPE (@4)) == TYPE_PRECISION (TREE_TYPE (@0))
+ && TYPE_PRECISION (type) < TYPE_PRECISION (TREE_TYPE (@0)))
(with
{
unsigned itype_prec = TYPE_PRECISION (TREE_TYPE (@0));
diff --git a/gcc/testsuite/gcc.dg/vect/pr126982.c
b/gcc/testsuite/gcc.dg/vect/pr126982.c
new file mode 100644
index 00000000000..384df709c8a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/pr126982.c
@@ -0,0 +1,53 @@
+/* PR tree-optimization/126982 */
+/* { dg-require-effective-target vect_int } */
+
+#include "tree-vect.h"
+
+#define N 64
+
+/* A variable shift keeps the shift result in int, so the comparison of the
+ saturating truncation runs at a narrower precision than the value being
+ truncated. The comparison then only looks at the low bits, and the
+ truncation is not a saturating one. */
+
+__attribute__ ((noipa)) void
+sat_trunc (signed char *__restrict out, const short *__restrict in,
+ const unsigned short *__restrict shifts, int n)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ short x = in[i] >> (shifts[i] & 15);
+ signed char t = (signed char) x;
+ out[i] = (-128 <= x && x <= 127 ? t : x < 0 ? -128 : 127);
+ }
+}
+
+int
+main (void)
+{
+ short in[N];
+ unsigned short shifts[N];
+ signed char out[N];
+
+ check_vect ();
+
+ for (int i = 0; i < N; ++i)
+ {
+ in[i] = (short) (i * 7919 - 32768);
+ shifts[i] = i % 16;
+ }
+
+ sat_trunc (out, in, shifts, N);
+
+#pragma GCC novector
+ for (int i = 0; i < N; ++i)
+ {
+ short x = in[i] >> (shifts[i] & 15);
+ signed char t = (signed char) x;
+ signed char ref = (-128 <= x && x <= 127 ? t : x < 0 ? -128 : 127);
+ if (out[i] != ref)
+ abort ();
+ }
+
+ return 0;
+}
--
2.50.1 (Apple Git-155)