1253 lines
26 KiB
C
1253 lines
26 KiB
C
/* $Id: reader.c,v 1.1.1.3 2008/02/10 20:05:06 ragge Exp $ */
|
||
/*
|
||
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
|
||
* All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* 3. The name of the author may not be used to endorse or promote products
|
||
* derived from this software without specific prior written permission
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
*
|
||
* Redistributions of source code and documentation must retain the above
|
||
* copyright notice, this list of conditions and the following disclaimer.
|
||
* Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditionsand the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
* All advertising materials mentioning features or use of this software
|
||
* must display the following acknowledgement:
|
||
* This product includes software developed or owned by Caldera
|
||
* International, Inc.
|
||
* Neither the name of Caldera International, Inc. nor the names of other
|
||
* contributors may be used to endorse or promote products derived from
|
||
* this software without specific prior written permission.
|
||
*
|
||
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
||
* INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
* DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
|
||
* FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT,
|
||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
* POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
* Everything is entered via pass2_compile(). Three functions are
|
||
* allowed to recurse into pass2_compile(), so be careful:
|
||
* - deluseless()
|
||
* - myreader()
|
||
* Especially in myreader note that trees may be rewritten twice if
|
||
* things are not carefully handled.
|
||
*/
|
||
|
||
# include "pass2.h"
|
||
|
||
#include <string.h>
|
||
#include <stdarg.h>
|
||
#include <stdlib.h>
|
||
|
||
/* some storage declarations */
|
||
int nrecur;
|
||
int lflag;
|
||
int x2debug;
|
||
int udebug = 0;
|
||
int thisline;
|
||
int fregs;
|
||
int p2autooff, p2maxautooff;
|
||
|
||
NODE *nodepole;
|
||
FILE *prfil;
|
||
static struct interpass prepole;
|
||
|
||
void saveip(struct interpass *ip);
|
||
void deltemp(NODE *p);
|
||
void cvtemps(struct interpass *epil);
|
||
NODE *store(NODE *);
|
||
static void fixxasm(struct interpass *ip);
|
||
|
||
static void gencode(NODE *p, int cookie);
|
||
static void genxasm(NODE *p);
|
||
|
||
char *ltyp[] = { "", "LREG", "LOREG", "LTEMP" };
|
||
char *rtyp[] = { "", "RREG", "ROREG", "RTEMP" };
|
||
|
||
/* used when removing nodes */
|
||
struct tmpsave {
|
||
struct tmpsave *next;
|
||
CONSZ tempaddr;
|
||
int tempno;
|
||
} *tmpsave;
|
||
|
||
#ifdef PCC_DEBUG
|
||
static void
|
||
cktree(NODE *p)
|
||
{
|
||
if (p->n_op > MAXOP)
|
||
cerror("op %d slipped through", p->n_op);
|
||
if (BTYPE(p->n_type) > MAXTYPES)
|
||
cerror("type %x slipped through", p->n_type);
|
||
if (p->n_op == CBRANCH && !logop(p->n_left->n_op))
|
||
cerror("not logop branch");
|
||
if ((dope[p->n_op] & ASGOPFLG) && p->n_op != RETURN)
|
||
cerror("asgop %d slipped through", p->n_op);
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Check if a node has side effects.
|
||
*/
|
||
static int
|
||
isuseless(NODE *n)
|
||
{
|
||
switch (n->n_op) {
|
||
case XASM:
|
||
case FUNARG:
|
||
case UCALL:
|
||
case UFORTCALL:
|
||
case FORCE:
|
||
case ASSIGN:
|
||
case CALL:
|
||
case FORTCALL:
|
||
case CBRANCH:
|
||
case RETURN:
|
||
case GOTO:
|
||
case STCALL:
|
||
case USTCALL:
|
||
case STASG:
|
||
case STARG:
|
||
return 0;
|
||
default:
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Delete statements with no meaning (like a+b; or 513.4;)
|
||
*/
|
||
static NODE *
|
||
deluseless(NODE *p)
|
||
{
|
||
struct interpass *ip;
|
||
NODE *l, *r;
|
||
|
||
if (optype(p->n_op) == LTYPE) {
|
||
nfree(p);
|
||
return NULL;
|
||
}
|
||
if (isuseless(p) == 0)
|
||
return p;
|
||
|
||
if (optype(p->n_op) == UTYPE) {
|
||
l = p->n_left;
|
||
nfree(p);
|
||
return deluseless(l);
|
||
}
|
||
|
||
/* Be sure that both leaves may be valid */
|
||
l = deluseless(p->n_left);
|
||
r = deluseless(p->n_right);
|
||
nfree(p);
|
||
if (l && r) {
|
||
ip = ipnode(l);
|
||
DLIST_INSERT_AFTER(&prepole, ip, qelem);
|
||
return r;
|
||
} else if (l)
|
||
return l;
|
||
else if (r)
|
||
return r;
|
||
return NULL;
|
||
}
|
||
|
||
static struct interpass ipole;
|
||
struct interpass_prolog *ipp, *epp;
|
||
|
||
/*
|
||
* Receives interpass structs from pass1.
|
||
*/
|
||
void
|
||
pass2_compile(struct interpass *ip)
|
||
{
|
||
if (ip->type == IP_PROLOG) {
|
||
tmpsave = NULL;
|
||
ipp = (struct interpass_prolog *)ip;
|
||
DLIST_INIT(&ipole, qelem);
|
||
}
|
||
DLIST_INSERT_BEFORE(&ipole, ip, qelem);
|
||
if (ip->type != IP_EPILOG)
|
||
return;
|
||
|
||
#ifdef PCC_DEBUG
|
||
if (e2debug) {
|
||
printf("Entering pass2\n");
|
||
printip(&ipole);
|
||
}
|
||
#endif
|
||
|
||
epp = (struct interpass_prolog *)DLIST_PREV(&ipole, qelem);
|
||
p2maxautooff = p2autooff = epp->ipp_autos;
|
||
|
||
myreader(&ipole); /* local massage of input */
|
||
|
||
DLIST_FOREACH(ip, &ipole, qelem) {
|
||
if (ip->type != IP_NODE)
|
||
continue;
|
||
if (xtemps == 0)
|
||
walkf(ip->ip_node, deltemp);
|
||
}
|
||
DLIST_INIT(&prepole, qelem);
|
||
DLIST_FOREACH(ip, &ipole, qelem) {
|
||
if (ip->type != IP_NODE)
|
||
continue;
|
||
canon(ip->ip_node);
|
||
walkf(ip->ip_node, cktree);
|
||
if ((ip->ip_node = deluseless(ip->ip_node)) == NULL) {
|
||
DLIST_REMOVE(ip, qelem);
|
||
} else while (!DLIST_ISEMPTY(&prepole, qelem)) {
|
||
struct interpass *ipp;
|
||
|
||
ipp = DLIST_NEXT(&prepole, qelem);
|
||
DLIST_REMOVE(ipp, qelem);
|
||
DLIST_INSERT_BEFORE(ip, ipp, qelem);
|
||
}
|
||
}
|
||
|
||
fixxasm(&ipole); /* setup for extended asm */
|
||
|
||
optimize(&ipole);
|
||
ngenregs(&ipole);
|
||
|
||
DLIST_FOREACH(ip, &ipole, qelem)
|
||
emit(ip);
|
||
}
|
||
|
||
void
|
||
emit(struct interpass *ip)
|
||
{
|
||
NODE *p, *r;
|
||
struct optab *op;
|
||
int o;
|
||
|
||
switch (ip->type) {
|
||
case IP_NODE:
|
||
p = ip->ip_node;
|
||
|
||
nodepole = p;
|
||
//printf("bu:\n");
|
||
//fwalk(p, e2print, 0);
|
||
canon(p); /* may convert stuff after genregs */
|
||
//fwalk(p, e2print, 0);
|
||
switch (p->n_op) {
|
||
case CBRANCH:
|
||
/* Only emit branch insn if RESCC */
|
||
/* careful when an OPLOG has been elided */
|
||
if (p->n_left->n_su == 0 && p->n_left->n_left != NULL) {
|
||
op = &table[TBLIDX(p->n_left->n_left->n_su)];
|
||
r = p->n_left;
|
||
} else {
|
||
op = &table[TBLIDX(p->n_left->n_su)];
|
||
r = p;
|
||
}
|
||
if (op->rewrite & RESCC) {
|
||
o = p->n_left->n_op;
|
||
gencode(r, FORCC);
|
||
cbgen(o, p->n_right->n_lval);
|
||
} else {
|
||
gencode(r, FORCC);
|
||
}
|
||
break;
|
||
case FORCE:
|
||
gencode(p->n_left, INREGS);
|
||
break;
|
||
case XASM:
|
||
genxasm(p);
|
||
break;
|
||
default:
|
||
if (p->n_op != REG || p->n_type != VOID) /* XXX */
|
||
gencode(p, FOREFF); /* Emit instructions */
|
||
}
|
||
|
||
tfree(p);
|
||
break;
|
||
case IP_PROLOG:
|
||
prologue((struct interpass_prolog *)ip);
|
||
break;
|
||
case IP_EPILOG:
|
||
eoftn((struct interpass_prolog *)ip);
|
||
tmpsave = NULL; /* Always forget old nodes */
|
||
p2maxautooff = p2autooff = AUTOINIT/SZCHAR;
|
||
break;
|
||
case IP_DEFLAB:
|
||
deflab(ip->ip_lbl);
|
||
break;
|
||
case IP_ASM:
|
||
printf("\t%s\n", ip->ip_asm);
|
||
break;
|
||
default:
|
||
cerror("emit %d", ip->type);
|
||
}
|
||
}
|
||
|
||
#ifdef PCC_DEBUG
|
||
char *cnames[] = {
|
||
"SANY",
|
||
"SAREG",
|
||
"SBREG",
|
||
"SCREG",
|
||
"SDREG",
|
||
"SCC",
|
||
"SNAME",
|
||
"SCON",
|
||
"SFLD",
|
||
"SOREG",
|
||
"STARNM",
|
||
"STARREG",
|
||
"INTEMP",
|
||
"FORARG",
|
||
"SWADD",
|
||
0,
|
||
};
|
||
|
||
/*
|
||
* print a nice-looking description of cookie
|
||
*/
|
||
char *
|
||
prcook(int cookie)
|
||
{
|
||
static char buf[50];
|
||
int i, flag;
|
||
|
||
if (cookie & SPECIAL) {
|
||
switch (cookie) {
|
||
case SZERO:
|
||
return "SZERO";
|
||
case SONE:
|
||
return "SONE";
|
||
case SMONE:
|
||
return "SMONE";
|
||
default:
|
||
snprintf(buf, sizeof(buf), "SPECIAL+%d", cookie & ~SPECIAL);
|
||
return buf;
|
||
}
|
||
}
|
||
|
||
flag = 0;
|
||
buf[0] = 0;
|
||
for (i = 0; cnames[i]; ++i) {
|
||
if (cookie & (1<<i)) {
|
||
if (flag)
|
||
strlcat(buf, "|", sizeof(buf));
|
||
++flag;
|
||
strlcat(buf, cnames[i], sizeof(buf));
|
||
}
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
#endif
|
||
|
||
int odebug = 0;
|
||
|
||
int
|
||
geninsn(NODE *p, int cookie)
|
||
{
|
||
NODE *p1, *p2;
|
||
int o, rv = 0;
|
||
|
||
#ifdef PCC_DEBUG
|
||
if (odebug) {
|
||
printf("geninsn(%p, %s)\n", p, prcook(cookie));
|
||
fwalk(p, e2print, 0);
|
||
}
|
||
#endif
|
||
|
||
again: switch (o = p->n_op) {
|
||
case EQ:
|
||
case NE:
|
||
case LE:
|
||
case LT:
|
||
case GE:
|
||
case GT:
|
||
case ULE:
|
||
case ULT:
|
||
case UGE:
|
||
case UGT:
|
||
p1 = p->n_left;
|
||
p2 = p->n_right;
|
||
if (p2->n_op == ICON && p2->n_lval == 0 &&
|
||
optype(p1->n_op) == BITYPE) {
|
||
if (findops(p1, FORCC) == 0)
|
||
break;
|
||
}
|
||
rv = relops(p);
|
||
break;
|
||
|
||
case PLUS:
|
||
case MINUS:
|
||
case MUL:
|
||
case DIV:
|
||
case MOD:
|
||
case AND:
|
||
case OR:
|
||
case ER:
|
||
case LS:
|
||
case RS:
|
||
rv = findops(p, cookie);
|
||
break;
|
||
|
||
case ASSIGN:
|
||
case STASG:
|
||
rv = findasg(p, cookie);
|
||
break;
|
||
|
||
case UMUL: /* May turn into an OREG */
|
||
rv = findumul(p, cookie);
|
||
break;
|
||
|
||
case REG:
|
||
case TEMP:
|
||
case NAME:
|
||
case ICON:
|
||
case FCON:
|
||
case OREG:
|
||
rv = findleaf(p, cookie);
|
||
break;
|
||
|
||
case STCALL:
|
||
case CALL:
|
||
/* CALL arguments are handled special */
|
||
for (p1 = p->n_right; p1->n_op == CM; p1 = p1->n_left)
|
||
geninsn(p1->n_right, FOREFF);
|
||
geninsn(p1, FOREFF);
|
||
/* FALLTHROUGH */
|
||
case FLD:
|
||
case COMPL:
|
||
case UMINUS:
|
||
case PCONV:
|
||
case SCONV:
|
||
/* case INIT: */
|
||
case GOTO:
|
||
case FUNARG:
|
||
case STARG:
|
||
case UCALL:
|
||
case USTCALL:
|
||
case ADDROF:
|
||
rv = finduni(p, cookie);
|
||
break;
|
||
|
||
case CBRANCH:
|
||
p1 = p->n_left;
|
||
p2 = p->n_right;
|
||
p1->n_label = p2->n_lval;
|
||
geninsn(p1, FORCC);
|
||
p->n_su = 0;
|
||
break;
|
||
|
||
case FORCE: /* XXX needed? */
|
||
geninsn(p->n_left, INREGS);
|
||
p->n_su = 0; /* su calculations traverse left */
|
||
break;
|
||
|
||
case XASM:
|
||
for (p1 = p->n_left; p1->n_op == CM; p1 = p1->n_left)
|
||
geninsn(p1->n_right, FOREFF);
|
||
geninsn(p1, FOREFF);
|
||
break; /* all stuff already done? */
|
||
|
||
case XARG:
|
||
/* generate code for correct class here */
|
||
geninsn(p->n_left, 1 << p->n_label);
|
||
break;
|
||
|
||
default:
|
||
comperr("geninsn: bad op %s, node %p", opst[o], p);
|
||
}
|
||
if (rv == FFAIL)
|
||
comperr("Cannot generate code, node %p op %s", p,opst[p->n_op]);
|
||
if (rv == FRETRY)
|
||
goto again;
|
||
return rv;
|
||
}
|
||
|
||
/*
|
||
* Store a given subtree in a temporary location.
|
||
* Return an OREG node where it is located.
|
||
*/
|
||
NODE *
|
||
store(NODE *p)
|
||
{
|
||
extern struct interpass *storesave;
|
||
struct interpass *ip;
|
||
NODE *q, *r;
|
||
int s;
|
||
|
||
s = BITOOR(freetemp(szty(p->n_type)));
|
||
q = mklnode(OREG, s, FPREG, p->n_type);
|
||
r = mklnode(OREG, s, FPREG, p->n_type);
|
||
ip = ipnode(mkbinode(ASSIGN, q, p, p->n_type));
|
||
|
||
storesave = ip;
|
||
return r;
|
||
}
|
||
|
||
#ifdef PCC_DEBUG
|
||
#define CDEBUG(x) if (c2debug) printf x
|
||
#else
|
||
#define CDEBUG(x)
|
||
#endif
|
||
|
||
/*
|
||
* Do a register-register move if necessary.
|
||
*/
|
||
static void
|
||
ckmove(NODE *p, NODE *q)
|
||
{
|
||
if (q->n_op != REG || p->n_reg == -1)
|
||
return; /* no register */
|
||
if (DECRA(p->n_reg, 0) == DECRA(q->n_reg, 0))
|
||
return; /* no move necessary */
|
||
CDEBUG(("rmove: node %p, %s -> %s\n", p, rnames[DECRA(q->n_reg, 0)],
|
||
rnames[DECRA(p->n_reg, 0)]));
|
||
rmove(DECRA(q->n_reg, 0), DECRA(p->n_reg, 0), p->n_type);
|
||
q->n_reg = q->n_rval = DECRA(p->n_reg, 0);
|
||
}
|
||
|
||
/*
|
||
* Rewrite node to register after instruction emit.
|
||
*/
|
||
static void
|
||
rewrite(NODE *p, int rewrite, int cookie)
|
||
{
|
||
NODE *l, *r;
|
||
int o;
|
||
|
||
l = getlr(p, 'L');
|
||
r = getlr(p, 'R');
|
||
o = p->n_op;
|
||
p->n_op = REG;
|
||
p->n_lval = 0;
|
||
p->n_name = "";
|
||
|
||
if (o == ASSIGN) {
|
||
/* special rewrite care */
|
||
int reg = DECRA(p->n_reg, 0);
|
||
#define TL(x) (TBLIDX(x->n_su) || x->n_op == REG)
|
||
if (p->n_reg == -1)
|
||
;
|
||
else if (TL(l) && (DECRA(l->n_reg, 0) == reg))
|
||
;
|
||
else if (TL(r) && (DECRA(r->n_reg, 0) == reg))
|
||
;
|
||
else if (TL(l))
|
||
rmove(DECRA(l->n_reg, 0), reg, p->n_type);
|
||
else if (TL(r))
|
||
rmove(DECRA(r->n_reg, 0), reg, p->n_type);
|
||
#if 0
|
||
else
|
||
comperr("rewrite");
|
||
#endif
|
||
#undef TL
|
||
}
|
||
if (optype(o) != LTYPE)
|
||
tfree(l);
|
||
if (optype(o) == BITYPE)
|
||
tfree(r);
|
||
if (rewrite == 0)
|
||
return;
|
||
CDEBUG(("rewrite: %p, reg %s\n", p,
|
||
p->n_reg == -1? "<none>" : rnames[DECRA(p->n_reg, 0)]));
|
||
p->n_rval = DECRA(p->n_reg, 0);
|
||
}
|
||
|
||
/*
|
||
* printout extended assembler.
|
||
*/
|
||
void
|
||
genxasm(NODE *p)
|
||
{
|
||
NODE *q, **nary;
|
||
int n = 1, o = 0;
|
||
char *w;
|
||
|
||
for (q = p->n_left; q->n_op == CM; q = q->n_left)
|
||
n++;
|
||
nary = tmpalloc(sizeof(NODE *)*n);
|
||
o = n;
|
||
for (q = p->n_left; q->n_op == CM; q = q->n_left) {
|
||
gencode(q->n_right->n_left, INREGS);
|
||
nary[--o] = q->n_right;
|
||
}
|
||
gencode(q->n_left, INREGS);
|
||
nary[--o] = q;
|
||
|
||
w = p->n_name;
|
||
putchar('\t');
|
||
while (*w != 0) {
|
||
if (*w == '%') {
|
||
if (w[1] < '1' || w[1] > (n + '0'))
|
||
uerror("bad xasm arg number");
|
||
else
|
||
adrput(stdout, nary[(int)w[1]-'1']->n_left);
|
||
w++;
|
||
} else
|
||
putchar(*w);
|
||
w++;
|
||
}
|
||
putchar('\n');
|
||
}
|
||
|
||
void
|
||
gencode(NODE *p, int cookie)
|
||
{
|
||
struct optab *q = &table[TBLIDX(p->n_su)];
|
||
NODE *p1, *l, *r;
|
||
int o = optype(p->n_op);
|
||
|
||
l = p->n_left;
|
||
r = p->n_right;
|
||
|
||
if (TBLIDX(p->n_su) == 0) {
|
||
if (o == BITYPE && (p->n_su & DORIGHT))
|
||
gencode(r, 0);
|
||
if (optype(p->n_op) != LTYPE)
|
||
gencode(l, 0);
|
||
if (o == BITYPE && !(p->n_su & DORIGHT))
|
||
gencode(r, 0);
|
||
return;
|
||
}
|
||
|
||
CDEBUG(("gencode: node %p\n", p));
|
||
|
||
if (p->n_op == REG && DECRA(p->n_reg, 0) == p->n_rval)
|
||
return; /* meaningless move to itself */
|
||
|
||
if (callop(p->n_op))
|
||
lastcall(p); /* last chance before function args */
|
||
if (p->n_op == CALL || p->n_op == FORTCALL || p->n_op == STCALL) {
|
||
/* Print out arguments first */
|
||
for (p1 = r; p1->n_op == CM; p1 = p1->n_left)
|
||
gencode(p1->n_right, FOREFF);
|
||
gencode(p1, FOREFF);
|
||
o = UTYPE; /* avoid going down again */
|
||
}
|
||
|
||
if (o == BITYPE && (p->n_su & DORIGHT)) {
|
||
gencode(r, INREGS);
|
||
if (q->rewrite & RRIGHT)
|
||
ckmove(p, r);
|
||
}
|
||
if (o != LTYPE) {
|
||
gencode(l, INREGS);
|
||
if (q->rewrite & RLEFT)
|
||
ckmove(p, l);
|
||
}
|
||
if (o == BITYPE && !(p->n_su & DORIGHT)) {
|
||
gencode(r, INREGS);
|
||
if (q->rewrite & RRIGHT)
|
||
ckmove(p, r);
|
||
}
|
||
|
||
canon(p);
|
||
|
||
if (q->needs & NSPECIAL) {
|
||
int rr = rspecial(q, NRIGHT);
|
||
int lr = rspecial(q, NLEFT);
|
||
|
||
if (rr >= 0) {
|
||
#ifdef PCC_DEBUG
|
||
if (optype(p->n_op) != BITYPE)
|
||
comperr("gencode: rspecial borked");
|
||
#endif
|
||
if (r->n_op != REG)
|
||
comperr("gencode: rop != REG");
|
||
if (rr != r->n_rval)
|
||
rmove(r->n_rval, rr, r->n_type);
|
||
r->n_rval = r->n_reg = rr;
|
||
}
|
||
if (lr >= 0) {
|
||
if (l->n_op != REG)
|
||
comperr("gencode: %p lop != REG", p);
|
||
if (lr != l->n_rval)
|
||
rmove(l->n_rval, lr, l->n_type);
|
||
l->n_rval = l->n_reg = lr;
|
||
}
|
||
if (rr >= 0 && lr >= 0 && (l->n_reg == rr || r->n_reg == lr))
|
||
comperr("gencode: cross-reg-move");
|
||
}
|
||
|
||
if (p->n_op == ASSIGN &&
|
||
p->n_left->n_op == REG && p->n_right->n_op == REG &&
|
||
p->n_left->n_rval == p->n_right->n_rval){
|
||
/* do not emit anything */
|
||
CDEBUG(("gencode(%p) assign nothing\n", p));
|
||
rewrite(p, q->rewrite, cookie);
|
||
return;
|
||
}
|
||
|
||
CDEBUG(("emitting node %p\n", p));
|
||
if (TBLIDX(p->n_su) == 0)
|
||
return;
|
||
|
||
expand(p, cookie, q->cstring);
|
||
if (callop(p->n_op) && cookie != FOREFF &&
|
||
DECRA(p->n_reg, 0) != RETREG(p->n_type)) {
|
||
CDEBUG(("gencode(%p) retreg\n", p));
|
||
rmove(RETREG(p->n_type), DECRA(p->n_reg, 0), p->n_type);
|
||
} else if (q->needs & NSPECIAL) {
|
||
int rr = rspecial(q, NRES);
|
||
|
||
if (rr >= 0 && DECRA(p->n_reg, 0) != rr) {
|
||
CDEBUG(("gencode(%p) nspec retreg\n", p));
|
||
rmove(rr, DECRA(p->n_reg, 0), p->n_type);
|
||
}
|
||
} else if ((q->rewrite & RESC1) &&
|
||
(DECRA(p->n_reg, 1) != DECRA(p->n_reg, 0))) {
|
||
CDEBUG(("gencode(%p) RESC1 retreg\n", p));
|
||
rmove(DECRA(p->n_reg, 1), DECRA(p->n_reg, 0), p->n_type);
|
||
}
|
||
#if 0
|
||
/* XXX - kolla upp det h<>r */
|
||
else if (p->n_op == ASSIGN) {
|
||
/* may need move added if RLEFT/RRIGHT */
|
||
/* XXX should be handled in sucomp() */
|
||
if ((q->rewrite & RLEFT) && (p->n_left->n_op == REG) &&
|
||
(p->n_left->n_rval != DECRA(p->n_reg, 0)) &&
|
||
TCLASS(p->n_su)) {
|
||
rmove(p->n_left->n_rval, DECRA(p->n_reg, 0), p->n_type);
|
||
} else if ((q->rewrite & RRIGHT) && (p->n_right->n_op == REG) &&
|
||
(p->n_right->n_rval != DECRA(p->n_reg, 0)) &&
|
||
TCLASS(p->n_su)) {
|
||
rmove(p->n_right->n_rval, DECRA(p->n_reg, 0), p->n_type);
|
||
}
|
||
}
|
||
#endif
|
||
rewrite(p, q->rewrite, cookie);
|
||
}
|
||
|
||
int negrel[] = { NE, EQ, GT, GE, LT, LE, UGT, UGE, ULT, ULE } ; /* negatives of relationals */
|
||
size_t negrelsize = sizeof negrel / sizeof negrel[0];
|
||
|
||
#ifdef PCC_DEBUG
|
||
#undef PRTABLE
|
||
void
|
||
e2print(NODE *p, int down, int *a, int *b)
|
||
{
|
||
#ifdef PRTABLE
|
||
extern int tablesize;
|
||
#endif
|
||
|
||
prfil = stdout;
|
||
*a = *b = down+1;
|
||
while( down >= 2 ){
|
||
fprintf(prfil, "\t");
|
||
down -= 2;
|
||
}
|
||
if( down-- ) fprintf(prfil, " " );
|
||
|
||
|
||
fprintf(prfil, "%p) %s", p, opst[p->n_op] );
|
||
switch( p->n_op ) { /* special cases */
|
||
|
||
case REG:
|
||
fprintf(prfil, " %s", rnames[p->n_rval] );
|
||
break;
|
||
|
||
case TEMP:
|
||
fprintf(prfil, " %d", regno(p));
|
||
break;
|
||
|
||
case XASM:
|
||
case XARG:
|
||
fprintf(prfil, " '%s'", p->n_name);
|
||
break;
|
||
|
||
case ICON:
|
||
case NAME:
|
||
case OREG:
|
||
fprintf(prfil, " " );
|
||
adrput(prfil, p );
|
||
break;
|
||
|
||
case STCALL:
|
||
case USTCALL:
|
||
case STARG:
|
||
case STASG:
|
||
fprintf(prfil, " size=%d", p->n_stsize );
|
||
fprintf(prfil, " align=%d", p->n_stalign );
|
||
break;
|
||
}
|
||
|
||
fprintf(prfil, ", " );
|
||
tprint(prfil, p->n_type, p->n_qual);
|
||
fprintf(prfil, ", " );
|
||
|
||
prtreg(prfil, p);
|
||
fprintf(prfil, ", SU= %d(%cREG,%s,%s,%s,%s)\n",
|
||
TBLIDX(p->n_su),
|
||
TCLASS(p->n_su)+'@',
|
||
#ifdef PRTABLE
|
||
TBLIDX(p->n_su) >= 0 && TBLIDX(p->n_su) <= tablesize ?
|
||
table[TBLIDX(p->n_su)].cstring : "",
|
||
#else
|
||
"",
|
||
#endif
|
||
ltyp[LMASK&p->n_su],
|
||
rtyp[(p->n_su&RMASK) >> 2], p->n_su & DORIGHT ? "DORIGHT" : "");
|
||
}
|
||
#endif
|
||
|
||
#ifndef FIELDOPS
|
||
/*
|
||
* do this if there is no special hardware support for fields
|
||
*/
|
||
static void
|
||
ffld(NODE *p, int down, int *down1, int *down2 )
|
||
{
|
||
/*
|
||
* look for fields that are not in an lvalue context,
|
||
* and rewrite them...
|
||
*/
|
||
NODE *shp;
|
||
int s, o, v, ty;
|
||
|
||
*down1 = asgop( p->n_op );
|
||
*down2 = 0;
|
||
|
||
if( !down && p->n_op == FLD ){ /* rewrite the node */
|
||
|
||
if( !rewfld(p) ) return;
|
||
|
||
ty = p->n_type;
|
||
v = p->n_rval;
|
||
s = UPKFSZ(v);
|
||
# ifdef RTOLBYTES
|
||
o = UPKFOFF(v); /* amount to shift */
|
||
# else
|
||
o = szty(p->n_type)*SZINT - s - UPKFOFF(v); /* amount to shift */
|
||
#endif
|
||
|
||
/* make & mask part */
|
||
|
||
if (ISUNSIGNED(ty)) {
|
||
|
||
p->n_left->n_type = ty;
|
||
p->n_op = AND;
|
||
p->n_right = mklnode(ICON, ((CONSZ)1 << s)-1, 0, ty);
|
||
|
||
/* now, if a shift is needed, do it */
|
||
if( o != 0 ){
|
||
shp = mkbinode(RS, p->n_left,
|
||
mklnode(ICON, o, 0, INT), ty);
|
||
p->n_left = shp;
|
||
/* whew! */
|
||
}
|
||
} else {
|
||
/* must sign-extend, assume RS will do */
|
||
/* if not, arch must use rewfld() */
|
||
p->n_left->n_type = INT; /* Ok? */
|
||
p->n_op = RS;
|
||
p->n_right = mklnode(ICON, SZINT-s, 0, INT);
|
||
p->n_left = mkbinode(LS, p->n_left,
|
||
mklnode(ICON, SZINT-s-o, 0, INT), INT);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* change left TEMPs into OREGs
|
||
*/
|
||
void
|
||
deltemp(NODE *p)
|
||
{
|
||
struct tmpsave *w;
|
||
NODE *l;
|
||
|
||
if (p->n_op == TEMP) {
|
||
/* Check if already existing */
|
||
for (w = tmpsave; w; w = w->next)
|
||
if (w->tempno == regno(p))
|
||
break;
|
||
if (w == NULL) {
|
||
/* new on stack */
|
||
w = tmpalloc(sizeof(struct tmpsave));
|
||
w->tempno = regno(p);
|
||
w->tempaddr = BITOOR(freetemp(szty(p->n_type)));
|
||
w->next = tmpsave;
|
||
tmpsave = w;
|
||
}
|
||
p->n_op = OREG;
|
||
p->n_rval = FPREG;
|
||
p->n_lval = w->tempaddr;
|
||
} else if (p->n_op == ADDROF && p->n_left->n_op != NAME) {
|
||
/* TEMPs are already converted to OREGs */
|
||
if ((l = p->n_left)->n_op != OREG)
|
||
comperr("bad U&");
|
||
p->n_op = PLUS;
|
||
l->n_op = REG;
|
||
l->n_type = INCREF(l->n_type);
|
||
p->n_right = mklnode(ICON, l->n_lval, 0, INT);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* for pointer/integer arithmetic, set pointer at left node
|
||
*/
|
||
static void
|
||
setleft(NODE *p)
|
||
{
|
||
NODE *q;
|
||
|
||
/* only additions for now */
|
||
if (p->n_op != PLUS)
|
||
return;
|
||
if (ISPTR(p->n_right->n_type) && !ISPTR(p->n_left->n_type)) {
|
||
q = p->n_right;
|
||
p->n_right = p->n_left;
|
||
p->n_left = q;
|
||
}
|
||
}
|
||
|
||
/* It is OK to have these as externals */
|
||
static int oregr;
|
||
static CONSZ oregtemp;
|
||
static char *oregcp;
|
||
/*
|
||
* look for situations where we can turn * into OREG
|
||
* If sharp then do not allow temps.
|
||
*/
|
||
int
|
||
oregok(NODE *p, int sharp)
|
||
{
|
||
|
||
NODE *q;
|
||
NODE *ql, *qr;
|
||
int r;
|
||
CONSZ temp;
|
||
char *cp;
|
||
|
||
q = p->n_left;
|
||
#if 0
|
||
if ((q->n_op == REG || (q->n_op == TEMP && !sharp)) &&
|
||
q->n_rval == DECRA(q->n_reg, 0)) {
|
||
#endif
|
||
if (q->n_op == REG || (q->n_op == TEMP && !sharp)) {
|
||
temp = q->n_lval;
|
||
r = q->n_rval;
|
||
cp = q->n_name;
|
||
goto ormake;
|
||
}
|
||
|
||
if (q->n_op != PLUS && q->n_op != MINUS)
|
||
return 0;
|
||
ql = q->n_left;
|
||
qr = q->n_right;
|
||
|
||
#ifdef R2REGS
|
||
|
||
/* look for doubly indexed expressions */
|
||
/* XXX - fix checks */
|
||
|
||
if( q->n_op == PLUS) {
|
||
int i;
|
||
if( (r=base(ql))>=0 && (i=offset(qr, tlen(p)))>=0) {
|
||
makeor2(p, ql, r, i);
|
||
return 1;
|
||
} else if((r=base(qr))>=0 && (i=offset(ql, tlen(p)))>=0) {
|
||
makeor2(p, qr, r, i);
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
|
||
#endif
|
||
|
||
#if 0
|
||
if( (q->n_op==PLUS || q->n_op==MINUS) && qr->n_op == ICON &&
|
||
(ql->n_op==REG || (ql->n_op==TEMP && !sharp)) &&
|
||
szty(qr->n_type)==1 &&
|
||
(ql->n_rval == DECRA(ql->n_reg, 0) ||
|
||
/* XXX */
|
||
ql->n_rval == FPREG || ql->n_rval == STKREG)) {
|
||
#endif
|
||
if ((q->n_op==PLUS || q->n_op==MINUS) && qr->n_op == ICON &&
|
||
(ql->n_op==REG || (ql->n_op==TEMP && !sharp))) {
|
||
|
||
temp = qr->n_lval;
|
||
if( q->n_op == MINUS ) temp = -temp;
|
||
r = ql->n_rval;
|
||
temp += ql->n_lval;
|
||
cp = qr->n_name;
|
||
if( *cp && ( q->n_op == MINUS || *ql->n_name ) )
|
||
return 0;
|
||
if( !*cp ) cp = ql->n_name;
|
||
|
||
ormake:
|
||
if( notoff( p->n_type, r, temp, cp ))
|
||
return 0;
|
||
oregtemp = temp;
|
||
oregr = r;
|
||
oregcp = cp;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
ormake(NODE *p)
|
||
{
|
||
NODE *q = p->n_left;
|
||
|
||
p->n_op = OREG;
|
||
p->n_rval = oregr;
|
||
p->n_lval = oregtemp;
|
||
p->n_name = oregcp;
|
||
tfree(q);
|
||
}
|
||
|
||
/*
|
||
* look for situations where we can turn * into OREG
|
||
*/
|
||
void
|
||
oreg2(NODE *p)
|
||
{
|
||
if (p->n_op != UMUL)
|
||
return;
|
||
if (oregok(p, 1))
|
||
ormake(p);
|
||
if (p->n_op == UMUL)
|
||
myormake(p);
|
||
}
|
||
|
||
void
|
||
canon(p) NODE *p; {
|
||
/* put p in canonical form */
|
||
|
||
walkf(p, setleft); /* ptrs at left node for arithmetic */
|
||
walkf(p, oreg2); /* look for and create OREG nodes */
|
||
#ifndef FIELDOPS
|
||
fwalk(p, ffld, 0); /* look for field operators */
|
||
# endif
|
||
mycanon(p); /* your own canonicalization routine(s) */
|
||
|
||
}
|
||
|
||
void
|
||
comperr(char *str, ...)
|
||
{
|
||
extern char *ftitle;
|
||
va_list ap;
|
||
|
||
if (nerrors) {
|
||
fprintf(stderr,
|
||
"cannot recover from earlier errors: goodbye!\n");
|
||
exit(1);
|
||
}
|
||
|
||
va_start(ap, str);
|
||
fprintf(stderr, "%s, line %d: compiler error: ", ftitle, thisline);
|
||
vfprintf(stderr, str, ap);
|
||
fprintf(stderr, "\n");
|
||
va_end(ap);
|
||
prfil = stderr;
|
||
|
||
if (nodepole && nodepole->n_op != FREE)
|
||
fwalk(nodepole, e2print, 0);
|
||
exit(1);
|
||
}
|
||
|
||
/*
|
||
* allocate k integers worth of temp space
|
||
* we also make the convention that, if the number of words is
|
||
* more than 1, it must be aligned for storing doubles...
|
||
* Returns bits offset from base register.
|
||
* XXX - redo this.
|
||
*/
|
||
int
|
||
freetemp(int k)
|
||
{
|
||
#ifndef BACKTEMP
|
||
int t;
|
||
|
||
if (k > 1)
|
||
SETOFF(p2autooff, ALDOUBLE/ALCHAR);
|
||
|
||
t = p2autooff;
|
||
p2autooff += k*(SZINT/SZCHAR);
|
||
if (p2autooff > p2maxautooff)
|
||
p2maxautooff = p2autooff;
|
||
return (t);
|
||
|
||
#else
|
||
p2autooff += k*(SZINT/SZCHAR);
|
||
if (k > 1)
|
||
SETOFF(p2autooff, ALDOUBLE/ALCHAR);
|
||
|
||
if (p2autooff > p2maxautooff)
|
||
p2maxautooff = p2autooff;
|
||
return( -p2autooff );
|
||
#endif
|
||
}
|
||
|
||
NODE *
|
||
mklnode(int op, CONSZ lval, int rval, TWORD type)
|
||
{
|
||
NODE *p = talloc();
|
||
|
||
p->n_name = "";
|
||
p->n_qual = 0;
|
||
p->n_op = op;
|
||
p->n_lval = lval;
|
||
p->n_rval = rval;
|
||
p->n_type = type;
|
||
p->n_regw = NULL;
|
||
p->n_su = 0;
|
||
return p;
|
||
}
|
||
|
||
NODE *
|
||
mkbinode(int op, NODE *left, NODE *right, TWORD type)
|
||
{
|
||
NODE *p = talloc();
|
||
|
||
p->n_name = "";
|
||
p->n_qual = 0;
|
||
p->n_op = op;
|
||
p->n_left = left;
|
||
p->n_right = right;
|
||
p->n_type = type;
|
||
p->n_regw = NULL;
|
||
return p;
|
||
}
|
||
|
||
NODE *
|
||
mkunode(int op, NODE *left, int rval, TWORD type)
|
||
{
|
||
NODE *p = talloc();
|
||
|
||
p->n_name = "";
|
||
p->n_qual = 0;
|
||
p->n_op = op;
|
||
p->n_left = left;
|
||
p->n_rval = rval;
|
||
p->n_type = type;
|
||
p->n_regw = NULL;
|
||
return p;
|
||
}
|
||
|
||
struct interpass *
|
||
ipnode(NODE *p)
|
||
{
|
||
struct interpass *ip = tmpalloc(sizeof(struct interpass));
|
||
|
||
ip->ip_node = p;
|
||
ip->type = IP_NODE;
|
||
ip->lineno = thisline;
|
||
return ip;
|
||
}
|
||
|
||
int
|
||
rspecial(struct optab *q, int what)
|
||
{
|
||
struct rspecial *r = nspecial(q);
|
||
while (r->op) {
|
||
if (r->op == what)
|
||
return r->num;
|
||
r++;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* Ensure that a node is correct for the destination.
|
||
*/
|
||
static void
|
||
ltypify(struct interpass *ip, NODE *p)
|
||
{
|
||
struct interpass *ip2;
|
||
TWORD t = p->n_left->n_type;
|
||
NODE *q, *r;
|
||
char *w;
|
||
// int asg = 0, and = 0;
|
||
|
||
#ifdef notyet
|
||
if (myxasm(ip, p))
|
||
return; /* handled by target-specific code */
|
||
#endif
|
||
w = p->n_name;
|
||
// if (*w == '=')
|
||
// w++, asg = 1;
|
||
switch (*w) {
|
||
case 'r': /* general reg */
|
||
/* set register class */
|
||
p->n_label = gclass(p->n_left->n_type);
|
||
if (optype(p->n_left->n_op) == LTYPE)
|
||
break;
|
||
q = mklnode(TEMP, 0, epp->ip_tmpnum++, t);
|
||
r = tcopy(q);
|
||
ip2 = ipnode(mkbinode(ASSIGN, q, p->n_left, t));
|
||
DLIST_INSERT_BEFORE(ip, ip2, qelem);
|
||
p->n_left = r;
|
||
break;
|
||
default:
|
||
uerror("unsupported xasm option string '%s'", p->n_name);
|
||
}
|
||
|
||
|
||
// fwalk(p, e2print, 0);
|
||
}
|
||
|
||
/* Extended assembler hacks */
|
||
static void
|
||
fixxasm(struct interpass *ipole)
|
||
{
|
||
struct interpass *ip;
|
||
NODE *p;
|
||
|
||
DLIST_FOREACH(ip, ipole, qelem) {
|
||
if (ip->type != IP_NODE || ip->ip_node->n_op != XASM)
|
||
continue;
|
||
/* Got an assembler node */
|
||
p = ip->ip_node->n_left;
|
||
|
||
/*
|
||
* Ensure that the arg nodes can be directly addressable
|
||
* We decide that everything shall be LTYPE here.
|
||
*/
|
||
for (; p->n_op == CM; p = p->n_left)
|
||
ltypify(ip, p->n_right);
|
||
ltypify(ip, p);
|
||
p = ip->ip_node->n_right;
|
||
if (p->n_op != ICON || p->n_type != STRTY)
|
||
uerror("xasm constraints not supported");
|
||
}
|
||
}
|