diff options
Diffstat (limited to 'usr.bin/pcc/mip')
-rw-r--r-- | usr.bin/pcc/mip/common.c | 631 | ||||
-rw-r--r-- | usr.bin/pcc/mip/manifest.h | 316 | ||||
-rw-r--r-- | usr.bin/pcc/mip/match.c | 995 | ||||
-rw-r--r-- | usr.bin/pcc/mip/mkext.c | 318 | ||||
-rw-r--r-- | usr.bin/pcc/mip/node.h | 194 | ||||
-rw-r--r-- | usr.bin/pcc/mip/optim2.c | 882 | ||||
-rw-r--r-- | usr.bin/pcc/mip/pass2.h | 418 | ||||
-rw-r--r-- | usr.bin/pcc/mip/protos.h | 83 | ||||
-rw-r--r-- | usr.bin/pcc/mip/reader.c | 1098 | ||||
-rw-r--r-- | usr.bin/pcc/mip/regs.c | 2244 |
10 files changed, 7179 insertions, 0 deletions
diff --git a/usr.bin/pcc/mip/common.c b/usr.bin/pcc/mip/common.c new file mode 100644 index 00000000000..e5d49df9282 --- /dev/null +++ b/usr.bin/pcc/mip/common.c @@ -0,0 +1,631 @@ +/* $Id: common.c,v 1.1 2007/09/15 18:12:35 otto 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. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "pass2.h" + +# ifndef EXIT +# define EXIT exit +# endif + +int nerrors = 0; /* number of errors */ +char *ftitle; +int lineno; + +#ifndef WHERE +#define WHERE(ch) fprintf(stderr, "%s, line %d: ", ftitle, lineno); +#endif + +/* + * nonfatal error message + * the routine where is different for pass 1 and pass 2; + * it tells where the error took place + */ +void +uerror(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + ++nerrors; + WHERE('u'); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + if (nerrors > 30) + cerror("too many errors"); + va_end(ap); +} + +/* + * compiler error: die + */ +void +cerror(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + WHERE('c'); + + /* give the compiler the benefit of the doubt */ + if (nerrors && nerrors <= 30) { + fprintf(stderr, + "cannot recover from earlier errors: goodbye!\n"); + } else { + fprintf(stderr, "compiler error: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + } + va_end(ap); + EXIT(1); +} + +/* + * warning + */ +void +werror(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + WHERE('w'); + fprintf(stderr, "warning: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); +} + +#ifndef MKEXT +static NODE *freelink; +static int usednodes; + +NODE * +talloc() +{ + extern int inlnodecnt, recovernodes; + register NODE *p; + + usednodes++; + + if (recovernodes) + inlnodecnt++; + if (freelink != NULL) { + p = freelink; + freelink = p->next; + if (p->n_op != FREE) + cerror("node not FREE: %p", p); + if (nflag) + printf("alloc node %p from freelist\n", p); + return p; + } + + p = permalloc(sizeof(NODE)); + p->n_op = FREE; + if (nflag) + printf("alloc node %p from memory\n", p); + return p; +} + +/* + * make a fresh copy of p + */ +NODE * +tcopy(NODE *p) +{ + NODE *q; + + q = talloc(); + *q = *p; + + switch (optype(q->n_op)) { + case BITYPE: + q->n_right = tcopy(p->n_right); + case UTYPE: + q->n_left = tcopy(p->n_left); + } + + return(q); +} + + +/* + * ensure that all nodes have been freed + */ +void +tcheck() +{ + extern int inlnodecnt; + + if (nerrors) + return; + + if ((usednodes - inlnodecnt) != 0) + cerror("usednodes == %d, inlnodecnt %d", usednodes, inlnodecnt); +} + +/* + * free the tree p + */ +void +tfree(NODE *p) +{ + if (p->n_op != FREE) + walkf(p, (void (*)(NODE *))nfree); +} + +/* + * Free a node, and return its left descendant. + * It is up to the caller to know whether the return value is usable. + */ +NODE * +nfree(NODE *p) +{ + extern int inlnodecnt, recovernodes; + NODE *l; +#ifdef PCC_DEBUG_NODES + NODE *q; +#endif + + if (p == NULL) + cerror("freeing blank node!"); + + l = p->n_left; + if (p->n_op == FREE) + cerror("freeing FREE node", p); +#ifdef PCC_DEBUG_NODES + q = freelink; + while (q != NULL) { + if (q == p) + cerror("freeing free node %p", p); + q = q->next; + } +#endif + + if (nflag) + printf("freeing node %p\n", p); + p->n_op = FREE; + p->next = freelink; + freelink = p; + usednodes--; + if (recovernodes) + inlnodecnt--; + return l; +} +#endif + +#ifdef MKEXT +#define coptype(o) (dope[o]&TYFLG) +#else +int cdope(int); +#define coptype(o) (cdope(o)&TYFLG) +#endif + +void +fwalk(NODE *t, void (*f)(NODE *, int, int *, int *), int down) +{ + + int down1, down2; + + more: + down1 = down2 = 0; + + (*f)(t, down, &down1, &down2); + + switch (coptype( t->n_op )) { + + case BITYPE: + fwalk( t->n_left, f, down1 ); + t = t->n_right; + down = down2; + goto more; + + case UTYPE: + t = t->n_left; + down = down1; + goto more; + + } +} + +void +walkf(NODE *t, void (*f)(NODE *)) +{ + int opty; + + opty = coptype(t->n_op); + + if (opty != LTYPE) + walkf( t->n_left, f ); + if (opty == BITYPE) + walkf( t->n_right, f ); + (*f)(t); +} + +int dope[DSIZE]; +char *opst[DSIZE]; + +struct dopest { + int dopeop; + char opst[8]; + int dopeval; +} indope[] = { + { NAME, "NAME", LTYPE, }, + { REG, "REG", LTYPE, }, + { OREG, "OREG", LTYPE, }, + { TEMP, "TEMP", LTYPE, }, + { MOVE, "MOVE", UTYPE, }, + { ICON, "ICON", LTYPE, }, + { FCON, "FCON", LTYPE, }, + { CCODES, "CCODES", LTYPE, }, + { UMINUS, "U-", UTYPE, }, + { UMUL, "U*", UTYPE, }, + { FUNARG, "FUNARG", UTYPE, }, + { UCALL, "UCALL", UTYPE|CALLFLG, }, + { UFORTCALL, "UFCALL", UTYPE|CALLFLG, }, + { COMPL, "~", UTYPE, }, + { FORCE, "FORCE", UTYPE, }, +/* { INIT, "INIT", UTYPE, }, */ + { SCONV, "SCONV", UTYPE, }, + { PCONV, "PCONV", UTYPE, }, + { PLUS, "+", BITYPE|FLOFLG|SIMPFLG|COMMFLG, }, + { MINUS, "-", BITYPE|FLOFLG|SIMPFLG, }, + { MUL, "*", BITYPE|FLOFLG|MULFLG, }, + { AND, "&", BITYPE|SIMPFLG|COMMFLG, }, + { CM, ",", BITYPE, }, + { ASSIGN, "=", BITYPE|ASGFLG, }, + { DIV, "/", BITYPE|FLOFLG|MULFLG|DIVFLG, }, + { MOD, "%", BITYPE|DIVFLG, }, + { LS, "<<", BITYPE|SHFFLG, }, + { RS, ">>", BITYPE|SHFFLG, }, + { OR, "|", BITYPE|COMMFLG|SIMPFLG, }, + { ER, "^", BITYPE|COMMFLG|SIMPFLG, }, + { STREF, "->", BITYPE, }, + { CALL, "CALL", BITYPE|CALLFLG, }, + { FORTCALL, "FCALL", BITYPE|CALLFLG, }, + { EQ, "==", BITYPE|LOGFLG, }, + { NE, "!=", BITYPE|LOGFLG, }, + { LE, "<=", BITYPE|LOGFLG, }, + { LT, "<", BITYPE|LOGFLG, }, + { GE, ">=", BITYPE|LOGFLG, }, + { GT, ">", BITYPE|LOGFLG, }, + { UGT, "UGT", BITYPE|LOGFLG, }, + { UGE, "UGE", BITYPE|LOGFLG, }, + { ULT, "ULT", BITYPE|LOGFLG, }, + { ULE, "ULE", BITYPE|LOGFLG, }, + { CBRANCH, "CBRANCH", BITYPE, }, + { FLD, "FLD", UTYPE, }, + { PMCONV, "PMCONV", BITYPE, }, + { PVCONV, "PVCONV", BITYPE, }, + { RETURN, "RETURN", BITYPE|ASGFLG|ASGOPFLG, }, + { GOTO, "GOTO", UTYPE, }, + { STASG, "STASG", BITYPE|ASGFLG, }, + { STARG, "STARG", UTYPE, }, + { STCALL, "STCALL", BITYPE|CALLFLG, }, + { USTCALL, "USTCALL", UTYPE|CALLFLG, }, + { ADDROF, "U&", UTYPE, }, + + { -1, "", 0 }, +}; + +void +mkdope() +{ + struct dopest *q; + + for( q = indope; q->dopeop >= 0; ++q ){ + dope[q->dopeop] = q->dopeval; + opst[q->dopeop] = q->opst; + } +} + +/* + * output a nice description of the type of t + */ +void +tprint(FILE *fp, TWORD t, TWORD q) +{ + static char * tnames[] = { + "undef", + "farg", + "char", + "uchar", + "short", + "ushort", + "int", + "unsigned", + "long", + "ulong", + "longlong", + "ulonglong", + "float", + "double", + "ldouble", + "strty", + "unionty", + "enumty", + "moety", + "void", + "signed", /* pass1 */ + "bool", /* pass1 */ + "?", "?" + }; + + for(;; t = DECREF(t), q = DECREF(q)) { + if (ISCON(q)) + fputc('C', fp); + if (ISVOL(q)) + fputc('V', fp); + + if (ISPTR(t)) + fprintf(fp, "PTR "); + else if (ISFTN(t)) + fprintf(fp, "FTN "); + else if (ISARY(t)) + fprintf(fp, "ARY "); + else { + fprintf(fp, "%s%s%s", ISCON(q << TSHIFT) ? "const " : "", + ISVOL(q << TSHIFT) ? "volatile " : "", tnames[t]); + return; + } + } +} + +int crslab = 10; +/* + * Return a number for internal labels. + */ +int +getlab() +{ + return crslab++; +} + +/* + * Memory allocation routines. + * Memory are allocated from the system in MEMCHUNKSZ blocks. + * permalloc() returns a bunch of memory that is never freed. + * Memory allocated through tmpalloc() will be released the + * next time a function is ended (via tmpfree()). + */ + +#define MEMCHUNKSZ 8192 /* 8k per allocation */ +struct b { + char a1; + union { + long long l; + long double d; + } a2; +}; + +#define ALIGNMENT ((int)&((struct b *)0)->a2) +#define ROUNDUP(x) ((x) + (sizeof(ALIGNMENT)-1)) & ~(sizeof(ALIGNMENT)-1) + +static char *allocpole; +static int allocleft; +static char *tmppole; +static int tmpleft; +int permallocsize, tmpallocsize, lostmem; + +void * +permalloc(int size) +{ + void *rv; + +//printf("permalloc: allocpole %p allocleft %d size %d ", allocpole, allocleft, size); + if (size > MEMCHUNKSZ) + cerror("permalloc"); + if (size <= 0) + cerror("permalloc2"); + if (allocleft < size) { + /* looses unused bytes */ + lostmem += allocleft; +//fprintf(stderr, "allocating perm\n"); + if ((allocpole = malloc(MEMCHUNKSZ)) == NULL) + cerror("permalloc: out of memory"); + allocleft = MEMCHUNKSZ; + } + size = ROUNDUP(size); + rv = &allocpole[MEMCHUNKSZ-allocleft]; +//printf("rv %p\n", rv); + allocleft -= size; + permallocsize += size; + return rv; +} + +static char *tmplink; + +void * +tmpcalloc(int size) +{ + void *rv; + + rv = tmpalloc(size); + memset(rv, 0, size); + return rv; +} + +#define TMPOLE &tmppole[MEMCHUNKSZ-tmpleft] +void * +tmpalloc(int size) +{ + void *rv; + + if (size > MEMCHUNKSZ) { + return malloc(size); + // cerror("tmpalloc %d", size); + } + if (size <= 0) + cerror("tmpalloc2"); +//printf("tmpalloc: tmppole %p tmpleft %d size %d ", tmppole, tmpleft, size); + size = ROUNDUP(size); + if (tmpleft < size) { + if ((tmppole = malloc(MEMCHUNKSZ)) == NULL) + cerror("tmpalloc: out of memory"); +//fprintf(stderr, "allocating tmp\n"); + tmpleft = MEMCHUNKSZ - (ROUNDUP(sizeof(char *))); + *(char **)tmppole = tmplink; + tmplink = tmppole; + } + rv = TMPOLE; +//printf("rv %p\n", rv); + tmpleft -= size; + tmpallocsize += size; + return rv; +} + +#if 0 +/* + * Print and pack strings on heap. + */ +char *tmpsprintf(char *fmt, ...); +char * +tmpsprintf(char *fmt, ...) +{ + va_list ap; + int len; + char *tmp; + + tmp = TMPOLE; + va_start(ap, fmt); + if ((len = vsnprintf(tmp, tmpleft, fmt, ap)) >= tmpleft) { + (void)tmpalloc(tmpleft); /* ugly */ + tmp = TMPOLE; + if ((len = vsnprintf(tmp, tmpleft, fmt, ap)) >= tmpleft) + cerror("bad tmpsprintf len"); + } + va_end(ap); + tmpleft += len; + return tmp; +} +#endif + +/* + * Print and pack vararg string on heap. + */ +char *tmpvsprintf(char *fmt, va_list ap); +char * +tmpvsprintf(char *fmt, va_list ap) +{ + int len; + char *tmp; + + if (tmpleft == 0) + (void)tmpalloc(1); /* XXX ugly */ + tmp = TMPOLE; + if ((len = vsnprintf(tmp, tmpleft, fmt, ap)) >= tmpleft) { + (void)tmpalloc(tmpleft+1); /* ugly */ + tmp = TMPOLE; + if ((len = vsnprintf(tmp, tmpleft, fmt, ap)) >= tmpleft) + cerror("bad tmpsprintf len"); + } + tmpleft -= len+1; + return tmp; +} + +void +tmpfree() +{ + char *f, *of; + + f = tmplink; + if (f == NULL) + return; + if (*(char **)f == NULL) { + tmpleft = MEMCHUNKSZ - (ROUNDUP(sizeof(char *))); + return; + } + while (f != NULL) { + of = f; + f = *(char **)f; + free(of); + } + tmplink = tmppole = NULL; + tmpleft = 0; +//fprintf(stderr, "freeing tmp\n"); + /* XXX - nothing right now */ +} + +/* + * Allocate space on the permanent stack for a string of length len+1 + * and copy it there. + * Return the new address. + */ +char * +newstring(char *s, int len) +{ + char *u, *c; + + len++; + if (allocleft < len) { + u = c = permalloc(len); + } else { + u = c = &allocpole[MEMCHUNKSZ-allocleft]; + allocleft -= ROUNDUP(len+1); + } + while (len--) + *c++ = *s++; + return u; +} diff --git a/usr.bin/pcc/mip/manifest.h b/usr.bin/pcc/mip/manifest.h new file mode 100644 index 00000000000..dd4cf0ecafb --- /dev/null +++ b/usr.bin/pcc/mip/manifest.h @@ -0,0 +1,316 @@ +/* $Id: manifest.h,v 1.1 2007/09/15 18:12:35 otto Exp $ */ +/* + * 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. + */ + +#ifndef MANIFEST +#define MANIFEST + +#include <stdio.h> +#include "../config.h" +#include "macdefs.h" +#include "node.h" + +/* + * Node types + */ +#define LTYPE 02 /* leaf */ +#define UTYPE 04 /* unary */ +#define BITYPE 010 /* binary */ + +/* + * DSIZE is the size of the dope array + */ +#define DSIZE (MAXOP+1) + +/* + * Type names, used in symbol table building. + * The order of the integer types are important. + * Signed types must have bit 0 unset, unsigned types set (used below). + */ +#define UNDEF 0 /* free symbol table entry */ +#define FARG 1 /* function argument */ +#define CHAR 2 +#define UCHAR 3 +#define SHORT 4 +#define USHORT 5 +#define INT 6 +#define UNSIGNED 7 +#define LONG 8 +#define ULONG 9 +#define LONGLONG 10 +#define ULONGLONG 11 +#define FLOAT 12 +#define DOUBLE 13 +#define LDOUBLE 14 +#define STRTY 15 +#define UNIONTY 16 +#define ENUMTY 17 +#define MOETY 18 /* member of enum */ +#define VOID 19 + +#define MAXTYPES 19 /* highest type+1 to be used by lang code */ +/* + * Various flags + */ +#define NOLAB (-1) + +/* + * Type modifiers. + */ +#define PTR 0x20 +#define FTN 0x40 +#define ARY 0x60 +#define CON 0x20 +#define VOL 0x40 + +/* + * Type packing constants + */ +#define TMASK 0x060 +#define TMASK1 0x180 +#define TMASK2 0x1e0 +#define BTMASK 0x1f +#define BTSHIFT 5 +#define TSHIFT 2 + +/* + * Macros + */ +#define MODTYPE(x,y) x = ((x)&(~BTMASK))|(y) /* set basic type of x to y */ +#define BTYPE(x) ((x)&BTMASK) /* basic type of x */ +#define ISLONGLONG(x) ((x) == LONGLONG || (x) == ULONGLONG) +#define ISUNSIGNED(x) (((x) <= ULONGLONG) && (((x) & 1) == (UNSIGNED & 1))) +#define UNSIGNABLE(x) (((x)<=ULONGLONG&&(x)>=CHAR) && !ISUNSIGNED(x)) +#define ENUNSIGN(x) ((x)|1) +#define DEUNSIGN(x) ((x)&~1) +#define ISPTR(x) (((x)&TMASK)==PTR) +#define ISFTN(x) (((x)&TMASK)==FTN) /* is x a function type? */ +#define ISARY(x) (((x)&TMASK)==ARY) /* is x an array type? */ +#define ISCON(x) (((x)&CON)==CON) /* is x const? */ +#define ISVOL(x) (((x)&VOL)==VOL) /* is x volatile? */ +#define INCREF(x) ((((x)&~BTMASK)<<TSHIFT)|PTR|((x)&BTMASK)) +#define INCQAL(x) ((((x)&~BTMASK)<<TSHIFT)|((x)&BTMASK)) +#define DECREF(x) ((((x)>>TSHIFT)&~BTMASK)|((x)&BTMASK)) +#define DECQAL(x) ((((x)>>TSHIFT)&~BTMASK)|((x)&BTMASK)) +#define SETOFF(x,y) { if ((x)%(y) != 0) (x) = (((x)/(y) + 1) * (y)); } + /* advance x to a multiple of y */ +#define NOFIT(x,y,z) (((x)%(z) + (y)) > (z)) + /* can y bits be added to x without overflowing z */ + +#ifndef SPECIAL_INTEGERS +#define ASGLVAL(lval, val) +#endif + +/* + * Pack and unpack field descriptors (size and offset) + */ +#define PKFIELD(s,o) (((o)<<6)| (s)) +#define UPKFSZ(v) ((v)&077) +#define UPKFOFF(v) ((v)>>6) + +/* + * Operator information + */ +#define TYFLG 016 +#define ASGFLG 01 +#define LOGFLG 020 + +#define SIMPFLG 040 +#define COMMFLG 0100 +#define DIVFLG 0200 +#define FLOFLG 0400 +#define LTYFLG 01000 +#define CALLFLG 02000 +#define MULFLG 04000 +#define SHFFLG 010000 +#define ASGOPFLG 020000 + +#define SPFLG 040000 + +/* + * Location counters + */ +#define PROG 0 /* (ro) program segment */ +#define DATA 1 /* (rw) data segment */ +#define RDATA 2 /* (ro) data segment */ +#define STRNG 3 /* (ro) string segment */ + + +/* + * + */ +extern int bdebug, tdebug, edebug; +extern int ddebug, xdebug, f2debug; +extern int iTflag, oTflag; +extern int vdebug, sflag, nflag, gflag; +extern int Wstrict_prototypes, Wmissing_prototypes, Wimplicit_int, + Wimplicit_function_declaration; +extern int xssaflag, xtailcallflag, xtemps, xdeljumps; + +int yyparse(void); +void yyaccpt(void); + +/* + * List handling macros, similar to those in 4.4BSD. + * The double-linked list is insque-style. + */ +/* Double-linked list macros */ +#define DLIST_INIT(h,f) { (h)->f.q_forw = (h); (h)->f.q_back = (h); } +#define DLIST_ENTRY(t) struct { struct t *q_forw, *q_back; } +#define DLIST_NEXT(h,f) (h)->f.q_forw +#define DLIST_PREV(h,f) (h)->f.q_back +#define DLIST_ISEMPTY(h,f) ((h)->f.q_forw == (h)) +#define DLIST_FOREACH(v,h,f) \ + for ((v) = (h)->f.q_forw; (v) != (h); (v) = (v)->f.q_forw) +#define DLIST_FOREACH_REVERSE(v,h,f) \ + for ((v) = (h)->f.q_back; (v) != (h); (v) = (v)->f.q_back) +#define DLIST_INSERT_BEFORE(h,e,f) { \ + (e)->f.q_forw = (h); \ + (e)->f.q_back = (h)->f.q_back; \ + (e)->f.q_back->f.q_forw = (e); \ + (h)->f.q_back = (e); \ +} +#define DLIST_INSERT_AFTER(h,e,f) { \ + (e)->f.q_forw = (h)->f.q_forw; \ + (e)->f.q_back = (h); \ + (e)->f.q_forw->f.q_back = (e); \ + (h)->f.q_forw = (e); \ +} +#define DLIST_REMOVE(e,f) { \ + (e)->f.q_forw->f.q_back = (e)->f.q_back; \ + (e)->f.q_back->f.q_forw = (e)->f.q_forw; \ +} + +/* Single-linked list */ +#define SLIST_INIT(h) \ + { (h)->q_forw = NULL; (h)->q_last = &(h)->q_forw; } +#define SLIST_ENTRY(t) struct { struct t *q_forw; } +#define SLIST_HEAD(n,t) struct n { struct t *q_forw, **q_last; } +#define SLIST_FIRST(h) ((h)->q_forw) +#define SLIST_FOREACH(v,h,f) \ + for ((v) = (h)->q_forw; (v) != NULL; (v) = (v)->f.q_forw) +#define SLIST_INSERT_LAST(h,e,f) { \ + (e)->f.q_forw = NULL; \ + *(h)->q_last = (e); \ + (h)->q_last = &(e)->f.q_forw; \ +} + +/* + * Functions for inter-pass communication. + * + */ +struct interpass { + DLIST_ENTRY(interpass) qelem; + int type; + int lineno; + union { + NODE *_p; + int _locctr; + int _label; + int _curoff; + char *_name; + } _un; +}; + +/* + * Special struct for prologue/epilogue. + * - ip_lblnum contains the lowest/highest+1 label used + * - ip_lbl is set before/after all code and after/before the prolog/epilog. + */ +struct interpass_prolog { + struct interpass ipp_ip; + char *ipp_name; /* Function name */ + int ipp_vis; /* Function visibility */ + TWORD ipp_type; /* Function type */ + int ipp_regs; /* Bitmask of registers to save */ + int ipp_autos; /* Size on stack needed */ + int ip_tmpnum; /* # allocated temp nodes so far */ + int ip_lblnum; /* # used labels so far */ +}; + +/* + * Epilog/prolog takes following arguments (in order): + * - type + * - regs + * - autos + * - name + * - type + * - retlab + */ + +#define ip_node _un._p +#define ip_locc _un._locctr +#define ip_lbl _un._label +#define ip_name _un._name +#define ip_asm _un._name +#define ip_off _un._curoff + +/* Types of inter-pass structs */ +#define IP_NODE 1 +#define IP_PROLOG 2 +#define IP_EPILOG 4 +#define IP_DEFLAB 5 +#define IP_DEFNAM 6 +#define IP_ASM 7 +#define MAXIP 7 + +void send_passt(int type, ...); +/* + * External declarations, typedefs and the like + */ +char *hash(char *s); +char *savestr(char *cp); +char *tstr(char *cp); + +/* memory management stuff */ +void *permalloc(int size); +void *tmpcalloc(int size); +void *tmpalloc(int size); +void tmpfree(void); +char *newstring(char *, int len); + +void tprint(FILE *, TWORD, TWORD); + +/* pass t communication subroutines */ +void topt_compile(struct interpass *); + +/* pass 2 communication subroutines */ +void pass2_compile(struct interpass *); + +/* node routines */ +NODE *nfree(NODE *); +void fwalk(NODE *t, void (*f)(NODE *, int, int *, int *), int down); + +extern int nerrors; /* number of errors seen so far */ +#endif diff --git a/usr.bin/pcc/mip/match.c b/usr.bin/pcc/mip/match.c new file mode 100644 index 00000000000..503b4698ab0 --- /dev/null +++ b/usr.bin/pcc/mip/match.c @@ -0,0 +1,995 @@ +/* $Id: match.c,v 1.1 2007/09/15 18:12:36 otto 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. + */ + +# include "pass2.h" + +#include <strings.h> + +void prttype(int t); +void setclass(int tmp, int class); +int getclass(int tmp); + +int fldsz, fldshf; + +int s2debug = 0; + +extern char *ltyp[], *rtyp[]; + +static char *srtyp[] = { "SRNOPE", "SRDIR", "SROREG", "SRREG" }; + +/* + * return true if shape is appropriate for the node p + * side effect for SFLD is to set up fldsz, etc + * + * Return values: + * SRNOPE Cannot match this shape. + * SRDIR Direct match, may or may not traverse down. + * SRREG Will match if put in a regster XXX - kill this? + */ +int +tshape(NODE *p, int shape) +{ + int o, mask; + + o = p->n_op; + +#ifdef PCC_DEBUG + if (s2debug) + printf("tshape(%p, %s) op = %s\n", p, prcook(shape), opst[o]); +#endif + + if (shape & SPECIAL) { + + switch (shape) { + case SZERO: + case SONE: + case SMONE: + case SSCON: + case SCCON: + if (o != ICON || p->n_name[0]) + return SRNOPE; + if (p->n_lval == 0 && shape == SZERO) + return SRDIR; + if (p->n_lval == 1 && shape == SONE) + return SRDIR; + if (p->n_lval == -1 && shape == SMONE) + return SRDIR; + if (p->n_lval > -257 && p->n_lval < 256 && + shape == SCCON) + return SRDIR; + if (p->n_lval > -32769 && p->n_lval < 32768 && + shape == SSCON) + return SRDIR; + return SRNOPE; + + case SSOREG: /* non-indexed OREG */ + if (o == OREG && !R2TEST(p->n_rval)) + return SRDIR; + return SRNOPE; + + default: + return (special(p, shape)); + } + } + + if (shape & SANY) + return SRDIR; + + if ((shape&INTEMP) && shtemp(p)) /* XXX remove? */ + return SRDIR; + + if ((shape&SWADD) && (o==NAME||o==OREG)) + if (BYTEOFF(p->n_lval)) + return SRNOPE; + + switch (o) { + + case NAME: + if (shape & SNAME) + return SRDIR; + break; + + case ICON: + if (shape & SCON) + return SRDIR; + break; + + case FLD: + if (shape & SFLD) { + int sh; + + if ((sh = flshape(p->n_left)) == SRNOPE) + return sh; + /* it is a FIELD shape; make side-effects */ + /* XXX - this will not work for multi-matches */ + o = p->n_rval; + fldsz = UPKFSZ(o); +# ifdef RTOLBYTES + fldshf = UPKFOFF(o); +# else + fldshf = SZINT - fldsz - UPKFOFF(o); +# endif + return sh; + } + break; + + case CCODES: + if (shape & SCC) + return SRDIR; + break; + + case REG: + case TEMP: + mask = PCLASS(p); + if (shape & mask) + return SRDIR; + break; + + case OREG: + if (shape & SOREG) + return SRDIR; + break; + + case UMUL: + if (shumul(p->n_left) & shape) + return SROREG; /* Calls offstar to traverse down */ + break; + + } + return SRNOPE; +} + +/* + * does the type t match tword + */ +int +ttype(TWORD t, int tword) +{ + if (tword & TANY) + return(1); + +#ifdef PCC_DEBUG + if (t2debug) + printf("ttype(%o, %o)\n", t, tword); +#endif + if (ISPTR(t) && ISFTN(DECREF(t)) && (tword & TFTN)) { + /* For funny function pointers */ + return 1; + } + if (ISPTR(t) && (tword&TPTRTO)) { + do { + t = DECREF(t); + } while (ISARY(t)); + /* arrays that are left are usually only + * in structure references... + */ + return (ttype(t, tword&(~TPTRTO))); + } + if (t != BTYPE(t)) + return (tword & TPOINT); /* TPOINT means not simple! */ + if (tword & TPTRTO) + return(0); + + switch (t) { + case CHAR: + return( tword & TCHAR ); + case SHORT: + return( tword & TSHORT ); + case STRTY: + case UNIONTY: + return( tword & TSTRUCT ); + case INT: + return( tword & TINT ); + case UNSIGNED: + return( tword & TUNSIGNED ); + case USHORT: + return( tword & TUSHORT ); + case UCHAR: + return( tword & TUCHAR ); + case ULONG: + return( tword & TULONG ); + case LONG: + return( tword & TLONG ); + case LONGLONG: + return( tword & TLONGLONG ); + case ULONGLONG: + return( tword & TULONGLONG ); + case FLOAT: + return( tword & TFLOAT ); + case DOUBLE: + return( tword & TDOUBLE ); + case LDOUBLE: + return( tword & TLDOUBLE ); + } + + return(0); +} + +/* + * generate code by interpreting table entry + */ +void +expand(NODE *p, int cookie, char *cp) +{ + CONSZ val; + + for( ; *cp; ++cp ){ + switch( *cp ){ + + default: + PUTCHAR( *cp ); + continue; /* this is the usual case... */ + + case 'Z': /* special machine dependent operations */ + zzzcode( p, *++cp ); + continue; + + case 'F': /* this line deleted if FOREFF is active */ + if( cookie & FOREFF ) while( *++cp != '\n' ) ; /* VOID */ + continue; + + case 'S': /* field size */ + printf( "%d", fldsz ); + continue; + + case 'H': /* field shift */ + printf( "%d", fldshf ); + continue; + + case 'M': /* field mask */ + case 'N': /* complement of field mask */ + val = 1; + val <<= fldsz; + --val; + val <<= fldshf; + adrcon( *cp=='M' ? val : ~val ); + continue; + + case 'L': /* output special label field */ + if (*++cp == 'C') + printf(LABFMT, p->n_label); + else + printf(LABFMT, (int)getlr(p,*cp)->n_lval); + continue; + + case 'O': /* opcode string */ + hopcode( *++cp, p->n_op ); + continue; + + case 'B': /* byte offset in word */ + val = getlr(p,*++cp)->n_lval; + val = BYTEOFF(val); + printf( CONFMT, val ); + continue; + + case 'C': /* for constant value only */ + conput(stdout, getlr( p, *++cp ) ); + continue; + + case 'I': /* in instruction */ + insput( getlr( p, *++cp ) ); + continue; + + case 'A': /* address of */ + adrput(stdout, getlr( p, *++cp ) ); + continue; + + case 'U': /* for upper half of address, only */ + upput(getlr(p, *++cp), SZLONG); + continue; + + } + + } + + } + +NODE resc[4]; + +NODE * +getlr(NODE *p, int c) +{ + NODE *q; + + /* return the pointer to the left or right side of p, or p itself, + depending on the optype of p */ + + switch (c) { + + case '1': + case '2': + case '3': + case 'D': + if (c == 'D') + c = 0; + else + c -= '0'; + q = &resc[c]; + q->n_op = REG; + q->n_type = p->n_type; /* XXX should be correct type */ + q->n_rval = DECRA(p->n_reg, c); + q->n_su = p->n_su; + return q; + + case 'L': + return( optype( p->n_op ) == LTYPE ? p : p->n_left ); + + case 'R': + return( optype( p->n_op ) != BITYPE ? p : p->n_right ); + + } + cerror( "bad getlr: %c", c ); + /* NOTREACHED */ + return NULL; +} + +static char *tarr[] = { + "CHAR", "SHORT", "INT", "LONG", "FLOAT", "DOUBLE", "POINT", "UCHAR", + "USHORT", "UINT", "ULONG", "PTRTO", "ANY", "STRUCT", "LONGLONG", + "ULONGLONG", +}; + +void +prttype(int t) +{ + int i, gone = 0; + + for (i = 0; i < 16; i++) + if ((t >> i) & 1) { + if (gone) putchar('|'); + gone++; + printf("%s", tarr[i]); + } +} + + +#ifdef PCC_DEBUG +#define F2DEBUG(x) if (f2debug) printf x +#define F2WALK(x) if (f2debug) fwalk(x, e2print, 0) +#else +#define F2DEBUG(x) +#define F2WALK(x) +#endif + +/* + * Convert a node to REG or OREG. + * Shape is register class where we want the result. + * Returns register class if register nodes. + * If w is: (should be shapes) + * - LREG - result in register, call geninsn(). + * - LOREG - create OREG; call offstar(). + * - 0 - clear su, walk down. + */ +static int +swmatch(NODE *p, int shape, int w) +{ + int rv = 0; + + switch (w) { + case LREG: + rv = geninsn(p, shape); + break; + + case LOREG: + /* should be here only if op == UMUL */ + if (p->n_op != UMUL && p->n_op != FLD) + comperr("swmatch %p", p); + if (p->n_op == FLD) { + offstar(p->n_left->n_left, shape); + p->n_left->n_su = 0; + } else + offstar(p->n_left, shape); + p->n_su = 0; + rv = ffs(shape)-1; + break; + + case 0: + if (optype(p->n_op) == BITYPE) + swmatch(p->n_right, 0, 0); + if (optype(p->n_op) != LTYPE) + swmatch(p->n_left, 0, 0); + p->n_su = 0; + } + return rv; + +} + +/* + * Help routines for find*() functions. + * If the node will be a REG node and it will be rewritten in the + * instruction, ask for it to be put in a register. + */ +static int +chcheck(NODE *p, int shape, int rew) +{ + int sh, sha; + + sha = shape; + if (shape & SPECIAL) + shape = 0; + + switch ((sh = tshape(p, sha))) { + case SRNOPE: + if (shape & INREGS) + sh = SRREG; + break; + + case SROREG: + case SRDIR: + if (rew == 0) + break; + if (shape & INREGS) + sh = SRREG; + else + sh = SRNOPE; + break; + } + return sh; +} + +/* + * Check how to walk further down. + * Merge with swmatch()? + * sh - shape for return value (register class). + * p - node (for this leg) + * shape - given shape for this leg + * cookie - cookie given for parent node + * rv - switch key for traversing down + * returns register class. + */ +static int +shswitch(int sh, NODE *p, int shape, int cookie, int rew, int go) +{ + int lsh; + + switch (go) { + case SRDIR: /* direct match, just clear su */ + (void)swmatch(p, 0, 0); + break; + + case SROREG: /* call offstar to prepare for OREG conversion */ + (void)swmatch(p, shape, LOREG); + break; + + case SRREG: /* call geninsn() to get value into register */ + lsh = shape & INREGS; + if (rew && cookie != FOREFF) + lsh &= (cookie & INREGS); + lsh = swmatch(p, lsh, LREG); + if (rew) + sh = lsh; + break; + } + return sh; +} + +/* + * Find the best instruction to evaluate the given tree. + * Best is to match both subnodes directly, second-best is if + * subnodes must be evaluated into OREGs, thereafter if nodes + * must be put into registers. + * Whether 2-op instructions or 3-op is preferred is depending on in + * which order they are found in the table. + * mtchno is set to the count of regs needed for its legs. + */ +int +findops(NODE *p, int cookie) +{ + extern int *qtable[]; + struct optab *q, *qq = NULL; + int i, shl, shr, *ixp, sh; + int lvl = 10, idx = 0, gol = 0, gor = 0; + NODE *l, *r; + + F2DEBUG(("findops node %p (%s)\n", p, prcook(cookie))); + F2WALK(p); + + ixp = qtable[p->n_op]; + l = getlr(p, 'L'); + r = getlr(p, 'R'); + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("findop: ixp %d\n", ixp[i])); + if (ttype(l->n_type, q->ltype) == 0 || + ttype(r->n_type, q->rtype) == 0) + continue; /* Types must be correct */ + + if ((cookie & q->visit) == 0) + continue; /* must get a result */ + + F2DEBUG(("findop got types\n")); + + if ((shl = chcheck(l, q->lshape, q->rewrite & RLEFT)) == SRNOPE) + continue; + + F2DEBUG(("findop lshape %d\n", shl)); + F2WALK(l); + + if ((shr = chcheck(r, q->rshape, q->rewrite & RRIGHT))== SRNOPE) continue; + + F2DEBUG(("findop rshape %d\n", shr)); + F2WALK(r); + + if (q->needs & REWRITE) + break; /* Done here */ + + if (lvl <= (shl + shr)) + continue; + lvl = shl + shr; + qq = q; + idx = ixp[i]; + gol = shl; + gor = shr; + } + if (lvl == 10) { + F2DEBUG(("findops failed\n")); + if (setbin(p)) + return FRETRY; + return FFAIL; + } + + F2DEBUG(("findops entry %d(%s,%s)\n", idx, srtyp[gol], srtyp[gor])); + + sh = -1; + + sh = shswitch(sh, p->n_left, qq->lshape, cookie, + qq->rewrite & RLEFT, gol); + sh = shswitch(sh, p->n_right, qq->rshape, cookie, + qq->rewrite & RRIGHT, gor); + + if (sh == -1) { + if (cookie == FOREFF) + sh = 0; + else + sh = ffs(cookie & qq->visit & INREGS)-1; + } + F2DEBUG(("findops: node %p (%s)\n", p, prcook(1 << sh))); + p->n_su = MKIDX(idx, 0); + SCLASS(p->n_su, sh); + return sh; +} + +/* + * Find the best relation op for matching the two trees it has. + * This is a sub-version of the function findops() above. + * The instruction with the lowest grading is emitted. + * + * Level assignment for priority: + * left right prio + * - - - + * direct direct 1 + * direct OREG 2 # make oreg + * OREG direct 2 # make oreg + * OREG OREG 2 # make both oreg + * direct REG 3 # put in reg + * OREG REG 3 # put in reg, make oreg + * REG direct 3 # put in reg + * REG OREG 3 # put in reg, make oreg + * REG REG 4 # put both in reg + */ +int +relops(NODE *p) +{ + extern int *qtable[]; + struct optab *q; + int i, shl = 0, shr = 0; + NODE *l, *r; + int *ixp, idx = 0; + int lvl = 10, gol = 0, gor = 0; + + F2DEBUG(("relops tree:\n")); + F2WALK(p); + + l = getlr(p, 'L'); + r = getlr(p, 'R'); + ixp = qtable[p->n_op]; + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("relops: ixp %d\n", ixp[i])); + if (ttype(l->n_type, q->ltype) == 0 || + ttype(r->n_type, q->rtype) == 0) + continue; /* Types must be correct */ + + F2DEBUG(("relops got types\n")); + if ((shl = chcheck(l, q->lshape, 0)) == SRNOPE) + continue; + F2DEBUG(("relops lshape %d\n", shl)); + F2WALK(p); + if ((shr = chcheck(r, q->rshape, 0)) == SRNOPE) + continue; + F2DEBUG(("relops rshape %d\n", shr)); + F2WALK(p); + if (q->needs & REWRITE) + break; /* Done here */ + + if (lvl <= (shl + shr)) + continue; + lvl = shl + shr; + idx = ixp[i]; + gol = shl; + gor = shr; + } + if (lvl == 10) { + F2DEBUG(("relops failed\n")); + if (setbin(p)) + return FRETRY; + return FFAIL; + } + F2DEBUG(("relops entry %d(%s %s)\n", idx, srtyp[gol], srtyp[gor])); + + q = &table[idx]; + + (void)shswitch(-1, p->n_left, q->lshape, FORCC, + q->rewrite & RLEFT, gol); + + (void)shswitch(-1, p->n_right, q->rshape, FORCC, + q->rewrite & RRIGHT, gor); + + F2DEBUG(("findops: node %p\n", p)); + p->n_su = MKIDX(idx, 0); + SCLASS(p->n_su, CLASSA); /* XXX */ + return 0; +} + +/* + * Find a matching assign op. + * + * Level assignment for priority: + * left right prio + * - - - + * direct direct 1 + * direct REG 2 + * direct OREG 3 + * OREG direct 4 + * OREG REG 5 + * OREG OREG 6 + */ +int +findasg(NODE *p, int cookie) +{ + extern int *qtable[]; + struct optab *q; + int i, sh, shl, shr, lvl = 10; + NODE *l, *r; + int *ixp; + struct optab *qq = NULL; /* XXX gcc */ + int idx = 0, gol = 0, gor = 0; + + shl = shr = 0; + + F2DEBUG(("findasg tree: %s\n", prcook(cookie))); + F2WALK(p); + + ixp = qtable[p->n_op]; + l = getlr(p, 'L'); + r = getlr(p, 'R'); + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("asgop: ixp %d\n", ixp[i])); + if (ttype(l->n_type, q->ltype) == 0 || + ttype(r->n_type, q->rtype) == 0) + continue; /* Types must be correct */ + + if ((cookie & q->visit) == 0) + continue; /* must get a result */ + + F2DEBUG(("asgop got types\n")); + if ((shl = tshape(l, q->lshape)) == SRNOPE) + continue; + + if (shl == SRREG) + continue; + + F2DEBUG(("asgop lshape %d\n", shl)); + F2WALK(l); + + if ((shr = chcheck(r, q->rshape, q->rewrite & RRIGHT))== SRNOPE) + continue; + + F2DEBUG(("asgop rshape %d\n", shr)); + F2WALK(r); + if (q->needs & REWRITE) + break; /* Done here */ + + if (lvl <= (shl + shr)) + continue; + + lvl = shl + shr; + qq = q; + idx = ixp[i]; + gol = shl; + gor = shr; + } + + if (lvl == 10) { + F2DEBUG(("findasg failed\n")); + if (setasg(p, cookie)) + return FRETRY; + return FFAIL; + } + F2DEBUG(("findasg entry %d(%s,%s)\n", idx, srtyp[gol], srtyp[gor])); + + sh = -1; + sh = shswitch(sh, p->n_left, qq->lshape, cookie, + qq->rewrite & RLEFT, gol); + + sh = shswitch(sh, p->n_right, qq->rshape, cookie, + qq->rewrite & RRIGHT, gor); + + if (sh == -1) { + if (cookie == FOREFF) + sh = 0; + else + sh = ffs(cookie & qq->visit & INREGS)-1; + } + F2DEBUG(("findasg: node %p class %d\n", p, sh)); + + p->n_su = MKIDX(idx, 0); + SCLASS(p->n_su, sh); + + return sh; +} + +/* + * Search for an UMUL table entry that can turn an indirect node into + * a move from an OREG. + */ +int +findumul(NODE *p, int cookie) +{ + extern int *qtable[]; + struct optab *q = NULL; /* XXX gcc */ + int i, shl = 0, shr = 0, sh; + int *ixp; + + F2DEBUG(("findumul p %p (%s)\n", p, prcook(cookie))); + F2WALK(p); + + ixp = qtable[p->n_op]; + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("findumul: ixp %d\n", ixp[i])); + if ((q->visit & cookie) == 0) + continue; /* wrong registers */ + + if (ttype(p->n_type, q->rtype) == 0) + continue; /* Types must be correct */ + + + F2DEBUG(("findumul got types, rshape %s\n", prcook(q->rshape))); + /* + * Try to create an OREG of the node. + * Fake left even though it's right node, + * to be sure of conversion if going down left. + */ + if ((shl = chcheck(p, q->rshape, 0)) == SRNOPE) + continue; + + shr = 0; + + if (q->needs & REWRITE) + break; /* Done here */ + + F2DEBUG(("findumul got shape %s\n", srtyp[shl])); + + break; /* XXX search better matches */ + } + if (ixp[i] < 0) { + F2DEBUG(("findumul failed\n")); + if (setuni(p, cookie)) + return FRETRY; + return FFAIL; + } + F2DEBUG(("findumul entry %d(%s %s)\n", ixp[i], srtyp[shl], srtyp[shr])); + + sh = shswitch(-1, p, q->rshape, cookie, q->rewrite & RLEFT, shl); + if (sh == -1) + sh = ffs(cookie & q->visit & INREGS)-1; + + F2DEBUG(("findumul: node %p (%s)\n", p, prcook(1 << sh))); + p->n_su = MKIDX(ixp[i], 0); + SCLASS(p->n_su, sh); + return sh; +} + +/* + * Find a leaf type node that puts the value into a register. + */ +int +findleaf(NODE *p, int cookie) +{ + extern int *qtable[]; + struct optab *q = NULL; /* XXX gcc */ + int i, sh; + int *ixp; + + F2DEBUG(("findleaf p %p (%s)\n", p, prcook(cookie))); + F2WALK(p); + + ixp = qtable[p->n_op]; + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("findleaf: ixp %d\n", ixp[i])); + if ((q->visit & cookie) == 0) + continue; /* wrong registers */ + + if (ttype(p->n_type, q->rtype) == 0 || + ttype(p->n_type, q->ltype) == 0) + continue; /* Types must be correct */ + + F2DEBUG(("findleaf got types, rshape %s\n", prcook(q->rshape))); + + if (chcheck(p, q->rshape, 0) != SRDIR) + continue; + + if (q->needs & REWRITE) + break; /* Done here */ + + break; + } + if (ixp[i] < 0) { + F2DEBUG(("findleaf failed\n")); + if (setuni(p, cookie)) + return FRETRY; + return FFAIL; + } + F2DEBUG(("findleaf entry %d\n", ixp[i])); + + sh = ffs(cookie & q->visit & INREGS)-1; + F2DEBUG(("findleaf: node %p (%s)\n", p, prcook(1 << sh))); + p->n_su = MKIDX(ixp[i], 0); + SCLASS(p->n_su, sh); + return sh; +} + +/* + * Find a UNARY op that satisfy the needs. + * For now, the destination is always a register. + * Both source and dest types must match, but only source (left) + * shape is of interest. + */ +int +finduni(NODE *p, int cookie) +{ + extern int *qtable[]; + struct optab *q; + NODE *l, *r; + int i, shl = 0, num = 4; + int *ixp, idx = 0; + int sh; + + F2DEBUG(("finduni tree: %s\n", prcook(cookie))); + F2WALK(p); + + l = getlr(p, 'L'); + if (p->n_op == CALL || p->n_op == FORTCALL || p->n_op == STCALL) + r = p; + else + r = getlr(p, 'R'); + ixp = qtable[p->n_op]; + for (i = 0; ixp[i] >= 0; i++) { + q = &table[ixp[i]]; + + F2DEBUG(("finduni: ixp %d\n", ixp[i])); + if (ttype(l->n_type, q->ltype) == 0) + continue; /* Type must be correct */ + + F2DEBUG(("finduni got left type\n")); + if (ttype(r->n_type, q->rtype) == 0) + continue; /* Type must be correct */ + + F2DEBUG(("finduni got types\n")); + if ((shl = chcheck(l, q->lshape, q->rewrite & RLEFT)) == SRNOPE) + continue; + + F2DEBUG(("finduni got shapes %d\n", shl)); + + if ((cookie & q->visit) == 0) /* check correct return value */ + continue; /* XXX - should check needs */ + + /* avoid clobbering of longlived regs */ + /* let register allocator coalesce */ + if ((q->rewrite & RLEFT) && (shl == SRDIR) /* && isreg(l) */) + shl = SRREG; + + F2DEBUG(("finduni got cookie\n")); + if (q->needs & REWRITE) + break; /* Done here */ + + if (shl >= num) + continue; + num = shl; + idx = ixp[i]; + + if (shl == SRDIR) + break; + } + + if (num == 4) { + F2DEBUG(("finduni failed\n")); + } else + F2DEBUG(("finduni entry %d(%s)\n", idx, srtyp[num])); + + if (num == 4) { + if (setuni(p, cookie)) + return FRETRY; + return FFAIL; + } + q = &table[idx]; + + sh = shswitch(-1, p->n_left, q->lshape, cookie, + q->rewrite & RLEFT, num); + if (sh == -1) + sh = ffs(cookie & q->visit & INREGS)-1; + if (sh == -1) + sh = 0; + + F2DEBUG(("finduni: node %p (%s)\n", p, prcook(1 << sh))); + p->n_su = MKIDX(idx, 0); + SCLASS(p->n_su, sh); + return sh; +} diff --git a/usr.bin/pcc/mip/mkext.c b/usr.bin/pcc/mip/mkext.c new file mode 100644 index 00000000000..347dd8ee43f --- /dev/null +++ b/usr.bin/pcc/mip/mkext.c @@ -0,0 +1,318 @@ + +/* + * Generate defines for the needed hardops. + */ +#include "pass2.h" + +#include <string.h> + +int chkop[MAXOP]; + +void mktables(void); + +char *cname = "external.c"; +char *hname = "external.h"; +FILE *fc, *fh; + +/* + * masks for matching dope with shapes + */ +int mamask[] = { + SIMPFLG, /* OPSIMP */ + SIMPFLG|ASGFLG, /* ASG OPSIMP */ + COMMFLG, /* OPCOMM */ + COMMFLG|ASGFLG, /* ASG OPCOMM */ + MULFLG, /* OPMUL */ + MULFLG|ASGFLG, /* ASG OPMUL */ + DIVFLG, /* OPDIV */ + DIVFLG|ASGFLG, /* ASG OPDIV */ + UTYPE, /* OPUNARY */ + TYFLG, /* ASG OPUNARY is senseless */ + LTYPE, /* OPLEAF */ + TYFLG, /* ASG OPLEAF is senseless */ + 0, /* OPANY */ + ASGOPFLG|ASGFLG, /* ASG OPANY */ + LOGFLG, /* OPLOG */ + TYFLG, /* ASG OPLOG is senseless */ + FLOFLG, /* OPFLOAT */ + FLOFLG|ASGFLG, /* ASG OPFLOAT */ + SHFFLG, /* OPSHFT */ + SHFFLG|ASGFLG, /* ASG OPSHIFT */ + SPFLG, /* OPLTYPE */ + TYFLG, /* ASG OPLTYPE is senseless */ + }; + + +struct checks { + int op, type; + char *name; +} checks[] = { + { MUL, TLONGLONG, "SMULLL", }, + { DIV, TLONGLONG, "SDIVLL", }, + { MOD, TLONGLONG, "SMODLL", }, + { PLUS, TLONGLONG, "SPLUSLL", }, + { MINUS, TLONGLONG, "SMINUSLL", }, + { MUL, TULONGLONG, "UMULLL", }, + { DIV, TULONGLONG, "UDIVLL", }, + { MOD, TULONGLONG, "UMODLL", }, + { PLUS, TULONGLONG, "UPLUSLL", }, + { MINUS, TULONGLONG, "UMINUSLL", }, + { 0, 0, 0, }, +}; + +int rstatus[] = { RSTATUS }; +int roverlay[MAXREGS][MAXREGS] = { ROVERLAP }; +int regclassmap[NUMCLASS][MAXREGS]; + +static void +compl(struct optab *q, char *str) +{ + printf("table entry %td, op %s: %s\n", q - table, opst[q->op], str); +} + +int +main(int argc, char *argv[]) +{ + struct optab *q; + struct checks *ch; + int i, j, areg, breg, creg, dreg, mx; + char *bitary; + int bitsz, rval, nelem; + + mkdope(); + + for (q = table; q->op != FREE; q++) { + if (q->op >= OPSIMP) + continue; + if ((q->ltype & TLONGLONG) && + (q->rtype & TLONGLONG)) + chkop[q->op] |= TLONGLONG; + if ((q->ltype & TULONGLONG) && + (q->rtype & TULONGLONG)) + chkop[q->op] |= TULONGLONG; + } + if ((fc = fopen(cname, "w")) == NULL) { + perror("open cfile"); + return(1); + } + if ((fh = fopen(hname, "w")) == NULL) { + perror("open hfile"); + return(1); + } + for (ch = checks; ch->op != 0; ch++) { + if ((chkop[ch->op] & ch->type) == 0) + fprintf(fh, "#define NEED_%s\n", ch->name); + } + + fprintf(fc, "#include \"pass2.h\"\n"); + /* create fast-lookup tables */ + mktables(); + + /* create efficient bitset sizes */ + if (sizeof(long) == 8) { /* 64-bit arch */ + bitary = "long"; + bitsz = 64; + } else { + bitary = "int"; + bitsz = sizeof(int) == 4 ? 32 : 16; + } + fprintf(fh, "#define NUMBITS %d\n", bitsz); + fprintf(fh, "#define BITSET(arr, bit) " + "(arr[bit/NUMBITS] |= (1 << (bit & (NUMBITS-1))))\n"); + fprintf(fh, "#define BITCLEAR(arr, bit) " + "(arr[bit/NUMBITS] &= ~(1 << (bit & (NUMBITS-1))))\n"); + fprintf(fh, "#define TESTBIT(arr, bit) " + "(arr[bit/NUMBITS] & (1 << (bit & (NUMBITS-1))))\n"); + fprintf(fh, "typedef %s bittype;\n", bitary); + + /* register class definitions, used by graph-coloring */ + /* TODO */ + + /* Sanity-check the table */ + rval = 0; + for (q = table; q->op != FREE; q++) { + if (q->op == ASSIGN) { +#define F(x) (q->visit & x && q->rewrite & (RLEFT|RRIGHT) && \ + q->lshape & ~x && q->rshape & ~x) + if (F(INAREG) || F(INBREG) || F(INCREG) || F(INDREG)) { + compl(q, "may match without result register"); + rval++; + } +#undef F + if ((q->visit & INREGS) && q->rewrite != RDEST) { + compl(q, "ASSIGN reclaim must be RDEST"); + rval++; + } + } + if (q->rewrite & (RESC1|RESC2|RESC1) && q->visit & FOREFF) + compl(q, "FOREFF may cause reclaim of wrong class"); + } + + /* print out list of scratched and permanent registers */ + fprintf(fh, "extern int tempregs[], permregs[];\n"); + fprintf(fc, "int tempregs[] = { "); + for (i = j = 0; i < MAXREGS; i++) + if (rstatus[i] & TEMPREG) + fprintf(fc, "%d, ", i), j++; + fprintf(fc, "-1 };\n"); + fprintf(fh, "#define NTEMPREG %d\n", j+1); + fprintf(fh, "#define FREGS %d\n", j); /* XXX - to die */ + fprintf(fc, "int permregs[] = { "); + for (i = j = 0; i < MAXREGS; i++) + if (rstatus[i] & PERMREG) + fprintf(fc, "%d, ", i), j++; + fprintf(fc, "-1 };\n"); + fprintf(fh, "#define NPERMREG %d\n", j+1); + + /* + * The register allocator uses bitmasks of registers for each class. + */ + areg = breg = creg = dreg = 0; + for (i = 0; i < MAXREGS; i++) { + regclassmap[0][i] = regclassmap[1][i] = regclassmap[2][i] = + regclassmap[3][i] = -1; + if (rstatus[i] & SAREG) regclassmap[0][i] = areg++; + if (rstatus[i] & SBREG) regclassmap[1][i] = breg++; + if (rstatus[i] & SCREG) regclassmap[2][i] = creg++; + if (rstatus[i] & SDREG) regclassmap[3][i] = dreg++; + } + fprintf(fh, "#define AREGCNT %d\n", areg); + fprintf(fh, "#define BREGCNT %d\n", breg); + fprintf(fh, "#define CREGCNT %d\n", creg); + fprintf(fh, "#define DREGCNT %d\n", dreg); + if (areg > bitsz) + printf("%d regs in class A (max %d)\n", areg, bitsz), rval++; + if (breg > bitsz) + printf("%d regs in class B (max %d)\n", breg, bitsz), rval++; + if (creg > bitsz) + printf("%d regs in class C (max %d)\n", creg, bitsz), rval++; + if (dreg > bitsz) + printf("%d regs in class D (max %d)\n", dreg, bitsz), rval++; + + fprintf(fc, "static int amap[MAXREGS][NUMCLASS] = {\n"); + for (i = 0; i < MAXREGS; i++) { + int ba, bb, bc, bd, r; + ba = bb = bc = bd = 0; + if (rstatus[i] & SAREG) ba = (1 << regclassmap[0][i]); + if (rstatus[i] & SBREG) bb = (1 << regclassmap[1][i]); + if (rstatus[i] & SCREG) bc = (1 << regclassmap[2][i]); + if (rstatus[i] & SDREG) bd = (1 << regclassmap[3][i]); + for (j = 0; roverlay[i][j] >= 0; j++) { + r = roverlay[i][j]; + if (rstatus[r] & SAREG) + ba |= (1 << regclassmap[0][r]); + if (rstatus[r] & SBREG) + bb |= (1 << regclassmap[1][r]); + if (rstatus[r] & SCREG) + bc |= (1 << regclassmap[2][r]); + if (rstatus[r] & SDREG) + bd |= (1 << regclassmap[3][r]); + } + fprintf(fc, "\t{ 0x%x,0x%x,0x%x,0x%x },\n", ba, bb, bc, bd); + } + fprintf(fc, "};\n"); + + fprintf(fh, "int aliasmap(int class, int regnum);\n"); + fprintf(fc, "int\naliasmap(int class, int regnum)\n{\n"); + fprintf(fc, " return amap[regnum][class-1];\n}\n"); + + /* routines to convert back from color to regnum */ + mx = areg; + if (breg > mx) mx = breg; + if (creg > mx) mx = creg; + if (dreg > mx) mx = dreg; + fprintf(fc, "static int rmap[NUMCLASS][%d] = {\n", mx); + for (j = 0; j < NUMCLASS; j++) { + int cl = (1 << (j+1)); + fprintf(fc, "\t{ "); + for (i = 0; i < MAXREGS; i++) + if (rstatus[i] & cl) fprintf(fc, "%d, ", i); + fprintf(fc, "},\n"); + } + fprintf(fc, "};\n\n"); + + fprintf(fh, "int color2reg(int color, int class);\n"); + fprintf(fc, "int\ncolor2reg(int color, int class)\n{\n"); + fprintf(fc, " return rmap[class-1][color];\n}\n"); + + /* used by register allocator */ + fprintf(fc, "int regK[] = { 0, %d, %d, %d, %d };\n", + areg, breg, creg, dreg); + fprintf(fc, "int\nclassmask(int class)\n{\n"); + fprintf(fc, "\tif(class == CLASSA) return 0x%x;\n", (1 << areg)-1); + fprintf(fc, "\tif(class == CLASSB) return 0x%x;\n", (1 << breg)-1); + fprintf(fc, "\tif(class == CLASSC) return 0x%x;\n", (1 << creg)-1); + fprintf(fc, "\treturn 0x%x;\n}\n", (1 << dreg)-1); + + fprintf(fh, "int interferes(int reg1, int reg2);\n"); + nelem = (MAXREGS+bitsz-1)/bitsz; + fprintf(fc, "static bittype ovlarr[MAXREGS][%d] = {\n", nelem); + for (i = 0; i < MAXREGS; i++) { + int el[10]; + memset(el, 0, sizeof(el)); + el[i/bitsz] = 1 << (i % bitsz); + for (j = 0; roverlay[i][j] >= 0; j++) { + int k = roverlay[i][j]; + el[k/bitsz] |= (1 << (k % bitsz)); + } + fprintf(fc, "{ "); + for (j = 0; j < MAXREGS; j += bitsz) + fprintf(fc, "0x%x, ", el[j/bitsz]); + fprintf(fc, " },\n"); + } + fprintf(fc, "};\n"); + + fprintf(fc, "int\ninterferes(int reg1, int reg2)\n{\n"); + fprintf(fc, "return TESTBIT(ovlarr[reg1], reg2);\n}\n"); + fclose(fc); + fclose(fh); + return rval; +} + +#define P(x) fprintf x + +void +mktables() +{ + struct optab *op; + int mxalen = 0, curalen; + int i; + +// P((fc, "#include \"pass2.h\"\n\n")); + for (i = 0; i <= MAXOP; i++) { + curalen = 0; + P((fc, "static int op%d[] = { ", i)); + if (dope[i] != 0) + for (op = table; op->op != FREE; op++) { + if (op->op < OPSIMP) { + if (op->op == i) { + P((fc, "%td, ", op - table)); + curalen++; + } + } else { + int opmtemp; + if ((opmtemp=mamask[op->op - OPSIMP])&SPFLG) { + if (i==NAME || i==ICON || i==TEMP || + i==OREG || i == REG) { + P((fc, "%td, ", op - table)); + curalen++; + } + } else if ((dope[i]&(opmtemp|ASGFLG))==opmtemp){ + P((fc, "%td, ", op - table)); + curalen++; + } + } + } + if (curalen > mxalen) + mxalen = curalen; + P((fc, "-1 };\n")); + } + P((fc, "\n")); + + P((fc, "int *qtable[] = { \n")); + for (i = 0; i <= MAXOP; i++) { + P((fc, " op%d,\n", i)); + } + P((fc, "};\n")); + P((fh, "#define MAXOPLEN %d\n", mxalen+1)); +} diff --git a/usr.bin/pcc/mip/node.h b/usr.bin/pcc/mip/node.h new file mode 100644 index 00000000000..3d42df7500c --- /dev/null +++ b/usr.bin/pcc/mip/node.h @@ -0,0 +1,194 @@ +/* $Id: node.h,v 1.1 2007/09/15 18:12:36 otto 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. + */ + +/* + * The node structure is the basic element in the compiler. + * Depending on the operator, it may be one of several types. + * + * This is rewritten to be a struct instead of a union as it + * was in the old compiler. + */ +typedef unsigned int TWORD; +#define NIL (NODE *)0 + +struct symtab; +struct suedef; +struct regw; + +typedef struct node { + struct node *next; + int n_op; + union { + int _reg; + struct regw *_regw; + } n_3; +#define n_reg n_3._reg +#define n_regw n_3._regw + TWORD n_type; + TWORD n_qual; + int n_su; + union { + char * _name; + int _stsize; + union dimfun *_df; + } n_5; + union { + int _label; + int _stalign; + struct suedef *_sue; + } n_6; + union { + struct { + union { + struct node *_left; + CONSZ _lval; +#ifdef SPECIAL_INTEGERS + SPECLVAL _slval; +#endif + } n_l; + union { + struct node *_right; + int _rval; + struct symtab *_sp; + } n_r; + } n_u; + long double _dcon; + } n_f; +} NODE; + +#define n_name n_5._name +#define n_stsize n_5._stsize +#define n_df n_5._df + +#define n_label n_6._label +#define n_stalign n_6._stalign +#define n_sue n_6._sue + +#define n_left n_f.n_u.n_l._left +#define n_lval n_f.n_u.n_l._lval +#define n_slval n_f.n_u.n_l._slval +#define n_right n_f.n_u.n_r._right +#define n_rval n_f.n_u.n_r._rval +#define n_sp n_f.n_u.n_r._sp +#define n_dcon n_f._dcon + +/* + * Node types. + * + * MAXOP is the highest number used by the backend. + */ + +#define FREE 1 +/* + * Value nodes. + */ +#define NAME 2 +#define ICON 4 +#define FCON 5 +#define REG 6 +#define OREG 7 +#define TEMP 8 +#define MOVE 9 /* Special reg-reg move node */ + +/* + * Arithmetic nodes. + */ +#define PLUS 10 +#define MINUS 11 +#define DIV 12 +#define MOD 13 +#define MUL 14 + +/* + * Bitwise operations. + */ +#define AND 15 +#define OR 16 +#define ER 17 +#define LS 18 +#define RS 19 +#define COMPL 20 + +#define UMUL 23 +#define UMINUS 24 + +/* + * Logical compare nodes. + */ +#define EQ 25 +#define NE 26 +#define LE 27 +#define LT 28 +#define GE 29 +#define GT 30 +#define ULE 31 +#define ULT 32 +#define UGE 33 +#define UGT 34 + +/* + * Branch nodes. + */ +#define CBRANCH 35 + +/* + * Convert types. + */ +#define FLD 36 +#define SCONV 37 +#define PCONV 38 +#define PMCONV 39 +#define PVCONV 40 + +/* + * Function calls. + */ +#define CALL 41 +#define UCALL 42 +#define FORTCALL 43 +#define UFORTCALL 44 +#define STCALL 45 +#define USTCALL 46 + +/* + * Other used nodes. + */ +#define CCODES 47 +#define CM 48 +#define ASSIGN 49 +#define STASG 50 +#define STARG 51 +#define FORCE 52 +/* #define INIT 53 */ +#define GOTO 54 +#define RETURN 55 +#define STREF 56 +#define FUNARG 57 +#define ADDROF 58 + +#define MAXOP 58 diff --git a/usr.bin/pcc/mip/optim2.c b/usr.bin/pcc/mip/optim2.c new file mode 100644 index 00000000000..fcf367eb412 --- /dev/null +++ b/usr.bin/pcc/mip/optim2.c @@ -0,0 +1,882 @@ +/* $Id: optim2.c,v 1.1 2007/09/15 18:12:36 otto Exp $ */ +/* + * Copyright (c) 2004 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. + */ + +#include "pass2.h" + +#include <string.h> +#include <stdlib.h> + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#define BDEBUG(x) if (b2debug) printf x + +static int dfsnum; + +void saveip(struct interpass *ip); +void deljumps(struct interpass *); +void optdump(struct interpass *ip); +void printip(struct interpass *pole); + +static struct varinfo defsites; +struct interpass *storesave; +static struct interpass_prolog *ipp, *epp; /* prolog/epilog */ + +void bblocks_build(struct interpass *, struct labelinfo *, struct bblockinfo *); +void cfg_build(struct labelinfo *labinfo); +void cfg_dfs(struct basicblock *bb, unsigned int parent, + struct bblockinfo *bbinfo); +void dominators(struct bblockinfo *bbinfo); +struct basicblock * +ancestorwithlowestsemi(struct basicblock *bblock, struct bblockinfo *bbinfo); +void link(struct basicblock *parent, struct basicblock *child); +void computeDF(struct basicblock *bblock, struct bblockinfo *bbinfo); +void findTemps(struct interpass *ip); +void placePhiFunctions(struct bblockinfo *bbinfo); +void remunreach(void); + +struct basicblock bblocks; +int nbblocks; +static struct interpass *cvpole; + +struct addrof { + struct addrof *next; + int tempnum; + int oregoff; +} *otlink; + +static int +getoff(int num) +{ + struct addrof *w; + + for (w = otlink; w; w = w->next) + if (w->tempnum == num) + return w->oregoff; + return 0; +} + +/* + * Use stack argument addresses instead of copying if & is used on a var. + */ +static int +setargs(int tval, struct addrof *w) +{ + struct interpass *ip; + NODE *p; + + ip = DLIST_NEXT(cvpole, qelem); /* PROLOG */ + ip = DLIST_NEXT(ip, qelem); /* first DEFLAB */ + ip = DLIST_NEXT(ip, qelem); /* first NODE */ + for (; ip->type != IP_DEFLAB; ip = DLIST_NEXT(ip, qelem)) { + p = ip->ip_node; +#ifdef PCC_DEBUG + if (p->n_op != ASSIGN || p->n_left->n_op != TEMP) + comperr("temparg"); +#endif + if (p->n_right->n_op != OREG) + continue; /* arg in register */ + if (tval != p->n_left->n_lval) + continue; /* wrong assign */ + w->oregoff = p->n_right->n_lval; + tfree(p); + DLIST_REMOVE(ip, qelem); + return 1; + } + return 0; +} + +/* + * Search for ADDROF elements and, if found, record them. + */ +static void +findaddrof(NODE *p) +{ + struct addrof *w; + + if (p->n_op != ADDROF) + return; + if (getoff(p->n_left->n_lval)) + return; + w = tmpalloc(sizeof(struct addrof)); + w->tempnum = p->n_left->n_lval; + if (setargs(p->n_left->n_lval, w) == 0) + w->oregoff = BITOOR(freetemp(szty(p->n_left->n_type))); + w->next = otlink; + otlink = w; +} + + +/* + * Convert address-taken temps to OREGs. + */ +static void +cvtaddrof(NODE *p) +{ + NODE *l; + int n; + + if (p->n_op != ADDROF && p->n_op != TEMP) + return; + if (p->n_op == TEMP) { + n = getoff(p->n_lval); + if (n == 0) + return; + p->n_op = OREG; + p->n_lval = n; + p->n_rval = FPREG; + } else { + l = p->n_left; + l->n_type = p->n_type; + p->n_right = mklnode(ICON, l->n_lval, 0, l->n_type); + p->n_op = PLUS; + l->n_op = REG; + l->n_lval = 0; + l->n_rval = FPREG; + + } +} + +void +optimize(struct interpass *ipole) +{ + struct interpass *ip; + struct labelinfo labinfo; + struct bblockinfo bbinfo; + + ipp = (struct interpass_prolog *)DLIST_NEXT(ipole, qelem); + epp = (struct interpass_prolog *)DLIST_PREV(ipole, qelem); + + if (b2debug) { + printf("initial links\n"); + printip(ipole); + } + + /* + * Convert ADDROF TEMP to OREGs. + */ + if (xtemps) { + otlink = NULL; + cvpole = ipole; + DLIST_FOREACH(ip, ipole, qelem) { + if (ip->type != IP_NODE) + continue; + walkf(ip->ip_node, findaddrof); + } + if (otlink) { + DLIST_FOREACH(ip, ipole, qelem) { + if (ip->type != IP_NODE) + continue; + walkf(ip->ip_node, cvtaddrof); + } + } + } + + if (xdeljumps) + deljumps(ipole); /* Delete redundant jumps and dead code */ + +#ifdef PCC_DEBUG + if (b2debug) { + printf("links after deljumps\n"); + printip(ipole); + } +#endif + if (xssaflag || xtemps) { + DLIST_INIT(&bblocks, bbelem); + bblocks_build(ipole, &labinfo, &bbinfo); + BDEBUG(("Calling cfg_build\n")); + cfg_build(&labinfo); + } + if (xssaflag) { + BDEBUG(("Calling dominators\n")); + dominators(&bbinfo); + BDEBUG(("Calling computeDF\n")); + computeDF(DLIST_NEXT(&bblocks, bbelem), &bbinfo); + BDEBUG(("Calling remunreach\n")); + remunreach(); +#if 0 + dfg = dfg_build(cfg); + ssa = ssa_build(cfg, dfg); +#endif + } + +#ifdef PCC_DEBUG + if (epp->ipp_regs != 0) + comperr("register error"); +#endif + +#ifdef MYOPTIM + myoptim((struct interpass *)ipp); +#endif +} + +/* + * Delete unused labels, excess of labels, gotos to gotos. + * This routine can be made much more efficient. + */ +void +deljumps(struct interpass *ipole) +{ + struct interpass *ip, *n, *ip2; + int gotone,low, high; + int *lblary, sz, o, i; + + low = ipp->ip_lblnum; + high = epp->ip_lblnum; + +#ifdef notyet + mark = tmpmark(); /* temporary used memory */ +#endif + + sz = (high-low) * sizeof(int); + lblary = tmpalloc(sz); + +again: gotone = 0; + memset(lblary, 0, sz); + + /* refcount and coalesce all labels */ + DLIST_FOREACH(ip, ipole, qelem) { + if (ip->type == IP_DEFLAB) { + n = DLIST_NEXT(ip, qelem); + while (n->type == IP_DEFLAB) { + if (n->type == IP_DEFLAB && + lblary[n->ip_lbl-low] >= 0) + lblary[n->ip_lbl-low] = -ip->ip_lbl; + n = DLIST_NEXT(n, qelem); + } + } + if (ip->type != IP_NODE) + continue; + o = ip->ip_node->n_op; + if (o == GOTO) + i = ip->ip_node->n_left->n_lval; + else if (o == CBRANCH) + i = ip->ip_node->n_right->n_lval; + else + continue; + lblary[i-low] |= 1; + } + + /* delete coalesced/unused labels and rename gotos */ + DLIST_FOREACH(ip, ipole, qelem) { + n = DLIST_NEXT(ip, qelem); + if (n->type == IP_DEFLAB) { + if (lblary[n->ip_lbl-low] <= 0) { + DLIST_REMOVE(n, qelem); + gotone = 1; + } + continue; + } + if (n->type != IP_NODE) + continue; + o = n->ip_node->n_op; + if (o == GOTO) + i = n->ip_node->n_left->n_lval; + else if (o == CBRANCH) + i = n->ip_node->n_right->n_lval; + else + continue; + if (lblary[i-low] < 0) { + if (o == GOTO) + n->ip_node->n_left->n_lval = -lblary[i-low]; + else + n->ip_node->n_right->n_lval = -lblary[i-low]; + } + } + + /* Delete gotos to the next statement */ + DLIST_FOREACH(ip, ipole, qelem) { + n = DLIST_NEXT(ip, qelem); + if (n->type != IP_NODE) + continue; + o = n->ip_node->n_op; + if (o == GOTO) + i = n->ip_node->n_left->n_lval; + else if (o == CBRANCH) + i = n->ip_node->n_right->n_lval; + else + continue; + + ip2 = n; + ip2 = DLIST_NEXT(ip2, qelem); + + if (ip2->type != IP_DEFLAB) + continue; + if (ip2->ip_lbl == i) { + tfree(n->ip_node); + DLIST_REMOVE(n, qelem); + gotone = 1; + } + } + + if (gotone) + goto again; + +#ifdef notyet + tmpfree(mark); +#endif +} + +void +optdump(struct interpass *ip) +{ + static char *nm[] = { "node", "prolog", "newblk", "epilog", "locctr", + "deflab", "defnam", "asm" }; + printf("type %s\n", nm[ip->type-1]); + switch (ip->type) { + case IP_NODE: + fwalk(ip->ip_node, e2print, 0); + break; + case IP_DEFLAB: + printf("label " LABFMT "\n", ip->ip_lbl); + break; + case IP_ASM: + printf(": %s\n", ip->ip_asm); + break; + } +} + +/* + * Build the basic blocks, algorithm 9.1, pp 529 in Compilers. + * + * Also fills the labelinfo struct with information about which bblocks + * that contain which label. + */ + +void +bblocks_build(struct interpass *ipole, struct labelinfo *labinfo, + struct bblockinfo *bbinfo) +{ + struct interpass *ip; + struct basicblock *bb = NULL; + int low, high; + int count = 0; + int i; + + BDEBUG(("bblocks_build (%p, %p)\n", labinfo, bbinfo)); + low = ipp->ip_lblnum; + high = epp->ip_lblnum; + + /* + * First statement is a leader. + * Any statement that is target of a jump is a leader. + * Any statement that immediately follows a jump is a leader. + */ + DLIST_FOREACH(ip, ipole, qelem) { + if (bb == NULL || (ip->type == IP_EPILOG) || + (ip->type == IP_DEFLAB) || (ip->type == IP_DEFNAM)) { + bb = tmpalloc(sizeof(struct basicblock)); + bb->first = ip; + SLIST_INIT(&bb->children); + SLIST_INIT(&bb->parents); + bb->dfnum = 0; + bb->dfparent = 0; + bb->semi = 0; + bb->ancestor = 0; + bb->idom = 0; + bb->samedom = 0; + bb->bucket = NULL; + bb->df = NULL; + bb->dfchildren = NULL; + bb->Aorig = NULL; + bb->Aphi = NULL; + bb->bbnum = count; + DLIST_INSERT_BEFORE(&bblocks, bb, bbelem); + count++; + } + bb->last = ip; + if ((ip->type == IP_NODE) && (ip->ip_node->n_op == GOTO || + ip->ip_node->n_op == CBRANCH)) + bb = NULL; + if (ip->type == IP_PROLOG) + bb = NULL; + } + nbblocks = count; + + if (b2debug) { + printf("Basic blocks in func: %d, low %d, high %d\n", + count, low, high); + DLIST_FOREACH(bb, &bblocks, bbelem) { + printf("bb %p: first %p last %p\n", bb, + bb->first, bb->last); + } + } + + labinfo->low = low; + labinfo->size = high - low + 1; + labinfo->arr = tmpalloc(labinfo->size * sizeof(struct basicblock *)); + for (i = 0; i < labinfo->size; i++) { + labinfo->arr[i] = NULL; + } + + bbinfo->size = count + 1; + bbinfo->arr = tmpalloc(bbinfo->size * sizeof(struct basicblock *)); + for (i = 0; i < bbinfo->size; i++) { + bbinfo->arr[i] = NULL; + } + + /* Build the label table */ + DLIST_FOREACH(bb, &bblocks, bbelem) { + if (bb->first->type == IP_DEFLAB) + labinfo->arr[bb->first->ip_lbl - low] = bb; + } + + if (b2debug) { + printf("Label table:\n"); + for (i = 0; i < labinfo->size; i++) + if (labinfo->arr[i]) + printf("Label %d bblock %p\n", i+low, + labinfo->arr[i]); + } +} + +/* + * Build the control flow graph. + */ + +void +cfg_build(struct labelinfo *labinfo) +{ + /* Child and parent nodes */ + struct cfgnode *cnode; + struct cfgnode *pnode; + struct basicblock *bb; + + DLIST_FOREACH(bb, &bblocks, bbelem) { + + if (bb->first->type == IP_EPILOG) { + break; + } + + cnode = tmpalloc(sizeof(struct cfgnode)); + pnode = tmpalloc(sizeof(struct cfgnode)); + pnode->bblock = bb; + + if ((bb->last->type == IP_NODE) && + (bb->last->ip_node->n_op == GOTO)) { + if (bb->last->ip_node->n_left->n_lval - labinfo->low > + labinfo->size) { + comperr("Label out of range: %d, base %d", + bb->last->ip_node->n_left->n_lval, + labinfo->low); + } + cnode->bblock = labinfo->arr[bb->last->ip_node->n_left->n_lval - labinfo->low]; + SLIST_INSERT_LAST(&cnode->bblock->parents, pnode, cfgelem); + SLIST_INSERT_LAST(&bb->children, cnode, cfgelem); + continue; + } + if ((bb->last->type == IP_NODE) && + (bb->last->ip_node->n_op == CBRANCH)) { + if (bb->last->ip_node->n_right->n_lval - labinfo->low > + labinfo->size) + comperr("Label out of range: %d", + bb->last->ip_node->n_left->n_lval); + + cnode->bblock = labinfo->arr[bb->last->ip_node->n_right->n_lval - labinfo->low]; + SLIST_INSERT_LAST(&cnode->bblock->parents, pnode, cfgelem); + SLIST_INSERT_LAST(&bb->children, cnode, cfgelem); + cnode = tmpalloc(sizeof(struct cfgnode)); + pnode = tmpalloc(sizeof(struct cfgnode)); + pnode->bblock = bb; + } + + cnode->bblock = DLIST_NEXT(bb, bbelem); + SLIST_INSERT_LAST(&cnode->bblock->parents, pnode, cfgelem); + SLIST_INSERT_LAST(&bb->children, cnode, cfgelem); + } +} + +void +cfg_dfs(struct basicblock *bb, unsigned int parent, struct bblockinfo *bbinfo) +{ + struct cfgnode *cnode; + + if (bb->dfnum != 0) + return; + + bb->dfnum = ++dfsnum; + bb->dfparent = parent; + bbinfo->arr[bb->dfnum] = bb; + SLIST_FOREACH(cnode, &bb->children, cfgelem) { + cfg_dfs(cnode->bblock, bb->dfnum, bbinfo); + } + /* Don't bring in unreachable nodes in the future */ + bbinfo->size = dfsnum + 1; +} + +static bittype * +setalloc(int nelem) +{ + bittype *b; + int sz = (nelem+NUMBITS-1)/NUMBITS; + + b = tmpalloc(sz * sizeof(bittype)); + memset(b, 0, sz * sizeof(bittype)); + return b; +} + +/* + * Algorithm 19.9, pp 414 from Appel. + */ + +void +dominators(struct bblockinfo *bbinfo) +{ + struct cfgnode *cnode; + struct basicblock *bb, *y, *v; + struct basicblock *s, *sprime, *p; + int h, i; + + DLIST_FOREACH(bb, &bblocks, bbelem) { + bb->bucket = setalloc(bbinfo->size); + bb->df = setalloc(bbinfo->size); + bb->dfchildren = setalloc(bbinfo->size); + } + + dfsnum = 0; + cfg_dfs(DLIST_NEXT(&bblocks, bbelem), 0, bbinfo); + + if (b2debug) { + struct basicblock *bbb; + struct cfgnode *ccnode; + + DLIST_FOREACH(bbb, &bblocks, bbelem) { + printf("Basic block %d, parents: ", bbb->dfnum); + SLIST_FOREACH(ccnode, &bbb->parents, cfgelem) { + printf("%d, ", ccnode->bblock->dfnum); + } + printf("\nChildren: "); + SLIST_FOREACH(ccnode, &bbb->children, cfgelem) { + printf("%d, ", ccnode->bblock->dfnum); + } + printf("\n"); + } + } + + for(h = bbinfo->size - 1; h > 1; h--) { + bb = bbinfo->arr[h]; + p = s = bbinfo->arr[bb->dfparent]; + SLIST_FOREACH(cnode, &bb->parents, cfgelem) { + if (cnode->bblock->dfnum <= bb->dfnum) + sprime = cnode->bblock; + else + sprime = bbinfo->arr[ancestorwithlowestsemi + (cnode->bblock, bbinfo)->semi]; + if (sprime->dfnum < s->dfnum) + s = sprime; + } + bb->semi = s->dfnum; + BITSET(s->bucket, bb->dfnum); + link(p, bb); + for (i = 1; i < bbinfo->size; i++) { + if(TESTBIT(p->bucket, i)) { + v = bbinfo->arr[i]; + y = ancestorwithlowestsemi(v, bbinfo); + if (y->semi == v->semi) + v->idom = p->dfnum; + else + v->samedom = y->dfnum; + } + } + memset(p->bucket, 0, (bbinfo->size + 7)/8); + } + + if (b2debug) { + printf("Num\tSemi\tAncest\tidom\n"); + DLIST_FOREACH(bb, &bblocks, bbelem) { + printf("%d\t%d\t%d\t%d\n", bb->dfnum, bb->semi, + bb->ancestor, bb->idom); + } + } + + for(h = 2; h < bbinfo->size; h++) { + bb = bbinfo->arr[h]; + if (bb->samedom != 0) { + bb->idom = bbinfo->arr[bb->samedom]->idom; + } + } + DLIST_FOREACH(bb, &bblocks, bbelem) { + if (bb->idom != 0 && bb->idom != bb->dfnum) { + BDEBUG(("Setting child %d of %d\n", + bb->dfnum, bbinfo->arr[bb->idom]->dfnum)); + BITSET(bbinfo->arr[bb->idom]->dfchildren, bb->dfnum); + } + } +} + + +struct basicblock * +ancestorwithlowestsemi(struct basicblock *bblock, struct bblockinfo *bbinfo) +{ + struct basicblock *u = bblock; + struct basicblock *v = bblock; + + while (v->ancestor != 0) { + if (bbinfo->arr[v->semi]->dfnum < + bbinfo->arr[u->semi]->dfnum) + u = v; + v = bbinfo->arr[v->ancestor]; + } + return u; +} + +void +link(struct basicblock *parent, struct basicblock *child) +{ + child->ancestor = parent->dfnum; +} + +void +computeDF(struct basicblock *bblock, struct bblockinfo *bbinfo) +{ + struct cfgnode *cnode; + int h, i; + + SLIST_FOREACH(cnode, &bblock->children, cfgelem) { + if (cnode->bblock->idom != bblock->dfnum) + BITSET(bblock->df, cnode->bblock->dfnum); + } + for (h = 1; h < bbinfo->size; h++) { + if (!TESTBIT(bblock->dfchildren, h)) + continue; + computeDF(bbinfo->arr[h], bbinfo); + for (i = 1; i < bbinfo->size; i++) { + if (TESTBIT(bbinfo->arr[h]->df, i) && + (bbinfo->arr[h] == bblock || + (bblock->idom != bbinfo->arr[h]->dfnum))) + BITSET(bblock->df, i); + } + } +} + +static struct basicblock *currbb; +static struct interpass *currip; + +/* Helper function for findTemps, Find assignment nodes. */ +static void +searchasg(NODE *p) +{ + struct pvarinfo *pv; + + if (p->n_op != ASSIGN) + return; + + if (p->n_left->n_op != TEMP) + return; + + pv = tmpcalloc(sizeof(struct pvarinfo)); + pv->next = defsites.arr[p->n_left->n_lval]; + pv->bb = currbb; + pv->top = currip->ip_node; + pv->n = p->n_left; + BITSET(currbb->Aorig, p->n_left->n_lval); + + defsites.arr[p->n_left->n_lval] = pv; +} + +/* Walk the interpass looking for assignment nodes. */ +void findTemps(struct interpass *ip) +{ + if (ip->type != IP_NODE) + return; + + currip = ip; + + walkf(ip->ip_node, searchasg); +} + +/* + * Algorithm 19.6 from Appel. + */ + +void +placePhiFunctions(struct bblockinfo *bbinfo) +{ + struct basicblock *bb; + struct interpass *ip; + int maxtmp, i, j, k, l; + struct pvarinfo *n; + struct cfgnode *cnode; + TWORD ntype; + NODE *p; + struct pvarinfo *pv; + + bb = DLIST_NEXT(&bblocks, bbelem); + defsites.low = ((struct interpass_prolog *)bb->first)->ip_tmpnum; + bb = DLIST_PREV(&bblocks, bbelem); + maxtmp = ((struct interpass_prolog *)bb->first)->ip_tmpnum; + defsites.size = maxtmp - defsites.low + 1; + defsites.arr = tmpcalloc(defsites.size*sizeof(struct pvarinfo *)); + + /* Find all defsites */ + DLIST_FOREACH(bb, &bblocks, bbelem) { + currbb = bb; + ip = bb->first; + bb->Aorig = setalloc(defsites.size); + bb->Aphi = setalloc(defsites.size); + + + while (ip != bb->last) { + findTemps(ip); + ip = DLIST_NEXT(ip, qelem); + } + /* Make sure we get the last statement in the bblock */ + findTemps(ip); + } + /* For each variable */ + for (i = defsites.low; i < defsites.size; i++) { + /* While W not empty */ + while (defsites.arr[i] != NULL) { + /* Remove some node n from W */ + n = defsites.arr[i]; + defsites.arr[i] = n->next; + /* For each y in n->bb->df */ + for (j = 0; j < bbinfo->size; j++) { + if (!TESTBIT(n->bb->df, j)) + continue; + + if (TESTBIT(bbinfo->arr[j]->Aphi, i)) + continue; + + ntype = n->n->n_type; + k = 0; + /* Amount of predecessors for y */ + SLIST_FOREACH(cnode, &n->bb->parents, cfgelem) + k++; + /* Construct phi(...) */ + p = mklnode(TEMP, i, 0, ntype); + for (l = 0; l < k-1; l++) + p = mkbinode(PHI, p, + mklnode(TEMP, i, 0, ntype), ntype); + ip = ipnode(mkbinode(ASSIGN, + mklnode(TEMP, i, 0, ntype), p, ntype)); + /* Insert phi at top of basic block */ + DLIST_INSERT_BEFORE(((struct interpass*)&n->bb->first), ip, qelem); + n->bb->first = ip; + BITSET(bbinfo->arr[j]->Aphi, i); + if (!TESTBIT(bbinfo->arr[j]->Aorig, i)) { + pv = tmpalloc(sizeof(struct pvarinfo)); + // XXXpj Ej fullständig information. + pv->bb = bbinfo->arr[j]; + pv->next = defsites.arr[i]->next; + defsites.arr[i] = pv; + } + + + } + } + } +} + +/* + * Remove unreachable nodes in the CFG. + */ + +void +remunreach(void) +{ + struct basicblock *bb, *nbb; + struct interpass *next, *ctree; + + bb = DLIST_NEXT(&bblocks, bbelem); + while (bb != &bblocks) { + nbb = DLIST_NEXT(bb, bbelem); + + /* Code with dfnum 0 is unreachable */ + if (bb->dfnum != 0) { + bb = nbb; + continue; + } + + /* Need the epilogue node for other parts of the + compiler, set its label to 0 and backend will + handle it. */ + if (bb->first->type == IP_EPILOG) { + bb->first->ip_lbl = 0; + bb = nbb; + continue; + } + + next = bb->first; + do { + ctree = next; + next = DLIST_NEXT(ctree, qelem); + + if (ctree->type == IP_NODE) + tfree(ctree->ip_node); + DLIST_REMOVE(ctree, qelem); + } while (ctree != bb->last); + + DLIST_REMOVE(bb, bbelem); + bb = nbb; + } +} + +void +printip(struct interpass *pole) +{ + static char *foo[] = { + 0, "NODE", "PROLOG", "STKOFF", "EPILOG", "DEFLAB", "DEFNAM", "ASM" }; + struct interpass *ip; + struct interpass_prolog *ipp, *epp; + + DLIST_FOREACH(ip, pole, qelem) { + if (ip->type > MAXIP) + printf("IP(%d) (%p): ", ip->type, ip); + else + printf("%s (%p): ", foo[ip->type], ip); + switch (ip->type) { + case IP_NODE: printf("\n"); + fwalk(ip->ip_node, e2print, 0); break; + case IP_PROLOG: + ipp = (struct interpass_prolog *)ip; + printf("%s %s regs %x autos %d mintemp %d minlbl %d\n", + ipp->ipp_name, ipp->ipp_vis ? "(local)" : "", + ipp->ipp_regs, ipp->ipp_autos, ipp->ip_tmpnum, + ipp->ip_lblnum); + break; + case IP_EPILOG: + epp = (struct interpass_prolog *)ip; + printf("%s %s regs %x autos %d mintemp %d minlbl %d\n", + epp->ipp_name, epp->ipp_vis ? "(local)" : "", + epp->ipp_regs, epp->ipp_autos, epp->ip_tmpnum, + epp->ip_lblnum); + break; + case IP_DEFLAB: printf(LABFMT "\n", ip->ip_lbl); break; + case IP_DEFNAM: printf("\n"); break; + case IP_ASM: printf("%s\n", ip->ip_asm); break; + default: + break; + } + } +} diff --git a/usr.bin/pcc/mip/pass2.h b/usr.bin/pcc/mip/pass2.h new file mode 100644 index 00000000000..35ccb7f4220 --- /dev/null +++ b/usr.bin/pcc/mip/pass2.h @@ -0,0 +1,418 @@ +/* $Id: pass2.h,v 1.1 2007/09/15 18:12:36 otto Exp $ */ +/* + * 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. + */ +#include <sys/types.h> + +#include "manifest.h" +#include "protos.h" +#ifndef MKEXT +#include "external.h" +#else +typedef int bittype; /* XXX - for basicblock */ +#endif + +/* cookies, used as arguments to codgen */ +#define FOREFF 01 /* compute for effects only */ +#define INAREG 02 /* compute into a register */ +#define INBREG 04 /* compute into a register */ +#define INCREG 010 /* compute into a register */ +#define INDREG 020 /* compute into a register */ +#define INREGS (INAREG|INBREG|INCREG|INDREG) +#define FORCC 040 /* compute for condition codes only */ +#define INTEMP 010000 /* compute into a temporary location */ +#define FORREW 040000 /* search the table for a rewrite rule */ + +/* + * OP descriptors, + * the ASG operator may be used on some of these + */ +#define OPSIMP 010000 /* +, -, &, |, ^ */ +#define OPCOMM 010002 /* +, &, |, ^ */ +#define OPMUL 010004 /* *, / */ +#define OPDIV 010006 /* /, % */ +#define OPUNARY 010010 /* unary ops */ +#define OPLEAF 010012 /* leaves */ +#define OPANY 010014 /* any op... */ +#define OPLOG 010016 /* logical ops */ +#define OPFLOAT 010020 /* +, -, *, or / (for floats) */ +#define OPSHFT 010022 /* <<, >> */ +#define OPLTYPE 010024 /* leaf type nodes (e.g, NAME, ICON, etc.) */ + +/* shapes */ +#define SANY 01 /* same as FOREFF */ +#define SAREG 02 /* same as INAREG */ +#define SBREG 04 /* same as INBREG */ +#define SCREG 010 /* same as INCREG */ +#define SDREG 020 /* same as INDREG */ +#define SCC 040 /* same as FORCC */ +#define SNAME 0100 +#define SCON 0200 +#define SFLD 0400 +#define SOREG 01000 +#define STARNM 02000 +#define STARREG 04000 +#define SWADD 040000 +#define SPECIAL 0100000 +#define SZERO SPECIAL +#define SONE (SPECIAL|1) +#define SMONE (SPECIAL|2) +#define SCCON (SPECIAL|3) /* -256 <= constant < 256 */ +#define SSCON (SPECIAL|4) /* -32768 <= constant < 32768 */ +#define SSOREG (SPECIAL|5) /* non-indexed OREG */ +#define MAXSPECIAL (SPECIAL|5) + +/* These are used in rstatus[] in conjunction with SxREG */ +#define TEMPREG 0100 +#define PERMREG 0200 + +/* tshape() return values */ +#define SRNOPE 0 /* Cannot match any shape */ +#define SRDIR 1 /* Direct match */ +#define SROREG 2 /* Can convert into OREG */ +#define SRREG 3 /* Must put into REG */ + +/* find*() return values */ +#define FRETRY -2 +#define FFAIL -1 + +/* INTEMP is carefully not conflicting with shapes */ + +/* types */ +#define TCHAR 01 /* char */ +#define TSHORT 02 /* short */ +#define TINT 04 /* int */ +#define TLONG 010 /* long */ +#define TFLOAT 020 /* float */ +#define TDOUBLE 040 /* double */ +#define TPOINT 0100 /* pointer to something */ +#define TUCHAR 0200 /* unsigned char */ +#define TUSHORT 0400 /* unsigned short */ +#define TUNSIGNED 01000 /* unsigned int */ +#define TULONG 02000 /* unsigned long */ +#define TPTRTO 04000 /* pointer to one of the above */ +#define TANY 010000 /* matches anything within reason */ +#define TSTRUCT 020000 /* structure or union */ +#define TLONGLONG 040000 /* long long */ +#define TULONGLONG 0100000 /* unsigned long long */ +#define TLDOUBLE 0200000 /* long double; exceeds 16 bit */ +#define TFTN 0400000 /* function pointer; exceeds 16 bit */ + +/* reclamation cookies */ +#define RNULL 0 /* clobber result */ +#define RLEFT 01 +#define RRIGHT 02 +#define RESC1 04 +#define RESC2 010 +#define RESC3 020 +#define RDEST 040 +#define RESCC 04000 +#define RNOP 010000 /* DANGER: can cause loops.. */ + +/* needs */ +#define NAREG 0000001 +#define NACOUNT 0000003 +#define NAMASK 0000017 +#define NASL 0000004 /* may share left register */ +#define NASR 0000010 /* may share right register */ +#define NBREG 0000020 +#define NBCOUNT 0000060 +#define NBMASK 0000360 +#define NBSL 0000100 +#define NBSR 0000200 +#define NTEMP 0000400 +#define NTMASK 0001400 +#define NSPECIAL 0040000 /* need special register treatment */ +#define REWRITE 0100000 +#define NCSL 0x10000 /* Above 16 bit */ +#define NCSR 0x20000 /* Above 16 bit */ +#define NCREG 0x40000 /* Above 16 bit */ +#define NCCOUNT 0xc0000 +#define NDSL 0x100000 /* Above 16 bit */ +#define NDSR 0x200000 /* Above 16 bit */ +#define NDREG 0x400000 /* Above 16 bit */ +#define NDCOUNT 0xc00000 + +/* special treatment */ +#define NLEFT (0001) /* left leg register (moveadd) */ +#define NOLEFT (0002) /* avoid regs for left (addedge) */ +#define NRIGHT (0004) /* right leg register */ +#define NORIGHT (0010) /* avoid reg for right */ +#define NEVER (0020) /* registers trashed (addalledges) */ +#define NRES (0040) /* result register (moveadd) */ +#define NMOVTO (0100) /* move between classes */ + + +#define MUSTDO 010000 /* force register requirements */ +#define NOPREF 020000 /* no preference for register assignment */ + +#define isreg(p) (p->n_op == REG || p->n_op == TEMP) + +#define TBUSY 01000 + +#define SETSTO(x,y) (stotree = (x), stocook = (y)) +extern int stocook; + +extern NODE *stotree; +extern int callflag; + +extern int fregs; + +/* code tables */ +extern struct optab { + int op; + int visit; + int lshape; + int ltype; + int rshape; + int rtype; + int needs; + int rewrite; + char *cstring; +} table[]; + +/* Special needs for register allocations */ +struct rspecial { + int op, num; +#if 0 + int left; /* left leg register */ + int noleft; /* avoid regs for left */ + int right; /* right leg register */ + int noright; /* avoid right leg register */ + int *rmask; /* array of destroyed registers */ + int res; /* Result ends up here */ +// void (*rew)(struct optab *, NODE *); /* special rewrite */ +#endif +}; + +extern NODE resc[]; + +extern int p2autooff, p2maxautooff; + +extern NODE + *talloc(void), + *eread(void), + *tcopy(NODE *), + *mklnode(int, CONSZ, int, TWORD), + *mkbinode(int, NODE *, NODE *, TWORD), + *mkunode(int, NODE *, int, TWORD), + *getlr(NODE *p, int); + +void eoftn(struct interpass_prolog *); +void prologue(struct interpass_prolog *); +void setlocc(int locctr); +void e2print(NODE *p, int down, int *a, int *b); +void myoptim(struct interpass *); +void cbgen(int op, int label); +struct optab *nxtmatch(struct optab *); +int chkmatch(NODE *, int, int, int); +int match(NODE *p, int cookie); +int nmatch(NODE *p, int what); +#ifndef special +int special(NODE *, int); +#endif +int setasg(NODE *, int); +int setuni(NODE *, int); +int sucomp(NODE *); +int nsucomp(NODE *); +int setorder(NODE *); +int geninsn(NODE *, int cookie); +void adrput(FILE *, NODE *); +void comperr(char *str, ...); +void genregs(NODE *p); +void ngenregs(struct interpass *); +NODE *store(NODE *); +void gencall(NODE *, NODE *prev); +struct interpass *ipnode(NODE *); +void deflab(int); +void rmove(int, int, TWORD); +int rspecial(struct optab *, int); +struct rspecial *nspecial(struct optab *q); +void printip(struct interpass *pole); +int findops(NODE *p, int); +int findasg(NODE *p, int); +int finduni(NODE *p, int); +int findumul(NODE *p, int); +int findleaf(NODE *p, int); +int relops(NODE *p); +void offstar(NODE *p, int shape); +int gclass(TWORD); +void lastcall(NODE *); +void myreader(struct interpass *pole); +int oregok(NODE *p, int sharp); +void myormake(NODE *); + +char *prcook(int); + +void conput(FILE *, NODE *); + +extern char *rnames[]; +extern int rstatus[]; +extern int roverlap[MAXREGS][MAXREGS]; + +extern int classmask(int), tclassmask(int); +extern void cmapinit(void); +extern int aliasmap(int adjclass, int regnum); +extern int regK[]; +#define CLASSA 1 +#define CLASSB 2 +#define CLASSC 3 +#define CLASSD 4 +#define CLASSE 5 + +/* routines to handle double indirection */ +#ifdef R2REGS +void makeor2(NODE *p, NODE *q, int, int); +int base(NODE *); +int offset(NODE *p, int); +#endif + +extern int lineno; +extern int fldshf, fldsz; +extern int lflag, x2debug, udebug, e2debug, odebug, mdebug; +extern int rdebug, radebug, t2debug, s2debug, b2debug, c2debug; +extern int kflag; +#ifdef FORT +extern int Oflag; +#endif + +#ifndef callchk +#define callchk(x) allchk() +#endif + +#ifndef PUTCHAR +#define PUTCHAR(x) putchar(x) +#endif + +#define optype(o) (dope[o]&TYFLG) +#define asgop(o) (dope[o]&ASGFLG) +#define logop(o) (dope[o]&LOGFLG) +#define callop(o) (dope[o]&CALLFLG) +extern int dope[]; /* a vector containing operator information */ +extern char *opst[]; /* a vector containing names for ops */ + + /* macros for doing double indexing */ +#define R2PACK(x,y,z) (0200*((x)+1)+y+040000*z) +#define R2UPK1(x) ((((x)>>7)-1)&0177) +#define R2UPK2(x) ((x)&0177) +#define R2UPK3(x) (x>>14) +#define R2TEST(x) ((x)>=0200) + +/* + * Layout of findops() return value: + * bit 0-1 where to store left node. + * bit 2-3 where to store right node. + * bit 4 set if right leg should be evaluated first + * bit 5- table index + * + * LOREG means: walk down left node, after code emission call canon() to + * convert the tree to an OREG. + */ +#define LREG 001 +#define LOREG 002 +#define LTEMP 003 +#define LDIR 003 +#define LMASK 003 +#define RREG 004 +#define ROREG 010 +#define RTEMP 014 +#define RDIR 014 +#define RMASK 014 +#define DORIGHT 020 +#define SCLASS(v,x) ((v) |= ((x) << 5)) +#define TCLASS(x) (((x) >> 5) & 7) +#define TBSH 8 +#define TBLIDX(idx) ((idx) >> TBSH) +#define MKIDX(tbl,mod) (((tbl) << TBSH) | (mod)) + +#ifndef BREGS +#define BREGS 0 +#define TBREGS 0 +#endif +#define REGBIT(x) (1 << (x)) + +void emit(struct interpass *); +void optimize(struct interpass *); + +struct basicblock { + DLIST_ENTRY(basicblock) bbelem; + SLIST_HEAD(, cfgnode) children; /* CFG - children to this node */ + SLIST_HEAD(, cfgnode) parents; /* CFG - parents to this node */ + int bbnum; /* this basic block number */ + unsigned int dfnum; /* DFS-number */ + unsigned int dfparent; /* Parent in DFS */ + unsigned int semi; + unsigned int ancestor; + unsigned int idom; + unsigned int samedom; + bittype *bucket; + bittype *df; + bittype *dfchildren; + bittype *Aorig; + bittype *Aphi; + struct interpass *first; /* first element of basic block */ + struct interpass *last; /* last element of basic block */ +}; + +struct labelinfo { + struct basicblock **arr; + unsigned int size; + unsigned int low; +}; + +struct bblockinfo { + unsigned int size; + struct basicblock **arr; +}; + +struct varinfo { + struct pvarinfo **arr; + int size; + int low; +}; + +struct pvarinfo { + struct pvarinfo *next; + struct basicblock *bb; + NODE *top, *n; +}; + +struct cfgnode { + SLIST_ENTRY(cfgnode) cfgelem; + struct basicblock *bblock; +}; + +/* + * C compiler second pass extra defines. + */ +#define PHI (MAXOP + 1) /* Used in SSA trees */ diff --git a/usr.bin/pcc/mip/protos.h b/usr.bin/pcc/mip/protos.h new file mode 100644 index 00000000000..a0a436fdd45 --- /dev/null +++ b/usr.bin/pcc/mip/protos.h @@ -0,0 +1,83 @@ + +struct optab; +struct symtab; +struct sw; + +void cerror(char *s, ...); +void werror(char *s, ...); +void uerror(char *s, ...); +void reclaim(NODE *p, int, int); +void walkf(NODE *, void (*f)(NODE *)); +void allchk(void); +void tfree(NODE *); +int tshape(NODE *, int); +void prtdcon(NODE *p); +void tinit(void); +void tcheck(void); +void mkdope(void); +int tshape(NODE *p, int shape); +int shtemp(NODE *p); +int flshape(NODE *p); +int shumul(NODE *p); +int ttype(TWORD t, int tword); +void expand(NODE *, int, char *); +void hopcode(int, int); +void adrcon(CONSZ); +void zzzcode(NODE *, int); +void insput(NODE *); +void upput(NODE *, int); +void econvert(NODE *); +int andable(NODE *); +int conval(NODE *, int, NODE *); +int ispow2(CONSZ); +void defid(NODE *q, int class); +int getlab(void); +void ftnend(void); +void efcode(void); +void dclargs(void); +void fixarg(struct symtab *); +void cendarg(void); +void defalign(int); +int fldal(unsigned int); +void vfdzero(int); +void zecode(int); +void putbyte(int v); +void ecomp(NODE *p); +void cinit(NODE *, int); +void bccode(void); +int upoff(int size, int alignment, int *poff); +void fldty(struct symtab *p); +void nidcl(NODE *p, int class); +int noinit(void); +void eprint(NODE *, int, int *, int *); +int uclass(int class); +int fixclass(int, TWORD type); +void lineid(int, char *); +void mycanon(NODE *); +void delay(NODE *); +int delay1(NODE *); +void delay2(NODE *); +void setregs(void); +int autoincr(NODE *); +int deltest(NODE *); +void canon(NODE *); +void order(NODE *, int); +int tlen(NODE *p); +int setincr(NODE *); +int setbin(NODE *); +void stoarg(NODE *p, int); +void constore(NODE *); +void markcall(NODE *); +void oreg2(NODE *p); +int notoff(TWORD, int, CONSZ, char *); +void bycode(int, int); +void pstab(char *, int); +void psline(void); +int notlval(NODE *); +int icons(NODE *); +void ecode(NODE *p); +int yylex(void); +void yyerror(char *s); +void p2tree(NODE *p); +int rewfld(NODE *p); +int freetemp(int k); diff --git a/usr.bin/pcc/mip/reader.c b/usr.bin/pcc/mip/reader.c new file mode 100644 index 00000000000..01135685094 --- /dev/null +++ b/usr.bin/pcc/mip/reader.c @@ -0,0 +1,1098 @@ +/* $Id: reader.c,v 1.1 2007/09/15 18:12:37 otto 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; + +void saveip(struct interpass *ip); +void deljumps(void); +void deltemp(NODE *p); +void mkhardops(NODE *p); +void optdump(struct interpass *ip); +void cvtemps(struct interpass *epil); +NODE *store(NODE *); +void rcount(void); +void compile2(struct interpass *ip); +void compile3(struct interpass *ip); +void compile4(struct interpass *ip); + +static void gencode(NODE *p, int cookie); + +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 FUNARG: + case UCALL: + case UFORTCALL: + case FORCE: +/* case INIT: */ + 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) { + /* Put left on queue first */ + ip = tmpalloc(sizeof(*ip)); + ip->type = IP_NODE; + ip->lineno = 0; /* XXX */ + ip->ip_node = l; + pass2_compile(ip); + 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_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); + } + + optimize(&ipole); + ngenregs(&ipole); + + DLIST_FOREACH(ip, &ipole, qelem) + emit(ip); +} + +void +emit(struct interpass *ip) +{ + NODE *p; + 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 */ + if (table[TBLIDX(p->n_left->n_su)].rewrite & RESCC) { + o = p->n_left->n_op; + gencode(p, FORCC); + cbgen(o, p->n_right->n_lval); + } else + gencode(p, FORCC); + break; + case FORCE: + gencode(p->n_left, INREGS); + 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("compile4 %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: + 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 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 COMPL: + case UMINUS: + case PCONV: + case SCONV: +/* case INIT: */ + case GOTO: + case FUNARG: + case STARG: + case UCALL: + case USTCALL: + rv = finduni(p, cookie); + break; + + case CBRANCH: + p1 = p->n_left; + p2 = p->n_right; + p1->n_label = p2->n_lval; + o = p1->n_op; + 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; + + 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, rnames[DECRA(p->n_reg, 0)])); + p->n_rval = DECRA(p->n_reg, 0); +} + +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) { + 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 */ + +#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, " " CONFMT, p->n_lval); + 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, ", " ); + { + int gregn(struct regw *); + if (p->n_reg == -1) + fprintf(prfil, "REG <undef>"); + else if (p->n_reg < 100000) /* XXX */ + fprintf(prfil, "REG %s", rnames[DECRA(p->n_reg, 0)]); + else + fprintf(prfil, "TEMP %d", gregn(p->n_regw)); + } + 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 */ + + p->n_left->n_type = ty; + + p->n_op = AND; + p->n_right = mklnode(ICON, (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! */ + } + } +} +#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 == p->n_lval) + break; + if (w == NULL) { + /* new on stack */ + w = tmpalloc(sizeof(struct tmpsave)); + w->tempno = p->n_lval; + 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) { + /* 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; + } else if((r=base(qr))>=0 && (i=offset(ql, tlen(p)))>=0) { + makeor2(p, qr, r, i); + return; + } + } + + +#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 +#ifdef MYCANON + MYCANON(p); /* your own canonicalization routine(s) */ +#endif + +} + +void +comperr(char *str, ...) +{ + extern char *ftitle; + va_list ap; + + 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; +} diff --git a/usr.bin/pcc/mip/regs.c b/usr.bin/pcc/mip/regs.c new file mode 100644 index 00000000000..3371b4bee79 --- /dev/null +++ b/usr.bin/pcc/mip/regs.c @@ -0,0 +1,2244 @@ +/* $Id: regs.c,v 1.1 2007/09/15 18:12:37 otto Exp $ */ +/* + * Copyright (c) 2005 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. + */ + +#include "pass2.h" +#include <string.h> +#include <stdlib.h> + +#define MAXLOOP 20 /* Max number of allocation loops XXX 3 should be enough */ + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +/* + * New-style register allocator using graph coloring. + * The design is based on the George and Appel paper + * "Iterated Register Coalescing", ACM Transactions, No 3, May 1996. + */ + +#define BIT2BYTE(bits) ((((bits)+NUMBITS-1)/NUMBITS)*(NUMBITS/8)) +#define BITALLOC(ptr,all,sz) { \ + int __s = BIT2BYTE(sz); ptr = all(__s); memset(ptr, 0, __s); } + +#undef COMPERR_PERM_MOVE +#define RDEBUG(x) if (rdebug) printf x +#define RRDEBUG(x) if (rdebug > 1) printf x +#define RPRINTIP(x) if (rdebug) printip(x) +#define RDX(x) x +#define UDEBUG(x) if (udebug) printf x + +/* + * Data structure overview for this implementation of graph coloring: + * + * Each temporary (called "node") is described by the type REGW. + * Space for all nodes is allocated initially as an array, so + * the nodes can be can be referenced both by the node number and + * by pointer. + * + * All moves are represented by the type REGM, allocated when needed. + * + * The "live" set used during graph building is represented by a bitset. + * + * Interference edges are represented by struct AdjSet, hashed and linked + * from index into the edgehash array. + * + * A mapping from each node to the moves it is assiciated with is + * maintained by an array moveList which for each node number has a linked + * list of MOVL types, each pointing to a REGM. + * + * Adjacency list is maintained by the adjList array, indexed by the + * node number. Each adjList entry points to an ADJL type, and is a + * single-linked list for all adjacent nodes. + * + * degree, alias and color are integer arrays indexed by node number. + */ + +/* + * linked list of adjacent nodes. + */ +typedef struct regw3 { + struct regw3 *r_next; + struct regw *a_temp; +} ADJL; + +/* + * Structure describing a move. + */ +typedef struct regm { + DLIST_ENTRY(regm) link; + struct regw *src, *dst; + int queue; +} REGM; + +typedef struct movlink { + struct movlink *next; + REGM *regm; +} MOVL; + +/* + * Structure describing a temporary. + */ +typedef struct regw { + DLIST_ENTRY(regw) link; + ADJL *r_adjList; /* linked list of adjacent nodes */ + int r_class; /* this nodes class */ + int r_nclass[NUMCLASS+1]; /* count of adjacent classes */ + struct regw *r_alias; /* aliased temporary */ + int r_color; /* final node color */ + struct regw *r_onlist; /* which work list this node belongs to */ + MOVL *r_moveList; /* moves associated with this node */ +#ifdef PCC_DEBUG + int nodnum; /* Debug number */ +#endif +} REGW; + +/* + * Worklists, a node is always on exactly one of these lists. + */ +static REGW precolored, simplifyWorklist, freezeWorklist, spillWorklist, + spilledNodes, coalescedNodes, coloredNodes, selectStack; +static REGW initial, *nblock; +static void insnwalk(NODE *p); +#ifdef PCC_DEBUG +int nodnum = 100; +#define SETNUM(x) (x)->nodnum = nodnum++ +#define ASGNUM(x) (x)->nodnum +#else +#define SETNUM(x) +#define ASGNUM(x) +#endif + +#define ALLNEEDS (NACOUNT|NBCOUNT|NCCOUNT|NDCOUNT) + +/* XXX */ +REGW *ablock; + +static int tempmin, tempmax, basetemp, xbits; +/* + * nsavregs is an array that matches the permregs array. + * Each entry in the array may have the values: + * 0 : register coalesced, just ignore. + * 1 : save register on stack + * If the entry is 0 but the resulting color differs from the + * corresponding permregs index, add moves. + * XXX - should be a bitfield! + */ +static int *nsavregs, *ndontregs; + +/* + * Return the REGW struct for a temporary. + * If first time touched, enter into list for existing vars. + * Only called from sucomp(). + */ +static REGW * +newblock(NODE *p) +{ + REGW *nb = &nblock[(int)p->n_lval]; + if (nb->link.q_forw == 0) { + DLIST_INSERT_AFTER(&initial, nb, link); + ASGNUM(nb) = p->n_lval; + RDEBUG(("Adding longtime %d for tmp %d\n", + nb->nodnum, (int)p->n_lval)); + } + if (nb->r_class == 0) + nb->r_class = gclass(p->n_type); + RDEBUG(("newblock: p %p, node %d class %d\n", + p, nb->nodnum, nb->r_class)); + return nb; +} + +/* + * Count the number of registers needed to evaluate a tree. + * This is only done to find the evaluation order of the tree. + * While here, assign temp numbers to the registers that will + * be needed when the tree is evaluated. + * + * While traversing the tree, assign REGW nodes to the registers + * used by all instructions: + * - n_regw[0] is always set to the outgoing node. If the + * instruction is 2-op (addl r0,r1) then an implicit move + * is inserted just before the left (clobbered) operand. + * - if the instruction has needs then REGW nodes are + * allocated as n_regw[1] etc. + */ +int +nsucomp(NODE *p) +{ + struct optab *q; + int left, right; + int nreg, need, i, nxreg, o; + int nareg, nbreg, ncreg, ndreg; + REGW *w; + + o = optype(p->n_op); + + UDEBUG(("entering nsucomp, node %p\n", p)); + + if (TBLIDX(p->n_su) == 0) { + int a = 0, b; + + p->n_regw = NULL; + if (o == LTYPE ) { + if (p->n_op == TEMP) + p->n_regw = newblock(p); + } else + a = nsucomp(p->n_left); + if (o == BITYPE) { + b = nsucomp(p->n_right); + if (b > a) + p->n_su |= DORIGHT; + a = MAX(a, b); + } + return a; + } + + q = &table[TBLIDX(p->n_su)]; + nareg = (q->needs & NACOUNT); + + for (i = (q->needs & NBCOUNT), nbreg = 0; i; i -= NBREG) + nbreg++; + for (i = (q->needs & NCCOUNT), ncreg = 0; i; i -= NCREG) + ncreg++; + for (i = (q->needs & NDCOUNT), ndreg = 0; i; i -= NDREG) + ndreg++; + + nxreg = nareg + nbreg + ncreg + ndreg; + nreg = nxreg; + if (callop(p->n_op)) + nreg = MAX(fregs, nreg); + + if (o == BITYPE) { + right = nsucomp(p->n_right); + } else + right = 0; + + if (o != LTYPE) + left = nsucomp(p->n_left); + else + left = 0; + + UDEBUG(("node %p left %d right %d\n", p, left, right)); + + if (o == BITYPE) { + /* Two children */ + if (right == left) { + need = left + MAX(nreg, 1); + } else { + need = MAX(right, left); + need = MAX(need, nreg); + } + if (setorder(p) == 0) { + /* XXX - should take care of overlapping needs */ + if (right > left) { + p->n_su |= DORIGHT; + } else if (right == left) { + /* A favor to 2-operand architectures */ + if ((q->rewrite & RRIGHT) == 0) + p->n_su |= DORIGHT; + } + } + } else if (o != LTYPE) { + /* One child */ + need = MAX(right, left) + nreg; + } else + need = nreg; + + if (p->n_op == TEMP) + (void)newblock(p); + + if (TCLASS(p->n_su) == 0 && nxreg == 0) { + UDEBUG(("node %p no class\n", p)); + p->n_regw = NULL; /* may be set earlier */ + return need; + } + +#define ADCL(n, cl) \ + for (i = 0; i < n; i++, w++) { w->r_class = cl; \ + DLIST_INSERT_BEFORE(&initial, w, link); SETNUM(w); \ + UDEBUG(("Adding " #n " %d\n", w->nodnum)); \ + } + + UDEBUG(("node %p numregs %d\n", p, nxreg+1)); + w = p->n_regw = tmpalloc(sizeof(REGW) * (nxreg+1)); + memset(w, 0, sizeof(REGW) * (nxreg+1)); + + w->r_class = TCLASS(p->n_su); + if (w->r_class == 0) + w->r_class = gclass(p->n_type); + SETNUM(w); + if (w->r_class) + DLIST_INSERT_BEFORE(&initial, w, link); + UDEBUG(("Adding short %d calss %d\n", w->nodnum, w->r_class)); + w++; + ADCL(nareg, CLASSA); + ADCL(nbreg, CLASSB); + ADCL(ncreg, CLASSC); + ADCL(ndreg, CLASSD); + + if (q->rewrite & RESC1) { + w = p->n_regw + 1; + w->r_class = -1; + DLIST_REMOVE(w,link); + } else if (q->rewrite & RESC2) { + w = p->n_regw + 2; + w->r_class = -1; + DLIST_REMOVE(w,link); + } else if (q->rewrite & RESC3) { + w = p->n_regw + 3; + w->r_class = -1; + DLIST_REMOVE(w,link); + } + + UDEBUG(("node %p return regs %d\n", p, need)); + + return need; +} + +#define CLASS(x) (x)->r_class +#define NCLASS(x,c) (x)->r_nclass[c] +#define ADJLIST(x) (x)->r_adjList +#define ALIAS(x) (x)->r_alias +#define ONLIST(x) (x)->r_onlist +#define MOVELIST(x) (x)->r_moveList +#define COLOR(x) (x)->r_color + +static bittype *live; + +#define PUSHWLIST(w, l) DLIST_INSERT_AFTER(&l, w, link); w->r_onlist = &l +#define POPWLIST(l) popwlist(&l); +#define DELWLIST(w) DLIST_REMOVE(w, link) +#define WLISTEMPTY(h) DLIST_ISEMPTY(&h,link) +#define PUSHMLIST(w, l, q) DLIST_INSERT_AFTER(&l, w, link); w->queue = q +#define POPMLIST(l) popmlist(&l); + +#define trivially_colorable(x) \ + trivially_colorable_p((x)->r_class, (x)->r_nclass) +/* + * Determine if a node is trivially colorable ("degree < K"). + * This implementation is a dumb one, without considering speed. + */ +static int +trivially_colorable_p(int c, int *n) +{ + int r[NUMCLASS+1]; + int i; + + for (i = 1; i < NUMCLASS+1; i++) + r[i] = n[i] < regK[i] ? n[i] : regK[i]; + +#if 0 + /* add the exclusion nodes. */ + /* XXX can we do someything smart here? */ + /* worst-case for exclusion nodes are better than the `worst-case' */ + for (; excl; excl >>= 1) + if (excl & 1) + r[c]++; +#endif + + i = COLORMAP(c, r); +if (i < 0 || i > 1) + comperr("trivially_colorable_p"); +//printf("trivially_colorable_p: n[1] %d n[2] %d n[3] %d n[4] %d class %d, triv %d\n", n[1], n[2], n[3], n[4], c, i); + return i; +} + +static int +ncnt(int needs) +{ + int i = 0; + + while (needs & NACOUNT) + needs -= NAREG, i++; + while (needs & NBCOUNT) + needs -= NBREG, i++; + while (needs & NCCOUNT) + needs -= NCREG, i++; + while (needs & NDCOUNT) + needs -= NDREG, i++; + return i; +} + +static inline REGW * +popwlist(REGW *l) +{ + REGW *w = DLIST_NEXT(l, link); + + DLIST_REMOVE(w, link); + w->r_onlist = NULL; + return w; +} + +/* + * Move lists, a move node is always on only one list. + */ +static REGM coalescedMoves, constrainedMoves, frozenMoves, + worklistMoves, activeMoves; +enum { COAL, CONSTR, FROZEN, WLIST, ACTIVE }; + +static inline REGM * +popmlist(REGM *l) +{ + REGM *w = DLIST_NEXT(l, link); + + DLIST_REMOVE(w, link); + return w; +} + +/* + * About data structures used in liveness analysis: + * + * The temporaries generated in pass1 are numbered between tempmin and + * tempmax. Temporaries generated in pass2 are numbered above tempmax, + * so they are sequentially numbered. + * + * Bitfields are used for liveness. Bit arrays are allocated on the + * heap for the "live" variable and on the stack for the in, out, gen + * and kill variables. Therefore, for a temp number, the bit number must + * be biased with tempmin. + * + * There may be an idea to use a different data structure to store + * pass2 allocated temporaries, because they are very sparse. + */ + +#ifdef PCC_DEBUG +static void +LIVEADD(int x) +{ + RDEBUG(("Liveadd: %d\n", x)); + if (x < tempmin || x >= tempmax) + comperr("LIVEADD: out of range"); + BITSET(live, (x-tempmin)); +} +static void +LIVEDEL(int x) +{ + RDEBUG(("Livedel: %d\n", x)); + if (x < tempmin || x >= tempmax) + comperr("LIVEDEL: out of range"); + BITCLEAR(live, (x-tempmin)); +} +#else +#define LIVEADD(x) BITSET(live, (x-tempmin)) +#define LIVEDEL(x) BITCLEAR(live, (x-tempmin)) +#endif + +static struct lives { + DLIST_ENTRY(lives) link; + REGW *var; +} lused, lunused; + +static void +LIVEADDR(REGW *x) +{ + struct lives *l; + +#ifdef PCC_DEBUG + RDEBUG(("LIVEADDR: %d\n", x->nodnum)); + DLIST_FOREACH(l, &lused, link) + if (l->var == x) + return; +// comperr("LIVEADDR: multiple %d", ASGNUM(x)); +#endif + if (!DLIST_ISEMPTY(&lunused, link)) { + l = DLIST_NEXT(&lunused, link); + DLIST_REMOVE(l, link); + } else + l = tmpalloc(sizeof(struct lives)); + + l->var = x; + DLIST_INSERT_AFTER(&lused, l, link); +} + +static void +LIVEDELR(REGW *x) +{ + struct lives *l; + + RDEBUG(("LIVEDELR: %d\n", x->nodnum)); + DLIST_FOREACH(l, &lused, link) { + if (l->var != x) + continue; + DLIST_REMOVE(l, link); + DLIST_INSERT_AFTER(&lunused, l, link); + return; + } +// comperr("LIVEDELR: %p not found", x); +} + +#define MOVELISTADD(t, p) movelistadd(t, p) +#define WORKLISTMOVEADD(s,d) worklistmoveadd(s,d) + +static void +movelistadd(REGW *t, REGM *p) +{ + MOVL *w = tmpalloc(sizeof(MOVL)); + + w->regm = p; + w->next = t->r_moveList; + t->r_moveList = w; +} + +static REGM * +worklistmoveadd(REGW *src, REGW *dst) +{ + REGM *w = tmpalloc(sizeof(REGM)); + + DLIST_INSERT_AFTER(&worklistMoves, w, link); + w->src = src; + w->dst = dst; + w->queue = WLIST; + return w; +} + +struct AdjSet { + struct AdjSet *next; + REGW *u, *v; +} *edgehash[256]; + +/* Check if a node pair is adjacent */ +static int +adjSet(REGW *u, REGW *v) +{ + struct AdjSet *w; + REGW *t; + + if (ONLIST(u) == &precolored) { + ADJL *a = ADJLIST(v); + /* + * Check if any of the registers that have edges against v + * alias to u. + */ + for (; a; a = a->r_next) { + if (ONLIST(a->a_temp) != &precolored) + continue; + t = a->a_temp; + if (interferes(t - ablock, u - ablock)) + return 1; + } + } + if (u > v) + t = v, v = u, u = t; + w = edgehash[((int)u+(int)v) & 255]; + for (; w; w = w->next) { + if (u == w->u && v == w->v) + return 1; + } + return 0; +} + +/* Add a pair to adjset. No check for dups */ +static void +adjSetadd(REGW *u, REGW *v) +{ + struct AdjSet *w; + int x; + REGW *t; + + if (u > v) + t = v, v = u, u = t; + x = ((int)u+(int)v) & 255; + w = tmpalloc(sizeof(struct AdjSet)); + w->u = u, w->v = v; + w->next = edgehash[x]; + edgehash[x] = w; +} + +/* + * Add an interference edge between two nodes. + */ +static void +AddEdge(REGW *u, REGW *v) +{ + ADJL *x; + + RRDEBUG(("AddEdge: u %d v %d\n", ASGNUM(u), ASGNUM(v))); + +#ifdef PCC_DEBUG +#if 0 + if (ASGNUM(u) == 0) + comperr("AddEdge 0"); +#endif + if (CLASS(u) == 0 || CLASS(v) == 0) + comperr("AddEdge class == 0 (%d=%d, %d=%d)", + CLASS(u), ASGNUM(u), CLASS(v), ASGNUM(v)); +#endif + + if (u == v) + return; + if (adjSet(u, v)) + return; + + adjSetadd(u, v); + +#if 0 + if (ONLIST(u) == &precolored || ONLIST(v) == &precolored) + comperr("precolored node in AddEdge"); +#endif + + if (ONLIST(u) != &precolored) { + x = tmpalloc(sizeof(ADJL)); + x->a_temp = v; + x->r_next = u->r_adjList; + u->r_adjList = x; + NCLASS(u, CLASS(v))++; + } + + if (ONLIST(v) != &precolored) { + x = tmpalloc(sizeof(ADJL)); + x->a_temp = u; + x->r_next = v->r_adjList; + v->r_adjList = x; + NCLASS(v, CLASS(u))++; + } + +#if 0 + RDEBUG(("AddEdge: u %d(d %d) v %d(d %d)\n", u, DEGREE(u), v, DEGREE(v))); +#endif +} + +static int +MoveRelated(REGW *n) +{ + MOVL *l; + REGM *w; + + for (l = MOVELIST(n); l; l = l->next) { + w = l->regm; + if (w->queue == ACTIVE || w->queue == WLIST) + return 1; + } + return 0; +} + +static void +MkWorklist(void) +{ + REGW *w; + + RDX(int s=0); + RDX(int f=0); + RDX(int d=0); + + DLIST_INIT(&precolored, link); + DLIST_INIT(&simplifyWorklist, link); + DLIST_INIT(&freezeWorklist, link); + DLIST_INIT(&spillWorklist, link); + DLIST_INIT(&spilledNodes, link); + DLIST_INIT(&coalescedNodes, link); + DLIST_INIT(&coloredNodes, link); + DLIST_INIT(&selectStack, link); + + /* + * Remove all nodes from the initial list and put them on + * one of the worklists. + */ + while (!DLIST_ISEMPTY(&initial, link)) { + w = DLIST_NEXT(&initial, link); + DLIST_REMOVE(w, link); + if (!trivially_colorable(w)) { + PUSHWLIST(w, spillWorklist); + RDX(s++); + } else if (MoveRelated(w)) { + PUSHWLIST(w, freezeWorklist); + RDX(f++); + } else { + PUSHWLIST(w, simplifyWorklist); + RDX(d++); + } + } + RDEBUG(("MkWorklist: spill %d freeze %d simplify %d\n", s,f,d)); +} + +static void +addalledges(REGW *e) +{ + int i, j, k; + int nbits = xbits; + struct lives *l; + + RDEBUG(("addalledges for %d\n", e->nodnum)); + + if (e->r_class == -1) + return; /* unused */ + + if (ONLIST(e) != &precolored) { + for (i = 0; ndontregs[i] >= 0; i++) + AddEdge(e, &ablock[ndontregs[i]]); + } + + /* First add to long-lived temps */ + RDEBUG(("addalledges longlived ")); + for (i = 0; i < nbits; i += NUMBITS) { + if ((k = live[i/NUMBITS]) == 0) + continue; + while (k) { + j = ffs(k)-1; + AddEdge(&nblock[i+j+tempmin], e); + RRDEBUG(("%d ", i+j+tempmin)); + k &= ~(1 << j); + } + } + RDEBUG(("done\n")); + /* short-lived temps */ + RDEBUG(("addalledges shortlived ")); + DLIST_FOREACH(l, &lused, link) { + RRDEBUG(("%d ", ASGNUM(l->var))); + AddEdge(l->var, e); + } + RDEBUG(("done\n")); +} + +/* + * Add a move edge between def and use. + */ +static void +moveadd(REGW *def, REGW *use) +{ + REGM *r; + + if (def == use) + return; /* no move to itself XXX - ``shouldn't happen'' */ + RDEBUG(("moveadd: def %d use %d\n", ASGNUM(def), ASGNUM(use))); + + r = WORKLISTMOVEADD(use, def); + MOVELISTADD(def, r); + MOVELISTADD(use, r); +} + +/* + * Traverse arguments backwards. + * XXX - can this be tricked in some other way? + */ +static void +argswalk(NODE *p) +{ + + if (p->n_op == CM) { + argswalk(p->n_left); + insnwalk(p->n_right); + } else + insnwalk(p); +} + +/* + * Add to (or remove from) live set variables that must not + * be clobbered when traversing down on the other leg for + * a BITYPE node. + */ +static void +setlive(NODE *p, int set, REGW *rv) +{ + if (rv != NULL) + return set ? LIVEADDR(rv) : LIVEDELR(rv); + + if (p->n_regw != NULL) + return set ? LIVEADDR(p->n_regw) : LIVEDELR(p->n_regw); + + switch (optype(p->n_op)) { + case LTYPE: + if (p->n_op == TEMP) + set ? LIVEADD((int)p->n_lval) : LIVEDEL((int)p->n_lval); +#ifdef notyet + else if (p->n_op == REG) + ... +#endif + break; + case BITYPE: + setlive(p->n_right, set, rv); + /* FALLTHROUGH */ + case UTYPE: + setlive(p->n_left, set, rv); + break; + } +} + +/* + * Add edges for temporary w against all temporaries that may be + * used simultaneously (like index registers). + */ +static void +addedge_r(NODE *p, REGW *w) +{ + if (p->n_regw != NULL) + return AddEdge(p->n_regw, w); + + if (optype(p->n_op) == BITYPE) + addedge_r(p->n_right, w); + if (optype(p->n_op) != LTYPE) + addedge_r(p->n_left, w); +} + +/* + * Do the in-tree part of liveness analysis. (the difficult part) + * + * Walk down the tree in reversed-evaluation order (backwards). + * The moves and edges inserted and evaluation order for + * instructions when code is emitted is described here, hence + * this code runs the same but backwards. + * + * 2-op reclaim LEFT: eval L, move to DEST, eval R. + * moveadd L,DEST; addedge DEST,R + * 2-op reclaim LEFT DORIGHT: eval R, eval L, move to DEST. + * moveadd L,DEST; addedge DEST,R; addedge L,R + * 2-op reclaim RIGHT; eval L, eval R, move to DEST. + * moveadd R,DEST; addedge DEST,L; addedge L,R + * 2-op reclaim RIGHT DORIGHT: eval R, move to DEST, eval L. + * moveadd R,DEST; addedge DEST,L + * 3-op: eval L, eval R + * addedge L,R + * 3-op DORIGHT: eval R, eval L + * addedge L,R + * + * Instructions with special needs are handled just like these variants, + * with the exception of extra added moves and edges. + * Moves to special regs are scheduled after the evaluation of both legs. + */ + +#define ASGLEFT(p) (p->n_op == ASSIGN && p->n_left->n_op == TEMP) + +static void +insnwalk(NODE *p) +{ + int o = p->n_op; + struct optab *q = &table[TBLIDX(p->n_su)]; + REGW *lr, *rr, *rv, *r, *rrv, *lrv; + int i, n; + + RDEBUG(("insnwalk %p\n", p)); + + rv = p->n_regw; + + rrv = lrv = NULL; + if (ASGLEFT(p)) { + int v = p->n_left->n_lval; + LIVEDEL(v); /* remove assigned temp from live set */ + addalledges(&nblock[v]); + } + + /* Add edges for the result of this node */ + if (rv && (q->visit & INREGS || o == TEMP)) + addalledges(rv); + + /* special handling of CALL operators */ + if (callop(o)) { + if (rv) + moveadd(rv, &ablock[RETREG(p->n_type)]); + for (i = 0; tempregs[i] >= 0; i++) + addalledges(&ablock[tempregs[i]]); + } + + /* for special return value registers add moves */ + if ((q->needs & NSPECIAL) && (n = rspecial(q, NRES)) >= 0) { + rv = &ablock[n]; + moveadd(p->n_regw, rv); + } + + /* Check leaves for results in registers */ + lr = optype(o) != LTYPE ? p->n_left->n_regw : NULL; + rr = optype(o) == BITYPE ? p->n_right->n_regw : NULL; + + /* simple needs */ + n = ncnt(q->needs); + for (i = 0; i < n; i++) { +#if 1 + static int ncl[] = { 0, NASL, NBSL, NCSL, NDSL }; + static int ncr[] = { 0, NASR, NBSR, NCSR, NDSR }; + + /* edges are already added */ + if ((r = &p->n_regw[1+i])->r_class == -1) + r = p->n_regw; + else + addalledges(r); + if (optype(o) != LTYPE && (q->needs & ncl[CLASS(r)]) == 0) + addedge_r(p->n_left, r); + if (optype(o) == BITYPE && (q->needs & ncr[CLASS(r)]) == 0) + addedge_r(p->n_right, r); +#else + if ((r = &p->n_regw[1+i])->r_class == -1) + continue; + addalledges(r); + if (optype(o) != LTYPE && (q->needs & NASL) == 0) + addedge_r(p->n_left, r); + if (optype(o) == BITYPE && (q->needs & NASR) == 0) + addedge_r(p->n_right, r); +#endif + } + + /* special needs */ + if (q->needs & NSPECIAL) { + struct rspecial *rc; + for (rc = nspecial(q); rc->op; rc++) { + switch (rc->op) { +#define ONLY(c,s) if (c) s(c, &ablock[rc->num]) + case NLEFT: + addalledges(&ablock[rc->num]); + ONLY(lr, moveadd); + break; + case NOLEFT: + addedge_r(p->n_left, &ablock[rc->num]); + break; + case NRIGHT: + addalledges(&ablock[rc->num]); + ONLY(rr, moveadd); + break; + case NORIGHT: + addedge_r(p->n_right, &ablock[rc->num]); + break; + case NEVER: + addalledges(&ablock[rc->num]); + break; +#undef ONLY + } + } + } + + if (o == ASSIGN) { + /* needs special treatment */ + if (lr && rr) + moveadd(lr, rr); + if (lr && rv) + moveadd(lr, rv); + if (rr && rv) + moveadd(rr, rv); + } else if (callop(o)) { +#ifdef notdef + /* calls needs special treatment */ + for (i = 0; tempregs[i] >= 0; i++) + addalledges(&ablock[i]); + if (rv) + moveadd(rv, &ablock[RETREG(p->n_type)]); +#endif + /* XXX - here must all live arg registers be added + * for archs with arguments in registers */ + } else if (q->rewrite & (RESC1|RESC2|RESC3)) { + if (lr && rr) + AddEdge(lr, rr); + } else if (q->rewrite & RLEFT) { + if (lr && rv) + moveadd(rv, lr), lrv = rv; + if (rr && rv) + AddEdge(rr, rv); + } else if (q->rewrite & RRIGHT) { + if (rr && rv) + moveadd(rv, rr), rrv = rv; + if (lr && rv) + AddEdge(lr, rv); + } + + switch (optype(o)) { + case BITYPE: + if (ASGLEFT(p)) { + /* only go down right node */ + insnwalk(p->n_right); + } else if (callop(o)) { + insnwalk(p->n_left); + /* Do liveness analysis on arguments (backwards) */ + argswalk(p->n_right); + } else if ((p->n_su & DORIGHT) == 0) { + setlive(p->n_left, 1, lrv); + insnwalk(p->n_right); + setlive(p->n_left, 0, lrv); + insnwalk(p->n_left); + } else { + setlive(p->n_right, 1, rrv); + insnwalk(p->n_left); + setlive(p->n_right, 0, rrv); + insnwalk(p->n_right); + } + break; + + case UTYPE: + insnwalk(p->n_left); + break; + + case LTYPE: + switch (o) { + case TEMP: + rr = &nblock[(int)p->n_lval]; + if (rv != rr) { + addalledges(rr); + moveadd(rv, rr); + } + LIVEADD((int)p->n_lval); + break; + case REG: + case OREG: + /* Liveness for regs??? */ + break; + default: + break; + } + break; + } +} + +static bittype **gen, **kill, **in, **out; + +static void +unionize(NODE *p, int bb) +{ + int i, o, ty; + + if ((o = p->n_op) == TEMP) { +#ifdef notyet + for (i = 0; i < szty(p->n_type); i++) { + BITSET(gen[bb], ((int)p->n_lval - tempmin+i)); + } +#else + i = 0; + BITSET(gen[bb], ((int)p->n_lval - tempmin+i)); +#endif + } + if (asgop(o) && p->n_left->n_op == TEMP) { + int b = p->n_left->n_lval - tempmin; +#ifdef notyet + for (i = 0; i < szty(p->n_type); i++) { + BITCLEAR(gen[bb], (b+i)); + BITSET(kill[bb], (b+i)); + } +#else + i = 0; + BITCLEAR(gen[bb], (b+i)); + BITSET(kill[bb], (b+i)); +#endif + unionize(p->n_right, bb); + return; + } + ty = optype(o); + if (ty != LTYPE) + unionize(p->n_left, bb); + if (ty == BITYPE) + unionize(p->n_right, bb); +} + +/* + * Do variable liveness analysis. Only analyze the long-lived + * variables, and save the live-on-exit temporaries in a bit-field + * at the end of each basic block. This bit-field is later used + * when doing short-range liveness analysis in Build(). + */ +static void +LivenessAnalysis(void) +{ + extern struct basicblock bblocks; + struct basicblock *bb; + struct interpass *ip; + int i, bbnum; + + /* + * generate the gen-kill sets for all basic blocks. + */ + DLIST_FOREACH(bb, &bblocks, bbelem) { + bbnum = bb->bbnum; + for (ip = bb->last; ; ip = DLIST_PREV(ip, qelem)) { + /* gen/kill is 'p', this node is 'n' */ + if (ip->type == IP_NODE) + unionize(ip->ip_node, bbnum); + if (ip == bb->first) + break; + } + memcpy(in[bbnum], gen[bbnum], BIT2BYTE(tempmax-tempmin)); +#ifdef PCC_DEBUG + if (rdebug) { + printf("basic block %d\ngen: ", bbnum); + for (i = 0; i < tempmax-tempmin; i++) + if (TESTBIT(gen[bbnum], i)) + printf("%d ", i+tempmin); + printf("\nkill: "); + for (i = 0; i < tempmax-tempmin; i++) + if (TESTBIT(kill[bbnum], i)) + printf("%d ", i+tempmin); + printf("\n"); + } +#endif + } +} + +#define SETCOPY(t,f,i,n) for (i = 0; i < n/NUMBITS; i++) t[i] = f[i] +#define SETSET(t,f,i,n) for (i = 0; i < n/NUMBITS; i++) t[i] |= f[i] +#define SETCLEAR(t,f,i,n) for (i = 0; i < n/NUMBITS; i++) t[i] &= ~f[i] +#define SETCMP(v,t,f,i,n) for (i = 0; i < n/NUMBITS; i++) \ + if (t[i] != f[i]) v = 1 + +/* + * Build the set of interference edges and adjacency list. + */ +static void +Build(struct interpass *ipole) +{ + extern struct basicblock bblocks; + struct basicblock bbfake; + struct interpass *ip; + struct basicblock *bb; + struct cfgnode *cn; + extern int nbblocks; + bittype *saved; + int i, j, again, nbits; + + if (xtemps == 0) { + /* + * No basic block splitup is done if not optimizing, + * so fake one basic block to keep the liveness analysis + * happy. + */ + nbblocks = 1; + bbfake.bbnum = 0; + bbfake.last = DLIST_PREV(ipole, qelem); + bbfake.first = DLIST_NEXT(ipole, qelem); + DLIST_INIT(&bblocks, bbelem); + DLIST_INSERT_AFTER(&bblocks, &bbfake, bbelem); + SLIST_INIT(&bbfake.children); + } + + /* Just fetch space for the temporaries from stack */ + nbits = xbits+(NUMBITS-1); + gen = alloca(nbblocks*sizeof(bittype*)); + kill = alloca(nbblocks*sizeof(bittype*)); + in = alloca(nbblocks*sizeof(bittype*)); + out = alloca(nbblocks*sizeof(bittype*)); + for (i = 0; i < nbblocks; i++) { + BITALLOC(gen[i],alloca,nbits); + BITALLOC(kill[i],alloca,nbits); + BITALLOC(in[i],alloca,nbits); + BITALLOC(out[i],alloca,nbits); + } + BITALLOC(saved,alloca,nbits); + LivenessAnalysis(); + + /* register variable temporaries are live */ + for (i = 0; i < NPERMREG-1; i++) { + if (nsavregs[i]) + continue; + BITSET(out[nbblocks-1], i); + for (j = i+1; j < NPERMREG-1; j++) { + if (nsavregs[j]) + continue; + AddEdge(&nblock[i+tempmin], &nblock[j+tempmin]); + } + } + + /* do liveness analysis on basic block level */ + do { + again = 0; + /* XXX - loop should be in reversed execution-order */ + DLIST_FOREACH_REVERSE(bb, &bblocks, bbelem) { + int i = bb->bbnum; + SETCOPY(saved, out[i], j, nbits); + SLIST_FOREACH(cn, &bb->children, cfgelem) { + SETSET(out[i], in[cn->bblock->bbnum], + j, nbits); + } + SETCMP(again, saved, out[i], j, nbits); + SETCOPY(saved, in[i], j, nbits); + SETCOPY(in[i], out[i], j, nbits); + SETCLEAR(in[i], kill[i], j, nbits); + SETSET(in[i], gen[i], j, nbits); + SETCMP(again, saved, in[i], j, nbits); + } + } while (again); + +#ifdef PCC_DEBUG + if (rdebug) { + DLIST_FOREACH(bb, &bblocks, bbelem) { + printf("basic block %d\nin: ", bb->bbnum); + for (i = 0; i < tempmax-tempmin; i++) + if (TESTBIT(in[bb->bbnum], i)) + printf("%d ", i+tempmin); + printf("\nout: "); + for (i = 0; i < tempmax-tempmin; i++) + if (TESTBIT(out[bb->bbnum], i)) + printf("%d ", i+tempmin); + printf("\n"); + } + } +#endif + + DLIST_FOREACH(bb, &bblocks, bbelem) { + RDEBUG(("liveadd bb %d\n", bb->bbnum)); + i = bb->bbnum; + for (j = 0; j < (tempmax-tempmin); j += NUMBITS) + live[j/NUMBITS] = 0; + SETCOPY(live, out[i], j, nbits); + for (ip = bb->last; ; ip = DLIST_PREV(ip, qelem)) { + if (ip->type == IP_NODE) + insnwalk(ip->ip_node); + if (ip == bb->first) + break; + } + } + +#ifdef PCC_DEBUG + if (rdebug) { + int i; + struct AdjSet *w; + ADJL *x; + REGW *y; + MOVL *m; + + printf("Interference edges\n"); + for (i = 0; i < 256; i++) { + if ((w = edgehash[i]) == NULL) + continue; + for (; w; w = w->next) + printf("%d <-> %d\n", ASGNUM(w->u), ASGNUM(w->v)); + } + printf("Degrees\n"); + DLIST_FOREACH(y, &initial, link) { + printf("%d (%c): trivial [%d] ", ASGNUM(y), + CLASS(y)+'@', trivially_colorable(y)); + for (x = ADJLIST(y); x; x = x->r_next) { + if (ONLIST(x->a_temp) != &selectStack && + ONLIST(x->a_temp) != &coalescedNodes) + printf("%d ", ASGNUM(x->a_temp)); + else + printf("(%d) ", ASGNUM(x->a_temp)); + } + printf("\n"); + } + printf("Move nodes\n"); + DLIST_FOREACH(y, &initial, link) { + if (MOVELIST(y) == NULL) + continue; + printf("%d: ", ASGNUM(y)); + for (m = MOVELIST(y); m; m = m->next) { + REGW *yy = m->regm->src == y ? + m->regm->dst : m->regm->src; + printf("%d ", ASGNUM(yy)); + } + printf("\n"); + } + } +#endif + +} + +static void +EnableMoves(REGW *n) +{ + MOVL *l; + REGM *m; + + for (l = MOVELIST(n); l; l = l->next) { + m = l->regm; + if (m->queue != ACTIVE) + continue; + DLIST_REMOVE(m, link); + PUSHMLIST(m, worklistMoves, WLIST); + } +} + +static void +EnableAdjMoves(REGW *nodes) +{ + ADJL *w; + REGW *n; + + EnableMoves(nodes); + for (w = ADJLIST(nodes); w; w = w->r_next) { + n = w->a_temp; + if (ONLIST(n) == &selectStack || ONLIST(n) == &coalescedNodes) + continue; + EnableMoves(w->a_temp); + } +} + +/* + * Decrement the degree of node w for class c. + */ +static void +DecrementDegree(REGW *w, int c) +{ + int wast; + + RRDEBUG(("DecrementDegree: w %d, c %d\n", ASGNUM(w), c)); + + wast = trivially_colorable(w); + NCLASS(w, c)--; + if (wast == trivially_colorable(w)) + return; + + EnableAdjMoves(w); + DELWLIST(w); + ONLIST(w) = 0; + if (MoveRelated(w)) { + PUSHWLIST(w, freezeWorklist); + } else { + PUSHWLIST(w, simplifyWorklist); + } +} + +static void +Simplify(void) +{ + REGW *w; + ADJL *l; + + w = POPWLIST(simplifyWorklist); + PUSHWLIST(w, selectStack); + RDEBUG(("Simplify: node %d class %d\n", ASGNUM(w), w->r_class)); + + l = w->r_adjList; + for (; l; l = l->r_next) { + if (ONLIST(l->a_temp) == &selectStack || + ONLIST(l->a_temp) == &coalescedNodes) + continue; + DecrementDegree(l->a_temp, w->r_class); + } +} + +static REGW * +GetAlias(REGW *n) +{ + if (ONLIST(n) == &coalescedNodes) + return GetAlias(ALIAS(n)); + return n; +} + +static int +OK(REGW *t, REGW *r) +{ + RDEBUG(("OK: t %d CLASS(t) %d adjSet(%d,%d)=%d\n", + ASGNUM(t), CLASS(t), ASGNUM(t), ASGNUM(r), adjSet(t, r))); + +#ifdef PCC_DEBUG + if (rdebug > 1) { + ADJL *w; + int ndeg = 0; + printf("OK degree: "); + for (w = ADJLIST(t); w; w = w->r_next) { + if (ONLIST(w->a_temp) != &selectStack && + ONLIST(w->a_temp) != &coalescedNodes) + printf("%c%d ", CLASS(w->a_temp)+'@', + ASGNUM(w->a_temp)), ndeg++; + else + printf("(%d) ", ASGNUM(w->a_temp)); + } + printf("\n"); +#if 0 + if (ndeg != DEGREE(t) && DEGREE(t) >= 0) + printf("!!!ndeg %d != DEGREE(t) %d\n", ndeg, DEGREE(t)); +#endif + } +#endif + + if (trivially_colorable(t) || ONLIST(t) == &precolored || + (adjSet(t, r) || !aliasmap(CLASS(t), COLOR(r))))/* XXX - check aliasmap */ + return 1; + return 0; +} + +static int +adjok(REGW *v, REGW *u) +{ + ADJL *w; + REGW *t; + + RDEBUG(("adjok\n")); + for (w = ADJLIST(v); w; w = w->r_next) { + t = w->a_temp; + if (ONLIST(t) == &selectStack || ONLIST(t) == &coalescedNodes) + continue; + if (OK(t, u) == 0) + return 0; + } + RDEBUG(("adjok returns OK\n")); + return 1; +} + +#define oldcons /* check some more */ +/* + * Do a conservative estimation of whether two temporaries can + * be coalesced. This is "Briggs-style" check. + * Neither u nor v is precolored when called. + */ +static int +Conservative(REGW *u, REGW *v) +{ + ADJL *w, *ww; + REGW *n; +#ifdef oldcons + int i, ncl[NUMCLASS+1]; + + if (CLASS(u) != CLASS(v)) + comperr("Conservative: u(%d = %d), v(%d = %d)", + ASGNUM(u), CLASS(u), ASGNUM(v), CLASS(v)); + + for (i = 0; i < NUMCLASS+1; i++) + ncl[i] = 0; + + RDEBUG(("Conservative (%d,%d)\n", ASGNUM(u), ASGNUM(v))); + + for (w = ADJLIST(u); w; w = w->r_next) { + n = w->a_temp; + if (ONLIST(n) == &selectStack || ONLIST(n) == &coalescedNodes) + continue; + for (ww = ADJLIST(v); ww; ww = ww->r_next) + if (ww->a_temp == n) + break; + if (ww) + continue; + if (!trivially_colorable(n)) + ncl[CLASS(n)]++; + } + for (w = ADJLIST(v); w; w = w->r_next) { + n = w->a_temp; + if (ONLIST(n) == &selectStack || ONLIST(n) == &coalescedNodes) + continue; + if (!trivially_colorable(n)) + ncl[CLASS(n)]++; + } + i = trivially_colorable_p(CLASS(u), ncl); +#endif +{ + int xncl[NUMCLASS+1], mcl = 0, j; + for (j = 0; j < NUMCLASS+1; j++) + xncl[j] = 0; + /* + * Increment xncl[class] up to K for each class. + * If all classes has reached K then check colorability and return. + */ + for (w = ADJLIST(u); w; w = w->r_next) { + n = w->a_temp; + if (ONLIST(n) == &selectStack || ONLIST(n) == &coalescedNodes) + continue; + if (xncl[CLASS(n)] == regK[CLASS(n)]) + continue; + if (!trivially_colorable(n)) + xncl[CLASS(n)]++; + if (xncl[CLASS(n)] < regK[CLASS(n)]) + continue; + if (++mcl == NUMCLASS) + goto out; /* cannot get more out of it */ + } + for (w = ADJLIST(v); w; w = w->r_next) { + n = w->a_temp; + if (ONLIST(n) == &selectStack || ONLIST(n) == &coalescedNodes) + continue; + if (xncl[CLASS(n)] == regK[CLASS(n)]) + continue; + /* ugly: have we been here already? */ + for (ww = ADJLIST(u); ww; ww = ww->r_next) + if (ww->a_temp == n) + break; + if (ww) + continue; + if (!trivially_colorable(n)) + xncl[CLASS(n)]++; + if (xncl[CLASS(n)] < regK[CLASS(n)]) + continue; + if (++mcl == NUMCLASS) + break; + } +out: j = trivially_colorable_p(CLASS(u), xncl); +#ifdef oldcons + if (j != i) + comperr("Conservative: j %d i %d", j, i); +#else + return j; +#endif +} +#ifdef oldcons + RDEBUG(("Conservative i=%d\n", i)); + return i; +#endif +} + +static void +AddWorkList(REGW *w) +{ + + if (ONLIST(w) != &precolored && !MoveRelated(w) && + trivially_colorable(w)) { + DELWLIST(w); + PUSHWLIST(w, simplifyWorklist); + } +} + +static void +Combine(REGW *u, REGW *v) +{ + MOVL *m; + ADJL *l; + REGW *t; + + RDEBUG(("Combine (%d,%d)\n", ASGNUM(u), ASGNUM(v))); + + if (ONLIST(v) == &freezeWorklist) { + DELWLIST(v); + } else { + DELWLIST(v); + } + PUSHWLIST(v, coalescedNodes); + ALIAS(v) = u; + if (rdebug) { + printf("adjlist(%d): ", ASGNUM(v)); + for (l = ADJLIST(v); l; l = l->r_next) + printf("%d ", l->a_temp->nodnum); + printf("\n"); + } +#if 1 +{ + MOVL *m0 = MOVELIST(v); + + for (m0 = MOVELIST(v); m0; m0 = m0->next) { + for (m = MOVELIST(u); m; m = m->next) + if (m->regm == m0->regm) + break; /* Already on list */ + if (m) + continue; /* already on list */ + MOVELISTADD(u, m0->regm); + } +} +#else + + if ((m = MOVELIST(u))) { + while (m->next) + m = m->next; + m->next = MOVELIST(v); + } else + MOVELIST(u) = MOVELIST(v); +#endif + EnableMoves(v); + for (l = ADJLIST(v); l; l = l->r_next) { + t = l->a_temp; + if (ONLIST(t) == &selectStack || ONLIST(t) == &coalescedNodes) + continue; + /* Do not add edge if u cannot affect the colorability of t */ + /* XXX - check aliasmap */ + if (ONLIST(u) != &precolored || aliasmap(CLASS(t), COLOR(u))) + AddEdge(t, u); + DecrementDegree(t, CLASS(v)); + } + if (!trivially_colorable(u) && ONLIST(u) == &freezeWorklist) { + DELWLIST(u); + PUSHWLIST(u, spillWorklist); + } +if (rdebug) { + ADJL *w; + printf("Combine %d class (%d): ", ASGNUM(u), CLASS(u)); + for (w = ADJLIST(u); w; w = w->r_next) { + if (ONLIST(w->a_temp) != &selectStack && + ONLIST(w->a_temp) != &coalescedNodes) + printf("%d ", ASGNUM(w->a_temp)); + else + printf("(%d) ", ASGNUM(w->a_temp)); + } + printf("\n"); +} +} + +static void +Coalesce(void) +{ + REGM *m; + REGW *x, *y, *u, *v; + + m = POPMLIST(worklistMoves); + x = GetAlias(m->src); + y = GetAlias(m->dst); + + if (ONLIST(y) == &precolored) + u = y, v = x; + else + u = x, v = y; + + RDEBUG(("Coalesce: src %d dst %d u %d v %d x %d y %d\n", + ASGNUM(m->src), ASGNUM(m->dst), ASGNUM(u), ASGNUM(v), + ASGNUM(x), ASGNUM(y))); + + if (CLASS(m->src) != CLASS(m->dst)) + comperr("Coalesce: src class %d, dst class %d", + CLASS(m->src), CLASS(m->dst)); + + if (u == v) { + RDEBUG(("Coalesce: u == v\n")); + PUSHMLIST(m, coalescedMoves, COAL); + AddWorkList(u); + } else if (ONLIST(v) == &precolored || adjSet(u, v)) { + RDEBUG(("Coalesce: constrainedMoves\n")); + PUSHMLIST(m, constrainedMoves, CONSTR); + AddWorkList(u); + AddWorkList(v); + } else if ((ONLIST(u) == &precolored && adjok(v, u)) || + (ONLIST(u) != &precolored && Conservative(u, v))) { + RDEBUG(("Coalesce: Conservative\n")); + PUSHMLIST(m, coalescedMoves, COAL); + Combine(u, v); + AddWorkList(u); + } else { + RDEBUG(("Coalesce: activeMoves\n")); + PUSHMLIST(m, activeMoves, ACTIVE); + } +} + +static void +FreezeMoves(REGW *u) +{ + MOVL *w, *o; + REGM *m; + REGW *z; + REGW *x, *y, *v; + + for (w = MOVELIST(u); w; w = w->next) { + m = w->regm; + if (m->queue != WLIST && m->queue != ACTIVE) + continue; + x = m->src; + y = m->dst; + if (GetAlias(y) == GetAlias(u)) + v = GetAlias(x); + else + v = GetAlias(y); + RDEBUG(("FreezeMoves: u %d (%d,%d) v %d\n", + ASGNUM(u),ASGNUM(x),ASGNUM(y),ASGNUM(v))); + DLIST_REMOVE(m, link); + PUSHMLIST(m, frozenMoves, FROZEN); + if (ONLIST(v) != &freezeWorklist) + continue; + for (o = MOVELIST(v); o; o = o->next) + if (o->regm->queue == WLIST || o->regm->queue == ACTIVE) + break; + if (o == NULL) { + z = v; + DELWLIST(z); + PUSHWLIST(z, simplifyWorklist); + } + } +} + +static void +Freeze(void) +{ + REGW *u; + + /* XXX + * Should check if the moves to freeze have exactly the same + * interference edges. If they do, coalesce them instead, it + * may free up other nodes that they interfere with. + */ + u = POPWLIST(freezeWorklist); + PUSHWLIST(u, simplifyWorklist); + RDEBUG(("Freeze %d\n", ASGNUM(u))); + FreezeMoves(u); +} + +static void +SelectSpill(void) +{ + REGW *w; + + RDEBUG(("SelectSpill\n")); + if (rdebug) + DLIST_FOREACH(w, &spillWorklist, link) + printf("SelectSpill: %d\n", ASGNUM(w)); + + /* First check if we can spill register variables */ + DLIST_FOREACH(w, &spillWorklist, link) { + if (w >= &nblock[tempmin] && w < &nblock[basetemp]) + break; + } + + if (w == &spillWorklist) { + /* try to find another long-range variable */ + DLIST_FOREACH(w, &spillWorklist, link) { + if (w >= &nblock[tempmin] && w < &nblock[tempmax]) + break; + } + } + + if (w == &spillWorklist) { + /* no heuristics, just fetch first element */ + w = DLIST_NEXT(&spillWorklist, link); + } + + DLIST_REMOVE(w, link); + + PUSHWLIST(w, simplifyWorklist); + RDEBUG(("Freezing node %d\n", ASGNUM(w))); + FreezeMoves(w); +} + +int gregn(REGW *); + +int +gregn(REGW *w) +{ + return w->nodnum; +} + +/* + * Set class on long-lived temporaries based on its type. + */ +static void +traclass(NODE *p) +{ + REGW *nb; + + if (p->n_op != TEMP) + return; + + nb = &nblock[(int)p->n_lval]; + if (CLASS(nb) == 0) + CLASS(nb) = gclass(p->n_type); +} + +static void +paint(NODE *p) +{ + struct optab *q; + REGW *w, *ww; + int i; + + if (p->n_regw != NULL) { + /* Must color all allocated regs also */ + ww = w = p->n_regw; + q = &table[TBLIDX(p->n_su)]; + p->n_reg = COLOR(w); + w++; + if (q->needs & ALLNEEDS) + for (i = 0; i < ncnt(q->needs); i++) { + if (w->r_class == -1) + p->n_reg |= ENCRA(COLOR(ww), i); + else + p->n_reg |= ENCRA(COLOR(w), i); + w++; + } + } else + p->n_reg = -1; + if (p->n_op == TEMP) { + REGW *nb = &nblock[(int)p->n_lval]; + p->n_rval = COLOR(nb); + if (TCLASS(p->n_su) == 0) + SCLASS(p->n_su, CLASS(nb)); + p->n_op = REG; + p->n_lval = 0; + } +} + +static void +AssignColors(struct interpass *ip) +{ + int okColors, c; + REGW *o, *w; + ADJL *x; + + RDEBUG(("AssignColors\n")); + while (!WLISTEMPTY(selectStack)) { + w = POPWLIST(selectStack); + okColors = classmask(CLASS(w)); + RDEBUG(("classmask av %d, class %d: %x\n", + w->nodnum, CLASS(w), okColors)); + + for (x = ADJLIST(w); x; x = x->r_next) { + o = GetAlias(x->a_temp); + RRDEBUG(("Adj(%d): %d (%d)\n", + ASGNUM(w), ASGNUM(o), ASGNUM(x->a_temp))); + + if (ONLIST(o) == &coloredNodes || + ONLIST(o) == &precolored) { + c = aliasmap(CLASS(w), COLOR(o)); + RRDEBUG(("aliasmap in class %d by color %d: " + "%x, okColors %x\n", + CLASS(w), COLOR(o), c, okColors)); + + okColors &= ~c; + } + } + if (okColors == 0) { + PUSHWLIST(w, spilledNodes); + RDEBUG(("Spilling node %d\n", ASGNUM(w))); + } else { + PUSHWLIST(w, coloredNodes); + c = ffs(okColors)-1; + COLOR(w) = color2reg(c, CLASS(w)); + RDEBUG(("Coloring %d with %s, free %x\n", + ASGNUM(w), rnames[COLOR(w)], okColors)); + } + } + DLIST_FOREACH(w, &coalescedNodes, link) { + REGW *ww = GetAlias(w); + COLOR(w) = COLOR(ww); + if (ONLIST(ww) == &spilledNodes) { + RDEBUG(("coalesced node %d spilled\n", w->nodnum)); + ww = DLIST_PREV(w, link); + DLIST_REMOVE(w, link); + PUSHWLIST(w, spilledNodes); + w = ww; + } else + RDEBUG(("Giving coalesced node %d color %s\n", + w->nodnum, rnames[COLOR(w)])); + } + + if (rdebug) + DLIST_FOREACH(w, &coloredNodes, link) + printf("%d: color %s\n", ASGNUM(w), rnames[COLOR(w)]); + if (DLIST_ISEMPTY(&spilledNodes, link)) { + struct interpass *ip2; + DLIST_FOREACH(ip2, ip, qelem) + if (ip2->type == IP_NODE) + walkf(ip2->ip_node, paint); + } +} + +static REGW *spole; +/* + * Store all spilled nodes in memory by fetching a temporary on the stack. + * Will never end up here if not optimizing. + */ +static void +longtemp(NODE *p) +{ + REGW *w; + + if (p->n_op != TEMP) + return; + /* XXX - should have a bitmask to find temps to convert */ + DLIST_FOREACH(w, spole, link) { + if (w != &nblock[(int)p->n_lval]) + continue; + if (w->r_class == 0) { + w->r_color = BITOOR(freetemp(szty(p->n_type))); + w->r_class = 1; + } + p->n_op = OREG; + p->n_lval = w->r_color; + p->n_rval = FPREG; + p->n_regw = NULL; + break; + } +} + +static struct interpass *cip; +/* + * Rewrite a tree by storing a variable in memory. + * XXX - must check if basic block structure is destroyed! + */ +static void +shorttemp(NODE *p) +{ + struct interpass *nip; + REGW *w; + NODE *l, *r; + int off; + + /* XXX - optimize this somewhat */ + DLIST_FOREACH(w, spole, link) { + if (w != p->n_regw) + continue; + /* XXX - use canaddr() */ + if (p->n_op == OREG || p->n_op == NAME) { + DLIST_REMOVE(w, link); + RDEBUG(("Node %d already in memory\n", ASGNUM(w))); + break; + } + RDEBUG(("rewriting node %d\n", ASGNUM(w))); + off = BITOOR(freetemp(szty(p->n_type))); + l = mklnode(OREG, off, FPREG, p->n_type); + r = talloc(); + *r = *p; + nip = ipnode(mkbinode(ASSIGN, l, r, p->n_type)); + *p = *l; + DLIST_INSERT_BEFORE(cip, nip, qelem); + DLIST_REMOVE(w, link); + break; + } +} + +/* + * Change the TEMPs in the ipole list to stack variables. + */ +static void +treerewrite(struct interpass *ipole, REGW *rpole) +{ + struct interpass *ip; + + spole = rpole; + + DLIST_FOREACH(ip, ipole, qelem) { + if (ip->type != IP_NODE) + continue; + cip = ip; + walkf(ip->ip_node, shorttemp); /* convert temps to oregs */ + } + if (!DLIST_ISEMPTY(spole, link)) + comperr("treerewrite not empty"); +} + +/* + * Change the TEMPs in the ipole list to stack variables. + */ +static void +leafrewrite(struct interpass *ipole, REGW *rpole) +{ + extern NODE *nodepole; + extern int thisline; + struct interpass *ip; + + spole = rpole; + DLIST_FOREACH(ip, ipole, qelem) { + if (ip->type != IP_NODE) + continue; + nodepole = ip->ip_node; + thisline = ip->lineno; + walkf(ip->ip_node, longtemp); /* convert temps to oregs */ + } + nodepole = NIL; +} + +/* + * Avoid copying spilled argument to new position on stack. + */ +static int +temparg(struct interpass *ipole, REGW *w) +{ + struct interpass *ip; + NODE *p; + + ip = DLIST_NEXT(ipole, qelem); /* PROLOG */ + ip = DLIST_NEXT(ip, qelem); /* first DEFLAB */ + ip = DLIST_NEXT(ip, qelem); /* first NODE */ + for (; ip->type != IP_DEFLAB; ip = DLIST_NEXT(ip, qelem)) { + if (ip->type == IP_ASM) + continue; + p = ip->ip_node; +#ifdef PCC_DEBUG + if (p->n_op != ASSIGN || p->n_left->n_op != TEMP) + comperr("temparg"); +#endif + if (p->n_right->n_op != OREG) + continue; /* arg in register */ + if (w != &nblock[(int)p->n_left->n_lval]) + continue; + w->r_color = p->n_right->n_lval; + tfree(p); + /* Cannot DLIST_REMOVE here, would break basic blocks */ + /* Make it a nothing instead */ + ip->type = IP_ASM; + ip->ip_asm=""; + return 1; + } + return 0; +} + +#define ONLYPERM 1 +#define LEAVES 2 +#define SMALL 3 + +/* + * Scan the whole function and search for temporaries to be stored + * on-stack. + * + * Be careful to not destroy the basic block structure in the first scan. + */ +static int +RewriteProgram(struct interpass *ip) +{ + REGW shortregs, longregs, saveregs, *q; + REGW *w; + int rwtyp; + + RDEBUG(("RewriteProgram\n")); + DLIST_INIT(&shortregs, link); + DLIST_INIT(&longregs, link); + DLIST_INIT(&saveregs, link); + + /* sort the temporaries in three queues, short, long and perm */ + while (!DLIST_ISEMPTY(&spilledNodes, link)) { + w = DLIST_NEXT(&spilledNodes, link); + DLIST_REMOVE(w, link); + + if (w >= &nblock[tempmin] && w < &nblock[basetemp]) { + q = &saveregs; + } else if (w >= &nblock[basetemp] && w < &nblock[tempmax]) { + q = &longregs; + } else + q = &shortregs; + DLIST_INSERT_AFTER(q, w, link); + } +#ifdef PCC_DEBUG + if (rdebug) { + printf("permanent: "); + DLIST_FOREACH(w, &saveregs, link) + printf("%d ", ASGNUM(w)); + printf("\nlong-lived: "); + DLIST_FOREACH(w, &longregs, link) + printf("%d ", ASGNUM(w)); + printf("\nshort-lived: "); + DLIST_FOREACH(w, &shortregs, link) + printf("%d ", ASGNUM(w)); + printf("\n"); + } +#endif + rwtyp = 0; + + if (!DLIST_ISEMPTY(&saveregs, link)) { + rwtyp = ONLYPERM; + DLIST_FOREACH(w, &saveregs, link) { + int num = w - nblock - tempmin; + nsavregs[num] = 1; + } + } + if (!DLIST_ISEMPTY(&longregs, link)) { + rwtyp = LEAVES; + DLIST_FOREACH(w, &longregs, link) { + w->r_class = xtemps ? temparg(ip, w) : 0; + } + } + + if (rwtyp == LEAVES) { + leafrewrite(ip, &longregs); + rwtyp = ONLYPERM; + } + + if (rwtyp == 0 && !DLIST_ISEMPTY(&shortregs, link)) { + /* Must rewrite the trees */ + treerewrite(ip, &shortregs); +// if (xtemps) +// comperr("treerewrite"); + rwtyp = SMALL; + } + + RDEBUG(("savregs %x rwtyp %d\n", 0, rwtyp)); + + return rwtyp; +} + +#ifdef notyet +/* + * Assign instructions, calculate evaluation order and + * set temporary register numbers. + */ +static void +insgen() +{ + geninsn(); /* instruction assignment */ + sucomp(); /* set evaluation order */ + slong(); /* set long temp types */ + sshort(); /* set short temp numbers */ +} +#endif + +/* + * Do register allocation for trees by graph-coloring. + */ +void +ngenregs(struct interpass *ipole) +{ + extern NODE *nodepole; + struct interpass_prolog *ipp, *epp; + struct interpass *ip; + int i, j, nbits = 0; + int uu[NPERMREG] = { -1 }; + int xnsavregs[NPERMREG]; + int beenhere = 0; + + DLIST_INIT(&lunused, link); + DLIST_INIT(&lused, link); + + /* + * Do some setup before doing the real thing. + */ + ipp = (struct interpass_prolog *)DLIST_NEXT(ipole, qelem); + epp = (struct interpass_prolog *)DLIST_PREV(ipole, qelem); + + tempmin = ipp->ip_tmpnum; + tempmax = epp->ip_tmpnum; + + /* + * Allocate space for the permanent registers in the + * same block as the long-lived temporaries. + * These temporaries will be handled the same way as + * all other variables. + */ + basetemp = tempmin; + nsavregs = xnsavregs; + for (i = 0; i < NPERMREG; i++) + xnsavregs[i] = 0; + ndontregs = uu; /* currently never avoid any regs */ + + tempmin -= (NPERMREG-1); +#ifdef notyet + if (xavoidfp) + dontregs |= REGBIT(FPREG); +#endif + +#ifdef PCC_DEBUG + nodnum = tempmax; +#endif + nbits = xbits = tempmax - tempmin; + if (nbits) { + nblock = tmpalloc(nbits * sizeof(REGW)); + + nblock -= tempmin; + live = tmpalloc(BIT2BYTE(nbits)); + RDEBUG(("nblock %p num %d size %d\n", + nblock, nbits, (int)(nbits * sizeof(REGW)))); + } + + + /* Block for precolored nodes */ + ablock = tmpalloc(sizeof(REGW)*MAXREGS); + memset(ablock, 0, sizeof(REGW)*MAXREGS); + for (i = 0; i < MAXREGS; i++) { + ablock[i].r_onlist = &precolored; + ablock[i].r_class = GCLASS(i); /* XXX */ + ablock[i].r_color = i; +#ifdef PCC_DEBUG + ablock[i].nodnum = i; +#endif + } +#ifdef notyet + TMPMARK(); +#endif + + +recalc: +onlyperm: /* XXX - should not have to redo all */ + + if (nbits) { + memset(nblock+tempmin, 0, nbits * sizeof(REGW)); + memset(live, 0, BIT2BYTE(nbits)); + memset(edgehash, 0, sizeof(edgehash)); +#ifdef PCC_DEBUG + for (i = tempmin; i < tempmax; i++) + nblock[i].nodnum = i; +#endif + } + RPRINTIP(ipole); + DLIST_INIT(&initial, link); + DLIST_FOREACH(ip, ipole, qelem) { + extern int thisline; + if (ip->type != IP_NODE) + continue; + nodepole = ip->ip_node; + thisline = ip->lineno; + geninsn(ip->ip_node, FOREFF); + nsucomp(ip->ip_node); + walkf(ip->ip_node, traclass); + } + nodepole = NIL; + RDEBUG(("nsucomp allocated %d temps (%d,%d)\n", + tempmax-tempmin, tempmin, tempmax)); + + RPRINTIP(ipole); + RDEBUG(("ngenregs: numtemps %d (%d, %d)\n", tempmax-tempmin, + tempmin, tempmax)); + + DLIST_INIT(&coalescedMoves, link); + DLIST_INIT(&constrainedMoves, link); + DLIST_INIT(&frozenMoves, link); + DLIST_INIT(&worklistMoves, link); + DLIST_INIT(&activeMoves, link); + + /* Set class and move-related for perm regs */ + for (i = 0; i < (NPERMREG-1); i++) { + if (nsavregs[i]) + continue; + nblock[i+tempmin].r_class = GCLASS(permregs[i]); + DLIST_INSERT_AFTER(&initial, &nblock[i+tempmin], link); + moveadd(&nblock[i+tempmin], &ablock[permregs[i]]); + addalledges(&nblock[i+tempmin]); + } + + Build(ipole); + RDEBUG(("Build done\n")); + MkWorklist(); + RDEBUG(("MkWorklist done\n")); + do { + if (!WLISTEMPTY(simplifyWorklist)) + Simplify(); + else if (!WLISTEMPTY(worklistMoves)) + Coalesce(); + else if (!WLISTEMPTY(freezeWorklist)) + Freeze(); + else if (!WLISTEMPTY(spillWorklist)) + SelectSpill(); + } while (!WLISTEMPTY(simplifyWorklist) || !WLISTEMPTY(worklistMoves) || + !WLISTEMPTY(freezeWorklist) || !WLISTEMPTY(spillWorklist)); + AssignColors(ipole); + + RDEBUG(("After AssignColors\n")); + RPRINTIP(ipole); + + if (!WLISTEMPTY(spilledNodes)) { + switch (RewriteProgram(ipole)) { + case ONLYPERM: + goto onlyperm; + case SMALL: + optimize(ipole); + if (beenhere++ == MAXLOOP) + comperr("beenhere"); + goto recalc; + } + } + + /* fill in regs to save */ + ipp->ipp_regs = 0; + for (i = 0; i < NPERMREG-1; i++) { + NODE *p; + + if (nsavregs[i]) { + ipp->ipp_regs |= (1 << permregs[i]); + continue; /* Spilled */ + } + if (nblock[i+tempmin].r_color == permregs[i]) + continue; /* Coalesced */ + /* + * If the original color of this permreg is used for + * coloring another register, swap them to avoid + * unneccessary moves. + */ + for (j = i+1; j < NPERMREG-1; j++) { + if (nblock[j+tempmin].r_color != permregs[i]) + continue; + nblock[j+tempmin].r_color = nblock[i+tempmin].r_color; + break; + } + if (j != NPERMREG-1) + continue; + + /* Generate reg-reg move nodes for save */ + p = mkbinode(ASSIGN, + mklnode(REG, 0, nblock[i+tempmin].r_color, INT), + mklnode(REG, 0, permregs[i], INT), INT); + p->n_reg = p->n_left->n_reg = p->n_right->n_reg = -1; + p->n_left->n_su = p->n_right->n_su = 0; + geninsn(p, FOREFF); + ip = ipnode(p); + DLIST_INSERT_AFTER(ipole->qelem.q_forw, ip, qelem); + /* XXX not int */ + p = mkbinode(ASSIGN, mklnode(REG, 0, permregs[i], INT), + mklnode(REG, 0, nblock[i+tempmin].r_color, INT), INT); + p->n_reg = p->n_left->n_reg = p->n_right->n_reg = -1; + p->n_left->n_su = p->n_right->n_su = 0; + geninsn(p, FOREFF); + ip = ipnode(p); + DLIST_INSERT_BEFORE(ipole->qelem.q_back, ip, qelem); + } + epp->ipp_regs = ipp->ipp_regs; + /* Done! */ +} |