/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved. The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */
static char rcsid[] = "$Header:regalloc.c 12.0$";
#include "defs.h"
#include "optim.h"

#define LABTABSIZE 101
#define VARTABSIZE 1009
#define TABLELIMIT 12

#define MSKREGTYPES M(TYLOGICAL) | M(TYADDR) | M(TYSHORT) | M(TYLONG)

#define ISREGTYPE(x) ONEOF(x, MSKREGTYPES)

#define MSKVARS M(STGAUTO) | M(STGBSS) | M(STGINIT) | M(STGCONST) |\
		M(STGEQUIV) | M(STGARG) | M(STGCOMMON)

#define ISVAR(x) ((((expptr) x)->headblock.vclass == CLVAR || \
			((expptr) x)->headblock.vclass == CLUNKNOWN) \
                  && ONEOF(((expptr) x)->headblock.vstg, MSKVARS))


typedef
  struct regdata
    {
      field vstg;
      field vtype;
      int memno;
      int memoffset;
      int refs;
      Addrp stgp;
      unsigned isarrayarg : 1;
      unsigned istemp : 1;
      unsigned isset : 1;
      unsigned setfirst : 1;
    } REGDATA;


typedef
  struct labelnode
    {
      struct labelnode *link;
      int labelno;
    } LABELNODE;



typedef
  struct varnode
    {
      struct varnode *link;
      int memoffset;
      unsigned isset : 1;
      unsigned isused : 1;
      unsigned setfirst : 1;
      unsigned unusable : 1;
      int refs;
      Addrp stgp;
    } VARNODE;


typedef
  struct addrnode
    {
      struct addrnode *link;
      field vtype;
      field vstg;
      int memno;
      unsigned istemp : 1;
      unsigned isset : 1;
      unsigned freeuse : 1;
      unsigned mixedtype : 1;
      int refs;
      struct addrnode *commonlink;
      VARNODE *varlist;
    } ADDRNODE;


typedef
  struct setnode
    {
      struct setnode *link;
      field vstg;
      int memno;
      int memoffset;
    } SETNODE;


typedef
  struct doqueue
    {
      struct doqueue *up, *down;
      Slotp dohead, doend;
      int nregvars;
      REGNODE *reg[MAXREGVAR];
    }  DOQUEUE;

LOCAL DOQUEUE *dqptr, *dqtop, *dqbottom;


LOCAL Slotp dohead;
LOCAL Slotp doend;
LOCAL Slotp newcode;



LOCAL LABELNODE *labeltable[LABTABSIZE];
LOCAL ADDRNODE *vartable[VARTABSIZE];
LOCAL ADDRNODE *commonvars;
LOCAL SETNODE *setlist;
LOCAL int topregvar;
LOCAL int toplcv;
LOCAL int allset;
LOCAL ADDRNODE *currentaddr;
LOCAL int loopcost;
LOCAL REGDATA *regtab[MAXREGVAR];
LOCAL REGDATA *rt[TABLELIMIT];
LOCAL int tabletop;
LOCAL int linearcode;
LOCAL int globalbranch;
LOCAL int commonunusable;
LOCAL int regdefined[MAXREGVAR];
LOCAL int memdefined[MAXREGVAR];
LOCAL int regaltered[MAXREGVAR];



LOCAL insertlabel(l)
int l;

{
  int key;
  LABELNODE *p;

  key = l % LABTABSIZE;
  for (p = labeltable[key]; p; p = p->link)
    if (p->labelno == l) return;
  p = labeltable[key];
  labeltable[key] = ALLOC(labelnode);
  labeltable[key]->link = p;
  labeltable[key]->labelno = l;
  return;
}



LOCAL int locallabel(l)
int l;

{
  int key;
  LABELNODE *p;

  key = l % LABTABSIZE;
  for (p = labeltable[key]; p; p = p->link)
    if (p->labelno == l) return YES;

  return NO;
}



LOCAL freelabtab()

{
  int i;
  LABELNODE *p, *q;

  for (i = 0; i < LABTABSIZE; i++)
    if (labeltable[i])
      {
	p = labeltable[i];
	labeltable[i] = NULL;
	while (p)
	  {
	    q = p->link;
	    free(p);
	    p = q;
	  }
      }
  return;
}



LOCAL ADDRNODE *getaddr(ap)
Addrp ap;

{
  int key;
  field vstg;
  int memno;
  register ADDRNODE *q;
  ADDRNODE *q1;

  if (!ISVAR(ap))
    fatal("regalloc: bad data sent to getaddr");
  vstg = ap->vstg;
  memno = ap->memno;
  key = (256*vstg + memno) % VARTABSIZE;

  for (q = vartable[key]; q; q = q->link)
    if ((q->vstg == vstg) && (q->memno == memno)) 
      {
	if (ap->istemp) q->istemp = YES;
	if (ap->vtype != q->vtype)
	  q->mixedtype = YES;
	return q;
      }

  q1 = vartable[key];
  vartable[key] = q = ALLOC(addrnode);
  q->link = q1;
  q->vstg = vstg;
  q->memno = memno;
  if (ap->istemp) q->istemp = YES;
  q->vtype = ap->vtype;
  q->varlist = NULL;
  if (vstg == STGCOMMON)
    {
      q->commonlink = commonvars;
      commonvars = q;
    }
  return q;
}



LOCAL VARNODE *getvar(ainfo, ap)
ADDRNODE *ainfo;
Addrp ap;

{
  register VARNODE *q;
  VARNODE *q1;
  int memoffset;

  if (!ISVAR(ap))
    fatal("regalloc:  bad data sent to getvar");

  memoffset = ap->memoffset->constblock.constant.ci;

  for (q = ainfo->varlist; q; q = q->link)
    if (q->memoffset == memoffset)
      return q;

  q1 = ainfo->varlist;
  ainfo->varlist = q = ALLOC(varnode);
  q->link = q1;
  q->memoffset = memoffset;
  q->stgp = (Addrp) cpexpr(ap);
  return q;
}


LOCAL ADDRNODE *lookupaddr(vstg, memno)
field vstg;
int memno;

{
  int key;
  register ADDRNODE *q;

  key = (256*vstg + memno) % VARTABSIZE;

  for (q = vartable[key]; q; q = q->link)
    if ((q->vstg == vstg) && (q->memno == memno))
      return q;

  fatal("regalloc:  lookupaddr");
}


LOCAL VARNODE *lookupvar(ainfo, memoffset)
ADDRNODE *ainfo;
int memoffset;

{
  register VARNODE *q;

  for (q = ainfo->varlist; q; q = q->link)
    if (q->memoffset == memoffset)
      return q;

  fatal("regalloc:  lookupvar");
}



LOCAL int invartable(p)
REGNODE *p;

{
  field vstg;
  int memno;
  int key;
  register ADDRNODE *q;

  vstg = p->vstg;
  memno = p->memno;
  key = (256*vstg + memno) % VARTABSIZE;

  for (q = vartable[key]; q; q = q->link)
    if ((q->vstg == vstg) && (q->memno == memno))
      return YES;

  return NO;
}



LOCAL freevartab()

{
  register ADDRNODE *p;
  ADDRNODE *p1;
  register VARNODE *q;
  VARNODE *q1;
  register int i;

  for (i = 0; i < VARTABSIZE; i++)
    if (vartable[i])
      {
	p = vartable[i];
	vartable[i] = NULL;

	while (p)
	  {
	    for (q = p->varlist; q; q = q1)
	      {
		q1 = q->link;
		frexpr(q->stgp);
		free ((char *) q);
	      }
	    p1 = p->link;
	    free((char *) p);
	    p = p1;
	  }
      }
}



LOCAL insertset(vstg, memno, memoffset)
field vstg;
int memno;
int memoffset;

{
  register SETNODE *p;
  SETNODE *q;

  if (allset) return;
  for (p = setlist; p; p = p->link)
    if((p->vstg == vstg) && (p->memno == memno) && (p->memoffset == memoffset))
      return;

  q = p;
  setlist = p = ALLOC(setnode);
  p->link = q;
  p->vstg = vstg;
  p->memno = memno;
  p->memoffset = memoffset;
  return;
}



LOCAL int insetlist(vstg, memno, memoffset)
field vstg;
int memno;
int memoffset;

{
  register SETNODE *p;

  if (allset) return YES;
  for (p = setlist; p; p = p->link)
    if((p->vstg == vstg) && (p->memno == memno) && (p->memoffset == memoffset))
      return YES;

  return NO;
}



LOCAL clearsets()

{
  register SETNODE *p, *q;

  allset = NO;

  p = setlist;
  while (p)
    {
      q = p->link;
      free ((char *) p);
      p = q;
    }
  setlist = NULL;
  return;
}



LOCAL alreg()

{
  register Slotp sp;
  register int i;
  register ADDRNODE *p;
  register VARNODE *q;
  Slotp sp1, sp2;
  ADDRNODE *addrinfo;
  VARNODE *varinfo;
  int docount;
  struct Labelblock **lp;
  int toptrack;
  int track[MAXREGVAR];
  Addrp ap, ap1;
  DOQUEUE *dqp;
  REGDATA *rp;
  REGNODE *regp;

  if (nregvar >= maxregvar) return;

  commonvars = NULL;

  for (sp = dohead; sp != doend->next; sp = sp->next)
    switch (sp->type)
      {
      case SKLABEL:
	insertlabel(sp->label);
	break;

      case SKARIF:
      case SKASGOTO:
      case SKCALL:
      case SKCMGOTO:
      case SKEQ:
      case SKIFN:
      case SKIOIFN:
      case SKSTOP:
      case SKPAUSE:
      case SKRETURN:
	scanvars(sp->expr);
	break;

      case SKNULL:
      case SKGOTO:
      case SKDOHEAD:
      case SKENDDO:
      case SKASSIGN:
	break;

      default:
	badthing ("SKtype", "alreg-1", sp->type);
      }

  loopcost = 0;
  docount = 1;
  commonunusable = NO;
  if (! dohead->nullslot) fatal ("missing dohead->nullslot -cbb");
  for (sp = dohead->next, globalbranch = NO;
       docount;
       sp = sp->next, clearsets(), globalbranch = NO)
    if (docount > 1)
      switch (sp->type)
	{
	case SKDOHEAD:
	  docount++;
	  break;

	case SKENDDO:
	  docount--;

	default:
	  break;
	}
    else
      switch (sp->type)
	{
	case SKARIF:
#define LM   ((struct Labelblock * *)sp->ctlinfo)[0]->labelno 
#define LZ   ((struct Labelblock * *)sp->ctlinfo)[1]->labelno 
#define LP   ((struct Labelblock * *)sp->ctlinfo)[2]->labelno 

	  if (!locallabel(LM) || !locallabel(LZ) || !locallabel(LP))
	    {
	      setall();
	      globalbranch = YES;
	    }
	  countrefs(sp->expr);
	  break;

	case SKASGOTO:
	  setall();
	  globalbranch = YES;
	  countrefs(sp->expr);
	  break;

	case SKCMGOTO:
	  lp = (struct Labelblock **) sp->ctlinfo;
	  for (i = 0; i < sp->label; i++, lp++)
	    if (!locallabel((*lp)->labelno))
	      {
		setall();
		globalbranch = YES;
		break;
	      }
	  countrefs(sp->expr);
	  break;

	case SKDOHEAD:
	  globalbranch = YES;
	  loopcost = 2;
	  docount++;
	  break;

	case SKENDDO:
	  docount = 0;
	  break;

	case SKGOTO:
	  if (!locallabel(sp->label))
	    {
	      setall();
	      globalbranch = YES;
	    }
	  break;

	case SKIFN:
	case SKIOIFN:
	  if (!locallabel(sp->label))
	    {
	      setall();
	      globalbranch = YES;
	    }
	  countrefs(sp->expr);
	  break;

	case SKEQ:
	case SKCALL:
	case SKSTOP:
	case SKPAUSE:
	  linearcode = YES;
	  countrefs(sp->expr);
	  linearcode = NO;
	  break;
	}

  topregvar = toplcv = nregvar - 1;

  for (i = 0; i < nregvar; i++)
    {
      ap = memversion(regnamep[i]);
      regtab[i] = rp = ALLOC(regdata);
      rp->vstg = ap->vstg;
      rp->vtype = ap->vtype;
      rp->memno = ap->memno;
      rp->memoffset = ap->memoffset->constblock.constant.ci;
      rp->isarrayarg = NO;
      rp->stgp = ap;
    }

  for (i = 0; i < MAXREGVAR; i++)
    track[i] = YES;

  for (dqp = dqptr->down; dqp; dqp = dqp->down)
    {
      if (dqp->nregvars - 1 > topregvar)
	topregvar = dqp->nregvars -1;
      for (i = toplcv + 1; i < dqp->nregvars; i++)
	if (track[i])
	  if (regp = dqp->reg[i])
	    if (rp = regtab[i])
	      {
		if (!samevar(rp, regp))
		  track[i] = NO;
	      }
	    else if (invartable(regp))
	      {
		regtab[i] = rp = ALLOC(regdata);
		rp->vstg = regp->vstg;
		rp->vtype = regp->vtype;
		rp->memno = regp->memno;
		rp->memoffset = regp->memoffset;
		addrinfo = lookupaddr(rp->vstg, rp->memno);
		if (regp->isarrayarg)
		  {
		    rp->isarrayarg = YES;
		    rp->refs = addrinfo->refs;
		  }
		else
		  {
		    varinfo = lookupvar(addrinfo, regp->memoffset);
		    rp->refs = varinfo->refs;
		    rp->stgp = (Addrp) cpexpr(varinfo->stgp);
		    rp->istemp = addrinfo->istemp;
		    rp->isset = varinfo->isset;
		    rp->setfirst = varinfo->setfirst;
		  }
	      }
	    else
              track[i] = NO;
	  else
	    track[i] = NO;
    }

  toptrack = topregvar;

  for (i = toplcv + 1; i <= topregvar; i++)
    if (regtab[i])
      if ((track[i] == NO) || (regtab[i]->refs <= 0))
        {
	  free((char *) regtab[i]);
	  regtab[i] = NULL;
        }

  tabletop = -1;
  if (topregvar < maxregvar - 1)
    for (i = 0; i < VARTABSIZE; i++)
      for (p = vartable[i]; p; p = p->link)
	{
	  entableaddr(p);
	  if ((!p->mixedtype) &&
	      (p->vstg != STGARG) &&
	      !((p->vstg == STGCOMMON) && commonunusable))
	    for (q = p->varlist; q; q = q->link)
	      entablevar(q);
	}

  for (i = 0; (i <= tabletop) && (topregvar + 1 < maxregvar); i++)
    {
      if (inregtab(rt[i]) || (loopcost && rt[i]->isset))
	continue;
      topregvar++;
      regtab[topregvar] = rp = ALLOC(regdata);
      rp->vstg = rt[i]->vstg;
      rp->vtype = rt[i]->vtype;
      rp->memno = rt[i]->memno;
      rp->memoffset = rt[i]->memoffset;
      rp->refs = rt[i]->refs;
      rp->stgp = (Addrp) cpexpr(rt[i]->stgp);
      rp->isarrayarg = rt[i]->isarrayarg;
      rp->istemp = rt[i]->istemp;
      rp->isset = rt[i]->isset;
      rp->setfirst = rt[i]->setfirst;
    }

  for (i = toplcv + 1; i <= topregvar; i++)
    {
      if (rp = regtab[i])
	if (rp->isarrayarg)
	  {
	    ap = ALLOC(Addrblock);
	    ap->tag = TADDR;
	    ap->vstg = STGREG;
	    ap->vtype = TYADDR;
	    ap->vclass = CLVAR;
	    ap->memno = regnum[i];
	    ap->memoffset = ICON(0);

	    ap1 = ALLOC(Addrblock);
	    ap1->tag = TADDR;
	    ap1->vstg = rp->vstg;
	    ap1->vtype = rp->vtype;
	    ap1->vclass = CLVAR;
	    ap1->memno = rp->memno;
	    ap1->memoffset = ICON(0);

	    insertassign(dohead, ap, addrof(ap1));
	  }
        else if (!(rp->setfirst && rp->istemp))
	  {
	    if (rp->istemp)
	      for (sp = newcode; sp && sp != dohead; sp = sp->next)
	        if (sp->type == SKEQ)
		  {
		    ap = (Addrp) sp->expr->exprblock.leftp;
		    if ((ap->vstg == rp->vstg) && (ap->memno == rp->memno) &&
			fixedaddress(ap) &&
			(ap->memoffset->constblock.constant.ci == rp->memoffset))
		      {
			changetoreg(ap, i);
			goto L1;
		      }
		  }
	    ap = (Addrp) cpexpr(rp->stgp);
	    changetoreg(ap, i);
	    insertassign(dohead, ap, cpexpr(rp->stgp));
	  }
L1:;
    }

  for (i = toplcv + 1; i <= topregvar; i++)
    if (rp = regtab[i])
      if (rp->isset && !(rp->istemp || rp->isarrayarg))
	{
	  ap = (Addrp) cpexpr(rp->stgp);
	  changetoreg(ap, i);
	  appendassign(doend, cpexpr(rp->stgp), ap);
	}

  docount = 1;
  clearmems();
  setregs();
  sp = dohead->next;
  if (loopcost)
    for (i = toptrack + 1; i <= topregvar; i++)
      if ((rp = regtab[i]) && !rp->isarrayarg)
	{
	  ap = (Addrp) cpexpr(rp->stgp);
	  changetoreg(ap, i);
	  insertassign(sp, cpexpr(rp->stgp), ap);
	}

  for ( sp = dohead->next;
	docount || sp->type != SKNULL;
	sp = sp->next)
    if (docount > 1)
      switch (sp->type)
	{
	case SKDOHEAD:
	  docount++;
	  break;

	case SKENDDO:
	  if (--docount == 1)
	    {
	      sp1 = sp->nullslot->next;
	      while (sp1)
		{
		  if (regtomem(sp1))
		    {
		      ap = (Addrp) sp1->expr->exprblock.rightp;
		      sp2 = sp1->next;
		      for (i = toplcv + 2; i <= toptrack; i++)
			if (regtab[i] && (regnum[i] == ap->memno))
			  {
			    deleteslot(sp1);
			    break;
			  }
		      sp1 = sp2;
		    }
		  else
		    sp1 = NULL;
		}
	      clearmems();
	      if (toplcv + 1 < maxregvar)
		memdefined[toplcv + 1] = YES;
	    }
	  break;
	}
      else
	{
	  setregs();
	  for (i = 0; i <= MAXREGVAR; i++)
	    regaltered[i] = NO;
	  globalbranch = NO;

	  switch (sp->type)
	    {
	    case SKLABEL:
	      clearmems();
	      break;

	    case SKGOTO:
	      if (!locallabel(sp->label))
		gensetall(sp);
	      break;

	    case SKENDDO:
	      docount = 0;
	      break;

	    case SKRETURN:
	      gensetcommon(sp);
	      linearcode = YES;
	      regwrite(sp, sp->expr);
	      linearcode = NO;
	      break;

	    case SKDOHEAD:
	      docount++;
	      globalbranch = YES;
	      sp1 = sp->prev;
	      while (sp1)
		if (regtoreg(sp1))
		  {
		    ap = (Addrp) sp1->expr->exprblock.leftp;
		    sp2 = sp1->prev;
		    for (i = toplcv + 2; i <= toptrack; i++)
		      if (regtab[i] && (regnum[i] == ap->memno))
			{
			  deleteslot(sp1);
			  break;
			}
		    sp1 = sp2;
		  }
		else
		  sp1 = NULL;
	      break;

	    case SKEQ:
	    case SKCALL:
	    case SKSTOP:
	    case SKPAUSE:
	      linearcode = YES;
	      regwrite(sp, sp->expr);
	      for (i = toplcv + 1; i <= topregvar; i++)
		if (!regdefined[i] && ((rp = regtab[i]) && rp->isset))
		  {
		    ap = (Addrp) cpexpr(rp->stgp);
		    changetoreg(ap, i);
		    appendassign(sp, ap, cpexpr(rp->stgp));
		    sp = sp->next;
		    regdefined[i] = YES;
		  }
	      linearcode = NO;
	      break;

	    case SKARIF:
	      if (!locallabel(LM) || !locallabel(LZ) || !locallabel(LP))
		{
		  gensetall(sp);
		  globalbranch = YES;
		}
	      regwrite(sp, sp->expr);
	      break;

	    case SKASGOTO:
	      gensetall(sp);
	      globalbranch = YES;
	      regwrite(sp, sp->expr);
	      break;

	    case SKCMGOTO:
	      lp = (struct Labelblock **) sp->ctlinfo;
	      for (i = 0; i <= sp->label; i++, lp++)
		if (!locallabel((*lp)->labelno))
		  {
		    gensetall(sp);
		    globalbranch = YES;
		    break;
		  }
	      regwrite(sp, sp->expr);
	      break;

	    case SKIFN:
	    case SKIOIFN:
	      if (!locallabel(sp->label))
		{
		  gensetall(sp);
		  globalbranch = YES;
		}
	      regwrite(sp, sp->expr);
	      break;

	    case SKNULL:
	    case SKASSIGN:
	      break;

	    default:
	      badthing ("SKtype","alreg-3",sp->type);
	    }
	  
	  for (i = toplcv + 1; i <= topregvar; i++)
	    if (regaltered[i])
	      memdefined[i] = NO;
	}

  if (topregvar + 1 > highregvar)
    highregvar = topregvar + 1;
  dqptr->nregvars = topregvar + 1;
  for (i = 0; i <= topregvar; i++)
    if (rp = regtab[i])
      {
	dqptr->reg[i] = regp = ALLOC(regnode);
	regp->vstg = rp->vstg;
	regp->vtype = rp->vtype;
	regp->memno = rp->memno;
	regp->memoffset = rp->memoffset;
	regp->isarrayarg = rp->isarrayarg;
	frexpr(rp->stgp);
	free((char *) rp);
	regtab[i] = NULL;
      }

  while (tabletop >= 0)
    free((char *) rt[tabletop--]);
  freelabtab();
  freevartab();
  return;
}



LOCAL scanvars(p)
expptr p;

{
  Addrp ap;
  ADDRNODE *addrinfo;
  VARNODE *varinfo;
  chainp args;
  VARNODE *q;

  if (p == NULL) return;

  switch (p->tag)
    {
    case TCONST:
      return;

    case TEXPR:
      switch (p->exprblock.opcode)
	{
	case OPASSIGN:
	  scanassign(p);
	  return;

	case OPPLUSEQ:
	case OPSTAREQ:
	  scanopeq(p);
	  return;

	case OPCALL:
	  scancall(p);
	  return;

	default:
	  scanvars(p->exprblock.vleng);
	  scanvars(p->exprblock.leftp);
	  scanvars(p->exprblock.rightp);
	  return;
	}

    case TADDR:
      ap = (Addrp) p;
      scanvars(ap->vleng);
      scanvars(ap->memoffset);
      if (!ISVAR(ap)) return;

      addrinfo = getaddr(ap);
      if (fixedaddress(ap))
	{
	  if (ISREGTYPE(ap->vtype))
	    {
	      varinfo = getvar(addrinfo, ap);
	      varinfo->isused = YES;
	    }
	}
      else
	{
	  addrinfo->freeuse = YES;
	  for (q = addrinfo->varlist; q; q = q->link)
	    q->isused = YES;
	}
      return;

    case TLIST:
      for (args = p->listblock.listp; args; args = args->nextp)
	scanvars(args->datap);
      return;

    default:
      badtag ("regalloc:scanvars", p->tag);
    }
}



LOCAL scanassign(ep)
Exprp ep;

{
  Addrp lhs;
  VARNODE *varinfo;
  ADDRNODE *addrinfo;

  scanvars(ep->rightp);
  if (ep->leftp->tag == TADDR)
    {
      lhs = (Addrp) ep->leftp;
      scanvars(lhs->vleng);
      scanvars(lhs->memoffset);
      if ((lhs->vstg == STGREG) || (lhs->vstg == STGPREG))
	return;
      if (ISVAR(lhs))
	{
          addrinfo = getaddr(lhs);
          addrinfo->isset = YES;
          if (fixedaddress(lhs) && ISREGTYPE(lhs->vtype))
	    {
	      varinfo = getvar(addrinfo, lhs);
	      if (addrinfo->freeuse) varinfo->isused = YES;
	      varinfo->isset = YES;
	      if (!addrinfo->freeuse && !varinfo->isused)
	        varinfo->setfirst = YES;
	    }
        }
    }
  else
    badtag ("regalloc:scanassign", ep->leftp->tag);
}



LOCAL scanopeq(ep)
Exprp ep;

{
  Addrp lhs;
  ADDRNODE *addrinfo;
  VARNODE *varinfo;

  scanvars(ep->rightp);
  if (ep->leftp->tag == TADDR)
    {
      lhs = (Addrp) ep->leftp;
      scanvars(lhs->vleng);
      scanvars(lhs->memoffset);
      if ((lhs->vstg == STGREG) || (lhs->vstg == STGPREG))
	return;
      if (ISVAR(lhs))
	{
          addrinfo = getaddr(lhs);
          addrinfo->isset = YES;
          if (fixedaddress(lhs))
	    {
	      if (ISREGTYPE(lhs->vtype))
	        {
	          varinfo = getvar(addrinfo, lhs);
	          varinfo->isused = YES;
	          varinfo->isset = YES;
	        }
	    }
        }
      else
	addrinfo->freeuse = YES;
    }
  else
    badtag ("regalloc:scanopeq", ep->leftp->tag);
}



LOCAL scancall(ep)
Exprp ep;

{
  Addrp lhs;
  chainp args;
  Addrp ap;
  VARNODE *varinfo;
  ADDRNODE *addrinfo;

  lhs = (Addrp) ep->leftp;
  scanvars(lhs->vleng);
  scanvars(lhs->memoffset);

  if (ep->rightp == NULL) return;

  if (lhs->vstg == STGEXT)
    {
      args = ep->rightp->listblock.listp;
      for (; args; args = args->nextp)
	{
	  if (args->datap->tag == TADDR)
	    {
	      ap = (Addrp) args->datap;
	      scanvars(ap->vleng);
	      scanvars(ap->memoffset);
	      if (!ISVAR(ap)) continue;

	      addrinfo = getaddr(ap);
	      addrinfo->isset = YES;
	      if (fixedaddress(ap))
		{
		  varinfo = getvar(addrinfo, ap);
		  if (ap->vstg != STGCONST)
		    varinfo->isset = YES;
		  varinfo->isused = YES;
		}
	      else
		addrinfo->freeuse = YES;
	    }
	  else
	    scanvars(args->datap);
	}
    }
  else
    scanvars(ep->rightp);

  return;
}



LOCAL int fixedaddress(ap)
Addrp ap;

{
  if (!ap->memoffset)
    return NO;
  return (ISCONST(ap->memoffset) && ISINT(ap->memoffset->headblock.vtype));
}



LOCAL countrefs(p)
expptr p;

{
  Addrp ap;
  ADDRNODE *addrinfo;
  VARNODE *varinfo;
  VARNODE *vp;
  chainp args;

  if (p == NULL) return;

  switch (p->tag)
    {
    case TCONST:
      return;

    case TEXPR:
      switch (p->exprblock.opcode)
	{
	case OPCALL:
	  if (p->exprblock.leftp->tag != TADDR)
	    badtag ("regalloc:countrefs", p->exprblock.leftp->tag);
	  countrefs(p->exprblock.leftp->addrblock.vleng);
	  countrefs(p->exprblock.leftp->addrblock.memoffset);

	  if (p->exprblock.leftp->addrblock.vstg == STGEXT)
	    {
	      if (!commonunusable)
		if (linearcode)
		  setcommon();
	        else
		  commonunusable = YES;
	      if (p->exprblock.rightp == NULL) return;
	      args = p->exprblock.rightp->listblock.listp;
	      for (; args; args = args->nextp)
		if (args->datap->tag == TADDR)
		  {
		    ap = (Addrp) args->datap;
		    countrefs(ap->vleng);
		    countrefs(ap->memoffset);
		    if (!ISVAR(ap) || ap->vstg == STGCONST) continue;
		    addrinfo = lookupaddr(ap->vstg, ap->memno);
		    if (ap->vstg == STGARG)
		      addrinfo->refs++;
		    for (vp = addrinfo->varlist; vp; vp = vp->link)
		      if (linearcode)
		        if (!insetlist(ap->vstg, ap->memno, vp->memoffset))
			  if (addrinfo->istemp)
			    vp->refs--;
			  else
			    {
			      vp->refs -= 2;
			      insertset(ap->vstg, ap->memno, vp->memoffset);
			    }
		        else
			  vp->refs--;
		      else
			{
			  if (!addrinfo->istemp)
			    vp->unusable = YES;
			}
		  }
		else
		  countrefs(args->datap);
            }
	  else
	    {
	      if (p->exprblock.rightp == NULL) return;
	      args = p->exprblock.rightp->listblock.listp;
	      for (; args; args = args->nextp)
		if (args->datap->tag == TADDR)
		  {
		    ap = (Addrp) args->datap;
		    countrefs(ap->vleng);
		    countrefs(ap->memoffset);
		    if (!ISVAR(ap) || ap->vstg == STGCONST) continue;
		    addrinfo = lookupaddr(ap->vstg, ap->memno);
		    addrinfo->refs++;
		    for (vp = addrinfo->varlist; vp; vp = vp->link)
		      if (!insetlist(ap->vstg, ap->memno, vp->memoffset))
			{
			  vp->refs--;
			  insertset(ap->vstg, ap->memno, vp->memoffset);
			}
		  }
		else
		  countrefs(args->datap);
	    }
	  return;

	case OPASSIGN:
	case OPPLUSEQ:
	case OPSTAREQ:
	  countrefs(p->exprblock.vleng);
	  countrefs(p->exprblock.rightp);
	  ap = (Addrp) p->exprblock.leftp;
	  if (fixedaddress(ap))
	    if (globalbranch)
	      {
		countrefs(ap->vleng);
		countrefs(ap->memoffset);
	      }
	    else
	      countrefs(ap);
	  else if (linearcode)
	    {
	      countrefs(ap);
	      for (vp = lookupaddr(ap->vstg, ap->memno)->varlist;
		   vp;
		   vp = vp->link)
		vp->refs--;
	    }
	  else
	    {
	      countrefs(ap);
	      for (vp = lookupaddr(ap->vstg, ap->memno)->varlist;
		   vp;
		   vp = vp->link)
		vp->unusable = YES;
	    }
	  return;

	default:
	  countrefs(p->exprblock.vleng);
	  countrefs(p->exprblock.leftp);
	  countrefs(p->exprblock.rightp);
	  return;
	}

    case TADDR:
      ap = (Addrp) p;
      countrefs(ap->vleng);
      countrefs(ap->memoffset);
      if (!ISVAR(ap)) return;

      addrinfo = lookupaddr(ap->vstg, ap->memno);
      if (ap->vstg == STGARG)
	addrinfo->refs++;

      if (fixedaddress(ap))
	{
	  if (ISREGTYPE(ap->vtype))
	    {
	      varinfo = lookupvar(addrinfo, ap->memoffset->constblock.constant.ci);
	      varinfo->refs++;
	    }
	}
      else
	for (vp = addrinfo->varlist; vp; vp = vp->link)
	  if (!insetlist(ap->vstg, ap->memno, vp->memoffset))
	    {
	      vp->refs--;
	      insertset(ap->vstg, ap->memno, vp->memoffset);
	    }
      return;

    case TLIST:
      args = p->listblock.listp;
      for (; args; args = args->nextp)
	countrefs(args->datap);
      return;

    default:
      badtag ("regalloc:countrefs", p->tag);
    }
}



LOCAL regwrite(sp, p)
Slotp sp;
expptr p;

{
  register int i;
  REGDATA *rp;
  chainp args;
  Addrp ap, ap1;
  int memoffset;

  if (p == NULL) return;

  switch (p->tag)
    {
    case TCONST:
      return;

    case TEXPR:
      switch (p->exprblock.opcode)
	{
	case OPCALL:
	  ap = (Addrp) p->exprblock.leftp;
	  regwrite(sp, ap->vleng);
	  regwrite(sp, ap->memoffset);
	  if (ap->vstg == STGEXT)
	    {
	      if (linearcode)
		{
		  gensetcommon(sp);
		  for (i = toplcv + 1; i <= topregvar; i++)
		    if ((rp = regtab[i]) && (rp->vstg == STGCOMMON))
		      regdefined[i] = NO;
		}
	      if (p->exprblock.rightp == NULL) return;
	      args = p->exprblock.rightp->listblock.listp;
	      for (; args; args = args->nextp)
		if (args->datap->tag == TADDR)
		  {
		    ap = (Addrp) args->datap;
		    regwrite(sp, ap->vleng);
		    regwrite(sp, ap->memoffset);
		    for (i = toplcv + 1; i <= topregvar; i++)
		      if ((rp = regtab[i]) &&
			  !rp->isarrayarg &&
			  !rp->istemp &&
			  (rp->vstg == ap->vstg) &&
			  (rp->memno == ap->memno))
			{
			  regdefined[i] = NO;
			  if (!memdefined[i])
			    {
			      ap1 = (Addrp) cpexpr(rp->stgp);
			      changetoreg(ap1, i);
			      insertassign(sp, cpexpr(rp->stgp), ap1);
			      memdefined[i] = YES;
			    }
			}
		      else if (rp->isarrayarg &&
			       (ap->vstg == STGARG) &&
			       (ap->memno == rp->memno))
			{
			  ap->vstg = STGPREG;
			  ap->memno = regnum[i];
			}
		  }
		else
		  regwrite(sp, args->datap);
	    }
	  else
	    {
	      if (p->exprblock.rightp == NULL) return;
	      args = p->exprblock.rightp->listblock.listp;
	      for (; args; args = args->nextp)
		if (args->datap->tag == TADDR)
		  {
		    ap = (Addrp) args->datap;
		    regwrite(sp, ap->vleng);
		    regwrite(sp, ap->memoffset);
		    for (i = toplcv + 1; i <= topregvar; i++)
		      if ((rp = regtab[i]) &&
			  !rp->isarrayarg &&
			  !rp->istemp &&
			  (rp->vstg == ap->vstg) &&
			  (rp->memno == ap->memno) &&
			  !memdefined[i])
			{
			  ap1 = (Addrp) cpexpr(rp->stgp);
			  changetoreg(ap1, i);
			  insertassign(sp, cpexpr(rp->stgp), ap1);
			  memdefined[i] = YES;
			}
		      else if (rp->isarrayarg &&
			       (ap->vstg == STGARG) &&
			       (rp->memno == ap->memno))
			{
			  ap->vstg = STGPREG;
			  ap->memno = regnum[i];
			}
		  }
	    }
	  return;

	case OPASSIGN:
	case OPPLUSEQ:
	case OPSTAREQ:
	  regwrite(sp, p->exprblock.vleng);
	  regwrite(sp, p->exprblock.rightp);
	  ap = (Addrp) p->exprblock.leftp;
	  regwrite(sp, ap->vleng);
	  regwrite(sp, ap->memoffset);

	  if (ap->vstg == STGARG)
	    for (i = toplcv + 1; i<=topregvar; i++)
	      if ((rp = regtab[i]) &&
		  rp->isarrayarg &&
		  (rp->memno == ap->memno))
		{
		  ap->vstg = STGPREG;
		  ap->memno = regnum[i];
		  return;
		}

	  if (fixedaddress(ap))
	    {
	      memoffset = ap->memoffset->constblock.constant.ci;
	      for (i = toplcv + 1; i <= topregvar; i++)
		if ((rp = regtab[i]) &&
		    !rp->isarrayarg &&
		    (rp->vstg == ap->vstg) &&
		    (rp->memno == ap->memno) &&
		    (rp->memoffset == memoffset))
		  {
		    changetoreg(ap, i);
		    if (globalbranch)
		      {
			p->exprblock.rightp = (expptr) cpexpr(p);
			p->exprblock.leftp = (expptr) cpexpr(rp->stgp);
			p->exprblock.opcode = OPASSIGN;
			memdefined[i] = YES;
		      }
		    else
		      regaltered[i] = YES;
		  }
	      return;
	    }

	  if (linearcode)
	    for (i = toplcv + 1; i <= topregvar; i++)
	      if ((rp = regtab[i]) &&
		  !rp->isarrayarg &&
		  (rp->vstg == ap->vstg) &&
		  (rp->memno == ap->memno))
		regdefined[i] = NO;
	  return;

	default:
	  regwrite(sp, p->exprblock.vleng);
	  regwrite(sp, p->exprblock.leftp);
	  regwrite(sp, p->exprblock.rightp);
	  return;
	}

    case TADDR:
      ap = (Addrp) p;
      regwrite(sp, ap->vleng);
      regwrite(sp, ap->memoffset);

      if (ap->vstg == STGARG)
	for (i = toplcv + 1; i <= topregvar; i++)
	  if ((rp = regtab[i]) &&
	      rp->isarrayarg &&
	      (rp->memno == ap->memno))
	    {
	      ap->vstg = STGPREG;
	      ap->memno = regnum[i];
	      return;
	    }

      if (fixedaddress(ap))
	{
          memoffset = ap->memoffset->constblock.constant.ci;
	  for (i = toplcv + 1; i <= topregvar; i++)
	    if ((rp = regtab[i]) &&
		!rp->isarrayarg &&
		(rp->vstg == ap->vstg) &&
		(rp->memno == ap->memno) &&
		(rp->memoffset == memoffset))
	      {
		changetoreg(ap, i);
		return;
	      }
	}
      else
	{
	  for (i = toplcv + 1; i <= topregvar; i++)
	    if ((rp = regtab[i]) &&
		!rp->isarrayarg &&
		(rp->vstg == ap->vstg) &&
		(rp->memno == ap->memno) &&
		!memdefined[i])
	      {
		ap1 = (Addrp) cpexpr(rp->stgp);
		changetoreg(ap1, i);
		insertassign(sp, cpexpr(rp->stgp), ap1);
		memdefined[i] = YES;
	      }
	}
      return;

    case TLIST:
      for (args = p->listblock.listp; args; args = args->nextp)
	regwrite(sp, args->datap);
      return;

    default:
      badtag ("regalloc:regwrite", p->tag);
    }
}



LOCAL setcommon()

{
  ADDRNODE *ap;
  VARNODE *vp;

  for (ap = commonvars; ap; ap = ap->commonlink)
    for (vp = ap->varlist; vp; vp = vp->link)
      if (!insetlist(ap->vstg, ap->memno, vp->memoffset))
	{
	  vp->refs -= 2;
	  insertset(ap->vstg, ap->memno, vp->memoffset);
	}
      else
	vp->refs--;

  return;
}

 

LOCAL setall()

{
  register int i;
  register ADDRNODE *p;
  register VARNODE *q;

  for (i = 0; i < VARTABSIZE; i++)
    for (p = vartable[i]; p; p = p->link)
      if (p->istemp || !p->isset)
	break;
      else
	for (q = p->varlist; q; q = q->link)
	  if (q->isset && !insetlist(p->vstg, p->memno, q->memoffset))
	    q->refs--;

  allset = YES;
  return;
}



LOCAL int samevar(r1, r2)
register REGDATA *r1;
register REGNODE *r2;

{
  if ((r1->vstg != r2->vstg) ||
      (r1->memno != r2->memno) ||
      (r1->isarrayarg != r2->isarrayarg))
    return NO;
  if (r1->isarrayarg)
    return YES;
  return (r1->memoffset == r2->memoffset);
}



LOCAL entableaddr(p)
ADDRNODE *p;

{
  int refs;
  Addrp ap;
  register int i;

  if (p->vstg != STGARG)
    {
      currentaddr = p;
      return;
    }

  refs = p->refs;
  if (refs <= 0) return;

  if (tabletop < 0)
    tabletop = i = 0;
  else if (refs > rt[tabletop]->refs)
    {
      if (tabletop + 1 < TABLELIMIT)
	tabletop++;
      else
	{
	  frexpr(rt[tabletop]->stgp);
	  free((char *) rt[tabletop]);
	}

      for (i = tabletop; i > 0; i--)
	if (refs > rt[i - 1]->refs)
	  rt[i] = rt[i - 1];
	else
	  break;
    }
  else if (tabletop + 1 < TABLELIMIT)
    i = ++tabletop;
  else
    return;

  rt[i] = ALLOC(regdata);
  rt[i]->vstg = p->vstg;
  rt[i]->vtype = p->vtype;
  rt[i]->memno = p->memno;
  rt[i]->refs = refs;
  rt[i]->isarrayarg = YES;

  return;
}




LOCAL entablevar(p)
VARNODE *p;

{
  int refs;
  register int i;

  if (p->unusable) return;
  refs = p->refs - loopcost;
  if (refs <= 0) return;

  if (tabletop < 0)
    tabletop = i = 0;
  else if (refs > rt[tabletop]->refs)
    {
      if (tabletop + 1 < TABLELIMIT)
        tabletop++;
      else
	{
	  frexpr(rt[tabletop]->stgp);
          free((char *) rt[tabletop]);
	}

      for (i = tabletop; i > 0; i--)
        if (refs > rt[i - 1]->refs)
          rt[i] = rt[i - 1];
        else
          break;
    }
  else if (tabletop + 1 < TABLELIMIT)
    i = ++tabletop;
  else
    return;

  rt[i] = ALLOC(regdata);
  rt[i]->vstg = currentaddr->vstg;
  rt[i]->vtype = currentaddr->vtype;
  rt[i]->memno = currentaddr->memno;
  rt[i]->memoffset = p->memoffset;
  rt[i]->refs = refs;
  rt[i]->stgp = (Addrp) cpexpr(p->stgp);
  rt[i]->isarrayarg = NO;
  rt[i]->istemp = currentaddr->istemp;
  rt[i]->isset = p->isset;
  rt[i]->setfirst = p->setfirst;

  return;
}



LOCAL int inregtab(p)
register REGDATA *p;

{
  register REGDATA *rp;
  register int i;

  for (i = 0; i <= topregvar; i++)
    if (rp = regtab[i])
      if ((rp->vstg == p->vstg) &&
	  (rp->memno == p->memno) &&
	  (rp->isarrayarg == p->isarrayarg))
	if (rp->isarrayarg)
          return YES;
        else if (rp->memoffset == p->memoffset)
          return YES;

  return NO;
}



LOCAL changetoreg(ap, i)
register Addrp ap;
int i;

{
  ap->vstg = STGREG;
  ap->memno = regnum[i];
  frexpr(ap->memoffset);
  ap->memoffset = ICON(0);
  ap->istemp = NO;
  return;
}



LOCAL insertassign(sp, dest, src)
Slotp sp;
Addrp dest;
expptr src;

{
  Slotp newslot;
  expptr p;

  p = mkexpr(OPASSIGN, dest, src);
  newslot = optinsert (SKEQ,p,0,0,sp);

  if (sp == dohead)
    if (!newcode)
      newcode = newslot;

  return;
}


LOCAL appendassign(sp, dest, src)
Slotp sp;
Addrp dest;
expptr src;

{
  Slotp newslot;
  expptr p;

  if (!sp)
    fatal ("regalloc:appendassign");

  p = mkexpr(OPASSIGN, dest, src);
  newslot = optinsert (SKEQ,p,0,0,sp->next);

  return;
}



LOCAL int regtomem(sp)
Slotp sp;

{
  expptr p, l, r;
  int i;

  if (sp->type != SKEQ) return NO;

  p = sp->expr;
  if ((p->tag != TEXPR) || (p->exprblock.opcode != OPASSIGN))
    return NO;

  r = p->exprblock.rightp;
  if ((r->tag != TADDR) || (r->addrblock.vstg != STGREG))
    return NO;

  l = p->exprblock.leftp;
  if (l->tag != TADDR)
    return NO;
  i = r->addrblock.memno;
  if (regtab[i] &&
      (l->addrblock.vstg == regtab[i]->vstg) &&
      (l->addrblock.memno == regtab[i]->memno) &&
      fixedaddress(l) &&
      (l->addrblock.memoffset->constblock.constant.ci == regtab[i]->memoffset))
    return YES;

  return NO;
}



LOCAL int regtoreg(sp)
Slotp sp;

{
  expptr p, l, r;

  if (sp->type != SKEQ) return NO;

  p = sp->expr;
  if ((p->tag != TEXPR) || (p->exprblock.opcode != OPASSIGN))
    return NO;

  l = p->exprblock.leftp;
  if ((l->tag != TADDR) || (l->addrblock.vstg != STGREG))
    return NO;

  r = p->exprblock.rightp;
  if ((r->tag == TADDR) &&
      (r->addrblock.vstg == STGREG) &&
      (r->addrblock.memno == l->addrblock.memno))
    return YES;
  if ((r->tag == TEXPR) &&
      (r->exprblock.opcode == OPADDR))
    return YES;

  return NO;
}



LOCAL deleteslot(sp)
Slotp sp;

{
  if (newcode == sp)
    {
      newcode = sp->next;
      if (newcode == dohead)
	newcode = NULL;
    }

  delslot (sp);
  return;
}



LOCAL gensetall(sp)
Slotp sp;

{
  register int i;
  register REGDATA *rp;
  register Addrp ap;

  for (i = toplcv + 1; i <= topregvar; i++)
    if (rp = regtab[i])
      if (rp->isset && !(rp->istemp || rp->isarrayarg))
	if (!memdefined[i])
	  {
	    ap = (Addrp) cpexpr(rp->stgp);
	    changetoreg(ap, i);
	    insertassign(sp, cpexpr(rp->stgp), ap);
	    memdefined[i] = YES;
	  }

  return;
}



LOCAL gensetcommon(sp)
Slotp sp;

{
  register int i;
  register REGDATA *rp;
  register Addrp ap;

  for (i = toplcv + 1; i <= topregvar; i++)
    if (rp = regtab[i])
      if ((rp->vstg == STGCOMMON) && !rp->isarrayarg)
	if (!memdefined[i])
	  {
	    ap = (Addrp) cpexpr(rp->stgp);
	    changetoreg(ap, i);
	    insertassign(sp, cpexpr(rp->stgp), ap);
	    memdefined[i] = YES;
	  }

  return;
}



LOCAL clearmems()

{
  REGDATA *rp;
  register int i;

  for (i = 0; i <= toplcv; i++)
    memdefined[i] = YES;
  for (; i <= topregvar; i++)
    if ((rp = regtab[i]) && rp->isset)
      memdefined[i] = NO;
    else
      memdefined[i] = YES;
  return;
}


LOCAL setregs()

{
  register int i;

  for (i = 0; i <= topregvar; i++)
    regdefined[i] = YES;
  return;
}



regalloc()

{
int	match;
Slotp	nextslot;
Slotp	sl1,sl2;
Slotp	lastlabslot;

if (! optimflag) return;

lastlabslot = NULL;
for (sl1 = firstslot; sl1; sl1 = nextslot)
	{
	nextslot = sl1->next;
	switch (sl1->type)
	    {

/* temporarily commented out -----
	    case SKLABEL:
		lastlabslot = sl1;
		break;

	    case SKGOTO:
		if (lastlabslot && sl1->label == lastlabslot->label)
			{
			dohead = lastlabslot;
			doend = sl1;
			alreg ();
			}
		break;
----- */

	    case SKDOHEAD:
		pushq (sl1);
		break;

	    case SKENDDO:
		match = 0;
		for (sl2 = sl1; sl2; sl2 = sl2->prev)
			{
			if (sl2->type == SKDOHEAD) match++;
			else if (sl2->type == SKENDDO) match--;
			if (match == 0) break;
			}
		if (sl2)
			dohead = sl2;
		else
			fatal ("unmatched enddo in code buffer");
		if (sl2->type != SKDOHEAD)
			fatal ("internal error in regalloc");

		for (dqptr = dqbottom; dqptr; dqptr = dqptr->up)
			{
			if (dqptr->dohead == dohead)
				break;
			}

		if (!dqptr)
			fatal ("garbled doqueue in regalloc");

		/*  sl1 now points to the SKENDDO slot; the SKNULL slot
		 *  is reached through sl1->nullslot
		 */
		doend = (Slotp) sl1->nullslot;

		alreg ();
		break;

	    default:
		break;
	    }
	}

while (dqtop)
	popq (dqtop->dohead);

return;
}



LOCAL pushq(sp)
Slotp sp;

{
  DOQUEUE *t;

  if (sp->type != SKDOHEAD)
    fatal("regalloc:pushq:  DO statement expected");

  if (dqbottom)
    {
      t = ALLOC(doqueue);
      t->up = dqbottom;
      dqbottom->down = t;
      dqbottom = t;
    }
  else
    dqtop = dqbottom = ALLOC(doqueue);

  dqbottom->dohead = sp;
}


LOCAL popq(sp)
Slotp sp;

{
  DOQUEUE *t;
  register int i;

  if (!dqtop)
    fatal("regalloc:popq:  empty DO queue");
  if (dqtop->dohead != sp)
    fatal("regalloc:popq:  garbled DO queue");

  t = dqtop;
  
  dqtop = t->down;
  if (dqtop)
    dqtop->up = NULL;
  else
    dqbottom = NULL;
  for (i = 0; i < MAXREGVAR; i++)
    if (t->reg[i])
      free((char *) t->reg[i]);
  free(t);
}
