mapaccess1* and mapaccess2* functions share the same implementation and differ only in whether the boolean "found" is returned.
This change replaces mapaccess1* calls with mapaccess2*.
We can do this transparently, since the call site can safely discard the second (boolean) result.
Ideally, mapacces1* functions could be removed entirely, but this change keeps them as thin wrappers for compatibility.
Fixes #73196
Change-Id: I07c3423d22ed1095ac3666d00e134c2747b2f9c1
Reviewed-on: https://go-review.googlesource.com/c/go/+/736020
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@golang.org>
r := n.Rhs[0].(*ir.IndexExpr)
walkExprListSafe(n.Lhs, init)
+
r.X = walkExpr(r.X, init)
r.Index = walkExpr(r.Index, init)
+ map_ := r.X
t := r.X.Type()
-
fast := mapfast(t)
key := mapKeyArg(fast, r, r.Index, false)
+ args := []ir.Node{reflectdata.IndexMapRType(base.Pos, r), map_, key}
// from:
// a,b = m[i]
// a = *var
a := n.Lhs[0]
- var call *ir.CallExpr
- if w := t.Elem().Size(); w <= abi.ZeroValSize {
- fn := mapfn(mapaccess2[fast], t, false)
- call = mkcall1(fn, fn.Type().ResultsTuple(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key)
+ var mapFn ir.Node
+ if t.Elem().Size() > abi.ZeroValSize {
+ args = append(args, reflectdata.ZeroAddr(t.Elem().Size()))
+ mapFn = mapfn("mapaccess2_fat", t, true)
} else {
- fn := mapfn("mapaccess2_fat", t, true)
- z := reflectdata.ZeroAddr(w)
- call = mkcall1(fn, fn.Type().ResultsTuple(), init, reflectdata.IndexMapRType(base.Pos, r), r.X, key, z)
+ mapFn = mapfn(mapaccess[fast], t, false)
}
+ call := mkcall1(mapFn, mapFn.Type().ResultsTuple(), init, args...)
// mapaccess2* returns a typed bool, but due to spec changes,
// the boolean result of i.(T) is now untyped so we make it the
key := mapKeyArg(fast, n, n.Index, n.Assigned)
args := []ir.Node{reflectdata.IndexMapRType(base.Pos, n), map_, key}
+ if n.Assigned {
+ mapFn := mapfn(mapassign[fast], t, false)
+ call := mkcall1(mapFn, nil, init, args...)
+ call.SetType(types.NewPtr(t.Elem()))
+ call.MarkNonNil() // mapassign always return non-nil pointers.
+ star := ir.NewStarExpr(base.Pos, call)
+ star.SetType(t.Elem())
+ star.SetTypecheck(1)
+ return star
+ }
+
+ // from:
+ // m[i]
+ // to:
+ // var, _ = mapaccess2*(t, m, i)
+ // *var
var mapFn ir.Node
- switch {
- case n.Assigned:
- mapFn = mapfn(mapassign[fast], t, false)
- case t.Elem().Size() > abi.ZeroValSize:
+ if t.Elem().Size() > abi.ZeroValSize {
args = append(args, reflectdata.ZeroAddr(t.Elem().Size()))
- mapFn = mapfn("mapaccess1_fat", t, true)
- default:
- mapFn = mapfn(mapaccess1[fast], t, false)
+ mapFn = mapfn("mapaccess2_fat", t, true)
+ } else {
+ mapFn = mapfn(mapaccess[fast], t, false)
}
- call := mkcall1(mapFn, nil, init, args...)
- call.SetType(types.NewPtr(t.Elem()))
- call.MarkNonNil() // mapaccess1* and mapassign always return non-nil pointers.
- star := ir.NewStarExpr(base.Pos, call)
+ call := mkcall1(mapFn, mapFn.Type().ResultsTuple(), init, args...)
+
+ var_ := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewPtr(t.Elem()))
+ var_.SetTypecheck(1)
+ var_.MarkNonNil() // mapaccess always returns a non-nill pointer
+
+ bool_ := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
+ bool_.SetTypecheck(1)
+
+ r := ir.NewAssignListStmt(base.Pos, ir.OAS2FUNC, []ir.Node{var_, bool_}, []ir.Node{call})
+ r.SetTypecheck(1)
+ init.Append(walkExpr(r, init))
+
+ star := ir.NewStarExpr(base.Pos, var_)
star.SetType(t.Elem())
star.SetTypecheck(1)
return star
return mapnames{base, base + "_fast32", base + "_fast32" + ptr, base + "_fast64", base + "_fast64" + ptr, base + "_faststr"}
}
-var mapaccess1 = mkmapnames("mapaccess1", "")
-var mapaccess2 = mkmapnames("mapaccess2", "")
+var mapaccess = mkmapnames("mapaccess2", "")
var mapassign = mkmapnames("mapassign", "ptr")
var mapdelete = mkmapnames("mapdelete", "")
//go:linkname errNilAssign
var errNilAssign error
-// Pull from runtime. It is important that is this the exact same copy as the
-// runtime because runtime.mapaccess1_fat compares the returned pointer with
-// &runtime.zeroVal[0].
// TODO: move zeroVal to internal/abi?
//
//go:linkname zeroVal runtime.zeroVal
//
//go:linkname runtime_mapaccess1 runtime.mapaccess1
func runtime_mapaccess1(typ *abi.MapType, m *Map, key unsafe.Pointer) unsafe.Pointer {
- if race.Enabled && m != nil {
- callerpc := sys.GetCallerPC()
- pc := abi.FuncPCABIInternal(runtime_mapaccess1)
- race.ReadPC(unsafe.Pointer(m), callerpc, pc)
- race.ReadObjectPC(typ.Key, key, callerpc, pc)
- }
- if msan.Enabled && m != nil {
- msan.Read(key, typ.Key.Size_)
- }
- if asan.Enabled && m != nil {
- asan.Read(key, typ.Key.Size_)
- }
-
- if m == nil || m.Used() == 0 {
- if err := mapKeyError(typ, key); err != nil {
- panic(err) // see issue 23734
- }
- return unsafe.Pointer(&zeroVal[0])
- }
-
- if m.writing != 0 {
- fatal("concurrent map read and map write")
- }
-
- hash := typ.Hasher(key, m.seed)
-
- if m.dirLen <= 0 {
- _, elem, ok := m.getWithKeySmall(typ, hash, key)
- if !ok {
- return unsafe.Pointer(&zeroVal[0])
- }
- return elem
- }
-
- // Select table.
- idx := m.directoryIndex(hash)
- t := m.directoryAt(idx)
-
- // Probe table.
- seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
- h2Hash := h2(hash)
- for ; ; seq = seq.next() {
- g := t.groups.group(typ, seq.offset)
-
- match := g.ctrls().matchH2(h2Hash)
-
- for match != 0 {
- i := match.first()
-
- slotKey := g.key(typ, i)
- slotKeyOrig := slotKey
- if typ.IndirectKey() {
- slotKey = *((*unsafe.Pointer)(slotKey))
- }
- if typ.Key.Equal(key, slotKey) {
- slotElem := unsafe.Pointer(uintptr(slotKeyOrig) + typ.ElemOff)
- if typ.IndirectElem() {
- slotElem = *((*unsafe.Pointer)(slotElem))
- }
- return slotElem
- }
- match = match.removeFirst()
- }
-
- match = g.ctrls().matchEmpty()
- if match != 0 {
- // Finding an empty slot means we've reached the end of
- // the probe sequence.
- return unsafe.Pointer(&zeroVal[0])
- }
- }
+ p, _ := runtime_mapaccess2(typ, m, key)
+ return p
}
//go:linkname runtime_mapaccess2 runtime.mapaccess2
func runtime_mapaccess2(typ *abi.MapType, m *Map, key unsafe.Pointer) (unsafe.Pointer, bool) {
if race.Enabled && m != nil {
callerpc := sys.GetCallerPC()
- pc := abi.FuncPCABIInternal(runtime_mapaccess1)
+ pc := abi.FuncPCABIInternal(runtime_mapaccess2)
race.ReadPC(unsafe.Pointer(m), callerpc, pc)
race.ReadObjectPC(typ.Key, key, callerpc, pc)
}
//go:linkname runtime_mapaccess1_fast32 runtime.mapaccess1_fast32
func runtime_mapaccess1_fast32(typ *abi.MapType, m *Map, key uint32) unsafe.Pointer {
- if race.Enabled && m != nil {
- callerpc := sys.GetCallerPC()
- pc := abi.FuncPCABIInternal(runtime_mapaccess1_fast32)
- race.ReadPC(unsafe.Pointer(m), callerpc, pc)
- }
-
- if m == nil || m.Used() == 0 {
- return unsafe.Pointer(&zeroVal[0])
- }
-
- if m.writing != 0 {
- fatal("concurrent map read and map write")
- return nil
- }
-
- if m.dirLen == 0 {
- g := groupReference{
- data: m.dirPtr,
- }
- full := g.ctrls().matchFull()
- slotKey := g.key(typ, 0)
- slotSize := typ.SlotSize
- for full != 0 {
- if key == *(*uint32)(slotKey) && full.lowestSet() {
- slotElem := unsafe.Pointer(uintptr(slotKey) + typ.ElemOff)
- return slotElem
- }
- slotKey = unsafe.Pointer(uintptr(slotKey) + slotSize)
- full = full.shiftOutLowest()
- }
- return unsafe.Pointer(&zeroVal[0])
- }
-
- k := key
- hash := typ.Hasher(abi.NoEscape(unsafe.Pointer(&k)), m.seed)
-
- // Select table.
- idx := m.directoryIndex(hash)
- t := m.directoryAt(idx)
-
- // Probe table.
- seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
- h2Hash := h2(hash)
- for ; ; seq = seq.next() {
- g := t.groups.group(typ, seq.offset)
-
- match := g.ctrls().matchH2(h2Hash)
-
- for match != 0 {
- i := match.first()
-
- slotKey := g.key(typ, i)
- if key == *(*uint32)(slotKey) {
- slotElem := unsafe.Pointer(uintptr(slotKey) + typ.ElemOff)
- return slotElem
- }
- match = match.removeFirst()
- }
-
- match = g.ctrls().matchEmpty()
- if match != 0 {
- // Finding an empty slot means we've reached the end of
- // the probe sequence.
- return unsafe.Pointer(&zeroVal[0])
- }
- }
+ p, _ := runtime_mapaccess2_fast32(typ, m, key)
+ return p
}
//go:linkname runtime_mapaccess2_fast32 runtime.mapaccess2_fast32
//go:linkname runtime_mapaccess1_fast64 runtime.mapaccess1_fast64
func runtime_mapaccess1_fast64(typ *abi.MapType, m *Map, key uint64) unsafe.Pointer {
- if race.Enabled && m != nil {
- callerpc := sys.GetCallerPC()
- pc := abi.FuncPCABIInternal(runtime_mapaccess1_fast64)
- race.ReadPC(unsafe.Pointer(m), callerpc, pc)
- }
-
- if m == nil || m.Used() == 0 {
- return unsafe.Pointer(&zeroVal[0])
- }
-
- if m.writing != 0 {
- fatal("concurrent map read and map write")
- return nil
- }
-
- if m.dirLen == 0 {
- g := groupReference{
- data: m.dirPtr,
- }
- full := g.ctrls().matchFull()
- slotKey := g.key(typ, 0)
- slotSize := typ.SlotSize
- for full != 0 {
- if key == *(*uint64)(slotKey) && full.lowestSet() {
- slotElem := unsafe.Pointer(uintptr(slotKey) + 8)
- return slotElem
- }
- slotKey = unsafe.Pointer(uintptr(slotKey) + slotSize)
- full = full.shiftOutLowest()
- }
- return unsafe.Pointer(&zeroVal[0])
- }
-
- k := key
- hash := typ.Hasher(abi.NoEscape(unsafe.Pointer(&k)), m.seed)
-
- // Select table.
- idx := m.directoryIndex(hash)
- t := m.directoryAt(idx)
-
- // Probe table.
- seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
- h2Hash := h2(hash)
- for ; ; seq = seq.next() {
- g := t.groups.group(typ, seq.offset)
-
- match := g.ctrls().matchH2(h2Hash)
-
- for match != 0 {
- i := match.first()
-
- slotKey := g.key(typ, i)
- if key == *(*uint64)(slotKey) {
- slotElem := unsafe.Pointer(uintptr(slotKey) + 8)
- return slotElem
- }
- match = match.removeFirst()
- }
-
- match = g.ctrls().matchEmpty()
- if match != 0 {
- // Finding an empty slot means we've reached the end of
- // the probe sequence.
- return unsafe.Pointer(&zeroVal[0])
- }
- }
+ p, _ := runtime_mapaccess2_fast64(typ, m, key)
+ return p
}
//go:linkname runtime_mapaccess2_fast64 runtime.mapaccess2_fast64
//go:linkname runtime_mapaccess1_faststr runtime.mapaccess1_faststr
func runtime_mapaccess1_faststr(typ *abi.MapType, m *Map, key string) unsafe.Pointer {
- if race.Enabled && m != nil {
- callerpc := sys.GetCallerPC()
- pc := abi.FuncPCABIInternal(runtime_mapaccess1_faststr)
- race.ReadPC(unsafe.Pointer(m), callerpc, pc)
- }
-
- if m == nil || m.Used() == 0 {
- return unsafe.Pointer(&zeroVal[0])
- }
-
- if m.writing != 0 {
- fatal("concurrent map read and map write")
- return nil
- }
-
- if m.dirLen <= 0 {
- elem := m.getWithoutKeySmallFastStr(typ, key)
- if elem == nil {
- return unsafe.Pointer(&zeroVal[0])
- }
- return elem
- }
-
- k := key
- hash := typ.Hasher(abi.NoEscape(unsafe.Pointer(&k)), m.seed)
-
- // Select table.
- idx := m.directoryIndex(hash)
- t := m.directoryAt(idx)
-
- // Probe table.
- seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
- h2Hash := h2(hash)
- for ; ; seq = seq.next() {
- g := t.groups.group(typ, seq.offset)
-
- match := g.ctrls().matchH2(h2Hash)
-
- for match != 0 {
- i := match.first()
-
- slotKey := g.key(typ, i)
- if key == *(*string)(slotKey) {
- slotElem := unsafe.Pointer(uintptr(slotKey) + 2*goarch.PtrSize)
- return slotElem
- }
- match = match.removeFirst()
- }
-
- match = g.ctrls().matchEmpty()
- if match != 0 {
- // Finding an empty slot means we've reached the end of
- // the probe sequence.
- return unsafe.Pointer(&zeroVal[0])
- }
- }
+ p, _ := runtime_mapaccess2_faststr(typ, m, key)
+ return p
}
//go:linkname runtime_mapaccess2_faststr runtime.mapaccess2_faststr
func mapaccess2(t *abi.MapType, m *maps.Map, key unsafe.Pointer) (unsafe.Pointer, bool)
func mapaccess1_fat(t *abi.MapType, m *maps.Map, key, zero unsafe.Pointer) unsafe.Pointer {
- e := mapaccess1(t, m, key)
- if e == unsafe.Pointer(&zeroVal[0]) {
+ e, ok := mapaccess2(t, m, key)
+ if !ok {
return zero
}
return e
}
func mapaccess2_fat(t *abi.MapType, m *maps.Map, key, zero unsafe.Pointer) (unsafe.Pointer, bool) {
- e := mapaccess1(t, m, key)
- if e == unsafe.Pointer(&zeroVal[0]) {
+ e, ok := mapaccess2(t, m, key)
+ if !ok {
return zero, false
}
return e, true