From 620cfeabe0b91860c923a13132b754a6cc0db729 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 14 Dec 2024 19:28:06 +0800 Subject: [PATCH] internal/runtime: type hash --- internal/runtime/z_face.go | 101 +++++++++++++++++++++++++++++++++++-- internal/runtime/z_type.go | 31 +++++++++--- 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/internal/runtime/z_face.go b/internal/runtime/z_face.go index 143d2c58..4a9f4b3b 100644 --- a/internal/runtime/z_face.go +++ b/internal/runtime/z_face.go @@ -116,6 +116,7 @@ func NewNamed(pkgPath string, name string, kind abi.Kind, size uintptr, methods, } ret := allocUncommonType(kind, size, methods, abi.TFlagUninited|abi.TFlagNamed|abi.TFlagUncommon, pkgPath) ret.Str_ = name + ret.Hash = 9157 + hashString(pkgPath) + hashString(name) if ptrMethods == 0 { ret.PtrToThis_ = newPointer(ret) } else { @@ -210,7 +211,6 @@ func Func(in, out []*Type, variadic bool) *FuncType { Type: Type{ Size_: 2 * unsafe.Sizeof(uintptr(0)), PtrBytes: 2 * pointerSize, - Hash: uint32(abi.Func), // TODO(xsw): hash Align_: uint8(pointerAlign), FieldAlign_: uint8(pointerAlign), Kind_: uint8(abi.Func), @@ -218,9 +218,13 @@ func Func(in, out []*Type, variadic bool) *FuncType { In: in, Out: out, } + var hash uint32 = 9091 if variadic { + hash *= 8863 ret.TFlag |= abi.TFlagVariadic } + hash += 3*hashTuple(in) + 5*hashTuple(out) + ret.Hash = hash ret.Str_ = funcStr(ret) rtypeList.addType(&ret.Type) return ret @@ -243,7 +247,7 @@ func NewNamedInterface(pkgPath, name string) *InterfaceType { Type: Type{ Size_: unsafe.Sizeof(eface{}), PtrBytes: 2 * pointerSize, - Hash: uint32(abi.Interface), // TODO(xsw): hash + Hash: 9157 + hashString(pkgPath) + hashString(name), Align_: uint8(pointerAlign), FieldAlign_: uint8(pointerAlign), Kind_: uint8(abi.Interface), @@ -277,7 +281,6 @@ func Interface(pkgPath string, methods []Imethod) *InterfaceType { Type: Type{ Size_: unsafe.Sizeof(eface{}), PtrBytes: 2 * pointerSize, - Hash: uint32(abi.Interface), // TODO(xsw): hash Align_: uint8(pointerAlign), FieldAlign_: uint8(pointerAlign), Kind_: uint8(abi.Interface), @@ -291,6 +294,14 @@ func Interface(pkgPath string, methods []Imethod) *InterfaceType { ret.Equal = interequal } ret.Str_ = interfaceStr(ret) + var hash uint32 = 9103 + // Hash methods. + for _, m := range methods { + // Use shallow hash on method signature to + // avoid anonymous interface cycles. + hash += 3*hashString(m.Name()) + 5*shallowHash(&m.Typ_.Type) + } + ret.Hash = hash rtypeList.addType(&ret.Type) return ret } @@ -541,4 +552,88 @@ func funcStr(ft *abi.FuncType) string { return string(repr) } +func hashTuple(tuple []*Type) uint32 { + // See go/types.identicalTypes for rationale. + n := len(tuple) + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 3 * tuple[i].Hash + } + return hash +} + +// shallowHash computes a hash of t without looking at any of its +// element Types, to avoid potential anonymous cycles in the types of +// interface methods. +// +// When an unnamed non-empty interface type appears anywhere among the +// arguments or results of an interface method, there is a potential +// for endless recursion. Consider: +// +// type X interface { m() []*interface { X } } +// +// The problem is that the Methods of the interface in m's result type +// include m itself; there is no mention of the named type X that +// might help us break the cycle. +// (See comment in go/types.identical, case *Interface, for more.) +func shallowHash(t *abi.Type) uint32 { + // t is the type of an interface method (Signature), + // its params or results (Tuples), or their immediate + // elements (mostly Slice, Pointer, Basic, Named), + // so there's no need to optimize anything else. + + if t.HasName() { + return 9157 + hashString(t.Uncommon().PkgPath_) + hashString(t.Str_) + } + + switch t.Kind() { + case abi.Bool, abi.Int, abi.Int8, abi.Int16, abi.Int32, abi.Int64, + abi.Uint, abi.Uint8, abi.Uint16, abi.Uint32, abi.Uint64, abi.Uintptr, + abi.Float32, abi.Float64, abi.Complex64, abi.Complex128, abi.String, abi.UnsafePointer: + return 45212177 * uint32(t.Kind()) + + case abi.Func: + t := t.FuncType() + var hash uint32 = 604171 + if t.Variadic() { + hash *= 971767 + } + // The Signature/Tuple recursion is always finite + // and invariably shallow. + return hash + 1062599*shallowHashTuple(t.In) + 1282529*shallowHashTuple(t.Out) + + case abi.Array: + return 1524181 + 2*uint32(t.ArrayType().Len) + + case abi.Slice: + return 2690201 + + case abi.Struct: + return 3326489 + + case abi.Pointer: + return 4393139 + + case abi.Interface: + return 2124679 // no recursion here + + case abi.Map: + return 9109 + + case abi.Chan: + return 9127 + } + + panic("shallowHash:" + t.String()) +} + +func shallowHashTuple(tuple []*Type) uint32 { + n := len(tuple) + hash := 9137 + 2*uint32(n) + for i := 0; i < n; i++ { + hash += 53471161 * shallowHash(tuple[i]) + } + return hash +} + // ----------------------------------------------------------------------------- diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index 013e044b..c93bcff4 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -79,7 +79,7 @@ func Basic(_kind Kind) *Type { tyBasic[kind] = &Type{ Size_: size, PtrBytes: bytes, - Hash: uint32(kind), // TODO(xsw): hash + Hash: uint32(kind), Align_: uint8(align), FieldAlign_: uint8(align), Kind_: uint8(_kind), @@ -154,13 +154,13 @@ func Struct(pkgPath string, size uintptr, fields ...abi.StructField) *Type { ret := &abi.StructType{ Type: Type{ Size_: size, - Hash: uint32(abi.Struct), // TODO(xsw): hash Kind_: uint8(abi.Struct), Str_: structStr(fields), }, PkgPath_: pkgPath, Fields: fields, } + var hash uint32 = 9059 var comparable bool = true var typalign uint8 for _, f := range fields { @@ -172,7 +172,14 @@ func Struct(pkgPath string, size uintptr, fields ...abi.StructField) *Type { ret.PtrBytes = f.Offset + f.Typ.PtrBytes } comparable = comparable && (ft.Equal != nil) + if f.Embedded_ { + hash += 8861 + } + hash += hashString(f.Tag_) + hash += hashString(f.Name_) + hash += f.Typ.Hash } + ret.Hash = hash ret.Align_ = typalign ret.FieldAlign_ = typalign if comparable { @@ -220,7 +227,7 @@ func newPointer(elem *Type) *Type { Type: Type{ Size_: unsafe.Sizeof(uintptr(0)), PtrBytes: pointerSize, - Hash: uint32(abi.Pointer), // TODO(xsw): hash + Hash: 9067 + 2*elem.Hash, Align_: pointerAlign, FieldAlign_: pointerAlign, Kind_: uint8(abi.Pointer), @@ -259,7 +266,7 @@ func SliceOf(elem *Type) *Type { Type: Type{ Size_: unsafe.Sizeof([]int{}), PtrBytes: pointerSize, - Hash: uint32(abi.Slice), + Hash: 9049 + 2*elem.Hash, Align_: pointerAlign, FieldAlign_: pointerAlign, Kind_: uint8(abi.Slice), @@ -279,7 +286,7 @@ func ArrayOf(length uintptr, elem *Type) *Type { ret := &abi.ArrayType{ Type: Type{ Size_: length * elem.Size_, - Hash: uint32(abi.Array), + Hash: 9043 + 2*uint32(length) + 3*elem.Hash, Align_: elem.Align_, FieldAlign_: elem.FieldAlign_, Kind_: uint8(abi.Array), @@ -326,7 +333,7 @@ func ChanOf(dir int, strChan string, elem *Type) *Type { Type: Type{ Size_: pointerSize, PtrBytes: pointerSize, - Hash: uint32(abi.Chan), + Hash: 9127 + 2*uint32(dir) + 3*elem.Hash, Align_: pointerAlign, TFlag: abi.TFlagRegularMemory, FieldAlign_: pointerAlign, @@ -349,7 +356,7 @@ func MapOf(key, elem *Type, bucket *Type, flags int) *Type { Type: Type{ Size_: unsafe.Sizeof(uintptr(0)), PtrBytes: pointerSize, - Hash: uint32(abi.Map), + Hash: 9109 + 2*key.Hash + 3*elem.Hash, Align_: pointerAlign, FieldAlign_: pointerAlign, Kind_: uint8(abi.Map), @@ -567,4 +574,14 @@ func (r *rtypes) addType(typ *Type) { var rtypeList rtypes +// hashString computes the Fowler–Noll–Vo hash of s. +func hashString(s string) uint32 { + var h uint32 + for i := 0; i < len(s); i++ { + h ^= uint32(s[i]) + h *= 16777619 + } + return h +} + // -----------------------------------------------------------------------------