@@ -17,7 +17,7 @@ use super::{
17
17
RedundantMoveEliminator , VRegIndex , SLOT_NONE ,
18
18
} ;
19
19
use crate :: ion:: data_structures:: {
20
- u128_key , u64_key, BlockparamIn , BlockparamOut , CodeRange , FixedRegFixupLevel , LiveRangeKey ,
20
+ u64_key, BlockparamIn , BlockparamOut , CodeRange , FixedRegFixupLevel , LiveRangeKey ,
21
21
LiveRangeListEntry , PosWithPrio ,
22
22
} ;
23
23
use crate :: ion:: reg_traversal:: RegTraversalIter ;
@@ -230,25 +230,16 @@ impl<'a, F: Function> Env<'a, F> {
230
230
let mut inter_block_sources: FxHashMap < Block , Allocation > = FxHashMap :: default ( ) ;
231
231
let mut inter_block_dests = Vec :: with_capacity ( self . func . num_blocks ( ) ) ;
232
232
233
- // This is the same data as BlockparamOut, but the fields are in a different order to
234
- // prefer sorting by destination vreg first. This allows us to advance two pointers
235
- // simultaneously into block_param_sources and block_param_dests when generating moves.
236
- struct BlockparamSource {
237
- from_vreg : VRegIndex ,
238
- from_block : Block ,
239
- to_block : Block ,
240
- to_vreg : VRegIndex ,
241
- alloc : Allocation ,
233
+ #[ derive( Hash , Eq , PartialEq ) ]
234
+ struct BlockparamSourceKey {
235
+ bits : u64 ,
242
236
}
243
237
244
- impl BlockparamSource {
245
- fn key ( & self ) -> u128 {
246
- u128_key (
247
- self . to_vreg . raw_u32 ( ) ,
248
- self . to_block . raw_u32 ( ) ,
249
- self . from_block . raw_u32 ( ) ,
250
- self . from_vreg . raw_u32 ( ) ,
251
- )
238
+ impl BlockparamSourceKey {
239
+ fn new ( from_block : Block , to_vreg : VRegIndex ) -> Self {
240
+ BlockparamSourceKey {
241
+ bits : u64_key ( from_block. raw_u32 ( ) , to_vreg. raw_u32 ( ) ) ,
242
+ }
252
243
}
253
244
}
254
245
@@ -263,9 +254,17 @@ impl<'a, F: Function> Env<'a, F> {
263
254
fn key ( & self ) -> u64 {
264
255
u64_key ( self . to_block . raw_u32 ( ) , self . from_block . raw_u32 ( ) )
265
256
}
257
+
258
+ fn source ( & self ) -> BlockparamSourceKey {
259
+ BlockparamSourceKey :: new ( self . from_block , self . to_vreg )
260
+ }
266
261
}
267
262
268
- let mut block_param_sources = Vec :: with_capacity ( 3 * self . func . num_insts ( ) ) ;
263
+ let mut block_param_sources =
264
+ FxHashMap :: < BlockparamSourceKey , Allocation > :: with_capacity_and_hasher (
265
+ 3 * self . func . num_insts ( ) ,
266
+ Default :: default ( ) ,
267
+ ) ;
269
268
let mut block_param_dests = Vec :: with_capacity ( 3 * self . func . num_insts ( ) ) ;
270
269
271
270
let debug_labels = self . func . debug_value_labels ( ) ;
@@ -287,7 +286,6 @@ impl<'a, F: Function> Env<'a, F> {
287
286
// `blockparam_outs`, which are sorted by (block, vreg),
288
287
// to fill in allocations.
289
288
let mut prev = PrevBuffer :: new ( blockparam_in_idx) ;
290
- let dests_start = block_param_dests. len ( ) ;
291
289
for range_idx in 0 ..self . vregs [ vreg. index ( ) ] . ranges . len ( ) {
292
290
let entry = self . vregs [ vreg. index ( ) ] . ranges [ range_idx] ;
293
291
let alloc = self . get_alloc_for_range ( entry. index ) ;
@@ -426,13 +424,19 @@ impl<'a, F: Function> Env<'a, F> {
426
424
to_vreg. index( )
427
425
) ;
428
426
429
- block_param_sources. push ( BlockparamSource {
430
- from_block,
431
- to_block,
432
- to_vreg,
433
- from_vreg : vreg,
434
- alloc,
435
- } ) ;
427
+ let key = BlockparamSourceKey :: new ( from_block, to_vreg) ;
428
+ match block_param_sources. entry ( key) {
429
+ // As with inter-block moves, if the entry is already present we'll
430
+ // try to prefer a register allocation.
431
+ Entry :: Occupied ( mut entry) => {
432
+ if !entry. get ( ) . is_reg ( ) {
433
+ entry. insert ( alloc) ;
434
+ }
435
+ }
436
+ Entry :: Vacant ( entry) => {
437
+ entry. insert ( alloc) ;
438
+ }
439
+ }
436
440
437
441
if self . annotations_enabled {
438
442
self . annotate (
@@ -615,11 +619,6 @@ impl<'a, F: Function> Env<'a, F> {
615
619
}
616
620
}
617
621
618
- // Sort the newly added block_param destinations. The key function ignores the vreg,
619
- // which is why we sort here: we preserve the vreg order automatically, and sort
620
- // smaller slices of the dests according to their source/destination blocks.
621
- block_param_dests[ dests_start..] . sort_unstable_by_key ( BlockparamDest :: key) ;
622
-
623
622
if !inter_block_dests. is_empty ( ) {
624
623
self . stats . halfmoves_count += inter_block_dests. len ( ) * 2 ;
625
624
@@ -650,53 +649,12 @@ impl<'a, F: Function> Env<'a, F> {
650
649
self . stats . halfmoves_count += block_param_sources. len ( ) ;
651
650
self . stats . halfmoves_count += block_param_dests. len ( ) ;
652
651
653
- // Sort the sources to mirror the destinations now, ordering by dest vreg/dest
654
- // block/source block.
655
- block_param_sources. sort_unstable_by_key ( BlockparamSource :: key) ;
656
-
657
- // We traverse the `block_param_sources` and `block_param_dests` vectors in parallel,
658
- // advancing a pointer into the sources vector as we disocver dests that don't match
659
- // it. There are two places that we ensure that the order of those two vectors enables
660
- // this traversal: above when we sort `block_param_sources` according to its key; at
661
- // the end of the main vreg loop above when we sort a slice of `block_param_dests` by
662
- // its key.
663
- //
664
- // In both cases, the key function will order entries lexicographically according to
665
- // (destination vreg, destination block, source block). One implication of this is that
666
- // if there are multiple sources available for a destination, we'll always pick the one
667
- // with the lowest source vreg. We could potentially improve this by selecting from the
668
- // range of possible sources based on some heuristic (prefer registers, for example).
669
-
670
652
trace ! ( "processing block-param moves" ) ;
671
- let mut block_param_sources = block_param_sources. into_iter ( ) . peekable ( ) ;
672
- ' outer: for dest in block_param_dests {
673
- while let Some ( src) = block_param_sources. peek ( ) {
674
- if src. to_vreg == dest. to_vreg
675
- && src. from_block == dest. from_block
676
- && src. to_block == dest. to_block
677
- {
678
- trace ! ( " -> moving from {} to {}" , src. alloc, dest. alloc) ;
679
-
680
- let ( pos, prio) =
681
- choose_move_location ( self , dest. from_block , dest. to_block ) ;
682
- inserted_moves. push (
683
- pos,
684
- prio,
685
- src. alloc ,
686
- dest. alloc ,
687
- self . vreg ( dest. to_vreg ) ,
688
- ) ;
689
-
690
- // We don't advance the block_param_sources iterator here because there
691
- // could be additional destinations that would take from that source. Thus,
692
- // we continue the outer loop to keep the iterator unchanged.
693
- continue ' outer;
694
- }
695
-
696
- block_param_sources. next ( ) ;
697
- }
698
-
699
- panic ! ( "Ran out of block-param sources" ) ;
653
+ for dest in block_param_dests {
654
+ let src = dest. source ( ) ;
655
+ let src_alloc = block_param_sources. get ( & src) . unwrap ( ) ;
656
+ let ( pos, prio) = choose_move_location ( self , dest. from_block , dest. to_block ) ;
657
+ inserted_moves. push ( pos, prio, * src_alloc, dest. alloc , self . vreg ( dest. to_vreg ) ) ;
700
658
}
701
659
}
702
660
0 commit comments