mirror of
https://github.com/golang/go
synced 2024-11-17 05:44:52 -07:00
cmd/compile: switch to computing dict format on instantiated functions
Change to computing the dictionary form on each shape-instantiated function, rather than once on the underlying generic function/method. The problem with computing the dictionary format on the generic function is that we had to force early transformations to create all the needed/implicit CONVIFACE nodes, since many of these nodes cause the need for a dictionary entry. Also, the dictionary entries needed can different with different instantiations of the same generic function, especially depending on whether a type argument is a non-interface or interface type, or a instantiated type vs. a non-instantiated type. By computing the dictionary format on the instantiated function, we are scanning a function where all the transformations have been done to create implicit CONVFIFACE nodes, and we know the above relevant information about the type params (which are shapes). Much of the change is more mechanical changes from typeparams to shapes, and generic functions/info to instantiated functions/info. Some of the most important non-mechanical changes are: - Separated out the dictionary transformations to nodes into a separate dictPass, since we need to analyze instantiated functions after stenciling, but before the dictionary transformations. - Added type param index to shape types, since we need to be able distinguish type params of an instantiation which are different but happen to have the same shape. - Allow the type substituter to work with shapes again (since for the dictionary entries we need to substitute shape params to the concrete type args). - Support types.IdentityStrict() that does strict type comparison (no special case for shapes). This needed for type substitution, formatting and creating dictionaries, etc. We can maybe create better names for this function. - Add new information to instInfo to contain a mapping from the shape type params to their instantiated type bound. This is needed when doing the dictionary transformations related to type bounds. Change-Id: I1c3ca312c5384f318c4dd7d0858dba9766396ff6 Reviewed-on: https://go-review.googlesource.com/c/go/+/349613 Trust: Dan Scales <danscales@google.com> Run-TryBot: Dan Scales <danscales@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
0edc6c4fa0
commit
59a9a035ff
@ -97,39 +97,42 @@ func check2(noders []*noder) {
|
||||
}
|
||||
}
|
||||
|
||||
// gfInfo is information gathered on a generic function.
|
||||
type gfInfo struct {
|
||||
tparams []*types.Type
|
||||
// dictInfo is the dictionary format for an instantiation of a generic function with
|
||||
// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
|
||||
// the actual dictionary entries in order, and the remaining fields are other info
|
||||
// needed in doing dictionary processing during compilation.
|
||||
type dictInfo struct {
|
||||
// Types substituted for the type parameters, which are shape types.
|
||||
shapeParams []*types.Type
|
||||
// All types derived from those typeparams used in the instantiation.
|
||||
derivedTypes []*types.Type
|
||||
// Nodes in generic function that requires a subdictionary. Includes
|
||||
// Nodes in the instantiation that requires a subdictionary. Includes
|
||||
// method and function calls (OCALL), function values (OFUNCINST), method
|
||||
// values/expressions (OXDOT).
|
||||
subDictCalls []ir.Node
|
||||
// Nodes in generic functions that are a conversion from a typeparam/derived
|
||||
// Nodes in the instantiation that are a conversion from a typeparam/derived
|
||||
// type to a specific interface.
|
||||
itabConvs []ir.Node
|
||||
|
||||
// Mapping from each shape type that substitutes a type param, to its
|
||||
// type bound (which is also substitued with shapes if it is parameterized)
|
||||
shapeToBound map[*types.Type]*types.Type
|
||||
|
||||
// For type switches on nonempty interfaces, a map from OTYPE entries of
|
||||
// HasTParam type, to the interface type we're switching from.
|
||||
// TODO: what if the type we're switching from is a shape type?
|
||||
// HasShape type, to the interface type we're switching from.
|
||||
type2switchType map[ir.Node]*types.Type
|
||||
}
|
||||
|
||||
// instInfo is information gathered on an gcshape (or fully concrete)
|
||||
// instantiation of a function.
|
||||
type instInfo struct {
|
||||
fun *ir.Func // The instantiated function (with body)
|
||||
dictParam *ir.Name // The node inside fun that refers to the dictionary param
|
||||
|
||||
gf *ir.Name // The associated generic function
|
||||
gfInfo *gfInfo
|
||||
|
||||
startSubDict int // Start of dict entries for subdictionaries
|
||||
startItabConv int // Start of dict entries for itab conversions
|
||||
dictLen int // Total number of entries in dictionary
|
||||
}
|
||||
|
||||
// Map from nodes in instantiated fun (OCALL, OCALLMETHOD, OFUNCINST, and
|
||||
// OMETHEXPR) to the associated dictionary entry for a sub-dictionary
|
||||
dictEntryMap map[ir.Node]int
|
||||
// instInfo is information gathered on an shape instantiation of a function.
|
||||
type instInfo struct {
|
||||
fun *ir.Func // The instantiated function (with body)
|
||||
dictParam *ir.Name // The node inside fun that refers to the dictionary param
|
||||
|
||||
dictInfo *dictInfo
|
||||
}
|
||||
|
||||
type irgen struct {
|
||||
@ -155,13 +158,8 @@ type irgen struct {
|
||||
|
||||
dnum int // for generating unique dictionary variables
|
||||
|
||||
// Map from generic function to information about its type params, derived
|
||||
// types, and subdictionaries.
|
||||
gfInfoMap map[*types.Sym]*gfInfo
|
||||
|
||||
// Map from a name of function that been instantiated to information about
|
||||
// its instantiated function, associated generic function/method, and the
|
||||
// mapping from IR nodes to dictionary entries.
|
||||
// its instantiated function (including dictionary format).
|
||||
instInfoMap map[*types.Sym]*instInfo
|
||||
|
||||
// dictionary syms which we need to finish, by writing out any itabconv
|
||||
@ -179,10 +177,11 @@ func (g *irgen) later(fn func()) {
|
||||
}
|
||||
|
||||
type delayInfo struct {
|
||||
gf *ir.Name
|
||||
targs []*types.Type
|
||||
sym *types.Sym
|
||||
off int
|
||||
gf *ir.Name
|
||||
targs []*types.Type
|
||||
sym *types.Sym
|
||||
off int
|
||||
isMeth bool
|
||||
}
|
||||
|
||||
type typeDelayInfo struct {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1921,7 +1921,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||
// Target method uses shaped names.
|
||||
targs2 := make([]*types.Type, len(targs))
|
||||
for i, t := range targs {
|
||||
targs2[i] = typecheck.Shapify(t)
|
||||
targs2[i] = typecheck.Shapify(t, i)
|
||||
}
|
||||
targs = targs2
|
||||
|
||||
|
@ -1019,10 +1019,11 @@ type Tsubster struct {
|
||||
SubstForwFunc func(*types.Type) *types.Type
|
||||
}
|
||||
|
||||
// Typ computes the type obtained by substituting any type parameter in t with the
|
||||
// corresponding type argument in subst. If t contains no type parameters, the
|
||||
// result is t; otherwise the result is a new type. It deals with recursive types
|
||||
// by using TFORW types and finding partially or fully created types via sym.Def.
|
||||
// Typ computes the type obtained by substituting any type parameter or shape in t
|
||||
// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
|
||||
// If t contains no type parameters, the result is t; otherwise the result is a new
|
||||
// type. It deals with recursive types by using TFORW types and finding partially or
|
||||
// fully created types via sym.Def.
|
||||
func (ts *Tsubster) Typ(t *types.Type) *types.Type {
|
||||
// Defer the CheckSize calls until we have fully-defined
|
||||
// (possibly-recursive) top-level type.
|
||||
@ -1033,14 +1034,14 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type {
|
||||
}
|
||||
|
||||
func (ts *Tsubster) typ1(t *types.Type) *types.Type {
|
||||
if !t.HasTParam() && t.Kind() != types.TFUNC {
|
||||
if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
|
||||
// Note: function types need to be copied regardless, as the
|
||||
// types of closures may contain declarations that need
|
||||
// to be copied. See #45738.
|
||||
return t
|
||||
}
|
||||
|
||||
if t.IsTypeParam() {
|
||||
if t.IsTypeParam() || t.IsShape() {
|
||||
for i, tp := range ts.Tparams {
|
||||
if tp == t {
|
||||
return ts.Targs[i]
|
||||
@ -1072,14 +1073,14 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
|
||||
var targsChanged bool
|
||||
var forw *types.Type
|
||||
|
||||
if t.Sym() != nil && t.HasTParam() {
|
||||
if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
|
||||
// Need to test for t.HasTParam() again because of special TFUNC case above.
|
||||
// Translate the type params for this type according to
|
||||
// the tparam/targs mapping from subst.
|
||||
neededTargs = make([]*types.Type, len(t.RParams()))
|
||||
for i, rparam := range t.RParams() {
|
||||
neededTargs[i] = ts.typ1(rparam)
|
||||
if !types.Identical(neededTargs[i], rparam) {
|
||||
if !types.IdenticalStrict(neededTargs[i], rparam) {
|
||||
targsChanged = true
|
||||
}
|
||||
}
|
||||
@ -1286,7 +1287,7 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
|
||||
// fields, set force to true.
|
||||
func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
|
||||
if t.NumFields() == 0 {
|
||||
if t.HasTParam() {
|
||||
if t.HasTParam() || t.HasShape() {
|
||||
// For an empty struct, we need to return a new type,
|
||||
// since it may now be fully instantiated (HasTParam
|
||||
// becomes false).
|
||||
@ -1388,19 +1389,20 @@ func genericTypeName(sym *types.Sym) string {
|
||||
return sym.Name[0:strings.Index(sym.Name, "[")]
|
||||
}
|
||||
|
||||
// Shapify takes a concrete type and returns a GCshape type that can
|
||||
// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
|
||||
// be used in place of the input type and still generate identical code.
|
||||
// No methods are added - all methods calls directly on a shape should
|
||||
// be done by converting to an interface using the dictionary.
|
||||
//
|
||||
// TODO: this could take the generic function and base its decisions
|
||||
// on how that generic function uses this type argument. For instance,
|
||||
// if it doesn't use it as a function argument/return value, then
|
||||
// we don't need to distinguish int64 and float64 (because they only
|
||||
// differ in how they get passed as arguments). For now, we only
|
||||
// unify two different types if they are identical in every possible way.
|
||||
func Shapify(t *types.Type) *types.Type {
|
||||
assert(!t.HasShape())
|
||||
// For now, we only consider two types to have the same shape, if they have exactly
|
||||
// the same underlying type or they are both pointer types.
|
||||
//
|
||||
// Shape types are also distinguished by the index of the type in a type param/arg
|
||||
// list. We need to do this so we can distinguish and substitute properly for two
|
||||
// type params in the same function that have the same shape for a particular
|
||||
// instantiation.
|
||||
func Shapify(t *types.Type, index int) *types.Type {
|
||||
assert(!t.IsShape())
|
||||
// Map all types with the same underlying type to the same shape.
|
||||
u := t.Underlying()
|
||||
|
||||
@ -1410,15 +1412,24 @@ func Shapify(t *types.Type) *types.Type {
|
||||
u = types.Types[types.TUINT8].PtrTo()
|
||||
}
|
||||
|
||||
if s := shaped[u]; s != nil {
|
||||
if shapeMap == nil {
|
||||
shapeMap = map[int]map[*types.Type]*types.Type{}
|
||||
}
|
||||
submap := shapeMap[index]
|
||||
if submap == nil {
|
||||
submap = map[*types.Type]*types.Type{}
|
||||
shapeMap[index] = submap
|
||||
}
|
||||
if s := submap[u]; s != nil {
|
||||
return s
|
||||
}
|
||||
|
||||
sym := types.ShapePkg.Lookup(u.LinkString())
|
||||
nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
|
||||
sym := types.ShapePkg.Lookup(nm)
|
||||
if sym.Def != nil {
|
||||
// Use any existing type with the same name
|
||||
shaped[u] = sym.Def.Type()
|
||||
return shaped[u]
|
||||
submap[u] = sym.Def.Type()
|
||||
return submap[u]
|
||||
}
|
||||
name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
|
||||
s := types.NewNamed(name)
|
||||
@ -1428,8 +1439,8 @@ func Shapify(t *types.Type) *types.Type {
|
||||
s.SetHasShape(true)
|
||||
name.SetType(s)
|
||||
name.SetTypecheck(1)
|
||||
shaped[u] = s
|
||||
submap[u] = s
|
||||
return s
|
||||
}
|
||||
|
||||
var shaped = map[*types.Type]*types.Type{}
|
||||
var shapeMap map[int]map[*types.Type]*types.Type
|
||||
|
@ -4,19 +4,30 @@
|
||||
|
||||
package types
|
||||
|
||||
const (
|
||||
identIgnoreTags = 1 << iota
|
||||
identStrict
|
||||
)
|
||||
|
||||
// Identical reports whether t1 and t2 are identical types, following the spec rules.
|
||||
// Receiver parameter types are ignored. Named (defined) types are only equal if they
|
||||
// are pointer-equal - i.e. there must be a unique types.Type for each specific named
|
||||
// type. Also, a type containing a shape type is considered identical to another type
|
||||
// (shape or not) if their underlying types are the same, or they are both pointers.
|
||||
func Identical(t1, t2 *Type) bool {
|
||||
return identical(t1, t2, true, nil)
|
||||
return identical(t1, t2, 0, nil)
|
||||
}
|
||||
|
||||
// IdenticalIgnoreTags is like Identical, but it ignores struct tags
|
||||
// for struct identity.
|
||||
func IdenticalIgnoreTags(t1, t2 *Type) bool {
|
||||
return identical(t1, t2, false, nil)
|
||||
return identical(t1, t2, identIgnoreTags, nil)
|
||||
}
|
||||
|
||||
// IdenticalStrict is like Identical, but matches types exactly, without the
|
||||
// exception for shapes.
|
||||
func IdenticalStrict(t1, t2 *Type) bool {
|
||||
return identical(t1, t2, identStrict, nil)
|
||||
}
|
||||
|
||||
type typePair struct {
|
||||
@ -24,7 +35,7 @@ type typePair struct {
|
||||
t2 *Type
|
||||
}
|
||||
|
||||
func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
|
||||
func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool {
|
||||
if t1 == t2 {
|
||||
return true
|
||||
}
|
||||
@ -32,7 +43,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
|
||||
return false
|
||||
}
|
||||
if t1.sym != nil || t2.sym != nil {
|
||||
if t1.HasShape() || t2.HasShape() {
|
||||
if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) {
|
||||
switch t1.kind {
|
||||
case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR:
|
||||
return true
|
||||
@ -78,7 +89,7 @@ cont:
|
||||
}
|
||||
for i, f1 := range t1.AllMethods().Slice() {
|
||||
f2 := t2.AllMethods().Index(i)
|
||||
if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
|
||||
if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -90,10 +101,10 @@ cont:
|
||||
}
|
||||
for i, f1 := range t1.FieldSlice() {
|
||||
f2 := t2.Field(i)
|
||||
if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
|
||||
if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) {
|
||||
return false
|
||||
}
|
||||
if cmpTags && f1.Note != f2.Note {
|
||||
if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -111,7 +122,7 @@ cont:
|
||||
}
|
||||
for i, f1 := range fs1 {
|
||||
f2 := fs2[i]
|
||||
if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
|
||||
if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -129,10 +140,10 @@ cont:
|
||||
}
|
||||
|
||||
case TMAP:
|
||||
if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
|
||||
if !identical(t1.Key(), t2.Key(), flags, assumedEqual) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
|
||||
return identical(t1.Elem(), t2.Elem(), flags, assumedEqual)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user