aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Donovan <adonovan@google.com>2014-12-29 13:20:22 -0500
committerAlan Donovan <adonovan@google.com>2015-01-22 14:03:32 +0000
commitf011631cea2c67954dff8b1551b6998d54a99feb (patch)
tree1eae4c52d34403659244fab9032aad581f25aa65
parente079f6c63283585db69001a632951cc88c70b052 (diff)
downloadtools-f011631cea2c67954dff8b1551b6998d54a99feb.tar.gz
go/ssa: simplify TypesWithMethodSets
Details: - rename (*Program).TypesWithMethodSets() to RuntimeTypes() - delete (*Package).TypesWithMethodSets() method and simplify - move code to methods.go - update test to use 1-2% improvement in space and time (though I barely trust this data because the GC at tip is in such terrible state). Change-Id: I38eab78b11e0ad0ff16e0530e775b6ff6a2ab246 Reviewed-on: https://go-review.googlesource.com/3148 Reviewed-by: Robert Griesemer <gri@golang.org>
-rw-r--r--go/pointer/gen.go2
-rw-r--r--go/ssa/builder.go128
-rw-r--r--go/ssa/builder_test.go22
-rw-r--r--go/ssa/emit.go2
-rw-r--r--go/ssa/methods.go184
-rw-r--r--go/ssa/ssa.go32
-rw-r--r--go/ssa/ssautil/visit.go2
7 files changed, 145 insertions, 227 deletions
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index 48ca368..6c256ac 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -1262,7 +1262,7 @@ func (a *analysis) generate() {
// Create nodes and constraints for all methods of all types
// that are dynamically accessible via reflection or interfaces.
- for _, T := range a.prog.TypesWithMethodSets() {
+ for _, T := range a.prog.RuntimeTypes() {
a.genMethodsOf(T)
}
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 93755d0..3e70a85 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -2193,7 +2193,7 @@ func (p *Package) Build() {
// that would require package creation in topological order.
for name, mem := range p.Members {
if ast.IsExported(name) {
- p.needMethodsOf(mem.Type())
+ p.Prog.needMethodsOf(mem.Type())
}
}
if p.Prog.mode&LogSource != 0 {
@@ -2301,129 +2301,3 @@ func (p *Package) typeOf(e ast.Expr) types.Type {
panic(fmt.Sprintf("no type for %T @ %s",
e, p.Prog.Fset.Position(e.Pos())))
}
-
-// needMethodsOf ensures that runtime type information (including the
-// complete method set) is available for the specified type T and all
-// its subcomponents.
-//
-// needMethodsOf must be called for at least every type that is an
-// operand of some MakeInterface instruction, and for the type of
-// every exported package member.
-//
-// Precondition: T is not a method signature (*Signature with Recv()!=nil).
-//
-// Thread-safe. (Called via emitConv from multiple builder goroutines.)
-//
-// TODO(adonovan): make this faster. It accounts for 20% of SSA build
-// time. Do we need to maintain a distinct needRTTI and methodSets per
-// package? Using just one in the program might be much faster.
-//
-func (p *Package) needMethodsOf(T types.Type) {
- p.methodsMu.Lock()
- p.needMethods(T, false)
- p.methodsMu.Unlock()
-}
-
-// Precondition: T is not a method signature (*Signature with Recv()!=nil).
-// Precondition: the p.methodsMu lock is held.
-// Recursive case: skip => don't call makeMethods(T).
-func (p *Package) needMethods(T types.Type, skip bool) {
- // Each package maintains its own set of types it has visited.
- if prevSkip, ok := p.needRTTI.At(T).(bool); ok {
- // needMethods(T) was previously called
- if !prevSkip || skip {
- return // already seen, with same or false 'skip' value
- }
- }
- p.needRTTI.Set(T, skip)
-
- // Prune the recursion if we find a named or *named type
- // belonging to another package.
- var n *types.Named
- switch T := T.(type) {
- case *types.Named:
- n = T
- case *types.Pointer:
- n, _ = T.Elem().(*types.Named)
- }
- if n != nil {
- owner := n.Obj().Pkg()
- if owner == nil {
- return // built-in error type
- }
- if owner != p.Object {
- return // belongs to another package
- }
- }
-
- // All the actual method sets live in the Program so that
- // multiple packages can share a single copy in memory of the
- // symbols that would be compiled into multiple packages (as
- // weak symbols).
- if !skip && p.Prog.makeMethods(T) {
- p.methodSets = append(p.methodSets, T)
- }
-
- // Recursion over signatures of each method.
- tmset := p.Prog.MethodSets.MethodSet(T)
- for i := 0; i < tmset.Len(); i++ {
- sig := tmset.At(i).Type().(*types.Signature)
- p.needMethods(sig.Params(), false)
- p.needMethods(sig.Results(), false)
- }
-
- switch t := T.(type) {
- case *types.Basic:
- // nop
-
- case *types.Interface:
- // nop---handled by recursion over method set.
-
- case *types.Pointer:
- p.needMethods(t.Elem(), false)
-
- case *types.Slice:
- p.needMethods(t.Elem(), false)
-
- case *types.Chan:
- p.needMethods(t.Elem(), false)
-
- case *types.Map:
- p.needMethods(t.Key(), false)
- p.needMethods(t.Elem(), false)
-
- case *types.Signature:
- if t.Recv() != nil {
- panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
- }
- p.needMethods(t.Params(), false)
- p.needMethods(t.Results(), false)
-
- case *types.Named:
- // A pointer-to-named type can be derived from a named
- // type via reflection. It may have methods too.
- p.needMethods(types.NewPointer(T), false)
-
- // Consider 'type T struct{S}' where S has methods.
- // Reflection provides no way to get from T to struct{S},
- // only to S, so the method set of struct{S} is unwanted,
- // so set 'skip' flag during recursion.
- p.needMethods(t.Underlying(), true)
-
- case *types.Array:
- p.needMethods(t.Elem(), false)
-
- case *types.Struct:
- for i, n := 0, t.NumFields(); i < n; i++ {
- p.needMethods(t.Field(i).Type(), false)
- }
-
- case *types.Tuple:
- for i, n := 0, t.Len(); i < n; i++ {
- p.needMethods(t.At(i).Type(), false)
- }
-
- default:
- panic(T)
- }
-}
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index bda9ac6..dc7434f 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -151,8 +151,8 @@ func main() {
}
}
-// TestTypesWithMethodSets tests that Package.TypesWithMethodSets includes all necessary types.
-func TestTypesWithMethodSets(t *testing.T) {
+// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
+func TestRuntimeTypes(t *testing.T) {
tests := []struct {
input string
want []string
@@ -167,7 +167,7 @@ func TestTypesWithMethodSets(t *testing.T) {
},
// Subcomponents of type of exported package-level var are needed.
{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
- []string{"*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
+ []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported package-level var are not needed.
{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
@@ -175,7 +175,7 @@ func TestTypesWithMethodSets(t *testing.T) {
},
// Subcomponents of type of exported package-level function are needed.
{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
- []string{"struct{*bytes.Buffer}"},
+ []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported package-level function are not needed.
{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
@@ -187,11 +187,11 @@ func TestTypesWithMethodSets(t *testing.T) {
},
// ...unless used by MakeInterface.
{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
- []string{"*p.x", "p.x", "struct{*bytes.Buffer}"},
+ []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
},
// Subcomponents of type of unexported method are not needed.
{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
- []string{"*p.X", "p.X", "struct{*bytes.Buffer}"},
+ []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
},
// Local types aren't needed.
{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
@@ -199,11 +199,11 @@ func TestTypesWithMethodSets(t *testing.T) {
},
// ...unless used by MakeInterface.
{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
- []string{"*p.T", "p.T"},
+ []string{"*bytes.Buffer", "*p.T", "p.T"},
},
// Types used as operand of MakeInterface are needed.
{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
- []string{"struct{*bytes.Buffer}"},
+ []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
},
// MakeInterface is optimized away when storing to a blank.
{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
@@ -226,17 +226,17 @@ func TestTypesWithMethodSets(t *testing.T) {
continue
}
prog := ssa.Create(iprog, ssa.SanityCheckFunctions)
- mainPkg := prog.Package(iprog.Created[0].Pkg)
prog.BuildAll()
var typstrs []string
- for _, T := range mainPkg.TypesWithMethodSets() {
+ for _, T := range prog.RuntimeTypes() {
typstrs = append(typstrs, T.String())
}
sort.Strings(typstrs)
if !reflect.DeepEqual(typstrs, test.want) {
- t.Errorf("test 'package %s': got %q, want %q", f.Name.Name, typstrs, test.want)
+ t.Errorf("test 'package %s': got %q, want %q",
+ f.Name.Name, typstrs, test.want)
}
}
}
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 261d6e0..f1ba0f7 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -208,7 +208,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
val = emitConv(f, val, DefaultType(ut_src))
}
- f.Pkg.needMethodsOf(val.Type())
+ f.Pkg.Prog.needMethodsOf(val.Type())
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index 1ef725c..12534de 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -55,44 +55,6 @@ func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string)
return prog.Method(sel)
}
-// makeMethods ensures that all concrete methods of type T are
-// generated. It is equivalent to calling prog.Method() on all
-// members of T.methodSet(), but acquires fewer locks.
-//
-// It reports whether the type's (concrete) method set is non-empty.
-//
-// Thread-safe.
-//
-// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
-//
-func (prog *Program) makeMethods(T types.Type) bool {
- if isInterface(T) {
- return false // abstract method
- }
- tmset := prog.MethodSets.MethodSet(T)
- n := tmset.Len()
- if n == 0 {
- return false // empty (common case)
- }
-
- if prog.mode&LogSource != 0 {
- defer logStack("makeMethods %s", T)()
- }
-
- prog.methodsMu.Lock()
- defer prog.methodsMu.Unlock()
-
- mset := prog.createMethodSet(T)
- if !mset.complete {
- mset.complete = true
- for i := 0; i < n; i++ {
- prog.addMethod(mset, tmset.At(i))
- }
- }
-
- return true
-}
-
// methodSet contains the (concrete) methods of a non-interface type.
type methodSet struct {
mapping map[string]*Function // populated lazily
@@ -135,18 +97,15 @@ func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function
return fn
}
-// TypesWithMethodSets returns a new unordered slice containing all
+// RuntimeTypes returns a new unordered slice containing all
// concrete types in the program for which a complete (non-empty)
// method set is required at run-time.
//
-// It is the union of pkg.TypesWithMethodSets() for all pkg in
-// prog.AllPackages().
-//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
-func (prog *Program) TypesWithMethodSets() []types.Type {
+func (prog *Program) RuntimeTypes() []types.Type {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
@@ -159,39 +118,126 @@ func (prog *Program) TypesWithMethodSets() []types.Type {
return res
}
-// TypesWithMethodSets returns an unordered slice containing the set
-// of all concrete types referenced within package pkg and not
-// belonging to some other package, for which a complete (non-empty)
-// method set is required at run-time.
+// declaredFunc returns the concrete function/method denoted by obj.
+// Panic ensues if there is none.
+//
+func (prog *Program) declaredFunc(obj *types.Func) *Function {
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Function)
+ }
+ panic("no concrete method: " + obj.String())
+}
+
+// needMethodsOf ensures that runtime type information (including the
+// complete method set) is available for the specified type T and all
+// its subcomponents.
+//
+// needMethodsOf must be called for at least every type that is an
+// operand of some MakeInterface instruction, and for the type of
+// every exported package member.
//
-// A type belongs to a package if it is a named type or a pointer to a
-// named type, and the name was defined in that package. All other
-// types belong to no package.
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
//
-// A type may appear in the TypesWithMethodSets() set of multiple
-// distinct packages if that type belongs to no package. Typical
-// compilers emit method sets for such types multiple times (using
-// weak symbols) into each package that references them, with the
-// linker performing duplicate elimination.
+// Thread-safe. (Called via emitConv from multiple builder goroutines.)
//
-// This set includes the types of all operands of some MakeInterface
-// instruction, the types of all exported members of some package, and
-// all types that are subcomponents, since even types that aren't used
-// directly may be derived via reflection.
+// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
//
-// Callers must not mutate the result.
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
-func (pkg *Package) TypesWithMethodSets() []types.Type {
- // pkg.methodsMu not required; concurrent (build) phase is over.
- return pkg.methodSets
+func (prog *Program) needMethodsOf(T types.Type) {
+ prog.methodsMu.Lock()
+ prog.needMethods(T, false)
+ prog.methodsMu.Unlock()
}
-// declaredFunc returns the concrete function/method denoted by obj.
-// Panic ensues if there is none.
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Recursive case: skip => don't create methods for T.
//
-func (prog *Program) declaredFunc(obj *types.Func) *Function {
- if v := prog.packageLevelValue(obj); v != nil {
- return v.(*Function)
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+//
+func (prog *Program) needMethods(T types.Type, skip bool) {
+ // Each package maintains its own set of types it has visited.
+ if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
+ // needMethods(T) was previously called
+ if !prevSkip || skip {
+ return // already seen, with same or false 'skip' value
+ }
+ }
+ prog.runtimeTypes.Set(T, skip)
+
+ tmset := prog.MethodSets.MethodSet(T)
+
+ if !skip && !isInterface(T) && tmset.Len() > 0 {
+ // Create methods of T.
+ mset := prog.createMethodSet(T)
+ if !mset.complete {
+ mset.complete = true
+ n := tmset.Len()
+ for i := 0; i < n; i++ {
+ prog.addMethod(mset, tmset.At(i))
+ }
+ }
+ }
+
+ // Recursion over signatures of each method.
+ for i := 0; i < tmset.Len(); i++ {
+ sig := tmset.At(i).Type().(*types.Signature)
+ prog.needMethods(sig.Params(), false)
+ prog.needMethods(sig.Results(), false)
+ }
+
+ switch t := T.(type) {
+ case *types.Basic:
+ // nop
+
+ case *types.Interface:
+ // nop---handled by recursion over method set.
+
+ case *types.Pointer:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Slice:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Chan:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Map:
+ prog.needMethods(t.Key(), false)
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Signature:
+ if t.Recv() != nil {
+ panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
+ }
+ prog.needMethods(t.Params(), false)
+ prog.needMethods(t.Results(), false)
+
+ case *types.Named:
+ // A pointer-to-named type can be derived from a named
+ // type via reflection. It may have methods too.
+ prog.needMethods(types.NewPointer(T), false)
+
+ // Consider 'type T struct{S}' where S has methods.
+ // Reflection provides no way to get from T to struct{S},
+ // only to S, so the method set of struct{S} is unwanted,
+ // so set 'skip' flag during recursion.
+ prog.needMethods(t.Underlying(), true)
+
+ case *types.Array:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Struct:
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ prog.needMethods(t.Field(i).Type(), false)
+ }
+
+ case *types.Tuple:
+ for i, n := 0, t.Len(); i < n; i++ {
+ prog.needMethods(t.At(i).Type(), false)
+ }
+
+ default:
+ panic(T)
}
- panic("no concrete method: " + obj.String())
}
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 406611e..ac46f27 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -28,11 +28,12 @@ type Program struct {
mode BuilderMode // set of mode bits for SSA construction
MethodSets types.MethodSetCache // cache of type-checker's method-sets
- methodsMu sync.Mutex // guards the following maps:
- methodSets typeutil.Map // maps type to its concrete methodSet
- canon typeutil.Map // type canonicalization map
- bounds map[*types.Func]*Function // bounds for curried x.Method closures
- thunks map[selectionKey]*Function // thunks for T.Method expressions
+ methodsMu sync.Mutex // guards the following maps:
+ methodSets typeutil.Map // maps type to its concrete methodSet
+ runtimeTypes typeutil.Map // types for which rtypes are needed
+ canon typeutil.Map // type canonicalization map
+ bounds map[*types.Func]*Function // bounds for curried x.Method closures
+ thunks map[selectionKey]*Function // thunks for T.Method expressions
}
// A Package is a single analyzed Go package containing Members for
@@ -41,21 +42,18 @@ type Program struct {
// type-specific accessor methods Func, Type, Var and Const.
//
type Package struct {
- Prog *Program // the owning program
- Object *types.Package // the type checker's package object for this package
- Members map[string]Member // all package members keyed by name
- methodsMu sync.Mutex // guards needRTTI and methodSets
- methodSets []types.Type // types whose method sets are included in this package
- values map[types.Object]Value // package members (incl. types and methods), keyed by object
- init *Function // Func("init"); the package's init function
- debug bool // include full debug info in this package.
+ Prog *Program // the owning program
+ Object *types.Package // the type checker's package object for this package
+ Members map[string]Member // all package members keyed by name
+ values map[types.Object]Value // package members (incl. types and methods), keyed by object
+ init *Function // Func("init"); the package's init function
+ debug bool // include full debug info in this package.
// The following fields are set transiently, then cleared
// after building.
- started int32 // atomically tested and set at start of build phase
- ninit int32 // number of init functions
- info *loader.PackageInfo // package ASTs and type information
- needRTTI typeutil.Map // types for which runtime type info is needed
+ started int32 // atomically tested and set at start of build phase
+ ninit int32 // number of init functions
+ info *loader.PackageInfo // package ASTs and type information
}
// A Member is a member of a Go package, implemented by *NamedConst,
diff --git a/go/ssa/ssautil/visit.go b/go/ssa/ssautil/visit.go
index 14de154..30843c3 100644
--- a/go/ssa/ssautil/visit.go
+++ b/go/ssa/ssautil/visit.go
@@ -41,7 +41,7 @@ func (visit *visitor) program() {
}
}
}
- for _, T := range visit.prog.TypesWithMethodSets() {
+ for _, T := range visit.prog.RuntimeTypes() {
mset := visit.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
visit.function(visit.prog.Method(mset.At(i)))