diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index 6f902560903..fd728fd7bfe 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -11,61 +11,39 @@ import ( ) func (p *noder) funcLit(expr *syntax.FuncLit) *Node { + xtype := p.typeExpr(expr.Type) ntype := p.typeExpr(expr.Type) - n := p.nod(expr, OCLOSURE, nil, nil) - n.Func.SetIsHiddenClosure(Curfn != nil) - n.Func.Ntype = ntype - n.Func.Outerfunc = Curfn + xfunc := p.nod(expr, ODCLFUNC, nil, nil) + xfunc.Func.SetIsHiddenClosure(Curfn != nil) + xfunc.Func.Nname = newfuncname(nblank.Sym) // filled in by typecheckclosure + xfunc.Func.Nname.Name.Param.Ntype = xtype + xfunc.Func.Nname.Name.Defn = xfunc - old := p.funchdr(n) + clo := p.nod(expr, OCLOSURE, nil, nil) + clo.Func.Ntype = ntype - // 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.Set(ntype.List.Slice()) - n.Rlist.Set(ntype.Rlist.Slice()) + xfunc.Func.Closure = clo + clo.Func.Closure = xfunc - ntype.List.Set(nil) - ntype.Rlist.Set(nil) - for _, n1 := range n.List.Slice() { - name := n1.Left - if name != nil { - name = newname(name.Sym) - } - a := nod(ODCLFIELD, name, n1.Right) - a.SetIsddd(n1.Isddd()) - if name != nil { - name.SetIsddd(a.Isddd()) - } - ntype.List.Append(a) - } - for _, n2 := range n.Rlist.Slice() { - name := n2.Left - if name != nil { - name = newname(name.Sym) - } - ntype.Rlist.Append(nod(ODCLFIELD, name, n2.Right)) - } + oldScope := p.funchdr(xfunc) body := p.stmts(expr.Body.List) - - lineno = p.makeXPos(expr.Body.Rbrace) - if len(body) == 0 { + if body == nil { body = []*Node{nod(OEMPTY, nil, nil)} } + xfunc.Nbody.Set(body) - n.Nbody.Set(body) - n.Func.Endlineno = lineno - p.funcbody(old) + lineno = p.makeXPos(expr.Body.Rbrace) + xfunc.Func.Endlineno = lineno + + p.funcbody(oldScope) // 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 _, v := range n.Func.Cvars.Slice() { + for _, v := range xfunc.Func.Cvars.Slice() { // Unlink from v1; see comment in syntax.go type Param for these fields. v1 := v.Name.Defn v1.Name.Param.Innermost = v.Name.Param.Outer @@ -101,11 +79,13 @@ func (p *noder) funcLit(expr *syntax.FuncLit) *Node { v.Name.Param.Outer = oldname(v.Sym) } - return n + return clo } -func typecheckclosure(func_ *Node, top int) { - for _, ln := range func_.Func.Cvars.Slice() { +func typecheckclosure(clo *Node, top int) { + xfunc := clo.Func.Closure + + for _, ln := range xfunc.Func.Cvars.Slice() { n := ln.Name.Defn if !n.Name.Captured() { n.Name.SetCaptured(true) @@ -121,129 +101,59 @@ func typecheckclosure(func_ *Node, top int) { } } - for _, ln := range func_.Func.Dcl { - if ln.Op == ONAME && (ln.Class() == PPARAM || ln.Class() == PPARAMOUT) { - ln.Name.Decldepth = 1 - } - } + xfunc.Func.Nname.Sym = closurename(Curfn) + xfunc.Func.Nname.Sym.SetExported(true) // disable export + declare(xfunc.Func.Nname, PFUNC) + xfunc = typecheck(xfunc, Etop) - oldfn := Curfn - func_.Func.Ntype = typecheck(func_.Func.Ntype, Etype) - func_.Type = func_.Func.Ntype.Type - func_.Func.Top = top + clo.Func.Ntype = typecheck(clo.Func.Ntype, Etype) + clo.Type = clo.Func.Ntype.Type + clo.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 != nil && func_.Type != nil { - Curfn = func_ + if Curfn != nil && clo.Type != nil { + oldfn := Curfn + Curfn = xfunc olddd := decldepth decldepth = 1 - typecheckslice(func_.Nbody.Slice(), Etop) + typecheckslice(xfunc.Nbody.Slice(), Etop) decldepth = olddd Curfn = oldfn } - // Create top-level function - xtop = append(xtop, makeclosure(func_)) + xtop = append(xtop, xfunc) } -// 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. +// globClosgen is like Func.Closgen, but for the global scope. +var globClosgen int -var closurename_closgen int +// closurename generates a new unique name for a closure within +// outerfunc. +func closurename(outerfunc *Node) *types.Sym { + outer := "glob." + prefix := "func" + gen := &globClosgen -func closurename(n *Node) *types.Sym { - if n.Sym != nil { - return n.Sym - } - gen := 0 - outer := "" - prefix := "" - switch { - case n.Func.Outerfunc == nil: - // Global closure. - outer = "glob." - - prefix = "func" - closurename_closgen++ - gen = closurename_closgen - case n.Func.Outerfunc.Op == ODCLFUNC: - // The outermost closure inside of a named function. - outer = n.Func.Outerfunc.funcname() - - 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.Func.Outerfunc.Func.Nname) { - n.Func.Outerfunc.Func.Closgen++ - gen = n.Func.Outerfunc.Func.Closgen - } else { - closurename_closgen++ - gen = closurename_closgen + if outerfunc != nil { + if outerfunc.Func.Closure != nil { + prefix = "" } - case n.Func.Outerfunc.Op == OCLOSURE: - // Nested closure, recurse. - outer = closurename(n.Func.Outerfunc).Name - prefix = "" - n.Func.Outerfunc.Func.Closgen++ - gen = n.Func.Outerfunc.Func.Closgen - default: - Fatalf("closurename called for %S", n) - } - n.Sym = lookup(fmt.Sprintf("%s.%s%d", outer, prefix, gen)) - return n.Sym -} + outer = outerfunc.funcname() -func makeclosure(func_ *Node) *Node { - // wrap body in external function - // that begins by reading closure parameters. - xtype := nod(OTFUNC, nil, nil) - - xtype.List.Set(func_.List.Slice()) - xtype.Rlist.Set(func_.Rlist.Slice()) - - // create the function - xfunc := nod(ODCLFUNC, nil, nil) - xfunc.Func.SetIsHiddenClosure(Curfn != nil) - - xfunc.Func.Nname = newfuncname(closurename(func_)) - xfunc.Func.Nname.Sym.SetExported(true) // disable export - xfunc.Func.Nname.Name.Param.Ntype = xtype - xfunc.Func.Nname.Name.Defn = xfunc - declare(xfunc.Func.Nname, PFUNC) - xfunc.Func.Endlineno = func_.Func.Endlineno - if Ctxt.Flag_dynlink { - makefuncsym(xfunc.Func.Nname.Sym) + // There may be multiple functions named "_". In those + // cases, we can't use their individual Closgens as it + // would lead to name clashes. + if !isblank(outerfunc.Func.Nname) { + gen = &outerfunc.Func.Closgen + } } - xfunc.Nbody.Set(func_.Nbody.Slice()) - xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...) - xfunc.Func.Parents = func_.Func.Parents - xfunc.Func.Marks = func_.Func.Marks - func_.Func.Dcl = nil - func_.Func.Parents = nil - func_.Func.Marks = nil - if xfunc.Nbody.Len() == 0 { - Fatalf("empty body - won't generate any code") - } - xfunc = typecheck(xfunc, Etop) - - xfunc.Func.Closure = func_ - func_.Func.Closure = xfunc - - func_.Nbody.Set(nil) - func_.List.Set(nil) - func_.Rlist.Set(nil) - - return xfunc + *gen++ + return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) } // capturevarscomplete is set to true when the capturevars phase is done. @@ -258,20 +168,20 @@ func capturevars(xfunc *Node) { lno := lineno lineno = xfunc.Pos - func_ := xfunc.Func.Closure - func_.Func.Enter.Set(nil) - for _, v := range func_.Func.Cvars.Slice() { + clo := xfunc.Func.Closure + cvars := xfunc.Func.Cvars.Slice() + out := cvars[:0] + for _, v := range cvars { if v.Type == nil { - // 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 - + // If v.Type is nil, it means v looked like it + // was going to be used in the closure, but + // isn't. This happens in struct literals like + // s{f: x} where we can't distinguish whether + // f is a field identifier or expression until + // resolving s. continue } + out = append(out, v) // type check the & of closed variables outside the closure, // so that the outer frame also grabs them and knows they escape. @@ -301,9 +211,10 @@ func capturevars(xfunc *Node) { } outer = typecheck(outer, Erv) - func_.Func.Enter.Append(outer) + clo.Func.Enter.Append(outer) } + xfunc.Func.Cvars.Set(out) lineno = lno } @@ -312,9 +223,9 @@ func capturevars(xfunc *Node) { func transformclosure(xfunc *Node) { lno := lineno lineno = xfunc.Pos - func_ := xfunc.Func.Closure + clo := xfunc.Func.Closure - if func_.Func.Top&Ecall != 0 { + if clo.Func.Top&Ecall != 0 { // 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) @@ -336,33 +247,27 @@ func transformclosure(xfunc *Node) { // We are going to insert captured variables before input args. var params []*types.Field var decls []*Node - for _, v := range func_.Func.Cvars.Slice() { - if v.Op == OXXX { - continue - } - fld := types.NewField() - fld.Funarg = types.FunargParams - if v.Name.Byval() { - // If v is captured by value, we merely downgrade it to PPARAM. - v.SetClass(PPARAM) - fld.Nname = asTypesNode(v) - } else { + for _, v := range xfunc.Func.Cvars.Slice() { + if !v.Name.Byval() { // If v of type T is captured by reference, // we introduce function param &v *T // and v remains PAUTOHEAP with &v heapaddr // (accesses will implicitly deref &v). addr := newname(lookup("&" + v.Sym.Name)) addr.Type = types.NewPtr(v.Type) - addr.SetClass(PPARAM) v.Name.Param.Heapaddr = addr - fld.Nname = asTypesNode(addr) + v = addr } - fld.Type = asNode(fld.Nname).Type - fld.Sym = asNode(fld.Nname).Sym + v.SetClass(PPARAM) + decls = append(decls, v) + fld := types.NewField() + fld.Funarg = types.FunargParams + fld.Nname = asTypesNode(v) + fld.Type = v.Type + fld.Sym = v.Sym params = append(params, fld) - decls = append(decls, asNode(fld.Nname)) } if len(params) > 0 { @@ -377,11 +282,7 @@ func transformclosure(xfunc *Node) { // The closure is not called, so it is going to stay as closure. var body []*Node offset := int64(Widthptr) - for _, v := range func_.Func.Cvars.Slice() { - if v.Op == OXXX { - continue - } - + for _, v := range xfunc.Func.Cvars.Slice() { // cv refers to the field inside of closure OSTRUCTLIT. cv := nod(OCLOSUREVAR, nil, nil) @@ -425,42 +326,40 @@ func transformclosure(xfunc *Node) { lineno = lno } -// hasemptycvars returns true iff closure func_ has an -// empty list of captured vars. OXXX nodes don't count. -func hasemptycvars(func_ *Node) bool { - for _, v := range func_.Func.Cvars.Slice() { - if v.Op == OXXX { - continue - } - return false - } - return true +// hasemptycvars returns true iff closure clo has an +// empty list of captured vars. +func hasemptycvars(clo *Node) bool { + xfunc := clo.Func.Closure + return xfunc.Func.Cvars.Len() == 0 } // closuredebugruntimecheck applies boilerplate checks for debug flags // and compiling runtime -func closuredebugruntimecheck(r *Node) { +func closuredebugruntimecheck(clo *Node) { if Debug_closure > 0 { - if r.Esc == EscHeap { - Warnl(r.Pos, "heap closure, captured vars = %v", r.Func.Cvars) + xfunc := clo.Func.Closure + if clo.Esc == EscHeap { + Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars) } else { - Warnl(r.Pos, "stack closure, captured vars = %v", r.Func.Cvars) + Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars) } } - if compiling_runtime && r.Esc == EscHeap { - yyerrorl(r.Pos, "heap-allocated closure, not allowed in runtime.") + if compiling_runtime && clo.Esc == EscHeap { + yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime.") } } -func walkclosure(func_ *Node, init *Nodes) *Node { +func walkclosure(clo *Node, init *Nodes) *Node { + xfunc := clo.Func.Closure + // If no closure vars, don't bother wrapping. - if hasemptycvars(func_) { + if hasemptycvars(clo) { if Debug_closure > 0 { - Warnl(func_.Pos, "closure converted to global") + Warnl(clo.Pos, "closure converted to global") } - return func_.Func.Closure.Func.Nname + return xfunc.Func.Nname } - closuredebugruntimecheck(func_) + closuredebugruntimecheck(clo) // Create closure in the form of a composite literal. // supposing the closure captures an int i and a string s @@ -479,10 +378,7 @@ func walkclosure(func_ *Node, init *Nodes) *Node { fields := []*Node{ namedfield(".F", types.Types[TUINTPTR]), } - for _, v := range func_.Func.Cvars.Slice() { - if v.Op == OXXX { - continue - } + for _, v := range xfunc.Func.Cvars.Slice() { typ := v.Type if !v.Name.Byval() { typ = types.NewPtr(typ) @@ -493,27 +389,27 @@ func walkclosure(func_ *Node, init *Nodes) *Node { typ.SetNoalg(true) clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil)) - clos.Esc = func_.Esc + clos.Esc = clo.Esc clos.Right.SetImplicit(true) - clos.List.Set(append([]*Node{nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)}, func_.Func.Enter.Slice()...)) + clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...)) // Force type conversion from *struct to the func type. clos = nod(OCONVNOP, clos, nil) - clos.Type = func_.Type + clos.Type = clo.Type clos = typecheck(clos, Erv) // typecheck will insert a PTRLIT node under CONVNOP, // tag it with escape analysis result. - clos.Left.Esc = func_.Esc + clos.Left.Esc = clo.Esc // non-escaping temp to use, if any. // orderexpr did not compute the type; fill it in now. - if x := prealloc[func_]; x != nil { + if x := prealloc[clo]; x != nil { x.Type = clos.Left.Left.Type x.Orig.Type = x.Type clos.Left.Right = x - delete(prealloc, func_) + delete(prealloc, clo) } return walkexpr(clos, init) @@ -619,12 +515,11 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { // Declare and initialize variable holding receiver. xfunc.Func.SetNeedctxt(true) + cv := nod(OCLOSUREVAR, nil, nil) - cv.Xoffset = int64(Widthptr) cv.Type = rcvrtype - if int(cv.Type.Align) > Widthptr { - cv.Xoffset = int64(cv.Type.Align) - } + cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align)) + ptr := newname(lookup("rcvr")) ptr.SetClass(PAUTO) ptr.Name.SetUsed(true) diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 48945e2868f..c5021f4a48e 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -948,7 +948,7 @@ func (e *EscState) esc(n *Node, parent *Node) { case OCLOSURE: // Link addresses of captured variables to closure. - for _, v := range n.Func.Cvars.Slice() { + for _, v := range n.Func.Closure.Func.Cvars.Slice() { if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs continue } diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 32981c0f2cc..d622307c631 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -816,7 +816,7 @@ func mkinlcall1(n, fn *Node) *Node { // handle captured variables when inlining closures if c := fn.Name.Defn.Func.Closure; c != nil { - for _, v := range c.Func.Cvars.Slice() { + for _, v := range c.Func.Closure.Func.Cvars.Slice() { if v.Op == OXXX { continue } diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 0e88d0f67c1..d3f76953cca 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -1124,7 +1124,7 @@ func (o *Order) expr(n, lhs *Node) *Node { } case OCLOSURE: - if n.Noescape() && n.Func.Cvars.Len() > 0 { + if n.Noescape() && n.Func.Closure.Func.Cvars.Len() > 0 { prealloc[n] = o.newTemp(types.Types[TUINT8], false) // walk will fill in correct type } diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index 358814c5ce3..c7104030bdc 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Func{}, 128, 232}, + {Func{}, 124, 224}, {Name{}, 32, 56}, {Param{}, 24, 48}, {Node{}, 76, 128}, diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 182f93da14d..28befbeb857 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -471,8 +471,11 @@ type Func struct { // Marks records scope boundary changes. Marks []Mark - Closgen int - Outerfunc *Node // outer function (for closure) + // Closgen tracks how many closures have been generated within + // this function. Used by closurename for creating unique + // function names. + Closgen int + FieldTrack map[*types.Sym]struct{} DebugInfo *ssa.FuncDebug Ntype *Node // signature