Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;
}