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 >
