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
>

Reply via email to