The following fixes a GC issue I run into when doing 
prune_unused_types_prune early.  The issue is that the DIE struct
has a chain_circular marked field (die_sib) which cannot tolerate
spurious extra entries from old removed entries into the circular
chain.  Otherwise we fail to properly mark parts of the chain.
Those stray entries are kept live referenced from TYPE_SYMTAB_DIE.

So the following patch that makes sure to clear ->die_sib for
nodes we remove.  (these DIEs remaining in TYPE_SYMTAB_DIE also
means we may end up re-using them which is probably not what we
want ... in the original LTO experiment I had a ->removed flag
in the DIE struct and removed DIEs from the cache at cache lookup
time if I hit a removed DIE)

Bootstrapped and tested on x86_64-unknown-linux-gnu, gdb tested there
as well.

Ok for trunk?

Thanks,
Richard.

2015-08-26  Richard Biener  <rguent...@suse.de>

        * dwarf2out.c (remove_child_with_prev): Clear child->die_sib.
        (replace_child): Likewise.
        (remove_child_TAG): Adjust.
        (move_marked_base_types): Likewise.
        (prune_unused_types_prune): Clear die_sib of removed children.

Index: trunk/gcc/dwarf2out.c
===================================================================
--- trunk.orig/gcc/dwarf2out.c  2015-08-26 09:30:54.679185817 +0200
+++ trunk/gcc/dwarf2out.c       2015-08-25 16:54:09.150506037 +0200
@@ -4827,6 +4827,7 @@ remove_child_with_prev (dw_die_ref child
     prev->die_sib = child->die_sib;
   if (child->die_parent->die_child == child)
     child->die_parent->die_child = prev;
+  child->die_sib = NULL;
 }
 
 /* Replace OLD_CHILD with NEW_CHILD.  PREV must have the property that
@@ -4853,6 +4854,7 @@ replace_child (dw_die_ref old_child, dw_
     }
   if (old_child->die_parent->die_child == old_child)
     old_child->die_parent->die_child = new_child;
+  old_child->die_sib = NULL;
 }
 
 /* Move all children from OLD_PARENT to NEW_PARENT.  */
@@ -4883,9 +4885,9 @@ remove_child_TAG (dw_die_ref die, enum d
        remove_child_with_prev (c, prev);
        c->die_parent = NULL;
        /* Might have removed every child.  */
-       if (c == c->die_sib)
+       if (die->die_child == NULL)
          return;
-       c = c->die_sib;
+       c = prev->die_sib;
       }
   } while (c != die->die_child);
 }
@@ -24565,8 +24590,8 @@ prune_unused_types_prune (dw_die_ref die
 
   c = die->die_child;
   do {
-    dw_die_ref prev = c;
-    for (c = c->die_sib; ! c->die_mark; c = c->die_sib)
+    dw_die_ref prev = c, next;
+    for (c = c->die_sib; ! c->die_mark; c = next)
       if (c == die->die_child)
        {
          /* No marked children between 'prev' and the end of the list.  */
@@ -24578,8 +24603,14 @@ prune_unused_types_prune (dw_die_ref die
              prev->die_sib = c->die_sib;
              die->die_child = prev;
            }
+         c->die_sib = NULL;
          return;
        }
+      else
+       {
+         next = c->die_sib;
+         c->die_sib = NULL;
+       }
 
     if (c != prev->die_sib)
       prev->die_sib = c;
@@ -24824,8 +24855,8 @@ move_marked_base_types (void)
          remove_child_with_prev (c, prev);
          /* As base types got marked, there must be at least
             one node other than DW_TAG_base_type.  */
-         gcc_assert (c != c->die_sib);
-         c = c->die_sib;
+         gcc_assert (die->die_child != NULL);
+         c = prev->die_sib;
        }
     }
   while (c != die->die_child);

Reply via email to