On Wed, 3 Dec 2025, Martin Jambor wrote:

> Hello,
> 
> On Mon, Nov 10 2025, Richard Biener wrote:
> > On Sat, 8 Nov 2025, Martin Jambor wrote:
> >
> 
> [...]
> 
> >> --- a/gcc/gimple-range-fold.cc
> >> +++ b/gcc/gimple-range-fold.cc
> [...]
> >> @@ -891,6 +895,190 @@ fold_using_range::range_of_address (prange &r, 
> >> gimple *stmt, fur_source &src)
> >>    return true;
> >>  }
> >>  
> >> +/* If TYPE is a pointer, return false.  Otherwise, add zero of TYPE 
> >> (which must
> >> +   be an integer) to R and return true.  */
> >> +
> >> +static bool
> >> +range_from_missing_constructor_part (vrange &r, tree type)
> >> +{
> >> +  if (POINTER_TYPE_P (type))
> >> +    return false;
> >> +  gcc_checking_assert (irange::supports_p (type));
> >> +  wide_int zero = wi::zero (TYPE_PRECISION (type));
> >> +  r.union_ (int_range<1> (type, zero, zero));
> >> +  return true;
> >> +}
> >> +
> >> +// One step of fold_using_range::range_from_readonly_var.  Process 
> >> expressions
> >> +// in COMPS which together load a value of TYPE, from index I to 0 
> >> according to
> >> +// the corresponding static initializer in CST which should be either a 
> >> scalar
> >> +// invariant or a constructor.  Currently TYPE must be either a pointer 
> >> or an
> >> +// integer.  If TYPE is a pointer, return true if all potentially loaded 
> >> values
> >> +// are known not to be zero and false if any of them can be zero.  
> >> Otherwise
> >> +// return true if it is possible to add all constants which can be loaded 
> >> from
> >> +// CST (which must be storable to TYPE) to R and do so.  In any case, 
> >> decrement
> >> +// *BUDGET when inspecting a scalar constant and return false (and fail) 
> >> if it
> >> +// is already zero.
> >> +// TODO: Add support for franges.
> >> +
> >> +static bool
> >> +range_from_readonly_load (vrange &r, tree type, tree cst,
> >> +                    const vec <tree> &comps, unsigned i,
> >> +                    int *budget)
> >> +{
> >> +  if (i == 0)
> >> +    {
> >> +      if (!*budget)
> >> +  return false;
> >> +      (*budget)--;
> >> +
> >> +      if (!is_gimple_ip_invariant (cst)
> >> +    || !useless_type_conversion_p (type, TREE_TYPE (cst)))
> >> +  return false;
> >
> > It might be beneficial to move the is_gimple_ip_invariant check
> > into the POINTER_TYPE_P branch.  The integer type case could
> > use a much simpler TREE_CODE (cst) == INTEGER_CST check.
> 
> OK, I have used the simpler check and then simply omitted
> is_gimple_ip_invariant because tree_single_nonzero_warnv_p should not
> strictly need it anyway.
> 
> >
> >> +
> >> +      if (POINTER_TYPE_P (type))
> >> +  {
> >> +    bool strict_overflow_p;
> >> +    return tree_single_nonzero_warnv_p (cst, &strict_overflow_p);
> >> +  }
> >> +      gcc_checking_assert (irange::supports_p (type));
> >> +      wide_int wi_cst = wi::to_wide (cst);
> >> +      r.union_ (int_range<1> (type, wi_cst, wi_cst));
> >> +      return true;
> >> +    }
> >> +  /* TODO: Perhaps handle RAW_DATA_CST too.  */
> >> +  if (TREE_CODE (cst) != CONSTRUCTOR)
> >> +    return false;
> >> +
> >> +  i--;
> >> +  tree expr = comps[i];
> >> +  unsigned ix;
> >> +  tree index, val;
> >> +
> >> +  if (TREE_CODE (expr) == COMPONENT_REF)
> >> +    {
> >> +      tree ref_fld = TREE_OPERAND (expr, 1);
> >> +      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (cst), ix, index, val)
> >> +  {
> >> +    if (index != ref_fld)
> >> +      continue;
> >
> > This should count towards budget, even if we do nothing with the
> > skipped element.
> 
> See below.
> 
> >
> >> +    return range_from_readonly_load (r, type, val, comps, i, budget);
> >> +  }
> >> +      if (TREE_CODE (TREE_TYPE (cst)) == RECORD_TYPE)
> >> +  return range_from_missing_constructor_part (r, type);
> >> +      else
> >> +  /* Missing constructor of a union field just isn't like other missing
> >> +     constructor parts.  */
> >> +  return false;
> >> +    }
> >> +
> >> +  gcc_assert (TREE_CODE (expr) == ARRAY_REF);
> >> +  tree op1 = TREE_OPERAND (expr, 1);
> >> +
> >> +  if (TREE_CODE (op1) == INTEGER_CST)
> >> +    {
> >> +      unsigned ctor_idx;
> >> +      val = get_array_ctor_element_at_index (cst, wi::to_offset (op1),
> >> +                                       &ctor_idx);
> >
> > This (IIRC) will at least use some kind of binary-search.
> >
> > I suppose the simplest way to implement the budget would be to
> > base it on the size of the readonly var right after we got the
> > ctor_for_folding.  't' should be a VAR_DECL or a CONST_DECL and
> > you should be able to reject non-constant DECL_SIZE_UNIT and
> > have a byte limit on the CTORs we support.
> >
> > That said, I'm still worried about the possibly large walk.  It's
> > bound by the size of the CTOR, at least we shouldn't walk any
> > CTOR element more than once.  But CTORs can be large.  You could
> > also do an estimation of an upper size bound by multiplying the
> > value-range element type size by the vrp-cstload-limit and taking
> > that as upper size bound.
> 
> OK, so hopefully I understood the above correctly and indeed implemented
> the limit in the way that I take the DECL_SIZE_UNIT of the variable and
> compare it to param_vrp_cstload_limit times the type size of the scalar
> the statement is loading and bail out if it is bigger.
> 
> >
> > Otherwise this looks OK now.
> 
> thanks a lot, I'd like to commit the patch below within a few days then.
> Please stop me if the above was not a pre-approval or if you find there
> is something wrong with the changed bits of the patch.

LGTM.

Richard.

> Martin
> 
> 
> This patch adds the ability to infer ranges from loads from global
> constant static aggregates which have static initializers.  Even when
> the load has one or more ARRAY_REFs with an unknown index and thus we
> do not know the particular constant that is being loaded, we can
> traverse the correponding elements of the initializer and see if we
> know in what range(s) the loaed 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.
> 
> The differences from v3 posted in
> https://inbox.sourceware.org/gcc-patches/[email protected]/
> are:
> 
>   - The walking budget is not passed around recursively and
>     decremented but is estimated from the size of the constant
>     variable, which must not be bigger than param_vrp_cstload_limit
>     times the size of the loaded type.
> 
>   - range_from_readonly_load uses a test for INTEGER_CST tree code to
>     verify a tree is an integer constant and leaves all pointer checks
>     to tree_single_nonzero_warnv_p.
> 
>   - In the testcases, all comparisons with number 666 were replaced
>     with tighter bounds.
> 
> The one important change compared to v2 posted in
> https://inbox.sourceware.org/gcc-patches/[email protected]/T/#u
> is:
> 
> - To get at the static constructor, the patch now uses
>   ctor_for_folding instead of directly looking into DECL_INITIAL.  In
>   LTO this loads the constructor on demand if necessary, and is also
>   capable of looking through symbol aliases, which is necessary for
>   example when the variable in question has been merged with another
>   one by IPA-ICF.
> 
> The changes compared to v1 posted in
> https://inbox.sourceware.org/gcc-patches/[email protected]/T/#m974fa6138bb074a239fb2a300e1d60bc087913ad
> are:
> 
> - The limiting parameter limits the number of constructor leaf
>   "elements" that we traverse.
> 
> - I have incorporated (hopefully) all suggestions from Richi, mainly
>   assuming value zero if we do not encounter a corresponding part of a
>   static initializer and using get_array_ctor_element_at_index to get
>   at an element of an array with known constant index.  I tried to
>   emulate how it is used in fold_array_ctor_reference, I hope I got it
>   right.
> 
> - I have changed to code to only work for pranges and iranges for now.
>   I will file a bug about extending it for frange and attach a patch
>   to the code I'm now adding to handle frange.  But as I mentioned in
>   an earlier email, something else will need to change too to get it
>   working as expected.
> 
> - I have changed range_from_readonly_load to simply work true or false
>   for pointers - where true means we can infer that it does not
>   contain NULL and false means we cannot do anything.  For integers, I
>   have changed it to instantiate int_range<1> rather than value_range
>   to add another constant.
> 
> - I have introduced range_from_missing_constructor_part to handle the
>   cases of missing bits in a constructor.
> 
> - I have put the (now simplified) functionality of
>   add_loaded_invariant_to_range at the beginning of
>   range_from_readonly_load.  That function remains recursive and walks
>   the memory reference expression "backwards" for reasons explained in
>   the previous email thread.
> 
> - The case where we need to iterate over all elements of an array was
>   optimized for pointers, we first check if all elements are present and
>   do not iterate if not.  For integers, we still need to iterate, of
>   course.
> 
> - I have added testcases to cover situations with two-dimensional
>   arrays.  Both when both indices are unknown and when only the second
>   one is (in the C-sense).
> 
> gcc/ChangeLog:
> 
> 2025-11-28  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.
>       (range_from_missing_constructor_part): 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-11-28  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/tree-ssa/vrp-from-cst-agg-5.c: Likewise.
>       * gcc.dg/tree-ssa/vrp-from-cst-agg-6.c: Likewise.
>       * gcc.dg/tree-ssa/vrp-from-cst-agg-7.c: Likewise.
>       * gcc.dg/ipa/vrp-from-cst-agg-1.c: Likewise.
> ---
>  gcc/doc/invoke.texi                           |   3 +
>  gcc/gimple-range-fold.cc                      | 201 +++++++++++++++++-
>  gcc/gimple-range-fold.h                       |   1 +
>  gcc/params.opt                                |   4 +
>  gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-1.c |  33 +++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-1.c      |  32 +++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-2.c      |  57 +++++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-3.c      |  30 +++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-4.c      |  30 +++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-5.c      |  36 ++++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-6.c      |  42 ++++
>  .../gcc.dg/tree-ssa/vrp-from-cst-agg-7.c      |  36 ++++
>  12 files changed, 501 insertions(+), 4 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-1.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
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-5.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-6.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-7.c
> 
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 3be6635c70a..d2ab45ac61a 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -17842,6 +17842,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 steps when inferring a value range 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 bd5e53516b7..6581e45870a 100644
> --- a/gcc/gimple-range-fold.cc
> +++ b/gcc/gimple-range-fold.cc
> @@ -652,10 +652,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)
> @@ -904,6 +908,195 @@ fold_using_range::range_of_address (prange &r, gimple 
> *stmt, fur_source &src)
>    return true;
>  }
>  
> +/* If TYPE is a pointer, return false.  Otherwise, add zero of TYPE (which 
> must
> +   be an integer) to R and return true.  */
> +
> +static bool
> +range_from_missing_constructor_part (vrange &r, tree type)
> +{
> +  if (POINTER_TYPE_P (type))
> +    return false;
> +  gcc_checking_assert (irange::supports_p (type));
> +  wide_int zero = wi::zero (TYPE_PRECISION (type));
> +  r.union_ (int_range<1> (type, zero, zero));
> +  return true;
> +}
> +
> +// One step of fold_using_range::range_from_readonly_var.  Process 
> expressions
> +// in COMPS which together load a value of TYPE, from index I to 0 according 
> to
> +// the corresponding static initializer in CST which should be either a 
> scalar
> +// invariant or a constructor.  Currently TYPE must be either a pointer or an
> +// integer.  If TYPE is a pointer, return true if all potentially loaded 
> values
> +// are known not to be zero and false if any of them can be zero.  Otherwise
> +// return true if it is possible to add all constants which can be loaded 
> from
> +// CST (which must be storable to TYPE) to R and do so.
> +// TODO: Add support for franges.
> +
> +static bool
> +range_from_readonly_load (vrange &r, tree type, tree cst,
> +                       const vec <tree> &comps, unsigned i)
> +{
> +  if (i == 0)
> +    {
> +      if (!useless_type_conversion_p (type, TREE_TYPE (cst)))
> +     return false;
> +
> +      if (POINTER_TYPE_P (type))
> +     {
> +       bool strict_overflow_p;
> +       return tree_single_nonzero_warnv_p (cst, &strict_overflow_p);
> +     }
> +
> +      if (TREE_CODE (cst) != INTEGER_CST)
> +     return false;
> +
> +      wide_int wi_cst = wi::to_wide (cst);
> +      r.union_ (int_range<1> (type, wi_cst, wi_cst));
> +      return true;
> +    }
> +  /* TODO: Perhaps handle RAW_DATA_CST too.  */
> +  if (TREE_CODE (cst) != CONSTRUCTOR)
> +    return false;
> +
> +  i--;
> +  tree expr = comps[i];
> +  unsigned ix;
> +  tree index, val;
> +
> +  if (TREE_CODE (expr) == COMPONENT_REF)
> +    {
> +      tree ref_fld = TREE_OPERAND (expr, 1);
> +      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (cst), ix, index, val)
> +     {
> +       if (index != ref_fld)
> +         continue;
> +       return range_from_readonly_load (r, type, val, comps, i);
> +     }
> +      if (TREE_CODE (TREE_TYPE (cst)) == RECORD_TYPE)
> +     return range_from_missing_constructor_part (r, type);
> +      else
> +     /* Missing constructor of a union field just isn't like other missing
> +        constructor parts.  */
> +     return false;
> +    }
> +
> +  gcc_assert (TREE_CODE (expr) == ARRAY_REF);
> +  tree op1 = TREE_OPERAND (expr, 1);
> +
> +  if (TREE_CODE (op1) == INTEGER_CST)
> +    {
> +      unsigned ctor_idx;
> +      val = get_array_ctor_element_at_index (cst, wi::to_offset (op1),
> +                                          &ctor_idx);
> +      if (!val)
> +     {
> +       if (ctor_idx < CONSTRUCTOR_NELTS (cst))
> +         return false;
> +       return range_from_missing_constructor_part (r, type);
> +     }
> +      return range_from_readonly_load (r, type, val, comps, i);
> +    }
> +
> +  tree arr_type = TREE_TYPE (cst);
> +  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);
> +  if (CONSTRUCTOR_NELTS (cst) < needed_count)
> +    {
> +      if (!range_from_missing_constructor_part (r, type))
> +     return false;
> +    }
> +
> +  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (cst), 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.  */
> +      if (!range_from_readonly_load (r, type, val, comps, i))
> +     return false;
> +    }
> +
> +  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 type = TREE_TYPE (gimple_assign_lhs (stmt));
> +  /* TODO: Add support for frange.  */
> +  if (!irange::supports_p (type)
> +      && !prange::supports_p (type))
> +    return false;
> +
> +  unsigned HOST_WIDE_INT limit = param_vrp_cstload_limit;
> +  if (!limit)
> +    return false;
> +
> +  tree t = gimple_assign_rhs1 (stmt);
> +  if (!tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (t))))
> +    return false;
> +  limit *= tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (t)));
> +
> +  unsigned count = 0;
> +  while (TREE_CODE (t) == ARRAY_REF
> +      || TREE_CODE (t) == COMPONENT_REF)
> +    {
> +      count++;
> +      t = TREE_OPERAND (t, 0);
> +    }
> +  if (!count
> +      || (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;
> +
> +  /* 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)
> +    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++;
> +    }
> +
> +  value_range tmp (type);
> +  bool res = range_from_readonly_load (tmp, type, ctor, comps, count);
> +  if (res)
> +    {
> +      if (POINTER_TYPE_P (type))
> +     r.set_nonzero (type);
> +      else
> +     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 760a107821a..6c389339548 100644
> --- a/gcc/gimple-range-fold.h
> +++ b/gcc/gimple-range-fold.h
> @@ -182,6 +182,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 7c226355c08..2863f132dff 100644
> --- a/gcc/params.opt
> +++ b/gcc/params.opt
> @@ -1269,6 +1269,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(32) IntegerRange(0, 
> 255) Optimization Param
> +Maximum number of steps when inferring a value range 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-1.c 
> b/gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-1.c
> new file mode 100644
> index 00000000000..c18146a1798
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/ipa/vrp-from-cst-agg-1.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 "irange" "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..6612a88f029
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-1.c
> @@ -0,0 +1,32 @@
> +/* { 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]] void foo (int index)
> +{
> +  const int *p = vars[index];
> +  if (!p)
> +    link_error ();
> +  else
> +    gi = *p;
> +}
> +
> +[[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..d7a5e617897
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-3.c
> @@ -0,0 +1,30 @@
> +/* { dg-do link } */
> +/* { dg-options "-O2" } */
> +
> +volatile int gi;
> +
> +static const int values[3] = {5, 7, 11};
> +
> +void link_error (void);
> +
> +[[gnu::noinline]] void foo (int index)
> +{
> +  const int v = values[index];
> +  if (v <= 2
> +      || v > 11)
> +    link_error ();
> +  else
> +    gi = v;
> +}
> +
> +[[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..333a24bf8c9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-4.c
> @@ -0,0 +1,30 @@
> +/* { dg-do link } */
> +/* { dg-options "-O2" } */
> +
> +volatile int gi;
> +
> +static const int values[25] = {5, 7, 11};
> +
> +void link_error (void);
> +
> +[[gnu::noinline]] void foo (int index)
> +{
> +  const int v = values[index];
> +  if (v <= -2
> +      || v > 11)
> +    link_error ();
> +  else
> +    gi = v;
> +}
> +
> +[[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-5.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-5.c
> new file mode 100644
> index 00000000000..08e21172d92
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-5.c
> @@ -0,0 +1,36 @@
> +/* { dg-do link } */
> +/* { dg-options "-O2" } */
> +
> +volatile int gi;
> +
> +static const struct {
> +    int a;
> +    int b;
> +} values[2][2] = {
> +  { {1000, 1 }, {1001, 2} },
> +  { {1003, 1 }, {1004, 2} }
> +};
> +
> +void link_error (void);
> +
> +[[gnu::noinline]] void foo (int i, int j)
> +{
> +  const int v = values[i][j].b;
> +  if (v <= 0
> +      || v > 2)
> +    link_error ();
> +  else
> +    gi = v;
> +}
> +
> +[[gnu::noipa]] int
> +get_index (void)
> +{
> +  return 1;
> +}
> +
> +int main (int argc, char **argv)
> +{
> +  foo (get_index (), get_index ());
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-6.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-6.c
> new file mode 100644
> index 00000000000..6f93f09138e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-6.c
> @@ -0,0 +1,42 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +volatile int gi;
> +
> +static const struct {
> +    int a;
> +    int b;
> +} values[2][2] = {
> +  { {0, 1 }, {0, 2} },
> +  { {0, 1 }, {0, 2} }
> +};
> +
> +[[gnu::noipa]] static void
> +check (int v)
> +{
> +  if (!v)
> +    __builtin_abort ();
> +  gi = v;
> +}
> +
> +
> +[[gnu::noinline]] void foo (int i, int j)
> +{
> +  const int v = values[i][j].a;
> +  if (v <= 0
> +      || v > 2)
> +    return;
> +  check (v);
> +}
> +
> +[[gnu::noipa]] int
> +get_index (void)
> +{
> +  return 1;
> +}
> +
> +int main (int argc, char **argv)
> +{
> +  foo (get_index (), get_index ());
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-7.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-7.c
> new file mode 100644
> index 00000000000..da01abbfcb8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-from-cst-agg-7.c
> @@ -0,0 +1,36 @@
> +/* { dg-do link } */
> +/* { dg-options "-O2" } */
> +
> +volatile int gi;
> +
> +static const struct {
> +    int a;
> +    int b;
> +} values[2][2] = {
> +  { {1000, 1 }, {1001, 2} },
> +  { {1003, 1 }, {1004, 2} }
> +};
> +
> +void link_error (void);
> +
> +[[gnu::noinline]] void foo (int i)
> +{
> +  const int v = values[0][i].b;
> +  if (v <= 0
> +      || v > 2)
> +    link_error ();
> +  else
> +    gi = v;
> +}
> +
> +[[gnu::noipa]] int
> +get_index (void)
> +{
> +  return 1;
> +}
> +
> +int main (int argc, char **argv)
> +{
> +  foo (get_index ());
> +  return 0;
> +}
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to