https://gcc.gnu.org/g:e3121605d2a5507b4ec456ad28f133c4f11f67b6
commit r17-5659-ge3121605d2a5507b4ec456ad28f133c4f11f67b6 Author: Jakub Jelinek <[email protected]> Date: Wed Oct 28 11:58:37 2026 +0100 gimple-fold-range: For range_from_readonly_var handle also STRING_CSTs and RAW_DATA_CSTs [PR127879] When writing the PR127685 fix/workaround, I've noticed the comment about RAW_DATA_CST and decided to implement it. WHen playing with testcases, I've noticed it doesn't handle even more important case, STRING_CST. RAW_DATA_CST is less important because it usually appears only for larger chunks, CPP_EMBED should usually appear for 64+ bytes in the #embed directive and being turned into RAW_DATA_CST, similarly the optimizations to turn large array initializers into RAW_DATA_CST usually need 64+ bytes or more. Sure, e.g. C designated initializers then can break it into smaller pieces, down to even 2 bytes in it, but the vrp-cstload-limit param has 255 as the upper limit (why?) and checks the whole size of the corresponding complete object, not even its subobject, so to handle RAW_DATA_CST, one needs to bump the limit I think. On the other side, the C/C++ braced_list_to_string optimization can turn initializers like {,un}signed char xyz[] = { 1, 2, 3 }; into = "\1\2\3", even for much smaller sizes. Anyway, both of these IMHO need to be handled inside of accumulate_range_from_array_ref, because that is where we know the indexes, can check if a specific byte in those intersects the index range and in that case, we don't need to build_int_cst just to throw it away and can just create a wide_int and union that. In both of these cases we can do this only for the end of the component path and have to verify m_type is integral and with CHAR_BIT precision. It can be either signed or unsigned, we need to either sign or zero extend the host byte from STRING_CST or RAW_DATA_CST when creating the wide_int. 2026-10-28 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. Reviewed-by: Richard Biener <[email protected]> Diff: --- gcc/gimple-range-fold.cc | 109 ++++++++++++++++++++- .../gcc.dg/tree-ssa/vrp-from-cst-agg-14.c | 78 +++++++++++++++ .../gcc.dg/tree-ssa/vrp-from-cst-agg-15.c | 29 ++++++ .../gcc.dg/tree-ssa/vrp-from-cst-agg-16.c | 29 ++++++ .../gcc.dg/tree-ssa/vrp-from-cst-agg-17.c | 66 +++++++++++++ .../gcc.dg/tree-ssa/vrp-from-cst-agg-18.c | 28 ++++++ .../gcc.dg/tree-ssa/vrp-from-cst-agg-19.c | 28 ++++++ 7 files changed, 363 insertions(+), 4 deletions(-) diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 3391916ff0db..72a74a737cbd 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -1041,8 +1041,7 @@ ctor_ref_range::accumulate_range (tree init, unsigned 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_component_ref (tree init, unsigned i, { unsigned ix; tree index, val; + if (TREE_CODE (init) != CONSTRUCTOR) + return false; if (TYPE_MAIN_VARIANT (TREE_TYPE (init)) == DECL_CONTEXT (fld)) FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), ix, index, val) { @@ -1212,6 +1213,51 @@ ctor_ref_range::accumulate_range_from_array_ref (tree init, unsigned i, 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. @@ -1238,7 +1284,61 @@ ctor_ref_range::accumulate_range_from_array_ref (tree init, unsigned i, 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; } @@ -1308,7 +1408,8 @@ fold_using_range::range_from_readonly_var (vrange &r, gimple *stmt, 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); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-14.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-14.c new file mode 100644 index 000000000000..eacc6ae00f43 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-14.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-15.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-15.c new file mode 100644 index 000000000000..fd798e3d3c05 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-15.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-16.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-16.c new file mode 100644 index 000000000000..cb6bba3e4ac5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-16.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-17.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-17.c new file mode 100644 index 000000000000..ea0ef7167316 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-17.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-18.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-18.c new file mode 100644 index 000000000000..ff4331bd9ca3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-18.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-19.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-19.c new file mode 100644 index 000000000000..e6175d451b80 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-19.c @@ -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; +}
