⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ccgen2.c

📁 KCC , a good c compiler, write by Ken Harrenstien
💻 C
📖 第 1 页 / 共 5 页
字号:
/*	CCGEN2.C - Generate code for parse-tree expressions
**
**	(c) Copyright Ken Harrenstien 1989
**		All changes after v.295, 14-May-1989
**	(c) Copyright Ken Harrenstien, SRI International 1985, 1986
**		All changes after v.79, 8-Aug-1985
**
**	Original version (C) 1981  K. Chen
*/
#define NEWTERN 1	/* Try new ternary code */

#include "cc.h"
#include "ccgen.h"
#include <string.h>

/* Imported functions */
extern SYMBOL *newlabel(void);		/* CCSYM */
extern int elembsize(TYPE *);			/* CCSYM */
extern INT sizetype(TYPE *), sizeptobj(TYPE *);
extern void				/* CCCODE */
	codek0(int, VREG *, VREG *), codek4(int, VREG *, VREG *),
	code4s(int, VREG *, VREG *, int, INT),
	code0(int, VREG *, VREG *), code1(int, VREG *, INT),
	codebp(int, int, INT, int, SYMBOL *, INT),
	code3(int, VREG *, SYMBOL *), code4(int, VREG *, VREG *),
	code5(int, VREG *), code6(int, VREG *, SYMBOL *),
	codemdx(int, int, SYMBOL *, INT, int), code8(int, VREG *, INT),
	code9(int, VREG *, double, int),
	code10(int, VREG *, SYMBOL *, INT, INT), code13(int, VREG *, INT),
	codestr(char *, int), codlabel(SYMBOL *), fixprev(void),
	flushcode(void);
extern void code4m(int, VREG *, VREG *, char *), code5m(int, VREG *, char *);
extern void code00(int, int, int), code40(int,int,int,INT); /* Reg linkage */
extern int codcreg(VREG *, VREG *);		/* CCCODE */
extern int sideffp(NODE *);		/* CCEVAL */
extern int vrispair(VREG *);
extern void folddiv(VREG *);
extern int unjump(SYMBOL *);
extern SYMBOL *cregupto(SYMBOL *);	/* CCCREG for gternary() */
extern PCODE *before(PCODE *);
extern VREG *vrdget(void);
extern void vrfree (VREG *);
extern VREG *vrget(void);
extern int vrispair(VREG *);
extern void vrnarrow (VREG *);
extern int vrreal (VREG *);
extern int vrstoreal (VREG *, VREG *);
extern int vrtoreal(VREG *);
extern void vrufcreg(VREG *);
extern void vralspill(void);
extern void vrlowiden (VREG *);
extern VREG *vrretdget(void);
extern VREG *vrretget(void);
extern void vrallspill(void);	/* VERY non-optimal!! */

/* Exported functions */
VREG *genexpr(NODE *);
void genxrelease(NODE *);
void relflush(VREG *reg);		/* Maybe move to CCOPT */
void gboolean(NODE *n, SYMBOL *false, int reverse);
VREG *getmem(VREG *reg, TYPE *t, int byte, int keep),
  *stomem(VREG *reg, VREG *ra, INT siz, int byteptr); /* CCGEN1 auto inits */
VREG *gmuuo(NODE *);

/* Internal functions */
static VREG *gexpr(NODE *),
	*gternary(NODE *);
static void gor(NODE *, SYMBOL *, int),
	gand(NODE *, SYMBOL *, int),
	gboolop(NODE *, int);
static VREG *gassign(NODE *),
	*gbinary(NODE *),
	*garithop(int, VREG *, VREG *, int),
	*gptrop(int, VREG *, VREG *, TYPE *, TYPE *),
	*gptraddend(TYPE *, NODE *),
	*glogical(NODE *),
	*gunary(NODE *),
	*gcast(NODE *),
	*gcastr(int, VREG *, TYPE *, TYPE *, NODE *),
	*gintwiden(VREG *, TYPE *, TYPE *, NODE *),
	*guintwiden(VREG *, int, NODE *),
	*gincdec(NODE *, int, int),
	*gprimary(NODE *),
	*gcall(NODE *);
static void emit_blissargs(NODE *);
static INT sizeargs(NODE *);
static void gfnarg(NODE *);
static VREG *gaddress(NODE *);
static void pitopc(VREG *, int, int, int);
static  int bptrref(NODE *);
static void gasm(NODE *);
#if 0
static VREG *rgetmem(VREG *reg, TYPE *t, int byte, int keep);
#else
static VREG *rgetmem(VREG *reg, TYPE *t, int keep);
#endif
#if 0
static VREG* rstomem(VREG *reg, int ra, INT siz, int byteptr);
static VREG *gexpr(), *gternary();
static void gor(), gand(), gboolop();
static VREG *gassign(), *gbinary(), *garithop(), *gptrop(), *gptraddend(),
       *glogical(), *gunary(), *gcast(), *gcastr(),
       *gintwiden(), *guintwiden(), *gincdec(),  *gprimary(),  *gcall();
static void emit_blissargs();
static INT sizeargs();
static void gfnarg();
static VREG *gaddress();
static void pitopc();
static int bptrref();
static void gasm();
#endif

/* GENEXPR - Main function for expression code generation.
**	Argument is pointer to a parse-tree node expression;
**	Result is pointer to a virtual register.
** NOTE: the result may be NULL if the expression was marked
**	to be discarded, or was cast to (void), or an error
**	was encountered.
*/
VREG *
genexpr(NODE *n)
{
    if (!n)
	return NULL;		/* Check for null exprs here */
    if (n->Nflag & NF_DISCARD)	/* If discarding result, */
	{
	relflush(gexpr(n));		/* flush any resulting register(s) */
	return NULL;
	}
    return gexpr(n);		/* Normal case, generate code & return reg */
}

/* GENXRELEASE - Auxiliary like genexpr but called when we want to make
**	sure that the resulting value is forced to be discarded.
*/
void
genxrelease(NODE *n)
{
    n->Nflag |= NF_DISCARD;	/* Will be discarding this value */
    genexpr(n);
}

/* GEXPR - workhorse routine for genexpr().  Note arg is guaranteed non-null.
*/
static VREG *
gexpr(NODE *n)
{
    switch (tok[n->Nop].tktype)
	{
	case TKTY_ASOP:
	    return gassign(n);
	case TKTY_TERNARY:
	    return gternary(n);
	case TKTY_BINOP:
	    return gbinary(n);
	case TKTY_BOOLOP:
	case TKTY_BOOLUN:
	    return glogical(n);
	case TKTY_UNOP:
	    return gunary(n);
	case TKTY_RWOP:				/* For now, RW's go below */
	case TKTY_PRIMARY:
	    return gprimary(n);
	case TKTY_SEQ:				/* Comma operator */
	    if (n->Nleft)
		genxrelease(n->Nleft);	/* Flush result of left */
	    return genexpr(n->Nright);	/* and return that of right */
	}

    int_error("gexpr: bad op %N", n);
    return NULL;
}

/*
** RELFLUSH - Flush no-longer-wanted register value
**	Mainly called by genexpr(); also called by gcastr() when casting
** a value to (void).
**	Note that the reg argument may be NULL.  This can happen for
** a generated value of type "void" (size 0).
*/
void
relflush (VREG *reg)
{
    int r;			/* get physical register */
    PCODE *p, *before();

    if (reg == NULL)
	return;
    r = vrtoreal(reg);		/* Save physical reg # */
    vrfree(reg);		/* Now release virtual reg or reg pair */

    /* This should be moved to someplace in CCOPT */
    if (optobj)
	for (p = previous; p != NULL; p = before (p))
	    {
	    if (p->Pop == P_ADJSP)
		continue; /* skip back across P_ADJSP */
	    else if (p->Pop != P_MOVE
		     || p->Preg != r
		     || prevskips(p))
		break; /* not flushable */
	    else 
	/* Do NOT discard preserved reg values.
	 * Later, add optimization at end of basic block to do it.
	 */ if (Register_Nopreserve(p->Preg))
		{
		p->Pop = P_NOP;		/* drop pointless NOP */
		fixprev();			/* fix up for drop */
		}
	    }
}

/* GTERNARY - generate code for ternary operators
**	Note: handles case where one or both result expression pointers may be
** NULL.  The overall value had better be "void" if so.
**
** There is some suboptimal code here, namely the call to "vrallspill()" to
** save any registers that are active at the time this operand is executed.
** This is necessary because we don't know at this point whether either the
** true or false path will require saving registers (eg if a function call is
** done), but control still has to merge back to the same place.  If one
** path saves regs and the other doesn't, the stack is going to be confused
** at the point where control merges (ie at end of ternary expression).
** The active registers have to be in the same state afterwards as they
** were before, and at the moment the only safe way of doing this is to
** bite the bullet and save them all prior to doing the ternary expression.
**	A similar problem exists for the logical operands; anything that
** branches during expression evaluation.  Conditional statements like "if"
** are not affected because there are never any registers active across a
** statement.
*/

static VREG *
gternary (NODE *n)
{
    SYMBOL *false, *done;
    int siz;
    NODE *nfirst, *nsecond;
    VREG *reg;
#if NEWTERN
    int uptof = 0;
    SYMBOL *savupto;
#endif

    /* find the pieces of code we're going to use */
    siz = sizetype(n->Ntype);		/* Find size of overall result */
    if (n->Nflag & NF_DISCARD)		/* If result being discarded, */
	siz = 0;			/* pretend size is 0 (void) */
    nfirst = n->Nright->Nleft;		/* First (if-true) result expr */
    nsecond = n->Nright->Nright;	/* Second (if-false) result expr */

    /* Check for (very unlikely) case of both results non-existent.
    ** CCEVAL's evaldiscard() should have substituted some other node op, 
    ** so this may be a bug.  We handle this mainly to ensure following
    ** code is guaranteed of having at least one result expr.
    */
    if (!nfirst && !nsecond)		/* Check for unlikely case */
	{
	if (siz != 0)			/* Overall result must be void! */
	    int_error("gternary: no operands %N", n);
	genxrelease(n->Nleft);		/* Generate condition */
	return NULL;
	}
    if (n->Ntype->Tspec == TS_ARRAY)	/* Another just-in-case check */
	{
	int_error("gternary: array type %N", n);
	siz = 0;
	}

#if 1
    /* Clean up previously allocated registers */
    if (siz == 2)
	vrfree(vrretdget());		/* Make sure ACs 1 & 2 free */
    else if (siz >= 1)
	vrfree(vrretget());	/* else just ensure AC1 free */
    /* Else void return value */
#endif

    false = (nfirst && nsecond)			/* If we'll need it, */
		 ? newlabel() : NULL;		/* get a new label for false */
    done = (n->Nendlab ?			/* Get overall end label */
		n->Nendlab : newlabel());
    if (nfirst)
	nfirst->Nendlab = done;		/* Make that be expr endlab */
    if (nsecond)
	nsecond->Nendlab = done;
    if (!false)				/* If don't have both exprs */
	false = done;			/* then use endlab as false jump */

    /* Now just before generating the code to test a condition and branch,
    ** we have to ensure that any active registers are saved.  See note at
    ** top of page.
    */
    vrallspill();

    /* There are three possible configurations:
    ** (1) Both nfirst and nsecond exist.  Failing test jumps to "false".
    **		Both results must be moved to a common register.
    ** (2) Only nfirst exists.  Failing test jumps to "done".
    **		We return the register nfirst gives, if any.
    ** (3) Only nsecond exists.  Test is REVERSED; if fails, jumps to "done".
    **		We return the register nsecond gives, if any.
    ** We've already set up the "false" label to be the same as "done"
    ** if either of the latter two cases holds.
    */
    reg = NULL;			/* Ensure no return reg initially */
    gboolean(n->Nleft, false,		/* Generate code to test condition */
		nfirst == NULL);	/* (reverse sense if 1st is gone) */

    if (nfirst)
	{
	if (siz > 0)
	    {
#if 0 /*NEWTERN*/
	    reg = genexpr(nfirst);	/* Just return what we get */
	    if (optgen && nsecond		/* If optimizing, and other */
	     && (nsecond->Nop == N_FNCALL	/* val is a function call, */
		|| (nsecond->Nop == N_CAST &&
		    nsecond->Nleft->Nop == N_FNCALL))
	     && vrtoreal(reg) != R_RETVAL)	/* and 1st val in diff reg, */
		{
		code0(siz == 2 ? P_DMOVE : P_MOVE,	/* then put 1st val */
				VR_RETVAL, reg);	/* into this reg! */
		reg = (siz == 2 ? vrdget() : vrget());
		}
	    else if (optgen)		/* One more optimization try */
		backreg(reg);		/* Flush a MOVE R,S as we don't care */
					/* at this point what phys reg is */
#else
	    if (Register_Id(nfirst))
		code00(P_MOVE, R_RETVAL, nfirst->Nid->Sreg);
	    else
		code0(siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nfirst));
#endif
	    }
	else
	    genxrelease(nfirst);
	if (nsecond)
	    {
	    code6(P_JRST, (VREG *)NULL, done);	/* skip over the hard way */
	    codlabel(false);			/* now start second part */
	    }
	}

    if (nsecond)
	{
	if (siz > 0)
	    {
#if NEWTERN
	    if (nfirst)
		{
		savupto = cregupto(done);	/* Set fence for changereg */
		uptof++;			/* say fence set */
		}
	    if (Register_Id(nsecond))
		code00(P_MOVE, R_RETVAL, nsecond->Nid->Sreg);
	    else
		code0 (siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nsecond));
#else
	    code0 (siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nsecond));
#endif
	    }
	else
	    genxrelease(nsecond);
	}

    if (n->Nendlab == NULL)
	{
	codlabel(done);		/* second clause done here */
#if NEWTERN
	if (uptof)
	    {
	    cregupto(savupto);	/* Restore saved fence value */
	    uptof = 0;
	    }
#if 1
	/* If only one result register was used, try to make it a "normal"
	** (non-return) reg to avoid hogging reg 1 and interfering with
	** common sub-expression matching.
	*/
	if (siz > 0 && siz != 2		/* Only one register? */
		 && optobj)
	    {
	    reg = vrget();		/* Get normal reg */
	    reg->Vrtype = n->Ntype;	/* Set C type of result */
	    if (codcreg(reg, VR_RETVAL))
		return reg;
	    vrfree(reg);		/* didn't work, put back that reg. */
	    }
#endif
#endif
	}

#if NEWTERN
    if (uptof)
	cregupto(savupto);	/* Restore saved fence if one */
#if 0
    if (reg)
#else
    if (siz <= 0)
	return NULL;		/* Void */
    reg = (siz == 2 ? vrretdget() : vrretget());	/* One or two return regs */
#endif
#endif
    reg->Vrtype = n->Ntype;		/* Set C type of result obj */
    return reg;
}

/* GBOOLEAN - Generate code for boolean expressions
**	jump to false label if expr not true
**	reverse sense of test if reverse bit set
*/
void
gboolean(NODE *n, SYMBOL *false, int reverse)
{
    VREG *r;
    int op;

    if (n == NULL)		/* Paranoia: bug catcher */
	{
	int_error("gboolean: null arg");
	return;
	}

    /*
    ** 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 Q_NOT:
	    n->Nleft->Nendlab = n->Nendlab;	/* set up variables */
	    n = n->Nleft;			/* with parity switched */
	    reverse = !reverse;		/* for tail recursive call */
	    gboolean(n, false, reverse);	/* to self */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -