github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/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/go-asm/go/cmd/compile/base" 9 "github.com/go-asm/go/cmd/compile/ir" 10 "github.com/go-asm/go/cmd/compile/typecheck" 11 "github.com/go-asm/go/cmd/compile/types" 12 "github.com/go-asm/go/cmd/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.Fun.(*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), types.NewPtr(v.Type())) 51 addr.Curfn = clofn 52 v.Heapaddr = addr 53 v = addr 54 } 55 56 v.Class = ir.PPARAM 57 decls = append(decls, v) 58 59 fld := types.NewField(src.NoXPos, v.Sym(), v.Type()) 60 fld.Nname = v 61 params = append(params, fld) 62 } 63 64 // f is ONAME of the actual function. 65 f := clofn.Nname 66 typ := f.Type() 67 68 // Create new function type with parameters prepended, and 69 // then update type and declarations. 70 typ = types.NewSignature(nil, append(params, typ.Params()...), typ.Results()) 71 f.SetType(typ) 72 clofn.Dcl = append(decls, clofn.Dcl...) 73 74 // Rewrite call. 75 n.Fun = f 76 n.Args.Prepend(closureArgs(clo)...) 77 78 // Update the call expression's type. We need to do this 79 // because typecheck gave it the result type of the OCLOSURE 80 // node, but we only rewrote the ONAME node's type. Logically, 81 // they're the same, but the stack offsets probably changed. 82 if typ.NumResults() == 1 { 83 n.SetType(typ.Result(0).Type) 84 } else { 85 n.SetType(typ.ResultsTuple()) 86 } 87 88 // Add to Closures for enqueueFunc. It's no longer a proper 89 // closure, but we may have already skipped over it in the 90 // functions list as a non-trivial closure, so this just 91 // ensures it's compiled. 92 ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn) 93 } 94 95 func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node { 96 clofn := clo.Func 97 98 // If no closure vars, don't bother wrapping. 99 if ir.IsTrivialClosure(clo) { 100 if base.Debug.Closure > 0 { 101 base.WarnfAt(clo.Pos(), "closure converted to global") 102 } 103 return clofn.Nname 104 } 105 106 // The closure is not trivial or directly called, so it's going to stay a closure. 107 ir.ClosureDebugRuntimeCheck(clo) 108 clofn.SetNeedctxt(true) 109 110 // The closure expression may be walked more than once if it appeared in composite 111 // literal initialization (e.g, see issue #49029). 112 // 113 // Don't add the closure function to compilation queue more than once, since when 114 // compiling a function twice would lead to an ICE. 115 if !clofn.Walked() { 116 clofn.SetWalked(true) 117 ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn) 118 } 119 120 typ := typecheck.ClosureType(clo) 121 122 clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil) 123 clos.SetEsc(clo.Esc()) 124 clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...) 125 for i, value := range clos.List { 126 clos.List[i] = ir.NewStructKeyExpr(base.Pos, typ.Field(i), value) 127 } 128 129 addr := typecheck.NodAddr(clos) 130 addr.SetEsc(clo.Esc()) 131 132 // Force type conversion from *struct to the func type. 133 cfn := typecheck.ConvNop(addr, clo.Type()) 134 135 // non-escaping temp to use, if any. 136 if x := clo.Prealloc; x != nil { 137 if !types.Identical(typ, x.Type()) { 138 panic("closure type does not match order's assigned type") 139 } 140 addr.Prealloc = x 141 clo.Prealloc = nil 142 } 143 144 return walkExpr(cfn, init) 145 } 146 147 // closureArgs returns a slice of expressions that can be used to 148 // initialize the given closure's free variables. These correspond 149 // one-to-one with the variables in clo.Func.ClosureVars, and will be 150 // either an ONAME node (if the variable is captured by value) or an 151 // OADDR-of-ONAME node (if not). 152 func closureArgs(clo *ir.ClosureExpr) []ir.Node { 153 fn := clo.Func 154 155 args := make([]ir.Node, len(fn.ClosureVars)) 156 for i, v := range fn.ClosureVars { 157 var outer ir.Node 158 outer = v.Outer 159 if !v.Byval() { 160 outer = typecheck.NodAddrAt(fn.Pos(), outer) 161 } 162 args[i] = typecheck.Expr(outer) 163 } 164 return args 165 } 166 167 func walkMethodValue(n *ir.SelectorExpr, init *ir.Nodes) ir.Node { 168 // Create closure in the form of a composite literal. 169 // For x.M with receiver (x) type T, the generated code looks like: 170 // 171 // clos = &struct{F uintptr; R T}{T.M·f, x} 172 // 173 // Like walkClosure above. 174 175 if n.X.Type().IsInterface() { 176 // Trigger panic for method on nil interface now. 177 // Otherwise it happens in the wrapper and is confusing. 178 n.X = cheapExpr(n.X, init) 179 n.X = walkExpr(n.X, nil) 180 181 tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X) 182 check := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab) 183 init.Append(typecheck.Stmt(check)) 184 } 185 186 typ := typecheck.MethodValueType(n) 187 188 clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil) 189 clos.SetEsc(n.Esc()) 190 clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, methodValueWrapper(n)), n.X} 191 192 addr := typecheck.NodAddr(clos) 193 addr.SetEsc(n.Esc()) 194 195 // Force type conversion from *struct to the func type. 196 cfn := typecheck.ConvNop(addr, n.Type()) 197 198 // non-escaping temp to use, if any. 199 if x := n.Prealloc; x != nil { 200 if !types.Identical(typ, x.Type()) { 201 panic("partial call type does not match order's assigned type") 202 } 203 addr.Prealloc = x 204 n.Prealloc = nil 205 } 206 207 return walkExpr(cfn, init) 208 } 209 210 // methodValueWrapper returns the ONAME node representing the 211 // wrapper function (*-fm) needed for the given method value. If the 212 // wrapper function hasn't already been created yet, it's created and 213 // added to typecheck.Target.Decls. 214 func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name { 215 if dot.Op() != ir.OMETHVALUE { 216 base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op()) 217 } 218 219 meth := dot.Sel 220 rcvrtype := dot.X.Type() 221 sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm") 222 223 if sym.Uniq() { 224 return sym.Def.(*ir.Name) 225 } 226 sym.SetUniq(true) 227 228 base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth) 229 panic("unreachable") 230 }