Skip to content

Commit 4742c52

Browse files
mknyszekgopherbot
authored andcommitted
internal/abi: define EmptyInterface, TypeOf, and NoEscape
This change defines two commonly-defined functions and a commonly-defined type in internal/abi to try and deduplicate some definitions. This is motivated by a follow-up CL which will want access to TypeOf in yet another package. There still exist duplicate definitions of all three of these things in the runtime, and this CL doesn't try to handle that yet. There are far too many uses in the runtime to handle manually in a way that feels comfortable; automated refactoring will help. For #62483. Change-Id: I02fc64a28f11af618f6071f94d27f45c135fa8ac Reviewed-on: https://go-review.googlesource.com/c/go/+/573955 Auto-Submit: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 2073b35 commit 4742c52

File tree

8 files changed

+75
-65
lines changed

8 files changed

+75
-65
lines changed

src/internal/abi/escape.go

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package abi
6+
7+
import "unsafe"
8+
9+
// NoEscape hides the pointer p from escape analysis, preventing it
10+
// from escaping to the heap. It compiles down to nothing.
11+
//
12+
// WARNING: This is very subtle to use correctly. The caller must
13+
// ensure that it's truly safe for p to not escape to the heap by
14+
// maintaining runtime pointer invariants (for example, that globals
15+
// and the heap may not generally point into a stack).
16+
//
17+
//go:nosplit
18+
//go:nocheckptr
19+
func NoEscape(p unsafe.Pointer) unsafe.Pointer {
20+
x := uintptr(p)
21+
return unsafe.Pointer(x ^ 0)
22+
}

src/internal/abi/iface.go

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package abi
66

7+
import "unsafe"
8+
79
// The first word of every non-empty interface type contains an *ITab.
810
// It records the underlying concrete type (Type), the interface type it
911
// is implementing (Inter), and some ancillary information.
@@ -15,3 +17,11 @@ type ITab struct {
1517
Hash uint32 // copy of Type.Hash. Used for type switches.
1618
Fun [1]uintptr // variable sized. fun[0]==0 means Type does not implement Inter.
1719
}
20+
21+
// EmptyInterface describes the layout of a "interface{}" or a "any."
22+
// These are represented differently than non-empty interface, as the first
23+
// word always points to an abi.Type.
24+
type EmptyInterface struct {
25+
Type *Type
26+
Data unsafe.Pointer
27+
}

src/internal/abi/type.go

+11
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,17 @@ var kindNames = []string{
166166
UnsafePointer: "unsafe.Pointer",
167167
}
168168

169+
// TypeOf returns the abi.Type of some value.
170+
func TypeOf(a any) *Type {
171+
eface := *(*EmptyInterface)(unsafe.Pointer(&a))
172+
// Types are either static (for compiler-created types) or
173+
// heap-allocated but always reachable (for reflection-created
174+
// types, held in the central map). So there is no need to
175+
// escape types. noescape here help avoid unnecessary escape
176+
// of v.
177+
return (*Type)(NoEscape(unsafe.Pointer(eface.Type)))
178+
}
179+
169180
func (t *Type) Kind() Kind { return t.Kind_ & KindMask }
170181

171182
func (t *Type) HasName() bool {

src/internal/reflectlite/type.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,7 @@ func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
398398
// TypeOf returns the reflection Type that represents the dynamic type of i.
399399
// If i is a nil interface value, TypeOf returns nil.
400400
func TypeOf(i any) Type {
401-
eface := *(*emptyInterface)(unsafe.Pointer(&i))
402-
// Noescape so this doesn't make i to escape. See the comment
403-
// at Value.typ for why this is safe.
404-
return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
401+
return toType(abi.TypeOf(i))
405402
}
406403

407404
func (t rtype) Implements(u Type) bool {

src/internal/reflectlite/value.go

+9-21
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (v Value) typ() *abi.Type {
9494
// types, held in the central map). So there is no need to
9595
// escape types. noescape here help avoid unnecessary escape
9696
// of v.
97-
return (*abi.Type)(noescape(unsafe.Pointer(v.typ_)))
97+
return (*abi.Type)(abi.NoEscape(unsafe.Pointer(v.typ_)))
9898
}
9999

100100
// pointer returns the underlying pointer represented by v.
@@ -113,7 +113,7 @@ func (v Value) pointer() unsafe.Pointer {
113113
func packEface(v Value) any {
114114
t := v.typ()
115115
var i any
116-
e := (*emptyInterface)(unsafe.Pointer(&i))
116+
e := (*abi.EmptyInterface)(unsafe.Pointer(&i))
117117
// First, fill in the data portion of the interface.
118118
switch {
119119
case ifaceIndir(t):
@@ -127,36 +127,36 @@ func packEface(v Value) any {
127127
typedmemmove(t, c, ptr)
128128
ptr = c
129129
}
130-
e.word = ptr
130+
e.Data = ptr
131131
case v.flag&flagIndir != 0:
132132
// Value is indirect, but interface is direct. We need
133133
// to load the data at v.ptr into the interface data word.
134-
e.word = *(*unsafe.Pointer)(v.ptr)
134+
e.Data = *(*unsafe.Pointer)(v.ptr)
135135
default:
136136
// Value is direct, and so is the interface.
137-
e.word = v.ptr
137+
e.Data = v.ptr
138138
}
139139
// Now, fill in the type portion. We're very careful here not
140140
// to have any operation between the e.word and e.typ assignments
141141
// that would let the garbage collector observe the partially-built
142142
// interface value.
143-
e.typ = t
143+
e.Type = t
144144
return i
145145
}
146146

147147
// unpackEface converts the empty interface i to a Value.
148148
func unpackEface(i any) Value {
149-
e := (*emptyInterface)(unsafe.Pointer(&i))
149+
e := (*abi.EmptyInterface)(unsafe.Pointer(&i))
150150
// NOTE: don't read e.word until we know whether it is really a pointer or not.
151-
t := e.typ
151+
t := e.Type
152152
if t == nil {
153153
return Value{}
154154
}
155155
f := flag(t.Kind())
156156
if ifaceIndir(t) {
157157
f |= flagIndir
158158
}
159-
return Value{t, e.word, f}
159+
return Value{t, e.Data, f}
160160
}
161161

162162
// A ValueError occurs when a Value method is invoked on
@@ -185,12 +185,6 @@ func methodName() string {
185185
return f.Name()
186186
}
187187

188-
// emptyInterface is the header for an interface{} value.
189-
type emptyInterface struct {
190-
typ *abi.Type
191-
word unsafe.Pointer
192-
}
193-
194188
// mustBeExported panics if f records that the value was obtained using
195189
// an unexported field.
196190
func (f flag) mustBeExported() {
@@ -482,9 +476,3 @@ var dummy struct {
482476
b bool
483477
x any
484478
}
485-
486-
//go:nosplit
487-
func noescape(p unsafe.Pointer) unsafe.Pointer {
488-
x := uintptr(p)
489-
return unsafe.Pointer(x ^ 0)
490-
}

src/reflect/type.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -1212,16 +1212,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) {
12121212
// TypeOf returns the reflection [Type] that represents the dynamic type of i.
12131213
// If i is a nil interface value, TypeOf returns nil.
12141214
func TypeOf(i any) Type {
1215-
eface := *(*emptyInterface)(unsafe.Pointer(&i))
1216-
// Noescape so this doesn't make i to escape. See the comment
1217-
// at Value.typ for why this is safe.
1218-
return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
1215+
return toType(abi.TypeOf(i))
12191216
}
12201217

12211218
// rtypeOf directly extracts the *rtype of the provided value.
12221219
func rtypeOf(i any) *abi.Type {
1223-
eface := *(*emptyInterface)(unsafe.Pointer(&i))
1224-
return eface.typ
1220+
return abi.TypeOf(i)
12251221
}
12261222

12271223
// ptrMap is the cache for PointerTo.

src/reflect/value.go

+18-20
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func (v Value) typ() *abi.Type {
9999
// types, held in the central map). So there is no need to
100100
// escape types. noescape here help avoid unnecessary escape
101101
// of v.
102-
return (*abi.Type)(noescape(unsafe.Pointer(v.typ_)))
102+
return (*abi.Type)(abi.NoEscape(unsafe.Pointer(v.typ_)))
103103
}
104104

105105
// pointer returns the underlying pointer represented by v.
@@ -119,7 +119,7 @@ func (v Value) pointer() unsafe.Pointer {
119119
func packEface(v Value) any {
120120
t := v.typ()
121121
var i any
122-
e := (*emptyInterface)(unsafe.Pointer(&i))
122+
e := (*abi.EmptyInterface)(unsafe.Pointer(&i))
123123
// First, fill in the data portion of the interface.
124124
switch {
125125
case t.IfaceIndir():
@@ -133,36 +133,36 @@ func packEface(v Value) any {
133133
typedmemmove(t, c, ptr)
134134
ptr = c
135135
}
136-
e.word = ptr
136+
e.Data = ptr
137137
case v.flag&flagIndir != 0:
138138
// Value is indirect, but interface is direct. We need
139139
// to load the data at v.ptr into the interface data word.
140-
e.word = *(*unsafe.Pointer)(v.ptr)
140+
e.Data = *(*unsafe.Pointer)(v.ptr)
141141
default:
142142
// Value is direct, and so is the interface.
143-
e.word = v.ptr
143+
e.Data = v.ptr
144144
}
145145
// Now, fill in the type portion. We're very careful here not
146146
// to have any operation between the e.word and e.typ assignments
147147
// that would let the garbage collector observe the partially-built
148148
// interface value.
149-
e.typ = t
149+
e.Type = t
150150
return i
151151
}
152152

153153
// unpackEface converts the empty interface i to a Value.
154154
func unpackEface(i any) Value {
155-
e := (*emptyInterface)(unsafe.Pointer(&i))
155+
e := (*abi.EmptyInterface)(unsafe.Pointer(&i))
156156
// NOTE: don't read e.word until we know whether it is really a pointer or not.
157-
t := e.typ
157+
t := e.Type
158158
if t == nil {
159159
return Value{}
160160
}
161161
f := flag(t.Kind())
162162
if t.IfaceIndir() {
163163
f |= flagIndir
164164
}
165-
return Value{t, e.word, f}
165+
return Value{t, e.Data, f}
166166
}
167167

168168
// A ValueError occurs when a Value method is invoked on
@@ -200,12 +200,6 @@ func valueMethodName() string {
200200
return "unknown method"
201201
}
202202

203-
// emptyInterface is the header for an interface{} value.
204-
type emptyInterface struct {
205-
typ *abi.Type
206-
word unsafe.Pointer
207-
}
208-
209203
// nonEmptyInterface is the header for an interface value with methods.
210204
type nonEmptyInterface struct {
211205
itab *abi.ITab
@@ -1597,7 +1591,7 @@ func (v Value) IsZero() bool {
15971591
// v.ptr doesn't escape, as Equal functions are compiler generated
15981592
// and never escape. The escape analysis doesn't know, as it is a
15991593
// function pointer call.
1600-
return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
1594+
return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&zeroVal[0]))
16011595
}
16021596
if typ.TFlag&abi.TFlagRegularMemory != 0 {
16031597
// For some types where the zero value is a value where all bits of this type are 0
@@ -1623,7 +1617,7 @@ func (v Value) IsZero() bool {
16231617
// If the type is comparable, then compare directly with zero.
16241618
if typ.Equal != nil && typ.Size() <= abi.ZeroValSize {
16251619
// See noescape justification above.
1626-
return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
1620+
return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&zeroVal[0]))
16271621
}
16281622
if typ.TFlag&abi.TFlagRegularMemory != 0 {
16291623
// For some types where the zero value is a value where all bits of this type are 0
@@ -1736,7 +1730,7 @@ func (v Value) SetZero() {
17361730
case Slice:
17371731
*(*unsafeheader.Slice)(v.ptr) = unsafeheader.Slice{}
17381732
case Interface:
1739-
*(*emptyInterface)(v.ptr) = emptyInterface{}
1733+
*(*abi.EmptyInterface)(v.ptr) = abi.EmptyInterface{}
17401734
case Chan, Func, Map, Pointer, UnsafePointer:
17411735
*(*unsafe.Pointer)(v.ptr) = nil
17421736
case Array, Struct:
@@ -4015,8 +4009,12 @@ func contentEscapes(x unsafe.Pointer) {
40154009
}
40164010
}
40174011

4012+
// This is just a wrapper around abi.NoEscape. The inlining heuristics are
4013+
// finnicky and for whatever reason treat the local call to noescape as much
4014+
// lower cost with respect to the inliner budget. (That is, replacing calls to
4015+
// noescape with abi.NoEscape will cause inlining tests to fail.)
4016+
//
40184017
//go:nosplit
40194018
func noescape(p unsafe.Pointer) unsafe.Pointer {
4020-
x := uintptr(p)
4021-
return unsafe.Pointer(x ^ 0)
4019+
return abi.NoEscape(p)
40224020
}

src/strings/builder.go

+2-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package strings
66

77
import (
8+
"internal/abi"
89
"internal/bytealg"
910
"unicode/utf8"
1011
"unsafe"
@@ -22,27 +23,14 @@ type Builder struct {
2223
buf []byte
2324
}
2425

25-
// noescape hides a pointer from escape analysis. It is the identity function
26-
// but escape analysis doesn't think the output depends on the input.
27-
// noescape is inlined and currently compiles down to zero instructions.
28-
// USE CAREFULLY!
29-
// This was copied from the runtime; see issues 23382 and 7921.
30-
//
31-
//go:nosplit
32-
//go:nocheckptr
33-
func noescape(p unsafe.Pointer) unsafe.Pointer {
34-
x := uintptr(p)
35-
return unsafe.Pointer(x ^ 0)
36-
}
37-
3826
func (b *Builder) copyCheck() {
3927
if b.addr == nil {
4028
// This hack works around a failing of Go's escape analysis
4129
// that was causing b to escape and be heap allocated.
4230
// See issue 23382.
4331
// TODO: once issue 7921 is fixed, this should be reverted to
4432
// just "b.addr = b".
45-
b.addr = (*Builder)(noescape(unsafe.Pointer(b)))
33+
b.addr = (*Builder)(abi.NoEscape(unsafe.Pointer(b)))
4634
} else if b.addr != b {
4735
panic("strings: illegal use of non-zero Builder copied by value")
4836
}

0 commit comments

Comments
 (0)