On Thu, Oct 15, 2026 at 2:30 PM Jakub Jelinek <[email protected]> wrote:
>
> On Thu, Oct 15, 2026 at 01:03:46PM +0200, Richard Biener wrote:
> > > Do you want those changes as part of this patch, or can that be done
> > > incrementally?
> >
> > I think changes to the param heuristic should be done as followup.  Feel 
> > free to
> > improve the STRING_CST/RAW_DATA_CST processing though as you say
> > with the current param value it shouldn't matter much in practice.
>
> Ok, here is an updated patch.  I've changed it to always walk through
> num_pairs for both STRING_CST/RAW_DATA_CST, and added comment about the
> possible 2 loop optimizations for larger cases (but haven't implemented
> those because with the current limit cap it would never trigger).
> Added an extra test to check for a range with num_pairs () > 1.
>
> So far lightly tested (on tree-ssa.exp=vrp-from-cst*), ok for trunk if
> it passes full bootstrap/regtest?

OK.

Richard.

> 2026-10-15  Jakub Jelinek  <[email protected]>
>
>         PR tree-optimization/127879
>         * gimple-range-fold.cc (ctor_ref_range::accumulate_range): Remove
>         comment about RAW_DATA_CST.  Allow also STRING_CST, not just
>         CONSTRUCTOR.
>         (ctor_ref_range::accumulate_range_from_component_ref): Punt if
>         init is not a CONSTRUCTOR.
>         (ctor_ref_range::accumulate_range_from_array_ref): Handle
>         init being a STRING_CST.  Handle RAW_DATA_CSTs inside of a
>         CONSTRUCTOR.
>         (fold_using_range::range_from_readonly_var): Allow ctor being
>         a STRING_CST, rather than only a CONSTRUCTOR.
>
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-14.c: New test.
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-15.c: New test.
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-16.c: New test.
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-17.c: New test.
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-18.c: New test.
>         * gcc.dg/tree-ssa/vrp-from-cst-agg-19.c: New test.
>
> --- a/gcc/gimple-range-fold.cc  2026-10-14 11:14:47.238727728 +0200
> +++ b/gcc/gimple-range-fold.cc  2026-10-15 14:01:23.707258995 +0200
> @@ -1041,8 +1041,7 @@ ctor_ref_range::accumulate_range (tree i
>  {
>    if (i == m_component_path.length ())
>      return accumulate_range_from_scalar (init);
> -  /* TODO: Perhaps handle RAW_DATA_CST too.  */
> -  if (TREE_CODE (init) != CONSTRUCTOR)
> +  if (TREE_CODE (init) != CONSTRUCTOR && TREE_CODE (init) != STRING_CST)
>      return false;
>
>    tree ref = m_component_path[i];
> @@ -1112,6 +1111,8 @@ ctor_ref_range::accumulate_range_from_co
>  {
>    unsigned ix;
>    tree index, val;
> +  if (TREE_CODE (init) != CONSTRUCTOR)
> +    return false;
>    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, index, val)
>      {
>        if (index != fld)
> @@ -1182,6 +1183,51 @@ ctor_ref_range::accumulate_range_from_ar
>    unsigned ix;
>    tree index, val;
>
> +  if (TREE_CODE (init) == STRING_CST)
> +    {
> +      if (i != m_component_path.length ()
> +         || !INTEGRAL_TYPE_P (m_type)
> +         || TYPE_PRECISION (m_type) != CHAR_BIT
> +         || !useless_type_conversion_p (m_type, TREE_TYPE (TREE_TYPE (init)))
> +         || wi::gtu_p (max + 1 - min, TREE_STRING_LENGTH (init)))
> +       return false;
> +      for (unsigned j = 0; j < idx_r.num_pairs (); ++j)
> +       {
> +         wide_int l = idx_r.lower_bound (j);
> +         wide_int u = idx_r.upper_bound (j);
> +         if (wi::lt_p (u, min, TYPE_SIGN (type))
> +             || wi::gt_p (l, max, TYPE_SIGN (type)))
> +           continue;
> +         l = wi::max (l, min, TYPE_SIGN (type));
> +         u = wi::min (u, max, TYPE_SIGN (type));
> +         unsigned n = (l - min).to_uhwi ();
> +         unsigned e = (u - min).to_uhwi ();
> +         /* Note, if we'd have more than 256 iterations of the loops
> +            below or even across all the outer loop iterations, it might
> +            be more efficient to have a 256 bools array or 256-bit bitmask,
> +            gather which bytes are seen and then union_ those which were
> +            seen.  Given the current param limits this can't ever happen.  */
> +         wide_int wi_cst;
> +         if (TYPE_UNSIGNED (m_type))
> +           for (; n <= e; ++n)
> +             {
> +               wi_cst
> +                 = wi::uhwi ((unsigned char) TREE_STRING_POINTER (init)[n],
> +                             CHAR_BIT);
> +               m_r.union_ (int_range<1> (m_type, wi_cst, wi_cst));
> +             }
> +         else
> +           for (; n <= e; ++n)
> +             {
> +               wi_cst
> +                 = wi::shwi ((signed char) TREE_STRING_POINTER (init)[n],
> +                             CHAR_BIT);
> +               m_r.union_ (int_range<1> (m_type, wi_cst, wi_cst));
> +             }
> +       }
> +      return true;
> +    }
> +
>    // Build a range with the entire domain, and take out any elements which 
> are
>    // initialized.  If there's anything left in that range, it means there are
>    // missing elements and we should add a [0, 0] to the final range.
> @@ -1208,7 +1254,61 @@ ctor_ref_range::accumulate_range_from_ar
>        missing.intersect (int_range<2> (type, lo, hi, VR_ANTI_RANGE));
>        next = wi::add (hi, 1);
>
> -      if (intersects_p (idx_r, lo, hi) && !accumulate_range (val, i))
> +      if (TREE_CODE (val) == RAW_DATA_CST)
> +       {
> +         if (!intersects_p (idx_r, lo, hi))
> +           continue;
> +
> +         if (i != m_component_path.length ()
> +             || !INTEGRAL_TYPE_P (m_type)
> +             || TYPE_PRECISION (m_type) != CHAR_BIT
> +             || !useless_type_conversion_p (m_type,
> +                                            TREE_TYPE (val)))
> +           return false;
> +
> +         for (unsigned j = 0; j < idx_r.num_pairs (); ++j)
> +           {
> +             wide_int l = idx_r.lower_bound (j);
> +             wide_int u = idx_r.upper_bound (j);
> +             if (wi::lt_p (u, lo, TYPE_SIGN (type))
> +                 || wi::gt_p (l, hi, TYPE_SIGN (type)))
> +               continue;
> +             l = wi::max (l, lo, TYPE_SIGN (type));
> +             u = wi::min (u, hi, TYPE_SIGN (type));
> +             unsigned n = (l - lo).to_uhwi ();
> +             unsigned e = (u - lo).to_uhwi ();
> +             /* Note, if we'd have more than 256 iterations of the loops
> +                below or even across all the outer loop iterations, it might
> +                be more efficient to have a 256 bools array or 256-bit
> +                bitmask, gather which bytes are seen and then union_ those
> +                which were seen.  Given the current param limits this can't
> +                ever happen.  */
> +             wide_int wi_cst;
> +             if (TYPE_UNSIGNED (m_type))
> +               for (; n <= e; ++n)
> +                 {
> +                   wi_cst = wi::uhwi (RAW_DATA_UCHAR_ELT (val, n), CHAR_BIT);
> +                   m_r.union_ (int_range<1> (m_type, wi_cst, wi_cst));
> +                 }
> +             else
> +               for (; n <= e; ++n)
> +                 {
> +                   wi_cst = wi::shwi (RAW_DATA_SCHAR_ELT (val, n), CHAR_BIT);
> +                   m_r.union_ (int_range<1> (m_type, wi_cst, wi_cst));
> +                 }
> +           }
> +         continue;
> +       }
> +
> +      if (lo == hi)
> +       {
> +         if (!idx_r.contains_p (lo))
> +           continue;
> +       }
> +      else if (!intersects_p (idx_r, lo, hi))
> +       continue;
> +
> +      if (!accumulate_range (val, i))
>         return false;
>      }
>
> @@ -1278,7 +1378,8 @@ fold_using_range::range_from_readonly_va
>       the right part of the constructor then.  */
>    tree ctor = ctor_for_folding (t);
>    if (!ctor
> -      || TREE_CODE (ctor) != CONSTRUCTOR)
> +      || (TREE_CODE (ctor) != CONSTRUCTOR
> +         && TREE_CODE (ctor) != STRING_CST))
>      return false;
>
>    value_range tmp (type);
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-14.c       2026-10-15 
> 13:31:13.511589115 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-14.c       2026-10-15 
> 14:18:33.591339214 +0200
> @@ -0,0 +1,78 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized --param vrp-cstload-limit=255" } 
> */
> +/* { dg-final { scan-tree-dump-times "PHI <(?:-1\\\(\[0-9]*\\\), 
> 1|1\\\(\[0-9]*\\\), -1)\\\(\[0-9]*\\\)>" 4 "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = \[abce]\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = d.b\\\[" "optimized" } } */
> +
> +const unsigned char a[] = { 100, 108, 48, 49, 50, 51, 52, 53, 54, 55, 56, 
> 57, 0, 24, 18, 35, 98 };
> +const unsigned char b[] = { 100, 108, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           0, 24, 18, 35, 98 };
> +const struct { int a; unsigned char b; } c[] = { { 0, 100 }, { 0, 108 }, { 
> 0, 48 }, { 0, 49 }, { 0, 50 }, { 0, 51 },
> +                                                { 0, 52 }, { 0, 53 }, { 0, 
> 54 }, { 0, 55 }, { 0, 56 }, { 0, 57 },
> +                                                { 0, 0 }, { 0, 24 }, { 0, 18 
> }, { 0, 35 }, { 0, 98 } };
> +const struct { int a; unsigned char b[]; } d = { 0, { 100, 108, 48, 49, 50, 
> 51, 52, 53, 54, 55, 56, 57, 0, 24, 18, 35, 98 } };
> +const unsigned char e[] = { 100, 108, 48, 48, 90, 1, 108, 53, 54, 55, 56, 
> 57, 0, 24, 18, 35, 98 };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= 48 && a[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 220)
> +    return -1;
> +  if (b[x] >= 48 && b[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +baz (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (c[x].b >= 48 && c[x].b <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +qux (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (d.b[x] >= 48 && d.b[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +xyzzy (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (x >= 4 && x <= 6)
> +    return -2;
> +  if (e[x] >= 48 && e[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-15.c       2026-10-15 
> 13:31:13.511725686 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-15.c       2026-10-15 
> 13:31:13.511725686 +0200
> @@ -0,0 +1,29 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fexec-charset=UTF-8" } */
> +/* { dg-final { scan-tree-dump-times "PHI <(?:-1\\\(\[0-9]*\\\), 
> 1|1\\\(\[0-9]*\\\), -1)\\\(\[0-9]*\\\)>" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = a\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = b.b\\\[" "optimized" } } */
> +
> +const unsigned char a[] = " !0123456789\0{}~";
> +const struct { int a; unsigned char b[]; } b = { 0, " !0123456789\0{}~" };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= '0' && a[x] <= '9')
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (b.b[x] >= '0' && b.b[x] <= '9')
> +    return 1;
> +  return 0;
> +}
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-16.c       2026-10-15 
> 13:31:13.511854671 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-16.c       2026-10-15 
> 13:31:13.511854671 +0200
> @@ -0,0 +1,29 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times "PHI <(?:-1\\\(\[0-9]*\\\), 
> 1|1\\\(\[0-9]*\\\), -1)\\\(\[0-9]*\\\)>" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = a\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " = b.b\\\[" "optimized" } } */
> +
> +const signed char a[] = { 28, -36, -2, -1, 2, 4, -3, -4, 1, 3, 0, 5, -37, 29 
> };
> +const struct { int a; signed char b[]; } b = { 0, { 28, -36, -2, -1, 2, 4, 
> -3, -4, 1, 3, 0, 5, -37, 29 } };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= -4 && a[x] <= 5)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (b.b[x] >= -4 && b.b[x] <= 5)
> +    return 1;
> +  return 0;
> +}
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-17.c       2026-10-15 
> 13:31:13.511981391 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-17.c       2026-10-15 
> 13:31:13.511981391 +0200
> @@ -0,0 +1,66 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized --param vrp-cstload-limit=255" } 
> */
> +/* { dg-final { scan-tree-dump " = a\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump " = b\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump " = c\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump " = d.b\\\[" "optimized" } } */
> +
> +const unsigned char a[] = { 100, 108, 48, 49, 50, 51, 52, 53, 58, 55, 56, 
> 57, 0, 24, 18, 35, 98 };
> +const unsigned char b[] = { 100, 108, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 47, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 
> 50, 51, 52, 53, 54, 55, 56, 57,
> +                           0, 24, 18, 35, 98 };
> +const struct { int a; unsigned char b; } c[] = { { 0, 100 }, { 0, 108 }, { 
> 0, 48 }, { 0, 49 }, { 0, 50 }, { 0, 51 },
> +                                                { 0, 52 }, { 0, 58 }, { 0, 
> 54 }, { 0, 55 }, { 0, 56 }, { 0, 57 },
> +                                                { 0, 0 }, { 0, 24 }, { 0, 18 
> }, { 0, 35 }, { 0, 98 } };
> +const struct { int a; unsigned char b[]; } d = { 0, { 100, 108, 48, 49, 50, 
> 51, 47, 53, 54, 55, 56, 57, 0, 24, 18, 35, 98 } };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= 48 && a[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 220)
> +    return -1;
> +  if (b[x] >= 48 && b[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +baz (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (c[x].b >= 48 && c[x].b <= 57)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +qux (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (d.b[x] >= 48 && d.b[x] <= 57)
> +    return 1;
> +  return 0;
> +}
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-18.c       2026-10-15 
> 13:31:13.512113945 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-18.c       2026-10-15 
> 13:31:13.512113945 +0200
> @@ -0,0 +1,28 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fexec-charset=UTF-8" } */
> +/* { dg-final { scan-tree-dump " = a\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump " = b.b\\\[" "optimized" } } */
> +
> +const unsigned char a[] = " !012345+789\0{}~";
> +const struct { int a; unsigned char b[]; } b = { 0, " !01234567+9\0{}~" };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= '0' && a[x] <= '9')
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (b.b[x] >= '0' && b.b[x] <= '9')
> +    return 1;
> +  return 0;
> +}
> --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-19.c       2026-10-15 
> 13:31:13.512222455 +0200
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-19.c       2026-10-15 
> 13:31:13.512222455 +0200
> @@ -0,0 +1,28 @@
> +/* PR tree-optimization/127879 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump " = a\\\[" "optimized" } } */
> +/* { dg-final { scan-tree-dump " = b.b\\\[" "optimized" } } */
> +
> +const signed char a[] = { 28, -36, -2, -1, 2, 4, -3, -4, 1, 3, 0, -5, -37, 
> 29 };
> +const struct { int a; signed char b[]; } b = { 0, { 28, -36, -2, -1, 2, 4, 
> -3, -4, 1, 3, 0, -5, -37, 29 } };
> +
> +int
> +foo (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (a[x] >= -4 && a[x] <= 5)
> +    return 1;
> +  return 0;
> +}
> +
> +int
> +bar (int x)
> +{
> +  if (x < 2 || x >= 12)
> +    return -1;
> +  if (b.b[x] >= -4 && b.b[x] <= 5)
> +    return 1;
> +  return 0;
> +}
>
>
>         Jakub
>

Reply via email to