Source File
abi.go
Belonging Package
reflect
// Copyright 2021 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 reflect
import (
)
// These variables are used by the register assignment
// algorithm in this file.
//
// They should be modified with care (no other reflect code
// may be executing) and are generally only modified
// when testing this package.
//
// They should never be set higher than their internal/abi
// constant counterparts, because the system relies on a
// structure that is at least large enough to hold the
// registers the system supports.
//
// Currently they're set to zero because using the actual
// constants will break every part of the toolchain that
// uses reflect to call functions (e.g. go test, or anything
// that uses text/template). The values that are currently
// commented out there should be the actual values once
// we're ready to use the register ABI everywhere.
var (
intArgRegs = abi.IntArgRegs
floatArgRegs = abi.FloatArgRegs
floatRegSize = uintptr(abi.EffectiveFloatRegSize)
)
// abiStep represents an ABI "instruction." Each instruction
// describes one part of how to translate between a Go value
// in memory and a call frame.
type abiStep struct {
kind abiStepKind
// offset and size together describe a part of a Go value
// in memory.
offset uintptr
size uintptr // size in bytes of the part
// These fields describe the ABI side of the translation.
stkOff uintptr // stack offset, used if kind == abiStepStack
ireg int // integer register index, used if kind == abiStepIntReg or kind == abiStepPointer
freg int // FP register index, used if kind == abiStepFloatReg
}
// abiStepKind is the "op-code" for an abiStep instruction.
type abiStepKind int
const (
abiStepBad abiStepKind = iota
abiStepStack // copy to/from stack
abiStepIntReg // copy to/from integer register
abiStepPointer // copy pointer to/from integer register
abiStepFloatReg // copy to/from FP register
)
// abiSeq represents a sequence of ABI instructions for copying
// from a series of reflect.Values to a call frame (for call arguments)
// or vice-versa (for call results).
//
// An abiSeq should be populated by calling its addArg method.
type abiSeq struct {
// steps is the set of instructions.
//
// The instructions are grouped together by whole arguments,
// with the starting index for the instructions
// of the i'th Go value available in valueStart.
//
// For instance, if this abiSeq represents 3 arguments
// passed to a function, then the 2nd argument's steps
// begin at steps[valueStart[1]].
//
// Because reflect accepts Go arguments in distinct
// Values and each Value is stored separately, each abiStep
// that begins a new argument will have its offset
// field == 0.
steps []abiStep
valueStart []int
stackBytes uintptr // stack space used
iregs, fregs int // registers used
}
func ( *abiSeq) () {
for , := range .steps {
println("part", , .kind, .offset, .size, .stkOff, .ireg, .freg)
}
print("values ")
for , := range .valueStart {
print(, " ")
}
println()
println("stack", .stackBytes)
println("iregs", .iregs)
println("fregs", .fregs)
}
// stepsForValue returns the ABI instructions for translating
// the i'th Go argument or return value represented by this
// abiSeq to the Go ABI.
func ( *abiSeq) ( int) []abiStep {
:= .valueStart[]
var int
if == len(.valueStart)-1 {
= len(.steps)
} else {
= .valueStart[+1]
}
return .steps[:]
}
// addArg extends the abiSeq with a new Go value of type t.
//
// If the value was stack-assigned, returns the single
// abiStep describing that translation, and nil otherwise.
func ( *abiSeq) ( *abi.Type) *abiStep {
// We'll always be adding a new value, so do that first.
:= len(.steps)
.valueStart = append(.valueStart, )
if .Size() == 0 {
// If the size of the argument type is zero, then
// in order to degrade gracefully into ABI0, we need
// to stack-assign this type. The reason is that
// although zero-sized types take up no space on the
// stack, they do cause the next argument to be aligned.
// So just do that here, but don't bother actually
// generating a new ABI step for it (there's nothing to
// actually copy).
//
// We cannot handle this in the recursive case of
// regAssign because zero-sized *fields* of a
// non-zero-sized struct do not cause it to be
// stack-assigned. So we need a special case here
// at the top.
.stackBytes = align(.stackBytes, uintptr(.Align()))
return nil
}
// Hold a copy of "a" so that we can roll back if
// register assignment fails.
:= *
if !.regAssign(, 0) {
// Register assignment failed. Roll back any changes
// and stack-assign.
* =
.stackAssign(.Size(), uintptr(.Align()))
return &.steps[len(.steps)-1]
}
return nil
}
// addRcvr extends the abiSeq with a new method call
// receiver according to the interface calling convention.
//
// If the receiver was stack-assigned, returns the single
// abiStep describing that translation, and nil otherwise.
// Returns true if the receiver is a pointer.
func ( *abiSeq) ( *abi.Type) (*abiStep, bool) {
// The receiver is always one word.
.valueStart = append(.valueStart, len(.steps))
var , bool
if .IfaceIndir() || .Pointers() {
= .assignIntN(0, goarch.PtrSize, 1, 0b1)
= true
} else {
// TODO(mknyszek): Is this case even possible?
// The interface data work never contains a non-pointer
// value. This case was copied over from older code
// in the reflect package which only conditionally added
// a pointer bit to the reflect.(Value).Call stack frame's
// GC bitmap.
= .assignIntN(0, goarch.PtrSize, 1, 0b0)
= false
}
if ! {
.stackAssign(goarch.PtrSize, goarch.PtrSize)
return &.steps[len(.steps)-1],
}
return nil,
}
// regAssign attempts to reserve argument registers for a value of
// type t, stored at some offset.
//
// It returns whether or not the assignment succeeded, but
// leaves any changes it made to a.steps behind, so the caller
// must undo that work by adjusting a.steps if it fails.
//
// This method along with the assign* methods represent the
// complete register-assignment algorithm for the Go ABI.
func ( *abiSeq) ( *abi.Type, uintptr) bool {
switch Kind(.Kind()) {
case UnsafePointer, Pointer, Chan, Map, Func:
return .assignIntN(, .Size(), 1, 0b1)
case Bool, Int, Uint, Int8, Uint8, Int16, Uint16, Int32, Uint32, Uintptr:
return .assignIntN(, .Size(), 1, 0b0)
case Int64, Uint64:
switch goarch.PtrSize {
case 4:
return .assignIntN(, 4, 2, 0b0)
case 8:
return .assignIntN(, 8, 1, 0b0)
}
case Float32, Float64:
return .assignFloatN(, .Size(), 1)
case Complex64:
return .assignFloatN(, 4, 2)
case Complex128:
return .assignFloatN(, 8, 2)
case String:
return .assignIntN(, goarch.PtrSize, 2, 0b01)
case Interface:
return .assignIntN(, goarch.PtrSize, 2, 0b10)
case Slice:
return .assignIntN(, goarch.PtrSize, 3, 0b001)
case Array:
:= (*arrayType)(unsafe.Pointer())
switch .Len {
case 0:
// There's nothing to assign, so don't modify
// a.steps but succeed so the caller doesn't
// try to stack-assign this value.
return true
case 1:
return .(.Elem, )
default:
return false
}
case Struct:
:= (*structType)(unsafe.Pointer())
for := range .Fields {
:= &.Fields[]
if !.(.Typ, +.Offset) {
return false
}
}
return true
default:
print("t.Kind == ", .Kind(), "\n")
panic("unknown type kind")
}
panic("unhandled register assignment path")
}
// assignIntN assigns n values to registers, each "size" bytes large,
// from the data at [offset, offset+n*size) in memory. Each value at
// [offset+i*size, offset+(i+1)*size) for i < n is assigned to the
// next n integer registers.
//
// Bit i in ptrMap indicates whether the i'th value is a pointer.
// n must be <= 8.
//
// Returns whether assignment succeeded.
func ( *abiSeq) (, uintptr, int, uint8) bool {
if > 8 || < 0 {
panic("invalid n")
}
if != 0 && != goarch.PtrSize {
panic("non-empty pointer map passed for non-pointer-size values")
}
if .iregs+ > intArgRegs {
return false
}
for := 0; < ; ++ {
:= abiStepIntReg
if &(uint8(1)<<) != 0 {
= abiStepPointer
}
.steps = append(.steps, abiStep{
kind: ,
offset: + uintptr()*,
size: ,
ireg: .iregs,
})
.iregs++
}
return true
}
// assignFloatN assigns n values to registers, each "size" bytes large,
// from the data at [offset, offset+n*size) in memory. Each value at
// [offset+i*size, offset+(i+1)*size) for i < n is assigned to the
// next n floating-point registers.
//
// Returns whether assignment succeeded.
func ( *abiSeq) (, uintptr, int) bool {
if < 0 {
panic("invalid n")
}
if .fregs+ > floatArgRegs || floatRegSize < {
return false
}
for := 0; < ; ++ {
.steps = append(.steps, abiStep{
kind: abiStepFloatReg,
offset: + uintptr()*,
size: ,
freg: .fregs,
})
.fregs++
}
return true
}
// stackAssign reserves space for one value that is "size" bytes
// large with alignment "alignment" to the stack.
//
// Should not be called directly; use addArg instead.
func ( *abiSeq) (, uintptr) {
.stackBytes = align(.stackBytes, )
.steps = append(.steps, abiStep{
kind: abiStepStack,
offset: 0, // Only used for whole arguments, so the memory offset is 0.
size: ,
stkOff: .stackBytes,
})
.stackBytes +=
}
// abiDesc describes the ABI for a function or method.
type abiDesc struct {
// call and ret represent the translation steps for
// the call and return paths of a Go function.
call, ret abiSeq
// These fields describe the stack space allocated
// for the call. stackCallArgsSize is the amount of space
// reserved for arguments but not return values. retOffset
// is the offset at which return values begin, and
// spill is the size in bytes of additional space reserved
// to spill argument registers into in case of preemption in
// reflectcall's stack frame.
stackCallArgsSize, retOffset, spill uintptr
// stackPtrs is a bitmap that indicates whether
// each word in the ABI stack space (stack-assigned
// args + return values) is a pointer. Used
// as the heap pointer bitmap for stack space
// passed to reflectcall.
stackPtrs *bitVector
// inRegPtrs is a bitmap whose i'th bit indicates
// whether the i'th integer argument register contains
// a pointer. Used by makeFuncStub and methodValueCall
// to make result pointers visible to the GC.
//
// outRegPtrs is the same, but for result values.
// Used by reflectcall to make result pointers visible
// to the GC.
inRegPtrs, outRegPtrs abi.IntArgRegBitmap
}
func ( *abiDesc) () {
println("ABI")
println("call")
.call.dump()
println("ret")
.ret.dump()
println("stackCallArgsSize", .stackCallArgsSize)
println("retOffset", .retOffset)
println("spill", .spill)
print("inRegPtrs:")
dumpPtrBitMap(.inRegPtrs)
println()
print("outRegPtrs:")
dumpPtrBitMap(.outRegPtrs)
println()
}
func dumpPtrBitMap( abi.IntArgRegBitmap) {
for := 0; < intArgRegs; ++ {
:= 0
if .Get() {
= 1
}
print(" ", )
}
}
func newAbiDesc( *funcType, *abi.Type) abiDesc {
// We need to add space for this argument to
// the frame so that it can spill args into it.
//
// The size of this space is just the sum of the sizes
// of each register-allocated type.
//
// TODO(mknyszek): Remove this when we no longer have
// caller reserved spill space.
:= uintptr(0)
// Compute gc program & stack bitmap for stack arguments
:= new(bitVector)
// Compute the stack frame pointer bitmap and register
// pointer bitmap for arguments.
:= abi.IntArgRegBitmap{}
// Compute abiSeq for input parameters.
var abiSeq
if != nil {
, := .addRcvr()
if != nil {
if {
.append(1)
} else {
.append(0)
}
} else {
+= goarch.PtrSize
}
}
for , := range .InSlice() {
:= .addArg()
if != nil {
addTypeBits(, .stkOff, )
} else {
= align(, uintptr(.Align()))
+= .Size()
for , := range .stepsForValue() {
if .kind == abiStepPointer {
.Set(.ireg)
}
}
}
}
= align(, goarch.PtrSize)
// From the input parameters alone, we now know
// the stackCallArgsSize and retOffset.
:= .stackBytes
:= align(.stackBytes, goarch.PtrSize)
// Compute the stack frame pointer bitmap and register
// pointer bitmap for return values.
:= abi.IntArgRegBitmap{}
// Compute abiSeq for output parameters.
var abiSeq
// Stack-assigned return values do not share
// space with arguments like they do with registers,
// so we need to inject a stack offset here.
// Fake it by artificially extending stackBytes by
// the return offset.
.stackBytes =
for , := range .OutSlice() {
:= .addArg()
if != nil {
addTypeBits(, .stkOff, )
} else {
for , := range .stepsForValue() {
if .kind == abiStepPointer {
.Set(.ireg)
}
}
}
}
// Undo the faking from earlier so that stackBytes
// is accurate.
.stackBytes -=
return abiDesc{, , , , , , , }
}
// intFromReg loads an argSize sized integer from reg and places it at to.
//
// argSize must be non-zero, fit in a register, and a power-of-two.
func intFromReg( *abi.RegArgs, int, uintptr, unsafe.Pointer) {
memmove(, .IntRegArgAddr(, ), )
}
// intToReg loads an argSize sized integer and stores it into reg.
//
// argSize must be non-zero, fit in a register, and a power-of-two.
func intToReg( *abi.RegArgs, int, uintptr, unsafe.Pointer) {
memmove(.IntRegArgAddr(, ), , )
}
// floatFromReg loads a float value from its register representation in r.
//
// argSize must be 4 or 8.
func floatFromReg( *abi.RegArgs, int, uintptr, unsafe.Pointer) {
switch {
case 4:
*(*float32)() = archFloat32FromReg(.Floats[])
case 8:
*(*float64)() = *(*float64)(unsafe.Pointer(&.Floats[]))
default:
panic("bad argSize")
}
}
// floatToReg stores a float value in its register representation in r.
//
// argSize must be either 4 or 8.
func floatToReg( *abi.RegArgs, int, uintptr, unsafe.Pointer) {
switch {
case 4:
.Floats[] = archFloat32ToReg(*(*float32)())
case 8:
.Floats[] = *(*uint64)()
default:
panic("bad argSize")
}
}
The pages are generated with Golds v0.7.3. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |