On Fri, Oct 31, 2025 at 2:56 PM Andrew Pinski
<[email protected]> wrote:
>
> On Fri, Oct 31, 2025 at 2:47 PM Martin Jambor <[email protected]> wrote:
> >
> > Hi,
> >
> > this patch adds the ability to infer ranges from loads from global
> > constant static aggregates which have static initializers.  Even when
> > the load has an ARRAY_REF with an unknown index and thus we do not know
> > the particular constant that is being loaded, we can traverse the
> > corresponding elements of the initializer and see if we know in what
> > range(s) the loaded value must fall - or for pointers we can sometimes
> > infer that the value cannot be NULL.
> >
> > I thought this was similar to fold_using_range::range_of_address and
> > so I decided to put my implementation alongside of it.  I hope that is
> > the correct place.
> >
> > Bootstrapped, LTO-bootstrapped and tested on x86_64-linux, bootstrap
> > on aarch64-linux is underway.  OK for master if it passes?
>
> I am 99% sure there are a few bug reports which this will fix.
> PR 102981 for one.

Actually no, that is a different issue. I can't find the bug report
which I know exists since I commented on it saying we could get the
range of the initializers.

Thanks,
Andrew

>
> >
> > Thanks,
> >
> > Martin
> >
> >
> > gcc/ChangeLog:
> >
> > 2025-10-31  Martin Jambor  <[email protected]>
> >
> >         * gimple-range-fold.h (class fold_using_range): New member
> >         function range_from_readonly_var.
> >         * gimple-range-fold.cc (fold_using_range::fold_stmt): Call
> >         range_from_readonly_var on assignments.
> >         (add_loaded_invariant_to_range): New function.
> >         (range_from_readonly_load): Likewise.
> >         (fold_using_range::range_from_readonly_var): Likewise.
> >         * params.opt (param_vrp_cstload_limit): New.
> >         * doc/invoke.texi (vrp-cstload-limit): Likewise.
> >
> > gcc/testsuite/ChangeLog:
> >
> > 2025-10-31  Martin Jambor  <[email protected]>
> >
> >         * gcc.dg/tree-ssa/vrp-from-cst-agg-1.c: New test.
> >         * gcc.dg/tree-ssa/vrp-from-cst-agg-2.c: Likewise.
> >         * gcc.dg/tree-ssa/vrp-from-cst-agg-3.c: Likewise.
> >         * gcc.dg/tree-ssa/vrp-from-cst-agg-4.c: Likewise.
> >         * gcc.dg/ipa/vrp-from-cst-agg-5.c: Likewise.
> > ---
> >  gcc/doc/invoke.texi                           |   3 +
> >  gcc/gimple-range-fold.cc                      | 214 +++++++++++++++++-
> >  gcc/gimple-range-fold.h                       |   1 +
> >  gcc/params.opt                                |   4 +
> >  gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-5.c |  33 +++
> >  .../gcc.dg/tree-ssa/vrp-from-cst-agg-1.c      |  36 +++
> >  .../gcc.dg/tree-ssa/vrp-from-cst-agg-2.c      |  57 +++++
> >  .../gcc.dg/tree-ssa/vrp-from-cst-agg-3.c      |  34 +++
> >  .../gcc.dg/tree-ssa/vrp-from-cst-agg-4.c      |  34 +++
> >  9 files changed, 412 insertions(+), 4 deletions(-)
> >  create mode 100644 gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-5.c
> >  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-1.c
> >  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-2.c
> >  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-3.c
> >  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-4.c
> >
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index b40fc892fa0..c2542f8b882 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -17632,6 +17632,9 @@ Increasing the cost multiplier will make vector 
> > loops more profitable.
> >  @item vrp-block-limit
> >  Maximum number of basic blocks before VRP switches to a lower memory 
> > algorithm.
> >
> > +@item vrp-cstload-limit
> > +Maximum number of VRP intervals inferred from a load from a constant 
> > aggregate.
> > +
> >  @item vrp-sparse-threshold
> >  Maximum number of basic blocks before VRP uses a sparse bitmap cache.
> >
> > diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> > index 06c645f3d08..b455589428e 100644
> > --- a/gcc/gimple-range-fold.cc
> > +++ b/gcc/gimple-range-fold.cc
> > @@ -639,10 +639,14 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, 
> > fur_source &src, tree name)
> >    if (!name)
> >      name = gimple_get_lhs (s);
> >
> > -  // Process addresses.
> > -  if (gimple_code (s) == GIMPLE_ASSIGN
> > -      && gimple_assign_rhs_code (s) == ADDR_EXPR)
> > -    return range_of_address (as_a <prange> (r), s, src);
> > +  // Process addresses and loads from static constructors.
> > +  if (gimple_code (s) == GIMPLE_ASSIGN)
> > +    {
> > +      if (gimple_assign_rhs_code (s) == ADDR_EXPR)
> > +       return range_of_address (as_a <prange> (r), s, src);
> > +      if (range_from_readonly_var (r, s))
> > +       return true;
> > +    }
> >
> >    gimple_range_op_handler handler (s);
> >    if (handler)
> > @@ -891,6 +895,208 @@ fold_using_range::range_of_address (prange &r, gimple 
> > *stmt, fur_source &src)
> >    return true;
> >  }
> >
> > +// VAL must be an interprocedural invariant.  If VAL is a pointer which 
> > cannot
> > +// be zero, then set it to nonzero if it is undefined and return true.  If 
> > VAL
> > +// is a pointer which can be zero return false.  If VAL is of another 
> > type, add
> > +// the constant to the set of ranges to R and return true.
> > +
> > +static bool
> > +add_loaded_invariant_to_range (vrange &r, tree val)
> > +{
> > +  if (POINTER_TYPE_P (TREE_TYPE (val)))
> > +    {
> > +      bool strict_overflow_p;
> > +      if (tree_single_nonzero_warnv_p (val, &strict_overflow_p))
> > +       {
> > +         if (r.undefined_p ())
> > +           r.set_nonzero (TREE_TYPE (val));
> > +         return true;
> > +       }
> > +      else
> > +       return false;
> > +    }
> > +  else
> > +    {
> > +      if (TREE_CODE (val) == REAL_CST
> > +         && real_isnan (TREE_REAL_CST_PTR (val)))
> > +       return false;
> > +
> > +      if (is_a <irange> (r))
> > +       {
> > +         irange &ir = as_a <irange> (r);
> > +         if (!r.contains_p (val)
> > +             && ir.num_pairs () >= (unsigned) param_vrp_cstload_limit)
> > +           return false;
> > +       }
> > +
> > +      value_range tmp (val, val, VR_RANGE);
> > +      if (r.undefined_p ())
> > +       r = tmp;
> > +      else
> > +       r.union_ (tmp);
> > +      return true;
> > +    }
> > +}
> > +
> > +// One step of fold_using_range::range_from_readonly_var.  Process 
> > expressions
> > +// in comps from index I to 0 according to the corresponding static 
> > initializer
> > +// in CONSTUCTOR, adding all possible loaded constants (which must be 
> > storable
> > +// to TYPE) to R.  Return true if the all necessary values were extracted 
> > and
> > +// added to R successfully.
> > +
> > +static bool
> > +range_from_readonly_load (vrange &r, tree type, tree constructor,
> > +                         const vec <tree> &comps, int i)
> > +{
> > +  unsigned ix;
> > +  tree index, val;
> > +  tree expr = comps[i];
> > +
> > +  gcc_assert (TREE_CODE (expr) == COMPONENT_REF
> > +             || TREE_CODE (expr) == ARRAY_REF);
> > +  tree op1 = TREE_OPERAND (expr, 1);
> > +  if (TREE_CODE (expr) == COMPONENT_REF
> > +      || TREE_CODE (op1) == INTEGER_CST)
> > +    {
> > +      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, 
> > val)
> > +       {
> > +         if (TREE_CODE (index) == FIELD_DECL)
> > +           {
> > +             if (index != op1)
> > +               continue;
> > +           }
> > +         else if (TREE_CODE (index) == INTEGER_CST
> > +                  && TREE_CODE (op1) == INTEGER_CST)
> > +           {
> > +             if (!tree_int_cst_equal (index, op1))
> > +               continue;
> > +           }
> > +         else
> > +           gcc_unreachable ();
> > +
> > +         if (TREE_CODE (val) == CONSTRUCTOR)
> > +           {
> > +             if (i > 0)
> > +               return range_from_readonly_load (r, type, val, comps, i - 
> > 1);
> > +             else
> > +               return false;
> > +           }
> > +         if (is_gimple_ip_invariant (val))
> > +           {
> > +             if (i != 0
> > +                 || (!useless_type_conversion_p (type, TREE_TYPE (val))))
> > +               return false;
> > +
> > +             return add_loaded_invariant_to_range (r, val);
> > +           }
> > +         return false;
> > +       }
> > +      return false;
> > +    }
> > +
> > +  tree arr_type = TREE_TYPE (constructor);
> > +  tree domain = TYPE_DOMAIN (arr_type);
> > +  if (!TYPE_MIN_VALUE (domain)
> > +      || !TYPE_MAX_VALUE (domain)
> > +      || !tree_fits_uhwi_p (TYPE_MIN_VALUE (domain))
> > +      || !tree_fits_uhwi_p (TYPE_MAX_VALUE (domain)))
> > +    return false;
> > +  unsigned HOST_WIDE_INT needed_count
> > +    = (tree_to_uhwi (TYPE_MAX_VALUE (domain))
> > +       - tree_to_uhwi (TYPE_MIN_VALUE (domain)) + 1);
> > +  unsigned HOST_WIDE_INT count = 0;
> > +
> > +  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
> > +    {
> > +      /* TODO: If the array index in the expr is an SSA_NAME with a known
> > +        range, we could use just values loaded from the corresponding array
> > +        elements.  */
> > +      count++;
> > +      if (TREE_CODE (val) == CONSTRUCTOR)
> > +       {
> > +         if (i <= 0
> > +             || !range_from_readonly_load (r, type, val, comps, i - 1))
> > +           return false;
> > +       }
> > +      else if (is_gimple_ip_invariant (val))
> > +       {
> > +         if (i != 0
> > +             || (!useless_type_conversion_p (type, TREE_TYPE (val))))
> > +           return false;
> > +
> > +         if (!add_loaded_invariant_to_range (r, val))
> > +           return false;
> > +       }
> > +      else
> > +       return false;
> > +    }
> > +
> > +  if (count < needed_count)
> > +    {
> > +      if (AGGREGATE_TYPE_P (type)
> > +         || !irange::supports_p (type))
> > +       return false;
> > +      if (!add_loaded_invariant_to_range (r, build_zero_cst (type)))
> > +       return false;
> > +      return true;
> > +    }
> > +  else
> > +    {
> > +      gcc_assert (count == needed_count);
> > +      return true;
> > +    }
> > +}
> > +
> > +// Attempt to calculate the range of value loaded by STMT (which must be an
> > +// assignment) if it is a load from a read-only aggregate variable.  If
> > +// successful, return true and set the discovered range in R.  Otherwise 
> > return
> > +// false and leave R untouched.
> > +
> > +bool
> > +fold_using_range::range_from_readonly_var (vrange &r, gimple *stmt)
> > +{
> > +  gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
> > +
> > +  tree t = gimple_assign_rhs1 (stmt);
> > +  if (TREE_CODE (t) != ARRAY_REF
> > +      && TREE_CODE (t) != COMPONENT_REF)
> > +    return false;
> > +  int count = 0;
> > +  while (TREE_CODE (t) == ARRAY_REF
> > +        || TREE_CODE (t) == COMPONENT_REF)
> > +    {
> > +      count++;
> > +      t = TREE_OPERAND (t, 0);
> > +    }
> > +  if (!VAR_P (t)
> > +      || !is_global_var (t)
> > +      || !TREE_READONLY (t)
> > +      || !DECL_INITIAL (t)
> > +      || TREE_CODE (DECL_INITIAL (t)) != CONSTRUCTOR)
> > +    return false;
> > +
> > +  t = gimple_assign_rhs1 (stmt);
> > +  auto_vec <tree, 4> comps;
> > +  comps.safe_grow (count, true);
> > +  int i = 0;
> > +  while (TREE_CODE (t) == ARRAY_REF
> > +        || TREE_CODE (t) == COMPONENT_REF)
> > +    {
> > +      comps[i] = t;
> > +      t = TREE_OPERAND (t, 0);
> > +      i++;
> > +    }
> > +
> > +  tree type = TREE_TYPE (gimple_assign_lhs (stmt));
> > +  gcc_checking_assert (value_range::supports_type_p (type));
> > +  value_range tmp (type);
> > +  bool res = range_from_readonly_load (tmp, type, DECL_INITIAL (t), comps,
> > +                                      count - 1);
> > +  if (res)
> > +    r = tmp;
> > +  return res;
> > +}
> > +
> >  // Calculate a range for phi statement S and return it in R.
> >  // If a range cannot be calculated, return false.
> >
> > diff --git a/gcc/gimple-range-fold.h b/gcc/gimple-range-fold.h
> > index 826a10fd9e8..bc5c6686a65 100644
> > --- a/gcc/gimple-range-fold.h
> > +++ b/gcc/gimple-range-fold.h
> > @@ -180,6 +180,7 @@ protected:
> >    bool range_of_call (vrange &r, gcall *call, fur_source &src);
> >    bool range_of_cond_expr (vrange &r, gassign* cond, fur_source &src);
> >    bool range_of_address (prange &r, gimple *s, fur_source &src);
> > +  bool range_from_readonly_var (vrange &r, gimple *stmt);
> >    bool range_of_phi (vrange &r, gphi *phi, fur_source &src);
> >    void range_of_ssa_name_with_loop_info (vrange &, tree, class loop *, 
> > gphi *,
> >                                          fur_source &src);
> > diff --git a/gcc/params.opt b/gcc/params.opt
> > index f8884e976e7..85c93b82963 100644
> > --- a/gcc/params.opt
> > +++ b/gcc/params.opt
> > @@ -1273,6 +1273,10 @@ The scaling multiplier as a percentage to apply to 
> > all scalar loop costing when
> >  Common Joined UInteger Var(param_vrp_block_limit) Init(150000) 
> > Optimization Param
> >  Maximum number of basic blocks before VRP switches to a fast model with 
> > less memory requirements.
> >
> > +-param=vrp-cstload-limit=
> > +Common Joined UInteger Var(param_vrp_cstload_limit) Init(4) 
> > IntegerRange(0, 255) Optimization Param
> > +Maximum number of VRP intervals inferred from a load from a constant 
> > aggregate.
> > +
> >  -param=vrp-sparse-threshold=
> >  Common Joined UInteger Var(param_vrp_sparse_threshold) Init(3000) 
> > Optimization Param
> >  Maximum number of basic blocks before VRP uses a sparse bitmap cache.
> > diff --git a/gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-5.c 
> > b/gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-5.c
> > new file mode 100644
> > index 00000000000..cd71d329487
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-5.c
> > @@ -0,0 +1,33 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O3 -fdump-ipa-cp-details" } */
> > +
> > +static const struct {
> > +    int w;
> > +    int h;
> > +} sizes[7] = {
> > +    { 16, 16 },
> > +    { 16,  8 },
> > +    {  8, 16 },
> > +    {  8,  8 },
> > +    {  8,  4 },
> > +    {  4,  8 },
> > +    {  4,  4 }
> > +};
> > +
> > +int baz(int, int);
> > +
> > +[[gnu::noinline]] void bar(int w, int h)
> > +{
> > +  for (int i = 0; i < w; i++)
> > +    for (int j = 0; i < h; j++)
> > +      baz (i, j);
> > +}
> > +
> > +void foo (int index)
> > +{
> > + int w = sizes[index].w;
> > + int h = sizes[index].h;
> > +
> > + bar (w, h);
> > +}
> > +/* { dg-final { scan-ipa-dump-times "irange" 2 "cp" } } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-1.c 
> > b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-1.c
> > new file mode 100644
> > index 00000000000..51b1fe6329c
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-1.c
> > @@ -0,0 +1,36 @@
> > +/* { dg-do link } */
> > +/* { dg-options "-O2" } */
> > +
> > +int ga = 1;
> > +int gb = 2;
> > +int gc = 3;
> > +volatile int gi;
> > +
> > +static const int *vars[3] = {&ga, &gb, &gc};
> > +
> > +void link_error (void);
> > +
> > +[[gnu::noinline]] int foo (int index)
> > +{
> > +  for (int i = 0; i < 4; ++i)
> > +    {
> > +      const int *p = vars[index];
> > +      if (!p)
> > +       link_error ();
> > +      else
> > +       gi = *p;
> > +    }
> > +  return 0;
> > +}
> > +
> > +[[gnu::noipa]] int
> > +get_index (void)
> > +{
> > +  return 1;
> > +}
> > +
> > +int main (int argc, char **argv)
> > +{
> > +  foo (get_index ());
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-2.c 
> > b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-2.c
> > new file mode 100644
> > index 00000000000..0f7677d90b8
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-2.c
> > @@ -0,0 +1,57 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O2" } */
> > +
> > +enum comp_cat_tag
> > +{
> > +  cc_partial_ordering,
> > +  cc_weak_ordering,
> > +  cc_strong_ordering,
> > +  cc_last
> > +};
> > +
> > +struct comp_cat_info_t
> > +{
> > +  const char *name;
> > +  const char *members[4];
> > +};
> > +
> > +static const struct comp_cat_info_t comp_cat_info[cc_last]
> > += {
> > +   { "partial_ordering", { "equivalent", "greater", "less", "unordered" } 
> > },
> > +   { "weak_ordering", { "equivalent", "greater", "less" } },
> > +   { "strong_ordering", { "equal", "greater", "less" } }
> > +};
> > +
> > +volatile const char *gp;
> > +
> > +[[gnu::noipa]] static void
> > +check (const char *p)
> > +{
> > +  if (!p)
> > +    __builtin_abort ();
> > +  gp = p;
> > +}
> > +
> > +[[gnu::noinline]] int foo (enum comp_cat_tag tag)
> > +{
> > +  for (int i = 0; i < 4; ++i)
> > +    {
> > +      const char *p = comp_cat_info[tag].members[i];
> > +      if (!p)
> > +       continue;;
> > +      check (p);
> > +    }
> > +  return 0;
> > +}
> > +
> > +[[gnu::noipa]] enum comp_cat_tag
> > +get_index (void)
> > +{
> > +  return cc_strong_ordering;
> > +}
> > +
> > +int main (int argc, char **argv)
> > +{
> > +  foo (get_index ());
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-3.c 
> > b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-3.c
> > new file mode 100644
> > index 00000000000..484099ca5a1
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-3.c
> > @@ -0,0 +1,34 @@
> > +/* { dg-do link } */
> > +/* { dg-options "-O2" } */
> > +
> > +volatile int gi;
> > +
> > +static const int values[3] = {5, 7, 11};
> > +
> > +void link_error (void);
> > +
> > +[[gnu::noinline]] int foo (int index)
> > +{
> > +  for (int i = 0; i < 3; ++i)
> > +    {
> > +      const int v = values[index];
> > +      if (v <= 2
> > +         || v > 666)
> > +       link_error ();
> > +      else
> > +       gi = v;
> > +    }
> > +  return 0;
> > +}
> > +
> > +[[gnu::noipa]] int
> > +get_index (void)
> > +{
> > +  return 1;
> > +}
> > +
> > +int main (int argc, char **argv)
> > +{
> > +  foo (get_index ());
> > +  return 0;
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-4.c 
> > b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-4.c
> > new file mode 100644
> > index 00000000000..7999ee1753a
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-4.c
> > @@ -0,0 +1,34 @@
> > +/* { dg-do link } */
> > +/* { dg-options "-O2" } */
> > +
> > +volatile int gi;
> > +
> > +static const int values[25] = {5, 7, 11};
> > +
> > +void link_error (void);
> > +
> > +[[gnu::noinline]] int foo (int index)
> > +{
> > +  for (int i = 0; i < 3; ++i)
> > +    {
> > +      const int v = values[index];
> > +      if (v <= -2
> > +         || v > 666)
> > +       link_error ();
> > +      else
> > +       gi = v;
> > +    }
> > +  return 0;
> > +}
> > +
> > +[[gnu::noipa]] int
> > +get_index (void)
> > +{
> > +  return 1;
> > +}
> > +
> > +int main (int argc, char **argv)
> > +{
> > +  foo (get_index ());
> > +  return 0;
> > +}
> > --
> > 2.51.1
> >

Reply via email to