|
|
coherent
/*
* The routines in this file print addresses, generate function
* prologue and epilogue sequences,
* compile switches and perform other non-tree-like functions.
* Both SMALL and LARGE model iAPX-86.
*/
#ifdef vax
#include "INC$LIB:cc1.h"
#else
#include "cc1.h"
#endif
/*
* These machine dependent variables hold values
* that are used by the machine dependent parts
* of register and/or temporary storage allocation.
* They are set up by routines in this file.
*/
int maxauto; /* Max autos in this function */
int maxtemp; /* Max temps in this function */
int curtemp; /* Current temp */
PREGSET regbusy; /* Busy flags */
/*
* This table, indexed by register code,
* yields the correct addressing mode for the
* register. This is either stashed in an AFIELD or
* written out to the intermediate file.
*/
static short ramode[] = {
A_RAX, A_RDX, A_RBX, A_RCX,
A_RSI, A_RDI, A_RSP, A_RBP,
0,
A_RES, A_RCS, A_RSS, A_RDS,
0, 0,
0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
A_RAL, A_RBL, A_RCL, A_RDL,
A_RAH, A_RBH, A_RCH, A_RDH
};
/*
* Machine-dependent coder initialization.
* The i8086 version zeros patcache[] entries which are
* inconsistent with specified machine-dependent variants.
* This lets the coder decide when the compiler is executed
* whether to use certain code tables entries.
* Code tables using 80186 instructions include P80186 pattern flag.
* Code tables using 80287 instructions include P80287 pattern flag.
* Code tables using 8087 instructions should include P8087 pattern flag,
* but this flag is currently unimplemented in the tables;
* the tables are instead conditionalized for NDP or no NDP when tabgen'ed.
*/
coderinit()
{
extern int patcsize;
register int i;
register PATFLAG *pfp;
register PATFLAG pflag;
for (pfp=patcache, i=0; i < patcsize; pfp++, i++) {
if (((pflag = *pfp) & MDPFLAGS) != 0)
*pfp = ((notvariant(V80287) && ((pflag&P80287)!=0))
|| (notvariant(V80186) && ((pflag&P80186)!=0)))
? 0 : (pflag & ~MDPFLAGS);
}
}
/*
* Function prolog.
* Clear out max. values of autos
* and temps.
*/
doprolog()
{
blkflab = 0;
maxauto = 0;
maxtemp = 0;
}
/*
* This routine gets called just before
* the EPILOG item is put out. It puts out a
* single AUTOS item; this item tells CC2 how much
* auto space should be reserved.
* The second ival_t of the AUTOS record (the register
* mask) is not used by CC2.
*/
doepilog()
{
bput(AUTOS);
iput(maxtemp);
iput(0);
}
/*
* Read in and treasure up a new
* automatic (and register) variable allocation
* item. The CC1 phase will toss out a single AUTOS
* item, just before the EPILOG, to tell CC2 how many
* bytes of automatic storage should be reserved.
* CC0 tosses one of these for each auto or register bound so that
* allocated space is not clobbered by temps during auto initialization.
*/
doautos()
{
maxauto = iget();
regbusy = iget();
}
/*
* Unconditional jump.
*/
genubr(n)
{
genl(ZJMP, n);
}
/*
* Conditional jump.
*/
gencbr(c, n)
{
genl(optab[c-MIOBASE][0], n);
}
/*
* Generate code for switches.
* Look for special cases, etc. and
* generate the best type of switch
* logic.
* The switch value is in AX.
*/
genswitch(def, n)
{
register ival_t l, r, u;
register int i, lab0, lab1;
register int adjust;
register char *opp;
/*
* If "n" is small pretend the
* user said:
* if (ax == case0)
* goto caselabel0;
* if (ax == case1)
* goto caselabel1;
* ...
*/
if (n < 3) {
for (i=0; i<n; ++i) {
if ((l = cases[i].c_val) == 0)
genrr(ZOR, A_RAX, A_RAX);
else
genri(ZCMP, A_RAX, l);
gencbr(EQ, cases[i].c_lab);
}
genubr(def);
return;
}
/*
* Try for a direct jump table
* if it seems reasonable to do so.
*/
l = cases[0].c_val;
u = cases[n-1].c_val;
r = u-l;
if (r>0 && r<=3*n) {
if ((adjust=l) != 0) {
opp = &optab[SUB-MIOBASE][0];
if (adjust < 0) {
opp = &optab[ADD-MIOBASE][0];
adjust = -adjust;
}
if (adjust == 1)
genr(opp[1], A_RAX);
else
genri(opp[0], A_RAX, adjust);
}
genri(ZCMP, A_RAX, r);
gencbr(UGT, def);
genri(ZSAL, A_RAX, 1);
genrr(ZMOV, A_RBX, A_RAX);
lab0 = newlab();
genone(ZIJMP, A_LID|A_CS|A_XBX, lab0);
genlab(lab0);
for (i=0; l<=u; ++l) {
lab0 = def;
if (l == cases[i].c_val)
lab0 = cases[i++].c_lab;
genl(ZLPTR, lab0);
}
return;
}
/*
* Table search.
*/
lab0 = newlab();
gentwo(ZMOV, A_RBX, A_OFFS|A_LID|A_IMM, -2, lab0);
genri(ZMOV, A_RCX, n);
lab1 = newlab();
genlab(lab1);
genr(ZINC, A_RBX);
genr(ZINC, A_RBX);
gentwo(ZCMP, A_RAX, A_CS|A_XBX);
genl(ZLOOPNE, lab1);
gencbr(NE, def);
genone(ZIJMP, A_OFFS|A_CS|A_XBX, 2*n);
genlab(lab0);
for (i=0; i<n; ++i)
genone(ZWORD, A_OFFS|A_DIR, cases[i].c_val);
for (i=0; i<n; ++i)
genl(ZLPTR, cases[i].c_lab);
}
/*
* Output an address.
* "tp" is a pointer to a TREE.
* The "nsef" flag is true if no side effects are desired;
* it can be set from the code tables
* and is used to supress escape bytes on "LEA" instructions.
* The "pfx" array holds "npfx" address prefix bytes.
* There is some strangeness here.
* In memory a is LO and a+2 is HI;
* this is not the same for constants.
*/
genadr(tp, nsef, npfx, pfx)
register TREE *tp;
unsigned char pfx[];
{
register int op;
register int bias;
register int memf;
register int byte;
register int reg;
register ival_t ival;
int mode;
int offs;
lval_t loffs;
int lidn;
SYM *gidp;
static char basebias[] = {
0, 0, /* S8, U8, */
1, 1, /* S16, U16, */
2, 2, /* S32, U32, */
2, 4, /* F32, F64, */
0, /* BLK, */
0, 1, /* FLD8, FLD16, */
2, 2, /* LPTR, LPTRB, */
1, 1 /* SPTR, SPTRB */
};
while ((op=tp->t_op) == LEAF)
tp = tp->t_lp;
/*
* The "HI" and "LO" options,
* when applied to a register node, must
* arrange to call the "hihalf" and
* "lohalf" macros.
*/
if (op==REG && (reg=tp->t_reg)!=FPAC) {
while (npfx--) {
if (pfx[npfx] == M_LO)
reg = lohalf(reg);
else
reg = hihalf(reg);
}
iput(ramode[reg]);
return;
}
/*
* For constants and memory locations,
* the "HI" and "LO" macros dial the
* selected byte or word out of the operand.
* Watch out for the "_fpac_" register,
* which is actually 64 bits of memory.
*/
offs = 0;
if (npfx) {
memf = 0;
if (op!=ICON && op!=LCON && op!=DCON)
++memf;
if (op == REG)
bias = 4;
else
bias = basebias[tp->t_type];
while (npfx--) {
byte = pfx[npfx];
if (memf && bias == 2) {
if (byte == M_HI)
offs += 2;
} else if (byte == M_LO)
offs += bias;
bias >>= 1;
}
}
/*
* This "REG" is the floating point
* pseudo register, which is actually a memory
* array, 4 words long, called "_fpac_".
*/
if (op == REG) {
iput(A_OFFS|A_GID|A_DIR);
iput(offs);
nput("_fpac");
return;
}
/*
* Constant nodes are used as immediate
* operands of instructions. Pull the appropriate
* 16 bit chunk, and write it out as an immediate
* operand.
*/
if (op == DCON) {
ival = tp->t_dval[7-offs] & 0377;
ival |= tp->t_dval[6-offs] << 8;
iput(A_OFFS|A_IMM);
iput(ival);
return;
}
if (op == LCON) {
ival = lower(tp->t_lval);
if (offs == 0)
ival = upper(tp->t_lval);
iput(A_OFFS|A_IMM);
iput(ival);
return;
}
if (op == ICON) {
ival = tp->t_ival;
iput(A_OFFS|A_IMM);
iput(ival);
return;
}
/*
* Collect address.
* Turn the "f" argument on in the call
* to "gencoll" if this is a "lea", so that it
* won't generate immediate mode addressing
* when it shouldn't.
*/
mode = A_DIR;
loffs = offs;
if (gencoll(tp, &mode, &loffs, &lidn, &gidp, 0, nsef) == 0)
cbotch("collect");
/*
* No prefix byte on an immediate or on
* a "lea" (which is indicated by the "nsef" flag
* being set by the macro in the table.
*/
if ((mode&A_AMOD)==A_IMM || nsef!=0)
mode &= ~A_PREFX;
offs = loffs;
if (offs == 0)
iput(mode);
else {
iput(mode|A_OFFS);
iput(offs);
}
if ((mode&A_LID) != 0)
iput(lidn);
else if ((mode&A_GID) != 0)
sput(gidp->s_id);
}
/*
* Walk down an address tree, building up
* the addressing mode, the offset and the symbol base
* for a general addressing item. Store the data back through
* the argument pointers. The caller must set the initial mode
* to "A_DIR" and the offset to 0.
*/
gencoll(tp, modep, offsp, lidnp, gidpp, s, f)
TREE *tp;
int *modep;
lval_t *offsp;
int *lidnp;
SYM **gidpp;
{
register int op;
register lval_t offs;
register int seg;
while ((op=tp->t_op) == LEAF)
tp = tp->t_lp;
switch (op) {
case ADDR:
if (gencoll(tp->t_lp, modep, offsp, lidnp, gidpp, s, f) == 0)
return (0);
if (f == 0) {
*modep &= ~A_AMOD;
*modep |= A_IMM;
}
break;
case STAR:
if (gencoll(tp->t_lp, modep, offsp, lidnp, gidpp, s, 1) == 0)
return (0);
break;
case ADD:
case SUB:
if (gencoll(tp->t_lp, modep, offsp, lidnp, gidpp, s, f) == 0)
return (0);
if (op == SUB)
s = !s;
if (gencoll(tp->t_rp, modep, offsp, lidnp, gidpp, s, f) == 0)
return (0);
break;
case ICON:
case LCON:
offs = grabnval(tp);
if (s != 0)
offs = -offs;
*offsp += offs;
break;
case LID:
if ((*modep&(A_GID|A_LID))!=0 || s!=0)
return (0);
*modep |= A_LID;
*lidnp = tp->t_label;
goto lidgid;
case GID:
if ((*modep&(A_GID|A_LID))!=0 || s!=0)
return (0);
*modep |= A_GID;
*gidpp = tp->t_sp;
lidgid:
*offsp += tp->t_offs;
/*
* The parser may have told the code generator
* where this symbol is located.
* Apply a CS: escape if the output writer
* will put it in the CODE or module_CODE segment.
* This only affects links and MUL/DIM immediate words.
*/
seg = tp->t_seg;
if (seg==SCODE || seg==SLINK
|| (isvariant(VLARGE)
&& ((seg==SPURE && notvariant(VRAM))
|| (seg==SSTRN && isvariant(VROM))))) {
*modep &= ~A_PREFX;
*modep |= A_CS;
}
break;
case REG:
if ((*modep&A_AMOD)!=A_DIR || s!=0)
return (0);
switch (tp->t_reg) {
case DSBX:
case BX:
*modep = A_XBX;
break;
case ESBX:
*modep = A_ES|A_XBX;
break;
case SSBP:
case BP:
*modep = A_XBP;
break;
case DSSI:
case SI:
*modep = A_XSI;
break;
case ESSI:
*modep = A_ES|A_XSI;
break;
case DSDI:
case DI:
*modep = A_XDI;
break;
case ESDI:
*modep = A_ES|A_XDI;
break;
default:
return (0);
}
break;
default:
return (0);
}
return (1);
}
/*
* Output an instruction that takes
* a single register as an operand.
*/
genr(op, r)
{
bput(CODE);
bput(op);
iput(r);
}
/*
* Output an instruction that takes
* two registers as operands.
*/
genrr(op, r1, r2)
{
bput(CODE);
bput(op);
iput(r1);
iput(r2);
}
/*
* Output an instruction that takes
* a register and an immediate constant value.
*/
genri(op, r, i)
{
bput(CODE);
bput(op);
iput(r);
iput(A_OFFS|A_IMM);
iput(i);
}
/*
* Output an instruction with a single
* local label parameter.
*/
genl(op, l)
{
bput(CODE);
bput(op);
iput(A_LID|A_DIR);
iput(l);
}
/*
* Output an instruction that takes
* a single global identifier as an operand.
*/
geng(op, g)
char *g;
{
bput(CODE);
bput(op);
iput(A_GID|A_DIR);
sput(g);
}
/*
* Output an instruction that takes an immediate constant value.
*/
geni(op, i)
{
bput(CODE);
bput(op);
iput(A_OFFS|A_IMM);
iput(i);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.