File:  [MW Coherent from dump] / coherent / d / bin / cc / c / n1 / i8086 / gen1.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
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);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.