runtime: require explicit GC layouts

This commit is contained in:
Jake Bailey
2026-08-07 10:21:19 -07:00
committed by Ron Evans
parent 1a4cb2032e
commit a7360d5ad3
28 changed files with 279 additions and 108 deletions
+9 -4
View File
@@ -17,10 +17,15 @@ const (
sizeShift = sizeBits + 1
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1)
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1)
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1)
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1)
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
PointerPair = Layout((3 << sizeShift) | ((2 * unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1)
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1)
// Conservative is reserved for stack storage, which does not have an
// ordinary Go object layout.
Conservative = Layout(2)
)
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
+27 -4
View File
@@ -166,6 +166,11 @@ type RawType struct {
meta uint8 // metadata byte, contains kind and flags (see constants above)
}
type basicType struct {
RawType
ptrTo *RawType
}
// All types that have an element type: named, chan, slice, array, map (but not
// pointer because it doesn't have ptrTo).
type elemType struct {
@@ -200,6 +205,7 @@ type arrayType struct {
elem *RawType
arrayLen uintptr
slicePtr *RawType
layout unsafe.Pointer
}
type mapType struct {
@@ -208,6 +214,7 @@ type mapType struct {
ptrTo *RawType
elem *RawType
key *RawType
typeInfo unsafe.Pointer
}
// namedType is the type descriptor for named types. The numMethod field uses
@@ -243,6 +250,7 @@ type structType struct {
pkgpath *byte
size uint32
numField uint16
layout unsafe.Pointer
fields [1]structField // the remaining fields are all of type structField
// methods methodSet follows after fields, only when numMethod & numMethodHasMethodSet != 0
}
@@ -298,6 +306,8 @@ func pointerTo(t *RawType) *RawType {
}
switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Complex64, Complex128, Float32, Float64, String, UnsafePointer:
return (*basicType)(unsafe.Pointer(t)).ptrTo
case Pointer:
if tag := t.ptrtag(); tag < 3 {
return (*RawType)(unsafe.Add(unsafe.Pointer(t), 1))
@@ -306,6 +316,8 @@ func pointerTo(t *RawType) *RawType {
// TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131
// We need to be able to create types that match existing types to prevent typecode equality.
panic("reflect: cannot make *****T type")
case Interface, Func:
return (*interfaceType)(unsafe.Pointer(t)).ptrTo
case Struct:
return (*structType)(unsafe.Pointer(t)).ptrTo
default:
@@ -729,6 +741,7 @@ func (t *RawType) Align() int {
}
func (r *RawType) gcLayout() unsafe.Pointer {
r = r.underlying()
kind := r.Kind()
if kind < String {
@@ -736,16 +749,26 @@ func (r *RawType) gcLayout() unsafe.Pointer {
}
switch kind {
case Pointer, UnsafePointer, Chan, Map:
return gclayout.Pointer.AsPtr()
case String:
return gclayout.String.AsPtr()
case UnsafePointer, Chan, Pointer, Map:
return gclayout.Pointer.AsPtr()
case Interface, Func:
return gclayout.PointerPair.AsPtr()
case Slice:
return gclayout.Slice.AsPtr()
case Array:
return (*arrayType)(unsafe.Pointer(r)).layout
case Struct:
return (*structType)(unsafe.Pointer(r)).layout
default:
panic("reflect: invalid GC layout kind")
}
}
// Unknown (for now); let the conservative pointer scanning handle it
return nil
func (r *RawType) hashmapTypeInfo() unsafe.Pointer {
r = r.underlying()
return (*mapType)(unsafe.Pointer(r)).typeInfo
}
// FieldAlign returns the alignment if this type is used in a struct field. It
+14 -12
View File
@@ -1,6 +1,7 @@
package reflectlite
import (
"internal/gclayout"
"math"
"unsafe"
)
@@ -1644,7 +1645,7 @@ func makeInt(flags valueFlags, bits uint64, t *RawType) Value {
ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil)
ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr
}
@@ -1671,7 +1672,7 @@ func makeFloat(flags valueFlags, f float64, t *RawType) Value {
ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil)
ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr
}
@@ -1703,7 +1704,7 @@ func makeComplex(flags valueFlags, f complex128, t *RawType) Value {
ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil)
ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr
}
@@ -1834,7 +1835,7 @@ func Zero(typ Type) Value {
return Value{
typecode: typ.(*RawType),
value: alloc(size, nil),
value: alloc(size, typ.(*RawType).gcLayout()),
flags: valueFlagExported | valueFlagRO,
}
}
@@ -1844,7 +1845,7 @@ func Zero(typ Type) Value {
func New(typ Type) Value {
return Value{
typecode: pointerTo(typ.(*RawType)),
value: alloc(typ.Size(), nil),
value: alloc(typ.Size(), typ.(*RawType).gcLayout()),
flags: valueFlagExported,
}
}
@@ -2203,13 +2204,13 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
}
//go:linkname hashmapMake runtime.hashmapMake
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, typeInfo unsafe.Pointer, alg uint8) unsafe.Pointer
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, typeInfo, keyType unsafe.Pointer) unsafe.Pointer
//go:linkname chanMake runtime.chanMake
func chanMake(elementSize uintptr, bufSize uintptr) unsafe.Pointer
func chanMake(elementSize uintptr, bufSize uintptr, elementLayout unsafe.Pointer) unsafe.Pointer
// MakeMapWithSize creates a new map with the specified type and initial space
// for approximately n elements.
@@ -2231,18 +2232,19 @@ func MakeMapWithSize(typ Type, n int) Value {
key := typ.Key().(*RawType)
val := typ.Elem().(*RawType)
typeInfo := typ.(*RawType).hashmapTypeInfo()
var m unsafe.Pointer
if key.Kind() == String {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString)
m = hashmapMake(key.Size(), val.Size(), uintptr(n), typeInfo, hashmapAlgorithmString)
} else if key.isBinary() {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary)
m = hashmapMake(key.Size(), val.Size(), uintptr(n), typeInfo, hashmapAlgorithmBinary)
} else {
// Composite key type (struct with strings, floats, etc.).
// Use runtime-generated hash/equal closures that walk the
// type structure, matching the compiler-generated functions.
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), unsafe.Pointer(key))
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), typeInfo, unsafe.Pointer(key))
}
return Value{
@@ -2269,7 +2271,7 @@ func MakeChan(typ Type, size int) Value {
panic("reflect.MakeChan: unidirectional channel type")
}
elem := typ.Elem().(*RawType)
ch := chanMake(elem.Size(), uintptr(size))
ch := chanMake(elem.Size(), uintptr(size), elem.gcLayout())
return Value{
typecode: typ.(*RawType),
value: ch,
+2 -1
View File
@@ -3,6 +3,7 @@
package task
import (
"internal/gclayout"
"unsafe"
)
@@ -73,7 +74,7 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
s.args = args
// Create a stack.
stack := runtime_alloc(stackSize, nil)
stack := runtime_alloc(stackSize, gclayout.Conservative.AsPtr())
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
+2 -1
View File
@@ -3,6 +3,7 @@
package task
import (
"internal/gclayout"
"unsafe"
)
@@ -36,7 +37,7 @@ func taskExit() {
// initialize the state and prepare to call the specified function with the specified argument bundle.
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Create a stack.
stack := runtime_alloc(stackSize, nil)
stack := runtime_alloc(stackSize, gclayout.Conservative.AsPtr())
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer