Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
kcc-4/kcc/ccreg.c
There are 8 other files named ccreg.c in the archive. Click here to see a list.
/* CCREG.C - Register management
**
** All changes after version 26 (8/8/85), unless otherwise specified, are
** Copyright 1985, 1986 by Ken Harrenstien, SRI International.
**
** David Eppstein / Stanford University / 8 Mar 1985
*/
#include "cc.h"
#include "ccgen.h"
#define REGLIST(x) static VREG x = {0, 0, (TYPE *)NULL, 0, &x, &x}
REGLIST(freelist); /* virtual regs not now in use */
REGLIST(reglist); /* regs associated with real regs */
REGLIST(spillist); /* regs spilled onto the stack */
static VREG *regis[NREGS]; /* who is using which registers */
static int regfree[NREGS]; /* what regs are used in code */
/* Exported functions */
void iniregs(), endregs(), spill(), release(), widen(), narrow(), backreg();
VREG *getreg(), *getpair(), *getret(), *getrpair();
int rispair(), realreg(), rfree();
/* Imported functions */
extern PCODE *before(); /* routine to poke through pbuf */
/* Internal functions */
static int empty(), findreg(), findpair();
static void unlink(), linkreg(), spillreg(), spillone(),
updrfree(), setreg(), setpair();
static VREG *newreg();
/*
** INIREGS - Initialize for start of new code
** Called at the start of each function or code initializer by inicode()
** ENDREGS - Perform wrap-up checks at end of code.
** Called at end of each function or initializer by gend().
*/
void
iniregs()
{
int i;
endregs();
for (i = 0; i < NREGS; i++) regis[i] = NULL;
}
void
endregs()
{
if (!empty(®list) || !empty(&spillist)) {
warn (ERELREG, "unreleased registers left over from previous code");
/* Try to release all regs */
while (!empty(®list)) {
if ((int) reglist.next < NREGS)
efatal(EINT, "bad reglist pointer");
release(reglist.next);
}
while (!empty(&spillist)) {
if ((int) spillist.next < NREGS)
efatal(EINT, "bad spillist pointer");
release(spillist.next);
}
}
}
/*
** Determine whether a list is empty
** Call with &freelist, ®list, or &spillist.
*/
static int
empty(list)
VREG *list;
{
return (list->next == list);
}
/*
** Remove a register from whatever list it's on
** This is the first half of changing from one list to another
*/
static void
unlink(reg)
VREG *reg;
{
if (empty(reg)) {
warn(ERELREG, "attempt to unlink list head");
return;
}
reg->next->prev = reg->prev;
reg->prev->next = reg->next;
}
/*
** Add a register to a list
** Used when first create a new reg and when moving from one list to another
*/
static void
linkreg(reg, list)
VREG *reg, *list;
{
reg->next = list->next;
list->next->prev = reg;
reg->prev = list;
list->next = reg;
}
/*
** Get an unused pseudo register
*/
static VREG *
newreg()
{
char *cp, *malloc();
VREG *rp;
if (empty (&freelist)) {
cp = malloc (sizeof (VREG));
if (cp == NULL) fatal (EOUTMEM);
rp = (VREG *) cp;
} else {
rp = freelist.next;
unlink (rp);
}
rp->flags = 0;
return rp;
}
/*
** Spill one real register
** This takes care of stacking it and deassigning it.
*/
static void
spillreg (reg)
{
code0 (P_PUSH, R_SP, reg); /* push onto stack */
regis[reg] = NULL; /* no longer here */
++stackoffset; /* stack is bigger now */
}
/*
** Spill a register.
** Either we needed to reallocate it or we are calling a function.
** In either case the register moves onto the stack.
*/
static void
spillone(reg)
VREG *reg;
{
if (reg->flags & VRF_SPILLED)
warn (ERELREG, "attempt to spill already spilled reg");
unlink (reg); /* remove from assigned list */
spillist.next->prevloc = stackoffset; /* remember where we are */
spillreg (reg->location);
if (reg->flags & VRF_REGPAIR) spillreg (reg->location + 1);
reg->location = stackoffset;
reg->flags |= VRF_SPILLED;
linkreg (reg, &spillist); /* it's now spilled */
}
/*
** Spill all registers
** This is needed to save values over subr calls
*/
void
spill()
{
while (!empty (®list)) spillone (reglist.next);
}
/*
** See which registers are in use in the peephole buffer.
** We try to avoid assigning these so that common subexpression
** elimination will have the greatest opportunity to work.
**
** Since this is merely a heuristic and since it is called very often
** per compilation, we care more about speed than accuracy.
** In particular, we don't even bother looking at the opcode or
** addressing mode of each instruction.
*/
static void
updrfree()
{
int r;
PCODE *p;
for (r = 0; r < NREGS; r++) regfree[r] = (regis[r] == NULL);
for (p = previous; p != NULL && p->Pop != P_PUSHJ; p = before(p))
regfree[p->Preg] = 0;
}
/*
** Find an unused scratch register.
** If none exist, we spill what is likely to be the
** earliest allocated register (since our register allocation
** will tend to act like a stack this is a win).
*/
static int
findreg(reg)
VREG *reg;
{
int rnum;
updrfree(); /* update regfree[] to pbuf contents */
for (rnum = R_MINREG; rnum <= R_MAXREG; rnum++) /* try for a register not */
if (regfree[rnum]) return rnum; /* ..used at all in this local block */
for (rnum = R_MINREG; rnum <= R_MAXREG; rnum++) /* no, try for a free one */
if (regis[rnum] == NULL) return rnum;
spillone (regis[R_MINREG]); /* spill something. better random */
return R_MINREG; /* ..but this never happens anyway */
}
/*
** Same, but this time we return the first of a pair of registers.
** We have to be careful not to return the very last register.
*/
static int
findpair()
{
int rnum;
updrfree(); /* update regfree[] to pbuf contents */
for (rnum = R_MINREG; rnum < R_MAXREG; rnum++) /* try for unused reg */
if (regfree[rnum] && regfree[rnum+1]) return rnum;
for (rnum = R_MINREG; rnum < R_MAXREG; rnum++) /* no, try for a free one */
if (regis[rnum] == NULL && regis[rnum+1] == NULL) return rnum;
/* None free, pick an "arbitrary" pair (the first one).
** Note that both regs must be checked since either might already
** be null, and the release of the first reg may free up the second if
** these two regs were already a pair.
*/
if (regis[R_MINREG])
spillone (regis[R_MINREG]); /* no, spill something */
if (regis[R_MINREG+1])
spillone (regis[R_MINREG+1]); /* and another to make a pair */
return R_MINREG;
}
/*
** Set a virtual register's location to be some real reg
*/
static void
setreg (reg, loc)
VREG *reg;
{
reg->location = loc;
regis[loc] = reg;
}
/*
** Same, but set it for a double register pair
*/
static void
setpair (reg, loc)
VREG *reg;
{
reg->location = loc;
regis[loc] = reg;
regis[loc+1] = reg;
reg->flags |= VRF_REGPAIR;
}
/*
** Assign a new register
*/
VREG *
getreg()
{
VREG *reg;
reg = newreg();
setreg (reg, findreg());
linkreg (reg, ®list);
return reg;
}
/*
** Assign a pair of registers
*/
VREG *
getpair()
{
VREG *reg;
reg = newreg();
setpair (reg, findpair());
linkreg (reg, ®list);
return reg;
}
/*
** Get a register for holding a return value
*/
VREG *
getret()
{
VREG *reg = newreg();
if (regis[R_RETVAL] != NULL) spillone(regis[R_RETVAL]);
setreg (reg, R_RETVAL);
linkreg (reg, ®list);
return reg;
}
/*
** Return a doubleword
*/
VREG *
getrpair()
{
VREG *reg = newreg();
if (regis[R_RETVAL] != NULL) spillone(regis[R_RETVAL]);
if (regis[R_RETDBL] != NULL) spillone(regis[R_RETDBL]);
setpair (reg, R_RETVAL);
linkreg (reg, ®list);
return reg;
}
/*
** Forget about a no-longer-in-use register
** The reg can be a small integer, in which case we ignore it.
*/
void
release(reg)
VREG *reg;
{
if ((int) reg < NREGS) return;
if (reg->flags & VRF_SPILLED) warn (ERELREG, "release of a spilled register");
unlink (reg);
regis[reg->location] = NULL;
if (reg->flags & VRF_REGPAIR) regis[reg->location + 1] = NULL;
linkreg (reg, &freelist);
}
/* RISPAIR - Return true if register is a doubleword pair
*/
int
rispair(reg)
VREG *reg;
{ return (reg->flags & VRF_REGPAIR) != 0;
}
/*
** Make a singleword register into a doubleword
*/
void
widen(reg)
VREG *reg;
{
int nreg;
if (reg->flags & VRF_REGPAIR) warn (ERELREG, "doubly widened register");
reg->flags |= VRF_REGPAIR; /* make it wide */
if (reg->flags & VRF_SPILLED) return; /* on stack, don't need to find reg */
if (reg->location < R_MAXREG && regis[reg->location+1] == NULL) {
regis[reg->location+1] = reg; /* space available after it */
return; /* take it and return */
}
nreg = findpair(); /* not available, get new pair */
code0 (P_MOVE, nreg, reg->location); /* move registers across */
regis[reg->location] = NULL; /* free old place */
setpair (reg, nreg); /* and set up new one instead */
}
/*
** Extract one word of a doubleword register
** If the second arg is one, we take the second word rather than the first
*/
void
narrow(reg, low)
VREG *reg;
{
if (!(reg->flags & VRF_REGPAIR)) warn(ERELREG, "doubly narrowed register");
if (low) regis[reg->location++] = NULL;
else regis[reg->location + 1] = NULL;
reg->flags &=~ VRF_REGPAIR;
}
/*
** Return a physical register number for some virtual register
** Pulls it back off the stack if necessary
*/
int
realreg(reg)
VREG *reg;
{
if ((int) reg < NREGS) return (int) reg;
/* check for spilled register now somewhere on stack */
if (reg->flags & VRF_SPILLED) {
int nreg;
/* put it back into a register */
reg->flags &=~ VRF_SPILLED;
unlink (reg);
if (reg->flags & VRF_REGPAIR) {
code12 (P_DMOVE, nreg = findpair(), reg->location - 1 - stackoffset);
setpair (reg, nreg);
} else {
code12 (P_MOVE, nreg = findreg(), reg->location - stackoffset);
setreg (reg, nreg);
}
/* drop stack to top remaining spilled reg */
if (reg->next == spillist.next) {
code8 (P_ADJSP, R_SP, spillist.next->prevloc - stackoffset);
stackoffset = spillist.next->prevloc;
}
linkreg (reg, ®list);
}
/* in a reg now no matter what, so just return it */
return reg->location;
}
/*
** Return whether register is still assigned
** This is necessary because the outside world can't see regis[]
*/
int
rfree(reg)
{
return regis[reg] == NULL;
}
/*
** Hack to back up over P_MOVE R,S leaving resulting reg allocated.
** Used by switch case jump generation to avoid lossage from ufcreg().
*/
void
backreg (reg)
VREG *reg;
{
if ((int) reg < NREGS) return; /* only for virtual register */
regis[realreg (reg)] = NULL; /* swap in and deassign */
regis[reg->location = ufcreg (reg->location)] = reg; /* reassign */
}