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