]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/gc/closure.go
[dev.ssa] Merge remote-tracking branch 'origin/master' into ssamerge
[gostls13.git] / src / cmd / compile / internal / gc / closure.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package gc
6
7 import (
8         "cmd/internal/obj"
9         "fmt"
10 )
11
12 // function literals aka closures
13 func closurehdr(ntype *Node) {
14         var name *Node
15         var a *Node
16
17         n := Nod(OCLOSURE, nil, nil)
18         n.Func.Ntype = ntype
19         n.Func.Depth = Funcdepth
20         n.Func.Outerfunc = Curfn
21
22         funchdr(n)
23
24         // steal ntype's argument names and
25         // leave a fresh copy in their place.
26         // references to these variables need to
27         // refer to the variables in the external
28         // function declared below; see walkclosure.
29         n.List = ntype.List
30
31         n.Rlist = ntype.Rlist
32         ntype.List = nil
33         ntype.Rlist = nil
34         for l := n.List; l != nil; l = l.Next {
35                 name = l.N.Left
36                 if name != nil {
37                         name = newname(name.Sym)
38                 }
39                 a = Nod(ODCLFIELD, name, l.N.Right)
40                 a.Isddd = l.N.Isddd
41                 if name != nil {
42                         name.Isddd = a.Isddd
43                 }
44                 ntype.List = list(ntype.List, a)
45         }
46
47         for l := n.Rlist; l != nil; l = l.Next {
48                 name = l.N.Left
49                 if name != nil {
50                         name = newname(name.Sym)
51                 }
52                 ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, name, l.N.Right))
53         }
54 }
55
56 func closurebody(body *NodeList) *Node {
57         if body == nil {
58                 body = list1(Nod(OEMPTY, nil, nil))
59         }
60
61         func_ := Curfn
62         func_.Nbody = body
63         func_.Func.Endlineno = lineno
64         funcbody(func_)
65
66         // closure-specific variables are hanging off the
67         // ordinary ones in the symbol table; see oldname.
68         // unhook them.
69         // make the list of pointers for the closure call.
70         for _, v := range func_.Func.Cvars.Slice() {
71                 v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
72                 v.Name.Param.Outerexpr = oldname(v.Sym)
73         }
74
75         return func_
76 }
77
78 func typecheckclosure(func_ *Node, top int) {
79         for _, ln := range func_.Func.Cvars.Slice() {
80                 n := ln.Name.Param.Closure
81                 if !n.Name.Captured {
82                         n.Name.Captured = true
83                         if n.Name.Decldepth == 0 {
84                                 Fatalf("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, obj.FmtShort))
85                         }
86
87                         // Ignore assignments to the variable in straightline code
88                         // preceding the first capturing by a closure.
89                         if n.Name.Decldepth == decldepth {
90                                 n.Assigned = false
91                         }
92                 }
93         }
94
95         for _, ln := range func_.Func.Dcl {
96                 if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) {
97                         ln.Name.Decldepth = 1
98                 }
99         }
100
101         oldfn := Curfn
102         typecheck(&func_.Func.Ntype, Etype)
103         func_.Type = func_.Func.Ntype.Type
104         func_.Func.Top = top
105
106         // Type check the body now, but only if we're inside a function.
107         // At top level (in a variable initialization: curfn==nil) we're not
108         // ready to type check code yet; we'll check it later, because the
109         // underlying closure function we create is added to xtop.
110         if Curfn != nil && func_.Type != nil {
111                 Curfn = func_
112                 olddd := decldepth
113                 decldepth = 1
114                 typechecklist(func_.Nbody, Etop)
115                 decldepth = olddd
116                 Curfn = oldfn
117         }
118
119         // Create top-level function
120         xtop = list(xtop, makeclosure(func_))
121 }
122
123 // closurename returns name for OCLOSURE n.
124 // It is not as simple as it ought to be, because we typecheck nested closures
125 // starting from the innermost one. So when we check the inner closure,
126 // we don't yet have name for the outer closure. This function uses recursion
127 // to generate names all the way up if necessary.
128
129 var closurename_closgen int
130
131 func closurename(n *Node) *Sym {
132         if n.Sym != nil {
133                 return n.Sym
134         }
135         gen := 0
136         outer := ""
137         prefix := ""
138         if n.Func.Outerfunc == nil {
139                 // Global closure.
140                 outer = "glob"
141
142                 prefix = "func"
143                 closurename_closgen++
144                 gen = closurename_closgen
145         } else if n.Func.Outerfunc.Op == ODCLFUNC {
146                 // The outermost closure inside of a named function.
147                 outer = n.Func.Outerfunc.Func.Nname.Sym.Name
148
149                 prefix = "func"
150
151                 // Yes, functions can be named _.
152                 // Can't use function closgen in such case,
153                 // because it would lead to name clashes.
154                 if !isblank(n.Func.Outerfunc.Func.Nname) {
155                         n.Func.Outerfunc.Func.Closgen++
156                         gen = n.Func.Outerfunc.Func.Closgen
157                 } else {
158                         closurename_closgen++
159                         gen = closurename_closgen
160                 }
161         } else if n.Func.Outerfunc.Op == OCLOSURE {
162                 // Nested closure, recurse.
163                 outer = closurename(n.Func.Outerfunc).Name
164
165                 prefix = ""
166                 n.Func.Outerfunc.Func.Closgen++
167                 gen = n.Func.Outerfunc.Func.Closgen
168         } else {
169                 Fatalf("closurename called for %v", Nconv(n, obj.FmtShort))
170         }
171         n.Sym = Lookupf("%s.%s%d", outer, prefix, gen)
172         return n.Sym
173 }
174
175 func makeclosure(func_ *Node) *Node {
176         // wrap body in external function
177         // that begins by reading closure parameters.
178         xtype := Nod(OTFUNC, nil, nil)
179
180         xtype.List = func_.List
181         xtype.Rlist = func_.Rlist
182
183         // create the function
184         xfunc := Nod(ODCLFUNC, nil, nil)
185
186         xfunc.Func.Nname = newfuncname(closurename(func_))
187         xfunc.Func.Nname.Sym.Flags |= SymExported // disable export
188         xfunc.Func.Nname.Name.Param.Ntype = xtype
189         xfunc.Func.Nname.Name.Defn = xfunc
190         declare(xfunc.Func.Nname, PFUNC)
191         xfunc.Func.Nname.Name.Funcdepth = func_.Func.Depth
192         xfunc.Func.Depth = func_.Func.Depth
193         xfunc.Func.Endlineno = func_.Func.Endlineno
194         makefuncsym(xfunc.Func.Nname.Sym)
195
196         xfunc.Nbody = func_.Nbody
197         xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
198         func_.Func.Dcl = nil
199         if xfunc.Nbody == nil {
200                 Fatalf("empty body - won't generate any code")
201         }
202         typecheck(&xfunc, Etop)
203
204         xfunc.Func.Closure = func_
205         func_.Func.Closure = xfunc
206
207         func_.Nbody = nil
208         func_.List = nil
209         func_.Rlist = nil
210
211         return xfunc
212 }
213
214 // capturevars is called in a separate phase after all typechecking is done.
215 // It decides whether each variable captured by a closure should be captured
216 // by value or by reference.
217 // We use value capturing for values <= 128 bytes that are never reassigned
218 // after capturing (effectively constant).
219 func capturevars(xfunc *Node) {
220         var outer *Node
221
222         lno := int(lineno)
223         lineno = xfunc.Lineno
224
225         func_ := xfunc.Func.Closure
226         func_.Func.Enter.Set(nil)
227         for _, v := range func_.Func.Cvars.Slice() {
228                 if v.Type == nil {
229                         // if v->type is nil, it means v looked like it was
230                         // going to be used in the closure but wasn't.
231                         // this happens because when parsing a, b, c := f()
232                         // the a, b, c gets parsed as references to older
233                         // a, b, c before the parser figures out this is a
234                         // declaration.
235                         v.Op = OXXX
236
237                         continue
238                 }
239
240                 // type check the & of closed variables outside the closure,
241                 // so that the outer frame also grabs them and knows they escape.
242                 dowidth(v.Type)
243
244                 outer = v.Name.Param.Outerexpr
245                 v.Name.Param.Outerexpr = nil
246
247                 // out parameters will be assigned to implicitly upon return.
248                 if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 {
249                         v.Name.Byval = true
250                 } else {
251                         v.Name.Param.Closure.Addrtaken = true
252                         outer = Nod(OADDR, outer, nil)
253                 }
254
255                 if Debug['m'] > 1 {
256                         var name *Sym
257                         if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
258                                 name = v.Name.Curfn.Func.Nname.Sym
259                         }
260                         how := "ref"
261                         if v.Name.Byval {
262                                 how = "value"
263                         }
264                         Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width))
265                 }
266
267                 typecheck(&outer, Erv)
268                 func_.Func.Enter.Append(outer)
269         }
270
271         lineno = int32(lno)
272 }
273
274 // transformclosure is called in a separate phase after escape analysis.
275 // It transform closure bodies to properly reference captured variables.
276 func transformclosure(xfunc *Node) {
277         lno := int(lineno)
278         lineno = xfunc.Lineno
279         func_ := xfunc.Func.Closure
280
281         if func_.Func.Top&Ecall != 0 {
282                 // If the closure is directly called, we transform it to a plain function call
283                 // with variables passed as args. This avoids allocation of a closure object.
284                 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
285                 // will complete the transformation later.
286                 // For illustration, the following closure:
287                 //      func(a int) {
288                 //              println(byval)
289                 //              byref++
290                 //      }(42)
291                 // becomes:
292                 //      func(a int, byval int, &byref *int) {
293                 //              println(byval)
294                 //              (*&byref)++
295                 //      }(byval, &byref, 42)
296
297                 // f is ONAME of the actual function.
298                 f := xfunc.Func.Nname
299
300                 // Get pointer to input arguments.
301                 // We are going to insert captured variables before input args.
302                 param := &getinargx(f.Type).Type
303                 original_args := *param // old input args
304                 original_dcl := xfunc.Func.Dcl
305                 xfunc.Func.Dcl = nil
306
307                 var addr *Node
308                 var fld *Type
309                 for _, v := range func_.Func.Cvars.Slice() {
310                         if v.Op == OXXX {
311                                 continue
312                         }
313                         fld = typ(TFIELD)
314                         fld.Funarg = true
315                         if v.Name.Byval {
316                                 // If v is captured by value, we merely downgrade it to PPARAM.
317                                 v.Class = PPARAM
318
319                                 v.Ullman = 1
320                                 fld.Nname = v
321                         } else {
322                                 // If v of type T is captured by reference,
323                                 // we introduce function param &v *T
324                                 // and v remains PPARAMREF with &v heapaddr
325                                 // (accesses will implicitly deref &v).
326                                 addr = newname(Lookupf("&%s", v.Sym.Name))
327                                 addr.Type = Ptrto(v.Type)
328                                 addr.Class = PPARAM
329                                 v.Name.Heapaddr = addr
330                                 fld.Nname = addr
331                         }
332
333                         fld.Type = fld.Nname.Type
334                         fld.Sym = fld.Nname.Sym
335
336                         // Declare the new param and add it the first part of the input arguments.
337                         xfunc.Func.Dcl = append(xfunc.Func.Dcl, fld.Nname)
338
339                         *param = fld
340                         param = &fld.Down
341                 }
342                 *param = original_args
343                 xfunc.Func.Dcl = append(xfunc.Func.Dcl, original_dcl...)
344
345                 // Recalculate param offsets.
346                 if f.Type.Width > 0 {
347                         Fatalf("transformclosure: width is already calculated")
348                 }
349                 dowidth(f.Type)
350                 xfunc.Type = f.Type // update type of ODCLFUNC
351         } else {
352                 // The closure is not called, so it is going to stay as closure.
353                 var body []*Node
354                 offset := int64(Widthptr)
355                 var addr *Node
356                 var cv *Node
357                 for _, v := range func_.Func.Cvars.Slice() {
358                         if v.Op == OXXX {
359                                 continue
360                         }
361
362                         // cv refers to the field inside of closure OSTRUCTLIT.
363                         cv = Nod(OCLOSUREVAR, nil, nil)
364
365                         cv.Type = v.Type
366                         if !v.Name.Byval {
367                                 cv.Type = Ptrto(v.Type)
368                         }
369                         offset = Rnd(offset, int64(cv.Type.Align))
370                         cv.Xoffset = offset
371                         offset += cv.Type.Width
372
373                         if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) {
374                                 // If it is a small variable captured by value, downgrade it to PAUTO.
375                                 v.Class = PAUTO
376                                 v.Ullman = 1
377                                 xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
378                                 body = append(body, Nod(OAS, v, cv))
379                         } else {
380                                 // Declare variable holding addresses taken from closure
381                                 // and initialize in entry prologue.
382                                 addr = newname(Lookupf("&%s", v.Sym.Name))
383                                 addr.Name.Param.Ntype = Nod(OIND, typenod(v.Type), nil)
384                                 addr.Class = PAUTO
385                                 addr.Used = true
386                                 addr.Name.Curfn = xfunc
387                                 xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
388                                 v.Name.Heapaddr = addr
389                                 if v.Name.Byval {
390                                         cv = Nod(OADDR, cv, nil)
391                                 }
392                                 body = append(body, Nod(OAS, addr, cv))
393                         }
394                 }
395
396                 if len(body) > 0 {
397                         typecheckslice(body, Etop)
398                         walkstmtslice(body)
399                         xfunc.Func.Enter.Set(body)
400                         xfunc.Func.Needctxt = true
401                 }
402         }
403
404         lineno = int32(lno)
405 }
406
407 func walkclosure(func_ *Node, init **NodeList) *Node {
408         // If no closure vars, don't bother wrapping.
409         if len(func_.Func.Cvars.Slice()) == 0 {
410                 return func_.Func.Closure.Func.Nname
411         }
412
413         // Create closure in the form of a composite literal.
414         // supposing the closure captures an int i and a string s
415         // and has one float64 argument and no results,
416         // the generated code looks like:
417         //
418         //      clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
419         //
420         // The use of the struct provides type information to the garbage
421         // collector so that it can walk the closure. We could use (in this case)
422         // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
423         // The information appears in the binary in the form of type descriptors;
424         // the struct is unnamed so that closures in multiple packages with the
425         // same struct type can share the descriptor.
426
427         typ := Nod(OTSTRUCT, nil, nil)
428
429         typ.List = list1(Nod(ODCLFIELD, newname(Lookup(".F")), typenod(Types[TUINTPTR])))
430         var typ1 *Node
431         for _, v := range func_.Func.Cvars.Slice() {
432                 if v.Op == OXXX {
433                         continue
434                 }
435                 typ1 = typenod(v.Type)
436                 if !v.Name.Byval {
437                         typ1 = Nod(OIND, typ1, nil)
438                 }
439                 typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1))
440         }
441
442         clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
443         clos.Esc = func_.Esc
444         clos.Right.Implicit = true
445         clos.List = concat(list1(Nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)), func_.Func.Enter.NodeList())
446
447         // Force type conversion from *struct to the func type.
448         clos = Nod(OCONVNOP, clos, nil)
449
450         clos.Type = func_.Type
451
452         typecheck(&clos, Erv)
453
454         // typecheck will insert a PTRLIT node under CONVNOP,
455         // tag it with escape analysis result.
456         clos.Left.Esc = func_.Esc
457
458         // non-escaping temp to use, if any.
459         // orderexpr did not compute the type; fill it in now.
460         if x := prealloc[func_]; x != nil {
461                 x.Type = clos.Left.Left.Type
462                 x.Orig.Type = x.Type
463                 clos.Left.Right = x
464                 delete(prealloc, func_)
465         }
466
467         walkexpr(&clos, init)
468
469         return clos
470 }
471
472 func typecheckpartialcall(fn *Node, sym *Node) {
473         switch fn.Op {
474         case ODOTINTER, ODOTMETH:
475                 break
476
477         default:
478                 Fatalf("invalid typecheckpartialcall")
479         }
480
481         // Create top-level function.
482         xfunc := makepartialcall(fn, fn.Type, sym)
483         fn.Func = xfunc.Func
484         fn.Right = sym
485         fn.Op = OCALLPART
486         fn.Type = xfunc.Type
487 }
488
489 var makepartialcall_gopkg *Pkg
490
491 func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node {
492         var p string
493
494         rcvrtype := fn.Left.Type
495         if exportname(meth.Sym.Name) {
496                 p = fmt.Sprintf("(%v).%s-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), meth.Sym.Name)
497         } else {
498                 p = fmt.Sprintf("(%v).(%v)-fm", Tconv(rcvrtype, obj.FmtLeft|obj.FmtShort), Sconv(meth.Sym, obj.FmtLeft))
499         }
500         basetype := rcvrtype
501         if Isptr[rcvrtype.Etype] {
502                 basetype = basetype.Type
503         }
504         if basetype.Etype != TINTER && basetype.Sym == nil {
505                 Fatalf("missing base type for %v", rcvrtype)
506         }
507
508         var spkg *Pkg
509         if basetype.Sym != nil {
510                 spkg = basetype.Sym.Pkg
511         }
512         if spkg == nil {
513                 if makepartialcall_gopkg == nil {
514                         makepartialcall_gopkg = mkpkg("go")
515                 }
516                 spkg = makepartialcall_gopkg
517         }
518
519         sym := Pkglookup(p, spkg)
520
521         if sym.Flags&SymUniq != 0 {
522                 return sym.Def
523         }
524         sym.Flags |= SymUniq
525
526         savecurfn := Curfn
527         Curfn = nil
528
529         xtype := Nod(OTFUNC, nil, nil)
530         i := 0
531         var l *NodeList
532         var callargs *NodeList
533         ddd := false
534         xfunc := Nod(ODCLFUNC, nil, nil)
535         Curfn = xfunc
536         var fld *Node
537         var n *Node
538         for t := getinargx(t0).Type; t != nil; t = t.Down {
539                 n = newname(Lookupf("a%d", i))
540                 i++
541                 n.Class = PPARAM
542                 xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
543                 callargs = list(callargs, n)
544                 fld = Nod(ODCLFIELD, n, typenod(t.Type))
545                 if t.Isddd {
546                         fld.Isddd = true
547                         ddd = true
548                 }
549
550                 l = list(l, fld)
551         }
552
553         xtype.List = l
554         i = 0
555         l = nil
556         var retargs *NodeList
557         for t := getoutargx(t0).Type; t != nil; t = t.Down {
558                 n = newname(Lookupf("r%d", i))
559                 i++
560                 n.Class = PPARAMOUT
561                 xfunc.Func.Dcl = append(xfunc.Func.Dcl, n)
562                 retargs = list(retargs, n)
563                 l = list(l, Nod(ODCLFIELD, n, typenod(t.Type)))
564         }
565
566         xtype.Rlist = l
567
568         xfunc.Func.Dupok = true
569         xfunc.Func.Nname = newfuncname(sym)
570         xfunc.Func.Nname.Sym.Flags |= SymExported // disable export
571         xfunc.Func.Nname.Name.Param.Ntype = xtype
572         xfunc.Func.Nname.Name.Defn = xfunc
573         declare(xfunc.Func.Nname, PFUNC)
574
575         // Declare and initialize variable holding receiver.
576
577         xfunc.Func.Needctxt = true
578         cv := Nod(OCLOSUREVAR, nil, nil)
579         cv.Xoffset = int64(Widthptr)
580         cv.Type = rcvrtype
581         if int(cv.Type.Align) > Widthptr {
582                 cv.Xoffset = int64(cv.Type.Align)
583         }
584         ptr := Nod(ONAME, nil, nil)
585         ptr.Sym = Lookup("rcvr")
586         ptr.Class = PAUTO
587         ptr.Addable = true
588         ptr.Ullman = 1
589         ptr.Used = true
590         ptr.Name.Curfn = xfunc
591         ptr.Xoffset = 0
592         xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
593         var body *NodeList
594         if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
595                 ptr.Name.Param.Ntype = typenod(rcvrtype)
596                 body = list(body, Nod(OAS, ptr, cv))
597         } else {
598                 ptr.Name.Param.Ntype = typenod(Ptrto(rcvrtype))
599                 body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
600         }
601
602         call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil)
603         call.List = callargs
604         call.Isddd = ddd
605         if t0.Outtuple == 0 {
606                 body = list(body, call)
607         } else {
608                 n := Nod(OAS2, nil, nil)
609                 n.List = retargs
610                 n.Rlist = list1(call)
611                 body = list(body, n)
612                 n = Nod(ORETURN, nil, nil)
613                 body = list(body, n)
614         }
615
616         xfunc.Nbody = body
617
618         typecheck(&xfunc, Etop)
619         sym.Def = xfunc
620         xtop = list(xtop, xfunc)
621         Curfn = savecurfn
622
623         return xfunc
624 }
625
626 func walkpartialcall(n *Node, init **NodeList) *Node {
627         // Create closure in the form of a composite literal.
628         // For x.M with receiver (x) type T, the generated code looks like:
629         //
630         //      clos = &struct{F uintptr; R T}{M.T·f, x}
631         //
632         // Like walkclosure above.
633
634         if Isinter(n.Left.Type) {
635                 // Trigger panic for method on nil interface now.
636                 // Otherwise it happens in the wrapper and is confusing.
637                 n.Left = cheapexpr(n.Left, init)
638
639                 checknil(n.Left, init)
640         }
641
642         typ := Nod(OTSTRUCT, nil, nil)
643         typ.List = list1(Nod(ODCLFIELD, newname(Lookup("F")), typenod(Types[TUINTPTR])))
644         typ.List = list(typ.List, Nod(ODCLFIELD, newname(Lookup("R")), typenod(n.Left.Type)))
645
646         clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil))
647         clos.Esc = n.Esc
648         clos.Right.Implicit = true
649         clos.List = list1(Nod(OCFUNC, n.Func.Nname, nil))
650         clos.List = list(clos.List, n.Left)
651
652         // Force type conversion from *struct to the func type.
653         clos = Nod(OCONVNOP, clos, nil)
654
655         clos.Type = n.Type
656
657         typecheck(&clos, Erv)
658
659         // typecheck will insert a PTRLIT node under CONVNOP,
660         // tag it with escape analysis result.
661         clos.Left.Esc = n.Esc
662
663         // non-escaping temp to use, if any.
664         // orderexpr did not compute the type; fill it in now.
665         if x := prealloc[n]; x != nil {
666                 x.Type = clos.Left.Left.Type
667                 x.Orig.Type = x.Type
668                 clos.Left.Right = x
669                 delete(prealloc, n)
670         }
671
672         walkexpr(&clos, init)
673
674         return clos
675 }