public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH][RFC] Re-organize how we stream trees in LTO
@ 2012-10-16 15:03 Richard Biener
  2012-10-16 15:12 ` Richard Biener
  2012-10-16 18:14 ` Diego Novillo
  0 siblings, 2 replies; 6+ messages in thread
From: Richard Biener @ 2012-10-16 15:03 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo, Jan Hubicka


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 re-organizes lto_output_tree (the write_tree streamer
hook for LTO) in a way so that we output all tree fields
in easy to discover places.  This means that we have forward
references to trees not yet (fully) written, something the
current state avoids by "inline-expanding" forward referenced
trees at the point of reference (which results in interweaved
trees on disk).  To be able to achieve this lto_output_tree
now performs a DFS walk along tree edges to collect trees
that need to be output and outputs them in SCC chunks
in a new LTO stream container, LTO_tree_scc.

This will allow the reader side at WPA stage to call uniquify
nodes on each tree SCC, avoiding completely the DFS walks done
there (hopefully, we do DFS walks for both hashing and comparing
there - note that WPA gathers type SCCs, not tree SCCs - a subtle
difference that remains to be seen on whether it is important ...)

One complication is how we handle streaming INTEGER_CSTs.
When an INTEGER_CST refers to an already input type we can
allocate it using the build_int_cst_wide routine which takes
care of putting it into the appropriate hashtables for caching.
If an INTEGER_CST is part of a SCC (TYPE_DOMAIN values are
the most obvious cases) we now stream them as regular trees
(they cannot already exist in any cache as the type is being
read in) - but the patch does not yet populate the caches
in case the type ends up prevailing (thus it will produce
some unshared INTEGER_CSTs for now).

One uglyness of the patch is how I mis-use the streamer hooks
to switch the tree streamer routines from actually writing
tree references to performing the DFS walk.  For the DFS walk
I need information on the edge, thus a 'from' tree argument
is added to the write_tree hook.  Eventually my plan was
to transform this to a walk_tree-like interface.

Diego - is PTH still live?  Thus, do I need to bother about
inventing things in a way that can be hook-ized?  Do you
forsee any complication for PTH or C++ modules the way
I re-arranged things?  Any good (C++) ideas on how to
design this lto_walk_tree?

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.

Any comments welcome.

Thanks,
Richard.

Index: trunk/gcc/streamer-hooks.h
===================================================================
*** trunk.orig/gcc/streamer-hooks.h	2012-10-15 16:17:56.000000000 +0200
--- trunk/gcc/streamer-hooks.h	2012-10-16 14:42:15.571464478 +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/tree-streamer-out.c
===================================================================
*** trunk.orig/gcc/tree-streamer-out.c	2012-10-15 16:17:56.000000000 +0200
--- trunk/gcc/tree-streamer-out.c	2012-10-16 15:44:51.688240278 +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,577 ****
  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
       dwarf2out.c.  */
  
    if (TREE_CODE (expr) == PARM_DECL)
!     streamer_write_chain (ob, TREE_CHAIN (expr), ref_p);
  
    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);
  }
  
  
--- 564,591 ----
  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
       dwarf2out.c.  */
  
    if (TREE_CODE (expr) == PARM_DECL)
!     streamer_write_chain (ob, expr, TREE_CHAIN (expr), ref_p);
  
    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 (
*** 585,596 ****
  {
    if (TREE_CODE (expr) == FUNCTION_DECL)
      {
!       stream_write_tree (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);
  }
  
  
--- 599,610 ----
  {
    if (TREE_CODE (expr) == FUNCTION_DECL)
      {
!       stream_write_tree_edge (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
*** 604,615 ****
  {
    /* 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);
  }
  
  
--- 618,629 ----
  {
    /* 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
*** 621,631 ****
  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);
  }
  
  
--- 635,645 ----
  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
*** 639,647 ****
  {
    /* 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);
  }
  
  
--- 653,664 ----
  {
    /* 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
*** 653,671 ****
  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
--- 670,688 ----
  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 (
*** 677,696 ****
  					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);
  }
  
  
--- 694,713 ----
  					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 (
*** 701,709 ****
  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);
  }
  
  
--- 718,726 ----
  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
*** 719,725 ****
    /* 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);
  }
  
  
--- 736,742 ----
    /* 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
*** 733,740 ****
    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);
  }
  
  
--- 750,757 ----
    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
*** 745,753 ****
  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.
--- 762,770 ----
  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
*** 755,761 ****
    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);
--- 772,778 ----
    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
*** 785,805 ****
       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);
  }
  
  
--- 802,822 ----
       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
*** 816,823 ****
  
    FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, index, value)
      {
!       stream_write_tree (ob, index, ref_p);
!       stream_write_tree (ob, value, ref_p);
      }
  }
  
--- 833,840 ----
  
    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/lto-streamer-out.c
===================================================================
*** trunk.orig/gcc/lto-streamer-out.c	2012-10-15 16:17:56.000000000 +0200
--- trunk/gcc/lto-streamer-out.c	2012-10-16 15:45:32.743237874 +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-15 16:17:56.000000000 +0200
--- trunk/gcc/lto-streamer.h	2012-10-15 17:49:26.909952828 +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 (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 *,
--- 824,830 ----
  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-10 16:27:40.000000000 +0200
--- trunk/gcc/lto-streamer-in.c	2012-10-16 15:41:04.930253750 +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,1053 ****
       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,1062 ----
       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;
  }
  
*************** lto_input_tree (struct lto_input_block *
*** 1092,1097 ****
--- 1101,1146 ----
  	 words.  */
        result = streamer_read_integer_cst (ib, data_in);
      }
+   else if (tag == LTO_tree_scc)
+     {
+       /* 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)
+ 	    {
+ 	      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);
+ 	    }
+ 	}
+ 
+       /* Recurse.  */
+       return lto_input_tree (ib, data_in);
+     }
    else
      {
        /* Otherwise, materialize a new node from IB.  */
Index: trunk/gcc/lto/lto.c
===================================================================
*** trunk.orig/gcc/lto/lto.c	2012-10-12 12:46:45.000000000 +0200
--- trunk/gcc/lto/lto.c	2012-10-16 15:50:04.656221556 +0200
*************** uniquify_nodes (struct data_in *data_in,
*** 1858,1863 ****
--- 1858,1869 ----
    unsigned len = VEC_length (tree, cache->nodes);
    unsigned i;
  
+   /* ???  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).  */
+ 
    /* Go backwards because children streamed for the first time come
       as part of their parents, and hence are created after them.  */
  
Index: trunk/gcc/tree-streamer-in.c
===================================================================
*** trunk.orig/gcc/tree-streamer-in.c	2012-10-15 13:36:11.000000000 +0200
--- trunk/gcc/tree-streamer-in.c	2012-10-16 15:45:05.909239397 +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);
  

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

* Re: [PATCH][RFC] Re-organize how we stream trees in LTO
  2012-10-16 15:03 [PATCH][RFC] Re-organize how we stream trees in LTO Richard Biener
@ 2012-10-16 15:12 ` Richard Biener
  2012-10-17 12:30   ` Richard Biener
  2012-10-16 18:14 ` Diego Novillo
  1 sibling, 1 reply; 6+ messages in thread
From: Richard Biener @ 2012-10-16 15:12 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo, Jan Hubicka

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 re-organizes lto_output_tree (the write_tree streamer
> hook for LTO) in a way so that we output all tree fields
> in easy to discover places.  This means that we have forward
> references to trees not yet (fully) written, something the
> current state avoids by "inline-expanding" forward referenced
> trees at the point of reference (which results in interweaved
> trees on disk).  To be able to achieve this lto_output_tree
> now performs a DFS walk along tree edges to collect trees
> that need to be output and outputs them in SCC chunks
> in a new LTO stream container, LTO_tree_scc.
> 
> This will allow the reader side at WPA stage to call uniquify
> nodes on each tree SCC, avoiding completely the DFS walks done
> there (hopefully, we do DFS walks for both hashing and comparing
> there - note that WPA gathers type SCCs, not tree SCCs - a subtle
> difference that remains to be seen on whether it is important ...)
> 
> One complication is how we handle streaming INTEGER_CSTs.
> When an INTEGER_CST refers to an already input type we can
> allocate it using the build_int_cst_wide routine which takes
> care of putting it into the appropriate hashtables for caching.
> If an INTEGER_CST is part of a SCC (TYPE_DOMAIN values are
> the most obvious cases) we now stream them as regular trees
> (they cannot already exist in any cache as the type is being
> read in) - but the patch does not yet populate the caches
> in case the type ends up prevailing (thus it will produce
> some unshared INTEGER_CSTs for now).
> 
> One uglyness of the patch is how I mis-use the streamer hooks
> to switch the tree streamer routines from actually writing
> tree references to performing the DFS walk.  For the DFS walk
> I need information on the edge, thus a 'from' tree argument
> is added to the write_tree hook.  Eventually my plan was
> to transform this to a walk_tree-like interface.
> 
> Diego - is PTH still live?  Thus, do I need to bother about
> inventing things in a way that can be hook-ized?  Do you
> forsee any complication for PTH or C++ modules the way
> I re-arranged things?  Any good (C++) ideas on how to
> design this lto_walk_tree?
> 
> 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.

To followup myself, here are statistics for WPAing cc1:

There are a total of 2530667 SCCs, 2405450 of them are
singletons (that's 95%), 98% have less than 5 trees.
The biggest SCCs have 4178 trees.

2405450 1
  31929 2
  35922 3
  15554 4
   7648 5
   6288 6
   5865 7
   4677 8
   1819 9
   3529 10
    659 11
   1467 12
    916 13
    320 14
    305 15
    940 16
    138 17
    815 18
    434 19
    341 20
    109 21
    375 22
...
      1 1452
      1 1466
      1 1470
      1 1544
    260 1892
      1 2042
      5 2090
     24 4178

LTO file-sizes grow with the current patch and it's certainly
worth optimizing the singleton case some more.  It will definitely
be worth optimizing singletons on the merging side as well.

The above info was collected by adding

      if (flag_wpa)
        fprintf (stderr, "%lu\n", size);

to the LTO_tree_scc path in lto_input_tree.

Richard.

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

* Re: [PATCH][RFC] Re-organize how we stream trees in LTO
  2012-10-16 15:03 [PATCH][RFC] Re-organize how we stream trees in LTO Richard Biener
  2012-10-16 15:12 ` Richard Biener
@ 2012-10-16 18:14 ` Diego Novillo
  2012-10-17  9:28   ` Richard Biener
  2012-10-22 20:39   ` Lawrence Crowl
  1 sibling, 2 replies; 6+ messages in thread
From: Diego Novillo @ 2012-10-16 18:14 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, Jan Hubicka, Lawrence Crowl, Jason Merrill

On 2012-10-16 10:43 , 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.

You mean to LTRANS, the stage after WPA, right?

> The patch re-organizes lto_output_tree (the write_tree streamer
> hook for LTO) in a way so that we output all tree fields
> in easy to discover places.  This means that we have forward
> references to trees not yet (fully) written, something the
> current state avoids by "inline-expanding" forward referenced
> trees at the point of reference (which results in interweaved
> trees on disk).  To be able to achieve this lto_output_tree
> now performs a DFS walk along tree edges to collect trees
> that need to be output and outputs them in SCC chunks
> in a new LTO stream container, LTO_tree_scc.

Nice.  The interleaved output is ugly.

> This will allow the reader side at WPA stage to call uniquify
> nodes on each tree SCC, avoiding completely the DFS walks done
> there (hopefully, we do DFS walks for both hashing and comparing
> there - note that WPA gathers type SCCs, not tree SCCs - a subtle
> difference that remains to be seen on whether it is important ...)
>
> One complication is how we handle streaming INTEGER_CSTs.
> When an INTEGER_CST refers to an already input type we can
> allocate it using the build_int_cst_wide routine which takes
> care of putting it into the appropriate hashtables for caching.
> If an INTEGER_CST is part of a SCC (TYPE_DOMAIN values are
> the most obvious cases) we now stream them as regular trees
> (they cannot already exist in any cache as the type is being
> read in) - but the patch does not yet populate the caches
> in case the type ends up prevailing (thus it will produce
> some unshared INTEGER_CSTs for now).
>
> One uglyness of the patch is how I mis-use the streamer hooks
> to switch the tree streamer routines from actually writing
> tree references to performing the DFS walk.  For the DFS walk
> I need information on the edge, thus a 'from' tree argument
> is added to the write_tree hook.  Eventually my plan was
> to transform this to a walk_tree-like interface.
>
> Diego - is PTH still live?  Thus, do I need to bother about
> inventing things in a way that can be hook-ized?

We will eventually revive PPH.  But not in the short term.  I think it 
will come back when/if we start implementing C++ modules.  Jason, 
Lawrence, is that something that you see coming for the next standard?

I suspect that the front end will need to distance itself from 'tree' 
and have its own streamable IL.  So, the hooks may not be something we 
need to keep long term.

Emitting the trees in SCC groups should not affect the C++ streamer too 
much.  It already is doing its own strategy of emitting tree headers so 
it can do declaration and type merging.  As long as the trees can be 
fully materialized from the SCC groups, it should be fine.

> forsee any complication for PTH or C++ modules the way
> I re-arranged things?  Any good (C++) ideas on how to
> design this lto_walk_tree?

Sorry.  What lto_walk_tree?

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

You want to document what the new 'tree' argument does here.

> ! #define stream_write_tree_edge_shallow_non_ref(OB, FROM, TO, REF_P) \
> !     streamer_hooks.write_tree(OB, FROM, TO, REF_P, false)

Why the stream_write_tree_edge() naming?  Not sure what you mean by this.


Diego.

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

* Re: [PATCH][RFC] Re-organize how we stream trees in LTO
  2012-10-16 18:14 ` Diego Novillo
@ 2012-10-17  9:28   ` Richard Biener
  2012-10-22 20:39   ` Lawrence Crowl
  1 sibling, 0 replies; 6+ messages in thread
From: Richard Biener @ 2012-10-17  9:28 UTC (permalink / raw)
  To: Diego Novillo; +Cc: gcc-patches, Jan Hubicka, Lawrence Crowl, Jason Merrill

On Tue, 16 Oct 2012, Diego Novillo wrote:

> On 2012-10-16 10:43 , 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.
> 
> You mean to LTRANS, the stage after WPA, right?

No, to compile stage, before WPA.  Obviously I can only move
the non-merging parts of type merging there ;)

> > The patch re-organizes lto_output_tree (the write_tree streamer
> > hook for LTO) in a way so that we output all tree fields
> > in easy to discover places.  This means that we have forward
> > references to trees not yet (fully) written, something the
> > current state avoids by "inline-expanding" forward referenced
> > trees at the point of reference (which results in interweaved
> > trees on disk).  To be able to achieve this lto_output_tree
> > now performs a DFS walk along tree edges to collect trees
> > that need to be output and outputs them in SCC chunks
> > in a new LTO stream container, LTO_tree_scc.
> 
> Nice.  The interleaved output is ugly.
> 
> > This will allow the reader side at WPA stage to call uniquify
> > nodes on each tree SCC, avoiding completely the DFS walks done
> > there (hopefully, we do DFS walks for both hashing and comparing
> > there - note that WPA gathers type SCCs, not tree SCCs - a subtle
> > difference that remains to be seen on whether it is important ...)
> > 
> > One complication is how we handle streaming INTEGER_CSTs.
> > When an INTEGER_CST refers to an already input type we can
> > allocate it using the build_int_cst_wide routine which takes
> > care of putting it into the appropriate hashtables for caching.
> > If an INTEGER_CST is part of a SCC (TYPE_DOMAIN values are
> > the most obvious cases) we now stream them as regular trees
> > (they cannot already exist in any cache as the type is being
> > read in) - but the patch does not yet populate the caches
> > in case the type ends up prevailing (thus it will produce
> > some unshared INTEGER_CSTs for now).
> > 
> > One uglyness of the patch is how I mis-use the streamer hooks
> > to switch the tree streamer routines from actually writing
> > tree references to performing the DFS walk.  For the DFS walk
> > I need information on the edge, thus a 'from' tree argument
> > is added to the write_tree hook.  Eventually my plan was
> > to transform this to a walk_tree-like interface.
> > 
> > Diego - is PTH still live?  Thus, do I need to bother about
> > inventing things in a way that can be hook-ized?
> 
> We will eventually revive PPH.  But not in the short term.  I think it will
> come back when/if we start implementing C++ modules.  Jason, Lawrence, is that
> something that you see coming for the next standard?
> 
> I suspect that the front end will need to distance itself from 'tree' and have
> its own streamable IL.  So, the hooks may not be something we need to keep
> long term.
> 
> Emitting the trees in SCC groups should not affect the C++ streamer too much.
> It already is doing its own strategy of emitting tree headers so it can do
> declaration and type merging.  As long as the trees can be fully materialized
> from the SCC groups, it should be fine.
> 
> > forsee any complication for PTH or C++ modules the way
> > I re-arranged things?  Any good (C++) ideas on how to
> > design this lto_walk_tree?
> 
> Sorry.  What lto_walk_tree?

Basically I'd like to unify the tree walks done by tree-streamer-out.c
(the recursion inherent in calling the streamer_write_tree hook),
by tree-streamer-in.c (same for streamer_reaad_tree) and eventually
lto_fixup_type by means of a lto_walk_tree which in C terms would
get a function pointer to invoke on each tree field (that LTO
cares about) in a tree.  With C++ instead of a function pointer and
a void * data argument you'd probably use a functor.

With the patch I introduced a third usage, the DFS walk (thus the
idea to somehow factor this out in a more elegant way than how I
have it factored by re-writing the hook)

> > --- 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);
> 
> You want to document what the new 'tree' argument does here.

Ah, indeed.

> > ! #define stream_write_tree_edge_shallow_non_ref(OB, FROM, TO, REF_P) \
> > !     streamer_hooks.write_tree(OB, FROM, TO, REF_P, false)
> 
> Why the stream_write_tree_edge() naming?  Not sure what you mean by this.

For the DFS walk I need to walk all 'edges' from a tree node, an
edge from tree node a to tree node b is the pair FROM, TO.

Any better idea?

Thanks,
Richard.

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

* Re: [PATCH][RFC] Re-organize how we stream trees in LTO
  2012-10-16 15:12 ` Richard Biener
@ 2012-10-17 12:30   ` Richard Biener
  0 siblings, 0 replies; 6+ messages in thread
From: Richard Biener @ 2012-10-17 12:30 UTC (permalink / raw)
  To: gcc-patches; +Cc: Diego Novillo, Jan Hubicka, andi

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) \

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

* Re: [PATCH][RFC] Re-organize how we stream trees in LTO
  2012-10-16 18:14 ` Diego Novillo
  2012-10-17  9:28   ` Richard Biener
@ 2012-10-22 20:39   ` Lawrence Crowl
  1 sibling, 0 replies; 6+ messages in thread
From: Lawrence Crowl @ 2012-10-22 20:39 UTC (permalink / raw)
  To: Diego Novillo; +Cc: Richard Biener, gcc-patches, Jan Hubicka, Jason Merrill

On 10/16/12, Diego Novillo <dnovillo@google.com> wrote:
> On 2012-10-16 10:43 , Richard Biener wrote:
> > Diego - is PTH still live?  Thus, do I need to bother about
> > inventing things in a way that can be hook-ized?
>
> We will eventually revive PPH.  But not in the short term.  I think
> it will come back when/if we start implementing C++ modules.
> Jason, Lawrence, is that something that you see coming for the
> next standard?

There are some people working on it, though not very publically.
Many folks would like to see modules in the next full standard,
probably circa 2017.

It is likely that the design point for standard modules will differ
from PPH, and so I don't think that the current PPH implementation
should serve as a constraint on other work.

> I suspect that the front end will need to distance itself from
> 'tree' and have its own streamable IL.  So, the hooks may not be
> something we need to keep long term.
>
> Emitting the trees in SCC groups should not affect the C++
> streamer too much.  It already is doing its own strategy of
> emitting tree headers so it can do declaration and type merging.
> As long as the trees can be fully materialized from the SCC groups,
> it should be fine.

-- 
Lawrence Crowl

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

end of thread, other threads:[~2012-10-22 20:32 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-16 15:03 [PATCH][RFC] Re-organize how we stream trees in LTO Richard Biener
2012-10-16 15:12 ` Richard Biener
2012-10-17 12:30   ` Richard Biener
2012-10-16 18:14 ` Diego Novillo
2012-10-17  9:28   ` Richard Biener
2012-10-22 20:39   ` Lawrence Crowl

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