# /* * C compiler * * */ #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 struct tnode *p1, *p2; struct tnode *p3; int dope, leftc, cvn, pcvn; /* * a[i] => *(a+i) */ if (op==LBRACK) { build(PLUS); op = STAR; } dope = opdope[op]; if ((dope&BINARY)!=0) { p2 = chkfun(disarray(*--cp)); t2 = p2->type; } p1 = *--cp; /* * sizeof gets turned into a number here. */ if (op==SIZEOF) { t1 = cblock(length(p1)); t1->type = UNSIGN; *cp++ = t1; return; } if (op!=AMPER) { p1 = disarray(p1); if (op!=CALL) p1 = chkfun(p1); } t1 = p1->type; pcvn = 0; t = INT; switch (op) { case CAST: if ((t1&XTYPE)==FUNC || (t1&XTYPE)==ARRAY) error("Disallowed conversion"); if (t1==UNSIGN && t2==CHAR) { t2 = INT; p2 = block(AND,INT,NULL,NULL,p2,cblock(0377)); } break; /* end of expression */ case 0: *cp++ = p1; return; /* no-conversion operators */ case QUEST: if (p2->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, NULL, NULL, 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->subsp,p1->strp,p1,p2); return; case STAR: if ((t1&XTYPE) == FUNC) error("Illegal indirection"); *cp++ = block(STAR, decref(t1), p1->subsp, p1->strp, p1); return; case AMPER: if (p1->op==NAME || p1->op==STAR) { *cp++ = block(op,incref(t1),p1->subsp,p1->strp,p1); return; } error("Illegal lvalue"); break; /* * a.b goes to (&a)->b */ case DOT: if (p1->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->op!=NAME || p2->tr1->hclass!=MOS) { error("Illegal structure ref"); *cp++ = p1; return; } if (t2==INT && p2->tr1->hflag&FFIELD) t2 = UNSIGN; t = incref(t2); chkw(p1, -1); setype(p1, t, p2); *cp++ = block(PLUS,t,p2->subsp,p2->strp,p1,cblock(p2->tr1->hoffset)); build(STAR); if (p2->tr1->hflag&FFIELD) *cp++ = block(FSEL,UNSIGN,NULL,NULL,*--cp,p2->tr1->hstrp); return; } if ((dope&LVALUE)!=0) chklval(p1); if ((dope&LWORD)!=0) chkw(p1, LONG); if ((dope&RWORD)!=0) chkw(p2, LONG); if ((dope&BINARY)==0) { if (op==ITOF) t1 = DOUBLE; else if (op==FTOI) t1 = INT; if (!fold(op, p1, 0)) *cp++ = block(op,t1,p1->subsp,p1->strp,p1); return; } cvn = 0; if (t1==STRUCT || t2==STRUCT) { if (t1!=t2 || p1->strp != p2->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) { 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) { t = INT; pcvn++; } else { if (t1!=t2 || t1!=(PTR+CHAR)) cvn = XX; } } if (cvn) { 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->type!=CHAR)) { p2->type = t; p2->subsp = p1->subsp; p2->strp = p1->strp; } if (t==INT && p1->type==CHAR) p2 = block(ITOC, INT, NULL, NULL, p2); *cp++ = p2; return; } if (fold(op, p1, p2)==0) { p3 = leftc?p2:p1; *cp++ = block(op, t, p3->subsp, p3->strp, p1, p2); } if (pcvn && t1!=(PTR+CHAR)) { p1 = *--cp; *cp++ = convert(p1, 0, PTI, plength(p1->tr1)); } } /* * Generate the appropriate conversion operator. */ struct tnode * convert(p, t, cvn, len) struct tnode *p; { register int op; op = cvntab[cvn]; if (opdope[op]&BINARY) { if (len==0) error("Illegal conversion"); return(block(op, t, NULL, NULL, p, cblock(len))); } return(block(op, t, NULL, NULL, p)); } /* * 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(ap, at, anewp) struct tnode *ap, *anewp; { register struct tnode *p, *newp; register t; p = ap; t = at; newp = anewp; for (;; p = p->tr1) { p->subsp = newp->subsp; p->strp = newp->strp; p->type = t; if (p->op==AMPER) t = decref(t); else if (p->op==STAR) t = incref(t); else if (p->op!=PLUS) break; } } /* * A mention of a function name is turned into * a pointer to that function. */ struct tnode * chkfun(ap) struct tnode *ap; { register struct tnode *p; register int t; p = ap; if (((t = p->type)&XTYPE)==FUNC && p->op!=ETYPE) return(block(AMPER,incref(t),p->subsp,p->strp,p)); return(p); } /* * A mention of an array is turned into * a pointer to the base of the array. */ struct tnode * disarray(ap) struct tnode *ap; { register int t; register struct tnode *p; p = ap; /* check array & not MOS and not typer */ if (((t = p->type)&XTYPE)!=ARRAY || p->op==NAME&&p->tr1->hclass==MOS || p->op==ETYPE) return(p); p->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) struct tnode *p; { register int t; if ((t=p->type)!=INT && top = op; p->type = t; p->subsp = subs; p->strp = str; p->tr1 = p1; if (opdope[op]&BINARY) p->tr2 = p2; else p->tr2 = NULL; return(p); } struct tnode * nblock(ads) struct hshtab *ads; { register struct hshtab *ds; ds = ads; return(block(NAME, ds->htype, ds->hsubsp, ds->hstrp, ds)); } /* * Generate a block for a constant */ struct cnode * cblock(v) { register struct cnode *p; p = gblock(sizeof(*p)); p->op = CON; p->type = INT; p->subsp = NULL; p->strp = NULL; p->value = v; return(p); } /* * A block for a float or long constant */ struct fnode * fblock(t, string) char *string; { register struct fnode *p; p = gblock(sizeof(*p)); p->op = FCON; p->type = t; p->subsp = NULL; p->strp = NULL; p->cstr = string; return(p); } /* * Assign a block for use in the * expression tree. */ char * gblock(n) { register int *p; p = curbase; if ((curbase =+ n) >= coremax) { if (sbrk(1024) == -1) { error("Out of space"); exit(1); } coremax =+ 1024; } return(p); } /* * Check that a tree can be used as an lvalue. */ chklval(ap) struct tnode *ap; { register struct tnode *p; p = ap; if (p->op==FSEL) p = p->tr1; if (p->op!=NAME && p->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, ap1, ap2) struct tnode *ap1, *ap2; { register struct tnode *p1; register int v1, v2; int unsignf; p1 = ap1; if (p1->op!=CON) return(0); unsignf = p1->type==UNSIGN; if (op==QUEST) { if (ap2->tr1->op==CON && ap2->tr2->op==CON) { p1->value = p1->value? ap2->tr1->value: ap2->tr2->value; *cp++ = p1; return(1); } return(0); } if (ap2) { if (ap2->op!=CON) return(0); v2 = ap2->value; unsignf |= ap2->type==UNSIGN; } v1 = p1->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) { v1 = (unsigned)v1 / v2; break; } v1 =/ v2; break; case MOD: if (v2==0) goto divchk; if (unsignf) { 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 = v1v2; break; case LESSEQ: v1 = v1<=v2; break; case GREATEQ: v1 = v1>=v2; break; divchk: error("Divide check"); default: return(0); } p1->value = v1; *cp++ = p1; return(1); } /* * Compile an expression expected to have constant value, * for example an array bound or a case value. */ conexp() { register struct tnode *t; initflg++; if (t = tree()) if (t->op != CON) error("Constant required"); initflg--; curbase = funcbase; return(t->value); }