Hi,

On Sun, Nov 02 2025, Richard Biener wrote:
> On Fri, Oct 31, 2025 at 10: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?
>
> Comments below.
>
>> 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.
>> ---
[...]
>> 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);
>
> Please use get_array_ctor_element_at_index for constant indexing into
> an ARRAY_REF.

Will do, thanks for the pointer.

> I think you should be able to use fold_const_aggregate_ref
> to get you the CTOR for the "inner" part when you pass it the parent of the
> single variable ref.

The patch can handle more than one variable ARRAY_REF.  And I think it
should be able to handle accesses to small MxN arrays with both indices
unknown too.  My commit message might not have been clear about this, I
will adjust it and add a testcase for a 2x2 array.

> Then you can use the same function to fold you the
> outer (also constant refs) by tacking the resulting CTOR as base to the
> outer ref with the constant interesting index.
>
> IMO the recursion doesn't help readability.

In order to support more than one ARRAY_REF it is necessary, I'm afraid
(Or emulating it with a stack, of course).

>
> I think the param, instead of limiting the range complexity, needs to
> limit the amount
> of CTOR walking done.  Otherwise we'd scan a multi-gigabyte two-valued CTOR
> like {0, 1, 0, 1 ..} with arr[i] many many times.

Right.  Do we want both?  While it probably makes sense to traverse an
5x5 array containing ones and zeros, I am not sure if harm can be done
by building ranges with 25 pairs if the values are unique?  But perhaps
not.

> Having a CTOR pre-classified by IPA would be nice.

I guess we could cache the results and keep them for all functions, but
this is probably something for later.

>> +  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.  */
>
> Until you do this this really boils down to the complete static CTOR range 
> which
> should be easier to cache than the VR, CTOR pair you'd have to use otherwise.

I think caching the queries for particular known fields of RECORED_REFs
and/or particular known indices of ARRAY_REFs would not be much more
difficult.

In any event, thanks for looking at this,

Martin

Reply via email to