public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: Cgraph thunk reorg
@ 2011-05-10 21:48 David Edelsohn
  2011-05-11 13:42 ` Jan Hubicka
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: David Edelsohn @ 2011-05-10 21:48 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: GCC Patches, Richard Guenther, martin jambor

Honza,

This patch seems to have introduced approximately 150 new G++
testsuite failures on AIX.  The errors all are verify_cgraph_node ICEs
related to thunks.

The all have a similar pattern:

/src/gcc/testsuite/g++.dg/abi/covariant2.C:31:20: error:
non-DECL_ONE_ONLY node in a same_comdat_group list
c3* c7::_ZTch0_v0_n12_N2c72f6Ev()/3 @70097790 (asm:
_ZTch0_v0_n12_N2c72f6Ev) (same comdat group as virtual c3* c7::f6()/1)
availability:available analyzed address_taken externally_visible
finalized asm_written
  thunk of c3* *LTHUNK..0() (asm: *LTHUNK..0) fixed offset 0 virtual
value -12 has virtual offset 1)
  called by:
  calls: virtual c3* c7::f6()/1 (1.00 per call)
  References:
  Refering this function:  var:int (* c24::_ZTC3c244_2c7 [5])(...) (addr)
/src/gcc/testsuite/g++.dg/abi/covariant2.C:31:20: internal compiler
error: verify_cgraph_node failed

/src/gcc/testsuite/g++.old-deja/g++.robertl/eb30.C:16:1: error:
non-DECL_ONE_ONLY node in a same_comdat_group list
void bifstream::_ZTv0_n12_N9bifstreamD0Ev()/172 @709d1420 (asm:
_ZTv0_n12_N9bifstreamD0Ev) (same comdat group as virtual
bifstream::~bifstream()/170) availability:available analyzed
address_taken externally_visible finalized asm_written
  thunk of void *LTHUNK..1() (asm: *LTHUNK..1) fixed offset 0 virtual
value -12 has virtual offset 1)
  called by:
  calls: virtual bifstream::~bifstream()/170 (1.00 per call)
  References:
  Refering this function:  var:int (* bifstream::_ZTV9bifstream
[10])(...) (addr)
/src/gcc/testsuite/g++.old-deja/g++.robertl/eb30.C:16:1: internal
compiler error: verify_cgraph_node failed

/src/gcc/testsuite/g++.old-deja/g++.jason/mi.C:17:1: error:
non-DECL_ONE_ONLY node in a same_comdat_group list
void E::_ZThn4_N1E3fooEv()/4 @700976e0 (asm: _ZThn4_N1E3fooEv) (same
comdat group as virtual void E::foo()/2) availability:available
analyzed address_taken externally_visible finalized asm_written
  thunk of void *LTHUNK..0() (asm: *LTHUNK..0) fixed offset -4 virtual
value 0 has virtual offset 0)
  called by:
  calls: virtual void E::foo()/2 (1.00 per call)
  References:
  Refering this function:  var:int (* E::_ZTV1E [7])(...) (addr)
/src/gcc/testsuite/g++.old-deja/g++.jason/mi.C:17:1: internal compiler
error: verify_cgraph_node failed

Thanks, David

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-10 21:48 Cgraph thunk reorg David Edelsohn
@ 2011-05-11 13:42 ` Jan Hubicka
  2011-05-13 14:02 ` Jan Hubicka
       [not found] ` <20110513144959.GR32522@kam.mff.cuni.cz>
  2 siblings, 0 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-11 13:42 UTC (permalink / raw)
  To: David Edelsohn; +Cc: Jan Hubicka, GCC Patches, Richard Guenther, martin jambor

> Honza,
> 
> This patch seems to have introduced approximately 150 new G++
> testsuite failures on AIX.  The errors all are verify_cgraph_node ICEs
> related to thunks.
> 
> The all have a similar pattern:
Hmm, thanks.  It seems that visibility needs even more tweaking.
I am currently on a trip till Saturday but I will try to look into it ASAP.

Honza

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-10 21:48 Cgraph thunk reorg David Edelsohn
  2011-05-11 13:42 ` Jan Hubicka
@ 2011-05-13 14:02 ` Jan Hubicka
       [not found] ` <20110513144959.GR32522@kam.mff.cuni.cz>
  2 siblings, 0 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-13 14:02 UTC (permalink / raw)
  To: David Edelsohn; +Cc: Jan Hubicka, GCC Patches, Richard Guenther, martin jambor

Hi,
I built a cros with --enable-languages=c,c++ --disable-bootstrap --target=powerpc-ibm-aix5.3.0.0
and tried to compile covariant2.C and some of the other testcases you mention, but I don't
get the ICE.
Anything that could help me to reproduce this?

Honza
> Honza,
> 
> This patch seems to have introduced approximately 150 new G++
> testsuite failures on AIX.  The errors all are verify_cgraph_node ICEs
> related to thunks.
> 
> The all have a similar pattern:
> 
> /src/gcc/testsuite/g++.dg/abi/covariant2.C:31:20: error:
> non-DECL_ONE_ONLY node in a same_comdat_group list
> c3* c7::_ZTch0_v0_n12_N2c72f6Ev()/3 @70097790 (asm:
> _ZTch0_v0_n12_N2c72f6Ev) (same comdat group as virtual c3* c7::f6()/1)
> availability:available analyzed address_taken externally_visible
> finalized asm_written
>   thunk of c3* *LTHUNK..0() (asm: *LTHUNK..0) fixed offset 0 virtual
> value -12 has virtual offset 1)
>   called by:
>   calls: virtual c3* c7::f6()/1 (1.00 per call)
>   References:
>   Refering this function:  var:int (* c24::_ZTC3c244_2c7 [5])(...) (addr)
> /src/gcc/testsuite/g++.dg/abi/covariant2.C:31:20: internal compiler
> error: verify_cgraph_node failed
> 
> /src/gcc/testsuite/g++.old-deja/g++.robertl/eb30.C:16:1: error:
> non-DECL_ONE_ONLY node in a same_comdat_group list
> void bifstream::_ZTv0_n12_N9bifstreamD0Ev()/172 @709d1420 (asm:
> _ZTv0_n12_N9bifstreamD0Ev) (same comdat group as virtual
> bifstream::~bifstream()/170) availability:available analyzed
> address_taken externally_visible finalized asm_written
>   thunk of void *LTHUNK..1() (asm: *LTHUNK..1) fixed offset 0 virtual
> value -12 has virtual offset 1)
>   called by:
>   calls: virtual bifstream::~bifstream()/170 (1.00 per call)
>   References:
>   Refering this function:  var:int (* bifstream::_ZTV9bifstream
> [10])(...) (addr)
> /src/gcc/testsuite/g++.old-deja/g++.robertl/eb30.C:16:1: internal
> compiler error: verify_cgraph_node failed
> 
> /src/gcc/testsuite/g++.old-deja/g++.jason/mi.C:17:1: error:
> non-DECL_ONE_ONLY node in a same_comdat_group list
> void E::_ZThn4_N1E3fooEv()/4 @700976e0 (asm: _ZThn4_N1E3fooEv) (same
> comdat group as virtual void E::foo()/2) availability:available
> analyzed address_taken externally_visible finalized asm_written
>   thunk of void *LTHUNK..0() (asm: *LTHUNK..0) fixed offset -4 virtual
> value 0 has virtual offset 0)
>   called by:
>   calls: virtual void E::foo()/2 (1.00 per call)
>   References:
>   Refering this function:  var:int (* E::_ZTV1E [7])(...) (addr)
> /src/gcc/testsuite/g++.old-deja/g++.jason/mi.C:17:1: internal compiler
> error: verify_cgraph_node failed
> 
> Thanks, David

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
       [not found]   ` <BANLkTi=SYNwzNK4mpM9H4cEf4xcssfi4gQ@mail.gmail.com>
@ 2011-05-25 14:55     ` David Edelsohn
  2011-05-25 23:49       ` Jan Hubicka
  0 siblings, 1 reply; 13+ messages in thread
From: David Edelsohn @ 2011-05-25 14:55 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: GCC Patches

Honza,

After we debugged this offline, I assume that you applied a version of
the patch to trunk?

Thanks, David

On Fri, May 13, 2011 at 3:14 PM, David Edelsohn <dje.gcc@gmail.com> wrote:
> Honza,
>
> Testing is not complete, but testcases that failed with DECL_ONE_ONLY
> error now are passing with the later version of the patch you sent.
>
> - David
>
> On Fri, May 13, 2011 at 10:49 AM, Jan Hubicka <hubicka@ucw.cz> wrote:
>> Hi,
>> please also try this patch
>>
>> Index: ipa.c
>> ===================================================================
>> --- ipa.c       (revision 173723)
>> +++ ipa.c       (working copy)
>> @@ -886,6 +886,9 @@ function_and_variable_visibility (bool w
>>          while (decl_node->thunk.thunk_p)
>>            decl_node = decl_node->callees->callee;
>>
>> +          DECL_COMDAT_GROUP (node->decl) = DECL_COMDAT_GROUP (decl_node->decl);
>> +         DECL_COMDAT (node->decl) = DECL_COMDAT (decl_node->decl);
>> +
>>          /* 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
>> @@ -893,10 +896,8 @@ function_and_variable_visibility (bool w
>>
>>             We also need to arrange the thunk into the same comdat group as
>>             the function it reffers to.  */
>> -         if (DECL_COMDAT (decl_node->decl))
>> +         if (DECL_ONE_ONLY (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;
>>
>

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-25 14:55     ` David Edelsohn
@ 2011-05-25 23:49       ` Jan Hubicka
  0 siblings, 0 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-25 23:49 UTC (permalink / raw)
  To: David Edelsohn; +Cc: Jan Hubicka, GCC Patches

> Honza,
> 
> After we debugged this offline, I assume that you applied a version of
> the patch to trunk?

Hi,
sorry, there was a miscommunication.  I was still waiting for a confirmation
that the patch works and I do not recall receiving any. I was not waiting very
actively, I must admit, since I left to conference :)  Rainer today commited
variant of this patch for equivalent failure happening on Alpha, so hope the
problem is solved now.


Honza

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 23:31   ` Jan Hubicka
@ 2011-05-15 22:19     ` H.J. Lu
  0 siblings, 0 replies; 13+ messages in thread
From: H.J. Lu @ 2011-05-15 22:19 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Richard Guenther, gcc-patches, mjambor

On Fri, May 6, 2011 at 4:02 PM, Jan Hubicka <hubicka@ucw.cz> wrote:
> 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.

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48938


H.J.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 13:00 ` Richard Guenther
  2011-05-06 16:56   ` Jan Hubicka
  2011-05-06 17:05   ` Jan Hubicka
@ 2011-05-06 23:31   ` Jan Hubicka
  2011-05-15 22:19     ` H.J. Lu
  2 siblings, 1 reply; 13+ messages in thread
From: Jan Hubicka @ 2011-05-06 23:31 UTC (permalink / raw)
  To: Richard Guenther; +Cc: Jan Hubicka, gcc-patches, mjambor

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;

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 13:00 ` Richard Guenther
  2011-05-06 16:56   ` Jan Hubicka
@ 2011-05-06 17:05   ` Jan Hubicka
  2011-05-06 23:31   ` Jan Hubicka
  2 siblings, 0 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-06 17:05 UTC (permalink / raw)
  To: Richard Guenther; +Cc: Jan Hubicka, gcc-patches, mjambor

Forgot to reply this one...
> > *************** assemble_thunk (struct cgraph_node *node
> > *** 1406,1411 ****
> > --- 1427,1433 ----
> >         free_after_compilation (cfun);
> >         set_cfun (NULL);
> >         TREE_ASM_WRITTEN (thunk_fndecl) = 1;
> > +       node->thunk.thunk_p = false;
> 
> Hmm.  Doesn't that confuse regular passes who now see the thunk no
> longer as thunk?

... this happens only at expansion times. No other regular passes are run, so
it is safe.  Clearing thunk_p flag has several resons: it makes
add_new_function happy, it prevents verifier from complaining on thunk with
gimple body and it prevents assemble_thunks to output thunk several times.

Incrementally perhaps I can allow thunks to optionally have gimple bodies. This
would be handy also for inliner who consequently would be able to handle them
as normal function as it don't really care about variadic thunks (unlike
ipa-prop).  This is just minor loss of code quality as we inline only variadic
functions that never start va_start. I.e. they either ignore the variadic arugments
or use va_arg_pack.  I can imagine people doing the first for virtual functions,
the second don't really make sense as va_arg_pack should be used only in always_inlines
and those can not be virtual.

negative side of doing so is that, I am affraid that IPA passes will start
modify these bodies in free manner and boil down the ASM output path.  So
perhaps it is easier to explain inliner explicietly what "inlining thunk" means
that will also allow us to handle variadic ones and stick on current way.

Something I want to handle incrementally: obviously solution exists, just we want one
that is not too ugly. At the moment inlning of thunks is moot given that we have
only one testcase that produce direct call to a thunk.  Hopefully we will noticeably
improve in devirtualization and this will become all much more fun.

Honza
> 
> >       }
> >     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
> > --- 1552,1587 ----
> >         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.  */
> > --- 1609,1620 ----
> >   	  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);
> > --- 1730,1736 ----
> >   
> >     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, 1);
> >     lto_output_bitpack (&bp);
> >     lto_output_uleb128_stream (ob->main_stream, node->resolution);
> >   
> > +   if (node->thunk.thunk_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,922 ----
> >   	       segfault though. */
> >   	    dissolve_same_comdat_group_list (node);
> >   	}
> > +       if (node->thunk.thunk_p)
> > + 	{
> > + 	  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 = decl_node;
> > + 		    }
> > + 		}
> > + 	    }
> > + 	  if (DECL_EXTERNAL (decl_node->decl))
> > + 	    DECL_EXTERNAL (node->decl) = 1;
> 
> That's indeed remarkably ugly and I hope the C++ FE people can do
> sth about this ...
> 
> > + 	}
> >         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
> 
> carefully
> 
> > +      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
> > *** 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);
> > --- 2898,2905 ----
> >     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;
> 
> I think the rest looks reasonable.
> 
> Thanks,
> Richard.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 13:00 ` Richard Guenther
@ 2011-05-06 16:56   ` Jan Hubicka
  2011-05-06 17:05   ` Jan Hubicka
  2011-05-06 23:31   ` Jan Hubicka
  2 siblings, 0 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-06 16:56 UTC (permalink / raw)
  To: Richard Guenther; +Cc: Jan Hubicka, gcc-patches, mjambor

> > *************** 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)
> > + 	  && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))
> 
> && optimize ?  That somehow looks weird.

The tests arranges that virtual functions are kept in cgraph even if they don't
seem to be needed.  This is because type based devirtualization may create
direct references for them and we won't see them before this happens.  The
functions, when they did not became reachable, are removed after IPA inline
pass.

With -O0 we don't care about devirtualization and it already knows to now introduce
new references to already optimized out functions.
As FIXME on the other occurence of this test says, this will all go away once we
have MAY edges in the callgraph.  Then we will see what functions are possible
destinations of virtual calls.

> > --- 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))))
> 
> again?

Same reason.

> > !       if (gimple_has_body_p (node->decl))
> > !         {
> > ! 	  error ("Thunk is not supposed to have body");
> > !           error_found = true;
> 
> Is that true for targets that do not have asm thunks?  Thus, did
> we get rid of the path in the C++ FE that emits thunks as regular
> GENERIC functions?

C++ FE never emits thunks as regular generic functions.  Cgraph does that at a
time function they are associated to is expanded and at that time the thunk
info is cleared and they are turned into regular node (and output to assemby at
the same time).

> > !   if (node->thunk.thunk_p)
> > !     {
> > !       cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
> > ! 			  NULL, 0, CGRAPH_FREQ_BASE);
> 
> Ick ;)
> 
> Why not do this at thunk cgraph node creation time?

Well, analysis anyway has to walk them, because it does the reachability computation.
Also at the time we get thunk, C++ frontend did not finalized yet the function.  I am
trying to move us away from creating random stale nodes and instead creating all function
nodes at the time they are finalized and all external nodes at cgraph construction.

Honza

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 12:34 Jan Hubicka
  2011-05-06 13:00 ` Richard Guenther
  2011-05-06 15:08 ` Michael Matz
@ 2011-05-06 16:05 ` Mike Stump
  2 siblings, 0 replies; 13+ messages in thread
From: Mike Stump @ 2011-05-06 16:05 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, mjambor

On May 6, 2011, at 5:12 AM, Jan Hubicka wrote:
>  2) As real functions calling the function they are associated with.
> 
>      Because backend don't handle alternative entry points, we really implement
>      thunks as small functions that usually tail call into the associated
>      functions after doing adjustments to THIS.

I don't like the word usually here...  that tail call for non-adjusting covariant returns and non-covariant returns, and a normal call otherwise.... Since this is just the email description, not a problem, but I did want to ensure people don't think of thunks as tail calling.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 12:34 Jan Hubicka
  2011-05-06 13:00 ` Richard Guenther
@ 2011-05-06 15:08 ` Michael Matz
  2011-05-06 16:05 ` Mike Stump
  2 siblings, 0 replies; 13+ messages in thread
From: Michael Matz @ 2011-05-06 15:08 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, mjambor

Hi,

On Fri, 6 May 2011, Jan Hubicka wrote:

>   
> *************** 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,1907 ----
>     if (node->only_called_at_exit)
>       fprintf (f, " only_called_at_exit");
>   
> !   fprintf (f, "\n");
> ! 
> !   if (node->thunk.thunk_p)
> !     {
> !       if (node->thunk.thunk_p)
> ! 	{

Doubled conditional.


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Cgraph thunk reorg
  2011-05-06 12:34 Jan Hubicka
@ 2011-05-06 13:00 ` Richard Guenther
  2011-05-06 16:56   ` Jan Hubicka
                     ` (2 more replies)
  2011-05-06 15:08 ` Michael Matz
  2011-05-06 16:05 ` Mike Stump
  2 siblings, 3 replies; 13+ messages in thread
From: Richard Guenther @ 2011-05-06 13:00 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, mjambor

On Fri, 6 May 2011, Jan Hubicka wrote:

> Hi,
> this patch implements thunks as real cgraph nodes instead of alias nodes.  I am not
> entirely happy about it, but I can't come with anything better.
> 
> The main problem is that thunks can be seen in two ways:
> 
>   1) As alternative entry points into functions
> 
>       This is how the existing code attempts to be structured: thunks do not
>       appear in callgraph, instead of the calgraph edges points to the
>       functions the thunk are associated with.
> 
>       The problem with current code is that none of IPA code nor rest of
>       compiler is familiar with the concept of alternative entry points.
>       Consequentely the direct calls to thunks appears in the program
>       in equivalent way as direct calls to function they are associated
>       to that consequentely may lead to miscompilations when we decide to
>       inline and ignore thunk or do ipa-prop.
> 
>       As a temporary measure, we declared direct calls to thunk invalid.
>       This lead to need for devirtualization code to "inline" the thunk
>       when devirtualizing the call or to not devirtualize.  For siple thunks
>       this is not big deal to do, but for covariant thunks this imply
>       extra control flow that is something Richi don't like.
>       Also we now devirtualize "implicitely" via folding lookups into the
>       vtables. Requiring that code to ponder about thunk adjustments don't
>       look quite right.
> 
>       Next problem is that with LTO we can merge direct call to external
>       function with thunk and in this case we have to represent the direct
>       call to thunk.
> 
>       To allow direct calls to thunks would mean adding concept of entry
>       points into callgraph edgess that would mean next pointer to something
>       that would describe it.  Most probably chain of thunk structures:
>       we do allow and build thunks of thunks.
> 
>       We discussed this quite few times on IRC and always this was voted
>       down as weird. One argument agains is that it will be easy to do
>       simple wrong code bugs by forgetting about the info hanging on cgraph
>       edges, since in most cases there is nothing.
> 
>   2) As real functions calling the function they are associated with.
> 
>       Because backend don't handle alternative entry points, we really implement
>       thunks as small functions that usually tail call into the associated
>       functions after doing adjustments to THIS.
> 
>       Other natural abstraction seems to be handle thunks as real functions.
>       This is what the patch does.  There are several issues with this.
> 
>       1) Not all thunks have bodies that represent in gimple. The variadic
>       thunks currently don't have any gimple representation. While we can
>       come with some, there is not that much of value for it because...
>       2) We can't expand thunks into RTL.  On many archs we have existing
>       ASM output machinery that leads to better code (and only possible code
>       for variadic thunks that are not really representable in RTL either).
>       3) Thunks are not real functions in C++ ABI sense. They share comdat
>       groups implicitely and they must be output in specified order to
>       get proper comdat group signatures
> 
>       This patch takes this route and does the compensation where needed.
>       In particular all IPA passes that worries about gimple bodies needs
>       to be updated to handle thunks.  This is not that hard to do and as
>       first cut I simply disabled inlining, ipa-prop and cloning on thunks.
>       We can handle that incrementally.
> 
> The problem of thunks is related to problem of proper representation of aliases.
> Again aliases can be "transparent" that is not having cgraph nodes to them
> and all edges going to the final destination or they can be separate nodes.
> I originally indended to go for the first case that also has problem with
> representing the visibilities of aliases: i.e. depending on alias used, the
> edges may or may not be overwritable by the linker, so the alternative entry
> point info would need to represent this, too.
> 
> With thunks as separate nodes, I will turn aliases into separate nodes, too
> that will have link via ipa-ref infrastructure (i.e. in addition to load/store
> and address links we will also have alias links).
> 
> Because IPA passes really care about objects themselves, not the aliases
> (i.e. ipa-reference or ipa-pta wants to see the variable and all its aliases
> as one object, so wants the inliner or ipa-propagate), we will need to add
> some accessor functions that will walk to real destination of the edge
> and also walk all real objects referencing the given object skipping the
> aliases.
> 
> This approach has the advantage of getting cgraph/varpool closer to symbol
> table and making things bit easier at lto-symtab side.
> 
> The patch does basicaly the following:
> 
>  1) turns thunks from alias node into function nodes with node->thunk.thunk_p
>     flag set
>  2) updates verifier, dumping and LTO streaming to handle them correctly
>  3) updates way how functions are expanded: thunks can not be handled as
>     normal function, since they are required to appear at specific place
>     in the asm file
>  4) Adds hack to ipa visibility since C++ frontend gets visibility of thunks
>     wrong.  COMDAT functions do have non-comdat thunks and they are not in
>     the same comdat group. Fixing this at C++ frontend seems hard becuase
>     it does use the flags for other purposes
>  5) Adds code to ipa-inline-analysis and ipa-prop to make thunks as opaque
>     as possible, for now
>  6) Make ipa-pure-const to see them transparently.
>  7) Talks out ipa-cp from idea of redirecting thunks
>  8) Updates WHOPR partitioning so thunks are always associated with their functions
>     and not split into different partition.
>  9) Adds FOR_EACH_FUNCTION_WITH_GIMPLE_BODY/FOR_EACH_DEFINED_FUNCTION
>     functions to walk cgraph nodes.  This is borrowed from my symtab code
>     where I no longer have the topleve list of cgraph nodes per se, just
>     list of symbols (and symbols are functions,variables and aliases)
> 
> The patch regstests&bootstraps x86_64-linux.  I plan to give it more testing
> with Mozilla and other C++ apps and wait for few days for comments before
> comitting.

Some comments/questions inline

> Honza
> 
> 	* 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)
> + 	  && optimize && (DECL_COMDAT (decl) || DECL_EXTERNAL (decl))))

&& optimize ?  That somehow looks weird.

> +     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,1907 ----
>     if (node->only_called_at_exit)
>       fprintf (f, " only_called_at_exit");
>   
> !   fprintf (f, "\n");
> ! 
> !   if (node->thunk.thunk_p)
> !     {
> !       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)));
>   	}
> --- 1953,1962 ----
>     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))))

again?

>       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;

Is that true for targets that do not have asm thunks?  Thus, did
we get rid of the path in the C++ FE that emits thunks as regular
GENERIC functions?

> !         }
> !     }
> !   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);

Ick ;)

Why not do this at thunk cgraph node creation time?

> !     }
> !   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)
> *** 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));
> --- 1047,1058 ----
>         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)
> --- 1061,1068 ----
>   	}
>         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 ****
> --- 1151,1157 ----
>   	 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)
> --- 1165,1172 ----
>   	      for (next = node->same_comdat_group;
>   		   next != node;
>   		   next = next->same_comdat_group)
> ! 		if (!node->thunk.thunk_p)
> ! 		  next->process = 1;
>   	    }
>   	}
>         else if (node->same_comdat_group)
> *************** assemble_thunk (struct cgraph_node *node
> *** 1406,1411 ****
> --- 1427,1433 ----
>         free_after_compilation (cfun);
>         set_cfun (NULL);
>         TREE_ASM_WRITTEN (thunk_fndecl) = 1;
> +       node->thunk.thunk_p = false;

Hmm.  Doesn't that confuse regular passes who now see the thunk no
longer as thunk?

>       }
>     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
> --- 1552,1587 ----
>         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.  */
> --- 1609,1620 ----
>   	  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);
> --- 1730,1736 ----
>   
>     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, 1);
>     lto_output_bitpack (&bp);
>     lto_output_uleb128_stream (ob->main_stream, node->resolution);
>   
> +   if (node->thunk.thunk_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,922 ----
>   	       segfault though. */
>   	    dissolve_same_comdat_group_list (node);
>   	}
> +       if (node->thunk.thunk_p)
> + 	{
> + 	  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 = decl_node;
> + 		    }
> + 		}
> + 	    }
> + 	  if (DECL_EXTERNAL (decl_node->decl))
> + 	    DECL_EXTERNAL (node->decl) = 1;

That's indeed remarkably ugly and I hope the C++ FE people can do
sth about this ...

> + 	}
>         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

carefully

> +      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
> *** 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);
> --- 2898,2905 ----
>     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;

I think the rest looks reasonable.

Thanks,
Richard.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Cgraph thunk reorg
@ 2011-05-06 12:34 Jan Hubicka
  2011-05-06 13:00 ` Richard Guenther
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Jan Hubicka @ 2011-05-06 12:34 UTC (permalink / raw)
  To: gcc-patches, rguenther, mjambor

Hi,
this patch implements thunks as real cgraph nodes instead of alias nodes.  I am not
entirely happy about it, but I can't come with anything better.

The main problem is that thunks can be seen in two ways:

  1) As alternative entry points into functions

      This is how the existing code attempts to be structured: thunks do not
      appear in callgraph, instead of the calgraph edges points to the
      functions the thunk are associated with.

      The problem with current code is that none of IPA code nor rest of
      compiler is familiar with the concept of alternative entry points.
      Consequentely the direct calls to thunks appears in the program
      in equivalent way as direct calls to function they are associated
      to that consequentely may lead to miscompilations when we decide to
      inline and ignore thunk or do ipa-prop.

      As a temporary measure, we declared direct calls to thunk invalid.
      This lead to need for devirtualization code to "inline" the thunk
      when devirtualizing the call or to not devirtualize.  For siple thunks
      this is not big deal to do, but for covariant thunks this imply
      extra control flow that is something Richi don't like.
      Also we now devirtualize "implicitely" via folding lookups into the
      vtables. Requiring that code to ponder about thunk adjustments don't
      look quite right.

      Next problem is that with LTO we can merge direct call to external
      function with thunk and in this case we have to represent the direct
      call to thunk.

      To allow direct calls to thunks would mean adding concept of entry
      points into callgraph edgess that would mean next pointer to something
      that would describe it.  Most probably chain of thunk structures:
      we do allow and build thunks of thunks.

      We discussed this quite few times on IRC and always this was voted
      down as weird. One argument agains is that it will be easy to do
      simple wrong code bugs by forgetting about the info hanging on cgraph
      edges, since in most cases there is nothing.

  2) As real functions calling the function they are associated with.

      Because backend don't handle alternative entry points, we really implement
      thunks as small functions that usually tail call into the associated
      functions after doing adjustments to THIS.

      Other natural abstraction seems to be handle thunks as real functions.
      This is what the patch does.  There are several issues with this.

      1) Not all thunks have bodies that represent in gimple. The variadic
      thunks currently don't have any gimple representation. While we can
      come with some, there is not that much of value for it because...
      2) We can't expand thunks into RTL.  On many archs we have existing
      ASM output machinery that leads to better code (and only possible code
      for variadic thunks that are not really representable in RTL either).
      3) Thunks are not real functions in C++ ABI sense. They share comdat
      groups implicitely and they must be output in specified order to
      get proper comdat group signatures

      This patch takes this route and does the compensation where needed.
      In particular all IPA passes that worries about gimple bodies needs
      to be updated to handle thunks.  This is not that hard to do and as
      first cut I simply disabled inlining, ipa-prop and cloning on thunks.
      We can handle that incrementally.

The problem of thunks is related to problem of proper representation of aliases.
Again aliases can be "transparent" that is not having cgraph nodes to them
and all edges going to the final destination or they can be separate nodes.
I originally indended to go for the first case that also has problem with
representing the visibilities of aliases: i.e. depending on alias used, the
edges may or may not be overwritable by the linker, so the alternative entry
point info would need to represent this, too.

With thunks as separate nodes, I will turn aliases into separate nodes, too
that will have link via ipa-ref infrastructure (i.e. in addition to load/store
and address links we will also have alias links).

Because IPA passes really care about objects themselves, not the aliases
(i.e. ipa-reference or ipa-pta wants to see the variable and all its aliases
as one object, so wants the inliner or ipa-propagate), we will need to add
some accessor functions that will walk to real destination of the edge
and also walk all real objects referencing the given object skipping the
aliases.

This approach has the advantage of getting cgraph/varpool closer to symbol
table and making things bit easier at lto-symtab side.

The patch does basicaly the following:

 1) turns thunks from alias node into function nodes with node->thunk.thunk_p
    flag set
 2) updates verifier, dumping and LTO streaming to handle them correctly
 3) updates way how functions are expanded: thunks can not be handled as
    normal function, since they are required to appear at specific place
    in the asm file
 4) Adds hack to ipa visibility since C++ frontend gets visibility of thunks
    wrong.  COMDAT functions do have non-comdat thunks and they are not in
    the same comdat group. Fixing this at C++ frontend seems hard becuase
    it does use the flags for other purposes
 5) Adds code to ipa-inline-analysis and ipa-prop to make thunks as opaque
    as possible, for now
 6) Make ipa-pure-const to see them transparently.
 7) Talks out ipa-cp from idea of redirecting thunks
 8) Updates WHOPR partitioning so thunks are always associated with their functions
    and not split into different partition.
 9) Adds FOR_EACH_FUNCTION_WITH_GIMPLE_BODY/FOR_EACH_DEFINED_FUNCTION
    functions to walk cgraph nodes.  This is borrowed from my symtab code
    where I no longer have the topleve list of cgraph nodes per se, just
    list of symbols (and symbols are functions,variables and aliases)

The patch regstests&bootstraps x86_64-linux.  I plan to give it more testing
with Mozilla and other C++ apps and wait for few days for comments before
comitting.

Honza

	* 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)
+ 	  && optimize && (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,1907 ----
    if (node->only_called_at_exit)
      fprintf (f, " only_called_at_exit");
  
!   fprintf (f, "\n");
! 
!   if (node->thunk.thunk_p)
!     {
!       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)));
  	}
--- 1953,1962 ----
    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)
*** 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));
--- 1047,1058 ----
        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)
--- 1061,1068 ----
  	}
        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 ****
--- 1151,1157 ----
  	 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)
--- 1165,1172 ----
  	      for (next = node->same_comdat_group;
  		   next != node;
  		   next = next->same_comdat_group)
! 		if (!node->thunk.thunk_p)
! 		  next->process = 1;
  	    }
  	}
        else if (node->same_comdat_group)
*************** assemble_thunk (struct cgraph_node *node
*** 1406,1411 ****
--- 1427,1433 ----
        free_after_compilation (cfun);
        set_cfun (NULL);
        TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+       node->thunk.thunk_p = 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
--- 1552,1587 ----
        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.  */
--- 1609,1620 ----
  	  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);
--- 1730,1736 ----
  
    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, 1);
    lto_output_bitpack (&bp);
    lto_output_uleb128_stream (ob->main_stream, node->resolution);
  
+   if (node->thunk.thunk_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,922 ----
  	       segfault though. */
  	    dissolve_same_comdat_group_list (node);
  	}
+       if (node->thunk.thunk_p)
+ 	{
+ 	  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 = decl_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
*** 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);
--- 2898,2905 ----
    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;

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2011-05-25 21:58 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-10 21:48 Cgraph thunk reorg David Edelsohn
2011-05-11 13:42 ` Jan Hubicka
2011-05-13 14:02 ` Jan Hubicka
     [not found] ` <20110513144959.GR32522@kam.mff.cuni.cz>
     [not found]   ` <BANLkTi=SYNwzNK4mpM9H4cEf4xcssfi4gQ@mail.gmail.com>
2011-05-25 14:55     ` David Edelsohn
2011-05-25 23:49       ` Jan Hubicka
  -- strict thread matches above, loose matches on Subject: below --
2011-05-06 12:34 Jan Hubicka
2011-05-06 13:00 ` Richard Guenther
2011-05-06 16:56   ` Jan Hubicka
2011-05-06 17:05   ` Jan Hubicka
2011-05-06 23:31   ` Jan Hubicka
2011-05-15 22:19     ` H.J. Lu
2011-05-06 15:08 ` Michael Matz
2011-05-06 16:05 ` Mike Stump

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).