Implemented project structure
This commit is contained in:
@@ -0,0 +1,936 @@
|
||||
/* 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 */
|
||||
Reference in New Issue
Block a user