diff options
Diffstat (limited to 'src/cmd/gc/gen.c')
-rw-r--r-- | src/cmd/gc/gen.c | 987 |
1 files changed, 0 insertions, 987 deletions
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c deleted file mode 100644 index 72bcc492a5..0000000000 --- a/src/cmd/gc/gen.c +++ /dev/null @@ -1,987 +0,0 @@ -// 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 <u.h> -#include <libc.h> -#include "go.h" - -static void cgen_dcl(Node *n); -static void cgen_proc(Node *n, int proc); -static void checkgoto(Node*, Node*); - -static Label *labellist; -static Label *lastlabel; - -Node* -sysfunc(char *name) -{ - Node *n; - - n = newname(pkglookup(name, runtimepkg)); - n->class = PFUNC; - return n; -} - -/* - * the address of n has been taken and might be used after - * the current function returns. mark any local vars - * as needing to move to the heap. - */ -void -addrescapes(Node *n) -{ - char buf[100]; - Node *oldfn; - - switch(n->op) { - default: - // probably a type error already. - // dump("addrescapes", n); - break; - - case ONAME: - if(n == nodfp) - break; - - // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping. - // on PPARAM it means something different. - if(n->class == PAUTO && n->esc == EscNever) - break; - - switch(n->class) { - case PPARAMREF: - addrescapes(n->defn); - break; - case PPARAM: - case PPARAMOUT: - // if func param, need separate temporary - // to hold heap pointer. - // the function type has already been checked - // (we're in the function body) - // so the param already has a valid xoffset. - - // expression to refer to stack copy - n->stackparam = nod(OPARAM, n, N); - n->stackparam->type = n->type; - n->stackparam->addable = 1; - if(n->xoffset == BADWIDTH) - fatal("addrescapes before param assignment"); - n->stackparam->xoffset = n->xoffset; - // fallthrough - - case PAUTO: - n->class |= PHEAP; - n->addable = 0; - n->ullman = 2; - n->xoffset = 0; - - // create stack variable to hold pointer to heap - oldfn = curfn; - curfn = n->curfn; - n->heapaddr = temp(ptrto(n->type)); - snprint(buf, sizeof buf, "&%S", n->sym); - n->heapaddr->sym = lookup(buf); - n->heapaddr->orig->sym = n->heapaddr->sym; - n->esc = EscHeap; - if(debug['m']) - print("%L: moved to heap: %N\n", n->lineno, n); - curfn = oldfn; - break; - } - break; - - case OIND: - case ODOTPTR: - break; - - case ODOT: - case OINDEX: - // ODOTPTR has already been introduced, - // so these are the non-pointer ODOT and OINDEX. - // In &x[0], if x is a slice, then x does not - // escape--the pointer inside x does, but that - // is always a heap pointer anyway. - if(!isslice(n->left->type)) - addrescapes(n->left); - break; - } -} - -void -clearlabels(void) -{ - Label *l; - - for(l=labellist; l!=L; l=l->link) - l->sym->label = L; - - labellist = L; - lastlabel = L; -} - -static Label* -newlab(Node *n) -{ - Sym *s; - Label *lab; - - s = n->left->sym; - if((lab = s->label) == L) { - lab = mal(sizeof(*lab)); - if(lastlabel == nil) - labellist = lab; - else - lastlabel->link = lab; - lastlabel = lab; - lab->sym = s; - s->label = lab; - } - - if(n->op == OLABEL) { - if(lab->def != N) - yyerror("label %S already defined at %L", s, lab->def->lineno); - else - lab->def = n; - } else - lab->use = list(lab->use, n); - - return lab; -} - -void -checklabels(void) -{ - Label *lab; - NodeList *l; - - for(lab=labellist; lab!=L; lab=lab->link) { - if(lab->def == N) { - for(l=lab->use; l; l=l->next) - yyerrorl(l->n->lineno, "label %S not defined", lab->sym); - continue; - } - if(lab->use == nil && !lab->used) { - yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym); - continue; - } - if(lab->gotopc != P) - fatal("label %S never resolved", lab->sym); - for(l=lab->use; l; l=l->next) - checkgoto(l->n, lab->def); - } -} - -static void -checkgoto(Node *from, Node *to) -{ - int nf, nt; - Sym *block, *dcl, *fs, *ts; - int lno; - - if(from->sym == to->sym) - return; - - nf = 0; - for(fs=from->sym; fs; fs=fs->link) - nf++; - nt = 0; - for(fs=to->sym; fs; fs=fs->link) - nt++; - fs = from->sym; - for(; nf > nt; nf--) - fs = fs->link; - if(fs != to->sym) { - lno = lineno; - setlineno(from); - - // decide what to complain about. - // prefer to complain about 'into block' over declarations, - // so scan backward to find most recent block or else dcl. - block = S; - dcl = S; - ts = to->sym; - for(; nt > nf; nt--) { - if(ts->pkg == nil) - block = ts; - else - dcl = ts; - ts = ts->link; - } - while(ts != fs) { - if(ts->pkg == nil) - block = ts; - else - dcl = ts; - ts = ts->link; - fs = fs->link; - } - - if(block) - yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno); - else - yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno); - lineno = lno; - } -} - -static Label* -stmtlabel(Node *n) -{ - Label *lab; - - if(n->sym != S) - if((lab = n->sym->label) != L) - if(lab->def != N) - if(lab->def->defn == n) - return lab; - return L; -} - -/* - * 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; - int32 wasregalloc; - -//dump("gen", n); - - lno = setlineno(n); - wasregalloc = thearch.anyregalloc(); - - if(n == N) - goto ret; - - if(n->ninit) - genlist(n->ninit); - - setlineno(n); - - switch(n->op) { - default: - fatal("gen: unknown op %+hN", n); - break; - - case OCASE: - case OFALL: - case OXCASE: - case OXFALL: - case ODCLCONST: - case ODCLFUNC: - case ODCLTYPE: - break; - - case OEMPTY: - break; - - case OBLOCK: - genlist(n->list); - break; - - case OLABEL: - if(isblanksym(n->left->sym)) - break; - - lab = newlab(n); - - // if there are pending gotos, resolve them all to the current pc. - for(p1=lab->gotopc; p1; p1=p2) { - p2 = unpatch(p1); - patch(p1, pc); - } - lab->gotopc = P; - if(lab->labelpc == P) - lab->labelpc = pc; - - if(n->defn) { - switch(n->defn->op) { - case OFOR: - case OSWITCH: - case OSELECT: - // so stmtlabel can find the label - n->defn->sym = lab->sym; - } - } - break; - - case OGOTO: - // if label is defined, emit jump to it. - // otherwise save list of pending gotos in lab->gotopc. - // the list is linked through the normal jump target field - // to avoid a second list. (the jumps are actually still - // valid code, since they're just going to another goto - // to the same label. we'll unwind it when we learn the pc - // of the label in the OLABEL case above.) - lab = newlab(n); - if(lab->labelpc != P) - gjmp(lab->labelpc); - else - lab->gotopc = gjmp(lab->gotopc); - break; - - case OBREAK: - if(n->left != N) { - lab = n->left->sym->label; - if(lab == L) { - yyerror("break label not defined: %S", n->left->sym); - break; - } - lab->used = 1; - if(lab->breakpc == P) { - yyerror("invalid break label %S", n->left->sym); - break; - } - gjmp(lab->breakpc); - break; - } - if(breakpc == P) { - yyerror("break is not in a loop"); - break; - } - gjmp(breakpc); - break; - - case OCONTINUE: - if(n->left != N) { - lab = n->left->sym->label; - if(lab == L) { - yyerror("continue label not defined: %S", n->left->sym); - break; - } - lab->used = 1; - if(lab->continpc == P) { - yyerror("invalid continue label %S", n->left->sym); - break; - } - gjmp(lab->continpc); - break; - } - if(continpc == P) { - yyerror("continue is not in a loop"); - break; - } - gjmp(continpc); - 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 = stmtlabel(n)) != L) { - lab->breakpc = breakpc; - lab->continpc = continpc; - } - gen(n->nincr); // contin: incr - patch(p1, pc); // test: - thearch.bgen(n->ntest, 0, -1, breakpc); // if(!test) goto break - genlist(n->nbody); // body - gjmp(continpc); - patch(breakpc, pc); // done: - continpc = scontin; - breakpc = sbreak; - if(lab) { - lab->breakpc = P; - lab->continpc = P; - } - break; - - case OIF: - p1 = gjmp(P); // goto test - p2 = gjmp(P); // p2: goto else - patch(p1, pc); // test: - thearch.bgen(n->ntest, 0, -n->likely, 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 = stmtlabel(n)) != L) - lab->breakpc = breakpc; - - patch(p1, pc); // test: - genlist(n->nbody); // switch(test) body - patch(breakpc, pc); // done: - breakpc = sbreak; - if(lab != L) - lab->breakpc = P; - break; - - case OSELECT: - sbreak = breakpc; - p1 = gjmp(P); // goto test - breakpc = gjmp(P); // break: goto done - - // define break label - if((lab = stmtlabel(n)) != L) - lab->breakpc = breakpc; - - patch(p1, pc); // test: - genlist(n->nbody); // select() body - patch(breakpc, pc); // done: - breakpc = sbreak; - if(lab != L) - lab->breakpc = P; - 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: - thearch.cgen_callinter(n, N, 0); - break; - - case OCALLFUNC: - thearch.cgen_call(n, 0); - break; - - case OPROC: - cgen_proc(n, 1); - break; - - case ODEFER: - cgen_proc(n, 2); - break; - - case ORETURN: - case ORETJMP: - thearch.cgen_ret(n); - break; - - case OCHECKNIL: - cgen_checknil(n->left); - break; - - case OVARKILL: - gvarkill(n->left); - break; - } - -ret: - if(thearch.anyregalloc() != wasregalloc) { - dump("node", n); - fatal("registers left allocated"); - } - - 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 n2; - Node *l; - - // generate a rewrite in n2 for the method call - // (p.f)(...) goes to (f)(p,...) - - l = n->left; - if(l->op != ODOTMETH) - fatal("cgen_callmeth: not dotmethod: %N"); - - n2 = *n; - n2.op = OCALLFUNC; - n2.left = l->right; - n2.left->type = l->type; - - if(n2.left->op == ONAME) - n2.left->class = PFUNC; - thearch.cgen_call(&n2, proc); -} - -/* - * generate code to start new proc running call n. - */ -static 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: - thearch.cgen_callinter(n->left, N, proc); - break; - - case OCALLFUNC: - thearch.cgen_call(n->left, proc); - break; - } - -} - -/* - * generate declaration. - * have to allocate heap copy - * for escaped variables. - */ -static 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; - if(compiling_runtime) - yyerror("%N escapes to heap, not allowed in runtime.", n); - if(n->alloc == nil) - n->alloc = callnew(n->type); - cgen_as(n->heapaddr, n->alloc); -} - -/* - * generate discard of value - */ -static 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 && nr->class != PPARAMREF) - 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; - - case OIND: - cgen_checknil(nr->left); - break; - - // special enough to just evaluate - default: - tempname(&tmp, nr->type); - cgen_as(&tmp, nr); - gused(&tmp); - } -} - -/* - * clearslim generates code to zero a slim node. - */ -void -clearslim(Node *n) -{ - Node z; - Mpflt zero; - - memset(&z, 0, sizeof(z)); - z.op = OLITERAL; - z.type = n->type; - z.addable = 1; - - switch(simtype[n->type->etype]) { - case TCOMPLEX64: - case TCOMPLEX128: - z.val.u.cval = mal(sizeof(*z.val.u.cval)); - mpmovecflt(&z.val.u.cval->real, 0.0); - mpmovecflt(&z.val.u.cval->imag, 0.0); - break; - - case TFLOAT32: - case TFLOAT64: - mpmovecflt(&zero, 0.0); - z.val.ctype = CTFLT; - z.val.u.fval = &zero; - break; - - case TPTR32: - case TPTR64: - case TCHAN: - case TMAP: - z.val.ctype = CTNIL; - break; - - case TBOOL: - z.val.ctype = CTBOOL; - break; - - case TINT8: - case TINT16: - case TINT32: - case TINT64: - case TUINT8: - case TUINT16: - case TUINT32: - case TUINT64: - z.val.ctype = CTINT; - z.val.u.xval = mal(sizeof(*z.val.u.xval)); - mpmovecfix(z.val.u.xval, 0); - break; - - default: - fatal("clearslim called on type %T", n->type); - } - - ullmancalc(&z); - thearch.cgen(&z, n); -} - -/* - * generate assignment: - * nl = nr - * nr == N means zero nl. - */ -void -cgen_as(Node *nl, Node *nr) -{ - Type *tl; - - if(debug['g']) { - dump("cgen_as", nl); - dump("cgen_as = ", nr); - } - - while(nr != N && nr->op == OCONVNOP) - nr = nr->left; - - if(nl == N || isblank(nl)) { - cgen_discard(nr); - return; - } - - if(nr == N || iszero(nr)) { - // heaps should already be clear - if(nr == N && (nl->class & PHEAP)) - return; - - tl = nl->type; - if(tl == T) - return; - if(isfat(tl)) { - if(nl->op == ONAME) - gvardef(nl); - thearch.clearfat(nl); - return; - } - clearslim(nl); - return; - } - - tl = nl->type; - if(tl == T) - return; - - thearch.cgen(nr, nl); -} - -/* - * generate: - * res = iface{typ, data} - * n->left is typ - * n->right is data - */ -void -cgen_eface(Node *n, Node *res) -{ - /* - * the right node of an eface may contain function calls that uses res as an argument, - * so it's important that it is done first - */ - Node dst; - Node *tmp; - - tmp = temp(types[tptr]); - thearch.cgen(n->right, tmp); - - gvardef(res); - - dst = *res; - dst.type = types[tptr]; - dst.xoffset += widthptr; - thearch.cgen(tmp, &dst); - - dst.xoffset -= widthptr; - thearch.cgen(n->left, &dst); -} - -/* - * generate: - * res = s[lo, hi]; - * n->left is s - * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)]) - * caller (cgen) guarantees res is an addable ONAME. - * - * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR. - */ -void -cgen_slice(Node *n, Node *res) -{ - Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con; - Prog *p1, *p2; - - cap = n->list->n; - len = n->list->next->n; - offs = N; - if(n->list->next->next) - offs = n->list->next->next->n; - - // evaluate base pointer first, because it is the only - // possibly complex expression. once that is evaluated - // and stored, updating the len and cap can be done - // without making any calls, so without doing anything that - // might cause preemption or garbage collection. - // this makes the whole slice update atomic as far as the - // garbage collector can see. - - base = temp(types[TUINTPTR]); - tmplen = temp(types[TINT]); - if(n->op != OSLICESTR) - tmpcap = temp(types[TINT]); - else - tmpcap = tmplen; - - if(isnil(n->left)) { - tempname(&src, n->left->type); - thearch.cgen(n->left, &src); - } else - src = *n->left; - if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR) - src.xoffset += Array_array; - - if(n->op == OSLICEARR || n->op == OSLICE3ARR) { - if(!isptr[n->left->type->etype]) - fatal("slicearr is supposed to work on pointer: %+N\n", n); - thearch.cgen(&src, base); - cgen_checknil(base); - } else { - src.type = types[tptr]; - thearch.cgen(&src, base); - } - - // committed to the update - gvardef(res); - - // compute len and cap. - // len = n-i, cap = m-i, and offs = i*width. - // computing offs last lets the multiply overwrite i. - thearch.cgen(len, tmplen); - if(n->op != OSLICESTR) - thearch.cgen(cap, tmpcap); - - // if new cap != 0 { base += add } - // This avoids advancing base past the end of the underlying array/string, - // so that it cannot point at the next object in memory. - // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero. - // In essence we are replacing x[i:j:k] where i == j == k - // or x[i:j] where i == j == cap(x) with x[0:0:0]. - if(offs != N) { - p1 = gjmp(P); - p2 = gjmp(P); - patch(p1, pc); - - nodconst(&con, tmpcap->type, 0); - cmp = nod(OEQ, tmpcap, &con); - typecheck(&cmp, Erv); - thearch.bgen(cmp, 1, -1, p2); - - add = nod(OADD, base, offs); - typecheck(&add, Erv); - thearch.cgen(add, base); - - patch(p2, pc); - } - - // dst.array = src.array [ + lo *width ] - dst = *res; - dst.xoffset += Array_array; - dst.type = types[tptr]; - thearch.cgen(base, &dst); - - // dst.len = hi [ - lo ] - dst = *res; - dst.xoffset += Array_nel; - dst.type = types[simtype[TUINT]]; - thearch.cgen(tmplen, &dst); - - if(n->op != OSLICESTR) { - // dst.cap = cap [ - lo ] - dst = *res; - dst.xoffset += Array_cap; - dst.type = types[simtype[TUINT]]; - thearch.cgen(tmpcap, &dst); - } -} - -/* - * gather series of offsets - * >=0 is direct addressed field - * <0 is pointer to next field (+1) - */ -int -dotoffset(Node *n, int64 *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 *nn, Type *t) -{ - Node *n; - Sym *s; - - if(curfn == N) - fatal("no curfn for tempname"); - - 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); - n = nod(ONAME, N, N); - n->sym = s; - s->def = n; - n->type = t; - n->class = PAUTO; - n->addable = 1; - n->ullman = 1; - n->esc = EscNever; - n->curfn = curfn; - curfn->dcl = list(curfn->dcl, n); - - dowidth(t); - n->xoffset = 0; - *nn = *n; -} - -Node* -temp(Type *t) -{ - Node *n; - - n = nod(OXXX, N, N); - tempname(n, t); - n->sym->def->used = 1; - return n->orig; -} |