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