/* register.c */
/* author: Edward A. Green */
/* purpose: to manage register allocation */

/* work out spill protocols */


#include "proj3.h"

/* float register usage */
char *f_regs[9] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

/* integer register usage */
char *r_regs[9] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

MOD *temp_des = NULL;    /* temporary value descriptor */
MOD *spill_des = NULL;   /* memory locations for storing temps */
MOD *type_des = NULL;    /* types of temporary values */
MOD *addr_des = NULL;    /* "a" if an address is stored at temp */

void wipe_mod(MOD *list);
char *constant(char *, MOD *);
MOD  *find_const_list(char *, MOD *);
MOD  *insert_const_list(char *, char *, MOD *);
MOD  *remove_const_list(char *, MOD *);
char *get_reg(char *, int, char, char);
char *strsave(char *);
void return_reg(char *s);

void init_regs(void)
{
	int i;

	wipe_mod(temp_des);
	wipe_mod(spill_des);
	wipe_mod(type_des);
	wipe_mod(addr_des);
	temp_des=NULL;
	spill_des=NULL;
	type_des=NULL;
	addr_des=NULL;
	for (i=0; i<8; i++)
	  {
	  if (f_regs[i]!=NULL) { free(f_regs[i]); f_regs[i]=NULL; }
	  if (r_regs[i]!=NULL) { free(r_regs[i]); r_regs[i]=NULL; }
	  }
}


/* find the address descriptor list for a variable */
/* if not found, a new descriptor is added */
char *temp_look_up(char *tmp, int memsize, char type, char addr)
{
	char *ret_val;
	char s[100];


	/* find in temp_des */
	ret_val=constant(tmp,temp_des);

	if (ret_val!=NULL)
	  {
	  if (ret_val[0]>='0' && ret_val[0]<='9')
	    sprintf(s,"-%s(fp)",ret_val);
	  else
	    sprintf(s,"%s",ret_val);
	  ret_val=strsave(s);
	  return(ret_val);
	  }

	else
	  {
	  ret_val=get_reg(tmp, memsize, type, addr);
	  ret_val=strsave(ret_val);
	  return(ret_val);
	  }

}

char type_look_up(char *tmp)
{
	char *ret_val;

	ret_val=constant(tmp,type_des);
	if (ret_val==NULL) return(0);
	return(ret_val[0]);
}

char addr_look_up(char *tmp)
{
	char *ret_val;

	ret_val=constant(tmp,addr_des);
	if (ret_val==NULL) return(0);
	return(ret_val[0]);
}

void temp_remove(char *s)
{
	return_reg(s);
	return;
}


/* retrieve a free register or memory, given a temp value */
char *get_reg(char *s, int memsize, char type, char addr)
{
	char *ret_val;
	int  i;
	char buff[100];
	char rtype;

	/* set up type descriptor */
	sprintf(buff,"%c",type);
	type_des=insert_const_list(s,buff,type_des);

	/* set up addr descriptor */
	sprintf(buff,"%c",addr);
	addr_des=insert_const_list(s,buff,addr_des);

	/* find register type */
	rtype=type;
	if (addr=='a') rtype='r';

	/* search register array */
	for (i=2; i<8; i++)
	  if   ( (rtype=='r' && r_regs[i]==NULL)
	     ||  (rtype=='f' && f_regs[i]==NULL) ) break;

	/* found a free register */
	if (i<8)
	  {
	  sprintf(buff,"%c%d",rtype,i);
	  if (rtype=='r') r_regs[i]=strsave(s);
	  else            f_regs[i]=strsave(s);
	  temp_des=insert_const_list(s,buff,temp_des);
	  }

	/* no free register; use spilled memory */
	else
	  {
	  sprintf(buff,"-%d(fp)",memsize);
	  while (find_const_list(buff,spill_des)!=NULL)
	    {
	    memsize=memsize-4;
	    sprintf(buff,"-%d(fp)",memsize);
	    }
	  spill_des=insert_const_list(buff,s,spill_des);
	  temp_des=insert_const_list(s,buff,temp_des);
	  }

	/* return the memory reference */
	ret_val=strsave(buff);
	return(ret_val);
}


/* free up a register or memory ref corresponding to a temp */
void return_reg(char *s)
{
	int i;
	char *temp_loc;

	/* look up temp name in temp descriptor */
	temp_loc=constant(s,temp_des);
	if (temp_loc!=NULL)
	  {
	  /* if we have a memory reference, delete from spill des. */
	  if (temp_loc[0]=='-')
	    {
	    spill_des=remove_const_list(temp_loc,spill_des);
	    }

	  /* else we have a register reverence. free the register */
	  else
	    {
	    i=temp_loc[1]-'0';
	    if (temp_loc[0]=='r') { free(r_regs[i]); r_regs[i]=NULL; }
	    else                  { free(f_regs[i]); f_regs[i]=NULL; }
	    }

	  /* delete the temp descriptor */
	  temp_des=remove_const_list(s,temp_des);

	  /* delete the type descriptor */
	  type_des=remove_const_list(s,type_des);

	  /* delete the addr descriptor */
	  addr_des=remove_const_list(s,addr_des);
	  }
}


/* spill all registers to memory locations */
void spill_all_reg(int memsize)
{
	int i;
	char *s, *d;
	char adr;
	char type;

	for (i=2; i<8; i++)
	  {
	  if (r_regs[i]==NULL) r_regs[i]="$";
	  if (f_regs[i]==NULL) f_regs[i]="$";
	  }

	for (i=2; i<8; i++)
	  {
	  if (r_regs[i][0]!='$')
	    {
	    s=strsave(r_regs[i]);
	    adr=addr_look_up(s);
	    type=type_look_up(s);
	    return_reg(s);
	    r_regs[i]="$";
	    d=get_reg(s,memsize,type,adr);
	    printf("\tmovd r%d,%s\n",i,d);
	    free(s);
	    free(d);
	    }
	  if (f_regs[i][0]!='$')
	    {
	    s=strsave(f_regs[i]);
	    adr=addr_look_up(s);
	    type=type_look_up(s);
	    return_reg(s);
	    f_regs[i]="$";
	    d=get_reg(s,memsize,type,adr);
	    printf("\tmovd f%d,%s\n",i,d);
	    free(s);
	    free(d);
	    }
	  }

	for (i=2; i<8; i++)
	  {
	  r_regs[i]=NULL;
	  f_regs[i]=NULL;
	  }
}
