| // 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); | |
| 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, TUSHORT); | |
| 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); | |
| } | |
| 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; | |
| 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*)); | |
| 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; | |
| } |