mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			190 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! This pass finds basic blocks that are completely equal,
 | |
| //! and replaces all uses with just one of them.
 | |
| 
 | |
| use std::{collections::hash_map::Entry, hash::Hash, hash::Hasher, iter};
 | |
| 
 | |
| use crate::MirPass;
 | |
| 
 | |
| use rustc_data_structures::fx::FxHashMap;
 | |
| use rustc_middle::mir::visit::MutVisitor;
 | |
| use rustc_middle::mir::*;
 | |
| use rustc_middle::ty::TyCtxt;
 | |
| 
 | |
| use super::simplify::simplify_cfg;
 | |
| 
 | |
| pub struct DeduplicateBlocks;
 | |
| 
 | |
| impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
 | |
|     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
 | |
|         sess.mir_opt_level() >= 4
 | |
|     }
 | |
| 
 | |
|     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
 | |
|         debug!("Running DeduplicateBlocks on `{:?}`", body.source);
 | |
|         let duplicates = find_duplicates(body);
 | |
|         let has_opts_to_apply = !duplicates.is_empty();
 | |
| 
 | |
|         if has_opts_to_apply {
 | |
|             let mut opt_applier = OptApplier { tcx, duplicates };
 | |
|             opt_applier.visit_body(body);
 | |
|             simplify_cfg(tcx, body);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| struct OptApplier<'tcx> {
 | |
|     tcx: TyCtxt<'tcx>,
 | |
|     duplicates: FxHashMap<BasicBlock, BasicBlock>,
 | |
| }
 | |
| 
 | |
| impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
 | |
|     fn tcx(&self) -> TyCtxt<'tcx> {
 | |
|         self.tcx
 | |
|     }
 | |
| 
 | |
|     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
 | |
|         for target in terminator.successors_mut() {
 | |
|             if let Some(replacement) = self.duplicates.get(target) {
 | |
|                 debug!("SUCCESS: Replacing: `{:?}` with `{:?}`", target, replacement);
 | |
|                 *target = *replacement;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         self.super_terminator(terminator, location);
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
 | |
|     let mut duplicates = FxHashMap::default();
 | |
| 
 | |
|     let bbs_to_go_through =
 | |
|         body.basic_blocks.iter_enumerated().filter(|(_, bbd)| !bbd.is_cleanup).count();
 | |
| 
 | |
|     let mut same_hashes =
 | |
|         FxHashMap::with_capacity_and_hasher(bbs_to_go_through, Default::default());
 | |
| 
 | |
|     // Go through the basic blocks backwards. This means that in case of duplicates,
 | |
|     // we can use the basic block with the highest index as the replacement for all lower ones.
 | |
|     // For example, if bb1, bb2 and bb3 are duplicates, we will first insert bb3 in same_hashes.
 | |
|     // Then we will see that bb2 is a duplicate of bb3,
 | |
|     // and insert bb2 with the replacement bb3 in the duplicates list.
 | |
|     // When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the duplicates list
 | |
|     // with replacement bb3.
 | |
|     // When the duplicates are removed, we will end up with only bb3.
 | |
|     for (bb, bbd) in body.basic_blocks.iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup) {
 | |
|         // Basic blocks can get really big, so to avoid checking for duplicates in basic blocks
 | |
|         // that are unlikely to have duplicates, we stop early. The early bail number has been
 | |
|         // found experimentally by eprintln while compiling the crates in the rustc-perf suite.
 | |
|         if bbd.statements.len() > 10 {
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         let to_hash = BasicBlockHashable { basic_block_data: bbd };
 | |
|         let entry = same_hashes.entry(to_hash);
 | |
|         match entry {
 | |
|             Entry::Occupied(occupied) => {
 | |
|                 // The basic block was already in the hashmap, which means we have a duplicate
 | |
|                 let value = *occupied.get();
 | |
|                 debug!("Inserting {:?} -> {:?}", bb, value);
 | |
|                 duplicates.try_insert(bb, value).expect("key was already inserted");
 | |
|             }
 | |
|             Entry::Vacant(vacant) => {
 | |
|                 vacant.insert(bb);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     duplicates
 | |
| }
 | |
| 
 | |
| struct BasicBlockHashable<'tcx, 'a> {
 | |
|     basic_block_data: &'a BasicBlockData<'tcx>,
 | |
| }
 | |
| 
 | |
| impl Hash for BasicBlockHashable<'_, '_> {
 | |
|     fn hash<H: Hasher>(&self, state: &mut H) {
 | |
|         hash_statements(state, self.basic_block_data.statements.iter());
 | |
|         // Note that since we only hash the kind, we lose span information if we deduplicate the blocks
 | |
|         self.basic_block_data.terminator().kind.hash(state);
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Eq for BasicBlockHashable<'_, '_> {}
 | |
| 
 | |
| impl PartialEq for BasicBlockHashable<'_, '_> {
 | |
|     fn eq(&self, other: &Self) -> bool {
 | |
|         self.basic_block_data.statements.len() == other.basic_block_data.statements.len()
 | |
|             && &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind
 | |
|             && iter::zip(&self.basic_block_data.statements, &other.basic_block_data.statements)
 | |
|                 .all(|(x, y)| statement_eq(&x.kind, &y.kind))
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn hash_statements<'a, 'tcx, H: Hasher>(
 | |
|     hasher: &mut H,
 | |
|     iter: impl Iterator<Item = &'a Statement<'tcx>>,
 | |
| ) where
 | |
|     'tcx: 'a,
 | |
| {
 | |
|     for stmt in iter {
 | |
|         statement_hash(hasher, &stmt.kind);
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn statement_hash<H: Hasher>(hasher: &mut H, stmt: &StatementKind<'_>) {
 | |
|     match stmt {
 | |
|         StatementKind::Assign(box (place, rvalue)) => {
 | |
|             place.hash(hasher);
 | |
|             rvalue_hash(hasher, rvalue)
 | |
|         }
 | |
|         x => x.hash(hasher),
 | |
|     };
 | |
| }
 | |
| 
 | |
| fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'_>) {
 | |
|     match rvalue {
 | |
|         Rvalue::Use(op) => operand_hash(hasher, op),
 | |
|         x => x.hash(hasher),
 | |
|     };
 | |
| }
 | |
| 
 | |
| fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'_>) {
 | |
|     match operand {
 | |
|         Operand::Constant(box Constant { user_ty: _, literal, span: _ }) => literal.hash(hasher),
 | |
|         x => x.hash(hasher),
 | |
|     };
 | |
| }
 | |
| 
 | |
| fn statement_eq<'tcx>(lhs: &StatementKind<'tcx>, rhs: &StatementKind<'tcx>) -> bool {
 | |
|     let res = match (lhs, rhs) {
 | |
|         (
 | |
|             StatementKind::Assign(box (place, rvalue)),
 | |
|             StatementKind::Assign(box (place2, rvalue2)),
 | |
|         ) => place == place2 && rvalue_eq(rvalue, rvalue2),
 | |
|         (x, y) => x == y,
 | |
|     };
 | |
|     debug!("statement_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
 | |
|     res
 | |
| }
 | |
| 
 | |
| fn rvalue_eq<'tcx>(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
 | |
|     let res = match (lhs, rhs) {
 | |
|         (Rvalue::Use(op1), Rvalue::Use(op2)) => operand_eq(op1, op2),
 | |
|         (x, y) => x == y,
 | |
|     };
 | |
|     debug!("rvalue_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
 | |
|     res
 | |
| }
 | |
| 
 | |
| fn operand_eq<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
 | |
|     let res = match (lhs, rhs) {
 | |
|         (
 | |
|             Operand::Constant(box Constant { user_ty: _, literal, span: _ }),
 | |
|             Operand::Constant(box Constant { user_ty: _, literal: literal2, span: _ }),
 | |
|         ) => literal == literal2,
 | |
|         (x, y) => x == y,
 | |
|     };
 | |
|     debug!("operand_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
 | |
|     res
 | |
| }
 | 
