Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/kcc/ccreg.c
There are 8 other files named ccreg.c in the archive. Click here to see a list.
/* CCREG.C - Register management
**
** (c) Copyright Ken Harrenstien 1989
** All changes after v.45, 6-Apr-1988
** (c) Copyright Ken Harrenstien, SRI International 1985, 1986
** All changes after v.26, 8-Aug-1985
**
** Original version by David Eppstein / Stanford University / 8 Mar 1985
*/
#include "cc.h"
#include "ccgen.h"
/* Exported functions */
void vrinit(); /* Init regs for new routine */
void vrendchk(); /* Check regs at end of routine */
VREG *vrget(), *vrdget(); /* Allocate virtual register or pair */
VREG *vrretget(), *vrretdget(); /* Same but use return-value regs */
void vrfree(); /* Release vreg */
int vrreal(); /* Get real reg # for active virtual reg */
int vrtoreal(); /* Ensure reg is active, return real # */
int vrstoreal(); /* Same for 2 regs, return # of 1st */
void vrallspill(); /* Spill all active regs onto stack */
VREG *vrwiden(); /* Widen a vreg into a pair */
void vrlowiden(); /* Common case: widen vreg in low direction */
void vrnarrow(); /* Narrow a vreg pair into single vreg */
void vrufcreg(); /* Undo MOVE of failed changereg w/o freeing */
int vrispair(); /* TRUE if vreg is 1st of a vreg pair */
int rfree(); /* TRUE if real register is assigned */
int rhasval(); /* TRUE if real register has a value */
/* Imported functions */
extern PCODE *before(); /* CCCODE */
extern void code00(), code8(), code12(); /* CCCODE */
extern int ufcreg(); /* CCCREG */
/* Internal functions */
static int rrfind(), rrdfind();
static void updrfree();
static void vr1free();
static void vrspill(), vr1spill(), vrunlink(), vr1unlink();
static VREG *vrsetrr(), *vr1setrr(), *vr1link(), *vrlink();
static VREG *vralloc(), *vrdalloc();
#define empty(vr) ((vr)->Vrnext == (vr)) /* TRUE if list is empty */
#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 */
/* Special vregs which the macros VR_RETVAL and VR_SP point to. These
** exist only so routines can force the use of certain physical regs in
** special places, by providing the appropriate phys reg value. These
** vregs are never strung on a list or used in any other way.
*/
VREG vr_retval = { VRF_SPECIAL|VRF_LOCK, R_RETVAL };
VREG vr_sp = { VRF_SPECIAL|VRF_LOCK, R_SP };
#if SYS_CSI /* For FORTRAN linkage */
VREG vr_fap = { VRF_SPECIAL|VRF_LOCK, R_FAP };
VREG vr_zero = { VRF_SPECIAL|VRF_LOCK, R_ZERO };
#endif
/*
** VRINIT - Initialize for start of new code
** Called at the start of each function or code initializer by inicode()
** VRENDCHK - Perform wrap-up checks at end of code.
** Called at end of each function or initializer by gend().
*/
void
vrinit()
{
int i;
vrendchk();
for (i = 0; i < NREGS; i++) regis[i] = NULL;
}
void
vrendchk()
{
if (!empty(®list) || !empty(&spillist)) {
int_warn("vrendchk: leftover regs");
/* Try to release all regs */
while (!empty(®list)) {
if ((int) reglist.Vrnext < NREGS) {
int_error("vrendchk: bad reglist");
break;
}
vrfree(reglist.Vrnext);
}
while (!empty(&spillist)) {
if ((int) spillist.Vrnext < NREGS) {
int_error("vrendchk: bad spillist");
break;
}
vrfree(spillist.Vrnext);
}
}
}
/* VRGET - Assign a new virtual register with corresponding real register.
*/
VREG *
vrget()
{
return vr1link(vr1setrr(vralloc(), rrfind()), ®list);
}
/* VRDGET - Same as VRGET but assigns a double-word register.
*/
VREG *
vrdget()
{
return vrlink(vrsetrr(vrdalloc(), rrdfind()), ®list);
}
/* VRRETGET - Get a register for holding a return value
*/
VREG *
vrretget()
{
if (regis[R_RETVAL] != NULL) /* Ensure return reg is free */
vrspill(regis[R_RETVAL]);
return vr1link(vr1setrr(vralloc(), R_RETVAL), ®list);
}
/* VRRETDGET - Get a double register for holding return value
*/
VREG *
vrretdget()
{
if (regis[R_RETVAL] != NULL) vrspill(regis[R_RETVAL]);
if (regis[R_RETDBL] != NULL) vrspill(regis[R_RETDBL]);
return vrlink(vrsetrr(vrdalloc(), R_RETVAL), ®list);
}
/* VRFREE - Forget about a no-longer-in-use register
**
*/
void
vrfree(vr)
VREG *vr;
{
if ((int)vr < NREGS) { /* Catch obsolete usage */
int_error("vrfree: bad vr %o", vr);
return;
}
if (vr->Vrflags & VRF_REGPAIR) /* If 1st of a pair, */
vr1free(vr->Vrmate); /* free 2nd first */
vr1free(vr);
}
static void
vr1free(vr)
VREG *vr;
{
if (vr->Vrflags & VRF_SPECIAL) return;
if (vr->Vrflags & VRF_SPILLED)
int_warn("vr1free: spilled reg");
else regis[vr->Vrloc] = NULL; /* Say real reg now free */
vr1unlink(vr); /* Unlink reg(s), move to freelist */
vr1link(vr, &freelist);
/* No need to clear flags as vralloc() will do this. */
}
/* VRALLSPILL - Spill all registers
** This is needed to save values over subr calls and conditional exprs.
*/
void
vrallspill()
{
while (!empty(®list))
vrspill(reglist.Vrnext);
}
/* VRSPILL - Spill a virtual register.
** Either we needed to reallocate it or we are calling a function.
** In either case the register moves onto the stack.
*/
static void
vrspill(vr)
VREG *vr;
{
vr1spill(vr); /* Must ALWAYS push 1st reg first!!!! */
if (vr->Vrflags & VRF_REGPAIR)
vr1spill(vr->Vrmate); /* then 2nd if a pair */
}
static void
vr1spill(vr)
VREG *vr;
{
if (vr->Vrflags & VRF_SPILLED)
int_warn("vr1spill: reg already spilled");
vr1unlink(vr); /* remove from assigned list */
spillist.Vrnext->Vroldstk = stackoffset; /* remember where we are */
code00(P_PUSH, R_SP, vr->Vrloc); /* push on stack, don't release */
regis[vr->Vrloc] = NULL; /* no longer here */
vr->Vrloc = ++stackoffset; /* Stack bigger now, note new loc */
vr->Vrflags |= VRF_SPILLED;
vr1link(vr, &spillist); /* it's now spilled */
}
/* VRWIDEN - Make single register into a register pair and returns that.
** If "low" argument non-zero, register becomes the low word.
** Contents of additional word are indeterminate.
*/
VREG *
vrwiden(reg, low)
VREG *reg; /* Old existing register */
{
VREG *nreg; /* New added register */
VREG *vr1, *vr2; /* 1st and 2nd regs of pair */
int rr;
if (reg->Vrflags & (VRF_REGPAIR|VRF_REG2ND)) {
int_warn("vrwiden: reg already wide");
return reg;
}
/* Turn a single vreg into a vreg pair */
reg->Vrmate = nreg = vralloc(); /* Get another vreg to make a pair */
nreg->Vrmate = reg; /* Link them together */
vr1link(nreg, reg); /* Add new to same list after old */
if (low) {
vr1 = nreg, vr2 = reg;
vr1->Vrloc = reg->Vrloc-1;
} else {
vr1 = reg, vr2 = nreg;
vr2->Vrloc = reg->Vrloc+1;
}
vr1->Vrflags |= VRF_REGPAIR;
vr2->Vrflags |= VRF_REG2ND;
if (reg->Vrflags & VRF_SPILLED) {
/* On stack, don't need to find reg */
nreg->Vrflags |= VRF_SPILLED; /* Pretend new reg also spilled */
nreg->Vroldstk = reg->Vroldstk; /* Preserve this just in case */
return vr1; /* (note reg not really on stk!) */
}
/* Existing reg is in a real reg, see if neighbor is free */
if (regis[nreg->Vrloc] == NULL /* New reg available? */
&& vr1->Vrloc >= R_RETVAL /* And pair is within range of */
&& vr2->Vrloc <= R_MAXREG) { /* useable registers? */
regis[nreg->Vrloc] = nreg; /* Win! Take the new reg */
return vr1; /* and return */
}
/* Can't use neighboring real reg, find another pair of real regs.
** We lock old reg while hunting to avoid unpleasant surprises.
*/
if (reg->Vrflags & VRF_LOCK)
rr = rrdfind();
else {
reg->Vrflags |= VRF_LOCK;
rr = rrdfind();
reg->Vrflags &= ~VRF_LOCK;
}
code00(P_MOVE, (low ? rr+1 : rr), reg->Vrloc); /* Copy old reg */
regis[reg->Vrloc] = NULL; /* Free old real reg, and */
return vrsetrr(vr1, rr); /* set up new ones instead */
}
/* VRLOWIDEN - Widen a single virtual reg in low direction.
** No return value is needed since the vreg pointer doesn't change.
*/
void
vrlowiden(vr)
VREG *vr;
{
(void) vrwiden(vr, 0); /* Widen, existing word becomes 1st */
}
/* VRNARROW - Extract one word of a doubleword register
** The pointer furnished as arg must point to the 1st or 2nd reg of
** the pair, and that one is retained while the other is flushed.
*/
void
vrnarrow(vr)
VREG *vr;
{
if (vr->Vrflags & (VRF_REGPAIR|VRF_REG2ND)) {
/* Flush flags and ensure that vr1free doesn't complain if
** the vreg happens to be spilled at moment.
*/
vr->Vrflags &= ~(VRF_REGPAIR|VRF_REG2ND|VRF_SPILLED);
vr1free(vr->Vrmate); /* Flush other reg */
} else
int_warn("vrnarrow: already narrow");
}
/* VRREAL - return real (physical) register # for an active virtual reg.
** If reg is not active (is on stack) then generates internal error,
** but tries to recover by getting it anyway.
*/
int
vrreal(vr)
VREG *vr;
{
if (!(vr->Vrflags & VRF_SPILLED))
return vr->Vrloc;
int_error("vrreal: using spilled reg");
return vrtoreal(vr);
}
/* VRTOREAL - Return a physical register number for some virtual register
** Pulls it back off the stack if necessary
*/
int
vrtoreal(reg)
VREG *reg;
{
if ((int)reg < NREGS) { /* Catch any obsolete uses */
int_error("vrtoreal: bad vr %o", reg);
return (int)reg;
}
/* Check for spilled register now somewhere on stack */
if (reg->Vrflags & VRF_SPILLED) {
int stkloc;
/* Put it back into a register */
reg->Vrflags &= ~ VRF_SPILLED;
vrunlink(reg); /* Unlink from spill list */
stkloc = reg->Vrloc; /* Note location on stack */
if (reg->Vrflags & VRF_REGPAIR) {
reg->Vrmate->Vrflags &= ~VRF_SPILLED;
vrsetrr(reg, rrdfind());
code12(P_DMOVE, reg, stkloc - stackoffset);
} else {
vr1setrr(reg, rrfind());
code12(P_MOVE, reg, stkloc - stackoffset);
}
/* drop stack to top remaining spilled reg */
if (reg->Vrnext == spillist.Vrnext) {
code8(P_ADJSP, VR_SP, spillist.Vrnext->Vroldstk - stackoffset);
stackoffset = spillist.Vrnext->Vroldstk;
}
vrlink(reg, ®list);
}
/* In a reg now no matter what, so just return it */
return reg->Vrloc;
}
/* VRSTOREAL - Same as vrtoreal but for two registers at once.
** This ensures that we don't mistakenly spill one of the regs that
** a two-reg instruction needs.
*/
int
vrstoreal(vr, vr2)
VREG *vr, *vr2;
{
if ((int)vr < NREGS) { /* Catch any obsolete uses */
int_warn("vrstoreal: bad vr %o", vr);
return (int)vr;
}
if (vr->Vrflags & VRF_LOCK) { /* If 1st reg already locked, */
(void) vrtoreal(vr2); /* use fast method that avoids */
return vr->Vrloc; /* unlocking the 1st reg when done. */
}
if (vr->Vrflags & VRF_SPILLED) /* Nope, so ensure 1st reg active */
(void) vrtoreal(vr);
vr->Vrflags |= VRF_LOCK; /* Lock it to that phys reg, */
(void) vrtoreal(vr2); /* while we ensure 2nd active too! */
vr->Vrflags &= ~VRF_LOCK; /* OK, can unlock 1st now */
return vr->Vrloc;
}
/* VRISPAIR - Return true if virtual register is a doubleword pair
*/
int
vrispair(reg)
VREG *reg;
{ return (reg->Vrflags & VRF_REGPAIR) != 0;
}
/* VRUFCREG - Vreg version of ufcreg().
** If changereg() fails to change a reg (S) to the desired # (R), it
** emits a MOVE R,S. Often the code generator later realizes the exact
** # didn't matter and so the MOVE to R can be flushed; this routine does
** exactly that for a virtual reg by updating it to reflect the new
** real reg it's associated with (S) once the MOVE is flushed.
**
** Currently only used by switch case jump generation to avoid
** lossage that would ensue from CCCODE's calls to ufcreg.
*/
void
vrufcreg(vr)
VREG *vr;
{
regis[vrtoreal(vr)] = NULL; /* Swap in, deassign it */
regis[vr->Vrloc = ufcreg(vr->Vrloc)] = vr; /* Maybe flush MOVE; reassign*/
}
/* RFREE - True if real register is NOT assigned to a virtual reg.
** A function is necessary because the outside world can't see regis[]
*/
int
rfree(rr)
{
return regis[rr] == NULL;
}
/* RHASVAL - True if real reg is still assigned and VRF_HASVAL is set
** indicating it contains a needed value.
**
** This isn't actually used by anything yet.
*/
int
rhasval(rr)
{
return (regis[rr] ? regis[rr]->Vrflags&VRF_HASVAL : 0);
}
/* From this point, all routines are internal auxiliaries */
/* RRFIND - Find or create an unused real 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
rrfind()
{
VREG *vr;
int r;
updrfree(); /* update regfree[] to pbuf contents */
for (r = R_MINREG; r <= R_MAXREG; r++) /* try for unused free reg */
if (regfree[r]) return r;
for (r = R_MINREG; r <= R_MAXREG; r++) /* none, try for a free one */
if (regis[r] == NULL) return r;
/* All registers in use, have to decide which one to spill to stack.
** The heuristic for this is to use the "oldest" thing on the register
** list (this is the least recently created -- not necessary the least
** recently used -- register)
** This is where VRF_LOCK has its effect of preventing regs from being
** spilled.
** Also, for time being, don't spill 2nd reg of a pair.
*/
for (vr = reglist.Vrprev; vr != ®list; vr = vr->Vrprev)
if (!(vr->Vrflags & (VRF_LOCK | VRF_REG2ND))) {
r = vr->Vrloc; /* Remember phys reg # */
vrspill(vr); /* Spill this reg to stack! */
return r;
}
int_error("rrfind: no regs");
}
/* UPDRFIND - auxiliary for rrfind() and rrdfind().
** Sees 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 intensively,
** 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 = R_MINREG; r <= R_MAXREG; r++)
regfree[r] = (regis[r] == NULL);
for (p = previous; p != NULL && p->Pop != P_PUSHJ; p = before(p))
regfree[p->Preg] = 0;
}
/* RRDFIND - Find (or create) a real double-register pair, returning the
** # of the first real reg of the pair.
** We have to be careful not to return the very last register.
*/
static int
rrdfind()
{
VREG *vr, *vrok[NREGS];
int i, nvrs, r;
updrfree(); /* update regfree[] to pbuf contents */
for (r = R_MINREG; r < R_MAXREG; r++) /* try for unused free reg */
if (regfree[r] && regfree[r+1]) return r;
for (r = R_MINREG; r < R_MAXREG; r++) /* none, try for a free one */
if (regis[r] == NULL && regis[r+1] == NULL) return r;
/* None free, scan the reglist in the same way as for rrfind. But
** since we need a pair, we must look for the first virtual reg or
** combination thereof that forms a pair.
** Note that for time being, we avoid spilling just the 2nd reg of a pair.
*/
nvrs = 0;
for (vr = reglist.Vrprev; vr != ®list; vr = vr->Vrprev) {
if (vr->Vrflags & (VRF_LOCK | VRF_REG2ND)) /* If locked, */
continue; /* don't consider it */
if (vr->Vrflags & VRF_REGPAIR) {
r = vr->Vrloc; /* Remember phys reg # */
vrspill(vr); /* Spill this reg to stack! */
return r;
}
/* Not a pair, see if forms pair with anything already seen. */
for (i = 0; i < nvrs; ++i)
if ((r = vrok[i]->Vrloc) == vr->Vrloc+1
|| r == vr->Vrloc-1) { /* If combo wins, */
if (r > vr->Vrloc) /* get low phys reg # */
r = vr->Vrloc;
vrspill(vr); /* Spill both to stack */
vrspill(vrok[i]);
return r;
}
/* Nope, add to array and keep looking. Should never have more
** than NREGS active registers, so array bounds ought to be safe.
*/
vrok[nvrs++] = vr;
}
int_error("rrdfind: no regs");
}
/* VRSETRR - Set a virtual register's location(s) to be some real reg(s).
** If the vreg is a pair, both are set.
*/
static VREG *
vrsetrr(vr, rr)
VREG *vr;
{
if (vr->Vrflags & VRF_REGPAIR)
vr1setrr(vr->Vrmate, rr+1);
return vr1setrr(vr, rr);
}
/* VR1SETRR - Set a virtual register's location to be some real reg
*/
static VREG *
vr1setrr(vr, rr)
VREG *vr;
{
return regis[vr->Vrloc = rr] = vr;
}
/* VRUNLINK - Unlink a virtual reg from whatever list it's on.
** If the vreg is a pair, both are unlinked.
*/
static void
vrunlink(vr)
VREG *vr;
{
if (vr->Vrflags & VRF_REGPAIR)
vr1unlink(vr->Vrmate);
vr1unlink(vr);
}
/* VR1UNLINK - Remove a register from whatever list it's on.
** This is the first half of changing from one list to another
*/
static void
vr1unlink(reg)
VREG *reg;
{
if (reg->Vrnext == reg) {
int_warn("vr1unlink: list head");
return;
}
reg->Vrnext->Vrprev = reg->Vrprev;
reg->Vrprev->Vrnext = reg->Vrnext;
}
/* VRLINK - Link a register that may be the 1st of a pair; if so, link
** the 2nd reg as well.
*/
static VREG *
vrlink(reg, list)
VREG *reg, *list;
{
if (reg->Vrflags & VRF_REGPAIR)
vr1link(reg->Vrmate, list); /* Is pair, link 2nd first */
return vr1link(reg, list);
}
/* VR1LINK - Add a register to a list
** Used when a new vreg is created and when moving between lists
*/
static VREG *
vr1link(reg, list)
VREG *reg, *list;
{
reg->Vrnext = list->Vrnext;
list->Vrnext->Vrprev = reg;
reg->Vrprev = list;
return list->Vrnext = reg;
}
/* VRALLOC - Allocate a new virtual register structure
** VRDALLOC - Same, but returns 1st of a double register pair, linked together.
*/
static VREG *
vralloc()
{
extern char *malloc();
VREG *rp;
if (empty(&freelist)) {
rp = (VREG *)malloc(sizeof (VREG));
if (rp == NULL) efatal("Out of memory for virtual registers");
} else {
rp = freelist.Vrnext;
vr1unlink(rp);
}
rp->Vrflags = 0;
return rp;
}
static VREG *
vrdalloc()
{
VREG *vr1 = vralloc();
VREG *vr2 = vralloc();
vr1->Vrflags |= VRF_REGPAIR;
vr2->Vrflags |= VRF_REG2ND;
vr1->Vrmate = vr2;
vr2->Vrmate = vr1;
return vr1;
}