freeze-thaw.c   [plain text]


/* APPLE LOCAL PFE */
/* Freeze/thaw common language trees and other data.
   Copyright (C) 2001  Free Software Foundation, Inc.
   Contributed by Ira L. Ruben (ira@apple.com)

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/*
  All the pointers that require freezing or thawing defined by tree.h
  are handled here.  Language specific handling of ..._DECL, ..._TYPE,
  and "special" (type 'x' and 'c') nodes are handled by three REQUIRED
  routines which should be uniquely defined as a function of that
  language (e.g., for C, C++, etc.) via the language hooks.
  
  For ..._DECL ('d' type) nodes,
     int pfe_freeze_thaw_decl(tree node);
  
  For ..._TYPE ('t' type) nodes,    
     int pfe_freeze_thaw_type(tree node);
  
  For 'x' and 'c' type nodes,    
     int pfe_freeze_thaw_special(tree node);
  
  These routines are expected to handle the language-specific portions of
  the specified tree node.  It should freeze or thaw depending on the
  setting of pfe_operation (in pfe.h).  For convenience a single function
  pointer, pfe_freeze_thaw_ptr_fp, pointing to either pfe_freeze_ptr() or
  pfe_thaw_ptr() is set (as determined by the action) to allow factoring
  out of the freeze and thaw code when such operations are symmetric.
  
  Each of these routines are expected to return 0 if the routine does not
  handle the node.  If it does, then 1 should be returned if the common
  processing of certain selected nodes is to be bypassed.  See code in
  freeze_thaw_tree_decl(), freeze_thaw_tree_type(), and
  freeze_thaw_tree_special() for which nodes have some additional
  processing in the "normal" case.
  
  Note, pfe_freeze_thaw_decl() may freely use DECL_LANG_SPECIFIC.
  Similarly pfe_freeze_thaw_type() may freely use TYPE_LANG_SPECIFIC.
  The freezing and thawing of these fields should not be done by these
  routines.
*/

#include "config.h"

#ifdef PFE

#include "system.h"
#include "tree.h"
#include "c-common.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "langhooks.h"

#include "pfe.h"

static int debug_pfe_walk    = 0;	/* intended to be set from debugger */
static int debug_rtx_walk    = 0;
static int tree_nesting_level= -1;
static int rtx_nesting_level = -1;
#if !PFE_NEW_TREE_WALK
static int max_nesting_level = 0;
static tree max_nested_node  = NULL;
static int max_stk_size      = 0;
#endif

static void freeze_thaw_tree_common  	  PARAMS ((tree));
static void freeze_thaw_tree_decl    	  PARAMS ((tree));
static void freeze_thaw_tree_type    	  PARAMS ((tree));
static void freeze_thaw_tree_block   	  PARAMS ((tree));
static void freeze_thaw_tree_expr    	  PARAMS ((tree));
static void freeze_thaw_tree_special 	  PARAMS ((tree));
static void freeze_thaw_type_variant	  PARAMS ((tree));

static void freeze_thaw_trace_tree     	  PARAMS ((tree, tree));
static void freeze_thaw_trace_rtx	  PARAMS ((rtx));

static void freeze_thaw_struct_mem_attrs  PARAMS ((mem_attrs **));
static void freeze_thaw_bitmap_element 	  PARAMS ((bitmap_element **));
static void freeze_thaw_bitmap_head_def   PARAMS ((struct bitmap_head_def **));
static void freeze_thaw_edge_def 	  PARAMS ((struct edge_def **));
static void freeze_thaw_basic_block_def	  PARAMS ((struct basic_block_def **));

#define PFE_DEBUG 0

#if PFE_DEBUG
#define HASH_SIZE 37
struct bucket
{
  void *node;
  struct bucket *next;
};
static struct bucket **table = NULL;
static int pfe_seen_node (tree);
#endif

#if PFE_NEW_TREE_WALK
#define PFE_NEW_TREE_WALK_DISPLAY 1
extern int pfe_display_tree_walk;
#endif

/*-------------------------------------------------------------------*/

/* Trace pfe_freeze_thaw_tree_walk() calls.  */
static void freeze_thaw_trace_tree (node, chain_start)
     tree node;
     tree chain_start;
{
  tree type0, tname, dname;
  char *s;
  
  if (tree_nesting_level > 0)
    fprintf (stderr, "%*c", MIN (tree_nesting_level, 41), ' ');
  else
    fputc ('\n', stderr);
  fprintf (stderr, "%s:", tree_code_name[(int) TREE_CODE (node)]);
  fprintf (stderr, HOST_PTR_PRINTF, node);
  fprintf (stderr, " (\'%c\') [depth=%d", 
			   TREE_CODE_CLASS (TREE_CODE (node)),
			   tree_nesting_level);
  if (chain_start)
    {
      fprintf (stderr, ", on ");
      fprintf (stderr, HOST_PTR_PRINTF, chain_start);
      fprintf (stderr, " chain]");
    }  
  else
    fputc (']', stderr);
  
  #if 0
  /* pfe_freeze_ptrs and pfe_thaw_ptrs are static inside pfe.c.  But we can
     temporarily make them non-static and use this code to make sure that
     freeze/thaw operations are totally symmetric in the freeze/thaw counts.  */
  if (PFE_FREEZING)
    {
       extern int pfe_freeze_ptrs;
       fprintf (stderr, "(%d)", pfe_freeze_ptrs);
    }
  else
    {
       extern int pfe_thaw_ptrs;
       fprintf (stderr, "(%d)", pfe_thaw_ptrs);
    }
  #endif
   
  if ((type0 = RP (TREE_TYPE (node)))
      && TREE_CODE_CLASS (TREE_CODE (node)) != 't'
      && TREE_CODE_CLASS (TREE_CODE (type0)) == 't')
    {
      fprintf (stderr, " t=");
      fprintf (stderr, HOST_PTR_PRINTF, type0);
      
      if ((tname = RP (TYPE_NAME (type0))))
	{
	  if (TREE_CODE (tname) == IDENTIFIER_NODE)
	    {
	      s = RP ((char *)IDENTIFIER_POINTER (tname));
	      if (s && *s)
		fprintf (stderr, " {%s}", s);
	    }
	  else if (TREE_CODE (tname) == TYPE_DECL
		   && (dname = RP (DECL_NAME (tname)))
		   && (s = RP ((char *)IDENTIFIER_POINTER (dname)))
		   && *s)
	    fprintf (stderr, " {%s}", s);
	}
      else
	fprintf (stderr, " {%s}", tree_code_name[(int) TREE_CODE (type0)]);
    }
  else if (type0)
    {
      fprintf (stderr, " t=");
      fprintf (stderr, HOST_PTR_PRINTF, type0);
    }  
  if (TREE_CODE (node) == IDENTIFIER_NODE
      && (s = RP ((char *)IDENTIFIER_POINTER (node)))
      && *s)
    fprintf (stderr, " %s", s);
  fputc ('\n', stderr);
}

#if PFE_NEW_TREE_WALK
/* We use TREE_LANG_FLAG_7(node) to mark nodes as "currently being
   visited" or "has been visited".  For freezing we set the flag to 1
   to mark it as visited and for thawing we set the flag to 0 (since
   it will be loaded with the 1's we set when we froze it).
   
   The following macro encodes this test in terms of the pfe_operation.
   For freezing !PFE_FREEZING is always 0.  So if the visited flag is
   still 0 we haven't visited the node and the equality is true.
   Thus negation means we HAVE visitied the node.
   
   Conversly, for thawing, !PFE_FREEZING is always 1.  A node which
   hasn't been visited will still be 1 (since this is what was
   written when we froze them).  So if the visited flag is still 1
   we haven't visited the node and the equality is true.  Thus
   again negation means we HAVE visitied the node.  */
    
#define NODE_VISITED(node) !(TREE_LANG_FLAG_7 (node) == !PFE_FREEZING)

typedef tree pfe_tree_stack_entry;
static pfe_tree_stack_entry *pfe_tree_stack = NULL;
static int pfe_tree_stack_size = 0;
static int pfe_tree_stack_last = -1;
static int pfe_tree_stack_total = 0;
static int pfe_tree_stack_max = -1;
#define PFE_INITIAL_TREE_STACK_SIZE (64 * 1024)
#define PFE_INITIAL_TREE_STACK_INCR (16 * 1024)

/* Push a pointer to a tree node that needs freezing/thawing onto
   a stack of nodes still to be handled.  Make sure that the pointer
   itself is frozen/thawed.  The actual argument here is a pointer
   to the pointer to the tree node, so that the pointer can be
   modified in place.  Nodes are marked as visited when they are
   pushed onto the stack so that they will not be put in the stack
   more than once.  */
void 
pfe_freeze_thaw_tree_push (nodep)
     tree *nodep;
{
  tree node;
  
  /* Nothing to do if we did not get a pointer.  */
  if (!nodep)
    return;
  
  /* If we are freezing, there is nothing to do if we have a NULL
     pointer or if the pointer is already frozen; otherwise, we
     freeze the pointer.  If we are thawing, there is nothing to do 
     if the pointer is already thawed; otherwise, we thaw the 
     pointer.  */
  if (PFE_FREEZING)
    {
      node = *nodep;
      if (!node || PFE_IS_FROZEN (node))
        return;
      pfe_freeze_ptr (nodep);
    }
  else
    {
      if (!PFE_IS_FROZEN (*nodep))
        return;
      node = pfe_thaw_ptr (nodep);
    }
  
  /* If the node has already been marked as visited, that means that
     it already is or has been in the stack, so nothing needs to be
     done.  */
  if (!node || NODE_VISITED (node))
    return;
  
  /* If we get here we need to add the tree node pointer to the
     tree stack.  Create the stack if it doesn't exist.  Grow it
     if it needs to be larger to accomodate the new entry.  */
  if (pfe_tree_stack == NULL)
    {
      pfe_tree_stack_size = PFE_INITIAL_TREE_STACK_SIZE;
      pfe_tree_stack = (pfe_tree_stack_entry *) 
      		       xmalloc (sizeof (pfe_tree_stack_entry) 
      		       		* pfe_tree_stack_size);
    }
  
  if (++pfe_tree_stack_last >= pfe_tree_stack_size)
    {
      pfe_tree_stack_size += PFE_INITIAL_TREE_STACK_INCR;
      pfe_tree_stack = (pfe_tree_stack_entry *) 
      		       xrealloc (pfe_tree_stack,
      		       		 sizeof (pfe_tree_stack_entry) 
      		       		 * pfe_tree_stack_size);
    }
  
  pfe_tree_stack_total++;
  if (pfe_tree_stack_last > pfe_tree_stack_max)
    pfe_tree_stack_max = pfe_tree_stack_last;
    
  pfe_tree_stack[pfe_tree_stack_last] = node;
  
  /* Mark this node as "visited" so we don't try to stack it
     again.  */
  TREE_LANG_FLAG_7 (node) = PFE_FREEZING;
}

/* Process all of the tree nodes that have been pushed onto the
   pfe_tree_stack and freeze/thaw them.  We do this until the stack
   is exhausted, i.e., the walk is complete.  Processing the
   tree nodes will add new nodes to the stack if nodes that have
   not been frozen/thawed are encountered.  */
void
pfe_freeze_thaw_tree_walk (void)
{
  tree node;

#if PFE_NEW_TREE_WALK_DISPLAY
  if (pfe_display_tree_walk)
    printf ("pfe_freeze_thaw_tree_walk (entry): pfe_tree_stack_max = %d\n",
            pfe_tree_stack_max);
#endif

  while (pfe_tree_stack_last >= 0)
    {
      /* Get the last tree node pointer off the top (end) of the tree
         stack.  */
      node = pfe_tree_stack[pfe_tree_stack_last--];
      
      if (debug_pfe_walk)
	freeze_thaw_trace_tree (node, NULL);
      
      /* For thawing, we need to thaw the common tree portion first
	 because there may be need to use the TREE_TYPE in places like
	 freeze_thaw_tree_decl(), freeze_thaw_tree_type(), etc.  For
	 example, the lang specific c++ code for type decls needs
	 TREE_TYPE.  */
    
      if (PFE_THAWING)
	freeze_thaw_tree_common (node);
      
      switch (TREE_CODE_CLASS (TREE_CODE (node)))
	{
	case 'd':
	  freeze_thaw_tree_decl (node);
	  pfe_freeze_thaw_tree_push (&TREE_CHAIN (node));
	  break;
	  
	case 't':
	  freeze_thaw_tree_type (node);
	  pfe_freeze_thaw_tree_push (&TREE_CHAIN (node));
	  break;
	
	case 'b':
	  freeze_thaw_tree_block (node);
	  pfe_freeze_thaw_tree_push (&TREE_CHAIN (node));
	  break;
	
	case 'e':
	case '<':
	case '1':
	case '2':
	case 'r':
	case 's':
	  freeze_thaw_tree_expr (node);
	  pfe_freeze_thaw_tree_push (&TREE_CHAIN (node));
	  break;
	
	case 'c':
	  freeze_thaw_tree_special (node);
	  break;
	  
	case 'x':
	  freeze_thaw_tree_special (node);
	  pfe_freeze_thaw_tree_push (&TREE_CHAIN (node));
	  break;
	
	default:
	  break;
	}
      
      /* For freezing, we need to delay the common portion until here
         because there may be need to use the TREE_TYPE in places like
         freeze_thaw_tree_decl(), freeze_thaw_tree_type(), etc.  For
         example, the lang specific c++ code for type decls needs
         TREE_TYPE.  */
      
      if (PFE_FREEZING)
      	freeze_thaw_tree_common (node);
    }
    
  free (pfe_tree_stack);
  
#if PFE_NEW_TREE_WALK_DISPLAY
  if (pfe_display_tree_walk)
    printf ("pfe_freeze_thaw_tree_walk (exit): "
  	    "pfe_tree_stack_max = %d; pfe_tree_stack_total = %d\n", 
  	    pfe_tree_stack_max, pfe_tree_stack_total);
#endif
}

#else /* !PFE_NEW_TREE_WALK */

/* Freeze/thaw tree node and its direct descendents (i.e., walk tree).  */
void 
pfe_freeze_thaw_tree_walk (nodep)
     tree *nodep;
{
  tree node, chain_start;
  static int follow_chain;
  static void *start_stk_addr;
  
  /* We use TREE_LANG_FLAG_7(node) to mark nodes as "currently being
     visited" or "has been visited".  For freezing we set the flag to 1
     to mark it as visited and for thawing we set the flag to 0 (since
     it will be loaded with the 1's we set when we froze it).
     
     The following macro encodes this test in terms of the pfe_operation.
     For freezing !PFE_FREEZING is always 0.  So if the visited flag is
     still 0 we haven't visited the node and the equality is true.
     Thus negation means we HAVE visitied the node.
     
     Conversly, for thawing, !PFE_FREEZING is always 1.  A node which
     hasn't been visited will still be 1 (since this is what was
     written when we froze them).  So if the visited flag is still 1
     we haven't visited the node and the equality is true.  Thus
     again negation means we HAVE visitied the node.  */
      
  #define NODE_VISITED(node) !(TREE_LANG_FLAG_7 (node) == !PFE_FREEZING)
    
  if (!nodep)
    return;
  
  if (PFE_FREEZING)
    {
      node = *nodep;
      if (!node || PFE_IS_FROZEN (node))
        return;
      pfe_freeze_ptr (nodep);
    }
  else
    {
      if (!PFE_IS_FROZEN (*nodep))
        return;
      node = pfe_thaw_ptr (nodep);
    }
    
  if (!node || NODE_VISITED (node))
    return;
  
  #if PFE_DEBUG
  if (pfe_seen_node (node))
    {
      int stop_here;
      fprintf (stderr, "trying to freeze node %x more than once!\n", (unsigned)node);
      stop_here = 1;
    }
  #endif
    
  /* We always track nesting to for debugging purposes.  */
  
  if (++tree_nesting_level > max_nesting_level)
    {
      int stk_size = (int)start_stk_addr - (int)&node;
      if (stk_size < 0) stk_size = -stk_size;
      if (stk_size > max_stk_size)
	max_stk_size = stk_size;
      max_nesting_level = tree_nesting_level;
      max_nested_node   = node;
    }
  else if (tree_nesting_level == 0)
    start_stk_addr = &node;
      
  chain_start = node;
  if (debug_pfe_walk)
    freeze_thaw_trace_tree (node, NULL);
  
  do {
    TREE_LANG_FLAG_7 (node) = PFE_FREEZING;

    /* For thawing, we need to thaw the common tree portion first
       because there may be need to use the TREE_TYPE in places like
       freeze_thaw_tree_decl(), freeze_thaw_tree_type(), etc.  For
       example, the lang specific c++ code for type decls needs
       TREE_TYPE.  */
    
    if (PFE_THAWING)
      freeze_thaw_tree_common (node);
    
    switch (TREE_CODE_CLASS (TREE_CODE (node)))
      {
      case 'd':
	freeze_thaw_tree_decl (node);
	follow_chain = 1;
	break;
	
      case 't':
	freeze_thaw_tree_type (node);
	follow_chain = 1;
	break;
      
      case 'b':
	freeze_thaw_tree_block (node);
	follow_chain = 1;
	break;
      
      case 'e':
      case '<':
      case '1':
      case '2':
      case 'r':
      case 's':
	freeze_thaw_tree_expr (node);
	follow_chain = 1;
	break;
      
      case 'c':
	freeze_thaw_tree_special (node);
	follow_chain = 0;
	break;
	
      case 'x':
	freeze_thaw_tree_special (node);
	follow_chain = 1;
	break;
      
      default:
	follow_chain = 0;
      }

    /* For freezing, we need to delay the common portion until here
       because there may be need to use the TREE_TYPE in places like
       freeze_thaw_tree_decl(), freeze_thaw_tree_type(), etc.  For
       example, the lang specific c++ code for type decls needs
       TREE_TYPE.  */
    
    if (PFE_FREEZING)
      freeze_thaw_tree_common (node);
    
    if (follow_chain)
      {
        node = PFE_FREEZE_THAW_PTR (&TREE_CHAIN (node));
        follow_chain = node && !NODE_VISITED (node);
	if (debug_pfe_walk && follow_chain)
	  freeze_thaw_trace_tree (node, chain_start);
      }
    }
  while (follow_chain);
  
  --tree_nesting_level;
}
#endif

/* Handle tree_common portion of every node */
static void
freeze_thaw_tree_common (node)
     tree node;
{
  /* Put back all nodes in their "unwritten" state since,
     conceptually no asm or debug output has been generated
     when a file is loaded.  We save the setting in the file
     too since it doesn't pay to check for freeze or thaw.  */
  TREE_ASM_WRITTEN (node) = 0;
  PFE_FREEZE_THAW_WALK (TREE_TYPE (node));
  /* future common stuff goes here.  */
}

/* Handle 'd' nodes */
static void
freeze_thaw_tree_decl (node)
     tree node;
{
  pfe_freeze_thaw_ptr_fp (&DECL_SOURCE_FILE (node));
  PFE_FREEZE_THAW_WALK (DECL_SIZE (node));
  PFE_FREEZE_THAW_WALK (DECL_SIZE_UNIT (node));
  PFE_FREEZE_THAW_WALK (DECL_NAME (node));
  PFE_FREEZE_THAW_WALK (DECL_ARGUMENTS (node));
  PFE_FREEZE_THAW_WALK (DECL_INITIAL (node));
  PFE_FREEZE_THAW_WALK (DECL_ABSTRACT_ORIGIN (node));
  if (node->decl.assembler_name) /* no useful accessor macro */
    PFE_FREEZE_THAW_WALK ((node->decl.assembler_name));
  PFE_FREEZE_THAW_WALK (DECL_SECTION_NAME (node));	/* NEEDED? */
  PFE_FREEZE_THAW_WALK (DECL_ATTRIBUTES (node));
  PFE_FREEZE_THAW_RTX (node->decl.rtl);
  PFE_FREEZE_THAW_RTX (DECL_LIVE_RANGE_RTL (node));	/* FIXME: needed? */
  
  /* Some fields of the decl node are used to access the language
     specific portions of the node.  Thus we cannot freeeze them
     until that portion is done.  Similarly we need to thaw them
     before we use them.  */
     
  if (PFE_THAWING)
    {
      pfe_thaw_ptr (&DECL_LANG_SPECIFIC (node)); /* do this first */
      PFE_FREEZE_THAW_WALK (DECL_CONTEXT (node));
    }
    
  if (!(*lang_hooks.pfe_freeze_thaw_decl) (node))
    {
      switch (TREE_CODE (node))
	{
	  case FUNCTION_DECL:
	    pfe_freeze_thaw_function (&DECL_SAVED_INSNS (node));
      	    PFE_FREEZE_THAW_WALK (DECL_SAVED_TREE (node));
      	    PFE_FREEZE_THAW_WALK (DECL_INLINED_FNS (node));
	    PFE_FREEZE_THAW_WALK (DECL_VINDEX (node));
	    PFE_FREEZE_THAW_WALK (DECL_RESULT (node));
	    break;
	  
	  case PARM_DECL:
	    PFE_FREEZE_THAW_RTX (DECL_INCOMING_RTL (node));
	    PFE_FREEZE_THAW_WALK (DECL_ARG_TYPE_AS_WRITTEN (node));
	    break;
	  
	  case FIELD_DECL:
	    PFE_FREEZE_THAW_WALK (DECL_FIELD_BIT_OFFSET (node));
	    PFE_FREEZE_THAW_WALK (DECL_BIT_FIELD_TYPE (node));
	    PFE_FREEZE_THAW_WALK (DECL_FCONTEXT (node));
	    break;

	  case TYPE_DECL:
	    PFE_FREEZE_THAW_WALK (DECL_ORIGINAL_TYPE (node));
	    break;
	    
	  default: ;
	    /* Unless it's one of the above nodes, or unless
	       pfe_freeze_thaw_decl() handled the node,
	       we assume there is no other additional pointers
	       to freeze or thaw at this point.  */
	    break;
	}
    }
    
  if (PFE_FREEZING)
    {
      PFE_FREEZE_THAW_WALK (DECL_CONTEXT (node));
      pfe_freeze_ptr (&DECL_LANG_SPECIFIC (node)); /* do this last */
    }
}

/* Freeze/thaw a 't' node's TYPE_MAIN_VARIANT and all the nodes
   on that node's TYPE_NEXT_VARIANT chain (variants made by type
   modifiers such as "const" and "volatile").  */
static void freeze_thaw_type_variant (node)
     tree node;
{
  /* Each type has a TYPE_MAIN_VARIANT and TYPE_NEXT_VARIANT.  For the
     "main" type, TYPE_MAIN_VARIANT equals that type's node (i.e.,
     itself) and TYPE_NEXT_VARIANT points to the first variant of that
     main type.  
     
     Thus, for example, for variants of main type "int", it could have
     variants "const int", "volatile int", and "const volatile int", and
     maybe even FOO if FOO is a typedef of int.  The "int" node's
     TYPE_MAIN_VARIANT will point to itself and it's TYPE_NEXT_VARIANT
     to one of the other variants.  
     
     In each of the other variants, TYPE_MAIN_VARIANT points back to
     its main variant and TYPE_NEXT_VARIANT points to the next variant
     or is NULL.
     
     This algorithm here is "tricky" and complicated by the fact that
     current node could be the main variant or any of its next variants.
     It does not lend itself well to using the PFE_FREEZE_THAW_WALK macro
     so this is a rare instance we have to call pfe_freeze_thaw_tree_walk()
     explicitly.  */
  
  if (PFE_FREEZING)
    {
      tree *nodep = &TYPE_MAIN_VARIANT (node);
      while (*nodep && !PFE_IS_FROZEN (*nodep))
        {
          node = *nodep;
#if PFE_NEW_TREE_WALK
	  pfe_freeze_thaw_tree_push (nodep);
#else
          pfe_freeze_thaw_tree_walk (nodep);
#endif
	  PFE_FREEZE_THAW_PTR (&TYPE_MAIN_VARIANT (node));
          nodep = &TYPE_NEXT_VARIANT (node);
        }
    }
  else
    {
      tree *nodep = &TYPE_MAIN_VARIANT (node);
      while (*nodep && PFE_IS_FROZEN (*nodep))
        {
#if PFE_NEW_TREE_WALK
	  pfe_freeze_thaw_tree_push (nodep);
#else
          pfe_freeze_thaw_tree_walk (nodep);
#endif
	  PFE_FREEZE_THAW_PTR (&TYPE_MAIN_VARIANT (*nodep));
          nodep = &TYPE_NEXT_VARIANT (*nodep);
        }
    }
}

/* Handle 't' nodes */
static void
freeze_thaw_tree_type (node)
     tree node;
{
  PFE_FREEZE_THAW_WALK (TYPE_VALUES (node));
  PFE_FREEZE_THAW_WALK (TYPE_SIZE (node));
  PFE_FREEZE_THAW_WALK (TYPE_SIZE_UNIT (node));
  PFE_FREEZE_THAW_WALK (TYPE_ATTRIBUTES (node));
  PFE_FREEZE_THAW_WALK (TYPE_POINTER_TO (node));
  PFE_FREEZE_THAW_WALK (TYPE_REFERENCE_TO (node));
  
  /* FIXME: We know what to do for DBX_DEBUGGING_INFO.
     It's not clear we are correct for the others.  */
  #if defined(DBX_DEBUGGING_INFO)
  /* TYPE_SYMTAB_ADDRESS(node) used instead of TYPE_SYMTAB_POINTER().  */
  /* Always make sure this is zero when at least thawed.  This will
     cause dbxout.c to regenerate the stab info for this type.  */
  TYPE_SYMTAB_ADDRESS (node) = 0;
  #elif defined(XCOFF_DEBUGGING_INFO)
  /* TYPE_SYMTAB_ADDRESS(node) used instead of TYPE_SYMTAB_POINTER().  */
  TYPE_SYMTAB_ADDRESS (node) = 0;
  #elif defined(SDB_DEBUGGING_INFO)
  pfe_freeze_thaw_ptr_fp (&TYPE_SYMTAB_POINTER (node));
  #elif defined(DWARF_DEBUGGING_INFO)
  pfe_freeze_thaw_ptr_fp (&TYPE_SYMTAB_POINTER (node));
  #elif defined(DWARF2_DEBUGGING_INFO)
  pfe_freeze_thaw_ptr_fp (&TYPE_SYMTAB_POINTER (node));
  #endif
  
  PFE_FREEZE_THAW_WALK (TYPE_NAME (node));
  PFE_FREEZE_THAW_WALK (TYPE_MIN_VALUE (node));
  PFE_FREEZE_THAW_WALK (TYPE_MAX_VALUE (node));

  freeze_thaw_type_variant (node);
  PFE_FREEZE_THAW_WALK (TYPE_CONTEXT (node)); 		/* NEEDED? */
  
  if (PFE_THAWING)
    pfe_thaw_ptr (&TYPE_LANG_SPECIFIC (node));
    
  if (!(*lang_hooks.pfe_freeze_thaw_type) (node))
    {
      switch (TREE_CODE (node))
	{
	  case RECORD_TYPE:
	  case UNION_TYPE:
	  case QUAL_UNION_TYPE:
	    PFE_FREEZE_THAW_WALK (TYPE_METHODS (node));
	    PFE_FREEZE_THAW_WALK (TYPE_BINFO (node));
	    break;
	  
	  case FUNCTION_TYPE:
	  case METHOD_TYPE:
	    PFE_FREEZE_THAW_WALK (TYPE_METHOD_BASETYPE (node));
	    break;
	  
	  case OFFSET_TYPE:
	    PFE_FREEZE_THAW_WALK (TYPE_OFFSET_BASETYPE (node));
	    break;
	    
	  default: ;
	    /* Unless it's one of the above nodes, or unless
	       pfe_freeze_thaw_type() handled the node,
	       we assume there is no other additional pointers
	       to freeze or thaw at this point.  */
	    break;
	}
    }
    
  if (PFE_FREEZING)
    pfe_freeze_ptr (&TYPE_LANG_SPECIFIC (node));
}

/* Handle 'b' nodes */
static void
freeze_thaw_tree_block (node)
     tree node;
{
  PFE_FREEZE_THAW_WALK (BLOCK_VARS (node));
  PFE_FREEZE_THAW_WALK (BLOCK_SUBBLOCKS (node));
  PFE_FREEZE_THAW_WALK (BLOCK_SUPERCONTEXT (node));
  PFE_FREEZE_THAW_WALK (BLOCK_ABSTRACT_ORIGIN (node));
  PFE_FREEZE_THAW_WALK (BLOCK_FRAGMENT_ORIGIN (node));
  PFE_FREEZE_THAW_WALK (BLOCK_FRAGMENT_CHAIN (node));
}

/* Handle 'e', '<', '1', '2', 'r', and 's' nodes */
static void
freeze_thaw_tree_expr (node)
     tree node;
{
  int i, len, first_rtl;
  
  len = TREE_CODE_LENGTH (TREE_CODE (node));
  first_rtl = first_rtl_op (TREE_CODE (node));
  
  for (i = 0; i < len; ++i)
    {
      if (i >= first_rtl)
        {
          rtx *rtxp = (struct rtx_def **)&TREE_OPERAND (node, i);
	  pfe_freeze_thaw_rtx (rtxp); /* cannot use macro here */ /* FIXME: needed? */
	}
      else
	PFE_FREEZE_THAW_WALK (TREE_OPERAND (node, i));
    }
}

/* Handle 'c' and 'x' nodes */
static void
freeze_thaw_tree_special (node)
     tree node;
{
  int i, len;
  
  if (!(*lang_hooks.pfe_freeze_thaw_special) (node))
    {
      switch (TREE_CODE (node))
	{
	case INTEGER_CST:
	  PFE_FREEZE_THAW_RTX (TREE_CST_RTL(node)); /* FIXME: needed? */
	  break;
	
	case REAL_CST:
	  PFE_FREEZE_THAW_RTX (TREE_CST_RTL(node)); /* FIXME: needed? */
	  break;
	
	case COMPLEX_CST:
	  PFE_FREEZE_THAW_RTX (TREE_CST_RTL(node)); /* FIXME: needed? */
	  PFE_FREEZE_THAW_WALK (TREE_REALPART (node));
	  PFE_FREEZE_THAW_WALK (TREE_IMAGPART (node));
	  break;

#if 0 /* APPLE MERGE generalize this if necessary */	
	/* APPLE LOCAL: AltiVec */
	case VECTOR_CST:
	  PFE_FREEZE_THAW_RTX (TREE_CST_RTL(node)); /* FIXME: needed? */
	  PFE_FREEZE_THAW_WALK (TREE_VECTOR_CST_0 (node));
	  PFE_FREEZE_THAW_WALK (TREE_VECTOR_CST_1 (node));
	  PFE_FREEZE_THAW_WALK (TREE_VECTOR_CST_2 (node));
	  PFE_FREEZE_THAW_WALK (TREE_VECTOR_CST_3 (node));
	  break;
#endif

	case STRING_CST:
	  PFE_FREEZE_THAW_RTX (TREE_CST_RTL(node)); /* FIXME: needed? */
	  pfe_freeze_thaw_ptr_fp (&TREE_STRING_POINTER (node));
	  PFE_FREEZE_THAW_WALK (TREE_CHAIN (node));
	  break;
	
	case TREE_LIST:
	  PFE_FREEZE_THAW_WALK (TREE_PURPOSE (node));
	  PFE_FREEZE_THAW_WALK (TREE_VALUE (node));
	  break;
	
	case TREE_VEC:
	  len = TREE_VEC_LENGTH (node);
	  for (i = 0; i < len; i++)
	    PFE_FREEZE_THAW_WALK (TREE_VEC_ELT (node, i));
	  break;
	
	case IDENTIFIER_NODE:
	  /* All IDENTIFIER_NODE's contain a cpp_hashnode and
	     cpp_hashnode's are frozen/thawed as part of the
	     normal freezing and thawing of hashtables.  So
	     nothing futher needs to be done here.  */
	  break;
	  
	default: ;
	  /* Unless it's one of the above nodes, or unless
	     pfe_freeze_thaw_special() handled the node,
	     we assume there is no other additional pointers
	     to freeze or thaw at this point.  */
	  break;
	}
    }
}

/*-------------------------------------------------------------------*/

/* Trace pfe_freeze_thaw_rtx() calls.  */
static void freeze_thaw_trace_rtx (x)
     rtx x;
{
  if (rtx_nesting_level > 0)
    fprintf (stderr, "%*c", MIN (tree_nesting_level + rtx_nesting_level, 60), ' ');
    
  fprintf (stderr, "%s:", GET_RTX_NAME (GET_CODE (x)));
  fprintf (stderr, HOST_PTR_PRINTF, x);
  fprintf (stderr, " [depth=%d] codes=\"%s\"", 
			   rtx_nesting_level,
			   (char *)GET_RTX_FORMAT (GET_CODE (x)));
  #if 0
  /* pfe_freeze_ptrs and pfe_thaw_ptrs are static inside pfe.c.  But we can
     temporarily make them non-static and use this code to make sure that
     freeze/thaw operations are totally symmetric in the freeze/thaw counts.  */
  if (PFE_FREEZING)
    {
       extern int pfe_freeze_ptrs;
       fprintf (stderr, "(%d)", pfe_freeze_ptrs);
    }
  else
    {
       extern int pfe_thaw_ptrs;
       fprintf (stderr, "(%d)", pfe_thaw_ptrs);
    }
  #endif
  
  fputc ('\n', stderr);
}

/* Freeze/thaw rtx as directed by its format codes.
                        *** CAUTION/WARNING ***
   Like anything else that is frozen/thawed, the strings for XSTR and
   XTMPL rtx's must be allocated in pfe memory.  While this is taken
   care if on the compiler, care must be taken of how XSTR's are
   created in target dependent code (i.e., code in the gcc/config
   directory).  Using ggc_alloc_string() is fine.  But allocation
   any other way must involve one of the pfe allocators (e.g.,
   pfe_malloc).  Currently we have no good way to cover such cases.  */
void
pfe_freeze_thaw_rtx (xp)
     rtx *xp;
{
  rtx  x;
  int  i;
  char *format_ptr;

  if (PFE_FREEZING)
    {
      x = *xp;
      if (!x || PFE_IS_FROZEN (x))
        return;
      pfe_freeze_ptr (xp);
    }
  else
    {
      if (!PFE_IS_FROZEN (*xp))
        return;
      x = pfe_thaw_ptr (xp);
    }

  if (x == NULL_RTX)
    return;
    
  ++rtx_nesting_level;
  if (debug_rtx_walk)
    freeze_thaw_trace_rtx (x);
  
  /* Handle operands as a function of the format codes...  */

  format_ptr = (char *)GET_RTX_FORMAT (GET_CODE (x));

  for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
    switch (*format_ptr++)
      {
      case 'T':
	pfe_freeze_thaw_ptr_fp (&XTMPL (x, i));
	break;

      case 'S':
      case 's':
	pfe_freeze_thaw_ptr_fp (&XSTR (x, i));
	break;

      case '0':
	if (i == 3 && GET_CODE (x) == NOTE)
	  {
	    switch (NOTE_LINE_NUMBER (x))
	      {
	      case NOTE_INSN_EH_REGION_BEG:
	      case NOTE_INSN_EH_REGION_END:
		break;

	      case NOTE_INSN_BLOCK_BEG:
	      case NOTE_INSN_BLOCK_END:
		PFE_FREEZE_THAW_WALK (NOTE_BLOCK (x));
		break;

	      case NOTE_INSN_RANGE_BEG:
	      case NOTE_INSN_RANGE_END:
	      case NOTE_INSN_LIVE:
		PFE_FREEZE_THAW_RTX (NOTE_RANGE_INFO (x));
		break;

	      case NOTE_INSN_BASIC_BLOCK:
		freeze_thaw_basic_block_def (&NOTE_BASIC_BLOCK (x));
		break;

	      case NOTE_INSN_EXPECTED_VALUE:
		PFE_FREEZE_THAW_RTX (NOTE_EXPECTED_VALUE (x));
		break;

	      case NOTE_INSN_DELETED_LABEL:
	        pfe_freeze_thaw_ptr_fp (&NOTE_SOURCE_FILE (x));
		break;

	      default:
		if (NOTE_LINE_NUMBER (x) >= 0)
		  pfe_freeze_thaw_ptr_fp (&X0STR (x, i));
		break;
	      }
	  }
	else if (GET_CODE (x) == MEM && MEM_ATTRS (x))
	  freeze_thaw_struct_mem_attrs (&MEM_ATTRS (x));
	break;

      case 'e':
      case 'u':
	PFE_FREEZE_THAW_RTX (XEXP (x, i));
	break;

      case 'E':
      case 'V':
	pfe_freeze_thaw_rtvec (&XVEC (x, i));
	break;

      case 'b':
        freeze_thaw_bitmap_head_def (&XBITMAP (x, i));
	break;

      case 't':
	PFE_FREEZE_THAW_WALK (XTREE (x, i));
	break;

      case 'w':
      case 'i':
      case 'n':
      case '*':
	break;

      default:
	fprintf (stderr,
		 "switch format wrong in pfe_freeze_thaw_rtx(); format was: %c.\n",
		 format_ptr[-1]);
	abort ();
      }
      
  --rtx_nesting_level;
}

/* Freeze/thaw rtvec an rtvec.  */
void
pfe_freeze_thaw_rtvec (rtvecp)
     struct rtvec_def **rtvecp;
{
  struct rtvec_def *v = PFE_FREEZE_THAW_PTR (rtvecp);
  int i;
  
  if (!v)
    return;
    
  for (i = 0; i < GET_NUM_ELEM (v); ++i)
    {
      if (debug_rtx_walk)
        {
          ++rtx_nesting_level;
          fprintf (stderr, "%*c[%d]\n", 
                   MIN (tree_nesting_level + rtx_nesting_level, 61), ' ', i);
        }
        
      PFE_FREEZE_THAW_RTX (RTVEC_ELT (v, i));
      
      if (debug_rtx_walk)
        --rtx_nesting_level;
    }
}

/* Freeze/thaw a mem_attrs struct for a MEM rtx.  */
static void
freeze_thaw_struct_mem_attrs (mpp)
     mem_attrs **mpp;
{
  mem_attrs *mp = (mem_attrs *)PFE_FREEZE_THAW_PTR (mpp);
 
  if (!mp)
    return;
  PFE_FREEZE_THAW_WALK (mp->expr);
  PFE_FREEZE_THAW_RTX (mp->offset);
  PFE_FREEZE_THAW_RTX (mp->size);
}

/*-------------------------------------------------------------------*/

/* Freeze/thaw the chain of elts pointed to by *bep.  The elt pointed
   to by bep is frozen/thawed along with all elts that follow and
   preceed that elt on the chain of bitmap_element's.  */
static void 
freeze_thaw_bitmap_element (bep)
     bitmap_element **bep;
{
  bitmap_element *be, *next, *prev;
    
  be = (bitmap_element *)PFE_FREEZE_THAW_PTR (bep);
  if (!be)
    return;
    
  next = (bitmap_element *)PFE_FREEZE_THAW_PTR (&be->next);
  prev = (bitmap_element *)PFE_FREEZE_THAW_PTR (&be->prev);
  
  be = next;
  while (be)
    {
      PFE_FREEZE_THAW_PTR (&be->prev);
      be = (bitmap_element *)PFE_FREEZE_THAW_PTR (&be->next);
    }
  
  be = prev;  
  while (be)
    {
      PFE_FREEZE_THAW_PTR (&be->next);
      be = (bitmap_element *)PFE_FREEZE_THAW_PTR (&be->prev);
    }
}

/* Freeze/thaw the chain of bitmap_head_def pointed to by *bhp.  The
   entry pointed to by (bhp) is frozen/thawed along with all the
   entries linked to it in its succ_next and pred_next fields.  */
static void 
freeze_thaw_bitmap_head_def (bhp)
     struct bitmap_head_def **bhp;
{
  bitmap_head *bh = (bitmap_head *)PFE_FREEZE_THAW_PTR (bhp);
  
  if (!bh)
    return;
  
  PFE_FREEZE_THAW_PTR (&bh->current);
  freeze_thaw_bitmap_element (&bh->first);
}

/* Freeze/thaw a flow "edge".  */
static void 
freeze_thaw_edge_def (edgep)
     struct edge_def **edgep;
{
  edge e, succ_next, pred_next;
  
  /* Freeze/thaw caller's pointer.  */
  
  e = (edge)PFE_FREEZE_THAW_PTR (edgep);
  if (!e)
    return;
  
  freeze_thaw_basic_block_def (&e->src); 	/* FIXME: is this recursive? */
  freeze_thaw_basic_block_def (&e->dest); 	/* FIXME: is this recursive? */
  PFE_FREEZE_THAW_RTX (e->insns);
  succ_next = (edge)PFE_FREEZE_THAW_PTR (&e->succ_next);
  pred_next = (edge)PFE_FREEZE_THAW_PTR (&e->pred_next);
  
  e = succ_next;
  while (e)
    {
      freeze_thaw_basic_block_def (&e->src);
      freeze_thaw_basic_block_def (&e->dest);
      PFE_FREEZE_THAW_RTX (e->insns);
      PFE_FREEZE_THAW_PTR (&e->pred_next);
      e = (edge)PFE_FREEZE_THAW_PTR (&e->succ_next);
    }
  
  e = pred_next;  
  while (e)
    {
      freeze_thaw_basic_block_def (&e->src);
      freeze_thaw_basic_block_def (&e->dest);
      PFE_FREEZE_THAW_RTX (e->insns);
      PFE_FREEZE_THAW_PTR (&e->succ_next);
      e = (edge)PFE_FREEZE_THAW_PTR (&e->pred_next);
    }
}

/* Freeze/thaw a basic_block_def.  */
static void 
freeze_thaw_basic_block_def (bbp)
     struct basic_block_def **bbp;
{
  struct basic_block_def *bb = (struct basic_block_def *)PFE_FREEZE_THAW_PTR (bbp);
  
  if (!bb)
    return;
    
  PFE_FREEZE_THAW_RTX (bb->head);
  PFE_FREEZE_THAW_RTX (bb->end);
  PFE_FREEZE_THAW_WALK (bb->head_tree);
  PFE_FREEZE_THAW_WALK (bb->end_tree);
  freeze_thaw_edge_def (&bb->pred);
  freeze_thaw_edge_def (&bb->succ);
  freeze_thaw_bitmap_head_def (&bb->local_set);
  freeze_thaw_bitmap_head_def (&bb->cond_local_set);
  freeze_thaw_bitmap_head_def (&bb->global_live_at_start);
  freeze_thaw_bitmap_head_def (&bb->global_live_at_end);
}

/*-------------------------------------------------------------------*/

void pfe_print_TREE_CHAIN (tree);

void
pfe_print_TREE_CHAIN (node)
     tree node;
{
  tree node1;
  char *name;
  
  for (node1 = node, node = RP(node); node; node1 = TREE_CHAIN (node), node = RP(TREE_CHAIN (node)))
    {
      name = (char *)"?";
      if (TREE_CODE_CLASS (TREE_CODE (node)) != 't'
	  && TREE_TYPE (node)
	  && TREE_CODE_CLASS (TREE_CODE (TREE_TYPE (node))) == 't')
	{
	  tree type = TREE_TYPE (node);
	  
	  if (TYPE_NAME (type))
	    {
	      if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
	        {
	          if (IDENTIFIER_POINTER (TYPE_NAME (type))
	              && *IDENTIFIER_POINTER (TYPE_NAME (type)))
		  name = (char *)IDENTIFIER_POINTER (TYPE_NAME (type));
		}
	      else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
		       && DECL_NAME (TYPE_NAME (type))
		       && IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))
		       && *IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))))
		name  = (char *)IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
	    }
	  else
	    name = (char *)tree_code_name[(int) TREE_CODE (type)];
	}
      
      if (PFE_FREEZING)
        {
          if (!PFE_IS_FROZEN (node1))
            {
              fprintf (stderr, "0x%x (%s) is not frozen", (unsigned)node, name);
	      if (TREE_LANG_FLAG_7 (node) != 1)
		fprintf (stderr, "  and doesn't have TREE_LANG_FLAG_7 set to 1");
	      fprintf (stderr, "\n");
            }
          else if (TREE_LANG_FLAG_7 (node) != 1)
	    fprintf (stderr, "0x%x (%s) doesn't have TREE_LANG_FLAG_7 set to 1\n", (unsigned)node, name);
        }
      else
        {
          if (PFE_IS_FROZEN (node1))
            {
              fprintf (stderr, "0x%x (%s) is frozen", (unsigned)node, name);
	      if (TREE_LANG_FLAG_7 (node) != 0)
		fprintf (stderr, "  and doesn't have TREE_LANG_FLAG_7 set to 0");
	      fprintf (stderr, "\n");
            }
          else if (TREE_LANG_FLAG_7 (node) != 0)
	    fprintf (stderr, "0x%x  (%s) doesn't have TREE_LANG_FLAG_7 set to 0\n", (unsigned)node, name);
        }
    }
}

#if PFE_DEBUG
static int 
pfe_seen_node (node)
     tree node;
{
  int hash;
  struct bucket *b;
  
  if (!table)
    {
      table = (struct bucket **) xmalloc (HASH_SIZE * sizeof (struct bucket *));
      memset ((char *) table, 0, HASH_SIZE * sizeof (struct bucket *));
    }
  
  hash = ((unsigned long) node >> 4) % HASH_SIZE;

  for (b = table[hash]; b; b = b->next)
    if (b->node == node)
	return 1;
  
  if (tree_nesting_level >= 0)
    {
      b = (struct bucket *) xmalloc (sizeof (struct bucket));
      b->node = node;
      b->next = table[hash];
      table[hash] = b;
    }
    
  return 0;
}

static void analyze_hash()
{
  int i, n, t;
  struct bucket *b;
  
  for (i = t = 0; i < HASH_SIZE; ++ i)
    {
      for (n = 0, b = table[i]; b; b = b->next, ++n) ;
      fprintf (stderr, "%4d: %6d\n", i, n);
      t += n;
    }
  fprintf (stderr, "total number of hash entries = %d\n", t);
}
#endif

#endif /* PFE */

/*-------------------------------------------------------------------*/

#if 0

cd $gcc3/gcc/pfe; \
cc -no-cpp-precomp -c  -DIN_GCC  -g \
  -W -Wall -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wtraditional -pedantic -Wno-long-long \
  -DHAVE_CONFIG_H \
  -I$gcc3obj \
  -I. \
  -I.. \
  -I../config \
  -I../../include \
  freeze-thaw.c -o ~/tmp.o -w

#endif