Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generate fewer basic blocks for variant switches #33999

Merged
merged 3 commits into from
Jun 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc_data_structures/bitvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}
Expand Down
17 changes: 16 additions & 1 deletion src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -266,6 +267,7 @@ enum TestKind<'tcx> {
// test the branches of enum
Switch {
adt_def: AdtDef<'tcx>,
variants: BitVector,
},

// test the branches of enum
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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;
}
}
}
_ => { }
}

Expand All @@ -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
Expand Down
56 changes: 48 additions & 8 deletions src/librustc_mir/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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)),
},
}
}

Expand Down Expand Up @@ -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 |
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -383,7 +423,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);
Expand Down