Hi the ability of ranger to infer ranges from loads from contants and even string literals has gratly expanded recently but currently it still cannot say anything about _5 in:
_4 = "test" + _3; _5 = *_4; This patch pattern matches this sequence and when detected (and the string length is within the limit), builds an artificial ARRAY_REF that is then sent down the existing machinery. Alternatively, we could define that having an integer SSA_NAME in component_path means that it should be treated like an ARRAY_REF, but that also feels a bit ugly. Bootstrapped on x86_64, the testsuite is still running. OK if it passes? Thanks, Martin gcc/ChangeLog: 2026-11-13 Martin Jambor <[email protected]> * gimple-range-fold.cc (fold_using_range::range_from_readonly_var): Pattern match offseted load from a STRING_CST spread over two gimple statements. gcc/testsuite/ChangeLog: 2026-11-13 Martin Jambor <[email protected]> * gcc.dg/tree-ssa/vrp-from-cst-agg-20.c: New test. --- gcc/gimple-range-fold.cc | 68 +++++++++++++++---- .../gcc.dg/tree-ssa/vrp-from-cst-agg-20.c | 33 +++++++++ 2 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-20.c diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 72a74a737cb..cb5d472d4d6 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -1387,26 +1387,64 @@ fold_using_range::range_from_readonly_var (vrange &r, gimple *stmt, // The component path of the reference, from the aggregate out. auto_vec <tree, 4> component_path; - while (TREE_CODE (t) == ARRAY_REF - || TREE_CODE (t) == COMPONENT_REF) - { - component_path.safe_push (t); - t = TREE_OPERAND (t, 0); - } - component_path.reverse (); - if (component_path.is_empty () - || (TREE_CODE (t) != VAR_DECL - && TREE_CODE (t) != CONST_DECL)) - return false; + tree ctor = NULL_TREE; + bool fake = false; - if (!tree_fits_uhwi_p (DECL_SIZE_UNIT (t)) - || tree_to_uhwi (DECL_SIZE_UNIT (t)) > limit) - return false; + // The code below only handles accesses through handled_components but a common + // way of looking at a character of string looks like this: + // _4 = "test" + _3; + // _5 = *_4; + // When looking for a value range of _5, construct an artificial ARRAY_REF + // into the string constant. + if (TREE_CODE (t) == MEM_REF + && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (t, 0)) + && integer_zerop (TREE_OPERAND (t, 1))) + { + gimple *def = SSA_NAME_DEF_STMT (TREE_OPERAND (t, 0)); + if (gimple_code (def) == GIMPLE_ASSIGN + && gimple_assign_rhs_code (def) == POINTER_PLUS_EXPR) + { + tree def_op_rhs1 = gimple_assign_rhs1 (def); + if (TREE_CODE (def_op_rhs1) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (def_op_rhs1, 0)) == STRING_CST + && ((unsigned) TREE_STRING_LENGTH (TREE_OPERAND (def_op_rhs1, 0)) + <= limit)) + { + ctor = TREE_OPERAND (def_op_rhs1, 0); + tree ft = build4 (ARRAY_REF, type, + TREE_OPERAND (def_op_rhs1, 0), + gimple_assign_rhs2 (def), + NULL_TREE, NULL_TREE); + component_path.safe_push (ft); + fake = true; + } + } + } + + if (!fake) + { + while (TREE_CODE (t) == ARRAY_REF + || TREE_CODE (t) == COMPONENT_REF) + { + component_path.safe_push (t); + t = TREE_OPERAND (t, 0); + } + component_path.reverse (); + if (component_path.is_empty () + || (TREE_CODE (t) != VAR_DECL + && TREE_CODE (t) != CONST_DECL)) + return false; + + if (!tree_fits_uhwi_p (DECL_SIZE_UNIT (t)) + || tree_to_uhwi (DECL_SIZE_UNIT (t)) > limit) + return false; + ctor = ctor_for_folding (t); + } /* TODO: We perhaps should try to handle at least some cases when the declaration is wrapped in a MEM_REF, but we need to be careful to look at the right part of the constructor then. */ - tree ctor = ctor_for_folding (t); if (!ctor || (TREE_CODE (ctor) != CONSTRUCTOR && TREE_CODE (ctor) != STRING_CST)) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-20.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-20.c new file mode 100644 index 00000000000..ef38023506f --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-20.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +int idx; +void unwanted_call (void); + +#if 0 +int +#else +inline int __attribute__((always_inline)) +#endif +foo (char *s) +{ + if (s[idx] == '%') + { + unwanted_call (); + unwanted_call (); + unwanted_call (); + unwanted_call (); + unwanted_call (); + unwanted_call (); + unwanted_call (); + return 1; + } + return 0; +} + +int bar (void) +{ + return foo ("test"); +} + +/* { dg-final { scan-assembler-not "unwanted_call" } } */ -- 2.56.0
