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