937 lines
27 KiB
C++
937 lines
27 KiB
C++
/* Natural loop functions
|
|
Copyright (C) 1987-2023 Free Software Foundation, Inc.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef GCC_CFGLOOP_H
|
|
#define GCC_CFGLOOP_H
|
|
|
|
#include "cfgloopmanip.h"
|
|
|
|
/* Structure to hold decision about unrolling/peeling. */
|
|
enum lpt_dec
|
|
{
|
|
LPT_NONE,
|
|
LPT_UNROLL_CONSTANT,
|
|
LPT_UNROLL_RUNTIME,
|
|
LPT_UNROLL_STUPID
|
|
};
|
|
|
|
struct GTY (()) lpt_decision {
|
|
enum lpt_dec decision;
|
|
unsigned times;
|
|
};
|
|
|
|
/* The type of extend applied to an IV. */
|
|
enum iv_extend_code
|
|
{
|
|
IV_SIGN_EXTEND,
|
|
IV_ZERO_EXTEND,
|
|
IV_UNKNOWN_EXTEND
|
|
};
|
|
|
|
/* The structure describing a bound on number of iterations of a loop. */
|
|
|
|
class GTY ((chain_next ("%h.next"))) nb_iter_bound {
|
|
public:
|
|
/* The statement STMT is executed at most ... */
|
|
gimple *stmt;
|
|
|
|
/* ... BOUND + 1 times (BOUND must be an unsigned constant).
|
|
The + 1 is added for the following reasons:
|
|
|
|
a) 0 would otherwise be unused, while we would need to care more about
|
|
overflows (as MAX + 1 is sometimes produced as the estimate on number
|
|
of executions of STMT).
|
|
b) it is consistent with the result of number_of_iterations_exit. */
|
|
widest_int bound;
|
|
|
|
/* True if, after executing the statement BOUND + 1 times, we will
|
|
leave the loop; that is, all the statements after it are executed at most
|
|
BOUND times. */
|
|
bool is_exit;
|
|
|
|
/* The next bound in the list. */
|
|
class nb_iter_bound *next;
|
|
};
|
|
|
|
/* Description of the loop exit. */
|
|
|
|
struct GTY ((for_user)) loop_exit {
|
|
/* The exit edge. */
|
|
edge e;
|
|
|
|
/* Previous and next exit in the list of the exits of the loop. */
|
|
struct loop_exit *prev;
|
|
struct loop_exit *next;
|
|
|
|
/* Next element in the list of loops from that E exits. */
|
|
struct loop_exit *next_e;
|
|
};
|
|
|
|
struct loop_exit_hasher : ggc_ptr_hash<loop_exit>
|
|
{
|
|
typedef edge compare_type;
|
|
|
|
static hashval_t hash (loop_exit *);
|
|
static bool equal (loop_exit *, edge);
|
|
static void remove (loop_exit *);
|
|
};
|
|
|
|
typedef class loop *loop_p;
|
|
|
|
/* An integer estimation of the number of iterations. Estimate_state
|
|
describes what is the state of the estimation. */
|
|
enum loop_estimation
|
|
{
|
|
/* Estimate was not computed yet. */
|
|
EST_NOT_COMPUTED,
|
|
/* Estimate is ready. */
|
|
EST_AVAILABLE,
|
|
EST_LAST
|
|
};
|
|
|
|
/* The structure describing non-overflow control induction variable for
|
|
loop's exit edge. */
|
|
struct GTY ((chain_next ("%h.next"))) control_iv {
|
|
tree base;
|
|
tree step;
|
|
struct control_iv *next;
|
|
};
|
|
|
|
/* Structure to hold information for each natural loop. */
|
|
class GTY ((chain_next ("%h.next"))) loop {
|
|
public:
|
|
/* Index into loops array. Note indices will never be reused after loop
|
|
is destroyed. */
|
|
int num;
|
|
|
|
/* Number of loop insns. */
|
|
unsigned ninsns;
|
|
|
|
/* Basic block of loop header. */
|
|
basic_block header;
|
|
|
|
/* Basic block of loop latch. */
|
|
basic_block latch;
|
|
|
|
/* For loop unrolling/peeling decision. */
|
|
struct lpt_decision lpt_decision;
|
|
|
|
/* Average number of executed insns per iteration. */
|
|
unsigned av_ninsns;
|
|
|
|
/* Number of blocks contained within the loop. */
|
|
unsigned num_nodes;
|
|
|
|
/* Superloops of the loop, starting with the outermost loop. */
|
|
vec<loop_p, va_gc> *superloops;
|
|
|
|
/* The first inner (child) loop or NULL if innermost loop. */
|
|
class loop *inner;
|
|
|
|
/* Link to the next (sibling) loop. */
|
|
class loop *next;
|
|
|
|
/* Auxiliary info specific to a pass. */
|
|
void *GTY ((skip (""))) aux;
|
|
|
|
/* The number of times the latch of the loop is executed. This can be an
|
|
INTEGER_CST, or a symbolic expression representing the number of
|
|
iterations like "N - 1", or a COND_EXPR containing the runtime
|
|
conditions under which the number of iterations is non zero.
|
|
|
|
Don't access this field directly: number_of_latch_executions
|
|
computes and caches the computed information in this field. */
|
|
tree nb_iterations;
|
|
|
|
/* An integer guaranteed to be greater or equal to nb_iterations. Only
|
|
valid if any_upper_bound is true. */
|
|
widest_int nb_iterations_upper_bound;
|
|
|
|
widest_int nb_iterations_likely_upper_bound;
|
|
|
|
/* An integer giving an estimate on nb_iterations. Unlike
|
|
nb_iterations_upper_bound, there is no guarantee that it is at least
|
|
nb_iterations. */
|
|
widest_int nb_iterations_estimate;
|
|
|
|
/* If > 0, an integer, where the user asserted that for any
|
|
I in [ 0, nb_iterations ) and for any J in
|
|
[ I, min ( I + safelen, nb_iterations ) ), the Ith and Jth iterations
|
|
of the loop can be safely evaluated concurrently. */
|
|
int safelen;
|
|
|
|
/* Preferred vectorization factor for the loop if non-zero. */
|
|
int simdlen;
|
|
|
|
/* Constraints are generally set by consumers and affect certain
|
|
semantics of niter analyzer APIs. Currently the APIs affected are
|
|
number_of_iterations_exit* functions and their callers. One typical
|
|
use case of constraints is to vectorize possibly infinite loop:
|
|
|
|
1) Compute niter->assumptions by calling niter analyzer API and
|
|
record it as possible condition for loop versioning.
|
|
2) Clear buffered result of niter/scev analyzer.
|
|
3) Set constraint LOOP_C_FINITE assuming the loop is finite.
|
|
4) Analyze data references. Since data reference analysis depends
|
|
on niter/scev analyzer, the point is that niter/scev analysis
|
|
is done under circumstance of LOOP_C_FINITE constraint.
|
|
5) Version the loop with niter->assumptions computed in step 1).
|
|
6) Vectorize the versioned loop in which niter->assumptions is
|
|
checked to be true.
|
|
7) Update constraints in versioned loops so that niter analyzer
|
|
in following passes can use it.
|
|
|
|
Note consumers are usually the loop optimizers and it is consumers'
|
|
responsibility to set/clear constraints correctly. Failing to do
|
|
that might result in hard to track down bugs in niter/scev consumers. */
|
|
unsigned constraints;
|
|
|
|
/* An integer estimation of the number of iterations. Estimate_state
|
|
describes what is the state of the estimation. */
|
|
ENUM_BITFIELD(loop_estimation) estimate_state : 8;
|
|
|
|
unsigned any_upper_bound : 1;
|
|
unsigned any_estimate : 1;
|
|
unsigned any_likely_upper_bound : 1;
|
|
|
|
/* True if the loop can be parallel. */
|
|
unsigned can_be_parallel : 1;
|
|
|
|
/* True if -Waggressive-loop-optimizations warned about this loop
|
|
already. */
|
|
unsigned warned_aggressive_loop_optimizations : 1;
|
|
|
|
/* True if this loop should never be vectorized. */
|
|
unsigned dont_vectorize : 1;
|
|
|
|
/* True if we should try harder to vectorize this loop. */
|
|
unsigned force_vectorize : 1;
|
|
|
|
/* True if the loop is part of an oacc kernels region. */
|
|
unsigned in_oacc_kernels_region : 1;
|
|
|
|
/* True if the loop is known to be finite. This is a localized
|
|
flag_finite_loops or similar pragmas state. */
|
|
unsigned finite_p : 1;
|
|
|
|
/* The number of times to unroll the loop. 0 means no information given,
|
|
just do what we always do. A value of 1 means do not unroll the loop.
|
|
A value of USHRT_MAX means unroll with no specific unrolling factor.
|
|
Other values means unroll with the given unrolling factor. */
|
|
unsigned short unroll;
|
|
|
|
/* If this loop was inlined the main clique of the callee which does
|
|
not need remapping when copying the loop body. */
|
|
unsigned short owned_clique;
|
|
|
|
/* For SIMD loops, this is a unique identifier of the loop, referenced
|
|
by IFN_GOMP_SIMD_VF, IFN_GOMP_SIMD_LANE and IFN_GOMP_SIMD_LAST_LANE
|
|
builtins. */
|
|
tree simduid;
|
|
|
|
/* In loop optimization, it's common to generate loops from the original
|
|
loop. This field records the index of the original loop which can be
|
|
used to track the original loop from newly generated loops. This can
|
|
be done by calling function get_loop (cfun, orig_loop_num). Note the
|
|
original loop could be destroyed for various reasons thus no longer
|
|
exists, as a result, function call to get_loop returns NULL pointer.
|
|
In this case, this field should not be used and needs to be cleared
|
|
whenever possible. */
|
|
int orig_loop_num;
|
|
|
|
/* Upper bound on number of iterations of a loop. */
|
|
class nb_iter_bound *bounds;
|
|
|
|
/* Non-overflow control ivs of a loop. */
|
|
struct control_iv *control_ivs;
|
|
|
|
/* Head of the cyclic list of the exits of the loop. */
|
|
struct loop_exit *exits;
|
|
|
|
/* Number of iteration analysis data for RTL. */
|
|
class niter_desc *simple_loop_desc;
|
|
|
|
/* For sanity checking during loop fixup we record here the former
|
|
loop header for loops marked for removal. Note that this prevents
|
|
the basic-block from being collected but its index can still be
|
|
reused. */
|
|
basic_block former_header;
|
|
};
|
|
|
|
/* Set if the loop is known to be infinite. */
|
|
#define LOOP_C_INFINITE (1 << 0)
|
|
/* Set if the loop is known to be finite without any assumptions. */
|
|
#define LOOP_C_FINITE (1 << 1)
|
|
|
|
/* Set C to the LOOP constraint. */
|
|
inline void
|
|
loop_constraint_set (class loop *loop, unsigned c)
|
|
{
|
|
loop->constraints |= c;
|
|
}
|
|
|
|
/* Clear C from the LOOP constraint. */
|
|
inline void
|
|
loop_constraint_clear (class loop *loop, unsigned c)
|
|
{
|
|
loop->constraints &= ~c;
|
|
}
|
|
|
|
/* Check if C is set in the LOOP constraint. */
|
|
inline bool
|
|
loop_constraint_set_p (class loop *loop, unsigned c)
|
|
{
|
|
return (loop->constraints & c) == c;
|
|
}
|
|
|
|
/* Flags for state of loop structure. */
|
|
enum
|
|
{
|
|
LOOPS_HAVE_PREHEADERS = 1,
|
|
LOOPS_HAVE_SIMPLE_LATCHES = 2,
|
|
LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS = 4,
|
|
LOOPS_HAVE_RECORDED_EXITS = 8,
|
|
LOOPS_MAY_HAVE_MULTIPLE_LATCHES = 16,
|
|
LOOP_CLOSED_SSA = 32,
|
|
LOOPS_NEED_FIXUP = 64,
|
|
LOOPS_HAVE_FALLTHRU_PREHEADERS = 128
|
|
};
|
|
|
|
#define LOOPS_NORMAL (LOOPS_HAVE_PREHEADERS | LOOPS_HAVE_SIMPLE_LATCHES \
|
|
| LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)
|
|
#define AVOID_CFG_MODIFICATIONS (LOOPS_MAY_HAVE_MULTIPLE_LATCHES)
|
|
|
|
/* Structure to hold CFG information about natural loops within a function. */
|
|
struct GTY (()) loops {
|
|
/* State of loops. */
|
|
int state;
|
|
|
|
/* Array of the loops. */
|
|
vec<loop_p, va_gc> *larray;
|
|
|
|
/* Maps edges to the list of their descriptions as loop exits. Edges
|
|
whose sources or destinations have loop_father == NULL (which may
|
|
happen during the cfg manipulations) should not appear in EXITS. */
|
|
hash_table<loop_exit_hasher> *GTY(()) exits;
|
|
|
|
/* Pointer to root of loop hierarchy tree. */
|
|
class loop *tree_root;
|
|
};
|
|
|
|
/* Loop recognition. */
|
|
bool bb_loop_header_p (basic_block);
|
|
void init_loops_structure (struct function *, struct loops *, unsigned);
|
|
extern struct loops *flow_loops_find (struct loops *);
|
|
extern void disambiguate_loops_with_multiple_latches (void);
|
|
extern void flow_loops_free (struct loops *);
|
|
extern void flow_loops_dump (FILE *,
|
|
void (*)(const class loop *, FILE *, int), int);
|
|
extern void flow_loop_dump (const class loop *, FILE *,
|
|
void (*)(const class loop *, FILE *, int), int);
|
|
class loop *alloc_loop (void);
|
|
extern void flow_loop_free (class loop *);
|
|
int flow_loop_nodes_find (basic_block, class loop *);
|
|
unsigned fix_loop_structure (bitmap changed_bbs);
|
|
bool mark_irreducible_loops (void);
|
|
void release_recorded_exits (function *);
|
|
void record_loop_exits (void);
|
|
void rescan_loop_exit (edge, bool, bool);
|
|
void sort_sibling_loops (function *);
|
|
|
|
/* Loop data structure manipulation/querying. */
|
|
extern void flow_loop_tree_node_add (class loop *, class loop *,
|
|
class loop * = NULL);
|
|
extern void flow_loop_tree_node_remove (class loop *);
|
|
extern bool flow_loop_nested_p (const class loop *, const class loop *);
|
|
extern bool flow_bb_inside_loop_p (const class loop *, const_basic_block);
|
|
extern class loop * find_common_loop (class loop *, class loop *);
|
|
class loop *superloop_at_depth (class loop *, unsigned);
|
|
struct eni_weights;
|
|
extern int num_loop_insns (const class loop *);
|
|
extern int average_num_loop_insns (const class loop *);
|
|
extern unsigned get_loop_level (const class loop *);
|
|
extern bool loop_exit_edge_p (const class loop *, const_edge);
|
|
extern bool loop_exits_to_bb_p (class loop *, basic_block);
|
|
extern bool loop_exits_from_bb_p (class loop *, basic_block);
|
|
extern void mark_loop_exit_edges (void);
|
|
extern dump_user_location_t get_loop_location (class loop *loop);
|
|
|
|
/* Loops & cfg manipulation. */
|
|
extern basic_block *get_loop_body (const class loop *);
|
|
extern unsigned get_loop_body_with_size (const class loop *, basic_block *,
|
|
unsigned);
|
|
extern basic_block *get_loop_body_in_dom_order (const class loop *);
|
|
extern basic_block *get_loop_body_in_bfs_order (const class loop *);
|
|
extern basic_block *get_loop_body_in_custom_order (const class loop *,
|
|
int (*) (const void *, const void *));
|
|
extern basic_block *get_loop_body_in_custom_order (const class loop *, void *,
|
|
int (*) (const void *, const void *, void *));
|
|
|
|
extern auto_vec<edge> get_loop_exit_edges (const class loop *, basic_block * = NULL);
|
|
extern edge single_exit (const class loop *);
|
|
extern edge single_likely_exit (class loop *loop, const vec<edge> &);
|
|
extern unsigned num_loop_branches (const class loop *);
|
|
|
|
extern edge loop_preheader_edge (const class loop *);
|
|
extern edge loop_latch_edge (const class loop *);
|
|
|
|
extern void add_bb_to_loop (basic_block, class loop *);
|
|
extern void remove_bb_from_loops (basic_block);
|
|
|
|
extern void cancel_loop_tree (class loop *);
|
|
extern void delete_loop (class loop *);
|
|
|
|
|
|
extern void verify_loop_structure (void);
|
|
|
|
/* Loop analysis. */
|
|
extern bool just_once_each_iteration_p (const class loop *, const_basic_block);
|
|
gcov_type expected_loop_iterations_unbounded (const class loop *,
|
|
bool *read_profile_p = NULL, bool by_profile_only = false);
|
|
extern unsigned expected_loop_iterations (class loop *);
|
|
extern rtx doloop_condition_get (rtx_insn *);
|
|
|
|
void mark_loop_for_removal (loop_p);
|
|
|
|
/* Induction variable analysis. */
|
|
|
|
/* The description of induction variable. The things are a bit complicated
|
|
due to need to handle subregs and extends. The value of the object described
|
|
by it can be obtained as follows (all computations are done in extend_mode):
|
|
|
|
Value in i-th iteration is
|
|
delta + mult * extend_{extend_mode} (subreg_{mode} (base + i * step)).
|
|
|
|
If first_special is true, the value in the first iteration is
|
|
delta + mult * base
|
|
|
|
If extend = UNKNOWN, first_special must be false, delta 0, mult 1 and value is
|
|
subreg_{mode} (base + i * step)
|
|
|
|
The get_iv_value function can be used to obtain these expressions.
|
|
|
|
??? Add a third mode field that would specify the mode in that inner
|
|
computation is done, which would enable it to be different from the
|
|
outer one? */
|
|
|
|
class rtx_iv
|
|
{
|
|
public:
|
|
/* Its base and step (mode of base and step is supposed to be extend_mode,
|
|
see the description above). */
|
|
rtx base, step;
|
|
|
|
/* The type of extend applied to it (IV_SIGN_EXTEND, IV_ZERO_EXTEND,
|
|
or IV_UNKNOWN_EXTEND). */
|
|
enum iv_extend_code extend;
|
|
|
|
/* Operations applied in the extended mode. */
|
|
rtx delta, mult;
|
|
|
|
/* The mode it is extended to. */
|
|
scalar_int_mode extend_mode;
|
|
|
|
/* The mode the variable iterates in. */
|
|
scalar_int_mode mode;
|
|
|
|
/* Whether the first iteration needs to be handled specially. */
|
|
unsigned first_special : 1;
|
|
};
|
|
|
|
/* The description of an exit from the loop and of the number of iterations
|
|
till we take the exit. */
|
|
|
|
class GTY(()) niter_desc
|
|
{
|
|
public:
|
|
/* The edge out of the loop. */
|
|
edge out_edge;
|
|
|
|
/* The other edge leading from the condition. */
|
|
edge in_edge;
|
|
|
|
/* True if we are able to say anything about number of iterations of the
|
|
loop. */
|
|
bool simple_p;
|
|
|
|
/* True if the loop iterates the constant number of times. */
|
|
bool const_iter;
|
|
|
|
/* Number of iterations if constant. */
|
|
uint64_t niter;
|
|
|
|
/* Assumptions under that the rest of the information is valid. */
|
|
rtx assumptions;
|
|
|
|
/* Assumptions under that the loop ends before reaching the latch,
|
|
even if value of niter_expr says otherwise. */
|
|
rtx noloop_assumptions;
|
|
|
|
/* Condition under that the loop is infinite. */
|
|
rtx infinite;
|
|
|
|
/* Whether the comparison is signed. */
|
|
bool signed_p;
|
|
|
|
/* The mode in that niter_expr should be computed. */
|
|
scalar_int_mode mode;
|
|
|
|
/* The number of iterations of the loop. */
|
|
rtx niter_expr;
|
|
};
|
|
|
|
extern void iv_analysis_loop_init (class loop *);
|
|
extern bool iv_analyze (rtx_insn *, scalar_int_mode, rtx, class rtx_iv *);
|
|
extern bool iv_analyze_result (rtx_insn *, rtx, class rtx_iv *);
|
|
extern bool iv_analyze_expr (rtx_insn *, scalar_int_mode, rtx,
|
|
class rtx_iv *);
|
|
extern rtx get_iv_value (class rtx_iv *, rtx);
|
|
extern bool biv_p (rtx_insn *, scalar_int_mode, rtx);
|
|
extern void iv_analysis_done (void);
|
|
|
|
extern class niter_desc *get_simple_loop_desc (class loop *loop);
|
|
extern void free_simple_loop_desc (class loop *loop);
|
|
|
|
inline class niter_desc *
|
|
simple_loop_desc (class loop *loop)
|
|
{
|
|
return loop->simple_loop_desc;
|
|
}
|
|
|
|
/* Accessors for the loop structures. */
|
|
|
|
/* Returns the loop with index NUM from FNs loop tree. */
|
|
|
|
inline class loop *
|
|
get_loop (struct function *fn, unsigned num)
|
|
{
|
|
return (*loops_for_fn (fn)->larray)[num];
|
|
}
|
|
|
|
/* Returns the number of superloops of LOOP. */
|
|
|
|
inline unsigned
|
|
loop_depth (const class loop *loop)
|
|
{
|
|
return vec_safe_length (loop->superloops);
|
|
}
|
|
|
|
/* Returns the immediate superloop of LOOP, or NULL if LOOP is the outermost
|
|
loop. */
|
|
|
|
inline class loop *
|
|
loop_outer (const class loop *loop)
|
|
{
|
|
unsigned n = vec_safe_length (loop->superloops);
|
|
|
|
if (n == 0)
|
|
return NULL;
|
|
|
|
return (*loop->superloops)[n - 1];
|
|
}
|
|
|
|
/* Returns true if LOOP has at least one exit edge. */
|
|
|
|
inline bool
|
|
loop_has_exit_edges (const class loop *loop)
|
|
{
|
|
return loop->exits->next->e != NULL;
|
|
}
|
|
|
|
/* Returns the list of loops in FN. */
|
|
|
|
inline vec<loop_p, va_gc> *
|
|
get_loops (struct function *fn)
|
|
{
|
|
struct loops *loops = loops_for_fn (fn);
|
|
if (!loops)
|
|
return NULL;
|
|
|
|
return loops->larray;
|
|
}
|
|
|
|
/* Returns the number of loops in FN (including the removed
|
|
ones and the fake loop that forms the root of the loop tree). */
|
|
|
|
inline unsigned
|
|
number_of_loops (struct function *fn)
|
|
{
|
|
struct loops *loops = loops_for_fn (fn);
|
|
if (!loops)
|
|
return 0;
|
|
|
|
return vec_safe_length (loops->larray);
|
|
}
|
|
|
|
/* Returns true if state of the loops satisfies all properties
|
|
described by FLAGS. */
|
|
|
|
inline bool
|
|
loops_state_satisfies_p (function *fn, unsigned flags)
|
|
{
|
|
return (loops_for_fn (fn)->state & flags) == flags;
|
|
}
|
|
|
|
inline bool
|
|
loops_state_satisfies_p (unsigned flags)
|
|
{
|
|
return loops_state_satisfies_p (cfun, flags);
|
|
}
|
|
|
|
/* Sets FLAGS to the loops state. */
|
|
|
|
inline void
|
|
loops_state_set (function *fn, unsigned flags)
|
|
{
|
|
loops_for_fn (fn)->state |= flags;
|
|
}
|
|
|
|
inline void
|
|
loops_state_set (unsigned flags)
|
|
{
|
|
loops_state_set (cfun, flags);
|
|
}
|
|
|
|
/* Clears FLAGS from the loops state. */
|
|
|
|
inline void
|
|
loops_state_clear (function *fn, unsigned flags)
|
|
{
|
|
loops_for_fn (fn)->state &= ~flags;
|
|
}
|
|
|
|
inline void
|
|
loops_state_clear (unsigned flags)
|
|
{
|
|
if (!current_loops)
|
|
return;
|
|
loops_state_clear (cfun, flags);
|
|
}
|
|
|
|
/* Check loop structure invariants, if internal consistency checks are
|
|
enabled. */
|
|
|
|
inline void
|
|
checking_verify_loop_structure (void)
|
|
{
|
|
/* VERIFY_LOOP_STRUCTURE essentially asserts that no loops need fixups.
|
|
|
|
The loop optimizers should never make changes to the CFG which
|
|
require loop fixups. But the low level CFG manipulation code may
|
|
set the flag conservatively.
|
|
|
|
Go ahead and clear the flag here. That avoids the assert inside
|
|
VERIFY_LOOP_STRUCTURE, and if there is an inconsistency in the loop
|
|
structures VERIFY_LOOP_STRUCTURE will detect it.
|
|
|
|
This also avoid the compile time cost of excessive fixups. */
|
|
loops_state_clear (LOOPS_NEED_FIXUP);
|
|
if (flag_checking)
|
|
verify_loop_structure ();
|
|
}
|
|
|
|
/* Loop iterators. */
|
|
|
|
/* Flags for loop iteration. */
|
|
|
|
enum li_flags
|
|
{
|
|
LI_INCLUDE_ROOT = 1, /* Include the fake root of the loop tree. */
|
|
LI_FROM_INNERMOST = 2, /* Iterate over the loops in the reverse order,
|
|
starting from innermost ones. */
|
|
LI_ONLY_INNERMOST = 4 /* Iterate only over innermost loops. */
|
|
};
|
|
|
|
/* Provide the functionality of std::as_const to support range-based for
|
|
to use const iterator. (We can't use std::as_const itself because it's
|
|
a C++17 feature.) */
|
|
template <typename T>
|
|
constexpr const T &
|
|
as_const (T &t)
|
|
{
|
|
return t;
|
|
}
|
|
|
|
/* A list for visiting loops, which contains the loop numbers instead of
|
|
the loop pointers. If the loop ROOT is offered (non-null), the visiting
|
|
will start from it, otherwise it would start from the tree_root of
|
|
loops_for_fn (FN) instead. The scope is restricted in function FN and
|
|
the visiting order is specified by FLAGS. */
|
|
|
|
class loops_list
|
|
{
|
|
public:
|
|
loops_list (function *fn, unsigned flags, class loop *root = nullptr);
|
|
|
|
template <typename T> class Iter
|
|
{
|
|
public:
|
|
Iter (const loops_list &l, unsigned idx) : list (l), curr_idx (idx)
|
|
{
|
|
fill_curr_loop ();
|
|
}
|
|
|
|
T operator* () const { return curr_loop; }
|
|
|
|
Iter &
|
|
operator++ ()
|
|
{
|
|
if (curr_idx < list.to_visit.length ())
|
|
{
|
|
/* Bump the index and fill a new one. */
|
|
curr_idx++;
|
|
fill_curr_loop ();
|
|
}
|
|
else
|
|
gcc_assert (!curr_loop);
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool
|
|
operator!= (const Iter &rhs) const
|
|
{
|
|
return this->curr_idx != rhs.curr_idx;
|
|
}
|
|
|
|
private:
|
|
/* Fill the current loop starting from the current index. */
|
|
void fill_curr_loop ();
|
|
|
|
/* Reference to the loop list to visit. */
|
|
const loops_list &list;
|
|
|
|
/* The current index in the list to visit. */
|
|
unsigned curr_idx;
|
|
|
|
/* The loop implied by the current index. */
|
|
class loop *curr_loop;
|
|
};
|
|
|
|
using iterator = Iter<class loop *>;
|
|
using const_iterator = Iter<const class loop *>;
|
|
|
|
iterator
|
|
begin ()
|
|
{
|
|
return iterator (*this, 0);
|
|
}
|
|
|
|
iterator
|
|
end ()
|
|
{
|
|
return iterator (*this, to_visit.length ());
|
|
}
|
|
|
|
const_iterator
|
|
begin () const
|
|
{
|
|
return const_iterator (*this, 0);
|
|
}
|
|
|
|
const_iterator
|
|
end () const
|
|
{
|
|
return const_iterator (*this, to_visit.length ());
|
|
}
|
|
|
|
private:
|
|
/* Walk loop tree starting from ROOT as the visiting order specified
|
|
by FLAGS. */
|
|
void walk_loop_tree (class loop *root, unsigned flags);
|
|
|
|
/* The function we are visiting. */
|
|
function *fn;
|
|
|
|
/* The list of loops to visit. */
|
|
auto_vec<int, 16> to_visit;
|
|
};
|
|
|
|
/* Starting from current index CURR_IDX (inclusive), find one index
|
|
which stands for one valid loop and fill the found loop as CURR_LOOP,
|
|
if we can't find one, set CURR_LOOP as null. */
|
|
|
|
template <typename T>
|
|
inline void
|
|
loops_list::Iter<T>::fill_curr_loop ()
|
|
{
|
|
int anum;
|
|
|
|
while (this->list.to_visit.iterate (this->curr_idx, &anum))
|
|
{
|
|
class loop *loop = get_loop (this->list.fn, anum);
|
|
if (loop)
|
|
{
|
|
curr_loop = loop;
|
|
return;
|
|
}
|
|
this->curr_idx++;
|
|
}
|
|
|
|
curr_loop = nullptr;
|
|
}
|
|
|
|
/* Set up the loops list to visit according to the specified
|
|
function scope FN and iterating order FLAGS. If ROOT is
|
|
not null, the visiting would start from it, otherwise it
|
|
will start from tree_root of loops_for_fn (FN). */
|
|
|
|
inline loops_list::loops_list (function *fn, unsigned flags, class loop *root)
|
|
{
|
|
struct loops *loops = loops_for_fn (fn);
|
|
gcc_assert (!root || loops);
|
|
|
|
/* Check mutually exclusive flags should not co-exist. */
|
|
unsigned checked_flags = LI_ONLY_INNERMOST | LI_FROM_INNERMOST;
|
|
gcc_assert ((flags & checked_flags) != checked_flags);
|
|
|
|
this->fn = fn;
|
|
if (!loops)
|
|
return;
|
|
|
|
class loop *tree_root = root ? root : loops->tree_root;
|
|
|
|
this->to_visit.reserve_exact (number_of_loops (fn));
|
|
|
|
/* When root is tree_root of loops_for_fn (fn) and the visiting
|
|
order is LI_ONLY_INNERMOST, we would like to use linear
|
|
search here since it has a more stable bound than the
|
|
walk_loop_tree. */
|
|
if (flags & LI_ONLY_INNERMOST && tree_root == loops->tree_root)
|
|
{
|
|
gcc_assert (tree_root->num == 0);
|
|
if (tree_root->inner == NULL)
|
|
{
|
|
if (flags & LI_INCLUDE_ROOT)
|
|
this->to_visit.quick_push (0);
|
|
|
|
return;
|
|
}
|
|
|
|
class loop *aloop;
|
|
unsigned int i;
|
|
for (i = 1; vec_safe_iterate (loops->larray, i, &aloop); i++)
|
|
if (aloop != NULL && aloop->inner == NULL)
|
|
this->to_visit.quick_push (aloop->num);
|
|
}
|
|
else
|
|
walk_loop_tree (tree_root, flags);
|
|
}
|
|
|
|
/* The properties of the target. */
|
|
struct target_cfgloop {
|
|
/* Number of available registers. */
|
|
unsigned x_target_avail_regs;
|
|
|
|
/* Number of available registers that are call-clobbered. */
|
|
unsigned x_target_clobbered_regs;
|
|
|
|
/* Number of registers reserved for temporary expressions. */
|
|
unsigned x_target_res_regs;
|
|
|
|
/* The cost for register when there still is some reserve, but we are
|
|
approaching the number of available registers. */
|
|
unsigned x_target_reg_cost[2];
|
|
|
|
/* The cost for register when we need to spill. */
|
|
unsigned x_target_spill_cost[2];
|
|
};
|
|
|
|
extern struct target_cfgloop default_target_cfgloop;
|
|
#if SWITCHABLE_TARGET
|
|
extern struct target_cfgloop *this_target_cfgloop;
|
|
#else
|
|
#define this_target_cfgloop (&default_target_cfgloop)
|
|
#endif
|
|
|
|
#define target_avail_regs \
|
|
(this_target_cfgloop->x_target_avail_regs)
|
|
#define target_clobbered_regs \
|
|
(this_target_cfgloop->x_target_clobbered_regs)
|
|
#define target_res_regs \
|
|
(this_target_cfgloop->x_target_res_regs)
|
|
#define target_reg_cost \
|
|
(this_target_cfgloop->x_target_reg_cost)
|
|
#define target_spill_cost \
|
|
(this_target_cfgloop->x_target_spill_cost)
|
|
|
|
/* Register pressure estimation for induction variable optimizations & loop
|
|
invariant motion. */
|
|
extern unsigned estimate_reg_pressure_cost (unsigned, unsigned, bool, bool);
|
|
extern void init_set_costs (void);
|
|
|
|
/* Loop optimizer initialization. */
|
|
extern void loop_optimizer_init (unsigned);
|
|
extern void loop_optimizer_finalize (function *, bool = false);
|
|
inline void
|
|
loop_optimizer_finalize ()
|
|
{
|
|
loop_optimizer_finalize (cfun);
|
|
}
|
|
|
|
/* Optimization passes. */
|
|
enum
|
|
{
|
|
UAP_UNROLL = 1, /* Enables unrolling of loops if it seems profitable. */
|
|
UAP_UNROLL_ALL = 2 /* Enables unrolling of all loops. */
|
|
};
|
|
|
|
extern void doloop_optimize_loops (void);
|
|
extern void move_loop_invariants (void);
|
|
extern auto_vec<basic_block> get_loop_hot_path (const class loop *loop);
|
|
|
|
/* Returns the outermost loop of the loop nest that contains LOOP.*/
|
|
inline class loop *
|
|
loop_outermost (class loop *loop)
|
|
{
|
|
unsigned n = vec_safe_length (loop->superloops);
|
|
|
|
if (n <= 1)
|
|
return loop;
|
|
|
|
return (*loop->superloops)[1];
|
|
}
|
|
|
|
extern void record_niter_bound (class loop *, const widest_int &, bool, bool);
|
|
extern HOST_WIDE_INT get_estimated_loop_iterations_int (class loop *);
|
|
extern HOST_WIDE_INT get_max_loop_iterations_int (const class loop *);
|
|
extern HOST_WIDE_INT get_likely_max_loop_iterations_int (class loop *);
|
|
extern bool get_estimated_loop_iterations (class loop *loop, widest_int *nit);
|
|
extern bool get_max_loop_iterations (const class loop *loop, widest_int *nit);
|
|
extern bool get_likely_max_loop_iterations (class loop *loop, widest_int *nit);
|
|
extern int bb_loop_depth (const_basic_block);
|
|
|
|
/* Converts VAL to widest_int. */
|
|
|
|
inline widest_int
|
|
gcov_type_to_wide_int (gcov_type val)
|
|
{
|
|
HOST_WIDE_INT a[2];
|
|
|
|
a[0] = (unsigned HOST_WIDE_INT) val;
|
|
/* If HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_WIDEST_INT, avoid shifting by
|
|
the size of type. */
|
|
val >>= HOST_BITS_PER_WIDE_INT - 1;
|
|
val >>= 1;
|
|
a[1] = (unsigned HOST_WIDE_INT) val;
|
|
|
|
return widest_int::from_array (a, 2);
|
|
}
|
|
#endif /* GCC_CFGLOOP_H */
|