diff options
Diffstat (limited to 'utils/cc/pgen.c')
| -rw-r--r-- | utils/cc/pgen.c | 548 |
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; +} |
