blob: 117508fd6d943de7c6c010a9e22943fbe35da8af [file] [log] [blame]
// 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"
#include "../ld/textflag.h"
static int haspointers(Type*);
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;
} else if(s->type != T && !haspointers(s->type))
s->dataflag = NOPTR;
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 TIND:
return t->link->etype != TFUNC;
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;
if(hasdotdotdot(thisfn->link))
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;
if(c == CTYPEDEF && (typechlv[t->etype] || typefd[t->etype])) {
s->type = copytyp(t);
s->type->tag = s;
}
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)
diag(Z, "unnamed structure elements not supported");
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;
}