/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* @(#)c01.c 1.2 */ /* * C compiler */ static char *Sccsid = "@(#)c01.c 3.0 (ULTRIX) 4/21/86"; #include "c0.h" /* * Called from tree, this routine takes the top 1, 2, or 3 * operands on the expression stack, makes a new node with * the operator op, and puts it on the stack. * Essentially all the work is in inserting * appropriate conversions. */ build(op) { register int t1; int t2, t; register union tree *p1, *p2, *p3; int dope, leftc, cvn, pcvn; /* * a[i] => *(a+i) */ if (op==LBRACK) { build(PLUS); op = STAR; } dope = opdope[op]; t2 = INT; if ((dope&BINARY)!=0) { p2 = chkfun(disarray(*--cp)); t2 = p2->t.type; } p1 = *--cp; /* * sizeof gets turned into a number here. */ if (op==SIZEOF) { p1 = cblock(length(p1)); p1->c.type = UNSIGN; *cp++ = p1; return; } if (op!=AMPER) { p1 = disarray(p1); if (op!=CALL) p1 = chkfun(p1); } t1 = p1->t.type; if (t1==CHAR) t1 = INT; else if (t1==UNCHAR) t1 = UNSIGN; if (t2==CHAR) t2 = INT; else if (t2==UNCHAR) t2 = UNSIGN; pcvn = 0; t = INT; switch (op) { case CAST: if ((t1&XTYPE)==FUNC || (t1&XTYPE)==ARRAY) error("Disallowed conversion"); if (p2->t.type==CHAR || p2->t.type==UNCHAR) p2 = block(PLUS, t2, (int *)NULL, (union str *)NULL, p2, cblock(0)); break; /* end of expression */ case 0: *cp++ = p1; return; /* no-conversion operators */ case QUEST: if (p2->t.op!=COLON) error("Illegal conditional"); else if (fold(QUEST, p1, p2)) return; case SEQNC: t = t2; case COMMA: case LOGAND: case LOGOR: *cp++ = block(op, t, p2->t.subsp, p2->t.strp, p1, p2); return; case EXCLA: t1 = INT; break; case CALL: if ((t1&XTYPE) != FUNC) error("Call of non-function"); *cp++ = block(CALL,decref(t1),p1->t.subsp,p1->t.strp,p1,p2); return; case STAR: if ((t1&XTYPE) == FUNC) error("Illegal indirection"); *cp++ = block(STAR, decref(t1), p1->t.subsp, p1->t.strp, p1, TNULL); return; case AMPER: if (p1->t.op==NAME || p1->t.op==STAR) { *cp++ = block(op,incref(p1->t.type),p1->t.subsp,p1->t.strp,p1,TNULL); return; } error("Illegal lvalue"); break; /* * a.b goes to (&a)->b */ case DOT: if (p1->t.op==CALL && t1==STRUCT) { t1 = incref(t1); setype(p1, t1, p1); } else { *cp++ = p1; build(AMPER); p1 = *--cp; } /* * In a->b, a is given the type ptr-to-structure element; * then the offset is added in without conversion; * then * is tacked on to access the member. */ case ARROW: if (p2->t.op!=NAME || p2->t.tr1->n.hclass!=MOS) { error("Illegal structure ref"); *cp++ = p1; return; } p2 = structident(p1, p2); t2 = p2->n.htype; if (t2==INT && p2->t.tr1->n.hflag&FFIELD) t2 = UNSIGN; t = incref(t2); chkw(p1, -1); setype(p1, t, p2); *cp++ = block(PLUS, t, p2->t.subsp, p2->t.strp, p1, cblock(p2->t.tr1->n.hoffset)); build(STAR); if (p2->t.tr1->n.hflag&FFIELD) *cp++ = block(FSEL, UNSIGN, (int *)NULL, (union str *)NULL, *--cp, p2->t.tr1->n.hstrp); return; } if ((dope&LVALUE)!=0) chklval(p1); if ((dope&LWORD)!=0) chkw(p1, LONG); if ((dope&RWORD)!=0) chkw(p2, LONG); if ((t1==VOID && op!=CAST) || t2==VOID) { error("Illegal use of void object"); t = t1 = t2 = INT; } if ((dope&BINARY)==0) { if (op==ITOF) t1 = DOUBLE; else if (op==FTOI) t1 = INT; if (!fold(op, p1, (union tree *)NULL)) *cp++ = block(op, t1, p1->t.subsp, p1->t.strp, p1,TNULL); return; } cvn = 0; if (t1==STRUCT || t2==STRUCT) { if (t1!=t2 || p1->t.strp != p2->t.strp) error("Incompatible structures"); cvn = 0; } else cvn = cvtab[lintyp(t1)][lintyp(t2)]; leftc = (cvn>>4)&017; cvn &= 017; t = leftc? t2:t1; if ((t==INT||t==CHAR) && (t1==UNSIGN||t2==UNSIGN)) t = UNSIGN; if (dope&ASSGOP || op==CAST) { if (leftc && (op>=ASPLUS && op<=ASXOR)) { assignop(op, p1, p2); return; } t = t1; if (op==ASSIGN || op==CAST) { if (cvn==ITP||cvn==PTI) cvn = leftc = 0; else if (cvn==LTP) { if (leftc==0) cvn = LTI; else { cvn = ITL; leftc = 0; } } } if (leftc) cvn = leftc; leftc = 0; } else if (op==COLON || op==MAX || op==MIN) { if (t1>=PTR && t1==t2) cvn = 0; if (op!=COLON && (t1>=PTR || t2>=PTR)) op += MAXP-MAX; } else if (dope&RELAT) { if (op>=LESSEQ && (t1>=PTR||t2>=PTR||(t1==UNSIGN||t2==UNSIGN) && (t==INT||t==CHAR||t==UNSIGN))) op += LESSEQP-LESSEQ; if (cvn==ITP || cvn==PTI) cvn = 0; } if (cvn==PTI) { cvn = 0; if (op==MINUS) { pcvn++; p1 = block(ITOL, LONG, (int *)NULL, (union str *)NULL, p1, TNULL); p2 = block(ITOL, LONG, (int *)NULL, (union str *)NULL, p2, TNULL); t = LONG; } else { if (t1!=t2 || (t1!=(PTR+CHAR) && t1!=(PTR+UNCHAR))) cvn = XX; } } if (cvn) { if ((cvn==ITP || cvn==LTP) && (opdope[op]&PCVOK)==0) { p3 = leftc? p1: p2; if (p3->t.op!=CON || p3->c.value!=0) cvn = XX; } t1 = plength(p1); t2 = plength(p2); if (cvn==XX || (cvn==PTI&&t1!=t2)) error("Illegal conversion"); else if (leftc) p1 = convert(p1, t, cvn, t2); else p2 = convert(p2, t, cvn, t1); } if (dope&RELAT) t = INT; if (t==FLOAT) t = DOUBLE; if (t==CHAR) t = INT; if (op==CAST) { if (t!=DOUBLE && (t!=INT || p2->t.type!=CHAR || p2->t.type!=UNCHAR)) { p2->t.type = t; p2->t.subsp = p1->t.subsp; p2->t.strp = p1->t.strp; } if (t==INT && p1->t.type==CHAR) p2 = block(ITOC, INT, (int *)NULL, (union str *)NULL, p2, TNULL); if (p1->t.type==UNCHAR) p2 = block(AND, UNSIGN, (int *)NULL, (union str *)NULL, p2, cblock(0377)); *cp++ = p2; return; } if (pcvn) t2 = plength(p1->t.tr1); if (fold(op, p1, p2)==0) { p3 = leftc?p2:p1; *cp++ = block(op, t, p3->t.subsp, p3->t.strp, p1, p2); } if (pcvn) { p1 = *--cp; *cp++ = convert(p1, 0, PTI, t2); } } union tree * structident(p1, p2) register union tree *p1, *p2; { register struct nmlist *np; int vartypes = 0, namesame = 1; np = (struct nmlist *)p2->t.tr1; for (;;) { if (namesame && p1->t.type==STRUCT+PTR && p1->t.strp == np->sparent) { p2->t.type = np->htype; p2->t.strp = np->hstrp; p2->t.subsp = np->hsubsp; p2->t.tr1 = (union tree *)np; return(p2); } np = np->nextnm; if (np==NULL) break; namesame = 0; if (strncmp(p2->t.tr1->n.name, np->name, NCPS) != 0) continue; if ((p2->t.tr1->n.hflag&FKIND) != (np->hflag&FMOS)) continue; namesame = 1; if (p2->t.tr1->n.htype==np->htype && p2->t.tr1->n.hoffset==np->hoffset) continue; vartypes++; } if (vartypes) error("Ambiguous structure reference for %.8s", p2->t.tr1->n.name); else { error("Warning: %.8s not member of cited struct/union", p2->t.tr1->n.name); nerror--; } return(p2); } /* * Generate the appropriate conversion operator. */ union tree * convert(p, t, cvn, len) union tree *p; { register int op; op = cvntab[cvn]; if (opdope[op]&BINARY) { if (len==0) error("Illegal conversion"); return(block(op, t, (int *)NULL, (union str *)NULL, p, cblock(len))); } return(block(op, t, (int *)NULL, (union str *)NULL, p, TNULL)); } /* * Traverse an expression tree, adjust things * so the types of things in it are consistent * with the view that its top node has * type at. * Used with structure references. */ setype(p, t, newp) register union tree *p, *newp; register t; { for (;; p = p->t.tr1) { p->t.subsp = newp->t.subsp; p->t.strp = newp->t.strp; p->t.type = t; if (p->t.op==AMPER) t = decref(t); else if (p->t.op==STAR) t = incref(t); else if (p->t.op!=PLUS) break; } } /* * A mention of a function name is turned into * a pointer to that function. */ union tree * chkfun(p) register union tree *p; { register int t; if (((t = p->t.type)&XTYPE)==FUNC && p->t.op!=ETYPE) return(block(AMPER,incref(t),p->t.subsp,p->t.strp,p,TNULL)); return(p); } /* * A mention of an array is turned into * a pointer to the base of the array. */ union tree * disarray(p) register union tree *p; { register int t; if (p==NULL) return(p); /* check array & not MOS and not typer */ if (((t = p->t.type)&XTYPE)!=ARRAY || p->t.op==NAME && p->t.tr1->n.hclass==MOS || p->t.op==ETYPE) return(p); p->t.subsp++; *cp++ = p; setype(p, decref(t), p); build(AMPER); return(*--cp); } /* * make sure that p is a ptr to a node * with type int or char or 'okt.' * okt might be nonexistent or 'long' * (e.g. for <<). */ chkw(p, okt) union tree *p; { register int t; if ((t=p->t.type)!=INT && t<PTR && t!=CHAR && t!=UNCHAR && t!=UNSIGN && t!=okt) error("Illegal type of operand"); return; } /* *'linearize' a type for looking up in the * conversion table */ lintyp(t) { switch(t) { case INT: case CHAR: case UNSIGN: case UNCHAR: return(0); case FLOAT: case DOUBLE: return(1); case LONG: return(2); default: return(3); } } /* * Report an error. */ /* VARARGS1 */ error(s, p1, p2, p3, p4, p5, p6) char *s; { nerror++; if (filename[0]) fprintf(stderr, "%s:", filename); fprintf(stderr, "%d: ", line); fprintf(stderr, s, p1, p2, p3, p4, p5, p6); fprintf(stderr, "\n"); } /* * Generate a node in an expression tree, * setting the operator, type, dimen/struct table ptrs, * and the operands. */ union tree * block(op, t, subs, str, p1,p2) int *subs; union str *str; union tree *p1, *p2; { register union tree *p; p = (union tree *)Tblock(sizeof(struct tnode)); p->t.op = op; p->t.type = t; p->t.subsp = subs; p->t.strp = str; p->t.tr1 = p1; if (opdope[op]&BINARY) p->t.tr2 = p2; else p->t.tr2 = NULL; return(p); } union tree * nblock(ds) register struct nmlist *ds; { return(block(NAME, ds->htype, ds->hsubsp, ds->hstrp, (union tree *)ds, TNULL)); } /* * Generate a block for a constant */ union tree * cblock(v) { register union tree *p; p = (union tree *)Tblock(sizeof(struct cnode)); p->c.op = CON; p->c.type = INT; p->c.subsp = NULL; p->c.strp = NULL; p->c.value = v; return(p); } /* * A block for a float constant */ union tree * fblock(t, string) char *string; { register union tree *p; p = (union tree *)Tblock(sizeof(struct fnode)); p->f.op = FCON; p->f.type = t; p->f.subsp = NULL; p->f.strp = NULL; p->f.cstr = string; return(p); } /* * Assign a block for use in the * expression tree. */ char * Tblock(n) { register char *p; p = treebase; if (p==NULL) { error("c0 internal error: tree not active"); exit(1); } if ((treebase += n) >= coremax) { if (sbrk(1024) == (char *)-1) { error("Out of space"); exit(1); } coremax += 1024; } return(p); } char * starttree() { register char *st; st = treebase; if (st==NULL) treebot = treebase = locbase+DCLSLOP; return(st); } endtree(tp) char *tp; { treebase = tp; if (tp==NULL) treebot = NULL; } /* * Assign a block for use in a declaration */ char * Dblock(n) { register char *p; p = locbase; locbase += n; if (treebot && locbase > treebot) { error("Too much declaring in an expression"); exit(1); } if (locbase > coremax) { if (sbrk(1024) == (char *)-1) { error("out of space"); exit(1); } coremax += 1024; } return(p); } /* * Check that a tree can be used as an lvalue. */ chklval(p) register union tree *p; { if (p->t.op==FSEL) p = p->t.tr1; if (p->t.op!=NAME && p->t.op!=STAR) error("Lvalue required"); } /* * reduce some forms of `constant op constant' * to a constant. More of this is done in the next pass * but this is used to allow constant expressions * to be used in switches and array bounds. */ fold(op, p1, p2) register union tree *p1; union tree *p2; { register int v1, v2; int unsignf; if (p1->t.op!=CON) return(0); unsignf = p1->c.type==UNSIGN; if (op==QUEST) { if (p2->t.tr1->t.op==CON && p2->t.tr2->t.op==CON) { p1->c.value = p1->c.value? p2->t.tr1->c.value: p2->t.tr2->c.value; *cp++ = p1; p1->t.type = p2->t.type; return(1); } return(0); } if (p2) { if (p2->t.op!=CON) return(0); v2 = p2->c.value; unsignf |= p2->c.type==UNSIGN; } v1 = p1->c.value; switch (op) { case PLUS: v1 += v2; break; case MINUS: v1 -= v2; break; case TIMES: v1 *= v2; break; case DIVIDE: if (v2==0) goto divchk; if (unsignf) { if (v2==1) break; if (v2<0) { v1 = (unsigned)v1 >= (unsigned)v2; break; } v1 = (unsigned)v1 / v2; break; } v1 /= v2; break; case MOD: if (v2==0) goto divchk; if (unsignf) { if (v2==1) { v1 = 0; break; } if (v2<0) { if ((unsigned)v1 >= (unsigned)v2) v1 -= v2; break; } v1 = (unsigned)v1 % v2; break; } v1 %= v2; break; case AND: v1 &= v2; break; case OR: v1 |= v2; break; case EXOR: v1 ^= v2; break; case NEG: v1 = - v1; break; case COMPL: v1 = ~ v1; break; case LSHIFT: v1 <<= v2; break; case RSHIFT: if (unsignf) { v1 = (unsigned)v1 >> v2; break; } v1 >>= v2; break; case EQUAL: v1 = v1==v2; break; case NEQUAL: v1 = v1!=v2; break; case LESS: v1 = v1<v2; break; case GREAT: v1 = v1>v2; break; case LESSEQ: v1 = v1<=v2; break; case GREATEQ: v1 = v1>=v2; break; divchk: error("Divide check"); nerror--; default: return(0); } p1->c.value = v1; *cp++ = p1; if (unsignf) p1->t.type = UNSIGN; return(1); } /* * Compile an expression expected to have constant value, * for example an array bound or a case value. */ conexp() { register union tree *t; initflg++; if (t = tree(1)) if (t->t.op != CON) error("Constant required"); initflg--; return(t->c.value); } /* * Handle peculiar assignment ops that need a temporary. */ assignop(op, p1, p2) register union tree *p1, *p2; { register struct nmlist *np; op += PLUS - ASPLUS; if (p1->t.op==NAME) { *cp++ = p1; *cp++ = p1; *cp++ = p2; build(op); build(ASSIGN); return; } np = gentemp(incref(p1->t.type)); *cp++ = nblock(np); *cp++ = p1; build(AMPER); build(ASSIGN); *cp++ = nblock(np); build(STAR); *cp++ = nblock(np); build(STAR); *cp++ = p2; build(op); build(ASSIGN); build(SEQNC); } /* * Generate an automatic temporary for * use in certain assignment ops */ struct nmlist * gentemp(type) { register struct nmlist *tp; tp = (struct nmlist *)Tblock(sizeof(struct nmlist)); tp->hclass = AUTO; tp->htype = type; tp->hflag = 0; tp->hsubsp = NULL; tp->hstrp = NULL; tp->hblklev = blklev; autolen -= rlength((union tree *)tp); tp->hoffset = autolen; if (autolen < maxauto) maxauto = autolen; return(tp); }