mirror of
https://github.com/golang/go
synced 2024-10-05 00:11:21 -06:00
758f2bc556
5g/6g/8g: add import statements to export metadata, mapping package path to package name. recognize "" as the path of the package in export metadata. use "" as the path of the package in object symbol names. 5c/6c/8c, 5a/6a/8a: rewrite leading . to "". so that ·Sin means Sin in this package. 5l/6l/8l: rewrite "" in symbol names as object files are read. gotest: handle new symbol names. gopack: handle new import lines in export metadata. Collectively, these changes eliminate the assumption of a global name space in the object file formats. Higher level pieces such as reflect and the computation of type hashes still depend on the assumption; we're not done yet. R=ken2, r, ken3 CC=golang-dev https://golang.org/cl/186263
659 lines
11 KiB
C
659 lines
11 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.
|
|
|
|
/*
|
|
* portable half of code generator.
|
|
* mainly statements and control flow.
|
|
*/
|
|
|
|
#include "go.h"
|
|
|
|
Node*
|
|
sysfunc(char *name)
|
|
{
|
|
Node *n;
|
|
|
|
n = newname(pkglookup(name, runtimepkg));
|
|
n->class = PFUNC;
|
|
return n;
|
|
}
|
|
|
|
void
|
|
allocparams(void)
|
|
{
|
|
NodeList *l;
|
|
Node *n;
|
|
uint32 w;
|
|
Sym *s;
|
|
int lno;
|
|
|
|
if(stksize < 0)
|
|
fatal("allocparams not during code generation");
|
|
|
|
/*
|
|
* allocate (set xoffset) the stack
|
|
* slots for all automatics.
|
|
* allocated starting at -w down.
|
|
*/
|
|
lno = lineno;
|
|
for(l=curfn->dcl; l; l=l->next) {
|
|
n = l->n;
|
|
if(n->op == ONAME && n->class == PHEAP-1) {
|
|
// heap address variable; finish the job
|
|
// started in addrescapes.
|
|
s = n->sym;
|
|
tempname(n, n->type);
|
|
n->sym = s;
|
|
}
|
|
if(n->op != ONAME || n->class != PAUTO)
|
|
continue;
|
|
if(n->type == T)
|
|
continue;
|
|
dowidth(n->type);
|
|
w = n->type->width;
|
|
if(w >= MAXWIDTH)
|
|
fatal("bad width");
|
|
stksize += w;
|
|
stksize = rnd(stksize, w);
|
|
n->xoffset = -stksize;
|
|
}
|
|
lineno = lno;
|
|
}
|
|
|
|
void
|
|
newlab(int op, Sym *s, Node *stmt)
|
|
{
|
|
Label *lab;
|
|
|
|
lab = mal(sizeof(*lab));
|
|
lab->link = labellist;
|
|
labellist = lab;
|
|
|
|
lab->sym = s;
|
|
lab->op = op;
|
|
lab->label = pc;
|
|
lab->stmt = stmt;
|
|
}
|
|
|
|
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 OLABEL:
|
|
// these are definitions -
|
|
s = l->sym;
|
|
for(m=labellist; m!=L; m=m->link) {
|
|
if(m->sym != s)
|
|
continue;
|
|
switch(m->op) {
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* compile statements
|
|
*/
|
|
void
|
|
genlist(NodeList *l)
|
|
{
|
|
for(; l; l=l->next)
|
|
gen(l->n);
|
|
}
|
|
|
|
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)
|
|
genlist(n->ninit);
|
|
|
|
setlineno(n);
|
|
|
|
switch(n->op) {
|
|
default:
|
|
fatal("gen: unknown op %N", n);
|
|
break;
|
|
|
|
case OCASE:
|
|
case OFALL:
|
|
case OXCASE:
|
|
case OXFALL:
|
|
case ODCLCONST:
|
|
case ODCLFUNC:
|
|
case ODCLTYPE:
|
|
break;
|
|
|
|
case OEMPTY:
|
|
// insert no-op so that
|
|
// L:; for { }
|
|
// does not treat L as a label for the loop.
|
|
if(labellist && labellist->label == p3)
|
|
gused(N);
|
|
break;
|
|
|
|
case OBLOCK:
|
|
genlist(n->list);
|
|
break;
|
|
|
|
case OLABEL:
|
|
newlab(OLABEL, n->left->sym, n->right);
|
|
break;
|
|
|
|
case OGOTO:
|
|
newlab(OGOTO, n->left->sym, N);
|
|
gjmp(P);
|
|
break;
|
|
|
|
case OBREAK:
|
|
if(n->left != N) {
|
|
for(lab=labellist; lab!=L; lab=lab->link) {
|
|
if(lab->sym == n->left->sym) {
|
|
if(lab->breakpc == P)
|
|
yyerror("invalid break label %S", n->left->sym);
|
|
gjmp(lab->breakpc);
|
|
goto donebreak;
|
|
}
|
|
}
|
|
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);
|
|
donebreak:
|
|
break;
|
|
|
|
case OCONTINUE:
|
|
if(n->left != N) {
|
|
for(lab=labellist; lab!=L; lab=lab->link) {
|
|
if(lab->sym == n->left->sym) {
|
|
if(lab->continpc == P)
|
|
yyerror("invalid continue label %S", n->left->sym);
|
|
gjmp(lab->continpc);
|
|
goto donecont;
|
|
}
|
|
}
|
|
if(lab == L)
|
|
yyerror("continue label not defined: %S", n->left->sym);
|
|
break;
|
|
}
|
|
|
|
if(continpc == P) {
|
|
yyerror("continue is not in a loop");
|
|
break;
|
|
}
|
|
gjmp(continpc);
|
|
donecont:
|
|
break;
|
|
|
|
case OFOR:
|
|
sbreak = breakpc;
|
|
p1 = gjmp(P); // goto test
|
|
breakpc = gjmp(P); // break: goto done
|
|
scontin = continpc;
|
|
continpc = pc;
|
|
|
|
// define break and continue labels
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) {
|
|
lab->breakpc = breakpc;
|
|
lab->continpc = continpc;
|
|
}
|
|
|
|
gen(n->nincr); // contin: incr
|
|
patch(p1, pc); // test:
|
|
if(n->ntest != N)
|
|
if(n->ntest->ninit != nil)
|
|
genlist(n->ntest->ninit);
|
|
bgen(n->ntest, 0, breakpc); // if(!test) goto break
|
|
genlist(n->nbody); // body
|
|
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)
|
|
if(n->ntest->ninit != nil)
|
|
genlist(n->ntest->ninit);
|
|
bgen(n->ntest, 0, p2); // if(!test) goto p2
|
|
genlist(n->nbody); // then
|
|
p3 = gjmp(P); // goto done
|
|
patch(p2, pc); // else:
|
|
genlist(n->nelse); // else
|
|
patch(p3, pc); // done:
|
|
break;
|
|
|
|
case OSWITCH:
|
|
sbreak = breakpc;
|
|
p1 = gjmp(P); // goto test
|
|
breakpc = gjmp(P); // break: goto done
|
|
|
|
// define break label
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
|
|
lab->breakpc = breakpc;
|
|
|
|
patch(p1, pc); // test:
|
|
genlist(n->nbody); // switch(test) body
|
|
patch(breakpc, pc); // done:
|
|
breakpc = sbreak;
|
|
break;
|
|
|
|
case OSELECT:
|
|
sbreak = breakpc;
|
|
p1 = gjmp(P); // goto test
|
|
breakpc = gjmp(P); // break: goto done
|
|
|
|
// define break label
|
|
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
|
|
lab->breakpc = breakpc;
|
|
|
|
patch(p1, pc); // test:
|
|
genlist(n->nbody); // select() body
|
|
patch(breakpc, pc); // done:
|
|
breakpc = sbreak;
|
|
break;
|
|
|
|
case OASOP:
|
|
cgen_asop(n);
|
|
break;
|
|
|
|
case ODCL:
|
|
cgen_dcl(n->left);
|
|
break;
|
|
|
|
case OAS:
|
|
if(gen_as_init(n))
|
|
break;
|
|
cgen_as(n->left, n->right);
|
|
break;
|
|
|
|
case OCALLMETH:
|
|
cgen_callmeth(n, 0);
|
|
break;
|
|
|
|
case OCALLINTER:
|
|
cgen_callinter(n, N, 0);
|
|
break;
|
|
|
|
case OCALLFUNC:
|
|
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");
|
|
|
|
n->op = OCALLFUNC;
|
|
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;
|
|
|
|
case OCALLFUNC:
|
|
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 discard of value
|
|
*/
|
|
void
|
|
cgen_discard(Node *nr)
|
|
{
|
|
Node tmp;
|
|
|
|
if(nr == N)
|
|
return;
|
|
|
|
switch(nr->op) {
|
|
case ONAME:
|
|
if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC)
|
|
gused(nr);
|
|
break;
|
|
|
|
// unary
|
|
case OADD:
|
|
case OAND:
|
|
case ODIV:
|
|
case OEQ:
|
|
case OGE:
|
|
case OGT:
|
|
case OLE:
|
|
case OLSH:
|
|
case OLT:
|
|
case OMOD:
|
|
case OMUL:
|
|
case ONE:
|
|
case OOR:
|
|
case ORSH:
|
|
case OSUB:
|
|
case OXOR:
|
|
cgen_discard(nr->left);
|
|
cgen_discard(nr->right);
|
|
break;
|
|
|
|
// binary
|
|
case OCAP:
|
|
case OCOM:
|
|
case OLEN:
|
|
case OMINUS:
|
|
case ONOT:
|
|
case OPLUS:
|
|
cgen_discard(nr->left);
|
|
break;
|
|
|
|
// special enough to just evaluate
|
|
default:
|
|
tempname(&tmp, nr->type);
|
|
cgen_as(&tmp, nr);
|
|
gused(&tmp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
if(isblank(nl)) {
|
|
cgen_discard(nr);
|
|
return;
|
|
}
|
|
|
|
iszer = 0;
|
|
if(nr == N || isnil(nr)) {
|
|
// externals and heaps should already be clear
|
|
if(nr == N) {
|
|
if(nl->class == PEXTERN)
|
|
return;
|
|
if(nl->class & PHEAP)
|
|
return;
|
|
}
|
|
|
|
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:
|
|
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:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* gather series of offsets
|
|
* >=0 is direct addressed field
|
|
* <0 is pointer to next field (+1)
|
|
*/
|
|
int
|
|
dotoffset(Node *n, int *oary, Node **nn)
|
|
{
|
|
int i;
|
|
|
|
switch(n->op) {
|
|
case ODOT:
|
|
if(n->xoffset == BADWIDTH) {
|
|
dump("bad width in dotoffset", n);
|
|
fatal("bad width in dotoffset");
|
|
}
|
|
i = dotoffset(n->left, oary, nn);
|
|
if(i > 0) {
|
|
if(oary[i-1] >= 0)
|
|
oary[i-1] += n->xoffset;
|
|
else
|
|
oary[i-1] -= n->xoffset;
|
|
break;
|
|
}
|
|
if(i < 10)
|
|
oary[i++] = n->xoffset;
|
|
break;
|
|
|
|
case ODOTPTR:
|
|
if(n->xoffset == BADWIDTH) {
|
|
dump("bad width in dotoffset", n);
|
|
fatal("bad width in dotoffset");
|
|
}
|
|
i = dotoffset(n->left, oary, nn);
|
|
if(i < 10)
|
|
oary[i++] = -(n->xoffset+1);
|
|
break;
|
|
|
|
default:
|
|
*nn = n;
|
|
return 0;
|
|
}
|
|
if(i >= 10)
|
|
*nn = N;
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* make a new off the books
|
|
*/
|
|
void
|
|
tempname(Node *n, Type *t)
|
|
{
|
|
Sym *s;
|
|
uint32 w;
|
|
|
|
if(stksize < 0)
|
|
fatal("tempname not during code generation");
|
|
|
|
if(t == T) {
|
|
yyerror("tempname called with nil type");
|
|
t = types[TINT32];
|
|
}
|
|
|
|
// give each tmp a different name so that there
|
|
// a chance to registerizer them
|
|
snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen);
|
|
statuniqgen++;
|
|
s = lookup(namebuf);
|
|
|
|
memset(n, 0, sizeof(*n));
|
|
n->op = ONAME;
|
|
n->sym = s;
|
|
n->type = t;
|
|
n->class = PAUTO;
|
|
n->addable = 1;
|
|
n->ullman = 1;
|
|
n->noescape = 1;
|
|
|
|
dowidth(t);
|
|
w = t->width;
|
|
stksize += w;
|
|
stksize = rnd(stksize, w);
|
|
n->xoffset = -stksize;
|
|
}
|