cgraphbuild.c (cgraph_rebuild_references): Rebuild only non-speculative refs.
authorJan Hubicka <jh@suse.cz>
Fri, 9 Aug 2013 15:23:19 +0000 (17:23 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Fri, 9 Aug 2013 15:23:19 +0000 (15:23 +0000)
* cgraphbuild.c (cgraph_rebuild_references): Rebuild only non-speculative
refs.
* cgraph.c (cgraph_update_edge_in_call_site_hash): New function.
(cgraph_add_edge_to_call_site_hash): Deal with speculative calls.
(cgraph_set_call_stmt): Likewise.
(cgraph_create_edge_1): Fix release checking compilatoin;
clear lto_stmt_uid.
(cgraph_free_edge): Free indirect info.
(cgraph_turn_edge_to_speculative): New function.
(cgraph_speculative_call_info): New function.
(cgraph_make_edge_direct): Return direct edge; handle speculation.
(cgraph_redirect_edge_call_stmt_to_callee): Expand speculative
edges.
(dump_cgraph_node): Dump speculation.
(verify_edge_count_and_frequency): Accept speculative edges.
(verify_edge_corresponds_to_fndecl): Handle partitioned cgraph.
(verify_cgraph_node): Handle speculation.
* cgraph.h (cgraph_edge): Add SPECULATIVE flag.
(cgraph_set_call_stmt): Update prototype.
(cgraph_make_edge_direct): Update prototype.
(cgraph_speculative_call_info): Declare.
* ipa-cp.c (ipcp_discover_new_direct_edges): Be ready for edge
to change; update call of ipa_find_references.
* ipa-ref.c (ipa_record_reference): Fix return value; clear
lto_stmt_uid and speculative flags.
(ipa_dump_references): Dump speculation.
(ipa_clone_references): Clone speculative flag.
(ipa_clone_referring): Likewise.
(ipa_clone_ref): New function.
(ipa_find_reference): Look into lto_stmt_uids
(ipa_clear_stmts_in_references): Do not clear speculative calls.
* ipa-ref.h (ipa_ref): Add lto_stmt_uid and speculative flags.
(ipa_find_reference): Update declaration.
(ipa_clone_ref): Declare.
* lto-cgraph.c (lto_output_edge): Make lto_stmt_uids start from 0;
stream speculative flag.
(lto_output_ref): Stream statements uids and speculation.
(input_ref): Likewise.
(input_edge): Stream speuclation.
* cgraphclones.c (cgraph_clone_edge): Clone speculation.
(cgraph_set_call_stmt_including_clones): Handle speculation.
* ipa-inline.c (heap_edge_removal_hook): New function.
(inline_small_functions): Register it.
* lto-streamer-in.c (fixup_call_stmt_edges_1): Bounds checking;
also initialize refs.
* ipa-prop.c (ipa_make_edge_direct_to_target): Be ready for
edge to change.
(try_make_edge_direct_simple_call): Likewise.
(try_make_edge_direct_simple_call): Likewise.
(update_indirect_edges_after_inlining): Likewise.
(remove_described_reference): Look proper lto_stmt_uid.
(propagate_controlled_uses): Likewise.
(propagate_controlled_uses): Liekwise.
* tree-inline.c (copy_bb): Copy speculative edges.
(redirect_all_calls): New function.
(copy_cfg_body): Do redirection after loop info
is updated.
(delete_unreachable_blocks_update_callgraph): Updadte
speculation.

From-SVN: r201632

15 files changed:
gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphbuild.c
gcc/cgraphclones.c
gcc/ipa-cp.c
gcc/ipa-inline.c
gcc/ipa-prop.c
gcc/ipa-ref.c
gcc/ipa-ref.h
gcc/lto-cgraph.c
gcc/lto-streamer-in.c
gcc/tree-inline.c
gcc/value-prof.c
gcc/value-prof.h

index ef8197630c411cd5de2a6a17dfbea307801e27f7..0207b565fa5a29c9fbb47871c4e2e84fb5105b11 100644 (file)
@@ -1,3 +1,65 @@
+2013-08-09  Jan Hubicka  <jh@suse.cz>
+
+       * cgraphbuild.c (cgraph_rebuild_references): Rebuild only non-speculative
+       refs.
+       * cgraph.c (cgraph_update_edge_in_call_site_hash): New function.
+       (cgraph_add_edge_to_call_site_hash): Deal with speculative calls.
+       (cgraph_set_call_stmt): Likewise.
+       (cgraph_create_edge_1): Fix release checking compilatoin;
+       clear lto_stmt_uid.
+       (cgraph_free_edge): Free indirect info.
+       (cgraph_turn_edge_to_speculative): New function.
+       (cgraph_speculative_call_info): New function.
+       (cgraph_make_edge_direct): Return direct edge; handle speculation.
+       (cgraph_redirect_edge_call_stmt_to_callee): Expand speculative
+       edges.
+       (dump_cgraph_node): Dump speculation.
+       (verify_edge_count_and_frequency): Accept speculative edges.
+       (verify_edge_corresponds_to_fndecl): Handle partitioned cgraph.
+       (verify_cgraph_node): Handle speculation.
+       * cgraph.h (cgraph_edge): Add SPECULATIVE flag.
+       (cgraph_set_call_stmt): Update prototype.
+       (cgraph_make_edge_direct): Update prototype.
+       (cgraph_speculative_call_info): Declare.
+       * ipa-cp.c (ipcp_discover_new_direct_edges): Be ready for edge
+       to change; update call of ipa_find_references.
+       * ipa-ref.c (ipa_record_reference): Fix return value; clear
+       lto_stmt_uid and speculative flags.
+       (ipa_dump_references): Dump speculation.
+       (ipa_clone_references): Clone speculative flag.
+       (ipa_clone_referring): Likewise.
+       (ipa_clone_ref): New function.
+       (ipa_find_reference): Look into lto_stmt_uids
+       (ipa_clear_stmts_in_references): Do not clear speculative calls.
+       * ipa-ref.h (ipa_ref): Add lto_stmt_uid and speculative flags.
+       (ipa_find_reference): Update declaration.
+       (ipa_clone_ref): Declare.
+       * lto-cgraph.c (lto_output_edge): Make lto_stmt_uids start from 0;
+       stream speculative flag.
+       (lto_output_ref): Stream statements uids and speculation.
+       (input_ref): Likewise.
+       (input_edge): Stream speuclation.
+       * cgraphclones.c (cgraph_clone_edge): Clone speculation.
+       (cgraph_set_call_stmt_including_clones): Handle speculation.
+       * ipa-inline.c (heap_edge_removal_hook): New function.
+       (inline_small_functions): Register it.
+       * lto-streamer-in.c (fixup_call_stmt_edges_1): Bounds checking;
+       also initialize refs.
+       * ipa-prop.c (ipa_make_edge_direct_to_target): Be ready for
+       edge to change.
+       (try_make_edge_direct_simple_call): Likewise.
+       (try_make_edge_direct_simple_call): Likewise.
+       (update_indirect_edges_after_inlining): Likewise.
+       (remove_described_reference): Look proper lto_stmt_uid.
+       (propagate_controlled_uses): Likewise.
+       (propagate_controlled_uses): Liekwise.
+       * tree-inline.c (copy_bb): Copy speculative edges.
+       (redirect_all_calls): New function.
+       (copy_cfg_body): Do redirection after loop info
+       is updated.
+       (delete_unreachable_blocks_update_callgraph): Updadte
+       speculation.
+
 2013-08-09  Jan Hubicka  <jh@suse.cz>
 
        * lto-streamer-out.c (output_function): Renumber PHIs.
index d217b4af3c5406c2e986ee69d28242feb61b1749..43b499489f905b8c726ea823d9b4b509e5d1cf68 100644 (file)
@@ -673,15 +673,37 @@ edge_eq (const void *x, const void *y)
 
 /* Add call graph edge E to call site hash of its caller.  */
 
+static inline void
+cgraph_update_edge_in_call_site_hash (struct cgraph_edge *e)
+{
+  void **slot;
+  slot = htab_find_slot_with_hash (e->caller->call_site_hash,
+                                  e->call_stmt,
+                                  htab_hash_pointer (e->call_stmt),
+                                  INSERT);
+  *slot = e;
+}
+
+/* Add call graph edge E to call site hash of its caller.  */
+
 static inline void
 cgraph_add_edge_to_call_site_hash (struct cgraph_edge *e)
 {
   void **slot;
+  /* There are two speculative edges for every statement (one direct,
+     one indirect); always hash the direct one.  */
+  if (e->speculative && e->indirect_unknown_callee)
+    return;
   slot = htab_find_slot_with_hash (e->caller->call_site_hash,
                                   e->call_stmt,
                                   htab_hash_pointer (e->call_stmt),
                                   INSERT);
-  gcc_assert (!*slot);
+  if (*slot)
+    {
+      gcc_assert (((struct cgraph_edge *)*slot)->speculative);
+      return;
+    }
+  gcc_assert (!*slot || e->speculative);
   *slot = e;
 }
 
@@ -732,14 +754,33 @@ cgraph_edge (struct cgraph_node *node, gimple call_stmt)
 }
 
 
-/* Change field call_stmt of edge E to NEW_STMT.  */
+/* Change field call_stmt of edge E to NEW_STMT.
+   If UPDATE_SPECULATIVE and E is any component of speculative
+   edge, then update all components.  */
 
 void
-cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
+cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt,
+                     bool update_speculative)
 {
   tree decl;
 
-  if (e->caller->call_site_hash)
+  /* Speculative edges has three component, update all of them
+     when asked to.  */
+  if (update_speculative && e->speculative)
+    {
+      struct cgraph_edge *direct, *indirect;
+      struct ipa_ref *ref;
+
+      cgraph_speculative_call_info (e, direct, indirect, ref);
+      cgraph_set_call_stmt (direct, new_stmt, false);
+      cgraph_set_call_stmt (indirect, new_stmt, false);
+      ref->stmt = new_stmt;
+      return;
+    }
+
+  /* Only direct speculative edges go to call_site_hash.  */
+  if (e->caller->call_site_hash
+      && (!e->speculative || !e->indirect_unknown_callee))
     {
       htab_remove_elt_with_hash (e->caller->call_site_hash,
                                 e->call_stmt,
@@ -755,7 +796,7 @@ cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
       struct cgraph_node *new_callee = cgraph_get_node (decl);
 
       gcc_checking_assert (new_callee);
-      cgraph_make_edge_direct (e, new_callee);
+      e = cgraph_make_edge_direct (e, new_callee);
     }
 
   push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl));
@@ -781,7 +822,10 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
     {
       /* This is a rather expensive check possibly triggering
         construction of call stmt hashtable.  */
-      gcc_checking_assert (!cgraph_edge (caller, call_stmt));
+#ifdef ENABLE_CHECKING
+      struct cgraph_edge *e;
+      gcc_checking_assert (!(e=cgraph_edge (caller, call_stmt)) || e->speculative);
+#endif
 
       gcc_assert (is_gimple_call (call_stmt));
     }
@@ -804,6 +848,7 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
   edge->next_caller = NULL;
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
+  edge->lto_stmt_uid = 0;
 
   edge->count = count;
   gcc_assert (count >= 0);
@@ -937,6 +982,9 @@ cgraph_free_edge (struct cgraph_edge *e)
 {
   int uid = e->uid;
 
+  if (e->indirect_info)
+    ggc_free (e->indirect_info);
+
   /* Clear out the edge so we do not dangle pointers.  */
   memset (e, 0, sizeof (*e));
   e->uid = uid;
@@ -977,6 +1025,110 @@ cgraph_set_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
   e->callee = n;
 }
 
+/* Turn edge E into speculative call calling N2. Update
+   the profile so the direct call is taken COUNT times
+   with FREQUENCY.  
+
+   At clone materialization time, the indirect call E will
+   be expanded as:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
+   At this time the function just creates the direct call,
+   the referencd representing the if conditional and attaches
+   them all to the orginal indirect call statement.  */
+
+void
+cgraph_turn_edge_to_speculative (struct cgraph_edge *e,
+                                struct cgraph_node *n2,
+                                gcov_type direct_count,
+                                int direct_frequency)
+{
+  struct cgraph_node *n = e->caller;
+  struct ipa_ref *ref;
+  struct cgraph_edge *e2;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "Indirect call -> direct call from"
+              " other module %s/%i => %s/%i\n",
+              xstrdup (cgraph_node_name (n)), n->symbol.order,
+              xstrdup (cgraph_node_name (n2)), n2->symbol.order);
+    }
+  e->speculative = true;
+  e2 = cgraph_create_edge (n, n2, e->call_stmt, direct_count, direct_frequency);
+  initialize_inline_failed (e2);
+  e2->speculative = true;
+  e2->call_stmt = e->call_stmt;
+  e2->can_throw_external = e->can_throw_external;
+  e2->lto_stmt_uid = e->lto_stmt_uid;
+  e->count -= e2->count;
+  e->frequency -= e2->frequency;
+  cgraph_call_edge_duplication_hooks (e, e2);
+  ref = ipa_record_reference ((symtab_node)n, (symtab_node)n2,
+                             IPA_REF_ADDR, e->call_stmt);
+  ref->lto_stmt_uid = e->lto_stmt_uid;
+  ref->speculative = e->speculative;
+}
+
+/* Speculative call consist of three components:
+   1) an indirect edge representing the original call
+   2) an direct edge representing the new call
+   3) ADDR_EXPR reference representing the speculative check.
+   All three components are attached to single statement (the indirect
+   call) and if one of them exists, all of them must exist.
+
+   Given speculative call edge E, return all three components. 
+ */
+
+void
+cgraph_speculative_call_info (struct cgraph_edge *e,
+                             struct cgraph_edge *&indirect,
+                             struct cgraph_edge *&direct,
+                             struct ipa_ref *&reference)
+{
+  struct ipa_ref *ref;
+  int i;
+  struct cgraph_edge *e2;
+
+  if (!e->indirect_unknown_callee)
+    for (e2 = e->caller->indirect_calls;
+        e2->call_stmt != e->call_stmt || e2->lto_stmt_uid != e->lto_stmt_uid;
+        e2 = e2->next_callee)
+      ;
+  else
+    {
+      e2 = e;
+      /* We can take advantage of the call stmt hash.  */
+      if (e2->call_stmt)
+       {
+         e = cgraph_edge (e->caller, e2->call_stmt);
+         gcc_assert (!e->speculative && !e->indirect_unknown_callee);
+       }
+      else
+       for (e = e->caller->callees; 
+            e2->call_stmt != e->call_stmt || e2->lto_stmt_uid != e->lto_stmt_uid;
+            e = e->next_callee)
+         ;
+    }
+  gcc_assert (e->speculative && e2->speculative);
+  indirect = e;
+  direct = e2;
+
+  reference = NULL;
+  for (i = 0; ipa_ref_list_reference_iterate (&e->caller->symbol.ref_list, i, ref); i++)
+    if (ref->speculative
+       && ((ref->stmt && ref->stmt == e->call_stmt)
+           || (ref->lto_stmt_uid == e->lto_stmt_uid)))
+      {
+       reference = ref;
+       break;
+      }
+}
+
 /* Redirect callee of E to N.  The function does not update underlying
    call expression.  */
 
@@ -990,14 +1142,74 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
   cgraph_set_edge_callee (e, n);
 }
 
+/* Speculative call EDGE turned out to be direct call to CALLE_DECL.
+   Remove the speculative call sequence and return edge representing the call.
+   It is up to caller to redirect the call as appropriate. */
+
+static struct cgraph_edge *
+cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
+{
+  struct cgraph_edge *e2;
+  struct ipa_ref *ref;
+
+  gcc_assert (edge->speculative);
+  cgraph_speculative_call_info (edge, e2, edge, ref);
+  if (ref->referred->symbol.decl != callee_decl)
+    {
+      if (dump_file)
+       {
+         fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
+                  "turned out to have contradicitng known target ",
+                  xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
+                  xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
+         print_generic_expr (dump_file, callee_decl, 0);
+          fprintf (dump_file, "\n");
+       }
+    }
+  else
+    {
+      struct cgraph_edge *tmp = edge;
+      if (dump_file)
+        fprintf (dump_file, "Speculative call turned into direct call.\n");
+      edge = e2;
+      e2 = tmp;
+    }
+  edge->count += e2->count;
+  edge->frequency += e2->frequency;
+  edge->speculative = false;
+  e2->speculative = false;
+  if (e2->indirect_unknown_callee || e2->inline_failed)
+    cgraph_remove_edge (e2);
+  else
+    cgraph_remove_node_and_inline_clones (e2->callee, NULL);
+  if (edge->caller->call_site_hash)
+    cgraph_update_edge_in_call_site_hash (edge);
+  ipa_remove_reference (ref);
+  return edge;
+}
+
 /* Make an indirect EDGE with an unknown callee an ordinary edge leading to
    CALLEE.  DELTA is an integer constant that is to be added to the this
    pointer (first parameter) to compensate for skipping a thunk adjustment.  */
 
-void
+struct cgraph_edge *
 cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
 {
+  gcc_assert (edge->indirect_unknown_callee);
+
+  /* If we are redirecting speculative call, make it non-speculative.  */
+  if (edge->indirect_unknown_callee && edge->speculative)
+    {
+      edge = cgraph_resolve_speculation (edge, callee->symbol.decl);
+
+      /* On successful speculation just return the pre existing direct edge.  */
+      if (!edge->indirect_unknown_callee)
+        return edge;
+    }
+
   edge->indirect_unknown_callee = 0;
+  ggc_free (edge->indirect_info);
+  edge->indirect_info = NULL;
 
   /* Get the edge out of the indirect edge list. */
   if (edge->prev_callee)
@@ -1024,6 +1236,7 @@ cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
 
   /* We need to re-determine the inlining status of the edge.  */
   initialize_inline_failed (edge);
+  return edge;
 }
 
 /* If necessary, change the function declaration in the call statement
@@ -1039,6 +1252,50 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   struct cgraph_node *node;
 #endif
 
+  if (e->speculative)
+    {
+      struct cgraph_edge *e2;
+      gimple new_stmt;
+      struct ipa_ref *ref;
+
+      cgraph_speculative_call_info (e, e, e2, ref);
+      if (gimple_call_fndecl (e->call_stmt))
+       e = cgraph_resolve_speculation (e, gimple_call_fndecl (e->call_stmt));
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i\n",
+                    xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order,
+                    xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order);
+         gcc_assert (e2->speculative);
+         push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl));
+         new_stmt = gimple_ic (e->call_stmt, cgraph (ref->referred),
+                               e->count || e2->count
+                               ?  RDIV (e->count * REG_BR_PROB_BASE,
+                                        e->count + e2->count)
+                               : e->frequency || e2->frequency
+                               ? RDIV (e->frequency * REG_BR_PROB_BASE,
+                                       e->frequency + e2->frequency)
+                               : REG_BR_PROB_BASE / 2,
+                               e->count, e->count + e2->count);
+         e->speculative = false;
+         cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt, false);
+         e->frequency = compute_call_stmt_bb_frequency (e->caller->symbol.decl,
+                                                        gimple_bb (e->call_stmt));
+         e2->frequency = compute_call_stmt_bb_frequency (e2->caller->symbol.decl,
+                                                         gimple_bb (e2->call_stmt));
+         e2->speculative = false;
+         ref->speculative = false;
+         ref->stmt = NULL;
+         /* Indirect edges are not both in the call site hash.
+            get it updated.  */
+         if (e->caller->call_site_hash)
+           cgraph_update_edge_in_call_site_hash (e2);
+         pop_cfun ();
+         /* Continue redirecting E to proper target.  */
+       }
+    }
+
   if (e->indirect_unknown_callee
       || decl == e->callee->symbol.decl)
     return e->call_stmt;
@@ -1099,7 +1356,7 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
       update_stmt (new_stmt);
     }
 
-  cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt);
+  cgraph_set_call_stmt_including_clones (e->caller, e->call_stmt, new_stmt, false);
 
   if (cgraph_dump_file)
     {
@@ -1603,6 +1860,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
       if (edge->frequency)
        fprintf (f, "(%.2f per call) ",
                 edge->frequency / (double)CGRAPH_FREQ_BASE);
+      if (edge->speculative)
+       fprintf(f, "(speculative) ");
       if (!edge->inline_failed)
        fprintf(f, "(inlined) ");
       if (edge->indirect_inlining_edge)
@@ -1616,6 +1875,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
     {
       fprintf (f, "%s/%i ", cgraph_node_asm_name (edge->callee),
               edge->callee->symbol.order);
+      if (edge->speculative)
+       fprintf(f, "(speculative) ");
       if (!edge->inline_failed)
        fprintf(f, "(inlined) ");
       if (edge->indirect_inlining_edge)
@@ -2262,6 +2523,7 @@ verify_edge_count_and_frequency (struct cgraph_edge *e)
     }
   if (gimple_has_body_p (e->caller->symbol.decl)
       && !e->caller->global.inlined_to
+      && !e->speculative
       /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
         Remove this once edges are actually removed from the function at that time.  */
       && (e->frequency
@@ -2316,7 +2578,7 @@ verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
 
   /* We do not know if a node from a different partition is an alias or what it
      aliases and therefore cannot do the former_clone_of check reliably.  */
-  if (!node || node->symbol.in_other_partition)
+  if (!node || node->symbol.in_other_partition || e->callee->symbol.in_other_partition)
     return false;
   node = cgraph_function_or_thunk_node (node, NULL);
 
@@ -2625,7 +2887,7 @@ verify_cgraph_node (struct cgraph_node *node)
        }
       for (e = node->indirect_calls; e; e = e->next_callee)
        {
-         if (!e->aux)
+         if (!e->aux && !e->speculative)
            {
              error ("an indirect edge from %s has no corresponding call_stmt",
                     identifier_to_locale (cgraph_node_name (e->caller)));
index d681a1d65b4ef62d2a410ee68c990bfb16614ddb..78cee2987b166ced2630246356cd46d516744493 100644 (file)
@@ -483,6 +483,24 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgrap
   unsigned int call_stmt_cannot_inline_p : 1;
   /* Can this call throw externally?  */
   unsigned int can_throw_external : 1;
+  /* Edges with SPECULATIVE flag represents indirect calls that was
+     speculatively turned into direct (i.e. by profile feedback).
+     The final code sequence will have form:
+
+     if (call_target == expected_fn)
+       expected_fn ();
+     else
+       call_target ();
+
+     Every speculative call is represented by three components attached
+     to a same call statement:
+     1) a direct call (to expected_fn)
+     2) an indirect call (to call_target)
+     3) a IPA_REF_ADDR refrence to expected_fn.
+
+     Optimizers may later redirect direct call to clone, so 1) and 3)
+     do not need to necesarily agree with destination.  */
+  unsigned int speculative : 1;
 };
 
 #define CGRAPH_FREQ_BASE 1000
@@ -629,7 +647,7 @@ struct cgraph_node * cgraph_add_thunk (struct cgraph_node *, tree, tree, bool, H
                                       HOST_WIDE_INT, tree, tree);
 struct cgraph_node *cgraph_node_for_asm (tree);
 struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple);
-void cgraph_set_call_stmt (struct cgraph_edge *, gimple);
+void cgraph_set_call_stmt (struct cgraph_edge *, gimple, bool update_speculative = true);
 void cgraph_update_edges_for_call_stmt (gimple, tree, gimple);
 struct cgraph_local_info *cgraph_local_info (tree);
 struct cgraph_global_info *cgraph_global_info (tree);
@@ -641,7 +659,7 @@ void cgraph_call_edge_duplication_hooks (struct cgraph_edge *,
                                         struct cgraph_edge *);
 
 void cgraph_redirect_edge_callee (struct cgraph_edge *, struct cgraph_node *);
-void cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
+struct cgraph_edge *cgraph_make_edge_direct (struct cgraph_edge *, struct cgraph_node *);
 bool cgraph_only_called_directly_p (struct cgraph_node *);
 
 bool cgraph_function_possibly_inlined_p (tree);
@@ -702,6 +720,14 @@ bool cgraph_propagate_frequency (struct cgraph_node *node);
 struct cgraph_node * cgraph_function_node (struct cgraph_node *,
                                           enum availability *avail = NULL);
 bool cgraph_get_body (struct cgraph_node *node);
+void
+cgraph_turn_edge_to_speculative (struct cgraph_edge *,
+                                struct cgraph_node *,
+                                gcov_type, int);
+void cgraph_speculative_call_info (struct cgraph_edge *,
+                                  struct cgraph_edge *&,
+                                  struct cgraph_edge *&,
+                                  struct ipa_ref *&);
 
 /* In cgraphunit.c  */
 struct asm_node *add_asm_node (tree);
@@ -735,7 +761,8 @@ struct cgraph_node * cgraph_create_virtual_clone (struct cgraph_node *old_node,
                                                  const char *clone_name);
 struct cgraph_node *cgraph_find_replacement_node (struct cgraph_node *);
 bool cgraph_remove_node_and_inline_clones (struct cgraph_node *, struct cgraph_node *);
-void cgraph_set_call_stmt_including_clones (struct cgraph_node *, gimple, gimple);
+void cgraph_set_call_stmt_including_clones (struct cgraph_node *, gimple, gimple,
+                                           bool update_speculative = true);
 void cgraph_create_edge_including_clones (struct cgraph_node *,
                                          struct cgraph_node *,
                                          gimple, gimple, gcov_type, int,
index 333deed81460551190bb055d72560a5355bc5cb5..b9c112fef4d53ba39c602a75a4a3223a42ee547d 100644 (file)
@@ -483,8 +483,15 @@ cgraph_rebuild_references (void)
   basic_block bb;
   struct cgraph_node *node = cgraph_get_node (current_function_decl);
   gimple_stmt_iterator gsi;
-
-  ipa_remove_all_references (&node->symbol.ref_list);
+  struct ipa_ref *ref;
+  int i;
+
+  /* Keep speculative references for further cgraph edge expansion.  */
+  for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, ref);)
+    if (!ref->speculative)
+      ipa_remove_reference (ref);
+    else
+      i++;
 
   node->count = ENTRY_BLOCK_PTR->count;
 
index 464c524fd28fe4eaecea340e495f2f34a48e82b0..ae26a026d6453fdd4ed43dd632e4ee2d09b6a90b 100644 (file)
@@ -147,6 +147,7 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = e->can_throw_external;
   new_edge->call_stmt_cannot_inline_p = e->call_stmt_cannot_inline_p;
+  new_edge->speculative = e->speculative;
   if (update_original)
     {
       e->count -= new_edge->count;
@@ -474,17 +475,21 @@ cgraph_find_replacement_node (struct cgraph_node *node)
 }
 
 /* Like cgraph_set_call_stmt but walk the clone tree and update all
-   clones sharing the same function body.  */
+   clones sharing the same function body.  
+   When WHOLE_SPECULATIVE_EDGES is true, all three components of
+   speculative edge gets updated.  Otherwise we update only direct
+   call.  */
 
 void
 cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
-                                      gimple old_stmt, gimple new_stmt)
+                                      gimple old_stmt, gimple new_stmt,
+                                      bool update_speculative)
 {
   struct cgraph_node *node;
   struct cgraph_edge *edge = cgraph_edge (orig, old_stmt);
 
   if (edge)
-    cgraph_set_call_stmt (edge, new_stmt);
+    cgraph_set_call_stmt (edge, new_stmt, update_speculative);
 
   node = orig->clones;
   if (node)
@@ -492,7 +497,23 @@ cgraph_set_call_stmt_including_clones (struct cgraph_node *orig,
       {
        struct cgraph_edge *edge = cgraph_edge (node, old_stmt);
        if (edge)
-         cgraph_set_call_stmt (edge, new_stmt);
+         {
+           cgraph_set_call_stmt (edge, new_stmt, update_speculative);
+           /* If UPDATE_SPECULATIVE is false, it means that we are turning
+              speculative call into a real code sequence.  Update the
+              callgraph edges.  */
+           if (edge->speculative && !update_speculative)
+             {
+               struct cgraph_edge *direct, *indirect;
+               struct ipa_ref *ref;
+
+               gcc_assert (!edge->indirect_unknown_callee);
+               cgraph_speculative_call_info (edge, direct, indirect, ref);
+               direct->speculative = false;
+               indirect->speculative = false;
+               ref->speculative = false;
+             }
+         }
        if (node->clones)
          node = node->clones;
        else if (node->next_sibling_clone)
@@ -811,6 +832,7 @@ cgraph_materialize_all_clones (void)
 {
   struct cgraph_node *node;
   bool stabilized = false;
+  
 
   if (cgraph_dump_file)
     fprintf (cgraph_dump_file, "Materializing clones\n");
index 688209dd49c08c4337ff164274d35a8f047f0e61..a52bf7f5524cea70767dad659af4231836489c4f 100644 (file)
@@ -2278,14 +2278,15 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node,
                                               aggvals);
       if (target)
        {
+         bool agg_contents = ie->indirect_info->agg_contents;
+         bool polymorphic = ie->indirect_info->polymorphic;
+         bool param_index = ie->indirect_info->param_index;
          struct cgraph_edge *cs = ipa_make_edge_direct_to_target (ie, target);
          found = true;
 
-         if (cs && !ie->indirect_info->agg_contents
-             && !ie->indirect_info->polymorphic)
+         if (cs && !agg_contents && !polymorphic)
            {
              struct ipa_node_params *info = IPA_NODE_REF (node);
-             int param_index = ie->indirect_info->param_index;
              int c = ipa_get_controlled_uses (info, param_index);
              if (c != IPA_UNDESCRIBED_USE)
                {
@@ -2299,7 +2300,7 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node,
                  if (c == 0
                      && (to_del = ipa_find_reference ((symtab_node) node,
                                                       (symtab_node) cs->callee,
-                                                      NULL)))
+                                                      NULL, 0)))
                    {
                      if (dump_file && (dump_flags & TDF_DETAILS))
                        fprintf (dump_file, "       and even removing its "
index 8c5b430b5a2ccbf77f0380c3ec258e3fdcf709cf..033b314b34a33ada58d00c36fe884078bfe3f27d 100644 (file)
@@ -1388,6 +1388,17 @@ add_new_edges_to_heap (fibheap_t heap, vec<cgraph_edge_p> new_edges)
     }
 }
 
+/* Remove EDGE from the fibheap.  */
+
+static void
+heap_edge_removal_hook (struct cgraph_edge *e, void *data)
+{
+  if (e->aux)
+    {
+      fibheap_delete_node ((fibheap_t)data, (fibnode_t)e->aux);
+      e->aux = NULL;
+    }
+}
 
 /* We use greedy algorithm for inlining of small functions:
    All inline candidates are put into prioritized heap ordered in
@@ -1406,10 +1417,14 @@ inline_small_functions (void)
   vec<cgraph_edge_p> new_indirect_edges = vNULL;
   int initial_size = 0;
   struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
+  struct cgraph_edge_hook_list *edge_removal_hook_holder;
 
   if (flag_indirect_inlining)
     new_indirect_edges.create (8);
 
+  edge_removal_hook_holder
+    = cgraph_add_edge_removal_hook (&heap_edge_removal_hook, edge_heap);
+
   /* Compute overall unit size and other global parameters used by badness
      metrics.  */
 
@@ -1663,6 +1678,7 @@ inline_small_functions (void)
             initial_size, overall_size,
             initial_size ? overall_size * 100 / (initial_size) - 100: 0);
   BITMAP_FREE (updated_nodes);
+  cgraph_remove_edge_removal_hook (edge_removal_hook_holder);
 }
 
 /* Flatten NODE.  Performed both during early inlining and
index ef4dff72542733e7c5fb81a2b5d0344b4de85f41..b06f640b3f8727a7651b522b37cf66632c59def9 100644 (file)
@@ -2313,12 +2313,6 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
      the cgraph node too early.  */
   gcc_assert (!callee->global.inlined_to);
 
-  cgraph_make_edge_direct (ie, callee);
-  es = inline_edge_summary (ie);
-  es->call_stmt_size -= (eni_size_weights.indirect_call_cost
-                        - eni_size_weights.call_cost);
-  es->call_stmt_time -= (eni_time_weights.indirect_call_cost
-                        - eni_time_weights.call_cost);
   if (dump_file && !unreachable)
     {
       fprintf (dump_file, "ipa-prop: Discovered %s call to a known target "
@@ -2326,14 +2320,19 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
               ie->indirect_info->polymorphic ? "a virtual" : "an indirect",
               xstrdup (cgraph_node_name (ie->caller)),
               ie->caller->symbol.order,
-              xstrdup (cgraph_node_name (ie->callee)),
-              ie->callee->symbol.order);
+              xstrdup (cgraph_node_name (callee)),
+              callee->symbol.order);
       if (ie->call_stmt)
        print_gimple_stmt (dump_file, ie->call_stmt, 2, TDF_SLIM);
       else
        fprintf (dump_file, "with uid %i\n", ie->lto_stmt_uid);
-    }
-  callee = cgraph_function_or_thunk_node (callee, NULL);
+     }
+  ie = cgraph_make_edge_direct (ie, callee);
+  es = inline_edge_summary (ie);
+  es->call_stmt_size -= (eni_size_weights.indirect_call_cost
+                        - eni_size_weights.call_cost);
+  es->call_stmt_time -= (eni_time_weights.indirect_call_cost
+                        - eni_time_weights.call_cost);
 
   return ie;
 }
@@ -2374,7 +2373,7 @@ remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc)
 
   origin = rdesc->cs;
   to_del = ipa_find_reference ((symtab_node) origin->caller, symbol,
-                              origin->call_stmt);
+                              origin->call_stmt, origin->lto_stmt_uid);
   gcc_assert (to_del);
   ipa_remove_reference (to_del);
   if (dump_file)
@@ -2408,9 +2407,11 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
                                  struct ipa_jump_func *jfunc,
                                  struct ipa_node_params *new_root_info)
 {
-  struct ipa_cst_ref_desc *rdesc;
   struct cgraph_edge *cs;
   tree target;
+  bool agg_contents = ie->indirect_info->agg_contents;
+  bool speculative = ie->speculative;
+  struct ipa_cst_ref_desc *rdesc;
 
   if (ie->indirect_info->agg_contents)
     target = ipa_find_agg_cst_for_param (&jfunc->agg,
@@ -2422,7 +2423,8 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
     return NULL;
   cs = ipa_make_edge_direct_to_target (ie, target);
 
-  if (cs && !ie->indirect_info->agg_contents
+  /* FIXME: speculative edges can be handled.  */
+  if (cs && !agg_contents && !speculative
       && jfunc->type == IPA_JF_CONST
       && (rdesc = jfunc_rdesc_usable (jfunc))
       && --rdesc->refcount == 0)
@@ -2521,7 +2523,14 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
       else
        new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
                                                            new_root_info);
-      if (new_direct_edge)
+      /* If speculation was removed, then we need to do nothing.  */
+      if (new_direct_edge && new_direct_edge != ie)
+       {
+         new_direct_edge->indirect_inlining_edge = 1;
+         top = IPA_EDGE_REF (cs);
+         res = true;
+       }
+      else if (new_direct_edge)
        {
          new_direct_edge->indirect_inlining_edge = 1;
          if (new_direct_edge->call_stmt)
@@ -2532,9 +2541,9 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
          if (new_edges)
            {
              new_edges->safe_push (new_direct_edge);
-             top = IPA_EDGE_REF (cs);
              res = true;
            }
+         top = IPA_EDGE_REF (cs);
        }
       else if (jfunc->type == IPA_JF_PASS_THROUGH
               && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
@@ -2645,7 +2654,7 @@ propagate_controlled_uses (struct cgraph_edge *cs)
                  && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
                  && (n = cgraph_get_node (TREE_OPERAND (t, 0)))
                  && (ref = ipa_find_reference ((symtab_node) new_root,
-                                               (symtab_node) n, NULL)))
+                                               (symtab_node) n, NULL, 0)))
                {
                  if (dump_file)
                    fprintf (dump_file, "ipa-prop: Removing cloning-created "
@@ -2683,7 +2692,7 @@ propagate_controlled_uses (struct cgraph_edge *cs)
                    {
                      struct ipa_ref *ref;
                      ref = ipa_find_reference ((symtab_node) clone,
-                                               (symtab_node) n, NULL);
+                                               (symtab_node) n, NULL, 0);
                      if (ref)
                        {
                          if (dump_file)
index d2c6002e17674d75c51499b0d4ab37ca13f0ac1f..9f45c8eb866f35cca2e7aea12dc5812f312a9322 100644 (file)
@@ -38,7 +38,7 @@ ipa_record_reference (symtab_node referring_node,
                      symtab_node referred_node,
                      enum ipa_ref_use use_type, gimple stmt)
 {
-  struct ipa_ref *ref;
+  struct ipa_ref *ref, *ref2;
   struct ipa_ref_list *list, *list2;
   ipa_ref_t *old_references;
 
@@ -56,14 +56,16 @@ ipa_record_reference (symtab_node referring_node,
   ref->referring = referring_node;
   ref->referred = referred_node;
   ref->stmt = stmt;
+  ref->lto_stmt_uid = 0;
   ref->use = use_type;
+  ref->speculative = 0;
 
   /* If vector was moved in memory, update pointers.  */
   if (old_references != list->references->address ())
     {
       int i;
-      for (i = 0; ipa_ref_list_reference_iterate (list, i, ref); i++)
-       ipa_ref_referred_ref_list (ref)->referring[ref->referred_index] = ref;
+      for (i = 0; ipa_ref_list_reference_iterate (list, i, ref2); i++)
+       ipa_ref_referred_ref_list (ref2)->referring[ref2->referred_index] = ref2;
     }
   return ref;
 }
@@ -155,6 +157,8 @@ ipa_dump_references (FILE * file, struct ipa_ref_list *list)
                symtab_node_asm_name (ref->referred),
                ref->referred->symbol.order,
               ipa_ref_use_name [ref->use]);
+      if (ref->speculative)
+       fprintf (file, " (speculative)");
     }
   fprintf (file, "\n");
 }
@@ -172,22 +176,50 @@ ipa_dump_referring (FILE * file, struct ipa_ref_list *list)
                symtab_node_asm_name (ref->referring),
                ref->referring->symbol.order,
               ipa_ref_use_name [ref->use]);
+      if (ref->speculative)
+       fprintf (file, " (speculative)");
     }
   fprintf (file, "\n");
 }
 
+/* Clone reference REF to DEST_NODE and set its stmt to STMT.  */
+
+struct ipa_ref *
+ipa_clone_ref (struct ipa_ref *ref,
+              symtab_node dest_node,
+              gimple stmt)
+{
+  bool speculative = ref->speculative;
+  unsigned int stmt_uid = ref->lto_stmt_uid;
+  struct ipa_ref *ref2;
+
+  ref2 = ipa_record_reference (dest_node,
+                              ref->referred,
+                              ref->use, stmt);
+  ref2->speculative = speculative;
+  ref2->lto_stmt_uid = stmt_uid;
+  return ref2;
+}
+
 /* Clone all references from SRC to DEST_NODE or DEST_VARPOOL_NODE.  */
 
 void
 ipa_clone_references (symtab_node dest_node,
                      struct ipa_ref_list *src)
 {
-  struct ipa_ref *ref;
+  struct ipa_ref *ref, *ref2;
   int i;
   for (i = 0; ipa_ref_list_reference_iterate (src, i, ref); i++)
-    ipa_record_reference (dest_node,
-                         ref->referred,
-                         ref->use, ref->stmt);
+    {
+      bool speculative = ref->speculative;
+      unsigned int stmt_uid = ref->lto_stmt_uid;
+
+      ref2 = ipa_record_reference (dest_node,
+                                  ref->referred,
+                                  ref->use, ref->stmt);
+      ref2->speculative = speculative;
+      ref2->lto_stmt_uid = stmt_uid;
+    }
 }
 
 /* Clone all referring from SRC to DEST_NODE or DEST_VARPOOL_NODE.  */
@@ -196,12 +228,19 @@ void
 ipa_clone_referring (symtab_node dest_node,
                    struct ipa_ref_list *src)
 {
-  struct ipa_ref *ref;
+  struct ipa_ref *ref, *ref2;
   int i;
   for (i = 0; ipa_ref_list_referring_iterate (src, i, ref); i++)
-    ipa_record_reference (ref->referring,
-                         dest_node,
-                         ref->use, ref->stmt);
+    {
+      bool speculative = ref->speculative;
+      unsigned int stmt_uid = ref->lto_stmt_uid;
+
+      ref2 = ipa_record_reference (ref->referring,
+                                  dest_node,
+                                  ref->use, ref->stmt);
+      ref2->speculative = speculative;
+      ref2->lto_stmt_uid = stmt_uid;
+    }
 }
 
 /* Return true when execution of REF can lead to return from
@@ -230,14 +269,17 @@ ipa_ref_has_aliases_p (struct ipa_ref_list *ref_list)
 
 struct ipa_ref *
 ipa_find_reference (symtab_node referring_node, symtab_node referred_node,
-                   gimple stmt)
+                   gimple stmt, unsigned int lto_stmt_uid)
 {
   struct ipa_ref *r = NULL;
   int i;
 
   for (i = 0; ipa_ref_list_reference_iterate (&referring_node->symbol.ref_list, i, r); i++)
     if (r->referred == referred_node
-       && (in_lto_p || r->stmt == stmt))
+       && !r->speculative
+       && ((stmt && r->stmt == stmt)
+           || (lto_stmt_uid && r->lto_stmt_uid == lto_stmt_uid)
+           || (!stmt && !lto_stmt_uid && !r->stmt && !r->lto_stmt_uid)))
       return r;
   return NULL;
 }
@@ -257,7 +299,9 @@ ipa_remove_stmt_references (symtab_node referring_node, gimple stmt)
 }
 
 /* Remove all stmt references in non-speculative references.
-   Those are not maintained during inlining & clonning. */
+   Those are not maintained during inlining & clonning. 
+   The exception are speculative references that are updated along
+   with callgraph edges associated with them.  */
 
 void
 ipa_clear_stmts_in_references (symtab_node referring_node)
@@ -266,5 +310,6 @@ ipa_clear_stmts_in_references (symtab_node referring_node)
   int i;
 
   for (i = 0; ipa_ref_list_reference_iterate (&referring_node->symbol.ref_list, i, r); i++)
-    r->stmt = NULL;
+    if (!r->speculative)
+      r->stmt = NULL;
 }
index 0b37a14ee6e17e21f3d540c91052b2e6b3c93a6f..e0553bb660925f0b0aaae417553167a59da99d09 100644 (file)
@@ -40,8 +40,10 @@ struct GTY(()) ipa_ref
   symtab_node referring;
   symtab_node referred;
   gimple stmt;
+  unsigned int lto_stmt_uid;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:2;
+  unsigned int speculative:1;
 };
 
 typedef struct ipa_ref ipa_ref_t;
@@ -71,8 +73,9 @@ void ipa_dump_references (FILE *, struct ipa_ref_list *);
 void ipa_dump_referring (FILE *, struct ipa_ref_list *);
 void ipa_clone_references (symtab_node, struct ipa_ref_list *);
 void ipa_clone_referring (symtab_node, struct ipa_ref_list *);
+struct ipa_ref * ipa_clone_ref (struct ipa_ref *, symtab_node, gimple);
 bool ipa_ref_cannot_lead_to_return (struct ipa_ref *);
 bool ipa_ref_has_aliases_p (struct ipa_ref_list *);
-struct ipa_ref * ipa_find_reference (symtab_node, symtab_node, gimple);
+struct ipa_ref * ipa_find_reference (symtab_node, symtab_node, gimple, unsigned int);
 void ipa_remove_stmt_references (symtab_node, gimple);
 void ipa_clear_stmts_in_references (symtab_node);
index 51dc705b28fc4b8c6a1f6fa4b6c552d51fce0f0d..e0d09913ae75954e49225086419a1cbd754c87f1 100644 (file)
@@ -273,12 +273,13 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
 
   bp = bitpack_create (ob->main_stream);
   uid = (!gimple_has_body_p (edge->caller->symbol.decl)
-        ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt));
+        ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt) + 1);
   bp_pack_enum (&bp, cgraph_inline_failed_enum,
                CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
   bp_pack_var_len_unsigned (&bp, edge->frequency);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
+  bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
   bp_pack_value (&bp, edge->can_throw_external, 1);
   if (edge->indirect_unknown_callee)
@@ -589,13 +590,24 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
 {
   struct bitpack_d bp;
   int nref;
+  int uid = ref->lto_stmt_uid;
+  struct cgraph_node *node;
 
   bp = bitpack_create (ob->main_stream);
   bp_pack_value (&bp, ref->use, 2);
+  bp_pack_value (&bp, ref->speculative, 1);
   streamer_write_bitpack (&bp);
   nref = lto_symtab_encoder_lookup (encoder, ref->referred);
   gcc_assert (nref != LCC_NOT_FOUND);
   streamer_write_hwi_stream (ob->main_stream, nref);
+  
+  node = dyn_cast <cgraph_node> (ref->referring);
+  if (node)
+    {
+      if (ref->stmt)
+       uid = gimple_uid (ref->stmt) + 1;
+      streamer_write_hwi_stream (ob->main_stream, uid);
+    }
 }
 
 /* Stream out profile_summary to OB.  */
@@ -1116,11 +1128,17 @@ input_ref (struct lto_input_block *ib,
   symtab_node node = NULL;
   struct bitpack_d bp;
   enum ipa_ref_use use;
+  bool speculative;
+  struct ipa_ref *ref;
 
   bp = streamer_read_bitpack (ib);
   use = (enum ipa_ref_use) bp_unpack_value (&bp, 2);
+  speculative = (enum ipa_ref_use) bp_unpack_value (&bp, 1);
   node = nodes[streamer_read_hwi (ib)];
-  ipa_record_reference (referring_node, node, use, NULL);
+  ref = ipa_record_reference (referring_node, node, use, NULL);
+  ref->speculative = speculative;
+  if (is_a <cgraph_node> (referring_node))
+    ref->lto_stmt_uid = streamer_read_hwi (ib);
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1167,6 +1185,7 @@ input_edge (struct lto_input_block *ib, vec<symtab_node> nodes,
     edge = cgraph_create_edge (caller, callee, NULL, count, freq);
 
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
+  edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
index 9dc8438aa4776afffebc68258bc9b7594bee4491..0684cdaa7bad4bade829410ecf3373f09008cad2 100644 (file)
@@ -755,30 +755,60 @@ input_ssa_names (struct lto_input_block *ib, struct data_in *data_in,
    so they point to STMTS.  */
 
 static void
-fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple *stmts)
+fixup_call_stmt_edges_1 (struct cgraph_node *node, gimple *stmts,
+                        struct function *fn)
 {
   struct cgraph_edge *cedge;
+  struct ipa_ref *ref;
+  unsigned int i;
+
   for (cedge = node->callees; cedge; cedge = cedge->next_callee)
-    cedge->call_stmt = stmts[cedge->lto_stmt_uid];
+    {
+      if (gimple_stmt_max_uid (fn) < cedge->lto_stmt_uid)
+      fatal_error ("Cgraph edge statement index out of range");
+      cedge->call_stmt = stmts[cedge->lto_stmt_uid - 1];
+      if (!cedge->call_stmt)
+      fatal_error ("Cgraph edge statement index not found");
+    }
   for (cedge = node->indirect_calls; cedge; cedge = cedge->next_callee)
-    cedge->call_stmt = stmts[cedge->lto_stmt_uid];
+    {
+      if (gimple_stmt_max_uid (fn) < cedge->lto_stmt_uid)
+      fatal_error ("Cgraph edge statement index out of range");
+      cedge->call_stmt = stmts[cedge->lto_stmt_uid - 1];
+      if (!cedge->call_stmt)
+      fatal_error ("Cgraph edge statement index not found");
+    }
+  for (i = 0;
+       ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, ref);
+       i++)
+    if (ref->lto_stmt_uid)
+      {
+       if (gimple_stmt_max_uid (fn) < ref->lto_stmt_uid)
+         fatal_error ("Reference statement index out of range");
+       ref->stmt = stmts[ref->lto_stmt_uid - 1];
+       if (!ref->stmt)
+         fatal_error ("Reference statement index not found");
+      }
 }
 
+
 /* Fixup call_stmt pointers in NODE and all clones.  */
 
 static void
 fixup_call_stmt_edges (struct cgraph_node *orig, gimple *stmts)
 {
   struct cgraph_node *node;
+  struct function *fn;
 
   while (orig->clone_of)
     orig = orig->clone_of;
+  fn = DECL_STRUCT_FUNCTION (orig->symbol.decl);
 
-  fixup_call_stmt_edges_1 (orig, stmts);
+  fixup_call_stmt_edges_1 (orig, stmts, fn);
   if (orig->clones)
     for (node = orig->clones; node != orig;)
       {
-       fixup_call_stmt_edges_1 (node, stmts);
+       fixup_call_stmt_edges_1 (node, stmts, fn);
        if (node->clones)
          node = node->clones;
        else if (node->next_sibling_clone)
index e33e5a3a7e7607f7a0b62d1a106812d3ada57e51..00e31982957ba59d3513e486a2590b8a9fcb8fc1 100644 (file)
@@ -1676,6 +1676,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
                  if (edge)
                    {
                      int edge_freq = edge->frequency;
+                     int new_freq;
+                     struct cgraph_edge *old_edge = edge;
                      edge = cgraph_clone_edge (edge, id->dst_node, stmt,
                                                gimple_uid (stmt),
                                                REG_BR_PROB_BASE, CGRAPH_FREQ_BASE,
@@ -1683,25 +1685,52 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
                      /* We could also just rescale the frequency, but
                         doing so would introduce roundoff errors and make
                         verifier unhappy.  */
-                     edge->frequency
-                       = compute_call_stmt_bb_frequency (id->dst_node->symbol.decl,
-                                                         copy_basic_block);
-                     if (dump_file
-                         && profile_status_for_function (cfun) != PROFILE_ABSENT
-                         && (edge_freq > edge->frequency + 10
-                             || edge_freq < edge->frequency - 10))
+                     new_freq  = compute_call_stmt_bb_frequency (id->dst_node->symbol.decl,
+                                                                 copy_basic_block);
+
+                     /* Speculative calls consist of two edges - direct and indirect.
+                        Duplicate the whole thing and distribute frequencies accordingly.  */
+                     if (edge->speculative)
+                       {
+                         struct cgraph_edge *direct, *indirect;
+                         struct ipa_ref *ref;
+
+                         gcc_assert (!edge->indirect_unknown_callee);
+                         cgraph_speculative_call_info (old_edge, direct, indirect, ref);
+                         indirect = cgraph_clone_edge (indirect, id->dst_node, stmt,
+                                                       gimple_uid (stmt),
+                                                       REG_BR_PROB_BASE, CGRAPH_FREQ_BASE,
+                                                       true);
+                         if (old_edge->frequency + indirect->frequency)
+                           {
+                             edge->frequency = MIN (RDIV ((gcov_type)new_freq * old_edge->frequency,
+                                                          (old_edge->frequency + indirect->frequency)),
+                                                    CGRAPH_FREQ_MAX);
+                             indirect->frequency = MIN (RDIV ((gcov_type)new_freq * indirect->frequency,
+                                                              (old_edge->frequency + indirect->frequency)),
+                                                        CGRAPH_FREQ_MAX);
+                           }
+                         ipa_clone_ref (ref, (symtab_node)id->dst_node, stmt);
+                       }
+                     else
                        {
-                         fprintf (dump_file, "Edge frequency estimated by "
-                                  "cgraph %i diverge from inliner's estimate %i\n",
-                                  edge_freq,
-                                  edge->frequency);
-                         fprintf (dump_file,
-                                  "Orig bb: %i, orig bb freq %i, new bb freq %i\n",
-                                  bb->index,
-                                  bb->frequency,
-                                  copy_basic_block->frequency);
+                         edge->frequency = new_freq;
+                         if (dump_file
+                             && profile_status_for_function (cfun) != PROFILE_ABSENT
+                             && (edge_freq > edge->frequency + 10
+                                 || edge_freq < edge->frequency - 10))
+                           {
+                             fprintf (dump_file, "Edge frequency estimated by "
+                                      "cgraph %i diverge from inliner's estimate %i\n",
+                                      edge_freq,
+                                      edge->frequency);
+                             fprintf (dump_file,
+                                      "Orig bb: %i, orig bb freq %i, new bb freq %i\n",
+                                      bb->index,
+                                      bb->frequency,
+                                      copy_basic_block->frequency);
+                           }
                        }
-                     stmt = cgraph_redirect_edge_call_stmt_to_callee (edge);
                    }
                  break;
 
@@ -2232,6 +2261,23 @@ copy_loops (bitmap blocks_to_copy,
     }
 }
 
+/* Call cgraph_redirect_edge_call_stmt_to_callee on all calls in BB */
+
+void
+redirect_all_calls (copy_body_data * id, basic_block bb)
+{
+  gimple_stmt_iterator si;
+  for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+    {
+      if (is_gimple_call (gsi_stmt (si)))
+       {
+         struct cgraph_edge *edge = cgraph_edge (id->dst_node, gsi_stmt (si));
+         if (edge)
+           cgraph_redirect_edge_call_stmt_to_callee (edge);
+       }
+    }
+}
+
 /* Make a copy of the body of FN so that it can be inserted inline in
    another function.  Walks FN via CFG, returns new fndecl.  */
 
@@ -2356,6 +2402,10 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency_scale,
            && bb->index != ENTRY_BLOCK
            && bb->index != EXIT_BLOCK)
          maybe_move_debug_stmts_to_successors (id, (basic_block) bb->aux);
+       /* Update call edge destinations.  This can not be done before loop
+          info is updated, because we may split basic blocks.  */
+       if (id->transform_call_graph_edges == CB_CGE_DUPLICATE)
+         redirect_all_calls (id, (basic_block)bb->aux);
        ((basic_block)bb->aux)->aux = NULL;
        bb->aux = NULL;
       }
@@ -2367,6 +2417,10 @@ copy_cfg_body (copy_body_data * id, gcov_type count, int frequency_scale,
       if (need_debug_cleanup)
        maybe_move_debug_stmts_to_successors (id, BASIC_BLOCK (last));
       BASIC_BLOCK (last)->aux = NULL;
+      /* Update call edge destinations.  This can not be done before loop
+        info is updated, because we may split basic blocks.  */
+      if (id->transform_call_graph_edges == CB_CGE_DUPLICATE)
+       redirect_all_calls (id, BASIC_BLOCK (last));
     }
   entry_block_map->aux = NULL;
   exit_block_map->aux = NULL;
@@ -4941,43 +4995,47 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id)
           gimple_stmt_iterator bsi;
 
           for (bsi = gsi_start_bb (b); !gsi_end_p (bsi); gsi_next (&bsi))
-           if (gimple_code (gsi_stmt (bsi)) == GIMPLE_CALL)
-             {
-               struct cgraph_edge *e;
-               struct cgraph_node *node;
+           {
+             struct cgraph_edge *e;
+             struct cgraph_node *node;
 
-               if ((e = cgraph_edge (id->dst_node, gsi_stmt (bsi))) != NULL)
+             ipa_remove_stmt_references ((symtab_node)id->dst_node, gsi_stmt (bsi));
+
+             if (gimple_code (gsi_stmt (bsi)) == GIMPLE_CALL
+                 &&(e = cgraph_edge (id->dst_node, gsi_stmt (bsi))) != NULL)
+               {
+                 if (!e->inline_failed)
+                   cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
+                 else
+                   cgraph_remove_edge (e);
+               }
+             if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES
+                 && id->dst_node->clones)
+               for (node = id->dst_node->clones; node != id->dst_node;)
                  {
-                   if (!e->inline_failed)
-                     cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
+                   ipa_remove_stmt_references ((symtab_node)node, gsi_stmt (bsi));
+                   if (gimple_code (gsi_stmt (bsi)) == GIMPLE_CALL
+                       && (e = cgraph_edge (node, gsi_stmt (bsi))) != NULL)
+                     {
+                       if (!e->inline_failed)
+                         cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
+                       else
+                         cgraph_remove_edge (e);
+                     }
+
+                   if (node->clones)
+                     node = node->clones;
+                   else if (node->next_sibling_clone)
+                     node = node->next_sibling_clone;
                    else
-                     cgraph_remove_edge (e);
+                     {
+                       while (node != id->dst_node && !node->next_sibling_clone)
+                         node = node->clone_of;
+                       if (node != id->dst_node)
+                         node = node->next_sibling_clone;
+                     }
                  }
-               if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES
-                   && id->dst_node->clones)
-                 for (node = id->dst_node->clones; node != id->dst_node;)
-                   {
-                     if ((e = cgraph_edge (node, gsi_stmt (bsi))) != NULL)
-                       {
-                         if (!e->inline_failed)
-                           cgraph_remove_node_and_inline_clones (e->callee, id->dst_node);
-                         else
-                           cgraph_remove_edge (e);
-                       }
-
-                     if (node->clones)
-                       node = node->clones;
-                     else if (node->next_sibling_clone)
-                       node = node->next_sibling_clone;
-                     else
-                       {
-                         while (node != id->dst_node && !node->next_sibling_clone)
-                           node = node->clone_of;
-                         if (node != id->dst_node)
-                           node = node->next_sibling_clone;
-                       }
-                   }
-             }
+           }
          delete_basic_block (b);
          changed = true;
        }
index 67bc2c8ea8e86423ed877d435280d724c3fa418a..c198c650a6013e6c2514c7b972eadb954ace5b23 100644 (file)
@@ -1248,7 +1248,7 @@ check_ic_target (gimple call_stmt, struct cgraph_node *target)
     old call
  */
 
-static gimple
+gimple
 gimple_ic (gimple icall_stmt, struct cgraph_node *direct_call,
           int prob, gcov_type count, gcov_type all)
 {
index 7030d90e988a58a499a4a8a022627aab62980fac..fca6bc565b52ce0b890fa5d94d0c31ff090609eb 100644 (file)
@@ -86,6 +86,8 @@ void gimple_move_stmt_histograms (struct function *, gimple, gimple);
 void verify_histograms (void);
 void free_histograms (void);
 void stringop_block_profile (gimple, unsigned int *, HOST_WIDE_INT *);
+gimple gimple_ic (gimple, struct cgraph_node *, int, gcov_type, gcov_type);
+
 
 /* In tree-profile.c.  */
 extern void gimple_init_edge_profiler (void);