OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-stable/] [gcc-4.5.1/] [gcc/] [tree-ssa-loop-unswitch.c] - Diff between revs 816 and 826

Only display areas with differences | Details | Blame | View Log

Rev 816 Rev 826
/* Loop unswitching.
/* Loop unswitching.
   Copyright (C) 2004, 2005, 2007, 2008, 2010 Free Software Foundation, Inc.
   Copyright (C) 2004, 2005, 2007, 2008, 2010 Free Software Foundation, Inc.
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify it
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
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3, or (at your option) any
Free Software Foundation; either version 3, or (at your option) any
later version.
later version.
 
 
GCC is distributed in the hope that it will be useful, but WITHOUT
GCC is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.
for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
#include "tree.h"
#include "tree.h"
#include "rtl.h"
#include "rtl.h"
#include "tm_p.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "basic-block.h"
#include "output.h"
#include "output.h"
#include "diagnostic.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-flow.h"
#include "tree-dump.h"
#include "tree-dump.h"
#include "timevar.h"
#include "timevar.h"
#include "cfgloop.h"
#include "cfgloop.h"
#include "params.h"
#include "params.h"
#include "tree-pass.h"
#include "tree-pass.h"
#include "tree-inline.h"
#include "tree-inline.h"
 
 
/* This file implements the loop unswitching, i.e. transformation of loops like
/* This file implements the loop unswitching, i.e. transformation of loops like
 
 
   while (A)
   while (A)
     {
     {
       if (inv)
       if (inv)
         B;
         B;
 
 
       X;
       X;
 
 
       if (!inv)
       if (!inv)
         C;
         C;
     }
     }
 
 
   where inv is the loop invariant, into
   where inv is the loop invariant, into
 
 
   if (inv)
   if (inv)
     {
     {
       while (A)
       while (A)
         {
         {
           B;
           B;
           X;
           X;
         }
         }
     }
     }
   else
   else
     {
     {
       while (A)
       while (A)
         {
         {
           X;
           X;
           C;
           C;
         }
         }
     }
     }
 
 
   Inv is considered invariant iff the values it compares are both invariant;
   Inv is considered invariant iff the values it compares are both invariant;
   tree-ssa-loop-im.c ensures that all the suitable conditions are in this
   tree-ssa-loop-im.c ensures that all the suitable conditions are in this
   shape.  */
   shape.  */
 
 
static struct loop *tree_unswitch_loop (struct loop *, basic_block, tree);
static struct loop *tree_unswitch_loop (struct loop *, basic_block, tree);
static bool tree_unswitch_single_loop (struct loop *, int);
static bool tree_unswitch_single_loop (struct loop *, int);
static tree tree_may_unswitch_on (basic_block, struct loop *);
static tree tree_may_unswitch_on (basic_block, struct loop *);
 
 
/* Main entry point.  Perform loop unswitching on all suitable loops.  */
/* Main entry point.  Perform loop unswitching on all suitable loops.  */
 
 
unsigned int
unsigned int
tree_ssa_unswitch_loops (void)
tree_ssa_unswitch_loops (void)
{
{
  loop_iterator li;
  loop_iterator li;
  struct loop *loop;
  struct loop *loop;
  bool changed = false;
  bool changed = false;
 
 
  /* Go through inner loops (only original ones).  */
  /* Go through inner loops (only original ones).  */
  FOR_EACH_LOOP (li, loop, LI_ONLY_INNERMOST)
  FOR_EACH_LOOP (li, loop, LI_ONLY_INNERMOST)
    {
    {
      changed |= tree_unswitch_single_loop (loop, 0);
      changed |= tree_unswitch_single_loop (loop, 0);
    }
    }
 
 
  if (changed)
  if (changed)
    return TODO_cleanup_cfg;
    return TODO_cleanup_cfg;
  return 0;
  return 0;
}
}
 
 
/* Checks whether we can unswitch LOOP on condition at end of BB -- one of its
/* Checks whether we can unswitch LOOP on condition at end of BB -- one of its
   basic blocks (for what it means see comments below).  */
   basic blocks (for what it means see comments below).  */
 
 
static tree
static tree
tree_may_unswitch_on (basic_block bb, struct loop *loop)
tree_may_unswitch_on (basic_block bb, struct loop *loop)
{
{
  gimple stmt, def;
  gimple stmt, def;
  tree cond, use;
  tree cond, use;
  basic_block def_bb;
  basic_block def_bb;
  ssa_op_iter iter;
  ssa_op_iter iter;
 
 
  /* BB must end in a simple conditional jump.  */
  /* BB must end in a simple conditional jump.  */
  stmt = last_stmt (bb);
  stmt = last_stmt (bb);
  if (!stmt || gimple_code (stmt) != GIMPLE_COND)
  if (!stmt || gimple_code (stmt) != GIMPLE_COND)
    return NULL_TREE;
    return NULL_TREE;
 
 
  /* To keep the things simple, we do not directly remove the conditions,
  /* To keep the things simple, we do not directly remove the conditions,
     but just replace tests with 0 != 0 resp. 1 != 0.  Prevent the infinite
     but just replace tests with 0 != 0 resp. 1 != 0.  Prevent the infinite
     loop where we would unswitch again on such a condition.  */
     loop where we would unswitch again on such a condition.  */
  if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt))
  if (gimple_cond_true_p (stmt) || gimple_cond_false_p (stmt))
    return NULL_TREE;
    return NULL_TREE;
 
 
  /* Condition must be invariant.  */
  /* Condition must be invariant.  */
  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
  FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE)
    {
    {
      def = SSA_NAME_DEF_STMT (use);
      def = SSA_NAME_DEF_STMT (use);
      def_bb = gimple_bb (def);
      def_bb = gimple_bb (def);
      if (def_bb
      if (def_bb
          && flow_bb_inside_loop_p (loop, def_bb))
          && flow_bb_inside_loop_p (loop, def_bb))
        return NULL_TREE;
        return NULL_TREE;
    }
    }
 
 
  cond = build2 (gimple_cond_code (stmt), boolean_type_node,
  cond = build2 (gimple_cond_code (stmt), boolean_type_node,
                 gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
                 gimple_cond_lhs (stmt), gimple_cond_rhs (stmt));
 
 
  return cond;
  return cond;
}
}
 
 
/* Simplifies COND using checks in front of the entry of the LOOP.  Just very
/* Simplifies COND using checks in front of the entry of the LOOP.  Just very
   simplish (sufficient to prevent us from duplicating loop in unswitching
   simplish (sufficient to prevent us from duplicating loop in unswitching
   unnecessarily).  */
   unnecessarily).  */
 
 
static tree
static tree
simplify_using_entry_checks (struct loop *loop, tree cond)
simplify_using_entry_checks (struct loop *loop, tree cond)
{
{
  edge e = loop_preheader_edge (loop);
  edge e = loop_preheader_edge (loop);
  gimple stmt;
  gimple stmt;
 
 
  while (1)
  while (1)
    {
    {
      stmt = last_stmt (e->src);
      stmt = last_stmt (e->src);
      if (stmt
      if (stmt
          && gimple_code (stmt) == GIMPLE_COND
          && gimple_code (stmt) == GIMPLE_COND
          && gimple_cond_code (stmt) == TREE_CODE (cond)
          && gimple_cond_code (stmt) == TREE_CODE (cond)
          && operand_equal_p (gimple_cond_lhs (stmt),
          && operand_equal_p (gimple_cond_lhs (stmt),
                              TREE_OPERAND (cond, 0), 0)
                              TREE_OPERAND (cond, 0), 0)
          && operand_equal_p (gimple_cond_rhs (stmt),
          && operand_equal_p (gimple_cond_rhs (stmt),
                              TREE_OPERAND (cond, 1), 0))
                              TREE_OPERAND (cond, 1), 0))
        return (e->flags & EDGE_TRUE_VALUE
        return (e->flags & EDGE_TRUE_VALUE
                ? boolean_true_node
                ? boolean_true_node
                : boolean_false_node);
                : boolean_false_node);
 
 
      if (!single_pred_p (e->src))
      if (!single_pred_p (e->src))
        return cond;
        return cond;
 
 
      e = single_pred_edge (e->src);
      e = single_pred_edge (e->src);
      if (e->src == ENTRY_BLOCK_PTR)
      if (e->src == ENTRY_BLOCK_PTR)
        return cond;
        return cond;
    }
    }
}
}
 
 
/* Unswitch single LOOP.  NUM is number of unswitchings done; we do not allow
/* Unswitch single LOOP.  NUM is number of unswitchings done; we do not allow
   it to grow too much, it is too easy to create example on that the code would
   it to grow too much, it is too easy to create example on that the code would
   grow exponentially.  */
   grow exponentially.  */
 
 
static bool
static bool
tree_unswitch_single_loop (struct loop *loop, int num)
tree_unswitch_single_loop (struct loop *loop, int num)
{
{
  basic_block *bbs;
  basic_block *bbs;
  struct loop *nloop;
  struct loop *nloop;
  unsigned i, found;
  unsigned i, found;
  tree cond = NULL_TREE;
  tree cond = NULL_TREE;
  gimple stmt;
  gimple stmt;
  bool changed = false;
  bool changed = false;
 
 
  /* Only unswitch innermost loops.  */
  /* Only unswitch innermost loops.  */
  if (loop->inner)
  if (loop->inner)
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, ";; Not unswitching, not innermost loop\n");
        fprintf (dump_file, ";; Not unswitching, not innermost loop\n");
      return false;
      return false;
    }
    }
 
 
  /* Do not unswitch in cold regions.  */
  /* Do not unswitch in cold regions.  */
  if (optimize_loop_for_size_p (loop))
  if (optimize_loop_for_size_p (loop))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, ";; Not unswitching cold loops\n");
        fprintf (dump_file, ";; Not unswitching cold loops\n");
      return false;
      return false;
    }
    }
 
 
  /* The loop should not be too large, to limit code growth.  */
  /* The loop should not be too large, to limit code growth.  */
  if (tree_num_loop_insns (loop, &eni_size_weights)
  if (tree_num_loop_insns (loop, &eni_size_weights)
      > (unsigned) PARAM_VALUE (PARAM_MAX_UNSWITCH_INSNS))
      > (unsigned) PARAM_VALUE (PARAM_MAX_UNSWITCH_INSNS))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, ";; Not unswitching, loop too big\n");
        fprintf (dump_file, ";; Not unswitching, loop too big\n");
      return false;
      return false;
    }
    }
 
 
  i = 0;
  i = 0;
  bbs = get_loop_body (loop);
  bbs = get_loop_body (loop);
  found = loop->num_nodes;
  found = loop->num_nodes;
 
 
  while (1)
  while (1)
    {
    {
      /* Find a bb to unswitch on.  */
      /* Find a bb to unswitch on.  */
      for (; i < loop->num_nodes; i++)
      for (; i < loop->num_nodes; i++)
        if ((cond = tree_may_unswitch_on (bbs[i], loop)))
        if ((cond = tree_may_unswitch_on (bbs[i], loop)))
          break;
          break;
 
 
      if (i == loop->num_nodes)
      if (i == loop->num_nodes)
        {
        {
          if (dump_file
          if (dump_file
              && num > PARAM_VALUE (PARAM_MAX_UNSWITCH_LEVEL)
              && num > PARAM_VALUE (PARAM_MAX_UNSWITCH_LEVEL)
              && (dump_flags & TDF_DETAILS))
              && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, ";; Not unswitching anymore, hit max level\n");
            fprintf (dump_file, ";; Not unswitching anymore, hit max level\n");
 
 
          if (found == loop->num_nodes)
          if (found == loop->num_nodes)
            {
            {
              free (bbs);
              free (bbs);
              return changed;
              return changed;
            }
            }
          break;
          break;
        }
        }
 
 
      cond = simplify_using_entry_checks (loop, cond);
      cond = simplify_using_entry_checks (loop, cond);
      stmt = last_stmt (bbs[i]);
      stmt = last_stmt (bbs[i]);
      if (integer_nonzerop (cond))
      if (integer_nonzerop (cond))
        {
        {
          /* Remove false path.  */
          /* Remove false path.  */
          gimple_cond_set_condition_from_tree (stmt, boolean_true_node);
          gimple_cond_set_condition_from_tree (stmt, boolean_true_node);
          changed = true;
          changed = true;
        }
        }
      else if (integer_zerop (cond))
      else if (integer_zerop (cond))
        {
        {
          /* Remove true path.  */
          /* Remove true path.  */
          gimple_cond_set_condition_from_tree (stmt, boolean_false_node);
          gimple_cond_set_condition_from_tree (stmt, boolean_false_node);
          changed = true;
          changed = true;
        }
        }
      /* Do not unswitch too much.  */
      /* Do not unswitch too much.  */
      else if (num > PARAM_VALUE (PARAM_MAX_UNSWITCH_LEVEL))
      else if (num > PARAM_VALUE (PARAM_MAX_UNSWITCH_LEVEL))
        {
        {
          i++;
          i++;
          continue;
          continue;
        }
        }
      /* In nested tree_unswitch_single_loop first optimize all conditions
      /* In nested tree_unswitch_single_loop first optimize all conditions
         using entry checks, then discover still reachable blocks in the
         using entry checks, then discover still reachable blocks in the
         loop and find the condition only among those still reachable bbs.  */
         loop and find the condition only among those still reachable bbs.  */
      else if (num != 0)
      else if (num != 0)
        {
        {
          if (found == loop->num_nodes)
          if (found == loop->num_nodes)
            found = i;
            found = i;
          i++;
          i++;
          continue;
          continue;
        }
        }
      else
      else
        {
        {
          found = i;
          found = i;
          break;
          break;
        }
        }
 
 
      update_stmt (stmt);
      update_stmt (stmt);
      i++;
      i++;
    }
    }
 
 
  if (num != 0)
  if (num != 0)
    {
    {
      basic_block *tos, *worklist;
      basic_block *tos, *worklist;
 
 
      /* When called recursively, first do a quick discovery
      /* When called recursively, first do a quick discovery
         of reachable bbs after the above changes and only
         of reachable bbs after the above changes and only
         consider conditions in still reachable bbs.  */
         consider conditions in still reachable bbs.  */
      tos = worklist = XNEWVEC (basic_block, loop->num_nodes);
      tos = worklist = XNEWVEC (basic_block, loop->num_nodes);
 
 
      for (i = 0; i < loop->num_nodes; i++)
      for (i = 0; i < loop->num_nodes; i++)
        bbs[i]->flags &= ~BB_REACHABLE;
        bbs[i]->flags &= ~BB_REACHABLE;
 
 
      /* Start with marking header.  */
      /* Start with marking header.  */
      *tos++ = bbs[0];
      *tos++ = bbs[0];
      bbs[0]->flags |= BB_REACHABLE;
      bbs[0]->flags |= BB_REACHABLE;
 
 
      /* Iterate: find everything reachable from what we've already seen
      /* Iterate: find everything reachable from what we've already seen
         within the same innermost loop.  Don't look through false edges
         within the same innermost loop.  Don't look through false edges
         if condition is always true or true edges if condition is
         if condition is always true or true edges if condition is
         always false.  */
         always false.  */
      while (tos != worklist)
      while (tos != worklist)
        {
        {
          basic_block b = *--tos;
          basic_block b = *--tos;
          edge e;
          edge e;
          edge_iterator ei;
          edge_iterator ei;
          int flags = 0;
          int flags = 0;
 
 
          if (EDGE_COUNT (b->succs) == 2)
          if (EDGE_COUNT (b->succs) == 2)
            {
            {
              gimple stmt = last_stmt (b);
              gimple stmt = last_stmt (b);
              if (stmt
              if (stmt
                  && gimple_code (stmt) == GIMPLE_COND)
                  && gimple_code (stmt) == GIMPLE_COND)
                {
                {
                  if (gimple_cond_true_p (stmt))
                  if (gimple_cond_true_p (stmt))
                    flags = EDGE_FALSE_VALUE;
                    flags = EDGE_FALSE_VALUE;
                  else if (gimple_cond_false_p (stmt))
                  else if (gimple_cond_false_p (stmt))
                    flags = EDGE_TRUE_VALUE;
                    flags = EDGE_TRUE_VALUE;
                }
                }
            }
            }
 
 
          FOR_EACH_EDGE (e, ei, b->succs)
          FOR_EACH_EDGE (e, ei, b->succs)
            {
            {
              basic_block dest = e->dest;
              basic_block dest = e->dest;
 
 
              if (dest->loop_father == loop
              if (dest->loop_father == loop
                  && !(dest->flags & BB_REACHABLE)
                  && !(dest->flags & BB_REACHABLE)
                  && !(e->flags & flags))
                  && !(e->flags & flags))
                {
                {
                  *tos++ = dest;
                  *tos++ = dest;
                  dest->flags |= BB_REACHABLE;
                  dest->flags |= BB_REACHABLE;
                }
                }
            }
            }
        }
        }
 
 
      free (worklist);
      free (worklist);
 
 
      /* Find a bb to unswitch on.  */
      /* Find a bb to unswitch on.  */
      for (; found < loop->num_nodes; found++)
      for (; found < loop->num_nodes; found++)
        if ((bbs[found]->flags & BB_REACHABLE)
        if ((bbs[found]->flags & BB_REACHABLE)
            && (cond = tree_may_unswitch_on (bbs[found], loop)))
            && (cond = tree_may_unswitch_on (bbs[found], loop)))
          break;
          break;
 
 
      if (found == loop->num_nodes)
      if (found == loop->num_nodes)
        {
        {
          free (bbs);
          free (bbs);
          return changed;
          return changed;
        }
        }
    }
    }
 
 
  if (dump_file && (dump_flags & TDF_DETAILS))
  if (dump_file && (dump_flags & TDF_DETAILS))
    fprintf (dump_file, ";; Unswitching loop\n");
    fprintf (dump_file, ";; Unswitching loop\n");
 
 
  initialize_original_copy_tables ();
  initialize_original_copy_tables ();
  /* Unswitch the loop on this condition.  */
  /* Unswitch the loop on this condition.  */
  nloop = tree_unswitch_loop (loop, bbs[found], cond);
  nloop = tree_unswitch_loop (loop, bbs[found], cond);
  if (!nloop)
  if (!nloop)
    {
    {
      free_original_copy_tables ();
      free_original_copy_tables ();
      free (bbs);
      free (bbs);
      return changed;
      return changed;
    }
    }
 
 
  /* Update the SSA form after unswitching.  */
  /* Update the SSA form after unswitching.  */
  update_ssa (TODO_update_ssa);
  update_ssa (TODO_update_ssa);
  free_original_copy_tables ();
  free_original_copy_tables ();
 
 
  /* Invoke itself on modified loops.  */
  /* Invoke itself on modified loops.  */
  tree_unswitch_single_loop (nloop, num + 1);
  tree_unswitch_single_loop (nloop, num + 1);
  tree_unswitch_single_loop (loop, num + 1);
  tree_unswitch_single_loop (loop, num + 1);
  free (bbs);
  free (bbs);
  return true;
  return true;
}
}
 
 
/* Unswitch a LOOP w.r. to given basic block UNSWITCH_ON.  We only support
/* Unswitch a LOOP w.r. to given basic block UNSWITCH_ON.  We only support
   unswitching of innermost loops.  COND is the condition determining which
   unswitching of innermost loops.  COND is the condition determining which
   loop is entered -- the new loop is entered if COND is true.  Returns NULL
   loop is entered -- the new loop is entered if COND is true.  Returns NULL
   if impossible, new loop otherwise.  */
   if impossible, new loop otherwise.  */
 
 
static struct loop *
static struct loop *
tree_unswitch_loop (struct loop *loop,
tree_unswitch_loop (struct loop *loop,
                    basic_block unswitch_on, tree cond)
                    basic_block unswitch_on, tree cond)
{
{
  unsigned prob_true;
  unsigned prob_true;
  edge edge_true, edge_false;
  edge edge_true, edge_false;
 
 
  /* Some sanity checking.  */
  /* Some sanity checking.  */
  gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
  gcc_assert (flow_bb_inside_loop_p (loop, unswitch_on));
  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
  gcc_assert (EDGE_COUNT (unswitch_on->succs) == 2);
  gcc_assert (loop->inner == NULL);
  gcc_assert (loop->inner == NULL);
 
 
  extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);
  extract_true_false_edges_from_block (unswitch_on, &edge_true, &edge_false);
  prob_true = edge_true->probability;
  prob_true = edge_true->probability;
  return loop_version (loop, unshare_expr (cond),
  return loop_version (loop, unshare_expr (cond),
                       NULL, prob_true, prob_true,
                       NULL, prob_true, prob_true,
                       REG_BR_PROB_BASE - prob_true, false);
                       REG_BR_PROB_BASE - prob_true, false);
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.