Skip to content

Commit 5a76c3d

Browse files
committed
[dev.regabi] cmd/compile: modify abiutils for recently updated ABI
Discovered difficluties posed by earlier design, these modifications should work better. Updated tests, also added some helper functions for use in call lowering. Change-Id: I459f0f71ad8a6730c571244925c3f395e1df28de Reviewed-on: https://go-review.googlesource.com/c/go/+/285392 Trust: David Chase <[email protected]> Run-TryBot: David Chase <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent 063c72f commit 5a76c3d

File tree

3 files changed

+244
-134
lines changed

3 files changed

+244
-134
lines changed

src/cmd/compile/internal/abi/abiutils.go

+111-35
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ import (
2525
type ABIParamResultInfo struct {
2626
inparams []ABIParamAssignment // Includes receiver for method calls. Does NOT include hidden closure pointer.
2727
outparams []ABIParamAssignment
28-
intSpillSlots int
29-
floatSpillSlots int
3028
offsetToSpillArea int64
29+
spillAreaSize int64
3130
config *ABIConfig // to enable String() method
3231
}
3332

@@ -47,18 +46,14 @@ func (a *ABIParamResultInfo) OutParam(i int) ABIParamAssignment {
4746
return a.outparams[i]
4847
}
4948

50-
func (a *ABIParamResultInfo) IntSpillCount() int {
51-
return a.intSpillSlots
52-
}
53-
54-
func (a *ABIParamResultInfo) FloatSpillCount() int {
55-
return a.floatSpillSlots
56-
}
57-
5849
func (a *ABIParamResultInfo) SpillAreaOffset() int64 {
5950
return a.offsetToSpillArea
6051
}
6152

53+
func (a *ABIParamResultInfo) SpillAreaSize() int64 {
54+
return a.spillAreaSize
55+
}
56+
6257
// RegIndex stores the index into the set of machine registers used by
6358
// the ABI on a specific architecture for parameter passing. RegIndex
6459
// values 0 through N-1 (where N is the number of integer registers
@@ -78,7 +73,27 @@ type RegIndex uint8
7873
type ABIParamAssignment struct {
7974
Type *types.Type
8075
Registers []RegIndex
81-
Offset int32
76+
offset int32
77+
}
78+
79+
// Offset returns the stack offset for addressing the parameter that "a" describes.
80+
// This will panic if "a" describes a register-allocated parameter.
81+
func (a *ABIParamAssignment) Offset() int32 {
82+
if len(a.Registers) > 0 {
83+
panic("Register allocated parameters have no offset")
84+
}
85+
return a.offset
86+
}
87+
88+
// SpillOffset returns the offset *within the spill area* for the parameter that "a" describes.
89+
// Registers will be spilled here; if a memory home is needed (for a pointer method e.g.)
90+
// then that will be the address.
91+
// This will panic if "a" describes a stack-allocated parameter.
92+
func (a *ABIParamAssignment) SpillOffset() int32 {
93+
if len(a.Registers) == 0 {
94+
panic("Stack-allocated parameters have no spill offset")
95+
}
96+
return a.offset
8297
}
8398

8499
// RegAmounts holds a specified number of integer/float registers.
@@ -91,20 +106,58 @@ type RegAmounts struct {
91106
// by the ABI rules for parameter passing and result returning.
92107
type ABIConfig struct {
93108
// Do we need anything more than this?
94-
regAmounts RegAmounts
109+
regAmounts RegAmounts
110+
regsForTypeCache map[*types.Type]int
95111
}
96112

97113
// NewABIConfig returns a new ABI configuration for an architecture with
98114
// iRegsCount integer/pointer registers and fRegsCount floating point registers.
99115
func NewABIConfig(iRegsCount, fRegsCount int) *ABIConfig {
100-
return &ABIConfig{RegAmounts{iRegsCount, fRegsCount}}
116+
return &ABIConfig{regAmounts: RegAmounts{iRegsCount, fRegsCount}, regsForTypeCache: make(map[*types.Type]int)}
117+
}
118+
119+
// NumParamRegs returns the number of parameter registers used for a given type,
120+
// without regard for the number available.
121+
func (a *ABIConfig) NumParamRegs(t *types.Type) int {
122+
if n, ok := a.regsForTypeCache[t]; ok {
123+
return n
124+
}
125+
126+
if t.IsScalar() || t.IsPtrShaped() {
127+
var n int
128+
if t.IsComplex() {
129+
n = 2
130+
} else {
131+
n = (int(t.Size()) + types.RegSize - 1) / types.RegSize
132+
}
133+
a.regsForTypeCache[t] = n
134+
return n
135+
}
136+
typ := t.Kind()
137+
n := 0
138+
switch typ {
139+
case types.TARRAY:
140+
n = a.NumParamRegs(t.Elem()) * int(t.NumElem())
141+
case types.TSTRUCT:
142+
for _, f := range t.FieldSlice() {
143+
n += a.NumParamRegs(f.Type)
144+
}
145+
case types.TSLICE:
146+
n = a.NumParamRegs(synthSlice)
147+
case types.TSTRING:
148+
n = a.NumParamRegs(synthString)
149+
case types.TINTER:
150+
n = a.NumParamRegs(synthIface)
151+
}
152+
a.regsForTypeCache[t] = n
153+
return n
101154
}
102155

103156
// ABIAnalyze takes a function type 't' and an ABI rules description
104157
// 'config' and analyzes the function to determine how its parameters
105158
// and results will be passed (in registers or on the stack), returning
106159
// an ABIParamResultInfo object that holds the results of the analysis.
107-
func ABIAnalyze(t *types.Type, config *ABIConfig) ABIParamResultInfo {
160+
func (config *ABIConfig) ABIAnalyze(t *types.Type) ABIParamResultInfo {
108161
setup()
109162
s := assignState{
110163
rTotal: config.regAmounts,
@@ -116,28 +169,27 @@ func ABIAnalyze(t *types.Type, config *ABIConfig) ABIParamResultInfo {
116169
if t.NumRecvs() != 0 {
117170
rfsl := ft.Receiver.FieldSlice()
118171
result.inparams = append(result.inparams,
119-
s.assignParamOrReturn(rfsl[0].Type))
172+
s.assignParamOrReturn(rfsl[0].Type, false))
120173
}
121174

122175
// Inputs
123176
ifsl := ft.Params.FieldSlice()
124177
for _, f := range ifsl {
125178
result.inparams = append(result.inparams,
126-
s.assignParamOrReturn(f.Type))
179+
s.assignParamOrReturn(f.Type, false))
127180
}
128181
s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
129182

130-
// Record number of spill slots needed.
131-
result.intSpillSlots = s.rUsed.intRegs
132-
result.floatSpillSlots = s.rUsed.floatRegs
133-
134183
// Outputs
135184
s.rUsed = RegAmounts{}
136185
ofsl := ft.Results.FieldSlice()
137186
for _, f := range ofsl {
138-
result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type))
187+
result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type, true))
139188
}
140-
result.offsetToSpillArea = s.stackOffset
189+
// The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
190+
// TODO in theory could align offset only to minimum required by spilled data types.
191+
result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
192+
result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
141193

142194
return result
143195
}
@@ -160,10 +212,14 @@ func (c *RegAmounts) regString(r RegIndex) string {
160212
// form, suitable for debugging or unit testing.
161213
func (ri *ABIParamAssignment) toString(config *ABIConfig) string {
162214
regs := "R{"
215+
offname := "spilloffset" // offset is for spill for register(s)
216+
if len(ri.Registers) == 0 {
217+
offname = "offset" // offset is for memory arg
218+
}
163219
for _, r := range ri.Registers {
164220
regs += " " + config.regAmounts.regString(r)
165221
}
166-
return fmt.Sprintf("%s } offset: %d typ: %v", regs, ri.Offset, ri.Type)
222+
return fmt.Sprintf("%s } %s: %d typ: %v", regs, offname, ri.offset, ri.Type)
167223
}
168224

169225
// toString method renders an ABIParamResultInfo in human-readable
@@ -176,8 +232,8 @@ func (ri *ABIParamResultInfo) String() string {
176232
for k, r := range ri.outparams {
177233
res += fmt.Sprintf("OUT %d: %s\n", k, r.toString(ri.config))
178234
}
179-
res += fmt.Sprintf("intspill: %d floatspill: %d offsetToSpillArea: %d",
180-
ri.intSpillSlots, ri.floatSpillSlots, ri.offsetToSpillArea)
235+
res += fmt.Sprintf("offsetToSpillArea: %d spillAreaSize: %d",
236+
ri.offsetToSpillArea, ri.spillAreaSize)
181237
return res
182238
}
183239

@@ -188,16 +244,27 @@ type assignState struct {
188244
rUsed RegAmounts // regs used by params completely assigned so far
189245
pUsed RegAmounts // regs used by the current param (or pieces therein)
190246
stackOffset int64 // current stack offset
247+
spillOffset int64 // current spill offset
248+
}
249+
250+
// align returns a rounded up to t's alignment
251+
func align(a int64, t *types.Type) int64 {
252+
return alignTo(a, int(t.Align))
253+
}
254+
255+
// alignTo returns a rounded up to t, where t must be 0 or a power of 2.
256+
func alignTo(a int64, t int) int64 {
257+
if t == 0 {
258+
return a
259+
}
260+
return types.Rnd(a, int64(t))
191261
}
192262

193263
// stackSlot returns a stack offset for a param or result of the
194264
// specified type.
195265
func (state *assignState) stackSlot(t *types.Type) int64 {
196-
if t.Align > 0 {
197-
state.stackOffset = types.Rnd(state.stackOffset, int64(t.Align))
198-
}
199-
rv := state.stackOffset
200-
state.stackOffset += t.Width
266+
rv := align(state.stackOffset, t)
267+
state.stackOffset = rv + t.Width
201268
return rv
202269
}
203270

@@ -225,11 +292,17 @@ func (state *assignState) allocateRegs() []RegIndex {
225292
// regAllocate creates a register ABIParamAssignment object for a param
226293
// or result with the specified type, as a final step (this assumes
227294
// that all of the safety/suitability analysis is complete).
228-
func (state *assignState) regAllocate(t *types.Type) ABIParamAssignment {
295+
func (state *assignState) regAllocate(t *types.Type, isReturn bool) ABIParamAssignment {
296+
spillLoc := int64(-1)
297+
if !isReturn {
298+
// Spill for register-resident t must be aligned for storage of a t.
299+
spillLoc = align(state.spillOffset, t)
300+
state.spillOffset = spillLoc + t.Size()
301+
}
229302
return ABIParamAssignment{
230303
Type: t,
231304
Registers: state.allocateRegs(),
232-
Offset: -1,
305+
offset: int32(spillLoc),
233306
}
234307
}
235308

@@ -239,7 +312,7 @@ func (state *assignState) regAllocate(t *types.Type) ABIParamAssignment {
239312
func (state *assignState) stackAllocate(t *types.Type) ABIParamAssignment {
240313
return ABIParamAssignment{
241314
Type: t,
242-
Offset: int32(state.stackSlot(t)),
315+
offset: int32(state.stackSlot(t)),
243316
}
244317
}
245318

@@ -261,6 +334,9 @@ func (state *assignState) floatUsed() int {
261334
// accordingly).
262335
func (state *assignState) regassignIntegral(t *types.Type) bool {
263336
regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize))
337+
if t.IsComplex() {
338+
regsNeeded = 2
339+
}
264340

265341
// Floating point and complex.
266342
if t.IsFloat() || t.IsComplex() {
@@ -371,14 +447,14 @@ func (state *assignState) regassign(pt *types.Type) bool {
371447
// of type 'pt' to determine whether it can be register assigned.
372448
// The result of the analysis is recorded in the result
373449
// ABIParamResultInfo held in 'state'.
374-
func (state *assignState) assignParamOrReturn(pt *types.Type) ABIParamAssignment {
450+
func (state *assignState) assignParamOrReturn(pt *types.Type, isReturn bool) ABIParamAssignment {
375451
state.pUsed = RegAmounts{}
376452
if pt.Width == types.BADWIDTH {
377453
panic("should never happen")
378454
} else if pt.Width == 0 {
379455
return state.stackAllocate(pt)
380456
} else if state.regassign(pt) {
381-
return state.regAllocate(pt)
457+
return state.regAllocate(pt, isReturn)
382458
} else {
383459
return state.stackAllocate(pt)
384460
}

0 commit comments

Comments
 (0)