OSDN Git Service

2011-07-01 Jonathan Wakely <jwakely.gcc@gmail.com>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-threadupdate.c
index e6fa4f6..0cdf846 100644 (file)
@@ -1,5 +1,6 @@
 /* Thread edges through blocks and update the control flow and SSA graphs.
-   Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 201
+   Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -23,14 +24,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "tree.h"
 #include "flags.h"
-#include "rtl.h"
 #include "tm_p.h"
-#include "ggc.h"
 #include "basic-block.h"
 #include "output.h"
-#include "expr.h"
 #include "function.h"
-#include "diagnostic.h"
 #include "tree-flow.h"
 #include "tree-dump.h"
 #include "tree-pass.h"
@@ -71,10 +68,17 @@ along with GCC; see the file COPYING3.  If not see
      7. Put the duplicated resources in B and all the B' blocks into SSA form.
 
    Note that block duplication can be minimized by first collecting the
-   the set of unique destination blocks that the incoming edges should
-   be threaded to.  Block duplication can be further minimized by using
-   B instead of creating B' for one destination if all edges into B are
-   going to be threaded to a successor of B.
+   set of unique destination blocks that the incoming edges should
+   be threaded to.
+
+   Block duplication can be further minimized by using B instead of 
+   creating B' for one destination if all edges into B are going to be
+   threaded to a successor of B.  We had code to do this at one time, but
+   I'm not convinced it is correct with the changes to avoid mucking up
+   the loop structure (which may cancel threading requests, thus a block
+   which we thought was going to become unreachable may still be reachable).
+   This code was also going to get ugly with the introduction of the ability
+   for a single jump thread request to bypass multiple blocks. 
 
    We further reduce the number of edges and statements we create by
    not copying all the outgoing edges and the control statement in
@@ -117,14 +121,11 @@ struct redirection_data
      its single successor.  */
   edge outgoing_edge;
 
+  edge intermediate_edge;
+
   /* A list of incoming edges which we want to thread to
      OUTGOING_EDGE->dest.  */
   struct el *incoming_edges;
-
-  /* Flag indicating whether or not we should create a duplicate block
-     for this thread destination.  This is only true if we are threading
-     all incoming edges and thus are using BB itself as a duplicate block.  */
-  bool do_not_duplicate;
 };
 
 /* Main data structure to hold information for duplicates of BB.  */
@@ -150,6 +151,11 @@ struct local_info
    (original_edge, target_edge).  */
 static VEC(edge,heap) *threaded_edges;
 
+/* When we start updating the CFG for threading, data necessary for jump
+   threading is attached to the AUX field for the incoming edge.  Use these
+   macros to access the underlying structure attached to the AUX field.  */
+#define THREAD_TARGET(E) ((edge *)(E)->aux)[0]
+#define THREAD_TARGET2(E) ((edge *)(E)->aux)[1]
 
 /* Jump threading statistics.  */
 
@@ -168,23 +174,23 @@ struct thread_stats_d thread_stats;
 static void
 remove_ctrl_stmt_and_useless_edges (basic_block bb, basic_block dest_bb)
 {
-  block_stmt_iterator bsi;
+  gimple_stmt_iterator gsi;
   edge e;
   edge_iterator ei;
 
-  bsi = bsi_last (bb);
+  gsi = gsi_last_bb (bb);
 
   /* If the duplicate ends with a control statement, then remove it.
 
      Note that if we are duplicating the template block rather than the
      original basic block, then the duplicate might not have any real
      statements in it.  */
-  if (!bsi_end_p (bsi)
-      && bsi_stmt (bsi)
-      && (TREE_CODE (bsi_stmt (bsi)) == COND_EXPR
-         || TREE_CODE (bsi_stmt (bsi)) == GOTO_EXPR
-         || TREE_CODE (bsi_stmt (bsi)) == SWITCH_EXPR))
-    bsi_remove (&bsi, true);
+  if (!gsi_end_p (gsi)
+      && gsi_stmt (gsi)
+      && (gimple_code (gsi_stmt (gsi)) == GIMPLE_COND
+         || gimple_code (gsi_stmt (gsi)) == GIMPLE_GOTO
+         || gimple_code (gsi_stmt (gsi)) == GIMPLE_SWITCH))
+    gsi_remove (&gsi, true);
 
   for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
     {
@@ -195,27 +201,24 @@ remove_ctrl_stmt_and_useless_edges (basic_block bb, basic_block dest_bb)
     }
 }
 
-/* Create a duplicate of BB which only reaches the destination of the edge
-   stored in RD.  Record the duplicate block in RD.  */
+/* Create a duplicate of BB.  Record the duplicate block in RD.  */
 
 static void
 create_block_for_threading (basic_block bb, struct redirection_data *rd)
 {
+  edge_iterator ei;
+  edge e;
+
   /* We can use the generic block duplication code and simply remove
      the stuff we do not need.  */
   rd->dup_block = duplicate_block (bb, NULL, NULL);
 
+  FOR_EACH_EDGE (e, ei, rd->dup_block->succs)
+    e->aux = NULL;
+
   /* Zero out the profile, since the block is unreachable for now.  */
   rd->dup_block->frequency = 0;
   rd->dup_block->count = 0;
-
-  /* The call to duplicate_block will copy everything, including the
-     useless COND_EXPR or SWITCH_EXPR at the end of BB.  We just remove
-     the useless COND_EXPR or SWITCH_EXPR here rather than having a
-     specialized block copier.  We also remove all outgoing edges
-     from the duplicate block.  The appropriate edge will be created
-     later.  */
-  remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL);
 }
 
 /* Hashing and equality routines for our hash table.  */
@@ -231,8 +234,10 @@ redirection_data_eq (const void *p1, const void *p2)
 {
   edge e1 = ((const struct redirection_data *)p1)->outgoing_edge;
   edge e2 = ((const struct redirection_data *)p2)->outgoing_edge;
+  edge e3 = ((const struct redirection_data *)p1)->intermediate_edge;
+  edge e4 = ((const struct redirection_data *)p2)->intermediate_edge;
 
-  return e1 == e2;
+  return e1 == e2 && e3 == e4;
 }
 
 /* Given an outgoing edge E lookup and return its entry in our hash table.
@@ -242,7 +247,7 @@ redirection_data_eq (const void *p1, const void *p2)
    edges associated with E in the hash table.  */
 
 static struct redirection_data *
-lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
+lookup_redirection_data (edge e, enum insert_option insert)
 {
   void **slot;
   struct redirection_data *elt;
@@ -250,9 +255,10 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
  /* Build a hash table element so we can see if E is already
      in the table.  */
   elt = XNEW (struct redirection_data);
-  elt->outgoing_edge = e;
+  elt->intermediate_edge = THREAD_TARGET2 (e) ? THREAD_TARGET (e) : NULL;
+  elt->outgoing_edge = THREAD_TARGET2 (e) ? THREAD_TARGET2 (e) 
+                                         : THREAD_TARGET (e);
   elt->dup_block = NULL;
-  elt->do_not_duplicate = false;
   elt->incoming_edges = NULL;
 
   slot = htab_find_slot (redirection_data, elt, insert);
@@ -271,7 +277,7 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
     {
       *slot = (void *)elt;
       elt->incoming_edges = XNEW (struct el);
-      elt->incoming_edges->e = incoming_edge;
+      elt->incoming_edges->e = e;
       elt->incoming_edges->next = NULL;
       return elt;
     }
@@ -291,7 +297,7 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
        {
           struct el *el = XNEW (struct el);
          el->next = elt->incoming_edges;
-         el->e = incoming_edge;
+         el->e = e;
          elt->incoming_edges = el;
        }
 
@@ -299,6 +305,41 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
     }
 }
 
+/* For each PHI in BB, copy the argument associated with SRC_E to TGT_E.  */
+
+static void
+copy_phi_args (basic_block bb, edge src_e, edge tgt_e)
+{
+  gimple_stmt_iterator gsi;
+  int src_indx = src_e->dest_idx;
+
+  for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple phi = gsi_stmt (gsi);
+      source_location locus = gimple_phi_arg_location (phi, src_indx);
+      add_phi_arg (phi, gimple_phi_arg_def (phi, src_indx), tgt_e, locus);
+    }
+}
+
+/* We have recently made a copy of ORIG_BB, including its outgoing
+   edges.  The copy is NEW_BB.  Every PHI node in every direct successor of
+   ORIG_BB has a new argument associated with edge from NEW_BB to the
+   successor.  Initialize the PHI argument so that it is equal to the PHI
+   argument associated with the edge from ORIG_BB to the successor.  */
+
+static void
+update_destination_phis (basic_block orig_bb, basic_block new_bb)
+{
+  edge_iterator ei;
+  edge e;
+
+  FOR_EACH_EDGE (e, ei, orig_bb->succs)
+    {
+      edge e2 = find_edge (new_bb, e->dest);
+      copy_phi_args (e->dest, e, e2);
+    }
+}
+
 /* Given a duplicate block and its single destination (both stored
    in RD).  Create an edge between the duplicate and its single
    destination.
@@ -307,27 +348,70 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert)
    destination.  */
 
 static void
-create_edge_and_update_destination_phis (struct redirection_data *rd)
+create_edge_and_update_destination_phis (struct redirection_data *rd,
+                                        basic_block bb)
 {
-  edge e = make_edge (rd->dup_block, rd->outgoing_edge->dest, EDGE_FALLTHRU);
-  tree phi;
+  edge e = make_edge (bb, rd->outgoing_edge->dest, EDGE_FALLTHRU);
 
   rescan_loop_exit (e, true, false);
   e->probability = REG_BR_PROB_BASE;
-  e->count = rd->dup_block->count;
-  e->aux = rd->outgoing_edge->aux;
+  e->count = bb->count;
+
+  if (rd->outgoing_edge->aux)
+    {
+      e->aux = (edge *) XNEWVEC (edge, 2);
+      THREAD_TARGET(e) = THREAD_TARGET (rd->outgoing_edge);
+      THREAD_TARGET2(e) = THREAD_TARGET2 (rd->outgoing_edge);
+    }
+  else
+    {
+      e->aux = NULL;
+    }
 
   /* If there are any PHI nodes at the destination of the outgoing edge
      from the duplicate block, then we will need to add a new argument
      to them.  The argument should have the same value as the argument
      associated with the outgoing edge stored in RD.  */
-  for (phi = phi_nodes (e->dest); phi; phi = PHI_CHAIN (phi))
+  copy_phi_args (e->dest, rd->outgoing_edge, e);
+}
+
+/* Wire up the outgoing edges from the duplicate block and
+   update any PHIs as needed.  */
+static void
+fix_duplicate_block_edges (struct redirection_data *rd,
+                          struct local_info *local_info)
+{
+  /* If we were threading through an joiner block, then we want
+     to keep its control statement and redirect an outgoing edge.
+     Else we want to remove the control statement & edges, then create
+     a new outgoing edge.  In both cases we may need to update PHIs.  */
+  if (THREAD_TARGET2 (rd->incoming_edges->e))
     {
-      int indx = rd->outgoing_edge->dest_idx;
-      add_phi_arg (phi, PHI_ARG_DEF (phi, indx), e);
+      edge victim;
+      edge e2;
+      edge e = rd->incoming_edges->e;
+
+      /* This updates the PHIs at the destination of the duplicate
+        block.  */
+      update_destination_phis (local_info->bb, rd->dup_block);
+
+      /* Find the edge from the duplicate block to the block we're
+        threading through.  That's the edge we want to redirect.  */
+      victim = find_edge (rd->dup_block, THREAD_TARGET (e)->dest);
+      e2 = redirect_edge_and_branch (victim, THREAD_TARGET2 (e)->dest);
+
+      /* If we redirected the edge, then we need to copy PHI arguments
+        at the target.  If the edge already existed (e2 != victim case),
+        then the PHIs in the target already have the correct arguments.  */
+      if (e2 == victim)
+       copy_phi_args (e2->dest, THREAD_TARGET2 (e), e2);
+    }
+  else
+    {
+      remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL);
+      create_edge_and_update_destination_phis (rd, rd->dup_block);
     }
 }
-
 /* Hash table traversal callback routine to create duplicate blocks.  */
 
 static int
@@ -336,11 +420,6 @@ create_duplicates (void **slot, void *data)
   struct redirection_data *rd = (struct redirection_data *) *slot;
   struct local_info *local_info = (struct local_info *)data;
 
-  /* If this entry should not have a duplicate created, then there's
-     nothing to do.  */
-  if (rd->do_not_duplicate)
-    return 1;
-
   /* Create a template block if we have not done so already.  Otherwise
      use the template to create a new block.  */
   if (local_info->template_block == NULL)
@@ -357,8 +436,8 @@ create_duplicates (void **slot, void *data)
       create_block_for_threading (local_info->template_block, rd);
 
       /* Go ahead and wire up outgoing edges and update PHIs for the duplicate
-         block.  */
-      create_edge_and_update_destination_phis (rd);
+        block.   */
+      fix_duplicate_block_edges (rd, local_info);
     }
 
   /* Keep walking the hash table.  */
@@ -375,11 +454,16 @@ fixup_template_block (void **slot, void *data)
   struct redirection_data *rd = (struct redirection_data *) *slot;
   struct local_info *local_info = (struct local_info *)data;
 
-  /* If this is the template block, then create its outgoing edges
-     and halt the hash table traversal.  */
+  /* If this is the template block halt the traversal after updating
+     it appropriately.
+
+     If we were threading through an joiner block, then we want
+     to keep its control statement and redirect an outgoing edge.
+     Else we want to remove the control statement & edges, then create
+     a new outgoing edge.  In both cases we may need to update PHIs.  */
   if (rd->dup_block && rd->dup_block == local_info->template_block)
     {
-      create_edge_and_update_destination_phis (rd);
+      fix_duplicate_block_edges (rd, local_info);
       return 0;
     }
 
@@ -408,13 +492,19 @@ redirect_edges (void **slot, void *data)
       next = el->next;
       free (el);
 
-      /* Go ahead and clear E->aux.  It's not needed anymore and failure
-         to clear it will cause all kinds of unpleasant problems later.  */
-      e->aux = NULL;
-
       thread_stats.num_threaded_edges++;
+      /* If we are threading through a joiner block, then we have to
+        find the edge we want to redirect and update some PHI nodes.  */
+      if (THREAD_TARGET2 (e))
+       {
+         edge e2;
 
-      if (rd->dup_block)
+         /* We want to redirect the incoming edge to the joiner block (E)
+            to instead reach the duplicate of the joiner block.  */
+         e2 = redirect_edge_and_branch (e, rd->dup_block);
+         flush_pending_stmts (e2);
+       }
+      else if (rd->dup_block)
        {
          edge e2;
 
@@ -431,22 +521,12 @@ redirect_edges (void **slot, void *data)
          gcc_assert (e == e2);
          flush_pending_stmts (e2);
        }
-      else
-       {
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "  Threaded jump %d --> %d to %d\n",
-                    e->src->index, e->dest->index, local_info->bb->index);
 
-         /* We are using BB as the duplicate.  Remove the unnecessary
-            outgoing edges and statements from BB.  */
-         remove_ctrl_stmt_and_useless_edges (local_info->bb,
-                                             rd->outgoing_edge->dest);
+      /* Go ahead and clear E->aux.  It's not needed anymore and failure
+         to clear it will cause all kinds of unpleasant problems later.  */
+      free (e->aux);
+      e->aux = NULL;
 
-         /* And fixup the flags on the single remaining edge.  */
-         single_succ_edge (local_info->bb)->flags
-           &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE | EDGE_ABNORMAL);
-         single_succ_edge (local_info->bb)->flags |= EDGE_FALLTHRU;
-       }
     }
 
   /* Indicate that we actually threaded one or more jumps.  */
@@ -463,24 +543,25 @@ redirect_edges (void **slot, void *data)
 static bool
 redirection_block_p (basic_block bb)
 {
-  block_stmt_iterator bsi;
+  gimple_stmt_iterator gsi;
 
   /* Advance to the first executable statement.  */
-  bsi = bsi_start (bb);
-  while (!bsi_end_p (bsi)
-          && (TREE_CODE (bsi_stmt (bsi)) == LABEL_EXPR
-              || IS_EMPTY_STMT (bsi_stmt (bsi))))
-    bsi_next (&bsi);
+  gsi = gsi_start_bb (bb);
+  while (!gsi_end_p (gsi)
+         && (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
+            || is_gimple_debug (gsi_stmt (gsi))
+             || gimple_nop_p (gsi_stmt (gsi))))
+    gsi_next (&gsi);
 
   /* Check if this is an empty block.  */
-  if (bsi_end_p (bsi))
+  if (gsi_end_p (gsi))
     return true;
 
   /* Test that we've reached the terminating control statement.  */
-  return bsi_stmt (bsi)
-        && (TREE_CODE (bsi_stmt (bsi)) == COND_EXPR
-            || TREE_CODE (bsi_stmt (bsi)) == GOTO_EXPR
-            || TREE_CODE (bsi_stmt (bsi)) == SWITCH_EXPR);
+  return gsi_stmt (gsi)
+         && (gimple_code (gsi_stmt (gsi)) == GIMPLE_COND
+             || gimple_code (gsi_stmt (gsi)) == GIMPLE_GOTO
+             || gimple_code (gsi_stmt (gsi)) == GIMPLE_SWITCH);
 }
 
 /* BB is a block which ends with a COND_EXPR or SWITCH_EXPR and when BB
@@ -514,10 +595,6 @@ thread_block (basic_block bb, bool noloop_only)
   struct local_info local_info;
   struct loop *loop = bb->loop_father;
 
-  /* ALL indicates whether or not all incoming edges into BB should
-     be threaded to a duplicate of BB.  */
-  bool all = true;
-
   /* To avoid scanning a linear array for the element we need we instead
      use a hash table.  For normal code there should be no noticeable
      difference.  However, if we have a block with a large number of
@@ -533,7 +610,11 @@ thread_block (basic_block bb, bool noloop_only)
   if (loop->header == bb)
     {
       e = loop_latch_edge (loop);
-      e2 = (edge) e->aux;
+
+      if (e->aux)
+       e2 = THREAD_TARGET (e);
+      else
+       e2 = NULL;
 
       if (e2 && loop_exit_edge_p (loop, e2))
        {
@@ -546,35 +627,30 @@ thread_block (basic_block bb, bool noloop_only)
      efficient lookups.  */
   FOR_EACH_EDGE (e, ei, bb->preds)
     {
-      e2 = (edge) e->aux;
+      if (e->aux == NULL)
+       continue;
+
+      if (THREAD_TARGET2 (e))
+       e2 = THREAD_TARGET2 (e);
+      else
+       e2 = THREAD_TARGET (e);
 
       if (!e2
          /* If NOLOOP_ONLY is true, we only allow threading through the
             header of a loop to exit edges.  */
          || (noloop_only
              && bb == bb->loop_father->header
-             && !loop_exit_edge_p (bb->loop_father, e2)))
-       {
-         all = false;
-         continue;
-       }
+             && (!loop_exit_edge_p (bb->loop_father, e2)
+                 || THREAD_TARGET2 (e))))
+       continue;
 
-      update_bb_profile_for_threading (e->dest, EDGE_FREQUENCY (e),
-                                      e->count, (edge) e->aux);
+      if (e->dest == e2->src)
+       update_bb_profile_for_threading (e->dest, EDGE_FREQUENCY (e),
+                                        e->count, THREAD_TARGET (e));
 
       /* Insert the outgoing edge into the hash table if it is not
         already in the hash table.  */
-      lookup_redirection_data (e2, e, INSERT);
-    }
-
-  /* If we are going to thread all incoming edges to an outgoing edge, then
-     BB will become unreachable.  Rather than just throwing it away, use
-     it for one of the duplicates.  Mark the first incoming edge with the
-     DO_NOT_DUPLICATE attribute.  */
-  if (all)
-    {
-      edge e = (edge) EDGE_PRED (bb, 0)->aux;
-      lookup_redirection_data (e, NULL, NO_INSERT)->do_not_duplicate = true;
+      lookup_redirection_data (e, INSERT);
     }
 
   /* We do not update dominance info.  */
@@ -615,18 +691,18 @@ thread_block (basic_block bb, bool noloop_only)
   return local_info.jumps_threaded;
 }
 
-/* Threads edge E through E->dest to the edge E->aux.  Returns the copy
-   of E->dest created during threading, or E->dest if it was not necessary
+/* Threads edge E through E->dest to the edge THREAD_TARGET (E).  Returns the
+   copy of E->dest created during threading, or E->dest if it was not necessary
    to copy it (E is its single predecessor).  */
 
 static basic_block
 thread_single_edge (edge e)
 {
   basic_block bb = e->dest;
-  edge eto = (edge) e->aux;
+  edge eto = THREAD_TARGET (e);
   struct redirection_data rd;
-  struct local_info local_info;
 
+  free (e->aux);
   e->aux = NULL;
 
   thread_stats.num_threaded_edges++;
@@ -645,13 +721,14 @@ thread_single_edge (edge e)
     }
 
   /* Otherwise, we need to create a copy.  */
-  update_bb_profile_for_threading (bb, EDGE_FREQUENCY (e), e->count, eto);
+  if (e->dest == eto->src)
+    update_bb_profile_for_threading (bb, EDGE_FREQUENCY (e), e->count, eto);
 
-  local_info.bb = bb;
   rd.outgoing_edge = eto;
 
   create_block_for_threading (bb, &rd);
-  create_edge_and_update_destination_phis (&rd);
+  remove_ctrl_stmt_and_useless_edges (rd.dup_block, NULL);
+  create_edge_and_update_destination_phis (&rd, rd.dup_block);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file, "  Threaded jump %d --> %d to %d\n",
@@ -671,9 +748,9 @@ thread_single_edge (edge e)
 
 static basic_block dbds_ce_stop;
 static bool
-dbds_continue_enumeration_p (basic_block bb, void *stop)
+dbds_continue_enumeration_p (const_basic_block bb, const void *stop)
 {
-  return (bb != (basic_block) stop
+  return (bb != (const_basic_block) stop
          && bb != dbds_ce_stop);
 }
 
@@ -699,8 +776,9 @@ determine_bb_domination_status (struct loop *loop, basic_block bb)
   edge_iterator ei;
   edge e;
 
-#ifdef ENABLE_CHECKING
-  /* This function assumes BB is a successor of LOOP->header.  */
+  /* This function assumes BB is a successor of LOOP->header.
+     If that is not the case return DOMST_NONDOMINATING which
+     is always safe.  */
     {
       bool ok = false;
 
@@ -713,9 +791,9 @@ determine_bb_domination_status (struct loop *loop, basic_block bb)
            }
        }
 
-      gcc_assert (ok);
+      if (!ok)
+       return DOMST_NONDOMINATING;
     }
-#endif
 
   if (bb == loop->latch)
     return DOMST_DOMINATING;
@@ -827,7 +905,9 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers)
 
   if (latch->aux)
     {
-      tgt_edge = (edge) latch->aux;
+      if (THREAD_TARGET2 (latch))
+       goto fail;
+      tgt_edge = THREAD_TARGET (latch);
       tgt_bb = tgt_edge->dest;
     }
   else if (!may_peel_loop_headers
@@ -850,7 +930,9 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers)
              goto fail;
            }
 
-         tgt_edge = (edge) e->aux;
+         if (THREAD_TARGET2 (e))
+           goto fail;
+         tgt_edge = THREAD_TARGET (e);
          atgt_bb = tgt_edge->dest;
          if (!tgt_bb)
            tgt_bb = atgt_bb;
@@ -899,7 +981,7 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers)
       else
        tgt_bb = split_edge (tgt_edge);
     }
-      
+
   if (latch->aux)
     {
       /* First handle the case latch edge is redirected.  */
@@ -916,7 +998,7 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers)
 
       /* Now consider the case entry edges are redirected to the new entry
         block.  Remember one entry edge, so that we can find the new
-       preheader (its destination after threading).  */
+        preheader (its destination after threading).  */
       FOR_EACH_EDGE (e, ei, header->preds)
        {
          if (e->aux)
@@ -941,13 +1023,14 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers)
       loop->header = latch->dest;
       loop->latch = latch->src;
     }
-  
+
   return true;
 
 fail:
   /* We failed to thread anything.  Cancel the requests.  */
   FOR_EACH_EDGE (e, ei, header->preds)
     {
+      free (e->aux);
       e->aux = NULL;
     }
   return false;
@@ -976,18 +1059,20 @@ mark_threaded_blocks (bitmap threaded_blocks)
   edge e;
   edge_iterator ei;
 
-  for (i = 0; i < VEC_length (edge, threaded_edges); i += 2)
+  for (i = 0; i < VEC_length (edge, threaded_edges); i += 3)
     {
       edge e = VEC_index (edge, threaded_edges, i);
-      edge e2 = VEC_index (edge, threaded_edges, i + 1);
+      edge *x = (edge *) XNEWVEC (edge, 2);
 
-      e->aux = e2;
+      e->aux = x;
+      THREAD_TARGET (e) = VEC_index (edge, threaded_edges, i + 1);
+      THREAD_TARGET2 (e) = VEC_index (edge, threaded_edges, i + 2);
       bitmap_set_bit (tmp, e->dest->index);
     }
 
   /* If optimizing for size, only thread through block if we don't have
      to duplicate it or it's an otherwise empty redirection block.  */
-  if (optimize_size)
+  if (optimize_function_for_size_p (cfun))
     {
       EXECUTE_IF_SET_IN_BITMAP (tmp, 0, i, bi)
        {
@@ -996,7 +1081,10 @@ mark_threaded_blocks (bitmap threaded_blocks)
              && !redirection_block_p (bb))
            {
              FOR_EACH_EDGE (e, ei, bb->preds)
-                     e->aux = NULL;
+               {
+                 free (e->aux);
+                 e->aux = NULL;
+               }
            }
          else
            bitmap_set_bit (threaded_blocks, i);
@@ -1065,9 +1153,8 @@ thread_through_all_blocks (bool may_peel_loop_headers)
       retval |= thread_through_loop_header (loop, may_peel_loop_headers);
     }
 
-  if (dump_file && (dump_flags & TDF_STATS))
-    fprintf (dump_file, "\nJumps threaded: %lu\n",
-            thread_stats.num_threaded_edges);
+  statistics_counter_event (cfun, "Jumps threaded",
+                           thread_stats.num_threaded_edges);
 
   free_original_copy_tables ();
 
@@ -1086,16 +1173,27 @@ thread_through_all_blocks (bool may_peel_loop_headers)
    threading opportunities discovered by a pass and update the CFG
    and SSA form all at once.
 
-   E is the edge we can thread, E2 is the new target edge.  ie, we
+   E is the edge we can thread, E2 is the new target edge, i.e., we
    are effectively recording that E->dest can be changed to E2->dest
    after fixing the SSA graph.  */
 
 void
-register_jump_thread (edge e, edge e2)
+register_jump_thread (edge e, edge e2, edge e3)
 {
+  /* This can occur if we're jumping to a constant address or
+     or something similar.  Just get out now.  */
+  if (e2 == NULL)
+    return;
+
   if (threaded_edges == NULL)
-    threaded_edges = VEC_alloc (edge, heap, 10);
+    threaded_edges = VEC_alloc (edge, heap, 15);
+
+  if (dump_file && (dump_flags & TDF_DETAILS)
+      && e->dest != e2->src)
+    fprintf (dump_file,
+            "  Registering jump thread around one or more intermediate blocks\n");
 
   VEC_safe_push (edge, heap, threaded_edges, e);
   VEC_safe_push (edge, heap, threaded_edges, e2);
+  VEC_safe_push (edge, heap, threaded_edges, e3);
 }