@@ -25,9 +25,8 @@ import (
25
25
type ABIParamResultInfo struct {
26
26
inparams []ABIParamAssignment // Includes receiver for method calls. Does NOT include hidden closure pointer.
27
27
outparams []ABIParamAssignment
28
- intSpillSlots int
29
- floatSpillSlots int
30
28
offsetToSpillArea int64
29
+ spillAreaSize int64
31
30
config * ABIConfig // to enable String() method
32
31
}
33
32
@@ -47,18 +46,14 @@ func (a *ABIParamResultInfo) OutParam(i int) ABIParamAssignment {
47
46
return a .outparams [i ]
48
47
}
49
48
50
- func (a * ABIParamResultInfo ) IntSpillCount () int {
51
- return a .intSpillSlots
52
- }
53
-
54
- func (a * ABIParamResultInfo ) FloatSpillCount () int {
55
- return a .floatSpillSlots
56
- }
57
-
58
49
func (a * ABIParamResultInfo ) SpillAreaOffset () int64 {
59
50
return a .offsetToSpillArea
60
51
}
61
52
53
+ func (a * ABIParamResultInfo ) SpillAreaSize () int64 {
54
+ return a .spillAreaSize
55
+ }
56
+
62
57
// RegIndex stores the index into the set of machine registers used by
63
58
// the ABI on a specific architecture for parameter passing. RegIndex
64
59
// values 0 through N-1 (where N is the number of integer registers
@@ -78,7 +73,27 @@ type RegIndex uint8
78
73
type ABIParamAssignment struct {
79
74
Type * types.Type
80
75
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
82
97
}
83
98
84
99
// RegAmounts holds a specified number of integer/float registers.
@@ -91,20 +106,58 @@ type RegAmounts struct {
91
106
// by the ABI rules for parameter passing and result returning.
92
107
type ABIConfig struct {
93
108
// Do we need anything more than this?
94
- regAmounts RegAmounts
109
+ regAmounts RegAmounts
110
+ regsForTypeCache map [* types.Type ]int
95
111
}
96
112
97
113
// NewABIConfig returns a new ABI configuration for an architecture with
98
114
// iRegsCount integer/pointer registers and fRegsCount floating point registers.
99
115
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
101
154
}
102
155
103
156
// ABIAnalyze takes a function type 't' and an ABI rules description
104
157
// 'config' and analyzes the function to determine how its parameters
105
158
// and results will be passed (in registers or on the stack), returning
106
159
// 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 {
108
161
setup ()
109
162
s := assignState {
110
163
rTotal : config .regAmounts ,
@@ -116,28 +169,27 @@ func ABIAnalyze(t *types.Type, config *ABIConfig) ABIParamResultInfo {
116
169
if t .NumRecvs () != 0 {
117
170
rfsl := ft .Receiver .FieldSlice ()
118
171
result .inparams = append (result .inparams ,
119
- s .assignParamOrReturn (rfsl [0 ].Type ))
172
+ s .assignParamOrReturn (rfsl [0 ].Type , false ))
120
173
}
121
174
122
175
// Inputs
123
176
ifsl := ft .Params .FieldSlice ()
124
177
for _ , f := range ifsl {
125
178
result .inparams = append (result .inparams ,
126
- s .assignParamOrReturn (f .Type ))
179
+ s .assignParamOrReturn (f .Type , false ))
127
180
}
128
181
s .stackOffset = types .Rnd (s .stackOffset , int64 (types .RegSize ))
129
182
130
- // Record number of spill slots needed.
131
- result .intSpillSlots = s .rUsed .intRegs
132
- result .floatSpillSlots = s .rUsed .floatRegs
133
-
134
183
// Outputs
135
184
s .rUsed = RegAmounts {}
136
185
ofsl := ft .Results .FieldSlice ()
137
186
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 ))
139
188
}
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 )
141
193
142
194
return result
143
195
}
@@ -160,10 +212,14 @@ func (c *RegAmounts) regString(r RegIndex) string {
160
212
// form, suitable for debugging or unit testing.
161
213
func (ri * ABIParamAssignment ) toString (config * ABIConfig ) string {
162
214
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
+ }
163
219
for _ , r := range ri .Registers {
164
220
regs += " " + config .regAmounts .regString (r )
165
221
}
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 )
167
223
}
168
224
169
225
// toString method renders an ABIParamResultInfo in human-readable
@@ -176,8 +232,8 @@ func (ri *ABIParamResultInfo) String() string {
176
232
for k , r := range ri .outparams {
177
233
res += fmt .Sprintf ("OUT %d: %s\n " , k , r .toString (ri .config ))
178
234
}
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 )
181
237
return res
182
238
}
183
239
@@ -188,16 +244,27 @@ type assignState struct {
188
244
rUsed RegAmounts // regs used by params completely assigned so far
189
245
pUsed RegAmounts // regs used by the current param (or pieces therein)
190
246
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 ))
191
261
}
192
262
193
263
// stackSlot returns a stack offset for a param or result of the
194
264
// specified type.
195
265
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
201
268
return rv
202
269
}
203
270
@@ -225,11 +292,17 @@ func (state *assignState) allocateRegs() []RegIndex {
225
292
// regAllocate creates a register ABIParamAssignment object for a param
226
293
// or result with the specified type, as a final step (this assumes
227
294
// 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
+ }
229
302
return ABIParamAssignment {
230
303
Type : t ,
231
304
Registers : state .allocateRegs (),
232
- Offset : - 1 ,
305
+ offset : int32 ( spillLoc ) ,
233
306
}
234
307
}
235
308
@@ -239,7 +312,7 @@ func (state *assignState) regAllocate(t *types.Type) ABIParamAssignment {
239
312
func (state * assignState ) stackAllocate (t * types.Type ) ABIParamAssignment {
240
313
return ABIParamAssignment {
241
314
Type : t ,
242
- Offset : int32 (state .stackSlot (t )),
315
+ offset : int32 (state .stackSlot (t )),
243
316
}
244
317
}
245
318
@@ -261,6 +334,9 @@ func (state *assignState) floatUsed() int {
261
334
// accordingly).
262
335
func (state * assignState ) regassignIntegral (t * types.Type ) bool {
263
336
regsNeeded := int (types .Rnd (t .Width , int64 (types .PtrSize )) / int64 (types .PtrSize ))
337
+ if t .IsComplex () {
338
+ regsNeeded = 2
339
+ }
264
340
265
341
// Floating point and complex.
266
342
if t .IsFloat () || t .IsComplex () {
@@ -371,14 +447,14 @@ func (state *assignState) regassign(pt *types.Type) bool {
371
447
// of type 'pt' to determine whether it can be register assigned.
372
448
// The result of the analysis is recorded in the result
373
449
// ABIParamResultInfo held in 'state'.
374
- func (state * assignState ) assignParamOrReturn (pt * types.Type ) ABIParamAssignment {
450
+ func (state * assignState ) assignParamOrReturn (pt * types.Type , isReturn bool ) ABIParamAssignment {
375
451
state .pUsed = RegAmounts {}
376
452
if pt .Width == types .BADWIDTH {
377
453
panic ("should never happen" )
378
454
} else if pt .Width == 0 {
379
455
return state .stackAllocate (pt )
380
456
} else if state .regassign (pt ) {
381
- return state .regAllocate (pt )
457
+ return state .regAllocate (pt , isReturn )
382
458
} else {
383
459
return state .stackAllocate (pt )
384
460
}
0 commit comments