| // Copyright 2024 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 abi |
| |
| import "unsafe" |
| |
| // NoEscape hides the pointer p from escape analysis, preventing it |
| // from escaping to the heap. It compiles down to nothing. |
| // |
| // WARNING: This is very subtle to use correctly. The caller must |
| // ensure that it's truly safe for p to not escape to the heap by |
| // maintaining runtime pointer invariants (for example, that globals |
| // and the heap may not generally point into a stack). |
| // |
| //go:nosplit |
| //go:nocheckptr |
| func NoEscape(p unsafe.Pointer) unsafe.Pointer { |
| x := uintptr(p) |
| return unsafe.Pointer(x ^ 0) |
| } |
| |
| var alwaysFalse bool |
| var escapeSink any |
| |
| // Escape forces any pointers in x to escape to the heap. |
| func Escape[T any](x T) T { |
| if alwaysFalse { |
| escapeSink = x |
| } |
| return x |
| } |
| |
| // EscapeNonString forces v to be on the heap, if v contains a |
| // non-string pointer. |
| // |
| // This is used in hash/maphash.Comparable. We cannot hash pointers |
| // to local variables on stack, as their addresses might change on |
| // stack growth. Strings are okay as the hash depends on only the |
| // content, not the pointer. |
| // |
| // This is essentially |
| // |
| // if hasNonStringPointers(T) { Escape(v) } |
| // |
| // Implemented as a compiler intrinsic. |
| func EscapeNonString[T any](v T) { panic("intrinsic") } |
| |
| // EscapeToResultNonString models a data flow edge from v to the result, |
| // if v contains a non-string pointer. If v contains only string pointers, |
| // it returns a copy of v, but is not modeled as a data flow edge |
| // from the escape analysis's perspective. |
| // |
| // This is used in unique.clone, to model the data flow edge on the |
| // value with strings excluded, because strings are cloned (by |
| // content). |
| // |
| // TODO: probably we should define this as a intrinsic and EscapeNonString |
| // could just be "heap = EscapeToResultNonString(v)". This way we can model |
| // an edge to the result but not necessarily heap. |
| func EscapeToResultNonString[T any](v T) T { |
| EscapeNonString(v) |
| return *(*T)(NoEscape(unsafe.Pointer(&v))) |
| } |