// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "go.h" #undef EXTERN #define EXTERN #include "gen.h" static Node* curfn; void compile(Node *fn) { Plist *pl; if(fn->nbody == N) return; if(nerrors != 0) { walk(fn); return; } if(debug['w']) dump("--- pre walk ---", fn->nbody); walk(fn); if(nerrors != 0) return; if(debug['w']) dump("--- post walk ---", fn->nbody); curfn = fn; continpc = P; breakpc = P; pc = mal(sizeof(*pc)); firstpc = pc; pc->op = PEND; pc->addr.type = ANONE; pc->loc = 1; inarggen(); gen(curfn->nbody); if(curfn->type->outtuple != 0) gopcodet(PPANIC, N, N); if(debug['p']) proglist(); pl = mal(sizeof(*pl)); pl->name = curfn->nname; pl->locals = autodcl; pl->firstpc = firstpc; if(plist == nil) plist = pl; else plast->link = pl; plast = pl; if(debug['f']) frame(0); } /* * compile statements */ void gen(Node *n) { long lno; Prog *scontin, *sbreak; Prog *p1, *p2, *p3; Sym *s; lno = dynlineno; loop: if(n == N) goto ret; dynlineno = n->lineno; // for diagnostics switch(n->op) { default: dump("gen: unknown op", n); break; case OLIST: gen(n->left); n = n->right; goto loop; case OPANIC: case OPRINT: genprint(n->left); if(n->op == OPANIC) gopcodet(PPANIC, N, N); break; case OCASE: case OFALL: case OXCASE: case OXFALL: case OEMPTY: break; case OLABEL: // before declaration, s->label points at // a link list of PXGOTO instructions. // after declaration, s->label points // at a PGOTO to .+1 s = n->left->sym; p1 = (Prog*)s->label; if(p1 != P) { if(p1->op == PGOTO) { yyerror("label redeclared: %S", s); break; } while(p1 != P) { if(p1->op != PGOTOX) fatal("bad label pointer: %S", s); p2 = p1->addr.branch; p1->addr.branch = pc; p1->op = PGOTO; p1 = p2; } } s->label = pc; p1 = gbranch(PGOTO, N); patch(p1, pc); break; case OGOTO: s = n->left->sym; p1 = (Prog*)s->label; if(p1 != P && p1->op == PGOTO) { // already declared p2 = gbranch(PGOTO, N); patch(p2, p1->addr.branch); break; } // not declaraed yet p2 = gbranch(PGOTOX, N); p2->addr.node = n; // info for diagnostic if never declared patch(p2, p1); s->label = p2; break; case OBREAK: if(breakpc == P) { yyerror("gen: break is not in a loop"); break; } patch(gbranch(PGOTO, N), breakpc); break; case OCONTINUE: if(continpc == P) { yyerror("gen: continue is not in a loop"); break; } patch(gbranch(PGOTO, N), continpc); break; case OFOR: gen(n->ninit); // init p1 = gbranch(PGOTO, N); // goto test sbreak = breakpc; breakpc = gbranch(PGOTO, N); // break: goto done scontin = continpc; continpc = pc; gen(n->nincr); // contin: incr patch(p1, pc); // test: bgen(n->ntest, 0, breakpc); // if(!test) goto break gen(n->nbody); // body patch(gbranch(PGOTO, N), continpc); // goto contin patch(breakpc, pc); // done: continpc = scontin; breakpc = sbreak; break; case OIF: gen(n->ninit); // init p1 = gbranch(PGOTO, N); // goto test p2 = gbranch(PGOTO, N); // p2: goto else patch(p1, pc); // test: bgen(n->ntest, 0, p2); // if(!test) goto p2 gen(n->nbody); // then p3 = gbranch(PGOTO, N); // goto done patch(p2, pc); // else: gen(n->nelse); // else patch(p3, pc); // done: break; case OSWITCH: gen(n->ninit); // init p1 = gbranch(PGOTO, N); // goto test sbreak = breakpc; breakpc = gbranch(PGOTO, N); // break: goto done patch(p1, pc); // test: swgen(n); // switch(test) body patch(breakpc, pc); // done: breakpc = sbreak; break; case OASOP: cgen_asop(n->left, n->right, n->kaka); break; case OAS: cgen_as(n->left, n->right, n->op, n->kaka); break; case OCALL: case OCALLPTR: case OCALLMETH: case OCALLINTER: cgen_call(n, 1); break; case ORETURN: cgen_ret(n); break; } ret: dynlineno = lno; } /* * compile expression to (unnamed) reg */ void cgen(Node *n) { long lno; Node *nl, *nr, *r; int a; Prog *p1, *p2, *p3; if(n == N) return; lno = dynlineno; if(n->op != ONAME) dynlineno = n->lineno; // for diagnostics nl = n->left; nr = n->right; if(nr != N && nr->ullman >= UINF && nl != N && nl->ullman >= UINF) { cgen(nr); r = tempname(n->type); gopcodet(PSTORE, n->type, r); nr = r; } switch(n->op) { default: yyerror("cgen: unknown op %O", n->op); break; case ONAME: case OLITERAL: gopcodet(PLOAD, n->type, n); break; case ONEW: gopcodet(PNEW, n->type, n); break; // these call bgen to get a bool value case OOROR: case OANDAND: case OEQ: case ONE: case OLT: case OLE: case OGE: case OGT: case ONOT: p1 = gbranch(PGOTO, N); p2 = gopcodet(PLOAD, n->type, booltrue); p3 = gbranch(PGOTO, N); patch(p1, pc); bgen(n, 1, p2); p2 = gopcodet(PLOAD, n->type, boolfalse); patch(p3, pc); goto ret; case OPLUS: cgen(nl); goto ret; // unary case OMINUS: case OCOM: a = optopop(n->op); goto uop; // symmetric binary case OAND: case OOR: case OXOR: case OADD: case OMUL: a = optopop(n->op); goto sbop; // asymmetric binary case OMOD: case OSUB: case ODIV: case OLSH: case ORSH: case OCAT: a = optopop(n->op); goto abop; case OCONV: if(isbytearray(nl->type)) { if(nl->type->etype == TPTR) cgen(nl); else agen(nl); gopcode(PCONV, PTNIL, nod(OCONV, n->type, nl->type)); break; } cgen(nl); gopcode(PCONV, PTNIL, nod(OCONV, n->type, nl->type)); break; case OINDEXPTRSTR: nl = n->left; nr = n->right; if(nl->addable) { cgen(nr); cgen(nl); gopcode(PLOADI, PTADDR, N); gopcodet(PINDEXZ, nr->type, N); break; } fatal("xxx"); break; case OINDEXSTR: nl = n->left; nr = n->right; if(nl->addable) { cgen(nr); gopcodet(PINDEXZ, nr->type, nl); break; } cgen(nl); r = tempname(nl->type); gopcodet(PSTORE, nl->type, r); cgen(nr); gopcodet(PINDEXZ, nr->type, r); break; case OSLICESTR: case OSLICEPTRSTR: nl = n->left; // name nr = n->right; r = nr->right; // index2 if(!r->addable) { cgen(r); r = tempname(r->type); gopcodet(PSTORE, r->type, r); } // string into PTADDR if(!nl->addable) { cgen(nl); gconv(PTADDR, nl->type->etype); } else gopcode(PLOAD, PTADDR, nl); if(n->op == OSLICEPTRSTR) gopcode(PLOADI, PTADDR, N); // offset in int reg cgen(nr->left); // index 2 addressed gopcodet(PSLICE, r->type, r); break; case OINDEXPTR: case OINDEX: case ODOT: case ODOTPTR: case OIND: agen(n); gopcodet(PLOADI, n->type, N); break; case OLEN: cgen(nl); gopcodet(PLEN, nl->type, nl); break; case ODOTMETH: case ODOTINTER: cgen(n->left); break; case OADDR: agen(nl); gconv(PTPTR, PTADDR); break; case OCALL: case OCALLPTR: case OCALLMETH: case OCALLINTER: cgen_call(n, 0); cgen_callret(n, N); break; } goto ret; sbop: // symmetric if(nl->ullman < nr->ullman) { r = nl; nl = nr; nr = r; } abop: // asymmetric if(nr->addable) { cgen(nl); gopcodet(a, n->type, nr); goto ret; } cgen(nr); r = tempname(n->type); gopcodet(PSTORE, n->type, r); cgen(nl); gopcodet(a, n->type, r); goto ret; uop: // unary cgen(nl); gopcodet(a, n->type, N); goto ret; ret: dynlineno = lno; } /* * compile the address of a value */ void agen(Node *n) { Node *nl, *nr; Node *t, *r; if(n == N || n->type == N) return; switch(n->op) { default: dump("agen: unknown op", n); break; case ONAME: gopcode(PADDR, PTADDR, n); break; case OINDEXPTR: nl = n->left; nr = n->right; if(nl->addable) { cgen(nr); gopcode(PLOAD, PTADDR, nl); genindex(n); break; } if(nr->addable) { cgen(nl); gconv(PTADDR, nl->type->etype); cgen(nr); genindex(n); break; } cgen(nr); r = tempname(n->type); gopcodet(PSTORE, n->type, r); cgen(nl); gconv(PTADDR, nl->type->etype); cgen(r); genindex(n); break; case OINDEX: nl = n->left; nr = n->right; if(nl->addable) { cgen(nr); agen(nl); genindex(n); break; } if(nr->addable) { agen(nl); cgen(nr); genindex(n); break; } cgen(nr); r = tempname(n->type); gopcodet(PSTORE, n->type, r); agen(nl); cgen(r); genindex(n); break; case OIND: nl = n->left; if(nl->addable) { gopcode(PLOAD, PTADDR, nl); break; } cgen(nl); gconv(PTADDR, nl->type->etype); break; case ODOT: case ODOTPTR: nl = n->left; nr = n->right; t = nl->type; switch(t->etype) { default: badtype(n->op, n->left->type, n->right->type); break; case TPTR: if(nl->op != ONAME) { cgen(nl); gconv(PTADDR, nl->type->etype); } else gopcode(PLOAD, PTADDR, nl); gaddoffset(nr); break; case TSTRUCT: agen(nl); gaddoffset(nr); break; } break; } } /* * compile boolean expression * true is branch-true or branch-false * to is where to branch */ void bgen(Node *n, int true, Prog *to) { long lno; int et, a; Node *nl, *nr, *r; Prog *p1, *p2; if(n == N) n = booltrue; lno = dynlineno; if(n->op != ONAME) dynlineno = n->lineno; // for diagnostics if(n == N) goto ret; if(n->type == N) { convlit(n, types[TBOOL]); if(n->type == N) goto ret; } et = n->type->etype; if(et != TBOOL) { yyerror("cgen: bad type %T for %O", n->type, n->op); patch(gbranch(PERROR, N), to); goto ret; } nl = N; nr = N; switch(n->op) { default: cgen(n); gopcodet(PTEST, n->type, N); a = PBTRUE; if(!true) a = PBFALSE; patch(gbranch(a, n->type), to); goto ret; case OLITERAL: if(!true == !n->val.vval) patch(gbranch(PGOTO, N), to); goto ret; case ONAME: gopcodet(PTEST, n->type, n); a = PBTRUE; if(!true) a = PBFALSE; patch(gbranch(a, n->type), to); goto ret; case OANDAND: if(!true) goto caseor; caseand: p1 = gbranch(PGOTO, N); p2 = gbranch(PGOTO, N); patch(p1, pc); bgen(n->left, !true, p2); bgen(n->right, !true, p2); p1 = gbranch(PGOTO, N); patch(p1, to); patch(p2, pc); goto ret; case OOROR: if(!true) goto caseand; caseor: bgen(n->left, true, to); bgen(n->right, true, to); goto ret; case OEQ: case ONE: case OLT: case OGT: case OLE: case OGE: nr = n->right; if(nr == N || nr->type == N) goto ret; case ONOT: // unary nl = n->left; if(nl == N || nl->type == N) goto ret; } switch(n->op) { case ONOT: bgen(nl, !true, to); goto ret; case OEQ: a = PBEQ; goto br; case ONE: a = PBNE; goto br; case OLT: a = PBLT; goto br; case OGT: a = PBGT; goto br; case OLE: a = PBLE; goto br; case OGE: a = PBGE; goto br; br: if(!true) a = brcom(a); // make simplest on right if(nl->ullman < nr->ullman) { a = brrev(a); r = nl; nl = nr; nr = r; } if(nr->addable) { cgen(nl); gopcodet(PCMP, nr->type, nr); patch(gbranch(a, nr->type), to); break; } cgen(nr); r = tempname(nr->type); gopcodet(PSTORE, nr->type, r); cgen(nl); gopcodet(PCMP, nr->type, r); patch(gbranch(a, nr->type), to); break; } goto ret; ret: dynlineno = lno; } void swgen(Node *n) { Node *c1, *c2; Case *s0, *se, *s; Prog *p1, *dflt; long lno; int any; Iter save1, save2; lno = dynlineno; p1 = gbranch(PGOTO, N); s0 = C; se = C; // walk thru the body placing breaks // and labels into the case statements any = 0; dflt = P; c1 = listfirst(&save1, &n->nbody); while(c1 != N) { dynlineno = c1->lineno; // for diagnostics if(c1->op != OCASE) { if(s0 == C) yyerror("unreachable statements in a switch"); gen(c1); any = 1; if(c1->op == OFALL) any = 0; c1 = listnext(&save1); continue; } // put in the break between cases if(any) { patch(gbranch(PGOTO, N), breakpc); any = 0; } // over case expressions c2 = listfirst(&save2, &c1->left); if(c2 == N) dflt = pc; while(c2 != N) { s = mal(sizeof(*s)); if(s0 == C) s0 = s; else se->slink = s; se = s; s->scase = c2; // case expression s->sprog = pc; // where to go c2 = listnext(&save2); } c1 = listnext(&save1); } if(any) patch(gbranch(PGOTO, N), breakpc); patch(p1, pc); c1 = tempname(n->ntest->type); cgen(n->ntest); gopcodet(PSTORE, n->ntest->type, c1); for(s=s0; s!=C; s=s->slink) { cgen(s->scase); gopcodet(PCMP, n->ntest->type, c1); patch(gbranch(PBEQ, n->ntest->type), s->sprog); } if(dflt != P) { patch(gbranch(PGOTO, N), dflt); goto ret; } patch(gbranch(PGOTO, N), breakpc); ret: dynlineno = lno; } /* * does this tree use * the pointer register */ int usesptr(Node *n) { // if(n->addable) // return 0; return 1; } void cgen_as(Node *nl, Node *nr, int op, int kaka) { Node *r; loop: switch(op) { default: fatal("cgen_as: unknown op %O", op); case OAS: if(nr == N && nl->op == OLIST) { kaka = PAS_SINGLE; cgen_as(nl->left, nr, op, kaka); nl = nl->right; goto loop; } switch(kaka) { default: yyerror("cgen_as: unknown param %d %d", kaka, PAS_CALLM); break; case PAS_CALLM: // function returning multi values cgen_call(nr, 0); cgen_callret(nr, nl); break; case PAS_SINGLE: // single return val used in expr if(nr == N || isnil(nr)) { if(nl->addable) { gopcodet(PSTOREZ, nl->type, nl); break; } agen(nl); gopcodet(PSTOREZI, nl->type, N); break; } if(nl->addable) { cgen(nr); genconv(nl, nr); gopcodet(PSTORE, nl->type, nl); break; } if(nr->addable && !needconvert(nl->type, nr->type)) { agen(nl); gopcodet(PSTOREI, nr->type, nr); break; } if(!usesptr(nr)) { cgen(nr); genconv(nl, nr); agen(nl); gopcodet(PSTOREI, nr->type, N); break; } agen(nl); r = tempname(ptrto(nl->type)); gopcode(PSTORE, PTADDR, r); cgen(nr); genconv(nl, nr); gopcode(PLOAD, PTADDR, r); gopcodet(PSTOREI, nl->type, N); break; case PAS_STRUCT: // structure assignment r = ptrto(nr->type); if(!usesptr(nr)) { agen(nr); agen(nl); gopcodet(PLOAD, N, r); gopcodet(PERROR, nr->type, N); break; } r = tempname(r); agen(nr); gopcode(PSTORE, PTADDR, r); agen(nl); gopcodet(PERROR, nr->type, r); break; } break; } } void cgen_asop(Node *nl, Node *nr, int op) { Node *r; int a; a = optopop(op); if(nr->addable) { if(nl->addable) { gopcodet(PLOAD, nl->type, nl); gopcodet(a, nr->type, nr); gopcodet(PSTORE, nl->type, nl); return; } agen(nl); gopcodet(PLOADI, nl->type, N); gopcodet(a, nr->type, nr); gopcodet(PSTOREI, nl->type, N); return; } r = tempname(nr->type); cgen(nr); gopcodet(PSTORE, nr->type, r); agen(nl); gopcodet(PLOADI, nl->type, N); gopcodet(a, nr->type, r); gopcodet(PSTOREI, nl->type, N); } void inarggen(void) { Iter save; Node *arg, *t; int i; t = curfn->type; arg = structfirst(&save, getthis(t)); if(arg != N) { fnparam(t, 0, 0); gopcodet(PSTORE, arg->type, arg->nname); } i = 0; arg = structfirst(&save, getinarg(t)); while(arg != N) { fnparam(t, 2, i); gopcodet(PLOADI, arg->type, arg->nname); arg = structnext(&save); i++; } } void cgen_ret(Node *n) { Node *arg, *a, *f; Iter save; arg = listfirst(&save, &n->left); // expr list a = getoutargx(curfn->type); f = a->type; for(;;) { if(arg == N) break; if(f->etype != TFIELD) fatal("cgen_ret: not field"); if(arg->addable && !needconvert(f->type, arg->type)) { gopcode(PLOAD, PTADDR, a->nname); gopcode(PADDO, PTADDR, f->nname); gopcodet(PSTOREI, arg->type, arg); } else { cgen(arg); genconv(f, arg); gopcode(PLOAD, PTADDR, a->nname); gopcode(PADDO, PTADDR, f->nname); gopcodet(PSTOREI, f->type, N); } arg = listnext(&save); f = f->down; } gopcodet(PRETURN, N, N); } void cgen_call(Node *n, int toss) { Node *t, *at, *ae, *sn; Iter save; int i; /* * open a block */ gopcodet(PCALL1, N, n->left); /* * prepare the input args */ t = n->left->type; if(t->etype == TPTR) t = t->type; at = *getinarg(t); // parameter struct sn = at->nname; // in arg structure name at = at->type; // parameter fields ae = listfirst(&save, &n->right); // expr list for(i=0; iintuple; i++) { if(ae == N) fatal("cgen_call: tupleness"); if(ae->addable && !needconvert(at->type, ae->type)) { gopcode(PADDR, PTADDR, sn); gopcode(PADDO, PTADDR, at->nname); gopcodet(PSTOREI, at->type, ae); } else { cgen(ae); genconv(at, ae); gopcode(PADDR, PTADDR, sn); gopcode(PADDO, PTADDR, at->nname); gopcodet(PSTOREI, at->type, N); } ae = listnext(&save); at = at->down; } /* * call the function */ switch(n->op) { default: fatal("cgen_call: %O", n->op); case OCALL: gopcodet(PCALL2, N, n->left); break; case OCALLPTR: cgen(n->left); gopcodet(PCALLI2, N, n->left); break; case OCALLMETH: cgen(n->left); gopcodet(PCALLM2, N, n->left); break; case OCALLINTER: cgen(n->left); gopcodet(PCALLF2, N, n->left); break; } /* * toss the output args */ if(toss) { gopcodet(PCALL3, N, n->left); return; } } void cgen_callret(Node *n, Node *mas) { Node *t, *at, *ae, *sn; Iter save; int i; t = n->left->type; if(t->etype == TPTR) t = t->type; at = *getoutarg(t); // parameter struct sn = at->nname; // out arg structure name at = at->type; // parameter fields // call w single return val to a register if(mas == N) { gopcode(PADDR, PTADDR, sn); gopcode(PADDO, PTADDR, at->nname); gopcodet(PLOADI, at->type, N); gopcodet(PCALL3, N, N); return; } // call w multiple values to lval list ae = listfirst(&save, &mas); // expr list for(i=0; iouttuple; i++) { if(ae == N) fatal("cgen_callret: output arguments do not match"); if(ae->addable) { gopcode(PADDR, PTADDR, sn); gopcode(PADDO, PTADDR, at->nname); gopcodet(PLOADI, at->type, ae); } else { agen(ae); gopcode(PADDR, PTADDR, sn); gopcode(PADDO, PTADDR, at->nname); gopcodet(PLOADI, at->type, N); } ae = listnext(&save); at = at->down; } gopcodet(PCALL3, N, N); } void genprint(Node *n) { Node *arg; Iter save; arg = listfirst(&save, &n); while(arg != N) { cgen(arg); gopcodet(PPRINT, arg->type, N); arg = listnext(&save); } } int needconvert(Node *tl, Node *tr) { if(isinter(tl)) { if(isptrto(tr, TSTRUCT)) return 1; if(isinter(tr)) return 1; return 0; } if(isptrto(tl, TSTRUCT)) if(isinter(tr)) return 1; return 0; } void genconv(Node *l, Node *r) { Node *tl, *tr; tl = l->type; tr = r->type; if(needconvert(tl, tr)) gopcode(PCONV, PTNIL, nod(OCONV, tl, tr)); } void genindex(Node *n) { gopcode(PINDEX, n->right->type->etype, n); } int optopop(int op) { int a; switch(op) { default: fatal("optopop: unknown op %O\n", op); case OMINUS: a = PMINUS; break; case OCOM: a = PCOM; break; case OAND: a = PAND; break; case OOR: a = POR; break; case OXOR: a = PXOR; break; case OADD: a = PADD; break; case OMUL: a = PMUL; break; case OMOD: a = PMOD; break; case OSUB: a = PSUB; break; case ODIV: a = PDIV; break; case OLSH: a = PLSH; break; case ORSH: a = PRSH; break; case OCAT: a = PCAT; break; } return a; }