📄 ccgen2.c
字号:
/* 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 + -