mirror of
https://github.com/golang/go
synced 2024-11-20 06:14:53 -07:00
e1366f94ee
When dynamically linking Go code, it is no longer safe to assume that strings that end up in method names are identical if they are equal. The performance impact seems to be noise: benchmark old ns/op new ns/op delta BenchmarkAssertI2E2 13.3 13.1 -1.50% BenchmarkAssertE2I 23.5 23.2 -1.28% BenchmarkAssertE2E2Blank 0.83 0.82 -1.20% BenchmarkConvT2ISmall 60.7 60.1 -0.99% BenchmarkAssertI2T 10.2 10.1 -0.98% BenchmarkAssertE2T 10.2 10.3 +0.98% BenchmarkConvT2ESmall 56.7 57.2 +0.88% BenchmarkConvT2ILarge 59.4 58.9 -0.84% BenchmarkConvI2E 13.0 12.9 -0.77% BenchmarkAssertI2E 13.4 13.3 -0.75% BenchmarkConvT2IUintptr 57.9 58.3 +0.69% BenchmarkConvT2ELarge 55.9 55.6 -0.54% BenchmarkAssertI2I 23.8 23.7 -0.42% BenchmarkConvT2EUintptr 55.4 55.5 +0.18% BenchmarkAssertE2E 6.12 6.11 -0.16% BenchmarkAssertE2E2 14.4 14.4 +0.00% BenchmarkAssertE2T2 10.0 10.0 +0.00% BenchmarkAssertE2T2Blank 0.83 0.83 +0.00% BenchmarkAssertE2TLarge 10.7 10.7 +0.00% BenchmarkAssertI2E2Blank 0.83 0.83 +0.00% BenchmarkConvI2I 23.4 23.4 +0.00% Change-Id: I0b3dfc314215a4d4e09eec6b42c1e3ebce33eb56 Reviewed-on: https://go-review.googlesource.com/8239 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
433 lines
9.5 KiB
Go
433 lines
9.5 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
hashSize = 1009
|
|
)
|
|
|
|
var (
|
|
ifaceLock mutex // lock for accessing hash
|
|
hash [hashSize]*itab
|
|
)
|
|
|
|
// fInterface is our standard non-empty interface. We use it instead
|
|
// of interface{f()} in function prototypes because gofmt insists on
|
|
// putting lots of newlines in the otherwise concise interface{f()}.
|
|
type fInterface interface {
|
|
f()
|
|
}
|
|
|
|
func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
|
|
if len(inter.mhdr) == 0 {
|
|
throw("internal error - misuse of itab")
|
|
}
|
|
|
|
// easy case
|
|
x := typ.x
|
|
if x == nil {
|
|
if canfail {
|
|
return nil
|
|
}
|
|
panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *inter.mhdr[0].name})
|
|
}
|
|
|
|
// compiler has provided some good hash codes for us.
|
|
h := inter.typ.hash
|
|
h += 17 * typ.hash
|
|
// TODO(rsc): h += 23 * x.mhash ?
|
|
h %= hashSize
|
|
|
|
// look twice - once without lock, once with.
|
|
// common case will be no lock contention.
|
|
var m *itab
|
|
var locked int
|
|
for locked = 0; locked < 2; locked++ {
|
|
if locked != 0 {
|
|
lock(&ifaceLock)
|
|
}
|
|
for m = (*itab)(atomicloadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
|
|
if m.inter == inter && m._type == typ {
|
|
if m.bad != 0 {
|
|
m = nil
|
|
if !canfail {
|
|
// this can only happen if the conversion
|
|
// was already done once using the , ok form
|
|
// and we have a cached negative result.
|
|
// the cached result doesn't record which
|
|
// interface function was missing, so jump
|
|
// down to the interface check, which will
|
|
// do more work but give a better error.
|
|
goto search
|
|
}
|
|
}
|
|
if locked != 0 {
|
|
unlock(&ifaceLock)
|
|
}
|
|
return m
|
|
}
|
|
}
|
|
}
|
|
|
|
m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*ptrSize, 0, &memstats.other_sys))
|
|
m.inter = inter
|
|
m._type = typ
|
|
|
|
search:
|
|
// both inter and typ have method sorted by name,
|
|
// and interface names are unique,
|
|
// so can iterate over both in lock step;
|
|
// the loop is O(ni+nt) not O(ni*nt).
|
|
ni := len(inter.mhdr)
|
|
nt := len(x.mhdr)
|
|
j := 0
|
|
for k := 0; k < ni; k++ {
|
|
i := &inter.mhdr[k]
|
|
iname := i.name
|
|
ipkgpath := i.pkgpath
|
|
itype := i._type
|
|
for ; j < nt; j++ {
|
|
t := &x.mhdr[j]
|
|
if t.mtyp == itype && (t.name == iname || *t.name == *iname) && t.pkgpath == ipkgpath {
|
|
if m != nil {
|
|
*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*ptrSize)) = t.ifn
|
|
}
|
|
goto nextimethod
|
|
}
|
|
}
|
|
// didn't find method
|
|
if !canfail {
|
|
if locked != 0 {
|
|
unlock(&ifaceLock)
|
|
}
|
|
panic(&TypeAssertionError{"", *typ._string, *inter.typ._string, *iname})
|
|
}
|
|
m.bad = 1
|
|
break
|
|
nextimethod:
|
|
}
|
|
if locked == 0 {
|
|
throw("invalid itab locking")
|
|
}
|
|
m.link = hash[h]
|
|
atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
|
|
unlock(&ifaceLock)
|
|
if m.bad != 0 {
|
|
return nil
|
|
}
|
|
return m
|
|
}
|
|
|
|
func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
|
|
tab := getitab(inter, t, false)
|
|
atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
|
|
return tab
|
|
}
|
|
|
|
func convT2E(t *_type, elem unsafe.Pointer, x unsafe.Pointer) (e interface{}) {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
if isDirectIface(t) {
|
|
ep._type = t
|
|
typedmemmove(t, unsafe.Pointer(&ep.data), elem)
|
|
} else {
|
|
if x == nil {
|
|
x = newobject(t)
|
|
}
|
|
// TODO: We allocate a zeroed object only to overwrite it with
|
|
// actual data. Figure out how to avoid zeroing. Also below in convT2I.
|
|
typedmemmove(t, x, elem)
|
|
ep._type = t
|
|
ep.data = x
|
|
}
|
|
return
|
|
}
|
|
|
|
func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer, x unsafe.Pointer) (i fInterface) {
|
|
tab := (*itab)(atomicloadp(unsafe.Pointer(cache)))
|
|
if tab == nil {
|
|
tab = getitab(inter, t, false)
|
|
atomicstorep(unsafe.Pointer(cache), unsafe.Pointer(tab))
|
|
}
|
|
pi := (*iface)(unsafe.Pointer(&i))
|
|
if isDirectIface(t) {
|
|
pi.tab = tab
|
|
typedmemmove(t, unsafe.Pointer(&pi.data), elem)
|
|
} else {
|
|
if x == nil {
|
|
x = newobject(t)
|
|
}
|
|
typedmemmove(t, x, elem)
|
|
pi.tab = tab
|
|
pi.data = x
|
|
}
|
|
return
|
|
}
|
|
|
|
func panicdottype(have, want, iface *_type) {
|
|
haveString := ""
|
|
if have != nil {
|
|
haveString = *have._string
|
|
}
|
|
panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""})
|
|
}
|
|
|
|
func assertI2T(t *_type, i fInterface, r unsafe.Pointer) {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
panic(&TypeAssertionError{"", "", *t._string, ""})
|
|
}
|
|
if tab._type != t {
|
|
panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
|
|
}
|
|
if r != nil {
|
|
if isDirectIface(t) {
|
|
writebarrierptr((*uintptr)(r), uintptr(ip.data))
|
|
} else {
|
|
typedmemmove(t, r, ip.data)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertI2T2(t *_type, i fInterface, r unsafe.Pointer) bool {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil || tab._type != t {
|
|
if r != nil {
|
|
memclr(r, uintptr(t.size))
|
|
}
|
|
return false
|
|
}
|
|
if r != nil {
|
|
if isDirectIface(t) {
|
|
writebarrierptr((*uintptr)(r), uintptr(ip.data))
|
|
} else {
|
|
typedmemmove(t, r, ip.data)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func assertE2T(t *_type, e interface{}, r unsafe.Pointer) {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
if ep._type == nil {
|
|
panic(&TypeAssertionError{"", "", *t._string, ""})
|
|
}
|
|
if ep._type != t {
|
|
panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
|
|
}
|
|
if r != nil {
|
|
if isDirectIface(t) {
|
|
writebarrierptr((*uintptr)(r), uintptr(ep.data))
|
|
} else {
|
|
typedmemmove(t, r, ep.data)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The compiler ensures that r is non-nil.
|
|
func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
if ep._type != t {
|
|
memclr(r, uintptr(t.size))
|
|
return false
|
|
}
|
|
if isDirectIface(t) {
|
|
writebarrierptr((*uintptr)(r), uintptr(ep.data))
|
|
} else {
|
|
typedmemmove(t, r, ep.data)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func convI2E(i fInterface) (r interface{}) {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
return
|
|
}
|
|
rp := (*eface)(unsafe.Pointer(&r))
|
|
rp._type = tab._type
|
|
rp.data = ip.data
|
|
return
|
|
}
|
|
|
|
func assertI2E(inter *interfacetype, i fInterface, r *interface{}) {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
// explicit conversions require non-nil interface value.
|
|
panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
|
|
}
|
|
rp := (*eface)(unsafe.Pointer(r))
|
|
rp._type = tab._type
|
|
rp.data = ip.data
|
|
return
|
|
}
|
|
|
|
// The compiler ensures that r is non-nil.
|
|
func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
return false
|
|
}
|
|
rp := (*eface)(unsafe.Pointer(r))
|
|
rp._type = tab._type
|
|
rp.data = ip.data
|
|
return true
|
|
}
|
|
|
|
func convI2I(inter *interfacetype, i fInterface) (r fInterface) {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
return
|
|
}
|
|
rp := (*iface)(unsafe.Pointer(&r))
|
|
if tab.inter == inter {
|
|
rp.tab = tab
|
|
rp.data = ip.data
|
|
return
|
|
}
|
|
rp.tab = getitab(inter, tab._type, false)
|
|
rp.data = ip.data
|
|
return
|
|
}
|
|
|
|
func assertI2I(inter *interfacetype, i fInterface, r *fInterface) {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
// explicit conversions require non-nil interface value.
|
|
panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
|
|
}
|
|
rp := (*iface)(unsafe.Pointer(r))
|
|
if tab.inter == inter {
|
|
rp.tab = tab
|
|
rp.data = ip.data
|
|
return
|
|
}
|
|
rp.tab = getitab(inter, tab._type, false)
|
|
rp.data = ip.data
|
|
}
|
|
|
|
func assertI2I2(inter *interfacetype, i fInterface, r *fInterface) bool {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
if r != nil {
|
|
*r = nil
|
|
}
|
|
return false
|
|
}
|
|
if tab.inter != inter {
|
|
tab = getitab(inter, tab._type, true)
|
|
if tab == nil {
|
|
if r != nil {
|
|
*r = nil
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
if r != nil {
|
|
rp := (*iface)(unsafe.Pointer(r))
|
|
rp.tab = tab
|
|
rp.data = ip.data
|
|
}
|
|
return true
|
|
}
|
|
|
|
func assertE2I(inter *interfacetype, e interface{}, r *fInterface) {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
t := ep._type
|
|
if t == nil {
|
|
// explicit conversions require non-nil interface value.
|
|
panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
|
|
}
|
|
rp := (*iface)(unsafe.Pointer(r))
|
|
rp.tab = getitab(inter, t, false)
|
|
rp.data = ep.data
|
|
}
|
|
|
|
func assertE2I2(inter *interfacetype, e interface{}, r *fInterface) bool {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
t := ep._type
|
|
if t == nil {
|
|
if r != nil {
|
|
*r = nil
|
|
}
|
|
return false
|
|
}
|
|
tab := getitab(inter, t, true)
|
|
if tab == nil {
|
|
if r != nil {
|
|
*r = nil
|
|
}
|
|
return false
|
|
}
|
|
if r != nil {
|
|
rp := (*iface)(unsafe.Pointer(r))
|
|
rp.tab = tab
|
|
rp.data = ep.data
|
|
}
|
|
return true
|
|
}
|
|
|
|
//go:linkname reflect_ifaceE2I reflect.ifaceE2I
|
|
func reflect_ifaceE2I(inter *interfacetype, e interface{}, dst *fInterface) {
|
|
assertE2I(inter, e, dst)
|
|
}
|
|
|
|
func assertE2E(inter *interfacetype, e interface{}, r *interface{}) {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
if ep._type == nil {
|
|
// explicit conversions require non-nil interface value.
|
|
panic(&TypeAssertionError{"", "", *inter.typ._string, ""})
|
|
}
|
|
*r = e
|
|
}
|
|
|
|
// The compiler ensures that r is non-nil.
|
|
func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
if ep._type == nil {
|
|
*r = nil
|
|
return false
|
|
}
|
|
*r = e
|
|
return true
|
|
}
|
|
|
|
func ifacethash(i fInterface) uint32 {
|
|
ip := (*iface)(unsafe.Pointer(&i))
|
|
tab := ip.tab
|
|
if tab == nil {
|
|
return 0
|
|
}
|
|
return tab._type.hash
|
|
}
|
|
|
|
func efacethash(e interface{}) uint32 {
|
|
ep := (*eface)(unsafe.Pointer(&e))
|
|
t := ep._type
|
|
if t == nil {
|
|
return 0
|
|
}
|
|
return t.hash
|
|
}
|
|
|
|
func iterate_itabs(fn func(*itab)) {
|
|
for _, h := range &hash {
|
|
for ; h != nil; h = h.link {
|
|
fn(h)
|
|
}
|
|
}
|
|
}
|