On Wed, 1 Jun 2011, Diego Novillo wrote: > On Wed, Jun 1, 2011 at 08:07, Richard Guenther <rguent...@suse.de> wrote: > > >> 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) > >> + gimple_streamer_hooks_init (); > > > > Ugh. Isn't there a better entry for this? Are you going to add > > > > if (flag_pph) > > init_hooks_some_other_way (); > > > > here? It looks it rather belongs to opts.c or toplev.c if the hooks > > are really initialized dependent on compiler flags. > > Not at all, this is for gimple, specifically. The front end > initializes hooks in its own way. The problem here is that the gimple > hooks are needed by the middle end. If we initialize gimple hooks too > early, the FE will override them. So we need to initialize them after > the front end is done (hence the location for this call). > > I'm happy to move this somewhere else, but it needs to happen right > before the middle end starts calling LTO pickling routines. > > > > >> /* 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 88966f2..801fe6f 100644 > >> --- a/gcc/lto-streamer-in.c > >> +++ b/gcc/lto-streamer-in.c > >> @@ -1833,6 +1833,7 @@ static void > >> unpack_value_fields (struct bitpack_d *bp, tree expr) > >> { > >> enum tree_code code; > >> + lto_streamer_hooks *h = streamer_hooks (); > > > > A function to access a global ... we have lang_hooks and targetm, > > so please simply use streamer_hooks as a variable. > > streamer_hooks ()->preload_common_nodes (cache) looks super-ugly. > > I did not want to add yet another global. I don't feel too strong > about this one, given the presence of lang_hooks and targetm. So, you > prefer the direct global access?
Yes, I see no benefit of using a global function to get access to the address of a global variable. > >> @@ -1864,26 +1865,11 @@ unpack_value_fields (struct bitpack_d *bp, tree > >> expr) > >> if (CODE_CONTAINS_STRUCT (code, TS_BLOCK)) > >> unpack_ts_block_value_fields (bp, expr); > >> > >> - if (CODE_CONTAINS_STRUCT (code, TS_SSA_NAME)) > >> - { > >> - /* We only stream the version number of SSA names. */ > >> - gcc_unreachable (); > >> - } > >> - > >> - if (CODE_CONTAINS_STRUCT (code, TS_STATEMENT_LIST)) > >> - { > >> - /* This is only used by GENERIC. */ > >> - gcc_unreachable (); > >> - } > >> - > >> - if (CODE_CONTAINS_STRUCT (code, TS_OMP_CLAUSE)) > >> - { > >> - /* This is only used by High GIMPLE. */ > >> - gcc_unreachable (); > >> - } > >> - > >> if (CODE_CONTAINS_STRUCT (code, TS_TRANSLATION_UNIT_DECL)) > >> unpack_ts_translation_unit_decl_value_fields (bp, expr); > >> + > >> + if (h->unpack_value_fields) > >> + h->unpack_value_fields (bp, expr); > > > > I suppose the LTO implementation has a gcc_unreachable () for > > the cases we do not handle here? > > Right. This was already superfluous. It's tested already by > lto_is_streamable(). > > > > >> } > >> > >> > >> @@ -1935,8 +1921,17 @@ 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); > >> + lto_streamer_hooks *h = streamer_hooks (); > >> + > >> + /* For all other nodes, see if the streamer knows how to allocate > >> + it. */ > >> + if (h->alloc_tree) > >> + result = h->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 > >> @@ -2031,12 +2026,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); > >> - > > > > Why move those? DECL_INITIAL _is_ in decl_common. > > I needed to move the handling of DECL_INITIAL in the writer. This > forces us to move the handling in the reader. Otherwise, reader and > writer will be out of sync (DECL_INITIAL is now written last). > > > Where do those checks go? Or do we simply lose them? > > They already are in lto_is_streamable. See above. > > >> - if (TREE_CODE (result) == VAR_DECL) > >> - lto_register_var_decl_in_symtab (data_in, result); > >> - else if (TREE_CODE (result) == FUNCTION_DECL && !DECL_BUILT_IN (result)) > >> - lto_register_function_decl_in_symtab (data_in, result); > >> + if (h->register_decls_in_symtab_p) > >> + { > >> + if (TREE_CODE (result) == VAR_DECL) > >> + lto_register_var_decl_in_symtab (data_in, result); > >> + else if (TREE_CODE (result) == FUNCTION_DECL && !DECL_BUILT_IN > >> (result)) > >> + lto_register_function_decl_in_symtab (data_in, result); > >> + } > > > > I would say we should defer symtab registering to uniquify_nodes time, > > when we are sure we completely streamed in the tree we want to register > > (then we don't have to deal with partially read trees in those functions). > > > > Can you work on a preparatory patch for this please? That would get > > rid for the need of this hook and clean up things at the same time. > > That's a much better idea. Will do. Thanks. > > > >> > >> /* end_marker = */ lto_input_1_unsigned (ib); > >> > >> @@ -2682,6 +2659,22 @@ 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 > >> +gimple_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); > > > > What's wrong with doing this in the decl-common path? > > Just that if the writer code moves, the reader needs to move as well > to avoid out-of-sync problems. > > >> @@ -772,9 +758,23 @@ 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 (); > >> + { > >> + lto_streamer_hooks *h = streamer_hooks (); > >> + > >> + /* See if streamer hooks allows this node to be indexable with > >> + VAR_DECLs. */ > > > > with VAR_DECLs? More like "similar to global decls."? > > Changed. > > > > >> + 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? ;) > > > >> + } > >> + else > >> + { > >> + /* No other node is indexable, so it should have been > >> + handled by lto_output_tree. */ > >> + gcc_unreachable (); > >> + } > >> + } > >> } > >> } > >> > >> @@ -883,27 +883,10 @@ 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); > >> - } > >> + /* Do not stream DECL_INITIAL. */ > > > > That's a gross comment ;) We _do_ stream it, but now in a hook. > > Fair enough. > > > I suppose all the streamer functions should lose their lto_ prefix - > > reading them with such comments will get confusing ... > > Yeah, good point. I'll send a rename patch. > > >> /* We should not see any non-GIMPLE tree nodes here. */ > >> 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 (h->is_streamable && !h->is_streamable (expr)) > >> + internal_error ("tree code %qs is not supported in %s streams", > >> + h->name, tree_code_name[code]); > > > > I'd say this hook should always exist. > > Sure. > > >> /* The header of a tree node consists of its tag, the size of > >> the node, and any other information needed to instantiate > >> EXPR on the reading side (such as the number of slots in > >> variable sized nodes). */ > >> tag = lto_tree_code_to_tag (code); > >> + gcc_assert ((unsigned) tag < (unsigned) LTO_NUM_TAGS); > > > > Doesn't Honzas streaming this as enum already assert this? > > Yeah. I had removed this before testing, but I sent out the original patch. > > >> +/* GIMPLE hook for writing GIMPLE-specific parts of trees. OB, EXPR > >> + and REF_P are as in lto_write_tree. */ > >> + > >> +void > >> +gimple_streamer_write_tree (struct output_block *ob, tree expr, bool > >> ref_p) > >> +{ > >> + if (TREE_CODE (expr) == FUNCTION_DECL) > >> + { > >> + /* DECL_SAVED_TREE holds the GENERIC representation for DECL. > >> + At this point, it should not exist. Either because it was > >> + converted to gimple or because DECL didn't have a GENERIC > >> + representation in this TU. */ > >> + gcc_assert (DECL_SAVED_TREE (expr) == NULL_TREE); > >> + } > > > > I think we can drop this check. > > Done. > > >> @@ -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? Yes, I think this isn't the time to merge this piece. Ah, I think I get it - we don't stream integer constants as trees. 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) - 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, or only handle all pre-loaded nodes that way. > >> @@ -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. > >> + > >> + > >> +/* 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); > >> +} > > > > This change (with the removal of the cases with the unreachable()s) > > could be made separately (directly calling it instead of hooking it). > > I still need to hook this because pph_is_streamable is much more lenient. Of course, you can do that in the hookization patch. The above is just to make that smaller. > > > >> + > >> +/* 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? Richard.