/* external.c
   User level functions. (Externally visible.) 
*/

#include "prototypes.h"
#include "pageHeader.h"
#include "header.h"
#include "machine.h"

#include <assert.h>
#include <stdlib.h>

GcInfo gcInfo;

#ifdef REGISTER_ALLOCATE
register Word *gcAllocPtr asm ("g6");
#else
Word *gcAllocPtr;
#endif

/* Establish the bottom of the stack, setup the heap, .... The size is in
   words. */
void gcInit(unsigned int initialHeapSize)
{
  PageInfo *pi;
  unsigned int i;
  DECL_TIMER_ALWAYS(tInit);

  /* Simple tests to make sure the configuration is alright. With any kind of
     optimization this will all be eliminated.  But its better than assertions
     since it will fire even with -DNDEBUG. */
  if(sizeof(PageInfo)!=PI_SIZE_BYTES)
    {
      LOG_MESSAGE_ALWAYS("PageInfo size appears wrong.  PageInfo macros won't \
work.\n");
      exit(1);
    }

  if(~ALL_ONES != 0)
    {
      LOG_MESSAGE_ALWAYS("ALL_ONES incorrect.");
      exit(1);
    }

  if(((0x1 << (WORD_SIZE-1)) == 0) ||
     (((~(0x1 << (WORD_SIZE-1))) >> (WORD_SIZE - 1)) != 0))
    {
      LOG_MESSAGE_ALWAYS("WORD_SIZE incorrect.");
      exit(1);
    }

  if(WORD_SIZE != (0x1 << LOG_WORD_SIZE))
    {
      LOG_MESSAGE("LOG_WORD_SIZE incorrect.");
      exit(1);
    }

  if(PAGE_SIZE != (0x1 << LOG_PAGE_SIZE))
    {
      LOG_MESSAGE_ALWAYS("LOG_PAGE_SIZE incorrect or PAGE_SIZE != 2^n.");
      exit(1);
    }
  
  if((0x1 << OFFSET_SHIFT) <= PAGE_MODIFIED)
    {
      LOG_MESSAGE_ALWAYS("OFFSET_SHIFT too small.");
      exit(1);
    }

 INIT_ACCUM_ALWAYS(gcInfo.collectorTime);
 START_TIMER_ALWAYS(gcInfo.totalTime);
 START_TIMER_ALWAYS(tInit);

 INIT_ACCUM_ALWAYS(gcInfo.tAllocTime);
 gcInfo.nAllocCalls = 0;

 gcInfo.nCollections = 0;

 STATS(gcInfo.nLargeObjectPages = 0);
 STATS(gcInfo.nLiveObjects = 0);
 STATS(gcInfo.nLiveWords = 0);
 STATS(gcInfo.nCObjects = 0);
 STATS(gcInfo.nPinnedObjects = 0);
 STATS(gcInfo.nTraversedObjects = 0);
 STATS(gcInfo.nRoots = 0);

 STATS(gcInfo.nSystemPages = 0);
 STATS(gcInfo.nWastedSpace = 0);

 STATS(gcInfo.nObjectsAllocated = 0);
 STATS(gcInfo.nWordsAllocated = 0);

 BLACKLIST(gcInfo.nBlackListedPages = 0);

 gcInfo.nPages = 0;
 gcInfo.nPagesInUse = 0;
 gcInfo.nPagesAllocated = 0;

 setStaticBoundary();

 gcInfo.heap = NULL;
 gcInfo.heapEnd = NULL;
 gcInfo.blockHead = NULL;
 gcInfo.blockTail = NULL;
 gcInfo.nBlocks = 0;
 gcInfo.heapSize = 0;

 LIST_INIT(&gcInfo.fromSpaceList);
 LIST_INIT(&gcInfo.toSpaceList);
 LIST_INIT(&gcInfo.freeList);
 LIST_INIT(&gcInfo.pinnedPageList);

#ifdef PAGE_BLACKLISTING
 LIST_INIT(&gcInfo.blackList);
#endif
 
 for(i=0;i<BITMASK_WORD_OFFSET(MAXIMUM_BLOCKS);i++)
   {
     gcInfo.blockBitmask[i]=0;
   }

#ifdef HARD_LIMIT
 gcInfo.hardLimit = 0;
#endif

 if(initialHeapSize==0)
   {
     initialHeapSize = getHeapSizeFromEnv();
   }

 while(gcInfo.heapSize < initialHeapSize)
   {
     allocateHeapBlock();
   }
#ifdef HARD_LIMIT
 gcInfo.hardLimit = 1;
#endif

#ifdef GENERATIONS
 gcInfo.collectionBoundary = 256;

 if(NUM_GENERATIONS==2)
   {
     Generation *generation;
     
     for(i=0;i<NUM_GENERATIONS;i++)
       {
	 generation = &gcInfo.generation[i];
	 LIST_INIT(&(generation->space));
	 generation->cObjects = NULL;
	 generation->nPages = 0;
	 STATS(generation->nCollections=0);
       }
     STATS(gcInfo.nBasicCollections=0);
   }
 else
   {
     printf("Need to upgrade code in external.c to deal generations != 4\n");
     exit(0);
   }
 gcInfo.volatileObjects = newDeque();
#else
 gcInfo.collectionBoundary = (2*gcInfo.nPages)/3;
#endif

 gcInfo.cObjects = newDeque();
 gcInfo.explicitQueue = NULL;
 
 gcAllocPtr = NULL;

#ifdef PAGE_BLACKLISTING
 PUSH_REGS;
 STACK_TOP(gcInfo.stackTop);
 collect();
#endif

 pi = reservePage(PAGE_USER,NULL);
 LIST_INSERT_BEGIN(&gcInfo.fromSpaceList,pi);
 
 gcAllocPtr = PI_TO_PAGE(gcInfo.fromSpaceList.next);

 STACK_TOP(gcInfo.stackTop);

#ifdef STACK_INCREASING
     gcInfo.clearedStackBottom = gcInfo.stackBottom;
     gcInfo.clearedStackTop = gcInfo.stackBottom + STACK_INITIAL_CLEARANCE;
#else
     gcInfo.clearedStackBottom = gcInfo.stackBottom;
     gcInfo.clearedStackTop = gcInfo.stackBottom - STACK_INITIAL_CLEARANCE;
#endif

     gcInfo.clearStackCount = 0;

     gcInfo.goodStack = 1000;
     gcInfo.currentBestStack = 1000; /* Arbitrary values to begin with. */
     gcInfo.currentAttempt = 0;
     gcInfo.maxAttempt = gcInfo.nPages/100;

     END_TIMER_ALWAYS(tInit);
     ADD_ACCUM(gcInfo.collectorTime,tInit);

}

/* Plug-in replacement for malloc. */
void *gcMalloc(int bytes)
{
  int words;

  words = bytes/sizeof(Word) + ((bytes % sizeof(Word)) ? 1 : 0);

  return (void *)gcAllocHeader(GC_CREATE_COBJ(words,0));
}

/* Plug-in replacement for malloc for allocating objects that don't contain any
   pointers. */
void *gcMallocPtrFree(int bytes)
{
 int words;

 words = bytes/sizeof(Word) + ((bytes % sizeof(Word)) ? 1 : 0);

 return (void *)gcAllocHeader(GC_CREATE_COBJ(words,1));

}

/* Sets the global gcAllocPtr. */
void gcAllocSpace(void)
{
 unsigned int stackSize;
 PageInfo *pi;
 Word *gcLimitPtr;
 int wastedSpace;

 DECL_TIMER_ALWAYS(timeAlloc);
 START_TIMER_ALWAYS(timeAlloc);
 gcInfo.nAllocCalls ++;

 /* FMS : I changed this code because it looked wrong.  In particular I 
    added the -1 in the following line, and distinguished the case of 0
    from the negative case which was previously overlooked.
    */
 gcLimitPtr = GC_LIMIT_PTR;
 wastedSpace = (gcLimitPtr) - gcAllocPtr - 1;
 if(wastedSpace>0)
   *gcAllocPtr = GC_CREATE_JUNK(wastedSpace);
 else
   {
    if(wastedSpace==0) *gcAllocPtr = GC_EMPTY;
   }

 STATS(gcInfo.nWastedSpace += wastedSpace+1);

#ifdef GENERATIONS
 if(gcInfo.nPagesAllocated > gcInfo.collectionBoundary)
#else
 if(gcInfo.nPagesInUse > gcInfo.collectionBoundary)
#endif
   {
     STACK_TOP(gcInfo.stackTop);
     stackSize = STACK_SIZE;

     if(stackSize < gcInfo.currentBestStack)
       {
	 gcInfo.currentBestStack = stackSize;
       }

     if(stackSize <= gcInfo.goodStack || 
	gcInfo.currentAttempt > gcInfo.maxAttempt)
       {
	 gcInfo.goodStack = 2*gcInfo.currentBestStack;
	 gcInfo.currentBestStack = 2*gcInfo.currentBestStack;
	 gcInfo.currentAttempt = 0;
	 gcAllocPtr = NULL;
	 PUSH_REGS;
	 STACK_TOP(gcInfo.stackTop);
	 collect();
       }
     else
       {
	 gcInfo.currentAttempt++;
       } 
   }

 if((Word *)BLOCK(gcLimitPtr) != gcLimitPtr) 
   /* if we are still in the same heapblock */
   pi = PAGE_TO_PI(gcLimitPtr);
 else
   pi = NULL;
 
 pi=reservePage(PAGE_USER,pi);
 
 LIST_INSERT_END(&gcInfo.fromSpaceList,pi);

 gcAllocPtr = PI_TO_PAGE(pi);

 gcInfo.nPagesAllocated++;

 if(gcInfo.clearStackCount > CLEAR_STACK_FREQUENCY)
     clearStack();
 
 gcInfo.clearStackCount++;

 END_TIMER_ALWAYS(timeAlloc);
 ADD_ACCUM_ALWAYS(gcInfo.tAllocTime,timeAlloc);

}

Word *gcAllocHeader(Word header)
{
 int align;
 int size;
 Word *ptr;

 size = GC_OBJECT_SIZE(header) + 1;

 if(GC_MUST_BE_ALIGNED(header))
     align=1;
 else
   align=0;
 
  if(size < (PAGE_SIZE-align)) /* In case we need to align it. */
   {
     if(DOUBLEWORD_ALIGNED(gcAllocPtr))
	size+=align;
     else
       align=0;

     STATS(gcInfo.nWordsAllocated+=size-1);
     STATS(gcInfo.nObjectsAllocated++);

     if(GC_NEED_SPACE(size))
       {
	 gcAllocSpace();

	 if(align) size--;

	 if(GC_MUST_BE_ALIGNED(header) && DOUBLEWORD_ALIGNED(gcAllocPtr))
	   {
	     align=1;
	     size++;
	   }
	 else
	   align=0;

       }
     if(size>COPYABLE_OBJECT)
       {
	 GC_PIN(header);
       }

     if(align) 
       {
	 STATS(gcInfo.nWastedSpace++);
	 *gcAllocPtr=GC_EMPTY;
	 size--;
	 gcAllocPtr++;
       }
     *gcAllocPtr = header;
     ptr = gcAllocPtr + 1;
     gcAllocPtr += size;

     if(GC_EXTRACT_TYPE(header)==GC_COBJ)
       {
	 enqueue(gcInfo.cObjects,(Word)ptr);
	 STATS(gcInfo.nCObjects++);
       }

     return ptr;
   }
 else
   {     
     if(align) size++;

     ptr = gcAllocLarge(size);
     GC_PIN(header);

     if(align)
       {
	 STATS(gcInfo.nWastedSpace++);

	 if(DOUBLEWORD_ALIGNED(ptr))
	   {
	     *ptr = GC_EMPTY;
	     ptr++;
	   }
	 else
	   {
	     *(ptr+size-1)=GC_EMPTY;
	   }
       }

     *ptr = header;
     ptr++;

     if(GC_EXTRACT_TYPE(header)==GC_COBJ)
       {
	 STATS(gcInfo.nCObjects++);
	 enqueue(gcInfo.cObjects,(Word)ptr);
       }

     return ptr;
   }
}

/* Call to allocate large objects(size>page). words includes the header
   words. The user should pin objects this large by default. */
Word *gcAllocLarge(unsigned int words)
{
 unsigned int pages;
 Word *temp;
 unsigned int i;
 unsigned int residue; /* Words on the last page. */
 PageInfo *pi, *pi2; 
 DECL_TIMER_ALWAYS(timeAllocLarge);

 STATS(gcInfo.nWordsAllocated+=words);
 STATS(gcInfo.nObjectsAllocated++);

 residue = words % PAGE_SIZE;
 pages = words/PAGE_SIZE + ((residue) ? 1 : 0);

 if(pages<=1) /* They just want multiple header words its not actually large. */
   {
     if((GC_LIMIT_PTR - gcAllocPtr) <= words)
       {
	 gcAllocSpace();
       }

     temp = gcAllocPtr;
     gcAllocPtr += words;
     return temp;
   }

 START_TIMER_ALWAYS(timeAllocLarge);

 pi = reserveNPages(pages,PAGE_USER);
 gcInfo.nPagesAllocated += pages;
 STATS(gcInfo.nLargeObjectPages+=pages);

 assert(pi!=NULL); /* We were successful in finding the pages we wanted. */

 for(pi2=pi+1, i=1; i<pages; i++, pi2++)
   {
     PAGE_SET_OFFSET(pi2->header, NO_HEADER_OFFSET);  
   }
 pi2--;
 if(pi!=pi2 && residue!=0) PAGE_SET_OFFSET(pi2->header,residue);

 if(residue!=0)
   {
     temp=PI_TO_PAGE(pi2) + residue;
     *temp = GC_CREATE_JUNK(PAGE_SIZE - residue);
   }

 assert(pi2->next == NULL);
 LIST_APPEND_DOUBLY_LINKED(&gcInfo.fromSpaceList,pi,pi2);

 END_TIMER_ALWAYS(timeAllocLarge);
 ADD_ACCUM_ALWAYS(gcInfo.tAllocTime,timeAllocLarge);

 return PI_TO_PAGE(pi);
}

/* EOF: external.c */

/* (c) Frederick Smith, Greg Morrisett.
 *     October 1998, all rights reserved.
 *
 *
 *              Copyright 1990-1993 Digital Equipment Corporation
 *                         All Rights Reserved
 *
 * Permission to use, copy, and modify this software and its documentation is
 * hereby granted only under the following terms and conditions.  Both the
 * above copyright notice and this permission notice must appear in all copies
 * of the software, derivative works or modified versions, and any portions
 * thereof, and both notices must appear in supporting documentation.
 *
 * Users of this software agree to the terms and conditions set forth herein,
 * and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
 * right and license under any changes, enhancements or extensions made to the
 * core functions of the software, including but not limited to those affording
 * compatibility with other hardware or software environments, but excluding
 * applications which incorporate this software.  Users further agree to use
 * their best efforts to return to Digital any such changes, enhancements or
 * extensions that they make and inform Digital of noteworthy uses of this
 * software.  Correspondence should be provided to Digital at:
 * 
 *                       Director of Licensing
 *                       Western Research Laboratory
 *                       Digital Equipment Corporation
 *                       250 University Avenue
 *                       Palo Alto, California  94301  
 * 
 * This software may be distributed (but not offered for sale or transferred
 * for compensation) to third parties, provided such third parties agree to
 * abide by the terms and conditions of this notice.  
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
