github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/walk/closure.go (about) 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 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 9 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 12 "github.com/bir3/gocompiler/src/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, 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, 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 { 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 fn := typecheck.DeclFunc(sym, nil, 241 typecheck.NewFuncParams(t0.Params(), true), 242 typecheck.NewFuncParams(t0.Results(), false)) 243 fn.SetDupok(true) 244 fn.SetWrapper(true) 245 246 // Declare and initialize variable holding receiver. 247 ptr := ir.NewHiddenParam(base.Pos, fn, typecheck.Lookup(".this"), rcvrtype) 248 249 call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil) 250 call.Args = ir.ParamNames(fn.Type()) 251 call.IsDDD = fn.Type().IsVariadic() 252 253 var body ir.Node = call 254 if t0.NumResults() != 0 { 255 ret := ir.NewReturnStmt(base.Pos, nil) 256 ret.Results = []ir.Node{call} 257 body = ret 258 } 259 260 fn.Body = []ir.Node{body} 261 typecheck.FinishFuncBody() 262 263 typecheck.Func(fn) 264 // Need to typecheck the body of the just-generated wrapper. 265 // typecheckslice() requires that Curfn is set when processing an ORETURN. 266 ir.CurFunc = fn 267 typecheck.Stmts(fn.Body) 268 sym.Def = fn.Nname 269 typecheck.Target.Decls = append(typecheck.Target.Decls, fn) 270 ir.CurFunc = savecurfn 271 base.Pos = saveLineNo 272 273 return fn.Nname 274 }