We recompute the per-path stats at every node of the search, which is
rather expensive it turns out. There was a note hidden in the
original code saying we should do this incrementally, and this commit
does so and gives us a 7.8% improvement in the threader.
Tested on ppc64le Linux.
Over a corpus of .ii files from a stage1 bootstrap the assembly is
identical. Also, I instrumented the compiler before and after this
patch, and verified that the path and stats are identical, so
really... a NFC.
gcc/ChangeLog:
* tree-ssa-threadbackward.cc (back_threader_profitability): Add
push_bb, pop_bb, m_loop, unwind_state and m_unwind; add n_insns
to path_stats.
(back_threader_profitability::back_threader_profitability):
Initialize m_loop and m_stats.
(back_threader_profitability::account_bb): Return the number of
insns in the block.
(back_threader_profitability::push_bb): New.
(back_threader_profitability::pop_bb): New.
(back_threader_profitability::possibly_profitable_path_p): Use
the accumulated stats.
(back_threader::find_paths_to_names): Call push_bb/pop_bb.
---
gcc/tree-ssa-threadbackward.cc | 110 ++++++++++++++++++++-------------
1 file changed, 68 insertions(+), 42 deletions(-)
diff --git a/gcc/tree-ssa-threadbackward.cc b/gcc/tree-ssa-threadbackward.cc
index 1ae32312968..03a7e1db50c 100644
--- a/gcc/tree-ssa-threadbackward.cc
+++ b/gcc/tree-ssa-threadbackward.cc
@@ -62,24 +62,40 @@ class back_threader_profitability
{
public:
back_threader_profitability (bool speed_p, gimple *stmt);
+ void push_bb (const vec<basic_block> &);
+ void pop_bb ();
bool possibly_profitable_path_p (const vec<basic_block> &, bool *);
bool profitable_path_p (const vec<basic_block> &,
edge taken, bool *irreducible_loop);
private:
- void account_bb (basic_block, bool);
+ int account_bb (basic_block, bool);
const bool m_speed_p;
int m_exit_jump_benefit;
bool m_threaded_multiway_branch;
- // The following are computed by possibly_profitable_path_p
+ // The loop the path starts in, i.e. m_path[0]->loop_father.
+ class loop *m_loop;
+ // The following are accumulated by push_bb as the path grows and
+ // restored by pop_bb as it shrinks.
struct path_stats
{
+ int n_insns;
bool threaded_through_latch;
bool multiway_branch_in_path;
bool contains_hot_bb;
bool unprofitable_bb;
};
path_stats m_stats;
+ // Number of insns on the path, less the branch we get to remove.
int m_n_insns;
+ // One entry per push. These are the stats as they stood before that push,
+ // and the insns of the block. Basically the entry pushed for m_path[i]
+ // accounts m_path[i - 1].
+ struct unwind_state
+ {
+ path_stats stats;
+ int bb_insns;
+ };
+ auto_vec<unwind_state, 20> m_unwind;
};
back_threader_profitability::back_threader_profitability (bool speed_p,
@@ -92,15 +108,19 @@ back_threader_profitability::back_threader_profitability
(bool speed_p,
// particular it estimates further DCE from eliminating the exit
// control stmt.
m_exit_jump_benefit = estimate_num_insns (last, &eni_size_weights);
+ m_loop = NULL;
+ m_stats = path_stats ();
}
/* Account for BB in the cumulative stats for the path being threaded.
CHECK_MULTIWAY is true for all blocks except the block whose branch
- we are going to eliminate. */
+ we are going to eliminate. Return the number of insns in BB. */
-void
+int
back_threader_profitability::account_bb (basic_block bb, bool check_multiway)
{
+ int n_insns = 0;
+
if (!m_stats.contains_hot_bb && m_speed_p)
m_stats.contains_hot_bb |= optimize_bb_for_speed_p (bb);
@@ -117,12 +137,12 @@ back_threader_profitability::account_bb (basic_block bb,
bool check_multiway)
|| gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P))
{
m_stats.unprofitable_bb = true;
- return;
+ return n_insns;
}
/* Do not count empty statements and labels. */
if (gimple_code (stmt) != GIMPLE_NOP
&& !is_gimple_debug (stmt))
- m_n_insns += estimate_num_insns (stmt, &eni_size_weights);
+ n_insns += estimate_num_insns (stmt, &eni_size_weights);
}
/* We do not look at the block with the threaded branch in this loop.
@@ -136,6 +156,42 @@ back_threader_profitability::account_bb (basic_block bb,
bool check_multiway)
|| gimple_code (last) == GIMPLE_GOTO))
m_stats.multiway_branch_in_path = true;
}
+
+ return n_insns;
+}
+
+/* Update the stats after a block has been appended to PATH. */
+
+void
+back_threader_profitability::push_bb (const vec<basic_block> &path)
+{
+ unwind_state state = { m_stats, /*bb_insns=*/0 };
+
+ unsigned n = path.length ();
+ if (n == 1)
+ m_loop = path[0]->loop_father;
+ else
+ {
+ /* Appending a block makes the previous entry block part of the copied
+ path, so that's where to account for now. */
+ unsigned copied = n - 2;
+ bool check_multiway = copied > 0;
+ state.bb_insns = account_bb (path[copied], check_multiway);
+ m_stats.n_insns += state.bb_insns;
+ }
+ m_unwind.safe_push (state);
+
+ /* Note if we thread through the latch, we will want to include the
+ last entry in the array when determining if we thread through the
+ loop latch. */
+ if (m_loop->latch == path[n - 1])
+ m_stats.threaded_through_latch = true;
+}
+
+void
+back_threader_profitability::pop_bb ()
+{
+ m_stats = m_unwind.pop ().stats;
}
// Back threader flags.
@@ -440,6 +496,7 @@ back_threader::find_paths_to_names (basic_block bb, bitmap
interesting,
return;
m_path.safe_push (bb);
+ profit.push_bb (m_path);
// Try to resolve the path without looking back. Avoid resolving paths
// we know are large but are not (yet) recognized as Finite State Machine.
@@ -576,6 +633,7 @@ back_threader::find_paths_to_names (basic_block bb, bitmap
interesting,
param_max_jump_thread_paths);
// Reset things to their original state.
+ profit.pop_bb ();
m_path.pop ();
m_visited_bbs.remove (bb);
}
@@ -685,42 +743,10 @@ back_threader_profitability::possibly_profitable_path_p
if (m_path.length () <= 1)
return false;
- loop_p loop = m_path[0]->loop_father;
-
- // We recompute the following, when we rewrite possibly_profitable_path_p
- // to work incrementally on added BBs we have to unwind them on backtracking
- m_n_insns = 0;
- m_stats = path_stats ();
+ loop_p loop = m_loop;
- /* Count the number of instructions on the path: as these instructions
- will have to be duplicated, we will not record the path if there
- are too many instructions on the path. Also check that all the
- blocks in the path belong to a single loop. */
- for (unsigned j = 0; j < m_path.length (); j++)
- {
- basic_block bb = m_path[j];
-
- /* Remember, blocks in the path are stored in opposite order in
- the PATH array. The last entry in the array represents the
- block with an outgoing edge that we will redirect to the jump
- threading path. Thus we don't care how many statements are
- in that block because it will not be copied or whether or not
- it ends in a multiway branch. */
- if (j < m_path.length () - 1)
- {
- /* The block in PATH[0] is special, it's the block were we're
- going to be able to eliminate its branch. */
- account_bb (bb, /*check_multiway=*/j > 0);
- if (m_stats.unprofitable_bb)
- return false;
- }
-
- /* Note if we thread through the latch, we will want to include
- the last entry in the array when determining if we thread
- through the loop latch. */
- if (loop->latch == bb)
- m_stats.threaded_through_latch = true;
- }
+ if (m_stats.unprofitable_bb)
+ return false;
if (dump_file && (dump_flags & TDF_DETAILS))
{
@@ -736,7 +762,7 @@ back_threader_profitability::possibly_profitable_path_p
/* We are going to remove the control statement at the end of the
last block in the threading path. So don't count it against our
statement count. */
- m_n_insns -= m_exit_jump_benefit;
+ m_n_insns = m_stats.n_insns - m_exit_jump_benefit;
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "\n Control statement insns: %i\n"
--
2.53.0