]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gc: introduce hidden closure functions earlier
authorRuss Cox <rsc@golang.org>
Mon, 4 Jun 2012 21:07:59 +0000 (17:07 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 4 Jun 2012 21:07:59 +0000 (17:07 -0400)
The original implementation of closures created the
underlying top-level function during walk, which is fairly
late in the compilation process and caused ordering-based
complications due to earlier stages that had to be repeated
any number of times.

Create the underlying function during typecheck, much
earlier, so that later stages can be run just once.

The result is a simpler compilation sequence.

R=ken2
CC=golang-dev
https://golang.org/cl/6279049

src/cmd/gc/closure.c
src/cmd/gc/esc.c
src/cmd/gc/fmt.c
src/cmd/gc/inl.c
src/cmd/gc/lex.c
src/cmd/gc/sinit.c

index fa44e40fae0ea118bd6e2ac8bb0d7ff3fac4c5b1..acf666c79e116b00ad836319c4becc40a8b3ea5d 100644 (file)
@@ -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,18 +122,19 @@ 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;
        static int closgen;
        char *p;
 
-       USED(init);
-
        /*
         * wrap body in external function
         * with extra closure parameters.
@@ -168,8 +171,9 @@ makeclosure(Node *func, NodeList **init, int nowrap)
 
        // create the function
        xfunc = nod(ODCLFUNC, N, N);
-       snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen);
+       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);
@@ -180,7 +184,13 @@ makeclosure(Node *func, NodeList **init, int nowrap)
        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;
 }
@@ -194,7 +204,7 @@ walkclosure(Node *func, NodeList **init)
 
        // no closure vars, don't bother wrapping
        if(func->cvars == nil)
-               return makeclosure(func, init, 1)->nname;
+               return func->closure->nname;
 
        /*
         * wrap body in external function
@@ -202,7 +212,7 @@ walkclosure(Node *func, NodeList **init)
         */
 
        // create the function
-       xfunc = makeclosure(func, init, 0);
+       xfunc = func->closure;
        xtype = xfunc->nname->ntype;
 
        // prepare call of sys.closure that turns external func into func literal value.
@@ -250,7 +260,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
index 8a265ce59a684a92dd1fbf79c596f219dc11779d..c76f8e0b35769ec7d88661f38beafc3858f915c9 100644 (file)
@@ -74,7 +74,7 @@ escapes(NodeList *all)
 
        // flow-analyze functions
        for(l=all; l; l=l->next)
-               if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
+               if(l->n->op == ODCLFUNC)
                        escfunc(l->n);
 
        // print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
@@ -102,7 +102,7 @@ escapes(NodeList *all)
 static void
 escfunc(Node *func)
 {
-       Node *savefn, *n;
+       Node *savefn;
        NodeList *ll;
        int saveld;
 
@@ -130,23 +130,6 @@ escfunc(Node *func)
                }
        }
 
-       // walk will take the address of cvar->closure later and assign it to cvar.
-       // linking a fake oaddr node directly to the closure handles the case
-       // of the closure itself leaking.  Following the flow of the value to th
-       // paramref is done in escflow, because if we did that here, it would look
-       // like the original is assigned out of its loop depth, whereas it's just
-       // assigned to something in an inner function.  A paramref itself is never
-       // moved to the heap, only its original.
-       for(ll=curfn->cvars; ll; ll=ll->next) {
-               if(ll->n->op == OXXX)  // see dcl.c:398
-                       continue;
-
-               n = nod(OADDR, ll->n->closure, N);
-               n->lineno = ll->n->lineno;
-               typecheck(&n, Erv);
-               escassign(curfn, n);
-       }
-
        escloopdepthlist(curfn->nbody);
        esclist(curfn->nbody);
        curfn = savefn;
@@ -217,6 +200,7 @@ esc(Node *n)
 {
        int lno;
        NodeList *ll, *lr;
+       Node *a;
 
        if(n == N)
                return;
@@ -226,19 +210,16 @@ esc(Node *n)
        if(n->op == OFOR || n->op == ORANGE)
                loopdepth++;
 
-       if(n->op == OCLOSURE) {
-               escfunc(n);
-       } else {
-               esc(n->left);
-               esc(n->right);
-               esc(n->ntest);
-               esc(n->nincr);
-               esclist(n->ninit);
-               esclist(n->nbody);
-               esclist(n->nelse);
-               esclist(n->list);
-               esclist(n->rlist);
-       }
+       esc(n->left);
+       esc(n->right);
+       esc(n->ntest);
+       esc(n->nincr);
+       esclist(n->ninit);
+       esclist(n->nbody);
+       esclist(n->nelse);
+       esclist(n->list);
+       esclist(n->rlist);
+
        if(n->op == OFOR || n->op == ORANGE)
                loopdepth--;
 
@@ -388,6 +369,16 @@ esc(Node *n)
                break;
        
        case OCLOSURE:
+               // Link addresses of captured variables to closure.
+               for(ll=n->cvars; ll; ll=ll->next) {
+                       if(ll->n->op == OXXX)  // unnamed out argument; see dcl.c:/^funcargs
+                               continue;
+                       a = nod(OADDR, ll->n->closure, N);
+                       a->lineno = ll->n->lineno;
+                       typecheck(&a, Erv);
+                       escassign(n, a);
+               }
+               // fallthrough
        case OADDR:
        case OMAKECHAN:
        case OMAKEMAP:
@@ -726,11 +717,9 @@ escwalk(int level, Node *dst, Node *src)
                        if(debug['m'])
                                warnl(src->lineno, "leaking param: %hN", src);
                }
-               // handle the missing flow ref <- orig
-               // a paramref is automagically dereferenced, and taking its
-               // address produces the address of the original, so all we have to do here
-               // is keep track of the value flow, so level is unchanged.
-               // alternatively, we could have substituted PPARAMREFs with their ->closure in esc/escassign/flow,
+
+               // Treat a PPARAMREF closure variable as equivalent to the
+               // original variable.
                if(src->class == PPARAMREF) {
                        if(leaks && debug['m'])
                                warnl(src->lineno, "leaking closure reference %hN", src);
index a54af9ec129ed97a42cec683b206614cd0f29279..2ac28846ac0ff18b51ccb4cbca26b453dd563425 100644 (file)
@@ -1154,7 +1154,9 @@ exprfmt(Fmt *f, Node *n, int prec)
        case OCLOSURE:
                if(fmtmode == FErr)
                        return fmtstrcpy(f, "func literal");
-               return fmtprint(f, "%T { %H }", n->type, n->nbody);
+               if(n->nbody)
+                       return fmtprint(f, "%T { %H }", n->type, n->nbody);
+               return fmtprint(f, "%T { %H }", n->type, n->closure->nbody);
 
        case OCOMPLIT:
                if(fmtmode == FErr)
index efce56057d55e55d8c513b150441aa3f83736743..45a71495d4dbccef9f592ddddafb744ac1931c9a 100644 (file)
@@ -366,8 +366,8 @@ inlnode(Node **np)
                }
 
        case OCLOSURE:
-               // TODO do them here instead of in lex.c phase 6b, so escape analysis
-               // can avoid more heapmoves.
+               // TODO do them here (or earlier) instead of in walkcallclosure,
+               // so escape analysis can avoid more heapmoves.
                return;
        }
 
index 4b108ffc9b06ca6f7c9b914da63a2e2541e3e2c7..16dfd7c92cac7753bd42a1dcd7017efa2f24cb9e 100644 (file)
@@ -187,7 +187,7 @@ int
 main(int argc, char *argv[])
 {
        int i, c;
-       NodeList *l, *batch;
+       NodeList *l;
        char *p;
 
 #ifdef SIGBUS  
@@ -335,6 +335,7 @@ main(int argc, char *argv[])
                frame(1);
 
        // Process top-level declarations in phases.
+
        // Phase 1: const, type, and names and types of funcs.
        //   This will gather all the information about types
        //   and methods but doesn't depend on any of it.
@@ -368,7 +369,7 @@ main(int argc, char *argv[])
                errorexit();
 
        // Phase 4: Inlining
-       if (debug['l'] > 1) {
+       if(debug['l'] > 1) {
                // Typecheck imported function bodies if debug['l'] > 1,
                // otherwise lazily when used or re-exported.
                for(l=importlist; l; l=l->next)
@@ -381,7 +382,7 @@ main(int argc, char *argv[])
                        errorexit();
        }
 
-       if (debug['l']) {
+       if(debug['l']) {
                // Find functions that can be inlined and clone them before walk expands them.
                for(l=xtop; l; l=l->next)
                        if(l->n->op == ODCLFUNC)
@@ -393,7 +394,7 @@ main(int argc, char *argv[])
                                inlcalls(l->n);
        }
 
-       // Phase 5: escape analysis.
+       // Phase 5: Escape analysis.
        if(!debug['N'])
                escapes(xtop);
 
@@ -405,21 +406,7 @@ main(int argc, char *argv[])
        if(nsavederrors+nerrors == 0)
                fninit(xtop);
 
-       // Phase 6b: Compile all closures.
-       // Can generate more closures, so run in batches.
-       while(closures) {
-               batch = closures;
-               closures = nil;
-               if(debug['l'])
-                       for(l=batch; l; l=l->next)
-                               inlcalls(l->n);
-               if(!debug['N'])
-                       escapes(batch);
-               for(l=batch; l; l=l->next)
-                       funccompile(l->n, 1);
-       }
-
-       // Phase 7: check external declarations.
+       // Phase 6: Check external declarations.
        for(l=externdcl; l; l=l->next)
                if(l->n->op == ONAME)
                        typecheck(&l->n, Erv);
index 335d9b2538d245a573fe1a973d2316c5915ccbf1..34e6ea1a5789582878a5c955102162f578a0a78f 100644 (file)
@@ -167,6 +167,9 @@ init2(Node *n, NodeList **out)
        init2list(n->rlist, out);
        init2list(n->nbody, out);
        init2list(n->nelse, out);
+       
+       if(n->op == OCLOSURE)
+               init2list(n->closure->nbody, out);
 }
 
 static void