/* APPLE LOCAL PFE */
/* Persistent Front End (PFE) for the GNU compiler.
   Copyright (C) 2001
   Free Software Foundation, Inc.
   Contributed by Apple Computer Inc.

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.  */

#ifndef GCC_PFE_H
#define GCC_PFE_H

#ifdef PFE

#include <stdio.h>

/* Filename and FILE variable for the specified load/dump file. */
extern const char *pfe_name;
extern FILE *pfe_file;

/* Structs referenced as pointers.  */
union tree_node;		/* in tree.h			*/
struct language_function;	/* in c-common.h   		*/
struct binding_level;		/* in cp/decl.c or c-decl.c 	*/
struct named_label_use_list;	/* in cp/decl.c    		*/
struct named_label_list;	/* in cp/decl.c    		*/
struct unparsed_text;		/* in cp/spew.c    		*/
struct rtx_def;			/* in rtl.h			*/
struct rtvec_def;		/* in rtl.h			*/
struct pfe_compiler_state;	/* in pfe/pfe-header.h		*/
struct varray_head_tag;		/* in varray.h			*/
struct cpp_token;		/* in cpplib.h			*/
struct cpp_hashnode;		/* in cpplib.h			*/
struct function;		/* in function.h		*/
struct rtx_def;			/* in rtl.h			*/
struct pfe_lang_compiler_state;	/* in pfe/cp-freeze-thaw.c	*/

/* Magic number to identify a PFE dump file.  */
#define PFE_MAGIC_NUMBER 0x0ABACAB0

/* The version number of the current dump file format.  */
#define PFE_FORMAT_VERSION 1

/* Operations performed by the load/dump mechanism.  These are
   passed as arguments to pfe_init to indicate whether a load
   or dump is being performed in the current compilation.  */
enum pfe_action {
  PFE_NOT_INITIALIZED,	/* Identifies if PFE has been init'ed.	*/
  PFE_NOP,		/* Use when not loading or dumping.	*/
  PFE_DUMP,		/* Use when performing a dump.		*/
  PFE_LOAD		/* Use when performing a load.		*/
};

/* Identify whether the current operation is a load, dump, or neither,
   and whether the PFE system has been initialized.  */
extern enum pfe_action pfe_operation;

/* In the context of freezing/thawing, the following macros make
   things a little easier to read when we cannot do an operation
   that can be handled by a function handling both freezing and
   thawing.  */
#define PFE_FREEZING (pfe_operation == PFE_DUMP)
#define PFE_THAWING  (pfe_operation == PFE_LOAD)

/* Pointer to the PFE header with the global compiler state to be
   saved when dumping and restored when loading.  It contains the
   "roots" of various compiler data structures.  This pointer is
   set by pfe_dump_compiler_state when performing a dump, before
   the pfe_freeze_compiler_state is called to fill in the header.  
   When loading, the pointer is set by pfe_load_compiler_state
   before pfe_thaw_compiler_state is called.  */
extern struct pfe_compiler_state *pfe_compiler_state_ptr;

/* Initialize the PFE.  Specify whether we are doing a dump, load, or
   neither.  The intent is that pfe_init should be called once per
   compilation.  In the case of a dump, a memory "zone" will be 
   allocated for the PFE memory management routines, so that 
   allocations of compiler data structures can be routed to the PFE 
   memory zone so that the structures can be dumped at the end of the 
   compilation.  
   
   This function allocates the compiler state header which gets used 
   by the function freezing the compiler state.  Allocating the header
   early here allows other parts of the compiler to access and fill in
   the header.
   
   Note: the PFE memory management routines can be used when performing
   a dump, load, or neither.  The routines will try to do the right
   thing for the operation involved.  */
extern void pfe_init 			PARAMS ((enum pfe_action));

/* Shut down the PFE.  */
extern void pfe_term 			PARAMS ((void));

/* Print out a message for an error and quit.  */
extern void error                       PARAMS ((const char *, ...));
/* Print out a message for an internal error and quit.  */
extern void  pfe_internal_error		PARAMS ((const char *));

/* Write out a precompiled header file for the current compiler state
   to the specified file.  A non-zero return code indicates a failure
   during the dump.
   
   The parameters to pfe_dump are the file in which to write the
   dump data, a "freezing" function for the dumper to call to freeze
   all the pointers in the dumped data structures, a pointer to the
   header containing the "roots" of the dumped data structures, and
   the size of that header.
   
   The header passed to the pfe_dump should have the "roots" of
   (i.e., pointers to) all of the compiler data structures in the
   load/dump file.  This data structure is used as the starting
   point for walking all of the compiler structures, "freezing"
   pointers (converting them to file offsets) during a dump, and
   "thawing" them (converting them back to pointers) during a load.
   Freezing takes place in the middle of a dump, after all of the
   memory ranges to be dumped have been identified and assigned
   file offset.  The header should be allocated in non-PFE memory
   (using malloc instead of pfe_malloc) because it will be written
   at a known place (the beginning) in the dump file.  (If it were
   allocated via pfe_malloc its location would be dependent on the
   implementation of the underlying memory manager.)  When the
   freezing function is called, it is passed a pointer to the
   header with the roots.  
   
   A refinement of the dump/load mechanism is the "no thaw load" 
   which assumes that the load file will be loaded at a specific
   address.  This is possible if the OS provides a call so that
   memory can be allocated at a requested address (in some address
   range that is distinguishable for the usual addresses returned
   by malloc).  When using this scheme, the dump file is frozen
   so that pointers are adjusted to this assumed address range.
   Frozen pointers are then identified by the fact that they lie
   in this range (above the assumed load address on Mac OS X),
   rather than by the usual method of turning on the low order bit
   of the pointer.  At load time, if the file can be loaded at
   the requested address, the thawing step can be skipped because
   the pointers will not require any fixing up.  If the file cannot
   be loaded at the assumed address, the load can continue with
   a thawing process that identifies frozen pointers by the fact
   that they lie in a range of addresses outside of the usual
   malloc range.
   
   Note: pfe_dump is a generic form of the dumping mechanism that
   can be called independently of the compiler for testing purposes.
   The pfe_dump_compiler_state function should be called to dump
   the compiler state.  This function assumes the compiler state
   is kept in the pfe_compiler_state record and that the
   pfe_freeze_compiler_state function is used to freeze the state
   information.  
   
   Note: the PFE allocation routines cannot be called after the
   freezing process of a dump has been started because allocated
   memory chunks are assigned dump file offsets before pointers
   can be frozen.  */
extern int pfe_dump 			PARAMS ((FILE *, void (*) (void *), void *, size_t));

/* Variant of pfe_dump which assumes that pfe_compiler_state_ptr is the
   pointer to the state header to be saved and that pfe_freeze_compiler_state
   is the function to be called to save (and "freeze") the compiler state.  
   This function allocates the compiler state header which gets filled
   in by the function freezing the compiler state.  */
extern int pfe_dump_compiler_state 	PARAMS ((FILE *));

/* Read in the precompiled header from the specified file.  Return a
   pointer to the header with compiler data structure "roots".  The
   data structures just loaded should then be "thawed" by walking
   all of the data structures pointed to by the roots and thawing
   all pointers.
   
   This is the generic function to do a load, and can be used to test
   the load/dump mechanism independent of the compiler.  The 
   pfe_load_compiler_state calls this function to load the compiler 
   state.  */
extern void *pfe_load 			PARAMS ((FILE *));

/* Read in the precompiled header from the specified file and restore
   the compiler state.  The pfe_thaw_compiler_state function will be
   called to thaw the data in the compiler state header and copy
   the appropriate values back to compiler globals.  */
extern void pfe_load_compiler_state 	PARAMS ((FILE *));

/* A "no thaw load" is possible if we can load the pre-compiled 
   header information at a pre-determined address, so that no thawing
   is required when loading.  */
#define PFE_NO_THAW_LOAD 1
#if PFE_NO_THAW_LOAD
#define PFE_NO_THAW_LOAD_ADDR 0x30000000
#endif

/* Determine whether a pointer has been frozen.  This assumes
   that there are no valid pointers to odd addresses.  */
#if PFE_NO_THAW_LOAD
#define PFE_IS_FROZEN(p) pfe_is_frozen (p)
extern int pfe_is_frozen		PARAMS ((void *));
#else
#define PFE_IS_FROZEN(p) ((int)(p) & 1)
#endif

/* Determine whether a pointer points into PFE allocated memory.  This
   routine will return a non-zero result if the pointer is to memory
   in the area managed by the PFE when dumping or in the load memory
   area when loading.  Otherwise zero will be returned, including 
   when the pointer is to memory allocated by the PFE during a load
   but outside of the load memory area (i.e., for stuff not in the
   load file) and for memory allocated via PFE calls when not doing
   a load or dump.  */
extern int pfe_is_pfe_mem 		PARAMS ((void *));

/* Convert a pointer to an offset so that it can be saved to a file.  
   The parameter is a pointer to the pointer in question.  Returns
   the original pointer before freezing.  */
extern void *pfe_freeze_ptr 		PARAMS ((void *));

/* Convert an offset to a pointer after it has been restored from a 
   file.  The parameter is a pointer to the offset that will be
   converted to a pointer.  Returns the thawed pointer.  */
extern void *pfe_thaw_ptr 		PARAMS ((void *));

/* If pfe_freeze_ptr() or pfe_thaw_ptr() is passed as a function
   pointer then they ore of the following function pointer type.  */
typedef void *(*pfe_freeze_thaw_ptr_t)(void *);

/* Function pointer to either pfe_freeze_ptr() or pfe_thaw_ptr()
   as determined by pfe_operation.  */
extern pfe_freeze_thaw_ptr_t pfe_freeze_thaw_ptr_fp;

/* Freeze/thaw a pointer (passed as a pointer to that pointer).
   Freezing or thawing is indicated by the setting of pfe_operation
   declared above.  */
extern void *pfe_freeze_thaw_ptr        PARAMS ((void **));

/* Macro used to call pfe_freeze_thaw_ptr().  */
#define PFE_FREEZE_THAW_PTR(p) pfe_freeze_thaw_ptr ((void **)(p))

/* Variant of pfe_freeze_thaw_ptr which allows ptrs to be slightly
   beyond the upper bound of a memory range.  The parameter n 
   specifies how far beyond the range a pointer may be to still be
   considered to be a part of that range.  
   
   In a few GCC structures there are pointers that point to the
   "limit" of the structure, which happens to be one byte beyond
   the end of the structure.  In some cases the pointer can end up
   pointing outside of PFE memory if the structure in question is
   allocated at the very end of a PFE memory range.  This means
   special consideration must be given to such pointers when they
   are being frozen and thawed to account for the fact that they
   would normally not appear to belong to any range.  For specific
   pointers known to have this property, this special variant of 
   pfe_freeze_thaw_ptr is called which allows small deviations from 
   the upper bound of memory ranges when freezing.  */

extern void *pfe_freeze_thaw_ptr_with_variance PARAMS ((void **, unsigned long));
#define PFE_FREEZE_THAW_PTR_WITH_VARIANCE(p, n) \
  pfe_freeze_thaw_ptr_with_variance ((void **)(p), n)

/* Convert what might be a frozen pointer to a real pointer.  Returns 
   non-frozen pointers as is.  This can be called during a load or a 
   dump.  (For internal diagnostic purposes.)  */
extern void *pfe_real_ptr 		PARAMS ((void *));

/* A pithy macro to access pfe_real_ptr().  (I question whether we
   want this macro in the global compiler namespace. -ff)  */
#define RP(p) (pfe_real_ptr (p))

/* Identify temporaries not in PFE memory being using during the
   freezing/thawing process.  Identifying the temporary currently
   in use will suppress diagnostics about trying to freeze/thaw
   a pointer not in PFE memory.  */
extern void pfe_using_temp_ptr		PARAMS ((void **));

/* PFE's malloc: allocates memory in a "zone" controlled by the PFE when 
   performing a dump, otherwise it calls the normal malloc.  */
extern void *pfe_malloc 		PARAMS ((size_t));

/* PFE's calloc: allocates and zeros memory for n objects in a "zone" 
   controlled by the PFE when performing a dump, otherwise it calls
   the normal calloc.  */
extern void *pfe_calloc 		PARAMS ((size_t, size_t));

/* PFE's realloc: reallocates memory for pointer in a "zone" 
   controlled by the PFE when performing a dump, otherwise it calls
   the normal realloc.  */
extern void *pfe_realloc 		PARAMS ((void *, size_t));

/* PFE's free: free memory in a "zone" controlled by the PFE when 
   performing a dump, otherwise it calls the normal free except
   when the memory is in the load memory area.  */
extern void pfe_free 			PARAMS ((void *));

#if PFE_MALLOC_STATS
/* A cover routine for PFE's malloc that keeps statistics about
   malloc allocations by kind and size.  */
extern void *pfe_s_malloc 		PARAMS ((size_t, enum pfe_alloc_object_kinds));

/* A cover routine for PFE's calloc that keeps statistics about
   calloc allocations by kind and size.  */
extern void *pfe_s_calloc 		PARAMS ((size_t, size_t, enum pfe_alloc_object_kinds));

/* A cover routine for PFE's realloc that keeps statistics about
   realloc allocations by kind and size.  */
extern void *pfe_s_realloc 		PARAMS ((void *, size_t, enum pfe_alloc_object_kinds));
#endif

/* When doing a dump, allocate a copy of the specified string using 
   pfe_malloc.  When not doing a dump, just return the original string.
   
   Caution: We use a hash table to ensure that there is only one copy
   of each unique string.  So don't use this to allocate strings that
   are not const.  */
extern void *pfe_savestring 		PARAMS ((char *));
#undef PFE_SAVESTRING
#define PFE_SAVESTRING(s) (pfe_savestring ((char *)(s)))

/* Freeze/thaw the compiler state.  */	
extern void pfe_freeze_compiler_state   PARAMS ((void *));
extern void pfe_thaw_compiler_state     PARAMS ((struct pfe_compiler_state *));

#define PFE_NEW_TREE_WALK 1
#if PFE_NEW_TREE_WALK
/* Add a tree node to the stack of nodes needing freezing/thawing.  */
extern void pfe_freeze_thaw_tree_push   PARAMS ((union tree_node **));

/* Freeze/thaw all of the tree nodes that have been pushed by
   pfe_freeze_thaw_tree_push and their descendents (i.e., walk the
   trees).  */
extern void pfe_freeze_thaw_tree_walk   PARAMS ((void));
#else
/* Freeze/thaw tree node and its direct descendents (i.e., walk tree).  */
extern void pfe_freeze_thaw_tree_walk   PARAMS ((union tree_node **));
#endif

#if PFE_NEW_TREE_WALK
/* Macro to call freeze_thaw_tree_push().  Note that the macro is 
   passed just the node, not the address of the node.  The address 
   is taken here.  */
#define PFE_FREEZE_THAW_WALK(node) pfe_freeze_thaw_tree_push(&(node))
#else
/* Macro to make calling freeze_thaw_tree_walk().  Note that the
   macro is passed just the node, not the address of the node.  The
   address is taken here.  */
#define PFE_FREEZE_THAW_WALK(node) pfe_freeze_thaw_tree_walk(&(node))
#endif

/* Freeze/thaw a rtl node.  */
extern void pfe_freeze_thaw_rtx		PARAMS ((struct rtx_def **));

/*                       *** 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.  */
   
/* Macro to make calling pfe_freeze_thaw_rtx().  Note that the macro
   is passed just the rtx (ptr to rtl entry), not the address of the
   entry.  The address is taken here.  */
#define PFE_FREEZE_THAW_RTX(rtx) pfe_freeze_thaw_rtx (&(rtx))

/* Freeze/thaw a rtvec node.  */
extern void pfe_freeze_thaw_rtvec	PARAMS ((struct rtvec_def **));

/* Freeze/thaw various data objects.  */

/* pfe/c-common-freeze-thaw.c  */
extern void pfe_freeze_thaw_common_language_function PARAMS ((struct language_function *));

/* c-decl.c or cp-freeze-thaw.c  */
extern void pfe_freeze_thaw_language_function PARAMS ((struct language_function **));

/* cp/decl.c or c-decl.c  */
extern void pfe_freeze_thaw_binding_level PARAMS ((struct binding_level **));

/* cp/decl.c  */
extern void pfe_freeze_thaw_named_label_use_list PARAMS ((struct named_label_use_list **));
extern void pfe_freeze_thaw_named_label_list     PARAMS ((struct named_label_list **));

/* cp/spew.c  */
extern void pfe_freeze_thaw_unparsed_text PARAMS ((struct unparsed_text **));

/* function.c - freeze/thaw a  pointer to a struct function.  */
extern void pfe_freeze_thaw_function	         PARAMS ((struct function **));

/* Macros to make it a  little "easier" (?) to move globals between the pfe
   headedr and compiler globals.  */
   
#define PFE_GLOBAL_TO_HDR_IF_FREEZING(g) if (PFE_FREEZING) hdr->g = g
#define PFE_HDR_TO_GLOBAL_IF_THAWING(g)  if (PFE_THAWING)  g = hdr->g

#define PFE_FREEZE_THAW_GLOBAL_TREE(g) \
  do { \
    PFE_GLOBAL_TO_HDR_IF_FREEZING (g); \
    PFE_FREEZE_THAW_WALK (hdr->g); \
    PFE_HDR_TO_GLOBAL_IF_THAWING (g); \
  } while (0)

#define PFE_GLOBAL_TREE_ARRAY_TO_HDR(g, n) \
  if (PFE_FREEZING) memcpy (hdr->g, g, (int)(n) * sizeof (tree))
#define PFE_HDR_TO_GLOBAL_TREE_ARRAY(g, n) \
  if (PFE_THAWING) memcpy (g, hdr->g, (int)(n) * sizeof (tree))
  
#define PFE_FREEZE_THAW_GLOBAL_TREE_ARRAY(g, n) \
  do { \
    PFE_GLOBAL_TREE_ARRAY_TO_HDR(g, n); \
    for (i = 0; i < (int)(n); ++i) PFE_FREEZE_THAW_WALK (hdr->g[i]); \
    PFE_HDR_TO_GLOBAL_TREE_ARRAY(g, n); \
  } while (0)

#define PFE_FREEZE_THAW_GLOBAL_RTX(g) \
  do { \
    PFE_GLOBAL_TO_HDR_IF_FREEZING (g); \
    PFE_FREEZE_THAW_RTX (hdr->g); \
    PFE_HDR_TO_GLOBAL_IF_THAWING (g); \
  } while (0)

#define PFE_GLOBAL_RTX_ARRAY_TO_HDR(g, n) \
  if (PFE_FREEZING) memcpy (hdr->g, g, (int)(n) * sizeof (struct rtx_def *))
#define PFE_HDR_TO_GLOBAL_RTX_ARRAY(g, n) \
  if (PFE_THAWING) memcpy (g, hdr->g, (int)(n) * sizeof (struct rtx_def *))
  
#define PFE_FREEZE_THAW_GLOBAL_RTX_ARRAY(g, n) \
  do { \
    PFE_GLOBAL_TREE_ARRAY_TO_HDR(g, n); \
    for (i = 0; i < (int)(n); ++i) PFE_FREEZE_THAW_RTX (hdr->g[i]); \
    PFE_HDR_TO_GLOBAL_TREE_ARRAY(g, n); \
  } while (0)
  
#ifndef DMP_TREE /* don't define these in the xx-dmp-tree.[ch] routines. */
/* The following overrides any uses of VARRAY_FREE defined in varray.h.  This
   allows us to decide which memory the varray_type was allocated in.  This
   also means that this header MUST follow varray.h in any file which includes
   both these headers.  Note, the referenced routines are all in varray.c.  */
#undef VARRAY_FREE
#define VARRAY_FREE(vp) \
  do { if (vp) { pfe_varray_free (vp); vp = (varray_type)0; } } while (0)
extern void pfe_varray_free 	        PARAMS ((struct varray_head_tag *));
extern void pfe_freeze_thaw_varray_tree PARAMS ((struct varray_head_tag **));
#endif

#endif /* PFE */
#endif /* GCC_PFE_H */
