
/****************************************************************************
 *
 *  alloca() implementation for SAS/C
 *
 *  Copyright: PUBLIC DOMAIN
 *  Written by Stefan Proels <proels@fmi.uni-passau.de>
 *
 */

#include <stddef.h>
#include <stdlib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <clib/alib_protos.h>
#include <constructor.h>


#ifndef __SASC
#error Wrong compiler (SAS/C required)
#endif

#ifdef _PROFILE
#error This file must NOT be compiled with PROFILE enabled
#endif


/*
 * each alloca()ted block is preceded by this header
 */
struct block
  {
    struct block *down;
    ULONG size;
    ULONG level;
    /* alloca()ted memory below */
  };

static APTR pool = NULL;
static struct block *top = NULL;
ULONG __alloca_virtual_SP = 0;


/*
 * SAS/C autoinitialization function
 */
PROFILE_CONSTRUCTOR(Sprof)
{
  if (pool = LibCreatePool(MEMF_ANY, 4096, 2048))
    return 0;
  return -1;
}

/*
 * SAS/C autotermination function
 */
PROFILE_DESTRUCTOR(Sprof)
{
  if (pool)
    LibDeletePool(pool);
}


/*
 * called whenever a function is entered
 */
void __asm _PROLOG(register __a0 char *dummy)
{
  __alloca_virtual_SP++;
}

/*
 * called on return from a function
 */
void __asm _EPILOG(register __a0 char *dummy)
{
  struct block *mem;

  for (mem = top; mem && mem->level >= __alloca_virtual_SP; mem = top)
    {
      top = mem->down;
      LibFreePooled(pool, mem, mem->size);
    }
  --__alloca_virtual_SP;
}


/*
 * that's what the whole thing is all about -- alloca()te memory
 */
void *alloca(size_t size)
{
  struct block *mem;

  if (size <= 0)  /* garbage collection -- not required */
    return NULL;

  size += sizeof(struct block);
  if (!(mem = (struct block *)LibAllocPooled(pool, (ULONG)size)))
    {
      /* do whatever you want here */
      abort();
    }
  mem->down = top;
  mem->size = size;
  mem->level = __alloca_virtual_SP;
  top = mem;
  return ++mem;
}

