diff options
| author | Manish Goregaokar <manishsmail@gmail.com> | 2016-05-14 11:57:48 +0200 |
|---|---|---|
| committer | Manish Goregaokar <manishsmail@gmail.com> | 2016-05-14 11:57:48 +0200 |
| commit | 25ca82aa17bb01927bb66a31ec817b0f39509c4d (patch) | |
| tree | 1cc1162f64b276dba98e41504389e72f1365c27e | |
| parent | f5462013ee8cbf6c5bf82d1e089de6512d1f52cb (diff) | |
| parent | 49b2cdf47c983d5ea8a576346d08120f0e3af30a (diff) | |
| download | rust-25ca82aa17bb01927bb66a31ec817b0f39509c4d.tar.gz rust-25ca82aa17bb01927bb66a31ec817b0f39509c4d.zip | |
Rollup merge of #33566 - dotdash:biased_switch, r=nagisa
[MIR trans] Optimize trans for biased switches Currently, all switches in MIR are exhausitive, meaning that we can have a lot of arms that all go to the same basic block, the extreme case being an if-let expression which results in just 2 possible cases, be might end up with hundreds of arms for large enums. To improve this situation and give LLVM less code to chew on, we can detect whether there's a pre-dominant target basic block in a switch and then promote this to be the default target, not translating the corresponding arms at all. In combination with #33544 this makes unoptimized MIR trans of nickel.rs as fast as using old trans and greatly improves the times for optimized builds, which are only 30-40% slower instead of ~300%. cc #33111
| -rw-r--r-- | src/librustc_trans/mir/block.rs | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index e1318396e31..4e3386bc736 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -24,6 +24,7 @@ use meth; use type_of; use glue; use type_::Type; +use rustc_data_structures::fnv::FnvHashMap; use super::{MirContext, TempRef, drop}; use super::constant::Const; @@ -95,17 +96,32 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { adt::trans_get_discr(bcx, &repr, discr_lvalue.llval, None, true) ); - // The else branch of the Switch can't be hit, so branch to an unreachable - // instruction so LLVM knows that - let unreachable_blk = self.unreachable_block(); - let switch = bcx.switch(discr, unreachable_blk.llbb, targets.len()); + let mut bb_hist = FnvHashMap(); + for target in targets { + *bb_hist.entry(target).or_insert(0) += 1; + } + let (default_bb, default_blk) = match bb_hist.iter().max_by_key(|&(_, c)| c) { + // If a single target basic blocks is predominant, promote that to be the + // default case for the switch instruction to reduce the size of the generated + // code. This is especially helpful in cases like an if-let on a huge enum. + // Note: This optimization is only valid for exhaustive matches. + Some((&&bb, &c)) if c > targets.len() / 2 => { + (Some(bb), self.blocks[bb.index()]) + } + // We're generating an exhaustive switch, so the else branch + // can't be hit. Branching to an unreachable instruction + // lets LLVM know this + _ => (None, self.unreachable_block()) + }; + let switch = bcx.switch(discr, default_blk.llbb, targets.len()); assert_eq!(adt_def.variants.len(), targets.len()); - for (adt_variant, target) in adt_def.variants.iter().zip(targets) { - let llval = bcx.with_block(|bcx| - adt::trans_case(bcx, &repr, Disr::from(adt_variant.disr_val)) - ); - let llbb = self.llblock(*target); - build::AddCase(switch, llval, llbb) + for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { + if default_bb != Some(target) { + let llbb = self.llblock(target); + let llval = bcx.with_block(|bcx| adt::trans_case( + bcx, &repr, Disr::from(adt_variant.disr_val))); + build::AddCase(switch, llval, llbb) + } } } |
