mirror of https://github.com/golang/go.git
364 lines
10 KiB
Go
364 lines
10 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.
|
|
|
|
//go:build goexperiment.swissmap
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"internal/abi"
|
|
"internal/runtime/maps"
|
|
"internal/runtime/sys"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
// TODO: remove? These are used by tests but not the actual map
|
|
loadFactorNum = 7
|
|
loadFactorDen = 8
|
|
)
|
|
|
|
type maptype = abi.SwissMapType
|
|
|
|
//go:linkname maps_errNilAssign internal/runtime/maps.errNilAssign
|
|
var maps_errNilAssign error = plainError("assignment to entry in nil map")
|
|
|
|
//go:linkname maps_mapKeyError internal/runtime/maps.mapKeyError
|
|
func maps_mapKeyError(t *abi.SwissMapType, p unsafe.Pointer) error {
|
|
return mapKeyError(t, p)
|
|
}
|
|
|
|
func makemap64(t *abi.SwissMapType, hint int64, m *maps.Map) *maps.Map {
|
|
if int64(int(hint)) != hint {
|
|
hint = 0
|
|
}
|
|
return makemap(t, int(hint), m)
|
|
}
|
|
|
|
// makemap_small implements Go map creation for make(map[k]v) and
|
|
// make(map[k]v, hint) when hint is known to be at most abi.SwissMapGroupSlots
|
|
// at compile time and the map needs to be allocated on the heap.
|
|
//
|
|
// makemap_small should be an internal detail,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/bytedance/sonic
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname makemap_small
|
|
func makemap_small() *maps.Map {
|
|
return maps.NewEmptyMap()
|
|
}
|
|
|
|
// makemap implements Go map creation for make(map[k]v, hint).
|
|
// If the compiler has determined that the map or the first group
|
|
// can be created on the stack, m and optionally m.dirPtr may be non-nil.
|
|
// If m != nil, the map can be created directly in m.
|
|
// If m.dirPtr != nil, it points to a group usable for a small map.
|
|
//
|
|
// makemap should be an internal detail,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/ugorji/go/codec
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname makemap
|
|
func makemap(t *abi.SwissMapType, hint int, m *maps.Map) *maps.Map {
|
|
if hint < 0 {
|
|
hint = 0
|
|
}
|
|
|
|
return maps.NewMap(t, uintptr(hint), m, maxAlloc)
|
|
}
|
|
|
|
// mapaccess1 returns a pointer to h[key]. Never returns nil, instead
|
|
// it will return a reference to the zero object for the elem type if
|
|
// the key is not in the map.
|
|
// NOTE: The returned pointer may keep the whole map live, so don't
|
|
// hold onto it for very long.
|
|
//
|
|
// mapaccess1 is pushed from internal/runtime/maps. We could just call it, but
|
|
// we want to avoid one layer of call.
|
|
//
|
|
//go:linkname mapaccess1
|
|
func mapaccess1(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer
|
|
|
|
// mapaccess2 should be an internal detail,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/ugorji/go/codec
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname mapaccess2
|
|
func mapaccess2(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) (unsafe.Pointer, bool)
|
|
|
|
func mapaccess1_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) unsafe.Pointer {
|
|
e := mapaccess1(t, m, key)
|
|
if e == unsafe.Pointer(&zeroVal[0]) {
|
|
return zero
|
|
}
|
|
return e
|
|
}
|
|
|
|
func mapaccess2_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) (unsafe.Pointer, bool) {
|
|
e := mapaccess1(t, m, key)
|
|
if e == unsafe.Pointer(&zeroVal[0]) {
|
|
return zero, false
|
|
}
|
|
return e, true
|
|
}
|
|
|
|
// mapassign is pushed from internal/runtime/maps. We could just call it, but
|
|
// we want to avoid one layer of call.
|
|
//
|
|
// mapassign should be an internal detail,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/bytedance/sonic
|
|
// - github.com/RomiChan/protobuf
|
|
// - github.com/segmentio/encoding
|
|
// - github.com/ugorji/go/codec
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname mapassign
|
|
func mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer
|
|
|
|
// mapdelete should be an internal detail,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/ugorji/go/codec
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname mapdelete
|
|
func mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) {
|
|
if raceenabled && m != nil {
|
|
callerpc := sys.GetCallerPC()
|
|
pc := abi.FuncPCABIInternal(mapdelete)
|
|
racewritepc(unsafe.Pointer(m), callerpc, pc)
|
|
raceReadObjectPC(t.Key, key, callerpc, pc)
|
|
}
|
|
if msanenabled && m != nil {
|
|
msanread(key, t.Key.Size_)
|
|
}
|
|
if asanenabled && m != nil {
|
|
asanread(key, t.Key.Size_)
|
|
}
|
|
|
|
m.Delete(t, key)
|
|
}
|
|
|
|
// mapIterStart initializes the Iter struct used for ranging over maps and
|
|
// performs the first step of iteration. The Iter struct pointed to by 'it' is
|
|
// allocated on the stack by the compilers order pass or on the heap by
|
|
// reflect. Both need to have zeroed it since the struct contains pointers.
|
|
func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) {
|
|
if raceenabled && m != nil {
|
|
callerpc := sys.GetCallerPC()
|
|
racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart))
|
|
}
|
|
|
|
it.Init(t, m)
|
|
it.Next()
|
|
}
|
|
|
|
// mapIterNext performs the next step of iteration. Afterwards, the next
|
|
// key/elem are in it.Key()/it.Elem().
|
|
func mapIterNext(it *maps.Iter) {
|
|
if raceenabled {
|
|
callerpc := sys.GetCallerPC()
|
|
racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext))
|
|
}
|
|
|
|
it.Next()
|
|
}
|
|
|
|
// mapclear deletes all keys from a map.
|
|
func mapclear(t *abi.SwissMapType, m *maps.Map) {
|
|
if raceenabled && m != nil {
|
|
callerpc := sys.GetCallerPC()
|
|
pc := abi.FuncPCABIInternal(mapclear)
|
|
racewritepc(unsafe.Pointer(m), callerpc, pc)
|
|
}
|
|
|
|
m.Clear(t)
|
|
}
|
|
|
|
// Reflect stubs. Called from ../reflect/asm_*.s
|
|
|
|
// reflect_makemap is for package reflect,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - gitee.com/quant1x/gox
|
|
// - github.com/modern-go/reflect2
|
|
// - github.com/goccy/go-json
|
|
// - github.com/RomiChan/protobuf
|
|
// - github.com/segmentio/encoding
|
|
// - github.com/v2pro/plz
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname reflect_makemap reflect.makemap
|
|
func reflect_makemap(t *abi.SwissMapType, cap int) *maps.Map {
|
|
// Check invariants and reflects math.
|
|
if t.Key.Equal == nil {
|
|
throw("runtime.reflect_makemap: unsupported map key type")
|
|
}
|
|
// TODO: other checks
|
|
|
|
return makemap(t, cap, nil)
|
|
}
|
|
|
|
// reflect_mapaccess is for package reflect,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - gitee.com/quant1x/gox
|
|
// - github.com/modern-go/reflect2
|
|
// - github.com/v2pro/plz
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname reflect_mapaccess reflect.mapaccess
|
|
func reflect_mapaccess(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer {
|
|
elem, ok := mapaccess2(t, m, key)
|
|
if !ok {
|
|
// reflect wants nil for a missing element
|
|
elem = nil
|
|
}
|
|
return elem
|
|
}
|
|
|
|
//go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr
|
|
func reflect_mapaccess_faststr(t *abi.SwissMapType, m *maps.Map, key string) unsafe.Pointer {
|
|
elem, ok := mapaccess2_faststr(t, m, key)
|
|
if !ok {
|
|
// reflect wants nil for a missing element
|
|
elem = nil
|
|
}
|
|
return elem
|
|
}
|
|
|
|
// reflect_mapassign is for package reflect,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - gitee.com/quant1x/gox
|
|
// - github.com/v2pro/plz
|
|
//
|
|
// Do not remove or change the type signature.
|
|
//
|
|
//go:linkname reflect_mapassign reflect.mapassign0
|
|
func reflect_mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer, elem unsafe.Pointer) {
|
|
p := mapassign(t, m, key)
|
|
typedmemmove(t.Elem, p, elem)
|
|
}
|
|
|
|
//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr0
|
|
func reflect_mapassign_faststr(t *abi.SwissMapType, m *maps.Map, key string, elem unsafe.Pointer) {
|
|
p := mapassign_faststr(t, m, key)
|
|
typedmemmove(t.Elem, p, elem)
|
|
}
|
|
|
|
//go:linkname reflect_mapdelete reflect.mapdelete
|
|
func reflect_mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) {
|
|
mapdelete(t, m, key)
|
|
}
|
|
|
|
//go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr
|
|
func reflect_mapdelete_faststr(t *abi.SwissMapType, m *maps.Map, key string) {
|
|
mapdelete_faststr(t, m, key)
|
|
}
|
|
|
|
// reflect_maplen is for package reflect,
|
|
// but widely used packages access it using linkname.
|
|
// Notable members of the hall of shame include:
|
|
// - github.com/goccy/go-json
|
|
// - github.com/wI2L/jettison
|
|
//
|
|
// Do not remove or change the type signature.
|
|
// See go.dev/issue/67401.
|
|
//
|
|
//go:linkname reflect_maplen reflect.maplen
|
|
func reflect_maplen(m *maps.Map) int {
|
|
if m == nil {
|
|
return 0
|
|
}
|
|
if raceenabled {
|
|
callerpc := sys.GetCallerPC()
|
|
racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(reflect_maplen))
|
|
}
|
|
return int(m.Used())
|
|
}
|
|
|
|
//go:linkname reflect_mapclear reflect.mapclear
|
|
func reflect_mapclear(t *abi.SwissMapType, m *maps.Map) {
|
|
mapclear(t, m)
|
|
}
|
|
|
|
//go:linkname reflectlite_maplen internal/reflectlite.maplen
|
|
func reflectlite_maplen(m *maps.Map) int {
|
|
if m == nil {
|
|
return 0
|
|
}
|
|
if raceenabled {
|
|
callerpc := sys.GetCallerPC()
|
|
racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(reflect_maplen))
|
|
}
|
|
return int(m.Used())
|
|
}
|
|
|
|
// mapinitnoop is a no-op function known the Go linker; if a given global
|
|
// map (of the right size) is determined to be dead, the linker will
|
|
// rewrite the relocation (from the package init func) from the outlined
|
|
// map init function to this symbol. Defined in assembly so as to avoid
|
|
// complications with instrumentation (coverage, etc).
|
|
func mapinitnoop()
|
|
|
|
// mapclone for implementing maps.Clone
|
|
//
|
|
//go:linkname mapclone maps.clone
|
|
func mapclone(m any) any {
|
|
e := efaceOf(&m)
|
|
e.data = unsafe.Pointer(mapclone2((*abi.SwissMapType)(unsafe.Pointer(e._type)), (*maps.Map)(e.data)))
|
|
return m
|
|
}
|
|
|
|
func mapclone2(t *abi.SwissMapType, src *maps.Map) *maps.Map {
|
|
dst := makemap(t, int(src.Used()), nil)
|
|
|
|
var iter maps.Iter
|
|
iter.Init(t, src)
|
|
for iter.Next(); iter.Key() != nil; iter.Next() {
|
|
dst.Put(t, iter.Key(), iter.Elem())
|
|
}
|
|
|
|
return dst
|
|
}
|
|
|
|
// keys for implementing maps.keys
|
|
//
|
|
//go:linkname keys maps.keys
|
|
func keys(m any, p unsafe.Pointer) {
|
|
// Currently unused in the maps package.
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// values for implementing maps.values
|
|
//
|
|
//go:linkname values maps.values
|
|
func values(m any, p unsafe.Pointer) {
|
|
// Currently unused in the maps package.
|
|
panic("unimplemented")
|
|
}
|