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