summaryrefslogtreecommitdiff
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.c165
1 files changed, 93 insertions, 72 deletions
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
index fa44e40fa..4e029ef83 100644
--- a/src/cmd/gc/closure.c
+++ b/src/cmd/gc/closure.c
@@ -75,6 +75,8 @@ closurebody(NodeList *body)
return func;
}
+static Node* makeclosure(Node *func, int nowrap);
+
void
typecheckclosure(Node *func, int top)
{
@@ -85,12 +87,12 @@ typecheckclosure(Node *func, int top)
oldfn = curfn;
typecheck(&func->ntype, Etype);
func->type = func->ntype->type;
- if(curfn == nil) {
- xtop = list(xtop, func);
- return;
- }
-
- if(func->type != T) {
+
+ // 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;
typechecklist(func->nbody, Etop);
curfn = oldfn;
@@ -120,26 +122,43 @@ typecheckclosure(Node *func, int top)
func->enter = list(func->enter, v->heapaddr);
v->heapaddr = N;
}
+
+ // Create top-level function
+ xtop = list(xtop, makeclosure(func, func->cvars==nil || (top&Ecall)));
}
static Node*
-makeclosure(Node *func, NodeList **init, int nowrap)
+makeclosure(Node *func, int nowrap)
{
- Node *xtype, *v, *addr, *xfunc;
- NodeList *l;
+ Node *xtype, *v, *addr, *xfunc, *cv;
+ NodeList *l, *body;
static int closgen;
char *p;
-
- USED(init);
+ int offset;
/*
* wrap body in external function
- * with extra closure parameters.
+ * that begins by reading closure parameters.
*/
xtype = nod(OTFUNC, N, N);
+ xtype->list = func->list;
+ xtype->rlist = func->rlist;
- // each closure variable has a corresponding
- // address parameter.
+ // create the function
+ xfunc = nod(ODCLFUNC, N, N);
+ snprint(namebuf, sizeof namebuf, "funcĀ·%.3d", ++closgen);
+ xfunc->nname = newname(lookup(namebuf));
+ 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;
+
+ // declare variables holding addresses taken from closure
+ // and initialize in entry prologue.
+ body = nil;
+ offset = widthptr;
for(l=func->cvars; l; l=l->next) {
v = l->n;
if(v->op == 0)
@@ -149,38 +168,35 @@ makeclosure(Node *func, NodeList **init, int nowrap)
addr->sym = lookup(p);
free(p);
addr->ntype = nod(OIND, typenod(v->type), N);
- addr->class = PPARAM;
+ addr->class = PAUTO;
addr->addable = 1;
addr->ullman = 1;
-
+ addr->used = 1;
+ addr->curfn = xfunc;
+ xfunc->dcl = list(xfunc->dcl, addr);
v->heapaddr = addr;
-
- xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
+ cv = nod(OCLOSUREVAR, N, N);
+ cv->type = ptrto(v->type);
+ cv->xoffset = offset;
+ body = list(body, nod(OAS, addr, cv));
+ offset += widthptr;
}
+ typechecklist(body, Etop);
+ walkstmtlist(body);
+ xfunc->enter = body;
- // then a dummy arg where the closure's caller pc sits
- if (!nowrap)
- xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
-
- // then the function arguments
- xtype->list = concat(xtype->list, func->list);
- xtype->rlist = concat(xtype->rlist, func->rlist);
-
- // create the function
- xfunc = nod(ODCLFUNC, N, N);
- snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen);
- xfunc->nname = newname(lookup(namebuf));
- xfunc->nname->ntype = xtype;
- xfunc->nname->defn = xfunc;
- declare(xfunc->nname, PFUNC);
- xfunc->nname->funcdepth = func->funcdepth;
- xfunc->funcdepth = func->funcdepth;
xfunc->nbody = func->nbody;
- xfunc->dcl = func->dcl;
+ xfunc->dcl = concat(func->dcl, xfunc->dcl);
if(xfunc->nbody == nil)
fatal("empty body - won't generate any code");
typecheck(&xfunc, Etop);
- closures = list(closures, xfunc);
+
+ xfunc->closure = func;
+ func->closure = xfunc;
+
+ func->nbody = nil;
+ func->list = nil;
+ func->rlist = nil;
return xfunc;
}
@@ -188,51 +204,55 @@ makeclosure(Node *func, NodeList **init, int nowrap)
Node*
walkclosure(Node *func, NodeList **init)
{
+ Node *clos, *typ;
+ NodeList *l;
+ char buf[20];
int narg;
- Node *xtype, *xfunc, *call, *clos;
- NodeList *l, *in;
- // no closure vars, don't bother wrapping
+ // If no closure vars, don't bother wrapping.
if(func->cvars == nil)
- return makeclosure(func, init, 1)->nname;
+ 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; A0 *int; A1 *string}{funcĀ·001, &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.
- /*
- * wrap body in external function
- * with extra closure parameters.
- */
-
- // create the function
- xfunc = makeclosure(func, init, 0);
- xtype = xfunc->nname->ntype;
-
- // prepare call of sys.closure that turns external func into func literal value.
- clos = syslook("closure", 1);
- clos->type = T;
- clos->ntype = nod(OTFUNC, N, N);
- in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz
- in = list(in, nod(ODCLFIELD, N, xtype));
narg = 0;
+ typ = nod(OTSTRUCT, N, N);
+ typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
for(l=func->cvars; l; l=l->next) {
if(l->n->op == 0)
continue;
- narg++;
- in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype));
+ snprint(buf, sizeof buf, "A%d", narg++);
+ typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype));
}
- clos->ntype->list = in;
- clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
+
+ 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;
+ walkexpr(&clos, init);
- call = nod(OCALL, clos, N);
- if(narg*widthptr > 100)
- yyerror("closure needs too many variables; runtime will reject it");
- in = list1(nodintconst(narg*widthptr));
- in = list(in, xfunc->nname);
- in = concat(in, func->enter);
- call->list = in;
-
- typecheck(&call, Erv);
- walkexpr(&call, init);
- return call;
+ return clos;
}
// Special case for closures that get called in place.
@@ -242,6 +262,7 @@ walkclosure(Node *func, NodeList **init)
void
walkcallclosure(Node *n, NodeList **init)
{
+ USED(init);
if (n->op != OCALLFUNC || n->left->op != OCLOSURE) {
dump("walkcallclosure", n);
fatal("abuse of walkcallclosure");
@@ -250,7 +271,7 @@ walkcallclosure(Node *n, NodeList **init)
// New arg list for n. First the closure-args
// and then the original parameter list.
n->list = concat(n->left->enter, n->list);
- n->left = makeclosure(n->left, init, 1)->nname;
+ n->left = n->left->closure->nname;
dowidth(n->left->type);
n->type = getoutargx(n->left->type);
// for a single valued function, pull the field type out of the struct