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 reflectimport ()// 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.IntArgRegsfloatArgRegs = abi.FloatArgRegsfloatRegSize = 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 uintptrsize uintptr // size in bytes of the part// These fields describe the ABI side of the translation.stkOff uintptr // stack offset, used if kind == abiStepStackireg int // integer register index, used if kind == abiStepIntReg or kind == abiStepPointerfreg int // FP register index, used if kind == abiStepFloatReg}// abiStepKind is the "op-code" for an abiStep instruction.type abiStepKind intconst (abiStepBad abiStepKind = iotaabiStepStack // copy to/from stackabiStepIntReg // copy to/from integer registerabiStepPointer // copy pointer to/from integer registerabiStepFloatReg // 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 []abiStepvalueStart []intstackBytes uintptr // stack space usediregs, 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 intif == 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 , boolif .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 truecase 1:return .(.Elem, )default:return false}case Struct::= (*structType)(unsafe.Pointer())for := range .Fields {:= &.Fields[]if !.(.Typ, +.Offset) {return false}}return truedefault: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; < ; ++ {:= abiStepIntRegif &(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; ++ {:= 0if .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 abiSeqif != 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.9-preview. (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. |