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