Make cgraph_edge::resolve_speculation static

Message ID ri65zhrytbg.fsf@suse.cz
State New
Headers show
Series
  • Make cgraph_edge::resolve_speculation static
Related show

Commit Message

Martin Jambor Jan. 4, 2020, 6:23 p.m.
Hi,

throughout this year a few of us got burnt by the fact that
cgraph_edge::resolve_speculation method sometimes removed and
deallocated its this pointer, sometimes making the this pointer of a few
other methods of the class also suddenly invalid.

We postponed dealing with the issue because simply making these methods
static would be a bit ugly and hoped that someone would come with
something better.  Well, that did not happen and so the semi-mechanical
patch below does exactly that and fixing a few (potential) problems I
encountered: I made the iteration over edges in
function_and_variable_visibility cope with such edge removal and fixed
local variable hiding in cgraph_node::set_call_stmt_including_clones and
cgraph_node::create_edge_including_clones.  I did not unify calls to
resolve_speculation and make_direct in redirect_to_unreachable in this
patch but I believe that is a logical follow-up.

The patch has passed bootstrap and LTO bootstrap and testing on an
x86_64-linux.  What do you think?

Thanks,

Martin


2020-01-04  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (cgraph_edge): Make set_call_stmt, make_direct,
	resolve_speculation and redirect_call_stmt_to_callee static.  Change
	return type of set_call_stmt to cgraph_edge *.
	* auto-profile.c (afdo_indirect_call): Adjust call to
	redirect_call_stmt_to_callee.
	* cgraph.c (cgraph_edge::set_call_stmt): Make return cgraph-edge *,
	make the this pointer explicit, adjust self-recursive calls and the
	call top make_direct.  Return the resulting edge.
	(cgraph_edge::resolve_speculation): Make this pointer explicit.
	(cgraph_edge::make_direct): Likewise, adjust call to
	resolve_speculation.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise, also adjust
	call to set_call_stmt.
	(cgraph_update_edges_for_call_stmt_node): Update call to
	set_call_stmt.
	* cgraphclones.c (cgraph_node::set_call_stmt_including_clones):
	Renamed edge to master_edge.  Adjusted calls to set_call_stmt.
	(cgraph_node::create_edge_including_clones): Moved "first" definition
	of edge to the block where it was used.  Adjusted calls to
	set_call_stmt.
	* cgraphunit.c (walk_polymorphic_call_targets): Adjusted calls to
	make_direct and redirect_call_stmt_to_callee.
	* ipa-fnsummary.c (redirect_to_unreachable): Adjust calls to
	resolve_speculation and make_direct.
	* ipa-inline-transform.c (inline_transform): Adjust call to
	redirect_call_stmt_to_callee.
	(check_speculations_1):: Adjust call to resolve_speculation.
	* ipa-inline.c (resolve_noninline_speculation): Adjust call to
	resolve-speculation.
	(inline_small_functions): Adjust call to resolve_speculation.
	(ipa_inline): Likewise.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Adjust call to
	make_direct.
	* ipa-visibility.c (function_and_variable_visibility): Make iteration
	safe with regards to edge removal, adjust calls to
	redirect_call_stmt_to_callee.
	* ipa.c (walk_polymorphic_call_targets): Adjust calls to make_direct
	and redirect_call_stmt_to_callee.
	* multiple_target.c (create_dispatcher_calls): Adjust call to
	redirect_call_stmt_to_callee
	(redirect_to_specific_clone): Likewise.
	* tree-inline.c (copy_bb): Adjust call to set_call_stmt.
	(redirect_all_calls): Adjust call to redirect_call_stmt_to_callee.
---
 gcc/auto-profile.c         |   2 +-
 gcc/cgraph.c               | 113 +++++++++++++++++++------------------
 gcc/cgraph.h               |  35 +++++++-----
 gcc/cgraphclones.c         |  14 ++---
 gcc/cgraphunit.c           |  11 ++--
 gcc/ipa-fnsummary.c        |   4 +-
 gcc/ipa-inline-transform.c |   4 +-
 gcc/ipa-inline.c           |   6 +-
 gcc/ipa-prop.c             |   2 +-
 gcc/ipa-visibility.c       |   8 ++-
 gcc/ipa.c                  |   4 +-
 gcc/multiple_target.c      |   4 +-
 gcc/tree-inline.c          |   5 +-
 13 files changed, 110 insertions(+), 102 deletions(-)

-- 
2.24.0

Comments

Martin Liška Jan. 6, 2020, 10:48 a.m. | #1
On 1/4/20 7:23 PM, Martin Jambor wrote:
> Hi,

> 

> throughout this year a few of us got burnt by the fact that

> cgraph_edge::resolve_speculation method sometimes removed and

> deallocated its this pointer, sometimes making the this pointer of a few

> other methods of the class also suddenly invalid.


Hello.

Yes, I can confirm that I was victim at least once :)

> 

> We postponed dealing with the issue because simply making these methods

> static would be a bit ugly and hoped that someone would come with

> something better.  Well, that did not happen and so the semi-mechanical

> patch below does exactly that and fixing a few (potential) problems I

> encountered: I made the iteration over edges in

> function_and_variable_visibility cope with such edge removal and fixed

> local variable hiding in cgraph_node::set_call_stmt_including_clones and

> cgraph_node::create_edge_including_clones.  I did not unify calls to

> resolve_speculation and make_direct in redirect_to_unreachable in this

> patch but I believe that is a logical follow-up.

> 

> The patch has passed bootstrap and LTO bootstrap and testing on an

> x86_64-linux.  What do you think?


I've just read the patch and I see it as an improvement.

Thanks for it.
Martin

> 

> Thanks,

> 

> Martin
Jan Hubicka Jan. 6, 2020, 11:47 a.m. | #2
> Hi,

> 

> throughout this year a few of us got burnt by the fact that

> cgraph_edge::resolve_speculation method sometimes removed and

> deallocated its this pointer, sometimes making the this pointer of a few

> other methods of the class also suddenly invalid.

> 

> We postponed dealing with the issue because simply making these methods

> static would be a bit ugly and hoped that someone would come with

> something better.  Well, that did not happen and so the semi-mechanical

> patch below does exactly that and fixing a few (potential) problems I

> encountered: I made the iteration over edges in

> function_and_variable_visibility cope with such edge removal and fixed

> local variable hiding in cgraph_node::set_call_stmt_including_clones and

> cgraph_node::create_edge_including_clones.  I did not unify calls to

> resolve_speculation and make_direct in redirect_to_unreachable in this

> patch but I believe that is a logical follow-up.

> 

> The patch has passed bootstrap and LTO bootstrap and testing on an

> x86_64-linux.  What do you think?

It looks reasonable to me, but if we do not want member function to
deallocate THIS, then we probably want to make remove()
methods also static?

Honza
Martin Jambor Jan. 7, 2020, 9:20 a.m. | #3
Hi,

On Mon, Jan 06 2020, Jan Hubicka wrote:
>> Hi,

>> 

>> throughout this year a few of us got burnt by the fact that

>> cgraph_edge::resolve_speculation method sometimes removed and

>> deallocated its this pointer, sometimes making the this pointer of a few

>> other methods of the class also suddenly invalid.

>> 

>> We postponed dealing with the issue because simply making these methods

>> static would be a bit ugly and hoped that someone would come with

>> something better.  Well, that did not happen and so the semi-mechanical

>> patch below does exactly that and fixing a few (potential) problems I

>> encountered: I made the iteration over edges in

>> function_and_variable_visibility cope with such edge removal and fixed

>> local variable hiding in cgraph_node::set_call_stmt_including_clones and

>> cgraph_node::create_edge_including_clones.  I did not unify calls to

>> resolve_speculation and make_direct in redirect_to_unreachable in this

>> patch but I believe that is a logical follow-up.

>> 

>> The patch has passed bootstrap and LTO bootstrap and testing on an

>> x86_64-linux.  What do you think?

> It looks reasonable to me, but if we do not want member function to

> deallocate THIS, then we probably want to make remove()

> methods also static?

>


Right, remove is not quite as treacherous but I guess we do.  Updated
patch is below, it has also passed bootstrap, LTO bootstrap and testing
on an x86_64-linux.  (On a related note, varpool_node::remove should be
static too, but that is something for another patch.)

Thanks,

Martin


2020-01-06  Martin Jambor  <mjambor@suse.cz>

	* cgraph.h (cgraph_edge): Make remove, set_call_stmt, make_direct,
	resolve_speculation and redirect_call_stmt_to_callee static.  Change
	return type of set_call_stmt to cgraph_edge *.
	* auto-profile.c (afdo_indirect_call): Adjust call to
	redirect_call_stmt_to_callee.
	* cgraph.c (cgraph_edge::set_call_stmt): Make return cgraph-edge *,
	make the this pointer explicit, adjust self-recursive calls and the
	call top make_direct.  Return the resulting edge.
	(cgraph_edge::remove): Make this pointer explicit.
	(cgraph_edge::resolve_speculation): Likewise, adjust call to remove.
	(cgraph_edge::make_direct): Likewise, adjust call to
	resolve_speculation.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise, also adjust
	call to set_call_stmt.
	(cgraph_update_edges_for_call_stmt_node): Update call to
	set_call_stmt and remove.
	* cgraphclones.c (cgraph_node::set_call_stmt_including_clones):
	Renamed edge to master_edge.  Adjusted calls to set_call_stmt.
	(cgraph_node::create_edge_including_clones): Moved "first" definition
	of edge to the block where it was used.  Adjusted calls to
	set_call_stmt.
	(cgraph_node::remove_symbol_and_inline_clones): Adjust call to
	cgraph_edge::remove.
	* cgraphunit.c (walk_polymorphic_call_targets): Adjusted calls to
	make_direct and redirect_call_stmt_to_callee.
	* ipa-fnsummary.c (redirect_to_unreachable): Adjust calls to
	resolve_speculation and make_direct.
	* ipa-inline-transform.c (inline_transform): Adjust call to
	redirect_call_stmt_to_callee.
	(check_speculations_1):: Adjust call to resolve_speculation.
	* ipa-inline.c (resolve_noninline_speculation): Adjust call to
	resolve-speculation.
	(inline_small_functions): Adjust call to resolve_speculation.
	(ipa_inline): Likewise.
	* ipa-prop.c (ipa_make_edge_direct_to_target): Adjust call to
	make_direct.
	* ipa-visibility.c (function_and_variable_visibility): Make iteration
	safe with regards to edge removal, adjust calls to
	redirect_call_stmt_to_callee.
	* ipa.c (walk_polymorphic_call_targets): Adjust calls to make_direct
	and redirect_call_stmt_to_callee.
	* multiple_target.c (create_dispatcher_calls): Adjust call to
	redirect_call_stmt_to_callee
	(redirect_to_specific_clone): Likewise.
	* tree-cfgcleanup.c (delete_unreachable_blocks_update_callgraph):
	Adjust calls to cgraph_edge::remove.
	* tree-inline.c (copy_bb): Adjust call to set_call_stmt.
	(redirect_all_calls): Adjust call to redirect_call_stmt_to_callee.
	(expand_call_inline): Adjust call to cgraph_edge::remove.
---
 gcc/auto-profile.c         |   2 +-
 gcc/cgraph.c               | 129 +++++++++++++++++++------------------
 gcc/cgraph.h               |  39 ++++++-----
 gcc/cgraphclones.c         |  16 ++---
 gcc/cgraphunit.c           |  11 ++--
 gcc/ipa-fnsummary.c        |   4 +-
 gcc/ipa-inline-transform.c |   4 +-
 gcc/ipa-inline.c           |   6 +-
 gcc/ipa-prop.c             |   2 +-
 gcc/ipa-visibility.c       |   8 ++-
 gcc/ipa.c                  |   4 +-
 gcc/multiple_target.c      |   4 +-
 gcc/tree-cfgcleanup.c      |   4 +-
 gcc/tree-inline.c          |   7 +-
 14 files changed, 124 insertions(+), 116 deletions(-)

diff --git a/gcc/auto-profile.c b/gcc/auto-profile.c
index 6aca2f29022..fc0a370dc27 100644
--- a/gcc/auto-profile.c
+++ b/gcc/auto-profile.c
@@ -1055,7 +1055,7 @@ afdo_indirect_call (gimple_stmt_iterator *gsi, const icall_target_map &map,
   struct cgraph_edge *new_edge
       = indirect_edge->make_speculative (direct_call,
 					 profile_count::uninitialized ());
-  new_edge->redirect_call_stmt_to_callee ();
+  cgraph_edge::redirect_call_stmt_to_callee (new_edge);
   gimple_remove_histogram_value (cfun, stmt, hist);
   inline_call (new_edge, true, NULL, NULL, false);
 }
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 13238d3d442..a502fe92ad9 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -767,41 +767,41 @@ cgraph_node::get_edge (gimple *call_stmt)
 }
 
 
-/* Change field call_stmt of edge to NEW_STMT.
-   If UPDATE_SPECULATIVE and E is any component of speculative
-   edge, then update all components.  */
+/* 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.
+   Speculations can be resolved in the process and EDGE can be removed and
+   deallocated.  Return the edge that now represents the call.  */
 
-void
-cgraph_edge::set_call_stmt (gcall *new_stmt, bool update_speculative)
+cgraph_edge *
+cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt,
+			    bool update_speculative)
 {
   tree decl;
 
   /* Speculative edges has three component, update all of them
      when asked to.  */
-  if (update_speculative && speculative)
+  if (update_speculative && e->speculative)
     {
       cgraph_edge *direct, *indirect;
       ipa_ref *ref;
+      bool e_indirect = e->indirect_unknown_callee;
 
-      speculative_call_info (direct, indirect, ref);
-      direct->set_call_stmt (new_stmt, false);
-      indirect->set_call_stmt (new_stmt, false);
+      e->speculative_call_info (direct, indirect, ref);
       ref->stmt = new_stmt;
-      return;
+      cgraph_edge *d2 = set_call_stmt (direct, new_stmt, false);
+      gcc_assert (direct == d2);
+      indirect = set_call_stmt (indirect, new_stmt, false);
+      return e_indirect ? indirect : direct;
     }
 
   /* Only direct speculative edges go to call_site_hash.  */
-  if (caller->call_site_hash
-      && (!speculative || !indirect_unknown_callee))
-    {
-      caller->call_site_hash->remove_elt_with_hash
-	(call_stmt, cgraph_edge_hasher::hash (call_stmt));
-    }
-
-  cgraph_edge *e = this;
+  if (e->caller->call_site_hash
+      && (!e->speculative || !e->indirect_unknown_callee))
+    e->caller->call_site_hash->remove_elt_with_hash
+      (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt));
 
-  call_stmt = new_stmt;
-  if (indirect_unknown_callee
+  e->call_stmt = new_stmt;
+  if (e->indirect_unknown_callee
       && (decl = gimple_call_fndecl (new_stmt)))
     {
       /* Constant propagation (and possibly also inlining?) can turn an
@@ -809,13 +809,14 @@ cgraph_edge::set_call_stmt (gcall *new_stmt, bool update_speculative)
       cgraph_node *new_callee = cgraph_node::get (decl);
 
       gcc_checking_assert (new_callee);
-      e = make_direct (new_callee);
+      e = make_direct (e, new_callee);
     }
 
   function *fun = DECL_STRUCT_FUNCTION (e->caller->decl);
   e->can_throw_external = stmt_can_throw_external (fun, new_stmt);
   if (e->caller->call_site_hash)
     cgraph_add_edge_to_call_site_hash (e);
+  return e;
 }
 
 /* Allocate a cgraph_edge structure and fill it with data according to the
@@ -1011,20 +1012,20 @@ symbol_table::free_edge (cgraph_edge *e)
 /* Remove the edge in the cgraph.  */
 
 void
-cgraph_edge::remove (void)
+cgraph_edge::remove (cgraph_edge *edge)
 {
   /* Call all edge removal hooks.  */
-  symtab->call_edge_removal_hooks (this);
+  symtab->call_edge_removal_hooks (edge);
 
-  if (!indirect_unknown_callee)
+  if (!edge->indirect_unknown_callee)
     /* Remove from callers list of the callee.  */
-    remove_callee ();
+    edge->remove_callee ();
 
   /* Remove from callees list of the callers.  */
-  remove_caller ();
+  edge->remove_caller ();
 
   /* Put the edge onto the free list.  */
-  symtab->free_edge (this);
+  symtab->free_edge (edge);
 }
 
 /* Turn edge into speculative call calling N2. Update
@@ -1135,14 +1136,15 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
   gcc_assert (e && e2 && ref);
 }
 
-/* Speculative call edge turned out to be direct call to CALLEE_DECL.
-   Remove the speculative call sequence and return edge representing the call.
-   It is up to caller to redirect the call as appropriate. */
+/* Speculative call EDGE turned out to be direct call to CALLEE_DECL.  Remove
+   the speculative call sequence and return edge representing the call, the
+   original EDGE can be removed and deallocated.  It is up to caller to
+   redirect the call as appropriate.  Return the edge that now represents the
+   call.  */
 
 cgraph_edge *
-cgraph_edge::resolve_speculation (tree callee_decl)
+cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
 {
-  cgraph_edge *edge = this;
   cgraph_edge *e2;
   ipa_ref *ref;
 
@@ -1186,7 +1188,7 @@ cgraph_edge::resolve_speculation (tree callee_decl)
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
-    e2->remove ();
+    remove (e2);
   else
     e2->callee->remove_symbol_and_inline_clones ();
   if (edge->caller->call_site_hash)
@@ -1195,43 +1197,42 @@ cgraph_edge::resolve_speculation (tree callee_decl)
 }
 
 /* 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.  */
+   CALLEE.  Speculations can be resolved in the process and EDGE can be removed
+   and deallocated.  Return the edge that now represents the call.  */
 
 cgraph_edge *
-cgraph_edge::make_direct (cgraph_node *callee)
+cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
 {
-  cgraph_edge *edge = this;
-  gcc_assert (indirect_unknown_callee);
+  gcc_assert (edge->indirect_unknown_callee);
 
   /* If we are redirecting speculative call, make it non-speculative.  */
-  if (indirect_unknown_callee && speculative)
+  if (edge->speculative)
     {
-      edge = edge->resolve_speculation (callee->decl);
+      edge = resolve_speculation (edge, callee->decl);
 
       /* On successful speculation just return the pre existing direct edge.  */
       if (!edge->indirect_unknown_callee)
         return edge;
     }
 
-  indirect_unknown_callee = 0;
-  ggc_free (indirect_info);
-  indirect_info = NULL;
+  edge->indirect_unknown_callee = 0;
+  ggc_free (edge->indirect_info);
+  edge->indirect_info = NULL;
 
   /* Get the edge out of the indirect edge list. */
-  if (prev_callee)
-    prev_callee->next_callee = next_callee;
-  if (next_callee)
-    next_callee->prev_callee = prev_callee;
-  if (!prev_callee)
-    caller->indirect_calls = next_callee;
+  if (edge->prev_callee)
+    edge->prev_callee->next_callee = edge->next_callee;
+  if (edge->next_callee)
+    edge->next_callee->prev_callee = edge->prev_callee;
+  if (!edge->prev_callee)
+    edge->caller->indirect_calls = edge->next_callee;
 
   /* Put it into the normal callee list */
-  prev_callee = NULL;
-  next_callee = caller->callees;
-  if (caller->callees)
-    caller->callees->prev_callee = edge;
-  caller->callees = edge;
+  edge->prev_callee = NULL;
+  edge->next_callee = edge->caller->callees;
+  if (edge->caller->callees)
+    edge->caller->callees->prev_callee = edge;
+  edge->caller->callees = edge;
 
   /* Insert to callers list of the new callee.  */
   edge->set_callee (callee);
@@ -1242,13 +1243,12 @@ cgraph_edge::make_direct (cgraph_node *callee)
 }
 
 /* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
+   associated with E so that it corresponds to the edge callee.  Speculations
+   can be resolved in the process and EDGE can be removed and deallocated.  */
 
 gimple *
-cgraph_edge::redirect_call_stmt_to_callee (void)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
 {
-  cgraph_edge *e = this;
-
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
   gimple_stmt_iterator gsi;
@@ -1263,7 +1263,7 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
       /* If there already is an direct call (i.e. as a result of inliner's
 	 substitution), forget about speculating.  */
       if (decl)
-	e = e->resolve_speculation (decl);
+	e = resolve_speculation (e, decl);
       else
 	{
 	  /* Expand speculation into GIMPLE code.  */
@@ -1455,8 +1455,8 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
 	  if (new_stmt && is_gimple_call (new_stmt) && e->callee
 	      && fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE))
 	    {
-              node->get_edge (old_stmt)->set_call_stmt
-		 (as_a <gcall *> (new_stmt));
+	      cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+					  as_a <gcall *> (new_stmt));
 	      return;
 	    }
 	  /* See if the edge is already there and has the correct callee.  It
@@ -1470,7 +1470,7 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
 		  if (callee->decl == new_call
 		      || callee->former_clone_of == new_call)
 		    {
-		      e->set_call_stmt (as_a <gcall *> (new_stmt));
+		      cgraph_edge::set_call_stmt (e, as_a <gcall *> (new_stmt));
 		      return;
 		    }
 		  callee = callee->clone_of;
@@ -1482,7 +1482,7 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
 	     attached to edge is invalid.  */
 	  count = e->count;
  	  if (e->indirect_unknown_callee || e->inline_failed)
-	    e->remove ();
+	    cgraph_edge::remove (e);
 	  else
 	    e->callee->remove_symbol_and_inline_clones ();
 	}
@@ -1502,7 +1502,8 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
     }
   /* We only updated the call stmt; update pointer in cgraph edge..  */
   else if (old_stmt != new_stmt)
-    node->get_edge (old_stmt)->set_call_stmt (as_a <gcall *> (new_stmt));
+    cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+				as_a <gcall *> (new_stmt));
 }
 
 /* Update or remove the corresponding cgraph edge if a GIMPLE_CALL
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index c5cee2274a0..71cd902312e 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1736,13 +1736,15 @@ public:
   friend struct cgraph_node;
   friend class symbol_table;
 
-  /* Remove the edge in the cgraph.  */
-  void remove (void);
+  /* Remove EDGE from the cgraph.  */
+  static void remove (cgraph_edge *edge);
 
-  /* Change field call_stmt of edge to NEW_STMT.
-     If UPDATE_SPECULATIVE and E is any component of speculative
-     edge, then update all components.  */
-  void set_call_stmt (gcall *new_stmt, bool update_speculative = true);
+  /* 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.
+     Speculations can be resolved in the process and EDGE can be removed and
+     deallocated.  Return the edge that now represents the call.  */
+  static cgraph_edge *set_call_stmt (cgraph_edge *e, gcall *new_stmt,
+				     bool update_speculative = true);
 
   /* Redirect callee of the edge to N.  The function does not update underlying
      call expression.  */
@@ -1755,10 +1757,10 @@ public:
   void redirect_callee_duplicating_thunks (cgraph_node *n);
 
   /* 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.  */
-  cgraph_edge *make_direct (cgraph_node *callee);
+     CALLEE.  Speculations can be resolved in the process and EDGE can be
+     removed and deallocated.  Return the edge that now represents the
+     call.  */
+  static cgraph_edge *make_direct (cgraph_edge *edge, cgraph_node *callee);
 
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
@@ -1769,14 +1771,19 @@ public:
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
 			      ipa_ref *&reference);
 
-  /* Speculative call edge turned out to be direct call to CALLEE_DECL.
-     Remove the speculative call sequence and return edge representing the call.
-     It is up to caller to redirect the call as appropriate. */
-  cgraph_edge *resolve_speculation (tree callee_decl = NULL);
+  /* Speculative call edge turned out to be direct call to CALLEE_DECL.  Remove
+     the speculative call sequence and return edge representing the call, the
+     original EDGE can be removed and deallocated.  It is up to caller to
+     redirect the call as appropriate.  Return the edge that now represents the
+     call.  */
+  static cgraph_edge *resolve_speculation (cgraph_edge *edge,
+					   tree callee_decl = NULL);
 
   /* If necessary, change the function declaration in the call statement
-     associated with the edge so that it corresponds to the edge callee.  */
-  gimple *redirect_call_stmt_to_callee (void);
+     associated with edge E so that it corresponds to the edge callee.
+     Speculations can be resolved in the process and EDGE can be removed and
+     deallocated.  */
+  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
 
   /* Create clone of edge in the node N represented
      by CALL_EXPR the callgraph.  */
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index bd44063a1ac..d2c506e5a28 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -744,10 +744,10 @@ cgraph_node::set_call_stmt_including_clones (gimple *old_stmt,
 					     bool update_speculative)
 {
   cgraph_node *node;
-  cgraph_edge *edge = get_edge (old_stmt);
+  cgraph_edge *master_edge = get_edge (old_stmt);
 
-  if (edge)
-    edge->set_call_stmt (new_stmt, update_speculative);
+  if (master_edge)
+    cgraph_edge::set_call_stmt (master_edge, new_stmt, update_speculative);
 
   node = clones;
   if (node)
@@ -756,7 +756,8 @@ cgraph_node::set_call_stmt_including_clones (gimple *old_stmt,
 	cgraph_edge *edge = node->get_edge (old_stmt);
 	if (edge)
 	  {
-	    edge->set_call_stmt (new_stmt, update_speculative);
+	    edge = cgraph_edge::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.  */
@@ -800,11 +801,10 @@ cgraph_node::create_edge_including_clones (cgraph_node *callee,
 					   cgraph_inline_failed_t reason)
 {
   cgraph_node *node;
-  cgraph_edge *edge;
 
   if (!get_edge (stmt))
     {
-      edge = create_edge (callee, stmt, count);
+      cgraph_edge *edge = create_edge (callee, stmt, count);
       edge->inline_failed = reason;
     }
 
@@ -821,7 +821,7 @@ cgraph_node::create_edge_including_clones (cgraph_node *callee,
 	     call in the clone or we are processing clones of unreachable
 	     master where edges has been removed.  */
 	  if (edge)
-	    edge->set_call_stmt (stmt);
+	    edge = cgraph_edge::set_call_stmt (edge, stmt);
 	  else if (! node->get_edge (stmt))
 	    {
 	      edge = node->create_edge (callee, stmt, count);
@@ -855,7 +855,7 @@ cgraph_node::remove_symbol_and_inline_clones (cgraph_node *forbidden_node)
 
   if (this == forbidden_node)
     {
-      callers->remove ();
+      cgraph_edge::remove (callers);
       return true;
     }
   for (e = callees; e; e = next)
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 6201444694c..471c9458112 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1023,16 +1023,13 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 			       edge->caller->name (), target->name ());
 	    }
 
-	  edge->make_direct (target);
-	  edge->redirect_call_stmt_to_callee ();
+	  edge = cgraph_edge::make_direct (edge, target);
+	  gimple *new_call = cgraph_edge::redirect_call_stmt_to_callee (edge);
 
 	  if (symtab->dump_file)
 	    {
-	      fprintf (symtab->dump_file,
-		       "Devirtualized as: ");
-	      print_gimple_stmt (symtab->dump_file,
-				 edge->call_stmt, 0,
-				 TDF_SLIM);
+	      fprintf (symtab->dump_file, "Devirtualized as: ");
+	      print_gimple_stmt (symtab->dump_file, new_call, 0, TDF_SLIM);
 	    }
 	}
     }
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index fa01cb6c083..866e8702ccb 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -245,9 +245,9 @@ redirect_to_unreachable (struct cgraph_edge *e)
 		      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
   if (e->speculative)
-    e = e->resolve_speculation (target->decl);
+    e = cgraph_edge::resolve_speculation (e, target->decl);
   else if (!e->callee)
-    e->make_direct (target);
+    e = cgraph_edge::make_direct (e, target);
   else
     e->redirect_callee (target);
   class ipa_call_summary *es = ipa_call_summaries->get (e);
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
index c53cd423b75..5c34b6dcb50 100644
--- a/gcc/ipa-inline-transform.c
+++ b/gcc/ipa-inline-transform.c
@@ -258,7 +258,7 @@ check_speculations_1 (cgraph_node *n, vec<cgraph_edge *> *new_edges,
 	    edge_set->add (new_edges->pop ());
 	  edge_set->remove (e);
 
-	  e->resolve_speculation (NULL);
+	  cgraph_edge::resolve_speculation (e, NULL);
 	  speculation_removed = true;
 	}
       else if (!e->inline_failed)
@@ -712,7 +712,7 @@ inline_transform (struct cgraph_node *node)
       if (!e->inline_failed)
 	has_inline = true;
       next = e->next_callee;
-      e->redirect_call_stmt_to_callee ();
+      cgraph_edge::redirect_call_stmt_to_callee (e);
     }
   node->remove_all_references ();
 
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 3b68fc47d01..6f01e0441e1 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1835,7 +1835,7 @@ resolve_noninline_speculation (edge_heap_t *edge_heap, struct cgraph_edge *edge)
 
       if (edge->count.ipa ().initialized_p ())
         spec_rem += edge->count.ipa ();
-      edge->resolve_speculation ();
+      cgraph_edge::resolve_speculation (edge);
       reset_edge_caches (where);
       ipa_update_overall_fn_summary (where);
       update_caller_keys (edge_heap, where,
@@ -1999,7 +1999,7 @@ inline_small_functions (void)
 	    if (edge->speculative
 		&& !speculation_useful_p (edge, edge->aux != NULL))
 	      {
-		edge->resolve_speculation ();
+		cgraph_edge::resolve_speculation (edge);
 		update = true;
 	      }
 	  }
@@ -2736,7 +2736,7 @@ ipa_inline (void)
 		{
 		  if (edge->count.ipa ().initialized_p ())
 		    spec_rem += edge->count.ipa ();
-		  edge->resolve_speculation ();
+		  cgraph_edge::resolve_speculation (edge);
 		  update = true;
 		  remove_functions = true;
 		}
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 035730d180d..fcb13dfbac4 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3299,7 +3299,7 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
   if (!speculative)
     {
       struct cgraph_edge *orig = ie;
-      ie = ie->make_direct (callee);
+      ie = cgraph_edge::make_direct (ie, callee);
       /* If we resolved speculative edge the cost is already up to date
 	 for direct call (adjusted by inline_edge_duplication_hook).  */
       if (ie == orig)
diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c
index 67d500b759b..72d7d3d4819 100644
--- a/gcc/ipa-visibility.c
+++ b/gcc/ipa-visibility.c
@@ -632,8 +632,10 @@ function_and_variable_visibility (bool whole_program)
 	continue;
 
       cgraph_node *alias = 0;
-      for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      cgraph_edge *next_edge;
+      for (cgraph_edge *e = node->callees; e; e = next_edge)
 	{
+	  next_edge = e->next_callee;
 	  /* Recursive function calls usually can't be interposed.  */
 
 	  if (!e->recursive_p ())
@@ -649,7 +651,7 @@ function_and_variable_visibility (bool whole_program)
 	  if (gimple_has_body_p (e->caller->decl))
 	    {
 	      push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
-	      e->redirect_call_stmt_to_callee ();
+	      cgraph_edge::redirect_call_stmt_to_callee (e);
 	      pop_cfun ();
 	    }
 	}
@@ -780,7 +782,7 @@ function_and_variable_visibility (bool whole_program)
 		  if (gimple_has_body_p (e->caller->decl))
 		    {
 		      push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
-		      e->redirect_call_stmt_to_callee ();
+		      cgraph_edge::redirect_call_stmt_to_callee (e);
 		      pop_cfun ();
 		    }
 		}
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 6129633b303..1f6c2b2a8fd 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -242,12 +242,12 @@ walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 			       edge->caller->dump_name (),
 			       target->dump_name ());
 	    }
-	  edge = edge->make_direct (target);
+	  edge = cgraph_edge::make_direct (edge, target);
 	  if (ipa_fn_summaries)
 	    ipa_update_overall_fn_summary (node->inlined_to
 					   ? node->inlined_to : node);
 	  else if (edge->call_stmt)
-	    edge->redirect_call_stmt_to_callee ();
+	    cgraph_edge::redirect_call_stmt_to_callee (edge);
 	}
     }
 }
diff --git a/gcc/multiple_target.c b/gcc/multiple_target.c
index 36fe58105df..cccfd2774db 100644
--- a/gcc/multiple_target.c
+++ b/gcc/multiple_target.c
@@ -126,7 +126,7 @@ create_dispatcher_calls (struct cgraph_node *node)
       FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
 	{
 	  e->redirect_callee (inode);
-	  e->redirect_call_stmt_to_callee ();
+	  cgraph_edge::redirect_call_stmt_to_callee (e);
 	}
 
       /* Redirect references.  */
@@ -501,7 +501,7 @@ redirect_to_specific_clone (cgraph_node *node)
 	      if (attribute_list_equal (attr_target, attr_target2))
 		{
 		  e->redirect_callee (callee);
-		  e->redirect_call_stmt_to_callee ();
+		  cgraph_edge::redirect_call_stmt_to_callee (e);
 		  break;
 		}
 	    }
diff --git a/gcc/tree-cfgcleanup.c b/gcc/tree-cfgcleanup.c
index a7fc0a6166d..4763cd45a1c 100644
--- a/gcc/tree-cfgcleanup.c
+++ b/gcc/tree-cfgcleanup.c
@@ -1598,7 +1598,7 @@ delete_unreachable_blocks_update_callgraph (cgraph_node *dst_node,
 		  if (!e->inline_failed)
 		    e->callee->remove_symbol_and_inline_clones (dst_node);
 		  else
-		    e->remove ();
+		    cgraph_edge::remove (e);
 		}
 	      if (update_clones && dst_node->clones)
 		for (node = dst_node->clones; node != dst_node;)
@@ -1610,7 +1610,7 @@ delete_unreachable_blocks_update_callgraph (cgraph_node *dst_node,
 			if (!e->inline_failed)
 			  e->callee->remove_symbol_and_inline_clones (dst_node);
 			else
-			  e->remove ();
+			  cgraph_edge::remove (e);
 		      }
 
 		    if (node->clones)
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 21a45255051..d1cf6cb6c46 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2225,7 +2225,7 @@ copy_bb (copy_body_data *id, basic_block bb,
 		case CB_CGE_MOVE:
 		  edge = id->dst_node->get_edge (orig_stmt);
 		  if (edge)
-		    edge->set_call_stmt (call_stmt);
+		    edge = cgraph_edge::set_call_stmt (edge, call_stmt);
 		  break;
 
 		default:
@@ -2899,7 +2899,8 @@ redirect_all_calls (copy_body_data * id, basic_block bb)
 	  struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
 	  if (edge)
 	    {
-	      gimple *new_stmt = edge->redirect_call_stmt_to_callee ();
+	      gimple *new_stmt
+		= cgraph_edge::redirect_call_stmt_to_callee (edge);
 	      /* If IPA-SRA transformation, run as part of edge redirection,
 		 removed the LHS because it is unused, save it to
 		 killed_new_ssa_names so that we can prune it from debug
@@ -4750,7 +4751,7 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
       tree op;
       gimple_stmt_iterator iter = gsi_for_stmt (stmt);
 
-      cg_edge->remove ();
+      cgraph_edge::remove (cg_edge);
       edge = id->src_node->callees->clone (id->dst_node, call_stmt,
 		   		           gimple_uid (stmt),
 				   	   profile_count::one (),
-- 
2.24.1

Patch

diff --git a/gcc/auto-profile.c b/gcc/auto-profile.c
index 6aca2f29022..fc0a370dc27 100644
--- a/gcc/auto-profile.c
+++ b/gcc/auto-profile.c
@@ -1055,7 +1055,7 @@  afdo_indirect_call (gimple_stmt_iterator *gsi, const icall_target_map &map,
   struct cgraph_edge *new_edge
       = indirect_edge->make_speculative (direct_call,
 					 profile_count::uninitialized ());
-  new_edge->redirect_call_stmt_to_callee ();
+  cgraph_edge::redirect_call_stmt_to_callee (new_edge);
   gimple_remove_histogram_value (cfun, stmt, hist);
   inline_call (new_edge, true, NULL, NULL, false);
 }
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 13238d3d442..b36df5166fa 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -767,41 +767,41 @@  cgraph_node::get_edge (gimple *call_stmt)
 }
 
 
-/* Change field call_stmt of edge to NEW_STMT.
-   If UPDATE_SPECULATIVE and E is any component of speculative
-   edge, then update all components.  */
+/* 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.
+   Speculations can be resolved in the process and EDGE can be removed and
+   deallocated.  Return the edge that now represents the call.  */
 
-void
-cgraph_edge::set_call_stmt (gcall *new_stmt, bool update_speculative)
+cgraph_edge *
+cgraph_edge::set_call_stmt (cgraph_edge *e, gcall *new_stmt,
+			    bool update_speculative)
 {
   tree decl;
 
   /* Speculative edges has three component, update all of them
      when asked to.  */
-  if (update_speculative && speculative)
+  if (update_speculative && e->speculative)
     {
       cgraph_edge *direct, *indirect;
       ipa_ref *ref;
+      bool e_indirect = e->indirect_unknown_callee;
 
-      speculative_call_info (direct, indirect, ref);
-      direct->set_call_stmt (new_stmt, false);
-      indirect->set_call_stmt (new_stmt, false);
+      e->speculative_call_info (direct, indirect, ref);
       ref->stmt = new_stmt;
-      return;
+      cgraph_edge *d2 = set_call_stmt (direct, new_stmt, false);
+      gcc_assert (direct == d2);
+      indirect = set_call_stmt (indirect, new_stmt, false);
+      return e_indirect ? indirect : direct;
     }
 
   /* Only direct speculative edges go to call_site_hash.  */
-  if (caller->call_site_hash
-      && (!speculative || !indirect_unknown_callee))
-    {
-      caller->call_site_hash->remove_elt_with_hash
-	(call_stmt, cgraph_edge_hasher::hash (call_stmt));
-    }
-
-  cgraph_edge *e = this;
+  if (e->caller->call_site_hash
+      && (!e->speculative || !e->indirect_unknown_callee))
+    e->caller->call_site_hash->remove_elt_with_hash
+      (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt));
 
-  call_stmt = new_stmt;
-  if (indirect_unknown_callee
+  e->call_stmt = new_stmt;
+  if (e->indirect_unknown_callee
       && (decl = gimple_call_fndecl (new_stmt)))
     {
       /* Constant propagation (and possibly also inlining?) can turn an
@@ -809,13 +809,14 @@  cgraph_edge::set_call_stmt (gcall *new_stmt, bool update_speculative)
       cgraph_node *new_callee = cgraph_node::get (decl);
 
       gcc_checking_assert (new_callee);
-      e = make_direct (new_callee);
+      e = make_direct (e, new_callee);
     }
 
   function *fun = DECL_STRUCT_FUNCTION (e->caller->decl);
   e->can_throw_external = stmt_can_throw_external (fun, new_stmt);
   if (e->caller->call_site_hash)
     cgraph_add_edge_to_call_site_hash (e);
+  return e;
 }
 
 /* Allocate a cgraph_edge structure and fill it with data according to the
@@ -1135,14 +1136,15 @@  cgraph_edge::speculative_call_info (cgraph_edge *&direct,
   gcc_assert (e && e2 && ref);
 }
 
-/* Speculative call edge turned out to be direct call to CALLEE_DECL.
-   Remove the speculative call sequence and return edge representing the call.
-   It is up to caller to redirect the call as appropriate. */
+/* Speculative call EDGE turned out to be direct call to CALLEE_DECL.  Remove
+   the speculative call sequence and return edge representing the call, the
+   original EDGE can be removed and deallocated.  It is up to caller to
+   redirect the call as appropriate.  Return the edge that now represents the
+   call.  */
 
 cgraph_edge *
-cgraph_edge::resolve_speculation (tree callee_decl)
+cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
 {
-  cgraph_edge *edge = this;
   cgraph_edge *e2;
   ipa_ref *ref;
 
@@ -1195,43 +1197,42 @@  cgraph_edge::resolve_speculation (tree callee_decl)
 }
 
 /* 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.  */
+   CALLEE.  Speculations can be resolved in the process and EDGE can be removed
+   and deallocated.  Return the edge that now represents the call.  */
 
 cgraph_edge *
-cgraph_edge::make_direct (cgraph_node *callee)
+cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
 {
-  cgraph_edge *edge = this;
-  gcc_assert (indirect_unknown_callee);
+  gcc_assert (edge->indirect_unknown_callee);
 
   /* If we are redirecting speculative call, make it non-speculative.  */
-  if (indirect_unknown_callee && speculative)
+  if (edge->speculative)
     {
-      edge = edge->resolve_speculation (callee->decl);
+      edge = resolve_speculation (edge, callee->decl);
 
       /* On successful speculation just return the pre existing direct edge.  */
       if (!edge->indirect_unknown_callee)
         return edge;
     }
 
-  indirect_unknown_callee = 0;
-  ggc_free (indirect_info);
-  indirect_info = NULL;
+  edge->indirect_unknown_callee = 0;
+  ggc_free (edge->indirect_info);
+  edge->indirect_info = NULL;
 
   /* Get the edge out of the indirect edge list. */
-  if (prev_callee)
-    prev_callee->next_callee = next_callee;
-  if (next_callee)
-    next_callee->prev_callee = prev_callee;
-  if (!prev_callee)
-    caller->indirect_calls = next_callee;
+  if (edge->prev_callee)
+    edge->prev_callee->next_callee = edge->next_callee;
+  if (edge->next_callee)
+    edge->next_callee->prev_callee = edge->prev_callee;
+  if (!edge->prev_callee)
+    edge->caller->indirect_calls = edge->next_callee;
 
   /* Put it into the normal callee list */
-  prev_callee = NULL;
-  next_callee = caller->callees;
-  if (caller->callees)
-    caller->callees->prev_callee = edge;
-  caller->callees = edge;
+  edge->prev_callee = NULL;
+  edge->next_callee = edge->caller->callees;
+  if (edge->caller->callees)
+    edge->caller->callees->prev_callee = edge;
+  edge->caller->callees = edge;
 
   /* Insert to callers list of the new callee.  */
   edge->set_callee (callee);
@@ -1242,13 +1243,12 @@  cgraph_edge::make_direct (cgraph_node *callee)
 }
 
 /* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
+   associated with E so that it corresponds to the edge callee.  Speculations
+   can be resolved in the process and EDGE can be removed and deallocated.  */
 
 gimple *
-cgraph_edge::redirect_call_stmt_to_callee (void)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
 {
-  cgraph_edge *e = this;
-
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
   gimple_stmt_iterator gsi;
@@ -1263,7 +1263,7 @@  cgraph_edge::redirect_call_stmt_to_callee (void)
       /* If there already is an direct call (i.e. as a result of inliner's
 	 substitution), forget about speculating.  */
       if (decl)
-	e = e->resolve_speculation (decl);
+	e = resolve_speculation (e, decl);
       else
 	{
 	  /* Expand speculation into GIMPLE code.  */
@@ -1455,8 +1455,8 @@  cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
 	  if (new_stmt && is_gimple_call (new_stmt) && e->callee
 	      && fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE))
 	    {
-              node->get_edge (old_stmt)->set_call_stmt
-		 (as_a <gcall *> (new_stmt));
+	      cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+					  as_a <gcall *> (new_stmt));
 	      return;
 	    }
 	  /* See if the edge is already there and has the correct callee.  It
@@ -1470,7 +1470,7 @@  cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
 		  if (callee->decl == new_call
 		      || callee->former_clone_of == new_call)
 		    {
-		      e->set_call_stmt (as_a <gcall *> (new_stmt));
+		      cgraph_edge::set_call_stmt (e, as_a <gcall *> (new_stmt));
 		      return;
 		    }
 		  callee = callee->clone_of;
@@ -1502,7 +1502,8 @@  cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
     }
   /* We only updated the call stmt; update pointer in cgraph edge..  */
   else if (old_stmt != new_stmt)
-    node->get_edge (old_stmt)->set_call_stmt (as_a <gcall *> (new_stmt));
+    cgraph_edge::set_call_stmt (node->get_edge (old_stmt),
+				as_a <gcall *> (new_stmt));
 }
 
 /* Update or remove the corresponding cgraph edge if a GIMPLE_CALL
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index c5cee2274a0..1aa737f44b4 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1739,10 +1739,12 @@  public:
   /* Remove the edge in the cgraph.  */
   void remove (void);
 
-  /* Change field call_stmt of edge to NEW_STMT.
-     If UPDATE_SPECULATIVE and E is any component of speculative
-     edge, then update all components.  */
-  void set_call_stmt (gcall *new_stmt, bool update_speculative = true);
+  /* 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.
+     Speculations can be resolved in the process and EDGE can be removed and
+     deallocated.  Return the edge that now represents the call.  */
+  static cgraph_edge *set_call_stmt (cgraph_edge *e, gcall *new_stmt,
+				     bool update_speculative = true);
 
   /* Redirect callee of the edge to N.  The function does not update underlying
      call expression.  */
@@ -1755,10 +1757,10 @@  public:
   void redirect_callee_duplicating_thunks (cgraph_node *n);
 
   /* 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.  */
-  cgraph_edge *make_direct (cgraph_node *callee);
+     CALLEE.  Speculations can be resolved in the process and EDGE can be
+     removed and deallocated.  Return the edge that now represents the
+     call.  */
+  static cgraph_edge *make_direct (cgraph_edge *edge, cgraph_node *callee);
 
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
@@ -1769,14 +1771,19 @@  public:
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
 			      ipa_ref *&reference);
 
-  /* Speculative call edge turned out to be direct call to CALLEE_DECL.
-     Remove the speculative call sequence and return edge representing the call.
-     It is up to caller to redirect the call as appropriate. */
-  cgraph_edge *resolve_speculation (tree callee_decl = NULL);
+  /* Speculative call edge turned out to be direct call to CALLEE_DECL.  Remove
+     the speculative call sequence and return edge representing the call, the
+     original EDGE can be removed and deallocated.  It is up to caller to
+     redirect the call as appropriate.  Return the edge that now represents the
+     call.  */
+  static cgraph_edge *resolve_speculation (cgraph_edge *edge,
+					   tree callee_decl = NULL);
 
   /* If necessary, change the function declaration in the call statement
-     associated with the edge so that it corresponds to the edge callee.  */
-  gimple *redirect_call_stmt_to_callee (void);
+     associated with edge E so that it corresponds to the edge callee.
+     Speculations can be resolved in the process and EDGE can be removed and
+     deallocated.  */
+  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
 
   /* Create clone of edge in the node N represented
      by CALL_EXPR the callgraph.  */
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index bd44063a1ac..07df84a2337 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -744,10 +744,10 @@  cgraph_node::set_call_stmt_including_clones (gimple *old_stmt,
 					     bool update_speculative)
 {
   cgraph_node *node;
-  cgraph_edge *edge = get_edge (old_stmt);
+  cgraph_edge *master_edge = get_edge (old_stmt);
 
-  if (edge)
-    edge->set_call_stmt (new_stmt, update_speculative);
+  if (master_edge)
+    cgraph_edge::set_call_stmt (master_edge, new_stmt, update_speculative);
 
   node = clones;
   if (node)
@@ -756,7 +756,8 @@  cgraph_node::set_call_stmt_including_clones (gimple *old_stmt,
 	cgraph_edge *edge = node->get_edge (old_stmt);
 	if (edge)
 	  {
-	    edge->set_call_stmt (new_stmt, update_speculative);
+	    edge = cgraph_edge::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.  */
@@ -800,11 +801,10 @@  cgraph_node::create_edge_including_clones (cgraph_node *callee,
 					   cgraph_inline_failed_t reason)
 {
   cgraph_node *node;
-  cgraph_edge *edge;
 
   if (!get_edge (stmt))
     {
-      edge = create_edge (callee, stmt, count);
+      cgraph_edge *edge = create_edge (callee, stmt, count);
       edge->inline_failed = reason;
     }
 
@@ -821,7 +821,7 @@  cgraph_node::create_edge_including_clones (cgraph_node *callee,
 	     call in the clone or we are processing clones of unreachable
 	     master where edges has been removed.  */
 	  if (edge)
-	    edge->set_call_stmt (stmt);
+	    edge = cgraph_edge::set_call_stmt (edge, stmt);
 	  else if (! node->get_edge (stmt))
 	    {
 	      edge = node->create_edge (callee, stmt, count);
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 6201444694c..471c9458112 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1023,16 +1023,13 @@  walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 			       edge->caller->name (), target->name ());
 	    }
 
-	  edge->make_direct (target);
-	  edge->redirect_call_stmt_to_callee ();
+	  edge = cgraph_edge::make_direct (edge, target);
+	  gimple *new_call = cgraph_edge::redirect_call_stmt_to_callee (edge);
 
 	  if (symtab->dump_file)
 	    {
-	      fprintf (symtab->dump_file,
-		       "Devirtualized as: ");
-	      print_gimple_stmt (symtab->dump_file,
-				 edge->call_stmt, 0,
-				 TDF_SLIM);
+	      fprintf (symtab->dump_file, "Devirtualized as: ");
+	      print_gimple_stmt (symtab->dump_file, new_call, 0, TDF_SLIM);
 	    }
 	}
     }
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index fa01cb6c083..866e8702ccb 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -245,9 +245,9 @@  redirect_to_unreachable (struct cgraph_edge *e)
 		      (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
   if (e->speculative)
-    e = e->resolve_speculation (target->decl);
+    e = cgraph_edge::resolve_speculation (e, target->decl);
   else if (!e->callee)
-    e->make_direct (target);
+    e = cgraph_edge::make_direct (e, target);
   else
     e->redirect_callee (target);
   class ipa_call_summary *es = ipa_call_summaries->get (e);
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
index c53cd423b75..5c34b6dcb50 100644
--- a/gcc/ipa-inline-transform.c
+++ b/gcc/ipa-inline-transform.c
@@ -258,7 +258,7 @@  check_speculations_1 (cgraph_node *n, vec<cgraph_edge *> *new_edges,
 	    edge_set->add (new_edges->pop ());
 	  edge_set->remove (e);
 
-	  e->resolve_speculation (NULL);
+	  cgraph_edge::resolve_speculation (e, NULL);
 	  speculation_removed = true;
 	}
       else if (!e->inline_failed)
@@ -712,7 +712,7 @@  inline_transform (struct cgraph_node *node)
       if (!e->inline_failed)
 	has_inline = true;
       next = e->next_callee;
-      e->redirect_call_stmt_to_callee ();
+      cgraph_edge::redirect_call_stmt_to_callee (e);
     }
   node->remove_all_references ();
 
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 3b68fc47d01..6f01e0441e1 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1835,7 +1835,7 @@  resolve_noninline_speculation (edge_heap_t *edge_heap, struct cgraph_edge *edge)
 
       if (edge->count.ipa ().initialized_p ())
         spec_rem += edge->count.ipa ();
-      edge->resolve_speculation ();
+      cgraph_edge::resolve_speculation (edge);
       reset_edge_caches (where);
       ipa_update_overall_fn_summary (where);
       update_caller_keys (edge_heap, where,
@@ -1999,7 +1999,7 @@  inline_small_functions (void)
 	    if (edge->speculative
 		&& !speculation_useful_p (edge, edge->aux != NULL))
 	      {
-		edge->resolve_speculation ();
+		cgraph_edge::resolve_speculation (edge);
 		update = true;
 	      }
 	  }
@@ -2736,7 +2736,7 @@  ipa_inline (void)
 		{
 		  if (edge->count.ipa ().initialized_p ())
 		    spec_rem += edge->count.ipa ();
-		  edge->resolve_speculation ();
+		  cgraph_edge::resolve_speculation (edge);
 		  update = true;
 		  remove_functions = true;
 		}
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 035730d180d..fcb13dfbac4 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -3299,7 +3299,7 @@  ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
   if (!speculative)
     {
       struct cgraph_edge *orig = ie;
-      ie = ie->make_direct (callee);
+      ie = cgraph_edge::make_direct (ie, callee);
       /* If we resolved speculative edge the cost is already up to date
 	 for direct call (adjusted by inline_edge_duplication_hook).  */
       if (ie == orig)
diff --git a/gcc/ipa-visibility.c b/gcc/ipa-visibility.c
index 67d500b759b..72d7d3d4819 100644
--- a/gcc/ipa-visibility.c
+++ b/gcc/ipa-visibility.c
@@ -632,8 +632,10 @@  function_and_variable_visibility (bool whole_program)
 	continue;
 
       cgraph_node *alias = 0;
-      for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      cgraph_edge *next_edge;
+      for (cgraph_edge *e = node->callees; e; e = next_edge)
 	{
+	  next_edge = e->next_callee;
 	  /* Recursive function calls usually can't be interposed.  */
 
 	  if (!e->recursive_p ())
@@ -649,7 +651,7 @@  function_and_variable_visibility (bool whole_program)
 	  if (gimple_has_body_p (e->caller->decl))
 	    {
 	      push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
-	      e->redirect_call_stmt_to_callee ();
+	      cgraph_edge::redirect_call_stmt_to_callee (e);
 	      pop_cfun ();
 	    }
 	}
@@ -780,7 +782,7 @@  function_and_variable_visibility (bool whole_program)
 		  if (gimple_has_body_p (e->caller->decl))
 		    {
 		      push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
-		      e->redirect_call_stmt_to_callee ();
+		      cgraph_edge::redirect_call_stmt_to_callee (e);
 		      pop_cfun ();
 		    }
 		}
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 6129633b303..1f6c2b2a8fd 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -242,12 +242,12 @@  walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
 			       edge->caller->dump_name (),
 			       target->dump_name ());
 	    }
-	  edge = edge->make_direct (target);
+	  edge = cgraph_edge::make_direct (edge, target);
 	  if (ipa_fn_summaries)
 	    ipa_update_overall_fn_summary (node->inlined_to
 					   ? node->inlined_to : node);
 	  else if (edge->call_stmt)
-	    edge->redirect_call_stmt_to_callee ();
+	    cgraph_edge::redirect_call_stmt_to_callee (edge);
 	}
     }
 }
diff --git a/gcc/multiple_target.c b/gcc/multiple_target.c
index 36fe58105df..cccfd2774db 100644
--- a/gcc/multiple_target.c
+++ b/gcc/multiple_target.c
@@ -126,7 +126,7 @@  create_dispatcher_calls (struct cgraph_node *node)
       FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
 	{
 	  e->redirect_callee (inode);
-	  e->redirect_call_stmt_to_callee ();
+	  cgraph_edge::redirect_call_stmt_to_callee (e);
 	}
 
       /* Redirect references.  */
@@ -501,7 +501,7 @@  redirect_to_specific_clone (cgraph_node *node)
 	      if (attribute_list_equal (attr_target, attr_target2))
 		{
 		  e->redirect_callee (callee);
-		  e->redirect_call_stmt_to_callee ();
+		  cgraph_edge::redirect_call_stmt_to_callee (e);
 		  break;
 		}
 	    }
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 21a45255051..0dccc9e8145 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2225,7 +2225,7 @@  copy_bb (copy_body_data *id, basic_block bb,
 		case CB_CGE_MOVE:
 		  edge = id->dst_node->get_edge (orig_stmt);
 		  if (edge)
-		    edge->set_call_stmt (call_stmt);
+		    edge = cgraph_edge::set_call_stmt (edge, call_stmt);
 		  break;
 
 		default:
@@ -2899,7 +2899,8 @@  redirect_all_calls (copy_body_data * id, basic_block bb)
 	  struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
 	  if (edge)
 	    {
-	      gimple *new_stmt = edge->redirect_call_stmt_to_callee ();
+	      gimple *new_stmt
+		= cgraph_edge::redirect_call_stmt_to_callee (edge);
 	      /* If IPA-SRA transformation, run as part of edge redirection,
 		 removed the LHS because it is unused, save it to
 		 killed_new_ssa_names so that we can prune it from debug