Index: cgraph.c
===================================================================
--- cgraph.c	(revision 201412)
+++ cgraph.c	(working copy)
@@ -1697,7 +1697,6 @@ enum availability
 cgraph_function_body_availability (struct cgraph_node *node)
 {
   enum availability avail;
-  gcc_assert (cgraph_function_flags_ready);
   if (!node->symbol.analyzed)
     avail = AVAIL_NOT_AVAILABLE;
   else if (node->local.local)
Index: cgraph.h
===================================================================
--- cgraph.h	(revision 201413)
+++ cgraph.h	(working copy)
@@ -597,6 +597,12 @@ symtab_node symtab_alias_ultimate_target
 					  enum availability *avail = NULL);
 bool symtab_resolve_alias (symtab_node node, symtab_node target);
 void fixup_same_cpp_alias_visibility (symtab_node node, symtab_node target);
+bool symtab_for_node_and_aliases (symtab_node,
+				  bool (*) (symtab_node, void *),
+				  void *,
+				  bool);
+symtab_node symtab_nonoverwritable_alias (symtab_node);
+enum availability symtab_node_availability (symtab_node);
 
 /* In cgraph.c  */
 void dump_cgraph (FILE *);
Index: ipa.c
===================================================================
--- ipa.c	(revision 201412)
+++ ipa.c	(working copy)
@@ -750,6 +750,21 @@ varpool_externally_visible_p (struct var
   return false;
 }
 
+/* Return true if reference to NODE can be replaced by a local alias.
+   Local aliases save dynamic linking overhead and enable more optimizations.
+ */
+
+bool
+can_replace_by_local_alias (symtab_node node)
+{
+  return (symtab_node_availability (node) > AVAIL_OVERWRITABLE
+	  && !DECL_EXTERNAL (node->symbol.decl)
+	  && (!DECL_ONE_ONLY (node->symbol.decl)
+	      || node->symbol.resolution == LDPR_PREVAILING_DEF
+	      || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY
+	      || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP));
+}
+
 /* Mark visibility of all functions.
 
    A local function is one whose calls can occur only in the current
@@ -871,7 +886,36 @@ function_and_variable_visibility (bool w
 	}
     }
   FOR_EACH_DEFINED_FUNCTION (node)
-    node->local.local = cgraph_local_node_p (node);
+    {
+      node->local.local = cgraph_local_node_p (node);
+
+      /* If we know that function can not be overwritten by a different semantics
+	 and moreover its section can not be discarded, replace all direct calls
+	 by calls to an nonoverwritable alias.  This make dynamic linking
+	 cheaper and enable more optimization.
+
+	 TODO: We can also update virtual tables.  */
+      if (node->callers && can_replace_by_local_alias ((symtab_node)node))
+	{
+	  struct cgraph_node *alias = cgraph (symtab_nonoverwritable_alias ((symtab_node) node));
+
+	  if (alias != node)
+	    {
+	      while (node->callers)
+		{
+		  struct cgraph_edge *e = node->callers;
+
+		  cgraph_redirect_edge_callee (e, alias);
+		  if (!flag_wpa)
+		    {
+		      push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl));
+		      cgraph_redirect_edge_call_stmt_to_callee (e);
+		      pop_cfun ();
+		    }
+		}
+	    }
+	}
+    }
   FOR_EACH_VARIABLE (vnode)
     {
       /* weak flag makes no sense on local variables.  */
Index: symtab.c
===================================================================
--- symtab.c	(revision 201412)
+++ symtab.c	(working copy)
@@ -1014,4 +1014,84 @@ symtab_resolve_alias (symtab_node node,
     symtab_alias_ultimate_target (target, NULL)->symbol.address_taken = true;
   return true;
 }
+
+/* Call calback on NODE and aliases associated to NODE. 
+   When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
+   skipped. */
+
+bool
+symtab_for_node_and_aliases (symtab_node node,
+			     bool (*callback) (symtab_node, void *),
+			     void *data,
+			     bool include_overwritable)
+{
+  int i;
+  struct ipa_ref *ref;
+
+  if (callback (node, data))
+    return true;
+  for (i = 0; ipa_ref_list_referring_iterate (&node->symbol.ref_list, i, ref); i++)
+    if (ref->use == IPA_REF_ALIAS)
+      {
+	symtab_node alias = ref->referring;
+	if (include_overwritable
+	    || symtab_node_availability (alias) > AVAIL_OVERWRITABLE)
+          if (symtab_for_node_and_aliases (alias, callback, data,
+					   include_overwritable))
+	    return true;
+      }
+  return false;
+}
+
+/* Worker searching nonoverwritable alias.  */
+
+static bool
+symtab_nonoverwritable_alias_1 (symtab_node node, void *data)
+{
+  if (decl_binds_to_current_def_p (node->symbol.decl))
+    {
+      *(symtab_node *)data = node;
+      return true;
+    }
+  return false;
+}
+
+symtab_node
+symtab_nonoverwritable_alias (symtab_node node)
+{
+  tree new_decl;
+  symtab_node new_node = NULL;
+  symtab_for_node_and_aliases (node, symtab_nonoverwritable_alias_1,
+		               (void *)&new_node, true);
+  if (new_node)
+    return new_node;
+
+  new_decl = copy_node (node->symbol.decl);
+  DECL_NAME (new_decl) = clone_function_name (node->symbol.decl, "localalias");
+  if (TREE_CODE (new_decl) == FUNCTION_DECL)
+    DECL_STRUCT_FUNCTION (new_decl) = NULL;
+  DECL_INITIAL (new_decl) = NULL;
+  SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+  SET_DECL_RTL (new_decl, NULL);
+
+  /* Update the properties.  */
+  DECL_EXTERNAL (new_decl) = 0;
+  if (DECL_ONE_ONLY (node->symbol.decl))
+    DECL_SECTION_NAME (new_decl) = NULL;
+  DECL_COMDAT_GROUP (new_decl) = 0;
+  TREE_PUBLIC (new_decl) = 0;
+  DECL_COMDAT (new_decl) = 0;
+  DECL_WEAK (new_decl) = 0;
+  DECL_VIRTUAL_P (new_decl) = 0;
+  if (TREE_CODE (new_decl) == FUNCTION_DECL)
+    {
+      DECL_STATIC_CONSTRUCTOR (new_decl) = 0;
+      DECL_STATIC_DESTRUCTOR (new_decl) = 0;
+      new_node = (symtab_node) cgraph_create_function_alias (new_decl, node->symbol.decl);
+    }
+  else
+    new_node = (symtab_node) varpool_create_variable_alias (new_decl, node->symbol.decl);
+  symtab_resolve_alias (new_node, node);  
+  return new_node;
+}
 #include "gt-symtab.h"
