Hopefully this isn't controversial :)

A an area where LLVM generates much better code than GCC today is in
optimization of VLA branches guarded by VLA constants.

Normally in GIMPLE POLY_INT_CSTs have no upport and lower bound because well,
they're poly.  However SVE has defined minimum and maximum vector sizes in
the architecture[1] and so for us we do have bounds on these constants.

[1] https://developer.arm.com/documentation/102476/0101/Introducing-SVE

Today LLVM optimizes these simple expressions

#include <arm_sve.h>

int g (void)
{
  unsigned int vl = svcntb ();
  return vl < 257;
}

int h (void)
{
  return svcntw () <= 64;
}

away to

g():
        mov     w0, #1
        ret

h():
        mov     w0, #1
        ret

which is right because both are always true for any SVE vector length.

while GCC generates:

g():
        cntb    x0
        cmp     w0, 257
        cset    w0, cc
        ret
h():
        cntw    x0
        cmp     x0, 65
        cset    w0, cc
        ret

This patch extends ranger with a target hook poly_int_cst_value_range which
is different from the current costing only hooks used in the vectorizer because
those hooks can't be relied upon for correctness.

This new hook allows us specify the minimum and maximum bounds of a POLY_INT_CST
and have ranger use it in range_query::get_tree_range.

This gets GCC to fold away many known true or known false comparisons in
codegen today and we generate much simpler loop pre-headers.

I have kept the AArch64 parts in this patch to get some extra eyes on it, but
will split them out on commit.

Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.

Ok for master?

Thanks,
Tamar

gcc/ChangeLog:

        * config/aarch64/aarch64.cc (aarch64_poly_int_cst_value_range): New.
        (TARGET_POLY_INT_CST_VALUE_RANGE): Implement hook using it.
        * target.def (poly_int_cst_value_range): New.
        * doc/tm.texi.in: Document it.
        * doc/tm.texi: Regenerate.
        * targhooks.cc (default_poly_int_cst_value_range): New.
        * targhooks.h (default_poly_int_cst_value_range): New.
        * value-query.cc (range_query::get_tree_range): Use it.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/sve/slp_12.c: Update testcases
        * gcc.target/aarch64/sve/cnt_fold_7.c: New test.
        * gcc.target/aarch64/sve/cnt_fold_7_run.c: New test.

---
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 
72822ca621f52afee144bffa76bc75aac28adae1..7e12bb0e2c003b80008dd0f3c9fa304c7273ef39
 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -31053,6 +31053,27 @@ aarch64_estimated_poly_value (poly_int64 val,
   return val.coeffs[0] + val.coeffs[1] * over_128 / 128;
 }
 
+/* Implement TARGET_POLY_INT_CST_VALUE_RANGE.  */
+
+static bool
+aarch64_poly_int_cst_value_range (poly_int64 val, HOST_WIDE_INT *min_val,
+                                 HOST_WIDE_INT *max_val)
+{
+  unsigned int const_vg;
+  if (aarch64_sve_vg.is_constant (&const_vg))
+    {
+      *min_val = val.coeffs[0];
+      *max_val = *min_val;
+      return true;
+    }
+
+  *min_val = val.coeffs[0];
+  *max_val = val.coeffs[0] + val.coeffs[1] * 15;
+  if (*min_val > *max_val)
+    std::swap (*min_val, *max_val);
+  return true;
+}
+
 
 /* Return true for types that could be supported as SIMD return or
    argument types.  */
@@ -34370,6 +34391,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_ESTIMATED_POLY_VALUE
 #define TARGET_ESTIMATED_POLY_VALUE aarch64_estimated_poly_value
 
+#undef TARGET_POLY_INT_CST_VALUE_RANGE
+#define TARGET_POLY_INT_CST_VALUE_RANGE aarch64_poly_int_cst_value_range
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE aarch64_attribute_table
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 
6178db74d2fc5d14e8618ee655de05739cb3207c..bfb7e94cf163a75688e6f99473bad4864a57fb24
 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7509,6 +7509,14 @@ the @code{POLY_VALUE_MIN}, @code{POLY_VALUE_MAX} and
 implementation returns the lowest possible value of @var{val}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_POLY_INT_CST_VALUE_RANGE (poly_int64 
@var{val}, HOST_WIDE_INT *@var{min_val}, HOST_WIDE_INT *@var{max_val})
+Return true if the target can give a conservative inclusive range for
+the runtime value of @var{val}, storing the lower bound in @var{min_val}
+and the upper bound in @var{max_val}.  This hook is used for correctness
+rather than for costing, so the returned bounds must be safe for all
+architecturally-valid runtime values.
+@end deftypefn
+
 @deftypefn {Target Hook} bool TARGET_AVOID_STORE_FORWARDING_P 
(vec<store_fwd_info>, @var{rtx}, @var{int}, @var{bool})
 Given a list of stores and a load instruction that reads from the location
 of the stores, this hook decides if it's profitable to emit additional code
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 
0a97b0fa2e44014fa9e27d07a06d407a25e1ecc4..3aae8b2de9a3ecee41803a5dcba8f31b4af55b93
 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4812,6 +4812,8 @@ Define this macro if a non-short-circuit operation 
produced by
 
 @hook TARGET_ESTIMATED_POLY_VALUE
 
+@hook TARGET_POLY_INT_CST_VALUE_RANGE
+
 @hook TARGET_AVOID_STORE_FORWARDING_P
 
 @node Scheduling
diff --git a/gcc/target.def b/gcc/target.def
index 
884fe1bd57e17b3ae92cd052a340f02a2559da31..aab20e89bb16fd9bb7d70d1c403268a65990a3e4
 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4217,6 +4217,16 @@ implementation returns the lowest possible value of 
@var{val}.",
  HOST_WIDE_INT, (poly_int64 val, poly_value_estimate_kind kind),
  default_estimated_poly_value)
 
+DEFHOOK
+(poly_int_cst_value_range,
+ "Return true if the target can give a conservative inclusive range for\n\
+the runtime value of @var{val}, storing the lower bound in @var{min_val}\n\
+and the upper bound in @var{max_val}.  This hook is used for correctness\n\
+rather than for costing, so the returned bounds must be safe for all\n\
+architecturally-valid runtime values.",
+ bool, (poly_int64 val, HOST_WIDE_INT *min_val, HOST_WIDE_INT *max_val),
+ default_poly_int_cst_value_range)
+
 /* Permit speculative instructions in delay slots during delayed-branch
    scheduling.  */
 DEFHOOK
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 
388f696c8aa1050edbbc7efbdb01db60d84b4fe0..71f6f769e50298e32434ee94e87a1f3a3dfeef69
 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -2164,6 +2164,19 @@ default_estimated_poly_value (poly_int64 x, 
poly_value_estimate_kind)
   return x.coeffs[0];
 }
 
+/* The default implementation of TARGET_POLY_INT_CST_VALUE_RANGE.  */
+
+bool
+default_poly_int_cst_value_range (poly_int64 x, HOST_WIDE_INT *min_val,
+                                 HOST_WIDE_INT *max_val)
+{
+  if (!x.is_constant (min_val))
+    return false;
+
+  *max_val = *min_val;
+  return true;
+}
+
 /* For hooks which use the MOVE_RATIO macro, this gives the legacy default
    behavior.  SPEED_P is true if we are compiling for speed.  */
 
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 
86de6ef8a699d72a36c665c697c89ca58314f3fe..90ebc1f514d47ca736c14a641c69ab94f968bb1b
 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -246,6 +246,8 @@ extern int default_frame_allocation_cost (frame_cost_type,
 extern bool default_slow_unaligned_access (machine_mode, unsigned int);
 extern HOST_WIDE_INT default_estimated_poly_value (poly_int64,
                                                   poly_value_estimate_kind);
+extern bool default_poly_int_cst_value_range (poly_int64, HOST_WIDE_INT *,
+                                             HOST_WIDE_INT *);
 
 extern bool default_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT,
                                                    unsigned int,
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c 
b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c
new file mode 100644
index 
0000000000000000000000000000000000000000..d64933527da7e3e85f37dc231355eb4cd30a1422
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c
@@ -0,0 +1,154 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+
+/*
+** b_lt_257:
+**     mov     w0, 1
+**     ret
+*/
+int
+b_lt_257 (void)
+{
+  unsigned int vl = svcntb ();
+
+  return vl < 257;
+}
+
+/*
+** h_ge_8:
+**     mov     w0, 1
+**     ret
+*/
+int
+h_ge_8 (void)
+{
+  return svcnth () >= 8;
+}
+
+/*
+** w_le_64:
+**     mov     w0, 1
+**     ret
+*/
+int
+w_le_64 (void)
+{
+  return svcntw () <= 64;
+}
+
+/*
+** d_gt_1:
+**     mov     w0, 1
+**     ret
+*/
+int
+d_gt_1 (void)
+{
+  return svcntd () > 1;
+}
+
+/*
+** b_ne_0:
+**     mov     w0, 1
+**     ret
+*/
+int
+b_ne_0 (void)
+{
+  return svcntb () != 0;
+}
+
+/*
+** b_le_15:
+**     mov     w0, 0
+**     ret
+*/
+int
+b_le_15 (void)
+{
+  return svcntb () <= 15;
+}
+
+/*
+** h_lt_8:
+**     mov     w0, 0
+**     ret
+*/
+int
+h_lt_8 (void)
+{
+  return svcnth () < 8;
+}
+
+/*
+** w_gt_64:
+**     mov     w0, 0
+**     ret
+*/
+int
+w_gt_64 (void)
+{
+  return svcntw () > 64;
+}
+
+/*
+** d_eq_0:
+**     mov     w0, 0
+**     ret
+*/
+int
+d_eq_0 (void)
+{
+  return svcntd () == 0;
+}
+
+/*
+** b_lt_256:
+**     cntb    x0
+**     cmp     x0, 256
+**     cset    w0, cc
+**     ret
+*/
+int
+b_lt_256 (void)
+{
+  return svcntb () < 256;
+}
+
+/*
+** w_gt_4:
+**     cntw    x0
+**     cmp     x0, 4
+**     cset    w0, hi
+**     ret
+*/
+int
+w_gt_4 (void)
+{
+  return svcntw () > 4;
+}
+
+/*
+** b_pat_all_lt_257:
+**     mov     w0, 1
+**     ret
+*/
+int
+b_pat_all_lt_257 (void)
+{
+  return svcntb_pat (SV_ALL) < 257;
+}
+
+/*
+** w_pat_all_le_64:
+**     mov     w0, 1
+**     ret
+*/
+int
+w_pat_all_le_64 (void)
+{
+  return svcntw_pat (SV_ALL) <= 64;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c 
b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c
new file mode 100644
index 
0000000000000000000000000000000000000000..59217ab343ec03509bccc6d12098e6264adaa8b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c
@@ -0,0 +1,47 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include <arm_sve.h>
+
+#define CHECK(EXPR) \
+  do \
+    { \
+      if (!(EXPR)) \
+       __builtin_abort (); \
+    } \
+  while (0)
+
+int
+main (void)
+{
+  unsigned int b = svcntb ();
+  unsigned int h = svcnth ();
+  unsigned int w = svcntw ();
+  unsigned int d = svcntd ();
+
+  CHECK (b >= 16 && b <= 256);
+  CHECK (h >= 8 && h <= 128);
+  CHECK (w >= 4 && w <= 64);
+  CHECK (d >= 2 && d <= 32);
+
+  CHECK (b < 257);
+  CHECK (h >= 8);
+  CHECK (w <= 64);
+  CHECK (d > 1);
+  CHECK (b != 0);
+
+  CHECK (!(b <= 15));
+  CHECK (!(h < 8));
+  CHECK (!(w > 64));
+  CHECK (!(d == 0));
+
+  CHECK ((svcntb () < 256) == (b < 256));
+  CHECK ((svcntw () > 4) == (w > 4));
+
+  CHECK (svcntb_pat (SV_ALL) == b);
+  CHECK (svcntw_pat (SV_ALL) == w);
+  CHECK (svcntb_pat (SV_ALL) < 257);
+  CHECK (svcntw_pat (SV_ALL) <= 64);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c 
b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
index 
0b9f8d9bdfaaf868ba0bd73ae83894c68e1613f8..0b1fef4d23a89b4fa46b45a2662924247eedce17
 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
@@ -47,14 +47,12 @@ TEST_ALL (VEC_PERM)
 
 /* We should use WHILEs for all accesses.  */
 /* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 30 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 30 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 18 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 24 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 21 } } */
 
 /* 6 for the 8-bit types and 2 for the 16-bit types.  */
 /* { dg-final { scan-assembler-times {\tuqdecb\t} 8 } } */
-/* 4 for the 16-bit types and 3 for the 32-bit types.  */
-/* { dg-final { scan-assembler-times {\tuqdech\t} 7 } } */
-/* 6 for the 32-bit types and 3 for the 64-bit types.  */
-/* { dg-final { scan-assembler-times {\tuqdecw\t} 9 } } */
-/* { dg-final { scan-assembler-times {\tuqdecd\t} 6 } } */
+/* { dg-final { scan-assembler-times {\tuqdech\t} 2 } } */
+/* { dg-final { scan-assembler-times {\tuqdecw\t} 3 } } */
+/* { dg-final { scan-assembler-not {\tuqdecd\t} } } */
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 
decf4e4d0ac0eddb2dd6657072bf7c62c2e2ed00..5ce7fac4caeddccd62b587d11b2174a1d0979bac
 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "gimple-range.h"
 #include "value-range-storage.h"
+#include "target.h"
 
 // range_query default methods.
 
@@ -404,7 +405,20 @@ range_query::get_tree_range (vrange &r, tree expr, gimple 
*stmt,
       if (POLY_INT_CST_P (expr))
        {
          unsigned int precision = TYPE_PRECISION (type);
-         r.set_varying (type);
+         HOST_WIDE_INT min_val, max_val;
+         if (tree_fits_poly_int64_p (expr)
+             && targetm.poly_int_cst_value_range (tree_to_poly_int64 (expr),
+                                                  &min_val, &max_val))
+           {
+             wide_int min = wi::shwi (min_val, precision);
+             wide_int max = wi::shwi (max_val, precision);
+             if (wi::le_p (min, max, TYPE_SIGN (type)))
+               as_a <irange> (r).set (type, min, max);
+             else
+               r.set_varying (type);
+           }
+         else
+           r.set_varying (type);
          r.update_bitmask ({ wi::zero (precision), get_nonzero_bits (expr) });
          return true;
        }


-- 
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 72822ca621f52afee144bffa76bc75aac28adae1..7e12bb0e2c003b80008dd0f3c9fa304c7273ef39 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -31053,6 +31053,27 @@ aarch64_estimated_poly_value (poly_int64 val,
   return val.coeffs[0] + val.coeffs[1] * over_128 / 128;
 }
 
+/* Implement TARGET_POLY_INT_CST_VALUE_RANGE.  */
+
+static bool
+aarch64_poly_int_cst_value_range (poly_int64 val, HOST_WIDE_INT *min_val,
+				  HOST_WIDE_INT *max_val)
+{
+  unsigned int const_vg;
+  if (aarch64_sve_vg.is_constant (&const_vg))
+    {
+      *min_val = val.coeffs[0];
+      *max_val = *min_val;
+      return true;
+    }
+
+  *min_val = val.coeffs[0];
+  *max_val = val.coeffs[0] + val.coeffs[1] * 15;
+  if (*min_val > *max_val)
+    std::swap (*min_val, *max_val);
+  return true;
+}
+
 
 /* Return true for types that could be supported as SIMD return or
    argument types.  */
@@ -34370,6 +34391,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_ESTIMATED_POLY_VALUE
 #define TARGET_ESTIMATED_POLY_VALUE aarch64_estimated_poly_value
 
+#undef TARGET_POLY_INT_CST_VALUE_RANGE
+#define TARGET_POLY_INT_CST_VALUE_RANGE aarch64_poly_int_cst_value_range
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE aarch64_attribute_table
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 6178db74d2fc5d14e8618ee655de05739cb3207c..bfb7e94cf163a75688e6f99473bad4864a57fb24 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7509,6 +7509,14 @@ the @code{POLY_VALUE_MIN}, @code{POLY_VALUE_MAX} and
 implementation returns the lowest possible value of @var{val}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_POLY_INT_CST_VALUE_RANGE (poly_int64 @var{val}, HOST_WIDE_INT *@var{min_val}, HOST_WIDE_INT *@var{max_val})
+Return true if the target can give a conservative inclusive range for
+the runtime value of @var{val}, storing the lower bound in @var{min_val}
+and the upper bound in @var{max_val}.  This hook is used for correctness
+rather than for costing, so the returned bounds must be safe for all
+architecturally-valid runtime values.
+@end deftypefn
+
 @deftypefn {Target Hook} bool TARGET_AVOID_STORE_FORWARDING_P (vec<store_fwd_info>, @var{rtx}, @var{int}, @var{bool})
 Given a list of stores and a load instruction that reads from the location
 of the stores, this hook decides if it's profitable to emit additional code
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 0a97b0fa2e44014fa9e27d07a06d407a25e1ecc4..3aae8b2de9a3ecee41803a5dcba8f31b4af55b93 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4812,6 +4812,8 @@ Define this macro if a non-short-circuit operation produced by
 
 @hook TARGET_ESTIMATED_POLY_VALUE
 
+@hook TARGET_POLY_INT_CST_VALUE_RANGE
+
 @hook TARGET_AVOID_STORE_FORWARDING_P
 
 @node Scheduling
diff --git a/gcc/target.def b/gcc/target.def
index 884fe1bd57e17b3ae92cd052a340f02a2559da31..aab20e89bb16fd9bb7d70d1c403268a65990a3e4 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4217,6 +4217,16 @@ implementation returns the lowest possible value of @var{val}.",
  HOST_WIDE_INT, (poly_int64 val, poly_value_estimate_kind kind),
  default_estimated_poly_value)
 
+DEFHOOK
+(poly_int_cst_value_range,
+ "Return true if the target can give a conservative inclusive range for\n\
+the runtime value of @var{val}, storing the lower bound in @var{min_val}\n\
+and the upper bound in @var{max_val}.  This hook is used for correctness\n\
+rather than for costing, so the returned bounds must be safe for all\n\
+architecturally-valid runtime values.",
+ bool, (poly_int64 val, HOST_WIDE_INT *min_val, HOST_WIDE_INT *max_val),
+ default_poly_int_cst_value_range)
+
 /* Permit speculative instructions in delay slots during delayed-branch
    scheduling.  */
 DEFHOOK
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 388f696c8aa1050edbbc7efbdb01db60d84b4fe0..71f6f769e50298e32434ee94e87a1f3a3dfeef69 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -2164,6 +2164,19 @@ default_estimated_poly_value (poly_int64 x, poly_value_estimate_kind)
   return x.coeffs[0];
 }
 
+/* The default implementation of TARGET_POLY_INT_CST_VALUE_RANGE.  */
+
+bool
+default_poly_int_cst_value_range (poly_int64 x, HOST_WIDE_INT *min_val,
+				  HOST_WIDE_INT *max_val)
+{
+  if (!x.is_constant (min_val))
+    return false;
+
+  *max_val = *min_val;
+  return true;
+}
+
 /* For hooks which use the MOVE_RATIO macro, this gives the legacy default
    behavior.  SPEED_P is true if we are compiling for speed.  */
 
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 86de6ef8a699d72a36c665c697c89ca58314f3fe..90ebc1f514d47ca736c14a641c69ab94f968bb1b 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -246,6 +246,8 @@ extern int default_frame_allocation_cost (frame_cost_type,
 extern bool default_slow_unaligned_access (machine_mode, unsigned int);
 extern HOST_WIDE_INT default_estimated_poly_value (poly_int64,
 						   poly_value_estimate_kind);
+extern bool default_poly_int_cst_value_range (poly_int64, HOST_WIDE_INT *,
+					      HOST_WIDE_INT *);
 
 extern bool default_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT,
 						    unsigned int,
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c
new file mode 100644
index 0000000000000000000000000000000000000000..d64933527da7e3e85f37dc231355eb4cd30a1422
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7.c
@@ -0,0 +1,154 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <arm_sve.h>
+
+/*
+** b_lt_257:
+**	mov	w0, 1
+**	ret
+*/
+int
+b_lt_257 (void)
+{
+  unsigned int vl = svcntb ();
+
+  return vl < 257;
+}
+
+/*
+** h_ge_8:
+**	mov	w0, 1
+**	ret
+*/
+int
+h_ge_8 (void)
+{
+  return svcnth () >= 8;
+}
+
+/*
+** w_le_64:
+**	mov	w0, 1
+**	ret
+*/
+int
+w_le_64 (void)
+{
+  return svcntw () <= 64;
+}
+
+/*
+** d_gt_1:
+**	mov	w0, 1
+**	ret
+*/
+int
+d_gt_1 (void)
+{
+  return svcntd () > 1;
+}
+
+/*
+** b_ne_0:
+**	mov	w0, 1
+**	ret
+*/
+int
+b_ne_0 (void)
+{
+  return svcntb () != 0;
+}
+
+/*
+** b_le_15:
+**	mov	w0, 0
+**	ret
+*/
+int
+b_le_15 (void)
+{
+  return svcntb () <= 15;
+}
+
+/*
+** h_lt_8:
+**	mov	w0, 0
+**	ret
+*/
+int
+h_lt_8 (void)
+{
+  return svcnth () < 8;
+}
+
+/*
+** w_gt_64:
+**	mov	w0, 0
+**	ret
+*/
+int
+w_gt_64 (void)
+{
+  return svcntw () > 64;
+}
+
+/*
+** d_eq_0:
+**	mov	w0, 0
+**	ret
+*/
+int
+d_eq_0 (void)
+{
+  return svcntd () == 0;
+}
+
+/*
+** b_lt_256:
+**	cntb	x0
+**	cmp	x0, 256
+**	cset	w0, cc
+**	ret
+*/
+int
+b_lt_256 (void)
+{
+  return svcntb () < 256;
+}
+
+/*
+** w_gt_4:
+**	cntw	x0
+**	cmp	x0, 4
+**	cset	w0, hi
+**	ret
+*/
+int
+w_gt_4 (void)
+{
+  return svcntw () > 4;
+}
+
+/*
+** b_pat_all_lt_257:
+**	mov	w0, 1
+**	ret
+*/
+int
+b_pat_all_lt_257 (void)
+{
+  return svcntb_pat (SV_ALL) < 257;
+}
+
+/*
+** w_pat_all_le_64:
+**	mov	w0, 1
+**	ret
+*/
+int
+w_pat_all_le_64 (void)
+{
+  return svcntw_pat (SV_ALL) <= 64;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c
new file mode 100644
index 0000000000000000000000000000000000000000..59217ab343ec03509bccc6d12098e6264adaa8b1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cnt_fold_7_run.c
@@ -0,0 +1,47 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O2" } */
+
+#include <arm_sve.h>
+
+#define CHECK(EXPR) \
+  do \
+    { \
+      if (!(EXPR)) \
+	__builtin_abort (); \
+    } \
+  while (0)
+
+int
+main (void)
+{
+  unsigned int b = svcntb ();
+  unsigned int h = svcnth ();
+  unsigned int w = svcntw ();
+  unsigned int d = svcntd ();
+
+  CHECK (b >= 16 && b <= 256);
+  CHECK (h >= 8 && h <= 128);
+  CHECK (w >= 4 && w <= 64);
+  CHECK (d >= 2 && d <= 32);
+
+  CHECK (b < 257);
+  CHECK (h >= 8);
+  CHECK (w <= 64);
+  CHECK (d > 1);
+  CHECK (b != 0);
+
+  CHECK (!(b <= 15));
+  CHECK (!(h < 8));
+  CHECK (!(w > 64));
+  CHECK (!(d == 0));
+
+  CHECK ((svcntb () < 256) == (b < 256));
+  CHECK ((svcntw () > 4) == (w > 4));
+
+  CHECK (svcntb_pat (SV_ALL) == b);
+  CHECK (svcntw_pat (SV_ALL) == w);
+  CHECK (svcntb_pat (SV_ALL) < 257);
+  CHECK (svcntw_pat (SV_ALL) <= 64);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
index 0b9f8d9bdfaaf868ba0bd73ae83894c68e1613f8..0b1fef4d23a89b4fa46b45a2662924247eedce17 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/slp_12.c
@@ -47,14 +47,12 @@ TEST_ALL (VEC_PERM)
 
 /* We should use WHILEs for all accesses.  */
 /* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 20 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 30 } } */
-/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 30 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h} 18 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s} 24 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d} 21 } } */
 
 /* 6 for the 8-bit types and 2 for the 16-bit types.  */
 /* { dg-final { scan-assembler-times {\tuqdecb\t} 8 } } */
-/* 4 for the 16-bit types and 3 for the 32-bit types.  */
-/* { dg-final { scan-assembler-times {\tuqdech\t} 7 } } */
-/* 6 for the 32-bit types and 3 for the 64-bit types.  */
-/* { dg-final { scan-assembler-times {\tuqdecw\t} 9 } } */
-/* { dg-final { scan-assembler-times {\tuqdecd\t} 6 } } */
+/* { dg-final { scan-assembler-times {\tuqdech\t} 2 } } */
+/* { dg-final { scan-assembler-times {\tuqdecw\t} 3 } } */
+/* { dg-final { scan-assembler-not {\tuqdecd\t} } } */
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index decf4e4d0ac0eddb2dd6657072bf7c62c2e2ed00..5ce7fac4caeddccd62b587d11b2174a1d0979bac 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "gimple-range.h"
 #include "value-range-storage.h"
+#include "target.h"
 
 // range_query default methods.
 
@@ -404,7 +405,20 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt,
       if (POLY_INT_CST_P (expr))
 	{
 	  unsigned int precision = TYPE_PRECISION (type);
-	  r.set_varying (type);
+	  HOST_WIDE_INT min_val, max_val;
+	  if (tree_fits_poly_int64_p (expr)
+	      && targetm.poly_int_cst_value_range (tree_to_poly_int64 (expr),
+						   &min_val, &max_val))
+	    {
+	      wide_int min = wi::shwi (min_val, precision);
+	      wide_int max = wi::shwi (max_val, precision);
+	      if (wi::le_p (min, max, TYPE_SIGN (type)))
+		as_a <irange> (r).set (type, min, max);
+	      else
+		r.set_varying (type);
+	    }
+	  else
+	    r.set_varying (type);
 	  r.update_bitmask ({ wi::zero (precision), get_nonzero_bits (expr) });
 	  return true;
 	}

Reply via email to