aboutsummaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-05-21 14:33:13 -0400
committerCherry Mui <cherryyz@google.com>2025-05-21 20:07:36 -0700
commit5e6a868b28d3e7a71fa328c18ff5e93d72a1fb67 (patch)
tree6d026379ac2e2544116d72cddab70ef935418e6b /src/internal
parent8bf816ae6879fa4537cc6e6e292769df2d7dbb78 (diff)
downloadgo-5e6a868b28d3e7a71fa328c18ff5e93d72a1fb67.tar.xz
cmd/compile, unique: model data flow of non-string pointers
Currently, hash/maphash.Comparable escapes its parameter if it contains non-string pointers, but does not escape strings or types that contain strings but no other pointers. This is achieved by a compiler intrinsic. unique.Make does something similar: it stores its parameter to a central map, with strings cloned. So from the escape analysis's perspective, the non-string pointers are passed through, whereas string pointers are not. We currently cannot model this type of type-dependent data flow directly in Go. So we do this with a compiler intrinsic. In fact, we can unify this and the intrinsic above. Tests are from Jake Bailey's CL 671955 (thanks!). Fixes #73680. Change-Id: Ia6a78e09dee39f8d9198a16758e4b5322ee2c56a Reviewed-on: https://go-review.googlesource.com/c/go/+/675156 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Jake Bailey <jacob.b.bailey@gmail.com>
Diffstat (limited to 'src/internal')
-rw-r--r--src/internal/abi/escape.go32
1 files changed, 32 insertions, 0 deletions
diff --git a/src/internal/abi/escape.go b/src/internal/abi/escape.go
index 8cdae1438e..d37be0177e 100644
--- a/src/internal/abi/escape.go
+++ b/src/internal/abi/escape.go
@@ -31,3 +31,35 @@ func Escape[T any](x T) T {
}
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)))
+}