Source file src/cmd/compile/internal/walk/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 walk
     6  
     7  import (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/typecheck"
    11  	"cmd/compile/internal/types"
    12  	"cmd/internal/src"
    13  )
    14  
    15  // directClosureCall rewrites a direct call of a function literal into
    16  // a normal function call with closure variables passed as arguments.
    17  // This avoids allocation of a closure object.
    18  //
    19  // For illustration, the following call:
    20  //
    21  //	func(a int) {
    22  //		println(byval)
    23  //		byref++
    24  //	}(42)
    25  //
    26  // becomes:
    27  //
    28  //	func(byval int, &byref *int, a int) {
    29  //		println(byval)
    30  //		(*&byref)++
    31  //	}(byval, &byref, 42)
    32  func directClosureCall(n *ir.CallExpr) {
    33  	clo := n.X.(*ir.ClosureExpr)
    34  	clofn := clo.Func
    35  
    36  	if ir.IsTrivialClosure(clo) {
    37  		return // leave for walkClosure to handle
    38  	}
    39  
    40  	// We are going to insert captured variables before input args.
    41  	var params []*types.Field
    42  	var decls []*ir.Name
    43  	for _, v := range clofn.ClosureVars {
    44  		if !v.Byval() {
    45  			// If v of type T is captured by reference,
    46  			// we introduce function param &v *T
    47  			// and v remains PAUTOHEAP with &v heapaddr
    48  			// (accesses will implicitly deref &v).
    49  
    50  			addr := ir.NewNameAt(clofn.Pos(), typecheck.Lookup("&"+v.Sym().Name))
    51  			addr.Curfn = clofn
    52  			addr.SetType(types.NewPtr(v.Type()))
    53  			v.Heapaddr = addr
    54  			v = addr
    55  		}
    56  
    57  		v.Class = ir.PPARAM
    58  		decls = append(decls, v)
    59  
    60  		fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
    61  		fld.Nname = v
    62  		params = append(params, fld)
    63  	}
    64  
    65  	// f is ONAME of the actual function.
    66  	f := clofn.Nname
    67  	typ := f.Type()
    68  
    69  	// Create new function type with parameters prepended, and
    70  	// then update type and declarations.
    71  	typ = types.NewSignature(typ.Pkg(), nil, nil, append(params, typ.Params().FieldSlice()...), typ.Results().FieldSlice())
    72  	f.SetType(typ)
    73  	clofn.Dcl = append(decls, clofn.Dcl...)
    74  
    75  	// Rewrite call.
    76  	n.X = f
    77  	n.Args.Prepend(closureArgs(clo)...)
    78  
    79  	// Update the call expression's type. We need to do this
    80  	// because typecheck gave it the result type of the OCLOSURE
    81  	// node, but we only rewrote the ONAME node's type. Logically,
    82  	// they're the same, but the stack offsets probably changed.
    83  	if typ.NumResults() == 1 {
    84  		n.SetType(typ.Results().Field(0).Type)
    85  	} else {
    86  		n.SetType(typ.Results())
    87  	}
    88  
    89  	// Add to Closures for enqueueFunc. It's no longer a proper
    90  	// closure, but we may have already skipped over it in the
    91  	// functions list as a non-trivial closure, so this just
    92  	// ensures it's compiled.
    93  	ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
    94  }
    95  
    96  func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
    97  	clofn := clo.Func
    98  
    99  	// If no closure vars, don't bother wrapping.
   100  	if ir.IsTrivialClosure(clo) {
   101  		if base.Debug.Closure > 0 {
   102  			base.WarnfAt(clo.Pos(), "closure converted to global")
   103  		}
   104  		return clofn.Nname
   105  	}
   106  
   107  	// The closure is not trivial or directly called, so it's going to stay a closure.
   108  	ir.ClosureDebugRuntimeCheck(clo)
   109  	clofn.SetNeedctxt(true)
   110  
   111  	// The closure expression may be walked more than once if it appeared in composite
   112  	// literal initialization (e.g, see issue #49029).
   113  	//
   114  	// Don't add the closure function to compilation queue more than once, since when
   115  	// compiling a function twice would lead to an ICE.
   116  	if !clofn.Walked() {
   117  		clofn.SetWalked(true)
   118  		ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
   119  	}
   120  
   121  	typ := typecheck.ClosureType(clo)
   122  
   123  	clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
   124  	clos.SetEsc(clo.Esc())
   125  	clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...)
   126  	for i, value := range clos.List {
   127  		clos.List[i] = ir.NewStructKeyExpr(base.Pos, typ.Field(i), value)
   128  	}
   129  
   130  	addr := typecheck.NodAddr(clos)
   131  	addr.SetEsc(clo.Esc())
   132  
   133  	// Force type conversion from *struct to the func type.
   134  	cfn := typecheck.ConvNop(addr, clo.Type())
   135  
   136  	// non-escaping temp to use, if any.
   137  	if x := clo.Prealloc; x != nil {
   138  		if !types.Identical(typ, x.Type()) {
   139  			panic("closure type does not match order's assigned type")
   140  		}
   141  		addr.Prealloc = x
   142  		clo.Prealloc = nil
   143  	}
   144  
   145  	return walkExpr(cfn, init)
   146  }
   147  
   148  // closureArgs returns a slice of expressions that an be used to
   149  // initialize the given closure's free variables. These correspond
   150  // one-to-one with the variables in clo.Func.ClosureVars, and will be
   151  // either an ONAME node (if the variable is captured by value) or an
   152  // OADDR-of-ONAME node (if not).
   153  func closureArgs(clo *ir.ClosureExpr) []ir.Node {
   154  	fn := clo.Func
   155  
   156  	args := make([]ir.Node, len(fn.ClosureVars))
   157  	for i, v := range fn.ClosureVars {
   158  		var outer ir.Node
   159  		outer = v.Outer
   160  		if !v.Byval() {
   161  			outer = typecheck.NodAddrAt(fn.Pos(), outer)
   162  		}
   163  		args[i] = typecheck.Expr(outer)
   164  	}
   165  	return args
   166  }
   167  
   168  func walkMethodValue(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
   169  	// Create closure in the form of a composite literal.
   170  	// For x.M with receiver (x) type T, the generated code looks like:
   171  	//
   172  	//	clos = &struct{F uintptr; R T}{T.M·f, x}
   173  	//
   174  	// Like walkClosure above.
   175  
   176  	if n.X.Type().IsInterface() {
   177  		// Trigger panic for method on nil interface now.
   178  		// Otherwise it happens in the wrapper and is confusing.
   179  		n.X = cheapExpr(n.X, init)
   180  		n.X = walkExpr(n.X, nil)
   181  
   182  		tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X)
   183  		check := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
   184  		init.Append(typecheck.Stmt(check))
   185  	}
   186  
   187  	typ := typecheck.MethodValueType(n)
   188  
   189  	clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
   190  	clos.SetEsc(n.Esc())
   191  	clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, methodValueWrapper(n)), n.X}
   192  
   193  	addr := typecheck.NodAddr(clos)
   194  	addr.SetEsc(n.Esc())
   195  
   196  	// Force type conversion from *struct to the func type.
   197  	cfn := typecheck.ConvNop(addr, n.Type())
   198  
   199  	// non-escaping temp to use, if any.
   200  	if x := n.Prealloc; x != nil {
   201  		if !types.Identical(typ, x.Type()) {
   202  			panic("partial call type does not match order's assigned type")
   203  		}
   204  		addr.Prealloc = x
   205  		n.Prealloc = nil
   206  	}
   207  
   208  	return walkExpr(cfn, init)
   209  }
   210  
   211  // methodValueWrapper returns the ONAME node representing the
   212  // wrapper function (*-fm) needed for the given method value. If the
   213  // wrapper function hasn't already been created yet, it's created and
   214  // added to typecheck.Target.Decls.
   215  func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
   216  	if dot.Op() != ir.OMETHVALUE {
   217  		base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op())
   218  	}
   219  
   220  	t0 := dot.Type()
   221  	meth := dot.Sel
   222  	rcvrtype := dot.X.Type()
   223  	sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
   224  
   225  	if sym.Uniq() {
   226  		return sym.Def.(*ir.Name)
   227  	}
   228  	sym.SetUniq(true)
   229  
   230  	if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
   231  		base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
   232  	}
   233  
   234  	savecurfn := ir.CurFunc
   235  	saveLineNo := base.Pos
   236  	ir.CurFunc = nil
   237  
   238  	base.Pos = base.AutogeneratedPos
   239  
   240  	tfn := ir.NewFuncType(base.Pos, nil,
   241  		typecheck.NewFuncParams(t0.Params(), true),
   242  		typecheck.NewFuncParams(t0.Results(), false))
   243  
   244  	fn := typecheck.DeclFunc(sym, tfn)
   245  	fn.SetDupok(true)
   246  	fn.SetWrapper(true)
   247  
   248  	// Declare and initialize variable holding receiver.
   249  	ptr := ir.NewHiddenParam(base.Pos, fn, typecheck.Lookup(".this"), rcvrtype)
   250  
   251  	call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
   252  	call.Args = ir.ParamNames(tfn.Type())
   253  	call.IsDDD = tfn.Type().IsVariadic()
   254  
   255  	var body ir.Node = call
   256  	if t0.NumResults() != 0 {
   257  		ret := ir.NewReturnStmt(base.Pos, nil)
   258  		ret.Results = []ir.Node{call}
   259  		body = ret
   260  	}
   261  
   262  	fn.Body = []ir.Node{body}
   263  	typecheck.FinishFuncBody()
   264  
   265  	typecheck.Func(fn)
   266  	// Need to typecheck the body of the just-generated wrapper.
   267  	// typecheckslice() requires that Curfn is set when processing an ORETURN.
   268  	ir.CurFunc = fn
   269  	typecheck.Stmts(fn.Body)
   270  	sym.Def = fn.Nname
   271  	typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
   272  	ir.CurFunc = savecurfn
   273  	base.Pos = saveLineNo
   274  
   275  	return fn.Nname
   276  }
   277  

View as plain text