Blob Blame History Raw
2006-12-08  Jakub Jelinek  <jakub@redhat.com>

	* g++.dg/debug/vartrack1.C: New test.

2006-09-11  Alexandre Oliva  <aoliva@redhat.com>

	PR target/28672
	* var-tracking.c (dump_dataflow_set): Start dumping at
	register zero.
	(clobber_variable_part): Kill only the variable part in
	registers holding it, leaving other variables alone.

2006-08-09  Alexandre Oliva  <aoliva@redhat.com>

	* var-tracking.c (enum micro_operation_type): Add MO_COPY.
	(var_debug_decl): New function.
	(var_reg_set): Follow debug decl link.  Add location even if
	reg is already known to hold some other variable.
	(var_mem_set): Follow debug decl link.
	(var_reg_delete_and_set, var_mem_delete_and_set): Follow debug
	decl link.  Delete other known locations of the variable part
	if requested.
	(var_reg_delete, var_mem_delete): Delete other known locations
	of the variable part if requested.
	(same_variable_part_p): New function.
	(add_stores): Select MO_COPY when appropriate.
	(vt_initialize): Handle it.
	(compute_bb_dataflow, emit_notes_in_bb): Likewise.  Delete
	known locations for MO_SET and MO_CLOBBER.
	(find_variable_location_part): New function.
	(set_variable_part, delete_variable_part): Use it.
	(clobber_variable_part): New function.

--- gcc/var-tracking.c.orig	2006-12-08 02:14:36.000000000 -0200
+++ gcc/var-tracking.c	2006-12-08 02:14:51.000000000 -0200
@@ -112,6 +112,8 @@ enum micro_operation_type
   MO_USE_NO_VAR,/* Use location which is not associated with a variable
 		   or the variable is not trackable.  */
   MO_SET,	/* Set location.  */
+  MO_COPY,	/* Copy the same portion of a variable from one
+		   loation to another.  */
   MO_CLOBBER,	/* Clobber location.  */
   MO_CALL,	/* Call insn.  */
   MO_ADJUST	/* Adjust stack pointer.  */
@@ -293,13 +295,14 @@ static void vars_clear (htab_t);
 static variable unshare_variable (dataflow_set *set, variable var);
 static int vars_copy_1 (void **, void *);
 static void vars_copy (htab_t, htab_t);
+static tree var_debug_decl (tree);
 static void var_reg_set (dataflow_set *, rtx);
-static void var_reg_delete_and_set (dataflow_set *, rtx);
-static void var_reg_delete (dataflow_set *, rtx);
+static void var_reg_delete_and_set (dataflow_set *, rtx, bool);
+static void var_reg_delete (dataflow_set *, rtx, bool);
 static void var_regno_delete (dataflow_set *, int);
 static void var_mem_set (dataflow_set *, rtx);
-static void var_mem_delete_and_set (dataflow_set *, rtx);
-static void var_mem_delete (dataflow_set *, rtx);
+static void var_mem_delete_and_set (dataflow_set *, rtx, bool);
+static void var_mem_delete (dataflow_set *, rtx, bool);
 
 static void dataflow_set_init (dataflow_set *, int);
 static void dataflow_set_clear (dataflow_set *);
@@ -316,6 +319,7 @@ static void dataflow_set_destroy (datafl
 
 static bool contains_symbol_ref (rtx);
 static bool track_expr_p (tree);
+static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
 static int count_uses (rtx *, void *);
 static void count_uses_1 (rtx *, void *);
 static void count_stores (rtx, rtx, void *);
@@ -333,6 +337,7 @@ static void dump_dataflow_sets (void);
 
 static void variable_was_changed (variable, htab_t);
 static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
+static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
 static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
 static int emit_note_insn_var_location (void **, void *);
 static void emit_notes_for_changes (rtx, enum emit_note_where);
@@ -794,6 +799,14 @@ vars_copy (htab_t dst, htab_t src)
   htab_traverse (src, vars_copy_1, dst);
 }
 
+/* Map a decl to its main debug decl.  */
+
+static inline tree
+var_debug_decl (tree decl)
+{
+  return decl;
+}
+
 /* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
 
 static void
@@ -801,23 +814,35 @@ var_reg_set (dataflow_set *set, rtx loc)
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
+  attrs node;
+
+  decl = var_debug_decl (decl);
 
-  if (set->regs[REGNO (loc)] == NULL)
+  for (node = set->regs[REGNO (loc)]; node; node = node->next)
+    if (node->decl == decl && node->offset == offset)
+      break;
+  if (!node)
     attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
   set_variable_part (set, loc, decl, offset);
 }
 
-/* Delete current content of register LOC in dataflow set SET
-   and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
+/* Delete current content of register LOC in dataflow set SET and set
+   the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
+   MODIFY is true, any other live copies of the same variable part are
+   also deleted from the dataflow set, otherwise the variable part is
+   assumed to be copied from another location holding the same
+   part.  */
 
 static void
-var_reg_delete_and_set (dataflow_set *set, rtx loc)
+var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify)
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
   attrs node, next;
   attrs *nextp;
 
+  decl = var_debug_decl (decl);
+
   nextp = &set->regs[REGNO (loc)];
   for (node = *nextp; node; node = next)
     {
@@ -834,17 +859,31 @@ var_reg_delete_and_set (dataflow_set *se
 	  nextp = &node->next;
 	}
     }
+  if (modify)
+    clobber_variable_part (set, loc, decl, offset);
   var_reg_set (set, loc);
 }
 
-/* Delete current content of register LOC in dataflow set SET.  */
+/* Delete current content of register LOC in dataflow set SET.  If
+   CLOBBER is true, also delete any other live copies of the same
+   variable part.  */
 
 static void
-var_reg_delete (dataflow_set *set, rtx loc)
+var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
   attrs *reg = &set->regs[REGNO (loc)];
   attrs node, next;
 
+  if (clobber)
+    {
+      tree decl = REG_EXPR (loc);
+      HOST_WIDE_INT offset = REG_OFFSET (loc);
+
+      decl = var_debug_decl (decl);
+
+      clobber_variable_part (set, NULL, decl, offset);
+    }
+
   for (node = *reg; node; node = next)
     {
       next = node->next;
@@ -881,28 +920,44 @@ var_mem_set (dataflow_set *set, rtx loc)
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
+  decl = var_debug_decl (decl);
+
   set_variable_part (set, loc, decl, offset);
 }
 
-/* Delete and set the location part of variable MEM_EXPR (LOC)
-   in dataflow set SET to LOC.
+/* Delete and set the location part of variable MEM_EXPR (LOC) in
+   dataflow set SET to LOC.  If MODIFY is true, any other live copies
+   of the same variable part are also deleted from the dataflow set,
+   otherwise the variable part is assumed to be copied from another
+   location holding the same part.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete_and_set (dataflow_set *set, rtx loc)
+var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify)
 {
+  tree decl = MEM_EXPR (loc);
+  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+
+  decl = var_debug_decl (decl);
+
+  if (modify)
+    clobber_variable_part (set, NULL, decl, offset);
   var_mem_set (set, loc);
 }
 
-/* Delete the location part LOC from dataflow set SET.
+/* Delete the location part LOC from dataflow set SET.  If CLOBBER is
+   true, also delete any other live copies of the same variable part.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete (dataflow_set *set, rtx loc)
+var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
 {
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
 
+  decl = var_debug_decl (decl);
+  if (clobber)
+    clobber_variable_part (set, NULL, decl, offset);
   delete_variable_part (set, loc, decl, offset);
 }
 
@@ -1476,6 +1531,41 @@ track_expr_p (tree expr)
   return 1;
 }
 
+/* Determine whether a given LOC refers to the same variable part as
+   EXPR+OFFSET.  */
+
+static bool
+same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
+{
+  tree expr2;
+  HOST_WIDE_INT offset2;
+
+  if (! DECL_P (expr))
+    return false;
+
+  if (GET_CODE (loc) == REG)
+    {
+      expr2 = REG_EXPR (loc);
+      offset2 = REG_OFFSET (loc);
+    }
+  else if (GET_CODE (loc) == MEM)
+    {
+      expr2 = MEM_EXPR (loc);
+      offset2 = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+    }
+  else
+    return false;
+
+  if (! expr2 || ! DECL_P (expr2))
+    return false;
+
+  expr = var_debug_decl (expr);
+  expr2 = var_debug_decl (expr2);
+
+  return (expr == expr2 && offset == offset2);
+}
+
+
 /* Count uses (register and memory references) LOC which will be tracked.
    INSN is instruction which the LOC is part of.  */
 
@@ -1570,9 +1660,18 @@ add_stores (rtx loc, rtx expr, void *ins
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc)
-		   && track_expr_p (REG_EXPR (loc)))
-		  ? MO_SET : MO_CLOBBER);
+      if (GET_CODE (expr) == CLOBBER
+	  || ! REG_EXPR (loc)
+	  || ! track_expr_p (REG_EXPR (loc)))
+	mo->type = MO_CLOBBER;
+      else if (GET_CODE (expr) == SET
+	       && SET_DEST (expr) == loc
+	       && same_variable_part_p (SET_SRC (expr),
+					REG_EXPR (loc),
+					REG_OFFSET (loc)))
+	mo->type = MO_COPY;
+      else
+	mo->type = MO_SET;
       mo->u.loc = loc;
       mo->insn = NEXT_INSN ((rtx) insn);
     }
@@ -1583,7 +1682,17 @@ add_stores (rtx loc, rtx expr, void *ins
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET;
+      if (GET_CODE (expr) == CLOBBER)
+	mo->type = MO_CLOBBER;
+      else if (GET_CODE (expr) == SET
+	       && SET_DEST (expr) == loc
+	       && same_variable_part_p (SET_SRC (expr),
+					MEM_EXPR (loc),
+					MEM_OFFSET (loc)
+					? INTVAL (MEM_OFFSET (loc)) : 0))
+	mo->type = MO_COPY;
+      else
+	mo->type = MO_SET;
       mo->u.loc = loc;
       mo->insn = NEXT_INSN ((rtx) insn);
     }
@@ -1631,21 +1740,42 @@ compute_bb_dataflow (basic_block bb)
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (GET_CODE (loc) == REG)
-		var_reg_delete_and_set (out, loc);
+		var_reg_delete_and_set (out, loc, true);
 	      else if (GET_CODE (loc) == MEM)
-		var_mem_delete_and_set (out, loc);
+		var_mem_delete_and_set (out, loc, true);
+	    }
+	    break;
+
+	  case MO_COPY:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (GET_CODE (loc) == REG)
+		var_reg_delete_and_set (out, loc, false);
+	      else if (GET_CODE (loc) == MEM)
+		var_mem_delete_and_set (out, loc, false);
 	    }
 	    break;
 
 	  case MO_USE_NO_VAR:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (GET_CODE (loc) == REG)
+		var_reg_delete (out, loc, false);
+	      else if (GET_CODE (loc) == MEM)
+		var_mem_delete (out, loc, false);
+	    }
+	    break;
+
 	  case MO_CLOBBER:
 	    {
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (GET_CODE (loc) == REG)
-		var_reg_delete (out, loc);
+		var_reg_delete (out, loc, true);
 	      else if (GET_CODE (loc) == MEM)
-		var_mem_delete (out, loc);
+		var_mem_delete (out, loc, true);
 	    }
 	    break;
 
@@ -1824,7 +1954,7 @@ dump_dataflow_set (dataflow_set *set)
 
   fprintf (rtl_dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n",
 	   set->stack_adjust);
-  for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
       if (set->regs[i])
 	{
@@ -1905,6 +2035,39 @@ variable_was_changed (variable var, htab
     }
 }
 
+/* Look for the index in VAR->var_part corresponding to OFFSET.
+   Return -1 if not found.  If INSERTION_POINT is non-NULL, the
+   referenced int will be set to the index that the part has or should
+   have, if it should be inserted.  */
+
+static inline int
+find_variable_location_part (variable var, HOST_WIDE_INT offset,
+			     int *insertion_point)
+{
+  int pos, low, high;
+
+  /* Find the location part.  */
+  low = 0;
+  high = var->n_var_parts;
+  while (low != high)
+    {
+      pos = (low + high) / 2;
+      if (var->var_part[pos].offset < offset)
+	low = pos + 1;
+      else
+	high = pos;
+    }
+  pos = low;
+
+  if (insertion_point)
+    *insertion_point = pos;
+
+  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+    return pos;
+
+  return -1;
+}
+
 /* Set the part of variable's location in the dataflow set SET.  The variable
    part is specified by variable's declaration DECL and offset OFFSET and the
    part's location by LOC.  */
@@ -1912,7 +2075,7 @@ variable_was_changed (variable var, htab
 static void
 set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
 {
-  int pos, low, high;
+  int pos;
   location_chain node, next;
   location_chain *nextp;
   variable var;
@@ -1935,22 +2098,13 @@ set_variable_part (dataflow_set *set, rt
     }
   else
     {
+      int inspos = 0;
+
       var = (variable) *slot;
 
-      /* Find the location part.  */
-      low = 0;
-      high = var->n_var_parts;
-      while (low != high)
-	{
-	  pos = (low + high) / 2;
-	  if (var->var_part[pos].offset < offset)
-	    low = pos + 1;
-	  else
-	    high = pos;
-	}
-      pos = low;
+      pos = find_variable_location_part (var, offset, &inspos);
 
-      if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+      if (pos >= 0)
 	{
 	  node = var->var_part[pos].loc_chain;
 
@@ -1985,10 +2139,10 @@ set_variable_part (dataflow_set *set, rt
 	    abort ();
 #endif
 
-	  /* We have to move the elements of array starting at index low to the
-	     next position.  */
-	  for (high = var->n_var_parts; high > low; high--)
-	    var->var_part[high] = var->var_part[high - 1];
+	  /* We have to move the elements of array starting at index
+	     inspos to the next position.  */
+	  for (pos = var->n_var_parts; pos > inspos; pos--)
+	    var->var_part[pos] = var->var_part[pos - 1];
 
 	  var->n_var_parts++;
 	  var->var_part[pos].offset = offset;
@@ -2028,6 +2182,67 @@ set_variable_part (dataflow_set *set, rt
     }
 }
 
+/* Remove all recorded register locations for the given variable part
+   from dataflow set SET, except for those that are identical to loc.
+   The variable part is specified by variable's declaration DECL and
+   offset OFFSET.  */
+
+static void
+clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
+		      HOST_WIDE_INT offset)
+{
+  void **slot;
+
+  if (! decl || ! DECL_P (decl))
+    return;
+
+  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
+				   NO_INSERT);
+  if (slot)
+    {
+      variable var = (variable) *slot;
+      int pos = find_variable_location_part (var, offset, NULL);
+
+      if (pos >= 0)
+	{
+	  location_chain node, next;
+
+	  /* Remove the register locations from the dataflow set.  */
+	  next = var->var_part[pos].loc_chain;
+	  for (node = next; node; node = next)
+	    {
+	      next = node->next;
+	      if (node->loc != loc)
+		{
+		  if (REG_P (node->loc))
+		    {
+		      attrs anode, anext;
+		      attrs *anextp;
+
+		      /* Remove the variable part from the register's
+			 list, but preserve any other variable parts
+			 that might be regarded as live in that same
+			 register.  */
+		      anextp = &set->regs[REGNO (node->loc)];
+		      for (anode = *anextp; anode; anode = anext)
+			{
+			  anext = anode->next;
+			  if (anode->decl == decl
+			      && anode->offset == offset)
+			    {
+			      pool_free (attrs_pool, anode);
+			      *anextp = anext;
+			    }
+			}
+		    }
+
+		  delete_variable_part (set, node->loc, decl, offset);
+		}
+	    }
+	}
+    }
+}
+
 /* Delete the part of variable's location from dataflow set SET.  The variable
    part is specified by variable's declaration DECL and offset OFFSET and the
    part's location by LOC.  */
@@ -2036,7 +2251,6 @@ static void
 delete_variable_part (dataflow_set *set, rtx loc, tree decl,
 		      HOST_WIDE_INT offset)
 {
-  int pos, low, high;
   void **slot;
     
   slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
@@ -2044,21 +2258,9 @@ delete_variable_part (dataflow_set *set,
   if (slot)
     {
       variable var = (variable) *slot;
+      int pos = find_variable_location_part (var, offset, NULL);
 
-      /* Find the location part.  */
-      low = 0;
-      high = var->n_var_parts;
-      while (low != high)
-	{
-	  pos = (low + high) / 2;
-	  if (var->var_part[pos].offset < offset)
-	    low = pos + 1;
-	  else
-	    high = pos;
-	}
-      pos = low;
-
-      if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+      if (pos >= 0)
 	{
 	  location_chain node, next;
 	  location_chain *nextp;
@@ -2124,7 +2326,7 @@ delete_variable_part (dataflow_set *set,
 		}
 	    }
 	  if (changed)
-	      variable_was_changed (var, set->vars);
+	    variable_was_changed (var, set->vars);
 	}
     }
 }
@@ -2418,28 +2620,50 @@ emit_notes_in_bb (basic_block bb)
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (GET_CODE (loc) == REG)
-		var_reg_delete_and_set (&set, loc);
+		var_reg_delete_and_set (&set, loc, true);
+	      else
+		var_mem_delete_and_set (&set, loc, true);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+	    }
+	    break;
+
+	  case MO_COPY:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
+
+	      if (GET_CODE (loc) == REG)
+		var_reg_delete_and_set (&set, loc, false);
 	      else
-		var_mem_delete_and_set (&set, loc);
+		var_mem_delete_and_set (&set, loc, false);
 
 	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
 	    }
 	    break;
 
 	  case MO_USE_NO_VAR:
-	  case MO_CLOBBER:
 	    {
 	      rtx loc = VTI (bb)->mos[i].u.loc;
 
 	      if (GET_CODE (loc) == REG)
-		var_reg_delete (&set, loc);
+		var_reg_delete (&set, loc, false);
 	      else
-		var_mem_delete (&set, loc);
+		var_mem_delete (&set, loc, false);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+	    }
+	    break;
+
+	  case MO_CLOBBER:
+	    {
+	      rtx loc = VTI (bb)->mos[i].u.loc;
 
-	      if (VTI (bb)->mos[i].type == MO_USE_NO_VAR)
-		emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+	      if (GET_CODE (loc) == REG)
+		var_reg_delete (&set, loc, true);
 	      else
-		emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+		var_mem_delete (&set, loc, true);
+
+	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
 	    }
 	    break;
 
@@ -2673,7 +2897,8 @@ vt_initialize (void)
 		{
 		  while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
 		    n1++;
-		  while (n1 < n2 && VTI (bb)->mos[n2].type == MO_SET)
+		  while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
+				     || VTI (bb)->mos[n2].type == MO_COPY))
 		    n2--;
 		  if (n1 < n2)
 		    {
--- gcc/testsuite/g++.dg/debug/vartrack1.C	2004-06-24 14:04:38.000000000 -0400
+++ gcc/testsuite/g++.dg/debug/vartrack1.C	2006-12-08 05:29:41.000000000 -0500
@@ -0,0 +1,99 @@
+// This testcase used to hang the compiler in vt_find_locations.
+// { dg-do compile }
+// { dg-options "-O2 -g" }
+
+struct S
+{
+  int a;
+  S *b, *c, *d;
+};
+
+struct T
+{
+  void f1 (S *x);
+  void f2 (S *x);
+  void f3 (S *x, S *y);
+  S *e;
+};
+
+void
+T::f3 (S *x, S *y)
+{
+  while (x != this->e && (!x || x->a == 1))
+    {
+      if (x == y->c)
+	{
+	  S *w = y->d;
+	  if (w && w->a == 0)
+	    {
+	      w->a = 1;
+	      y->a = 0;
+	      f2 (y);
+	      w = y->d;
+	    }
+	  if (w && (!w->c || w->c->a == 1) && (!w->d || w->d->a == 1))
+	    {
+	      w->a = 0;
+	      x = y;
+	      y = x->b;
+	    }
+	  else
+	    {
+	      if (w && (!w->d || w->d->a == 1))
+		{
+		  if (w->c)
+		    w->c->a = 1;
+		  w->a = 0;
+		  f1 (w);
+		  w = y->d;
+		}
+	      if (w)
+		{
+		  w->a = y->a;
+		  if (w->d)
+		    w->d->a = 1;
+		}
+	      y->a = 1;
+	      f2 (y);
+	      x = e;
+	    }
+	}
+      else
+	{
+	  S *w = y->c;
+	  if (w && w->a == 0)
+	    {
+	      w->a = 1;
+	      y->a = 0;
+	      f1 (y);
+	      w = y->c;
+	    }
+	  if (w && (!w->c || w->c->a == 1) && (!w->d || w->d->a == 1))
+	    {
+	      w->a = 0;
+	      x = y;
+	      y = x->b;
+	    }
+	  else
+	    {
+	      if (w && (!w->c || w->c->a == 1))
+		{
+		  w->a = 0;
+		  if (w->d)
+		    w->d->a = 1;
+		  f2 (w);
+		  w = y->c;
+		}
+	      if (w)
+		{
+		  w->a = y->a;
+		  if (w->c)
+		    w->c->a = 1;
+		}
+	      y->a = 1;
+	      f1 (y);
+	      x = e;
+	    }
+	}
+    }
+}