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;

Reply via email to