aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-loop-ivcanon.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-loop-ivcanon.c')
-rw-r--r--gcc/tree-ssa-loop-ivcanon.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/gcc/tree-ssa-loop-ivcanon.c b/gcc/tree-ssa-loop-ivcanon.c
new file mode 100644
index 00000000000..0cd64073eae
--- /dev/null
+++ b/gcc/tree-ssa-loop-ivcanon.c
@@ -0,0 +1,487 @@
+/* Induction variable canonicalization.
+ Copyright (C) 2004 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 2, 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 COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* This pass detects the loops that iterate a constant number of times,
+ adds a canonical induction variable (step -1, tested against 0)
+ and replaces the exit test. This enables the less powerful rtl
+ level analysis to use this information.
+
+ This might spoil the code in some cases (by increasing register pressure).
+ Note that in the case the new variable is not needed, ivopts will get rid
+ of it, so it might only be a problem when there are no other linear induction
+ variables. In that case the created optimization possibilities are likely
+ to pay up.
+
+ Additionally in case we detect that it is benefitial to unroll the
+ loop completely, we do it right here to expose the optimization
+ possibilities to the following passes. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "output.h"
+#include "diagnostic.h"
+#include "tree-flow.h"
+#include "tree-dump.h"
+#include "cfgloop.h"
+#include "tree-pass.h"
+#include "ggc.h"
+#include "tree-fold-const.h"
+#include "tree-chrec.h"
+#include "tree-scalar-evolution.h"
+#include "params.h"
+#include "flags.h"
+#include "tree-inline.h"
+
+/* Bound on the number of iterations we try to evaluate. */
+
+#define MAX_ITERATIONS_TO_TRACK 1000
+
+/* Determines a loop phi node of LOOP such that X is derived from it
+ by a chain of operations with constants. */
+
+static tree
+chain_of_csts_start (struct loop *loop, tree x)
+{
+ tree stmt = SSA_NAME_DEF_STMT (x);
+ basic_block bb = bb_for_stmt (stmt);
+ use_optype uses;
+
+ if (!bb
+ || !flow_bb_inside_loop_p (loop, bb))
+ return NULL_TREE;
+
+ if (TREE_CODE (stmt) == PHI_NODE)
+ {
+ if (bb == loop->header)
+ return stmt;
+
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE (stmt) != MODIFY_EXPR)
+ return NULL_TREE;
+
+ get_stmt_operands (stmt);
+ if (NUM_VUSES (STMT_VUSE_OPS (stmt)) > 0)
+ return NULL_TREE;
+ if (NUM_VDEFS (STMT_VDEF_OPS (stmt)) > 0)
+ return NULL_TREE;
+ if (NUM_DEFS (STMT_DEF_OPS (stmt)) > 1)
+ return NULL_TREE;
+ uses = STMT_USE_OPS (stmt);
+ if (NUM_USES (uses) != 1)
+ return NULL_TREE;
+
+ return chain_of_csts_start (loop, USE_OP (uses, 0));
+}
+
+/* Determines whether X is derived from a value of a phi node in LOOP
+ such that
+
+ * this derivation consists only from operations with constants
+ * the initial value of the phi node is constant
+ * its value in the next iteration can be derived from the current one
+ by a chain of operations with constants. */
+
+static tree
+get_base_for (struct loop *loop, tree x)
+{
+ tree phi, init, next;
+
+ if (is_gimple_min_invariant (x))
+ return x;
+
+ phi = chain_of_csts_start (loop, x);
+ if (!phi)
+ return NULL_TREE;
+
+ init = phi_element_for_edge (phi, loop_preheader_edge (loop))->def;
+ next = phi_element_for_edge (phi, loop_latch_edge (loop))->def;
+
+ if (TREE_CODE (next) != SSA_NAME)
+ return NULL_TREE;
+
+ if (!is_gimple_min_invariant (init))
+ return NULL_TREE;
+
+ if (chain_of_csts_start (loop, next) != phi)
+ return NULL_TREE;
+
+ return phi;
+}
+
+/* Evaluates value of X, provided that the value of the variable defined
+ in the loop phi node from that X is derived by operations with constants
+ is BASE. */
+
+static tree
+get_val_for (tree x, tree base)
+{
+ tree stmt, *op, nx, val;
+ use_optype uses;
+
+ if (!x)
+ return base;
+
+ stmt = SSA_NAME_DEF_STMT (x);
+ if (TREE_CODE (stmt) == PHI_NODE)
+ return base;
+
+ uses = STMT_USE_OPS (stmt);
+ op = USE_OP_PTR (uses, 0);
+
+ nx = *op;
+ val = get_val_for (nx, base);
+ *op = val;
+ val = fold (TREE_OPERAND (stmt, 1));
+ *op = nx;
+
+ return val;
+}
+
+/* Tries to count the number of iterations of LOOP till it exits by EXIT
+ by brute force. */
+
+static tree
+loop_niter_by_eval (struct loop *loop, edge exit)
+{
+ tree cond, cnd, acnd;
+ tree op[2], val[2], next[2], aval[2], phi[2];
+ unsigned i, j;
+ enum tree_code cmp;
+
+ cond = last_stmt (exit->src);
+ if (!cond || TREE_CODE (cond) != COND_EXPR)
+ return chrec_top;
+
+ cnd = COND_EXPR_COND (cond);
+ if (exit->flags & EDGE_TRUE_VALUE)
+ cnd = invert_truthvalue (cnd);
+
+ cmp = TREE_CODE (cnd);
+ switch (cmp)
+ {
+ case EQ_EXPR:
+ case NE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case LT_EXPR:
+ case LE_EXPR:
+ for (j = 0; j < 2; j++)
+ op[j] = TREE_OPERAND (cnd, j);
+ break;
+
+ default:
+ return chrec_top;
+ }
+
+ for (j = 0; j < 2; j++)
+ {
+ phi[j] = get_base_for (loop, op[j]);
+ if (!phi[j])
+ return chrec_top;
+ }
+
+ for (j = 0; j < 2; j++)
+ {
+ if (TREE_CODE (phi[j]) == PHI_NODE)
+ {
+ val[j] = phi_element_for_edge (phi[j],
+ loop_preheader_edge (loop))->def;
+ next[j] = phi_element_for_edge (phi[j],
+ loop_latch_edge (loop))->def;
+ }
+ else
+ {
+ val[j] = phi[j];
+ next[j] = NULL_TREE;
+ op[j] = NULL_TREE;
+ }
+ }
+
+ for (i = 0; i < MAX_ITERATIONS_TO_TRACK; i++)
+ {
+ for (j = 0; j < 2; j++)
+ aval[j] = get_val_for (op[j], val[j]);
+
+ acnd = fold (build (cmp, boolean_type_node, aval[0], aval[1]));
+ if (integer_zerop (acnd))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Proved that loop %d iterates %d times using brute force.\n",
+ loop->num, i);
+ return build_int_2 (i, 0);
+ }
+
+ for (j = 0; j < 2; j++)
+ val[j] = get_val_for (next[j], val[j]);
+ }
+
+ return chrec_top;
+}
+
+/* Finds the exit of the LOOP by that the loop exits after a constant
+ number of iterations and stores it to *EXIT. The iteration count
+ is returned. */
+
+static tree
+find_loop_niter_by_eval (struct loop *loop, edge *exit)
+{
+ unsigned n_exits, i;
+ edge *exits = get_loop_exit_edges (loop, &n_exits);
+ edge ex;
+ tree niter = NULL_TREE, aniter;
+
+ *exit = NULL;
+ for (i = 0; i < n_exits; i++)
+ {
+ ex = exits[i];
+ if (!just_once_each_iteration_p (loop, ex->src))
+ continue;
+
+ aniter = loop_niter_by_eval (loop, ex);
+ if (TREE_CODE (aniter) != INTEGER_CST)
+ continue;
+
+ if (niter
+ && !integer_nonzerop (fold (build (LT_EXPR, boolean_type_node,
+ aniter, niter))))
+ continue;
+
+ niter = aniter;
+ *exit = ex;
+ }
+ free (exits);
+
+ return niter ? niter : chrec_top;
+}
+
+/* Adds a canonical induction variable to LOOP iterating NITER times. EXIT
+ is the exit edge whose condition is replaced. */
+
+static void
+create_canonical_iv (struct loop *loop, edge exit, tree niter)
+{
+ edge in;
+ tree cond, type, var;
+ block_stmt_iterator incr_at;
+ enum tree_code cmp;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Added canonical iv to loop %d, ", loop->num);
+ print_generic_expr (dump_file, niter, TDF_SLIM);
+ fprintf (dump_file, " iterations.\n");
+ }
+
+ cond = last_stmt (exit->src);
+ in = exit->src->succ;
+ if (in == exit)
+ in = in->succ_next;
+
+ type = TREE_TYPE (niter);
+ niter = fold (build (PLUS_EXPR, type,
+ niter, convert (type, integer_one_node)));
+ incr_at = bsi_last (in->src);
+ create_iv (niter, convert (type, integer_minus_one_node), NULL_TREE, loop,
+ &incr_at, false, NULL, &var);
+
+ cmp = (exit->flags & EDGE_TRUE_VALUE) ? EQ_EXPR : NE_EXPR;
+ COND_EXPR_COND (cond) = build (cmp, boolean_type_node,
+ var, convert (type, integer_zero_node));
+ modify_stmt (cond);
+}
+
+/* Computes an estimated number of insns in LOOP. */
+
+unsigned
+estimate_loop_size (struct loop *loop)
+{
+ basic_block *body = get_loop_body (loop);
+ block_stmt_iterator bsi;
+ unsigned size = 0, i;
+
+ for (i = 0; i < loop->num_nodes; i++)
+ for (bsi = bsi_start (body[i]); !bsi_end_p (bsi); bsi_next (&bsi))
+ size += estimate_num_insns (bsi_stmt (bsi));
+ free (body);
+
+ return size;
+}
+
+/* Tries to unroll LOOP completely, i.e. NITER times. LOOPS is the
+ loop tree. COMPLETELY_UNROLL is true if we should unroll the loop
+ even if it may cause code growth. EXIT is the exit of the loop
+ that should be eliminated. */
+
+static bool
+try_unroll_loop_completely (struct loops *loops, struct loop *loop,
+ edge exit, tree niter,
+ bool completely_unroll)
+{
+ tree max_unroll = build_int_2 (PARAM_VALUE (PARAM_MAX_COMPLETELY_PEEL_TIMES),
+ 0);
+ unsigned n_unroll, ninsns;
+ tree cond, dont_exit, do_exit;
+
+ if (loop->inner)
+ return false;
+
+ if (!integer_nonzerop (fold (build (LE_EXPR, boolean_type_node,
+ niter, max_unroll))))
+ return false;
+ n_unroll = tree_low_cst (niter, 1);
+
+ if (n_unroll && !completely_unroll)
+ return false;
+
+ ninsns = estimate_loop_size (loop);
+
+ if (n_unroll * ninsns
+ > (unsigned) PARAM_VALUE (PARAM_MAX_COMPLETELY_PEELED_INSNS))
+ return false;
+
+ if (exit->flags & EDGE_TRUE_VALUE)
+ {
+ dont_exit = boolean_false_node;
+ do_exit = boolean_true_node;
+ }
+ else
+ {
+ dont_exit = boolean_true_node;
+ do_exit = boolean_false_node;
+ }
+ cond = last_stmt (exit->src);
+
+ if (n_unroll)
+ {
+ if (!flag_unroll_loops)
+ return false;
+
+ COND_EXPR_COND (cond) = dont_exit;
+ modify_stmt (cond);
+
+ if (!tree_duplicate_loop_to_header_edge (loop, loop_preheader_edge (loop),
+ loops, n_unroll, NULL,
+ NULL, NULL, NULL, 0))
+ return false;
+ }
+
+ COND_EXPR_COND (cond) = do_exit;
+ modify_stmt (cond);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Unrolled loop %d completely.\n", loop->num);
+
+ return true;
+}
+
+/* Adds a canonical induction variable to LOOP if suitable. LOOPS is the loops
+ tree. CREATE_IV is true if we may create a new iv. COMPLETELY_UNROLL is
+ true if we should do complete unrolling even if it may cause the code
+ growth. */
+
+static void
+canonicalize_loop_induction_variables (struct loops *loops, struct loop *loop,
+ bool create_iv, bool completely_unroll)
+{
+ edge exit = NULL;
+ tree niter;
+
+ /* ??? Why is this needed? I.e. from where comes the invalid info? */
+ loop->nb_iterations = NULL;
+
+ niter = number_of_iterations_in_loop (loop);
+
+ if (TREE_CODE (niter) == INTEGER_CST)
+ {
+#ifdef ENABLE_CHECKING
+ tree nit;
+ edge ex;
+#endif
+
+ exit = loop_exit_edge (loop, 0);
+ if (!just_once_each_iteration_p (loop, exit->src))
+ return;
+
+ /* The result of number_of_iterations_in_loop is by one higher than
+ we expect (i.e. it returns number of executions of the exit
+ condition, not of the loop latch edge). */
+ niter = fold (build (PLUS_EXPR, TREE_TYPE (niter), niter,
+ convert (TREE_TYPE (niter),
+ integer_minus_one_node)));
+
+#ifdef ENABLE_CHECKING
+ nit = find_loop_niter_by_eval (loop, &ex);
+
+ if (ex == exit
+ && TREE_CODE (nit) == INTEGER_CST
+ && !operand_equal_p (niter, nit, 0))
+ abort ();
+#endif
+ }
+ else
+ niter = find_loop_niter_by_eval (loop, &exit);
+
+ if (TREE_CODE (niter) != INTEGER_CST)
+ return;
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Loop %d iterates ", loop->num);
+ print_generic_expr (dump_file, niter, TDF_SLIM);
+ fprintf (dump_file, " times.\n");
+ }
+
+ if (try_unroll_loop_completely (loops, loop, exit, niter, completely_unroll))
+ return;
+
+ if (create_iv)
+ create_canonical_iv (loop, exit, niter);
+}
+
+/* The main entry point of the pass. Adds canonical induction variables
+ to the suitable LOOPS. */
+
+void
+canonicalize_induction_variables (struct loops *loops)
+{
+ unsigned i;
+ struct loop *loop;
+ bool create_ivs = flag_unroll_loops || flag_branch_on_count_reg;
+ bool completely_unroll_loops = flag_unroll_loops;
+
+ for (i = 1; i < loops->num; i++)
+ {
+ loop = loops->parray[i];
+
+ if (loop)
+ canonicalize_loop_induction_variables (loops, loop, create_ivs,
+ completely_unroll_loops);
+ }
+}