Source file src/cmd/compile/internal/abi/abiutils.go

     1  // Copyright 2020 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 (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/types"
    11  	"cmd/internal/src"
    12  	"fmt"
    13  	"sync"
    14  )
    15  
    16  //......................................................................
    17  //
    18  // Public/exported bits of the ABI utilities.
    19  //
    20  
    21  // ABIParamResultInfo stores the results of processing a given
    22  // function type to compute stack layout and register assignments. For
    23  // each input and output parameter we capture whether the param was
    24  // register-assigned (and to which register(s)) or the stack offset
    25  // for the param if is not going to be passed in registers according
    26  // to the rules in the Go internal ABI specification (1.17).
    27  type ABIParamResultInfo struct {
    28  	inparams          []ABIParamAssignment // Includes receiver for method calls.  Does NOT include hidden closure pointer.
    29  	outparams         []ABIParamAssignment
    30  	offsetToSpillArea int64
    31  	spillAreaSize     int64
    32  	inRegistersUsed   int
    33  	outRegistersUsed  int
    34  	config            *ABIConfig // to enable String() method
    35  }
    36  
    37  func (a *ABIParamResultInfo) Config() *ABIConfig {
    38  	return a.config
    39  }
    40  
    41  func (a *ABIParamResultInfo) InParams() []ABIParamAssignment {
    42  	return a.inparams
    43  }
    44  
    45  func (a *ABIParamResultInfo) OutParams() []ABIParamAssignment {
    46  	return a.outparams
    47  }
    48  
    49  func (a *ABIParamResultInfo) InRegistersUsed() int {
    50  	return a.inRegistersUsed
    51  }
    52  
    53  func (a *ABIParamResultInfo) OutRegistersUsed() int {
    54  	return a.outRegistersUsed
    55  }
    56  
    57  func (a *ABIParamResultInfo) InParam(i int) *ABIParamAssignment {
    58  	return &a.inparams[i]
    59  }
    60  
    61  func (a *ABIParamResultInfo) OutParam(i int) *ABIParamAssignment {
    62  	return &a.outparams[i]
    63  }
    64  
    65  func (a *ABIParamResultInfo) SpillAreaOffset() int64 {
    66  	return a.offsetToSpillArea
    67  }
    68  
    69  func (a *ABIParamResultInfo) SpillAreaSize() int64 {
    70  	return a.spillAreaSize
    71  }
    72  
    73  // ArgWidth returns the amount of stack needed for all the inputs
    74  // and outputs of a function or method, including ABI-defined parameter
    75  // slots and ABI-defined spill slots for register-resident parameters.
    76  // The name is inherited from (*Type).ArgWidth(), which it replaces.
    77  func (a *ABIParamResultInfo) ArgWidth() int64 {
    78  	return a.spillAreaSize + a.offsetToSpillArea - a.config.LocalsOffset()
    79  }
    80  
    81  // RegIndex stores the index into the set of machine registers used by
    82  // the ABI on a specific architecture for parameter passing.  RegIndex
    83  // values 0 through N-1 (where N is the number of integer registers
    84  // used for param passing according to the ABI rules) describe integer
    85  // registers; values N through M (where M is the number of floating
    86  // point registers used).  Thus if the ABI says there are 5 integer
    87  // registers and 7 floating point registers, then RegIndex value of 4
    88  // indicates the 5th integer register, and a RegIndex value of 11
    89  // indicates the 7th floating point register.
    90  type RegIndex uint8
    91  
    92  // ABIParamAssignment holds information about how a specific param or
    93  // result will be passed: in registers (in which case 'Registers' is
    94  // populated) or on the stack (in which case 'Offset' is set to a
    95  // non-negative stack offset. The values in 'Registers' are indices
    96  // (as described above), not architected registers.
    97  type ABIParamAssignment struct {
    98  	Type      *types.Type
    99  	Name      types.Object // should always be *ir.Name, used to match with a particular ssa.OpArg.
   100  	Registers []RegIndex
   101  	offset    int32
   102  }
   103  
   104  // Offset returns the stack offset for addressing the parameter that "a" describes.
   105  // This will panic if "a" describes a register-allocated parameter.
   106  func (a *ABIParamAssignment) Offset() int32 {
   107  	if len(a.Registers) > 0 {
   108  		base.Fatalf("register allocated parameters have no offset")
   109  	}
   110  	return a.offset
   111  }
   112  
   113  // RegisterTypes returns a slice of the types of the registers
   114  // corresponding to a slice of parameters.  The returned slice
   115  // has capacity for one more, likely a memory type.
   116  func RegisterTypes(apa []ABIParamAssignment) []*types.Type {
   117  	rcount := 0
   118  	for _, pa := range apa {
   119  		rcount += len(pa.Registers)
   120  	}
   121  	if rcount == 0 {
   122  		// Note that this catches top-level struct{} and [0]Foo, which are stack allocated.
   123  		return make([]*types.Type, 0, 1)
   124  	}
   125  	rts := make([]*types.Type, 0, rcount+1)
   126  	for _, pa := range apa {
   127  		if len(pa.Registers) == 0 {
   128  			continue
   129  		}
   130  		rts = appendParamTypes(rts, pa.Type)
   131  	}
   132  	return rts
   133  }
   134  
   135  func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64) {
   136  	l := len(pa.Registers)
   137  	if l == 0 {
   138  		return nil, nil
   139  	}
   140  	typs := make([]*types.Type, 0, l)
   141  	offs := make([]int64, 0, l)
   142  	offs, _ = appendParamOffsets(offs, 0, pa.Type)
   143  	return appendParamTypes(typs, pa.Type), offs
   144  }
   145  
   146  func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
   147  	w := t.Size()
   148  	if w == 0 {
   149  		return rts
   150  	}
   151  	if t.IsScalar() || t.IsPtrShaped() {
   152  		if t.IsComplex() {
   153  			c := types.FloatForComplex(t)
   154  			return append(rts, c, c)
   155  		} else {
   156  			if int(t.Size()) <= types.RegSize {
   157  				return append(rts, t)
   158  			}
   159  			// assume 64bit int on 32-bit machine
   160  			// TODO endianness? Should high-order (sign bits) word come first?
   161  			if t.IsSigned() {
   162  				rts = append(rts, types.Types[types.TINT32])
   163  			} else {
   164  				rts = append(rts, types.Types[types.TUINT32])
   165  			}
   166  			return append(rts, types.Types[types.TUINT32])
   167  		}
   168  	} else {
   169  		typ := t.Kind()
   170  		switch typ {
   171  		case types.TARRAY:
   172  			for i := int64(0); i < t.NumElem(); i++ { // 0 gets no registers, plus future-proofing.
   173  				rts = appendParamTypes(rts, t.Elem())
   174  			}
   175  		case types.TSTRUCT:
   176  			for _, f := range t.FieldSlice() {
   177  				if f.Type.Size() > 0 { // embedded zero-width types receive no registers
   178  					rts = appendParamTypes(rts, f.Type)
   179  				}
   180  			}
   181  		case types.TSLICE:
   182  			return appendParamTypes(rts, synthSlice)
   183  		case types.TSTRING:
   184  			return appendParamTypes(rts, synthString)
   185  		case types.TINTER:
   186  			return appendParamTypes(rts, synthIface)
   187  		}
   188  	}
   189  	return rts
   190  }
   191  
   192  // appendParamOffsets appends the offset(s) of type t, starting from "at",
   193  // to input offsets, and returns the longer slice and the next unused offset.
   194  func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) {
   195  	at = align(at, t)
   196  	w := t.Size()
   197  	if w == 0 {
   198  		return offsets, at
   199  	}
   200  	if t.IsScalar() || t.IsPtrShaped() {
   201  		if t.IsComplex() || int(t.Size()) > types.RegSize { // complex and *int64 on 32-bit
   202  			s := w / 2
   203  			return append(offsets, at, at+s), at + w
   204  		} else {
   205  			return append(offsets, at), at + w
   206  		}
   207  	} else {
   208  		typ := t.Kind()
   209  		switch typ {
   210  		case types.TARRAY:
   211  			for i := int64(0); i < t.NumElem(); i++ {
   212  				offsets, at = appendParamOffsets(offsets, at, t.Elem())
   213  			}
   214  		case types.TSTRUCT:
   215  			for i, f := range t.FieldSlice() {
   216  				offsets, at = appendParamOffsets(offsets, at, f.Type)
   217  				if f.Type.Size() == 0 && i == t.NumFields()-1 {
   218  					at++ // last field has zero width
   219  				}
   220  			}
   221  			at = align(at, t) // type size is rounded up to its alignment
   222  		case types.TSLICE:
   223  			return appendParamOffsets(offsets, at, synthSlice)
   224  		case types.TSTRING:
   225  			return appendParamOffsets(offsets, at, synthString)
   226  		case types.TINTER:
   227  			return appendParamOffsets(offsets, at, synthIface)
   228  		}
   229  	}
   230  	return offsets, at
   231  }
   232  
   233  // FrameOffset returns the frame-pointer-relative location that a function
   234  // would spill its input or output parameter to, if such a spill slot exists.
   235  // If there is none defined (e.g., register-allocated outputs) it panics.
   236  // For register-allocated inputs that is their spill offset reserved for morestack;
   237  // for stack-allocated inputs and outputs, that is their location on the stack.
   238  // (In a future version of the ABI, register-resident inputs may lose their defined
   239  // spill area to help reduce stack sizes.)
   240  func (a *ABIParamAssignment) FrameOffset(i *ABIParamResultInfo) int64 {
   241  	if a.offset == -1 {
   242  		base.Fatalf("function parameter has no ABI-defined frame-pointer offset")
   243  	}
   244  	if len(a.Registers) == 0 { // passed on stack
   245  		return int64(a.offset) - i.config.LocalsOffset()
   246  	}
   247  	// spill area for registers
   248  	return int64(a.offset) + i.SpillAreaOffset() - i.config.LocalsOffset()
   249  }
   250  
   251  // RegAmounts holds a specified number of integer/float registers.
   252  type RegAmounts struct {
   253  	intRegs   int
   254  	floatRegs int
   255  }
   256  
   257  // ABIConfig captures the number of registers made available
   258  // by the ABI rules for parameter passing and result returning.
   259  type ABIConfig struct {
   260  	// Do we need anything more than this?
   261  	offsetForLocals  int64 // e.g., obj.(*Link).FixedFrameSize() -- extra linkage information on some architectures.
   262  	regAmounts       RegAmounts
   263  	regsForTypeCache map[*types.Type]int
   264  }
   265  
   266  // NewABIConfig returns a new ABI configuration for an architecture with
   267  // iRegsCount integer/pointer registers and fRegsCount floating point registers.
   268  func NewABIConfig(iRegsCount, fRegsCount int, offsetForLocals int64) *ABIConfig {
   269  	return &ABIConfig{offsetForLocals: offsetForLocals, regAmounts: RegAmounts{iRegsCount, fRegsCount}, regsForTypeCache: make(map[*types.Type]int)}
   270  }
   271  
   272  // Copy returns a copy of an ABIConfig for use in a function's compilation so that access to the cache does not need to be protected with a mutex.
   273  func (a *ABIConfig) Copy() *ABIConfig {
   274  	b := *a
   275  	b.regsForTypeCache = make(map[*types.Type]int)
   276  	return &b
   277  }
   278  
   279  // LocalsOffset returns the architecture-dependent offset from SP for args and results.
   280  // In theory this is only used for debugging; it ought to already be incorporated into
   281  // results from the ABI-related methods
   282  func (a *ABIConfig) LocalsOffset() int64 {
   283  	return a.offsetForLocals
   284  }
   285  
   286  // FloatIndexFor translates r into an index in the floating point parameter
   287  // registers.  If the result is negative, the input index was actually for the
   288  // integer parameter registers.
   289  func (a *ABIConfig) FloatIndexFor(r RegIndex) int64 {
   290  	return int64(r) - int64(a.regAmounts.intRegs)
   291  }
   292  
   293  // NumParamRegs returns the number of parameter registers used for a given type,
   294  // without regard for the number available.
   295  func (a *ABIConfig) NumParamRegs(t *types.Type) int {
   296  	var n int
   297  	if n, ok := a.regsForTypeCache[t]; ok {
   298  		return n
   299  	}
   300  
   301  	if t.IsScalar() || t.IsPtrShaped() {
   302  		if t.IsComplex() {
   303  			n = 2
   304  		} else {
   305  			n = (int(t.Size()) + types.RegSize - 1) / types.RegSize
   306  		}
   307  	} else {
   308  		typ := t.Kind()
   309  		switch typ {
   310  		case types.TARRAY:
   311  			n = a.NumParamRegs(t.Elem()) * int(t.NumElem())
   312  		case types.TSTRUCT:
   313  			for _, f := range t.FieldSlice() {
   314  				n += a.NumParamRegs(f.Type)
   315  			}
   316  		case types.TSLICE:
   317  			n = a.NumParamRegs(synthSlice)
   318  		case types.TSTRING:
   319  			n = a.NumParamRegs(synthString)
   320  		case types.TINTER:
   321  			n = a.NumParamRegs(synthIface)
   322  		}
   323  	}
   324  	a.regsForTypeCache[t] = n
   325  
   326  	return n
   327  }
   328  
   329  // preAllocateParams gets the slice sizes right for inputs and outputs.
   330  func (a *ABIParamResultInfo) preAllocateParams(hasRcvr bool, nIns, nOuts int) {
   331  	if hasRcvr {
   332  		nIns++
   333  	}
   334  	a.inparams = make([]ABIParamAssignment, 0, nIns)
   335  	a.outparams = make([]ABIParamAssignment, 0, nOuts)
   336  }
   337  
   338  // ABIAnalyzeTypes takes an optional receiver type, arrays of ins and outs, and returns an ABIParamResultInfo,
   339  // based on the given configuration.  This is the same result computed by config.ABIAnalyze applied to the
   340  // corresponding method/function type, except that all the embedded parameter names are nil.
   341  // This is intended for use by ssagen/ssa.go:(*state).rtcall, for runtime functions that lack a parsed function type.
   342  func (config *ABIConfig) ABIAnalyzeTypes(rcvr *types.Type, ins, outs []*types.Type) *ABIParamResultInfo {
   343  	setup()
   344  	s := assignState{
   345  		stackOffset: config.offsetForLocals,
   346  		rTotal:      config.regAmounts,
   347  	}
   348  	result := &ABIParamResultInfo{config: config}
   349  	result.preAllocateParams(rcvr != nil, len(ins), len(outs))
   350  
   351  	// Receiver
   352  	if rcvr != nil {
   353  		result.inparams = append(result.inparams,
   354  			s.assignParamOrReturn(rcvr, nil, false))
   355  	}
   356  
   357  	// Inputs
   358  	for _, t := range ins {
   359  		result.inparams = append(result.inparams,
   360  			s.assignParamOrReturn(t, nil, false))
   361  	}
   362  	s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
   363  	result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
   364  
   365  	// Outputs
   366  	s.rUsed = RegAmounts{}
   367  	for _, t := range outs {
   368  		result.outparams = append(result.outparams, s.assignParamOrReturn(t, nil, true))
   369  	}
   370  	// The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
   371  	// TODO in theory could align offset only to minimum required by spilled data types.
   372  	result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
   373  	result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
   374  	result.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
   375  
   376  	return result
   377  }
   378  
   379  // ABIAnalyzeFuncType takes a function type 'ft' and an ABI rules description
   380  // 'config' and analyzes the function to determine how its parameters
   381  // and results will be passed (in registers or on the stack), returning
   382  // an ABIParamResultInfo object that holds the results of the analysis.
   383  func (config *ABIConfig) ABIAnalyzeFuncType(ft *types.Func) *ABIParamResultInfo {
   384  	setup()
   385  	s := assignState{
   386  		stackOffset: config.offsetForLocals,
   387  		rTotal:      config.regAmounts,
   388  	}
   389  	result := &ABIParamResultInfo{config: config}
   390  	result.preAllocateParams(ft.Receiver != nil, ft.Params.NumFields(), ft.Results.NumFields())
   391  
   392  	// Receiver
   393  	// TODO(register args) ? seems like "struct" and "fields" is not right anymore for describing function parameters
   394  	if ft.Receiver != nil && ft.Receiver.NumFields() != 0 {
   395  		r := ft.Receiver.FieldSlice()[0]
   396  		result.inparams = append(result.inparams,
   397  			s.assignParamOrReturn(r.Type, r.Nname, false))
   398  	}
   399  
   400  	// Inputs
   401  	ifsl := ft.Params.FieldSlice()
   402  	for _, f := range ifsl {
   403  		result.inparams = append(result.inparams,
   404  			s.assignParamOrReturn(f.Type, f.Nname, false))
   405  	}
   406  	s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
   407  	result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
   408  
   409  	// Outputs
   410  	s.rUsed = RegAmounts{}
   411  	ofsl := ft.Results.FieldSlice()
   412  	for _, f := range ofsl {
   413  		result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type, f.Nname, true))
   414  	}
   415  	// The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
   416  	// TODO in theory could align offset only to minimum required by spilled data types.
   417  	result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
   418  	result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
   419  	result.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
   420  	return result
   421  }
   422  
   423  // ABIAnalyze returns the same result as ABIAnalyzeFuncType, but also
   424  // updates the offsets of all the receiver, input, and output fields.
   425  // If setNname is true, it also sets the FrameOffset of the Nname for
   426  // the field(s); this is for use when compiling a function and figuring out
   427  // spill locations.  Doing this for callers can cause races for register
   428  // outputs because their frame location transitions from BOGUS_FUNARG_OFFSET
   429  // to zero to an as-if-AUTO offset that has no use for callers.
   430  func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResultInfo {
   431  	ft := t.FuncType()
   432  	result := config.ABIAnalyzeFuncType(ft)
   433  
   434  	// Fill in the frame offsets for receiver, inputs, results
   435  	k := 0
   436  	if t.NumRecvs() != 0 {
   437  		config.updateOffset(result, ft.Receiver.FieldSlice()[0], result.inparams[0], false, setNname)
   438  		k++
   439  	}
   440  	for i, f := range ft.Params.FieldSlice() {
   441  		config.updateOffset(result, f, result.inparams[k+i], false, setNname)
   442  	}
   443  	for i, f := range ft.Results.FieldSlice() {
   444  		config.updateOffset(result, f, result.outparams[i], true, setNname)
   445  	}
   446  	return result
   447  }
   448  
   449  func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn, setNname bool) {
   450  	// Everything except return values in registers has either a frame home (if not in a register) or a frame spill location.
   451  	if !isReturn || len(a.Registers) == 0 {
   452  		// The type frame offset DOES NOT show effects of minimum frame size.
   453  		// Getting this wrong breaks stackmaps, see liveness/plive.go:WriteFuncMap and typebits/typebits.go:Set
   454  		off := a.FrameOffset(result)
   455  		fOffset := f.Offset
   456  		if fOffset == types.BOGUS_FUNARG_OFFSET {
   457  			if setNname && f.Nname != nil {
   458  				f.Nname.(*ir.Name).SetFrameOffset(off)
   459  				f.Nname.(*ir.Name).SetIsOutputParamInRegisters(false)
   460  			}
   461  		} else {
   462  			base.Fatalf("field offset for %s at %s has been set to %d", f.Sym.Name, base.FmtPos(f.Pos), fOffset)
   463  		}
   464  	} else {
   465  		if setNname && f.Nname != nil {
   466  			fname := f.Nname.(*ir.Name)
   467  			fname.SetIsOutputParamInRegisters(true)
   468  			fname.SetFrameOffset(0)
   469  		}
   470  	}
   471  }
   472  
   473  //......................................................................
   474  //
   475  // Non-public portions.
   476  
   477  // regString produces a human-readable version of a RegIndex.
   478  func (c *RegAmounts) regString(r RegIndex) string {
   479  	if int(r) < c.intRegs {
   480  		return fmt.Sprintf("I%d", int(r))
   481  	} else if int(r) < c.intRegs+c.floatRegs {
   482  		return fmt.Sprintf("F%d", int(r)-c.intRegs)
   483  	}
   484  	return fmt.Sprintf("<?>%d", r)
   485  }
   486  
   487  // ToString method renders an ABIParamAssignment in human-readable
   488  // form, suitable for debugging or unit testing.
   489  func (ri *ABIParamAssignment) ToString(config *ABIConfig, extra bool) string {
   490  	regs := "R{"
   491  	offname := "spilloffset" // offset is for spill for register(s)
   492  	if len(ri.Registers) == 0 {
   493  		offname = "offset" // offset is for memory arg
   494  	}
   495  	for _, r := range ri.Registers {
   496  		regs += " " + config.regAmounts.regString(r)
   497  		if extra {
   498  			regs += fmt.Sprintf("(%d)", r)
   499  		}
   500  	}
   501  	if extra {
   502  		regs += fmt.Sprintf(" | #I=%d, #F=%d", config.regAmounts.intRegs, config.regAmounts.floatRegs)
   503  	}
   504  	return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type)
   505  }
   506  
   507  // String method renders an ABIParamResultInfo in human-readable
   508  // form, suitable for debugging or unit testing.
   509  func (ri *ABIParamResultInfo) String() string {
   510  	res := ""
   511  	for k, p := range ri.inparams {
   512  		res += fmt.Sprintf("IN %d: %s\n", k, p.ToString(ri.config, false))
   513  	}
   514  	for k, r := range ri.outparams {
   515  		res += fmt.Sprintf("OUT %d: %s\n", k, r.ToString(ri.config, false))
   516  	}
   517  	res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d",
   518  		ri.offsetToSpillArea, ri.spillAreaSize)
   519  	return res
   520  }
   521  
   522  // assignState holds intermediate state during the register assigning process
   523  // for a given function signature.
   524  type assignState struct {
   525  	rTotal      RegAmounts // total reg amounts from ABI rules
   526  	rUsed       RegAmounts // regs used by params completely assigned so far
   527  	pUsed       RegAmounts // regs used by the current param (or pieces therein)
   528  	stackOffset int64      // current stack offset
   529  	spillOffset int64      // current spill offset
   530  }
   531  
   532  // align returns a rounded up to t's alignment
   533  func align(a int64, t *types.Type) int64 {
   534  	return alignTo(a, int(uint8(t.Alignment())))
   535  }
   536  
   537  // alignTo returns a rounded up to t, where t must be 0 or a power of 2.
   538  func alignTo(a int64, t int) int64 {
   539  	if t == 0 {
   540  		return a
   541  	}
   542  	return types.Rnd(a, int64(t))
   543  }
   544  
   545  // stackSlot returns a stack offset for a param or result of the
   546  // specified type.
   547  func (state *assignState) stackSlot(t *types.Type) int64 {
   548  	rv := align(state.stackOffset, t)
   549  	state.stackOffset = rv + t.Size()
   550  	return rv
   551  }
   552  
   553  // allocateRegs returns an ordered list of register indices for a parameter or result
   554  // that we've just determined to be register-assignable. The number of registers
   555  // needed is assumed to be stored in state.pUsed.
   556  func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex {
   557  	if t.Size() == 0 {
   558  		return regs
   559  	}
   560  	ri := state.rUsed.intRegs
   561  	rf := state.rUsed.floatRegs
   562  	if t.IsScalar() || t.IsPtrShaped() {
   563  		if t.IsComplex() {
   564  			regs = append(regs, RegIndex(rf+state.rTotal.intRegs), RegIndex(rf+1+state.rTotal.intRegs))
   565  			rf += 2
   566  		} else if t.IsFloat() {
   567  			regs = append(regs, RegIndex(rf+state.rTotal.intRegs))
   568  			rf += 1
   569  		} else {
   570  			n := (int(t.Size()) + types.RegSize - 1) / types.RegSize
   571  			for i := 0; i < n; i++ { // looking ahead to really big integers
   572  				regs = append(regs, RegIndex(ri))
   573  				ri += 1
   574  			}
   575  		}
   576  		state.rUsed.intRegs = ri
   577  		state.rUsed.floatRegs = rf
   578  		return regs
   579  	} else {
   580  		typ := t.Kind()
   581  		switch typ {
   582  		case types.TARRAY:
   583  			for i := int64(0); i < t.NumElem(); i++ {
   584  				regs = state.allocateRegs(regs, t.Elem())
   585  			}
   586  			return regs
   587  		case types.TSTRUCT:
   588  			for _, f := range t.FieldSlice() {
   589  				regs = state.allocateRegs(regs, f.Type)
   590  			}
   591  			return regs
   592  		case types.TSLICE:
   593  			return state.allocateRegs(regs, synthSlice)
   594  		case types.TSTRING:
   595  			return state.allocateRegs(regs, synthString)
   596  		case types.TINTER:
   597  			return state.allocateRegs(regs, synthIface)
   598  		}
   599  	}
   600  	base.Fatalf("was not expecting type %s", t)
   601  	panic("unreachable")
   602  }
   603  
   604  // regAllocate creates a register ABIParamAssignment object for a param
   605  // or result with the specified type, as a final step (this assumes
   606  // that all of the safety/suitability analysis is complete).
   607  func (state *assignState) regAllocate(t *types.Type, name types.Object, isReturn bool) ABIParamAssignment {
   608  	spillLoc := int64(-1)
   609  	if !isReturn {
   610  		// Spill for register-resident t must be aligned for storage of a t.
   611  		spillLoc = align(state.spillOffset, t)
   612  		state.spillOffset = spillLoc + t.Size()
   613  	}
   614  	return ABIParamAssignment{
   615  		Type:      t,
   616  		Name:      name,
   617  		Registers: state.allocateRegs([]RegIndex{}, t),
   618  		offset:    int32(spillLoc),
   619  	}
   620  }
   621  
   622  // stackAllocate creates a stack memory ABIParamAssignment object for
   623  // a param or result with the specified type, as a final step (this
   624  // assumes that all of the safety/suitability analysis is complete).
   625  func (state *assignState) stackAllocate(t *types.Type, name types.Object) ABIParamAssignment {
   626  	return ABIParamAssignment{
   627  		Type:   t,
   628  		Name:   name,
   629  		offset: int32(state.stackSlot(t)),
   630  	}
   631  }
   632  
   633  // intUsed returns the number of integer registers consumed
   634  // at a given point within an assignment stage.
   635  func (state *assignState) intUsed() int {
   636  	return state.rUsed.intRegs + state.pUsed.intRegs
   637  }
   638  
   639  // floatUsed returns the number of floating point registers consumed at
   640  // a given point within an assignment stage.
   641  func (state *assignState) floatUsed() int {
   642  	return state.rUsed.floatRegs + state.pUsed.floatRegs
   643  }
   644  
   645  // regassignIntegral examines a param/result of integral type 't' to
   646  // determines whether it can be register-assigned. Returns TRUE if we
   647  // can register allocate, FALSE otherwise (and updates state
   648  // accordingly).
   649  func (state *assignState) regassignIntegral(t *types.Type) bool {
   650  	regsNeeded := int(types.Rnd(t.Size(), int64(types.PtrSize)) / int64(types.PtrSize))
   651  	if t.IsComplex() {
   652  		regsNeeded = 2
   653  	}
   654  
   655  	// Floating point and complex.
   656  	if t.IsFloat() || t.IsComplex() {
   657  		if regsNeeded+state.floatUsed() > state.rTotal.floatRegs {
   658  			// not enough regs
   659  			return false
   660  		}
   661  		state.pUsed.floatRegs += regsNeeded
   662  		return true
   663  	}
   664  
   665  	// Non-floating point
   666  	if regsNeeded+state.intUsed() > state.rTotal.intRegs {
   667  		// not enough regs
   668  		return false
   669  	}
   670  	state.pUsed.intRegs += regsNeeded
   671  	return true
   672  }
   673  
   674  // regassignArray processes an array type (or array component within some
   675  // other enclosing type) to determine if it can be register assigned.
   676  // Returns TRUE if we can register allocate, FALSE otherwise.
   677  func (state *assignState) regassignArray(t *types.Type) bool {
   678  
   679  	nel := t.NumElem()
   680  	if nel == 0 {
   681  		return true
   682  	}
   683  	if nel > 1 {
   684  		// Not an array of length 1: stack assign
   685  		return false
   686  	}
   687  	// Visit element
   688  	return state.regassign(t.Elem())
   689  }
   690  
   691  // regassignStruct processes a struct type (or struct component within
   692  // some other enclosing type) to determine if it can be register
   693  // assigned. Returns TRUE if we can register allocate, FALSE otherwise.
   694  func (state *assignState) regassignStruct(t *types.Type) bool {
   695  	for _, field := range t.FieldSlice() {
   696  		if !state.regassign(field.Type) {
   697  			return false
   698  		}
   699  	}
   700  	return true
   701  }
   702  
   703  // synthOnce ensures that we only create the synth* fake types once.
   704  var synthOnce sync.Once
   705  
   706  // synthSlice, synthString, and syncIface are synthesized struct types
   707  // meant to capture the underlying implementations of string/slice/interface.
   708  var synthSlice *types.Type
   709  var synthString *types.Type
   710  var synthIface *types.Type
   711  
   712  // setup performs setup for the register assignment utilities, manufacturing
   713  // a small set of synthesized types that we'll need along the way.
   714  func setup() {
   715  	synthOnce.Do(func() {
   716  		fname := types.BuiltinPkg.Lookup
   717  		nxp := src.NoXPos
   718  		bp := types.NewPtr(types.Types[types.TUINT8])
   719  		it := types.Types[types.TINT]
   720  		synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
   721  			types.NewField(nxp, fname("ptr"), bp),
   722  			types.NewField(nxp, fname("len"), it),
   723  			types.NewField(nxp, fname("cap"), it),
   724  		})
   725  		types.CalcStructSize(synthSlice)
   726  		synthString = types.NewStruct(types.NoPkg, []*types.Field{
   727  			types.NewField(nxp, fname("data"), bp),
   728  			types.NewField(nxp, fname("len"), it),
   729  		})
   730  		types.CalcStructSize(synthString)
   731  		unsp := types.Types[types.TUNSAFEPTR]
   732  		synthIface = types.NewStruct(types.NoPkg, []*types.Field{
   733  			types.NewField(nxp, fname("f1"), unsp),
   734  			types.NewField(nxp, fname("f2"), unsp),
   735  		})
   736  		types.CalcStructSize(synthIface)
   737  	})
   738  }
   739  
   740  // regassign examines a given param type (or component within some
   741  // composite) to determine if it can be register assigned.  Returns
   742  // TRUE if we can register allocate, FALSE otherwise.
   743  func (state *assignState) regassign(pt *types.Type) bool {
   744  	typ := pt.Kind()
   745  	if pt.IsScalar() || pt.IsPtrShaped() {
   746  		return state.regassignIntegral(pt)
   747  	}
   748  	switch typ {
   749  	case types.TARRAY:
   750  		return state.regassignArray(pt)
   751  	case types.TSTRUCT:
   752  		return state.regassignStruct(pt)
   753  	case types.TSLICE:
   754  		return state.regassignStruct(synthSlice)
   755  	case types.TSTRING:
   756  		return state.regassignStruct(synthString)
   757  	case types.TINTER:
   758  		return state.regassignStruct(synthIface)
   759  	default:
   760  		base.Fatalf("not expected")
   761  		panic("unreachable")
   762  	}
   763  }
   764  
   765  // assignParamOrReturn processes a given receiver, param, or result
   766  // of field f to determine whether it can be register assigned.
   767  // The result of the analysis is recorded in the result
   768  // ABIParamResultInfo held in 'state'.
   769  func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment {
   770  	state.pUsed = RegAmounts{}
   771  	if pt.Size() == types.BADWIDTH {
   772  		base.Fatalf("should never happen")
   773  		panic("unreachable")
   774  	} else if pt.Size() == 0 {
   775  		return state.stackAllocate(pt, n)
   776  	} else if state.regassign(pt) {
   777  		return state.regAllocate(pt, n, isReturn)
   778  	} else {
   779  		return state.stackAllocate(pt, n)
   780  	}
   781  }
   782  
   783  // ComputePadding returns a list of "post element" padding values in
   784  // the case where we have a structure being passed in registers. Given
   785  // a param assignment corresponding to a struct, it returns a list
   786  // containing padding values for each field, e.g. the Kth element in
   787  // the list is the amount of padding between field K and the following
   788  // field. For things that are not structs (or structs without padding)
   789  // it returns a list of zeros. Example:
   790  //
   791  // type small struct {
   792  //   x uint16
   793  //   y uint8
   794  //   z int32
   795  //   w int32
   796  // }
   797  //
   798  // For this struct we would return a list [0, 1, 0, 0], meaning that
   799  // we have one byte of padding after the second field, and no bytes of
   800  // padding after any of the other fields. Input parameter "storage" is
   801  // a slice with enough capacity to accommodate padding elements for
   802  // the architected register set in question.
   803  func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
   804  	nr := len(pa.Registers)
   805  	padding := storage[:nr]
   806  	for i := 0; i < nr; i++ {
   807  		padding[i] = 0
   808  	}
   809  	if pa.Type.Kind() != types.TSTRUCT || nr == 0 {
   810  		return padding
   811  	}
   812  	types := make([]*types.Type, 0, nr)
   813  	types = appendParamTypes(types, pa.Type)
   814  	if len(types) != nr {
   815  		panic("internal error")
   816  	}
   817  	off := int64(0)
   818  	for idx, t := range types {
   819  		ts := t.Size()
   820  		off += int64(ts)
   821  		if idx < len(types)-1 {
   822  			noff := align(off, types[idx+1])
   823  			if noff != off {
   824  				padding[idx] = uint64(noff - off)
   825  			}
   826  		}
   827  	}
   828  	return padding
   829  }
   830  

View as plain text