5
5
6
6
use entity:: { PrimaryMap , Keys } ;
7
7
use ir:: { Type , StackSlot } ;
8
+ use packed_option:: PackedOption ;
8
9
use std:: fmt;
9
10
use std:: ops:: Index ;
10
11
use std:: str:: FromStr ;
@@ -44,6 +45,12 @@ pub enum StackSlotKind {
44
45
/// stack slots are used to represent individual arguments in the outgoing call frame. These
45
46
/// stack slots are only valid while setting up a call.
46
47
OutgoingArg ,
48
+
49
+ /// An emergency spill slot.
50
+ ///
51
+ /// Emergency slots are allocated late when the register's constraint solver needs extra space
52
+ /// to shuffle registers around. The are only used briefly, and can be reused.
53
+ EmergencySlot ,
47
54
}
48
55
49
56
impl FromStr for StackSlotKind {
@@ -56,6 +63,7 @@ impl FromStr for StackSlotKind {
56
63
"spill_slot" => Ok ( SpillSlot ) ,
57
64
"incoming_arg" => Ok ( IncomingArg ) ,
58
65
"outgoing_arg" => Ok ( OutgoingArg ) ,
66
+ "emergency_slot" => Ok ( EmergencySlot ) ,
59
67
_ => Err ( ( ) ) ,
60
68
}
61
69
}
@@ -69,6 +77,7 @@ impl fmt::Display for StackSlotKind {
69
77
SpillSlot => "spill_slot" ,
70
78
IncomingArg => "incoming_arg" ,
71
79
OutgoingArg => "outgoing_arg" ,
80
+ EmergencySlot => "emergency_slot" ,
72
81
} )
73
82
}
74
83
}
@@ -135,6 +144,9 @@ pub struct StackSlots {
135
144
/// All the outgoing stack slots, ordered by offset.
136
145
outgoing : Vec < StackSlot > ,
137
146
147
+ /// All the emergency slots.
148
+ emergency : Vec < StackSlot > ,
149
+
138
150
/// The total size of the stack frame.
139
151
///
140
152
/// This is the distance from the stack pointer in the current function to the stack pointer in
@@ -152,6 +164,7 @@ impl StackSlots {
152
164
StackSlots {
153
165
slots : PrimaryMap :: new ( ) ,
154
166
outgoing : Vec :: new ( ) ,
167
+ emergency : Vec :: new ( ) ,
155
168
frame_size : None ,
156
169
}
157
170
}
@@ -160,6 +173,7 @@ impl StackSlots {
160
173
pub fn clear ( & mut self ) {
161
174
self . slots . clear ( ) ;
162
175
self . outgoing . clear ( ) ;
176
+ self . emergency . clear ( ) ;
163
177
self . frame_size = None ;
164
178
}
165
179
@@ -243,6 +257,43 @@ impl StackSlots {
243
257
self . outgoing . insert ( inspos, ss) ;
244
258
ss
245
259
}
260
+
261
+ /// Get an emergency spill slot that can be used to store a `ty` value.
262
+ ///
263
+ /// This may allocate a new slot, or it may reuse an existing emergency spill slot, excluding
264
+ /// any slots in the `in_use` list.
265
+ pub fn get_emergency_slot (
266
+ & mut self ,
267
+ ty : Type ,
268
+ in_use : & [ PackedOption < StackSlot > ] ,
269
+ ) -> StackSlot {
270
+ let size = ty. bytes ( ) ;
271
+
272
+ // Find the smallest existing slot that can fit the type.
273
+ if let Some ( & ss) = self . emergency
274
+ . iter ( )
275
+ . filter ( |& & ss| self [ ss] . size >= size && !in_use. contains ( & ss. into ( ) ) )
276
+ . min_by_key ( |& & ss| self [ ss] . size )
277
+ {
278
+ return ss;
279
+ }
280
+
281
+ // Alternatively, use the largest available slot and make it larger.
282
+ if let Some ( & ss) = self . emergency
283
+ . iter ( )
284
+ . filter ( |& & ss| !in_use. contains ( & ss. into ( ) ) )
285
+ . max_by_key ( |& & ss| self [ ss] . size )
286
+ {
287
+ self . slots [ ss] . size = size;
288
+ return ss;
289
+ }
290
+
291
+ // No existing slot found. Make one and insert it into `emergency`.
292
+ let data = StackSlotData :: new ( StackSlotKind :: EmergencySlot , size) ;
293
+ let ss = self . slots . push ( data) ;
294
+ self . emergency . push ( ss) ;
295
+ ss
296
+ }
246
297
}
247
298
248
299
#[ cfg( test) ]
@@ -304,4 +355,31 @@ mod tests {
304
355
assert_eq ! ( slot2. alignment( 16 ) , 8 ) ;
305
356
assert_eq ! ( slot2. alignment( 32 ) , 8 ) ;
306
357
}
358
+
359
+ #[ test]
360
+ fn emergency ( ) {
361
+ let mut sss = StackSlots :: new ( ) ;
362
+
363
+ let ss0 = sss. get_emergency_slot ( types:: I32 , & [ ] ) ;
364
+ assert_eq ! ( sss[ ss0] . size, 4 ) ;
365
+
366
+ // When a smaller size is requested, we should simply get the same slot back.
367
+ assert_eq ! ( sss. get_emergency_slot( types:: I8 , & [ ] ) , ss0) ;
368
+ assert_eq ! ( sss[ ss0] . size, 4 ) ;
369
+ assert_eq ! ( sss. get_emergency_slot( types:: F32 , & [ ] ) , ss0) ;
370
+ assert_eq ! ( sss[ ss0] . size, 4 ) ;
371
+
372
+ // Ask for a larger size and the slot should grow.
373
+ assert_eq ! ( sss. get_emergency_slot( types:: F64 , & [ ] ) , ss0) ;
374
+ assert_eq ! ( sss[ ss0] . size, 8 ) ;
375
+
376
+ // When one slot is in use, we should get a new one.
377
+ let ss1 = sss. get_emergency_slot ( types:: I32 , & [ None . into ( ) , ss0. into ( ) ] ) ;
378
+ assert_eq ! ( sss[ ss0] . size, 8 ) ;
379
+ assert_eq ! ( sss[ ss1] . size, 4 ) ;
380
+
381
+ // Now we should get the smallest fit of the two available slots.
382
+ assert_eq ! ( sss. get_emergency_slot( types:: F32 , & [ ] ) , ss1) ;
383
+ assert_eq ! ( sss. get_emergency_slot( types:: F64 , & [ ] ) , ss0) ;
384
+ }
307
385
}
0 commit comments