/* Copyright (C) 1997-1999 NEC Research Institute.
 * Please see the file LICENSE for license information.
 */
#ifndef _MLTON_GC_H
#define _MLTON_GC_H
/*
 * A stop-and-copy GC with a fixed size heap.
 * Handles fixed size objects and arrays.
 */

#include <stdio.h>
#include <stdlib.h>

#include "my-lib.h"

typedef uint word;
typedef char* pointer;

/* sizes (almost) always measured in bytes */
#define WORD_SIZE 4
#define POINTER_SIZE WORD_SIZE
#define GC_OBJECT_HEADER_SIZE WORD_SIZE
#define GC_ARRAY_HEADER_SIZE (WORD_SIZE + GC_OBJECT_HEADER_SIZE)

#define HIGH_BIT 0x80000000
#define NON_POINTERS_SHIFT 16

/* Build the one word header for an object, given the number of words of
 * nonpointers and the number of pointers.
 */
#define GC_objectHeader(np,p) (HIGH_BIT | ((np) << NON_POINTERS_SHIFT) | (p))

/* Build the one word header for an array, given the number of bytes of
 * nonpointers and the number of pointers.
 */
#define GC_arrayHeader(np,p) (((np) << NON_POINTERS_SHIFT) | (p))

/* ------------------------------------------------- */
/*                   GC_isPointer                    */
/* ------------------------------------------------- */

/* Returns true if p looks like a pointer, i.e. if p = 0 mod 4. */
static inline bool GC_isPointer(pointer p) {
	return(0 == ((word)p & 0x3));
}

/* ------------------------------------------------- */
/*                     wordAlign                     */
/* ------------------------------------------------- */

#define wordAlign(p) ((1 + (((p) - 1) >> 2)) << 2)

/* ------------------------------------------------- */
/*                  GC_frameLayout                   */
/* ------------------------------------------------- */

typedef ushort *GC_offsets;

typedef struct GC_frameLayout {
  /* Number of bytes in frame, including space for return address. */
  ushort numBytes;
  /* Offsets from stackTop pointing at bottom of frame at which pointers are
   * located. 
   */
  GC_offsets offsets;
} GC_frameLayout;

/* ------------------------------------------------- */
/*                     GC_state                      */
/* ------------------------------------------------- */

typedef struct GC_state {
	/* heap */
	uint fromSize;     /* size (bytes) of from space */
	pointer base;      /* start (lowest address) of from space */
	pointer frontier;  /* base <= frontier < limit */
	pointer limit;
	uint toSize;       /* size (bytes) of to space */
	pointer toBase;    /* start (lowest address) of to space */
	
	/* globals */
	uint numGlobals;
	pointer *globals;             /* an array of size numGlobals */
	
	/* locals */
	pointer *locals;              /* an array */
	GC_offsets localOffsets;      /* indices into locals */
	
	/* stack */
	uint stackSize;
	pointer stackTop;
	pointer stackBottom;
	pointer stackLimit;	/* stackBottom + stackSize - maxFrameSize */
	uint maxFrameIndex;     /* 0 <= frameIndex < maxFrameIndex */
	GC_frameLayout *frameLayouts;
	uint maxFrameSize;
	uint exnStack;     /* An offset added to stackBottom that specifies 
			    * where the top of the exnStack is.
			    */
	
	int bytesLive;     /* number of bytes copied by the most recent GC */
	
	/* Resizing contols (expressed as ratios of heapSize / live data) */
	uint minLive;      /* shrink heap when bytesLive * minLive < heapSize */
	uint maxLive;      /* grow heap when bytesLive * maxLive > heapSize */
	uint liveRatio;    /* when resizing, set heapSize to the smallest multiple
	* of pages greater than liveRatio * numBytesLive
	*/
	bool useFixedHeap; /* if true, then don't resize the heap */
	uint maxHeapSize;  /* if (not useFixedHeap), then s->fromSize <= maxHeapSize
			    * and s->toSize <= maxHeapSize
			    */

	/* print out a message at the start and end of each gc */
	bool messages;

	/* only used during GC */
	uint bytesRequested;  /* How much must be free after GC in order for 
			       * computation to proceed.
			       */
	uint recentStart; /* start time of most recent GC */
	pointer front;    /* front of toSpace queue */
	pointer back;     /* always points at the next avaiable word in toSpace */

	/* ------------------------------------------------- */
 	/*               gc-summary statistics               */
 	/* ------------------------------------------------- */
	bool summary; /* print a summary of gc info when the program is done */
	ullong bytesAllocated;
 	ullong bytesCopied;
 	uint numGCs; 
 	uint gcTime;    /* total amount of time spent in gc in milliseconds */
 	uint startTime; /* the time when GC_init or GC_loadWorld is called */
	uint maxHeapSizeSeen;
	uint maxBytesLive;

} GC_state;

/* ------------------------------------------------- */
/*                      GC_init                      */
/* ------------------------------------------------- */

/* GC_init must be called before doing any allocation.
 * It must also be called before MLTON_init, GC_createStrings, and GC_createIntInfs.
 * Before calling GC_init, you must initialize:
 *   numGlobals
 *   globals 
 *   locals
 *   maxFrameIndex
 *   frameLayouts
 *   maxFrameSize
 *   useFixedHeap
 * if (useFixedHeap)
 *   then fromSize should be set to the semispace size
 *   else fromSize be set to the initial amount of live data that will be placed
 *          into the heap (e.g. with GC_createStrings).  The initial heap size will
 *          be set to fromSize * s->liveRatio.
 *        maxHeapSize should be set to 0 if you want it to be figured out
 *          automatically, otherwise set it to what you want.
 */
void GC_init(GC_state *state);

/* ------------------------------------------------- */
/*                   GC_growStack                    */
/* ------------------------------------------------- */

/* Grow the stack, copying the old stack to the new stack, updating the state,
 * and updating all of the internal exnStack pointers.
 */
void GC_growStack(GC_state *state);

/* ------------------------------------------------- */
/*                      GC_done                      */
/* ------------------------------------------------- */

/* GC_done should be called after the program is done.
 * munmaps heap and stack.
 * Prints out gc statistics if s->summary is set.
 */
void GC_done(GC_state *state);

/* ------------------------------------------------- */
/*                       GC_gc                       */
/* ------------------------------------------------- */

/* Do a gc.
 * Before calling GC_gc, must set
 *   locals
 *   localOffsets 
 *   bytesRequested
 */
void GC_gc(GC_state *state);

/* ------------------------------------------------- */
/*                      GC_size                      */
/* ------------------------------------------------- */

/* Compute the size of the graph pointed to by root by performing a partial
 * GC with root as the only root.
 * Then, finish the GC.
 * Returns the size of the graph pointed to by root.
 */
uint GC_size(GC_state *s, pointer root);

/* ------------------------------------------------- */
/*                 GC_createStrings                  */
/* ------------------------------------------------- */

struct GC_stringInit {
  uint globalIndex;
  char *str;
  uint size;
};

/*  The inits array should be NULL terminated.
 *  I.E. the final element should be {0, NULL, 0}.
 */
void GC_createStrings(GC_state *state, struct GC_stringInit inits[]);

/* ------------------------------------------------- */
/*                GC_arrayNumElements                */
/* ------------------------------------------------- */

/* the array size is stored before the header */

#define GC_arrayNumElements(a) (*((uint*)(a) - 2))

/* ------------------------------------------------- */
/*                GC_arrayNumElements                */
/* ------------------------------------------------- */

static inline void GC_arrayShrink(pointer array, uint numElements) {
  GC_arrayNumElements(array) = numElements;
}

/* ------------------------------------------------- */
/*                   GC_saveStack                    */
/* ------------------------------------------------- */
pointer GC_saveStack(GC_state *s);

/* ------------------------------------------------- */
/*                  GC_restoreStack                  */
/* ------------------------------------------------- */
void GC_restoreStack(GC_state *s, pointer p);

/* ------------------------------------------------- */
/*                   GC_saveWorld                    */
/* ------------------------------------------------- */

void GC_saveWorld(GC_state *state, uint magic, pointer fileName, void (*saveGlobals)(FILE *file));

/* ------------------------------------------------- */
/*                   GC_loadWorld                    */
/* ------------------------------------------------- */

void GC_loadWorld(GC_state *state, 
			uint magic,
			char *fileName,
			bool heapSizeCommandLine,
			void (*loadGlobals)(FILE *file));

#endif /* #ifndef _MLTON_GC_H */
