Re: [lto]: merge from trunk

Top Page

Reply to this message
Author: Bill Maddox
Date:  
To: Kenneth Zadeck, Diego Novillo
CC: Ollie Wild, Hubicha, Jan, gcc-patches, Kai Tietz
Subject: Re: [lto]: merge from trunk
On Mon, Jul 14, 2008 at 12:06 AM, Bill Maddox <maddox@???> wrote:
> I've done some analysis of this bug. The failure appears to be caused
> by the following lines in builtings.c (std_canonical_list_type):
>
> wtype = va_list_type_node;
> htype = type;
> /* ... */
> if (TYPE_MAIN_VARIANT (wtype) == TYPE_MAIN_VARIANT (htype))
> return va_list_type_node;
>
> Here, we are expecting the "well-known" type va_list_type_node,
> stashed away in a global variable during compiler initialization by
> both cc1 and lto1, to share substructure (the TYPE_MAIN_VARIANT) with
> that of a type that has been internalized from a stream.


Here is a patch that corrects this problem. It reorganizes the preloading of
well-known nodes in the streamer, and preloads the TYPE_MAIN_VARIANT of
well-known type nodes as well. It is a step on the way to proper sharing of
all of the node graph subsidiary to the well-known nodes.
The patch is mostly due to Diego, however, he was unable to complete it
before travelling, and handed it off to me to finish debugging. This should
correct the many assertion failures we've been seeing in stabilize_va_list
since a recent merge with mainline.

gcc:

* tree-pretty-print.c (dump_generic_node): Suppress display of
structure fields with TDF_SLIM flag.
fields even if structure is named.
(print_struct_decl): Test correctly for direct self-reference
or self-reference via a pointer type.
* tree.c (free_lang_specifics): Set ptrdiff_type_node to
integer_type_node, independent of the front-end.
* lto-function-out.c (output_tree): Improve streamer tracing.
Factor out code for keeping hash table of streamed nodes for
sharing purposes, moved to new function get_ref_idx_for.
* lto-function-in.c (input_tree_operand): Improve streamer tracing.
* lto-tree-out.h (struct output_block): Remove field next_main_index.
* tree-flow.h (tree_node_can_be_shared): Declare.
* tree-cfg.c (tree_node_can_be_shared): Make non-static.
* lto-section-out.c: Remove unnecessary header file inclusions.
(get_ref_idx_for): New function.
(preload_common_node): New version can be shared by reader and writer,
also preloads TYPE_MAIN_VARIANT field of preloaded types.
(preload_common_nodes): Use new preload_common_node. New tracing code.
* lto-section-out.h (preload_common_node, get_ref

gcc/lto:

* lto.c: Reorder file inclusions. Include lto-section-out.h.
(preload_common_nodes): Use new shared preload_common_node.
Improved streamer tracing.
* Make-lang.in: Make lto.o depend on lto-section-out.h.

--Bill
Index: gcc/tree-pretty-print.c
===================================================================
--- gcc/tree-pretty-print.c    (revision 138093)
+++ gcc/tree-pretty-print.c    (working copy)
@@ -752,7 +752,12 @@ dump_generic_node (pretty_printer *buffe

if (TYPE_NAME (node))
     dump_generic_node (buffer, TYPE_NAME (node), spc, flags, false);
- else
+    else if (!(flags & TDF_SLIM))
+     /* FIXME: If we eliminate the 'else' above and attempt
+     to show the fields for named types, we may get stuck
+     following a cycle of pointers to structs. The alleged
+     self-reference check in print_struct_decl will not detect
+     cycles involving more than one pointer or struct type. */
     print_struct_decl (buffer, node, spc, flags);
break;
}
@@ -2364,8 +2369,8 @@ print_struct_decl (pretty_printer *buffe
     Maybe this could be solved by looking at the scope in which the
     structure was declared. */
    if (TREE_TYPE (tmp) != node
-     || (TREE_CODE (TREE_TYPE (tmp)) == POINTER_TYPE
-        && TREE_TYPE (TREE_TYPE (tmp)) != node))
+     && (TREE_CODE (TREE_TYPE (tmp)) != POINTER_TYPE
+        || TREE_TYPE (TREE_TYPE (tmp)) != node))
     {
     print_declaration (buffer, tmp, spc+2, flags);
     pp_newline (buffer);
Index: gcc/tree.c
===================================================================
--- gcc/tree.c    (revision 138093)
+++ gcc/tree.c    (working copy)
@@ -4004,6 +4004,7 @@ free_lang_specifics (void)
{
htab_traverse (uid2type_map, reset_type_lang_specific, NULL);
htab_traverse (decl_for_uid_map, reset_lang_specific, NULL);
+ ptrdiff_type_node = integer_type_node;
}

/* Return nonzero if IDENT is a valid name for attribute ATTR,
Index: gcc/lto-function-out.c
===================================================================
--- gcc/lto-function-out.c    (revision 138093)
+++ gcc/lto-function-out.c    (working copy)
@@ -2953,7 +2953,8 @@ output_tree (struct output_block *ob, tr
}

#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "%p : ", expr);
+ fprintf (stderr, "Emitting tree %p : [%s] ", (void *) expr,
+     tree_code_name[TREE_CODE (expr)]);
print_generic_expr (stderr, expr, 0);
fprintf (stderr, "\n");
#endif
@@ -2961,47 +2962,35 @@ output_tree (struct output_block *ob, tr
if (TYPE_P (expr) || DECL_P (expr) || TREE_CODE (expr) == TREE_BINFO)
{
unsigned int global_index;
- struct lto_decl_slot *new_slot;

/* If we've already pickled this node, emit a reference.
Otherwise, assign an index for the node we are about to emit. */
-
- d_slot.t = expr;
- slot = htab_find_slot (ob->main_hash_table, &d_slot, INSERT);
- if (*slot != NULL)
+ if (get_ref_idx_for (expr, ob->main_hash_table, NULL, &global_index))
{
- struct lto_decl_slot *old_slot = (struct lto_decl_slot *)*slot;
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "%p -> OLD %d\n", expr, old_slot->slot_num);
+ fprintf (stderr, "%p -> OLD %d\n", (void *) expr, global_index);
#endif
- output_global_record_start (ob, NULL, NULL, LTO_tree_pickle_reference);
- output_uleb128 (ob, old_slot->slot_num);
+ output_global_record_start (ob, NULL, NULL,
+                 LTO_tree_pickle_reference);
+ output_uleb128 (ob, global_index);
LTO_DEBUG_UNDENT ();
return;
}
-
- global_index = ob->next_main_index++;
- new_slot = (struct lto_decl_slot *) xmalloc (sizeof (struct lto_decl_slot));
- new_slot->t = expr;
- new_slot->slot_num = global_index;
- *slot = new_slot;
-
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "%p -> NEW %d\n", expr, new_slot->slot_num);
+ fprintf (stderr, "%p -> NEW %d\n", (void *) expr, global_index);
#endif
}
else
{
/* We don't share new instances of other classes of tree nodes,
but we always want to share the preloaded "well-known" nodes. */
-
d_slot.t = expr;
slot = htab_find_slot (ob->main_hash_table, &d_slot, NO_INSERT);
if (slot != NULL)
{
struct lto_decl_slot *old_slot = (struct lto_decl_slot *)*slot;
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "%p -> OLD %d\n", expr, old_slot->slot_num);
+ fprintf (stderr, "%p -> OLD %d\n", (void *) expr, old_slot->slot_num);
#endif
output_global_record_start (ob, NULL, NULL, LTO_tree_pickle_reference);
output_uleb128 (ob, old_slot->slot_num);
Index: gcc/lto-function-in.c
===================================================================
--- gcc/lto-function-in.c    (revision 138093)
+++ gcc/lto-function-in.c    (working copy)
@@ -2970,22 +2970,29 @@ input_tree_operand (struct lto_input_blo
unsigned int index = lto_input_uleb128 (ib);
gcc_assert (data_in->globals_index);
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "index 0x%x length 0x%x\n", index, VEC_length (tree, data_in->globals_index));
+ fprintf (stderr, "Found LTO_tree_pickle_reference index %u length %u\n",
+     index, VEC_length (tree, data_in->globals_index));
#endif
gcc_assert (index < VEC_length (tree, data_in->globals_index));
result = VEC_index (tree, data_in->globals_index, index);
gcc_assert (result);
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "0x%x -> REF %p\n", index, result);
+ fprintf (stderr, "%u -> REF 0x%p\n", index, (void *) result);
+ /* We cannot print the node, as we may be processing a recursive
+     reference to the node before we have finished reading all of
+     its fields. */
#endif
LTO_DEBUG_UNDENT();
return result;
}
-
- code = tag_to_expr[tag];

+ code = tag_to_expr[tag];
gcc_assert (code);

+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "Re-building tree code %s from stream\n", tree_code_name[code]);
+#endif
+
if (TREE_CODE_CLASS (code) != tcc_type
&& TREE_CODE_CLASS (code) != tcc_declaration
&& code != TREE_BINFO)
@@ -3567,10 +3574,11 @@ input_tree_operand (struct lto_input_blo
recompute_tree_invariant_for_addr_expr (result);
}

-#ifdef GLOBAL_STREAMER_DEBUG
+#ifdef GLOBAL_STREAMER_TRACE
{
unsigned int next_index = VEC_length (tree, data_in->globals_index);
- fprintf (stderr, "0x%x -> NEW %p : ", next_index-1, result);
+ fprintf (stderr, "%u -> NEW %p: [%s] ", next_index - 1, (void *) result,
+     tree_code_name[TREE_CODE (result)]);
print_generic_expr (stderr, result, 0);
fprintf (stderr, "\n");
}
Index: gcc/lto-tree-out.h
===================================================================
--- gcc/lto-tree-out.h    (revision 138093)
+++ gcc/lto-tree-out.h    (working copy)
@@ -107,9 +107,6 @@ struct output_block

/* Map global decls and types to indices in the main stream. */
htab_t main_hash_table;
-
- /* Index in main stream of next node. */
- unsigned int next_main_index;
};

struct output_block *create_output_block (enum lto_section_type);
Index: gcc/lto/lto.c
===================================================================
--- gcc/lto/lto.c    (revision 138093)
+++ gcc/lto/lto.c    (working copy)
@@ -29,16 +29,16 @@ Boston, MA 02110-1301, USA. */
#include "tm.h"
#include "cgraph.h"
#include "ggc.h"
+#include "tree-ssa-operands.h"
+#include "tree-pass.h"
+#include "langhooks.h"
#include "lto.h"
#include "lto-tree.h"
-#include "tree-ssa-operands.h" /* For init_ssa_operands. */
-#include "langhooks.h"
#include "lto-section.h"
#include "lto-section-in.h"
+#include "lto-section-out.h"
#include "lto-tree-in.h"
-#include "lto-tags.h"         /* For LTO_tree_tag_names. */
-#include "tree-pass.h"
-
+#include "lto-tags.h"

/* Read the constructors and inits. */

@@ -104,13 +104,14 @@ lto_materialize_function (struct cgraph_
cgraph_mark_reachable_node (cgraph_node (decl));
}

-/* ### */
+
/* Initialize the globals vector with pointers to well-known trees. */

static void
preload_common_nodes (struct data_in *data_in)
{
unsigned i;
+ htab_t index_table;

/* The global tree for the main identifier is filled in by language-specific
front-end initialization that is not run in the LTO back-end. It appears
@@ -119,28 +120,38 @@ preload_common_nodes (struct data_in *da
if (!main_identifier_node)
main_identifier_node = get_identifier ("main");

+ ptrdiff_type_node = integer_type_node;
+
+ index_table = htab_create (37, lto_hash_global_slot_node,
+             lto_eq_global_slot_node, free);
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloading all global_trees[]\n");
+#endif
+
for (i = 0; i < TI_MAX; i++)
- {
+ preload_common_node (global_trees[i], index_table, &data_in->globals_index,
+             NULL);
+
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "preloaded 0x%x: ", i);
- print_generic_expr (stderr, global_trees[i], 0);
- fprintf (stderr, "\n");
+ fprintf (stderr, "\n\nPreloaded %u entries in global_trees[]\n", i - 1);
+#endif
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloading all integer_types[]\n");
#endif
- VEC_safe_push (tree, heap, data_in->globals_index, global_trees[i]);
- }

for (i = 0; i < itk_none; i++)
- {
+ preload_common_node (integer_types[i], index_table, &data_in->globals_index,
+             NULL);
+
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "preloaded 0x%x: ", i);
- print_generic_expr (stderr, integer_types[i], 0);
- fprintf (stderr, "\n");
+ fprintf (stderr, "\n\nPreloaded %u entries in integer_types[]\n", i - 1);
#endif
- VEC_safe_push (tree, heap, data_in->globals_index, integer_types[i]);
- }
+
+ htab_delete (index_table);
}

-/* ### */
/* Load in the global vars and all of the types from the main symbol
table. */

Index: gcc/lto/Make-lang.in
===================================================================
--- gcc/lto/Make-lang.in    (revision 138093)
+++ gcc/lto/Make-lang.in    (working copy)
@@ -86,7 +86,8 @@ lto/lto-lang.o: lto/lto-lang.c $(CONFIG_
lto/lto.o: lto/lto.c $(CONFIG_H) $(CGRAPH_H) coretypes.h \
    $(GGC_H) opts.h $(SYSTEM_H) toplev.h $(TM_H) $(LTO_H) langhooks.h \
    lto/lto-tree.h dwarf2out.h tm.h tree-ssa-operands.h \
-    gt-lto-lto.h lto-section.h lto-section-in.h tree-pass.h
+    gt-lto-lto.h lto-section.h lto-section-in.h tree-pass.h \
+    lto-section-out.h
lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
    toplev.h $(LTO_H) $(TM_H)
lto/lto-symtab.o: lto/lto-symtab.c $(CONFIG_H) coretypes.h \
Index: gcc/tree-flow.h
===================================================================
--- gcc/tree-flow.h    (revision 138093)
+++ gcc/tree-flow.h    (working copy)
@@ -795,6 +795,7 @@ extern basic_block move_sese_region_to_f
                 basic_block, tree);
void remove_edge_and_dominated_blocks (edge);
void mark_virtual_ops_in_bb (basic_block);
+bool tree_node_can_be_shared (tree);

/* In tree-cfgcleanup.c */
extern bitmap cfgcleanup_altered_bbs;
Index: gcc/tree-cfg.c
===================================================================
--- gcc/tree-cfg.c    (revision 138093)
+++ gcc/tree-cfg.c    (working copy)
@@ -4280,7 +4280,7 @@ verify_stmt (tree stmt, bool last_in_blo

/* Return true when the T can be shared. */

-static bool
+bool
tree_node_can_be_shared (tree t)
{
if (IS_TYPE_OR_DECL_P (t)
Index: gcc/lto-section-out.c
===================================================================
--- gcc/lto-section-out.c    (revision 138093)
+++ gcc/lto-section-out.c    (working copy)
@@ -47,9 +47,6 @@ along with GCC; see the file COPYING3.
#include "lto-section.h"
#include "lto-section-out.h"
#include "lto-tree-out.h"
-#include <ctype.h>
-#include <strings.h>
-

/* Add FLAG onto the end of BASE. */

@@ -705,40 +702,94 @@ lto_get_out_decl_state (void)
}


-/* Assign an index to tree node T and enter it in the global streamer
- hash table OB->MAIN_HASH_TABLE. */
+/* Return a reference index for tree node T from hash table H. If
+ node T does not already exist in table H, a new entry is created
+ for it. The returned reference is used to identify multiple
+ instances of T on the file stream. This is used in two ways:
+
+ - On the writing side, the first time T is added to H, a new reference
+ index is created for T and T is emitted on the stream. If T
+ needs to be emitted again to the stream, instead of pickling it
+ again, the reference index is emitted.
+
+ - On the reading side, the first time T is read from the stream,
+ it is reconstructed in memory and a new reference index created
+ for T. The reconstructed T is inserted in some array so that
+ when the reference index for T is found in the input stream, it
+ can be used to look up into the array to get the reconstructed T.

-static void
-preload_common_node (struct output_block *ob, tree t)
+ *NEXT_REF_P points to the next available reference index to be
+ handed out. If T is inserted into H, this value is updated.
+
+ The reference index for T is returned in *REF_P. The function
+ returns true if T was already in H. Otherwise, it returns false. */
+
+bool
+get_ref_idx_for (tree t, htab_t h, VEC(tree, heap) **v, unsigned *ref_p)
{
void **slot;
struct lto_decl_slot d_slot;
+ unsigned next_ref_idx = htab_elements (h);
+ bool retval;

+ retval = true;
d_slot.t = t;
- slot = htab_find_slot (ob->main_hash_table, &d_slot, INSERT);
-
- /* If well-known trees are not unique, we don't create duplicate entries. */
+ slot = htab_find_slot (h, &d_slot, INSERT);
if (*slot == NULL)
{
- struct lto_decl_slot *new_slot
-    = (struct lto_decl_slot *) xmalloc (sizeof (struct lto_decl_slot));
- unsigned index = ob->next_main_index++;
+ struct lto_decl_slot *new_slot = XNEW (struct lto_decl_slot);
new_slot->t = t;
- new_slot->slot_num = index;
+ new_slot->slot_num = next_ref_idx;
*slot = new_slot;
+
+ if (v)
+    {
+     gcc_assert (next_ref_idx == VEC_length (tree, *v));
+     VEC_safe_push (tree, heap, *v, t);
+    }
+
+ /* Indicate that the item was not in the table before by
+     returning false. */
+ retval = false;
+ }
+
+ if (ref_p)
+ *ref_p = ((struct lto_decl_slot *) *slot)->slot_num;
+
+ return retval;
+}
+
+
+/* Assign an index to tree node T and enter it in the global streamer
+ hash table OB->MAIN_HASH_TABLE. */
+
+void
+preload_common_node (tree t, htab_t h, VEC(tree, heap) **v, unsigned *ref_p)
+{
+ /* Skip empty slots. Note that we will be in trouble if
+ the empty slots do not match on both the writer and reader side. */
+ if (!t) return;
+
#ifdef GLOBAL_STREAMER_TRACE
- fprintf (stderr, "preloaded 0x%x: ", index);
- print_generic_expr (stderr, t, 0);
- fprintf (stderr, "\n");
+ fprintf (stderr, "Preloading common node: [%s] ",
+     tree_code_name[TREE_CODE (t)]);
+ print_generic_expr (stderr, t, 0);
+ fprintf (stderr, "\n");
#endif
+
+ if (get_ref_idx_for (t, h, v, ref_p))
+ return;
+
+ /* FIXME: In principle, we should perform a walk over all nodes reachable
+ from each preloaded node. This is going to be a lot of work. At present,
+ we catch the case that was causing test failures. A small step. */
+ if (tree_node_can_be_shared (t))
+ {
+ if (TYPE_P (t))
+    {
+     get_ref_idx_for (TYPE_MAIN_VARIANT (t), h, v, ref_p);
+    }
}
- else
- /* Skip the index, which will leave an unused slot in the
- globals vector in the reader. Otherwise, the reader
- initialization must perform a similar duplicate-removal
- process to reconstruct a valid vector. NOTE: This isn't
- difficult. Perhaps we should just do it. */
- ob->next_main_index++;
}


@@ -760,11 +811,29 @@ preload_common_nodes (struct output_bloc
gcc_assert (strcmp (main_name, "main") == 0);
}

+ gcc_assert (ptrdiff_type_node == integer_type_node);
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloading all global_trees[]\n");
+#endif
+
for (i = 0; i < TI_MAX; i++)
- preload_common_node (ob, global_trees[i]);
+ preload_common_node (global_trees[i], ob->main_hash_table, NULL, NULL);
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloaded %u entries in global_trees[]\n", i - 1);
+#endif
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloading all integer_types[]\n");
+#endif

for (i = 0; i < itk_none; i++)
- preload_common_node (ob, integer_types[i]);
+ preload_common_node (integer_types[i], ob->main_hash_table, NULL, NULL);
+
+#ifdef GLOBAL_STREAMER_TRACE
+ fprintf (stderr, "\n\nPreloaded %u entries in integer_types[]\n", i - 1);
+#endif
}


@@ -854,7 +923,6 @@ produce_asm_for_decls (void)
ob->global = true;
ob->main_hash_table = htab_create (37, lto_hash_global_slot_node,
                 lto_eq_global_slot_node, free);
- ob->next_main_index = 0;

/* Assign reference indices for predefined trees. These need not be
serialized. */
Index: gcc/lto-section-out.h
===================================================================
--- gcc/lto-section-out.h    (revision 138093)
+++ gcc/lto-section-out.h    (working copy)
@@ -175,4 +175,7 @@ struct lto_out_decl_state *lto_get_out_d

bool gate_lto_out (void);

+void preload_common_node (tree, htab_t, VEC(tree, heap) **, unsigned *);
+bool get_ref_idx_for (tree, htab_t, VEC(tree, heap) **, unsigned *);
+
#endif /* GCC_LTO_SECTION_OUT_H */