Hi,
C++ virtual tables keyed to other compilation units are represented as 
DECL_EXTERNAL
variables with constructor known. Knowhing the constructor helps constant 
folding to do
devirtualization.

At the moment these costructors are not seen by varpool and thus they are not
represented by ipa-ref and thus WHOPR partitioning is not seeing them and we
later in can_refer_decl_in_current_unit_p try to work out if the partitioning
was done in lucky or unlucky way.

This patch makes external variables to be handled similarly to external 
functions.
That is the variables gets finalized and analyzed by varpool. They go in similar
way to partitioning as comdat functions.

Code removing unreachable nodes treats them as normal variables until after
inlining when vars/funcions referred only by those are considred unreachable.
This also allows us to remove the constructors from memory when we know they
are no longer needed saving couple hundred KB on compiling Mozilla with LTO.

The patch also enables aboud 3000 extra foldings on Mozilla LTO build.
Mostly those are devirtualized calls to libstdc++.

Bootstrapped/regtested x86_64-linux, comitted.
Index: ChangeLog
===================================================================
*** ChangeLog   (revision 187630)
--- ChangeLog   (working copy)
***************
*** 1,3 ****
--- 1,26 ----
+ 2012-05-17  Jan Hubicka  <j...@suse.cz>
+ 
+       * lto-symtab.c (lto_symtab_resolve_symbols): Preffer decl with 
constructor
+       over decl without.
+       * cgraph.c (cgraph_remove_node): Clear also body of unanalyzed nodes.
+       * cgraph.h (varpool_can_remove_if_no_refs): Handle external correctly.
+       * cgraphunit.c (process_function_and_variable_attributes): Finalize 
+       extrnal decls.
+       (mark_functions_to_output): Also accept bodies for functions with 
clones.
+       (output_in_order): Skip external vars.
+       * lto-cgraph.c (lto_output_node): External functions are never in other
+       partition.
+       (lto_output_varpool_node): Likewise.
+       * lto-streamer-out.c (lto_write_tree): Always use error_mark_nodes for
+       forgotten initializers.
+       * ipa.c (process_references): Handle external vars.
+       (symtab_remove_unreachable_nodes): Update to handle external vars.
+       (varpool_externally_visible_p): External vars are externally visible.
+       * gimple-fold.c (can_refer_decl_in_current_unit_p): Update.
+       * varpool.c (varpool_remove_node): Remove constructor.
+       (decide_is_variable_needed): Handle externals.
+       (varpool_remove_unreferenced_decls): Likewise.
+ 
  2012-05-17  Manuel López-Ibáñez  <m...@gcc.gnu.org>
  
        * opts.c (common_handle_option): -pedantic-errors enables -Wpedantic.
Index: lto/ChangeLog
===================================================================
*** lto/ChangeLog       (revision 187630)
--- lto/ChangeLog       (working copy)
***************
*** 1,3 ****
--- 1,9 ----
+ 2012-05-17  Jan Hubicka  <j...@suse.cz>
+ 
+       * lto-partition.c (add_references_to_partition): Handle external vars.
+       (partition_varpool_node_p): Likewise.
+       (lto_promote_cross_file_statics): Do not promote externals.
+ 
  2012-05-14  Bernd Schmidt  <ber...@codesourcery.com>
  
        * lto-lang.c (handle_fnspec_attribute): New static function.
Index: lto-symtab.c
===================================================================
*** lto-symtab.c        (revision 187630)
--- lto-symtab.c        (working copy)
*************** lto_symtab_resolve_symbols (void **slot)
*** 489,495 ****
        /* From variables that can prevail choose the largest one.  */
        if (!prevailing
          || tree_int_cst_lt (DECL_SIZE (prevailing->decl),
!                             DECL_SIZE (e->decl)))
        prevailing = e;
      }
  
--- 489,509 ----
        /* From variables that can prevail choose the largest one.  */
        if (!prevailing
          || tree_int_cst_lt (DECL_SIZE (prevailing->decl),
!                             DECL_SIZE (e->decl))
!         /* When variables are equivalent try to chose one that has useful
!            DECL_INITIAL.  This makes sense for keyed vtables that are
!            DECL_EXTERNAL but initialized.  In units that do not need them
!            we replace the initializer by error_mark_node to conserve
!            memory.
! 
!            We know that the vtable is keyed outside the LTO unit - otherwise
!            the keyed instance would prevail.  We still can preserve useful
!            info in the initializer.  */
!         || (DECL_SIZE (prevailing->decl) == DECL_SIZE (e->decl)
!             && (DECL_INITIAL (e->decl)
!                 && DECL_INITIAL (e->decl) != error_mark_node)
!             && (!DECL_INITIAL (prevailing->decl)
!                 || DECL_INITIAL (prevailing->decl) == error_mark_node)))
        prevailing = e;
      }
  
Index: cgraph.c
===================================================================
*** cgraph.c    (revision 187630)
--- cgraph.c    (working copy)
*************** cgraph_remove_node (struct cgraph_node *
*** 1242,1247 ****
--- 1242,1248 ----
          && (cgraph_global_info_ready
              && (TREE_ASM_WRITTEN (n->symbol.decl)
                  || DECL_EXTERNAL (n->symbol.decl)
+                 || !n->analyzed
                  || n->symbol.in_other_partition))))
      cgraph_release_function_body (node);
  
Index: cgraph.h
===================================================================
*** cgraph.h    (revision 187630)
--- cgraph.h    (working copy)
*************** cgraph_only_called_directly_or_aliased_p
*** 1123,1133 ****
  static inline bool
  varpool_can_remove_if_no_refs (struct varpool_node *node)
  {
    return (!node->symbol.force_output && 
!node->symbol.used_from_other_partition
          && (DECL_COMDAT (node->symbol.decl)
!         || !node->symbol.externally_visible
!         || DECL_HAS_VALUE_EXPR_P (node->symbol.decl)
!         || DECL_EXTERNAL (node->symbol.decl)));
  }
  
  /* Return true when all references to VNODE must be visible in ipa_ref_list.
--- 1123,1134 ----
  static inline bool
  varpool_can_remove_if_no_refs (struct varpool_node *node)
  {
+   if (DECL_EXTERNAL (node->symbol.decl))
+     return true;
    return (!node->symbol.force_output && 
!node->symbol.used_from_other_partition
          && (DECL_COMDAT (node->symbol.decl)
!             || !node->symbol.externally_visible
!             || DECL_HAS_VALUE_EXPR_P (node->symbol.decl)));
  }
  
  /* Return true when all references to VNODE must be visible in ipa_ref_list.
Index: cgraphunit.c
===================================================================
*** cgraphunit.c        (revision 187630)
--- cgraphunit.c        (working copy)
*************** process_function_and_variable_attributes
*** 782,787 ****
--- 782,791 ----
         vnode = varpool_next_variable (vnode))
      {
        tree decl = vnode->symbol.decl;
+       if (DECL_EXTERNAL (decl)
+         && DECL_INITIAL (decl)
+         && const_value_known_p (decl))
+       varpool_finalize_decl (decl);
        if (DECL_PRESERVE_P (decl))
        vnode->symbol.force_output = true;
        else if (lookup_attribute ("externally_visible", DECL_ATTRIBUTES 
(decl)))
*************** mark_functions_to_output (void)
*** 1161,1166 ****
--- 1165,1172 ----
          gcc_assert (node->global.inlined_to
                      || !gimple_has_body_p (decl)
                      || node->symbol.in_other_partition
+                     || node->clones
+                     || DECL_ARTIFICIAL (decl)
                      || DECL_EXTERNAL (decl));
  
        }
*************** output_in_order (void)
*** 1760,1771 ****
      }
  
    FOR_EACH_DEFINED_VARIABLE (pv)
!     {
!       i = pv->symbol.order;
!       gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
!       nodes[i].kind = ORDER_VAR;
!       nodes[i].u.v = pv;
!     }
  
    for (pa = asm_nodes; pa; pa = pa->next)
      {
--- 1766,1778 ----
      }
  
    FOR_EACH_DEFINED_VARIABLE (pv)
!     if (!DECL_EXTERNAL (pv->symbol.decl))
!       {
!       i = pv->symbol.order;
!       gcc_assert (nodes[i].kind == ORDER_UNDEFINED);
!       nodes[i].kind = ORDER_VAR;
!       nodes[i].u.v = pv;
!       }
  
    for (pa = asm_nodes; pa; pa = pa->next)
      {
Index: lto-cgraph.c
===================================================================
*** lto-cgraph.c        (revision 187630)
--- lto-cgraph.c        (working copy)
*************** lto_output_node (struct lto_simple_outpu
*** 440,446 ****
       Cherry-picked nodes:  These are nodes we pulled from other
       translation units into SET during IPA-inlining.  We make them as
       local static nodes to prevent clashes with other local statics.  */
!   if (boundary_p && node->analyzed)
      {
        /* Inline clones can not be part of boundary.  
           gcc_assert (!node->global.inlined_to);  
--- 440,446 ----
       Cherry-picked nodes:  These are nodes we pulled from other
       translation units into SET during IPA-inlining.  We make them as
       local static nodes to prevent clashes with other local statics.  */
!   if (boundary_p && node->analyzed && !DECL_EXTERNAL (node->symbol.decl))
      {
        /* Inline clones can not be part of boundary.  
           gcc_assert (!node->global.inlined_to);  
*************** lto_output_varpool_node (struct lto_simp
*** 575,580 ****
--- 575,581 ----
       FIXME: Alternatively at -Os we may want to avoid generating for them the 
local
       labels and share them across LTRANS partitions.  */
    if (DECL_IN_CONSTANT_POOL (node->symbol.decl)
+       && !DECL_EXTERNAL (node->symbol.decl)
        && !DECL_COMDAT (node->symbol.decl))
      {
        bp_pack_value (&bp, 0, 1);  /* used_from_other_parition.  */
*************** lto_output_varpool_node (struct lto_simp
*** 585,591 ****
        bp_pack_value (&bp, node->analyzed
                     && referenced_from_other_partition_p 
(&node->symbol.ref_list,
                                                           set, vset), 1);
!       bp_pack_value (&bp, boundary_p, 1);  /* in_other_partition.  */
      }
    streamer_write_bitpack (&bp);
    if (node->alias_of)
--- 586,593 ----
        bp_pack_value (&bp, node->analyzed
                     && referenced_from_other_partition_p 
(&node->symbol.ref_list,
                                                           set, vset), 1);
!       bp_pack_value (&bp, boundary_p && !DECL_EXTERNAL (node->symbol.decl), 
1);
!         /* in_other_partition.  */
      }
    streamer_write_bitpack (&bp);
    if (node->alias_of)
Index: lto-streamer-out.c
===================================================================
*** lto-streamer-out.c  (revision 187630)
--- lto-streamer-out.c  (working copy)
*************** lto_write_tree (struct output_block *ob,
*** 353,363 ****
  
          varpool_encoder = ob->decl_state->varpool_node_encoder;
          vnode = varpool_get_node (expr);
!         if (!vnode)
            initial = error_mark_node;
-         else if (!lto_varpool_encoder_encode_initializer_p (varpool_encoder,
-                                                             vnode))
-           initial = NULL;
        }
  
        stream_write_tree (ob, initial, ref_p);
--- 353,362 ----
  
          varpool_encoder = ob->decl_state->varpool_node_encoder;
          vnode = varpool_get_node (expr);
!         if (!vnode
!             || !lto_varpool_encoder_encode_initializer_p (varpool_encoder,
!                                                           vnode))
            initial = error_mark_node;
        }
  
        stream_write_tree (ob, initial, ref_p);
Index: ipa.c
===================================================================
*** ipa.c       (revision 187630)
--- ipa.c       (working copy)
*************** process_references (struct ipa_ref_list 
*** 100,106 ****
        {
          struct varpool_node *node = ipa_ref_varpool_node (ref);
  
!         if (node->analyzed)
            pointer_set_insert (reachable, node);
          enqueue_node ((symtab_node) node, first, reachable);
        }
--- 100,109 ----
        {
          struct varpool_node *node = ipa_ref_varpool_node (ref);
  
!         if (node->analyzed
!             && (!DECL_EXTERNAL (node->symbol.decl)
!                 || node->alias
!                 || before_inlining_p))
            pointer_set_insert (reachable, node);
          enqueue_node ((symtab_node) node, first, reachable);
        }
*************** has_addr_references_p (struct cgraph_nod
*** 187,192 ****
--- 190,201 ----
       reshape callgraph and preserve body when offline copy of function or
       inline clone is being removed.
  
+    - C++ virtual tables keyed to other unit are represented as DECL_EXTERNAL
+      variables with DECL_INITIAL set.  We finalize these and keep reachable
+      ones around for constant folding purposes.  After inlining we however
+      stop walking their references to let everything static referneced by them
+      to be removed when it is otherwise unreachable.
+ 
     We maintain queue of both reachable symbols (i.e. defined symbols that 
needs
     to stay) and symbols that are in boundary (i.e. external symbols referenced
     by reachable symbols or origins of clones).  The queue is represented
*************** symtab_remove_unreachable_nodes (bool be
*** 323,328 ****
--- 332,350 ----
                }
            }
        }
+       /* When we see constructor of external variable, keep referred nodes in 
the
+        boundary.  This will also hold initializers of the external vars NODE
+        reffers to.  */
+       if (symtab_variable_p (node)
+         && DECL_EXTERNAL (node->symbol.decl)
+         && !varpool (node)->alias
+         && in_boundary_p)
+         {
+         int i;
+         struct ipa_ref *ref;
+         for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, 
i, ref); i++)
+           enqueue_node (ref->referred, &first, reachable);
+         }
      }
  
    /* Remove unreachable functions.   */
*************** symtab_remove_unreachable_nodes (bool be
*** 347,353 ****
              changed = true;
            }
          if (!pointer_set_contains (body_needed_for_clonning, 
node->symbol.decl)
!             && !DECL_ARTIFICIAL (node->symbol.decl))
            cgraph_release_function_body (node);
          node->analyzed = false;
        }
--- 369,375 ----
              changed = true;
            }
          if (!pointer_set_contains (body_needed_for_clonning, 
node->symbol.decl)
!             && (node->local.finalized || !DECL_ARTIFICIAL 
(node->symbol.decl)))
            cgraph_release_function_body (node);
          node->analyzed = false;
        }
*************** varpool_externally_visible_p (struct var
*** 627,632 ****
--- 649,657 ----
    if (vnode->alias && DECL_EXTERNAL (vnode->symbol.decl))
      return true;
  
+   if (DECL_EXTERNAL (vnode->symbol.decl))
+     return true;
+ 
    if (!DECL_COMDAT (vnode->symbol.decl) && !TREE_PUBLIC (vnode->symbol.decl))
      return false;
  
Index: gimple-fold.c
===================================================================
*** gimple-fold.c       (revision 187630)
--- gimple-fold.c       (working copy)
*************** can_refer_decl_in_current_unit_p (tree d
*** 62,79 ****
    if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
      return true;
    /* External flag is set, so we deal with C++ reference
!      to static object from other file.  */
    if (DECL_EXTERNAL (decl) && TREE_STATIC (decl)
        && TREE_CODE (decl) == VAR_DECL)
!     {
!       /* Just be sure it is not big in frontend setting
!        flags incorrectly.  Those variables should never
!        be finalized.  */
!       gcc_checking_assert (!(vnode = varpool_get_node (decl))
!                          || vnode->alias
!                          || !vnode->finalized);
!       return false;
!     }
    /* When function is public, we always can introduce new reference.
       Exception are the COMDAT functions where introducing a direct
       reference imply need to include function body in the curren tunit.  */
--- 62,72 ----
    if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
      return true;
    /* External flag is set, so we deal with C++ reference
!      to static object from other file.
!      We also may see weakref that is always safe.  */
    if (DECL_EXTERNAL (decl) && TREE_STATIC (decl)
        && TREE_CODE (decl) == VAR_DECL)
!     return lookup_attribute ("weakref", DECL_ATTRIBUTES (decl)) != NULL;
    /* When function is public, we always can introduce new reference.
       Exception are the COMDAT functions where introducing a direct
       reference imply need to include function body in the curren tunit.  */
Index: lto/lto-partition.c
===================================================================
*** lto/lto-partition.c (revision 187630)
--- lto/lto-partition.c (working copy)
*************** add_references_to_partition (ltrans_part
*** 83,88 ****
--- 83,89 ----
        else
          if (symtab_variable_p (ref->referred)
            && (DECL_COMDAT (ipa_ref_varpool_node (ref)->symbol.decl)
+               || DECL_EXTERNAL (ipa_ref_varpool_node (ref)->symbol.decl)
                || (ref->use == IPA_REF_ALIAS
                    && lookup_attribute
                         ("weakref",
*************** partition_varpool_node_p (struct varpool
*** 287,292 ****
--- 288,294 ----
      return false;
    /* Constant pool and comdat are always only in partitions they are needed.  
*/
    if (DECL_IN_CONSTANT_POOL (vnode->symbol.decl)
+       || DECL_EXTERNAL (vnode->symbol.decl)
        || (DECL_COMDAT (vnode->symbol.decl)
          && !vnode->symbol.force_output
          && !symtab_used_from_object_file_p ((symtab_node) vnode)))
*************** lto_promote_cross_file_statics (void)
*** 843,848 ****
--- 845,851 ----
             be made global.  It is sensible to keep those ltrans local to
             allow better optimization.  */
          if (!DECL_IN_CONSTANT_POOL (vnode->symbol.decl)
+             && !DECL_EXTERNAL (vnode->symbol.decl)
              && !DECL_COMDAT (vnode->symbol.decl)
              && !vnode->symbol.externally_visible && vnode->analyzed
              && referenced_from_other_partition_p (&vnode->symbol.ref_list,
*************** lto_promote_cross_file_statics (void)
*** 893,900 ****
  
                  /* Constant pool references use internal labels and thus
                     cannot be made global.  It is sensible to keep those
!                    ltrans local to allow better optimization.  */
!                 if (DECL_IN_CONSTANT_POOL (v->symbol.decl))
                    {
                      if (!pointer_set_insert (inserted, vnode))
                        VEC_safe_push (varpool_node_ptr, heap,
--- 896,906 ----
  
                  /* Constant pool references use internal labels and thus
                     cannot be made global.  It is sensible to keep those
!                    ltrans local to allow better optimization.
!                    Similarly we ship external vars initializers into
!                    every ltrans unit possibly referring to it.  */
!                 if (DECL_IN_CONSTANT_POOL (v->symbol.decl)
!                     || DECL_EXTERNAL (v->symbol.decl))
                    {
                      if (!pointer_set_insert (inserted, vnode))
                        VEC_safe_push (varpool_node_ptr, heap,
Index: varpool.c
===================================================================
*** varpool.c   (revision 187630)
--- varpool.c   (working copy)
*************** void
*** 59,64 ****
--- 59,72 ----
  varpool_remove_node (struct varpool_node *node)
  {
    symtab_unregister_node ((symtab_node)node);
+   if (DECL_INITIAL (node->symbol.decl)
+       && !DECL_IN_CONSTANT_POOL (node->symbol.decl)
+       /* Keep vtables for BINFO folding.  */
+       && !DECL_VIRTUAL_P (node->symbol.decl)
+       /* dbxout output constant initializers for readonly vars.  */
+       && (!host_integerp (DECL_INITIAL (node->symbol.decl), 0)
+         || !TREE_READONLY (node->symbol.decl)))
+     DECL_INITIAL (node->symbol.decl) = error_mark_node;
    ggc_free (node);
  }
  
*************** varpool_node_for_asm (tree asmname)
*** 118,134 ****
  bool
  decide_is_variable_needed (struct varpool_node *node, tree decl)
  {
    /* If the user told us it is used, then it must be so.  */
    if (node->symbol.force_output)
      return true;
  
-   gcc_assert (!DECL_EXTERNAL (decl));
- 
    /* Externally visible variables must be output.  The exception is
       COMDAT variables that must be output only when they are needed.  */
    if (TREE_PUBLIC (decl)
!       && !DECL_COMDAT (decl)
!       && !DECL_EXTERNAL (decl))
      return true;
  
    return false;
--- 126,142 ----
  bool
  decide_is_variable_needed (struct varpool_node *node, tree decl)
  {
+   if (DECL_EXTERNAL (decl))
+     return false;
+ 
    /* If the user told us it is used, then it must be so.  */
    if (node->symbol.force_output)
      return true;
  
    /* Externally visible variables must be output.  The exception is
       COMDAT variables that must be output only when they are needed.  */
    if (TREE_PUBLIC (decl)
!       && !DECL_COMDAT (decl))
      return true;
  
    return false;
*************** varpool_remove_unreferenced_decls (void)
*** 348,354 ****
          && (!varpool_can_remove_if_no_refs (node)
              /* We just expanded all function bodies.  See if any of
                 them needed the variable.  */
!             || DECL_RTL_SET_P (node->symbol.decl)))
        {
          enqueue_node (node, &first);
            if (cgraph_dump_file)
--- 356,363 ----
          && (!varpool_can_remove_if_no_refs (node)
              /* We just expanded all function bodies.  See if any of
                 them needed the variable.  */
!             || (!DECL_EXTERNAL (node->symbol.decl)
!                 && DECL_RTL_SET_P (node->symbol.decl))))
        {
          enqueue_node (node, &first);
            if (cgraph_dump_file)
*************** varpool_remove_unreferenced_decls (void)
*** 372,377 ****
--- 381,388 ----
        }
        for (i = 0; ipa_ref_list_reference_iterate (&node->symbol.ref_list, i, 
ref); i++)
        if (symtab_variable_p (ref->referred)
+           && (!DECL_EXTERNAL (ref->referred->symbol.decl)
+               || varpool (ref->referred)->alias)
            && varpool (ref->referred)->analyzed)
          enqueue_node (varpool (ref->referred), &first);
      }

Reply via email to