/**********************************************************************/
/* cyclonelib.c                                                       */
/*                                                                    */
/* C implementations of the Cyclone macros CG_START, CG_DUMP, CG_END, */
/* and CG_ABORT.  We use a C implementation for easier debugging.     */
/**********************************************************************/


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

/**********************************************************************/
/* If DEBUG is defined, then the Cyclone functions will print out     */
/* debugging information when they are invoked.                       */
/* If DEBUG2 is defined, templates will start out with some NOP's     */
/* to help the disassembler synchronize with the first real           */
/* instruction of the template.                                       */
/**********************************************************************/
/*
#define DEBUG2
#define DEBUG
*/

/**********************************************************************/
/* If EXPAND_REGION is defined, when the code region is about to be   */
/* exceeded by a CGDUMP, the code region size is doubled.             */
/* THIS IS CURRENTLY BROKEN.                                          */
/**********************************************************************/
/*
#define EXPAND_REGION
*/

void *GC_malloc(int size);
void *GC_realloc(char *ptr, int size);

/**********************************************************************/
/* Use this to turn off garbage collection for the Cyclone regions    */
/**********************************************************************/
/*
#define GC_malloc(x) (malloc(x))
*/

/**********************************************************************/
/* Size of the code generation regions. This must be a multiple of 4  */
/**********************************************************************/
#define buffersize (1024)

typedef struct {
  int length;
  unsigned char contents[];
} template;

typedef struct cg_region {
  struct cg_region *prev;  /* previous region on the stack */
  int length;              /* length of the code gen region */
  int current;             /* place to dump the next template */
  int fills;               /* number of copies of fill values */
  unsigned char *contents; /* points to the code gen region */
} cg_region;

cg_region *regions = NULL;

void external_addr() { }  /* used for dummy hole value in cyclone.inc */

void CG_start(void)
{
  cg_region *newregion = GC_malloc(sizeof(cg_region));
  unsigned char *newf = GC_malloc(buffersize);

  /* Should we check newregion to be sure we didn't run out of space??
     Stdlib.c doesn't. */

  newregion->length = buffersize;
  newregion->current = 0;
  newregion->prev = regions;
  newregion->fills = 0;
  newregion->contents = newf;
  regions = newregion;

#ifdef DEBUG
  fflush(stdout);
  fprintf(stderr,"\nCG_start\n");
  fprintf(stderr, "new region at 0x%08x\n",(int)newregion);
  fflush(stderr);
#endif

#ifdef DEBUG2
  /** For debugging, it is convenient to make the first 8 instructions NOPs,
  *** so the disassembler can synchronize with the first real instruction
  *** of the template
  **/
  newregion->contents[0] =
    newregion->contents[1] =
    newregion->contents[2] =
    newregion->contents[3] =
    newregion->contents[4] =
    newregion->contents[5] =
    newregion->contents[6] =
    newregion->contents[7] = 0x90; /* NOP */
  newregion->current = 8;
#endif

#ifdef DEBUG
  fprintf(stderr,"CG_start finished\n");
  fflush(stderr);
#endif

}


void *CG_dump(template *tmpl)
{
  int i;
  void *retval;

#ifdef DEBUG
  fprintf(stderr, "CG_dump entered\n");
  fprintf(stderr, "CG_dump template at 0x%08x\n",(int)tmpl);
  fprintf(stderr, "CG_dump &template->contents is 0x%08x\n",(int)(&tmpl->contents));
  fprintf(stderr, "CG_dump template length is 0x%08x\n",tmpl->length);
  fprintf(stderr, "CG_dump template->contents[0-3]: 0x %02x %02x %02x %02x\n",
          tmpl->contents[0], tmpl->contents[1], tmpl->contents[2], tmpl->contents[3]);
  fflush(stderr);

  /* This case should be ruled out by the type system, so we don't
     perform the test except in debugging mode */
  if (regions == NULL) {
    fprintf(stderr, "Compiler error: cgdump with no region\n");
    fflush(stderr);
    exit(1);
  }
#endif

#ifdef EXPAND_REGION
  /* THIS DOESN'T WORK BECAUSE POINTERS TO INSIDE THE REGION
     ARE NOT ADJUSTED PROPERLY IF THE REGION MOVES */
  while (regions->current + tmpl->length >
         regions->length - 4*regions->fills) {
    int new_length;

    new_length = regions->length * 2;

#ifdef DEBUG
    fprintf(stderr, "Increasing code region length:\n");
    fprintf(stderr, "\tcurrent ptr:       %d\n", regions);
    fprintf(stderr, "\ttemplate length:   %d\n", tmpl->length);
    fprintf(stderr, "\told region length: %d\n", regions->length);
    fprintf(stderr, "\tnew region length: %d\n", new_length);
    fflush(stderr);
#endif /* DEBUG */

    regions = GC_realloc((char *)regions, new_length);
    regions->length = new_length;
  }
#else /* EXPAND_REGION */
  if (regions->current + tmpl->length > regions->length - 4*regions->fills)
    {
      fprintf(stderr, "Compiler error: cgdump exceeds region size.\n");
      fprintf(stderr, "Adjust buffersize in runtime/cyclonelib.c\n");
      fflush(stderr);
      exit(1);
    }
#endif /* EXPAND_REGION */

  for (i=0; i<tmpl->length; i++)
    regions->contents[regions->current+i] = tmpl->contents[i];
  retval = &regions->contents[regions->current];
  regions->current += tmpl->length;

#ifdef DEBUG
  fprintf(stderr, "CG_dump retval[0-3]: 0x %02x %02x %02x %02x\n",
          ((unsigned char *)retval)[0],
          ((unsigned char *)retval)[1],
          ((unsigned char *)retval)[2],
          ((unsigned char *)retval)[3]);
  fprintf(stderr, "CG_dump retval last 4 bytes: 0x %02x %02x %02x %02x\n",
          (unsigned int)((unsigned char *)retval)[tmpl->length-4],
          (unsigned int)((unsigned char *)retval)[tmpl->length-3],
          (unsigned int)((unsigned char *)retval)[tmpl->length-2],
          (unsigned int)((unsigned char *)retval)[tmpl->length-1]);
  fprintf(stderr, "CG_dump returning with 0x%08x\n",(int)retval);
  fflush(stderr);
#endif

  return retval;

}

#ifdef DEBUG
/* For debugging */
void print_opcodes(cg_region *r) {
  int i;

  if (r == NULL) return;
  for (i=0; i < r->current; i++) {
    fprintf(stderr, " %02.2x",r->contents[i]);
    if ((i%8)==7) fprintf(stderr,"\n");
  }
  if ((i%8)!=0) fprintf(stderr,"\n");
  fflush(stderr);
}
#endif

void *CG_end(void)
{
  void *retval;
  cg_region *this_region;

#ifdef DEBUG
  fprintf(stderr, "CG_end entered\n");

  /* This case should be ruled out by the type system, so we don't
     perform the test except in debugging mode */
  if (regions == NULL) {
    fprintf(stderr, "Compiler error: no more cg regions\n");
    fflush(stderr);
    exit(1);
  }

  fprintf(stderr, "CG_end region at 0x%08x\n",
          (int)regions);
  fprintf(stderr, "CG_end regions->current is 0x%08x\n",
          regions->current);
  fprintf(stderr, "CG_end regions->length is 0x%08x\n",
          regions->length);
  fflush(stderr);
#endif

  retval = (void *)regions->contents;
  this_region = regions;
  regions = regions->prev;
  this_region->prev = NULL; /* Zero out to prevent space leak */

#ifdef DEBUG
  fprintf(stderr, "CG_end returning with 0x%08x\n",(int)retval);
  fprintf(stderr, "Opcodes are:\n");
  print_opcodes(this_region);
  fflush(stderr);
#endif

  return retval;


}


void CG_abort(int x)
{
#ifdef DEBUG
  fprintf(stderr, "CG_abort entered with x = %d\n",x);
  fflush(stderr);
#endif

  while (x>0) {

#ifdef DEBUG
    /* This case should be ruled out by the type system, so we don't
       perform the test except in debugging mode */
    if (regions == NULL) {
      fprintf(stderr, "Compiler error: can't abort, no more cg regions\n");
      fflush(stderr);
      exit(1);
    }
#endif

    regions = regions->prev;
    x--;
  }

#ifdef DEBUG
  fprintf(stderr, "CG_abort exiting\n");
  fflush(stderr);
#endif

}

void CG_mark(void *x) {
  ((void **)(regions->contents))[buffersize/4-regions->fills-1] = x;
  regions->fills = regions->fills + 1;
}

#ifdef DEBUG
/* showmem and CG_fillitjc were used for debugging (Luke) */
void showmem(unsigned char *reg, int length)
{
  int i;

  for(i = 0; i < length; i += 4)
    fprintf(stderr, "mem[%x] 0x %02x %02x %02x %02x\n",
	    reg+i, reg[i], reg[i+1], reg[i+2], reg[i+3]);
}

void CG_fillitjc(template *holereg, template *holetmpllab, template *holelab,
		 template *targreg, template *targtmpllab, template *targlab)
{
  int fillval, *holeaddr;

  fprintf(stderr, "holereg: 0x %x  holetmplab: 0x %x  holelab: 0x %x\n",
	  holereg, holetmpllab, holelab);
  fprintf(stderr, "targreg: 0x %x  targtmplab: 0x %x  targlab: 0x %x\n",
	  targreg, targtmpllab, targlab);

  holeaddr = (int *) ((int)holereg + ((int)holelab - (int)holetmpllab) + 1);
  fillval = ((int)targreg - (int)holereg)
    + ((int)targlab - (int)targtmpllab)
    - ((int)holelab - (int)holetmpllab)
    - 9;

  showmem((unsigned char *)holereg, holetmpllab->length);
  fprintf(stderr, ">mem[%x] = %x \n", holeaddr, *holeaddr);
  *holeaddr = fillval;
  showmem((unsigned char *)holereg, holetmpllab->length);
  fprintf(stderr, ">mem[%x] = %x \n", holeaddr, *holeaddr);

  fflush(stderr);
}
#endif



/* EOF: cyclonelib.c */
