On Fri, Nov 13, 2026 at 2:30 PM Martin Jambor <[email protected]> wrote:
>
> 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);
The main thing I am worried about is that STRING_CST can be used with
more than byte sized objects (like wide strings or UTF16 strings) and
the above does not check that when you build the array_ref.
Thanks,
Andrea
> + 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
>