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

View as plain text