@@ -17,7 +17,7 @@ use super::{
1717 RedundantMoveEliminator , VRegIndex , SLOT_NONE ,
1818} ;
1919use crate :: ion:: data_structures:: {
20- u128_key , u64_key, BlockparamIn , BlockparamOut , CodeRange , FixedRegFixupLevel , LiveRangeKey ,
20+ u64_key, BlockparamIn , BlockparamOut , CodeRange , FixedRegFixupLevel , LiveRangeKey ,
2121 LiveRangeListEntry , PosWithPrio ,
2222} ;
2323use crate :: ion:: reg_traversal:: RegTraversalIter ;
@@ -230,25 +230,16 @@ impl<'a, F: Function> Env<'a, F> {
230230 let mut inter_block_sources: FxHashMap < Block , Allocation > = FxHashMap :: default ( ) ;
231231 let mut inter_block_dests = Vec :: with_capacity ( self . func . num_blocks ( ) ) ;
232232
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 ,
242236 }
243237
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+ }
252243 }
253244 }
254245
@@ -263,9 +254,17 @@ impl<'a, F: Function> Env<'a, F> {
263254 fn key ( & self ) -> u64 {
264255 u64_key ( self . to_block . raw_u32 ( ) , self . from_block . raw_u32 ( ) )
265256 }
257+
258+ fn source ( & self ) -> BlockparamSourceKey {
259+ BlockparamSourceKey :: new ( self . from_block , self . to_vreg )
260+ }
266261 }
267262
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+ ) ;
269268 let mut block_param_dests = Vec :: with_capacity ( 3 * self . func . num_insts ( ) ) ;
270269
271270 let debug_labels = self . func . debug_value_labels ( ) ;
@@ -287,7 +286,6 @@ impl<'a, F: Function> Env<'a, F> {
287286 // `blockparam_outs`, which are sorted by (block, vreg),
288287 // to fill in allocations.
289288 let mut prev = PrevBuffer :: new ( blockparam_in_idx) ;
290- let dests_start = block_param_dests. len ( ) ;
291289 for range_idx in 0 ..self . vregs [ vreg. index ( ) ] . ranges . len ( ) {
292290 let entry = self . vregs [ vreg. index ( ) ] . ranges [ range_idx] ;
293291 let alloc = self . get_alloc_for_range ( entry. index ) ;
@@ -426,13 +424,19 @@ impl<'a, F: Function> Env<'a, F> {
426424 to_vreg. index( )
427425 ) ;
428426
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+ }
436440
437441 if self . annotations_enabled {
438442 self . annotate (
@@ -615,11 +619,6 @@ impl<'a, F: Function> Env<'a, F> {
615619 }
616620 }
617621
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-
623622 if !inter_block_dests. is_empty ( ) {
624623 self . stats . halfmoves_count += inter_block_dests. len ( ) * 2 ;
625624
@@ -650,53 +649,12 @@ impl<'a, F: Function> Env<'a, F> {
650649 self . stats . halfmoves_count += block_param_sources. len ( ) ;
651650 self . stats . halfmoves_count += block_param_dests. len ( ) ;
652651
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-
670652 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 ) ) ;
700658 }
701659 }
702660
0 commit comments