Auto merge of #33999 - scottcarr:master, r=nikomatsakis

generate fewer basic blocks for variant switches

CC #33567
Adds a new field to TestKind::Switch that tracks the variants that are actually matched against.  The other candidates target a common "otherwise" block.
This commit is contained in:
bors 2016-06-05 03:12:38 -07:00
commit 22b36c70f9
3 changed files with 65 additions and 10 deletions

View File

@ -11,7 +11,7 @@
use std::iter::FromIterator;
/// A very simple BitVector type.
#[derive(Clone)]
#[derive(Clone, Debug, PartialEq)]
pub struct BitVector {
data: Vec<u64>,
}

View File

@ -15,6 +15,7 @@
use build::{BlockAnd, BlockAndExtension, Builder};
use rustc_data_structures::fnv::FnvHashMap;
use rustc_data_structures::bitvec::BitVector;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{AdtDef, Ty};
use rustc::mir::repr::*;
@ -266,6 +267,7 @@ enum TestKind<'tcx> {
// test the branches of enum
Switch {
adt_def: AdtDef<'tcx>,
variants: BitVector,
},
// test the branches of enum
@ -391,9 +393,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn join_otherwise_blocks(&mut self,
span: Span,
otherwise: Vec<BasicBlock>)
mut otherwise: Vec<BasicBlock>)
-> BasicBlock
{
otherwise.sort();
otherwise.dedup(); // variant switches can introduce duplicate target blocks
let scope_id = self.innermost_scope_id();
if otherwise.len() == 1 {
otherwise[0]
@ -502,6 +506,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
}
}
TestKind::Switch { adt_def: _, ref mut variants} => {
for candidate in candidates.iter() {
if !self.add_variants_to_switch(&match_pair.lvalue,
candidate,
variants) {
break;
}
}
}
_ => { }
}
@ -525,6 +538,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
&mut target_candidates))
.count();
assert!(tested_candidates > 0); // at least the last candidate ought to be tested
debug!("tested_candidates: {}", tested_candidates);
debug!("untested_candidates: {}", candidates.len() - tested_candidates);
// For each outcome of test, process the candidates that still
// apply. Collect a list of blocks where control flow will

View File

@ -19,6 +19,7 @@ use build::Builder;
use build::matches::{Candidate, MatchPair, Test, TestKind};
use hair::*;
use rustc_data_structures::fnv::FnvHashMap;
use rustc_data_structures::bitvec::BitVector;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
use rustc::mir::repr::*;
@ -33,7 +34,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => {
Test {
span: match_pair.pattern.span,
kind: TestKind::Switch { adt_def: adt_def.clone() },
kind: TestKind::Switch {
adt_def: adt_def.clone(),
variants: BitVector::new(self.hir.num_variants(adt_def)),
},
}
}
@ -125,9 +129,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
});
true
}
PatternKind::Variant { .. } => {
panic!("you should have called add_variants_to_switch instead!");
}
PatternKind::Range { .. } |
PatternKind::Variant { .. } |
PatternKind::Slice { .. } |
PatternKind::Array { .. } |
PatternKind::Wild |
@ -140,6 +145,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
}
pub fn add_variants_to_switch<'pat>(&mut self,
test_lvalue: &Lvalue<'tcx>,
candidate: &Candidate<'pat, 'tcx>,
variants: &mut BitVector)
-> bool
{
let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
Some(match_pair) => match_pair,
_ => { return false; }
};
match *match_pair.pattern.kind {
PatternKind::Variant { adt_def: _ , variant_index, .. } => {
// We have a pattern testing for variant `variant_index`
// set the corresponding index to true
variants.insert(variant_index);
true
}
_ => {
// don't know how to add these patterns to a switch
false
}
}
}
/// Generates the code to perform a test.
pub fn perform_test(&mut self,
block: BasicBlock,
@ -148,11 +178,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
-> Vec<BasicBlock> {
let scope_id = self.innermost_scope_id();
match test.kind {
TestKind::Switch { adt_def } => {
TestKind::Switch { adt_def, ref variants } => {
let num_enum_variants = self.hir.num_variants(adt_def);
let target_blocks: Vec<_> =
(0..num_enum_variants).map(|_| self.cfg.start_new_block())
.collect();
let mut otherwise_block = None;
let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| {
if variants.contains(i) {
self.cfg.start_new_block()
} else {
if otherwise_block.is_none() {
otherwise_block = Some(self.cfg.start_new_block());
}
otherwise_block.unwrap()
}
}).collect();
debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}",
num_enum_variants, variants.iter().count(), variants);
self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
discr: lvalue.clone(),
adt_def: adt_def,
@ -415,7 +455,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
match test.kind {
// If we are performing a variant switch, then this
// informs variant patterns, but nothing else.
TestKind::Switch { adt_def: tested_adt_def } => {
TestKind::Switch { adt_def: tested_adt_def , .. } => {
match *match_pair.pattern.kind {
PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
assert_eq!(adt_def, tested_adt_def);