mirror of
https://github.com/golang/go
synced 2024-10-04 11:11:21 -06:00
6b4d69e5f0
add src/test dir SVN=121166
1187 lines
19 KiB
C
1187 lines
19 KiB
C
// 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;
|
|
}
|