root/src/cmd/cc/dcl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dodecl
  2. mkstatic
  3. tcopy
  4. doinit
  5. peekinit
  6. nextinit
  7. isstruct
  8. init1
  9. newlist
  10. haspointers
  11. sualign
  12. xround
  13. ofnproto
  14. argmark
  15. walkparam
  16. markdcl
  17. revertdcl
  18. fnproto
  19. anyproto
  20. fnproto1
  21. dbgdecl
  22. push
  23. push1
  24. sametype
  25. rsametype
  26. sigind
  27. signat
  28. signature
  29. sign
  30. snap
  31. dotag
  32. dcllabel
  33. paramconv
  34. adecl
  35. pdecl
  36. xdecl
  37. tmerge
  38. edecl
  39. maxtype
  40. doenum
  41. symadjust
  42. contig

// Inferno utils/cc/dcl.c
// http://code.google.com/p/inferno-os/source/browse/utils/cc/dcl.c
//
//      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//      Portions Copyright © 1997-1999 Vita Nuova Limited
//      Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//      Portions Copyright © 2004,2006 Bruce Ellis
//      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//      Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//      Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <u.h>
#include "cc.h"

Node*
dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n)
{
        Sym *s;
        Node *n1;
        int32 v;

        nearln = lineno;
        lastfield = 0;

loop:
        if(n != Z)
        switch(n->op) {
        default:
                diag(n, "unknown declarator: %O", n->op);
                break;

        case OARRAY:
                t = typ(TARRAY, t);
                t->width = 0;
                n1 = n->right;
                n = n->left;
                if(n1 != Z) {
                        complex(n1);
                        v = -1;
                        if(n1->op == OCONST)
                                v = n1->vconst;
                        if(v <= 0) {
                                diag(n, "array size must be a positive constant");
                                v = 1;
                        }
                        t->width = v * t->link->width;
                }
                goto loop;

        case OIND:
                t = typ(TIND, t);
                t->garb = n->garb;
                n = n->left;
                goto loop;

        case OFUNC:
                t = typ(TFUNC, t);
                t->down = fnproto(n);
                n = n->left;
                goto loop;

        case OBIT:
                n1 = n->right;
                complex(n1);
                lastfield = -1;
                if(n1->op == OCONST)
                        lastfield = n1->vconst;
                if(lastfield < 0) {
                        diag(n, "field width must be non-negative constant");
                        lastfield = 1;
                }
                if(lastfield == 0) {
                        lastbit = 0;
                        firstbit = 1;
                        if(n->left != Z) {
                                diag(n, "zero width named field");
                                lastfield = 1;
                        }
                }
                if(!typei[t->etype]) {
                        diag(n, "field type must be int-like");
                        t = types[TINT];
                        lastfield = 1;
                }
                if(lastfield > tfield->width*8) {
                        diag(n, "field width larger than field unit");
                        lastfield = 1;
                }
                lastbit += lastfield;
                if(lastbit > tfield->width*8) {
                        lastbit = lastfield;
                        firstbit = 1;
                }
                n = n->left;
                goto loop;

        case ONAME:
                if(f == NODECL)
                        break;
                s = n->sym;
                (*f)(c, t, s);
                if(s->class == CLOCAL)
                        s = mkstatic(s);
                if(dataflag) {
                        s->dataflag = dataflag;
                        dataflag = 0;
                }
                firstbit = 0;
                n->sym = s;
                n->type = s->type;
                n->xoffset = s->offset;
                n->class = s->class;
                n->etype = TVOID;
                if(n->type != T)
                        n->etype = n->type->etype;
                if(debug['d'])
                        dbgdecl(s);
                acidvar(s);
                godefvar(s);
                s->varlineno = lineno;
                break;
        }
        lastdcl = t;
        return n;
}

Sym*
mkstatic(Sym *s)
{
        Sym *s1;

        if(s->class != CLOCAL)
                return s;
        snprint(symb, NSYMB, "%s$%d", s->name, s->block);
        s1 = lookup();
        if(s1->class != CSTATIC) {
                s1->type = s->type;
                s1->offset = s->offset;
                s1->block = s->block;
                s1->class = CSTATIC;
        }
        return s1;
}

/*
 * make a copy of a typedef
 * the problem is to split out incomplete
 * arrays so that it is in the variable
 * rather than the typedef.
 */
Type*
tcopy(Type *t)
{
        Type *tl, *tx;
        int et;

        if(t == T)
                return t;
        et = t->etype;
        if(typesu[et])
                return t;
        tl = tcopy(t->link);
        if(tl != t->link ||
          (et == TARRAY && t->width == 0)) {
                tx = copytyp(t);
                tx->link = tl;
                return tx;
        }
        return t;
}

Node*
doinit(Sym *s, Type *t, int32 o, Node *a)
{
        Node *n;

        if(t == T)
                return Z;
        if(s->class == CEXTERN) {
                s->class = CGLOBL;
                if(debug['d'])
                        dbgdecl(s);
        }
        if(debug['i']) {
                print("t = %T; o = %d; n = %s\n", t, o, s->name);
                prtree(a, "doinit value");
        }


        n = initlist;
        if(a->op == OINIT)
                a = a->left;
        initlist = a;

        a = init1(s, t, o, 0);
        if(initlist != Z)
                diag(initlist, "more initializers than structure: %s",
                        s->name);
        initlist = n;

        return a;
}

/*
 * get next major operator,
 * dont advance initlist.
 */
Node*
peekinit(void)
{
        Node *a;

        a = initlist;

loop:
        if(a == Z)
                return a;
        if(a->op == OLIST) {
                a = a->left;
                goto loop;
        }
        return a;
}

/*
 * consume and return next element on
 * initlist. expand strings.
 */
Node*
nextinit(void)
{
        Node *a, *b, *n;

        a = initlist;
        n = Z;

        if(a == Z)
                return a;
        if(a->op == OLIST) {
                n = a->right;
                a = a->left;
        }
        if(a->op == OUSED) {
                a = a->left;
                b = new(OCONST, Z, Z);
                b->type = a->type->link;
                if(a->op == OSTRING) {
                        b->vconst = convvtox(*a->cstring, TCHAR);
                        a->cstring++;
                }
                if(a->op == OLSTRING) {
                        b->vconst = convvtox(*a->rstring, TRUNE);
                        a->rstring++;
                }
                a->type->width -= b->type->width;
                if(a->type->width <= 0)
                        initlist = n;
                return b;
        }
        initlist = n;
        return a;
}

int
isstruct(Node *a, Type *t)
{
        Node *n;

        switch(a->op) {
        case ODOTDOT:
                n = a->left;
                if(n && n->type && sametype(n->type, t))
                        return 1;
        case OSTRING:
        case OLSTRING:
        case OCONST:
        case OINIT:
        case OELEM:
                return 0;
        }

        n = new(ODOTDOT, Z, Z);
        *n = *a;

        /*
         * ODOTDOT is a flag for tcom
         * a second tcom will not be performed
         */
        a->op = ODOTDOT;
        a->left = n;
        a->right = Z;

        if(tcom(n))
                return 0;

        if(sametype(n->type, t))
                return 1;
        return 0;
}

Node*
init1(Sym *s, Type *t, int32 o, int exflag)
{
        Node *a, *l, *r, nod;
        Type *t1;
        int32 e, w, so, mw;

        a = peekinit();
        if(a == Z)
                return Z;

        if(debug['i']) {
                print("t = %T; o = %d; n = %s\n", t, o, s->name);
                prtree(a, "init1 value");
        }

        if(exflag && a->op == OINIT)
                return doinit(s, t, o, nextinit());

        switch(t->etype) {
        default:
                diag(Z, "unknown type in initialization: %T to: %s", t, s->name);
                return Z;

        case TCHAR:
        case TUCHAR:
        case TINT:
        case TUINT:
        case TSHORT:
        case TUSHORT:
        case TLONG:
        case TULONG:
        case TVLONG:
        case TUVLONG:
        case TFLOAT:
        case TDOUBLE:
        case TIND:
        single:
                if(a->op == OARRAY || a->op == OELEM)
                        return Z;

                a = nextinit();
                if(a == Z)
                        return Z;

                if(t->nbits)
                        diag(Z, "cannot initialize bitfields");
                if(s->class == CAUTO) {
                        l = new(ONAME, Z, Z);
                        l->sym = s;
                        l->type = t;
                        l->etype = TVOID;
                        if(s->type)
                                l->etype = s->type->etype;
                        l->xoffset = s->offset + o;
                        l->class = s->class;

                        l = new(OASI, l, a);
                        return l;
                }

                complex(a);
                if(a->type == T)
                        return Z;

                if(a->op == OCONST) {
                        if(vconst(a) && t->etype == TIND && a->type && a->type->etype != TIND){
                                diag(a, "initialize pointer to an integer: %s", s->name);
                                return Z;
                        }
                        if(!sametype(a->type, t)) {
                                /* hoop jumping to save malloc */
                                if(nodcast == Z)
                                        nodcast = new(OCAST, Z, Z);
                                nod = *nodcast;
                                nod.left = a;
                                nod.type = t;
                                nod.lineno = a->lineno;
                                complex(&nod);
                                if(nod.type)
                                        *a = nod;
                        }
                        if(a->op != OCONST) {
                                diag(a, "initializer is not a constant: %s",
                                        s->name);
                                return Z;
                        }
                        if(vconst(a) == 0)
                                return Z;
                        goto gext;
                }
                if(t->etype == TIND) {
                        while(a->op == OCAST) {
                                warn(a, "CAST in initialization ignored");
                                a = a->left;
                        }
                        if(!sametype(t, a->type)) {
                                diag(a, "initialization of incompatible pointers: %s\n%T and %T",
                                        s->name, t, a->type);
                        }
                        if(a->op == OADDR)
                                a = a->left;
                        goto gext;
                }

                while(a->op == OCAST)
                        a = a->left;
                if(a->op == OADDR) {
                        warn(a, "initialize pointer to an integer: %s", s->name);
                        a = a->left;
                        goto gext;
                }
                diag(a, "initializer is not a constant: %s", s->name);
                return Z;

        gext:
                gextern(s, a, o, t->width);

                return Z;

        case TARRAY:
                w = t->link->width;
                if(a->op == OSTRING || a->op == OLSTRING)
                if(typei[t->link->etype]) {
                        /*
                         * get rid of null if sizes match exactly
                         */
                        a = nextinit();
                        mw = t->width/w;
                        so = a->type->width/a->type->link->width;
                        if(mw && so > mw) {
                                if(so != mw+1)
                                        diag(a, "string initialization larger than array");
                                a->type->width -= a->type->link->width;
                        }

                        /*
                         * arrange strings to be expanded
                         * inside OINIT braces.
                         */
                        a = new(OUSED, a, Z);
                        return doinit(s, t, o, a);
                }

                mw = -w;
                l = Z;
                for(e=0;;) {
                        /*
                         * peek ahead for element initializer
                         */
                        a = peekinit();
                        if(a == Z)
                                break;
                        if(a->op == OELEM && t->link->etype != TSTRUCT)
                                break;
                        if(a->op == OARRAY) {
                                if(e && exflag)
                                        break;
                                a = nextinit();
                                r = a->left;
                                complex(r);
                                if(r->op != OCONST) {
                                        diag(r, "initializer subscript must be constant");
                                        return Z;
                                }
                                e = r->vconst;
                                if(t->width != 0)
                                        if(e < 0 || e*w >= t->width) {
                                                diag(a, "initialization index out of range: %d", e);
                                                continue;
                                        }
                        }

                        so = e*w;
                        if(so > mw)
                                mw = so;
                        if(t->width != 0)
                                if(mw >= t->width)
                                        break;
                        r = init1(s, t->link, o+so, 1);
                        l = newlist(l, r);
                        e++;
                }
                if(t->width == 0)
                        t->width = mw+w;
                return l;

        case TUNION:
        case TSTRUCT:
                /*
                 * peek ahead to find type of rhs.
                 * if its a structure, then treat
                 * this element as a variable
                 * rather than an aggregate.
                 */
                if(isstruct(a, t))
                        goto single;

                if(t->width <= 0) {
                        diag(Z, "incomplete structure: %s", s->name);
                        return Z;
                }
                l = Z;

        again:
                for(t1 = t->link; t1 != T; t1 = t1->down) {
                        if(a->op == OARRAY && t1->etype != TARRAY)
                                break;
                        if(a->op == OELEM) {
                                if(t1->sym != a->sym)
                                        continue;
                                nextinit();
                        }
                        r = init1(s, t1, o+t1->offset, 1);
                        l = newlist(l, r);
                        a = peekinit();
                        if(a == Z)
                                break;
                        if(a->op == OELEM)
                                goto again;
                }
                if(a && a->op == OELEM)
                        diag(a, "structure element not found %F", a);
                return l;
        }
}

Node*
newlist(Node *l, Node *r)
{
        if(r == Z)
                return l;
        if(l == Z)
                return r;
        return new(OLIST, l, r);
}

static int
haspointers(Type *t)
{
        Type *fld;

        switch(t->etype) {
        case TSTRUCT:
                for(fld = t->link; fld != T; fld = fld->down) {
                        if(haspointers(fld))
                                return 1;
                }
                return 0;
        case TARRAY:
                return haspointers(t->link);
        case TFUNC:
        case TIND:
                return 1;
        default:
                return 0;
        }
}

void
sualign(Type *t)
{
        Type *l;
        int32 o, w, maxal;

        o = 0;
        maxal = 0;
        switch(t->etype) {

        case TSTRUCT:
                t->offset = 0;
                w = 0;
                for(l = t->link; l != T; l = l->down) {
                        if(l->nbits) {
                                if(l->shift <= 0) {
                                        l->shift = -l->shift;
                                        w = xround(w, tfield->width);
                                        o = w;
                                        w += tfield->width;
                                }
                                l->offset = o;
                        } else {
                                if(l->width <= 0)
                                if(l->down != T)
                                        if(l->sym)
                                                diag(Z, "incomplete structure element: %s",
                                                        l->sym->name);
                                        else
                                                diag(Z, "incomplete structure element");
                                w = align(w, l, Ael1, &maxal);
                                l->offset = w;
                                w = align(w, l, Ael2, &maxal);
                        }
                }
                w = align(w, t, Asu2, &maxal);
                t->width = w;
                t->align = maxal;
                acidtype(t);
                godeftype(t);
                return;

        case TUNION:
                t->offset = 0;
                w = 0;
                for(l = t->link; l != T; l = l->down) {
                        if(l->width <= 0)
                                if(l->sym)
                                        diag(Z, "incomplete union element: %s",
                                                l->sym->name);
                                else
                                        diag(Z, "incomplete union element");
                        l->offset = 0;
                        l->shift = 0;
                        if((debug['q'] || debug['Q']) && haspointers(l))
                                diag(Z, "precise garbage collector cannot handle unions with pointers");

                        o = align(align(0, l, Ael1, &maxal), l, Ael2, &maxal);
                        if(o > w)
                                w = o;
                }
                w = align(w, t, Asu2, &maxal);
                t->width = w;
                t->align = maxal;
                acidtype(t);
                godeftype(t);
                return;

        default:
                diag(Z, "unknown type in sualign: %T", t);
                break;
        }
}

int32
xround(int32 v, int w)
{
        int r;

        if(w <= 0 || w > 8) {
                diag(Z, "rounding by %d", w);
                w = 1;
        }
        r = v%w;
        if(r)
                v += w-r;
        return v;
}

Type*
ofnproto(Node *n)
{
        Type *tl, *tr, *t;

        if(n == Z)
                return T;
        switch(n->op) {
        case OLIST:
                tl = ofnproto(n->left);
                tr = ofnproto(n->right);
                if(tl == T)
                        return tr;
                tl->down = tr;
                return tl;

        case ONAME:
                t = copytyp(n->sym->type);
                t->down = T;
                return t;
        }
        return T;
}

#define ANSIPROTO       1
#define OLDPROTO        2

void
argmark(Node *n, int pass)
{
        Type *t;

        autoffset = align(0, thisfn->link, Aarg0, nil);
        stkoff = 0;
        for(; n->left != Z; n = n->left) {
                if(n->op != OFUNC || n->left->op != ONAME)
                        continue;
                walkparam(n->right, pass);
                if(pass != 0 && anyproto(n->right) == OLDPROTO) {
                        t = typ(TFUNC, n->left->sym->type->link);
                        t->down = typ(TOLD, T);
                        t->down->down = ofnproto(n->right);
                        tmerge(t, n->left->sym);
                        n->left->sym->type = t;
                }
                break;
        }
        autoffset = 0;
        stkoff = 0;
}

void
walkparam(Node *n, int pass)
{
        Sym *s;
        Node *n1;

        if(n != Z && n->op == OPROTO && n->left == Z && n->type == types[TVOID])
                return;

loop:
        if(n == Z)
                return;
        switch(n->op) {
        default:
                diag(n, "argument not a name/prototype: %O", n->op);
                break;

        case OLIST:
                walkparam(n->left, pass);
                n = n->right;
                goto loop;

        case OPROTO:
                for(n1 = n; n1 != Z; n1=n1->left)
                        if(n1->op == ONAME) {
                                if(pass == 0) {
                                        s = n1->sym;
                                        push1(s);
                                        s->offset = -1;
                                        break;
                                }
                                dodecl(pdecl, CPARAM, n->type, n->left);
                                break;
                        }
                if(n1)
                        break;
                if(pass == 0) {
                        /*
                         * extension:
                         *      allow no name in argument declaration
                        diag(Z, "no name in argument declaration");
                         */
                        break;
                }
                dodecl(NODECL, CPARAM, n->type, n->left);
                pdecl(CPARAM, lastdcl, S);
                break;

        case ODOTDOT:
                break;
        
        case ONAME:
                s = n->sym;
                if(pass == 0) {
                        push1(s);
                        s->offset = -1;
                        break;
                }
                if(s->offset != -1) {
                        if(autoffset == 0) {
                                firstarg = s;
                                firstargtype = s->type;
                        }
                        autoffset = align(autoffset, s->type, Aarg1, nil);
                        s->offset = autoffset;
                        autoffset = align(autoffset, s->type, Aarg2, nil);
                } else
                        dodecl(pdecl, CXXX, types[TINT], n);
                break;
        }
}

void
markdcl(void)
{
        Decl *d;

        blockno++;
        d = push();
        d->val = DMARK;
        d->offset = autoffset;
        d->block = autobn;
        autobn = blockno;
}

Node*
revertdcl(void)
{
        Decl *d;
        Sym *s;
        Node *n, *n1;

        n = Z;
        for(;;) {
                d = dclstack;
                if(d == D) {
                        diag(Z, "pop off dcl stack");
                        break;
                }
                dclstack = d->link;
                s = d->sym;
                switch(d->val) {
                case DMARK:
                        autoffset = d->offset;
                        autobn = d->block;
                        return n;

                case DAUTO:
                        if(debug['d'])
                                print("revert1 \"%s\"\n", s->name);
                        if(s->aused == 0) {
                                nearln = s->varlineno;
                                if(s->class == CAUTO)
                                        warn(Z, "auto declared and not used: %s", s->name);
                                if(s->class == CPARAM)
                                        warn(Z, "param declared and not used: %s", s->name);
                        }
                        if(s->type && (s->type->garb & GVOLATILE)) {
                                n1 = new(ONAME, Z, Z);
                                n1->sym = s;
                                n1->type = s->type;
                                n1->etype = TVOID;
                                if(n1->type != T)
                                        n1->etype = n1->type->etype;
                                n1->xoffset = s->offset;
                                n1->class = s->class;

                                n1 = new(OADDR, n1, Z);
                                n1 = new(OUSED, n1, Z);
                                if(n == Z)
                                        n = n1;
                                else
                                        n = new(OLIST, n1, n);
                        }
                        s->type = d->type;
                        s->class = d->class;
                        s->offset = d->offset;
                        s->block = d->block;
                        s->varlineno = d->varlineno;
                        s->aused = d->aused;
                        break;

                case DSUE:
                        if(debug['d'])
                                print("revert2 \"%s\"\n", s->name);
                        s->suetag = d->type;
                        s->sueblock = d->block;
                        break;

                case DLABEL:
                        if(debug['d'])
                                print("revert3 \"%s\"\n", s->name);
                        if(s->label && s->label->addable == 0)
                                warn(s->label, "label declared and not used \"%s\"", s->name);
                        s->label = Z;
                        break;
                }
        }
        return n;
}

Type*
fnproto(Node *n)
{
        int r;

        r = anyproto(n->right);
        if(r == 0 || (r & OLDPROTO)) {
                if(r & ANSIPROTO)
                        diag(n, "mixed ansi/old function declaration: %F", n->left);
                return T;
        }
        return fnproto1(n->right);
}

int
anyproto(Node *n)
{
        int r;

        r = 0;

loop:
        if(n == Z)
                return r;
        switch(n->op) {
        case OLIST:
                r |= anyproto(n->left);
                n = n->right;
                goto loop;

        case ODOTDOT:
        case OPROTO:
                return r | ANSIPROTO;
        }
        return r | OLDPROTO;
}

Type*
fnproto1(Node *n)
{
        Type *t;

        if(n == Z)
                return T;
        switch(n->op) {
        case OLIST:
                t = fnproto1(n->left);
                if(t != T)
                        t->down = fnproto1(n->right);
                return t;

        case OPROTO:
                lastdcl = T;
                dodecl(NODECL, CXXX, n->type, n->left);
                t = typ(TXXX, T);
                if(lastdcl != T)
                        *t = *paramconv(lastdcl, 1);
                return t;

        case ONAME:
                diag(n, "incomplete argument prototype");
                return typ(TINT, T);

        case ODOTDOT:
                return typ(TDOT, T);
        }
        diag(n, "unknown op in fnproto");
        return T;
}

void
dbgdecl(Sym *s)
{
        print("decl \"%s\": C=%s [B=%d:O=%d] T=%T\n",
                s->name, cnames[s->class], s->block, s->offset, s->type);
}

Decl*
push(void)
{
        Decl *d;

        d = alloc(sizeof(*d));
        d->link = dclstack;
        dclstack = d;
        return d;
}

Decl*
push1(Sym *s)
{
        Decl *d;

        d = push();
        d->sym = s;
        d->val = DAUTO;
        d->type = s->type;
        d->class = s->class;
        d->offset = s->offset;
        d->block = s->block;
        d->varlineno = s->varlineno;
        d->aused = s->aused;
        return d;
}

int
sametype(Type *t1, Type *t2)
{

        if(t1 == t2)
                return 1;
        return rsametype(t1, t2, 5, 1);
}

int
rsametype(Type *t1, Type *t2, int n, int f)
{
        int et;

        n--;
        for(;;) {
                if(t1 == t2)
                        return 1;
                if(t1 == T || t2 == T)
                        return 0;
                if(n <= 0)
                        return 1;
                et = t1->etype;
                if(et != t2->etype)
                        return 0;
                if(et == TFUNC) {
                        if(!rsametype(t1->link, t2->link, n, 0))
                                return 0;
                        t1 = t1->down;
                        t2 = t2->down;
                        while(t1 != T && t2 != T) {
                                if(t1->etype == TOLD) {
                                        t1 = t1->down;
                                        continue;
                                }
                                if(t2->etype == TOLD) {
                                        t2 = t2->down;
                                        continue;
                                }
                                while(t1 != T || t2 != T) {
                                        if(!rsametype(t1, t2, n, 0))
                                                return 0;
                                        t1 = t1->down;
                                        t2 = t2->down;
                                }
                                break;
                        }
                        return 1;
                }
                if(et == TARRAY)
                        if(t1->width != t2->width && t1->width != 0 && t2->width != 0)
                                return 0;
                if(typesu[et]) {
                        if(t1->link == T)
                                snap(t1);
                        if(t2->link == T)
                                snap(t2);
                        t1 = t1->link;
                        t2 = t2->link;
                        for(;;) {
                                if(t1 == t2)
                                        return 1;
                                if(!rsametype(t1, t2, n, 0))
                                        return 0;
                                t1 = t1->down;
                                t2 = t2->down;
                        }
                }
                t1 = t1->link;
                t2 = t2->link;
                if((f || !debug['V']) && et == TIND) {
                        if(t1 != T && t1->etype == TVOID)
                                return 1;
                        if(t2 != T && t2->etype == TVOID)
                                return 1;
                }
        }
}

typedef struct Typetab Typetab;

struct Typetab{
        int n;
        Type **a;
};

static int
sigind(Type *t, Typetab *tt)
{
        int n;
        Type **a, **na, **p, **e;

        n = tt->n;
        a = tt->a;
        e = a+n;
        /* linear search seems ok */
        for(p = a ; p < e; p++)
                if(sametype(*p, t))
                        return p-a;
        if((n&15) == 0){
                na = malloc((n+16)*sizeof(Type*));
                if(na == nil) {
                        print("%s: out of memory", argv0);
                        errorexit();
                }
                memmove(na, a, n*sizeof(Type*));
                free(a);
                a = tt->a = na;
        }
        a[tt->n++] = t;
        return -1;
}

static uint32
signat(Type *t, Typetab *tt)
{
        int i;
        Type *t1;
        int32 s;

        s = 0;
        for(; t; t=t->link) {
                s = s*thash1 + thash[t->etype];
                if(t->garb&GINCOMPLETE)
                        return s;
                switch(t->etype) {
                default:
                        return s;
                case TARRAY:
                        s = s*thash2 + 0;       /* was t->width */
                        break;
                case TFUNC:
                        for(t1=t->down; t1; t1=t1->down)
                                s = s*thash3 + signat(t1, tt);
                        break;
                case TSTRUCT:
                case TUNION:
                        if((i = sigind(t, tt)) >= 0){
                                s = s*thash2 + i;
                                return s;
                        }
                        for(t1=t->link; t1; t1=t1->down)
                                s = s*thash3 + signat(t1, tt);
                        return s;
                case TIND:
                        break;
                }
        }
        return s;
}

uint32
signature(Type *t)
{
        uint32 s;
        Typetab tt;

        tt.n = 0;
        tt.a = nil;
        s = signat(t, &tt);
        free(tt.a);
        return s;
}

uint32
sign(Sym *s)
{
        uint32 v;
        Type *t;

        if(s->sig == SIGINTERN)
                return SIGNINTERN;
        if((t = s->type) == T)
                return 0;
        v = signature(t);
        if(v == 0)
                v = SIGNINTERN;
        return v;
}

void
snap(Type *t)
{
        if(typesu[t->etype])
        if(t->link == T && t->tag && t->tag->suetag) {
                t->link = t->tag->suetag->link;
                t->width = t->tag->suetag->width;
        }
}

Type*
dotag(Sym *s, int et, int bn)
{
        Decl *d;

        if(bn != 0 && bn != s->sueblock) {
                d = push();
                d->sym = s;
                d->val = DSUE;
                d->type = s->suetag;
                d->block = s->sueblock;
                s->suetag = T;
        }
        if(s->suetag == T) {
                s->suetag = typ(et, T);
                s->sueblock = autobn;
        }
        if(s->suetag->etype != et)
                diag(Z, "tag used for more than one type: %s",
                        s->name);
        if(s->suetag->tag == S)
                s->suetag->tag = s;
        return s->suetag;
}

Node*
dcllabel(Sym *s, int f)
{
        Decl *d, d1;
        Node *n;

        n = s->label;
        if(n != Z) {
                if(f) {
                        if(n->complex)
                                diag(Z, "label reused: %s", s->name);
                        n->complex = 1; // declared
                } else
                        n->addable = 1; // used
                return n;
        }

        d = push();
        d->sym = s;
        d->val = DLABEL;
        dclstack = d->link;

        d1 = *firstdcl;
        *firstdcl = *d;
        *d = d1;

        firstdcl->link = d;
        firstdcl = d;

        n = new(OXXX, Z, Z);
        n->sym = s;
        n->complex = f;
        n->addable = !f;
        s->label = n;

        if(debug['d'])
                dbgdecl(s);
        return n;
}

Type*
paramconv(Type *t, int f)
{

        switch(t->etype) {
        case TUNION:
        case TSTRUCT:
                if(t->width <= 0)
                        diag(Z, "incomplete structure: %s", t->tag->name);
                break;

        case TARRAY:
                t = typ(TIND, t->link);
                t->width = types[TIND]->width;
                break;

        case TFUNC:
                t = typ(TIND, t);
                t->width = types[TIND]->width;
                break;

        case TFLOAT:
                if(!f)
                        t = types[TDOUBLE];
                break;

        case TCHAR:
        case TSHORT:
                if(!f)
                        t = types[TINT];
                break;

        case TUCHAR:
        case TUSHORT:
                if(!f)
                        t = types[TUINT];
                break;
        }
        return t;
}

void
adecl(int c, Type *t, Sym *s)
{

        if(c == CSTATIC)
                c = CLOCAL;
        if(t->etype == TFUNC) {
                if(c == CXXX)
                        c = CEXTERN;
                if(c == CLOCAL)
                        c = CSTATIC;
                if(c == CAUTO || c == CEXREG)
                        diag(Z, "function cannot be %s %s", cnames[c], s->name);
        }
        if(c == CXXX)
                c = CAUTO;
        if(s) {
                if(s->class == CSTATIC)
                        if(c == CEXTERN || c == CGLOBL) {
                                warn(Z, "just say static: %s", s->name);
                                c = CSTATIC;
                        }
                if(s->class == CAUTO || s->class == CPARAM || s->class == CLOCAL)
                if(s->block == autobn)
                        diag(Z, "auto redeclaration of: %s", s->name);
                if(c != CPARAM)
                        push1(s);
                s->block = autobn;
                s->offset = 0;
                s->type = t;
                s->class = c;
                s->aused = 0;
        }
        switch(c) {
        case CAUTO:
                autoffset = align(autoffset, t, Aaut3, nil);
                stkoff = maxround(stkoff, autoffset);
                s->offset = -autoffset;
                break;

        case CPARAM:
                if(autoffset == 0) {
                        firstarg = s;
                        firstargtype = t;
                }
                autoffset = align(autoffset, t, Aarg1, nil);
                if(s)
                        s->offset = autoffset;
                autoffset = align(autoffset, t, Aarg2, nil);
                break;
        }
}

void
pdecl(int c, Type *t, Sym *s)
{
        if(s && s->offset != -1) {
                diag(Z, "not a parameter: %s", s->name);
                return;
        }
        t = paramconv(t, c==CPARAM);
        if(c == CXXX)
                c = CPARAM;
        if(c != CPARAM) {
                diag(Z, "parameter cannot have class: %s", s->name);
                c = CPARAM;
        }
        adecl(c, t, s);
}

void
xdecl(int c, Type *t, Sym *s)
{
        int32 o;

        o = 0;
        switch(c) {
        case CEXREG:
                o = exreg(t);
                if(o == 0)
                        c = CEXTERN;
                if(s->class == CGLOBL)
                        c = CGLOBL;
                break;

        case CEXTERN:
                if(s->class == CGLOBL)
                        c = CGLOBL;
                break;

        case CXXX:
                c = CGLOBL;
                if(s->class == CEXTERN)
                        s->class = CGLOBL;
                break;

        case CAUTO:
                diag(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
                c = CEXTERN;
                break;

        case CTYPESTR:
                if(!typesuv[t->etype]) {
                        diag(Z, "typestr must be struct/union: %s", s->name);
                        break;
                }
                dclfunct(t, s);
                break;
        }

        if(s->class == CSTATIC)
                if(c == CEXTERN || c == CGLOBL) {
                        warn(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
                        c = CSTATIC;
                }
        if(s->type != T)
                if(s->class != c || !sametype(t, s->type) || t->etype == TENUM) {
                        diag(Z, "external redeclaration of: %s", s->name);
                        Bprint(&diagbuf, "      %s %T %L\n", cnames[c], t, nearln);
                        Bprint(&diagbuf, "      %s %T %L\n", cnames[s->class], s->type, s->varlineno);
                }
        tmerge(t, s);
        s->type = t;
        s->class = c;
        s->block = 0;
        s->offset = o;
}

void
tmerge(Type *t1, Sym *s)
{
        Type *ta, *tb, *t2;

        t2 = s->type;
        for(;;) {
                if(t1 == T || t2 == T || t1 == t2)
                        break;
                if(t1->etype != t2->etype)
                        break;
                switch(t1->etype) {
                case TFUNC:
                        ta = t1->down;
                        tb = t2->down;
                        if(ta == T) {
                                t1->down = tb;
                                break;
                        }
                        if(tb == T)
                                break;
                        while(ta != T && tb != T) {
                                if(ta == tb)
                                        break;
                                /* ignore old-style flag */
                                if(ta->etype == TOLD) {
                                        ta = ta->down;
                                        continue;
                                }
                                if(tb->etype == TOLD) {
                                        tb = tb->down;
                                        continue;
                                }
                                /* checking terminated by ... */
                                if(ta->etype == TDOT && tb->etype == TDOT) {
                                        ta = T;
                                        tb = T;
                                        break;
                                }
                                if(!sametype(ta, tb))
                                        break;
                                ta = ta->down;
                                tb = tb->down;
                        }
                        if(ta != tb)
                                diag(Z, "function inconsistently declared: %s", s->name);

                        /* take new-style over old-style */
                        ta = t1->down;
                        tb = t2->down;
                        if(ta != T && ta->etype == TOLD)
                                if(tb != T && tb->etype != TOLD)
                                        t1->down = tb;
                        break;

                case TARRAY:
                        /* should we check array size change? */
                        if(t2->width > t1->width)
                                t1->width = t2->width;
                        break;

                case TUNION:
                case TSTRUCT:
                        return;
                }
                t1 = t1->link;
                t2 = t2->link;
        }
}

void
edecl(int c, Type *t, Sym *s)
{
        Type *t1;

        if(s == S) {
                if(!typesu[t->etype])
                        diag(Z, "unnamed structure element must be struct/union");
                if(c != CXXX)
                        diag(Z, "unnamed structure element cannot have class");
        } else
                if(c != CXXX)
                        diag(Z, "structure element cannot have class: %s", s->name);
        t1 = t;
        t = copytyp(t1);
        t->sym = s;
        t->down = T;
        if(lastfield) {
                t->shift = lastbit - lastfield;
                t->nbits = lastfield;
                if(firstbit)
                        t->shift = -t->shift;
                if(typeu[t->etype])
                        t->etype = tufield->etype;
                else
                        t->etype = tfield->etype;
        }
        if(strf == T)
                strf = t;
        else
                strl->down = t;
        strl = t;
}

/*
 * this routine is very suspect.
 * ansi requires the enum type to
 * be represented as an 'int'
 * this means that 0x81234567
 * would be illegal. this routine
 * makes signed and unsigned go
 * to unsigned.
 */
Type*
maxtype(Type *t1, Type *t2)
{

        if(t1 == T)
                return t2;
        if(t2 == T)
                return t1;
        if(t1->etype > t2->etype)
                return t1;
        return t2;
}

void
doenum(Sym *s, Node *n)
{

        if(n) {
                complex(n);
                if(n->op != OCONST) {
                        diag(n, "enum not a constant: %s", s->name);
                        return;
                }
                en.cenum = n->type;
                en.tenum = maxtype(en.cenum, en.tenum);

                if(!typefd[en.cenum->etype])
                        en.lastenum = n->vconst;
                else
                        en.floatenum = n->fconst;
        }
        if(dclstack)
                push1(s);
        xdecl(CXXX, types[TENUM], s);

        if(en.cenum == T) {
                en.tenum = types[TINT];
                en.cenum = types[TINT];
                en.lastenum = 0;
        }
        s->tenum = en.cenum;

        if(!typefd[s->tenum->etype]) {
                s->vconst = convvtox(en.lastenum, s->tenum->etype);
                en.lastenum++;
        } else {
                s->fconst = en.floatenum;
                en.floatenum++;
        }

        if(debug['d'])
                dbgdecl(s);
        acidvar(s);
        godefvar(s);
}

void
symadjust(Sym *s, Node *n, int32 del)
{

        switch(n->op) {
        default:
                if(n->left)
                        symadjust(s, n->left, del);
                if(n->right)
                        symadjust(s, n->right, del);
                return;

        case ONAME:
                if(n->sym == s)
                        n->xoffset -= del;
                return;

        case OCONST:
        case OSTRING:
        case OLSTRING:
        case OINDREG:
        case OREGISTER:
                return;
        }
}

Node*
contig(Sym *s, Node *n, int32 v)
{
        Node *p, *r, *q, *m;
        int32 w;
        Type *zt;

        if(debug['i']) {
                print("contig v = %d; s = %s\n", v, s->name);
                prtree(n, "doinit value");
        }

        if(n == Z)
                goto no;
        w = s->type->width;

        /*
         * nightmare: an automatic array whose size
         * increases when it is initialized
         */
        if(v != w) {
                if(v != 0)
                        diag(n, "automatic adjustable array: %s", s->name);
                v = s->offset;
                autoffset = align(autoffset, s->type, Aaut3, nil);
                s->offset = -autoffset;
                stkoff = maxround(stkoff, autoffset);
                symadjust(s, n, v - s->offset);
        }
        if(w <= ewidth[TIND])
                goto no;
        if(n->op == OAS)
                diag(Z, "oops in contig");
/*ZZZ this appears incorrect
need to check if the list completely covers the data.
if not, bail
 */
        if(n->op == OLIST)
                goto no;
        if(n->op == OASI)
                if(n->left->type)
                if(n->left->type->width == w)
                        goto no;
        while(w & (ewidth[TIND]-1))
                w++;
/*
 * insert the following code, where long becomes vlong if pointers are fat
 *
        *(long**)&X = (long*)((char*)X + sizeof(X));
        do {
                *(long**)&X -= 1;
                **(long**)&X = 0;
        } while(*(long**)&X);
 */

        for(q=n; q->op != ONAME; q=q->left)
                ;

        zt = ewidth[TIND] > ewidth[TLONG]? types[TVLONG]: types[TLONG];

        p = new(ONAME, Z, Z);
        *p = *q;
        p->type = typ(TIND, zt);
        p->xoffset = s->offset;

        r = new(ONAME, Z, Z);
        *r = *p;
        r = new(OPOSTDEC, r, Z);

        q = new(ONAME, Z, Z);
        *q = *p;
        q = new(OIND, q, Z);

        m = new(OCONST, Z, Z);
        m->vconst = 0;
        m->type = zt;

        q = new(OAS, q, m);

        r = new(OLIST, r, q);

        q = new(ONAME, Z, Z);
        *q = *p;
        r = new(ODWHILE, q, r);

        q = new(ONAME, Z, Z);
        *q = *p;
        q->type = q->type->link;
        q->xoffset += w;
        q = new(OADDR, q, 0);

        q = new(OASI, p, q);
        r = new(OLIST, q, r);

        n = new(OLIST, r, n);

no:
        return n;
}

/* [<][>][^][v][top][bottom][index][help] */