aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/reflect/value.go79
-rw-r--r--src/runtime/chan.go2
-rw-r--r--src/runtime/map.go4
3 files changed, 69 insertions, 16 deletions
diff --git a/src/reflect/value.go b/src/reflect/value.go
index f079b8228b..60556e6349 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -1521,6 +1521,8 @@ func valueInterface(v Value, safe bool) any {
// compatible with InterfaceData.
func (v Value) InterfaceData() [2]uintptr {
v.mustBe(Interface)
+ // The compiler loses track as it converts to uintptr. Force escape.
+ escapes(v.ptr)
// We treat this as a read operation, so we allow
// it even for unexported data, because the caller
// has to import "unsafe" to turn it into something
@@ -2121,6 +2123,9 @@ func (v Value) OverflowUint(x uint64) bool {
//
// It's preferred to use uintptr(Value.UnsafePointer()) to get the equivalent result.
func (v Value) Pointer() uintptr {
+ // The compiler loses track as it converts to uintptr. Force escape.
+ escapes(v.ptr)
+
k := v.kind()
switch k {
case Pointer:
@@ -2682,6 +2687,8 @@ func (v Value) UnsafeAddr() uintptr {
if v.flag&flagAddr == 0 {
panic("reflect.Value.UnsafeAddr of unaddressable value")
}
+ // The compiler loses track as it converts to uintptr. Force escape.
+ escapes(v.ptr)
return uintptr(v.ptr)
}
@@ -2939,6 +2946,11 @@ type runtimeSelect struct {
// The conventional OK bool indicates whether the receive corresponds
// to a sent value.
//
+// rselect generally doesn't escape the runtimeSelect slice, except
+// that for the send case the value to send needs to escape. We don't
+// have a way to represent that in the function signature. So we handle
+// that with a forced escape in function Select.
+//
//go:noescape
func rselect([]runtimeSelect) (chosen int, recvOK bool)
@@ -3044,6 +3056,9 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
} else {
rc.val = unsafe.Pointer(&v.ptr)
}
+ // The value to send needs to escape. See the comment at rselect for
+ // why we need forced escape.
+ escapes(rc.val)
case SelectRecv:
if c.Send.IsValid() {
@@ -3150,6 +3165,14 @@ func Indirect(v Value) Value {
return v.Elem()
}
+// Before Go 1.21, ValueOf always escapes and a Value's content
+// is always heap allocated.
+// Set go121noForceValueEscape to true to avoid the forced escape,
+// allowing Value content to be on the stack.
+// Set go121noForceValueEscape to false for the legacy behavior
+// (for debugging).
+const go121noForceValueEscape = true
+
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i any) Value {
@@ -3157,11 +3180,9 @@ func ValueOf(i any) Value {
return Value{}
}
- // TODO: Maybe allow contents of a Value to live on the stack.
- // For now we make the contents always escape to the heap. It
- // makes life easier in a few places (see chanrecv/mapassign
- // comment below).
- escapes(i)
+ if !go121noForceValueEscape {
+ escapes(i)
+ }
return unpackEface(i)
}
@@ -3736,23 +3757,33 @@ func cvtI2I(v Value, typ Type) Value {
}
// implemented in ../runtime
+//
+//go:noescape
func chancap(ch unsafe.Pointer) int
+
+//go:noescape
func chanclose(ch unsafe.Pointer)
+
+//go:noescape
func chanlen(ch unsafe.Pointer) int
// Note: some of the noescape annotations below are technically a lie,
-// but safe in the context of this package. Functions like chansend
-// and mapassign don't escape the referent, but may escape anything
+// but safe in the context of this package. Functions like chansend0
+// and mapassign0 don't escape the referent, but may escape anything
// the referent points to (they do shallow copies of the referent).
-// It is safe in this package because the referent may only point
-// to something a Value may point to, and that is always in the heap
-// (due to the escapes() call in ValueOf).
+// We add a 0 to their names and wrap them in functions with the
+// proper escape behavior.
//go:noescape
func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, received bool)
//go:noescape
-func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
+func chansend0(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
+
+func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool {
+ contentEscapes(val)
+ return chansend0(ch, val, nb)
+}
func makechan(typ *abi.Type, size int) (ch unsafe.Pointer)
func makemap(t *abi.Type, cap int) (m unsafe.Pointer)
@@ -3764,10 +3795,22 @@ func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Po
func mapaccess_faststr(t *abi.Type, m unsafe.Pointer, key string) (val unsafe.Pointer)
//go:noescape
-func mapassign(t *abi.Type, m unsafe.Pointer, key, val unsafe.Pointer)
+func mapassign0(t *abi.Type, m unsafe.Pointer, key, val unsafe.Pointer)
+
+func mapassign(t *abi.Type, m unsafe.Pointer, key, val unsafe.Pointer) {
+ contentEscapes(key)
+ contentEscapes(val)
+ mapassign0(t, m, key, val)
+}
//go:noescape
-func mapassign_faststr(t *abi.Type, m unsafe.Pointer, key string, val unsafe.Pointer)
+func mapassign_faststr0(t *abi.Type, m unsafe.Pointer, key string, val unsafe.Pointer)
+
+func mapassign_faststr(t *abi.Type, m unsafe.Pointer, key string, val unsafe.Pointer) {
+ contentEscapes((*unsafeheader.String)(unsafe.Pointer(&key)).Data)
+ contentEscapes(val)
+ mapassign_faststr0(t, m, key, val)
+}
//go:noescape
func mapdelete(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer)
@@ -3876,3 +3919,13 @@ var dummy struct {
b bool
x any
}
+
+// Dummy annotation marking that the content of value x
+// escapes (i.e. modeling roughly heap=*x),
+// for use in cases where the reflect code is so clever that
+// the compiler cannot follow.
+func contentEscapes(x unsafe.Pointer) {
+ if dummy.b {
+ escapes(*(*any)(x)) // the dereference may not always be safe, but never executed
+ }
+}
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index 98e0836670..aff4cf87b7 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -714,7 +714,7 @@ func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {
return chanrecv(c, elem, false)
}
-//go:linkname reflect_chansend reflect.chansend
+//go:linkname reflect_chansend reflect.chansend0
func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
return chansend(c, elem, !nb, getcallerpc())
}
diff --git a/src/runtime/map.go b/src/runtime/map.go
index 7488945926..a1fe08f758 100644
--- a/src/runtime/map.go
+++ b/src/runtime/map.go
@@ -1365,13 +1365,13 @@ func reflect_mapaccess_faststr(t *maptype, h *hmap, key string) unsafe.Pointer {
return elem
}
-//go:linkname reflect_mapassign reflect.mapassign
+//go:linkname reflect_mapassign reflect.mapassign0
func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, elem unsafe.Pointer) {
p := mapassign(t, h, key)
typedmemmove(t.Elem, p, elem)
}
-//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr
+//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr0
func reflect_mapassign_faststr(t *maptype, h *hmap, key string, elem unsafe.Pointer) {
p := mapassign_faststr(t, h, key)
typedmemmove(t.Elem, p, elem)