1
0
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:
Alan Donovan 2013-07-23 13:38:52 -04:00
parent 2bff3a03e7
commit 7eafcedc87
3 changed files with 50 additions and 46 deletions

View File

@ -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.

View File

@ -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.
//

View File

@ -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.