summaryrefslogtreecommitdiff
path: root/utils/cc/pgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/cc/pgen.c')
-rw-r--r--utils/cc/pgen.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/utils/cc/pgen.c b/utils/cc/pgen.c
new file mode 100644
index 00000000..131ff012
--- /dev/null
+++ b/utils/cc/pgen.c
@@ -0,0 +1,548 @@
+#include "gc.h"
+
+void
+codgen(Node *n, Node *nn)
+{
+ Prog *sp;
+ Node *n1, nod, nod1;
+
+ cursafe = 0;
+ curarg = 0;
+ maxargsafe = 0;
+ hasdoubled = 0;
+
+ /*
+ * isolate name
+ */
+ for(n1 = nn;; n1 = n1->left) {
+ if(n1 == Z) {
+ diag(nn, "cant find function name");
+ return;
+ }
+ if(n1->op == ONAME)
+ break;
+ }
+ nearln = nn->lineno;
+ gpseudo(ATEXT, n1->sym, nodconst(stkoff));
+ sp = p;
+
+ if(typecmplx[thisfn->link->etype]) {
+ if(nodret == nil) {
+ nodret = new(ONAME, Z, Z);
+ nodret->sym = slookup(".ret");
+ nodret->class = CPARAM;
+ nodret->type = types[TIND];
+ nodret->etype = TIND;
+ nodret = new(OIND, nodret, Z);
+ }
+ n1 = nodret->left;
+ if(n1->type == T || n1->type->link != thisfn->link) {
+ n1->type = typ(TIND, thisfn->link);
+ n1->etype = n1->type->etype;
+ nodret = new(OIND, n1, Z);
+ complex(nodret);
+ }
+ }
+
+ /*
+ * isolate first argument
+ */
+ if(REGARG >= 0) {
+ if(typecmplx[thisfn->link->etype]) {
+ nod1 = *nodret->left;
+ nodreg(&nod, &nod1, REGARG);
+ gmove(&nod, &nod1);
+ } else
+ if(firstarg && typeword[firstargtype->etype]) {
+ nod1 = znode;
+ nod1.op = ONAME;
+ nod1.sym = firstarg;
+ nod1.type = firstargtype;
+ nod1.class = CPARAM;
+ nod1.xoffset = align(0, firstargtype, Aarg1);
+ nod1.etype = firstargtype->etype;
+ xcom(&nod1);
+ nodreg(&nod, &nod1, REGARG);
+ gmove(&nod, &nod1);
+ }
+ }
+
+ canreach = 1;
+ warnreach = 1;
+ gen(n);
+ if(canreach && thisfn->link->etype != TVOID)
+ warn(Z, "no return at end of function: %s", n1->sym->name);
+ noretval(3);
+ gbranch(ORETURN);
+
+ if(!debug['N'] || debug['R'] || debug['P'])
+ regopt(sp);
+
+ if(thechar=='6' || thechar=='7' || thechar=='9' || hasdoubled) /* [sic] */
+ maxargsafe = round(maxargsafe, 8);
+ sp->to.offset += maxargsafe;
+}
+
+void
+supgen(Node *n)
+{
+ int owarn;
+ long spc;
+ Prog *sp;
+
+ if(n == Z)
+ return;
+ suppress++;
+ owarn = warnreach;
+ warnreach = 0;
+ spc = pc;
+ sp = lastp;
+ gen(n);
+ lastp = sp;
+ pc = spc;
+ sp->link = nil;
+ suppress--;
+ warnreach = owarn;
+}
+
+void
+gen(Node *n)
+{
+ Node *l, nod;
+ Prog *sp, *spc, *spb;
+ Case *cn;
+ long sbc, scc;
+ int snbreak, sncontin;
+ int f, o, oldreach;
+
+loop:
+ if(n == Z)
+ return;
+ nearln = n->lineno;
+ o = n->op;
+ if(debug['G'])
+ if(o != OLIST)
+ print("%L %O\n", nearln, o);
+
+ if(!canreach) {
+ switch(o) {
+ case OLABEL:
+ case OCASE:
+ case OLIST:
+ case OBREAK:
+ case OFOR:
+ case OWHILE:
+ case ODWHILE:
+ /* all handled specially - see switch body below */
+ break;
+ default:
+ if(warnreach) {
+ warn(n, "unreachable code %O", o);
+ warnreach = 0;
+ }
+ }
+ }
+
+ switch(o) {
+
+ default:
+ complex(n);
+ cgen(n, Z);
+ break;
+
+ case OLIST:
+ gen(n->left);
+
+ rloop:
+ n = n->right;
+ goto loop;
+
+ case ORETURN:
+ canreach = 0;
+ warnreach = !suppress;
+ complex(n);
+ if(n->type == T)
+ break;
+ l = n->left;
+ if(l == Z) {
+ noretval(3);
+ gbranch(ORETURN);
+ break;
+ }
+ if(typecmplx[n->type->etype]) {
+ nod = znode;
+ nod.op = OAS;
+ nod.left = nodret;
+ nod.right = l;
+ nod.type = n->type;
+ nod.complex = l->complex;
+ cgen(&nod, Z);
+ noretval(3);
+ gbranch(ORETURN);
+ break;
+ }
+ regret(&nod, n);
+ cgen(l, &nod);
+ regfree(&nod);
+ if(typefd[n->type->etype])
+ noretval(1);
+ else
+ noretval(2);
+ gbranch(ORETURN);
+ break;
+
+ case OLABEL:
+ canreach = 1;
+ l = n->left;
+ if(l) {
+ l->pc = pc;
+ if(l->label)
+ patch(l->label, pc);
+ }
+ gbranch(OGOTO); /* prevent self reference in reg */
+ patch(p, pc);
+ goto rloop;
+
+ case OGOTO:
+ canreach = 0;
+ warnreach = !suppress;
+ n = n->left;
+ if(n == Z)
+ return;
+ if(n->complex == 0) {
+ diag(Z, "label undefined: %s", n->sym->name);
+ return;
+ }
+ if(suppress)
+ return;
+ gbranch(OGOTO);
+ if(n->pc) {
+ patch(p, n->pc);
+ return;
+ }
+ if(n->label)
+ patch(n->label, pc-1);
+ n->label = p;
+ return;
+
+ case OCASE:
+ canreach = 1;
+ l = n->left;
+ if(cases == C)
+ diag(n, "case/default outside a switch");
+ if(l == Z) {
+ casf();
+ cases->val = 0;
+ cases->def = 1;
+ cases->label = pc;
+ cases->isv = 0;
+ goto rloop;
+ }
+ complex(l);
+ if(l->type == T)
+ goto rloop;
+ if(l->op == OCONST)
+ if(typeword[l->type->etype] && l->type->etype != TIND) {
+ casf();
+ cases->val = l->vconst;
+ cases->def = 0;
+ cases->label = pc;
+ cases->isv = typev[l->type->etype];
+ goto rloop;
+ }
+ diag(n, "case expression must be integer constant");
+ goto rloop;
+
+ case OSWITCH:
+ l = n->left;
+ complex(l);
+ if(l->type == T)
+ break;
+ if(!typeword[l->type->etype] || l->type->etype == TIND) {
+ diag(n, "switch expression must be integer");
+ break;
+ }
+
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ cn = cases;
+ cases = C;
+ casf();
+
+ sbc = breakpc;
+ breakpc = pc;
+ snbreak = nbreak;
+ nbreak = 0;
+ gbranch(OGOTO);
+ spb = p;
+
+ gen(n->right); /* body */
+ if(canreach){
+ gbranch(OGOTO);
+ patch(p, breakpc);
+ nbreak++;
+ }
+
+ patch(sp, pc);
+ regalloc(&nod, l, Z);
+ /* always signed */
+ if(typev[l->type->etype])
+ nod.type = types[TVLONG];
+ else
+ nod.type = types[TLONG];
+ cgen(l, &nod);
+ doswit(&nod);
+ regfree(&nod);
+ patch(spb, pc);
+
+ cases = cn;
+ breakpc = sbc;
+ canreach = nbreak!=0;
+ if(canreach == 0)
+ warnreach = !suppress;
+ nbreak = snbreak;
+ break;
+
+ case OWHILE:
+ case ODWHILE:
+ l = n->left;
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ scc = continpc;
+ continpc = pc;
+ gbranch(OGOTO);
+ spc = p;
+
+ sbc = breakpc;
+ breakpc = pc;
+ snbreak = nbreak;
+ nbreak = 0;
+ gbranch(OGOTO);
+ spb = p;
+
+ patch(spc, pc);
+ if(n->op == OWHILE)
+ patch(sp, pc);
+ bcomplex(l, Z); /* test */
+ patch(p, breakpc);
+ if(l->op != OCONST || vconst(l) == 0)
+ nbreak++;
+
+ if(n->op == ODWHILE)
+ patch(sp, pc);
+ gen(n->right); /* body */
+ gbranch(OGOTO);
+ patch(p, continpc);
+
+ patch(spb, pc);
+ continpc = scc;
+ breakpc = sbc;
+ canreach = nbreak!=0;
+ if(canreach == 0)
+ warnreach = !suppress;
+ nbreak = snbreak;
+ break;
+
+ case OFOR:
+ l = n->left;
+ if(!canreach && l->right->left && warnreach) {
+ warn(n, "unreachable code FOR");
+ warnreach = 0;
+ }
+ gen(l->right->left); /* init */
+ gbranch(OGOTO); /* entry */
+ sp = p;
+
+ /*
+ * if there are no incoming labels in the
+ * body and the top's not reachable, warn
+ */
+ if(!canreach && warnreach && deadheads(n)) {
+ warn(n, "unreachable code %O", o);
+ warnreach = 0;
+ }
+
+ scc = continpc;
+ continpc = pc;
+ gbranch(OGOTO);
+ spc = p;
+
+ sbc = breakpc;
+ breakpc = pc;
+ snbreak = nbreak;
+ nbreak = 0;
+ sncontin = ncontin;
+ ncontin = 0;
+ gbranch(OGOTO);
+ spb = p;
+
+ patch(spc, pc);
+ gen(l->right->right); /* inc */
+ patch(sp, pc);
+ if(l->left != Z) { /* test */
+ bcomplex(l->left, Z);
+ patch(p, breakpc);
+ if(l->left->op != OCONST || vconst(l->left) == 0)
+ nbreak++;
+ }
+ canreach = 1;
+ gen(n->right); /* body */
+ if(canreach){
+ gbranch(OGOTO);
+ patch(p, continpc);
+ ncontin++;
+ }
+ if(!ncontin && l->right->right && warnreach) {
+ warn(l->right->right, "unreachable FOR inc");
+ warnreach = 0;
+ }
+
+ patch(spb, pc);
+ continpc = scc;
+ breakpc = sbc;
+ canreach = nbreak!=0;
+ if(canreach == 0)
+ warnreach = !suppress;
+ nbreak = snbreak;
+ ncontin = sncontin;
+ break;
+
+ case OCONTINUE:
+ if(continpc < 0) {
+ diag(n, "continue not in a loop");
+ break;
+ }
+ gbranch(OGOTO);
+ patch(p, continpc);
+ ncontin++;
+ canreach = 0;
+ warnreach = !suppress;
+ break;
+
+ case OBREAK:
+ if(breakpc < 0) {
+ diag(n, "break not in a loop");
+ break;
+ }
+ /*
+ * Don't complain about unreachable break statements.
+ * There are breaks hidden in yacc's output and some people
+ * write return; break; in their switch statements out of habit.
+ * However, don't confuse the analysis by inserting an
+ * unreachable reference to breakpc either.
+ */
+ if(!canreach)
+ break;
+ gbranch(OGOTO);
+ patch(p, breakpc);
+ nbreak++;
+ canreach = 0;
+ warnreach = !suppress;
+ break;
+
+ case OIF:
+ l = n->left;
+ if(bcomplex(l, n->right)) {
+ if(typefd[l->type->etype])
+ f = !l->fconst;
+ else
+ f = !l->vconst;
+ if(debug['c'])
+ print("%L const if %s\n", nearln, f ? "false" : "true");
+ if(f) {
+ canreach = 1;
+ supgen(n->right->left);
+ oldreach = canreach;
+ canreach = 1;
+ gen(n->right->right);
+ /*
+ * treat constant ifs as regular ifs for
+ * reachability warnings.
+ */
+ if(!canreach && oldreach && debug['w'] < 2)
+ warnreach = 0;
+ }
+ else {
+ canreach = 1;
+ gen(n->right->left);
+ oldreach = canreach;
+ canreach = 1;
+ supgen(n->right->right);
+ /*
+ * treat constant ifs as regular ifs for
+ * reachability warnings.
+ */
+ if(!oldreach && canreach && debug['w'] < 2)
+ warnreach = 0;
+ canreach = oldreach;
+ }
+ }
+ else {
+ sp = p;
+ canreach = 1;
+ if(n->right->left != Z)
+ gen(n->right->left);
+ oldreach = canreach;
+ canreach = 1;
+ if(n->right->right != Z) {
+ gbranch(OGOTO);
+ patch(sp, pc);
+ sp = p;
+ gen(n->right->right);
+ }
+ patch(sp, pc);
+ canreach = canreach || oldreach;
+ if(canreach == 0)
+ warnreach = !suppress;
+ }
+ break;
+
+ case OSET:
+ case OUSED:
+ usedset(n->left, o);
+ break;
+ }
+}
+
+void
+usedset(Node *n, int o)
+{
+ if(n->op == OLIST) {
+ usedset(n->left, o);
+ usedset(n->right, o);
+ return;
+ }
+ complex(n);
+ switch(n->op) {
+ case OADDR: /* volatile */
+ gins(ANOP, n, Z);
+ break;
+ case ONAME:
+ if(o == OSET)
+ gins(ANOP, Z, n);
+ else
+ gins(ANOP, n, Z);
+ break;
+ }
+}
+
+int
+bcomplex(Node *n, Node *c)
+{
+
+ complex(n);
+ if(n->type != T)
+ if(tcompat(n, T, n->type, tnot))
+ n->type = T;
+ if(n->type == T) {
+ gbranch(OGOTO);
+ return 0;
+ }
+ if(c != Z && n->op == OCONST && deadheads(c))
+ return 1;
+ bool64(n);
+ boolgen(n, 1, Z);
+ return 0;
+}