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


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

Re: [PATCH][RFC] Re-organize how we stream trees in LTO


On Tue, 16 Oct 2012, Richard Biener wrote:

> On Tue, 16 Oct 2012, Richard Biener wrote:
> 
> > 
> > This patch shows work-in-progress (read: implementation uglyness
> > hopefully to vanish ...) regarding to moving LTO type merging
> > work from WPA to compile stage.

...

> > The patch below survives LTO bootstrap (ok, we are only
> > in stage2 yet ...) so it serves as suitable vehicle for
> > gathering statistics about tree SCC sizes.  It's probably
> > the state I'll build on re-organizing uniquify_nodes to
> > avoid the various DFS walks done there.

This is the first step in doing so - avoid the DFS walk for
hashing (that's the cheaper part actually ...).  This change
will result in fewer type merges (well, at least I expect so)
because we hash in all types in a _tree_ SCC (compared to
a type SCC as far as iterative_hash_gimple_type was walking
tree edges).  But eventually that's good - we want to merge
trees after all, not just types.

As before this patch passes LTO bootstrap (ok, fingers crossing,
it's at stage3).

Honza, Andi - I am interested in if this improves some of the
WPA bottlenecks - in particular DFS-walking at lto_write_tree
will make LTRANS unit generation slower (yeah, well ... I
suppose we don't really need to repeat creating the global
section for each and every LTRANS file, do we?  I suppose
the differences shouldn't be that big).

The approach should definitely fix some of the instability
of type-merging by making sure all refered to types are
merged first.

Thanks,
Richard.


Index: trunk/gcc/tree-streamer-out.c
===================================================================
*** trunk.orig/gcc/tree-streamer-out.c	2012-10-17 12:26:37.000000000 +0200
--- trunk/gcc/tree-streamer-out.c	2012-10-17 12:27:36.878787928 +0200
*************** pack_ts_base_value_fields (struct bitpac
*** 112,117 ****
--- 112,128 ----
  }
  
  
+ /* Pack all the non-pointer fields of the TS_INTEGER_CST structure of
+    expression EXPR into bitpack BP.  */
+ 
+ static void
+ pack_ts_int_cst_value_fields (struct bitpack_d *bp, tree expr)
+ {
+   bp_pack_var_len_unsigned (bp, TREE_INT_CST_LOW (expr));
+   bp_pack_var_len_int (bp, TREE_INT_CST_HIGH (expr));
+ }
+ 
+ 
  /* Pack all the non-pointer fields of the TS_REAL_CST structure of
     expression EXPR into bitpack BP.  */
  
*************** streamer_pack_tree_bitfields (struct out
*** 373,378 ****
--- 384,392 ----
       the types and sizes of each of the fields being packed.  */
    pack_ts_base_value_fields (bp, expr);
  
+   if (CODE_CONTAINS_STRUCT (code, TS_INT_CST))
+     pack_ts_int_cst_value_fields (bp, expr);
+ 
    if (CODE_CONTAINS_STRUCT (code, TS_REAL_CST))
      pack_ts_real_cst_value_fields (bp, expr);
  
*************** streamer_write_builtin (struct output_bl
*** 460,466 ****
     as references.  */
  
  void
! streamer_write_chain (struct output_block *ob, tree t, bool ref_p)
  {
    while (t)
      {
--- 474,480 ----
     as references.  */
  
  void
! streamer_write_chain (struct output_block *ob, tree from, tree t, bool ref_p)
  {
    while (t)
      {
*************** streamer_write_chain (struct output_bloc
*** 477,485 ****
  	 for streaming BLOCK_VARS, but other callers are safe.  */
        if (VAR_OR_FUNCTION_DECL_P (t)
  	  && DECL_EXTERNAL (t))
! 	stream_write_tree_shallow_non_ref (ob, t, ref_p);
        else
! 	stream_write_tree (ob, t, ref_p);
  
        TREE_CHAIN (t) = saved_chain;
        t = TREE_CHAIN (t);
--- 491,499 ----
  	 for streaming BLOCK_VARS, but other callers are safe.  */
        if (VAR_OR_FUNCTION_DECL_P (t)
  	  && DECL_EXTERNAL (t))
! 	stream_write_tree_edge_shallow_non_ref (ob, from, t, ref_p);
        else
! 	stream_write_tree_edge (ob, from, t, ref_p);
  
        TREE_CHAIN (t) = saved_chain;
        t = TREE_CHAIN (t);
*************** static void
*** 498,504 ****
  write_ts_common_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
    if (TREE_CODE (expr) != IDENTIFIER_NODE)
!     stream_write_tree (ob, TREE_TYPE (expr), ref_p);
  }
  
  
--- 512,518 ----
  write_ts_common_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
    if (TREE_CODE (expr) != IDENTIFIER_NODE)
!     stream_write_tree_edge (ob, expr, TREE_TYPE (expr), ref_p);
  }
  
  
*************** write_ts_vector_tree_pointers (struct ou
*** 513,519 ****
    /* Note that the number of elements for EXPR has already been emitted
       in EXPR's header (see streamer_write_tree_header).  */
    for (i = 0; i < VECTOR_CST_NELTS (expr); ++i)
!     stream_write_tree (ob, VECTOR_CST_ELT (expr, i), ref_p);
  }
  
  
--- 527,533 ----
    /* Note that the number of elements for EXPR has already been emitted
       in EXPR's header (see streamer_write_tree_header).  */
    for (i = 0; i < VECTOR_CST_NELTS (expr); ++i)
!     stream_write_tree_edge (ob, expr, VECTOR_CST_ELT (expr, i), ref_p);
  }
  
  
*************** write_ts_vector_tree_pointers (struct ou
*** 524,531 ****
  static void
  write_ts_complex_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   stream_write_tree (ob, TREE_REALPART (expr), ref_p);
!   stream_write_tree (ob, TREE_IMAGPART (expr), ref_p);
  }
  
  
--- 538,545 ----
  static void
  write_ts_complex_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, TREE_REALPART (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TREE_IMAGPART (expr), ref_p);
  }
  
  
*************** static void
*** 537,544 ****
  write_ts_decl_minimal_tree_pointers (struct output_block *ob, tree expr,
  				     bool ref_p)
  {
!   stream_write_tree (ob, DECL_NAME (expr), ref_p);
!   stream_write_tree (ob, DECL_CONTEXT (expr), ref_p);
  }
  
  
--- 551,558 ----
  write_ts_decl_minimal_tree_pointers (struct output_block *ob, tree expr,
  				     bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, DECL_NAME (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_CONTEXT (expr), ref_p);
  }
  
  
*************** static void
*** 550,562 ****
  write_ts_decl_common_tree_pointers (struct output_block *ob, tree expr,
  				    bool ref_p)
  {
!   stream_write_tree (ob, DECL_SIZE (expr), ref_p);
!   stream_write_tree (ob, DECL_SIZE_UNIT (expr), ref_p);
  
    /* Note, DECL_INITIAL is not handled here.  Since DECL_INITIAL needs
       special handling in LTO, it must be handled by streamer hooks.  */
  
!   stream_write_tree (ob, DECL_ATTRIBUTES (expr), ref_p);
  
    /* Do not stream DECL_ABSTRACT_ORIGIN.  We cannot handle debug information
       for early inlining so drop it on the floor instead of ICEing in
--- 564,576 ----
  write_ts_decl_common_tree_pointers (struct output_block *ob, tree expr,
  				    bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, DECL_SIZE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_SIZE_UNIT (expr), ref_p);
  
    /* Note, DECL_INITIAL is not handled here.  Since DECL_INITIAL needs
       special handling in LTO, it must be handled by streamer hooks.  */
  
!   stream_write_tree_edge (ob, expr, DECL_ATTRIBUTES (expr), ref_p);
  
    /* Do not stream DECL_ABSTRACT_ORIGIN.  We cannot handle debug information
       for early inlining so drop it on the floor instead of ICEing in
*************** write_ts_decl_common_tree_pointers (stru
*** 565,574 ****
    if ((TREE_CODE (expr) == VAR_DECL
         || TREE_CODE (expr) == PARM_DECL)
        && DECL_HAS_VALUE_EXPR_P (expr))
!     stream_write_tree (ob, DECL_VALUE_EXPR (expr), ref_p);
  
    if (TREE_CODE (expr) == VAR_DECL)
!     stream_write_tree (ob, DECL_DEBUG_EXPR (expr), ref_p);
  }
  
  
--- 579,588 ----
    if ((TREE_CODE (expr) == VAR_DECL
         || TREE_CODE (expr) == PARM_DECL)
        && DECL_HAS_VALUE_EXPR_P (expr))
!     stream_write_tree_edge (ob, expr, DECL_VALUE_EXPR (expr), ref_p);
  
    if (TREE_CODE (expr) == VAR_DECL)
!     stream_write_tree_edge (ob, expr, DECL_DEBUG_EXPR (expr), ref_p);
  }
  
  
*************** write_ts_decl_non_common_tree_pointers (
*** 582,593 ****
  {
    if (TREE_CODE (expr) == FUNCTION_DECL)
      {
!       streamer_write_chain (ob, DECL_ARGUMENTS (expr), ref_p);
!       stream_write_tree (ob, DECL_RESULT (expr), ref_p);
      }
    else if (TREE_CODE (expr) == TYPE_DECL)
!     stream_write_tree (ob, DECL_ORIGINAL_TYPE (expr), ref_p);
!   stream_write_tree (ob, DECL_VINDEX (expr), ref_p);
  }
  
  
--- 596,607 ----
  {
    if (TREE_CODE (expr) == FUNCTION_DECL)
      {
!       streamer_write_chain (ob, expr, DECL_ARGUMENTS (expr), ref_p);
!       stream_write_tree_edge (ob, expr, DECL_RESULT (expr), ref_p);
      }
    else if (TREE_CODE (expr) == TYPE_DECL)
!     stream_write_tree_edge (ob, expr, DECL_ORIGINAL_TYPE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_VINDEX (expr), ref_p);
  }
  
  
*************** write_ts_decl_with_vis_tree_pointers (st
*** 601,612 ****
  {
    /* Make sure we don't inadvertently set the assembler name.  */
    if (DECL_ASSEMBLER_NAME_SET_P (expr))
!     stream_write_tree (ob, DECL_ASSEMBLER_NAME (expr), ref_p);
    else
      stream_write_tree (ob, NULL_TREE, false);
  
!   stream_write_tree (ob, DECL_SECTION_NAME (expr), ref_p);
!   stream_write_tree (ob, DECL_COMDAT_GROUP (expr), ref_p);
  }
  
  
--- 615,626 ----
  {
    /* Make sure we don't inadvertently set the assembler name.  */
    if (DECL_ASSEMBLER_NAME_SET_P (expr))
!     stream_write_tree_edge (ob, expr, DECL_ASSEMBLER_NAME (expr), ref_p);
    else
      stream_write_tree (ob, NULL_TREE, false);
  
!   stream_write_tree_edge (ob, expr, DECL_SECTION_NAME (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_COMDAT_GROUP (expr), ref_p);
  }
  
  
*************** static void
*** 618,628 ****
  write_ts_field_decl_tree_pointers (struct output_block *ob, tree expr,
  				   bool ref_p)
  {
!   stream_write_tree (ob, DECL_FIELD_OFFSET (expr), ref_p);
!   stream_write_tree (ob, DECL_BIT_FIELD_TYPE (expr), ref_p);
!   stream_write_tree (ob, DECL_BIT_FIELD_REPRESENTATIVE (expr), ref_p);
!   stream_write_tree (ob, DECL_FIELD_BIT_OFFSET (expr), ref_p);
!   stream_write_tree (ob, DECL_FCONTEXT (expr), ref_p);
  }
  
  
--- 632,642 ----
  write_ts_field_decl_tree_pointers (struct output_block *ob, tree expr,
  				   bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, DECL_FIELD_OFFSET (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_BIT_FIELD_TYPE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_BIT_FIELD_REPRESENTATIVE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_FIELD_BIT_OFFSET (expr), ref_p);
!   stream_write_tree_edge (ob, expr, DECL_FCONTEXT (expr), ref_p);
  }
  
  
*************** write_ts_function_decl_tree_pointers (st
*** 636,644 ****
  {
    /* DECL_STRUCT_FUNCTION is handled by lto_output_function.  FIXME lto,
       maybe it should be handled here?  */
!   stream_write_tree (ob, DECL_FUNCTION_PERSONALITY (expr), ref_p);
!   stream_write_tree (ob, DECL_FUNCTION_SPECIFIC_TARGET (expr), ref_p);
!   stream_write_tree (ob, DECL_FUNCTION_SPECIFIC_OPTIMIZATION (expr), ref_p);
  }
  
  
--- 650,661 ----
  {
    /* DECL_STRUCT_FUNCTION is handled by lto_output_function.  FIXME lto,
       maybe it should be handled here?  */
!   stream_write_tree_edge (ob, expr,
! 			  DECL_FUNCTION_PERSONALITY (expr), ref_p);
!   stream_write_tree_edge (ob, expr,
! 			  DECL_FUNCTION_SPECIFIC_TARGET (expr), ref_p);
!   stream_write_tree_edge (ob, expr,
! 			  DECL_FUNCTION_SPECIFIC_OPTIMIZATION (expr), ref_p);
  }
  
  
*************** static void
*** 650,668 ****
  write_ts_type_common_tree_pointers (struct output_block *ob, tree expr,
  				    bool ref_p)
  {
!   stream_write_tree (ob, TYPE_SIZE (expr), ref_p);
!   stream_write_tree (ob, TYPE_SIZE_UNIT (expr), ref_p);
!   stream_write_tree (ob, TYPE_ATTRIBUTES (expr), ref_p);
!   stream_write_tree (ob, TYPE_NAME (expr), ref_p);
    /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
       reconstructed during fixup.  */
    /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
       during fixup.  */
!   stream_write_tree (ob, TYPE_MAIN_VARIANT (expr), ref_p);
!   stream_write_tree (ob, TYPE_CONTEXT (expr), ref_p);
    /* TYPE_CANONICAL is re-computed during type merging, so no need
       to stream it here.  */
!   stream_write_tree (ob, TYPE_STUB_DECL (expr), ref_p);
  }
  
  /* Write all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
--- 667,685 ----
  write_ts_type_common_tree_pointers (struct output_block *ob, tree expr,
  				    bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, TYPE_SIZE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TYPE_SIZE_UNIT (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TYPE_ATTRIBUTES (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TYPE_NAME (expr), ref_p);
    /* Do not stream TYPE_POINTER_TO or TYPE_REFERENCE_TO.  They will be
       reconstructed during fixup.  */
    /* Do not stream TYPE_NEXT_VARIANT, we reconstruct the variant lists
       during fixup.  */
!   stream_write_tree_edge (ob, expr, TYPE_MAIN_VARIANT (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TYPE_CONTEXT (expr), ref_p);
    /* TYPE_CANONICAL is re-computed during type merging, so no need
       to stream it here.  */
!   stream_write_tree_edge (ob, expr, TYPE_STUB_DECL (expr), ref_p);
  }
  
  /* Write all pointer fields in the TS_TYPE_NON_COMMON structure of EXPR
*************** write_ts_type_non_common_tree_pointers (
*** 674,693 ****
  					bool ref_p)
  {
    if (TREE_CODE (expr) == ENUMERAL_TYPE)
!     stream_write_tree (ob, TYPE_VALUES (expr), ref_p);
    else if (TREE_CODE (expr) == ARRAY_TYPE)
!     stream_write_tree (ob, TYPE_DOMAIN (expr), ref_p);
    else if (RECORD_OR_UNION_TYPE_P (expr))
!     streamer_write_chain (ob, TYPE_FIELDS (expr), ref_p);
    else if (TREE_CODE (expr) == FUNCTION_TYPE
  	   || TREE_CODE (expr) == METHOD_TYPE)
!     stream_write_tree (ob, TYPE_ARG_TYPES (expr), ref_p);
  
    if (!POINTER_TYPE_P (expr))
!     stream_write_tree (ob, TYPE_MINVAL (expr), ref_p);
!   stream_write_tree (ob, TYPE_MAXVAL (expr), ref_p);
    if (RECORD_OR_UNION_TYPE_P (expr))
!     stream_write_tree (ob, TYPE_BINFO (expr), ref_p);
  }
  
  
--- 691,710 ----
  					bool ref_p)
  {
    if (TREE_CODE (expr) == ENUMERAL_TYPE)
!     stream_write_tree_edge (ob, expr, TYPE_VALUES (expr), ref_p);
    else if (TREE_CODE (expr) == ARRAY_TYPE)
!     stream_write_tree_edge (ob, expr, TYPE_DOMAIN (expr), ref_p);
    else if (RECORD_OR_UNION_TYPE_P (expr))
!     streamer_write_chain (ob, expr, TYPE_FIELDS (expr), ref_p);
    else if (TREE_CODE (expr) == FUNCTION_TYPE
  	   || TREE_CODE (expr) == METHOD_TYPE)
!     stream_write_tree_edge (ob, expr, TYPE_ARG_TYPES (expr), ref_p);
  
    if (!POINTER_TYPE_P (expr))
!     stream_write_tree_edge (ob, expr, TYPE_MINVAL (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TYPE_MAXVAL (expr), ref_p);
    if (RECORD_OR_UNION_TYPE_P (expr))
!     stream_write_tree_edge (ob, expr, TYPE_BINFO (expr), ref_p);
  }
  
  
*************** write_ts_type_non_common_tree_pointers (
*** 698,706 ****
  static void
  write_ts_list_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   stream_write_tree (ob, TREE_PURPOSE (expr), ref_p);
!   stream_write_tree (ob, TREE_VALUE (expr), ref_p);
!   streamer_write_chain (ob, TREE_CHAIN (expr), ref_p);
  }
  
  
--- 715,723 ----
  static void
  write_ts_list_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   stream_write_tree_edge (ob, expr, TREE_PURPOSE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, TREE_VALUE (expr), ref_p);
!   streamer_write_chain (ob, expr, TREE_CHAIN (expr), ref_p);
  }
  
  
*************** write_ts_vec_tree_pointers (struct outpu
*** 716,722 ****
    /* Note that the number of slots for EXPR has already been emitted
       in EXPR's header (see streamer_write_tree_header).  */
    for (i = 0; i < TREE_VEC_LENGTH (expr); i++)
!     stream_write_tree (ob, TREE_VEC_ELT (expr, i), ref_p);
  }
  
  
--- 733,739 ----
    /* Note that the number of slots for EXPR has already been emitted
       in EXPR's header (see streamer_write_tree_header).  */
    for (i = 0; i < TREE_VEC_LENGTH (expr); i++)
!     stream_write_tree_edge (ob, expr, TREE_VEC_ELT (expr, i), ref_p);
  }
  
  
*************** write_ts_exp_tree_pointers (struct outpu
*** 730,737 ****
    int i;
  
    for (i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
!     stream_write_tree (ob, TREE_OPERAND (expr, i), ref_p);
!   stream_write_tree (ob, TREE_BLOCK (expr), ref_p);
  }
  
  
--- 747,754 ----
    int i;
  
    for (i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
!     stream_write_tree_edge (ob, expr, TREE_OPERAND (expr, i), ref_p);
!   stream_write_tree_edge (ob, expr, TREE_BLOCK (expr), ref_p);
  }
  
  
*************** write_ts_exp_tree_pointers (struct outpu
*** 742,750 ****
  static void
  write_ts_block_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   streamer_write_chain (ob, BLOCK_VARS (expr), ref_p);
  
!   stream_write_tree (ob, BLOCK_SUPERCONTEXT (expr), ref_p);
  
    /* Stream BLOCK_ABSTRACT_ORIGIN for the limited cases we can handle - those
       that represent inlined function scopes.
--- 759,767 ----
  static void
  write_ts_block_tree_pointers (struct output_block *ob, tree expr, bool ref_p)
  {
!   streamer_write_chain (ob, expr, BLOCK_VARS (expr), ref_p);
  
!   stream_write_tree_edge (ob, expr, BLOCK_SUPERCONTEXT (expr), ref_p);
  
    /* Stream BLOCK_ABSTRACT_ORIGIN for the limited cases we can handle - those
       that represent inlined function scopes.
*************** write_ts_block_tree_pointers (struct out
*** 752,758 ****
    if (inlined_function_outer_scope_p (expr))
      {
        tree ultimate_origin = block_ultimate_origin (expr);
!       stream_write_tree (ob, ultimate_origin, ref_p);
      }
    else
      stream_write_tree (ob, NULL_TREE, ref_p);
--- 769,775 ----
    if (inlined_function_outer_scope_p (expr))
      {
        tree ultimate_origin = block_ultimate_origin (expr);
!       stream_write_tree_edge (ob, expr, ultimate_origin, ref_p);
      }
    else
      stream_write_tree (ob, NULL_TREE, ref_p);
*************** write_ts_binfo_tree_pointers (struct out
*** 782,802 ****
       EXPR's header (see streamer_write_tree_header) because this length
       is needed to build the empty BINFO node on the reader side.  */
    FOR_EACH_VEC_ELT (tree, BINFO_BASE_BINFOS (expr), i, t)
!     stream_write_tree (ob, t, ref_p);
    stream_write_tree (ob, NULL_TREE, false);
  
!   stream_write_tree (ob, BINFO_OFFSET (expr), ref_p);
!   stream_write_tree (ob, BINFO_VTABLE (expr), ref_p);
!   stream_write_tree (ob, BINFO_VPTR_FIELD (expr), ref_p);
  
    /* The number of BINFO_BASE_ACCESSES has already been emitted in
       EXPR's bitfield section.  */
    FOR_EACH_VEC_ELT (tree, BINFO_BASE_ACCESSES (expr), i, t)
!     stream_write_tree (ob, t, ref_p);
  
!   stream_write_tree (ob, BINFO_INHERITANCE_CHAIN (expr), ref_p);
!   stream_write_tree (ob, BINFO_SUBVTT_INDEX (expr), ref_p);
!   stream_write_tree (ob, BINFO_VPTR_INDEX (expr), ref_p);
  }
  
  
--- 799,819 ----
       EXPR's header (see streamer_write_tree_header) because this length
       is needed to build the empty BINFO node on the reader side.  */
    FOR_EACH_VEC_ELT (tree, BINFO_BASE_BINFOS (expr), i, t)
!     stream_write_tree_edge (ob, expr, t, ref_p);
    stream_write_tree (ob, NULL_TREE, false);
  
!   stream_write_tree_edge (ob, expr, BINFO_OFFSET (expr), ref_p);
!   stream_write_tree_edge (ob, expr, BINFO_VTABLE (expr), ref_p);
!   stream_write_tree_edge (ob, expr, BINFO_VPTR_FIELD (expr), ref_p);
  
    /* The number of BINFO_BASE_ACCESSES has already been emitted in
       EXPR's bitfield section.  */
    FOR_EACH_VEC_ELT (tree, BINFO_BASE_ACCESSES (expr), i, t)
!     stream_write_tree_edge (ob, expr, t, ref_p);
  
!   stream_write_tree_edge (ob, expr, BINFO_INHERITANCE_CHAIN (expr), ref_p);
!   stream_write_tree_edge (ob, expr, BINFO_SUBVTT_INDEX (expr), ref_p);
!   stream_write_tree_edge (ob, expr, BINFO_VPTR_INDEX (expr), ref_p);
  }
  
  
*************** write_ts_constructor_tree_pointers (stru
*** 813,820 ****
  
    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, index, value)
      {
!       stream_write_tree (ob, index, ref_p);
!       stream_write_tree (ob, value, ref_p);
      }
  }
  
--- 830,837 ----
  
    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, index, value)
      {
!       stream_write_tree_edge (ob, expr, index, ref_p);
!       stream_write_tree_edge (ob, expr, value, ref_p);
      }
  }
  
Index: trunk/gcc/tree-streamer-in.c
===================================================================
*** trunk.orig/gcc/tree-streamer-in.c	2012-10-17 12:26:37.000000000 +0200
--- trunk/gcc/tree-streamer-in.c	2012-10-17 12:27:36.879787928 +0200
*************** unpack_ts_base_value_fields (struct bitp
*** 140,145 ****
--- 140,156 ----
  }
  
  
+ /* Unpack all the non-pointer fields of the TS_INT_CST structure of
+    expression EXPR from bitpack BP.  */
+ 
+ static void
+ unpack_ts_int_cst_value_fields (struct bitpack_d *bp, tree expr)
+ {
+   TREE_INT_CST_LOW (expr) = (unsigned) bp_unpack_var_len_unsigned (bp);
+   TREE_INT_CST_HIGH (expr) = (unsigned) bp_unpack_var_len_int (bp);
+ }
+ 
+ 
  /* Unpack all the non-pointer fields of the TS_REAL_CST structure of
     expression EXPR from bitpack BP.  */
  
*************** unpack_value_fields (struct data_in *dat
*** 416,421 ****
--- 427,435 ----
       the types and sizes of each of the fields being packed.  */
    unpack_ts_base_value_fields (bp, expr);
  
+   if (CODE_CONTAINS_STRUCT (code, TS_INT_CST))
+     unpack_ts_int_cst_value_fields (bp, expr);
+ 
    if (CODE_CONTAINS_STRUCT (code, TS_REAL_CST))
      unpack_ts_real_cst_value_fields (bp, expr);
  
Index: trunk/gcc/streamer-hooks.h
===================================================================
*** trunk.orig/gcc/streamer-hooks.h	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/streamer-hooks.h	2012-10-17 12:27:36.877787928 +0200
*************** struct streamer_hooks {
*** 44,50 ****
       tree itself.  The second boolean parameter specifies this for
       the tree itself, the first for all siblings that are streamed.
       The referencing mechanism is up to each streamer to implement.  */
!   void (*write_tree) (struct output_block *, tree, bool, bool);
  
    /* [REQ] Called by every tree streaming routine that needs to read
       a tree node.  It takes two arguments: an lto_input_block pointing
--- 44,50 ----
       tree itself.  The second boolean parameter specifies this for
       the tree itself, the first for all siblings that are streamed.
       The referencing mechanism is up to each streamer to implement.  */
!   void (*write_tree) (struct output_block *, tree, tree, bool, bool);
  
    /* [REQ] Called by every tree streaming routine that needs to read
       a tree node.  It takes two arguments: an lto_input_block pointing
*************** struct streamer_hooks {
*** 61,70 ****
  };
  
  #define stream_write_tree(OB, EXPR, REF_P) \
!     streamer_hooks.write_tree(OB, EXPR, REF_P, REF_P)
  
  #define stream_write_tree_shallow_non_ref(OB, EXPR, REF_P) \
!     streamer_hooks.write_tree(OB, EXPR, REF_P, false)
  
  #define stream_read_tree(IB, DATA_IN) \
      streamer_hooks.read_tree(IB, DATA_IN)
--- 61,76 ----
  };
  
  #define stream_write_tree(OB, EXPR, REF_P) \
!     streamer_hooks.write_tree(OB, NULL_TREE, EXPR, REF_P, REF_P)
  
  #define stream_write_tree_shallow_non_ref(OB, EXPR, REF_P) \
!     streamer_hooks.write_tree(OB, NULL_TREE, EXPR, REF_P, false)
! 
! #define stream_write_tree_edge(OB, FROM, TO, REF_P) \
!     streamer_hooks.write_tree(OB, FROM, TO, REF_P, REF_P)
! 
! #define stream_write_tree_edge_shallow_non_ref(OB, FROM, TO, REF_P) \
!     streamer_hooks.write_tree(OB, FROM, TO, REF_P, false)
  
  #define stream_read_tree(IB, DATA_IN) \
      streamer_hooks.read_tree(IB, DATA_IN)
Index: trunk/gcc/lto-streamer-out.c
===================================================================
*** trunk.orig/gcc/lto-streamer-out.c	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/lto-streamer-out.c	2012-10-17 12:27:36.878787928 +0200
*************** lto_is_streamable (tree expr)
*** 298,318 ****
     where EXPR is stored.  */
  
  static void
! lto_write_tree (struct output_block *ob, tree expr, bool ref_p)
  {
-   struct bitpack_d bp;
- 
-   if (!lto_is_streamable (expr))
-     internal_error ("tree code %qs is not supported in LTO streams",
- 	            tree_code_name[TREE_CODE (expr)]);
- 
-   /* Write the header, containing everything needed to materialize
-      EXPR on the reading side.  */
-   streamer_write_tree_header (ob, expr);
- 
    /* Pack all the non-pointer fields in EXPR into a bitpack and write
       the resulting bitpack.  */
!   bp = bitpack_create (ob->main_stream);
    streamer_pack_tree_bitfields (ob, &bp, expr);
    streamer_write_bitpack (&bp);
  
--- 298,308 ----
     where EXPR is stored.  */
  
  static void
! lto_write_tree_1 (struct output_block *ob, tree expr, bool ref_p)
  {
    /* Pack all the non-pointer fields in EXPR into a bitpack and write
       the resulting bitpack.  */
!   bitpack_d bp = bitpack_create (ob->main_stream);
    streamer_pack_tree_bitfields (ob, &bp, expr);
    streamer_write_bitpack (&bp);
  
*************** lto_write_tree (struct output_block *ob,
*** 343,361 ****
  
        stream_write_tree (ob, initial, ref_p);
      }
  
    /* Mark the end of EXPR.  */
    streamer_write_zero (ob);
  }
  
  
  /* Emit the physical representation of tree node EXPR to output block
     OB.  If THIS_REF_P is true, the leaves of EXPR are emitted as references
     via lto_output_tree_ref.  REF_P is used for streaming siblings of EXPR.  */
  
! void
! lto_output_tree (struct output_block *ob, tree expr,
! 		 bool ref_p, bool this_ref_p)
  {
    unsigned ix;
    bool existed_p;
--- 333,374 ----
  
        stream_write_tree (ob, initial, ref_p);
      }
+ }
+ 
+ /* Write a physical representation of tree node EXPR to output block
+    OB.  If REF_P is true, the leaves of EXPR are emitted as references
+    via lto_output_tree_ref.  IX is the index into the streamer cache
+    where EXPR is stored.  */
+ 
+ static void
+ lto_write_tree (struct output_block *ob, tree expr, bool ref_p)
+ {
+   if (!lto_is_streamable (expr))
+     internal_error ("tree code %qs is not supported in LTO streams",
+ 	            tree_code_name[TREE_CODE (expr)]);
+ 
+   /* Write the header, containing everything needed to materialize
+      EXPR on the reading side.  */
+   streamer_write_tree_header (ob, expr);
+ 
+   lto_write_tree_1 (ob, expr, ref_p);
  
    /* Mark the end of EXPR.  */
    streamer_write_zero (ob);
  }
  
+ static void
+ null_output_location (struct output_block *, struct bitpack_d *, location_t)
+ {
+ }
  
  /* Emit the physical representation of tree node EXPR to output block
     OB.  If THIS_REF_P is true, the leaves of EXPR are emitted as references
     via lto_output_tree_ref.  REF_P is used for streaming siblings of EXPR.  */
  
! static void
! lto_output_tree_1 (struct output_block *ob, tree, tree expr,
! 		   bool ref_p, bool this_ref_p)
  {
    unsigned ix;
    bool existed_p;
*************** lto_output_tree (struct output_block *ob
*** 408,413 ****
--- 421,633 ----
      }
  }
  
+ struct sccs
+ {
+   unsigned int dfsnum;
+   unsigned int low;
+   bool on_sccstack;
+ };
+ 
+ static unsigned int next_dfs_num;
+ static VEC (tree, heap) *sccstack;
+ static struct pointer_map_t *sccstate;
+ static struct obstack sccstate_obstack;
+ 
+ static void
+ DFS_write_tree (struct output_block *ob, tree from, tree expr,
+ 		bool ref_p, bool this_ref_p)
+ {
+   sccs *state = NULL;
+   unsigned ix;
+   sccs **slot;
+ 
+   /* Handle special cases.  */
+   if (expr == NULL_TREE)
+     return;
+ 
+   /* Do not DFS walk into indexable trees.  */
+   if (this_ref_p && tree_is_indexable (expr))
+     return;
+ 
+   /* Check if we already streamed EXPR.  */
+   if (streamer_tree_cache_lookup (ob->writer_cache, expr, &ix))
+     return;
+ 
+   if (from)
+     state = (sccs *)*pointer_map_contains (sccstate, from);
+ 
+   slot = (sccs **)pointer_map_insert (sccstate, expr);
+   sccs *cstate = *slot;
+   if (!cstate)
+     {
+       /* Not yet visited.  DFS recurse and push it onto the stack.  */
+       *slot = cstate = XOBNEW (&sccstate_obstack, struct sccs);
+       VEC_safe_push (tree, heap, sccstack, expr);
+       cstate->dfsnum = next_dfs_num++;
+       cstate->low = cstate->dfsnum;
+       cstate->on_sccstack = true;
+ 
+       if (streamer_handle_as_builtin_p (expr))
+ 	;
+       else if (TREE_CODE (expr) == INTEGER_CST)
+ 	stream_write_tree_edge (ob, expr, TREE_TYPE (expr), ref_p);
+       else
+ 	streamer_write_tree_body (ob, expr, ref_p);
+ 
+       /* See if we found an SCC.  */
+       if (cstate->low == cstate->dfsnum)
+ 	{
+ 	  unsigned first, size;
+ 	  tree x;
+ 
+ 	  /* Pop the SCC and compute its size.  */
+ 	  first = VEC_length (tree, sccstack);
+ 	  do
+ 	    {
+ 	      x = VEC_index (tree, sccstack, --first);
+ 	      sccs *s = (sccs *)*pointer_map_contains (sccstate, x);
+ 	      s->on_sccstack = false;
+ 	    }
+ 	  while (x != expr);
+ 	  size = VEC_length (tree, sccstack) - first;
+ 
+ 	  streamer_hooks.output_location = lto_output_location;
+ 	  streamer_hooks.write_tree = lto_output_tree_1;
+ 
+ 
+ 	  /* Write LTO_tree_scc.  */
+ 	  streamer_write_record_start (ob, LTO_tree_scc);
+ 	  streamer_write_uhwi (ob, size);
+ 
+ 	  /* Write size-1 SCCs without wrapping them inside SCC bundles.
+ 	     All INTEGER_CSTs need to be handled this way as we need
+ 	     their type to materialize them.  Also builtins are handled
+ 	     this way.
+ 	     ???  We still wrap these in LTO_tree_scc so at the
+ 	     input side we can properly identify the tree we want
+ 	     to ultimatively return.  */
+ 	  if (size == 1)
+ 	    lto_output_tree_1 (ob, NULL_TREE, expr, ref_p, this_ref_p);
+ 	  else
+ 	    {
+ 	      /* Write all headers and populate the streamer cache.  */
+ 	      for (unsigned i = 0; i < size; ++i)
+ 		{
+ 		  x = VEC_index (tree, sccstack, first + i);
+ 		  bool exists_p = streamer_tree_cache_insert (ob->writer_cache, x, &ix);
+ 		  gcc_assert (!exists_p);
+ 
+ 		  if (!lto_is_streamable (x))
+ 		    internal_error ("tree code %qs is not supported in LTO streams",
+ 				    tree_code_name[TREE_CODE (x)]);
+ 
+ 		  if (streamer_handle_as_builtin_p (x))
+ 		    gcc_unreachable ();
+ 
+ 		  /* Write the header, containing everything needed to materialize
+ 		     EXPR on the reading side.  */
+ 		  streamer_write_tree_header (ob, x);
+ 		}
+ 
+ 	      /* Write the bitpacks and tree references.  */
+ 	      for (unsigned i = 0; i < size; ++i)
+ 		{
+ 		  x = VEC_index (tree, sccstack, first + i);
+ 		  lto_write_tree_1 (ob, x, ref_p);
+ 
+ 		  /* Mark the end of X.  */
+ 		  streamer_write_zero (ob);
+ 		}
+ 	    }
+ 
+ 	  streamer_hooks.output_location = null_output_location;
+ 	  streamer_hooks.write_tree = DFS_write_tree;
+ 
+ 	  /* Finally truncate the vector.  */
+ 	  VEC_truncate (tree, sccstack, first);
+ 	}
+ 
+       if (state)
+ 	state->low = MIN (state->low, cstate->low);
+       if (!cstate->on_sccstack)
+ 	return;
+     }
+   gcc_assert (from && state);
+   if (cstate->dfsnum < state->dfsnum
+       && cstate->on_sccstack)
+     state->low = MIN (cstate->dfsnum, state->low);
+ }
+ 
+ /* Emit the physical representation of tree node EXPR to output block
+    OB.  If THIS_REF_P is true, the leaves of EXPR are emitted as references
+    via lto_output_tree_ref.  REF_P is used for streaming siblings of EXPR.  */
+ 
+ void
+ lto_output_tree (struct output_block *ob, tree, tree expr,
+ 		 bool ref_p, bool this_ref_p)
+ {
+   unsigned ix;
+   bool existed_p;
+ 
+   if (expr == NULL_TREE)
+     {
+       streamer_write_record_start (ob, LTO_null);
+       return;
+     }
+ 
+   if (this_ref_p && tree_is_indexable (expr))
+     {
+       lto_output_tree_ref (ob, expr);
+       return;
+     }
+ 
+   /* INTEGER_CST nodes are special because they need their original type
+      to be materialized by the reader (to implement TYPE_CACHED_VALUES).  */
+   if (TREE_CODE (expr) == INTEGER_CST)
+     {
+       streamer_write_integer_cst (ob, expr, ref_p);
+       return;
+     }
+ 
+   existed_p = streamer_tree_cache_lookup (ob->writer_cache, expr, &ix);
+   if (existed_p)
+     {
+       /* If a node has already been streamed out, make sure that
+ 	 we don't write it more than once.  Otherwise, the reader
+ 	 will instantiate two different nodes for the same object.  */
+       streamer_write_record_start (ob, LTO_tree_pickle_reference);
+       streamer_write_uhwi (ob, ix);
+       streamer_write_enum (ob->main_stream, LTO_tags, LTO_NUM_TAGS,
+ 			   lto_tree_code_to_tag (TREE_CODE (expr)));
+     }
+   else
+     {
+       /* This is the first time we see EXPR, write all reachable
+ 	 trees to OB.  */
+ 
+       streamer_hooks.output_location = null_output_location;
+       streamer_hooks.write_tree = DFS_write_tree;
+       /* Start the DFS walk.  */
+       /* Save ob state ... */
+       /* let's see ... */
+       sccstate = pointer_map_create ();
+       gcc_obstack_init (&sccstate_obstack);
+       sccstack = NULL;
+       next_dfs_num = 1;
+       DFS_write_tree (ob, NULL_TREE, expr, ref_p, this_ref_p);
+       VEC_free (tree, heap, sccstack);
+       pointer_map_destroy (sccstate);
+       obstack_free (&sccstate_obstack, NULL);
+       streamer_hooks.output_location = lto_output_location;
+       streamer_hooks.write_tree = lto_output_tree;
+ 
+       /* Finally append a reference to the tree we were writing.
+ 	 ???  If expr ended up as a singleton we could have
+ 	 inlined it here and avoid outputting a reference.  */
+       lto_output_tree (ob, NULL_TREE, expr, ref_p, this_ref_p);
+     }
+ }
+ 
  
  /* Output to OB a list of try/catch handlers starting with FIRST.  */
  
Index: trunk/gcc/lto-streamer.h
===================================================================
*** trunk.orig/gcc/lto-streamer.h	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/lto-streamer.h	2012-10-17 12:27:36.878787928 +0200
*************** enum LTO_tags
*** 193,201 ****
    /* EH try/catch node.  */
    LTO_eh_catch,
  
!   /* Special for global streamer. Reference to previously-streamed node.  */
    LTO_tree_pickle_reference,
  
    /* References to indexable tree nodes.  These objects are stored in
       tables that are written separately from the function bodies that
       reference them.  This way they can be instantiated even when the
--- 193,204 ----
    /* EH try/catch node.  */
    LTO_eh_catch,
  
!   /* Special for global streamer.  Reference to previously-streamed node.  */
    LTO_tree_pickle_reference,
  
+   /* Special for global streamer.  A blob of unnamed tree nodes.  */
+   LTO_tree_scc,
+ 
    /* References to indexable tree nodes.  These objects are stored in
       tables that are written separately from the function bodies that
       reference them.  This way they can be instantiated even when the
*************** tree lto_input_tree_ref (struct lto_inpu
*** 814,819 ****
--- 817,824 ----
  			 struct function *, enum LTO_tags);
  void lto_tag_check_set (enum LTO_tags, int, ...);
  void lto_init_eh (void);
+ void lto_input_scc (struct lto_input_block *, struct data_in *);
+ tree lto_input_tree_1 (struct lto_input_block *, struct data_in *, enum LTO_tags);
  tree lto_input_tree (struct lto_input_block *, struct data_in *);
  
  
*************** tree lto_input_tree (struct lto_input_bl
*** 821,827 ****
  extern void lto_register_decl_definition (tree, struct lto_file_decl_data *);
  extern struct output_block *create_output_block (enum lto_section_type);
  extern void destroy_output_block (struct output_block *);
! extern void lto_output_tree (struct output_block *, tree, bool, bool);
  extern void lto_output_toplevel_asms (void);
  extern void produce_asm (struct output_block *ob, tree fn);
  void lto_output_decl_state_streams (struct output_block *,
--- 826,832 ----
  extern void lto_register_decl_definition (tree, struct lto_file_decl_data *);
  extern struct output_block *create_output_block (enum lto_section_type);
  extern void destroy_output_block (struct output_block *);
! extern void lto_output_tree (struct output_block *, tree, tree, bool, bool);
  extern void lto_output_toplevel_asms (void);
  extern void produce_asm (struct output_block *ob, tree fn);
  void lto_output_decl_state_streams (struct output_block *,
Index: trunk/gcc/lto-streamer-in.c
===================================================================
*** trunk.orig/gcc/lto-streamer-in.c	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/lto-streamer-in.c	2012-10-17 12:46:17.235721022 +0200
*************** lto_input_function_body (struct lto_file
*** 1007,1012 ****
--- 1007,1043 ----
  }
  
  
+ /* Read the physical representation of a tree node EXPR from
+    input block IB using the per-file context in DATA_IN.  */
+ 
+ static void
+ lto_read_tree_1 (struct lto_input_block *ib, struct data_in *data_in, tree expr)
+ {
+   /* Read all the bitfield values in EXPR.  Note that for LTO, we
+      only write language-independent bitfields, so no more unpacking is
+      needed.  */
+   streamer_read_tree_bitfields (ib, data_in, expr);
+ 
+   /* Read all the pointer fields in EXPR.  */
+   streamer_read_tree_body (ib, data_in, expr);
+ 
+   /* Read any LTO-specific data not read by the tree streamer.  */
+   if (DECL_P (expr)
+       && TREE_CODE (expr) != FUNCTION_DECL
+       && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
+     DECL_INITIAL (expr) = stream_read_tree (ib, data_in);
+ 
+   /* We should never try to instantiate an MD or NORMAL builtin here.  */
+   if (TREE_CODE (expr) == FUNCTION_DECL)
+     gcc_assert (!streamer_handle_as_builtin_p (expr));
+ 
+ #ifdef LTO_STREAMER_DEBUG
+   /* Remove the mapping to RESULT's original address set by
+      streamer_alloc_tree.  */
+   lto_orig_address_remove (expr);
+ #endif
+ }
+ 
  /* Read the physical representation of a tree node with tag TAG from
     input block IB using the per-file context in DATA_IN.  */
  
*************** lto_read_tree (struct lto_input_block *i
*** 1022,1054 ****
       structure can be resolved in subsequent calls to stream_read_tree.  */
    streamer_tree_cache_append (data_in->reader_cache, result);
  
!   /* Read all the bitfield values in RESULT.  Note that for LTO, we
!      only write language-independent bitfields, so no more unpacking is
!      needed.  */
!   streamer_read_tree_bitfields (ib, data_in, result);
  
!   /* Read all the pointer fields in RESULT.  */
!   streamer_read_tree_body (ib, data_in, result);
  
!   /* Read any LTO-specific data not read by the tree streamer.  */
!   if (DECL_P (result)
!       && TREE_CODE (result) != FUNCTION_DECL
!       && TREE_CODE (result) != TRANSLATION_UNIT_DECL)
!     DECL_INITIAL (result) = stream_read_tree (ib, data_in);
  
-   /* We should never try to instantiate an MD or NORMAL builtin here.  */
-   if (TREE_CODE (result) == FUNCTION_DECL)
-     gcc_assert (!streamer_handle_as_builtin_p (result));
  
!   /* end_marker = */ streamer_read_uchar (ib);
  
! #ifdef LTO_STREAMER_DEBUG
!   /* Remove the mapping to RESULT's original address set by
!      streamer_alloc_tree.  */
!   lto_orig_address_remove (result);
! #endif
  
!   return result;
  }
  
  
--- 1053,1106 ----
       structure can be resolved in subsequent calls to stream_read_tree.  */
    streamer_tree_cache_append (data_in->reader_cache, result);
  
!   lto_read_tree_1 (ib, data_in, result);
  
!   /* end_marker = */ streamer_read_uchar (ib);
  
!   return result;
! }
  
  
! /* Populate the reader cache with trees materialized from the SCC
!    following in the IB, DATA_IN stream.  */
  
! void
! lto_input_scc (struct lto_input_block *ib, struct data_in *data_in)
! {
!   /* A blob of unnamed tree nodes, fill the cache from it and
!      recurse.  */
!   unsigned HOST_WIDE_INT size = streamer_read_uhwi (ib);
  
!   if (size == 1)
!     lto_input_tree (ib, data_in);
!   else
!     {
!       unsigned int first = VEC_length (tree, data_in->reader_cache->nodes);
!       tree result;
! 
!       /* Materialize size trees by reading their headers.  */
!       for (unsigned i = 0; i < size; ++i)
! 	{
! 	  enum LTO_tags tag = streamer_read_record_start (ib);
! 	  if (tag == LTO_null
! 	      || (tag >= LTO_field_decl_ref && tag <= LTO_global_decl_ref)
! 	      || tag == LTO_tree_pickle_reference
! 	      || tag == LTO_builtin_decl
! 	      || tag == LTO_tree_scc)
! 	    gcc_unreachable ();
! 
! 	  result = streamer_alloc_tree (ib, data_in, tag);
! 	  streamer_tree_cache_append (data_in->reader_cache, result);
! 	}
! 
!       /* Read the tree bitpacks and references.  */
!       for (unsigned i = 0; i < size; ++i)
! 	{
! 	  result = streamer_tree_cache_get (data_in->reader_cache, first + i);
! 	  lto_read_tree_1 (ib, data_in, result);
! 	  /* end_marker = */ streamer_read_uchar (ib);
! 	}
!     }
  }
  
  
*************** lto_read_tree (struct lto_input_block *i
*** 1057,1068 ****
     to previously read nodes.  */
  
  tree
! lto_input_tree (struct lto_input_block *ib, struct data_in *data_in)
  {
-   enum LTO_tags tag;
    tree result;
  
-   tag = streamer_read_record_start (ib);
    gcc_assert ((unsigned) tag < (unsigned) LTO_NUM_TAGS);
  
    if (tag == LTO_null)
--- 1109,1119 ----
     to previously read nodes.  */
  
  tree
! lto_input_tree_1 (struct lto_input_block *ib, struct data_in *data_in,
! 		  enum LTO_tags tag)
  {
    tree result;
  
    gcc_assert ((unsigned) tag < (unsigned) LTO_NUM_TAGS);
  
    if (tag == LTO_null)
*************** lto_input_tree (struct lto_input_block *
*** 1092,1097 ****
--- 1143,1156 ----
  	 words.  */
        result = streamer_read_integer_cst (ib, data_in);
      }
+   else if (tag == LTO_tree_scc)
+     {
+       /* Input and skip the SCC.  */
+       lto_input_scc (ib, data_in);
+ 
+       /* Recurse.  */
+       return lto_input_tree (ib, data_in);
+     }
    else
      {
        /* Otherwise, materialize a new node from IB.  */
*************** lto_input_tree (struct lto_input_block *
*** 1101,1106 ****
--- 1160,1171 ----
    return result;
  }
  
+ tree
+ lto_input_tree (struct lto_input_block *ib, struct data_in *data_in)
+ {
+   return lto_input_tree_1 (ib, data_in, streamer_read_record_start (ib));
+ }
+ 
  
  /* Input toplevel asms.  */
  
Index: trunk/gcc/lto/lto.c
===================================================================
*** trunk.orig/gcc/lto/lto.c	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/lto/lto.c	2012-10-17 13:21:44.282593919 +0200
*************** along with GCC; see the file COPYING3.
*** 42,47 ****
--- 42,48 ----
  #include "lto.h"
  #include "lto-tree.h"
  #include "lto-streamer.h"
+ #include "data-streamer.h"
  #include "tree-streamer.h"
  #include "splay-tree.h"
  #include "lto-partition.h"
*************** struct sccs
*** 364,370 ****
    } u;
  };
  
- static unsigned int next_dfs_num;
  static unsigned int gtc_next_dfs_num;
  
  /* GIMPLE type merging cache.  A direct-mapped cache based on TYPE_UID.  */
--- 365,370 ----
*************** gimple_types_compatible_p (tree t1, tree
*** 977,1034 ****
    return res;
  }
  
- static hashval_t
- iterative_hash_gimple_type (tree, hashval_t, VEC(tree, heap) **,
- 			    struct pointer_map_t *, struct obstack *);
- 
- /* DFS visit the edge from the callers type with state *STATE to T.
-    Update the callers type hash V with the hash for T if it is not part
-    of the SCC containing the callers type and return it.
-    SCCSTACK, SCCSTATE and SCCSTATE_OBSTACK are state for the DFS walk done.  */
- 
- static hashval_t
- visit (tree t, struct sccs *state, hashval_t v,
-        VEC (tree, heap) **sccstack,
-        struct pointer_map_t *sccstate,
-        struct obstack *sccstate_obstack)
- {
-   struct sccs *cstate = NULL;
-   struct tree_int_map m;
-   void **slot;
- 
-   /* If there is a hash value recorded for this type then it can't
-      possibly be part of our parent SCC.  Simply mix in its hash.  */
-   m.base.from = t;
-   if ((slot = htab_find_slot (type_hash_cache, &m, NO_INSERT))
-       && *slot)
-     return iterative_hash_hashval_t (((struct tree_int_map *) *slot)->to, v);
- 
-   if ((slot = pointer_map_contains (sccstate, t)) != NULL)
-     cstate = (struct sccs *)*slot;
-   if (!cstate)
-     {
-       hashval_t tem;
-       /* Not yet visited.  DFS recurse.  */
-       tem = iterative_hash_gimple_type (t, v,
- 					sccstack, sccstate, sccstate_obstack);
-       if (!cstate)
- 	cstate = (struct sccs *)* pointer_map_contains (sccstate, t);
-       state->low = MIN (state->low, cstate->low);
-       /* If the type is no longer on the SCC stack and thus is not part
-          of the parents SCC mix in its hash value.  Otherwise we will
- 	 ignore the type for hashing purposes and return the unaltered
- 	 hash value.  */
-       if (!cstate->on_sccstack)
- 	return tem;
-     }
-   if (cstate->dfsnum < state->dfsnum
-       && cstate->on_sccstack)
-     state->low = MIN (cstate->dfsnum, state->low);
- 
-   /* We are part of our parents SCC, skip this type during hashing
-      and return the unaltered hash value.  */
-   return v;
- }
  
  /* Hash NAME with the previous hash value V and return it.  */
  
--- 977,982 ----
*************** type_hash_pair_compare (const void *p1_,
*** 1067,1120 ****
    return 0;
  }
  
! /* Returning a hash value for gimple type TYPE combined with VAL.
!    SCCSTACK, SCCSTATE and SCCSTATE_OBSTACK are state for the DFS walk done.
  
!    To hash a type we end up hashing in types that are reachable.
!    Through pointers we can end up with cycles which messes up the
!    required property that we need to compute the same hash value
!    for structurally equivalent types.  To avoid this we have to
!    hash all types in a cycle (the SCC) in a commutative way.  The
!    easiest way is to not mix in the hashes of the SCC members at
!    all.  To make this work we have to delay setting the hash
!    values of the SCC until it is complete.  */
  
  static hashval_t
! iterative_hash_gimple_type (tree type, hashval_t val,
! 			    VEC(tree, heap) **sccstack,
! 			    struct pointer_map_t *sccstate,
! 			    struct obstack *sccstate_obstack)
  {
!   hashval_t v;
    void **slot;
!   struct sccs *state;
  
!   /* Not visited during this DFS walk.  */
!   gcc_checking_assert (!pointer_map_contains (sccstate, type));
!   state = XOBNEW (sccstate_obstack, struct sccs);
!   *pointer_map_insert (sccstate, type) = state;
  
!   VEC_safe_push (tree, heap, *sccstack, type);
!   state->dfsnum = next_dfs_num++;
!   state->low = state->dfsnum;
!   state->on_sccstack = true;
  
    /* Combine a few common features of types so that types are grouped into
       smaller sets; when searching for existing matching types to merge,
       only existing types having the same features as the new type will be
       checked.  */
!   v = iterative_hash_name (TYPE_NAME (type), 0);
    if (TYPE_NAME (type)
        && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
        && DECL_CONTEXT (TYPE_NAME (type))
        && TYPE_P (DECL_CONTEXT (TYPE_NAME (type))))
!     v = visit (DECL_CONTEXT (TYPE_NAME (type)), state, v,
! 	       sccstack, sccstate, sccstate_obstack);
  
    /* Factor in the variant structure.  */
    if (TYPE_MAIN_VARIANT (type) != type)
!     v = visit (TYPE_MAIN_VARIANT (type), state, v,
! 	       sccstack, sccstate, sccstate_obstack);
  
    v = iterative_hash_hashval_t (TREE_CODE (type), v);
    v = iterative_hash_hashval_t (TYPE_QUALS (type), v);
--- 1015,1088 ----
    return 0;
  }
  
! /* Returns a hash value for P (assumed to be a type).  The hash value
!    is computed using some distinguishing features of the type.  Note
!    that we cannot use pointer hashing here as we may be dealing with
!    two distinct instances of the same type.
  
!    This function should produce the same hash value for two compatible
!    types according to gimple_types_compatible_p.  */
  
  static hashval_t
! gimple_type_hash (const void *p)
  {
!   const_tree t = (const_tree) p;
    void **slot;
!   struct tree_int_map m;
  
!   m.base.from = CONST_CAST_TREE (t);
!   slot = htab_find_slot (type_hash_cache, &m, NO_INSERT);
!   /* ???  For builtin types that do not end up being streamed we can
!      end up here with !slot.  Simply hash those by pointer.
!      Of course it sucks that we cannot assert we have computed all
!      required hashes here.
!      ???  Do that at cache-preloading time.  */
!   if (!slot)
!     return htab_hash_pointer (t);
!   return ((struct tree_int_map *) *slot)->to;
! }
  
! /* Mix in the hash value of the tree T if it is not part of the
!    trees in the current SCC.  */
! 
! static hashval_t
! iterative_hash_gimple_type_1 (tree t, hashval_t v)
! {
!   /* If this tree is not part of the current SCC, mix in its hash.
!      It has been computed when visiting the sibling SCCs.  */
!   if (!TREE_VISITED (t))
!     return iterative_hash_hashval_t (gimple_type_hash (t), v);
! 
!   /* We are part of our parents SCC, skip this type during hashing
!      and return the unaltered hash value.  */
!   return v;
! }
! 
! /* Returning a hash value for the gimple type TYPE contained in the
!    tree SCC marked with TREE_VISITED combined with VAL.
! 
!    To obtain stable hashes for the types inside a tree SCC this
!    function does not mix in hash values from trees refered to
!    by TYPE that are part of the SCC.  This mixing process is done
!    as followup.  */
  
+ static hashval_t
+ iterative_hash_gimple_type (tree type, hashval_t v)
+ {
    /* Combine a few common features of types so that types are grouped into
       smaller sets; when searching for existing matching types to merge,
       only existing types having the same features as the new type will be
       checked.  */
!   v = iterative_hash_name (TYPE_NAME (type), v);
    if (TYPE_NAME (type)
        && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
        && DECL_CONTEXT (TYPE_NAME (type))
        && TYPE_P (DECL_CONTEXT (TYPE_NAME (type))))
!     v = iterative_hash_gimple_type_1 (DECL_CONTEXT (TYPE_NAME (type)), v);
  
    /* Factor in the variant structure.  */
    if (TYPE_MAIN_VARIANT (type) != type)
!     v = iterative_hash_gimple_type_1 (TYPE_MAIN_VARIANT (type), v);
  
    v = iterative_hash_hashval_t (TREE_CODE (type), v);
    v = iterative_hash_hashval_t (TYPE_QUALS (type), v);
*************** iterative_hash_gimple_type (tree type, h
*** 1136,1143 ****
    /* For pointer and reference types, fold in information about the type
       pointed to.  */
    if (POINTER_TYPE_P (type))
!     v = visit (TREE_TYPE (type), state, v,
! 	       sccstack, sccstate, sccstate_obstack);
  
    /* For integer types hash the types min/max values and the string flag.  */
    if (TREE_CODE (type) == INTEGER_TYPE)
--- 1104,1110 ----
    /* For pointer and reference types, fold in information about the type
       pointed to.  */
    if (POINTER_TYPE_P (type))
!     v = iterative_hash_gimple_type_1 (TREE_TYPE (type), v);
  
    /* For integer types hash the types min/max values and the string flag.  */
    if (TREE_CODE (type) == INTEGER_TYPE)
*************** iterative_hash_gimple_type (tree type, h
*** 1155,1170 ****
    if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
      {
        v = iterative_hash_hashval_t (TYPE_STRING_FLAG (type), v);
!       v = visit (TYPE_DOMAIN (type), state, v,
! 		 sccstack, sccstate, sccstate_obstack);
      }
  
    /* Recurse for aggregates with a single element type.  */
    if (TREE_CODE (type) == ARRAY_TYPE
        || TREE_CODE (type) == COMPLEX_TYPE
        || TREE_CODE (type) == VECTOR_TYPE)
!     v = visit (TREE_TYPE (type), state, v,
! 	       sccstack, sccstate, sccstate_obstack);
  
    /* Incorporate function return and argument types.  */
    if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
--- 1122,1135 ----
    if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
      {
        v = iterative_hash_hashval_t (TYPE_STRING_FLAG (type), v);
!       v = iterative_hash_gimple_type_1 (TYPE_DOMAIN (type), v);
      }
  
    /* Recurse for aggregates with a single element type.  */
    if (TREE_CODE (type) == ARRAY_TYPE
        || TREE_CODE (type) == COMPLEX_TYPE
        || TREE_CODE (type) == VECTOR_TYPE)
!     v = iterative_hash_gimple_type_1 (TREE_TYPE (type), v);
  
    /* Incorporate function return and argument types.  */
    if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
*************** iterative_hash_gimple_type (tree type, h
*** 1174,1189 ****
  
        /* For method types also incorporate their parent class.  */
        if (TREE_CODE (type) == METHOD_TYPE)
! 	v = visit (TYPE_METHOD_BASETYPE (type), state, v,
! 		   sccstack, sccstate, sccstate_obstack);
  
        /* Check result and argument types.  */
!       v = visit (TREE_TYPE (type), state, v,
! 		 sccstack, sccstate, sccstate_obstack);
        for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p))
  	{
! 	  v = visit (TREE_VALUE (p), state, v,
! 		     sccstack, sccstate, sccstate_obstack);
  	  na++;
  	}
  
--- 1139,1151 ----
  
        /* For method types also incorporate their parent class.  */
        if (TREE_CODE (type) == METHOD_TYPE)
! 	v = iterative_hash_gimple_type_1 (TYPE_METHOD_BASETYPE (type), v);
  
        /* Check result and argument types.  */
!       v = iterative_hash_gimple_type_1 (TREE_TYPE (type), v);
        for (p = TYPE_ARG_TYPES (type), na = 0; p; p = TREE_CHAIN (p))
  	{
! 	  v = iterative_hash_gimple_type_1 (TREE_VALUE (p), v);
  	  na++;
  	}
  
*************** iterative_hash_gimple_type (tree type, h
*** 1198,1329 ****
        for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f))
  	{
  	  v = iterative_hash_name (DECL_NAME (f), v);
! 	  v = visit (TREE_TYPE (f), state, v,
! 		     sccstack, sccstate, sccstate_obstack);
  	  nf++;
  	}
  
        v = iterative_hash_hashval_t (nf, v);
      }
  
!   /* Record hash for us.  */
!   state->u.hash = v;
! 
!   /* See if we found an SCC.  */
!   if (state->low == state->dfsnum)
!     {
!       tree x;
!       struct tree_int_map *m;
! 
!       /* Pop off the SCC and set its hash values.  */
!       x = VEC_pop (tree, *sccstack);
!       /* Optimize SCC size one.  */
!       if (x == type)
! 	{
! 	  state->on_sccstack = false;
! 	  m = ggc_alloc_cleared_tree_int_map ();
! 	  m->base.from = x;
! 	  m->to = v;
! 	  slot = htab_find_slot (type_hash_cache, m, INSERT);
! 	  gcc_assert (!*slot);
! 	  *slot = (void *) m;
! 	}
!       else
! 	{
! 	  struct sccs *cstate;
! 	  unsigned first, i, size, j;
! 	  struct type_hash_pair *pairs;
! 	  /* Pop off the SCC and build an array of type, hash pairs.  */
! 	  first = VEC_length (tree, *sccstack) - 1;
! 	  while (VEC_index (tree, *sccstack, first) != type)
! 	    --first;
! 	  size = VEC_length (tree, *sccstack) - first + 1;
! 	  pairs = XALLOCAVEC (struct type_hash_pair, size);
! 	  i = 0;
! 	  cstate = (struct sccs *)*pointer_map_contains (sccstate, x);
! 	  cstate->on_sccstack = false;
! 	  pairs[i].type = x;
! 	  pairs[i].hash = cstate->u.hash;
! 	  do
! 	    {
! 	      x = VEC_pop (tree, *sccstack);
! 	      cstate = (struct sccs *)*pointer_map_contains (sccstate, x);
! 	      cstate->on_sccstack = false;
! 	      ++i;
! 	      pairs[i].type = x;
! 	      pairs[i].hash = cstate->u.hash;
! 	    }
! 	  while (x != type);
! 	  gcc_assert (i + 1 == size);
! 	  /* Sort the arrays of type, hash pairs so that when we mix in
! 	     all members of the SCC the hash value becomes independent on
! 	     the order we visited the SCC.  Disregard hashes equal to
! 	     the hash of the type we mix into because we cannot guarantee
! 	     a stable sort for those across different TUs.  */
! 	  qsort (pairs, size, sizeof (struct type_hash_pair),
! 		 type_hash_pair_compare);
! 	  for (i = 0; i < size; ++i)
! 	    {
! 	      hashval_t hash;
! 	      m = ggc_alloc_cleared_tree_int_map ();
! 	      m->base.from = pairs[i].type;
! 	      hash = pairs[i].hash;
! 	      /* Skip same hashes.  */
! 	      for (j = i + 1; j < size && pairs[j].hash == pairs[i].hash; ++j)
! 		;
! 	      for (; j < size; ++j)
! 		hash = iterative_hash_hashval_t (pairs[j].hash, hash);
! 	      for (j = 0; pairs[j].hash != pairs[i].hash; ++j)
! 		hash = iterative_hash_hashval_t (pairs[j].hash, hash);
! 	      m->to = hash;
! 	      if (pairs[i].type == type)
! 		v = hash;
! 	      slot = htab_find_slot (type_hash_cache, m, INSERT);
! 	      gcc_assert (!*slot);
! 	      *slot = (void *) m;
! 	    }
! 	}
!     }
! 
!   return iterative_hash_hashval_t (v, val);
  }
  
- /* Returns a hash value for P (assumed to be a type).  The hash value
-    is computed using some distinguishing features of the type.  Note
-    that we cannot use pointer hashing here as we may be dealing with
-    two distinct instances of the same type.
- 
-    This function should produce the same hash value for two compatible
-    types according to gimple_types_compatible_p.  */
  
- static hashval_t
- gimple_type_hash (const void *p)
- {
-   const_tree t = (const_tree) p;
-   VEC(tree, heap) *sccstack = NULL;
-   struct pointer_map_t *sccstate;
-   struct obstack sccstate_obstack;
-   hashval_t val;
-   void **slot;
-   struct tree_int_map m;
- 
-   m.base.from = CONST_CAST_TREE (t);
-   if ((slot = htab_find_slot (type_hash_cache, &m, NO_INSERT))
-       && *slot)
-     return iterative_hash_hashval_t (((struct tree_int_map *) *slot)->to, 0);
- 
-   /* Perform a DFS walk and pre-hash all reachable types.  */
-   next_dfs_num = 1;
-   sccstate = pointer_map_create ();
-   gcc_obstack_init (&sccstate_obstack);
-   val = iterative_hash_gimple_type (CONST_CAST_TREE (t), 0,
- 				    &sccstack, sccstate, &sccstate_obstack);
-   VEC_free (tree, heap, sccstack);
-   pointer_map_destroy (sccstate);
-   obstack_free (&sccstate_obstack, NULL);
- 
-   return val;
- }
  
  /* Returns nonzero if P1 and P2 are equal.  */
  
--- 1160,1176 ----
        for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f))
  	{
  	  v = iterative_hash_name (DECL_NAME (f), v);
! 	  v = iterative_hash_gimple_type_1 (TREE_TYPE (f), v);
  	  nf++;
  	}
  
        v = iterative_hash_hashval_t (nf, v);
      }
  
!   return v;
  }
  
  
  
  /* Returns nonzero if P1 and P2 are equal.  */
  
*************** uniquify_nodes (struct data_in *data_in,
*** 1856,1862 ****
  {
    struct streamer_tree_cache_d *cache = data_in->reader_cache;
    unsigned len = VEC_length (tree, cache->nodes);
!   unsigned i;
  
    /* Go backwards because children streamed for the first time come
       as part of their parents, and hence are created after them.  */
--- 1703,1777 ----
  {
    struct streamer_tree_cache_d *cache = data_in->reader_cache;
    unsigned len = VEC_length (tree, cache->nodes);
!   unsigned i, j, n;
! 
!   /* ???  INTEGER_CSTs materialized for types in the current chunk
!      of nodes are not yet registered in the respective caches.
!      That's both an advantage (if the type does not prevail)
!      and a disadvantage (if it does we have to duplicate the
!      registering logic).  */
! 
!   /* We get called with a single tree SCC, mark it and collect
!      all types in it.  */
!   struct type_hash_pair *pairs = XALLOCAVEC (type_hash_pair, len - from);
!   for (n = 0, i = from; i < len; ++i)
!     {
!       tree t = VEC_index (tree, cache->nodes, i);
!       if (!t)
! 	continue;
!       TREE_VISITED (t) = 1;
!       if (TYPE_P (t))
!         {
! 	  /* Populate the hash hashtable using the original algorithm.  */
! 	  gimple_type_hash (t);
!           pairs[n].type = t;
!           n++;
!         }
!     }
! 
!   /* Hash all types in it.  */
!   if (n == 1)
!     {
!       struct tree_int_map *m = ggc_alloc_cleared_tree_int_map ();
!       m->base.from = pairs[0].type;
!       m->to = iterative_hash_gimple_type (pairs[0].type, 0);
!       void **slot = htab_find_slot (type_hash_cache, m, INSERT);
!       gcc_assert (!*slot);
!       *slot = (void *)m;
!     }
!   else
!     {
!       for (i = 0; i < n; ++i)
! 	pairs[i].hash = iterative_hash_gimple_type (pairs[i].type, 0);
!       qsort (pairs, n, sizeof (type_hash_pair), type_hash_pair_compare);
!       for (i = 0; i < n; ++i)
! 	{
! 	  hashval_t hash;
! 	  struct tree_int_map *m = ggc_alloc_cleared_tree_int_map ();
! 	  m->base.from = pairs[i].type;
! 	  hash = pairs[i].hash;
! 	  /* Skip same hashes.  */
! 	  for (j = i + 1; j < n && pairs[j].hash == pairs[i].hash; ++j)
! 	    ;
! 	  for (; j < n; ++j)
! 	    hash = iterative_hash_hashval_t (pairs[j].hash, hash);
! 	  for (j = 0; pairs[j].hash != pairs[i].hash; ++j)
! 	    hash = iterative_hash_hashval_t (pairs[j].hash, hash);
! 	  m->to = hash;
! 	  void **slot = htab_find_slot (type_hash_cache, m, INSERT);
! 	  gcc_assert (!*slot);
! 	  *slot = (void *) m;
! 	}
!     }
! 
!   /* Restore states to match previous behavior.  */
!   for (n = 0, i = from; i < len; ++i)
!     {
!       tree t = VEC_index (tree, cache->nodes, i);
!       if (!t)
! 	continue;
!       TREE_VISITED (t) = 0;
!     }
  
    /* Go backwards because children streamed for the first time come
       as part of their parents, and hence are created after them.  */
*************** lto_read_decls (struct lto_file_decl_dat
*** 2091,2098 ****
      {
        tree t;
        unsigned from = VEC_length (tree, data_in->reader_cache->nodes);
!       t = stream_read_tree (&ib_main, data_in);
!       gcc_assert (t && ib_main.p <= ib_main.len);
        uniquify_nodes (data_in, from);
      }
  
--- 2006,2028 ----
      {
        tree t;
        unsigned from = VEC_length (tree, data_in->reader_cache->nodes);
!       /* Read and uniquify SCCs as in the input stream.  Every
!          non-LTO_tree_scc group is a singleton.  */
!       enum LTO_tags tag = streamer_read_record_start (&ib_main);
!       if (tag == LTO_tree_scc)
! 	lto_input_scc (&ib_main, data_in);
!       else
! 	{
! 	  t = lto_input_tree_1 (&ib_main, data_in, tag);
! 	  /* Currently even singletons go the SCC path above, so this
! 	     shouldn't enlarge the cache and thus there is nothing to do.
! 	     ???  The writer-side should simply not output the useless
! 	     refs but have a corresponding lto_output_scc.  */
! 	  gcc_assert (t && (VEC_length (tree, data_in->reader_cache->nodes)
! 			    == from));
! 	  continue;
! 	}
!       gcc_assert (ib_main.p <= ib_main.len);
        uniquify_nodes (data_in, from);
      }
  
Index: trunk/gcc/lto/Make-lang.in
===================================================================
*** trunk.orig/gcc/lto/Make-lang.in	2012-10-17 11:54:15.000000000 +0200
--- trunk/gcc/lto/Make-lang.in	2012-10-17 12:27:36.880787928 +0200
*************** lto/lto.o: lto/lto.c $(CONFIG_H) $(SYSTE
*** 86,92 ****
  	langhooks.h $(VEC_H) $(BITMAP_H) pointer-set.h $(IPA_PROP_H) \
  	$(COMMON_H) debug.h $(GIMPLE_H) $(LTO_H) $(LTO_TREE_H) \
  	$(LTO_TAGS_H) $(LTO_STREAMER_H) $(SPLAY_TREE_H) gt-lto-lto.h \
! 	$(TREE_STREAMER_H) lto/lto-partition.h
  lto/lto-partition.o: lto/lto-partition.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
  	toplev.h $(TREE_H) $(TM_H) \
  	$(CGRAPH_H) $(TIMEVAR_H) \
--- 86,92 ----
  	langhooks.h $(VEC_H) $(BITMAP_H) pointer-set.h $(IPA_PROP_H) \
  	$(COMMON_H) debug.h $(GIMPLE_H) $(LTO_H) $(LTO_TREE_H) \
  	$(LTO_TAGS_H) $(LTO_STREAMER_H) $(SPLAY_TREE_H) gt-lto-lto.h \
! 	$(TREE_STREAMER_H) $(DATA_STREAMER_H) lto/lto-partition.h
  lto/lto-partition.o: lto/lto-partition.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
  	toplev.h $(TREE_H) $(TM_H) \
  	$(CGRAPH_H) $(TIMEVAR_H) \


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