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 > >
