| // 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; i<t->intuple; 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; i<t->outtuple; 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; | |
| } |