go/callgraph/rta: optimise 'implements' operation

Before, RTA spent most of its time in types.Implements.
This change uses the fingerprint technique (similar to
a Bloom filter) from CL 452060 to quickly reject most
candidates.

The running time of the deadcode command on cmd/kubelet
dropped from 92s to 10s.

Updates golang/go#61162

Change-Id: Iacfba027270613e943a617129ff056c629b11f91
Reviewed-on: https://go-review.googlesource.com/c/tools/+/507855
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/go/callgraph/rta/rta.go b/go/callgraph/rta/rta.go
index 2e7180f..e548f76 100644
--- a/go/callgraph/rta/rta.go
+++ b/go/callgraph/rta/rta.go
@@ -52,6 +52,7 @@
 import (
 	"fmt"
 	"go/types"
+	"hash/crc32"
 
 	"golang.org/x/tools/go/callgraph"
 	"golang.org/x/tools/go/ssa"
@@ -112,18 +113,31 @@
 	// The following two maps together define the subset of the
 	// m:n "implements" relation needed by the algorithm.
 
-	// concreteTypes maps each concrete type to the set of interfaces that it implements.
-	// Keys are types.Type, values are unordered []*types.Interface.
+	// concreteTypes maps each concrete type to information about it.
+	// Keys are types.Type, values are *concreteTypeInfo.
 	// Only concrete types used as MakeInterface operands are included.
 	concreteTypes typeutil.Map
 
-	// interfaceTypes maps each interface type to
-	// the set of concrete types that implement it.
-	// Keys are *types.Interface, values are unordered []types.Type.
+	// interfaceTypes maps each interface type to information about it.
+	// Keys are *types.Interface, values are *interfaceTypeInfo.
 	// Only interfaces used in "invoke"-mode CallInstructions are included.
 	interfaceTypes typeutil.Map
 }
 
+type concreteTypeInfo struct {
+	C          types.Type
+	mset       *types.MethodSet
+	fprint     uint64             // fingerprint of method set
+	implements []*types.Interface // unordered set of implemented interfaces
+}
+
+type interfaceTypeInfo struct {
+	I               *types.Interface
+	mset            *types.MethodSet
+	fprint          uint64
+	implementations []types.Type // unordered set of concrete implementations
+}
+
 // addReachable marks a function as potentially callable at run-time,
 // and ensures that it gets processed.
 func (r *rta) addReachable(f *ssa.Function, addrTaken bool) {
@@ -295,6 +309,11 @@
 // Analyze performs Rapid Type Analysis, starting at the specified root
 // functions.  It returns nil if no roots were specified.
 //
+// The root functions must be one or more entrypoints (main and init
+// functions) of a complete SSA program, with function bodies for all
+// dependencies, constructed with the [ssa.InstantiateGenerics] mode
+// flag.
+//
 // If buildCallGraph is true, Result.CallGraph will contain a call
 // graph; otherwise, only the other fields (reachable functions) are
 // populated.
@@ -349,38 +368,59 @@
 
 // interfaces(C) returns all currently known interfaces implemented by C.
 func (r *rta) interfaces(C types.Type) []*types.Interface {
-	// Ascertain set of interfaces C implements
-	// and update 'implements' relation.
-	var ifaces []*types.Interface
-	r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) {
-		if I := I.(*types.Interface); types.Implements(C, I) {
-			concs, _ := concs.([]types.Type)
-			r.interfaceTypes.Set(I, append(concs, C))
-			ifaces = append(ifaces, I)
+	// Create an info for C the first time we see it.
+	var cinfo *concreteTypeInfo
+	if v := r.concreteTypes.At(C); v != nil {
+		cinfo = v.(*concreteTypeInfo)
+	} else {
+		mset := r.prog.MethodSets.MethodSet(C)
+		cinfo = &concreteTypeInfo{
+			C:      C,
+			mset:   mset,
+			fprint: fingerprint(mset),
 		}
-	})
-	r.concreteTypes.Set(C, ifaces)
-	return ifaces
+		r.concreteTypes.Set(C, cinfo)
+
+		// Ascertain set of interfaces C implements
+		// and update the 'implements' relation.
+		r.interfaceTypes.Iterate(func(I types.Type, v interface{}) {
+			iinfo := v.(*interfaceTypeInfo)
+			if I := I.(*types.Interface); implements(cinfo, iinfo) {
+				iinfo.implementations = append(iinfo.implementations, C)
+				cinfo.implements = append(cinfo.implements, I)
+			}
+		})
+	}
+
+	return cinfo.implements
 }
 
 // implementations(I) returns all currently known concrete types that implement I.
 func (r *rta) implementations(I *types.Interface) []types.Type {
-	var concs []types.Type
+	// Create an info for I the first time we see it.
+	var iinfo *interfaceTypeInfo
 	if v := r.interfaceTypes.At(I); v != nil {
-		concs = v.([]types.Type)
+		iinfo = v.(*interfaceTypeInfo)
 	} else {
-		// First time seeing this interface.
-		// Update the 'implements' relation.
-		r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) {
-			if types.Implements(C, I) {
-				ifaces, _ := ifaces.([]*types.Interface)
-				r.concreteTypes.Set(C, append(ifaces, I))
-				concs = append(concs, C)
+		mset := r.prog.MethodSets.MethodSet(I)
+		iinfo = &interfaceTypeInfo{
+			I:      I,
+			mset:   mset,
+			fprint: fingerprint(mset),
+		}
+		r.interfaceTypes.Set(I, iinfo)
+
+		// Ascertain set of concrete types that implement I
+		// and update the 'implements' relation.
+		r.concreteTypes.Iterate(func(C types.Type, v interface{}) {
+			cinfo := v.(*concreteTypeInfo)
+			if implements(cinfo, iinfo) {
+				cinfo.implements = append(cinfo.implements, I)
+				iinfo.implementations = append(iinfo.implementations, C)
 			}
 		})
-		r.interfaceTypes.Set(I, concs)
 	}
-	return concs
+	return iinfo.implementations
 }
 
 // addRuntimeType is called for each concrete type that can be the
@@ -501,3 +541,29 @@
 		panic(T)
 	}
 }
+
+// fingerprint returns a bitmask with one bit set per method id,
+// enabling 'implements' to quickly reject most candidates.
+func fingerprint(mset *types.MethodSet) uint64 {
+	var space [64]byte
+	var mask uint64
+	for i := 0; i < mset.Len(); i++ {
+		method := mset.At(i).Obj()
+		sig := method.Type().(*types.Signature)
+		sum := crc32.ChecksumIEEE(fmt.Appendf(space[:], "%s/%d/%d",
+			method.Id(),
+			sig.Params().Len(),
+			sig.Results().Len()))
+		mask |= 1 << (sum % 64)
+	}
+	return mask
+}
+
+// implements reports whether types.Implements(cinfo.C, iinfo.I),
+// but more efficiently.
+func implements(cinfo *concreteTypeInfo, iinfo *interfaceTypeInfo) (got bool) {
+	// The concrete type must have at least the methods
+	// (bits) of the interface type. Use a bitwise subset
+	// test to reject most candidates quickly.
+	return iinfo.fprint & ^cinfo.fprint == 0 && types.Implements(cinfo.C, iinfo.I)
+}