2012-03-27 23:13:14 +00:00
|
|
|
|
/* Loop optimizer initialization routines and RTL loop optimization passes.
|
2022-10-27 18:55:19 +00:00
|
|
|
|
Copyright (C) 2002-2022 Free Software Foundation, Inc.
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
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/>. */
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "coretypes.h"
|
2017-04-10 11:32:00 +00:00
|
|
|
|
#include "backend.h"
|
|
|
|
|
#include "target.h"
|
2012-03-27 23:13:14 +00:00
|
|
|
|
#include "rtl.h"
|
2014-09-21 17:33:12 +00:00
|
|
|
|
#include "tree.h"
|
2017-04-10 11:32:00 +00:00
|
|
|
|
#include "cfghooks.h"
|
|
|
|
|
#include "df.h"
|
2014-09-21 17:33:12 +00:00
|
|
|
|
#include "regs.h"
|
2015-08-28 15:33:40 +00:00
|
|
|
|
#include "cfgcleanup.h"
|
2012-03-27 23:13:14 +00:00
|
|
|
|
#include "cfgloop.h"
|
|
|
|
|
#include "tree-pass.h"
|
2014-09-21 17:33:12 +00:00
|
|
|
|
#include "tree-ssa-loop-niter.h"
|
2015-08-28 15:33:40 +00:00
|
|
|
|
#include "loop-unroll.h"
|
2017-04-10 11:32:00 +00:00
|
|
|
|
#include "tree-scalar-evolution.h"
|
2022-10-27 18:55:19 +00:00
|
|
|
|
#include "tree-cfgcleanup.h"
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
/* Apply FLAGS to the loop state. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
static void
|
|
|
|
|
apply_loop_flags (unsigned flags)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
|
|
|
|
if (flags & LOOPS_MAY_HAVE_MULTIPLE_LATCHES)
|
|
|
|
|
{
|
|
|
|
|
/* If the loops may have multiple latches, we cannot canonicalize
|
|
|
|
|
them further (and most of the loop manipulation functions will
|
|
|
|
|
not work). However, we avoid modifying cfg, which some
|
|
|
|
|
passes may want. */
|
|
|
|
|
gcc_assert ((flags & ~(LOOPS_MAY_HAVE_MULTIPLE_LATCHES
|
2022-10-27 18:55:19 +00:00
|
|
|
|
| LOOPS_HAVE_RECORDED_EXITS
|
|
|
|
|
| LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)) == 0);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
loops_state_set (LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
disambiguate_loops_with_multiple_latches ();
|
|
|
|
|
|
|
|
|
|
/* Create pre-headers. */
|
|
|
|
|
if (flags & LOOPS_HAVE_PREHEADERS)
|
|
|
|
|
{
|
|
|
|
|
int cp_flags = CP_SIMPLE_PREHEADERS;
|
|
|
|
|
|
|
|
|
|
if (flags & LOOPS_HAVE_FALLTHRU_PREHEADERS)
|
|
|
|
|
cp_flags |= CP_FALLTHRU_PREHEADERS;
|
|
|
|
|
|
|
|
|
|
create_preheaders (cp_flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Force all latches to have only single successor. */
|
|
|
|
|
if (flags & LOOPS_HAVE_SIMPLE_LATCHES)
|
|
|
|
|
force_single_succ_latches ();
|
|
|
|
|
|
|
|
|
|
/* Mark irreducible loops. */
|
|
|
|
|
if (flags & LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS)
|
|
|
|
|
mark_irreducible_loops ();
|
|
|
|
|
|
|
|
|
|
if (flags & LOOPS_HAVE_RECORDED_EXITS)
|
|
|
|
|
record_loop_exits ();
|
2014-09-21 17:33:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize loop structures. This is used by the tree and RTL loop
|
|
|
|
|
optimizers. FLAGS specify what properties to compute and/or ensure for
|
|
|
|
|
loops. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
loop_optimizer_init (unsigned flags)
|
|
|
|
|
{
|
|
|
|
|
timevar_push (TV_LOOP_INIT);
|
|
|
|
|
|
|
|
|
|
if (!current_loops)
|
|
|
|
|
{
|
|
|
|
|
gcc_assert (!(cfun->curr_properties & PROP_loops));
|
|
|
|
|
|
|
|
|
|
/* Find the loops. */
|
|
|
|
|
current_loops = flow_loops_find (NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool recorded_exits = loops_state_satisfies_p (LOOPS_HAVE_RECORDED_EXITS);
|
|
|
|
|
bool needs_fixup = loops_state_satisfies_p (LOOPS_NEED_FIXUP);
|
|
|
|
|
|
|
|
|
|
gcc_assert (cfun->curr_properties & PROP_loops);
|
|
|
|
|
|
|
|
|
|
/* Ensure that the dominators are computed, like flow_loops_find does. */
|
|
|
|
|
calculate_dominance_info (CDI_DOMINATORS);
|
|
|
|
|
|
|
|
|
|
if (!needs_fixup)
|
2017-04-10 11:32:00 +00:00
|
|
|
|
checking_verify_loop_structure ();
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
/* Clear all flags. */
|
|
|
|
|
if (recorded_exits)
|
2017-04-10 11:32:00 +00:00
|
|
|
|
release_recorded_exits (cfun);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
loops_state_clear (~0U);
|
|
|
|
|
|
|
|
|
|
if (needs_fixup)
|
|
|
|
|
{
|
|
|
|
|
/* Apply LOOPS_MAY_HAVE_MULTIPLE_LATCHES early as fix_loop_structure
|
|
|
|
|
re-applies flags. */
|
|
|
|
|
loops_state_set (flags & LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
|
|
|
|
|
fix_loop_structure (NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Apply flags to loops. */
|
|
|
|
|
apply_loop_flags (flags);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
/* Dump loops. */
|
|
|
|
|
flow_loops_dump (dump_file, NULL, 1);
|
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
checking_verify_loop_structure ();
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
timevar_pop (TV_LOOP_INIT);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finalize loop structures. */
|
|
|
|
|
|
|
|
|
|
void
|
2022-10-27 18:55:19 +00:00
|
|
|
|
loop_optimizer_finalize (struct function *fn, bool clean_loop_closed_phi)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
|
|
|
|
basic_block bb;
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
timevar_push (TV_LOOP_FINI);
|
|
|
|
|
|
2022-10-27 18:55:19 +00:00
|
|
|
|
if (clean_loop_closed_phi && loops_state_satisfies_p (fn, LOOP_CLOSED_SSA))
|
|
|
|
|
{
|
|
|
|
|
clean_up_loop_closed_phi (fn);
|
|
|
|
|
loops_state_clear (fn, LOOP_CLOSED_SSA);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
if (loops_state_satisfies_p (fn, LOOPS_HAVE_RECORDED_EXITS))
|
|
|
|
|
release_recorded_exits (fn);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
free_numbers_of_iterations_estimates (fn);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
/* If we should preserve loop structure, do not free it but clear
|
|
|
|
|
flags that advanced properties are there as we are not preserving
|
|
|
|
|
that in full. */
|
2017-04-10 11:32:00 +00:00
|
|
|
|
if (fn->curr_properties & PROP_loops)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
2017-04-10 11:32:00 +00:00
|
|
|
|
loops_state_clear (fn, LOOP_CLOSED_SSA
|
2014-09-21 17:33:12 +00:00
|
|
|
|
| LOOPS_HAVE_MARKED_IRREDUCIBLE_REGIONS
|
|
|
|
|
| LOOPS_HAVE_PREHEADERS
|
|
|
|
|
| LOOPS_HAVE_SIMPLE_LATCHES
|
|
|
|
|
| LOOPS_HAVE_FALLTHRU_PREHEADERS);
|
2017-04-10 11:32:00 +00:00
|
|
|
|
loops_state_set (fn, LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
goto loop_fini_done;
|
2012-03-27 23:13:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 18:55:19 +00:00
|
|
|
|
for (auto loop : loops_list (fn, 0))
|
2014-09-21 17:33:12 +00:00
|
|
|
|
free_simple_loop_desc (loop);
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
/* Clean up. */
|
2017-04-10 11:32:00 +00:00
|
|
|
|
flow_loops_free (loops_for_fn (fn));
|
|
|
|
|
ggc_free (loops_for_fn (fn));
|
|
|
|
|
set_loops_for_fn (fn, NULL);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
FOR_ALL_BB_FN (bb, fn)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
|
|
|
|
bb->loop_father = NULL;
|
|
|
|
|
}
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
loop_fini_done:
|
|
|
|
|
timevar_pop (TV_LOOP_FINI);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
/* The structure of loops might have changed. Some loops might get removed
|
|
|
|
|
(and their headers and latches were set to NULL), loop exists might get
|
|
|
|
|
removed (thus the loop nesting may be wrong), and some blocks and edges
|
|
|
|
|
were changed (so the information about bb --> loop mapping does not have
|
|
|
|
|
to be correct). But still for the remaining loops the header dominates
|
|
|
|
|
the latch, and loops did not get new subloops (new loops might possibly
|
|
|
|
|
get created, but we are not interested in them). Fix up the mess.
|
|
|
|
|
|
|
|
|
|
If CHANGED_BBS is not NULL, basic blocks whose loop depth has changed are
|
|
|
|
|
marked in it.
|
|
|
|
|
|
|
|
|
|
Returns the number of new discovered loops. */
|
|
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
|
fix_loop_structure (bitmap changed_bbs)
|
|
|
|
|
{
|
|
|
|
|
basic_block bb;
|
|
|
|
|
int record_exits = 0;
|
|
|
|
|
unsigned old_nloops, i;
|
|
|
|
|
|
|
|
|
|
timevar_push (TV_LOOP_INIT);
|
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
|
|
|
fprintf (dump_file, "fix_loop_structure: fixing up loops for function\n");
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
/* We need exact and fast dominance info to be available. */
|
|
|
|
|
gcc_assert (dom_info_state (CDI_DOMINATORS) == DOM_OK);
|
|
|
|
|
|
|
|
|
|
if (loops_state_satisfies_p (LOOPS_HAVE_RECORDED_EXITS))
|
|
|
|
|
{
|
2017-04-10 11:32:00 +00:00
|
|
|
|
release_recorded_exits (cfun);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
record_exits = LOOPS_HAVE_RECORDED_EXITS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remember the depth of the blocks in the loop hierarchy, so that we can
|
|
|
|
|
recognize blocks whose loop nesting relationship has changed. */
|
|
|
|
|
if (changed_bbs)
|
|
|
|
|
FOR_EACH_BB_FN (bb, cfun)
|
|
|
|
|
bb->aux = (void *) (size_t) loop_depth (bb->loop_father);
|
|
|
|
|
|
|
|
|
|
/* Remove the dead loops from structures. We start from the innermost
|
|
|
|
|
loops, so that when we remove the loops, we know that the loops inside
|
|
|
|
|
are preserved, and do not waste time relinking loops that will be
|
|
|
|
|
removed later. */
|
2022-10-27 18:55:19 +00:00
|
|
|
|
for (auto loop : loops_list (cfun, LI_FROM_INNERMOST))
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{
|
|
|
|
|
/* Detect the case that the loop is no longer present even though
|
|
|
|
|
it wasn't marked for removal.
|
|
|
|
|
??? If we do that we can get away with not marking loops for
|
|
|
|
|
removal at all. And possibly avoid some spurious removals. */
|
|
|
|
|
if (loop->header
|
|
|
|
|
&& bb_loop_header_p (loop->header))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
|
|
|
fprintf (dump_file, "fix_loop_structure: removing loop %d\n",
|
|
|
|
|
loop->num);
|
|
|
|
|
|
|
|
|
|
while (loop->inner)
|
|
|
|
|
{
|
2022-10-27 18:55:19 +00:00
|
|
|
|
class loop *ploop = loop->inner;
|
2014-09-21 17:33:12 +00:00
|
|
|
|
flow_loop_tree_node_remove (ploop);
|
|
|
|
|
flow_loop_tree_node_add (loop_outer (loop), ploop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remove the loop. */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
if (loop->header)
|
|
|
|
|
loop->former_header = loop->header;
|
|
|
|
|
else
|
|
|
|
|
gcc_assert (loop->former_header != NULL);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
loop->header = NULL;
|
|
|
|
|
flow_loop_tree_node_remove (loop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remember the number of loops so we can return how many new loops
|
|
|
|
|
flow_loops_find discovered. */
|
|
|
|
|
old_nloops = number_of_loops (cfun);
|
|
|
|
|
|
|
|
|
|
/* Re-compute loop structure in-place. */
|
|
|
|
|
flow_loops_find (current_loops);
|
|
|
|
|
|
|
|
|
|
/* Mark the blocks whose loop has changed. */
|
|
|
|
|
if (changed_bbs)
|
|
|
|
|
{
|
|
|
|
|
FOR_EACH_BB_FN (bb, cfun)
|
|
|
|
|
{
|
|
|
|
|
if ((void *) (size_t) loop_depth (bb->loop_father) != bb->aux)
|
|
|
|
|
bitmap_set_bit (changed_bbs, bb->index);
|
|
|
|
|
|
|
|
|
|
bb->aux = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finally free deleted loops. */
|
2017-04-10 11:32:00 +00:00
|
|
|
|
bool any_deleted = false;
|
2022-10-27 18:55:19 +00:00
|
|
|
|
class loop *loop;
|
2014-09-21 17:33:12 +00:00
|
|
|
|
FOR_EACH_VEC_ELT (*get_loops (cfun), i, loop)
|
|
|
|
|
if (loop && loop->header == NULL)
|
|
|
|
|
{
|
2015-08-28 15:33:40 +00:00
|
|
|
|
if (dump_file
|
|
|
|
|
&& ((unsigned) loop->former_header->index
|
|
|
|
|
< basic_block_info_for_fn (cfun)->length ()))
|
|
|
|
|
{
|
|
|
|
|
basic_block former_header
|
|
|
|
|
= BASIC_BLOCK_FOR_FN (cfun, loop->former_header->index);
|
|
|
|
|
/* If the old header still exists we want to check if the
|
|
|
|
|
original loop is re-discovered or the old header is now
|
|
|
|
|
part of a newly discovered loop.
|
|
|
|
|
In both cases we should have avoided removing the loop. */
|
|
|
|
|
if (former_header == loop->former_header)
|
|
|
|
|
{
|
|
|
|
|
if (former_header->loop_father->header == former_header)
|
|
|
|
|
fprintf (dump_file, "fix_loop_structure: rediscovered "
|
|
|
|
|
"removed loop %d as loop %d with old header %d\n",
|
|
|
|
|
loop->num, former_header->loop_father->num,
|
|
|
|
|
former_header->index);
|
|
|
|
|
else if ((unsigned) former_header->loop_father->num
|
|
|
|
|
>= old_nloops)
|
|
|
|
|
fprintf (dump_file, "fix_loop_structure: header %d of "
|
|
|
|
|
"removed loop %d is part of the newly "
|
|
|
|
|
"discovered loop %d with header %d\n",
|
|
|
|
|
former_header->index, loop->num,
|
|
|
|
|
former_header->loop_father->num,
|
|
|
|
|
former_header->loop_father->header->index);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-21 17:33:12 +00:00
|
|
|
|
(*get_loops (cfun))[i] = NULL;
|
|
|
|
|
flow_loop_free (loop);
|
2017-04-10 11:32:00 +00:00
|
|
|
|
any_deleted = true;
|
2014-09-21 17:33:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
/* If we deleted loops then the cached scalar evolutions refering to
|
|
|
|
|
those loops become invalid. */
|
|
|
|
|
if (any_deleted && scev_initialized_p ())
|
|
|
|
|
scev_reset_htab ();
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
loops_state_clear (LOOPS_NEED_FIXUP);
|
|
|
|
|
|
|
|
|
|
/* Apply flags to loops. */
|
|
|
|
|
apply_loop_flags (current_loops->state | record_exits);
|
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
checking_verify_loop_structure ();
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
timevar_pop (TV_LOOP_INIT);
|
|
|
|
|
|
|
|
|
|
return number_of_loops (cfun) - old_nloops;
|
|
|
|
|
}
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2022-10-27 18:55:19 +00:00
|
|
|
|
/* The RTL loop superpass. The actual passes are subpasses. See passes.cc for
|
2015-08-28 15:33:40 +00:00
|
|
|
|
more on that. */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const pass_data pass_data_loop2 =
|
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
|
|
|
|
"loop2", /* name */
|
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
|
|
|
|
TV_LOOP, /* tv_id */
|
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
0, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
|
|
|
|
0, /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
class pass_loop2 : public rtl_opt_pass
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
pass_loop2 (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_loop2, ctxt)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual bool gate (function *);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
}; // class pass_loop2
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
bool
|
|
|
|
|
pass_loop2::gate (function *fun)
|
|
|
|
|
{
|
|
|
|
|
if (optimize > 0
|
|
|
|
|
&& (flag_move_loop_invariants
|
|
|
|
|
|| flag_unswitch_loops
|
|
|
|
|
|| flag_unroll_loops
|
2018-12-28 15:30:48 +00:00
|
|
|
|
|| (flag_branch_on_count_reg && targetm.have_doloop_end ())
|
|
|
|
|
|| cfun->has_unroll))
|
2015-08-28 15:33:40 +00:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* No longer preserve loops, remove them now. */
|
|
|
|
|
fun->curr_properties &= ~PROP_loops;
|
|
|
|
|
if (current_loops)
|
|
|
|
|
loop_optimizer_finalize ();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
|
|
|
|
make_pass_loop2 (gcc::context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
return new pass_loop2 (ctxt);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
/* Initialization of the RTL loop passes. */
|
|
|
|
|
static unsigned int
|
|
|
|
|
rtl_loop_init (void)
|
|
|
|
|
{
|
|
|
|
|
gcc_assert (current_ir_type () == IR_RTL_CFGLAYOUT);
|
|
|
|
|
|
|
|
|
|
if (dump_file)
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{
|
|
|
|
|
dump_reg_info (dump_file);
|
|
|
|
|
dump_flow_info (dump_file, dump_flags);
|
|
|
|
|
}
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2017-04-10 11:32:00 +00:00
|
|
|
|
loop_optimizer_init (LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const pass_data pass_data_rtl_loop_init =
|
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
|
|
|
|
"loop2_init", /* name */
|
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
|
|
|
|
TV_LOOP, /* tv_id */
|
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
0, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
0, /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
class pass_rtl_loop_init : public rtl_opt_pass
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
pass_rtl_loop_init (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_rtl_loop_init, ctxt)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual unsigned int execute (function *) { return rtl_loop_init (); }
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
}; // class pass_rtl_loop_init
|
|
|
|
|
|
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
|
|
|
|
make_pass_rtl_loop_init (gcc::context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
return new pass_rtl_loop_init (ctxt);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
/* Finalization of the RTL loop passes. */
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const pass_data pass_data_rtl_loop_done =
|
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
|
|
|
|
"loop2_done", /* name */
|
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
|
|
|
|
TV_LOOP, /* tv_id */
|
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
PROP_loops, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
0, /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
class pass_rtl_loop_done : public rtl_opt_pass
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
pass_rtl_loop_done (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_rtl_loop_done, ctxt)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual unsigned int execute (function *);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
}; // class pass_rtl_loop_done
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
unsigned int
|
|
|
|
|
pass_rtl_loop_done::execute (function *fun)
|
|
|
|
|
{
|
|
|
|
|
/* No longer preserve loops, remove them now. */
|
|
|
|
|
fun->curr_properties &= ~PROP_loops;
|
|
|
|
|
loop_optimizer_finalize ();
|
|
|
|
|
free_dominance_info (CDI_DOMINATORS);
|
|
|
|
|
|
|
|
|
|
cleanup_cfg (0);
|
|
|
|
|
if (dump_file)
|
|
|
|
|
{
|
|
|
|
|
dump_reg_info (dump_file);
|
|
|
|
|
dump_flow_info (dump_file, dump_flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
|
|
|
|
make_pass_rtl_loop_done (gcc::context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
return new pass_rtl_loop_done (ctxt);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
|
|
|
|
/* Loop invariant code motion. */
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const pass_data pass_data_rtl_move_loop_invariants =
|
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
|
|
|
|
"loop2_invariant", /* name */
|
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
|
|
|
|
TV_LOOP_MOVE_INVARIANTS, /* tv_id */
|
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
0, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
( TODO_df_verify | TODO_df_finish ), /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
class pass_rtl_move_loop_invariants : public rtl_opt_pass
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
pass_rtl_move_loop_invariants (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_rtl_move_loop_invariants, ctxt)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual bool gate (function *) { return flag_move_loop_invariants; }
|
|
|
|
|
virtual unsigned int execute (function *fun)
|
|
|
|
|
{
|
|
|
|
|
if (number_of_loops (fun) > 1)
|
|
|
|
|
move_loop_invariants ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
}; // class pass_rtl_move_loop_invariants
|
|
|
|
|
|
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
|
|
|
|
make_pass_rtl_move_loop_invariants (gcc::context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
return new pass_rtl_move_loop_invariants (ctxt);
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
const pass_data pass_data_rtl_unroll_loops =
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
"loop2_unroll", /* name */
|
2014-09-21 17:33:12 +00:00
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
TV_LOOP_UNROLL, /* tv_id */
|
2014-09-21 17:33:12 +00:00
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
0, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
0, /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
class pass_rtl_unroll_loops : public rtl_opt_pass
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2015-08-28 15:33:40 +00:00
|
|
|
|
pass_rtl_unroll_loops (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_rtl_unroll_loops, ctxt)
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual bool gate (function *)
|
|
|
|
|
{
|
2018-12-28 15:30:48 +00:00
|
|
|
|
return (flag_unroll_loops || flag_unroll_all_loops || cfun->has_unroll);
|
2015-08-28 15:33:40 +00:00
|
|
|
|
}
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual unsigned int execute (function *);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
}; // class pass_rtl_unroll_loops
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
unsigned int
|
|
|
|
|
pass_rtl_unroll_loops::execute (function *fun)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
2015-08-28 15:33:40 +00:00
|
|
|
|
if (number_of_loops (fun) > 1)
|
2012-03-27 23:13:14 +00:00
|
|
|
|
{
|
|
|
|
|
int flags = 0;
|
|
|
|
|
if (dump_file)
|
|
|
|
|
df_dump (dump_file);
|
|
|
|
|
|
|
|
|
|
if (flag_unroll_loops)
|
|
|
|
|
flags |= UAP_UNROLL;
|
|
|
|
|
if (flag_unroll_all_loops)
|
|
|
|
|
flags |= UAP_UNROLL_ALL;
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
unroll_loops (flags);
|
2012-03-27 23:13:14 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
2015-08-28 15:33:40 +00:00
|
|
|
|
make_pass_rtl_unroll_loops (gcc::context *ctxt)
|
2014-09-21 17:33:12 +00:00
|
|
|
|
{
|
2015-08-28 15:33:40 +00:00
|
|
|
|
return new pass_rtl_unroll_loops (ctxt);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-03-27 23:13:14 +00:00
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
const pass_data pass_data_rtl_doloop =
|
|
|
|
|
{
|
|
|
|
|
RTL_PASS, /* type */
|
|
|
|
|
"loop2_doloop", /* name */
|
|
|
|
|
OPTGROUP_LOOP, /* optinfo_flags */
|
|
|
|
|
TV_LOOP_DOLOOP, /* tv_id */
|
|
|
|
|
0, /* properties_required */
|
|
|
|
|
0, /* properties_provided */
|
|
|
|
|
0, /* properties_destroyed */
|
|
|
|
|
0, /* todo_flags_start */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
0, /* todo_flags_finish */
|
2012-03-27 23:13:14 +00:00
|
|
|
|
};
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
class pass_rtl_doloop : public rtl_opt_pass
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
pass_rtl_doloop (gcc::context *ctxt)
|
|
|
|
|
: rtl_opt_pass (pass_data_rtl_doloop, ctxt)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
/* opt_pass methods: */
|
2015-08-28 15:33:40 +00:00
|
|
|
|
virtual bool gate (function *);
|
|
|
|
|
virtual unsigned int execute (function *);
|
2014-09-21 17:33:12 +00:00
|
|
|
|
|
|
|
|
|
}; // class pass_rtl_doloop
|
|
|
|
|
|
2015-08-28 15:33:40 +00:00
|
|
|
|
bool
|
|
|
|
|
pass_rtl_doloop::gate (function *)
|
|
|
|
|
{
|
2017-04-10 11:32:00 +00:00
|
|
|
|
return (flag_branch_on_count_reg && targetm.have_doloop_end ());
|
2015-08-28 15:33:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
2017-04-10 11:32:00 +00:00
|
|
|
|
pass_rtl_doloop::execute (function *fun)
|
2015-08-28 15:33:40 +00:00
|
|
|
|
{
|
|
|
|
|
if (number_of_loops (fun) > 1)
|
|
|
|
|
doloop_optimize_loops ();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 17:33:12 +00:00
|
|
|
|
} // anon namespace
|
|
|
|
|
|
|
|
|
|
rtl_opt_pass *
|
|
|
|
|
make_pass_rtl_doloop (gcc::context *ctxt)
|
|
|
|
|
{
|
|
|
|
|
return new pass_rtl_doloop (ctxt);
|
|
|
|
|
}
|