diff options
Diffstat (limited to 'src/cmd/gc/closure.c')
-rw-r--r-- | src/cmd/gc/closure.c | 165 |
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 |