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.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
new file mode 100644
index 000000000..1261eefb7
--- /dev/null
+++ b/src/cmd/gc/closure.c
@@ -0,0 +1,252 @@
+// 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 "go.h"
+
+void
+closurehdr(Node *ntype)
+{
+ Node *n, *name, *a;
+ NodeList *l;
+
+ n = nod(OCLOSURE, N, N);
+ n->ntype = ntype;
+ n->funcdepth = funcdepth;
+
+ 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;
+ l = func->dcl;
+ func->nbody = body;
+ 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->heapaddr = nod(OADDR, oldname(v->sym), N);
+ }
+
+ return func;
+}
+
+void
+typecheckclosure(Node *func, int top)
+{
+ Node *oldfn;
+ NodeList *l;
+ Node *v;
+
+ oldfn = curfn;
+ typecheck(&func->ntype, Etype);
+ func->type = func->ntype->type;
+ if(curfn == nil) {
+ xtop = list(xtop, func);
+ return;
+ }
+
+ if(func->type != T) {
+ curfn = func;
+ typechecklist(func->nbody, Etop);
+ curfn = oldfn;
+ }
+
+ // type check the & of closed variables outside the closure,
+ // so that the outer frame also grabs them and knows they
+ // escape.
+ 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 = 0;
+ continue;
+ }
+ // For a closure that is called in place, but not
+ // inside a go statement, avoid moving variables to the heap.
+ if ((top & (Ecall|Eproc)) == Ecall)
+ v->heapaddr->etype = 1;
+ typecheck(&v->heapaddr, Erv);
+ func->enter = list(func->enter, v->heapaddr);
+ v->heapaddr = N;
+ }
+}
+
+static Node*
+makeclosure(Node *func, NodeList **init, int nowrap)
+{
+ Node *xtype, *v, *addr, *xfunc;
+ NodeList *l;
+ static int closgen;
+ char *p;
+
+ /*
+ * wrap body in external function
+ * with extra closure parameters.
+ */
+ xtype = nod(OTFUNC, N, N);
+
+ // each closure variable has a corresponding
+ // address parameter.
+ for(l=func->cvars; l; l=l->next) {
+ v = l->n;
+ if(v->op == 0)
+ continue;
+ addr = nod(ONAME, N, N);
+ p = smprint("&%s", v->sym->name);
+ addr->sym = lookup(p);
+ free(p);
+ addr->ntype = nod(OIND, typenod(v->type), N);
+ addr->class = PPARAM;
+ addr->addable = 1;
+ addr->ullman = 1;
+
+ v->heapaddr = addr;
+
+ xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
+ }
+
+ // 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;
+ if(xfunc->nbody == nil)
+ fatal("empty body - won't generate any code");
+ typecheck(&xfunc, Etop);
+ closures = list(closures, xfunc);
+
+ return xfunc;
+}
+
+Node*
+walkclosure(Node *func, NodeList **init)
+{
+ int narg;
+ Node *xtype, *xfunc, *call, *clos;
+ NodeList *l, *in;
+
+ /*
+ * 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;
+ 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));
+ }
+ clos->ntype->list = in;
+ clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
+ typecheck(&clos, Erv);
+
+ 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;
+}
+
+// Special case for closures that get called in place.
+// Optimize runtime.closure(X, __func__xxxx_, .... ) away
+// to __func__xxxx_(Y ....).
+// On entry, expect n->op == OCALL, n->left->op == OCLOSURE.
+void
+walkcallclosure(Node *n, NodeList **init)
+{
+ if (n->op != OCALLFUNC || n->left->op != OCLOSURE) {
+ dump("walkcallclosure", n);
+ fatal("abuse of walkcallclosure");
+ }
+
+ // 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;
+ dowidth(n->left->type);
+ n->type = getoutargx(n->left->type);
+ // for a single valued function, pull the field type out of the struct
+ if (n->type && n->type->type && !n->type->type->down)
+ n->type = n->type->type->type;
+}