/*  I N T E R M E D I A T E   C O D E
 *
 *  I C _ L O O K U P . C
 */


#include <stdio.h>
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/map.h"
#include "../../../h/em_spec.h"
#include "ic.h"
#include "ic_lookup.h"
#include "../share/alloc.h"


sym_p symhash[NSYMHASH];
prc_p prochash[NPROCHASH];
num_p numhash[NNUMHASH];



#define newsym()	(sym_p) newstruct(sym)
#define newprc()	(prc_p) newstruct(prc)
#define newnum()	(num_p) newstruct(num)

#define oldsym(x)	oldstruct(sym,x)
#define oldprc(x)	oldstruct(prc,x)
#define oldnum(x)	oldstruct(num,x)


/* instr_lab */





lab_id instr_lab(number)
	short number;
{
	register num_p *npp, np;

	/* In EM assembly language, a label is an unsigned number,
	 * e.g. 120 in 'BRA *120'. In IC the labels of a procedure
	 * are represented by consecutive integer numbers, called
	 * lab_id. The mapping takes place here.
	 */


	npp = &numhash[number%NNUMHASH];
	while (*npp != (num_p) 0) {
		if ((*npp)->n_number == number) {
			return(*npp)->n_labid;
		} else {
			npp = &(*npp)->n_next;
		}
	}

	/* The label was not found in the hashtable, so
	 * create a new entry for it.
	 */

	*npp = np = newnum();
	np->n_number = number;
	np->n_labid = ++lastlid;
	/* Assign a new label identifier to the num struct.
	 * lastlid is reset to 0 at the beginning of
	 * every new EM procedure (by cleaninstrlabs).
	 */
	return (np->n_labid);
}



/*  symlookup */

STATIC unsigned hash(string) char *string; {
	register char *p;
	register unsigned i,sum;

	for (sum=i=0,p=string;*p;i += 3)
		sum ^= (*p++)<<(i&07);
	return(sum);
}

dblock_p symlookup(name, status)
	char *name;
	int  status;
{
	/* Look up the name of a data block. The name can appear
	 * in either a defining or applied occurrence (status is
	 * DEFINING, OCCURRING resp.), or in a MES ms_ext instruction
	 * as the name of a data block imported by a library module
	 * (status is IMPORTING). Things get complicated,
	 * because a HOL pseudo need not be preceded by a
	 * data label, i.e. a hol block need not have a name.
	 */


	register sym_p *spp,  sp;
	register dblock_p dp;

	if (name == (char *) 0) {
		assert(status == DEFINING);
		dp = newdblock();
	} else {
		spp = &symhash[hash(name)%NSYMHASH];
		while (*spp != (sym_p) 0) {
			/* Every hashtable entry points to a list
			 * of synonyms (i.e. names with the same
			 * hash values). Try to find 'name' in its
			 * list.
			 */
			if (strncmp((*spp)->sy_name, name, IDL) == 0) {
				/* found */
				return ((*spp)->sy_dblock);
			} else {
				spp = &(*spp)->sy_next;
			}
		}
		/* The name is not found, so create a new entry for it.
		 * However, if the status is IMPORTING, we just return 0,
		 * indicating that we don't need this name.
		 */
		if (status == IMPORTING) return (dblock_p) 0;
		*spp = sp = newsym();
		strncpy(sp->sy_name, name, IDL);
		dp = sp->sy_dblock = newdblock();
	}
	if (fdblock == (dblock_p) 0) {
		fdblock = dp;
		/* first data block */
	} else {
		ldblock->d_next = dp; /* link to last dblock */
	}
	ldblock = dp;
	dp->d_pseudo	= DUNKNOWN;	/* clear all fields */
	dp->d_id	= ++lastdid;
	dp->d_size	= 0;
	dp->d_objlist	= (obj_p) 0;
	dp->d_values	= (arg_p) 0;
	dp->d_next	= (dblock_p) 0;
	dp->d_flags1	= 0;
	dp->d_flags2	= 0;
	if (status == OCCURRING) {
		/* This is the first occurrence of the identifier,
		 * so if it is a used occurrence make the
		 * identifier externally visible, else make it
		 * internal.
		 */
		dp->d_flags1 |= DF_EXTERNAL;
	}
	return dp;
}



/* getsym */

dblock_p getsym(status)
	int status;
{
	if (table2() != DLBX) {
		error("symbol expected");
	}
	return(symlookup(string,status));
}



/* getproc */

proc_p getproc(status)
	int status;
{
	if (table2() != sp_pnam) {
		error("proc name expected");
	}
	return(proclookup(string,status));
}



/* proclookup */

proc_p proclookup(name, status)
	char *name;
	int  status;
{
	register prc_p *ppp,  pp;
	register proc_p dp;

	ppp = &prochash[hash(name)%NPROCHASH];
	while (*ppp != (prc_p) 0) {
		/* Every hashtable entry points to a list
		 * of synonyms (i.e. names with the same
		 * hash values). Try to find 'name' in its
		 * list.
		 */
		if (strncmp((*ppp)->pr_name, name, IDL) == 0) {
			/* found */
			return ((*ppp)->pr_proc);
		} else {
			ppp = &(*ppp)->pr_next;
		}
	}
	/* The name is not found, so create a new entry for it,
	 * unless the status is IMPORTING, in which case we
	 * return 0, indicating we don't want this proc.
	 */
	if (status == IMPORTING) return (proc_p) 0;
	*ppp = pp = newprc();
	strncpy(pp->pr_name, name, IDL);
	dp = pp->pr_proc = newproc();
	if (fproc == (proc_p) 0) {
		fproc = dp;  /* first proc */
	} else {
		lproc->p_next = dp;
	}
	lproc = dp;
	dp->p_id	= ++lastpid;	/* create a unique proc_id */
	dp->p_next	= (proc_p) 0;
	dp->p_flags1	= 0;
	dp->p_flags2	= 0;
	if (status == OCCURRING) {
		/* This is the first occurrence of the identifier,
		 * so if it is a used occurrence the make the
		 * identifier externally visible, else make it
		 * internal.
		 */
		dp->p_flags1 |= PF_EXTERNAL;
	}
	return dp;
}



/* cleaninstrlabs */

cleaninstrlabs()
{
	register num_p *npp, np, next;

	for (npp = numhash; npp < &numhash[NNUMHASH]; npp++) {
		for  (np = *npp; np != (num_p) 0; np = next) {
			next = np->n_next;
			oldnum(np);
		}
		*npp = (num_p) 0;
	}
	/* Reset last label id (used by instr_lab). */
	lastlid = (lab_id) 0;
}



/* dump_procnames */

dump_procnames(hash,n,f)
	prc_p  hash[];
	int    n;
	FILE   *f;
{
	/* Save the names of the EM procedures in file f.
	 * Note that the Optimizer Intermediate Code does not
	 * use identifiers but proc_ids, object_ids etc.
	 * The names, however, can be used after optimization
	 * is completed, to reconstruct Compact Assembly Language.
	 * The output consists of tuples (proc_id, name).
	 * This routine is called once for every input file.
	 * To prevent names of external procedures being written
	 * more than once, the PF_WRITTEN flag is used.
	 */

	register prc_p *pp, ph;
	proc_p p;
	char str[IDL+1];
	register int i;

#define PF_WRITTEN 01


	for (pp = &hash[0]; pp < &hash[n]; pp++) {
		/* Traverse the entire hash table */
		for (ph = *pp; ph != (prc_p) 0; ph = ph->pr_next) {
			/* Traverse the list of synonyms */
			p = ph->pr_proc;
			if ((p->p_flags2 & PF_WRITTEN) == 0) {
				/* not been written yet */
				for(i = 0; i < IDL; i++) {
					str[i] = ph->pr_name[i];
				}
				str[IDL] = '\0';
				fprintf(f,"%d	%s\n",p->p_id, str);
				p->p_flags2 |= PF_WRITTEN;
			}
		}
	}
}



/* cleanprocs */

cleanprocs(hash,n,mask)
	prc_p hash[];
	int   n,mask;
{
	/* After an EM input file has been processed, the names
	 * of those procedures that are internal (i.e. not visible
	 * outside the file they are defined in) must be removed
	 * from the procedure hash table. This is accomplished
	 * by removing the 'prc struct' from its synonym list.
	 * After the final input file has been processed, all
	 * remaining prc structs are also removed.
	 */

	register prc_p *pp, ph, x, next;

	for (pp = &hash[0]; pp < &hash[n]; pp++) {
		/* Traverse the hash table */
		x = (prc_p) 0;
		for (ph = *pp; ph != (prc_p) 0; ph = next) {
			/* Traverse the synonym list.
			 * x points to the prc struct just before ph,
			 * or is 0 if ph is the first struct of
			 * the list.
			 */
			next = ph->pr_next;
			if ((ph->pr_proc->p_flags1 & mask) == 0) {
				if (x == (prc_p) 0) {
					*pp = next;
				} else {
					x->pr_next = next;
				}
				oldprc(ph); /* delete the struct */
			} else {
				x = ph;
			}
		}
	}
}



/* dump_dblocknames */

dump_dblocknames(hash,n,f)
	sym_p  hash[];
	int    n;
	FILE   *f;
{
	/* Save the names of the EM data blocks in file f.
	 * The output consists of tuples (dblock_id, name).
	 * This routine is called once for every input file.
	 */

	register sym_p *sp, sh;
	dblock_p d;
	char str[IDL+1];
	register int i;

#define DF_WRITTEN 01


	for (sp = &hash[0]; sp < &hash[n]; sp++) {
		/* Traverse the entire hash table */
		for (sh = *sp; sh != (sym_p) 0; sh = sh->sy_next) {
			/* Traverse the list of synonyms */
			d = sh->sy_dblock;
			if ((d->d_flags2 & DF_WRITTEN) == 0) {
				/* not been written yet */
				for (i = 0; i < IDL; i++) {
					str[i] = sh->sy_name[i];
					str[IDL] = '\0';
				}
				fprintf(f,"%d	%s\n",d->d_id, str);
				d->d_flags2 |= DF_WRITTEN;
			}
		}
	}
}



/* cleandblocks */

cleandblocks(hash,n,mask)
	sym_p hash[];
	int   n,mask;
{
	/* After an EM input file has been processed, the names
	 * of those data blocks that are internal must be removed.
	 */

	register sym_p *sp, sh, x, next;

	for (sp = &hash[0]; sp < &hash[n]; sp++) {
		x = (sym_p) 0;
		for (sh = *sp; sh != (sym_p) 0; sh = next) {
			next = sh->sy_next;
			if ((sh->sy_dblock->d_flags1 & mask) == 0) {
				if (x == (sym_p) 0) {
					*sp = next;
				} else {
					x->sy_next = next;
				}
				oldsym(sh); /* delete the struct */
			} else {
				x = sh;
			}
		}
	}
}