#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){ if(debug['B']) warn(Z, "no return at end of function: %s", n1->sym->name); else diag(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; } Node* uncomma(Node *n) { while(n != Z && n->op == OCOMMA) { cgen(n->left, Z); n = n->right; } return n; } void gen(Node *n) { Node *l, nod, rn; 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 OCOMMA: 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: case OCOMMA: gen(n->left); rloop: n = n->right; goto loop; case ORETURN: canreach = 0; warnreach = !suppress; complex(n); if(n->type == T) break; l = uncomma(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; } if(newvlongcode && !typefd[n->type->etype]){ regret(&rn, n); regfree(&rn); nod = znode; nod.op = OAS; nod.left = &rn; nod.right = l; nod.type = n->type; nod.complex = l->complex; cgen(&nod, Z); noretval(2); 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 || !typeswitch[l->type->etype]) { diag(n, "case expression must be integer constant"); goto rloop; } casf(); cases->val = l->vconst; cases->def = 0; cases->label = pc; cases->isv = typev[l->type->etype]; goto rloop; case OSWITCH: l = n->left; complex(l); if(l->type == T) break; if(!typeswitch[l->type->etype]) { 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) { Node *b, nod; 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; /* this is not quite right yet, so ignore it for now */ if(0 && newvlongcode && typev[n->type->etype] && machcap(Z)) { b = &nod; b->op = ONE; b->left = n; b->right = new(0, Z, Z); *b->right = *nodconst(0); b->right->type = n->type; b->type = types[TLONG]; cgen(b, Z); return 0; } bool64(n); boolgen(n, 1, Z); return 0; }