mirror of
https://github.com/golang/go
synced 2024-11-18 16:14:46 -07:00
go.tools/ssa: opt: create elements of method sets lazily.
Reduces by 94% the number of wrappers created during the tests. (No appreciable difference in running time, sadly.) R=gri CC=golang-dev https://golang.org/cl/11619043
This commit is contained in:
parent
2bff3a03e7
commit
7eafcedc87
@ -686,9 +686,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
||||
// (*T).f or T.f, the method f from the method-set of type T.
|
||||
if fn.Pkg.info.IsType(e.X) {
|
||||
typ := fn.Pkg.typeOf(e.X)
|
||||
// TODO(adonovan): opt: it's overkill to
|
||||
// generate the entire method set here.
|
||||
if m := fn.Prog.MethodSet(typ)[id]; m != nil {
|
||||
if m := fn.Prog.LookupMethod(typ, id); m != nil {
|
||||
return emitConv(fn, m, fn.Pkg.typeOf(e))
|
||||
}
|
||||
|
||||
@ -776,9 +774,7 @@ func (b *builder) findMethod(fn *Function, base ast.Expr, id string) (*Function,
|
||||
typ := fn.Pkg.typeOf(base)
|
||||
|
||||
// Consult method-set of X.
|
||||
// TODO(adonovan): opt: it's overkill to generate the entire
|
||||
// method set here.
|
||||
if m := fn.Prog.MethodSet(typ)[id]; m != nil {
|
||||
if m := fn.Prog.LookupMethod(typ, id); m != nil {
|
||||
aptr := isPointer(typ)
|
||||
fptr := isPointer(m.Signature.Recv().Type())
|
||||
if aptr == fptr {
|
||||
@ -791,9 +787,7 @@ func (b *builder) findMethod(fn *Function, base ast.Expr, id string) (*Function,
|
||||
}
|
||||
if !isPointer(typ) {
|
||||
// Consult method-set of *X.
|
||||
// TODO(adonovan): opt: it's overkill to generate the
|
||||
// entire method set here.
|
||||
if m := fn.Prog.MethodSet(types.NewPointer(typ))[id]; m != nil {
|
||||
if m := fn.Prog.LookupMethod(types.NewPointer(typ), id); m != nil {
|
||||
// A method found only in MS(*X) must have a
|
||||
// pointer formal receiver; but the actual
|
||||
// value is not a pointer.
|
||||
|
@ -23,14 +23,20 @@ import (
|
||||
// *T receiver types, etc.
|
||||
// A nil result indicates an empty set.
|
||||
//
|
||||
// TODO(adonovan): opt: most uses of MethodSet() only access one
|
||||
// member; only generation of object code for MakeInterface needs them
|
||||
// all. Build it incrementally.
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
func (prog *Program) MethodSet(typ types.Type) MethodSet {
|
||||
if typ.MethodSet().Len() == 0 {
|
||||
return prog.populateMethodSet(typ, "")
|
||||
}
|
||||
|
||||
// populateMethodSet returns the method set for typ, ensuring that it
|
||||
// contains at least key id. If id is empty, the entire method set is
|
||||
// populated.
|
||||
//
|
||||
func (prog *Program) populateMethodSet(typ types.Type, id string) MethodSet {
|
||||
tmset := typ.MethodSet()
|
||||
n := tmset.Len()
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
if _, ok := deref(typ).Underlying().(*types.Interface); ok {
|
||||
@ -38,31 +44,55 @@ func (prog *Program) MethodSet(typ types.Type) MethodSet {
|
||||
// has no methods---yet go/types says it has!
|
||||
return nil
|
||||
}
|
||||
if !canHaveConcreteMethods(typ, true) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("MethodSet %s", typ)()
|
||||
defer logStack("MethodSet %s id=%s", typ, id)()
|
||||
}
|
||||
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
|
||||
if mset := prog.methodSets.At(typ); mset != nil {
|
||||
return mset.(MethodSet) // cache hit
|
||||
mset, _ := prog.methodSets.At(typ).(MethodSet)
|
||||
if mset == nil {
|
||||
mset = make(MethodSet)
|
||||
prog.methodSets.Set(typ, mset)
|
||||
}
|
||||
|
||||
mset := make(MethodSet)
|
||||
tmset := typ.MethodSet()
|
||||
for i, n := 0, tmset.Len(); i < n; i++ {
|
||||
obj := tmset.At(i)
|
||||
mset[obj.Func.Id()] = makeMethod(prog, typ, obj)
|
||||
if len(mset) < n {
|
||||
if id != "" { // single method
|
||||
// tmset.Lookup() is no use to us with only an Id string.
|
||||
if mset[id] == nil {
|
||||
for i := 0; i < n; i++ {
|
||||
obj := tmset.At(i)
|
||||
if obj.Id() == id {
|
||||
mset[id] = makeMethod(prog, typ, obj)
|
||||
return mset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// complete set
|
||||
for i := 0; i < n; i++ {
|
||||
obj := tmset.At(i)
|
||||
if id := obj.Id(); mset[id] == nil {
|
||||
mset[id] = makeMethod(prog, typ, obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
prog.methodSets.Set(typ, mset)
|
||||
|
||||
return mset
|
||||
}
|
||||
|
||||
// LookupMethod returns the method id of type typ, building wrapper
|
||||
// methods on demand. It returns nil if the typ has no such method.
|
||||
//
|
||||
// Thread-safe.
|
||||
//
|
||||
func (prog *Program) LookupMethod(typ types.Type, id string) *Function {
|
||||
return prog.populateMethodSet(typ, id)[id]
|
||||
}
|
||||
|
||||
// makeMethod returns the concrete Function for the method obj,
|
||||
// adapted if necessary so that its receiver type is typ.
|
||||
//
|
||||
|
20
ssa/util.go
20
ssa/util.go
@ -106,26 +106,6 @@ outer:
|
||||
return true
|
||||
}
|
||||
|
||||
// canHaveConcreteMethods returns true iff typ may have concrete
|
||||
// methods associated with it. Callers must supply allowPtr=true.
|
||||
//
|
||||
// TODO(gri): consider putting this in go/types. It's surprisingly subtle.
|
||||
func canHaveConcreteMethods(typ types.Type, allowPtr bool) bool {
|
||||
switch typ := typ.(type) {
|
||||
case *types.Pointer:
|
||||
return allowPtr && canHaveConcreteMethods(typ.Elem(), false)
|
||||
case *types.Named:
|
||||
switch typ.Underlying().(type) {
|
||||
case *types.Pointer, *types.Interface:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
case *types.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DefaultType returns the default "typed" type for an "untyped" type;
|
||||
// it returns the incoming type for all other types. The default type
|
||||
// for untyped nil is untyped nil.
|
||||
|
Loading…
Reference in New Issue
Block a user