aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/gc/closure.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/closure.c')
-rw-r--r--src/cmd/gc/closure.c659
1 files changed, 0 insertions, 659 deletions
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
deleted file mode 100644
index 35b6d4b1b4..0000000000
--- a/src/cmd/gc/closure.c
+++ /dev/null
@@ -1,659 +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.
-
-/*
- * function literals aka closures
- */
-
-#include <u.h>
-#include <libc.h>
-#include "go.h"
-
-void
-closurehdr(Node *ntype)
-{
- Node *n, *name, *a;
- NodeList *l;
-
- n = nod(OCLOSURE, N, N);
- n->ntype = ntype;
- n->funcdepth = funcdepth;
- n->outerfunc = curfn;
-
- funchdr(n);
-
- // steal ntype's argument names and
- // leave a fresh copy in their place.
- // references to these variables need to
- // refer to the variables in the external
- // function declared below; see walkclosure.
- n->list = ntype->list;
- n->rlist = ntype->rlist;
- ntype->list = nil;
- ntype->rlist = nil;
- for(l=n->list; l; l=l->next) {
- name = l->n->left;
- if(name)
- name = newname(name->sym);
- a = nod(ODCLFIELD, name, l->n->right);
- a->isddd = l->n->isddd;
- if(name)
- name->isddd = a->isddd;
- ntype->list = list(ntype->list, a);
- }
- for(l=n->rlist; l; l=l->next) {
- name = l->n->left;
- if(name)
- name = newname(name->sym);
- ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
- }
-}
-
-Node*
-closurebody(NodeList *body)
-{
- Node *func, *v;
- NodeList *l;
-
- if(body == nil)
- body = list1(nod(OEMPTY, N, N));
-
- func = curfn;
- func->nbody = body;
- func->endlineno = lineno;
- funcbody(func);
-
- // closure-specific variables are hanging off the
- // ordinary ones in the symbol table; see oldname.
- // unhook them.
- // make the list of pointers for the closure call.
- for(l=func->cvars; l; l=l->next) {
- v = l->n;
- v->closure->closure = v->outer;
- v->outerexpr = oldname(v->sym);
- }
-
- return func;
-}
-
-static Node* makeclosure(Node *func);
-
-void
-typecheckclosure(Node *func, int top)
-{
- Node *oldfn, *n;
- NodeList *l;
- int olddd;
-
- for(l=func->cvars; l; l=l->next) {
- n = l->n->closure;
- if(!n->captured) {
- n->captured = 1;
- if(n->decldepth == 0)
- fatal("typecheckclosure: var %hN does not have decldepth assigned", n);
- // Ignore assignments to the variable in straightline code
- // preceding the first capturing by a closure.
- if(n->decldepth == decldepth)
- n->assigned = 0;
- }
- }
-
- for(l=func->dcl; l; l=l->next)
- if(l->n->op == ONAME && (l->n->class == PPARAM || l->n->class == PPARAMOUT))
- l->n->decldepth = 1;
-
- oldfn = curfn;
- typecheck(&func->ntype, Etype);
- func->type = func->ntype->type;
- func->top = top;
-
- // Type check the body now, but only if we're inside a function.
- // At top level (in a variable initialization: curfn==nil) we're not
- // ready to type check code yet; we'll check it later, because the
- // underlying closure function we create is added to xtop.
- if(curfn && func->type != T) {
- curfn = func;
- olddd = decldepth;
- decldepth = 1;
- typechecklist(func->nbody, Etop);
- decldepth = olddd;
- curfn = oldfn;
- }
-
- // Create top-level function
- xtop = list(xtop, makeclosure(func));
-}
-
-// closurename returns name for OCLOSURE n.
-// It is not as simple as it ought to be, because we typecheck nested closures
-// starting from the innermost one. So when we check the inner closure,
-// we don't yet have name for the outer closure. This function uses recursion
-// to generate names all the way up if necessary.
-static Sym*
-closurename(Node *n)
-{
- static int closgen;
- char *outer, *prefix;
- int gen;
-
- if(n->sym != S)
- return n->sym;
- gen = 0;
- outer = nil;
- prefix = nil;
- if(n->outerfunc == N) {
- // Global closure.
- outer = "glob";
- prefix = "func";
- gen = ++closgen;
- } else if(n->outerfunc->op == ODCLFUNC) {
- // The outermost closure inside of a named function.
- outer = n->outerfunc->nname->sym->name;
- prefix = "func";
- // Yes, functions can be named _.
- // Can't use function closgen in such case,
- // because it would lead to name clashes.
- if(!isblank(n->outerfunc->nname))
- gen = ++n->outerfunc->closgen;
- else
- gen = ++closgen;
- } else if(n->outerfunc->op == OCLOSURE) {
- // Nested closure, recurse.
- outer = closurename(n->outerfunc)->name;
- prefix = "";
- gen = ++n->outerfunc->closgen;
- } else
- fatal("closurename called for %hN", n);
- snprint(namebuf, sizeof namebuf, "%s.%s%d", outer, prefix, gen);
- n->sym = lookup(namebuf);
- return n->sym;
-}
-
-static Node*
-makeclosure(Node *func)
-{
- Node *xtype, *xfunc;
-
- /*
- * wrap body in external function
- * that begins by reading closure parameters.
- */
- xtype = nod(OTFUNC, N, N);
- xtype->list = func->list;
- xtype->rlist = func->rlist;
-
- // create the function
- xfunc = nod(ODCLFUNC, N, N);
- xfunc->nname = newname(closurename(func));
- xfunc->nname->sym->flags |= SymExported; // disable export
- xfunc->nname->ntype = xtype;
- xfunc->nname->defn = xfunc;
- declare(xfunc->nname, PFUNC);
- xfunc->nname->funcdepth = func->funcdepth;
- xfunc->funcdepth = func->funcdepth;
- xfunc->endlineno = func->endlineno;
-
- xfunc->nbody = func->nbody;
- xfunc->dcl = concat(func->dcl, xfunc->dcl);
- if(xfunc->nbody == nil)
- fatal("empty body - won't generate any code");
- typecheck(&xfunc, Etop);
-
- xfunc->closure = func;
- func->closure = xfunc;
-
- func->nbody = nil;
- func->list = nil;
- func->rlist = nil;
-
- return xfunc;
-}
-
-// capturevars is called in a separate phase after all typechecking is done.
-// It decides whether each variable captured by a closure should be captured
-// by value or by reference.
-// We use value capturing for values <= 128 bytes that are never reassigned
-// after capturing (effectively constant).
-void
-capturevars(Node *xfunc)
-{
- Node *func, *v, *outer;
- NodeList *l;
- int lno;
-
- lno = lineno;
- lineno = xfunc->lineno;
-
- func = xfunc->closure;
- func->enter = nil;
- for(l=func->cvars; l; l=l->next) {
- v = l->n;
- if(v->type == T) {
- // if v->type is nil, it means v looked like it was
- // going to be used in the closure but wasn't.
- // this happens because when parsing a, b, c := f()
- // the a, b, c gets parsed as references to older
- // a, b, c before the parser figures out this is a
- // declaration.
- v->op = OXXX;
- continue;
- }
-
- // type check the & of closed variables outside the closure,
- // so that the outer frame also grabs them and knows they escape.
- dowidth(v->type);
- outer = v->outerexpr;
- v->outerexpr = N;
- // out parameters will be assigned to implicitly upon return.
- if(outer->class != PPARAMOUT && !v->closure->addrtaken && !v->closure->assigned && v->type->width <= 128)
- v->byval = 1;
- else {
- v->closure->addrtaken = 1;
- outer = nod(OADDR, outer, N);
- }
- if(debug['m'] > 1) {
- Sym *name;
- char *how;
- name = nil;
- if(v->curfn && v->curfn->nname)
- name = v->curfn->nname->sym;
- how = "ref";
- if(v->byval)
- how = "value";
- warnl(v->lineno, "%S capturing by %s: %S (addr=%d assign=%d width=%d)",
- name, how,
- v->sym, v->closure->addrtaken, v->closure->assigned, (int32)v->type->width);
- }
- typecheck(&outer, Erv);
- func->enter = list(func->enter, outer);
- }
-
- lineno = lno;
-}
-
-// transformclosure is called in a separate phase after escape analysis.
-// It transform closure bodies to properly reference captured variables.
-void
-transformclosure(Node *xfunc)
-{
- Node *func, *cv, *addr, *v, *f;
- NodeList *l, *body;
- Type **param, *fld;
- vlong offset;
- int lno, nvar;
-
- lno = lineno;
- lineno = xfunc->lineno;
- func = xfunc->closure;
-
- if(func->top&Ecall) {
- // If the closure is directly called, we transform it to a plain function call
- // with variables passed as args. This avoids allocation of a closure object.
- // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
- // will complete the transformation later.
- // For illustration, the following closure:
- // func(a int) {
- // println(byval)
- // byref++
- // }(42)
- // becomes:
- // func(a int, byval int, &byref *int) {
- // println(byval)
- // (*&byref)++
- // }(42, byval, &byref)
-
- // f is ONAME of the actual function.
- f = xfunc->nname;
- // Get pointer to input arguments and rewind to the end.
- // We are going to append captured variables to input args.
- param = &getinargx(f->type)->type;
- for(; *param; param = &(*param)->down) {
- }
- for(l=func->cvars; l; l=l->next) {
- v = l->n;
- if(v->op == OXXX)
- continue;
- fld = typ(TFIELD);
- fld->funarg = 1;
- if(v->byval) {
- // If v is captured by value, we merely downgrade it to PPARAM.
- v->class = PPARAM;
- v->ullman = 1;
- fld->nname = v;
- } else {
- // If v of type T is captured by reference,
- // we introduce function param &v *T
- // and v remains PPARAMREF with &v heapaddr
- // (accesses will implicitly deref &v).
- snprint(namebuf, sizeof namebuf, "&%s", v->sym->name);
- addr = newname(lookup(namebuf));
- addr->type = ptrto(v->type);
- addr->class = PPARAM;
- v->heapaddr = addr;
- fld->nname = addr;
- }
- fld->type = fld->nname->type;
- fld->sym = fld->nname->sym;
- // Declare the new param and append it to input arguments.
- xfunc->dcl = list(xfunc->dcl, fld->nname);
- *param = fld;
- param = &fld->down;
- }
- // Recalculate param offsets.
- if(f->type->width > 0)
- fatal("transformclosure: width is already calculated");
- dowidth(f->type);
- xfunc->type = f->type; // update type of ODCLFUNC
- } else {
- // The closure is not called, so it is going to stay as closure.
- nvar = 0;
- body = nil;
- offset = widthptr;
- for(l=func->cvars; l; l=l->next) {
- v = l->n;
- if(v->op == OXXX)
- continue;
- nvar++;
- // cv refers to the field inside of closure OSTRUCTLIT.
- cv = nod(OCLOSUREVAR, N, N);
- cv->type = v->type;
- if(!v->byval)
- cv->type = ptrto(v->type);
- offset = rnd(offset, cv->type->align);
- cv->xoffset = offset;
- offset += cv->type->width;
-
- if(v->byval && v->type->width <= 2*widthptr && thearch.thechar == '6') {
- // If it is a small variable captured by value, downgrade it to PAUTO.
- // This optimization is currently enabled only for amd64, see:
- // https://github.com/golang/go/issues/9865
- v->class = PAUTO;
- v->ullman = 1;
- xfunc->dcl = list(xfunc->dcl, v);
- body = list(body, nod(OAS, v, cv));
- } else {
- // Declare variable holding addresses taken from closure
- // and initialize in entry prologue.
- snprint(namebuf, sizeof namebuf, "&%s", v->sym->name);
- addr = newname(lookup(namebuf));
- addr->ntype = nod(OIND, typenod(v->type), N);
- addr->class = PAUTO;
- addr->used = 1;
- addr->curfn = xfunc;
- xfunc->dcl = list(xfunc->dcl, addr);
- v->heapaddr = addr;
- if(v->byval)
- cv = nod(OADDR, cv, N);
- body = list(body, nod(OAS, addr, cv));
- }
- }
- typechecklist(body, Etop);
- walkstmtlist(body);
- xfunc->enter = body;
- xfunc->needctxt = nvar > 0;
- }
-
- lineno = lno;
-}
-
-Node*
-walkclosure(Node *func, NodeList **init)
-{
- Node *clos, *typ, *typ1, *v;
- NodeList *l;
-
- // If no closure vars, don't bother wrapping.
- if(func->cvars == nil)
- return func->closure->nname;
-
- // Create closure in the form of a composite literal.
- // supposing the closure captures an int i and a string s
- // and has one float64 argument and no results,
- // the generated code looks like:
- //
- // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
- //
- // The use of the struct provides type information to the garbage
- // collector so that it can walk the closure. We could use (in this case)
- // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
- // The information appears in the binary in the form of type descriptors;
- // the struct is unnamed so that closures in multiple packages with the
- // same struct type can share the descriptor.
-
- typ = nod(OTSTRUCT, N, N);
- typ->list = list1(nod(ODCLFIELD, newname(lookup(".F")), typenod(types[TUINTPTR])));
- for(l=func->cvars; l; l=l->next) {
- v = l->n;
- if(v->op == OXXX)
- continue;
- typ1 = typenod(v->type);
- if(!v->byval)
- typ1 = nod(OIND, typ1, N);
- typ->list = list(typ->list, nod(ODCLFIELD, newname(v->sym), typ1));
- }
-
- clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
- clos->esc = func->esc;
- clos->right->implicit = 1;
- clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter);
-
- // Force type conversion from *struct to the func type.
- clos = nod(OCONVNOP, clos, N);
- clos->type = func->type;
-
- typecheck(&clos, Erv);
- // typecheck will insert a PTRLIT node under CONVNOP,
- // tag it with escape analysis result.
- clos->left->esc = func->esc;
- // non-escaping temp to use, if any.
- // orderexpr did not compute the type; fill it in now.
- if(func->alloc != N) {
- func->alloc->type = clos->left->left->type;
- func->alloc->orig->type = func->alloc->type;
- clos->left->right = func->alloc;
- func->alloc = N;
- }
- walkexpr(&clos, init);
-
- return clos;
-}
-
-static Node *makepartialcall(Node*, Type*, Node*);
-
-void
-typecheckpartialcall(Node *fn, Node *sym)
-{
- switch(fn->op) {
- case ODOTINTER:
- case ODOTMETH:
- break;
- default:
- fatal("invalid typecheckpartialcall");
- }
-
- // Create top-level function.
- fn->nname = makepartialcall(fn, fn->type, sym);
- fn->right = sym;
- fn->op = OCALLPART;
- fn->type = fn->nname->type;
-}
-
-static Node*
-makepartialcall(Node *fn, Type *t0, Node *meth)
-{
- Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv, *savecurfn;
- Type *rcvrtype, *basetype, *t;
- NodeList *body, *l, *callargs, *retargs;
- char *p;
- Sym *sym;
- Pkg *spkg;
- static Pkg* gopkg;
- int i, ddd;
-
- rcvrtype = fn->left->type;
- if(exportname(meth->sym->name))
- p = smprint("(%-hT).%s-fm", rcvrtype, meth->sym->name);
- else
- p = smprint("(%-hT).(%-S)-fm", rcvrtype, meth->sym);
- basetype = rcvrtype;
- if(isptr[rcvrtype->etype])
- basetype = basetype->type;
- if(basetype->etype != TINTER && basetype->sym == S)
- fatal("missing base type for %T", rcvrtype);
-
- spkg = nil;
- if(basetype->sym != S)
- spkg = basetype->sym->pkg;
- if(spkg == nil) {
- if(gopkg == nil)
- gopkg = mkpkg(newstrlit("go"));
- spkg = gopkg;
- }
- sym = pkglookup(p, spkg);
- free(p);
- if(sym->flags & SymUniq)
- return sym->def;
- sym->flags |= SymUniq;
-
- savecurfn = curfn;
- curfn = N;
-
- xtype = nod(OTFUNC, N, N);
- i = 0;
- l = nil;
- callargs = nil;
- ddd = 0;
- xfunc = nod(ODCLFUNC, N, N);
- curfn = xfunc;
- for(t = getinargx(t0)->type; t; t = t->down) {
- snprint(namebuf, sizeof namebuf, "a%d", i++);
- n = newname(lookup(namebuf));
- n->class = PPARAM;
- xfunc->dcl = list(xfunc->dcl, n);
- callargs = list(callargs, n);
- fld = nod(ODCLFIELD, n, typenod(t->type));
- if(t->isddd) {
- fld->isddd = 1;
- ddd = 1;
- }
- l = list(l, fld);
- }
- xtype->list = l;
- i = 0;
- l = nil;
- retargs = nil;
- for(t = getoutargx(t0)->type; t; t = t->down) {
- snprint(namebuf, sizeof namebuf, "r%d", i++);
- n = newname(lookup(namebuf));
- n->class = PPARAMOUT;
- xfunc->dcl = list(xfunc->dcl, n);
- retargs = list(retargs, n);
- l = list(l, nod(ODCLFIELD, n, typenod(t->type)));
- }
- xtype->rlist = l;
-
- xfunc->dupok = 1;
- xfunc->nname = newname(sym);
- xfunc->nname->sym->flags |= SymExported; // disable export
- xfunc->nname->ntype = xtype;
- xfunc->nname->defn = xfunc;
- declare(xfunc->nname, PFUNC);
-
- // Declare and initialize variable holding receiver.
- body = nil;
- xfunc->needctxt = 1;
- cv = nod(OCLOSUREVAR, N, N);
- cv->xoffset = widthptr;
- cv->type = rcvrtype;
- if(cv->type->align > widthptr)
- cv->xoffset = cv->type->align;
- ptr = nod(ONAME, N, N);
- ptr->sym = lookup("rcvr");
- ptr->class = PAUTO;
- ptr->addable = 1;
- ptr->ullman = 1;
- ptr->used = 1;
- ptr->curfn = xfunc;
- xfunc->dcl = list(xfunc->dcl, ptr);
- if(isptr[rcvrtype->etype] || isinter(rcvrtype)) {
- ptr->ntype = typenod(rcvrtype);
- body = list(body, nod(OAS, ptr, cv));
- } else {
- ptr->ntype = typenod(ptrto(rcvrtype));
- body = list(body, nod(OAS, ptr, nod(OADDR, cv, N)));
- }
-
- call = nod(OCALL, nod(OXDOT, ptr, meth), N);
- call->list = callargs;
- call->isddd = ddd;
- if(t0->outtuple == 0) {
- body = list(body, call);
- } else {
- n = nod(OAS2, N, N);
- n->list = retargs;
- n->rlist = list1(call);
- body = list(body, n);
- n = nod(ORETURN, N, N);
- body = list(body, n);
- }
-
- xfunc->nbody = body;
-
- typecheck(&xfunc, Etop);
- sym->def = xfunc;
- xtop = list(xtop, xfunc);
- curfn = savecurfn;
-
- return xfunc;
-}
-
-Node*
-walkpartialcall(Node *n, NodeList **init)
-{
- Node *clos, *typ;
-
- // Create closure in the form of a composite literal.
- // For x.M with receiver (x) type T, the generated code looks like:
- //
- // clos = &struct{F uintptr; R T}{M.T·f, x}
- //
- // Like walkclosure above.
-
- if(isinter(n->left->type)) {
- // Trigger panic for method on nil interface now.
- // Otherwise it happens in the wrapper and is confusing.
- n->left = cheapexpr(n->left, init);
- checknil(n->left, init);
- }
-
- typ = nod(OTSTRUCT, N, N);
- typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
- typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup("R")), typenod(n->left->type)));
-
- clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
- clos->esc = n->esc;
- clos->right->implicit = 1;
- clos->list = list1(nod(OCFUNC, n->nname->nname, N));
- clos->list = list(clos->list, n->left);
-
- // Force type conversion from *struct to the func type.
- clos = nod(OCONVNOP, clos, N);
- clos->type = n->type;
-
- typecheck(&clos, Erv);
- // typecheck will insert a PTRLIT node under CONVNOP,
- // tag it with escape analysis result.
- clos->left->esc = n->esc;
- // non-escaping temp to use, if any.
- // orderexpr did not compute the type; fill it in now.
- if(n->alloc != N) {
- n->alloc->type = clos->left->left->type;
- n->alloc->orig->type = n->alloc->type;
- clos->left->right = n->alloc;
- n->alloc = N;
- }
- walkexpr(&clos, init);
-
- return clos;
-}