diff mbox

[lto] Merge streamer hooks from pph branch. (issue4568043)

Message ID BANLkTik8dVqMM4CZCz8mB49CcAiMAO33uA@mail.gmail.com
State New
Headers show

Commit Message

Diego Novillo June 4, 2011, 7:30 p.m. UTC
On Wed, Jun 1, 2011 at 15:19, Richard Guenther <rguenther@suse.de> wrote:

> Yes, I see no benefit of using a global function to get access
> to the address of a global variable.

There is the minor benefit of being able to control  access to it, but
I don't have a really convincing reason to give you, so I changed it
to a global.

>> >> +     if (h->indexable_with_decls_p && h->indexable_with_decls_p (expr))
>> >> +       {
>> >> +         output_record_start (ob, LTO_global_decl_ref);
>> >> +         lto_output_var_decl_index (ob->decl_state, ob->main_stream, expr);
>> >
>> > Why hook it this way and not
>> >
>> >             if (h->output_tree_ref
>> >                 && h->output_tree_ref (...))
>> >               break;
>> >             gcc_unreachable ();
>> >
>> > I find the flag vs. function hook stuff somewhat odd.
>>
>> Sure.  It's
>
> ... missing words? ;)

Sorry.  I meant to continue with "It's just that this particular hook
is simpler if it only needs to decide whether the node can be written
as a decl reference.  The code to write the node will be the same
everywhere."  It would lead to duplication and the hooks would need to
know more internal details of the generic streamer (they need to write
the reference in exactly the way that lto_input_tree is expecting).

This is not a flag, actually.  It's a predicate function called on a
node.  If the node passes the predicate, then it is written in the
decl index table.


>> >> @@ -1438,8 +1450,27 @@ lto_output_tree (struct output_block *ob, tree expr, bool ref_p)
>> >>       to be materialized by the reader (to implement TYPE_CACHED_VALUES).  */
>> >>    if (TREE_CODE (expr) == INTEGER_CST)
>> >>      {
>> >> -      lto_output_integer_cst (ob, expr, ref_p);
>> >> -      return;
>> >> +      bool is_special;
>> >> +
>> >> +     /* There are some constants that are special to the streamer
>> >> +     (e.g., void_zero_node, truthvalue_false_node).
>> >> +     These constants cannot be rematerialized with
>> >> +     build_int_cst_wide because they may actually lack a type (like
>> >> +     void_zero_node) and they need to be pointer-identical to trees
>> >> +     materialized by the compiler tables like global_trees or
>> >> +     c_global_trees.
>> >> +
>> >> +     If the streamer told us that it has special constants, they
>> >> +     will be preloaded in the streamer cache.  If we find a match,
>> >> +     then stream the constant as a reference so the reader can
>> >> +     re-materialize it from the cache.  */
>> >> +      is_special = streamer_hooks ()->has_unique_integer_csts_p
>> >> +                && lto_streamer_cache_lookup (ob->writer_cache, expr, NULL);
>> >> +      if (!is_special)
>> >> +     {
>> >> +       lto_output_integer_cst (ob, expr, ref_p);
>> >> +       return;
>> >> +     }
>> >
>> > ???  We should not arrive here for such global trees.  Please do not
>> > merge this part of the patch as part of the hook introducing (keep
>> > patches simple, make them do a single thing ...)
>>
>> Not sure what you are objecting to.  We do execute this for global
>> trees in the C++ FE (as described in the comment).  Are you objecting
>> to never handling unique constants or to merging this handling until
>> the pph bits are in?
>
> Are you not pre-loading those global trees then?

I am, but since the streamer always wanted to stream INTEGER_CSTs
separately, it wasn't getting a chance to check the cache first.

> Yes, I think this isn't the time to merge this piece.

No problem.  I'll keep this part in the branch.

> Ah, I think I get it - we don't stream integer constants as trees.

Right.

> But it's odd that you only handle this
> for integer-csts and not other trees we don't stream as-is (and
> thus do not enter into the cache)

Because constants are the only ones that are handled right before the
cache is consulted.  Every other pre-built tree can be cached
(regardless of whether it's handled by the streamer).

> - I think this should be moved up a level and made generic to handle all trees.  Or we should
> handle integer-csts similar to builtins - always enter them in the cache,

I tried this, but the result was sub-optimal
(http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00563.html)
Putting constants in the cache, caused various failures which I never
fully debugged because I noticed an increase in the object size (I
remember it was noticeable, but not by how much).

I didn't insist too much with this approach, so maybe I could try it again.


> or only handle all pre-loaded nodes that way.

That is what we do.  Pre-loaded nodes always go through the cache.
The problem were pre-loaded constants, since they *never* go through
the cache.

>> >> @@ -2238,6 +2269,8 @@ static void
>> >>  lto_writer_init (void)
>> >>  {
>> >>    lto_streamer_init ();
>> >> +  if (streamer_hooks ()->writer_init)
>> >> +    streamer_hooks ()->writer_init ();
>> >
>> > This hook should always exist.  Why is this called in a context with
>> > lto_*?
>>
>> There is common streaming code that both the gimple and c++ streamers
>> use.  I suppose both could call lto_streamer_init and then call their
>> own initializer routines instead of doing a hook.
>
> Yeah, I'd prefer that.  I don't have a clear picture yet on what
> piece of the streamer is actually shared.

Sure.  Done.

>> >> +
>> >> +/* Initialize all the streamer hooks used for streaming GIMPLE.  */
>> >> +
>> >> +void
>> >> +gimple_streamer_hooks_init (void)
>> >
>> > It's more like lto_streamer_hooks_init, no?  You are basically
>> > turning lto_streamer_* to tree_streamer_* and make lto_streamer_*
>> > tree_streamer_* + lto_streamer_hooks, no?
>>
>> I was about to call them gimple_streamer_hooks, but lto_streamer_hooks
>> is also fine with me.  Subsequent patch.  So:
>>
>> 1- Call the generic implementation and streamer hooks tree_streamer_*
>> 2- Rename the lto-specific parts of streaming to lto_streamer_*
>> 3- Move generic streaming implementation to tree-streamer.[ch]
>>
>> Does that sound OK?
>
> That sounds ok.  You are only sharing the code streaming trees, right?

Right.  Patch coming up on top of the revised patch for streamer hooks.

The attached revision of the patch has been tested again with an LTO
profiledbootstrap on x86_64.  OK for trunk?

	* Makefile.in (lto-compress.o): Add dependency on LTO_STREAMER_H.
	(cgraph.o): Likewise.
	(cgraphunit.o): Likewise.
	* cgraphunit.c: Include lto-streamer.h
	(cgraph_finalize_compilation_unit): Call lto_streamer_hooks_init
	if LTO is enabled.
	* lto-streamer-in.c (unpack_value_fields): Call
	streamer_hooks.unpack_value_fields if set.
	(lto_materialize_tree): For unhandled nodes, first try to
	call lto_streamer_hooks.alloc_tree, if it exists.
	(lto_input_ts_decl_common_tree_pointers): Move reading of
	DECL_INITIAL to lto_streamer_read_tree.
	(lto_read_tree): Call lto_streamer_hooks.read_tree if set.
	(lto_streamer_read_tree): New.
	(lto_reader_init): Rename from lto_init_reader.
	Move initialization code to lto/lto.c.
	* lto-streamer-out.c (pack_value_fields): Call
	streamer_hooks.pack_value_fields if set.
	(lto_output_tree_ref): For tree nodes that are not
	normally indexable, call streamer_hooks.indexable_with_decls_p
	before giving up.
	(lto_output_ts_decl_common_tree_pointers): Move handling
	for FUNCTION_DECL and TRANSLATION_UNIT_DECL to
	lto_streamer_write_tree.
	(lto_output_tree_header): Call streamer_hooks.is_streamable
	instead of lto_is_streamable.
	Call lto_streamer_hooks.output_tree_header if set.
	(lto_write_tree): Call lto_streamer_hooks.write_tree if
	set.
	(lto_streamer_write_tree): New.
	(lto_output): Call lto_streamer_init directly.
	(lto_writer_init): Remove.
	* lto-streamer.c (streamer_hooks): New.
	(lto_streamer_cache_create): Call streamer_hooks.preload_common_nodes
	instead of lto_preload_common_nodes.
	(lto_is_streamable): Move from lto-streamer.h
	(lto_streamer_hooks_init): New.
	(streamer_hooks): New.
	(streamer_hooks_init): New.
	* lto-streamer.h (struct output_block): Forward declare.
	(struct lto_input_block): Likewise.
	(struct data_in): Likewise.
	(struct bitpack_d): Likewise.
	(struct streamer_hooks): Declare.
	(streamer_hooks): Declare.
	(lto_streamer_hooks_init): Declare.
	(lto_streamer_write_tree): Declare.
	(lto_streamer_read_tree): Declare.
	(streamer_hooks_init): Declare.
	(lto_is_streamable): Move to lto-streamer.c

lto/ChangeLog

	* lto.c (lto_init): New.
	(lto_main): Call it.


Diego.

Comments

Richard Biener June 6, 2011, 2:50 p.m. UTC | #1
On Sat, 4 Jun 2011, Diego Novillo wrote:

> On Wed, Jun 1, 2011 at 15:19, Richard Guenther <rguenther@suse.de> wrote:
> 
> > Yes, I see no benefit of using a global function to get access
> > to the address of a global variable.
> 
> There is the minor benefit of being able to control  access to it, but
> I don't have a really convincing reason to give you, so I changed it
> to a global.

Thanks.  It feels more consistent this way.

> >> >> +     if (h->indexable_with_decls_p && h->indexable_with_decls_p (expr))
> >> >> +       {
> >> >> +         output_record_start (ob, LTO_global_decl_ref);
> >> >> +         lto_output_var_decl_index (ob->decl_state, ob->main_stream, expr);
> >> >
> >> > Why hook it this way and not
> >> >
> >> >             if (h->output_tree_ref
> >> >                 && h->output_tree_ref (...))
> >> >               break;
> >> >             gcc_unreachable ();
> >> >
> >> > I find the flag vs. function hook stuff somewhat odd.
> >>
> >> Sure.  It's
> >
> > ... missing words? ;)
> 
> Sorry.  I meant to continue with "It's just that this particular hook
> is simpler if it only needs to decide whether the node can be written
> as a decl reference.  The code to write the node will be the same
> everywhere."  It would lead to duplication and the hooks would need to
> know more internal details of the generic streamer (they need to write
> the reference in exactly the way that lto_input_tree is expecting).
> 
> This is not a flag, actually.  It's a predicate function called on a
> node.  If the node passes the predicate, then it is written in the
> decl index table.

Hm, ok.

> 
> >> >> @@ -1438,8 +1450,27 @@ lto_output_tree (struct output_block *ob, tree expr, bool ref_p)
> >> >>       to be materialized by the reader (to implement TYPE_CACHED_VALUES).  */
> >> >>    if (TREE_CODE (expr) == INTEGER_CST)
> >> >>      {
> >> >> -      lto_output_integer_cst (ob, expr, ref_p);
> >> >> -      return;
> >> >> +      bool is_special;
> >> >> +
> >> >> +     /* There are some constants that are special to the streamer
> >> >> +     (e.g., void_zero_node, truthvalue_false_node).
> >> >> +     These constants cannot be rematerialized with
> >> >> +     build_int_cst_wide because they may actually lack a type (like
> >> >> +     void_zero_node) and they need to be pointer-identical to trees
> >> >> +     materialized by the compiler tables like global_trees or
> >> >> +     c_global_trees.
> >> >> +
> >> >> +     If the streamer told us that it has special constants, they
> >> >> +     will be preloaded in the streamer cache.  If we find a match,
> >> >> +     then stream the constant as a reference so the reader can
> >> >> +     re-materialize it from the cache.  */
> >> >> +      is_special = streamer_hooks ()->has_unique_integer_csts_p
> >> >> +                && lto_streamer_cache_lookup (ob->writer_cache, expr, NULL);
> >> >> +      if (!is_special)
> >> >> +     {
> >> >> +       lto_output_integer_cst (ob, expr, ref_p);
> >> >> +       return;
> >> >> +     }
> >> >
> >> > ???  We should not arrive here for such global trees.  Please do not
> >> > merge this part of the patch as part of the hook introducing (keep
> >> > patches simple, make them do a single thing ...)
> >>
> >> Not sure what you are objecting to.  We do execute this for global
> >> trees in the C++ FE (as described in the comment).  Are you objecting
> >> to never handling unique constants or to merging this handling until
> >> the pph bits are in?
> >
> > Are you not pre-loading those global trees then?
> 
> I am, but since the streamer always wanted to stream INTEGER_CSTs
> separately, it wasn't getting a chance to check the cache first.
> 
> > Yes, I think this isn't the time to merge this piece.
> 
> No problem.  I'll keep this part in the branch.
> 
> > Ah, I think I get it - we don't stream integer constants as trees.
> 
> Right.
> 
> > But it's odd that you only handle this
> > for integer-csts and not other trees we don't stream as-is (and
> > thus do not enter into the cache)
> 
> Because constants are the only ones that are handled right before the
> cache is consulted.  Every other pre-built tree can be cached
> (regardless of whether it's handled by the streamer).
> 
> > - I think this should be moved up a level and made generic to handle all trees.  Or we should
> > handle integer-csts similar to builtins - always enter them in the cache,
> 
> I tried this, but the result was sub-optimal
> (http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00563.html)
> Putting constants in the cache, caused various failures which I never
> fully debugged because I noticed an increase in the object size (I
> remember it was noticeable, but not by how much).
> 
> I didn't insist too much with this approach, so maybe I could try it again.
> 
> 
> > or only handle all pre-loaded nodes that way.
> 
> That is what we do.  Pre-loaded nodes always go through the cache.
> The problem were pre-loaded constants, since they *never* go through
> the cache.

Do you remember if it was only void_zero_node that causes problems?
We could just special-case it in 
lto_input_integer_cst/lto_output_integer_cst.  Or even fix the C family
frontends to not create or use this strange node.  It seems to be
a special tree for "empty valid something" which could as well be
a new tree code with a singleton object.

> >> >> @@ -2238,6 +2269,8 @@ static void
> >> >>  lto_writer_init (void)
> >> >>  {
> >> >>    lto_streamer_init ();
> >> >> +  if (streamer_hooks ()->writer_init)
> >> >> +    streamer_hooks ()->writer_init ();
> >> >
> >> > This hook should always exist.  Why is this called in a context with
> >> > lto_*?
> >>
> >> There is common streaming code that both the gimple and c++ streamers
> >> use.  I suppose both could call lto_streamer_init and then call their
> >> own initializer routines instead of doing a hook.
> >
> > Yeah, I'd prefer that.  I don't have a clear picture yet on what
> > piece of the streamer is actually shared.
> 
> Sure.  Done.
> 
> >> >> +
> >> >> +/* Initialize all the streamer hooks used for streaming GIMPLE.  */
> >> >> +
> >> >> +void
> >> >> +gimple_streamer_hooks_init (void)
> >> >
> >> > It's more like lto_streamer_hooks_init, no?  You are basically
> >> > turning lto_streamer_* to tree_streamer_* and make lto_streamer_*
> >> > tree_streamer_* + lto_streamer_hooks, no?
> >>
> >> I was about to call them gimple_streamer_hooks, but lto_streamer_hooks
> >> is also fine with me.  Subsequent patch.  So:
> >>
> >> 1- Call the generic implementation and streamer hooks tree_streamer_*
> >> 2- Rename the lto-specific parts of streaming to lto_streamer_*
> >> 3- Move generic streaming implementation to tree-streamer.[ch]
> >>
> >> Does that sound OK?
> >
> > That sounds ok.  You are only sharing the code streaming trees, right?
> 
> Right.  Patch coming up on top of the revised patch for streamer hooks.
> 
> The attached revision of the patch has been tested again with an LTO
> profiledbootstrap on x86_64.  OK for trunk?

Looks good to me.

Thanks,
Richard.

> 	* Makefile.in (lto-compress.o): Add dependency on LTO_STREAMER_H.
> 	(cgraph.o): Likewise.
> 	(cgraphunit.o): Likewise.
> 	* cgraphunit.c: Include lto-streamer.h
> 	(cgraph_finalize_compilation_unit): Call lto_streamer_hooks_init
> 	if LTO is enabled.
> 	* lto-streamer-in.c (unpack_value_fields): Call
> 	streamer_hooks.unpack_value_fields if set.
> 	(lto_materialize_tree): For unhandled nodes, first try to
> 	call lto_streamer_hooks.alloc_tree, if it exists.
> 	(lto_input_ts_decl_common_tree_pointers): Move reading of
> 	DECL_INITIAL to lto_streamer_read_tree.
> 	(lto_read_tree): Call lto_streamer_hooks.read_tree if set.
> 	(lto_streamer_read_tree): New.
> 	(lto_reader_init): Rename from lto_init_reader.
> 	Move initialization code to lto/lto.c.
> 	* lto-streamer-out.c (pack_value_fields): Call
> 	streamer_hooks.pack_value_fields if set.
> 	(lto_output_tree_ref): For tree nodes that are not
> 	normally indexable, call streamer_hooks.indexable_with_decls_p
> 	before giving up.
> 	(lto_output_ts_decl_common_tree_pointers): Move handling
> 	for FUNCTION_DECL and TRANSLATION_UNIT_DECL to
> 	lto_streamer_write_tree.
> 	(lto_output_tree_header): Call streamer_hooks.is_streamable
> 	instead of lto_is_streamable.
> 	Call lto_streamer_hooks.output_tree_header if set.
> 	(lto_write_tree): Call lto_streamer_hooks.write_tree if
> 	set.
> 	(lto_streamer_write_tree): New.
> 	(lto_output): Call lto_streamer_init directly.
> 	(lto_writer_init): Remove.
> 	* lto-streamer.c (streamer_hooks): New.
> 	(lto_streamer_cache_create): Call streamer_hooks.preload_common_nodes
> 	instead of lto_preload_common_nodes.
> 	(lto_is_streamable): Move from lto-streamer.h
> 	(lto_streamer_hooks_init): New.
> 	(streamer_hooks): New.
> 	(streamer_hooks_init): New.
> 	* lto-streamer.h (struct output_block): Forward declare.
> 	(struct lto_input_block): Likewise.
> 	(struct data_in): Likewise.
> 	(struct bitpack_d): Likewise.
> 	(struct streamer_hooks): Declare.
> 	(streamer_hooks): Declare.
> 	(lto_streamer_hooks_init): Declare.
> 	(lto_streamer_write_tree): Declare.
> 	(lto_streamer_read_tree): Declare.
> 	(streamer_hooks_init): Declare.
> 	(lto_is_streamable): Move to lto-streamer.c
> 
> lto/ChangeLog
> 
> 	* lto.c (lto_init): New.
> 	(lto_main): Call it.
> 
> 
> Diego.
>
Diego Novillo June 6, 2011, 3:33 p.m. UTC | #2
On Mon, Jun 6, 2011 at 10:50, Richard Guenther <rguenther@suse.de> wrote:

> Do you remember if it was only void_zero_node that causes problems?
> We could just special-case it in
> lto_input_integer_cst/lto_output_integer_cst.  Or even fix the C family
> frontends to not create or use this strange node.  It seems to be
> a special tree for "empty valid something" which could as well be
> a new tree code with a singleton object.

void_zero_node was the only one causing problems because it couldn't
be built with build_int_cst_wide.  However, the other well-known
constants where causing problem when the parser tried to
pointer-compare them.  This sounded a bit strange to me, since I was
expecting the constant cache in the type to give back shared
constants, but I did not investigate it further.

Making void_zero_node a new singleton object sounds like a good cleanup.


Diego.
diff mbox

Patch

2011-06-04  Diego Novillo  <dnovillo@google.com>

	* Makefile.in (lto-compress.o): Add dependency on LTO_STREAMER_H.
	(cgraph.o): Likewise.
	(cgraphunit.o): Likewise.
	* cgraphunit.c: Include lto-streamer.h
	(cgraph_finalize_compilation_unit): Call lto_streamer_hooks_init
	if LTO is enabled.
	* lto-streamer-in.c (unpack_value_fields): Call 
	streamer_hooks.unpack_value_fields if set.
	(lto_materialize_tree): For unhandled nodes, first try to
	call lto_streamer_hooks.alloc_tree, if it exists.
	(lto_input_ts_decl_common_tree_pointers): Move reading of
	DECL_INITIAL to lto_streamer_read_tree.
	(lto_read_tree): Call lto_streamer_hooks.read_tree if set.
	(lto_streamer_read_tree): New.
	(lto_reader_init): Rename from lto_init_reader.
	Move initialization code to lto/lto.c.
	* lto-streamer-out.c (pack_value_fields): Call
	streamer_hooks.pack_value_fields if set.
	(lto_output_tree_ref): For tree nodes that are not
	normally indexable, call streamer_hooks.indexable_with_decls_p
	before giving up.
	(lto_output_ts_decl_common_tree_pointers): Move handling
	for FUNCTION_DECL and TRANSLATION_UNIT_DECL to
	lto_streamer_write_tree.
	(lto_output_tree_header): Call streamer_hooks.is_streamable
	instead of lto_is_streamable.
	Call lto_streamer_hooks.output_tree_header if set.
	(lto_write_tree): Call lto_streamer_hooks.write_tree if
	set.
	(lto_streamer_write_tree): New.
	(lto_output): Call lto_streamer_init directly.
	(lto_writer_init): Remove.
	* lto-streamer.c (streamer_hooks): New.
	(lto_streamer_cache_create): Call streamer_hooks.preload_common_nodes
	instead of lto_preload_common_nodes.
	(lto_is_streamable): Move from lto-streamer.h
	(lto_streamer_hooks_init): New.
	(streamer_hooks): New.
	(streamer_hooks_init): New.
	* lto-streamer.h (struct output_block): Forward declare.
	(struct lto_input_block): Likewise.
	(struct data_in): Likewise.
	(struct bitpack_d): Likewise.
	(struct streamer_hooks): Declare.
	(streamer_hooks): Declare.
	(lto_streamer_hooks_init): Declare.
	(lto_streamer_write_tree): Declare.
	(lto_streamer_read_tree): Declare.
	(streamer_hooks_init): Declare.
	(lto_is_streamable): Move to lto-streamer.c

lto/ChangeLog

	* lto.c (lto_init): New.
	(lto_main): Call it.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 81736a0..496177c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2348,7 +2348,7 @@  double-int.o: double-int.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
 
 # lto-compress.o needs $(ZLIBINC) added to the include flags.
 lto-compress.o: lto-compress.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
-	$(TREE_H) langhooks.h $(LTO_HEADER_H) $(LTO_SECTION_H) \
+	$(TREE_H) langhooks.h $(LTO_STREAMER_H) $(LTO_SECTION_H) \
 	lto-compress.h $(DIAGNOSTIC_CORE_H) $(DIAGNOSTIC_CORE_H)
 	$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(ZLIBINC) $< $(OUTPUT_OPTION)
 
@@ -3030,14 +3030,15 @@  cgraph.o : cgraph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
    gt-cgraph.h output.h intl.h $(BASIC_BLOCK_H) debug.h $(HASHTAB_H) \
    $(TREE_INLINE_H) $(TREE_DUMP_H) $(TREE_FLOW_H) cif-code.def \
    value-prof.h $(EXCEPT_H) $(IPA_UTILS_H) $(DIAGNOSTIC_CORE_H) \
-   ipa-inline.h
+   ipa-inline.h $(LTO_STREAMER_H)
 cgraphunit.o : cgraphunit.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) langhooks.h $(TREE_INLINE_H) toplev.h $(DIAGNOSTIC_CORE_H) $(FLAGS_H) $(GGC_H) \
    $(TARGET_H) $(CGRAPH_H) intl.h pointer-set.h $(FUNCTION_H) $(GIMPLE_H) \
    $(TREE_FLOW_H) $(TREE_PASS_H) debug.h $(DIAGNOSTIC_H) \
    $(FIBHEAP_H) output.h $(PARAMS_H) $(RTL_H) $(TIMEVAR_H) $(IPA_PROP_H) \
    gt-cgraphunit.h tree-iterator.h $(COVERAGE_H) $(TREE_DUMP_H) \
-   tree-pretty-print.h gimple-pretty-print.h ipa-inline.h $(IPA_UTILS_H)
+   tree-pretty-print.h gimple-pretty-print.h ipa-inline.h $(IPA_UTILS_H) \
+   $(LTO_STREAMER_H)
 cgraphbuild.o : cgraphbuild.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) langhooks.h $(CGRAPH_H) intl.h pointer-set.h $(GIMPLE_H) \
    $(TREE_FLOW_H) $(TREE_PASS_H) $(IPA_UTILS_H) $(EXCEPT_H) \
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 834acb1..614b785 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -140,6 +140,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "ipa-inline.h"
 #include "ipa-utils.h"
+#include "lto-streamer.h"
 
 static void cgraph_expand_all_functions (void);
 static void cgraph_mark_functions_to_output (void);
@@ -1092,6 +1093,10 @@  cgraph_finalize_compilation_unit (void)
 {
   timevar_push (TV_CGRAPH);
 
+  /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE.  */
+  if (flag_lto)
+    lto_streamer_hooks_init ();
+
   /* If we're here there's no current function anymore.  Some frontends
      are lazy in clearing these.  */
   current_function_decl = NULL;
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
index 3a5eb5a..25ccb24 100644
--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -1841,6 +1841,9 @@  unpack_value_fields (struct bitpack_d *bp, tree expr)
 
   if (CODE_CONTAINS_STRUCT (code, TS_TRANSLATION_UNIT_DECL))
     unpack_ts_translation_unit_decl_value_fields (bp, expr);
+
+  if (streamer_hooks.unpack_value_fields)
+    streamer_hooks.unpack_value_fields (bp, expr);
 }
 
 
@@ -1892,8 +1895,15 @@  lto_materialize_tree (struct lto_input_block *ib, struct data_in *data_in,
     }
   else
     {
-      /* All other nodes can be materialized with a raw make_node call.  */
-      result = make_node (code);
+      /* For all other nodes, see if the streamer knows how to allocate
+	 it.  */
+      if (streamer_hooks.alloc_tree)
+	result = streamer_hooks.alloc_tree (code, ib, data_in);
+
+      /* If the hook did not handle it, materialize the tree with a raw
+	 make_node call.  */
+      if (result == NULL_TREE)
+	result = make_node (code);
     }
 
 #ifdef LTO_STREAMER_DEBUG
@@ -1988,12 +1998,8 @@  lto_input_ts_decl_common_tree_pointers (struct lto_input_block *ib,
 {
   DECL_SIZE (expr) = lto_input_tree (ib, data_in);
   DECL_SIZE_UNIT (expr) = lto_input_tree (ib, data_in);
-
-  if (TREE_CODE (expr) != FUNCTION_DECL
-      && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
-    DECL_INITIAL (expr) = lto_input_tree (ib, data_in);
-
   DECL_ATTRIBUTES (expr) = lto_input_tree (ib, data_in);
+
   /* 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.  */
@@ -2484,6 +2490,11 @@  lto_read_tree (struct lto_input_block *ib, struct data_in *data_in,
   /* Read all the pointer fields in RESULT.  */
   lto_input_tree_pointers (ib, data_in, result);
 
+  /* Call back into the streaming module to read anything else it
+     may need.  */
+  if (streamer_hooks.read_tree)
+    streamer_hooks.read_tree (ib, data_in, result);
+
   /* We should never try to instantiate an MD or NORMAL builtin here.  */
   if (TREE_CODE (result) == FUNCTION_DECL)
     gcc_assert (!lto_stream_as_builtin_p (result));
@@ -2500,6 +2511,21 @@  lto_read_tree (struct lto_input_block *ib, struct data_in *data_in,
 }
 
 
+/* LTO streamer hook for reading GIMPLE trees.  IB and DATA_IN are as in
+   lto_read_tree.  EXPR is the tree was materialized by lto_read_tree and
+   needs GIMPLE specific data to be filled in.  */
+
+void
+lto_streamer_read_tree (struct lto_input_block *ib, struct data_in *data_in,
+			tree expr)
+{
+  if (DECL_P (expr)
+      && TREE_CODE (expr) != FUNCTION_DECL
+      && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
+    DECL_INITIAL (expr) = lto_input_tree (ib, data_in);
+}
+
+
 /* Read and INTEGER_CST node from input block IB using the per-file
    context in DATA_IN.  */
 
@@ -2581,17 +2607,11 @@  lto_input_tree (struct lto_input_block *ib, struct data_in *data_in)
 /* Initialization for the LTO reader.  */
 
 void
-lto_init_reader (void)
+lto_reader_init (void)
 {
   lto_streamer_init ();
-
-  memset (&lto_stats, 0, sizeof (lto_stats));
-  bitmap_obstack_initialize (NULL);
-
   file_name_hash_table = htab_create (37, hash_string_slot_node,
 				      eq_string_slot_node, free);
-
-  gimple_register_cfg_hooks ();
 }
 
 
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 66b1ac6..02ac958 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -591,6 +591,9 @@  pack_value_fields (struct bitpack_d *bp, tree expr)
 
   if (CODE_CONTAINS_STRUCT (code, TS_TRANSLATION_UNIT_DECL))
     pack_ts_translation_unit_decl_value_fields (bp, expr);
+
+  if (streamer_hooks.pack_value_fields)
+    streamer_hooks.pack_value_fields (bp, expr);
 }
 
 
@@ -754,9 +757,22 @@  lto_output_tree_ref (struct output_block *ob, tree expr)
       break;
 
     default:
-      /* No other node is indexable, so it should have been handled
-	 by lto_output_tree.  */
-      gcc_unreachable ();
+      {
+	/* See if the streamer allows this node to be indexable
+	   like other global declarations.  */
+	if (streamer_hooks.indexable_with_decls_p
+	    && streamer_hooks.indexable_with_decls_p (expr))
+	  {
+	    output_record_start (ob, LTO_global_decl_ref);
+	    lto_output_var_decl_index (ob->decl_state, ob->main_stream, expr);
+	  }
+	else
+	  {
+	    /* No other node is indexable, so it should have been
+	      handled by lto_output_tree.  */
+	    gcc_unreachable ();
+	  }
+      }
     }
 }
 
@@ -865,27 +881,11 @@  lto_output_ts_decl_common_tree_pointers (struct output_block *ob, tree expr,
   lto_output_tree_or_ref (ob, DECL_SIZE (expr), ref_p);
   lto_output_tree_or_ref (ob, DECL_SIZE_UNIT (expr), ref_p);
 
-  if (TREE_CODE (expr) != FUNCTION_DECL
-      && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
-    {
-      tree initial = DECL_INITIAL (expr);
-      if (TREE_CODE (expr) == VAR_DECL
-	  && (TREE_STATIC (expr) || DECL_EXTERNAL (expr))
-	  && initial)
-	{
-	  lto_varpool_encoder_t varpool_encoder = ob->decl_state->varpool_node_encoder;
-	  struct varpool_node *vnode = varpool_get_node (expr);
-	  if (!vnode)
-	    initial = error_mark_node;
-	  else if (!lto_varpool_encoder_encode_initializer_p (varpool_encoder,
-							      vnode))
-	    initial = NULL;
-	}
-    
-      lto_output_tree_or_ref (ob, initial, ref_p);
-    }
+  /* Note, DECL_INITIAL is not handled here.  Since DECL_INITIAL needs
+     special handling in LTO, it must be handled by streamer hooks.  */
 
   lto_output_tree_or_ref (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.  */
@@ -1261,11 +1261,11 @@  lto_output_tree_header (struct output_block *ob, tree expr)
   enum LTO_tags tag;
   enum tree_code code;
 
-  /* We should not see any non-GIMPLE tree nodes here.  */
+  /* We should not see any tree nodes not handled by the streamer.  */
   code = TREE_CODE (expr);
-  if (!lto_is_streamable (expr))
-    internal_error ("tree code %qs is not supported in gimple streams",
-		    tree_code_name[code]);
+  if (!streamer_hooks.is_streamable (expr))
+    internal_error ("tree code %qs is not supported in %s streams",
+		    tree_code_name[code], streamer_hooks.name);
 
   /* The header of a tree node consists of its tag, the size of
      the node, and any other information needed to instantiate
@@ -1294,6 +1294,11 @@  lto_output_tree_header (struct output_block *ob, tree expr)
     output_sleb128 (ob, TREE_VEC_LENGTH (expr));
   else if (CODE_CONTAINS_STRUCT (code, TS_BINFO))
     output_uleb128 (ob, BINFO_N_BASE_BINFOS (expr));
+
+  /* Allow the streamer to write any streamer-specific information
+     needed to instantiate the node when reading.  */
+  if (streamer_hooks.output_tree_header)
+    streamer_hooks.output_tree_header (ob, expr);
 }
 
 
@@ -1355,11 +1360,49 @@  lto_write_tree (struct output_block *ob, tree expr, bool ref_p)
   /* Write all the pointer fields in EXPR.  */
   lto_output_tree_pointers (ob, expr, ref_p);
 
+  /* Call back into the streaming module to see if it needs to write
+     anything that was not written by the common streamer.  */
+  if (streamer_hooks.write_tree)
+    streamer_hooks.write_tree (ob, expr, ref_p);
+
   /* Mark the end of EXPR.  */
   output_zero (ob);
 }
 
 
+/* GIMPLE hook for writing GIMPLE-specific parts of trees.  OB, EXPR
+   and REF_P are as in lto_write_tree.  */
+
+void
+lto_streamer_write_tree (struct output_block *ob, tree expr, bool ref_p)
+{
+  if (DECL_P (expr)
+      && TREE_CODE (expr) != FUNCTION_DECL
+      && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
+    {
+      /* Handle DECL_INITIAL for symbols.  */
+      tree initial = DECL_INITIAL (expr);
+      if (TREE_CODE (expr) == VAR_DECL
+	  && (TREE_STATIC (expr) || DECL_EXTERNAL (expr))
+	  && initial)
+	{
+	  lto_varpool_encoder_t varpool_encoder;
+	  struct varpool_node *vnode;
+
+	  varpool_encoder = ob->decl_state->varpool_node_encoder;
+	  vnode = varpool_get_node (expr);
+	  if (!vnode)
+	    initial = error_mark_node;
+	  else if (!lto_varpool_encoder_encode_initializer_p (varpool_encoder,
+							      vnode))
+	    initial = NULL;
+	}
+
+      lto_output_tree_or_ref (ob, initial, ref_p);
+    }
+}
+
+
 /* Emit the integer constant CST to output block OB.  If REF_P is true,
    CST's type will be emitted as a reference.  */
 
@@ -2188,15 +2231,6 @@  copy_function (struct cgraph_node *node)
 }
 
 
-/* Initialize the LTO writer.  */
-
-static void
-lto_writer_init (void)
-{
-  lto_streamer_init ();
-}
-
-
 /* Main entry point from the pass manager.  */
 
 static void
@@ -2210,7 +2244,8 @@  lto_output (cgraph_node_set set, varpool_node_set vset)
   int i, n_nodes;
   lto_cgraph_encoder_t encoder = lto_get_out_decl_state ()->cgraph_node_encoder;
 
-  lto_writer_init ();
+  /* Initialize the streamer.  */
+  lto_streamer_init ();
 
   n_nodes = lto_cgraph_encoder_size (encoder);
   /* Process only the functions with bodies.  */
diff --git a/gcc/lto-streamer.c b/gcc/lto-streamer.c
index 763ecc5..0608b33 100644
--- a/gcc/lto-streamer.c
+++ b/gcc/lto-streamer.c
@@ -37,6 +37,9 @@  along with GCC; see the file COPYING3.  If not see
 /* Statistics gathered during LTO, WPA and LTRANS.  */
 struct lto_stats_d lto_stats;
 
+/* Streamer hooks.  */
+struct streamer_hooks streamer_hooks;
+
 /* LTO uses bitmaps with different life-times.  So use a seperate
    obstack for all LTO bitmaps.  */
 static bitmap_obstack lto_obstack;
@@ -568,7 +571,7 @@  lto_streamer_cache_create (void)
   /* Load all the well-known tree nodes that are always created by
      the compiler on startup.  This prevents writing them out
      unnecessarily.  */
-  lto_preload_common_nodes (cache);
+  streamer_hooks.preload_common_nodes (cache);
 
   return cache;
 }
@@ -713,3 +716,52 @@  lto_check_version (int major, int minor)
 		 major, minor,
 		 LTO_major_version, LTO_minor_version);
 }
+
+
+/* Return true if EXPR is a tree node that can be written to disk.  */
+static inline bool
+lto_is_streamable (tree expr)
+{
+  enum tree_code code = TREE_CODE (expr);
+
+  /* Notice that we reject SSA_NAMEs as well.  We only emit the SSA
+     name version in lto_output_tree_ref (see output_ssa_names).  */
+  return !is_lang_specific (expr)
+	 && code != SSA_NAME
+	 && code != CALL_EXPR
+	 && code != LANG_TYPE
+	 && code != MODIFY_EXPR
+	 && code != INIT_EXPR
+	 && code != TARGET_EXPR
+	 && code != BIND_EXPR
+	 && code != WITH_CLEANUP_EXPR
+	 && code != STATEMENT_LIST
+	 && code != OMP_CLAUSE
+	 && code != OPTIMIZATION_NODE
+	 && (code == CASE_LABEL_EXPR
+	     || code == DECL_EXPR
+	     || TREE_CODE_CLASS (code) != tcc_statement);
+}
+
+
+/* Initialize all the streamer hooks used for streaming GIMPLE.  */
+
+void
+lto_streamer_hooks_init (void)
+{
+  streamer_hooks_init ();
+  streamer_hooks.name = "gimple";
+  streamer_hooks.preload_common_nodes = lto_preload_common_nodes;
+  streamer_hooks.is_streamable = lto_is_streamable;
+  streamer_hooks.write_tree = lto_streamer_write_tree;
+  streamer_hooks.read_tree = lto_streamer_read_tree;
+}
+
+
+/* Initialize the current set of streamer hooks.  */
+
+void
+streamer_hooks_init (void)
+{
+  memset (&streamer_hooks, 0, sizeof (streamer_hooks));
+}
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 5f69655..157e5c0 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -33,6 +33,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "gcov-io.h"
 
+/* Forward declarations to avoid including unnecessary headers.  */
+struct output_block;
+struct lto_input_block;
+struct data_in;
+struct bitpack_d;
+
 /* Define when debugging the LTO streamer.  This causes the writer
    to output the numeric value for the memory address of the tree node
    being emitted.  When debugging a problem in the reader, check the
@@ -741,6 +747,86 @@  struct data_in
 };
 
 
+/* Streamer hooks.  These functions do additional processing as
+   needed by the module.  There are two types of callbacks, those that
+   replace the default behavior and those that supplement it.
+
+   Hooks marked [REQ] are required to be set.  Those marked [OPT] may
+   be NULL, if the streamer does not need to implement them.  */
+struct streamer_hooks {
+  /* [REQ] A string identifying this streamer.  */
+  const char *name;
+
+  /* [REQ] Called by lto_streamer_cache_create to instantiate a cache of
+     well-known nodes.  These are tree nodes that are always
+     instantiated by the compiler on startup.  Additionally, these
+     nodes need to be shared.  This function should call
+     lto_streamer_cache_append on every tree node that it wishes to
+     preload in the streamer cache.  This way, the writer will only
+     write out a reference to the tree and the reader will instantiate
+     the tree out of this pre-populated cache.  */
+  void (*preload_common_nodes) (struct lto_streamer_cache_d *);
+
+  /* [REQ] Return true if the given tree is supported by this streamer.  */
+  bool (*is_streamable) (tree);
+
+  /* [OPT] Called by lto_write_tree after writing all the common parts of
+     a tree.  If defined, the callback is in charge of writing all
+     the fields that lto_write_tree did not write out.  Arguments
+     are as in lto_write_tree.
+
+     The following tree fields are not handled by common code:
+
+	DECL_ABSTRACT_ORIGIN
+	DECL_INITIAL
+	DECL_SAVED_TREE
+
+     Callbacks may choose to ignore or handle them.  If handled,
+     the reader should read them in the exact same sequence written
+     by the writer.  */
+  void (*write_tree) (struct output_block *, tree, bool);
+
+  /* [OPT] Called by lto_read_tree after reading all the common parts of
+     a tree.  If defined, the callback is in charge of reading all
+     the fields that lto_read_tree did not read in.  Arguments
+     are as in lto_read_tree.  */
+  void (*read_tree) (struct lto_input_block *, struct data_in *, tree);
+
+  /* [OPT] Called by lto_output_tree_ref to determine if the given tree node
+     should be emitted as a reference to the table of declarations
+     (the same table that holds global declarations).  */
+  bool (*indexable_with_decls_p) (tree);
+
+  /* [OPT] Called by pack_value_fields to store any non-pointer fields
+     in the tree structure.  The arguments are as in pack_value_fields.  */
+  void (*pack_value_fields) (struct bitpack_d *, tree);
+
+  /* [OPT] Called by unpack_value_fields to retrieve any non-pointer fields
+     in the tree structure.  The arguments are as in unpack_value_fields.  */
+  void (*unpack_value_fields) (struct bitpack_d *, tree);
+
+  /* [OPT] Called by lto_materialize_tree for tree nodes that it does not
+     know how to allocate memory for.  If defined, this hook should
+     return a new tree node of the given code.  The data_in and
+     input_block arguments are passed in case the hook needs to
+     read more data from the stream to allocate the node.
+     If this hook returns NULL, then lto_materialize_tree will attempt
+     to allocate the tree by calling make_node directly.  */
+  tree (*alloc_tree) (enum tree_code, struct lto_input_block *,
+                      struct data_in *);
+
+  /* [OPT] Called by lto_output_tree_header to write any streamer-specific
+     information needed to allocate the tree.  This hook may assume
+     that the basic header data (tree code, etc) has already been
+     written.  It should only write any extra data needed to allocate
+     the node (e.g., in the case of CALL_EXPR, this hook would write
+     the number of arguments to the CALL_EXPR).  */
+  void (*output_tree_header) (struct output_block *, tree);
+};
+
+/* Streamer hooks.  */
+extern struct streamer_hooks streamer_hooks;
+
 /* In lto-section-in.c  */
 extern struct lto_input_block * lto_create_simple_input_block (
 			       struct lto_file_decl_data *,
@@ -851,17 +937,20 @@  extern intptr_t lto_orig_address_get (tree);
 extern void lto_orig_address_remove (tree);
 #endif
 extern void lto_check_version (int, int);
-
+extern void lto_streamer_hooks_init (void);
+extern void lto_streamer_write_tree (struct output_block *, tree, bool);
+extern void lto_streamer_read_tree (struct lto_input_block *,
+				     struct data_in *, tree);
+extern void streamer_hooks_init (void);
 
 /* In lto-streamer-in.c */
 extern void lto_input_cgraph (struct lto_file_decl_data *, const char *);
-extern void lto_init_reader (void);
+extern void lto_reader_init (void);
 extern tree lto_input_tree (struct lto_input_block *, struct data_in *);
 extern void lto_input_function_body (struct lto_file_decl_data *, tree,
 				     const char *);
 extern void lto_input_constructors_and_inits (struct lto_file_decl_data *,
 					      const char *);
-extern void lto_init_reader (void);
 extern struct data_in *lto_data_in_create (struct lto_file_decl_data *,
 				    const char *, unsigned,
 				    VEC(ld_plugin_symbol_resolution_t,heap) *);
@@ -1063,31 +1152,6 @@  lto_stream_as_builtin_p (tree expr)
 	      || DECL_BUILT_IN_CLASS (expr) == BUILT_IN_MD));
 }
 
-/* Return true if EXPR is a tree node that can be written to disk.  */
-static inline bool
-lto_is_streamable (tree expr)
-{
-  enum tree_code code = TREE_CODE (expr);
-
-  /* Notice that we reject SSA_NAMEs as well.  We only emit the SSA
-     name version in lto_output_tree_ref (see output_ssa_names).  */
-  return !is_lang_specific (expr)
-	 && code != SSA_NAME
-	 && code != CALL_EXPR
-	 && code != LANG_TYPE
-	 && code != MODIFY_EXPR
-	 && code != INIT_EXPR
-	 && code != TARGET_EXPR
-	 && code != BIND_EXPR
-	 && code != WITH_CLEANUP_EXPR
-	 && code != STATEMENT_LIST
-	 && code != OMP_CLAUSE
-	 && code != OPTIMIZATION_NODE
-	 && (code == CASE_LABEL_EXPR
-	     || code == DECL_EXPR
-	     || TREE_CODE_CLASS (code) != tcc_statement);
-}
-
 DEFINE_DECL_STREAM_FUNCS (TYPE, type)
 DEFINE_DECL_STREAM_FUNCS (FIELD_DECL, field_decl)
 DEFINE_DECL_STREAM_FUNCS (FN_DECL, fn_decl)
diff --git a/gcc/lto/lto.c b/gcc/lto/lto.c
index 70d5bde..74dfecd 100644
--- a/gcc/lto/lto.c
+++ b/gcc/lto/lto.c
@@ -2732,6 +2732,21 @@  lto_process_name (void)
     setproctitle ("lto1-ltrans");
 }
 
+
+/* Initialize the LTO front end.  */
+
+static void
+lto_init (void)
+{
+  lto_process_name ();
+  lto_streamer_hooks_init ();
+  lto_reader_init ();
+  memset (&lto_stats, 0, sizeof (lto_stats));
+  bitmap_obstack_initialize (NULL);
+  gimple_register_cfg_hooks ();
+}
+
+
 /* Main entry point for the GIMPLE front end.  This front end has
    three main personalities:
 
@@ -2755,9 +2770,8 @@  lto_process_name (void)
 void
 lto_main (void)
 {
-  lto_process_name ();
-
-  lto_init_reader ();
+  /* Initialize the LTO front end.  */
+  lto_init ();
 
   /* Read all the symbols and call graph from all the files in the
      command line.  */