File:  [Research Unix] / researchv10no / cmd / lcc / c / stmt.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:35 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv10, HEAD
researchv10 Norman

/* C compiler: statement parsing */

#include "c.h"

#define SWITCHSIZE 512		/* initial size of switch arrays */

float density = 0.5;		/* minimum switch density */
float refinc = 1.0;		/* amount by which ref field is incremented */

static struct equate {	/* deferred equates: */
	Symbol new;		/* new label, i.e., label being defined */
	Symbol old;		/* old label */
	struct equate *oldlink;	/* pointer to equate structure for old=x */
	struct equate *link;	/* next equate on list */
} *equlist;			/* list of deferred equates */
struct swtch {		/* switch statement data: */
	Symbol sym;		/* local holding the switch value */
	int lab;		/* label of selection code */
	Symbol deflab;		/* default label */
	int ncases;		/* number of cases */
	int size;		/* size of value & label arrays */
	int    *values;		/* value, label pairs */
	Symbol *labels;
};

dclproto(static void branch,(int));
dclproto(static void caselabel,(struct swtch *, int, int));
dclproto(static void cmp,(int, Symbol, int, int));
dclproto(static void dostmt,(int, struct swtch *, int));
dclproto(static int foldcond,(Tree, Tree));
dclproto(static void forstmt,(int, struct swtch *, int));
dclproto(static void ifstmt,(int, int, struct swtch *, int));
dclproto(static Symbol localaddr,(Tree));
dclproto(static void stmtlabel,(char *));
dclproto(static int swcode,(struct swtch *, int *, int, int, int));
dclproto(static void swgen,(struct swtch *));
dclproto(static void swstmt,(int, int, int));
dclproto(static void visit,(struct equate *));
dclproto(static void whilestmt,(int, struct swtch *, int));

/* branch - jump to lab */
static void branch(lab) {
	Code cp;

	walk(0, 0, 0);
	for (cp = codelist; cp->kind < Label; )
		cp = cp->prev;
	if (lab)
		while (cp->kind == Label && islabel(cp->u.node)
		&& cp->u.node->syms[0]->u.l.label != lab) {
			Symbol old = findlabel(lab);
			equatelab(cp->u.node->syms[0], old);
			old->ref++;
			if (cp->prev->next = cp->next)
				cp->next->prev = cp->prev;
			else
				codelist = cp->prev;
			do
				cp = cp->prev;
			while (cp->kind < Label);
		}
	if (cp->kind != Jump) {
		code(Jump);
		codelist->u.node = lab ? jump(lab) : (Node)0;
	}
	/* else do nothing and thus delete dead branch */
}

/* caselabel - add a label to the current switch list */
static void caselabel(swp, val, lab) struct swtch *swp; {
	int k;

	if (swp->ncases >= swp->size) {
		int    *vals = swp->values;
		Symbol *labs = swp->labels;
		swp->size *= 2;
		swp->values = (int    *) talloc(swp->size*sizeof (int));
		swp->labels = (Symbol *) talloc(swp->size*sizeof (Symbol));
		for (k = 0; k < swp->ncases; k++) {
			swp->values[k] = vals[k];
			swp->labels[k] = labs[k];
		}
	}	
	for (k = swp->ncases; k > 0 && swp->values[k-1] >= val; k--) {
		swp->values[k] = swp->values[k-1];
		swp->labels[k] = swp->labels[k-1];
	}
	if (k < swp->ncases && swp->values[k] == val)
		error("duplicate case label `%d'\n", val);
	swp->values[k] = val;
	swp->labels[k] = findlabel(lab);
	++swp->ncases;
	if (Aflag >= 2 && swp->ncases == 258)
		warning("more than 257 cases in a switch statement\n");
}

/* cmp - generate code for `if (p op n) goto lab' for integer n */
static void cmp(op, p, n, lab) Symbol p; {
	listnodes(eqnode(op, cast(idnode(p), inttype), constnode(n, inttype)), lab, 0);
}

/* definelab - define a label */
void definelab(lab) {
	Code cp;

	walk(0, 0, 0);
	code(Label);
	codelist->u.node = newnode(LABEL+V, 0, 0, findlabel(lab));
	for (cp = codelist; cp->kind <= Label; )
		cp = cp->prev;
	while (cp->kind == Jump && cp->u.node && cp->u.node->kids[0]
	&& cp->u.node->kids[0]->op == ADDRG+P
	&& cp->u.node->kids[0]->syms[0]->u.l.label == lab) {
		cp->u.node->kids[0]->syms[0]->ref--;
		assert(cp->next);
		cp->prev->next = cp->next;
		cp->next->prev = cp->prev;
		cp = cp->prev;
	}
}

/* definept - define an execution point n: current token at current pc */
Code definept(p) Coordinate *p; {
	int n;
	Code cp = code(Defpoint);

	cp->u.point.src = p ? *p : src;
	cp->u.point.point = npoints;
	if (ncalled > 0 && (n = findcount(src.file, src.x, src.y)) >= 0)
		refinc = n/ncalled + 1;
	if (glevel > 2)
		locus(identifiers, &cp->u.point.src);
	if (events.points) {
		Tree e = 0;
		apply(events.points, (Generic)&cp->u.point.src, (Generic)&e);
		if (e)
			listnodes(e, 0, 0);
	}
	return cp;
}

/* dostmt - do statement while ( expression ) */
static void dostmt(lab, swp, lev) struct swtch *swp; {
	refinc *= 10;
	t = gettok();
	definelab(lab);
	statement(lab, swp, lev);
	definelab(lab + 1);
	expect(WHILE);
	expect('(');
	definept(0);
	walk(conditional(')'), lab, 0);
	if (findlabel(lab + 2)->ref)
		definelab(lab + 2);
}

/* equatelab - add new=old to list of deferred equates */
void equatelab(new, old) Symbol new, old; {
	struct equate *e = (struct equate *) talloc(sizeof *e);

	e->new = new;
	e->old = old;
	e->oldlink = 0;
	e->link = equlist;
	equlist = e;
	for (e = e->link; e; e = e->link)
		if (e->old == new)
			e->oldlink = equlist;
		else if (e->new == old)
			equlist->oldlink = e;
}
	
/* flushequ - flush deferred equates */
void flushequ() {
	for ( ; equlist; equlist = equlist->link)
		visit(equlist);
}

/* foldcond - check if initial test in for(e1;e2;e3) S is necessary */
static int foldcond(e1, e2) Tree e1, e2; {
	Opcode op = generic(e2->op);
	Symbol v;

	if (e1 == 0 || e2 == 0)
		return 0;
	if (generic(e1->op) == ASGN && isaddrop(e1->kids[0]->op)
	&& generic(e1->kids[1]->op) == CNST) {
		v = e1->kids[0]->u.sym;
		e1 = e1->kids[1];
	} else
		return 0;
	if ((op==LE || op==LT || op==EQ || op==NE || op==GT || op==GE)
	&& generic(e2->kids[0]->op) == INDIR
	&& e2->kids[0]->kids[0]->u.sym == v
	&& e2->kids[1]->op == e1->op) {
		e1 = simplify(op, e2->type, e1, e2->kids[1]);
		if (e1->op == CNST+I)
			return e1->u.v.i;
	}
	return 0;
}

/* forstmt - for ( [expr1] ; [expr2] ; [expr3] ) statement */
static void forstmt(lab, swp, lev) struct swtch *swp; {
	Tree e1, e2, e3;
	Coordinate pt2, pt3;
	static char follow[] = { IF, ID, '}', 0 };

	e1 = e2 = e3 = 0;
	t = gettok();
	expect('(');
	definept(0);
	if (kind[t] == ID)
		e1 = expr0(';');
	else
		expect(';');
	walk(e1, 0, 0);
	pt2 = src;
	refinc *= 10;
	if (kind[t] == ID)
		e2 = texpr(conditional, ';');
	else
		expect(';');
	pt3 = src;
	if (kind[t] == ID)
		e3 = texpr(expr0, ')');
	else
		test(')', follow);
	if (glevel == 2) {	/* e1; L: if (!e2) goto L+2; S; L+1: e3; goto L; L+2: */
		definelab(lab);
		definept(&pt2);
		if (e2)
			walk(e2, 0, lab + 2);
		statement(lab, swp, lev);
		definelab(lab + 1);
		definept(&pt3);
		if (e3)
			walk(e3, 0, 0);
		branch(lab);
	} else {	/* e1; goto L+3; L: S; L+1: e3; L+3: if (e2) goto L; L+2: */
		int once = 0;
		if (e2) {
			once = foldcond(e1, e2);
			if (!once)
				branch(lab + 3);
		}
		definelab(lab);
		statement(lab, swp, lev);
		definelab(lab + 1);
		definept(&pt3);
		if (e3)
			walk(e3, 0, 0);
		if (e2) {
			if (!once)
				definelab(lab + 3);
			definept(&pt2);
			walk(e2, lab, 0);
		} else {
			definept(&pt2);
			branch(lab);
		}
	}
	if (findlabel(lab + 2)->ref)
		definelab(lab + 2);
}

/* ifstmt - if ( expression ) statement [ else statement ] */
static void ifstmt(lab, loop, swp, lev) struct swtch *swp; {
	t = gettok();
	expect('(');
	definept(0);
	walk(conditional(')'), 0, lab);
	refinc /= 2;
	statement(loop, swp, lev);
	if (t == ELSE) {
		branch(lab + 1);
		t = gettok();
		definelab(lab);
		statement(loop, swp, lev);
		if (findlabel(lab + 1)->ref)
			definelab(lab + 1);
	} else
		definelab(lab);
}

/* localaddr - returns q if p yields the address of local/parameter q; otherwise returns 0 */
static Symbol localaddr(p) Tree p; {
	if (p == 0)
		return 0;
	switch (generic(p->op)) {
	case INDIR: case CALL: case ARG:
		return 0;
	case ADDRL: case ADDRF:
		return p->u.sym;
	case RIGHT: case ASGN:
		if (p->kids[1])
			return localaddr(p->kids[1]);
		return localaddr(p->kids[0]);
	case COND: {
		Symbol q;
		if (p->kids[1] && (q = localaddr(p->kids[1])))
			return q;
		return localaddr(p->kids[2]);
		}
	default: {
		Symbol q;
		if (p->kids[0] && (q = localaddr(p->kids[0])))
			return q;
		return localaddr(p->kids[1]);
		}
	}
}

/* retcode - return p from the current function */
void retcode(p, lab) Tree p; {
	if (p == 0) {
		if (events.returns)
			apply(events.returns, (Generic)cfunc, (Generic)0);
		p = tree(RET+V, voidtype, 0, 0);
	} else {
		Type ty;
		p = pointer(p);
		if (ty = assign(freturn(cfunc->type), p))
			p = cast(p, ty);
		else
			error("illegal return type; found `%t' expected `%t'\n",
				p->type, freturn(cfunc->type));
		if (retv) {
			if (p->op == RIGHT && p->kids[0]->op == CALL+B
			&& p->kids[1]->op == INDIR+B) {
				p = p->kids[0];
				p->kids[1] = idnode(retv);
			} else
				p = asgnnode(ASGN, rvalue(idnode(retv)), p);
			walk(p, 0, 0);
			if (events.returns)
				apply(events.returns, (Generic)cfunc, (Generic)rvalue(idnode(retv)));
			p = tree(RET+V, voidtype, 0, 0);
		} else {
			if (events.returns) {
				Symbol t1 = genident(AUTO, p->type, level);
				addlocal(t1);
				walk(asgn(t1, p), 0, 0);
				apply(events.returns, (Generic)cfunc, (Generic)idnode(t1));
				p = idnode(t1);
			}
			p = cast(p, promote(p->type));
			if (isptr(p->type)) {
				Symbol q = localaddr(p);
				if (q && (q->computed || q->generated))
					warning("pointer to a %s is an illegal return value\n",
						q->scope == PARAM ? "parameter" : "local");
				else if (q)
					warning("pointer to %s `%s' is an illegal return value\n",
						q->scope == PARAM ? "parameter" : "local", q->name);
				p = cast(p, unsignedtype);
			}
			p = tree(RET + widen(p->type), p->type, p, 0);
		}
	}
	walk(p, 0, 0);
	branch(lab);
}

/* statement - parse statements */
void statement(loop, swp, lev) struct swtch *swp; {
	int lab;
	float ref = refinc;

	if (Aflag >= 2 && lev == 15)
		warning("more than 15 levels of nested statements\n");
	switch (t) {
	case IF:
		ifstmt(genlabel(2), loop, swp, lev + 1);
		break;
	case WHILE:
		whilestmt(genlabel(3), swp, lev + 1);
		break;
	case DO:
		dostmt(genlabel(3), swp, lev + 1);
		expect(';');
		break;
	case FOR:
		forstmt(genlabel(4), swp, lev + 1);
		break;
	case SWITCH:
		swstmt(loop, genlabel(2), lev + 1);
		break;
	case CASE:
		if (swp == 0)
			error("illegal case label\n");
		definelab(lab = genlabel(1));
		while (t == CASE) {
			static char follow[] = { IF, ID, 0 };
			Tree p;
			t = gettok();
			p = constexpr(0);
			if (generic(p->op) == CNST && isint(p->type)) {
				if (swp) {
					needconst++;
					p = cast(p, swp->sym->type);
					needconst--;
					caselabel(swp, p->u.v.i, lab);
				}
			} else
				error("case label must be a constant integer expression\n");
			test(':', follow);
			ntree = 0;
		}
		statement(loop, swp, lev);
		break;
	case DEFAULT:
		if (swp)
			if (swp->deflab)
				error("extra default label\n");
			else {
				swp->deflab = findlabel(swp->lab);
				definelab(swp->deflab->u.l.label);
			}
		else
			error("illegal default label\n");
		t = gettok();
		expect(':');
		statement(loop, swp, lev);
		break;
	case RETURN:
		t = gettok();
		definept(0);
		if (t != ';')
			if (freturn(cfunc->type) == voidtype) {
				error("extraneous return value\n");
				expr(0);
				retcode(0, cfunc->u.f.label);
			} else
				retcode(expr(0), cfunc->u.f.label);
		else {
			if (freturn(cfunc->type) != voidtype
			&& (freturn(cfunc->type) != inttype || Aflag >= 1))
				warning("missing return value\n");
			retcode(0, cfunc->u.f.label);
		}
		expect(';');
		break;
	case BREAK:
		walk(0, 0, 0);
		if (swp && swp->lab > loop) {
			definept(0);
			branch(swp->lab + 1);
			t = gettok();
			expect(';');
			break;
		}
	case CONTINUE:
		walk(0, 0, 0);
		definept(0);
		if (loop)
			branch(loop + (t == BREAK ? 2 : 1));
		else
			error("illegal %k statement\n", t);
		t = gettok();
		expect(';');
		break;
	case '{':
		compound(loop, swp, lev + 1);
		break;
	case ';':
		definept(0);
		t = gettok();
		break;
	case GOTO:
		walk(0, 0, 0);
		definept(0);
		t = gettok();
		if (t == ID) {
			Symbol p = lookup(token, labels[0]);
			if (p == 0) {
				p = install(token, &labels[0], 0);
				p->u.l.label = genlabel(1);
				p->u.l.equatedto = p;
				p->src = src;
			}
			p->ref++;
			use(p, src);
			branch(p->u.l.label);
			t = gettok();
		} else
			error("missing label in goto\n");
		expect(';');
		break;
	case ID:
		if (getchr() == ':') {
			stmtlabel(token);
			statement(loop, swp, lev);
			break;
		}
	default:
		definept(0);
		if (kind[t] != ID) {
			error("unrecognized statement\n");
			t = gettok();
		} else {
			Tree e = expr0(0);
			listnodes(e, 0, 0);
			if (glevel > 1 || nodecount == 0 || nodecount > 200)
				walk(0, 0, 0);
			ntree = 0;
		}
		expect(';');
	}
	if (kind[t] != IF && kind[t] != ID && t != '}' && t != EOI) {
		static char follow[] = { IF, ID, '}', 0 };
		error("illegal statement termination\n");
		skipto(0, follow);
	}
	refinc = ref;
}

/* stmtlabel - label : */
static void stmtlabel(label) char *label; {
	Symbol p;

	if ((p = lookup(label, labels[0])) == 0) {
		p = install(label, &labels[0], 0);
		p->u.l.label = genlabel(1);
		p->u.l.equatedto = p;
	}
	if (p->defined)
		error("redefinition of label `%s' previously defined at %w\n",
			label, &p->src);
	p->defined = 1;
	p->src = src;
	definelab(p->u.l.label);
	t = gettok();
	expect(':');
}

/* swcode - generate switch decision code for buckets b[lb..ub] */
static int swcode(swp, b, lb, ub, n) struct swtch *swp; int b[]; {
	int hilab, k, l, lolab, median, u, *v;

	v = swp->values;
	median = ((float)v[b[lb]] + (float)v[b[ub+1]-1] + 1)/2;
	for (l = lb, u = ub; l <= u; ) {
		k = (l + u)/2;
		if (v[b[k]] > median)
			u = k - 1;
		else if (v[b[k+1]-1] < median)
			l = k + 1;
		else
			break;
	}
	if (l > u) {	/* which of two buckets is closest to the median? */
		l = b[k];
		u = b[k+1] - 1;
		if (median < v[l] && k > lb && v[l] - median > median - v[l-1])
			k--;
		else if (median > v[u] && k < ub && median - v[u] > v[u+1] - median)
			k++;
	}
	l = b[k];
	u = b[k+1] - 1;
	lolab = hilab = swp->deflab->u.l.label;
	if (k > lb) {
		lolab = 0;
		if (k < ub)
			hilab = genlabel(1);
	} else if (k < ub)
		hilab = 0;
	if (n == 0)
		lolab = hilab = 0;
	if (u - l + 1 <= 3) {
		int i;
		for (i = l; i <= u; i++)
			cmp(EQ, swp->sym, swp->values[i], swp->labels[i]->u.l.label);
		if (lolab && lolab == hilab)
			branch(lolab);
		else {
			if (lolab)
				cmp(LT, swp->sym, swp->values[l], lolab);
			if (hilab)
				cmp(GT, swp->sym, swp->values[u], hilab);
			walk(0, 0, 0);
		}
	} else {
		int lab = genlabel(2);
		Tree e; 
		cmp(LT, swp->sym, swp->values[l], lolab ? lolab : lab);
		cmp(GT, swp->sym, swp->values[u], hilab ? hilab : lab);
		e = tree(ADDRG+P, ptr(voidptype), 0, 0);
		e->u.sym = findlabel(lab + 1);
		e->u.sym->sclass = STATIC;
		e->u.sym->type = e->type;
		walk(tree(JUMP, voidtype, rvalue((*opnode['+'])(ADD, e,
			(*opnode['-'])(SUB, cast(idnode(swp->sym), inttype),
			constnode(swp->values[l], inttype)))), 0), 0, 0);
		definelab(lab + 1);
		code(Switch);
		codelist->u.swtch.sym = swp->sym;
		codelist->u.swtch.deflab = swp->deflab;
		codelist->u.swtch.size = u - l + 1;
		codelist->u.swtch.values = &swp->values[l];
		codelist->u.swtch.labels = &swp->labels[l];
		branch(0);
		definelab(lab);
		if (swp->values[u] - swp->values[l] + 1 >= 10000)
			warning("switch generates a huge table\n");
	}
	if (k > lb)
		n = swcode(swp, b, lb, k - 1, n - 1);
	if (k < ub) {
		if (hilab)
			definelab(hilab);
		n = swcode(swp, b, k + 1, ub, n - 1);
	}
	return n;
}

/* swgen - partition case labels into buckets, initiate code generation */
#define den(k,n) ((float)(k-buckets[n]+1)/(float)(v[k]-v[buckets[n]]+1))
static void swgen(swp) struct swtch *swp; {
	int *buckets, k, n, *v;

	if (swp->ncases == 0)
		return;
	buckets = (int *) talloc((swp->ncases + 1)*sizeof *buckets);
	v = swp->values;
	n = 0;
	for (k = 0; k < swp->ncases; k++, n++) {
		buckets[n] = k;
		while (n > 0) {
			float d = den(k, n-1);
			if (d < density || k < swp->ncases - 1 && d < den(k+1, n))
				break;
			n--;
		}
	}
	buckets[n--] = swp->ncases;
	swcode(swp, buckets, 0, n, n);
}

/* swstmt - switch ( expression ) statement */
static void swstmt(loop, lab, lev) {
	Tree e;
	struct swtch sw;
	Code head, tail;

	t = gettok();
	expect('(');
	definept(0);
	e = expr(')');
	if (!isint(e->type)) {
		error("illegal type `%t' in switch expression\n", e->type);
		e = retype(e, inttype);
	}
	e = cast(e, promote(e->type));
	if (generic(e->op) == INDIR && isaddrop(e->kids[0]->op)
	&& e->kids[0]->u.sym->type == e->type && !isvolatile(e->kids[0]->u.sym->type)) {
		sw.sym = e->kids[0]->u.sym;
		walk(0, 0, 0);
	} else {
		sw.sym = genident(REGISTER, e->type, level);
		addlocal(sw.sym);
		walk(asgn(sw.sym, e), 0, 0);
	}
	code(Switch);
	head = codelist;
	sw.lab = lab;
	sw.deflab = 0;
	sw.ncases = 0;
	sw.size = SWITCHSIZE;
	sw.values = (int    *) talloc(SWITCHSIZE*sizeof (int));
	sw.labels = (Symbol *) talloc(SWITCHSIZE*sizeof (Symbol));
	refinc /= 10;
	statement(loop, &sw, lev);
	if (sw.deflab == 0) {
		sw.deflab = findlabel(lab);
		definelab(lab);
		if (sw.ncases == 0)
			warning("switch statement with no cases\n");
	}
	if (findlabel(lab + 1)->ref)
		definelab(lab + 1);
	tail = codelist;
	codelist = head->prev;
	swgen(&sw);
	branch(lab);
	head->next->prev = codelist;
	codelist->next = head->next;
	codelist = tail;
}

/* visit - equate p->new=p->old, equating predecessor first */
static void visit(p) struct equate *p; {
	if (p && p->old) {
		visit(p->oldlink);
		p->new->u.l.equatedto = p->old->u.l.equatedto;
		p->old = 0;
	}
}

/* whilestmt - while ( expression ) statement */
static void whilestmt(lab, swp, lev) struct swtch *swp; {
	Coordinate pt;
	Tree e;

	refinc *= 10;
	t = gettok();
	expect('(');
	walk(0, 0, 0);
	pt = src;
	e = texpr(conditional, ')');
	if (generic(e->op) == CNST || glevel == 2) {
			/* L, L+1: if (!e) goto L+2; S; goto L; L+2: */
		definelab(lab);
		definelab(lab + 1);
		definept(&pt);
		walk(e, 0, lab + 2);
		statement(lab, swp, lev);
		branch(lab);
	} else {	/* goto L+1; L: S; L+1: if (E) goto L; L+2: */
		branch(lab + 1);
		definelab(lab);
		statement(lab, swp, lev);
		definelab(lab + 1);
		definept(&pt);
		walk(e, lab, 0);
	}
	if (findlabel(lab + 2)->ref)
		definelab(lab + 2);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.