Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/kcc/ccgen2.c
There are 8 other files named ccgen2.c in the archive. Click here to see a list.
/* <KCC.CC>CCGEN2.C.79, 31-Jul-85 16:12:42, Edit by KRONJ */
/*  always set endlab for (?:) expressions */
/* <KCC.CC>CCGEN2.C.72, 15-Jul-85 10:25:19, Edit by KRONJ */
/*  flush holdret(), new peepholer works better without it */
/* <KCC.CC>CCGEN2.C.69, 12-Jul-85 15:38:52, Edit by KRONJ */
/*  gternary() no longer does skip optimization */
/*  (too hard to get right - let peepholer handle it). */
/* <KCC.CC>CCGEN2.C.68, 21-Jun-85 11:03:14, Edit by KRONJ */
/*  Make gternary() not break previously allocated registers */
/* <KCC.CC>CCGEN2.C.66,  9-Jun-85 17:25:16, Edit by KRONJ */
/*  Use holdret() and relret() in gternary() */
/* <KCC.CC>CCGEN2.C.55, 29-May-85 10:10:50, Edit by KRONJ */
/*  Flush getlabel() and maklabel() */
/* <KCC.CC>CCGEN2.C.47, 21-May-85 11:46:28, Edit by KRONJ */
/*  Expand int => double coercion inline */
/* <KCC.CC>CCGEN2.C.43, 14-Mar-85 07:37:22, Edit by KRONJ */
/*  Make (char *) <=> (int *) coercions leave null as is */
/* <KCC.CC>CCGEN2.C.38, 13-Mar-85 13:42:23, Edit by KRONJ */
/*  Double precision floating point comparisons */
/* <KCC.CC>CCGEN2.C.37, 12-Mar-85 09:01:45, Edit by KRONJ */
/*  Fix double arithmetic for new register allocation */
/* <KCC.CC>CCGEN2.C.34,  9-Mar-85 11:54:09, Edit by KRONJ */
/*  Clean up coercions for new register allocation */
/* <KCC.CC>CCGEN2.C.33,  8-Mar-85 15:19:06, Edit by KRONJ */
/*  Rework register allocation */
/*  Don't need TLZ in char ptr => int ptr conversion */
/* <KCC.CC>CCGEN2.C.32,  8-Mar-85 11:06:34, Edit by KRONJ */
/*  Move register allocation stuff to a new module */
/* <KCC.CC>CCGEN2.C.31, 26-Jan-85 12:37:20, Edit by KRONJ */
/*  Can't let condition endlabels escape to expression used as conditional. */
/*  This was causing gboolean to compile incorrectly in previous edit, */
/*  change code there to work for previous versions. */
/* <KCC.CC>CCGEN2.C.30, 26-Jan-85 12:02:07, Edit by KRONJ */
/*  more cleanup - gboolean and gboolop */
/* <KCC.CC>CCGEN2.C.29, 26-Jan-85 10:03:08, Edit by KRONJ */
/*  speed up before() */
/* <KCC.CC>CCGEN2.C.28,  2-Jan-85 15:26:31, Edit by KRONJ */
/*  different way to do literal strings */
/* <KCC.CC>CCGEN2.C.27, 29-Dec-84 11:05:49, Edit by KRONJ */
/*  leave optimization of float coercion to peepholer */
/* <KCC.ATBAT>CCGEN2.C.3, 28-Nov-84 21:24:00, Edit by DAGONE */
/*  Fix integer divide, used 16, trashes 17. == instead of = in IF () */
/* SCORE:<KCC.CC>CCGEN2.C.9, 29-Jul-84 22:31:02, Edit by KRONJ */
/*  clean up IDIV generation */
/* SCORE:<KCC.CC>CCGEN2.C.8, 29-Jul-84 12:06:03, Edit by KRONJ */
/*  gternary() knows to put result in register if called by greturn() */
/* SCORE:<KCC.CC>CCGEN2.C.7, 27-Jul-84 23:36:39, Edit by KRONJ */
/*  move out cell nextreg */
/* SCORE:<KCC.CC>CC83.C.70, 10-Jul-84 17:05:28, Edit by KRONJ */
/*  try harder not to invert a skip with a TRNA */
/* SCORE:<KCC.CC>CC83.C.68, 10-Jul-84 15:59:29, Edit by KRONJ */
/*  lost precedence race between = and ==, parenthesize */
/* SCORE:<KCC.CC>CC83.C.65,  2-Jul-84 12:25:02, Edit by KRONJ */
/*  eliminate changereg() at end of true case of ?: expr */
/* SCORE:<KCC.CC>CC83.C.32, 25-May-84 23:29:10, Edit by KRONJ */
/*  flush silly instances of flushcode() */
/* SCORE:<KCC.CC>CC83.C.8,  8-May-84 16:12:02, Edit by KRONJ */
/*  clean up QUERY generation so as not to confuse optimizer */
/* SCORE:<KCC.CC>CC83.C.5,  6-May-84 15:20:31, Edit by KRONJ */
/*  SETO */
/* SCORE:<KCC.CC>CC83.C.2,  5-May-84 22:36:03, Edit by KRONJ */
/*  clean up logical and expansion */

/* cc83.c -- Code generator TOPS-20 (contd)   (C) 1981  K. Chen */

#include "cc.h"
#include "ccgen.h"

struct vreg *genstmt();

/* --------------------------------------------- */
/*	generate code for ternary operators      */
/* --------------------------------------------- */

struct vreg *
gternary (n)
struct NODE *n;
{
    struct SYMBOL *false, *done;
    int siz, spos;
    node nfirst, nsecond;
    struct vreg *reg;

    /* find the pieces of code we're going to use */
    nfirst = n->right->left;
    nsecond = n->right->right;
    siz = (n->ntype->ttype == ARRAY? PTRSIZE : tsize (n->ntype));
    spos = stackoffset;

    /* clean up previously allocated registers */
    if (siz == 1) release (getret());	/* make sure AC1 free */
    else if (siz == 2) release (getrpair()); /* same for AC2 */
    else spill();			/* stacking things, flush them all */

    false = newlabel (1);
    nfirst->endlab = nsecond->endlab = done =
	(n->endlab != NULL? n->endlab : newlabel (1));
    gboolean(n->left, false, 0);

    if (siz > 2) {
	(void) genstmt (nfirst);	/* push struct */
	stackoffset = spos;		/* fix up after stack funnies */
    } else code0 (siz == 1? MOVE : DMOVE, RETVAL, genstmt (nfirst));
    code6 (JRST, 0, done);		/* skip over the hard way */

    emitlabel (false);			/* now start second part */
    if (siz > 2) (void) genstmt (nsecond); /* stack other struct */
    else code0 (siz == 2? DMOVE : MOVE, RETVAL, genstmt (nsecond));

    if (n->endlab == NULL) {
	emitlabel (done);		/* second clause done here */
	if (siz == 1 && optimize) {	/* if only one register */
	    reg = getreg();		/* make normal to avoid cse clash */
	    if (changereg (realreg (reg), RETVAL, previous)) return reg;
	    release (reg);		/* didn't work, put it back */
	}
    }
    if (siz == 1) return getret();	/* one return register */
    else if (siz == 2) return getrpair(); /* two */
    else return NULL;			/* stacked, no registers */
}
/* ----------------------------------------- */
/*	jump to false label if not true      */
/*	reverse sense if reverse bit set     */
/* ----------------------------------------- */

gboolean(n, false, reverse)
struct NODE *n;
struct SYMBOL *false;
{
    struct vreg *r;
    int op;

    /*
    ** The big switch.  Either we call some handler routine such as
    ** gor or gand, or we make a skip and then a jump.  If the former,
    ** we are done, and the call should tail recurse.  Otherwise, we
    ** need to add the jump to the given label, so we break from the switch.
    */

    switch (n->nop) {
    case NOT:
	n->left->endlab = n->endlab;	/* set up variables */
	n = n->left;			/* with parity switched */
	reverse = !reverse;		/* for tail recursive call */
	return gboolean(n, false, reverse); /* to self */

    case LAND:
	if (reverse) return gor(n, false, reverse); /* more tail recursion */
	else return gand(n, false, reverse);

    case LOR:
	if (reverse) return gand(n, false, reverse); /* still more */
	else return gor(n, false, reverse);

    case NEQ:
    case LEQ:
    case GEQ:
    case LESS:
    case GREAT:
    case EQUAL:
	gboolop(n, reverse);		/* comparison, make skip */
	break;				/* followed by GOTO */

    case ICONST:
	op = n->niconst;		/* unconditional condition */
	if (reverse && op) break;	/* jump when true and true? */
	if (!reverse && !op) break;	/* jump when false and false? */
	return;

    default:
	n->endlab = NULL;		/* cond endlab is not expr endlab */
	r = genstmt(n);			/* get expression into reg */
	code6 (reverse? JUMP+SKPN : JUMP+SKPE, r, false); /* test and jump */
	release (r);			/* now done with register */
	return;				/* don't make spurious JRST */
    }
    code6(JRST, 0, false);		/* broke out, want a GOTO */
}

/* ---------------------------- */
/*	code for && and ||	*/
/* ---------------------------- */

gor(n, false, reverse)
struct NODE *n;
struct SYMBOL *false;
{
    struct SYMBOL *lab;

    if ((lab = n->endlab) == 0) lab = newlabel (1); /* get label */
    gboolean(n->left, lab, !reverse);	/* output first clause */
    n->right->endlab = lab;		/* no more labels in second clause */
    gboolean(n->right, false, reverse);
    if (n->endlab == 0) emitlabel (lab); /* send out made label */
}

gand(n, false, reverse)
struct NODE *n;
{
    n->right->endlab = n->endlab;
    gboolean(n->left, false, reverse);
    gboolean(n->right, false, reverse);
}

/* ---------------------------------- */
/*	code for == > < <= >= !=      */
/* ---------------------------------- */

gboolop(n, reverse)
struct NODE *n;
{
    int op;
    struct vreg *r1, *r2;
    struct PSEUDO *p;

    /*
    ** Generate operands and skip instruction for the test
    **
    ** Note that floating point can use the same comparison
    ** instructions as integers, so we don't have to test for them.
    */

    switch (n->nop) {
    case EQUAL: op = CAM+ISSKIP+SKPE; break;
    case NEQ: op = CAM+ISSKIP+SKPN; break;
    case LEQ: op = CAM+ISSKIP+SKPLE; break;
    case GEQ: op = CAM+ISSKIP+SKPGE; break;
    case LESS: op = CAM+ISSKIP+SKPL; break;
    case GREAT: op = CAM+ISSKIP+SKPG; break;
    }

    r1 = genstmt (n->left);		/* calculate values to compare */
    r2 = genstmt (n->right);
    if (reverse) op = revop (op);	/* maybe invert test */

    /*
    ** Generate and optimize the test.
    **
    ** If we are comparing double precision floating point we need
    ** to look at both pairs of words, so we use a cascaded pair or
    ** trio of comparisons.
    */

    if (n->left->ntype->ttype == DOUBLE) {
	switch (op) {
	case CAM+ISSKIP+SKPL:
	    flushcode();		/* don't confuse peepholer */
	    code0 (CAM+ISSKIP+SKPL, realreg (r1), realreg (r2));
	    code0 (CAM+ISSKIP+SKPGE, realreg (r1) + 1, realreg (r2) + 1);
	    op = CAM+ISSKIP+SKPLE;
	    break;
	case CAM+ISSKIP+SKPLE:
	    flushcode();		/* don't confuse peepholer */
	    code0 (CAM+ISSKIP+SKPL, realreg (r1), realreg (r2));
	    code0 (CAM+ISSKIP+SKPG, realreg (r1) + 1, realreg (r2) + 1);
	    break;
	case CAM+ISSKIP+SKPG:
	    flushcode();		/* don't confuse peepholer */
	    code0 (CAM+ISSKIP+SKPG, realreg (r1), realreg (r2));
	    code0 (CAM+ISSKIP+SKPLE, realreg (r1) + 1, realreg (r2) + 1);
	    op = CAM+ISSKIP+SKPGE;
	    break;
	case CAM+ISSKIP+SKPGE:
	    flushcode();		/* don't confuse peepholer */
	    code0 (CAM+ISSKIP+SKPG, realreg (r1), realreg (r2));
	    code0 (CAM+ISSKIP+SKPL, realreg (r1) + 1, realreg (r2) + 1);
	    break;
	case CAM+ISSKIP+SKPE:
	    code0 (CAM+ISSKIP+SKPN, realreg (r1) + 1, realreg (r2) + 1);
	    break;
	case NEQ:
	    code0 (CAM+ISSKIP+SKPN, realreg (r1) + 1, realreg (r2) + 1);
	    code0 (CAM+ISSKIP+SKPE, r1, r2);
	    code5 (TRN+ISSKIP+SKPA, 0);
	    release (r1);
	    return;
	}
    }
    code0 (op, r1, r2);			/* generate and optimize test */
    release (r1);
}
/* ------------------------------------------------------ */
/*	return immediate version of boolean operator      */
/* ------------------------------------------------------ */

immedop(op)
{
    switch (op & OPCODE) {
    case CAM: return op ^ (CAM ^ CAI);
    case TDN: return op ^ (TDN ^ TRN);
    case TDO: return op ^ (TDO ^ TRO);
    case TDC: return op ^ (TDC ^ TRC);
    case TDZ: return op ^ (TDZ ^ TRZ);
    default: return 0;
    }
}

directop(op)
{
    switch (op & OPCODE) {
    case CAI: return op ^ (CAM ^ CAI);
    case TRN: return op ^ (TDN ^ TRN);
    case TRO: return op ^ (TDO ^ TRO);
    case TRC: return op ^ (TDC ^ TRC);
    case TRZ: return op ^ (TDZ ^ TRZ);
    default: return 0;
    }
}
/*
** Generate code for coercion
*/

struct vreg *
coerce(n)
struct NODE *n;
{
    struct vreg *r, *s;

    switch (n->nc) {
    case NOCOER:
	return genstmt(n->left);

    case PI_PC:
	r = genstmt(n->left);
	pitopc (r, 0, 0);
	return r;

    case PC_PI:
	if (n->nflag & RETEXPR) {
	    r = genstmt(n->left);
	    s = getret();
	} else r = s = genstmt(n->left);
	code0 (SKIP+ISSKIP+SKPGE, s, r); /* is it a OWGBP? */
	code8 (TLZ+ISSKIP+SKPA, s, 0770000); /* yes, just clear P/S field */
	setskip (previous);		/* that was skipped over */
	code1 (AND, s, 0777777);	/* local or null, clear left half */
	setskip (previous);		/* also skipped over */
	return s;

    case PC_PC:
	r = genstmt (n->left);
	code0 (BPCNT, r, r);
	return r;

    case INT_FL:
	r = genstmt (n->left);
	code0 (FLTR, r, r);
	return r;

    case INT_DF:
	r = genstmt (n->left);
	widen (r);			/* make into register pair */
	code5 (SETZ, realreg (r) + 1);	/* zero the next register */
	code8 (ASHC, r, -8);		/* shift out mantissa */
	code8 (TLC, r, 0243000);	/* put exponent in place */
	code5 (DFLOT, r);		/* normalize (by adding zero) */
	return r;

    case FL_DF:
	r = genstmt (n->left);
	widen (r);
	code5 (SETZ, realreg (r) + 1);
	return r;

    case FL_INT:
	r = genstmt (n->left);
	code0 (FIXR, r, r);
	return r;

    case DF_INT:
	r = genstmt (n->left);
	code0 (DFIX, r, r);
	narrow (r, 0);
	return r;

    case DF_FL:
	r = genstmt (n->left);		/* no coercion needed, just */
	narrow (r, 0);			/* forget about the second word */
	return r;

    default:
	error (EUNKCOE, n->nc);
	return 0;
    }
}
/* -------------------------------- */
/*      construct byte pointer      */
/* -------------------------------- */

pitopc (r, bnum, safe)
{
    /*
    ** Turn a word pointer into a byte pointer.  So that our programs
    ** should run in extended addressing as well as in section 0, we
    ** must be able to create either local or global byte pointers,
    ** so we add in our P/S fields from a table instead of literally.
    **
    ** Even if we know that the pointer will point to the same section
    ** that the code is in, we cannot use a local byte pointer, because
    ** pointers are local to where they are stored rather than where
    ** the PC that they are used from is.
    **
    ** If the pointer is merely going to be used to load or deposit
    ** a byte, it will get turned into a local byte pointer later.
    */

    if (!safe) {			/* unless we already know not NULL */
	code0 (SKIP+ISSKIP+SKPE, r, r); /* make (char *)(int *)0 == 0 */
	if (!isskip (previous->pop)) safe = 1; /* maybe XMOVEI */
    }
    code7 (IOR, r, &bytsym, bnum, 0);	/* otherwise turn it into a pointer */
    bytsym.svalue++;			/* count usage of $BYTE */
    if (!safe) setskip (previous);	/* careful with skipped-over OR */
}
/* ------------------------- */
/*	binary operator      */
/* ------------------------- */

struct vreg *
gbinop(n)
struct NODE *n;
{
    struct vreg *r0, *r1, *r2;

    /*
    ** First, check for byte-pointer arithmetic.  Supported operations are:
    **	    ptr - ptr
    **	    ptr - num
    **	    ptr + num
    **	    num + ptr
    */

    if (charpointer(n->left->ntype)) {
	if (charpointer(n->right->ntype)) {
	    if (n->nop == MINUS) {
		r1 = genstmt(n->left);
		r2 = genstmt(n->right);
		code0(SUBBP, r1, r2);	/* ptr - ptr */
		return r1;
	    }
	    emsg(EPTRPTR, n->nop);
	}

	/* handle addition or subtraction of zero */
	if (n->right->nop == ICONST && !n->right->niconst && optimize)
	    return genstmt(n->left);

	r2 = genstmt(n->right);		/* make the increment first */
	if (n->nop == MINUS) {
	    code0(MOVN, r2, r2);		/* ptr - num */
	    n->nop = PLUS;
	}
	r1 = genstmt(n->left);		/* now make the pointer */
	if (n->nop == PLUS) {
	    code0(ADJBP, r2, r1);	/* ptr + num */
	    return r2;
	}
	emsg(EUNKPTR, n->nop);
    } else if (charpointer(n->right->ntype)) {
	if (n->nop != PLUS) emsg(EUNKPTR, n->nop);

	/* handle addition or subtraction of zero */
	if (n->left->nop == ICONST && !n->left->niconst && optimize)
	    return genstmt(n->right);

	r1 = genstmt(n->left);		/* make increment first */
	r2 = genstmt(n->right);		/* and then the pointer */
	code0(ADJBP, r1, r2);		/* num + ptr */
	return r1;
    }

    /*
    ** Not byte pointer, now do more normal ops
    ** Floating point is fairly easy...
    */

    switch (n->ntype->ttype) {
    case FLOAT:
	r1 = genstmt(n->left);		/* get operand */
	r2 = genstmt(n->right);		/* and operator */
	switch (n->nop) {
	case PLUS:
	    code0(FADR, r1, r2);
	    break;
	case MINUS:
	    code0(FSBR, r1, r2);
	    break;
	case MPLY:
	    code0(FMPR, r1, r2);
	    break;
	case DIV:
	    code0(FDVR, r1, r2);
	    break;
	default:
	    emsg(EFLOAT, n->nop);
	}
	return r1;

    case DOUBLE:
	r1 = genstmt(n->left);		/* get operand */
	r2 = genstmt(n->right);		/* and operator */
	switch (n->nop) {
	case PLUS:
	    code0(DFAD, r1, r2);
	    break;
	case MINUS:
	    code0(DFSB, r1, r2);
	    break;
	case MPLY:
	    code0(DFMP, r1, r2);
	    break;
	case DIV:
	    code0(DFDV, r1, r2);
	    break;
	default:
	    emsg(EFLOAT, n->nop);
	}
	return r1;
    }

    /*
    ** If we got to here, we are doing integer arithmetic.
    ** Division is done differently from other operations because the
    ** IDIV instruction produces a doubleword result.
    **
    ** Note that we can't do the apparent optimization of using ASH or AND
    ** when the divisor is a constant power of two, because they perform
    ** inconsistently with IDIV on negative numbers.
    */

    switch (n->nop) {
    case DIV:
    case MOD:
	/* calculate two parts of equation */
	r0 = genstmt(n->left);		/* get the dividend */
	widen (r0);			/* make room for division */
	code0 (IDIV, r0, genstmt (n->right)); /* divide */
	narrow (r0, n->nop == MOD);	/* back down to one register */
	folddiv (r0);			/* do cse on result */
	return r0;			/* return quotient or remainder */
    }

    /*
    ** If we made it all the way down to here we have a normal singleword
    ** integer operation.  If we see a function call coming up we evaluate
    ** it first to avoid saving registers.
    */

    if (n->right->nop == FNCALL) {
	r2 = genstmt(n->right);
	r1 = genstmt(n->left);
    } else {
	r1 = genstmt(n->left);
	r2 = genstmt(n->right);
    }
    switch(n->nop) {
    case RSHFT:
	code0 (MOVN, r2, r2);		/* negate arg to make left shift */
    case LSHFT:
	code4 (LSH, r1, r2);		/* this takes arg as if RCONST */
	break;

    case PLUS:
	code0 (ADD, r1, r2);
	break;

    case MINUS:
	code0 (SUB, r1, r2);
	break;

    case MPLY:
	code0 (IMUL, r1, r2);
	break;

    case OR:
	code0 (IOR, r1, r2);
	break;

    case ANDT:
	code0 (AND, r1, r2);
	break;

    case XORT:
	code0 (XOR, r1, r2);
	break;
    }
    return r1;
}

/* --------------------------------------------------- */
/*	return location of one instruction before      */
/* --------------------------------------------------- */

struct PSEUDO *before(p)
struct PSEUDO *p;
{
    struct PSEUDO *b = &codes[mincode&(MAXCODE-1)];

    if (p == NULL) return NULL;		/* make sure we have a real pseudo */
    while (1) {
	if (p == b) return NULL;	/* start of buffer, can't back up */
	--p;				/* back before here */
	if (p < &codes[0]) p = &codes[MAXCODE-1]; /* wrap in circular buffer */
	if (p->pop != NOP) return p;	/* got a real op, return with it */
    }
}

struct PSEUDO *after(p)
struct PSEUDO *p;
{
    struct PSEUDO *b = &codes[maxcode&(MAXCODE-1)];

    if (p == NULL) return NULL;		/* make sure we have a real pseudo */
    while (1) {
	if (++p > &codes[MAXCODE-1]) p = &codes[0]; /* wrap */
	if (p == b) return NULL;	/* end of buffer, no more code */
	if (p->pop != NOP) return p;	/* got a real op, return with it */
    }
}
/* ---------------------------------------- */
/*	swap two pseudo code locations      */
/* ---------------------------------------- */

swappseudo(a,b)
struct PSEUDO *a, *b;
{
    struct PSEUDO temp;

    copypseudo (&temp, b);
    copypseudo (b, a);
    copypseudo (a, &temp);
}
/* ------------------------- */
/*	copy pseudocode      */
/* ------------------------- */

copypseudo(a,b)
struct PSEUDO *a, *b;
{
    a->ptype = b->ptype;
    a->pop = b->pop;
    a->preg = b->preg;
    a->pptr = b->pptr;
    a->poffset = b->poffset;
    a->pindex = b->pindex;
}
/* ---------------------------------------- */
/*	generate code for function end      */
/* ---------------------------------------- */

gend()
{
    int l;

    flushcode();			/* flush out peephole buffer */
    while (litstrings != NULL) {	/* output literal strings */
	emitgoto (litstrings->nsclab);	/* generated label, always emitted */
	freelabel (litstrings->nsclab);	/* and then freed */
	tab();				/* spaced out */
	asciz(litstrings->nsconst, litstrings->nsclen);	/* from str itself */
	nl();				/* with final newline */
	litstrings = litstrings->nscnext; /* chain through list */
    }
}