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.

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;
+}
-- 
2.51.1

Reply via email to