/* $Header:storage.c 12.0$ */
/* $ACIS:storage.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/c2_ca/RCS/storage.c,v $ */

#ifndef lint
static char *rcsid = "$Header:storage.c 12.0$";
#endif

/*

  ####    #####   ####   #####     ##     ####   ######           ####
 #          #    #    #  #    #   #  #   #    #  #               #    #
  ####      #    #    #  #    #  #    #  #       #####           #
      #     #    #    #  #####   ######  #  ###  #        ###    #
 #    #     #    #    #  #   #   #    #  #    #  #        ###    #    #
  ####      #     ####   #    #  #    #   ####   ######   ###     ####

 */

/* Remove comments from around following define to compile in
   the (very) verbose debug statements for this program.               */
/*
#define DEBUGSTG
 */

/*
     Storage management subroutines.


     J. C. O'Quin;  September 1984
     (c) 1984 by IBM; All Rights Reserved


   Because the optimizer was spending a considerable fraction of its
   time in malloc, this simplified storage management package was
   written.  It takes advantage of the fact that the optimizer allocates
   all storage for a function while reading and optimizing it.  All
   frees occur after processing for that function is complete.  This
   permits a simple, stack approach.  Storage is handed out at the
   end of the stack, and is never explicitly freed.  Whenever a new
   function is to be processed, init_storage is called to reset the
   stack to an empty state.  This approach has good locality of
   reference and extremely short pathlengths.

   Rather than allocate an extremely large stack area that might
   not be needed, chunks of storage called subpools are obtained.
   When the current subpool is exhausted, a new one is gotten
   via malloc.  If no more space is available, the process is abended.
   Any subpools that were allocated for the previous function are
   retained for use with the next.
*/

#include <stdio.h>
#include "opt.h"
#include "error.h"


/*
   Global storage management data.
*/

struct sp {                             /* subpool header mapping       */
   struct sp *next;                     /* next subpool allocated       */
   int       size;                      /* size of this subpool         */
};

static struct sp *sp_first   = NULL;    /* first subpool allocated      */
static struct sp *sp_current = NULL;    /* subpool currently in use     */
static char *sp_next  = NULL;           /* next available byte          */
static char *sp_limit = NULL;           /* current subpool limit        */



/*
   Allocate a new subpool.

   Output: function value points to new subpool allocated.
      new subpool's header filled in.

   Note:   This routine always tries to allocate as big a subpool
      as possible.  If it fails it tries to get a subpool
      that is half as big, continuing until the minimum size
      cannot be obtained.
*/
#define SP_MAX 8192                     /* maximum size for a subpool   */
#define SP_MIN  512                     /* minimum size for a subpool   */

static struct sp *
allocate_sp()
{
   static int sp_size = SP_MAX;         /* size of subpools to allocate */
   static char alloc_msg[] = "(0x________ bytes allocated)";
   register struct sp *new;             /* pointer to new subpool       */
   register int times;

   times = 0;
   do {
      new = (struct sp *) malloc( sp_size );
      times++;
   } while ( new == NULL && (sp_size >>= 1) >= SP_MIN  && times < 11);
   if (times > 10) {
      fprintf(stderr,"(c2) ERROR in memory allocation\n");
      fflush(stderr);
      exit(E_STORAGE);
   }

   if (new == NULL)  {
      sprintf(alloc_msg, "(0x%lx bytes allocated)", (long) c.storage );
      fprintf(stderr,"(c2) ERROR %s\n",alloc_msg);
      fflush(stderr);
/*    exit(E_STORAGE, alloc_msg ); */
      exit(E_STORAGE);
   }
   new->next = NULL;                    /* fill in subpool header       */
   new->size = sp_size;
   c.subpools++;                        /* count subpools allocated     */
   c.storage += sp_size;                /* count storage allocated      */

   if (Debug)  fprintf(stderr, "\nSubpool %ld of %d bytes allocated.\n",
                      (long) c.subpools, sp_size);

   return new;
}


/*
   Allocate storage.

   This routine is called from macros GET_STRING, GET_SNODE and GET_LABEL,
   depending on the type of storage required.  The macros pass any
   alignment that is required and cast the returned pointer to the correct
   type, and should always be used instead of calling alloc_stg directly.

   Input:  size = number of bytes to allocate.
      align = TRUE if word boundary alignment required.

   Output: function value is pointer to storage allocated.

   Note:   The size requested must never exceed the minimum subpool
      size minus the size of the subpool header.
      (If it does, then SP_MIN was too small.)
*/
   char *
alloc_stg(size, align)
register int size;                      /* number of bytes to allocate  */
register int align;                     /* log base 2 of alignment      */
{
   register char *ptr;                  /* pointer to storage allocated */
   register struct sp *new;             /* pointer to new subpool       */
   static char req_msg[] = "(9999999999-byte request too large)";

   if (align)                           /* force to four-byte boundary  */
      sp_next = (char *) ( (int) sp_next+3 >> 2 << 2 );
   ptr = sp_next;                       /* try to get from current sp   */

   if ( (sp_next += size) > sp_limit) { /* out of space in current sp?  */

      new = sp_current->next;           /* see if there's another sp    */
      if ( new == NULL)  {              /* no more subpools?            */
    new = sp_current->next = allocate_sp();
      }
      sp_current = new;                 /* make new subpool current     */
      sp_limit = (char *) new + new->size;
      ptr = (char *) new + sizeof(struct sp);
      sp_next = ptr + size;
      if ( sp_next > sp_limit )  {      /* request exceeds new subpool? */
         sprintf(req_msg, "(%d-byte request too large)", size );
	 fprintf(stderr,"(c2) ERROR %s\n",req_msg);
	 fflush(stderr);
/*       exit(E_STORAGE, req_msg ); */
         exit(E_STORAGE);
      }
   }

#ifdef DEBUGSTG
   if (Debug)  fprintf(stderr, "Allocated %d bytes at 0x%lx\n", size, (long) ptr);
#endif DEBUGSTG

   return ptr;
}


/*
   Initialize storage for processing of a function.

   Note:   Storage initialization has the effect of freeing all
      storage that had been allocated (left over from the
      previous function).  No explict free request is ever made.
*/
init_storage()
{
   if (sp_first == NULL)                /* first time through?          */
      sp_first = allocate_sp();         /* allocate first subpool       */

   sp_current = sp_first;               /* make first subpool current   */
   sp_limit = (char *) sp_first + sp_first->size;
   sp_next  = (char *) sp_first + sizeof(struct sp);
}
