2009-03-30 20:15:07 -06:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* portable half of code generator.
|
|
|
|
* mainly statements and control flow.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "go.h"
|
|
|
|
|
|
|
|
Node*
|
|
|
|
sysfunc(char *name)
|
|
|
|
{
|
|
|
|
Node *n;
|
|
|
|
|
|
|
|
n = newname(pkglookup(name, "sys"));
|
|
|
|
n->class = PFUNC;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
allocparams(void)
|
|
|
|
{
|
2009-08-04 23:59:23 -06:00
|
|
|
NodeList *l;
|
2009-03-30 20:15:07 -06:00
|
|
|
Node *n;
|
|
|
|
uint32 w;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate (set xoffset) the stack
|
|
|
|
* slots for all automatics.
|
|
|
|
* allocated starting at -w down.
|
|
|
|
*/
|
2009-08-04 23:59:23 -06:00
|
|
|
for(l=autodcl; l; l=l->next) {
|
|
|
|
n = l->n;
|
|
|
|
if(n->op != ONAME || n->class != PAUTO)
|
2009-03-30 20:15:07 -06:00
|
|
|
continue;
|
2009-08-04 17:53:06 -06:00
|
|
|
typecheck(&n, Erv);
|
2009-03-30 20:15:07 -06:00
|
|
|
dowidth(n->type);
|
|
|
|
w = n->type->width;
|
|
|
|
if(n->class & PHEAP)
|
|
|
|
w = widthptr;
|
|
|
|
stksize += w;
|
|
|
|
stksize = rnd(stksize, w);
|
|
|
|
|
|
|
|
n->xoffset = -stksize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
newlab(int op, Sym *s)
|
|
|
|
{
|
|
|
|
Label *lab;
|
|
|
|
|
|
|
|
lab = mal(sizeof(*lab));
|
|
|
|
lab->link = labellist;
|
|
|
|
labellist = lab;
|
|
|
|
|
|
|
|
lab->sym = s;
|
|
|
|
lab->op = op;
|
|
|
|
lab->label = pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
checklabels(void)
|
|
|
|
{
|
|
|
|
Label *l, *m;
|
|
|
|
Sym *s;
|
|
|
|
|
|
|
|
// // print the label list
|
|
|
|
// for(l=labellist; l!=L; l=l->link) {
|
|
|
|
// print("lab %O %S\n", l->op, l->sym);
|
|
|
|
// }
|
|
|
|
|
|
|
|
for(l=labellist; l!=L; l=l->link) {
|
|
|
|
switch(l->op) {
|
|
|
|
case OFOR:
|
|
|
|
case OLABEL:
|
|
|
|
// these are definitions -
|
|
|
|
s = l->sym;
|
|
|
|
for(m=labellist; m!=L; m=m->link) {
|
|
|
|
if(m->sym != s)
|
|
|
|
continue;
|
|
|
|
switch(m->op) {
|
|
|
|
case OFOR:
|
|
|
|
case OLABEL:
|
|
|
|
// these are definitions -
|
|
|
|
// look for redefinitions
|
|
|
|
if(l != m)
|
|
|
|
yyerror("label %S redefined", s);
|
|
|
|
break;
|
|
|
|
case OGOTO:
|
|
|
|
// these are references -
|
|
|
|
// patch to definition
|
|
|
|
patch(m->label, l->label);
|
|
|
|
m->sym = S; // mark done
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// diagnostic for all undefined references
|
|
|
|
for(l=labellist; l!=L; l=l->link)
|
|
|
|
if(l->op == OGOTO && l->sym != S)
|
|
|
|
yyerror("label %S not defined", l->sym);
|
|
|
|
}
|
|
|
|
|
|
|
|
Label*
|
|
|
|
findlab(Sym *s)
|
|
|
|
{
|
|
|
|
Label *l;
|
|
|
|
|
|
|
|
for(l=labellist; l!=L; l=l->link) {
|
|
|
|
if(l->sym != s)
|
|
|
|
continue;
|
|
|
|
if(l->op != OFOR)
|
|
|
|
continue;
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
return L;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* compile statements
|
|
|
|
*/
|
2009-07-17 02:00:44 -06:00
|
|
|
void
|
|
|
|
genlist(NodeList *l)
|
|
|
|
{
|
|
|
|
for(; l; l=l->next)
|
|
|
|
gen(l->n);
|
|
|
|
}
|
|
|
|
|
2009-03-30 20:15:07 -06:00
|
|
|
void
|
|
|
|
gen(Node *n)
|
|
|
|
{
|
|
|
|
int32 lno;
|
|
|
|
Prog *scontin, *sbreak;
|
|
|
|
Prog *p1, *p2, *p3;
|
|
|
|
Label *lab;
|
|
|
|
|
|
|
|
lno = setlineno(n);
|
|
|
|
|
|
|
|
if(n == N)
|
|
|
|
goto ret;
|
|
|
|
|
|
|
|
p3 = pc; // save pc for loop labels
|
|
|
|
if(n->ninit)
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->ninit);
|
2009-03-30 20:15:07 -06:00
|
|
|
|
|
|
|
setlineno(n);
|
|
|
|
|
|
|
|
switch(n->op) {
|
|
|
|
default:
|
|
|
|
fatal("gen: unknown op %N", n);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OCASE:
|
|
|
|
case OFALL:
|
|
|
|
case OXCASE:
|
|
|
|
case OXFALL:
|
2009-07-29 15:49:01 -06:00
|
|
|
break;
|
|
|
|
|
2009-03-30 20:15:07 -06:00
|
|
|
case OEMPTY:
|
2009-07-29 15:49:01 -06:00
|
|
|
// insert no-op so that
|
|
|
|
// L:; for { }
|
|
|
|
// does not treat L as a label for the loop.
|
|
|
|
if(labellist && labellist->label == p3)
|
|
|
|
gused(N);
|
2009-03-30 20:15:07 -06:00
|
|
|
break;
|
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
case OBLOCK:
|
|
|
|
genlist(n->list);
|
|
|
|
break;
|
|
|
|
|
2009-03-30 20:15:07 -06:00
|
|
|
case OLABEL:
|
|
|
|
newlab(OLABEL, n->left->sym);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OGOTO:
|
|
|
|
newlab(OGOTO, n->left->sym);
|
|
|
|
gjmp(P);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OBREAK:
|
|
|
|
if(n->left != N) {
|
|
|
|
for(lab=labellist; lab!=L; lab=lab->link) {
|
2009-07-29 15:49:01 -06:00
|
|
|
if(lab->sym == n->left->sym) {
|
|
|
|
if(lab->breakpc == P)
|
|
|
|
yyerror("invalid break label %S", n->left->sym);
|
2009-03-30 20:15:07 -06:00
|
|
|
gjmp(lab->breakpc);
|
2009-07-29 15:49:01 -06:00
|
|
|
goto donebreak;
|
2009-03-30 20:15:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(lab == L)
|
|
|
|
yyerror("break label not defined: %S", n->left->sym);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(breakpc == P) {
|
|
|
|
yyerror("break is not in a loop");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gjmp(breakpc);
|
2009-07-29 15:49:01 -06:00
|
|
|
donebreak:
|
2009-03-30 20:15:07 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OCONTINUE:
|
|
|
|
if(n->left != N) {
|
|
|
|
for(lab=labellist; lab!=L; lab=lab->link) {
|
2009-07-29 15:49:01 -06:00
|
|
|
if(lab->sym == n->left->sym) {
|
|
|
|
if(lab->continpc == P)
|
|
|
|
yyerror("invalid continue label %S", n->left->sym);
|
2009-03-30 20:15:07 -06:00
|
|
|
gjmp(lab->continpc);
|
2009-07-29 15:49:01 -06:00
|
|
|
goto donecont;
|
2009-03-30 20:15:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(lab == L)
|
2009-07-29 15:49:01 -06:00
|
|
|
yyerror("continue label not defined: %S", n->left->sym);
|
2009-03-30 20:15:07 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(continpc == P) {
|
2009-07-29 15:49:01 -06:00
|
|
|
yyerror("continue is not in a loop");
|
2009-03-30 20:15:07 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
gjmp(continpc);
|
2009-07-29 15:49:01 -06:00
|
|
|
donecont:
|
2009-03-30 20:15:07 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OFOR:
|
|
|
|
sbreak = breakpc;
|
2009-08-04 17:53:06 -06:00
|
|
|
p1 = gjmp(P); // goto test
|
2009-03-30 20:15:07 -06:00
|
|
|
breakpc = gjmp(P); // break: goto done
|
|
|
|
scontin = continpc;
|
|
|
|
continpc = pc;
|
|
|
|
|
2009-07-29 15:49:01 -06:00
|
|
|
// define break and continue labels
|
|
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL) {
|
|
|
|
lab->breakpc = breakpc;
|
|
|
|
lab->continpc = continpc;
|
2009-03-30 20:15:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
gen(n->nincr); // contin: incr
|
|
|
|
patch(p1, pc); // test:
|
|
|
|
if(n->ntest != N)
|
2009-07-17 02:00:44 -06:00
|
|
|
if(n->ntest->ninit != nil)
|
|
|
|
genlist(n->ntest->ninit);
|
2009-03-30 20:15:07 -06:00
|
|
|
bgen(n->ntest, 0, breakpc); // if(!test) goto break
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->nbody); // body
|
2009-03-30 20:15:07 -06:00
|
|
|
gjmp(continpc);
|
|
|
|
patch(breakpc, pc); // done:
|
|
|
|
continpc = scontin;
|
|
|
|
breakpc = sbreak;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OIF:
|
|
|
|
p1 = gjmp(P); // goto test
|
|
|
|
p2 = gjmp(P); // p2: goto else
|
|
|
|
patch(p1, pc); // test:
|
|
|
|
if(n->ntest != N)
|
2009-07-17 02:00:44 -06:00
|
|
|
if(n->ntest->ninit != nil)
|
|
|
|
genlist(n->ntest->ninit);
|
2009-08-04 17:53:06 -06:00
|
|
|
bgen(n->ntest, 0, p2); // if(!test) goto p2
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->nbody); // then
|
2009-03-30 20:15:07 -06:00
|
|
|
p3 = gjmp(P); // goto done
|
|
|
|
patch(p2, pc); // else:
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->nelse); // else
|
2009-03-30 20:15:07 -06:00
|
|
|
patch(p3, pc); // done:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OSWITCH:
|
|
|
|
sbreak = breakpc;
|
2009-08-04 17:53:06 -06:00
|
|
|
p1 = gjmp(P); // goto test
|
2009-03-30 20:15:07 -06:00
|
|
|
breakpc = gjmp(P); // break: goto done
|
|
|
|
|
|
|
|
// define break label
|
2009-07-29 15:49:01 -06:00
|
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL)
|
|
|
|
lab->breakpc = breakpc;
|
2009-03-30 20:15:07 -06:00
|
|
|
|
|
|
|
patch(p1, pc); // test:
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->nbody); // switch(test) body
|
2009-03-30 20:15:07 -06:00
|
|
|
patch(breakpc, pc); // done:
|
|
|
|
breakpc = sbreak;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OSELECT:
|
|
|
|
sbreak = breakpc;
|
2009-08-04 17:53:06 -06:00
|
|
|
p1 = gjmp(P); // goto test
|
2009-03-30 20:15:07 -06:00
|
|
|
breakpc = gjmp(P); // break: goto done
|
|
|
|
|
|
|
|
// define break label
|
2009-07-29 15:49:01 -06:00
|
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL)
|
|
|
|
lab->breakpc = breakpc;
|
2009-03-30 20:15:07 -06:00
|
|
|
|
|
|
|
patch(p1, pc); // test:
|
2009-07-17 02:00:44 -06:00
|
|
|
genlist(n->nbody); // select() body
|
2009-03-30 20:15:07 -06:00
|
|
|
patch(breakpc, pc); // done:
|
|
|
|
breakpc = sbreak;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OASOP:
|
|
|
|
cgen_asop(n);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODCL:
|
|
|
|
cgen_dcl(n->left);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OAS:
|
|
|
|
cgen_as(n->left, n->right);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OCALLMETH:
|
|
|
|
cgen_callmeth(n, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OCALLINTER:
|
|
|
|
cgen_callinter(n, N, 0);
|
|
|
|
break;
|
|
|
|
|
2009-07-30 17:53:08 -06:00
|
|
|
case OCALLFUNC:
|
2009-03-30 20:15:07 -06:00
|
|
|
cgen_call(n, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPROC:
|
|
|
|
cgen_proc(n, 1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODEFER:
|
|
|
|
cgen_proc(n, 2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ORETURN:
|
|
|
|
cgen_ret(n);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret:
|
|
|
|
lineno = lno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* generate call to non-interface method
|
|
|
|
* proc=0 normal call
|
|
|
|
* proc=1 goroutine run in new proc
|
|
|
|
* proc=2 defer call save away stack
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cgen_callmeth(Node *n, int proc)
|
|
|
|
{
|
|
|
|
Node *l;
|
|
|
|
|
|
|
|
// generate a rewrite for method call
|
|
|
|
// (p.f)(...) goes to (f)(p,...)
|
|
|
|
|
|
|
|
l = n->left;
|
|
|
|
if(l->op != ODOTMETH)
|
|
|
|
fatal("cgen_callmeth: not dotmethod: %N");
|
|
|
|
|
2009-07-30 17:53:08 -06:00
|
|
|
n->op = OCALLFUNC;
|
2009-03-30 20:15:07 -06:00
|
|
|
n->left = n->left->right;
|
|
|
|
n->left->type = l->type;
|
|
|
|
|
|
|
|
if(n->left->op == ONAME)
|
|
|
|
n->left->class = PFUNC;
|
|
|
|
cgen_call(n, proc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* generate code to start new proc running call n.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cgen_proc(Node *n, int proc)
|
|
|
|
{
|
|
|
|
switch(n->left->op) {
|
|
|
|
default:
|
|
|
|
fatal("cgen_proc: unknown call %O", n->left->op);
|
|
|
|
|
|
|
|
case OCALLMETH:
|
|
|
|
cgen_callmeth(n->left, proc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OCALLINTER:
|
|
|
|
cgen_callinter(n->left, N, proc);
|
|
|
|
break;
|
|
|
|
|
2009-07-30 17:53:08 -06:00
|
|
|
case OCALLFUNC:
|
2009-03-30 20:15:07 -06:00
|
|
|
cgen_call(n->left, proc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* generate declaration.
|
|
|
|
* nothing to do for on-stack automatics,
|
|
|
|
* but might have to allocate heap copy
|
|
|
|
* for escaped variables.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cgen_dcl(Node *n)
|
|
|
|
{
|
|
|
|
if(debug['g'])
|
|
|
|
dump("\ncgen-dcl", n);
|
|
|
|
if(n->op != ONAME) {
|
|
|
|
dump("cgen_dcl", n);
|
|
|
|
fatal("cgen_dcl");
|
|
|
|
}
|
|
|
|
if(!(n->class & PHEAP))
|
|
|
|
return;
|
|
|
|
cgen_as(n->heapaddr, n->alloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* generate assignment:
|
|
|
|
* nl = nr
|
|
|
|
* nr == N means zero nl.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cgen_as(Node *nl, Node *nr)
|
|
|
|
{
|
|
|
|
Node nc;
|
|
|
|
Type *tl;
|
|
|
|
int iszer;
|
|
|
|
|
|
|
|
if(nl == N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(debug['g']) {
|
|
|
|
dump("cgen_as", nl);
|
|
|
|
dump("cgen_as = ", nr);
|
|
|
|
}
|
|
|
|
|
|
|
|
iszer = 0;
|
|
|
|
if(nr == N || isnil(nr)) {
|
2009-05-04 22:48:46 -06:00
|
|
|
// externals and heaps should already be clear
|
2009-05-05 18:33:51 -06:00
|
|
|
if(nr == N) {
|
|
|
|
if(nl->class == PEXTERN)
|
|
|
|
return;
|
|
|
|
if(nl->class & PHEAP)
|
|
|
|
return;
|
2009-05-17 20:16:16 -06:00
|
|
|
if(gen_as_init(nr, nl))
|
|
|
|
return;
|
2009-05-05 18:33:51 -06:00
|
|
|
}
|
|
|
|
|
2009-03-30 20:15:07 -06:00
|
|
|
tl = nl->type;
|
|
|
|
if(tl == T)
|
|
|
|
return;
|
|
|
|
if(isfat(tl)) {
|
|
|
|
clearfat(nl);
|
|
|
|
goto ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* invent a "zero" for the rhs */
|
|
|
|
iszer = 1;
|
|
|
|
nr = &nc;
|
|
|
|
memset(nr, 0, sizeof(*nr));
|
|
|
|
switch(simtype[tl->etype]) {
|
|
|
|
default:
|
|
|
|
fatal("cgen_as: tl %T", tl);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TINT8:
|
|
|
|
case TUINT8:
|
|
|
|
case TINT16:
|
|
|
|
case TUINT16:
|
|
|
|
case TINT32:
|
|
|
|
case TUINT32:
|
|
|
|
case TINT64:
|
|
|
|
case TUINT64:
|
|
|
|
nr->val.u.xval = mal(sizeof(*nr->val.u.xval));
|
|
|
|
mpmovecfix(nr->val.u.xval, 0);
|
|
|
|
nr->val.ctype = CTINT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TFLOAT32:
|
|
|
|
case TFLOAT64:
|
|
|
|
case TFLOAT80:
|
|
|
|
nr->val.u.fval = mal(sizeof(*nr->val.u.fval));
|
|
|
|
mpmovecflt(nr->val.u.fval, 0.0);
|
|
|
|
nr->val.ctype = CTFLT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TBOOL:
|
|
|
|
nr->val.u.bval = 0;
|
|
|
|
nr->val.ctype = CTBOOL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TPTR32:
|
|
|
|
case TPTR64:
|
|
|
|
nr->val.ctype = CTNIL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
nr->op = OLITERAL;
|
|
|
|
nr->type = tl;
|
|
|
|
nr->addable = 1;
|
|
|
|
ullmancalc(nr);
|
|
|
|
}
|
|
|
|
|
|
|
|
tl = nl->type;
|
|
|
|
if(tl == T)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cgen(nr, nl);
|
|
|
|
if(iszer && nl->addable)
|
|
|
|
gused(nl);
|
|
|
|
|
|
|
|
ret:
|
|
|
|
;
|
|
|
|
}
|