summaryrefslogtreecommitdiff
path: root/usr.bin/pcc/cc/ccom/pftn.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/pcc/cc/ccom/pftn.c')
-rw-r--r--usr.bin/pcc/cc/ccom/pftn.c2549
1 files changed, 2549 insertions, 0 deletions
diff --git a/usr.bin/pcc/cc/ccom/pftn.c b/usr.bin/pcc/cc/ccom/pftn.c
new file mode 100644
index 00000000000..f5be111dc2c
--- /dev/null
+++ b/usr.bin/pcc/cc/ccom/pftn.c
@@ -0,0 +1,2549 @@
+/* $Id: pftn.c,v 1.1 2007/09/15 18:12:34 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.
+ */
+
+/*
+ * Many changes from the 32V sources, among them:
+ * - New symbol table manager (moved to another file).
+ * - Prototype saving/checks.
+ */
+
+# include "pass1.h"
+
+#include <string.h> /* XXX - for strcmp */
+
+struct symtab *spname;
+struct symtab *cftnsp;
+static int strunem; /* currently parsed member type */
+int arglistcnt, dimfuncnt; /* statistics */
+int symtabcnt, suedefcnt; /* statistics */
+int autooff, /* the next unused automatic offset */
+ maxautooff, /* highest used automatic offset in function */
+ argoff, /* the next unused argument offset */
+ strucoff; /* the next structure offset position */
+int retlab = NOLAB; /* return label for subroutine */
+int brklab;
+int contlab;
+int flostat;
+int instruct, blevel;
+int reached, prolab;
+
+struct params;
+
+#define ISSTR(ty) (ty == STRTY || ty == UNIONTY || ty == ENUMTY)
+#define ISSOU(ty) (ty == STRTY || ty == UNIONTY)
+#define MKTY(p, t, d, s) r = talloc(); *r = *p; \
+ r = argcast(r, t, d, s); *p = *r; nfree(r);
+
+/*
+ * Info stored for delaying string printouts.
+ */
+struct strsched {
+ struct strsched *next;
+ int locctr;
+ struct symtab *sym;
+} *strpole;
+
+/*
+ * Linked list stack while reading in structs.
+ */
+struct rstack {
+ struct rstack *rnext;
+ int rinstruct;
+ int rclass;
+ int rstrucoff;
+ struct params *rlparam;
+ struct symtab *rsym;
+};
+
+/*
+ * Linked list for parameter (and struct elements) declaration.
+ */
+static struct params {
+ struct params *next, *prev;
+ struct symtab *sym;
+} *lpole, *lparam;
+static int nparams;
+
+/* defines used for getting things off of the initialization stack */
+
+static NODE *arrstk[10];
+static int arrstkp;
+static int intcompare;
+
+void fixtype(NODE *p, int class);
+int fixclass(int class, TWORD type);
+int falloc(struct symtab *p, int w, int new, NODE *pty);
+static void dynalloc(struct symtab *p, int *poff);
+void inforce(OFFSZ n);
+void vfdalign(int n);
+static void ssave(struct symtab *);
+static void strprint(void);
+static void alprint(union arglist *al, int in);
+static void lcommadd(struct symtab *sp);
+
+int ddebug = 0;
+
+/*
+ * Declaration of an identifier. Handles redeclarations, hiding,
+ * incomplete types and forward declarations.
+ */
+
+void
+defid(NODE *q, int class)
+{
+ struct symtab *p;
+ TWORD type, qual;
+ TWORD stp, stq;
+ int scl;
+ union dimfun *dsym, *ddef;
+ int slev, temp, changed;
+
+ if (q == NIL)
+ return; /* an error was detected */
+
+ p = q->n_sp;
+
+#ifdef PCC_DEBUG
+ if (ddebug) {
+ printf("defid(%s (%p), ", p->sname, p);
+ tprint(stdout, q->n_type, q->n_qual);
+ printf(", %s, (%p,%p)), level %d\n", scnames(class),
+ q->n_df, q->n_sue, blevel);
+ }
+#endif
+
+ fixtype(q, class);
+
+ type = q->n_type;
+ qual = q->n_qual;
+ class = fixclass(class, type);
+
+ stp = p->stype;
+ stq = p->squal;
+ slev = p->slevel;
+
+#ifdef PCC_DEBUG
+ if (ddebug) {
+ printf(" modified to ");
+ tprint(stdout, type, qual);
+ printf(", %s\n", scnames(class));
+ printf(" previous def'n: ");
+ tprint(stdout, stp, stq);
+ printf(", %s, (%p,%p)), level %d\n",
+ scnames(p->sclass), p->sdf, p->ssue, slev);
+ }
+#endif
+
+ if (blevel == 1) {
+ switch (class) {
+ default:
+ if (!(class&FIELD))
+ uerror("declared argument %s missing",
+ p->sname );
+ case MOS:
+ case STNAME:
+ case MOU:
+ case UNAME:
+ case MOE:
+ case ENAME:
+ case TYPEDEF:
+ ;
+ }
+ }
+
+ if (stp == UNDEF)
+ goto enter; /* New symbol */
+
+ if (type != stp)
+ goto mismatch;
+
+ if (blevel > slev && (class == AUTO || class == REGISTER))
+ /* new scope */
+ goto mismatch;
+
+ /*
+ * test (and possibly adjust) dimensions.
+ * also check that prototypes are correct.
+ */
+ dsym = p->sdf;
+ ddef = q->n_df;
+ changed = 0;
+ for (temp = type; temp & TMASK; temp = DECREF(temp)) {
+ if (ISARY(temp)) {
+ if (dsym->ddim == 0) {
+ dsym->ddim = ddef->ddim;
+ changed = 1;
+ } else if (ddef->ddim != 0 && dsym->ddim!=ddef->ddim) {
+ goto mismatch;
+ }
+ ++dsym;
+ ++ddef;
+ } else if (ISFTN(temp)) {
+ /* add a late-defined prototype here */
+ if (cftnsp == NULL && dsym->dfun == NULL)
+ dsym->dfun = ddef->dfun;
+ if (!oldstyle && ddef->dfun != NULL &&
+ chkftn(dsym->dfun, ddef->dfun))
+ uerror("declaration doesn't match prototype");
+ dsym++, ddef++;
+ }
+ }
+#ifdef STABS
+ if (changed && gflag)
+ stabs_chgsym(p); /* symbol changed */
+#endif
+
+ /* check that redeclarations are to the same structure */
+ if ((temp == STRTY || temp == UNIONTY || temp == ENUMTY) &&
+ p->ssue != q->n_sue &&
+ class != STNAME && class != UNAME && class != ENAME) {
+ goto mismatch;
+ }
+
+ scl = p->sclass;
+
+#ifdef PCC_DEBUG
+ if (ddebug)
+ printf(" previous class: %s\n", scnames(scl));
+#endif
+
+ if (class&FIELD) {
+ /* redefinition */
+ if (!falloc(p, class&FLDSIZ, 1, NIL)) {
+ /* successful allocation */
+ ssave(p);
+ return;
+ }
+ /* blew it: resume at end of switch... */
+ } else switch(class) {
+
+ case EXTERN:
+ switch( scl ){
+ case STATIC:
+ case USTATIC:
+ if( slev==0 ) return;
+ break;
+ case EXTDEF:
+ case EXTERN:
+ case FORTRAN:
+ case UFORTRAN:
+ return;
+ }
+ break;
+
+ case STATIC:
+ if (scl==USTATIC || (scl==EXTERN && blevel==0)) {
+ p->sclass = STATIC;
+ return;
+ }
+ if (changed || (scl == STATIC && blevel == slev))
+ return; /* identical redeclaration */
+ break;
+
+ case USTATIC:
+ if (scl==STATIC || scl==USTATIC)
+ return;
+ break;
+
+ case TYPEDEF:
+ if (scl == class)
+ return;
+ break;
+
+ case UFORTRAN:
+ if (scl == UFORTRAN || scl == FORTRAN)
+ return;
+ break;
+
+ case FORTRAN:
+ if (scl == UFORTRAN) {
+ p->sclass = FORTRAN;
+ return;
+ }
+ break;
+
+ case MOU:
+ case MOS:
+ if (scl == class) {
+ if (oalloc(p, &strucoff))
+ break;
+ if (class == MOU)
+ strucoff = 0;
+ ssave(p);
+ return;
+ }
+ break;
+
+ case MOE:
+ break;
+
+ case EXTDEF:
+ switch (scl) {
+ case EXTERN:
+ p->sclass = EXTDEF;
+ return;
+ case USTATIC:
+ p->sclass = STATIC;
+ return;
+ }
+ break;
+
+ case STNAME:
+ case UNAME:
+ case ENAME:
+ if (scl != class)
+ break;
+ if (p->ssue->suesize == 0)
+ return; /* previous entry just a mention */
+ break;
+
+ case AUTO:
+ case REGISTER:
+ ; /* mismatch.. */
+ }
+
+ mismatch:
+
+ /*
+ * Only allowed for automatic variables.
+ */
+ if (blevel == slev || class == EXTERN || class == FORTRAN ||
+ class == UFORTRAN) {
+ if (ISSTR(class) && !ISSTR(p->sclass)) {
+ uerror("redeclaration of %s", p->sname);
+ return;
+ }
+ }
+ if (blevel == 0)
+ uerror("redeclaration of %s", p->sname);
+ q->n_sp = p = hide(p);
+
+ enter: /* make a new entry */
+
+#ifdef PCC_DEBUG
+ if(ddebug)
+ printf(" new entry made\n");
+#endif
+ p->stype = type;
+ p->squal = qual;
+ p->sclass = class;
+ p->slevel = blevel;
+ p->soffset = NOOFFSET;
+ p->suse = lineno;
+ if (class == STNAME || class == UNAME || class == ENAME) {
+ p->ssue = permalloc(sizeof(struct suedef));
+ suedefcnt++;
+ p->ssue->suesize = 0;
+ p->ssue->suelem = NULL;
+ p->ssue->suealign = ALSTRUCT;
+ } else {
+ switch (BTYPE(type)) {
+ case STRTY:
+ case UNIONTY:
+ case ENUMTY:
+ p->ssue = q->n_sue;
+ break;
+ default:
+ p->ssue = MKSUE(BTYPE(type));
+ }
+ }
+
+ /* copy dimensions */
+ p->sdf = q->n_df;
+ /* Do not save param info for old-style functions */
+ if (ISFTN(type) && oldstyle)
+ p->sdf->dfun = NULL;
+
+ /* allocate offsets */
+ if (class&FIELD) {
+ (void) falloc(p, class&FLDSIZ, 0, NIL); /* new entry */
+ ssave(p);
+ } else switch (class) {
+
+ case REGISTER:
+ cerror("register var");
+
+ case AUTO:
+ if (arrstkp)
+ dynalloc(p, &autooff);
+ else
+ oalloc(p, &autooff);
+ break;
+ case STATIC:
+ case EXTDEF:
+ p->soffset = getlab();
+#ifdef GCC_COMPAT
+ { extern char *renname;
+ if (renname)
+ gcc_rename(p, renname);
+ renname = NULL;
+ }
+#endif
+ break;
+
+ case EXTERN:
+ case UFORTRAN:
+ case FORTRAN:
+ p->soffset = getlab();
+#ifdef notdef
+ /* Cannot reset level here. What does the standard say??? */
+ p->slevel = 0;
+#endif
+#ifdef GCC_COMPAT
+ { extern char *renname;
+ if (renname)
+ gcc_rename(p, renname);
+ renname = NULL;
+ }
+#endif
+ break;
+ case MOU:
+ case MOS:
+ oalloc(p, &strucoff);
+ if (class == MOU)
+ strucoff = 0;
+ ssave(p);
+ break;
+
+ case MOE:
+ p->soffset = strucoff++;
+ ssave(p);
+ break;
+
+ }
+
+#ifdef STABS
+ if (gflag)
+ stabs_newsym(p);
+#endif
+
+#ifdef PCC_DEBUG
+ if (ddebug)
+ printf( " sdf, ssue, offset: %p, %p, %d\n",
+ p->sdf, p->ssue, p->soffset);
+#endif
+
+}
+
+void
+ssave(struct symtab *sym)
+{
+ struct params *p;
+
+ p = tmpalloc(sizeof(struct params));
+ p->next = NULL;
+ p->sym = sym;
+
+ if (lparam == NULL) {
+ p->prev = (struct params *)&lpole;
+ lpole = p;
+ } else {
+ lparam->next = p;
+ p->prev = lparam;
+ }
+ lparam = p;
+}
+
+/*
+ * end of function
+ */
+void
+ftnend()
+{
+ extern struct savbc *savbc;
+ extern struct swdef *swpole;
+ char *c;
+
+ if (retlab != NOLAB && nerrors == 0) { /* inside a real function */
+ plabel(retlab);
+ efcode(); /* struct return handled here */
+ c = cftnsp->sname;
+#ifdef GCC_COMPAT
+ c = gcc_findname(cftnsp);
+#endif
+ SETOFF(maxautooff, ALCHAR);
+ send_passt(IP_EPILOG, 0, maxautooff/SZCHAR, c,
+ cftnsp->stype, cftnsp->sclass == EXTDEF, retlab);
+ }
+
+ tcheck();
+ brklab = contlab = retlab = NOLAB;
+ flostat = 0;
+ if (nerrors == 0) {
+ if (savbc != NULL)
+ cerror("bcsave error");
+ if (lparam != NULL)
+ cerror("parameter reset error");
+ if (swpole != NULL)
+ cerror("switch error");
+ }
+ savbc = NULL;
+ lparam = NULL;
+ maxautooff = autooff = AUTOINIT;
+ reached = 1;
+
+ if (isinlining)
+ inline_end();
+ inline_prtout();
+
+ strprint();
+
+ tmpfree(); /* Release memory resources */
+}
+
+void
+dclargs()
+{
+ union dimfun *df;
+ union arglist *al, *al2, *alb;
+ struct params *a;
+ struct symtab *p, **parr = NULL; /* XXX gcc */
+ char *c;
+ int i;
+
+ argoff = ARGINIT;
+
+ /*
+ * Deal with fun(void) properly.
+ */
+ if (nparams == 1 && lparam->sym->stype == VOID)
+ goto done;
+
+ /*
+ * Generate a list for bfcode().
+ * Parameters were pushed in reverse order.
+ */
+ if (nparams != 0)
+ parr = tmpalloc(sizeof(struct symtab *) * nparams);
+
+ if (nparams)
+ for (a = lparam, i = 0; a != NULL && a != (struct params *)&lpole;
+ a = a->prev) {
+
+ p = a->sym;
+ parr[i++] = p;
+ if (p->stype == FARG) {
+ p->stype = INT;
+ p->ssue = MKSUE(INT);
+ }
+ if (ISARY(p->stype)) {
+ p->stype += (PTR-ARY);
+ p->sdf++;
+ } else if (ISFTN(p->stype)) {
+ werror("function declared as argument");
+ p->stype = INCREF(p->stype);
+ }
+ /* always set aside space, even for register arguments */
+ oalloc(p, &argoff);
+#ifdef STABS
+ if (gflag)
+ stabs_newsym(p);
+#endif
+ }
+ if (oldstyle && (df = cftnsp->sdf) && (al = df->dfun)) {
+ /*
+ * Check against prototype of oldstyle function.
+ */
+ alb = al2 = tmpalloc(sizeof(union arglist) * nparams * 3 + 1);
+ for (i = 0; i < nparams; i++) {
+ TWORD type = parr[i]->stype;
+ (al2++)->type = type;
+ if (ISSTR(BTYPE(type)))
+ (al2++)->sue = parr[i]->ssue;
+ while (!ISFTN(type) && !ISARY(type) && type > BTMASK)
+ type = DECREF(type);
+ if (type > BTMASK)
+ (al2++)->df = parr[i]->sdf;
+ }
+ al2->type = TNULL;
+ intcompare = 1;
+ if (chkftn(al, alb))
+ uerror("function doesn't match prototype");
+ intcompare = 0;
+ }
+done: cendarg();
+ c = cftnsp->sname;
+#ifdef GCC_COMPAT
+ c = gcc_findname(cftnsp);
+#endif
+#if 0
+ prolab = getlab();
+ send_passt(IP_PROLOG, -1, -1, c, cftnsp->stype,
+ cftnsp->sclass == EXTDEF, prolab);
+#endif
+ plabel(prolab); /* after prolog, used in optimization */
+ retlab = getlab();
+ bfcode(parr, nparams);
+ if (xtemps) {
+ /* put arguments in temporaries */
+ for (i = 0; i < nparams; i++) {
+ NODE *q, *r, *s;
+
+ p = parr[i];
+ if (p->stype == STRTY || p->stype == UNIONTY ||
+ cisreg(p->stype) == 0)
+ continue;
+ spname = p;
+ q = buildtree(NAME, 0, 0);
+ r = tempnode(0, p->stype, p->sdf, p->ssue);
+ s = buildtree(ASSIGN, r, q);
+ p->soffset = r->n_lval;
+ p->sflags |= STNODE;
+ ecomp(s);
+ }
+ plabel(getlab()); /* used when spilling */
+ }
+ lparam = NULL;
+ nparams = 0;
+}
+
+/*
+ * reference to a structure or union, with no definition
+ */
+NODE *
+rstruct(char *tag, int soru)
+{
+ struct symtab *p;
+ NODE *q;
+
+ p = (struct symtab *)lookup(tag, STAGNAME);
+ switch (p->stype) {
+
+ case UNDEF:
+ def:
+ q = block(NAME, NIL, NIL, 0, 0, 0);
+ q->n_sp = p;
+ q->n_type = (soru&INSTRUCT) ? STRTY :
+ ((soru&INUNION) ? UNIONTY : ENUMTY);
+ defid(q, (soru&INSTRUCT) ? STNAME :
+ ((soru&INUNION) ? UNAME : ENAME));
+ nfree(q);
+ break;
+
+ case STRTY:
+ if (soru & INSTRUCT)
+ break;
+ goto def;
+
+ case UNIONTY:
+ if (soru & INUNION)
+ break;
+ goto def;
+
+ case ENUMTY:
+ if (!(soru&(INUNION|INSTRUCT)))
+ break;
+ goto def;
+
+ }
+ q = mkty(p->stype, 0, p->ssue);
+ q->n_sue = p->ssue;
+ return q;
+}
+
+void
+moedef(char *name)
+{
+ NODE *q;
+
+ q = block(NAME, NIL, NIL, MOETY, 0, 0);
+ q->n_sp = lookup(name, 0);
+ defid(q, MOE);
+ nfree(q);
+}
+
+/*
+ * begining of structure or union declaration
+ */
+struct rstack *
+bstruct(char *name, int soru)
+{
+ struct rstack *r;
+ struct symtab *s;
+ NODE *q;
+
+ if (name != NULL)
+ s = lookup(name, STAGNAME);
+ else
+ s = NULL;
+
+ r = tmpalloc(sizeof(struct rstack));
+ r->rinstruct = instruct;
+ r->rclass = strunem;
+ r->rstrucoff = strucoff;
+
+ strucoff = 0;
+ instruct = soru;
+ q = block(NAME, NIL, NIL, 0, 0, 0);
+ q->n_sp = s;
+ if (instruct==INSTRUCT) {
+ strunem = MOS;
+ q->n_type = STRTY;
+ if (s != NULL)
+ defid(q, STNAME);
+ } else if(instruct == INUNION) {
+ strunem = MOU;
+ q->n_type = UNIONTY;
+ if (s != NULL)
+ defid(q, UNAME);
+ } else { /* enum */
+ strunem = MOE;
+ q->n_type = ENUMTY;
+ if (s != NULL)
+ defid(q, ENAME);
+ }
+ r->rsym = q->n_sp;
+ r->rlparam = lparam;
+ nfree(q);
+
+ return r;
+}
+
+/*
+ * Called after a struct is declared to restore the environment.
+ */
+NODE *
+dclstruct(struct rstack *r)
+{
+ NODE *n;
+ struct params *l, *m;
+ struct suedef *sue;
+ struct symtab *p;
+ int al, sa, sz;
+ TWORD temp;
+ int i, high, low;
+
+ if (r->rsym == NULL) {
+ sue = permalloc(sizeof(struct suedef));
+ suedefcnt++;
+ sue->suesize = 0;
+ sue->suealign = ALSTRUCT;
+ } else
+ sue = r->rsym->ssue;
+
+#ifdef PCC_DEBUG
+ if (ddebug)
+ printf("dclstruct(%s)\n", r->rsym ? r->rsym->sname : "??");
+#endif
+ temp = (instruct&INSTRUCT)?STRTY:((instruct&INUNION)?UNIONTY:ENUMTY);
+ instruct = r->rinstruct;
+ strunem = r->rclass;
+ al = ALSTRUCT;
+
+ high = low = 0;
+
+ if ((l = r->rlparam) == NULL)
+ l = lpole;
+ else
+ l = l->next;
+
+ /* memory for the element array must be allocated first */
+ for (m = l, i = 1; m != NULL; m = m->next)
+ i++;
+ sue->suelem = permalloc(sizeof(struct symtab *) * i);
+
+ for (i = 0; l != NULL; l = l->next) {
+ sue->suelem[i++] = p = l->sym;
+
+ if (p == NULL)
+ cerror("gummy structure member");
+ if (temp == ENUMTY) {
+ if (p->soffset < low)
+ low = p->soffset;
+ if (p->soffset > high)
+ high = p->soffset;
+ p->ssue = sue;
+ continue;
+ }
+ sa = talign(p->stype, p->ssue);
+ if (p->sclass & FIELD) {
+ sz = p->sclass&FLDSIZ;
+ } else {
+ sz = tsize(p->stype, p->sdf, p->ssue);
+ }
+ if (sz > strucoff)
+ strucoff = sz; /* for use with unions */
+ /*
+ * set al, the alignment, to the lcm of the alignments
+ * of the members.
+ */
+ SETOFF(al, sa);
+ }
+ sue->suelem[i] = NULL;
+ SETOFF(strucoff, al);
+
+ if (temp == ENUMTY) {
+ TWORD ty;
+
+#ifdef ENUMSIZE
+ ty = ENUMSIZE(high,low);
+#else
+ if ((char)high == high && (char)low == low)
+ ty = ctype(CHAR);
+ else if ((short)high == high && (short)low == low)
+ ty = ctype(SHORT);
+ else
+ ty = ctype(INT);
+#endif
+ strucoff = tsize(ty, 0, MKSUE(ty));
+ sue->suealign = al = talign(ty, MKSUE(ty));
+ }
+
+ sue->suesize = strucoff;
+ sue->suealign = al;
+
+#ifdef STABS
+ if (gflag)
+ stabs_struct(r->rsym, sue);
+#endif
+
+#ifdef PCC_DEBUG
+ if (ddebug>1) {
+ int i;
+
+ printf("\tsize %d align %d elem %p\n",
+ sue->suesize, sue->suealign, sue->suelem);
+ for (i = 0; sue->suelem[i] != NULL; ++i) {
+ printf("\tmember %s(%p)\n",
+ sue->suelem[i]->sname, sue->suelem[i]);
+ }
+ }
+#endif
+
+ strucoff = r->rstrucoff;
+ if ((lparam = r->rlparam) != NULL)
+ lparam->next = NULL;
+ n = mkty(temp, 0, sue);
+ return n;
+}
+
+/*
+ * error printing routine in parser
+ */
+void yyerror(char *s);
+void
+yyerror(char *s)
+{
+ uerror(s);
+}
+
+void yyaccpt(void);
+void
+yyaccpt(void)
+{
+ ftnend();
+}
+
+/*
+ * p is top of type list given to tymerge later.
+ * Find correct CALL node and declare parameters from there.
+ */
+void
+ftnarg(NODE *p)
+{
+ NODE *q;
+ struct symtab *s;
+
+#ifdef PCC_DEBUG
+ if (ddebug > 2)
+ printf("ftnarg(%p)\n", p);
+#endif
+ /*
+ * Enter argument onto param stack.
+ * Do not declare parameters until later (in dclargs);
+ * the function must be declared first.
+ * put it on the param stack in reverse order, due to the
+ * nature of the stack it will be reclaimed correct.
+ */
+ for (; p->n_op != NAME; p = p->n_left) {
+ if (p->n_op == (UCALL) && p->n_left->n_op == NAME)
+ return; /* Nothing to enter */
+ if (p->n_op == CALL && p->n_left->n_op == NAME)
+ break;
+ }
+
+ p = p->n_right;
+ blevel = 1;
+
+ while (p->n_op == CM) {
+ q = p->n_right;
+ if (q->n_op != ELLIPSIS) {
+ s = lookup((char *)q->n_sp, 0);
+ if (s->stype != UNDEF) {
+ if (s->slevel > 0)
+ uerror("parameter '%s' redefined",
+ s->sname);
+ s = hide(s);
+ }
+ s->soffset = NOOFFSET;
+ s->sclass = PARAM;
+ s->stype = q->n_type;
+ s->sdf = q->n_df;
+ s->ssue = q->n_sue;
+ ssave(s);
+ nparams++;
+#ifdef PCC_DEBUG
+ if (ddebug > 2)
+ printf(" saving sym %s (%p) from (%p)\n",
+ s->sname, s, q);
+#endif
+ }
+ p = p->n_left;
+ }
+ s = lookup((char *)p->n_sp, 0);
+ if (s->stype != UNDEF) {
+ if (s->slevel > 0)
+ uerror("parameter '%s' redefined", s->sname);
+ s = hide(s);
+ }
+ s->soffset = NOOFFSET;
+ s->sclass = PARAM;
+ s->stype = p->n_type;
+ s->sdf = p->n_df;
+ s->ssue = p->n_sue;
+ ssave(s);
+ if (p->n_type != VOID)
+ nparams++;
+ blevel = 0;
+
+#ifdef PCC_DEBUG
+ if (ddebug > 2)
+ printf(" saving sym %s (%p) from (%p)\n",
+ s->sname, s, p);
+#endif
+}
+
+/*
+ * compute the alignment of an object with type ty, sizeoff index s
+ */
+int
+talign(unsigned int ty, struct suedef *sue)
+{
+ int i;
+
+ if (ISPTR(ty))
+ return(ALPOINT); /* shortcut */
+
+ if(sue == NULL && ty!=INT && ty!=CHAR && ty!=SHORT &&
+ ty!=UNSIGNED && ty!=UCHAR && ty!=USHORT) {
+ return(fldal(ty));
+ }
+
+ for( i=0; i<=(SZINT-BTSHIFT-1); i+=TSHIFT ){
+ switch( (ty>>i)&TMASK ){
+
+ case FTN:
+ cerror("compiler takes alignment of function");
+ case PTR:
+ return(ALPOINT);
+ case ARY:
+ continue;
+ case 0:
+ break;
+ }
+ }
+
+ switch( BTYPE(ty) ){
+
+ case UNIONTY:
+ case ENUMTY:
+ case STRTY:
+ return((unsigned int)sue->suealign);
+ case BOOL:
+ return (ALBOOL);
+ case CHAR:
+ case UCHAR:
+ return (ALCHAR);
+ case FLOAT:
+ return (ALFLOAT);
+ case LDOUBLE:
+ return (ALLDOUBLE);
+ case DOUBLE:
+ return (ALDOUBLE);
+ case LONGLONG:
+ case ULONGLONG:
+ return (ALLONGLONG);
+ case LONG:
+ case ULONG:
+ return (ALLONG);
+ case SHORT:
+ case USHORT:
+ return (ALSHORT);
+ default:
+ return (ALINT);
+ }
+}
+
+/* compute the size associated with type ty,
+ * dimoff d, and sizoff s */
+/* BETTER NOT BE CALLED WHEN t, d, and s REFER TO A BIT FIELD... */
+OFFSZ
+tsize(TWORD ty, union dimfun *d, struct suedef *sue)
+{
+
+ int i;
+ OFFSZ mult, sz;
+
+ mult = 1;
+
+ for( i=0; i<=(SZINT-BTSHIFT-1); i+=TSHIFT ){
+ switch( (ty>>i)&TMASK ){
+
+ case FTN:
+ uerror( "cannot take size of function");
+ case PTR:
+ return( SZPOINT(ty) * mult );
+ case ARY:
+ mult *= d->ddim;
+ d++;
+ continue;
+ case 0:
+ break;
+
+ }
+ }
+
+ if (sue == NULL)
+ cerror("bad tsize sue");
+ sz = sue->suesize;
+#ifdef GCC_COMPAT
+ if (ty == VOID)
+ sz = SZCHAR;
+#endif
+ if (ty != STRTY && ty != UNIONTY) {
+ if (sz == 0) {
+ uerror("unknown size");
+ return(SZINT);
+ }
+ } else {
+ if (sue->suelem == NULL)
+ uerror("unknown structure/union/enum");
+ }
+
+ return((unsigned int)sz * mult);
+}
+
+/*
+ * Write last part of wide string.
+ * Do not bother to save wide strings.
+ */
+NODE *
+wstrend(char *str)
+{
+ struct symtab *sp = getsymtab(str, SSTRING|STEMP);
+ struct strsched *sc = tmpalloc(sizeof(struct strsched));
+ NODE *p = block(NAME, NIL, NIL, WCHAR_TYPE+ARY,
+ tmpalloc(sizeof(union dimfun)), MKSUE(WCHAR_TYPE));
+ int i;
+ char *c;
+
+ sp->sclass = ILABEL;
+ sp->soffset = getlab();
+ sp->stype = WCHAR_TYPE+ARY;
+
+ sc = tmpalloc(sizeof(struct strsched));
+ sc->locctr = STRNG;
+ sc->sym = sp;
+ sc->next = strpole;
+ strpole = sc;
+
+ /* length calculation, used only for sizeof */
+ for (i = 0, c = str; *c; ) {
+ if (*c++ == '\\')
+ (void)esccon(&c);
+ i++;
+ }
+ p->n_df->ddim = (i+1) * ((MKSUE(WCHAR_TYPE))->suesize/SZCHAR);
+ p->n_sp = sp;
+ return(p);
+}
+
+/*
+ * Write last part of string.
+ */
+NODE *
+strend(char *str)
+{
+// extern int maystr;
+ struct symtab *s;
+ NODE *p;
+ int i;
+ char *c;
+
+ /* If an identical string is already emitted, just forget this one */
+ str = addstring(str); /* enter string in string table */
+ s = lookup(str, SSTRING); /* check for existance */
+
+ if (s->soffset == 0 /* && maystr == 0 */) { /* No string */
+ struct strsched *sc;
+ s->sclass = ILABEL;
+
+ /*
+ * Delay printout of this string until after the current
+ * function, or the end of the statement.
+ */
+ sc = tmpalloc(sizeof(struct strsched));
+ sc->locctr = STRNG;
+ sc->sym = s;
+ sc->next = strpole;
+ strpole = sc;
+ s->soffset = getlab();
+ }
+
+ p = block(NAME, NIL, NIL, CHAR+ARY,
+ tmpalloc(sizeof(union dimfun)), MKSUE(CHAR));
+#ifdef CHAR_UNSIGNED
+ p->n_type = UCHAR+ARY;
+#endif
+ /* length calculation, used only for sizeof */
+ for (i = 0, c = str; *c; ) {
+ if (*c++ == '\\')
+ (void)esccon(&c);
+ i++;
+ }
+ p->n_df->ddim = i+1;
+ p->n_sp = s;
+ return(p);
+}
+
+/*
+ * Print out new strings, before temp memory is cleared.
+ */
+void
+strprint()
+{
+ char *wr;
+ int i, val, isw;
+ NODE *p = bcon(0);
+
+ while (strpole != NULL) {
+ setloc1(STRNG);
+ deflab1(strpole->sym->soffset);
+ isw = strpole->sym->stype == WCHAR_TYPE+ARY;
+
+ i = 0;
+ wr = strpole->sym->sname;
+ while (*wr != 0) {
+ if (*wr++ == '\\')
+ val = esccon(&wr);
+ else
+ val = (unsigned char)wr[-1];
+ if (isw) {
+ p->n_lval = val;
+ p->n_type = WCHAR_TYPE;
+ ninval(i*(WCHAR_TYPE/SZCHAR),
+ (MKSUE(WCHAR_TYPE))->suesize, p);
+ } else
+ bycode(val, i);
+ i++;
+ }
+ if (isw) {
+ p->n_lval = 0;
+ ninval(i*(WCHAR_TYPE/SZCHAR),
+ (MKSUE(WCHAR_TYPE))->suesize, p);
+ } else {
+ bycode(0, i++);
+ bycode(-1, i);
+ }
+ strpole = strpole->next;
+ }
+ nfree(p);
+}
+
+#if 0
+/*
+ * simulate byte v appearing in a list of integer values
+ */
+void
+putbyte(int v)
+{
+ NODE *p;
+ p = bcon(v);
+ incode( p, SZCHAR );
+ tfree( p );
+// gotscal();
+}
+#endif
+
+/*
+ * update the offset pointed to by poff; return the
+ * offset of a value of size `size', alignment `alignment',
+ * given that off is increasing
+ */
+int
+upoff(int size, int alignment, int *poff)
+{
+ int off;
+
+ off = *poff;
+ SETOFF(off, alignment);
+ if (off < 0)
+ cerror("structure or stack overgrown"); /* wrapped */
+ *poff = off+size;
+ return (off);
+}
+
+/*
+ * allocate p with offset *poff, and update *poff
+ */
+int
+oalloc(struct symtab *p, int *poff )
+{
+ int al, off, tsz;
+ int noff;
+
+ /*
+ * Only generate tempnodes if we are optimizing,
+ * and only for integers, floats or pointers,
+ * and not if the basic type is volatile.
+ */
+/* XXX OLDSTYLE */
+ if (xtemps && ((p->sclass == AUTO) || (p->sclass == REGISTER)) &&
+ (p->stype < STRTY || ISPTR(p->stype)) &&
+ !ISVOL((p->squal << TSHIFT)) && cisreg(p->stype)) {
+ NODE *tn = tempnode(0, p->stype, p->sdf, p->ssue);
+ p->soffset = tn->n_lval;
+ p->sflags |= STNODE;
+ nfree(tn);
+ return 0;
+ }
+
+ al = talign(p->stype, p->ssue);
+ noff = off = *poff;
+ tsz = tsize(p->stype, p->sdf, p->ssue);
+#ifdef BACKAUTO
+ if (p->sclass == AUTO) {
+ noff = off + tsz;
+ if (noff < 0)
+ cerror("stack overflow");
+ SETOFF(noff, al);
+ off = -noff;
+ } else
+#endif
+ if (p->sclass == PARAM && (p->stype == CHAR || p->stype == UCHAR ||
+ p->stype == SHORT || p->stype == USHORT)) {
+ off = upoff(SZINT, ALINT, &noff);
+#ifndef RTOLBYTES
+ off = noff - tsz;
+#endif
+ } else {
+ off = upoff(tsz, al, &noff);
+ }
+
+ if (p->sclass != REGISTER) {
+ /* in case we are allocating stack space for register arguments */
+ if (p->soffset == NOOFFSET)
+ p->soffset = off;
+ else if(off != p->soffset)
+ return(1);
+ }
+
+ *poff = noff;
+ return(0);
+}
+
+/*
+ * Allocate space on the stack for dynamic arrays.
+ * Strategy is as follows:
+ * - first entry is a pointer to the dynamic datatype.
+ * - if it's a one-dimensional array this will be the only entry used.
+ * - if it's a multi-dimensional array the following (numdim-1) integers
+ * will contain the sizes to multiply the indexes with.
+ * - code to write the dimension sizes this will be generated here.
+ * - code to allocate space on the stack will be generated here.
+ */
+static void
+dynalloc(struct symtab *p, int *poff)
+{
+ union dimfun *df;
+ NODE *n, *nn, *tn, *pol;
+ TWORD t;
+ int i, no;
+
+ /*
+ * The pointer to the array is stored in a TEMP node, which number
+ * is in the soffset field;
+ */
+ t = p->stype;
+ p->sflags |= (STNODE|SDYNARRAY);
+ p->stype = INCREF(p->stype); /* Make this an indirect pointer */
+ tn = tempnode(0, p->stype, p->sdf, p->ssue);
+ p->soffset = tn->n_lval;
+
+ df = p->sdf;
+
+ pol = NIL;
+ for (i = 0; ISARY(t); t = DECREF(t), df++) {
+ if (df->ddim >= 0)
+ continue;
+ n = arrstk[i++];
+ nn = tempnode(0, INT, 0, MKSUE(INT));
+ no = nn->n_lval;
+ ecomp(buildtree(ASSIGN, nn, n)); /* Save size */
+
+ df->ddim = -no;
+ n = tempnode(no, INT, 0, MKSUE(INT));
+ if (pol == NIL)
+ pol = n;
+ else
+ pol = buildtree(MUL, pol, n);
+ }
+ /* Create stack gap */
+ spalloc(tn, pol, tsize(t, 0, p->ssue));
+ arrstkp = 0;
+}
+
+/*
+ * allocate a field of width w
+ * new is 0 if new entry, 1 if redefinition, -1 if alignment
+ */
+int
+falloc(struct symtab *p, int w, int new, NODE *pty)
+{
+ int al,sz,type;
+
+ type = (new<0)? pty->n_type : p->stype;
+
+ /* this must be fixed to use the current type in alignments */
+ switch( new<0?pty->n_type:p->stype ){
+
+ case ENUMTY: {
+ struct suedef *sue;
+ sue = new < 0 ? pty->n_sue : p->ssue;
+ al = sue->suealign;
+ sz = sue->suesize;
+ break;
+ }
+
+ case CHAR:
+ case UCHAR:
+ al = ALCHAR;
+ sz = SZCHAR;
+ break;
+
+ case SHORT:
+ case USHORT:
+ al = ALSHORT;
+ sz = SZSHORT;
+ break;
+
+ case INT:
+ case UNSIGNED:
+ al = ALINT;
+ sz = SZINT;
+ break;
+
+ default:
+ if( new < 0 ) {
+ uerror( "illegal field type" );
+ al = ALINT;
+ } else
+ al = fldal( p->stype );
+ sz =SZINT;
+ }
+
+ if( w > sz ) {
+ uerror( "field too big");
+ w = sz;
+ }
+
+ if( w == 0 ){ /* align only */
+ SETOFF( strucoff, al );
+ if( new >= 0 ) uerror( "zero size field");
+ return(0);
+ }
+
+ if( strucoff%al + w > sz ) SETOFF( strucoff, al );
+ if( new < 0 ) {
+ strucoff += w; /* we know it will fit */
+ return(0);
+ }
+
+ /* establish the field */
+
+ if( new == 1 ) { /* previous definition */
+ if( p->soffset != strucoff || p->sclass != (FIELD|w) ) return(1);
+ }
+ p->soffset = strucoff;
+ strucoff += w;
+ p->stype = type;
+ fldty( p );
+ return(0);
+}
+
+/*
+ * handle unitialized declarations assumed to be not functions:
+ * int a;
+ * extern int a;
+ * static int a;
+ */
+void
+nidcl(NODE *p, int class)
+{
+ struct symtab *sp;
+ int commflag = 0;
+
+ /* compute class */
+ if (class == SNULL) {
+ if (blevel > 1)
+ class = AUTO;
+ else if (blevel != 0 || instruct)
+ cerror( "nidcl error" );
+ else /* blevel = 0 */
+ commflag = 1, class = EXTERN;
+ }
+
+ defid(p, class);
+
+ sp = p->n_sp;
+ /* check if forward decl */
+ if (ISARY(sp->stype) && sp->sdf->ddim == 0)
+ return;
+
+ if (sp->sflags & SASG)
+ return; /* already initialized */
+
+ switch (class) {
+ case EXTDEF:
+ /* simulate initialization by 0 */
+ simpleinit(p->n_sp, bcon(0));
+ break;
+ case EXTERN:
+ if (commflag)
+ lcommadd(p->n_sp);
+ else
+ extdec(p->n_sp);
+ break;
+ case STATIC:
+ if (blevel == 0)
+ lcommadd(p->n_sp);
+ else
+ lcommdec(p->n_sp);
+ break;
+ }
+}
+
+struct lcd {
+ SLIST_ENTRY(lcd) next;
+ struct symtab *sp;
+};
+
+static SLIST_HEAD(, lcd) lhead = { NULL, &lhead.q_forw};
+
+/*
+ * Add a local common statement to the printout list.
+ */
+void
+lcommadd(struct symtab *sp)
+{
+ struct lcd *lc, *lcp;
+
+ lcp = NULL;
+ SLIST_FOREACH(lc, &lhead, next) {
+ if (lc->sp == sp)
+ return; /* already exists */
+ if (lc->sp == NULL && lcp == NULL)
+ lcp = lc;
+ }
+ if (lcp == NULL) {
+ lc = permalloc(sizeof(struct lcd));
+ lc->sp = sp;
+ SLIST_INSERT_LAST(&lhead, lc, next);
+ } else
+ lcp->sp = sp;
+}
+
+/*
+ * Delete a local common statement.
+ */
+void
+lcommdel(struct symtab *sp)
+{
+ struct lcd *lc;
+
+ SLIST_FOREACH(lc, &lhead, next) {
+ if (lc->sp == sp) {
+ lc->sp = NULL;
+ return;
+ }
+ }
+}
+
+/*
+ * Print out the remaining common statements.
+ */
+void
+lcommprint(void)
+{
+ struct lcd *lc;
+
+ SLIST_FOREACH(lc, &lhead, next) {
+ if (lc->sp != NULL) {
+ if (lc->sp->sclass == STATIC)
+ lcommdec(lc->sp);
+ else
+ commdec(lc->sp);
+ }
+ }
+}
+
+/*
+ * Merges a type tree into one type. Returns one type node with merged types
+ * and class stored in the su field. Frees all other nodes.
+ * XXX - classes in typedefs?
+ */
+NODE *
+typenode(NODE *p)
+{
+ NODE *l, *sp = NULL;
+ int class = 0, adj, noun, sign;
+ TWORD qual = 0;
+
+ adj = INT; /* INT, LONG or SHORT */
+ noun = UNDEF; /* INT, CHAR or FLOAT */
+ sign = 0; /* 0, SIGNED or UNSIGNED */
+
+ /* Remove initial QUALIFIERs */
+ if (p && p->n_op == QUALIFIER) {
+ qual = p->n_type;
+ l = p->n_left;
+ nfree(p);
+ p = l;
+ }
+
+ /* Handle initial classes special */
+ if (p && p->n_op == CLASS) {
+ class = p->n_type;
+ l = p->n_left;
+ nfree(p);
+ p = l;
+ }
+
+ /* Remove more QUALIFIERs */
+ if (p && p->n_op == QUALIFIER) {
+ qual |= p->n_type;
+ l = p->n_left;
+ nfree(p);
+ p = l;
+ }
+
+ag: if (p && p->n_op == TYPE) {
+ if (p->n_left == NIL) {
+#ifdef CHAR_UNSIGNED
+ if (p->n_type == CHAR)
+ p->n_type = UCHAR;
+#endif
+ if (p->n_type == SIGNED)
+ p->n_type = INT;
+uni: p->n_lval = class;
+ p->n_qual = qual >> TSHIFT;
+ return p;
+ } else if (p->n_left->n_op == QUALIFIER) {
+ qual |= p->n_left->n_type;
+ l = p->n_left;
+ p->n_left = l->n_left;
+ nfree(l);
+ goto ag;
+ } else if (ISSTR(p->n_type)) {
+ /* Save node; needed for return */
+ sp = p;
+ p = p->n_left;
+ }
+ }
+
+ while (p != NIL) {
+ if (p->n_op == QUALIFIER) {
+ qual |= p->n_type;
+ goto next;
+ }
+ if (p->n_op == CLASS) {
+ if (class != 0)
+ uerror("too many storage classes");
+ class = p->n_type;
+ goto next;
+ }
+ if (p->n_op != TYPE)
+ cerror("typenode got notype %d", p->n_op);
+ switch (p->n_type) {
+ case UCHAR:
+ case USHORT: /* may come from typedef */
+ if (sign != 0 || adj != INT)
+ goto bad;
+ noun = p->n_type;
+ break;
+ case SIGNED:
+ case UNSIGNED:
+ if (sign != 0)
+ goto bad;
+ sign = p->n_type;
+ break;
+ case LONG:
+ if (adj == LONG) {
+ adj = LONGLONG;
+ break;
+ }
+ /* FALLTHROUGH */
+ case SHORT:
+ if (adj != INT)
+ goto bad;
+ adj = p->n_type;
+ break;
+ case INT:
+ case CHAR:
+ case FLOAT:
+ case DOUBLE:
+ if (noun != UNDEF)
+ goto bad;
+ noun = p->n_type;
+ break;
+ case VOID:
+ if (noun != UNDEF || adj != INT)
+ goto bad;
+ adj = noun = VOID;
+ break;
+ case STRTY:
+ case UNIONTY:
+ break;
+ default:
+ goto bad;
+ }
+ next:
+ l = p->n_left;
+ nfree(p);
+ p = l;
+ }
+
+ if (sp) {
+ p = sp;
+ goto uni;
+ }
+
+#ifdef CHAR_UNSIGNED
+ if (noun == CHAR && sign == 0)
+ sign = UNSIGNED;
+#endif
+ if (noun == UNDEF) {
+ noun = INT;
+ } else if (noun == FLOAT) {
+ if (sign != 0 || adj == SHORT)
+ goto bad;
+ noun = (adj == LONG ? DOUBLE : FLOAT);
+ } else if (noun == DOUBLE) {
+ if (sign != 0 || adj == SHORT)
+ goto bad;
+ noun = (adj == LONG ? LDOUBLE : DOUBLE);
+ } else if (noun == CHAR && adj != INT)
+ goto bad;
+
+ if (adj != INT && (noun != DOUBLE && noun != LDOUBLE))
+ noun = adj;
+ if (sign == UNSIGNED)
+ noun += (UNSIGNED-INT);
+
+ p = block(TYPE, NIL, NIL, noun, 0, 0);
+ p->n_qual = qual >> TSHIFT;
+ if (strunem != 0)
+ class = strunem;
+ p->n_lval = class;
+ return p;
+
+bad: uerror("illegal type combination");
+ return mkty(INT, 0, 0);
+}
+
+struct tylnk {
+ struct tylnk *next;
+ union dimfun df;
+};
+
+static void tyreduce(NODE *p, struct tylnk **, int *);
+
+static void
+tylkadd(union dimfun dim, struct tylnk **tylkp, int *ntdim)
+{
+ (*tylkp)->next = tmpalloc(sizeof(struct tylnk));
+ *tylkp = (*tylkp)->next;
+ (*tylkp)->next = NULL;
+ (*tylkp)->df = dim;
+ (*ntdim)++;
+}
+
+/* merge type typ with identifier idp */
+NODE *
+tymerge(NODE *typ, NODE *idp)
+{
+ NODE *p;
+ union dimfun *j;
+ struct tylnk *base, tylnk, *tylkp;
+ unsigned int t;
+ int ntdim, i;
+
+ if (typ->n_op != TYPE)
+ cerror("tymerge: arg 1");
+
+#ifdef PCC_DEBUG
+ if (ddebug > 2) {
+ printf("tymerge(%p,%p)\n", typ, idp);
+ fwalk(typ, eprint, 0);
+ fwalk(idp, eprint, 0);
+ }
+#endif
+
+ idp->n_type = typ->n_type;
+ idp->n_qual = (typ->n_qual << TSHIFT) | idp->n_qual; /* XXX ??? */
+
+ tylkp = &tylnk;
+ tylkp->next = NULL;
+ ntdim = 0;
+
+ tyreduce(idp, &tylkp, &ntdim);
+ idp->n_sue = typ->n_sue;
+
+ for (t = typ->n_type, j = typ->n_df; t&TMASK; t = DECREF(t))
+ if (ISARY(t) || ISFTN(t))
+ tylkadd(*j++, &tylkp, &ntdim);
+
+ if (ntdim) {
+ union dimfun *a = permalloc(sizeof(union dimfun) * ntdim);
+ dimfuncnt += ntdim;
+ for (i = 0, base = tylnk.next; base; base = base->next, i++)
+ a[i] = base->df;
+ idp->n_df = a;
+ } else
+ idp->n_df = NULL;
+
+ /* now idp is a single node: fix up type */
+
+ idp->n_type = ctype(idp->n_type);
+ idp->n_qual = DECQAL(idp->n_qual);
+
+ /* in case ctype has rewritten things */
+ if ((t = BTYPE(idp->n_type)) != STRTY && t != UNIONTY && t != ENUMTY)
+ idp->n_sue = MKSUE(t);
+
+ if (idp->n_op != NAME) {
+ for (p = idp->n_left; p->n_op != NAME; p = p->n_left)
+ nfree(p);
+ nfree(p);
+ idp->n_op = NAME;
+ }
+
+ return(idp);
+}
+
+/*
+ * Retrieve all CM-separated argument types, sizes and dimensions and
+ * put them in an array.
+ * XXX - can only check first type level, side effects?
+ */
+static union arglist *
+arglist(NODE *n)
+{
+ union arglist *al;
+ NODE *w = n, **ap;
+ int num, cnt, i, j, k;
+ TWORD ty;
+
+#ifdef PCC_DEBUG
+ if (pdebug) {
+ printf("arglist %p\n", n);
+ fwalk(n, eprint, 0);
+ }
+#endif
+ /* First: how much to allocate */
+ for (num = cnt = 0, w = n; w->n_op == CM; w = w->n_left) {
+ cnt++; /* Number of levels */
+ num++; /* At least one per step */
+ if (w->n_right->n_op == ELLIPSIS)
+ continue;
+ ty = w->n_right->n_type;
+ if (BTYPE(ty) == STRTY || BTYPE(ty) == UNIONTY ||
+ BTYPE(ty) == ENUMTY)
+ num++;
+ while (ISFTN(ty) == 0 && ISARY(ty) == 0 && ty > BTMASK)
+ ty = DECREF(ty);
+ if (ty > BTMASK)
+ num++;
+ }
+ cnt++;
+ ty = w->n_type;
+ if (BTYPE(ty) == STRTY || BTYPE(ty) == UNIONTY ||
+ BTYPE(ty) == ENUMTY)
+ num++;
+ while (ISFTN(ty) == 0 && ISARY(ty) == 0 && ty > BTMASK)
+ ty = DECREF(ty);
+ if (ty > BTMASK)
+ num++;
+ num += 2; /* TEND + last arg type */
+
+ /* Second: Create list to work on */
+ ap = tmpalloc(sizeof(NODE *) * cnt);
+ al = permalloc(sizeof(union arglist) * num);
+ arglistcnt += num;
+
+ for (w = n, i = 0; w->n_op == CM; w = w->n_left)
+ ap[i++] = w->n_right;
+ ap[i] = w;
+
+ /* Third: Create actual arg list */
+ for (k = 0, j = i; j >= 0; j--) {
+ if (ap[j]->n_op == ELLIPSIS) {
+ al[k++].type = TELLIPSIS;
+ ap[j]->n_op = ICON; /* for tfree() */
+ continue;
+ }
+ /* Convert arrays to pointers */
+ if (ISARY(ap[j]->n_type)) {
+ ap[j]->n_type += (PTR-ARY);
+ ap[j]->n_df++;
+ }
+ /* Convert (silently) functions to pointers */
+ if (ISFTN(ap[j]->n_type))
+ ap[j]->n_type = INCREF(ap[j]->n_type);
+ ty = ap[j]->n_type;
+ al[k++].type = ty;
+ if (BTYPE(ty) == STRTY || BTYPE(ty) == UNIONTY ||
+ BTYPE(ty) == ENUMTY)
+ al[k++].sue = ap[j]->n_sue;
+ while (ISFTN(ty) == 0 && ISARY(ty) == 0 && ty > BTMASK)
+ ty = DECREF(ty);
+ if (ty > BTMASK)
+ al[k++].df = ap[j]->n_df;
+ }
+ al[k++].type = TNULL;
+ if (k > num)
+ cerror("arglist: k%d > num%d", k, num);
+ tfree(n);
+ if (pdebug)
+ alprint(al, 0);
+ return al;
+}
+
+/*
+ * build a type, and stash away dimensions,
+ * from a parse tree of the declaration
+ * the type is build top down, the dimensions bottom up
+ */
+void
+tyreduce(NODE *p, struct tylnk **tylkp, int *ntdim)
+{
+ union dimfun dim;
+ NODE *r = NULL;
+ int o;
+ TWORD t, q;
+
+ o = p->n_op;
+ if (o == NAME)
+ return;
+
+ t = INCREF(p->n_type);
+ q = p->n_qual;
+ switch (o) {
+ case CALL:
+ t += (FTN-PTR);
+ dim.dfun = arglist(p->n_right);
+ break;
+ case UCALL:
+ t += (FTN-PTR);
+ dim.dfun = NULL;
+ break;
+ case LB:
+ t += (ARY-PTR);
+ if (p->n_right->n_op != ICON) {
+ r = p->n_right;
+ o = RB;
+ } else {
+ dim.ddim = p->n_right->n_lval;
+ nfree(p->n_right);
+#ifdef notdef
+ /* XXX - check dimensions at usage time */
+ if (dim.ddim == 0 && p->n_left->n_op == LB)
+ uerror("null dimension");
+#endif
+ }
+ break;
+ }
+
+ p->n_left->n_type = t;
+ p->n_left->n_qual = INCQAL(q) | p->n_left->n_qual;
+ tyreduce(p->n_left, tylkp, ntdim);
+
+ if (o == LB || o == (UCALL) || o == CALL)
+ tylkadd(dim, tylkp, ntdim);
+ if (o == RB) {
+ dim.ddim = -1;
+ tylkadd(dim, tylkp, ntdim);
+ arrstk[arrstkp++] = r;
+ }
+
+ p->n_sp = p->n_left->n_sp;
+ p->n_type = p->n_left->n_type;
+ p->n_qual = p->n_left->n_qual;
+}
+
+static NODE *
+argcast(NODE *p, TWORD t, union dimfun *d, struct suedef *sue)
+{
+ NODE *u, *r = talloc();
+
+ r->n_op = NAME;
+ r->n_type = t;
+ r->n_qual = 0; /* XXX */
+ r->n_df = d;
+ r->n_sue = sue;
+
+ u = buildtree(CAST, r, p);
+ nfree(u->n_left);
+ r = u->n_right;
+ nfree(u);
+ return r;
+}
+
+#ifndef NO_C_BUILTINS
+/*
+ * replace an alloca function with direct allocation on stack.
+ * return a destination temp node.
+ */
+static NODE *
+builtin_alloca(NODE *f, NODE *a)
+{
+ struct symtab *sp;
+ NODE *t, *u;
+
+#ifdef notyet
+ if (xnobuiltins)
+ return NULL;
+#endif
+
+ if (f->n_op != NAME)
+ return NULL; /* not direct call */
+ sp = f->n_sp;
+
+ /* XXX - strcmp is bad, use pointer comparision, redo someday */
+ if (strcmp(sp->sname, "__builtin_alloca")) /* use GCC name */
+ return NULL; /* not alloca */
+
+ if (a == NULL || a->n_op == CM) {
+ uerror("wrong arg count for alloca");
+ return NULL;
+ }
+ t = tempnode(0, VOID|PTR, 0, MKSUE(INT) /* XXX */);
+ u = tempnode(t->n_lval, VOID|PTR, 0, MKSUE(INT) /* XXX */);
+ spalloc(t, a, SZCHAR);
+ tfree(f);
+ return u;
+}
+#endif
+
+#ifdef PCC_DEBUG
+/*
+ * Print a prototype.
+ */
+static void
+alprint(union arglist *al, int in)
+{
+ int i = 0, j;
+
+ for (; al->type != TNULL; al++) {
+ for (j = in; j > 0; j--)
+ printf(" ");
+ printf("arg %d: ", i++);
+ tprint(stdout, al->type, 0);
+ if (BTYPE(al->type) == STRTY ||
+ BTYPE(al->type) == UNIONTY || BTYPE(al->type) == ENUMTY) {
+ al++;
+ printf("dim %d\n", al->df->ddim);
+ }
+ printf("\n");
+ if (ISFTN(DECREF(al->type))) {
+ al++;
+ alprint(al->df->dfun, in+1);
+ }
+ }
+ if (in == 0)
+ printf("end arglist\n");
+}
+#endif
+/*
+ * Do prototype checking and add conversions before calling a function.
+ * Argument f is function and a is a CM-separated list of arguments.
+ * Returns a merged node (via buildtree() of function and arguments.
+ */
+NODE *
+doacall(NODE *f, NODE *a)
+{
+ NODE *w, *r;
+ union arglist *al;
+ struct ap {
+ struct ap *next;
+ NODE *node;
+ } *at, *apole = NULL;
+ int argidx/* , hasarray = 0*/;
+ TWORD type, arrt;
+
+#ifdef PCC_DEBUG
+ if (ddebug) {
+ printf("doacall.\n");
+ fwalk(f, eprint, 0);
+ fwalk(a, eprint, 0);
+ }
+#endif
+
+ /* First let MD code do something */
+ calldec(f, a);
+/* XXX XXX hack */
+ if ((f->n_op == CALL || f->n_op == CALL) &&
+ f->n_left->n_op == ADDROF &&
+ f->n_left->n_left->n_op == NAME &&
+ (f->n_left->n_left->n_type & 0x7e0) == 0x4c0)
+ goto build;
+/* XXX XXX hack */
+
+#ifndef NO_C_BUILTINS
+ /* check for alloca */
+ if ((w = builtin_alloca(f, a)))
+ return w;
+#endif
+ /*
+ * Do some basic checks.
+ */
+ if (f->n_df == NULL || (al = f->n_df[0].dfun) == NULL) {
+ if (Wimplicit_function_declaration) {
+ if (f->n_sp != NULL)
+ werror("no prototype for function '%s()'",
+ f->n_sp->sname);
+ else
+ werror("no prototype for function pointer");
+ }
+ /* floats must be cast to double */
+ if (a == NULL)
+ goto build;
+ for (w = a; w->n_op == CM; w = w->n_left) {
+ if (w->n_right->n_type != FLOAT)
+ continue;
+ w->n_right = argcast(w->n_right, DOUBLE,
+ NULL, MKSUE(DOUBLE));
+ }
+ if (a->n_type == FLOAT) {
+ MKTY(a, DOUBLE, 0, 0);
+ }
+ goto build;
+ }
+ if (al->type == VOID) {
+ if (a != NULL)
+ uerror("function takes no arguments");
+ goto build; /* void function */
+ } else {
+ if (a == NULL) {
+ uerror("function needs arguments");
+ goto build;
+ }
+ }
+#ifdef PCC_DEBUG
+ if (pdebug) {
+ printf("arglist for %p\n",
+ f->n_sp != NULL ? f->n_sp->sname : "function pointer");
+ alprint(al, 0);
+ }
+#endif
+
+ /*
+ * Create a list of pointers to the nodes given as arg.
+ */
+ for (w = a; w->n_op == CM; w = w->n_left) {
+ at = tmpalloc(sizeof(struct ap));
+ at->node = w->n_right;
+ at->next = apole;
+ apole = at;
+ }
+ at = tmpalloc(sizeof(struct ap));
+ at->node = w;
+ at->next = apole;
+ apole = at;
+
+ /*
+ * Do the typechecking by walking up the list.
+ */
+ argidx = 1;
+ while (al->type != TNULL) {
+ if (al->type == TELLIPSIS) {
+ /* convert the rest of float to double */
+ for (; apole; apole = apole->next) {
+ if (apole->node->n_type != FLOAT)
+ continue;
+ MKTY(apole->node, DOUBLE, 0, 0);
+ }
+ goto build;
+ }
+ if (apole == NULL) {
+ uerror("too few arguments to function");
+ goto build;
+ }
+/* al = prototyp, apole = argument till ftn */
+/* type = argumentets typ, arrt = prototypens typ */
+ type = apole->node->n_type;
+ arrt = al->type;
+#if 0
+ if ((hasarray = ISARY(arrt)))
+ arrt += (PTR-ARY);
+#endif
+ if (ISARY(type))
+ type += (PTR-ARY);
+
+ /* Check structs */
+ if (type <= BTMASK && arrt <= BTMASK) {
+ if (type != arrt) {
+ if (ISSOU(BTYPE(type)) || ISSOU(BTYPE(arrt))) {
+incomp: uerror("incompatible types for arg %d",
+ argidx);
+ } else {
+ MKTY(apole->node, arrt, 0, 0)
+ }
+ } else if (ISSOU(BTYPE(type))) {
+ if (apole->node->n_sue != al[1].sue)
+ goto incomp;
+ }
+ goto out;
+ }
+
+ /* Hereafter its only pointers (or arrays) left */
+ /* Check for struct/union intermixing with other types */
+ if (((type <= BTMASK) && ISSOU(BTYPE(type))) ||
+ ((arrt <= BTMASK) && ISSOU(BTYPE(arrt))))
+ goto incomp;
+
+ /* Check for struct/union compatibility */
+ if (type == arrt) {
+ if (ISSOU(BTYPE(type))) {
+ if (apole->node->n_sue == al[1].sue)
+ goto out;
+ } else
+ goto out;
+ }
+ if (BTYPE(arrt) == ENUMTY && BTYPE(type) == INT &&
+ (arrt & ~BTMASK) == (type & ~BTMASK))
+ goto skip; /* XXX enumty destroyed in optim() */
+ if (BTYPE(arrt) == VOID && type > BTMASK)
+ goto skip; /* void *f = some pointer */
+ if (arrt > BTMASK && BTYPE(type) == VOID)
+ goto skip; /* some *f = void pointer */
+ if (apole->node->n_op == ICON && apole->node->n_lval == 0)
+ goto skip; /* Anything assigned a zero */
+
+ if ((type & ~BTMASK) == (arrt & ~BTMASK)) {
+ /* do not complain for intermixed char/uchar */
+ if ((BTYPE(type) == CHAR || BTYPE(type) == UCHAR) &&
+ (BTYPE(arrt) == CHAR || BTYPE(arrt) == UCHAR))
+ goto skip;
+ }
+
+ werror("implicit conversion of argument %d due to prototype",
+ argidx);
+
+skip: if (ISSTR(BTYPE(arrt))) {
+ MKTY(apole->node, arrt, 0, al[1].sue)
+ } else {
+ MKTY(apole->node, arrt, 0, 0)
+ }
+
+out: al++;
+ if (ISSTR(BTYPE(arrt)))
+ al++;
+#if 0
+ while (arrt > BTMASK && !ISFTN(arrt))
+ arrt = DECREF(arrt);
+ if (ISFTN(arrt) || hasarray)
+ al++;
+#else
+ while (arrt > BTMASK) {
+ if (ISARY(arrt) || ISFTN(arrt)) {
+ al++;
+ break;
+ }
+ arrt = DECREF(arrt);
+ }
+#endif
+ apole = apole->next;
+ argidx++;
+ }
+ if (apole != NULL)
+ uerror("too many arguments to function");
+
+build: return buildtree(a == NIL ? UCALL : CALL, f, a);
+}
+
+static int
+chk2(TWORD type, union dimfun *dsym, union dimfun *ddef)
+{
+ while (type > BTMASK) {
+ switch (type & TMASK) {
+ case ARY:
+ /* may be declared without dimension */
+ if (dsym->ddim == 0)
+ dsym->ddim = ddef->ddim;
+ if (ddef->ddim && dsym->ddim != ddef->ddim)
+ return 1;
+ dsym++, ddef++;
+ break;
+ case FTN:
+ /* old-style function headers with function pointers
+ * will most likely not have a prototype.
+ * This is not considered an error. */
+ if (ddef->dfun == NULL) {
+#ifdef notyet
+ werror("declaration not a prototype");
+#endif
+ } else if (chkftn(dsym->dfun, ddef->dfun))
+ return 1;
+ dsym++, ddef++;
+ break;
+ }
+ type = DECREF(type);
+ }
+ return 0;
+}
+
+/*
+ * Compare two function argument lists to see if they match.
+ */
+int
+chkftn(union arglist *usym, union arglist *udef)
+{
+ TWORD t2;
+ int ty, tyn;
+
+ if (usym == NULL)
+ return 0;
+ if (cftnsp != NULL && udef == NULL && usym->type == VOID)
+ return 0; /* foo() { function with foo(void); prototype */
+ if (udef == NULL && usym->type != TNULL)
+ return 1;
+ while (usym->type != TNULL) {
+ if (usym->type == udef->type)
+ goto done;
+ /*
+ * If an old-style declaration, then all types smaller than
+ * int are given as int parameters.
+ */
+ if (intcompare) {
+ ty = BTYPE(usym->type);
+ tyn = BTYPE(udef->type);
+ if (ty == tyn || ty != INT)
+ return 1;
+ if (tyn == CHAR || tyn == UCHAR ||
+ tyn == SHORT || tyn == USHORT)
+ goto done;
+ return 1;
+ } else
+ return 1;
+
+done: ty = BTYPE(usym->type);
+ t2 = usym->type;
+ if (ISSTR(ty)) {
+ usym++, udef++;
+ if (usym->sue != udef->sue)
+ return 1;
+ }
+
+ while (ISFTN(t2) == 0 && ISARY(t2) == 0 && t2 > BTMASK)
+ t2 = DECREF(t2);
+ if (t2 > BTMASK) {
+ usym++, udef++;
+ if (chk2(t2, usym->df, udef->df))
+ return 1;
+ }
+ usym++, udef++;
+ }
+ if (usym->type != udef->type)
+ return 1;
+ return 0;
+}
+
+void
+fixtype(NODE *p, int class)
+{
+ unsigned int t, type;
+ int mod1, mod2;
+ /* fix up the types, and check for legality */
+
+ if( (type = p->n_type) == UNDEF ) return;
+ if ((mod2 = (type&TMASK))) {
+ t = DECREF(type);
+ while( mod1=mod2, mod2 = (t&TMASK) ){
+ if( mod1 == ARY && mod2 == FTN ){
+ uerror( "array of functions is illegal" );
+ type = 0;
+ }
+ else if( mod1 == FTN && ( mod2 == ARY || mod2 == FTN ) ){
+ uerror( "function returns illegal type" );
+ type = 0;
+ }
+ t = DECREF(t);
+ }
+ }
+
+ /* detect function arguments, watching out for structure declarations */
+ if (instruct && ISFTN(type)) {
+ uerror("function illegal in structure or union");
+ type = INCREF(type);
+ }
+ p->n_type = type;
+}
+
+/*
+ * give undefined version of class
+ */
+int
+uclass(int class)
+{
+ if (class == SNULL)
+ return(EXTERN);
+ else if (class == STATIC)
+ return(USTATIC);
+ else if (class == FORTRAN)
+ return(UFORTRAN);
+ else
+ return(class);
+}
+
+int
+fixclass(int class, TWORD type)
+{
+ /* first, fix null class */
+ if (class == SNULL) {
+ if (instruct&INSTRUCT)
+ class = MOS;
+ else if (instruct&INUNION)
+ class = MOU;
+ else if (blevel == 0)
+ class = EXTDEF;
+ else
+ class = AUTO;
+ }
+
+ /* now, do general checking */
+
+ if( ISFTN( type ) ){
+ switch( class ) {
+ default:
+ uerror( "function has illegal storage class" );
+ case AUTO:
+ class = EXTERN;
+ case EXTERN:
+ case EXTDEF:
+ case FORTRAN:
+ case TYPEDEF:
+ case STATIC:
+ case UFORTRAN:
+ case USTATIC:
+ ;
+ }
+ }
+
+ if( class&FIELD ){
+ if( !(instruct&INSTRUCT) ) uerror( "illegal use of field" );
+ return( class );
+ }
+
+ switch( class ){
+
+ case MOU:
+ if( !(instruct&INUNION) ) uerror( "illegal MOU class" );
+ return( class );
+
+ case MOS:
+ if( !(instruct&INSTRUCT) ) uerror( "illegal MOS class" );
+ return( class );
+
+ case MOE:
+ if( instruct & (INSTRUCT|INUNION) ) uerror( "illegal MOE class" );
+ return( class );
+
+ case REGISTER:
+ if (blevel == 0)
+ uerror( "illegal register declaration" );
+ if (blevel == 1)
+ return(PARAM);
+ else
+ return(AUTO);
+
+ case AUTO:
+ if( blevel < 2 ) uerror( "illegal ULABEL class" );
+ return( class );
+
+ case UFORTRAN:
+ case FORTRAN:
+# ifdef NOFORTRAN
+ NOFORTRAN; /* a condition which can regulate the FORTRAN usage */
+# endif
+ if( !ISFTN(type) ) uerror( "fortran declaration must apply to function" );
+ else {
+ type = DECREF(type);
+ if( ISFTN(type) || ISARY(type) || ISPTR(type) ) {
+ uerror( "fortran function has wrong type" );
+ }
+ }
+ case STNAME:
+ case UNAME:
+ case ENAME:
+ case EXTERN:
+ case STATIC:
+ case EXTDEF:
+ case TYPEDEF:
+ case USTATIC:
+ return( class );
+
+ default:
+ cerror( "illegal class: %d", class );
+ /* NOTREACHED */
+
+ }
+ return 0; /* XXX */
+}
+
+/*
+ * Generates a goto statement; sets up label number etc.
+ */
+void
+gotolabel(char *name)
+{
+ struct symtab *s = lookup(name, SLBLNAME);
+
+ if (s->soffset == 0)
+ s->soffset = -getlab();
+ branch(s->soffset < 0 ? -s->soffset : s->soffset);
+}
+
+/*
+ * Sets a label for gotos.
+ */
+void
+deflabel(char *name)
+{
+ struct symtab *s = lookup(name, SLBLNAME);
+
+ if (s->soffset > 0)
+ uerror("label '%s' redefined", name);
+ if (s->soffset == 0)
+ s->soffset = getlab();
+ if (s->soffset < 0)
+ s->soffset = -s->soffset;
+ plabel( s->soffset);
+}
+
+struct symtab *
+getsymtab(char *name, int flags)
+{
+ struct symtab *s;
+
+ if (flags & STEMP) {
+ s = tmpalloc(sizeof(struct symtab));
+ } else {
+ s = permalloc(sizeof(struct symtab));
+ symtabcnt++;
+ }
+ s->sname = name;
+ s->snext = NULL;
+ s->stype = UNDEF;
+ s->squal = 0;
+ s->sclass = SNULL;
+ s->sflags = flags & SMASK;
+ s->soffset = 0;
+ s->slevel = blevel;
+ return s;
+}
+
+#ifdef PCC_DEBUG
+static char *
+ccnames[] = { /* names of storage classes */
+ "SNULL",
+ "AUTO",
+ "EXTERN",
+ "STATIC",
+ "REGISTER",
+ "EXTDEF",
+ "LABEL",
+ "ULABEL",
+ "MOS",
+ "PARAM",
+ "STNAME",
+ "MOU",
+ "UNAME",
+ "TYPEDEF",
+ "FORTRAN",
+ "ENAME",
+ "MOE",
+ "UFORTRAN",
+ "USTATIC",
+ };
+
+char *
+scnames(int c)
+{
+ /* return the name for storage class c */
+ static char buf[12];
+ if( c&FIELD ){
+ snprintf( buf, sizeof(buf), "FIELD[%d]", c&FLDSIZ );
+ return( buf );
+ }
+ return( ccnames[c] );
+ }
+#endif