github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/wrappers.go (about) 1 // Copyright 2013 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 ir 6 7 // This file defines synthesis of Functions that delegate to declared 8 // methods; they come in three kinds: 9 // 10 // (1) wrappers: methods that wrap declared methods, performing 11 // implicit pointer indirections and embedded field selections. 12 // 13 // (2) thunks: funcs that wrap declared methods. Like wrappers, 14 // thunks perform indirections and field selections. The thunk's 15 // first parameter is used as the receiver for the method call. 16 // 17 // (3) bounds: funcs that wrap declared methods. The bound's sole 18 // free variable, supplied by a closure, is used as the receiver 19 // for the method call. No indirections or field selections are 20 // performed since they can be done before the call. 21 22 import ( 23 "fmt" 24 "go/types" 25 ) 26 27 // -- wrappers ----------------------------------------------------------- 28 29 // makeWrapper returns a synthetic method that delegates to the 30 // declared method denoted by meth.Obj(), first performing any 31 // necessary pointer indirections or field selections implied by meth. 32 // 33 // The resulting method's receiver type is meth.Recv(). 34 // 35 // This function is versatile but quite subtle! Consider the 36 // following axes of variation when making changes: 37 // - optional receiver indirection 38 // - optional implicit field selections 39 // - meth.Obj() may denote a concrete or an interface method 40 // - the result may be a thunk or a wrapper. 41 // 42 // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) 43 func makeWrapper(prog *Program, sel *types.Selection) *Function { 44 obj := sel.Obj().(*types.Func) // the declared function 45 sig := sel.Type().(*types.Signature) // type of this wrapper 46 47 var recv *types.Var // wrapper's receiver or thunk's params[0] 48 name := obj.Name() 49 var description Synthetic 50 var start int // first regular param 51 if sel.Kind() == types.MethodExpr { 52 name += "$thunk" 53 description = SyntheticThunk 54 recv = sig.Params().At(0) 55 start = 1 56 } else { 57 description = SyntheticWrapper 58 recv = sig.Recv() 59 } 60 61 if prog.mode&LogSource != 0 { 62 defer logStack("make %s to (%s)", description, recv.Type())() 63 } 64 fn := &Function{ 65 name: name, 66 method: sel, 67 object: obj, 68 Signature: sig, 69 Synthetic: description, 70 Prog: prog, 71 functionBody: new(functionBody), 72 } 73 fn.initHTML(prog.PrintFunc) 74 fn.startBody() 75 fn.addSpilledParam(recv, nil) 76 createParams(fn, start) 77 78 indices := sel.Index() 79 80 var v Value = fn.Locals[0] // spilled receiver 81 if isPointer(sel.Recv()) { 82 v = emitLoad(fn, v, nil) 83 84 // For simple indirection wrappers, perform an informative nil-check: 85 // "value method (T).f called using nil *T pointer" 86 if len(indices) == 1 && !isPointer(recvType(obj)) { 87 var c Call 88 c.Call.Value = &Builtin{ 89 name: "ir:wrapnilchk", 90 sig: types.NewSignatureType(nil, nil, nil, 91 types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)), 92 types.NewTuple(anonVar(sel.Recv())), false), 93 } 94 c.Call.Args = []Value{ 95 v, 96 emitConst(fn, stringConst(deref(sel.Recv()).String(), nil)), 97 emitConst(fn, stringConst(sel.Obj().Name(), nil)), 98 } 99 c.setType(v.Type()) 100 v = fn.emit(&c, nil) 101 } 102 } 103 104 // Invariant: v is a pointer, either 105 // value of *A receiver param, or 106 // address of A spilled receiver. 107 108 // We use pointer arithmetic (FieldAddr possibly followed by 109 // Load) in preference to value extraction (Field possibly 110 // preceded by Load). 111 112 v = emitImplicitSelections(fn, v, indices[:len(indices)-1], nil) 113 114 // Invariant: v is a pointer, either 115 // value of implicit *C field, or 116 // address of implicit C field. 117 118 var c Call 119 if r := recvType(obj); !isInterface(r) { // concrete method 120 if !isPointer(r) { 121 v = emitLoad(fn, v, nil) 122 } 123 c.Call.Value = prog.declaredFunc(obj) 124 c.Call.Args = append(c.Call.Args, v) 125 } else { 126 c.Call.Method = obj 127 c.Call.Value = emitLoad(fn, v, nil) 128 } 129 for _, arg := range fn.Params[1:] { 130 c.Call.Args = append(c.Call.Args, arg) 131 } 132 emitTailCall(fn, &c, nil) 133 fn.finishBody() 134 return fn 135 } 136 137 // createParams creates parameters for wrapper method fn based on its 138 // Signature.Params, which do not include the receiver. 139 // start is the index of the first regular parameter to use. 140 func createParams(fn *Function, start int) { 141 tparams := fn.Signature.Params() 142 for i, n := start, tparams.Len(); i < n; i++ { 143 fn.addParamObj(tparams.At(i), nil) 144 } 145 } 146 147 // -- bounds ----------------------------------------------------------- 148 149 // makeBound returns a bound method wrapper (or "bound"), a synthetic 150 // function that delegates to a concrete or interface method denoted 151 // by obj. The resulting function has no receiver, but has one free 152 // variable which will be used as the method's receiver in the 153 // tail-call. 154 // 155 // Use MakeClosure with such a wrapper to construct a bound method 156 // closure. e.g.: 157 // 158 // type T int or: type T interface { meth() } 159 // func (t T) meth() 160 // var t T 161 // f := t.meth 162 // f() // calls t.meth() 163 // 164 // f is a closure of a synthetic wrapper defined as if by: 165 // 166 // f := func() { return t.meth() } 167 // 168 // Unlike makeWrapper, makeBound need perform no indirection or field 169 // selections because that can be done before the closure is 170 // constructed. 171 // 172 // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) 173 func makeBound(prog *Program, obj *types.Func) *Function { 174 prog.methodsMu.Lock() 175 defer prog.methodsMu.Unlock() 176 fn, ok := prog.bounds[obj] 177 if !ok { 178 if prog.mode&LogSource != 0 { 179 defer logStack("%s", SyntheticBound)() 180 } 181 fn = &Function{ 182 name: obj.Name() + "$bound", 183 object: obj, 184 Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver 185 Synthetic: SyntheticBound, 186 Prog: prog, 187 functionBody: new(functionBody), 188 } 189 fn.initHTML(prog.PrintFunc) 190 191 fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} 192 fn.FreeVars = []*FreeVar{fv} 193 fn.startBody() 194 createParams(fn, 0) 195 var c Call 196 197 if !isInterface(recvType(obj)) { // concrete 198 c.Call.Value = prog.declaredFunc(obj) 199 c.Call.Args = []Value{fv} 200 } else { 201 c.Call.Value = fv 202 c.Call.Method = obj 203 } 204 for _, arg := range fn.Params { 205 c.Call.Args = append(c.Call.Args, arg) 206 } 207 emitTailCall(fn, &c, nil) 208 fn.finishBody() 209 210 prog.bounds[obj] = fn 211 } 212 return fn 213 } 214 215 // -- thunks ----------------------------------------------------------- 216 217 // makeThunk returns a thunk, a synthetic function that delegates to a 218 // concrete or interface method denoted by sel.Obj(). The resulting 219 // function has no receiver, but has an additional (first) regular 220 // parameter. 221 // 222 // Precondition: sel.Kind() == types.MethodExpr. 223 // 224 // type T int or: type T interface { meth() } 225 // func (t T) meth() 226 // f := T.meth 227 // var t T 228 // f(t) // calls t.meth() 229 // 230 // f is a synthetic wrapper defined as if by: 231 // 232 // f := func(t T) { return t.meth() } 233 // 234 // TODO(adonovan): opt: currently the stub is created even when used 235 // directly in a function call: C.f(i, 0). This is less efficient 236 // than inlining the stub. 237 // 238 // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) 239 func makeThunk(prog *Program, sel *types.Selection) *Function { 240 if sel.Kind() != types.MethodExpr { 241 panic(sel) 242 } 243 244 key := selectionKey{ 245 kind: sel.Kind(), 246 recv: sel.Recv(), 247 obj: sel.Obj(), 248 index: fmt.Sprint(sel.Index()), 249 indirect: sel.Indirect(), 250 } 251 252 prog.methodsMu.Lock() 253 defer prog.methodsMu.Unlock() 254 255 // Canonicalize key.recv to avoid constructing duplicate thunks. 256 canonRecv, ok := prog.canon.At(key.recv) 257 if !ok { 258 canonRecv = key.recv 259 prog.canon.Set(key.recv, canonRecv) 260 } 261 key.recv = canonRecv 262 263 fn, ok := prog.thunks[key] 264 if !ok { 265 fn = makeWrapper(prog, sel) 266 if fn.Signature.Recv() != nil { 267 panic(fn) // unexpected receiver 268 } 269 prog.thunks[key] = fn 270 } 271 return fn 272 } 273 274 func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { 275 return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic()) 276 } 277 278 // selectionKey is like types.Selection but a usable map key. 279 type selectionKey struct { 280 kind types.SelectionKind 281 recv types.Type // canonicalized via Program.canon 282 obj types.Object 283 index string 284 indirect bool 285 } 286 287 // makeInstance creates a wrapper function with signature sig that calls the generic function fn. 288 // If targs is not nil, fn is a function and targs describes the concrete type arguments. 289 // If targs is nil, fn is a method and the type arguments are derived from the receiver. 290 func makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *types.TypeList) *Function { 291 if sig.Recv() != nil { 292 assert(targs == nil) 293 // Methods don't have their own type parameters, but the receiver does 294 targs = deref(sig.Recv().Type()).(*types.Named).TypeArgs() 295 } else { 296 assert(targs != nil) 297 } 298 299 wrapper := fn.generics.At(targs) 300 if wrapper != nil { 301 return wrapper 302 } 303 304 var name string 305 if sig.Recv() != nil { 306 name = fn.name 307 } else { 308 name = fmt.Sprintf("%s$generic#%d", fn.name, fn.generics.Len()) 309 } 310 w := &Function{ 311 name: name, 312 object: fn.object, 313 Signature: sig, 314 Synthetic: SyntheticGeneric, 315 Prog: prog, 316 functionBody: new(functionBody), 317 } 318 w.initHTML(prog.PrintFunc) 319 w.startBody() 320 if sig.Recv() != nil { 321 w.addParamObj(sig.Recv(), nil) 322 } 323 createParams(w, 0) 324 var c Call 325 c.Call.Value = fn 326 tresults := fn.Signature.Results() 327 if tresults.Len() == 1 { 328 c.typ = tresults.At(0).Type() 329 } else { 330 c.typ = tresults 331 } 332 333 changeType := func(v Value, typ types.Type) Value { 334 if types.Identical(v.Type(), typ) { 335 return v 336 } 337 var c ChangeType 338 c.X = v 339 c.typ = typ 340 return w.emit(&c, nil) 341 } 342 343 for i, arg := range w.Params { 344 if sig.Recv() != nil { 345 if i == 0 { 346 c.Call.Args = append(c.Call.Args, changeType(w.Params[0], fn.Signature.Recv().Type())) 347 } else { 348 c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i-1).Type())) 349 } 350 } else { 351 c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i).Type())) 352 } 353 } 354 for i := 0; i < targs.Len(); i++ { 355 arg := targs.At(i) 356 c.Call.TypeArgs = append(c.Call.TypeArgs, arg) 357 } 358 results := w.emit(&c, nil) 359 var ret Return 360 switch tresults.Len() { 361 case 0: 362 case 1: 363 ret.Results = []Value{changeType(results, sig.Results().At(0).Type())} 364 default: 365 for i := 0; i < tresults.Len(); i++ { 366 v := emitExtract(w, results, i, nil) 367 ret.Results = append(ret.Results, changeType(v, sig.Results().At(i).Type())) 368 } 369 } 370 371 w.Exit = w.newBasicBlock("exit") 372 emitJump(w, w.Exit, nil) 373 w.currentBlock = w.Exit 374 w.emit(&ret, nil) 375 w.currentBlock = nil 376 377 w.finishBody() 378 379 fn.generics.Set(targs, w) 380 return w 381 }