On Fri, Jul 10, 2026 at 6:13 PM Robin Dapp <[email protected]> wrote:
>
> From: Robin Dapp <[email protected]>
>
> This patch adds a path for scev to continue when encountering wrapping
> behavior instead of giving up. If scev_probably_wraps_p is passed a
> nonzero nowrap_bound tree it will use the type bound of the current step
> as upper bound for wrapping and store it.
>
> In order for the nowrap_bound parameter to reach scev_probably_wraps_p,
> the patch adds parameters to many of the internal scev functions. Most
> of them just forward the parameter.
> Initially, I attempted a global variable that was supposed to store the
> bound. None of my tries resulted in comprehensible code or correct
> results, however, and I found myself running into corner case
> after corner case. Thus, I arrived at the mostly mechanical "brute-force"
> solution.
>
> The patch also introduces a new helper scev_add_nowrap_bound that
> combines two bounds by building a MIN of each so that we'd get a
> condition like IV <= MIN (dr1.bound, dr2.bound, ...).
>
> Cache invalidation is handled by an additional bit (assumption or no
> assumption) for the respective entry.
>
> affine_iv gets a new field nowrap_bound. This allows passing the nowrap
> bounds to dataref analysis.
Thanks for working on this.
I wonder how to deal with the situation
where CHREC_RIGHT changes? Whenever we call chrec_fold_plus
with CHRECs of the same loop we get { a + b, step_a + step_b }_loop
which will invalidate nowrap bounds?
Likewise if you start with
{ init, +, 1 }_0 and a bound of N, adding 5 requires to adjust N?
So even changing CHREC_LEFT is problematic.
Should nowrap_bound be part of a CHREC itself? I see no particular
way to perform the nowrap_bound adjustment in chrec_fold_plus,
but I have to admit I haven't thought about this for too long. At least
it would give us the chance to drop to chrec_dont_know.
But maybe I'm missing something?
Thanks,
Richard.
> gcc/ChangeLog:
>
> * tree-chrec.cc (convert_affine_scev): Add nowrap_bound param.
> (chrec_convert_rhs): Ditto.
> (chrec_convert_1): Ditto.
> (chrec_convert): Ditto.
> * tree-chrec.h (chrec_convert): Ditto.
> (chrec_convert_rhs): Ditto.
> (convert_affine_scev): Ditto.
> * tree-scalar-evolution.cc (analyze_scalar_evolution_1): Ditto.
> (analyze_scalar_evolution_for_address_of): Ditto.
> (struct scev_info_str): Add nowrap_assumed.
> (new_scev_info_str): Ditto.
> (scev_info_hasher::hash): Ditto.
> (scev_info_hasher::equal): Ditto.
> (find_var_scev_info): Ditto.
> (scev_add_nowrap_bound): New function to accumulate bounds.
> (set_scalar_evolution): Add nowrap_bound param.
> (get_scalar_evolution): Ditto.
> (simplify_peeled_chrec): Ditto.
> (analyze_evolution_in_loop): Ditto.
> (interpret_loop_phi): Ditto.
> (interpret_condition_phi): Ditto.
> (interpret_rhs_expr): Ditto.
> (interpret_expr): Ditto.
> (interpret_gimple_assign): Ditto.
> (analyze_scalar_evolution): Ditto.
> (analyze_scalar_evolution_in_loop): Ditto.
> (simple_iv_with_niters): Add allow_wrapping.
> (simple_iv): Ditto.
> * tree-scalar-evolution.h (analyze_scalar_evolution): Add new
> param.
> (simple_iv_with_niters): Ditto.
> (scev_add_nowrap_bound): Declare.
> * tree-ssa-loop-niter.cc (scev_probably_wraps_p): Build no-wrap
> bound.
> * tree-ssa-loop-niter.h (scev_probably_wraps_p): Add param.
> * tree-ssa-loop.h (struct affine_iv): Add nowrap_bound.
> ---
> gcc/tree-chrec.cc | 51 ++++--
> gcc/tree-chrec.h | 7 +-
> gcc/tree-scalar-evolution.cc | 314 +++++++++++++++++++++++------------
> gcc/tree-scalar-evolution.h | 9 +-
> gcc/tree-ssa-loop-niter.cc | 16 +-
> gcc/tree-ssa-loop-niter.h | 2 +-
> gcc/tree-ssa-loop.h | 4 +
> 7 files changed, 270 insertions(+), 133 deletions(-)
>
> diff --git a/gcc/tree-chrec.cc b/gcc/tree-chrec.cc
> index 9869389a7b9..2b1659eab76 100644
> --- a/gcc/tree-chrec.cc
> +++ b/gcc/tree-chrec.cc
> @@ -1421,13 +1421,18 @@ nb_vars_in_chrec (tree chrec)
> that the rules for overflow of the given language apply (e.g., that signed
> arithmetics in C does not overflow) -- i.e., to use them to avoid
> unnecessary tests, but also to enforce that the result follows them.
> - FROM is the source variable converted if it's not NULL. Returns true if
> - the conversion succeeded, false otherwise. */
> + FROM is the source variable converted if it's not NULL.
> + If NOWRAP_BOUND is nonzero, pass it on to scev_probably_wraps_p etc.
> + to have them determine the bound below which no wrapping occurs.
> + Most other functions just forward NOWRAP_BOUND, therefore it is not
> + documented everywhere.
> + The function returns true if the conversion succeeded, false otherwise.
> */
>
> bool
> convert_affine_scev (class loop *loop, tree type,
> tree *base, tree *step, gimple *at_stmt,
> - bool use_overflow_semantics, tree from)
> + bool use_overflow_semantics, tree from,
> + tree *nowrap_bound)
> {
> tree ct = TREE_TYPE (*step);
> bool enforce_overflow_semantics;
> @@ -1452,6 +1457,11 @@ convert_affine_scev (class loop *loop, tree type,
> happen. */
> must_check_src_overflow = TYPE_PRECISION (ct) < TYPE_PRECISION (type);
>
> + /* MUST_CHECK_SRC_OVERFLOW implies chrec_convert_aggressive later
> + which can prove "no wrap" without assumptions. */
> + if (!must_check_src_overflow)
> + nowrap_bound = NULL;
> +
> enforce_overflow_semantics = (use_overflow_semantics
> && nowrap_type_p (type));
> if (enforce_overflow_semantics)
> @@ -1487,10 +1497,11 @@ convert_affine_scev (class loop *loop, tree type,
>
> if (must_check_src_overflow
> && scev_probably_wraps_p (from, *base, *step, at_stmt, loop,
> - use_overflow_semantics))
> + use_overflow_semantics, nowrap_bound))
> return false;
>
> - new_base = chrec_convert (type, *base, at_stmt, use_overflow_semantics);
> + new_base = chrec_convert (type, *base, at_stmt, nowrap_bound,
> + use_overflow_semantics);
> /* The step must be sign extended, regardless of the signedness
> of CT and TYPE. This only needs to be handled specially when
> CT is unsigned -- to avoid e.g. unsigned char [100, +, 255]
> @@ -1501,10 +1512,10 @@ convert_affine_scev (class loop *loop, tree type,
> if (TYPE_PRECISION (step_type) > TYPE_PRECISION (ct) && TYPE_UNSIGNED (ct))
> {
> tree signed_ct = signed_type_for (ct);
> - new_step = chrec_convert (signed_ct, new_step, at_stmt,
> + new_step = chrec_convert (signed_ct, new_step, at_stmt, nowrap_bound,
> use_overflow_semantics);
> }
> - new_step = chrec_convert (step_type, new_step, at_stmt,
> + new_step = chrec_convert (step_type, new_step, at_stmt, nowrap_bound,
> use_overflow_semantics);
>
> if (automatically_generated_chrec_p (new_base)
> @@ -1515,7 +1526,7 @@ convert_affine_scev (class loop *loop, tree type,
> /* Note that in this case we cannot use the fact that signed variables
> do not overflow, as this is what we are verifying for the new iv. */
> && scev_probably_wraps_p (NULL_TREE, new_base, new_step,
> - at_stmt, loop, false))
> + at_stmt, loop, false, nowrap_bound))
> return false;
>
> *base = new_base;
> @@ -1528,12 +1539,12 @@ convert_affine_scev (class loop *loop, tree type,
> The increment for a pointer type is always sizetype. */
>
> tree
> -chrec_convert_rhs (tree type, tree chrec, gimple *at_stmt)
> +chrec_convert_rhs (tree type, tree chrec, gimple *at_stmt, tree
> *nowrap_bound)
> {
> if (POINTER_TYPE_P (type))
> type = sizetype;
>
> - return chrec_convert (type, chrec, at_stmt);
> + return chrec_convert (type, chrec, at_stmt, nowrap_bound);
> }
>
> /* Convert CHREC to TYPE. When the analyzer knows the context in
> @@ -1552,7 +1563,7 @@ chrec_convert_rhs (tree type, tree chrec, gimple
> *at_stmt)
>
> static tree
> chrec_convert_1 (tree type, tree chrec, gimple *at_stmt,
> - bool use_overflow_semantics, tree from)
> + tree *nowrap_bound, bool use_overflow_semantics, tree from)
> {
> tree ct, res;
> tree base, step;
> @@ -1573,7 +1584,7 @@ chrec_convert_1 (tree type, tree chrec, gimple *at_stmt,
> step = CHREC_RIGHT (chrec);
>
> if (convert_affine_scev (loop, type, &base, &step, at_stmt,
> - use_overflow_semantics, from))
> + use_overflow_semantics, from, nowrap_bound))
> return build_polynomial_chrec (loop->num, base, step);
>
> /* If we cannot propagate the cast inside the chrec, just keep the cast.
> */
> @@ -1606,7 +1617,8 @@ keep_cast:
> CHREC_LEFT (chrec)),
> fold_convert (utype,
> CHREC_RIGHT (chrec)));
> - res = chrec_convert_1 (type, res, at_stmt, use_overflow_semantics,
> from);
> + res = chrec_convert_1 (type, res, at_stmt, nowrap_bound,
> + use_overflow_semantics, from);
> }
> /* Similar perform the trick that (unsigned T)(base + step) can be
> folded to ((unsigned T)x + (unsigned T)step). */
> @@ -1631,7 +1643,8 @@ keep_cast:
> CHREC_LEFT (chrec)),
> fold_convert (type,
> CHREC_RIGHT (chrec)));
> - res = chrec_convert_1 (type, res, at_stmt, use_overflow_semantics,
> from);
> + res = chrec_convert_1 (type, res, at_stmt, nowrap_bound,
> + use_overflow_semantics, from);
> }
> else
> res = fold_convert (type, chrec);
> @@ -1677,6 +1690,11 @@ keep_cast:
>
> {(uint) 0, +, (uint) 260}
>
> + If NOWRAP_BOUND is nonzero it is used to store the bound below which
> + no wrapping occurs. Here as well as in many other similar functions,
> + it is just passed through to other functions and thus not documented
> + everywhere.
> +
> USE_OVERFLOW_SEMANTICS is true if this function should assume that
> the rules for overflow of the given language apply (e.g., that signed
> arithmetics in C does not overflow) -- i.e., to use them to avoid
> @@ -1686,9 +1704,10 @@ keep_cast:
>
> tree
> chrec_convert (tree type, tree chrec, gimple *at_stmt,
> - bool use_overflow_semantics, tree from)
> + tree *nowrap_bound, bool use_overflow_semantics, tree from)
> {
> - return chrec_convert_1 (type, chrec, at_stmt, use_overflow_semantics,
> from);
> + return chrec_convert_1 (type, chrec, at_stmt, nowrap_bound,
> + use_overflow_semantics, from);
> }
>
> /* Convert CHREC to TYPE, without regard to signed overflows. Returns the
> new
> diff --git a/gcc/tree-chrec.h b/gcc/tree-chrec.h
> index 7777ff50942..9fdfaac0e15 100644
> --- a/gcc/tree-chrec.h
> +++ b/gcc/tree-chrec.h
> @@ -62,8 +62,9 @@ enum ev_direction scev_direction (const_tree);
> extern tree chrec_fold_plus (tree, tree, tree);
> extern tree chrec_fold_minus (tree, tree, tree);
> extern tree chrec_fold_multiply (tree, tree, tree);
> -extern tree chrec_convert (tree, tree, gimple *, bool = true, tree = NULL);
> -extern tree chrec_convert_rhs (tree, tree, gimple * = NULL);
> +extern tree chrec_convert (tree, tree, gimple *, tree * = nullptr,
> + bool = true, tree = NULL);
> +extern tree chrec_convert_rhs (tree, tree, gimple * = NULL, tree * =
> nullptr);
> extern tree chrec_convert_aggressive (tree, tree, bool *);
>
> /* Operations. */
> @@ -78,7 +79,7 @@ extern tree reset_evolution_in_loop (unsigned, tree, tree);
> extern tree chrec_merge (tree, tree);
> extern void for_each_scev_op (tree *, bool (*) (tree *, void *), void *);
> extern bool convert_affine_scev (class loop *, tree, tree *, tree *, gimple
> *,
> - bool, tree = NULL);
> + bool, tree = NULL, tree * = nullptr);
>
> /* Observers. */
> extern bool eq_evolutions_p (const_tree, const_tree);
> diff --git a/gcc/tree-scalar-evolution.cc b/gcc/tree-scalar-evolution.cc
> index b9acdbe9135..0d1500fec65 100644
> --- a/gcc/tree-scalar-evolution.cc
> +++ b/gcc/tree-scalar-evolution.cc
> @@ -286,18 +286,24 @@ along with GCC; see the file COPYING3. If not see
> #include "case-cfn-macros.h"
> #include "tree-eh.h"
>
> -static tree analyze_scalar_evolution_1 (class loop *, tree);
> +static tree analyze_scalar_evolution_1 (class loop *, tree, tree * =
> nullptr);
> static tree analyze_scalar_evolution_for_address_of (class loop *loop,
> - tree var);
> + tree var,
> + tree * = nullptr);
>
> /* The cached information about an SSA name with version NAME_VERSION,
> claiming that below basic block with index INSTANTIATED_BELOW, the
> - value of the SSA name can be expressed as CHREC. */
> + value of the SSA name can be expressed as CHREC.
> + NOWRAP_ASSUMPTION_P indicates whether or not the SSA name requires
> + an assumption to guarantee that it doesn't wrap. The assumption
> + itself is stored in NOWRAP_BOUND if necessary. */
>
> struct GTY((for_user)) scev_info_str {
> unsigned int name_version;
> int instantiated_below;
> + bool nowrap_assumption_p;
> tree chrec;
> + tree nowrap_bound;
> };
>
> /* Counters for the scev database. */
> @@ -316,14 +322,17 @@ static GTY (()) hash_table<scev_info_hasher>
> *scalar_evolution_info;
> /* Constructs a new SCEV_INFO_STR structure for VAR and INSTANTIATED_BELOW.
> */
>
> static inline struct scev_info_str *
> -new_scev_info_str (basic_block instantiated_below, tree var)
> +new_scev_info_str (basic_block instantiated_below, tree var,
> + bool nowrap_assumption_p)
> {
> struct scev_info_str *res;
>
> res = ggc_alloc<scev_info_str> ();
> res->name_version = SSA_NAME_VERSION (var);
> - res->chrec = chrec_not_analyzed_yet;
> res->instantiated_below = instantiated_below->index;
> + res->nowrap_assumption_p = nowrap_assumption_p;
> + res->chrec = chrec_not_analyzed_yet;
> + res->nowrap_bound = NULL_TREE;
>
> return res;
> }
> @@ -333,7 +342,8 @@ new_scev_info_str (basic_block instantiated_below, tree
> var)
> hashval_t
> scev_info_hasher::hash (scev_info_str *elt)
> {
> - return elt->name_version ^ elt->instantiated_below;
> + return (elt->name_version ^ elt->instantiated_below)
> + + elt->nowrap_assumption_p;
> }
>
> /* Compares database elements E1 and E2. */
> @@ -342,27 +352,29 @@ bool
> scev_info_hasher::equal (const scev_info_str *elt1, const scev_info_str
> *elt2)
> {
> return (elt1->name_version == elt2->name_version
> - && elt1->instantiated_below == elt2->instantiated_below);
> + && elt1->instantiated_below == elt2->instantiated_below
> + && elt1->nowrap_assumption_p == elt2->nowrap_assumption_p);
> }
>
> /* Get the scalar evolution of VAR for INSTANTIATED_BELOW basic block.
> A first query on VAR returns chrec_not_analyzed_yet. */
>
> -static tree *
> -find_var_scev_info (basic_block instantiated_below, tree var)
> +static scev_info_str *
> +find_var_scev_info (basic_block instantiated_below, tree var,
> + bool nowrap_assumption_p)
> {
> - struct scev_info_str *res;
> struct scev_info_str tmp;
>
> tmp.name_version = SSA_NAME_VERSION (var);
> tmp.instantiated_below = instantiated_below->index;
> + tmp.nowrap_assumption_p = nowrap_assumption_p;
> scev_info_str **slot = scalar_evolution_info->find_slot (&tmp, INSERT);
>
> if (!*slot)
> - *slot = new_scev_info_str (instantiated_below, var);
> - res = *slot;
> + *slot = new_scev_info_str (instantiated_below, var,
> + tmp.nowrap_assumption_p);
>
> - return &res->chrec;
> + return *slot;
> }
>
>
> @@ -396,6 +408,27 @@ instantiate_cache_type::~instantiate_cache_type ()
> or resolve_mixers call. */
> static instantiate_cache_type *global_cache;
>
> +/* Build a combined bound from BASE and BOUND by taking the minimum
> + of both. The result is stored in BASE. */
> +
> +void
> +scev_add_nowrap_bound (tree *base, tree bound)
> +{
> + if (!base || !bound)
> + return;
> + if (!*base)
> + {
> + *base = bound;
> + return;
> + }
> +
> + tree t1 = TREE_TYPE (*base);
> + tree t2 = TREE_TYPE (bound);
> + tree wide_type = TYPE_PRECISION (t1) >= TYPE_PRECISION (t2) ? t1 : t2;
> + *base = fold_build2 (MIN_EXPR, wide_type,
> + fold_convert (wide_type, *base),
> + fold_convert (wide_type, bound));
> +}
>
> /* Return true when PHI is a loop-phi-node. */
>
> @@ -493,14 +526,16 @@ compute_overall_effect_of_inner_loop (class loop *loop,
> tree evolution_fn)
> /* Associate CHREC to SCALAR. */
>
> static void
> -set_scalar_evolution (basic_block instantiated_below, tree scalar, tree
> chrec)
> +set_scalar_evolution (basic_block instantiated_below, tree scalar, tree
> chrec,
> + tree *nowrap_bound)
> {
> - tree *scalar_info;
> + scev_info_str *scalar_info;
>
> if (TREE_CODE (scalar) != SSA_NAME)
> return;
>
> - scalar_info = find_var_scev_info (instantiated_below, scalar);
> + scalar_info = find_var_scev_info (instantiated_below, scalar,
> + nowrap_bound != nullptr);
>
> if (dump_file)
> {
> @@ -519,14 +554,17 @@ set_scalar_evolution (basic_block instantiated_below,
> tree scalar, tree chrec)
> nb_set_scev++;
> }
>
> - *scalar_info = chrec;
> + scalar_info->chrec = chrec;
> + if (scalar_info->nowrap_assumption_p && nowrap_bound)
> + scalar_info->nowrap_bound = *nowrap_bound;
> }
>
> /* Retrieve the chrec associated to SCALAR instantiated below
> INSTANTIATED_BELOW block. */
>
> static tree
> -get_scalar_evolution (basic_block instantiated_below, tree scalar)
> +get_scalar_evolution (basic_block instantiated_below, tree scalar,
> + tree *nowrap_bound)
> {
> tree res;
>
> @@ -551,10 +589,19 @@ get_scalar_evolution (basic_block instantiated_below,
> tree scalar)
> switch (TREE_CODE (scalar))
> {
> case SSA_NAME:
> - if (SSA_NAME_IS_DEFAULT_DEF (scalar))
> + if (SSA_NAME_IS_DEFAULT_DEF (scalar))
> res = scalar;
> else
> - res = *find_var_scev_info (instantiated_below, scalar);
> + {
> + scev_info_str *info
> + = find_var_scev_info (instantiated_below, scalar,
> + nowrap_bound != nullptr);
> + res = info->chrec;
> + if (info->nowrap_assumption_p
> + && res != chrec_not_analyzed_yet
> + && info->nowrap_bound)
> + scev_add_nowrap_bound (nowrap_bound, info->nowrap_bound);
> + }
> break;
>
> case REAL_CST:
> @@ -1361,12 +1408,18 @@ get_loop_exit_condition (const_edge exit_edge)
> See PR41488. */
>
> static tree
> -simplify_peeled_chrec (class loop *loop, tree arg, tree init_cond)
> +simplify_peeled_chrec (class loop *loop, tree arg, tree init_cond,
> + tree *nowrap_bound = nullptr)
> {
> aff_tree aff1, aff2;
> tree ev, left, right, type, step_val;
> hash_map<tree, name_expansion *> *peeled_chrec_map = NULL;
>
> + tree local_nowrap_bound = NULL_TREE;
> + tree *bound = nullptr;
> + if (nowrap_bound)
> + bound = &local_nowrap_bound;
> +
> ev = instantiate_parameters (loop, analyze_scalar_evolution (loop, arg));
> if (ev == NULL_TREE)
> return chrec_dont_know;
> @@ -1390,7 +1443,7 @@ simplify_peeled_chrec (class loop *loop, tree arg, tree
> init_cond)
> if (TREE_CODE (left_before) == INTEGER_CST
> && wi::to_widest (init_cond) == wi::to_widest (left_before)
> && !scev_probably_wraps_p (NULL_TREE, left_before, right, NULL,
> - loop, false))
> + loop, false, bound))
> {
> tree tp = TREE_TYPE (right);
>
> @@ -1399,10 +1452,11 @@ simplify_peeled_chrec (class loop *loop, tree arg,
> tree init_cond)
> if (TYPE_UNSIGNED (tp))
> right = fold_convert (signed_type_for (tp), right);
>
> - return build_polynomial_chrec (loop->num, init_cond,
> - chrec_convert (TREE_TYPE (ev),
> - right, NULL,
> - false, NULL_TREE));
> + scev_add_nowrap_bound (nowrap_bound, local_nowrap_bound);
> + return build_polynomial_chrec
> + (loop->num, init_cond, chrec_convert (TREE_TYPE (ev), right, NULL,
> + nullptr, false,
> + NULL_TREE));
> }
> return chrec_dont_know;
> }
> @@ -1422,6 +1476,7 @@ simplify_peeled_chrec (class loop *loop, tree arg, tree
> init_cond)
> if (dump_file && (dump_flags & TDF_SCEV))
> fprintf (dump_file, "Simplify PEELED_CHREC into POLYNOMIAL_CHREC.\n");
>
> + scev_add_nowrap_bound (nowrap_bound, local_nowrap_bound);
> return build_polynomial_chrec (loop->num, init_cond, right);
> }
>
> @@ -1444,6 +1499,7 @@ simplify_peeled_chrec (class loop *loop, tree arg, tree
> init_cond)
> if (dump_file && (dump_flags & TDF_SCEV))
> fprintf (dump_file, "Simplify PEELED_CHREC into POLYNOMIAL_CHREC.\n");
>
> + scev_add_nowrap_bound (nowrap_bound, local_nowrap_bound);
> return build_polynomial_chrec (loop->num, init_cond, right);
> }
> return chrec_dont_know;
> @@ -1454,7 +1510,8 @@ simplify_peeled_chrec (class loop *loop, tree arg, tree
> init_cond)
>
> static tree
> analyze_evolution_in_loop (gphi *loop_phi_node,
> - tree init_cond)
> + tree init_cond,
> + tree *nowrap_bound = nullptr)
> {
> int i, n = gimple_phi_num_args (loop_phi_node);
> tree evolution_function = chrec_not_analyzed_yet;
> @@ -1517,7 +1574,8 @@ analyze_evolution_in_loop (gphi *loop_phi_node,
> if (simplify_peeled_chrec_p && TREE_CODE (arg) == SSA_NAME)
> {
> simplify_peeled_chrec_p = false;
> - ev_fn = simplify_peeled_chrec (loop, arg, init_cond);
> + ev_fn = simplify_peeled_chrec
> + (loop, arg, init_cond, nowrap_bound);
> simplify_peeled_chrec_p = true;
> }
> }
> @@ -1640,7 +1698,8 @@ analyze_initial_condition (gphi *loop_phi_node)
> /* Analyze the scalar evolution for LOOP_PHI_NODE. */
>
> static tree
> -interpret_loop_phi (class loop *loop, gphi *loop_phi_node)
> +interpret_loop_phi (class loop *loop, gphi *loop_phi_node,
> + tree *nowrap_bound = nullptr)
> {
> class loop *phi_loop = loop_containing_stmt (loop_phi_node);
> tree init_cond;
> @@ -1649,7 +1708,7 @@ interpret_loop_phi (class loop *loop, gphi
> *loop_phi_node)
>
> /* Otherwise really interpret the loop phi. */
> init_cond = analyze_initial_condition (loop_phi_node);
> - return analyze_evolution_in_loop (loop_phi_node, init_cond);
> + return analyze_evolution_in_loop (loop_phi_node, init_cond, nowrap_bound);
> }
>
> /* This function merges the branches of a condition-phi-node,
> @@ -1657,7 +1716,8 @@ interpret_loop_phi (class loop *loop, gphi
> *loop_phi_node)
> analyzed. */
>
> static tree
> -interpret_condition_phi (class loop *loop, gphi *condition_phi)
> +interpret_condition_phi (class loop *loop, gphi *condition_phi,
> + tree *nowrap_bound)
> {
> int i, n = gimple_phi_num_args (condition_phi);
> tree res = chrec_not_analyzed_yet;
> @@ -1673,7 +1733,7 @@ interpret_condition_phi (class loop *loop, gphi
> *condition_phi)
> }
>
> branch_chrec = analyze_scalar_evolution
> - (loop, PHI_ARG_DEF (condition_phi, i));
> + (loop, PHI_ARG_DEF (condition_phi, i), nowrap_bound);
>
> res = chrec_merge (res, branch_chrec);
> if (res == chrec_dont_know)
> @@ -1692,7 +1752,8 @@ interpret_condition_phi (class loop *loop, gphi
> *condition_phi)
>
> static tree
> interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> - tree type, tree rhs1, enum tree_code code, tree rhs2)
> + tree type, tree rhs1, enum tree_code code, tree rhs2,
> + tree *nowrap_bound = nullptr)
> {
> tree res, chrec1, chrec2, ctype;
> gimple *def;
> @@ -1703,8 +1764,10 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> return chrec_convert (type, rhs1, at_stmt);
>
> if (code == SSA_NAME)
> - return chrec_convert (type, analyze_scalar_evolution (loop, rhs1),
> - at_stmt);
> + return chrec_convert (type,
> + analyze_scalar_evolution (loop, rhs1,
> + nowrap_bound),
> + at_stmt, nowrap_bound);
> }
>
> switch (code)
> @@ -1730,25 +1793,28 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> rhs2 = TREE_OPERAND (base, 1);
> rhs1 = TREE_OPERAND (base, 0);
>
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> - chrec1 = chrec_convert (type, chrec1, at_stmt);
> - chrec2 = chrec_convert (TREE_TYPE (rhs2), chrec2, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> + chrec1 = chrec_convert (type, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (TREE_TYPE (rhs2), chrec2, at_stmt,
> + nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_plus (type, chrec1, chrec2);
> }
> else
> {
> - chrec1 = analyze_scalar_evolution_for_address_of (loop, base);
> - chrec1 = chrec_convert (type, chrec1, at_stmt);
> + chrec1 = analyze_scalar_evolution_for_address_of (loop, base,
> + nowrap_bound);
> + chrec1 = chrec_convert (type, chrec1, at_stmt, nowrap_bound);
> res = chrec1;
> }
>
> if (offset != NULL_TREE)
> {
> - chrec2 = analyze_scalar_evolution (loop, offset);
> - chrec2 = chrec_convert (TREE_TYPE (offset), chrec2, at_stmt);
> + chrec2 = analyze_scalar_evolution (loop, offset, nowrap_bound);
> + chrec2 = chrec_convert (TREE_TYPE (offset), chrec2, at_stmt,
> + nowrap_bound);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_plus (type, res, chrec2);
> }
> @@ -1756,8 +1822,9 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> if (maybe_ne (bitpos, 0))
> {
> unitpos = size_int (exact_div (bitpos, BITS_PER_UNIT));
> - chrec3 = analyze_scalar_evolution (loop, unitpos);
> - chrec3 = chrec_convert (TREE_TYPE (unitpos), chrec3, at_stmt);
> + chrec3 = analyze_scalar_evolution (loop, unitpos, nowrap_bound);
> + chrec3 = chrec_convert (TREE_TYPE (unitpos), chrec3, at_stmt,
> + nowrap_bound);
> chrec3 = instantiate_parameters (loop, chrec3);
> res = chrec_fold_plus (type, res, chrec3);
> }
> @@ -1767,10 +1834,11 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> break;
>
> case POINTER_PLUS_EXPR:
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> - chrec1 = chrec_convert (type, chrec1, at_stmt);
> - chrec2 = chrec_convert (TREE_TYPE (rhs2), chrec2, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> + chrec1 = chrec_convert (type, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (TREE_TYPE (rhs2), chrec2, at_stmt,
> + nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_plus (type, chrec1, chrec2);
> @@ -1779,20 +1847,20 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> case POINTER_DIFF_EXPR:
> {
> tree utype = unsigned_type_for (type);
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> - chrec1 = chrec_convert (utype, chrec1, at_stmt);
> - chrec2 = chrec_convert (utype, chrec2, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> + chrec1 = chrec_convert (utype, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (utype, chrec2, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_minus (utype, chrec1, chrec2);
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> break;
> }
>
> case PLUS_EXPR:
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> ctype = type;
> /* When the stmt is conditionally executed re-write the CHREC
> into a form that has well-defined behavior on overflow. */
> @@ -1802,18 +1870,18 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> && ! dominated_by_p (CDI_DOMINATORS, loop->latch,
> gimple_bb (at_stmt)))
> ctype = unsigned_type_for (type);
> - chrec1 = chrec_convert (ctype, chrec1, at_stmt);
> - chrec2 = chrec_convert (ctype, chrec2, at_stmt);
> + chrec1 = chrec_convert (ctype, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (ctype, chrec2, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_plus (ctype, chrec1, chrec2);
> if (type != ctype)
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> break;
>
> case MINUS_EXPR:
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> ctype = type;
> /* When the stmt is conditionally executed re-write the CHREC
> into a form that has well-defined behavior on overflow. */
> @@ -1823,17 +1891,17 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> && ! dominated_by_p (CDI_DOMINATORS,
> loop->latch, gimple_bb (at_stmt)))
> ctype = unsigned_type_for (type);
> - chrec1 = chrec_convert (ctype, chrec1, at_stmt);
> - chrec2 = chrec_convert (ctype, chrec2, at_stmt);
> + chrec1 = chrec_convert (ctype, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (ctype, chrec2, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_minus (ctype, chrec1, chrec2);
> if (type != ctype)
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> break;
>
> case NEGATE_EXPR:
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> ctype = type;
> /* When the stmt is conditionally executed re-write the CHREC
> into a form that has well-defined behavior on overflow. */
> @@ -1843,19 +1911,19 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> && ! dominated_by_p (CDI_DOMINATORS,
> loop->latch, gimple_bb (at_stmt)))
> ctype = unsigned_type_for (type);
> - chrec1 = chrec_convert (ctype, chrec1, at_stmt);
> + chrec1 = chrec_convert (ctype, chrec1, at_stmt, nowrap_bound);
> /* TYPE may be integer, real or complex, so use fold_convert. */
> chrec1 = instantiate_parameters (loop, chrec1);
> res = chrec_fold_multiply (ctype, chrec1,
> fold_convert (ctype,
> integer_minus_one_node));
> if (type != ctype)
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> break;
>
> case BIT_NOT_EXPR:
> /* Handle ~X as -1 - X. */
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec1 = chrec_convert (type, chrec1, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec1 = chrec_convert (type, chrec1, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> res = chrec_fold_minus (type,
> fold_convert (type, integer_minus_one_node),
> @@ -1863,8 +1931,8 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> break;
>
> case MULT_EXPR:
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> ctype = type;
> /* When the stmt is conditionally executed re-write the CHREC
> into a form that has well-defined behavior on overflow. */
> @@ -1874,29 +1942,29 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> && ! dominated_by_p (CDI_DOMINATORS,
> loop->latch, gimple_bb (at_stmt)))
> ctype = unsigned_type_for (type);
> - chrec1 = chrec_convert (ctype, chrec1, at_stmt);
> - chrec2 = chrec_convert (ctype, chrec2, at_stmt);
> + chrec1 = chrec_convert (ctype, chrec1, at_stmt, nowrap_bound);
> + chrec2 = chrec_convert (ctype, chrec2, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
> res = chrec_fold_multiply (ctype, chrec1, chrec2);
> if (type != ctype)
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> break;
>
> case LSHIFT_EXPR:
> {
> /* Handle A<<B as A * (1<<B). */
> tree uns = unsigned_type_for (type);
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec2 = analyze_scalar_evolution (loop, rhs2);
> - chrec1 = chrec_convert (uns, chrec1, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + chrec2 = analyze_scalar_evolution (loop, rhs2, nowrap_bound);
> + chrec1 = chrec_convert (uns, chrec1, at_stmt, nowrap_bound);
> chrec1 = instantiate_parameters (loop, chrec1);
> chrec2 = instantiate_parameters (loop, chrec2);
>
> tree one = build_int_cst (uns, 1);
> chrec2 = fold_build2 (LSHIFT_EXPR, uns, one, chrec2);
> res = chrec_fold_multiply (uns, chrec1, chrec2);
> - res = chrec_convert (type, res, at_stmt);
> + res = chrec_convert (type, res, at_stmt, nowrap_bound);
> }
> break;
>
> @@ -1920,11 +1988,12 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> chrec1 = interpret_rhs_expr (loop, at_stmt, utype,
> gimple_assign_rhs1 (def),
> gimple_assign_rhs_code (def),
> - gimple_assign_rhs2 (def));
> + gimple_assign_rhs2 (def),
> + nowrap_bound);
> }
> else
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - res = chrec_convert (type, chrec1, at_stmt, true, rhs1);
> + chrec1 = analyze_scalar_evolution (loop, rhs1, nowrap_bound);
> + res = chrec_convert (type, chrec1, at_stmt, nowrap_bound, true, rhs1);
> break;
>
> case BIT_AND_EXPR:
> @@ -1949,9 +2018,11 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
>
> if (TYPE_PRECISION (utype) < TYPE_PRECISION (TREE_TYPE (rhs1)))
> {
> - chrec1 = analyze_scalar_evolution (loop, rhs1);
> - chrec1 = chrec_convert (utype, chrec1, at_stmt);
> - res = chrec_convert (TREE_TYPE (rhs1), chrec1, at_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1,
> nowrap_bound);
> + chrec1 = chrec_convert (utype, chrec1, at_stmt,
> + nowrap_bound);
> + res = chrec_convert (TREE_TYPE (rhs1), chrec1, at_stmt,
> + nowrap_bound);
> }
> }
> }
> @@ -1968,7 +2039,8 @@ interpret_rhs_expr (class loop *loop, gimple *at_stmt,
> /* Interpret the expression EXPR. */
>
> static tree
> -interpret_expr (class loop *loop, gimple *at_stmt, tree expr)
> +interpret_expr (class loop *loop, gimple *at_stmt, tree expr,
> + tree *nowrap_bound)
> {
> enum tree_code code;
> tree type = TREE_TYPE (expr), op0, op1;
> @@ -1984,20 +2056,20 @@ interpret_expr (class loop *loop, gimple *at_stmt,
> tree expr)
> extract_ops_from_tree (expr, &code, &op0, &op1);
>
> return interpret_rhs_expr (loop, at_stmt, type,
> - op0, code, op1);
> + op0, code, op1, nowrap_bound);
> }
>
> /* Interpret the rhs of the assignment STMT. */
>
> static tree
> -interpret_gimple_assign (class loop *loop, gimple *stmt)
> +interpret_gimple_assign (class loop *loop, gimple *stmt, tree *nowrap_bound)
> {
> tree type = TREE_TYPE (gimple_assign_lhs (stmt));
> enum tree_code code = gimple_assign_rhs_code (stmt);
>
> return interpret_rhs_expr (loop, stmt, type,
> gimple_assign_rhs1 (stmt), code,
> - gimple_assign_rhs2 (stmt));
> + gimple_assign_rhs2 (stmt), nowrap_bound);
> }
>
>
> @@ -2011,7 +2083,7 @@ interpret_gimple_assign (class loop *loop, gimple *stmt)
> /* Helper recursive function. */
>
> static tree
> -analyze_scalar_evolution_1 (class loop *loop, tree var)
> +analyze_scalar_evolution_1 (class loop *loop, tree var, tree *nowrap_bound)
> {
> gimple *def;
> basic_block bb;
> @@ -2019,7 +2091,12 @@ analyze_scalar_evolution_1 (class loop *loop, tree var)
> tree res;
>
> if (TREE_CODE (var) != SSA_NAME)
> - return interpret_expr (loop, NULL, var);
> + return interpret_expr (loop, NULL, var, nowrap_bound);
> +
> + tree local_nowrap_bound = NULL_TREE;
> + tree *bound = nullptr;
> + if (nowrap_bound)
> + bound = &local_nowrap_bound;
>
> def = SSA_NAME_DEF_STMT (var);
> bb = gimple_bb (def);
> @@ -2034,26 +2111,26 @@ analyze_scalar_evolution_1 (class loop *loop, tree
> var)
>
> if (loop != def_loop)
> {
> - res = analyze_scalar_evolution_1 (def_loop, var);
> + res = analyze_scalar_evolution_1 (def_loop, var, bound);
> class loop *loop_to_skip = superloop_at_depth (def_loop,
> loop_depth (loop) + 1);
> res = compute_overall_effect_of_inner_loop (loop_to_skip, res);
> if (chrec_contains_symbols_defined_in_loop (res, loop->num))
> - res = analyze_scalar_evolution_1 (loop, res);
> + res = analyze_scalar_evolution_1 (loop, res, bound);
> goto set_and_end;
> }
>
> switch (gimple_code (def))
> {
> case GIMPLE_ASSIGN:
> - res = interpret_gimple_assign (loop, def);
> + res = interpret_gimple_assign (loop, def, bound);
> break;
>
> case GIMPLE_PHI:
> if (loop_phi_node_p (def))
> - res = interpret_loop_phi (loop, as_a <gphi *> (def));
> + res = interpret_loop_phi (loop, as_a <gphi *> (def), bound);
> else
> - res = interpret_condition_phi (loop, as_a <gphi *> (def));
> + res = interpret_condition_phi (loop, as_a <gphi *> (def), bound);
> break;
>
> default:
> @@ -2068,7 +2145,9 @@ analyze_scalar_evolution_1 (class loop *loop, tree var)
> res = var;
>
> if (loop == def_loop)
> - set_scalar_evolution (block_before_loop (loop), var, res);
> + set_scalar_evolution (block_before_loop (loop), var, res, bound);
> +
> + scev_add_nowrap_bound (nowrap_bound, local_nowrap_bound);
>
> return res;
> }
> @@ -2087,7 +2166,7 @@ analyze_scalar_evolution_1 (class loop *loop, tree var)
> */
>
> tree
> -analyze_scalar_evolution (class loop *loop, tree var)
> +analyze_scalar_evolution (class loop *loop, tree var, tree *nowrap_bound)
> {
> tree res;
>
> @@ -2104,7 +2183,7 @@ analyze_scalar_evolution (class loop *loop, tree var)
> fprintf (dump_file, ")\n");
> }
>
> - res = get_scalar_evolution (block_before_loop (loop), var);
> + res = get_scalar_evolution (block_before_loop (loop), var, nowrap_bound);
> if (res == chrec_not_analyzed_yet)
> {
> /* We'll recurse into instantiate_scev, avoid tearing down the
> @@ -2115,7 +2194,7 @@ analyze_scalar_evolution (class loop *loop, tree var)
> global_cache = new instantiate_cache_type;
> destr = true;
> }
> - res = analyze_scalar_evolution_1 (loop, var);
> + res = analyze_scalar_evolution_1 (loop, var, nowrap_bound);
> if (destr)
> {
> delete global_cache;
> @@ -2156,9 +2235,11 @@ bool nonwrapping_chrec_p (tree chrec)
> /* Analyzes and returns the scalar evolution of VAR address in LOOP. */
>
> static tree
> -analyze_scalar_evolution_for_address_of (class loop *loop, tree var)
> +analyze_scalar_evolution_for_address_of (class loop *loop, tree var,
> + tree *nowrap_bound)
> {
> - return analyze_scalar_evolution (loop, build_fold_addr_expr (var));
> + return analyze_scalar_evolution (loop, build_fold_addr_expr (var),
> + nowrap_bound);
> }
>
> /* Analyze scalar evolution of use of VERSION in USE_LOOP with respect to
> @@ -2213,7 +2294,8 @@ analyze_scalar_evolution_for_address_of (class loop
> *loop, tree var)
>
> static tree
> analyze_scalar_evolution_in_loop (class loop *wrto_loop, class loop
> *use_loop,
> - tree version, bool *folded_casts)
> + tree version, bool *folded_casts,
> + tree *nowrap_bound = nullptr)
> {
> bool val = false;
> tree ev = version, tmp;
> @@ -2241,7 +2323,7 @@ analyze_scalar_evolution_in_loop (class loop
> *wrto_loop, class loop *use_loop,
> *folded_casts = false;
> while (1)
> {
> - tmp = analyze_scalar_evolution (use_loop, ev);
> + tmp = analyze_scalar_evolution (use_loop, ev, nowrap_bound);
> ev = resolve_mixers (use_loop, tmp, folded_casts);
>
> if (use_loop == wrto_loop)
> @@ -3280,12 +3362,17 @@ derive_simple_iv_with_niters (tree ev, tree *niters)
> The derived condition normally is the maximum number the inner iv can
> iterate, and will be stored in IV_NITERS. This is useful in loop niter
> analysis, to derive break conditions when a loop must terminate, when is
> - infinite. */
> + infinite.
> +
> + If ALLOW_WRAPPING is true, let scev analysis continue rather than stop if
> + an IV might wrap. Store the bound under which no wrapping occurs in IV's
> + nowrap_bound. */
>
> bool
> simple_iv_with_niters (class loop *wrto_loop, class loop *use_loop,
> tree op, affine_iv *iv, tree *iv_niters,
> - bool allow_nonconstant_step)
> + bool allow_nonconstant_step,
> + bool allow_wrapping)
> {
> enum tree_code code;
> tree type, ev, base, e;
> @@ -3295,14 +3382,23 @@ simple_iv_with_niters (class loop *wrto_loop, class
> loop *use_loop,
> iv->base = NULL_TREE;
> iv->step = NULL_TREE;
> iv->no_overflow = false;
> + iv->nowrap_bound = NULL_TREE;
>
> type = TREE_TYPE (op);
> if (!POINTER_TYPE_P (type)
> && !INTEGRAL_TYPE_P (type))
> return false;
>
> + tree nowrap_bound = NULL_TREE;
> ev = analyze_scalar_evolution_in_loop (wrto_loop, use_loop, op,
> - &folded_casts);
> + &folded_casts,
> + allow_wrapping ? &nowrap_bound
> + : nullptr);
> +
> + /* If there was a nontrivial nowrap bound, add it to the IV. */
> + if (allow_wrapping && nowrap_bound)
> + iv->nowrap_bound = nowrap_bound;
> +
> if (chrec_contains_undetermined (ev)
> || chrec_contains_symbols_defined_in_loop (ev, wrto_loop->num))
> return false;
> @@ -3429,10 +3525,12 @@ simple_iv_with_niters (class loop *wrto_loop, class
> loop *use_loop,
>
> bool
> simple_iv (class loop *wrto_loop, class loop *use_loop, tree op,
> - affine_iv *iv, bool allow_nonconstant_step)
> + affine_iv *iv, bool allow_nonconstant_step,
> + bool allow_wrapping)
> {
> return simple_iv_with_niters (wrto_loop, use_loop, op, iv,
> - NULL, allow_nonconstant_step);
> + NULL, allow_nonconstant_step,
> + allow_wrapping);
> }
>
> /* Finalize the scalar evolution analysis. */
> diff --git a/gcc/tree-scalar-evolution.h b/gcc/tree-scalar-evolution.h
> index 0c15956de3f..4f94cf7baa0 100644
> --- a/gcc/tree-scalar-evolution.h
> +++ b/gcc/tree-scalar-evolution.h
> @@ -30,7 +30,7 @@ extern bool scev_initialized_p (void);
> extern void scev_reset (void);
> extern void scev_reset_htab (void);
> extern void scev_finalize (void);
> -extern tree analyze_scalar_evolution (class loop *, tree);
> +extern tree analyze_scalar_evolution (class loop *, tree, tree * = nullptr);
> extern tree instantiate_scev (edge, class loop *, tree);
> extern tree resolve_mixers (class loop *, tree, bool *);
> extern void gather_stats_on_scev_database (void);
> @@ -38,14 +38,17 @@ extern bool final_value_replacement_loop (class loop *);
> extern unsigned int scev_const_prop (void);
> extern bool expression_expensive_p (tree, bool *);
> extern bool simple_iv_with_niters (class loop *, class loop *, tree,
> - struct affine_iv *, tree *, bool);
> + struct affine_iv *, tree *, bool,
> + bool = false);
> extern bool simple_iv (class loop *, class loop *, tree, struct affine_iv *,
> - bool);
> + bool, bool = false);
> extern bool iv_can_overflow_p (class loop *, tree, tree, tree);
> extern tree compute_overall_effect_of_inner_loop (class loop *, tree);
> extern void record_nonwrapping_chrec (tree);
> extern bool nonwrapping_chrec_p (tree);
>
> +extern void scev_add_nowrap_bound (tree *, tree);
> +
> /* Returns the basic block preceding LOOP, or the CFG entry block when
> the loop is function's body. */
>
> diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc
> index fe179b69b05..a4630c8a0a1 100644
> --- a/gcc/tree-ssa-loop-niter.cc
> +++ b/gcc/tree-ssa-loop-niter.cc
> @@ -5604,12 +5604,16 @@ scev_var_range_cant_overflow (tree var, tree step,
> class loop *loop)
> arithmetics in C does not overflow).
>
> If VAR is a ssa variable, this function also returns false if VAR can
> - be proven not overflow with value range info. */
> + be proven not overflow with value range info.
> +
> + If NOWRAP_BOUND is nonzero and we can't prove it differently, get
> + STEP's type bound, build a bound MIN (NOWRAP_BOUND, STEP_TYPE_BOUND)
> + and store it in NOWRAP_BOUND. */
>
> bool
> scev_probably_wraps_p (tree var, tree base, tree step,
> gimple *at_stmt, class loop *loop,
> - bool use_overflow_semantics)
> + bool use_overflow_semantics, tree *nowrap_bound)
> {
> /* FIXME: We really need something like
> http://gcc.gnu.org/ml/gcc-patches/2005-06/msg02025.html.
> @@ -5657,6 +5661,14 @@ scev_probably_wraps_p (tree var, tree base, tree step,
> if (var && nonwrapping_chrec_p (analyze_scalar_evolution (loop, var)))
> return false;
>
> + /* If we can use a bound to guarantee no wrapping, save it. */
> + if (nowrap_bound)
> + {
> + tree b = get_niter_type_bound (TREE_TYPE (step), base, step);
> + scev_add_nowrap_bound (nowrap_bound, b);
> + return false;
> + }
> +
> /* At this point we still don't have a proof that the iv does not
> overflow: give up. */
> return true;
> diff --git a/gcc/tree-ssa-loop-niter.h b/gcc/tree-ssa-loop-niter.h
> index 24e458f08db..c789ec524f9 100644
> --- a/gcc/tree-ssa-loop-niter.h
> +++ b/gcc/tree-ssa-loop-niter.h
> @@ -54,7 +54,7 @@ extern bool stmt_dominates_stmt_p (gimple *, gimple *);
> extern bool nowrap_type_p (tree);
> extern tree get_niter_type_bound (tree, tree, tree);
> extern bool scev_probably_wraps_p (tree, tree, tree, gimple *,
> - class loop *, bool);
> + class loop *, bool, tree * = nullptr);
> extern void free_numbers_of_iterations_estimates (class loop *);
> extern void free_numbers_of_iterations_estimates (function *);
> extern tree simplify_replace_tree (tree, tree,
> diff --git a/gcc/tree-ssa-loop.h b/gcc/tree-ssa-loop.h
> index 73796a2732b..de8f9c69dac 100644
> --- a/gcc/tree-ssa-loop.h
> +++ b/gcc/tree-ssa-loop.h
> @@ -30,6 +30,10 @@ struct affine_iv
>
> /* True if this iv does not overflow. */
> bool no_overflow;
> +
> + /* Upper bound for the number of loop iterations for which this IV
> + does not wrap. */
> + tree nowrap_bound;
> };
>
> /* Description of number of iterations of a loop. All the expressions inside
> --
> 2.54.0
>