summaryrefslogtreecommitdiff
path: root/src/tools/lcc/src/stmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/lcc/src/stmt.c')
-rw-r--r--src/tools/lcc/src/stmt.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/src/tools/lcc/src/stmt.c b/src/tools/lcc/src/stmt.c
new file mode 100644
index 0000000..9c3bdbe
--- /dev/null
+++ b/src/tools/lcc/src/stmt.c
@@ -0,0 +1,696 @@
+#include "c.h"
+
+
+#define SWSIZE 512
+
+#define den(i,j) ((j-buckets[i]+1.0)/(v[j]-v[buckets[i]]+1))
+
+struct code codehead = { Start };
+Code codelist = &codehead;
+float density = 0.5;
+Table stmtlabs;
+
+static int foldcond(Tree e1, Tree e2);
+static void caselabel(Swtch, long, int);
+static void cmp(int, Symbol, long, int);
+static Tree conditional(int);
+static void dostmt(int, Swtch, int);
+static int equal(Symbol, Symbol);
+static void forstmt(int, Swtch, int);
+static void ifstmt(int, int, Swtch, int);
+static Symbol localaddr(Tree);
+static void stmtlabel(void);
+static void swstmt(int, int, int);
+static void whilestmt(int, Swtch, int);
+Code code(int kind) {
+ Code cp;
+
+ if (!reachable(kind))
+ warning("unreachable code\n");
+
+ NEW(cp, FUNC);
+ cp->kind = kind;
+ cp->prev = codelist;
+ cp->next = NULL;
+ codelist->next = cp;
+ codelist = cp;
+ return cp;
+}
+int reachable(int kind) {
+
+ if (kind > Start) {
+ Code cp;
+ for (cp = codelist; cp->kind < Label; )
+ cp = cp->prev;
+ if (cp->kind == Jump || cp->kind == Switch)
+ return 0;
+ }
+ return 1;
+}
+void addlocal(Symbol p) {
+ if (!p->defined) {
+ code(Local)->u.var = p;
+ p->defined = 1;
+ p->scope = level;
+ }
+}
+void definept(Coordinate *p) {
+ Code cp = code(Defpoint);
+
+ cp->u.point.src = p ? *p : src;
+ cp->u.point.point = npoints;
+ if (ncalled > 0) {
+ int n = findcount(cp->u.point.src.file,
+ cp->u.point.src.x, cp->u.point.src.y);
+ if (n > 0)
+ refinc = (float)n/ncalled;
+ }
+ if (glevel > 2) locus(identifiers, &cp->u.point.src);
+ if (events.points && reachable(Gen))
+ {
+ Tree e = NULL;
+ apply(events.points, &cp->u.point.src, &e);
+ if (e)
+ listnodes(e, 0, 0);
+ }
+}
+void statement(int loop, Swtch swp, int lev) {
+ 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 BREAK: walk(NULL, 0, 0);
+ definept(NULL);
+ if (swp && swp->lab > loop)
+ branch(swp->lab + 1);
+ else if (loop)
+ branch(loop + 2);
+ else
+ error("illegal break statement\n");
+ t = gettok(); expect(';');
+ break;
+
+ case CONTINUE: walk(NULL, 0, 0);
+ definept(NULL);
+ if (loop)
+ branch(loop + 1);
+ else
+ error("illegal continue statement\n");
+ t = gettok(); expect(';');
+ break;
+
+ case SWITCH: swstmt(loop, genlabel(2), lev + 1);
+ break;
+ case CASE: {
+ int lab = genlabel(1);
+ if (swp == NULL)
+ error("illegal case label\n");
+ definelab(lab);
+ while (t == CASE) {
+ static char stop[] = { 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);
+ if (p->type->op == UNSIGNED)
+ p->u.v.i = extend(p->u.v.u, p->type);
+ needconst--;
+ caselabel(swp, p->u.v.i, lab);
+ }
+ } else
+ error("case label must be a constant integer expression\n");
+
+ test(':', stop);
+ }
+ statement(loop, swp, lev);
+ } break;
+ case DEFAULT: if (swp == NULL)
+ error("illegal default label\n");
+ else if (swp->deflab)
+ error("extra default label\n");
+ else {
+ swp->deflab = findlabel(swp->lab);
+ definelab(swp->deflab->u.l.label);
+ }
+ t = gettok();
+ expect(':');
+ statement(loop, swp, lev); break;
+ case RETURN: {
+ Type rty = freturn(cfunc->type);
+ t = gettok();
+ definept(NULL);
+ if (t != ';')
+ if (rty == voidtype) {
+ error("extraneous return value\n");
+ expr(0);
+ retcode(NULL);
+ } else
+ retcode(expr(0));
+ else {
+ if (rty != voidtype)
+ warning("missing return value\n");
+ retcode(NULL);
+ }
+ branch(cfunc->u.f.label);
+ } expect(';');
+ break;
+
+ case '{': compound(loop, swp, lev + 1); break;
+ case ';': definept(NULL); t = gettok(); break;
+ case GOTO: walk(NULL, 0, 0);
+ definept(NULL);
+ t = gettok();
+ if (t == ID) {
+ Symbol p = lookup(token, stmtlabs);
+ if (p == NULL) {
+ p = install(token, &stmtlabs, 0, FUNC);
+ p->scope = LABELS;
+ p->u.l.label = genlabel(1);
+ p->src = src;
+ }
+ use(p, src);
+ branch(p->u.l.label);
+ t = gettok();
+ } else
+ error("missing label in goto\n"); expect(';');
+ break;
+
+ case ID: if (getchr() == ':') {
+ stmtlabel();
+ statement(loop, swp, lev);
+ break;
+ }
+ default: definept(NULL);
+ if (kind[t] != ID) {
+ error("unrecognized statement\n");
+ t = gettok();
+ } else {
+ Tree e = expr0(0);
+ listnodes(e, 0, 0);
+ if (nodecount == 0 || nodecount > 200)
+ walk(NULL, 0, 0);
+ else if (glevel) walk(NULL, 0, 0);
+ deallocate(STMT);
+ } expect(';');
+ break;
+
+ }
+ if (kind[t] != IF && kind[t] != ID
+ && t != '}' && t != EOI) {
+ static char stop[] = { IF, ID, '}', 0 };
+ error("illegal statement termination\n");
+ skipto(0, stop);
+ }
+ refinc = ref;
+}
+
+static void ifstmt(int lab, int loop, Swtch swp, int lev) {
+ t = gettok();
+ expect('(');
+ definept(NULL);
+ walk(conditional(')'), 0, lab);
+ refinc /= 2.0;
+ 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);
+}
+static Tree conditional(int tok) {
+ Tree p = expr(tok);
+
+ if (Aflag > 1 && isfunc(p->type))
+ warning("%s used in a conditional expression\n",
+ funcname(p));
+ return cond(p);
+}
+static void stmtlabel(void) {
+ Symbol p = lookup(token, stmtlabs);
+
+ if (p == NULL) {
+ p = install(token, &stmtlabs, 0, FUNC);
+ p->scope = LABELS;
+ p->u.l.label = genlabel(1);
+ p->src = src;
+ }
+ if (p->defined)
+ error("redefinition of label `%s' previously defined at %w\n", p->name, &p->src);
+
+ p->defined = 1;
+ definelab(p->u.l.label);
+ t = gettok();
+ expect(':');
+}
+static void forstmt(int lab, Swtch swp, int lev) {
+ int once = 0;
+ Tree e1 = NULL, e2 = NULL, e3 = NULL;
+ Coordinate pt2, pt3;
+
+ t = gettok();
+ expect('(');
+ definept(NULL);
+ if (kind[t] == ID)
+ e1 = texpr(expr0, ';', FUNC);
+ else
+ expect(';');
+ walk(e1, 0, 0);
+ pt2 = src;
+ refinc *= 10.0;
+ if (kind[t] == ID)
+ e2 = texpr(conditional, ';', FUNC);
+ else
+ expect(';');
+ pt3 = src;
+ if (kind[t] == ID)
+ e3 = texpr(expr0, ')', FUNC);
+ else {
+ static char stop[] = { IF, ID, '}', 0 };
+ test(')', stop);
+ }
+ 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);
+}
+static void swstmt(int loop, int lab, int lev) {
+ Tree e;
+ struct swtch sw;
+ Code head, tail;
+
+ t = gettok();
+ expect('(');
+ definept(NULL);
+ 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(NULL, 0, 0);
+ } else {
+ sw.sym = genident(REGISTER, e->type, level);
+ addlocal(sw.sym);
+ walk(asgn(sw.sym, e), 0, 0);
+ }
+ head = code(Switch);
+ sw.lab = lab;
+ sw.deflab = NULL;
+ sw.ncases = 0;
+ sw.size = SWSIZE;
+ sw.values = newarray(SWSIZE, sizeof *sw.values, FUNC);
+ sw.labels = newarray(SWSIZE, sizeof *sw.labels, FUNC);
+ refinc /= 10.0;
+ statement(loop, &sw, lev);
+ if (sw.deflab == NULL) {
+ 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;
+ codelist->next = head->prev = NULL;
+ if (sw.ncases > 0)
+ swgen(&sw);
+ branch(lab);
+ head->next->prev = codelist;
+ codelist->next = head->next;
+ codelist = tail;
+}
+static void caselabel(Swtch swp, long val, int lab) {
+ int k;
+
+ if (swp->ncases >= swp->size)
+ {
+ long *vals = swp->values;
+ Symbol *labs = swp->labels;
+ swp->size *= 2;
+ swp->values = newarray(swp->size, sizeof *swp->values, FUNC);
+ swp->labels = newarray(swp->size, sizeof *swp->labels, FUNC);
+ for (k = 0; k < swp->ncases; k++) {
+ swp->values[k] = vals[k];
+ swp->labels[k] = labs[k];
+ }
+ }
+ k = swp->ncases;
+ for ( ; 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\n");
+}
+void swgen(Swtch swp) {
+ int *buckets, k, n;
+ long *v = swp->values;
+
+ buckets = newarray(swp->ncases + 1,
+ sizeof *buckets, FUNC);
+ for (n = k = 0; k < swp->ncases; k++, n++) {
+ buckets[n] = k;
+ while (n > 0 && den(n-1, k) >= density)
+ n--;
+ }
+ buckets[n] = swp->ncases;
+ swcode(swp, buckets, 0, n - 1);
+}
+void swcode(Swtch swp, int b[], int lb, int ub) {
+ int hilab, lolab, l, u, k = (lb + ub)/2;
+ long *v = swp->values;
+
+ if (k > lb && k < ub) {
+ lolab = genlabel(1);
+ hilab = genlabel(1);
+ } else if (k > lb) {
+ lolab = genlabel(1);
+ hilab = swp->deflab->u.l.label;
+ } else if (k < ub) {
+ lolab = swp->deflab->u.l.label;
+ hilab = genlabel(1);
+ } else
+ lolab = hilab = swp->deflab->u.l.label;
+ l = b[k];
+ u = b[k+1] - 1;
+ if (u - l + 1 <= 3)
+ {
+ int i;
+ for (i = l; i <= u; i++)
+ cmp(EQ, swp->sym, v[i], swp->labels[i]->u.l.label);
+ if (k > lb && k < ub)
+ cmp(GT, swp->sym, v[u], hilab);
+ else if (k > lb)
+ cmp(GT, swp->sym, v[u], hilab);
+ else if (k < ub)
+ cmp(LT, swp->sym, v[l], lolab);
+ else
+ assert(lolab == hilab),
+ branch(lolab);
+ walk(NULL, 0, 0);
+ }
+ else {
+ Tree e;
+ Type ty = signedint(swp->sym->type);
+ Symbol table = genident(STATIC,
+ array(voidptype, u - l + 1, 0), GLOBAL);
+ (*IR->defsymbol)(table);
+ if (!isunsigned(swp->sym->type) || v[l] != 0)
+ cmp(LT, swp->sym, v[l], lolab);
+ cmp(GT, swp->sym, v[u], hilab);
+ e = (*optree['-'])(SUB, cast(idtree(swp->sym), ty), cnsttree(ty, v[l]));
+ if (e->type->size < unsignedptr->size)
+ e = cast(e, unsignedlong);
+ walk(tree(JUMP, voidtype,
+ rvalue((*optree['+'])(ADD, pointer(idtree(table)), e)), NULL),
+ 0, 0);
+ code(Switch);
+ codelist->u.swtch.table = table;
+ codelist->u.swtch.sym = swp->sym;
+ codelist->u.swtch.deflab = swp->deflab;
+ codelist->u.swtch.size = u - l + 1;
+ codelist->u.swtch.values = &v[l];
+ codelist->u.swtch.labels = &swp->labels[l];
+ if (v[u] - v[l] + 1 >= 10000)
+ warning("switch generates a huge table\n");
+ }
+ if (k > lb) {
+ assert(lolab != swp->deflab->u.l.label);
+ definelab(lolab);
+ swcode(swp, b, lb, k - 1);
+ }
+ if (k < ub) {
+ assert(hilab != swp->deflab->u.l.label);
+ definelab(hilab);
+ swcode(swp, b, k + 1, ub);
+ }
+}
+static void cmp(int op, Symbol p, long n, int lab) {
+ Type ty = signedint(p->type);
+
+ listnodes(eqtree(op,
+ cast(idtree(p), ty),
+ cnsttree(ty, n)),
+ lab, 0);
+}
+void retcode(Tree p) {
+ Type ty;
+
+ if (p == NULL) {
+ if (events.returns)
+ apply(events.returns, cfunc, NULL);
+ return;
+ }
+ p = pointer(p);
+ ty = assign(freturn(cfunc->type), p);
+ if (ty == NULL) {
+ error("illegal return type; found `%t' expected `%t'\n",
+ p->type, freturn(cfunc->type));
+ return;
+ }
+ p = cast(p, ty);
+ if (retv)
+ {
+ if (iscallb(p))
+ p = tree(RIGHT, p->type,
+ tree(CALL+B, p->type,
+ p->kids[0]->kids[0], idtree(retv)),
+ rvalue(idtree(retv)));
+ else
+ p = asgntree(ASGN, rvalue(idtree(retv)), p);
+ walk(p, 0, 0);
+ if (events.returns)
+ apply(events.returns, cfunc, rvalue(idtree(retv)));
+ return;
+ }
+ if (events.returns)
+ {
+ Symbol t1 = genident(AUTO, p->type, level);
+ addlocal(t1);
+ walk(asgn(t1, p), 0, 0);
+ apply(events.returns, cfunc, idtree(t1));
+ p = idtree(t1);
+ }
+ if (!isfloat(p->type))
+ 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);
+ }
+ walk(tree(mkop(RET,p->type), p->type, p, NULL), 0, 0);
+}
+void definelab(int lab) {
+ Code cp;
+ Symbol p = findlabel(lab);
+
+ assert(lab);
+ walk(NULL, 0, 0);
+ code(Label)->u.forest = newnode(LABEL+V, NULL, NULL, p);
+ for (cp = codelist->prev; cp->kind <= Label; )
+ cp = cp->prev;
+ while ( cp->kind == Jump
+ && cp->u.forest->kids[0]
+ && specific(cp->u.forest->kids[0]->op) == ADDRG+P
+ && cp->u.forest->kids[0]->syms[0] == p) {
+ assert(cp->u.forest->kids[0]->syms[0]->u.l.label == lab);
+ p->ref--;
+ assert(cp->next);
+ assert(cp->prev);
+ cp->prev->next = cp->next;
+ cp->next->prev = cp->prev;
+ cp = cp->prev;
+ while (cp->kind <= Label)
+ cp = cp->prev;
+ }
+}
+Node jump(int lab) {
+ Symbol p = findlabel(lab);
+
+ p->ref++;
+ return newnode(JUMP+V, newnode(ADDRG+ttob(voidptype), NULL, NULL, p),
+ NULL, NULL);
+}
+void branch(int lab) {
+ Code cp;
+ Symbol p = findlabel(lab);
+
+ assert(lab);
+ walk(NULL, 0, 0);
+ code(Label)->u.forest = jump(lab);
+ for (cp = codelist->prev; cp->kind < Label; )
+ cp = cp->prev;
+ while ( cp->kind == Label
+ && cp->u.forest->op == LABEL+V
+ && !equal(cp->u.forest->syms[0], p)) {
+ equatelab(cp->u.forest->syms[0], p);
+ assert(cp->next);
+ assert(cp->prev);
+ cp->prev->next = cp->next;
+ cp->next->prev = cp->prev;
+ cp = cp->prev;
+ while (cp->kind < Label)
+ cp = cp->prev;
+ }
+ if (cp->kind == Jump || cp->kind == Switch) {
+ p->ref--;
+ codelist->prev->next = NULL;
+ codelist = codelist->prev;
+ } else {
+ codelist->kind = Jump;
+ if (cp->kind == Label
+ && cp->u.forest->op == LABEL+V
+ && equal(cp->u.forest->syms[0], p))
+ warning("source code specifies an infinite loop");
+ }
+}
+void equatelab(Symbol old, Symbol new) {
+ assert(old->u.l.equatedto == NULL);
+ old->u.l.equatedto = new;
+ new->ref++;
+}
+static int equal(Symbol lprime, Symbol dst) {
+ assert(dst && lprime);
+ for ( ; dst; dst = dst->u.l.equatedto)
+ if (lprime == dst)
+ return 1;
+ return 0;
+}
+/* dostmt - do statement while ( expression ) */
+static void dostmt(int lab, Swtch swp, int lev) {
+ refinc *= 10.0;
+ t = gettok();
+ definelab(lab);
+ statement(lab, swp, lev);
+ definelab(lab + 1);
+ expect(WHILE);
+ expect('(');
+ definept(NULL);
+ walk(conditional(')'), lab, 0);
+ if (findlabel(lab + 2)->ref)
+ definelab(lab + 2);
+}
+
+/* foldcond - check if initial test in for(e1;e2;e3) S is necessary */
+static int foldcond(Tree e1, Tree e2) {
+ int 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;
+}
+
+/* localaddr - returns q if p yields the address of local/parameter q; otherwise returns 0 */
+static Symbol localaddr(Tree p) {
+ if (p == NULL)
+ return NULL;
+ switch (generic(p->op)) {
+ case INDIR: case CALL: case ARG:
+ return NULL;
+ 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;
+ assert(p->kids[1] && p->kids[1]->op == RIGHT);
+ if ((q = localaddr(p->kids[1]->kids[0])) != NULL)
+ return q;
+ return localaddr(p->kids[1]->kids[1]);
+ }
+ default: {
+ Symbol q;
+ if (p->kids[0] && (q = localaddr(p->kids[0])) != NULL)
+ return q;
+ return localaddr(p->kids[1]);
+ }
+ }
+}
+
+/* whilestmt - while ( expression ) statement */
+static void whilestmt(int lab, Swtch swp, int lev) {
+ Coordinate pt;
+ Tree e;
+
+ refinc *= 10.0;
+ t = gettok();
+ expect('(');
+ walk(NULL, 0, 0);
+ pt = src;
+ e = texpr(conditional, ')', FUNC);
+ 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);
+}