...

Source file src/pkg/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/compile/internal/syntax"
     9		"cmd/compile/internal/types"
    10		"fmt"
    11	)
    12	
    13	func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
    14		xtype := p.typeExpr(expr.Type)
    15		ntype := p.typeExpr(expr.Type)
    16	
    17		xfunc := p.nod(expr, ODCLFUNC, nil, nil)
    18		xfunc.Func.SetIsHiddenClosure(Curfn != nil)
    19		xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure
    20		xfunc.Func.Nname.Name.Param.Ntype = xtype
    21		xfunc.Func.Nname.Name.Defn = xfunc
    22	
    23		clo := p.nod(expr, OCLOSURE, nil, nil)
    24		clo.Func.Ntype = ntype
    25	
    26		xfunc.Func.Closure = clo
    27		clo.Func.Closure = xfunc
    28	
    29		p.funcBody(xfunc, expr.Body)
    30	
    31		// closure-specific variables are hanging off the
    32		// ordinary ones in the symbol table; see oldname.
    33		// unhook them.
    34		// make the list of pointers for the closure call.
    35		for _, v := range xfunc.Func.Cvars.Slice() {
    36			// Unlink from v1; see comment in syntax.go type Param for these fields.
    37			v1 := v.Name.Defn
    38			v1.Name.Param.Innermost = v.Name.Param.Outer
    39	
    40			// If the closure usage of v is not dense,
    41			// we need to make it dense; now that we're out
    42			// of the function in which v appeared,
    43			// look up v.Sym in the enclosing function
    44			// and keep it around for use in the compiled code.
    45			//
    46			// That is, suppose we just finished parsing the innermost
    47			// closure f4 in this code:
    48			//
    49			//	func f() {
    50			//		v := 1
    51			//		func() { // f2
    52			//			use(v)
    53			//			func() { // f3
    54			//				func() { // f4
    55			//					use(v)
    56			//				}()
    57			//			}()
    58			//		}()
    59			//	}
    60			//
    61			// At this point v.Outer is f2's v; there is no f3's v.
    62			// To construct the closure f4 from within f3,
    63			// we need to use f3's v and in this case we need to create f3's v.
    64			// We are now in the context of f3, so calling oldname(v.Sym)
    65			// obtains f3's v, creating it if necessary (as it is in the example).
    66			//
    67			// capturevars will decide whether to use v directly or &v.
    68			v.Name.Param.Outer = oldname(v.Sym)
    69		}
    70	
    71		return clo
    72	}
    73	
    74	func typecheckclosure(clo *Node, top int) {
    75		xfunc := clo.Func.Closure
    76		clo.Func.Ntype = typecheck(clo.Func.Ntype, Etype)
    77		clo.Type = clo.Func.Ntype.Type
    78		clo.Func.Top = top
    79	
    80		// Do not typecheck xfunc twice, otherwise, we will end up pushing
    81		// xfunc to xtop multiple times, causing initLSym called twice.
    82		// See #30709
    83		if xfunc.Typecheck() == 1 {
    84			return
    85		}
    86	
    87		for _, ln := range xfunc.Func.Cvars.Slice() {
    88			n := ln.Name.Defn
    89			if !n.Name.Captured() {
    90				n.Name.SetCaptured(true)
    91				if n.Name.Decldepth == 0 {
    92					Fatalf("typecheckclosure: var %S does not have decldepth assigned", n)
    93				}
    94	
    95				// Ignore assignments to the variable in straightline code
    96				// preceding the first capturing by a closure.
    97				if n.Name.Decldepth == decldepth {
    98					n.SetAssigned(false)
    99				}
   100			}
   101		}
   102	
   103		xfunc.Func.Nname.Sym = closurename(Curfn)
   104		disableExport(xfunc.Func.Nname.Sym)
   105		declare(xfunc.Func.Nname, PFUNC)
   106		xfunc = typecheck(xfunc, ctxStmt)
   107	
   108		// Type check the body now, but only if we're inside a function.
   109		// At top level (in a variable initialization: curfn==nil) we're not
   110		// ready to type check code yet; we'll check it later, because the
   111		// underlying closure function we create is added to xtop.
   112		if Curfn != nil && clo.Type != nil {
   113			oldfn := Curfn
   114			Curfn = xfunc
   115			olddd := decldepth
   116			decldepth = 1
   117			typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
   118			decldepth = olddd
   119			Curfn = oldfn
   120		}
   121	
   122		xtop = append(xtop, xfunc)
   123	}
   124	
   125	// globClosgen is like Func.Closgen, but for the global scope.
   126	var globClosgen int
   127	
   128	// closurename generates a new unique name for a closure within
   129	// outerfunc.
   130	func closurename(outerfunc *Node) *types.Sym {
   131		outer := "glob."
   132		prefix := "func"
   133		gen := &globClosgen
   134	
   135		if outerfunc != nil {
   136			if outerfunc.Func.Closure != nil {
   137				prefix = ""
   138			}
   139	
   140			outer = outerfunc.funcname()
   141	
   142			// There may be multiple functions named "_". In those
   143			// cases, we can't use their individual Closgens as it
   144			// would lead to name clashes.
   145			if !outerfunc.Func.Nname.isBlank() {
   146				gen = &outerfunc.Func.Closgen
   147			}
   148		}
   149	
   150		*gen++
   151		return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
   152	}
   153	
   154	// capturevarscomplete is set to true when the capturevars phase is done.
   155	var capturevarscomplete bool
   156	
   157	// capturevars is called in a separate phase after all typechecking is done.
   158	// It decides whether each variable captured by a closure should be captured
   159	// by value or by reference.
   160	// We use value capturing for values <= 128 bytes that are never reassigned
   161	// after capturing (effectively constant).
   162	func capturevars(xfunc *Node) {
   163		lno := lineno
   164		lineno = xfunc.Pos
   165	
   166		clo := xfunc.Func.Closure
   167		cvars := xfunc.Func.Cvars.Slice()
   168		out := cvars[:0]
   169		for _, v := range cvars {
   170			if v.Type == nil {
   171				// If v.Type is nil, it means v looked like it
   172				// was going to be used in the closure, but
   173				// isn't. This happens in struct literals like
   174				// s{f: x} where we can't distinguish whether
   175				// f is a field identifier or expression until
   176				// resolving s.
   177				continue
   178			}
   179			out = append(out, v)
   180	
   181			// type check the & of closed variables outside the closure,
   182			// so that the outer frame also grabs them and knows they escape.
   183			dowidth(v.Type)
   184	
   185			outer := v.Name.Param.Outer
   186			outermost := v.Name.Defn
   187	
   188			// out parameters will be assigned to implicitly upon return.
   189			if outermost.Class() != PPARAMOUT && !outermost.Addrtaken() && !outermost.Assigned() && v.Type.Width <= 128 {
   190				v.Name.SetByval(true)
   191			} else {
   192				outermost.SetAddrtaken(true)
   193				outer = nod(OADDR, outer, nil)
   194			}
   195	
   196			if Debug['m'] > 1 {
   197				var name *types.Sym
   198				if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
   199					name = v.Name.Curfn.Func.Nname.Sym
   200				}
   201				how := "ref"
   202				if v.Name.Byval() {
   203					how = "value"
   204				}
   205				Warnl(v.Pos, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken(), outermost.Assigned(), int32(v.Type.Width))
   206			}
   207	
   208			outer = typecheck(outer, ctxExpr)
   209			clo.Func.Enter.Append(outer)
   210		}
   211	
   212		xfunc.Func.Cvars.Set(out)
   213		lineno = lno
   214	}
   215	
   216	// transformclosure is called in a separate phase after escape analysis.
   217	// It transform closure bodies to properly reference captured variables.
   218	func transformclosure(xfunc *Node) {
   219		lno := lineno
   220		lineno = xfunc.Pos
   221		clo := xfunc.Func.Closure
   222	
   223		if clo.Func.Top&ctxCallee != 0 {
   224			// If the closure is directly called, we transform it to a plain function call
   225			// with variables passed as args. This avoids allocation of a closure object.
   226			// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
   227			// will complete the transformation later.
   228			// For illustration, the following closure:
   229			//	func(a int) {
   230			//		println(byval)
   231			//		byref++
   232			//	}(42)
   233			// becomes:
   234			//	func(byval int, &byref *int, a int) {
   235			//		println(byval)
   236			//		(*&byref)++
   237			//	}(byval, &byref, 42)
   238	
   239			// f is ONAME of the actual function.
   240			f := xfunc.Func.Nname
   241	
   242			// We are going to insert captured variables before input args.
   243			var params []*types.Field
   244			var decls []*Node
   245			for _, v := range xfunc.Func.Cvars.Slice() {
   246				if !v.Name.Byval() {
   247					// If v of type T is captured by reference,
   248					// we introduce function param &v *T
   249					// and v remains PAUTOHEAP with &v heapaddr
   250					// (accesses will implicitly deref &v).
   251					addr := newname(lookup("&" + v.Sym.Name))
   252					addr.Type = types.NewPtr(v.Type)
   253					v.Name.Param.Heapaddr = addr
   254					v = addr
   255				}
   256	
   257				v.SetClass(PPARAM)
   258				decls = append(decls, v)
   259	
   260				fld := types.NewField()
   261				fld.Nname = asTypesNode(v)
   262				fld.Type = v.Type
   263				fld.Sym = v.Sym
   264				params = append(params, fld)
   265			}
   266	
   267			if len(params) > 0 {
   268				// Prepend params and decls.
   269				f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...))
   270				xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...)
   271			}
   272	
   273			dowidth(f.Type)
   274			xfunc.Type = f.Type // update type of ODCLFUNC
   275		} else {
   276			// The closure is not called, so it is going to stay as closure.
   277			var body []*Node
   278			offset := int64(Widthptr)
   279			for _, v := range xfunc.Func.Cvars.Slice() {
   280				// cv refers to the field inside of closure OSTRUCTLIT.
   281				cv := nod(OCLOSUREVAR, nil, nil)
   282	
   283				cv.Type = v.Type
   284				if !v.Name.Byval() {
   285					cv.Type = types.NewPtr(v.Type)
   286				}
   287				offset = Rnd(offset, int64(cv.Type.Align))
   288				cv.Xoffset = offset
   289				offset += cv.Type.Width
   290	
   291				if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) {
   292					// If it is a small variable captured by value, downgrade it to PAUTO.
   293					v.SetClass(PAUTO)
   294					xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
   295					body = append(body, nod(OAS, v, cv))
   296				} else {
   297					// Declare variable holding addresses taken from closure
   298					// and initialize in entry prologue.
   299					addr := newname(lookup("&" + v.Sym.Name))
   300					addr.Type = types.NewPtr(v.Type)
   301					addr.SetClass(PAUTO)
   302					addr.Name.SetUsed(true)
   303					addr.Name.Curfn = xfunc
   304					xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
   305					v.Name.Param.Heapaddr = addr
   306					if v.Name.Byval() {
   307						cv = nod(OADDR, cv, nil)
   308					}
   309					body = append(body, nod(OAS, addr, cv))
   310				}
   311			}
   312	
   313			if len(body) > 0 {
   314				typecheckslice(body, ctxStmt)
   315				xfunc.Func.Enter.Set(body)
   316				xfunc.Func.SetNeedctxt(true)
   317			}
   318		}
   319	
   320		lineno = lno
   321	}
   322	
   323	// hasemptycvars reports whether closure clo has an
   324	// empty list of captured vars.
   325	func hasemptycvars(clo *Node) bool {
   326		xfunc := clo.Func.Closure
   327		return xfunc.Func.Cvars.Len() == 0
   328	}
   329	
   330	// closuredebugruntimecheck applies boilerplate checks for debug flags
   331	// and compiling runtime
   332	func closuredebugruntimecheck(clo *Node) {
   333		if Debug_closure > 0 {
   334			xfunc := clo.Func.Closure
   335			if clo.Esc == EscHeap {
   336				Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars)
   337			} else {
   338				Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars)
   339			}
   340		}
   341		if compiling_runtime && clo.Esc == EscHeap {
   342			yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime.")
   343		}
   344	}
   345	
   346	// closureType returns the struct type used to hold all the information
   347	// needed in the closure for clo (clo must be a OCLOSURE node).
   348	// The address of a variable of the returned type can be cast to a func.
   349	func closureType(clo *Node) *types.Type {
   350		// Create closure in the form of a composite literal.
   351		// supposing the closure captures an int i and a string s
   352		// and has one float64 argument and no results,
   353		// the generated code looks like:
   354		//
   355		//	clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
   356		//
   357		// The use of the struct provides type information to the garbage
   358		// collector so that it can walk the closure. We could use (in this case)
   359		// [3]unsafe.Pointer instead, but that would leave the gc in the dark.
   360		// The information appears in the binary in the form of type descriptors;
   361		// the struct is unnamed so that closures in multiple packages with the
   362		// same struct type can share the descriptor.
   363		fields := []*Node{
   364			namedfield(".F", types.Types[TUINTPTR]),
   365		}
   366		for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
   367			typ := v.Type
   368			if !v.Name.Byval() {
   369				typ = types.NewPtr(typ)
   370			}
   371			fields = append(fields, symfield(v.Sym, typ))
   372		}
   373		typ := tostruct(fields)
   374		typ.SetNoalg(true)
   375		return typ
   376	}
   377	
   378	func walkclosure(clo *Node, init *Nodes) *Node {
   379		xfunc := clo.Func.Closure
   380	
   381		// If no closure vars, don't bother wrapping.
   382		if hasemptycvars(clo) {
   383			if Debug_closure > 0 {
   384				Warnl(clo.Pos, "closure converted to global")
   385			}
   386			return xfunc.Func.Nname
   387		}
   388		closuredebugruntimecheck(clo)
   389	
   390		typ := closureType(clo)
   391	
   392		clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil))
   393		clos.Esc = clo.Esc
   394		clos.Right.SetImplicit(true)
   395		clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...))
   396	
   397		// Force type conversion from *struct to the func type.
   398		clos = convnop(clos, clo.Type)
   399	
   400		// typecheck will insert a PTRLIT node under CONVNOP,
   401		// tag it with escape analysis result.
   402		clos.Left.Esc = clo.Esc
   403	
   404		// non-escaping temp to use, if any.
   405		if x := prealloc[clo]; x != nil {
   406			if !types.Identical(typ, x.Type) {
   407				panic("closure type does not match order's assigned type")
   408			}
   409			clos.Left.Right = x
   410			delete(prealloc, clo)
   411		}
   412	
   413		return walkexpr(clos, init)
   414	}
   415	
   416	func typecheckpartialcall(fn *Node, sym *types.Sym) {
   417		switch fn.Op {
   418		case ODOTINTER, ODOTMETH:
   419			break
   420	
   421		default:
   422			Fatalf("invalid typecheckpartialcall")
   423		}
   424	
   425		// Create top-level function.
   426		xfunc := makepartialcall(fn, fn.Type, sym)
   427		fn.Func = xfunc.Func
   428		fn.Right = newname(sym)
   429		fn.Op = OCALLPART
   430		fn.Type = xfunc.Type
   431	}
   432	
   433	func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
   434		rcvrtype := fn.Left.Type
   435		sym := methodSymSuffix(rcvrtype, meth, "-fm")
   436	
   437		if sym.Uniq() {
   438			return asNode(sym.Def)
   439		}
   440		sym.SetUniq(true)
   441	
   442		savecurfn := Curfn
   443		saveLineNo := lineno
   444		Curfn = nil
   445	
   446		// Set line number equal to the line number where the method is declared.
   447		var m *types.Field
   448		if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() {
   449			lineno = m.Pos
   450		}
   451		// Note: !m.Pos.IsKnown() happens for method expressions where
   452		// the method is implicitly declared. The Error method of the
   453		// built-in error type is one such method.  We leave the line
   454		// number at the use of the method expression in this
   455		// case. See issue 29389.
   456	
   457		tfn := nod(OTFUNC, nil, nil)
   458		tfn.List.Set(structargs(t0.Params(), true))
   459		tfn.Rlist.Set(structargs(t0.Results(), false))
   460	
   461		disableExport(sym)
   462		xfunc := dclfunc(sym, tfn)
   463		xfunc.Func.SetDupok(true)
   464		xfunc.Func.SetNeedctxt(true)
   465	
   466		tfn.Type.SetPkg(t0.Pkg())
   467	
   468		// Declare and initialize variable holding receiver.
   469	
   470		cv := nod(OCLOSUREVAR, nil, nil)
   471		cv.Type = rcvrtype
   472		cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align))
   473	
   474		ptr := newname(lookup(".this"))
   475		declare(ptr, PAUTO)
   476		ptr.Name.SetUsed(true)
   477		var body []*Node
   478		if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
   479			ptr.Type = rcvrtype
   480			body = append(body, nod(OAS, ptr, cv))
   481		} else {
   482			ptr.Type = types.NewPtr(rcvrtype)
   483			body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil)))
   484		}
   485	
   486		call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil)
   487		call.List.Set(paramNnames(tfn.Type))
   488		call.SetIsDDD(tfn.Type.IsVariadic())
   489		if t0.NumResults() != 0 {
   490			n := nod(ORETURN, nil, nil)
   491			n.List.Set1(call)
   492			call = n
   493		}
   494		body = append(body, call)
   495	
   496		xfunc.Nbody.Set(body)
   497		funcbody()
   498	
   499		xfunc = typecheck(xfunc, ctxStmt)
   500		sym.Def = asTypesNode(xfunc)
   501		xtop = append(xtop, xfunc)
   502		Curfn = savecurfn
   503		lineno = saveLineNo
   504	
   505		return xfunc
   506	}
   507	
   508	// partialCallType returns the struct type used to hold all the information
   509	// needed in the closure for n (n must be a OCALLPART node).
   510	// The address of a variable of the returned type can be cast to a func.
   511	func partialCallType(n *Node) *types.Type {
   512		t := tostruct([]*Node{
   513			namedfield("F", types.Types[TUINTPTR]),
   514			namedfield("R", n.Left.Type),
   515		})
   516		t.SetNoalg(true)
   517		return t
   518	}
   519	
   520	func walkpartialcall(n *Node, init *Nodes) *Node {
   521		// Create closure in the form of a composite literal.
   522		// For x.M with receiver (x) type T, the generated code looks like:
   523		//
   524		//	clos = &struct{F uintptr; R T}{M.T·f, x}
   525		//
   526		// Like walkclosure above.
   527	
   528		if n.Left.Type.IsInterface() {
   529			// Trigger panic for method on nil interface now.
   530			// Otherwise it happens in the wrapper and is confusing.
   531			n.Left = cheapexpr(n.Left, init)
   532			n.Left = walkexpr(n.Left, nil)
   533	
   534			tab := nod(OITAB, n.Left, nil)
   535			tab = typecheck(tab, ctxExpr)
   536	
   537			c := nod(OCHECKNIL, tab, nil)
   538			c.SetTypecheck(1)
   539			init.Append(c)
   540		}
   541	
   542		typ := partialCallType(n)
   543	
   544		clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil))
   545		clos.Esc = n.Esc
   546		clos.Right.SetImplicit(true)
   547		clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
   548	
   549		// Force type conversion from *struct to the func type.
   550		clos = convnop(clos, n.Type)
   551	
   552		// The typecheck inside convnop will insert a PTRLIT node under CONVNOP.
   553		// Tag it with escape analysis result.
   554		clos.Left.Esc = n.Esc
   555	
   556		// non-escaping temp to use, if any.
   557		if x := prealloc[n]; x != nil {
   558			if !types.Identical(typ, x.Type) {
   559				panic("partial call type does not match order's assigned type")
   560			}
   561			clos.Left.Right = x
   562			delete(prealloc, n)
   563		}
   564	
   565		return walkexpr(clos, init)
   566	}
   567	

View as plain text