1414
1515use super :: { Env , LiveBundleIndex , SpillSet , SpillSlotIndex , VRegIndex } ;
1616use crate :: {
17- ion:: data_structures:: { BlockparamOut , CodeRange } ,
18- Function , Inst , OperandConstraint , OperandKind , PReg , ProgPoint ,
17+ ion:: data_structures:: CodeRange , Block , Function , OperandConstraint , OperandKind , PReg ,
18+ ProgPoint ,
1919} ;
20- use alloc:: format;
20+ use alloc:: { format, vec :: Vec } ;
2121use smallvec:: smallvec;
2222
2323impl < ' a , F : Function > Env < ' a , F > {
24- pub fn merge_bundles ( & mut self , from : LiveBundleIndex , to : LiveBundleIndex ) -> bool {
24+ fn merge_bundles ( & mut self , from : LiveBundleIndex , to : LiveBundleIndex ) -> bool {
2525 if from == to {
2626 // Merge bundle into self -- trivial merge.
2727 return true ;
@@ -224,9 +224,8 @@ impl<'a, F: Function> Env<'a, F> {
224224 true
225225 }
226226
227- pub fn merge_vreg_bundles ( & mut self ) {
228- // Create a bundle for every vreg, initially.
229- trace ! ( "merge_vreg_bundles: creating vreg bundles" ) ;
227+ // Create a bundle for every vreg, initially.
228+ fn create_vreg_bundles ( & mut self ) {
230229 for vreg in 0 ..self . vregs . len ( ) {
231230 let vreg = VRegIndex :: new ( vreg) ;
232231 if self . vregs [ vreg] . ranges . is_empty ( ) {
@@ -290,12 +289,15 @@ impl<'a, F: Function> Env<'a, F> {
290289 } ) ;
291290 self . bundles [ bundle] . spillset = ssidx;
292291 }
292+ }
293293
294- for inst in 0 ..self . func . num_insts ( ) {
295- let inst = Inst :: new ( inst) ;
294+ /// Merges bundles in the given block.
295+ fn merge_bundles_in_block ( & mut self , block : Block ) {
296+ let insns = self . func . block_insns ( block) ;
296297
297- // Attempt to merge Reuse-constraint operand outputs with the
298- // corresponding inputs.
298+ // Attempt to merge Reuse-constraint operand outputs with the
299+ // corresponding inputs.
300+ for inst in insns. iter ( ) {
299301 for op in self . func . inst_operands ( inst) {
300302 if let OperandConstraint :: Reuse ( reuse_idx) = op. constraint ( ) {
301303 let src_vreg = op. vreg ( ) ;
@@ -315,26 +317,81 @@ impl<'a, F: Function> Env<'a, F> {
315317 }
316318 }
317319
318- // Attempt to merge blockparams with their inputs.
319- for i in 0 ..self . blockparam_outs . len ( ) {
320- let BlockparamOut {
321- from_vreg, to_vreg, ..
322- } = self . blockparam_outs [ i] ;
323- trace ! (
324- "trying to merge blockparam v{} with input v{}" ,
325- to_vreg. index( ) ,
326- from_vreg. index( )
327- ) ;
328- let to_bundle = self . ranges [ self . vregs [ to_vreg] . ranges [ 0 ] . index ] . bundle ;
329- debug_assert ! ( to_bundle. is_valid( ) ) ;
330- let from_bundle = self . ranges [ self . vregs [ from_vreg] . ranges [ 0 ] . index ] . bundle ;
331- debug_assert ! ( from_bundle. is_valid( ) ) ;
332- trace ! (
333- " -> from bundle{} to bundle{}" ,
334- from_bundle. index( ) ,
335- to_bundle. index( )
336- ) ;
337- self . merge_bundles ( from_bundle, to_bundle) ;
320+ // Attempt to merge blockparams with their inputs from our predecessor.
321+ // Since critical edges are split we only have blockparams when we have
322+ // a single predecessor.
323+ if let & [ pred] = self . func . block_preds ( block) {
324+ // TODO: We're not passing the proper successor index here.
325+ let blockparams_in =
326+ self . func
327+ . branch_blockparams ( pred, self . func . block_insns ( pred) . last ( ) , 0 ) ;
328+ let blockparams_out = self . func . block_params ( block) ;
329+ for ( & blockparam_in, & blockparam_out) in blockparams_in. iter ( ) . zip ( blockparams_out) {
330+ let from_vreg = VRegIndex :: new ( blockparam_out. vreg ( ) ) ;
331+ let to_vreg = VRegIndex :: new ( blockparam_in. vreg ( ) ) ;
332+ trace ! (
333+ "trying to merge blockparam v{} with input v{}" ,
334+ to_vreg. index( ) ,
335+ from_vreg. index( )
336+ ) ;
337+ let to_bundle = self . ranges [ self . vregs [ to_vreg] . ranges [ 0 ] . index ] . bundle ;
338+ debug_assert ! ( to_bundle. is_valid( ) ) ;
339+ let from_bundle = self . ranges [ self . vregs [ from_vreg] . ranges [ 0 ] . index ] . bundle ;
340+ debug_assert ! ( from_bundle. is_valid( ) ) ;
341+ trace ! (
342+ " -> from bundle{} to bundle{}" ,
343+ from_bundle. index( ) ,
344+ to_bundle. index( )
345+ ) ;
346+ self . merge_bundles ( from_bundle, to_bundle) ;
347+ }
348+ }
349+ }
350+
351+ /// Get a list of blocks sorted by priority.
352+ ///
353+ /// Each merge eliminates the need for one move instruction in the final
354+ /// program. However a successful merge can cause a future merge to fail
355+ /// due to interference. Therefore we want to prioritize merges that have
356+ /// the highest impact:
357+ ///
358+ /// * We want to eliminate moves in loops that are executed many times.
359+ /// * We want to eliminate moves in split critical edge blocks, since it
360+ /// allows the entire block to be eliminated with jump threading.
361+ ///
362+ /// The heuristic is based on `compareMBBPriority` from LLVM.
363+ fn blocks_by_merge_priority ( & mut self ) -> Vec < Block > {
364+ let loop_depth = |block : Block | self . cfginfo . approx_loop_depth [ block. index ( ) ] ;
365+ let is_split_edge = |block : Block | {
366+ // A split critical edge is an artifical block which only exists for
367+ // the sake of passing blockparams to another block.
368+ self . func . block_insns ( block) . len ( ) == 1 && self . func . block_succs ( block) . len ( ) == 1
369+ } ;
370+ let num_connections =
371+ |block : Block | self . func . block_succs ( block) . len ( ) + self . func . block_preds ( block) . len ( ) ;
372+
373+ let mut blocks: Vec < _ > = ( 0 ..self . func . num_blocks ( ) ) . map ( |i| Block :: new ( i) ) . collect ( ) ;
374+ blocks. sort_by ( |& a, & b| {
375+ // Prioritize deeper loops first.
376+ loop_depth ( a)
377+ . cmp ( & loop_depth ( b) )
378+ . reverse ( )
379+ // Prioritize critical edges to enable jump threading.
380+ . then_with ( || is_split_edge ( a) . cmp ( & is_split_edge ( b) ) . reverse ( ) )
381+ // Prioritize blocks which are more connected in the CFG.
382+ . then_with ( || num_connections ( a) . cmp ( & num_connections ( b) ) . reverse ( ) )
383+ // Otherwise follow the original block order.
384+ } ) ;
385+ blocks
386+ }
387+
388+ pub fn merge_vreg_bundles ( & mut self ) {
389+ trace ! ( "merge_vreg_bundles: creating vreg bundles" ) ;
390+
391+ self . create_vreg_bundles ( ) ;
392+
393+ for block in self . blocks_by_merge_priority ( ) {
394+ self . merge_bundles_in_block ( block) ;
338395 }
339396
340397 trace ! ( "done merging bundles" ) ;
0 commit comments