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