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
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
@@ -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>,
}
17 changes: 16 additions & 1 deletion src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
@@ -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
56 changes: 48 additions & 8 deletions src/librustc_mir/build/matches/test.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -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);