go.tools/ssa: memoize synthesis of all wrapper methods.
methodIndex() utility was split and specialized to its two cases, *Interface vs *Named, which are logically quite different. We can't memoize promotion wrappers yet; we need typemap. Terminology: - "thunks" are now "wrappers" - "bridge methods" are now "promotion wrappers" Where the diff is messy it's just because of indentation. R=gri CC=golang-dev https://golang.org/cl/10282043
This commit is contained in:
parent
0f26bbae8f
commit
f1d4d01fed
|
|
@ -365,7 +365,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||||
// TypeName returns the type name for the named type t.
|
// TypeName returns the type name for the named type t.
|
||||||
func (t *Named) Obj() *TypeName { return t.obj }
|
func (t *Named) Obj() *TypeName { return t.obj }
|
||||||
|
|
||||||
// NumMethods returns the number of methods directly associated with named type t.
|
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||||
func (t *Named) NumMethods() int { return len(t.methods) }
|
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||||
|
|
||||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ package ssa
|
||||||
// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
|
// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
|
||||||
// (create.go), all packages are constructed and type-checked and
|
// (create.go), all packages are constructed and type-checked and
|
||||||
// definitions of all package members are created, method-sets are
|
// definitions of all package members are created, method-sets are
|
||||||
// computed, and bridge methods are synthesized. The create phase
|
// computed, and wrapper methods are synthesized. The create phase
|
||||||
// proceeds in topological order over the import dependency graph,
|
// proceeds in topological order over the import dependency graph,
|
||||||
// initiated by client calls to CreatePackages.
|
// initiated by client calls to CreatePackages.
|
||||||
//
|
//
|
||||||
|
|
@ -345,7 +345,7 @@ func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
|
||||||
if !wantAddr {
|
if !wantAddr {
|
||||||
if m, recv := b.findMethod(fn, e.X, id); m != nil {
|
if m, recv := b.findMethod(fn, e.X, id); m != nil {
|
||||||
c := &MakeClosure{
|
c := &MakeClosure{
|
||||||
Fn: makeBoundMethodThunk(fn.Prog, m, recv.Type()),
|
Fn: boundMethodWrapper(m),
|
||||||
Bindings: []Value{recv},
|
Bindings: []Value{recv},
|
||||||
}
|
}
|
||||||
c.setPos(e.Sel.Pos())
|
c.setPos(e.Sel.Pos())
|
||||||
|
|
@ -743,8 +743,8 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// T must be an interface; return method thunk.
|
// T must be an interface; return wrapper.
|
||||||
return makeImethodThunk(fn.Prog, typ, id)
|
return interfaceMethodWrapper(fn.Prog, typ, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// e.f where e is an expression. f may be a method.
|
// e.f where e is an expression. f may be a method.
|
||||||
|
|
@ -905,7 +905,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||||
// Case 4: x.f() where a dynamically dispatched call
|
// Case 4: x.f() where a dynamically dispatched call
|
||||||
// to an interface method f. f is a 'func' object in
|
// to an interface method f. f is a 'func' object in
|
||||||
// the Methods of types.Interface X
|
// the Methods of types.Interface X
|
||||||
c.Method, _ = methodIndex(t, id)
|
c.Method, _ = interfaceMethodIndex(t, id)
|
||||||
c.Recv = b.expr(fn, sel.X)
|
c.Recv = b.expr(fn, sel.X)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
|
||||||
methodSets: make(map[types.Type]MethodSet),
|
methodSets: make(map[types.Type]MethodSet),
|
||||||
concreteMethods: make(map[*types.Func]*Function),
|
concreteMethods: make(map[*types.Func]*Function),
|
||||||
indirectionWrappers: make(map[*Function]*Function),
|
indirectionWrappers: make(map[*Function]*Function),
|
||||||
|
boundMethodWrappers: make(map[*Function]*Function),
|
||||||
|
ifaceMethodWrappers: make(map[*types.Func]*Function),
|
||||||
mode: mode,
|
mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,8 +132,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
||||||
pkg.Members[name] = fn
|
pkg.Members[name] = fn
|
||||||
} else {
|
} else {
|
||||||
// Method declaration.
|
// Method declaration.
|
||||||
nt := recv.Type().Deref().(*types.Named)
|
_, method := namedTypeMethodIndex(
|
||||||
_, method := methodIndex(nt, MakeId(name, pkg.Types))
|
recv.Type().Deref().(*types.Named),
|
||||||
|
MakeId(name, pkg.Types))
|
||||||
pkg.Prog.concreteMethods[method] = fn
|
pkg.Prog.concreteMethods[method] = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
|
||||||
|
|
||||||
// emitTailCall emits to f a function call in tail position. The
|
// emitTailCall emits to f a function call in tail position. The
|
||||||
// caller is responsible for all fields of 'call' except its type.
|
// caller is responsible for all fields of 'call' except its type.
|
||||||
// Intended for delegating bridge methods.
|
// Intended for wrapper methods.
|
||||||
// Precondition: f does/will not use deferred procedure calls.
|
// Precondition: f does/will not use deferred procedure calls.
|
||||||
// Postcondition: f.currentBlock is nil.
|
// Postcondition: f.currentBlock is nil.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
12
ssa/func.go
12
ssa/func.go
|
|
@ -443,10 +443,10 @@ func (f *Function) emit(instr Instruction) Value {
|
||||||
// "math.IsNaN" // a package-level function
|
// "math.IsNaN" // a package-level function
|
||||||
// "IsNaN" // intra-package reference to same
|
// "IsNaN" // intra-package reference to same
|
||||||
// "(*sync.WaitGroup).Add" // a declared method
|
// "(*sync.WaitGroup).Add" // a declared method
|
||||||
// "(*exp/ssa.Ret).Block" // a bridge method
|
// "(*exp/ssa.Ret).Block" // a promotion wrapper method
|
||||||
// "(ssa.Instruction).Block" // an interface method thunk
|
// "(ssa.Instruction).Block" // an interface method wrapper
|
||||||
// "func@5.32" // an anonymous function
|
// "func@5.32" // an anonymous function
|
||||||
// "bound$(*T).f" // a bound method thunk
|
// "bound$(*T).f" // a bound method wrapper
|
||||||
//
|
//
|
||||||
func (f *Function) FullName() string {
|
func (f *Function) FullName() string {
|
||||||
return f.fullName(nil)
|
return f.fullName(nil)
|
||||||
|
|
@ -467,11 +467,11 @@ func (f *Function) fullName(from *Package) string {
|
||||||
if f.Pkg == nil {
|
if f.Pkg == nil {
|
||||||
var recvType types.Type
|
var recvType types.Type
|
||||||
if recv != nil {
|
if recv != nil {
|
||||||
recvType = recv.Type() // bridge method
|
recvType = recv.Type() // promotion wrapper
|
||||||
} else if strings.HasPrefix(f.name, "bound$") {
|
} else if strings.HasPrefix(f.name, "bound$") {
|
||||||
return f.name // bound method thunk
|
return f.name // bound method wrapper
|
||||||
} else {
|
} else {
|
||||||
recvType = f.Params[0].Type() // interface method thunk
|
recvType = f.Params[0].Type() // interface method wrapper
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("(%s).%s", recvType, f.name)
|
return fmt.Sprintf("(%s).%s", recvType, f.name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@ func (p *Package) DumpTo(w io.Writer) {
|
||||||
// We display only mset(*T) since its keys
|
// We display only mset(*T) since its keys
|
||||||
// are a superset of mset(T)'s keys, though the
|
// are a superset of mset(T)'s keys, though the
|
||||||
// methods themselves may differ,
|
// methods themselves may differ,
|
||||||
// e.g. different bridge methods.
|
// e.g. promotion wrappers.
|
||||||
// NB: if mem.Type() is a pointer, mset is empty.
|
// NB: if mem.Type() is a pointer, mset is empty.
|
||||||
mset := p.Prog.MethodSet(pointer(mem.Type()))
|
mset := p.Prog.MethodSet(pointer(mem.Type()))
|
||||||
var keys ids
|
var keys ids
|
||||||
|
|
|
||||||
184
ssa/promote.go
184
ssa/promote.go
|
|
@ -1,9 +1,15 @@
|
||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
// This file defines algorithms related to "promotion" of field and
|
// This file defines utilities for method-set computation, synthesis
|
||||||
// method selector expressions e.x, such as desugaring implicit field
|
// of wrapper methods, and desugaring of implicit field selections.
|
||||||
// and method selections, method-set computation, and construction of
|
//
|
||||||
// synthetic "bridge" methods.
|
// Wrappers include:
|
||||||
|
// - promotion wrappers for methods of embedded fields.
|
||||||
|
// - interface method wrappers for closures of I.f.
|
||||||
|
// - bound method wrappers, for uncalled obj.Method closures.
|
||||||
|
// - indirection wrappers, for calls to T-methods on a *T receiver.
|
||||||
|
|
||||||
|
// TODO(adonovan): rename to methods.go.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.tools/go/types"
|
"code.google.com/p/go.tools/go/types"
|
||||||
|
|
@ -90,9 +96,9 @@ func (c candidate) ptrRecv() bool {
|
||||||
return c.concrete != nil && isPointer(c.concrete.Signature.Recv().Type())
|
return c.concrete != nil && isPointer(c.concrete.Signature.Recv().Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodSet returns the method set for type typ,
|
// MethodSet returns the method set for type typ, building wrapper
|
||||||
// building bridge methods as needed for promoted methods
|
// methods as needed for embedded field promotion, and indirection for
|
||||||
// and indirection wrappers for *T receiver types.
|
// *T receiver types, etc.
|
||||||
// A nil result indicates an empty set.
|
// A nil result indicates an empty set.
|
||||||
//
|
//
|
||||||
// Thread-safe.
|
// Thread-safe.
|
||||||
|
|
@ -101,8 +107,8 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
p.methodSetsMu.Lock()
|
p.methodsMu.Lock()
|
||||||
defer p.methodSetsMu.Unlock()
|
defer p.methodsMu.Unlock()
|
||||||
|
|
||||||
// TODO(adonovan): Using Types as map keys doesn't properly
|
// TODO(adonovan): Using Types as map keys doesn't properly
|
||||||
// de-dup. e.g. *Named are canonical but *Struct and
|
// de-dup. e.g. *Named are canonical but *Struct and
|
||||||
|
|
@ -200,7 +206,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||||
list, next = next, list[:0] // reuse array
|
list, next = next, list[:0] // reuse array
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build method sets and bridge methods.
|
// Build method sets and wrapper methods.
|
||||||
mset := make(MethodSet)
|
mset := make(MethodSet)
|
||||||
for id, cand := range cands {
|
for id, cand := range cands {
|
||||||
if cand == nil {
|
if cand == nil {
|
||||||
|
|
@ -223,7 +229,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
||||||
method = indirectionWrapper(method)
|
method = indirectionWrapper(method)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
method = makeBridgeMethod(prog, typ, cand)
|
method = promotionWrapper(prog, typ, cand)
|
||||||
}
|
}
|
||||||
if method == nil {
|
if method == nil {
|
||||||
panic("unexpected nil method in method set")
|
panic("unexpected nil method in method set")
|
||||||
|
|
@ -251,7 +257,7 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeBridgeMethod creates a synthetic Function that delegates to a
|
// promotionWrapper returns a synthetic Function that delegates to a
|
||||||
// "promoted" method. For example, given these decls:
|
// "promoted" method. For example, given these decls:
|
||||||
//
|
//
|
||||||
// type A struct {B}
|
// type A struct {B}
|
||||||
|
|
@ -259,24 +265,25 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Func
|
||||||
// type C ...
|
// type C ...
|
||||||
// func (*C) f()
|
// func (*C) f()
|
||||||
//
|
//
|
||||||
// then makeBridgeMethod(typ=A, cand={method:(*C).f, path:[B,*C]}) will
|
// then promotionWrapper(typ=A, cand={method:(*C).f, path:[B,*C]}) will
|
||||||
// synthesize this bridge method:
|
// synthesize this wrapper method:
|
||||||
//
|
//
|
||||||
// func (a A) f() { return a.B.C->f() }
|
// func (a A) f() { return a.B.C->f() }
|
||||||
//
|
//
|
||||||
// prog is the program to which the synthesized method will belong.
|
// prog is the program to which the synthesized method will belong.
|
||||||
// typ is the receiver type of the bridge method. cand is the
|
// typ is the receiver type of the wrapper method. cand is the
|
||||||
// candidate method to be promoted; it may be concrete or an interface
|
// candidate method to be promoted; it may be concrete or an interface
|
||||||
// method.
|
// method.
|
||||||
//
|
//
|
||||||
func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function {
|
func promotionWrapper(prog *Program, typ types.Type, cand *candidate) *Function {
|
||||||
old := cand.method.Type().(*types.Signature)
|
old := cand.method.Type().(*types.Signature)
|
||||||
sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
|
sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
|
||||||
|
|
||||||
|
// TODO(adonovan): consult memoization cache keyed by (typ, cand).
|
||||||
|
// Needs typemap. Also needs hash/eq functions for 'candidate'.
|
||||||
if prog.mode&LogSource != 0 {
|
if prog.mode&LogSource != 0 {
|
||||||
defer logStack("makeBridgeMethod %s, %s, type %s", typ, cand, sig)()
|
defer logStack("promotionWrapper %s, %s, type %s", typ, cand, sig)()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn := &Function{
|
fn := &Function{
|
||||||
name: cand.method.Name(),
|
name: cand.method.Name(),
|
||||||
Signature: sig,
|
Signature: sig,
|
||||||
|
|
@ -286,7 +293,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
||||||
fn.addSpilledParam(sig.Recv())
|
fn.addSpilledParam(sig.Recv())
|
||||||
createParams(fn)
|
createParams(fn)
|
||||||
|
|
||||||
// Each bridge method performs a sequence of selections,
|
// Each promotion wrapper performs a sequence of selections,
|
||||||
// then tailcalls the promoted method.
|
// then tailcalls the promoted method.
|
||||||
// We use pointer arithmetic (FieldAddr possibly followed by
|
// We use pointer arithmetic (FieldAddr possibly followed by
|
||||||
// Load) in preference to value extraction (Field possibly
|
// Load) in preference to value extraction (Field possibly
|
||||||
|
|
@ -320,14 +327,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
||||||
c.Call.Func = cand.concrete
|
c.Call.Func = cand.concrete
|
||||||
c.Call.Args = append(c.Call.Args, v)
|
c.Call.Args = append(c.Call.Args, v)
|
||||||
} else {
|
} else {
|
||||||
c.Call.Method = -1
|
|
||||||
iface := v.Type().Underlying().(*types.Interface)
|
iface := v.Type().Underlying().(*types.Interface)
|
||||||
for i, n := 0, iface.NumMethods(); i < n; i++ {
|
id := MakeId(cand.method.Name(), cand.method.Pkg())
|
||||||
if iface.Method(i) == cand.method {
|
c.Call.Method, _ = interfaceMethodIndex(iface, id)
|
||||||
c.Call.Method = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Call.Recv = v
|
c.Call.Recv = v
|
||||||
}
|
}
|
||||||
for _, arg := range fn.Params[1:] {
|
for _, arg := range fn.Params[1:] {
|
||||||
|
|
@ -338,7 +340,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// createParams creates parameters for bridge method fn based on its
|
// createParams creates parameters for wrapper method fn based on its
|
||||||
// Signature.Params, which do not include the receiver.
|
// Signature.Params, which do not include the receiver.
|
||||||
//
|
//
|
||||||
func createParams(fn *Function) {
|
func createParams(fn *Function) {
|
||||||
|
|
@ -352,18 +354,18 @@ func createParams(fn *Function) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thunks for standalone interface methods ----------------------------------------
|
// Wrappers for standalone interface methods ----------------------------------
|
||||||
|
|
||||||
// makeImethodThunk returns a synthetic thunk function permitting a
|
// interfaceMethodWrapper returns a synthetic wrapper function permitting a
|
||||||
// method id of interface typ to be called like a standalone function,
|
// method id of interface typ to be called like a standalone function,
|
||||||
// e.g.:
|
// e.g.:
|
||||||
//
|
//
|
||||||
// type I interface { f(x int) R }
|
// type I interface { f(x int) R }
|
||||||
// m := I.f // thunk
|
// m := I.f // wrapper
|
||||||
// var i I
|
// var i I
|
||||||
// m(i, 0)
|
// m(i, 0)
|
||||||
//
|
//
|
||||||
// The thunk is defined as if by:
|
// The wrapper is defined as if by:
|
||||||
//
|
//
|
||||||
// func I.f(i I, x int, ...) R {
|
// func I.f(i I, x int, ...) R {
|
||||||
// return i.f(x, ...)
|
// return i.f(x, ...)
|
||||||
|
|
@ -372,39 +374,49 @@ func createParams(fn *Function) {
|
||||||
// TODO(adonovan): opt: currently the stub is created even when used
|
// TODO(adonovan): opt: currently the stub is created even when used
|
||||||
// in call position: I.f(i, 0). Clearly this is suboptimal.
|
// in call position: I.f(i, 0). Clearly this is suboptimal.
|
||||||
//
|
//
|
||||||
// TODO(adonovan): memoize creation of these functions in the Program.
|
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
|
||||||
//
|
//
|
||||||
func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
|
func interfaceMethodWrapper(prog *Program, typ types.Type, id Id) *Function {
|
||||||
if prog.mode&LogSource != 0 {
|
index, meth := interfaceMethodIndex(typ.Underlying().(*types.Interface), id)
|
||||||
defer logStack("makeImethodThunk %s.%s", typ, id)()
|
prog.methodsMu.Lock()
|
||||||
|
defer prog.methodsMu.Unlock()
|
||||||
|
// If one interface embeds another they'll share the same
|
||||||
|
// wrappers for common methods. This is safe, but it might
|
||||||
|
// confuse some tools because of the implicit interface
|
||||||
|
// conversion applied to the first argument. If this becomes
|
||||||
|
// a problem, we should include 'typ' in the memoization key.
|
||||||
|
fn, ok := prog.ifaceMethodWrappers[meth]
|
||||||
|
if !ok {
|
||||||
|
if prog.mode&LogSource != 0 {
|
||||||
|
defer logStack("interfaceMethodWrapper %s.%s", typ, id)()
|
||||||
|
}
|
||||||
|
fn = &Function{
|
||||||
|
name: meth.Name(),
|
||||||
|
Signature: meth.Type().(*types.Signature),
|
||||||
|
Prog: prog,
|
||||||
|
}
|
||||||
|
fn.startBody()
|
||||||
|
fn.addParam("recv", typ, token.NoPos)
|
||||||
|
createParams(fn)
|
||||||
|
var c Call
|
||||||
|
c.Call.Method = index
|
||||||
|
c.Call.Recv = fn.Params[0]
|
||||||
|
for _, arg := range fn.Params[1:] {
|
||||||
|
c.Call.Args = append(c.Call.Args, arg)
|
||||||
|
}
|
||||||
|
emitTailCall(fn, &c)
|
||||||
|
fn.finishBody()
|
||||||
|
|
||||||
|
prog.ifaceMethodWrappers[meth] = fn
|
||||||
}
|
}
|
||||||
itf := typ.Underlying().(*types.Interface)
|
|
||||||
index, meth := methodIndex(itf, id)
|
|
||||||
sig := *meth.Type().(*types.Signature) // copy; shared Values
|
|
||||||
fn := &Function{
|
|
||||||
name: meth.Name(),
|
|
||||||
Signature: &sig,
|
|
||||||
Prog: prog,
|
|
||||||
}
|
|
||||||
fn.startBody()
|
|
||||||
fn.addParam("recv", typ, token.NoPos)
|
|
||||||
createParams(fn)
|
|
||||||
var c Call
|
|
||||||
c.Call.Method = index
|
|
||||||
c.Call.Recv = fn.Params[0]
|
|
||||||
for _, arg := range fn.Params[1:] {
|
|
||||||
c.Call.Args = append(c.Call.Args, arg)
|
|
||||||
}
|
|
||||||
emitTailCall(fn, &c)
|
|
||||||
fn.finishBody()
|
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thunks for bound methods ----------------------------------------
|
// Wrappers for bound methods -------------------------------------------------
|
||||||
|
|
||||||
// makeBoundMethodThunk returns a synthetic thunk function that
|
// boundMethodWrapper returns a synthetic wrapper function that
|
||||||
// delegates to a concrete method. The thunk has one free variable,
|
// delegates to a concrete method. The wrapper has one free variable,
|
||||||
// the method's receiver. Use MakeClosure with such a thunk to
|
// the method's receiver. Use MakeClosure with such a wrapper to
|
||||||
// construct a bound-method closure.
|
// construct a bound-method closure.
|
||||||
// e.g.:
|
// e.g.:
|
||||||
//
|
//
|
||||||
|
|
@ -414,35 +426,43 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
|
||||||
// f := t.meth
|
// f := t.meth
|
||||||
// f() // calls t.meth()
|
// f() // calls t.meth()
|
||||||
//
|
//
|
||||||
// f is a closure of a synthetic thunk defined as if by:
|
// f is a closure of a synthetic wrapper defined as if by:
|
||||||
//
|
//
|
||||||
// f := func() { return t.meth() }
|
// f := func() { return t.meth() }
|
||||||
//
|
//
|
||||||
// TODO(adonovan): memoize creation of these functions in the Program.
|
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
|
||||||
//
|
//
|
||||||
func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *Function {
|
func boundMethodWrapper(meth *Function) *Function {
|
||||||
if prog.mode&LogSource != 0 {
|
prog := meth.Prog
|
||||||
defer logStack("makeBoundMethodThunk %s", meth)()
|
prog.methodsMu.Lock()
|
||||||
}
|
defer prog.methodsMu.Unlock()
|
||||||
s := meth.Signature
|
fn, ok := prog.boundMethodWrappers[meth]
|
||||||
fn := &Function{
|
if !ok {
|
||||||
name: "bound$" + meth.FullName(),
|
if prog.mode&LogSource != 0 {
|
||||||
Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv
|
defer logStack("boundMethodWrapper %s", meth)()
|
||||||
Prog: prog,
|
}
|
||||||
}
|
s := meth.Signature
|
||||||
|
fn = &Function{
|
||||||
|
name: "bound$" + meth.FullName(),
|
||||||
|
Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv
|
||||||
|
Prog: prog,
|
||||||
|
}
|
||||||
|
|
||||||
cap := &Capture{name: "recv", typ: recvType, parent: fn}
|
cap := &Capture{name: "recv", typ: s.Recv().Type(), parent: fn}
|
||||||
fn.FreeVars = []*Capture{cap}
|
fn.FreeVars = []*Capture{cap}
|
||||||
fn.startBody()
|
fn.startBody()
|
||||||
createParams(fn)
|
createParams(fn)
|
||||||
var c Call
|
var c Call
|
||||||
c.Call.Func = meth
|
c.Call.Func = meth
|
||||||
c.Call.Args = []Value{cap}
|
c.Call.Args = []Value{cap}
|
||||||
for _, arg := range fn.Params {
|
for _, arg := range fn.Params {
|
||||||
c.Call.Args = append(c.Call.Args, arg)
|
c.Call.Args = append(c.Call.Args, arg)
|
||||||
|
}
|
||||||
|
emitTailCall(fn, &c)
|
||||||
|
fn.finishBody()
|
||||||
|
|
||||||
|
prog.boundMethodWrappers[meth] = fn
|
||||||
}
|
}
|
||||||
emitTailCall(fn, &c)
|
|
||||||
fn.finishBody()
|
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,7 +475,7 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *F
|
||||||
// return (*recv).f(...)
|
// return (*recv).f(...)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodSetsMu)
|
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodsMu)
|
||||||
//
|
//
|
||||||
func indirectionWrapper(meth *Function) *Function {
|
func indirectionWrapper(meth *Function) *Function {
|
||||||
prog := meth.Prog
|
prog := meth.Prog
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ type Program struct {
|
||||||
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
|
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
|
||||||
mode BuilderMode // set of mode bits for SSA construction
|
mode BuilderMode // set of mode bits for SSA construction
|
||||||
|
|
||||||
methodSetsMu sync.Mutex // guards methodSets, indirectionWrappers
|
methodsMu sync.Mutex // guards the following maps:
|
||||||
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
|
methodSets map[types.Type]MethodSet // concrete method set each type [TODO(adonovan): de-dup]
|
||||||
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
|
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
|
||||||
|
boundMethodWrappers map[*Function]*Function // wrappers for curried x.Method closures
|
||||||
|
ifaceMethodWrappers map[*types.Func]*Function // wrappers for curried I.Method functions
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Package is a single analyzed Go package containing Members for
|
// A Package is a single analyzed Go package containing Members for
|
||||||
|
|
|
||||||
34
ssa/util.go
34
ssa/util.go
|
|
@ -55,17 +55,31 @@ func pointer(typ types.Type) *types.Pointer {
|
||||||
return types.NewPointer(typ)
|
return types.NewPointer(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// methodIndex returns the method (and its index) named id within the
|
// namedTypeMethodIndex returns the method (and its index) named id
|
||||||
// method table of named or interface type typ. If not found,
|
// within the set of explicitly declared concrete methods of named
|
||||||
// panic ensues.
|
// type typ. If not found, panic ensues.
|
||||||
//
|
//
|
||||||
func methodIndex(typ types.Type, id Id) (int, *types.Func) {
|
// TODO(gri): move this functionality into the go/types API?
|
||||||
t := typ.(interface {
|
//
|
||||||
NumMethods() int
|
func namedTypeMethodIndex(typ *types.Named, id Id) (int, *types.Func) {
|
||||||
Method(i int) *types.Func
|
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||||
})
|
m := typ.Method(i)
|
||||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
if MakeId(m.Name(), m.Pkg()) == id {
|
||||||
m := t.Method(i)
|
return i, m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Sprint("method not found: ", id, " in named type ", typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
// interfaceMethodIndex returns the method (and its index) named id
|
||||||
|
// within the method-set of interface type typ. If not found, panic
|
||||||
|
// ensues.
|
||||||
|
//
|
||||||
|
// TODO(gri): move this functionality into the go/types API.
|
||||||
|
//
|
||||||
|
func interfaceMethodIndex(typ *types.Interface, id Id) (int, *types.Func) {
|
||||||
|
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||||
|
m := typ.Method(i)
|
||||||
if MakeId(m.Name(), m.Pkg()) == id {
|
if MakeId(m.Name(), m.Pkg()) == id {
|
||||||
return i, m
|
return i, m
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue