This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Cgraph thunk reorg


Hi,
given that the patch has received feedback and I have weekend for fixing the
fallout, I decided to commit the following version today.  It contains fix in
visibility handling of thunks that has shown in Mozilla build.


	* cgraph.c (cgraph_add_thunk): Create real function node instead
	of alias node; finalize it and mark needed/reachale; arrange visibility
	to be right and add it into the corresponding same comdat group list.
	(dump_cgraph_node): Dump thunks.
	* cgraph.h (cgraph_first_defined_function, cgraph_next_defined_function,
	cgraph_function_with_gimple_body_p, cgraph_first_function_with_gimple_body,
	cgraph_next_function_with_gimple_body): New functions.
	(FOR_EACH_FUNCTION_WITH_GIMPLE_BODY, FOR_EACH_DEFINED_FUNCTION):
	New macros.
	* ipa-cp.c (ipcp_need_redirect_p): Thunks can't be redirected.
	(ipcp_generate_summary): Use FOR_EACH_FUNCTION_WITH_GIMPLE_BODY.
	* cgraphunit.c (cgraph_finalize_function): Only look into possible
	devirtualization when optimizing.
	(verify_cgraph_node): Verify thunks.
	(cgraph_analyze_function): Analyze thunks.
	(cgraph_mark_functions_to_output): Output thunks only in combination
	with function they are assigned to.
	(assemble_thunk): Turn thunk into non-thunk; don't try to turn
	alias into normal node.
	(assemble_thunks): New functoin.
	(cgraph_expand_function): Use it.
	* lto-cgraph.c (lto_output_node): Stream thunks.
	(input_overwrite_node): Stream in thunks.
	* ipa-pure-const.c (analyze_function): Thunks do nothing interesting.
	* lto-streamer-out.c (lto_output): Do not try to output thunk's body.
	* ipa-inline.c (inline_small_functions): Use FOR_EACH_DEFINED_FUNCTION.
	* ipa-inline-analysis.c (compute_inline_parameters): "Analyze" thunks.
	(inline_analyze_function): Do not care about thunk jump functions.
	(inline_generate_summary):Use FOR_EACH_DEFINED_FUNCTION.
	* ipa-prop.c (ipa_prop_write_jump_functions): Use cgraph_function_with_gimple_body_p.
	* passes.c (do_per_function_toporder): Use cgraph_function_with_gimple_body_p.
	(execute_one_pass);Use FOR_EACH_FUNCTION_WITH_GIMPLE_BODY.
	(ipa_write_summaries): Use cgraph_function_with_gimple_body_p.
	(function_called_by_processed_nodes_p): Likewise.

	* lto.c (lto_materialize_function): Use cgraph_function_with_gimple_body_p.
	(add_cgraph_node_to_partition): Do not re-add items to partition; handle thunks.
	(add_varpool_node_to_partition): Do not re-add items to partition.

Index: cgraph.c
===================================================================
*** cgraph.c	(revision 173251)
--- cgraph.c	(working copy)
*************** cgraph_same_body_alias (struct cgraph_no
*** 595,608 ****
     See comments in thunk_adjust for detail on the parameters.  */
  
  struct cgraph_node *
! cgraph_add_thunk (struct cgraph_node *decl_node, tree alias, tree decl,
  		  bool this_adjusting,
  		  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
  		  tree virtual_offset,
  		  tree real_alias)
  {
!   struct cgraph_node *node = cgraph_get_node (alias);
  
    if (node)
      {
        gcc_assert (node->local.finalized);
--- 595,610 ----
     See comments in thunk_adjust for detail on the parameters.  */
  
  struct cgraph_node *
! cgraph_add_thunk (struct cgraph_node *decl_node ATTRIBUTE_UNUSED,
! 		  tree alias, tree decl,
  		  bool this_adjusting,
  		  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
  		  tree virtual_offset,
  		  tree real_alias)
  {
!   struct cgraph_node *node;
  
+   node = cgraph_get_node (alias);
    if (node)
      {
        gcc_assert (node->local.finalized);
*************** cgraph_add_thunk (struct cgraph_node *de
*** 610,617 ****
        cgraph_remove_node (node);
      }
    
!   node = cgraph_same_body_alias_1 (decl_node, alias, decl);
!   gcc_assert (node);
    gcc_checking_assert (!virtual_offset
  		       || tree_int_cst_equal (virtual_offset,
  					      size_int (virtual_value)));
--- 612,618 ----
        cgraph_remove_node (node);
      }
    
!   node = cgraph_create_node (alias);
    gcc_checking_assert (!virtual_offset
  		       || tree_int_cst_equal (virtual_offset,
  					      size_int (virtual_value)));
*************** cgraph_add_thunk (struct cgraph_node *de
*** 621,626 ****
--- 622,636 ----
    node->thunk.virtual_offset_p = virtual_offset != NULL;
    node->thunk.alias = real_alias;
    node->thunk.thunk_p = true;
+   node->local.finalized = true;
+ 
+   if (cgraph_decide_is_function_needed (node, decl))
+     cgraph_mark_needed_node (node);
+ 
+   if ((TREE_PUBLIC (decl) && !DECL_COMDAT (decl) && !DECL_EXTERNAL (decl))
+       || (DECL_VIRTUAL_P (decl)
+ 	  && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
+     cgraph_mark_reachable_node (node);
    return node;
  }
  
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 1874,1880 ****
    if (node->only_called_at_exit)
      fprintf (f, " only_called_at_exit");
  
!   fprintf (f, "\n  called by: ");
    for (edge = node->callers; edge; edge = edge->next_caller)
      {
        fprintf (f, "%s/%i ", cgraph_node_name (edge->caller),
--- 1884,1904 ----
    if (node->only_called_at_exit)
      fprintf (f, " only_called_at_exit");
  
!   fprintf (f, "\n");
! 
!   if (node->thunk.thunk_p)
!     {
!       fprintf (f, "  thunk of %s (asm: %s) fixed offset %i virtual value %i has "
! 	       "virtual offset %i)\n",
! 	       lang_hooks.decl_printable_name (node->thunk.alias, 2),
! 	       IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->thunk.alias)),
! 	       (int)node->thunk.fixed_offset,
! 	       (int)node->thunk.virtual_value,
! 	       (int)node->thunk.virtual_offset_p);
!     }
!   
!   fprintf (f, "  called by: ");
! 
    for (edge = node->callers; edge; edge = edge->next_caller)
      {
        fprintf (f, "%s/%i ", cgraph_node_name (edge->caller),
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 1926,1945 ****
    if (node->same_body)
      {
        struct cgraph_node *n;
!       fprintf (f, "  aliases & thunks:");
        for (n = node->same_body; n; n = n->next)
          {
            fprintf (f, " %s/%i", cgraph_node_name (n), n->uid);
- 	  if (n->thunk.thunk_p)
- 	    {
- 	      fprintf (f, " (thunk of %s fixed offset %i virtual value %i has "
- 		       "virtual offset %i",
- 	      	       lang_hooks.decl_printable_name (n->thunk.alias, 2),
- 		       (int)n->thunk.fixed_offset,
- 		       (int)n->thunk.virtual_value,
- 		       (int)n->thunk.virtual_offset_p);
- 	      fprintf (f, ")");
- 	    }
  	  if (DECL_ASSEMBLER_NAME_SET_P (n->decl))
  	    fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
  	}
--- 1950,1959 ----
    if (node->same_body)
      {
        struct cgraph_node *n;
!       fprintf (f, "  aliases:");
        for (n = node->same_body; n; n = n->next)
          {
            fprintf (f, " %s/%i", cgraph_node_name (n), n->uid);
  	  if (DECL_ASSEMBLER_NAME_SET_P (n->decl))
  	    fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (n->decl)));
  	}
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 173251)
--- cgraph.h	(working copy)
*************** varpool_next_static_initializer (struct 
*** 715,720 ****
--- 715,793 ----
     for ((node) = varpool_first_static_initializer (); (node); \
          (node) = varpool_next_static_initializer (node))
  
+ /* Return first function with body defined.  */
+ static inline struct cgraph_node *
+ cgraph_first_defined_function (void)
+ {
+   struct cgraph_node *node;
+   for (node = cgraph_nodes; node; node = node->next)
+     {
+       if (node->analyzed)
+ 	return node;
+     }
+   return NULL;
+ }
+ 
+ /* Return next reachable static variable with initializer after NODE.  */
+ static inline struct cgraph_node *
+ cgraph_next_defined_function (struct cgraph_node *node)
+ {
+   for (node = node->next; node; node = node->next)
+     {
+       if (node->analyzed)
+ 	return node;
+     }
+   return NULL;
+ }
+ 
+ /* Walk all functions with body defined.  */
+ #define FOR_EACH_DEFINED_FUNCTION(node) \
+    for ((node) = cgraph_first_defined_function (); (node); \
+         (node) = cgraph_next_defined_function (node))
+ 
+ 
+ /* Return true when NODE is a function with Gimple body defined
+    in current unit.  Functions can also be define externally or they
+    can be thunks with no Gimple representation.
+ 
+    Note that at WPA stage, the function body may not be present in memory.  */
+ 
+ static inline bool
+ cgraph_function_with_gimple_body_p (struct cgraph_node *node)
+ {
+   return node->analyzed && !node->thunk.thunk_p;
+ }
+ 
+ /* Return first function with body defined.  */
+ static inline struct cgraph_node *
+ cgraph_first_function_with_gimple_body (void)
+ {
+   struct cgraph_node *node;
+   for (node = cgraph_nodes; node; node = node->next)
+     {
+       if (cgraph_function_with_gimple_body_p (node))
+ 	return node;
+     }
+   return NULL;
+ }
+ 
+ /* Return next reachable static variable with initializer after NODE.  */
+ static inline struct cgraph_node *
+ cgraph_next_function_with_gimple_body (struct cgraph_node *node)
+ {
+   for (node = node->next; node; node = node->next)
+     {
+       if (cgraph_function_with_gimple_body_p (node))
+ 	return node;
+     }
+   return NULL;
+ }
+ 
+ /* Walk all functions with body defined.  */
+ #define FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) \
+    for ((node) = cgraph_first_function_with_gimple_body (); (node); \
+         (node) = cgraph_next_function_with_gimple_body (node))
+ 
  /* Create a new static variable of type TYPE.  */
  tree add_new_static_var (tree type);
  
Index: ipa-cp.c
===================================================================
*** ipa-cp.c	(revision 173251)
--- ipa-cp.c	(working copy)
*************** ipcp_need_redirect_p (struct cgraph_edge
*** 951,956 ****
--- 951,960 ----
    if (!n_cloning_candidates)
      return false;
  
+   /* We can't redirect anything in thunks, yet.  */
+   if (cs->caller->thunk.thunk_p)
+     return true;
+ 
    if ((orig = ipcp_get_orig_node (node)) != NULL)
      node = orig;
    if (ipcp_get_orig_node (cs->caller))
*************** ipcp_generate_summary (void)
*** 1508,1515 ****
      fprintf (dump_file, "\nIPA constant propagation start:\n");
    ipa_register_cgraph_hooks ();
  
!   for (node = cgraph_nodes; node; node = node->next)
!     if (node->analyzed)
        {
  	/* Unreachable nodes should have been eliminated before ipcp.  */
  	gcc_assert (node->needed || node->reachable);
--- 1512,1520 ----
      fprintf (dump_file, "\nIPA constant propagation start:\n");
    ipa_register_cgraph_hooks ();
  
!   /* FIXME: We could propagate through thunks happily and we could be
!      even able to clone them, if needed.  Do that later.  */
!   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
        {
  	/* Unreachable nodes should have been eliminated before ipcp.  */
  	gcc_assert (node->needed || node->reachable);
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 173251)
--- cgraphunit.c	(working copy)
*************** cgraph_finalize_function (tree decl, boo
*** 370,376 ****
  	 to those so we need to analyze them.
  	 FIXME: We should introduce may edges for this purpose and update
  	 their handling in unreachable function removal and inliner too.  */
!       || (DECL_VIRTUAL_P (decl) && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
      cgraph_mark_reachable_node (node);
  
    /* If we've not yet emitted decl, tell the debug info about it.  */
--- 370,377 ----
  	 to those so we need to analyze them.
  	 FIXME: We should introduce may edges for this purpose and update
  	 their handling in unreachable function removal and inliner too.  */
!       || (DECL_VIRTUAL_P (decl)
! 	  && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
      cgraph_mark_reachable_node (node);
  
    /* If we've not yet emitted decl, tell the debug info about it.  */
*************** verify_cgraph_node (struct cgraph_node *
*** 624,633 ****
        while (n != node);
      }
  
!   if (node->analyzed && gimple_has_body_p (node->decl)
!       && !TREE_ASM_WRITTEN (node->decl)
!       && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
!       && !flag_wpa)
      {
        if (this_cfun->cfg)
  	{
--- 625,652 ----
        while (n != node);
      }
  
!   if (node->analyzed && node->thunk.thunk_p)
!     {
!       if (!node->callees)
! 	{
! 	  error ("No edge out of thunk node");
!           error_found = true;
! 	}
!       else if (node->callees->next_callee)
! 	{
! 	  error ("More than one edge out of thunk node");
!           error_found = true;
! 	}
!       if (gimple_has_body_p (node->decl))
!         {
! 	  error ("Thunk is not supposed to have body");
!           error_found = true;
!         }
!     }
!   else if (node->analyzed && gimple_has_body_p (node->decl)
!            && !TREE_ASM_WRITTEN (node->decl)
!            && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)
!            && !flag_wpa)
      {
        if (this_cfun->cfg)
  	{
*************** verify_cgraph_node (struct cgraph_node *
*** 656,663 ****
  			  }
  			if (!e->indirect_unknown_callee)
  			  {
- 			    struct cgraph_node *n;
- 
  			    if (e->callee->same_body_alias)
  			      {
  				error ("edge points to same body alias:");
--- 675,680 ----
*************** verify_cgraph_node (struct cgraph_node *
*** 678,693 ****
  				debug_tree (decl);
  				error_found = true;
  			      }
- 			    else if (decl
- 				     && (n = cgraph_get_node_or_alias (decl))
- 				     && (n->same_body_alias
- 					 && n->thunk.thunk_p))
- 			      {
- 				error ("a call to thunk improperly represented "
- 				       "in the call graph:");
- 				cgraph_debug_gimple_stmt (this_cfun, stmt);
- 				error_found = true;
- 			      }
  			  }
  			else if (decl)
  			  {
--- 695,700 ----
*************** cgraph_analyze_function (struct cgraph_n
*** 780,802 ****
    tree save = current_function_decl;
    tree decl = node->decl;
  
!   current_function_decl = decl;
!   push_cfun (DECL_STRUCT_FUNCTION (decl));
  
!   assign_assembler_name_if_neeeded (node->decl);
  
!   /* Make sure to gimplify bodies only once.  During analyzing a
!      function we lower it, which will require gimplified nested
!      functions, so we can end up here with an already gimplified
!      body.  */
!   if (!gimple_body (decl))
!     gimplify_function_tree (decl);
!   dump_function (TDI_generic, decl);
  
!   cgraph_lower_function (node);
    node->analyzed = true;
  
-   pop_cfun ();
    current_function_decl = save;
  }
  
--- 787,817 ----
    tree save = current_function_decl;
    tree decl = node->decl;
  
!   if (node->thunk.thunk_p)
!     {
!       cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
! 			  NULL, 0, CGRAPH_FREQ_BASE);
!     }
!   else
!     {
!       current_function_decl = decl;
!       push_cfun (DECL_STRUCT_FUNCTION (decl));
  
!       assign_assembler_name_if_neeeded (node->decl);
  
!       /* Make sure to gimplify bodies only once.  During analyzing a
! 	 function we lower it, which will require gimplified nested
! 	 functions, so we can end up here with an already gimplified
! 	 body.  */
!       if (!gimple_body (decl))
! 	gimplify_function_tree (decl);
!       dump_function (TDI_generic, decl);
  
!       cgraph_lower_function (node);
!       pop_cfun ();
!     }
    node->analyzed = true;
  
    current_function_decl = save;
  }
  
*************** cgraph_analyze_functions (void)
*** 969,975 ****
        /* ??? It is possible to create extern inline function and later using
  	 weak alias attribute to kill its body. See
  	 gcc.c-torture/compile/20011119-1.c  */
!       if (!DECL_STRUCT_FUNCTION (decl))
  	{
  	  cgraph_reset_node (node);
  	  continue;
--- 984,991 ----
        /* ??? It is possible to create extern inline function and later using
  	 weak alias attribute to kill its body. See
  	 gcc.c-torture/compile/20011119-1.c  */
!       if (!DECL_STRUCT_FUNCTION (decl)
! 	  && !node->thunk.thunk_p)
  	{
  	  cgraph_reset_node (node);
  	  continue;
*************** cgraph_analyze_functions (void)
*** 981,986 ****
--- 997,1005 ----
        for (edge = node->callees; edge; edge = edge->next_callee)
  	if (!edge->callee->reachable)
  	  cgraph_mark_reachable_node (edge->callee);
+       for (edge = node->callers; edge; edge = edge->next_caller)
+ 	if (!edge->caller->reachable && edge->caller->thunk.thunk_p)
+ 	  cgraph_mark_reachable_node (edge->caller);
  
        if (node->same_comdat_group)
  	{
*************** cgraph_analyze_functions (void)
*** 1031,1040 ****
        tree decl = node->decl;
        next = node->next;
  
!       if (node->local.finalized && !gimple_has_body_p (decl))
  	cgraph_reset_node (node);
  
!       if (!node->reachable && gimple_has_body_p (decl))
  	{
  	  if (cgraph_dump_file)
  	    fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
--- 1050,1061 ----
        tree decl = node->decl;
        next = node->next;
  
!       if (node->local.finalized && !gimple_has_body_p (decl)
! 	  && !node->thunk.thunk_p)
  	cgraph_reset_node (node);
  
!       if (!node->reachable
! 	  && (gimple_has_body_p (decl) || node->thunk.thunk_p))
  	{
  	  if (cgraph_dump_file)
  	    fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
*************** cgraph_analyze_functions (void)
*** 1043,1049 ****
  	}
        else
  	node->next_needed = NULL;
!       gcc_assert (!node->local.finalized || gimple_has_body_p (decl));
        gcc_assert (node->analyzed == node->local.finalized);
      }
    if (cgraph_dump_file)
--- 1064,1071 ----
  	}
        else
  	node->next_needed = NULL;
!       gcc_assert (!node->local.finalized || node->thunk.thunk_p
! 		  || gimple_has_body_p (decl));
        gcc_assert (node->analyzed == node->local.finalized);
      }
    if (cgraph_dump_file)
*************** cgraph_mark_functions_to_output (void)
*** 1132,1137 ****
--- 1154,1160 ----
  	 always inlined, as well as those that are reachable from
  	 outside the current compilation unit.  */
        if (node->analyzed
+ 	  && !node->thunk.thunk_p
  	  && !node->global.inlined_to
  	  && (!cgraph_only_called_directly_p (node)
  	      || (e && node->reachable))
*************** cgraph_mark_functions_to_output (void)
*** 1145,1151 ****
  	      for (next = node->same_comdat_group;
  		   next != node;
  		   next = next->same_comdat_group)
! 		next->process = 1;
  	    }
  	}
        else if (node->same_comdat_group)
--- 1168,1175 ----
  	      for (next = node->same_comdat_group;
  		   next != node;
  		   next = next->same_comdat_group)
! 		if (!next->thunk.thunk_p)
! 		  next->process = 1;
  	    }
  	}
        else if (node->same_comdat_group)
*************** assemble_thunk (struct cgraph_node *node
*** 1406,1411 ****
--- 1430,1437 ----
        free_after_compilation (cfun);
        set_cfun (NULL);
        TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+       node->thunk.thunk_p = false;
+       node->analyzed = false;
      }
    else
      {
*************** assemble_thunk (struct cgraph_node *node
*** 1530,1544 ****
        delete_unreachable_blocks ();
        update_ssa (TODO_update_ssa);
  
-       cgraph_remove_same_body_alias (node);
        /* Since we want to emit the thunk, we explicitly mark its name as
  	 referenced.  */
        cgraph_add_new_function (thunk_fndecl, true);
        bitmap_obstack_release (NULL);
      }
    current_function_decl = NULL;
  }
  
  /* Expand function specified by NODE.  */
  
  static void
--- 1556,1591 ----
        delete_unreachable_blocks ();
        update_ssa (TODO_update_ssa);
  
        /* Since we want to emit the thunk, we explicitly mark its name as
  	 referenced.  */
+       node->thunk.thunk_p = false;
+       cgraph_node_remove_callees (node);
        cgraph_add_new_function (thunk_fndecl, true);
        bitmap_obstack_release (NULL);
      }
    current_function_decl = NULL;
  }
  
+ 
+ /* Assemble thunks asociated to NODE.  */
+ 
+ static void
+ assemble_thunks (struct cgraph_node *node)
+ {
+   struct cgraph_edge *e;
+   for (e = node->callers; e;)
+     if (e->caller->thunk.thunk_p)
+       {
+ 	struct cgraph_node *thunk = e->caller;
+ 
+ 	e = e->next_caller;
+ 	assemble_thunks (thunk);
+         assemble_thunk (thunk);
+       }
+     else
+       e = e->next_caller;
+ }
+ 
  /* Expand function specified by NODE.  */
  
  static void
*************** cgraph_expand_function (struct cgraph_no
*** 1566,1578 ****
  	  if (!alias->thunk.thunk_p)
  	    assemble_alias (alias->decl,
  			    DECL_ASSEMBLER_NAME (alias->thunk.alias));
- 	  else
- 	    assemble_thunk (alias);
  	}
        node->alias = saved_alias;
        cgraph_process_new_functions ();
      }
  
    gcc_assert (node->lowered);
  
    /* Generate RTL for the body of DECL.  */
--- 1613,1624 ----
  	  if (!alias->thunk.thunk_p)
  	    assemble_alias (alias->decl,
  			    DECL_ASSEMBLER_NAME (alias->thunk.alias));
  	}
        node->alias = saved_alias;
        cgraph_process_new_functions ();
      }
  
+   assemble_thunks (node);
    gcc_assert (node->lowered);
  
    /* Generate RTL for the body of DECL.  */
*************** cgraph_output_in_order (void)
*** 1688,1694 ****
  
    for (pf = cgraph_nodes; pf; pf = pf->next)
      {
!       if (pf->process)
  	{
  	  i = pf->order;
  	  gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
--- 1734,1740 ----
  
    for (pf = cgraph_nodes; pf; pf = pf->next)
      {
!       if (pf->process && !pf->thunk.thunk_p)
  	{
  	  i = pf->order;
  	  gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
Index: lto-cgraph.c
===================================================================
*** lto-cgraph.c	(revision 173251)
--- lto-cgraph.c	(working copy)
*************** lto_output_node (struct lto_simple_outpu
*** 502,510 ****
--- 502,525 ----
    bp_pack_value (&bp, node->frequency, 2);
    bp_pack_value (&bp, node->only_called_at_startup, 1);
    bp_pack_value (&bp, node->only_called_at_exit, 1);
+   bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1);
    lto_output_bitpack (&bp);
    lto_output_uleb128_stream (ob->main_stream, node->resolution);
  
+   if (node->thunk.thunk_p && !boundary_p)
+     {
+       lto_output_uleb128_stream
+ 	 (ob->main_stream,
+ 	  1 + (node->thunk.this_adjusting != 0) * 2
+ 	  + (node->thunk.virtual_offset_p != 0) * 4);
+       lto_output_uleb128_stream (ob->main_stream,
+ 				 node->thunk.fixed_offset);
+       lto_output_uleb128_stream (ob->main_stream,
+ 				 node->thunk.virtual_value);
+       lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
+ 				node->thunk.alias);
+     }
+ 
    if (node->same_body)
      {
        struct cgraph_node *alias;
*************** lto_output_node (struct lto_simple_outpu
*** 516,540 ****
  	{
  	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
  				    alias->decl);
! 	  if (alias->thunk.thunk_p)
! 	    {
!               lto_output_uleb128_stream
! 	         (ob->main_stream,
! 	      	  1 + (alias->thunk.this_adjusting != 0) * 2
! 		  + (alias->thunk.virtual_offset_p != 0) * 4);
! 	      lto_output_uleb128_stream (ob->main_stream,
! 	      				 alias->thunk.fixed_offset);
! 	      lto_output_uleb128_stream (ob->main_stream,
! 	      				 alias->thunk.virtual_value);
! 	      lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
! 					alias->thunk.alias);
! 	    }
! 	  else
! 	    {
! 	      lto_output_uleb128_stream (ob->main_stream, 0);
! 	      lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
! 					alias->thunk.alias);
! 	    }
  	  gcc_assert (cgraph_get_node (alias->thunk.alias) == node);
  	  lto_output_uleb128_stream (ob->main_stream, alias->resolution);
  	  alias = alias->previous;
--- 531,538 ----
  	{
  	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
  				    alias->decl);
! 	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
! 				    alias->thunk.alias);
  	  gcc_assert (cgraph_get_node (alias->thunk.alias) == node);
  	  lto_output_uleb128_stream (ob->main_stream, alias->resolution);
  	  alias = alias->previous;
*************** input_overwrite_node (struct lto_file_de
*** 947,952 ****
--- 945,951 ----
    node->frequency = (enum node_frequency)bp_unpack_value (bp, 2);
    node->only_called_at_startup = bp_unpack_value (bp, 1);
    node->only_called_at_exit = bp_unpack_value (bp, 1);
+   node->thunk.thunk_p = bp_unpack_value (bp, 1);
    node->resolution = resolution;
  }
  
*************** input_node (struct lto_file_decl_data *f
*** 1031,1064 ****
    /* Store a reference for now, and fix up later to be a pointer.  */
    node->same_comdat_group = (cgraph_node_ptr) (intptr_t) ref2;
  
    same_body_count = lto_input_uleb128 (ib);
    while (same_body_count-- > 0)
      {
!       tree alias_decl;
!       int type;
        struct cgraph_node *alias;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       type = lto_input_uleb128 (ib);
!       if (!type)
! 	{
! 	  tree real_alias;
! 	  decl_index = lto_input_uleb128 (ib);
! 	  real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
! 	  alias = cgraph_same_body_alias (node, alias_decl, real_alias);
! 	}
!       else
!         {
! 	  HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib);
! 	  HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib);
! 	  tree real_alias;
! 	  decl_index = lto_input_uleb128 (ib);
! 	  real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
! 	  alias = cgraph_add_thunk (node, alias_decl, fn_decl, type & 2, fixed_offset,
! 				    virtual_value,
! 				    (type & 4) ? size_int (virtual_value) : NULL_TREE,
! 				    real_alias);
! 	}
        gcc_assert (alias);
        alias->resolution = (enum ld_plugin_symbol_resolution)lto_input_uleb128 (ib);
      }
--- 1030,1062 ----
    /* Store a reference for now, and fix up later to be a pointer.  */
    node->same_comdat_group = (cgraph_node_ptr) (intptr_t) ref2;
  
+   if (node->thunk.thunk_p)
+     {
+       int type = lto_input_uleb128 (ib);
+       HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib);
+       HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib);
+       tree real_alias;
+ 
+       decl_index = lto_input_uleb128 (ib);
+       real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
+       node->thunk.fixed_offset = fixed_offset;
+       node->thunk.this_adjusting = (type & 2);
+       node->thunk.virtual_value = virtual_value;
+       node->thunk.virtual_offset_p = (type & 4);
+       node->thunk.alias = real_alias;
+     }
+ 
    same_body_count = lto_input_uleb128 (ib);
    while (same_body_count-- > 0)
      {
!       tree alias_decl, real_alias;
        struct cgraph_node *alias;
+ 
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       decl_index = lto_input_uleb128 (ib);
!       real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       alias = cgraph_same_body_alias (node, alias_decl, real_alias);
        gcc_assert (alias);
        alias->resolution = (enum ld_plugin_symbol_resolution)lto_input_uleb128 (ib);
      }
Index: ipa-pure-const.c
===================================================================
*** ipa-pure-const.c	(revision 173251)
--- ipa-pure-const.c	(working copy)
*************** analyze_function (struct cgraph_node *fn
*** 731,736 ****
--- 731,746 ----
    l->looping_previously_known = true;
    l->looping = false;
    l->can_throw = false;
+   state_from_flags (&l->state_previously_known, &l->looping_previously_known,
+ 		    flags_from_decl_or_type (fn->decl),
+ 		    cgraph_node_cannot_return (fn));
+ 
+   if (fn->thunk.thunk_p)
+     {
+       /* Thunk gets propagated through, so nothing interesting happens.  */
+       gcc_assert (ipa);
+       return l;
+     }
  
    if (dump_file)
      {
*************** end:
*** 799,807 ****
  
    if (dump_file && (dump_flags & TDF_DETAILS))
      fprintf (dump_file, "    checking previously known:");
-   state_from_flags (&l->state_previously_known, &l->looping_previously_known,
- 		    flags_from_decl_or_type (fn->decl),
- 		    cgraph_node_cannot_return (fn));
  
    better_state (&l->pure_const_state, &l->looping,
  		l->state_previously_known,
--- 809,814 ----
Index: lto-streamer-out.c
===================================================================
*** lto-streamer-out.c	(revision 173251)
--- lto-streamer-out.c	(working copy)
*************** lto_output (cgraph_node_set set, varpool
*** 2197,2203 ****
    for (i = 0; i < n_nodes; i++)
      {
        node = lto_cgraph_encoder_deref (encoder, i);
!       if (lto_cgraph_encoder_encode_body_p (encoder, node))
  	{
  #ifdef ENABLE_CHECKING
  	  gcc_assert (!bitmap_bit_p (output, DECL_UID (node->decl)));
--- 2197,2204 ----
    for (i = 0; i < n_nodes; i++)
      {
        node = lto_cgraph_encoder_deref (encoder, i);
!       if (lto_cgraph_encoder_encode_body_p (encoder, node)
! 	  && !node->thunk.thunk_p)
  	{
  #ifdef ENABLE_CHECKING
  	  gcc_assert (!bitmap_bit_p (output, DECL_UID (node->decl)));
Index: ipa-inline.c
===================================================================
*** ipa-inline.c	(revision 173251)
--- ipa-inline.c	(working copy)
*************** inline_small_functions (void)
*** 1177,1185 ****
    max_count = 0;
    initialize_growth_caches ();
  
!   for (node = cgraph_nodes; node; node = node->next)
!     if (node->analyzed
! 	&& !node->global.inlined_to)
        {
  	struct inline_summary *info = inline_summary (node);
  
--- 1177,1184 ----
    max_count = 0;
    initialize_growth_caches ();
  
!   FOR_EACH_DEFINED_FUNCTION (node)
!     if (!node->global.inlined_to)
        {
  	struct inline_summary *info = inline_summary (node);
  
*************** inline_small_functions (void)
*** 1197,1205 ****
  
    /* Populate the heeap with all edges we might inline.  */
  
!   for (node = cgraph_nodes; node; node = node->next)
!     if (node->analyzed
! 	&& !node->global.inlined_to)
        {
  	if (dump_file)
  	  fprintf (dump_file, "Enqueueing calls of %s/%i.\n",
--- 1196,1203 ----
  
    /* Populate the heeap with all edges we might inline.  */
  
!   FOR_EACH_DEFINED_FUNCTION (node)
!     if (!node->global.inlined_to)
        {
  	if (dump_file)
  	  fprintf (dump_file, "Enqueueing calls of %s/%i.\n",
Index: ipa.c
===================================================================
*** ipa.c	(revision 173251)
--- ipa.c	(working copy)
*************** function_and_variable_visibility (bool w
*** 877,883 ****
--- 877,923 ----
  	       segfault though. */
  	    dissolve_same_comdat_group_list (node);
  	}
+ 
+       if (node->thunk.thunk_p
+ 	  && TREE_PUBLIC (node->decl))
+ 	{
+ 	  struct cgraph_node *decl_node = node;
+ 
+ 	  while (decl_node->thunk.thunk_p)
+ 	    decl_node = decl_node->callees->callee;
+ 
+ 	  /* Thunks have the same visibility as function they are attached to.
+ 	     For some reason C++ frontend don't seem to care. I.e. in 
+ 	     g++.dg/torture/pr41257-2.C the thunk is not comdat while function
+ 	     it is attached to is.
+ 
+ 	     We also need to arrange the thunk into the same comdat group as
+ 	     the function it reffers to.  */
+ 	  if (DECL_COMDAT (decl_node->decl))
+ 	    {
+ 	      DECL_COMDAT (node->decl) = 1;
+ 	      DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (decl_node->decl);
+ 	      if (!node->same_comdat_group)
+ 		{
+ 		  node->same_comdat_group = decl_node;
+ 		  if (!decl_node->same_comdat_group)
+ 		    decl_node->same_comdat_group = node;
+ 		  else
+ 		    {
+ 		      struct cgraph_node *n;
+ 		      for (n = decl_node->same_comdat_group;
+ 			   n->same_comdat_group != decl_node;
+ 			   n = n->same_comdat_group)
+ 			;
+ 		      n->same_comdat_group = node;
+ 		    }
+ 		}
+ 	    }
+ 	  if (DECL_EXTERNAL (decl_node->decl))
+ 	    DECL_EXTERNAL (node->decl) = 1;
+ 	}
        node->local.local = cgraph_local_node_p (node);
+ 
      }
    for (vnode = varpool_nodes; vnode; vnode = vnode->next)
      {
Index: ipa-inline-analysis.c
===================================================================
*** ipa-inline-analysis.c	(revision 173251)
--- ipa-inline-analysis.c	(working copy)
*************** compute_inline_parameters (struct cgraph
*** 1443,1448 ****
--- 1443,1465 ----
  
    info = inline_summary (node);
  
+   /* FIXME: Thunks are inlinable, but tree-inline don't know how to do that.
+      Once this happen, we will need to more curefully predict call
+      statement size.  */
+   if (node->thunk.thunk_p)
+     {
+       struct inline_edge_summary *es = inline_edge_summary (node->callees);
+       struct predicate t = true_predicate ();
+ 
+       info->inlinable = info->versionable = 0;
+       node->callees->call_stmt_cannot_inline_p = true;
+       node->local.can_change_signature = false;
+       es->call_stmt_time = 1;
+       es->call_stmt_size = 1;
+       account_size_time (info, 0, 0, &t);
+       return;
+     }
+ 
    /* Estimate the stack size for the function if we're optimizing.  */
    self_stack_size = optimize ? estimated_stack_frame_size (node) : 0;
    info->estimated_self_stack_size = self_stack_size;
*************** inline_analyze_function (struct cgraph_n
*** 2027,2033 ****
  	     cgraph_node_name (node), node->uid);
    /* FIXME: We should remove the optimize check after we ensure we never run
       IPA passes when not optimizing.  */
!   if (flag_indirect_inlining && optimize)
      inline_indirect_intraprocedural_analysis (node);
    compute_inline_parameters (node, false);
  
--- 2044,2050 ----
  	     cgraph_node_name (node), node->uid);
    /* FIXME: We should remove the optimize check after we ensure we never run
       IPA passes when not optimizing.  */
!   if (flag_indirect_inlining && optimize && !node->thunk.thunk_p)
      inline_indirect_intraprocedural_analysis (node);
    compute_inline_parameters (node, false);
  
*************** inline_generate_summary (void)
*** 2058,2065 ****
    if (flag_indirect_inlining)
      ipa_register_cgraph_hooks ();
  
!   for (node = cgraph_nodes; node; node = node->next)
!     if (node->analyzed)
        inline_analyze_function (node);
  }
  
--- 2075,2081 ----
    if (flag_indirect_inlining)
      ipa_register_cgraph_hooks ();
  
!   FOR_EACH_DEFINED_FUNCTION (node)
        inline_analyze_function (node);
  }
  
Index: lto/lto.c
===================================================================
*** lto/lto.c	(revision 173251)
--- lto/lto.c	(working copy)
*************** lto_materialize_function (struct cgraph_
*** 147,155 ****
    decl = node->decl;
    /* Read in functions with body (analyzed nodes)
       and also functions that are needed to produce virtual clones.  */
!   if (node->analyzed || has_analyzed_clone_p (node))
      {
!       /* Clones don't need to be read.  */
        if (node->clone_of)
  	return;
  
--- 147,155 ----
    decl = node->decl;
    /* Read in functions with body (analyzed nodes)
       and also functions that are needed to produce virtual clones.  */
!   if (cgraph_function_with_gimple_body_p (node) || has_analyzed_clone_p (node))
      {
!       /* Clones and thunks don't need to be read.  */
        if (node->clone_of)
  	return;
  
*************** static void
*** 1183,1188 ****
--- 1183,1194 ----
  add_cgraph_node_to_partition (ltrans_partition part, struct cgraph_node *node)
  {
    struct cgraph_edge *e;
+   cgraph_node_set_iterator csi;
+ 
+   /* If NODE is already there, we have nothing to do.  */
+   csi = cgraph_node_set_find (part->cgraph_set, node);
+   if (!csi_end_p (csi))
+     return;
  
    part->insns += inline_summary (node)->self_size;
  
*************** add_cgraph_node_to_partition (ltrans_par
*** 1197,1202 ****
--- 1203,1215 ----
  
    cgraph_node_set_add (part->cgraph_set, node);
  
+   /* Thunks always must go along with function they reffer to.  */
+   if (node->thunk.thunk_p)
+     add_cgraph_node_to_partition (part, node->callees->callee);
+   for (e = node->callers; e; e = e->next_caller)
+     if (e->caller->thunk.thunk_p)
+       add_cgraph_node_to_partition (part, e->caller);
+ 
    for (e = node->callees; e; e = e->next_callee)
      if ((!e->inline_failed || DECL_COMDAT (e->callee->decl))
  	&& !cgraph_node_in_set_p (e->callee, part->cgraph_set))
*************** add_cgraph_node_to_partition (ltrans_par
*** 1214,1219 ****
--- 1227,1239 ----
  static void
  add_varpool_node_to_partition (ltrans_partition part, struct varpool_node *vnode)
  {
+   varpool_node_set_iterator vsi;
+ 
+   /* If NODE is already there, we have nothing to do.  */
+   vsi = varpool_node_set_find (part->varpool_set, vnode);
+   if (!vsi_end_p (vsi))
+     return;
+ 
    varpool_node_set_add (part->varpool_set, vnode);
  
    if (vnode->aux)
Index: ipa-prop.c
===================================================================
*** ipa-prop.c	(revision 173251)
--- ipa-prop.c	(working copy)
*************** ipa_prop_write_jump_functions (cgraph_no
*** 2888,2894 ****
    for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
      {
        node = csi_node (csi);
!       if (node->analyzed && IPA_NODE_REF (node) != NULL)
  	count++;
      }
  
--- 2888,2895 ----
    for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
      {
        node = csi_node (csi);
!       if (cgraph_function_with_gimple_body_p (node)
! 	  && IPA_NODE_REF (node) != NULL)
  	count++;
      }
  
*************** ipa_prop_write_jump_functions (cgraph_no
*** 2898,2904 ****
    for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
      {
        node = csi_node (csi);
!       if (node->analyzed && IPA_NODE_REF (node) != NULL)
          ipa_write_node_info (ob, node);
      }
    lto_output_1_stream (ob->main_stream, 0);
--- 2899,2906 ----
    for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi))
      {
        node = csi_node (csi);
!       if (cgraph_function_with_gimple_body_p (node)
! 	  && IPA_NODE_REF (node) != NULL)
          ipa_write_node_info (ob, node);
      }
    lto_output_1_stream (ob->main_stream, 0);
Index: passes.c
===================================================================
*** passes.c	(revision 173251)
--- passes.c	(working copy)
*************** do_per_function_toporder (void (*callbac
*** 1135,1141 ****
  	  /* Allow possibly removed nodes to be garbage collected.  */
  	  order[i] = NULL;
  	  node->process = 0;
! 	  if (node->analyzed)
  	    {
  	      push_cfun (DECL_STRUCT_FUNCTION (node->decl));
  	      current_function_decl = node->decl;
--- 1135,1141 ----
  	  /* Allow possibly removed nodes to be garbage collected.  */
  	  order[i] = NULL;
  	  node->process = 0;
! 	  if (cgraph_function_with_gimple_body_p (node))
  	    {
  	      push_cfun (DECL_STRUCT_FUNCTION (node->decl));
  	      current_function_decl = node->decl;
*************** execute_one_pass (struct opt_pass *pass)
*** 1581,1590 ****
    if (pass->type == IPA_PASS)
      {
        struct cgraph_node *node;
!       for (node = cgraph_nodes; node; node = node->next)
!         if (node->analyzed)
!           VEC_safe_push (ipa_opt_pass, heap, node->ipa_transforms_to_apply,
! 			 (struct ipa_opt_pass_d *)pass);
      }
  
    if (!current_function_decl)
--- 1581,1589 ----
    if (pass->type == IPA_PASS)
      {
        struct cgraph_node *node;
!       FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
! 	VEC_safe_push (ipa_opt_pass, heap, node->ipa_transforms_to_apply,
! 		       (struct ipa_opt_pass_d *)pass);
      }
  
    if (!current_function_decl)
*************** ipa_write_summaries (void)
*** 1705,1711 ****
      {
        struct cgraph_node *node = order[i];
  
!       if (node->analyzed)
  	{
  	  /* When streaming out references to statements as part of some IPA
  	     pass summary, the statements need to have uids assigned and the
--- 1704,1710 ----
      {
        struct cgraph_node *node = order[i];
  
!       if (cgraph_function_with_gimple_body_p (node))
  	{
  	  /* When streaming out references to statements as part of some IPA
  	     pass summary, the statements need to have uids assigned and the
*************** ipa_write_summaries (void)
*** 1718,1724 ****
  	  pop_cfun ();
  	}
        if (node->analyzed)
! 	cgraph_node_set_add (set, node);
      }
    vset = varpool_node_set_new ();
  
--- 1717,1723 ----
  	  pop_cfun ();
  	}
        if (node->analyzed)
!         cgraph_node_set_add (set, node);
      }
    vset = varpool_node_set_new ();
  
*************** function_called_by_processed_nodes_p (vo
*** 2036,2042 ****
      {
        if (e->caller->decl == current_function_decl)
          continue;
!       if (!e->caller->analyzed)
          continue;
        if (TREE_ASM_WRITTEN (e->caller->decl))
          continue;
--- 2035,2041 ----
      {
        if (e->caller->decl == current_function_decl)
          continue;
!       if (!cgraph_function_with_gimple_body_p (e->caller))
          continue;
        if (TREE_ASM_WRITTEN (e->caller->decl))
          continue;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]